agent chat: every reply keeps its action row, whatever follows it
Every finished assistant reply keeps its action row (Copy, Pin, Read aloud, More, its age) whenever something follows it: a steer, another agent's message, a "Sent message to ā¦" row or a tool row. A steer waiting for the agent gets Copy too.
Leo, 2026-10-29: "i feel like im frequently missing the copy/whatever buttons on these steers... but claude code allows those buttons in these situations. let's have those"
Why it was missing
A reply's only action row was its turn's footer (TurnFooter). The footer comes at the turn's end and copies only the final answer. A steer, an incoming message, a sent-message row or a tool row continues the turn, so a reply before one of them had no row at all. Incoming lines already had Copy (ReceivedActions). A waiting steer had only Send now and ā.
Fix
-
model.ts:hasReplyActions(row, next)decides each reply from the row drawn after it. A finished reply with text gets its row unless one of these holds:- nothing follows it yet;
- the row after it is its turn's footer, which serves it;
- it sits right over the running line that the footer replaces (
isBeforeFooter).
So the turn's end moves nothing above it. The layout's row edge key carries the flag, which makes the height cache and re-placement notice when a later row arrives.
turns.tsis unchanged, so no turn-wide pass runs per token. -
App.tsx: each drawn row gets the flag in O(1):RowFrametakesreplyActions, compares it in its memo, and re-measures when it changes;- drawn heights carry the flag, both from the frame and from the resize observer;
- placement drops a drawn height whose flag no longer matches, and re-places from the last reply whose flag changed.
-
MessageActions.tsx: a newReplyActionsrow (Copy, Pin, Read aloud, More, age) in the footer'scv-turn-actionslook, drawn byMessageRow. It has no Fork, because assistant rows carry noseq. -
CSS: the row's space is always kept and it shows on hover or keyboard focus, as the other action rows do, so nothing jumps on hover. The estimate is 28 px: a 4 px gap plus a 24 px row.
-
PendingSteer.tsx: a waiting steer shows Copy after Send now and ā. Send now still leads the action row.
Visible change: a reply that now has the row is 28 px taller even when not hovered.
Gallery
agent-pane.reply-actions has four versions, each hovered so its row shows: after-steer, after-incoming, after-sent and steer-waiting. The before/after stills come from the gallery advisory run on this PR.
Tests
test/agent-pane-reply-actions.test.tsxcovers which replies get the row: after a steer, another agent's message and a sent message; the running-line case; the chat's last row; and the estimate. Committed alone before the fix (41ec213a), it was red on base, 0 of 5 passing: https://github.com/manaflow-ai/cmux/actions/runs/40837439863test/agent-pane-reply-actions-frame.test.tsxrendersVirtualTranscript:- When a steer arrives after a drawn reply, the reply gets its row, in paged history too. Red at the commit before the frame fix: https://github.com/manaflow-ai/cmux/actions/runs/40850533062
- A resize reported by the browser keeps the measured height of a reply that has its row. Red at the test-only commit a29e69b5: https://github.com/manaflow-ai/cmux/actions/runs/40851223433
- At the head:
tscpasses, and 57 of 58 tests pass across these suites plus pending-steer, steer take-back, transcript spacing, row estimates, queue scroll, reveal message and minimap: https://github.com/manaflow-ai/cmux/actions/runs/40850672617- The one failure is the
.cv-userchild-list check atagent-pane-pending-steer.test.tsx:99. It fails the same way at base 684f74f1, so it is a base red.
- The one failure is the
Observed / Not run
- Observed: the runs above.
- Not run: gallery stills (they come from the advisory run). No native build is needed, because the change is webview-only.
Changelog
Fixed: A reply followed by a steer, another agent's message or a sent message keeps its Copy, Pin and Read aloud buttons, and a waiting steer can be copied.
Agent session: agent session
š¤ Generated with Claude Code