cmux NEXT transcript: one turn footer at the bottom; copy copies the whole turn

Closed
TE
4d ago
+134-394

Takes back #27313: Leo ordered one action footer per turn on 2026-10-31 ("so let's invert our behaviour"), so a reply inside a turn no longer draws its own action row, and the frame test for that row goes with it.

Leo, 2026-10-31: "claude code doesn't have individual copy pastable things for the entire thing, only the bottom. ironically we have literally the complete opposite. so let's invert our behaviour. btw yes claude code desktop does copy all of it incl. tool call top level things.. tho idk if we also want to to do that"

Leo, on the reference screenshots of Claude Code desktop: "this is claude code". Those screenshots show one footer under a turn's last text (copy, fork, pin, speak, time). A turn that is only a tool row ("Ran a command, listed 62 agents") shows only fork, pin and the time on hover, with no copy and no speak. A long turn has one footer at the very bottom.

What changes

  • One action footer per turn, at the bottom. A reply inside a turn no longer draws its own copy/pin/speak/time row (model.ts hasReplyActions now returns false). The turn's footer (TurnFooter) is the only one. It keeps today's order (copy, fork, pin, speak, more, time) and today's hover reveal.
  • Copy copies the whole turn. The new conversation/turnCopy.ts builds what Copy puts on the clipboard: every reply's text in order, plus each run of tool calls between replies as its top-level line. A run of two or more calls gives its summary line ("Read a file, ran 2 commands"), and a single call gives its own label ("Read turns.ts"). Tool output is never copied. shapeTurn puts the pieces on the summary row as turnCopy.
    • Including the tool lines matches Claude Code desktop, and it is the default. Leo said "idk if we also want to to do that", so dropping them later is a one-line change in turnCopyText.
  • A turn with no text has no copy and no speak. This was already true, since the footer only drew them when the turn had answer text. Fork, pin and time stay, as in Claude Code.
  • Spec: teamleaderleo/leo-workspace#1947 adds "one action footer per turn, at the bottom; copy copies the turn" to design/chat-transcript.md.

This replaces the per-reply action row from 2026-10-29 (ReplyActions; Leo then: "i feel like im frequently missing the copy/whatever buttons on these steers"). Leo's order today inverts that behaviour, so this PR takes it back.

Tests

  • test/agent-pane-reply-actions.test.tsx is rewritten. It checks that no reply inside a turn has its own row (before a steer, a received message or a tool call, and in a running turn). It checks that the footer copies replies plus tool-run lines in order. It also checks that a turn of tool calls alone has no Copy.
  • test/agent-pane-reply-actions-frame.test.tsx is removed, because the per-reply row it tested no longer exists.
  • test/transcript-pr-revision.test.tsx and test/pr-dock-inline-layout.test.ts now expect a reply inside a turn to keep the PR fallback, since that reply no longer has its own action row.

Follow-ups

  • Delete the dead ReplyActions component, its gallery page, and the replyActions height plumbing in App.tsx. They are kept here so the diff stays small.
  • The summary row's version does not yet include earlier replies' versions. If an earlier reply changes after the turn ends, the memoized footer could keep old copy text. That happens rarely, since replies settle before the summary arrives.

No screenshots: per the rulebook, Leo tries the build himself.

Observed / Not run

  • Own review subagent: its blocking findings (two tests that expected .cv-reply-actions) are fixed in the second commit.
  • The webview suite runs in this PR's CI. It was not run locally, because this host is Leo's interactive Mac.

Changelog

Changed: An agent's turn has one action row at its bottom, and its Copy copies the whole turn, including each tool step's line.

Agent session: agent session

šŸ¤– Generated with Claude Code