From 36362ac9bc4840e5f302527f941770f85e5740c5 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Mon, 2 Nov 2026 00:40:28 +0000 Subject: [PATCH] Agent chat: the last turn's footer (Copy, Fork, Pin, Read aloud) shows without a hover once the turn ends Leo, 2026-11-02: "i should be able to see the copy buttons on your responses when your things end" Co-Authored-By: Claude Opus 5.5 Agent-Session: https://cmux.com/deeplink/agent?id=aid-71440d91e22e80b54684 --- webviews/src/agent-session/acpmux/App.tsx | 8 +++++++- .../agent-session/acpmux/conversation/conversation.css | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/webviews/src/agent-session/acpmux/App.tsx b/webviews/src/agent-session/acpmux/App.tsx index e469090aedb2..46cd9c48e635 100644 --- a/webviews/src/agent-session/acpmux/App.tsx +++ b/webviews/src/agent-session/acpmux/App.tsx @@ -2110,6 +2110,7 @@ const RowFrame = React.memo(function RowFrame({ replyActions, status, lastFooter, + finalFooter, continuationFooter, lastTurn, index, @@ -2147,6 +2148,10 @@ const RowFrame = React.memo(function RowFrame({ status: StatusEdge | undefined; /// The chat's last footer: it ends at the message gap, so no band sits over the composer. lastFooter: boolean; + /// The footer that ends the chat (nothing drawn after it): its turn has ended, so its Copy row + /// shows without a hover (Leo, 2026-11-02: "i should be able to see the copy buttons on your + /// responses when your things end"). + finalFooter: boolean; /// The next drawn turn belongs to this same assistant: half the normal turn gap. continuationFooter: boolean; /// The final row of a latest turn without a footer: preserve the normal turn gap at the composer. @@ -2256,7 +2261,7 @@ const RowFrame = React.memo(function RowFrame({ id={rowDomId(row.id)} data-row-id={row.id} data-row-card={row.ended ? "true" : undefined} - className={`acpmux-row acpmux-${kind}${isLineRow(row) ? " acpmux-row--line" : ""}${row.below ? " acpmux-row--below" : ""}${joinsPrevious ? " acpmux-row--after-step" : ""}${joinsNext ? " acpmux-row--before-step" : ""}${beforeFooter ? " acpmux-row--before-footer" : ""}${replyActions ? " acpmux-row--reply-room" : ""}${status ? ` acpmux-row--${status}` : ""}${lastFooter ? " acpmux-row--last-footer" : ""}${continuationFooter ? " acpmux-row--continuation-footer" : ""}${lastTurn ? " acpmux-row--last-turn" : ""}${messageEntering ? " acpmux-row--message-enter" : entering ? " acpmux-row--enter" : ""}${olderFade ? " acpmux-row--older" : ""}`} + className={`acpmux-row acpmux-${kind}${isLineRow(row) ? " acpmux-row--line" : ""}${row.below ? " acpmux-row--below" : ""}${joinsPrevious ? " acpmux-row--after-step" : ""}${joinsNext ? " acpmux-row--before-step" : ""}${beforeFooter ? " acpmux-row--before-footer" : ""}${replyActions ? " acpmux-row--reply-room" : ""}${status ? ` acpmux-row--${status}` : ""}${lastFooter ? " acpmux-row--last-footer" : ""}${finalFooter ? " acpmux-row--final-footer" : ""}${continuationFooter ? " acpmux-row--continuation-footer" : ""}${lastTurn ? " acpmux-row--last-turn" : ""}${messageEntering ? " acpmux-row--message-enter" : entering ? " acpmux-row--enter" : ""}${olderFade ? " acpmux-row--older" : ""}`} data-transcript-active={active ? "true" : undefined} data-transcript-selected={selected ? "true" : undefined} aria-current={active ? "true" : undefined} @@ -5397,6 +5402,7 @@ export function VirtualTranscript({ replyActions={keepsReplyRoom(row, next)} status={statusEdge(row, next, prevDrawnRow(rows, absoluteIndex))} lastFooter={isLastFooter(row, next)} + finalFooter={row.kind === "turnSummary" && !next} continuationFooter={isContinuationFooter(row, next)} lastTurn={row.lastTurn === true} index={absoluteIndex} diff --git a/webviews/src/agent-session/acpmux/conversation/conversation.css b/webviews/src/agent-session/acpmux/conversation/conversation.css index c361d3a92f41..b707153e4551 100644 --- a/webviews/src/agent-session/acpmux/conversation/conversation.css +++ b/webviews/src/agent-session/acpmux/conversation/conversation.css @@ -3784,7 +3784,8 @@ a.cv-sha:focus-visible { font-variant-numeric: tabular-nums; } /* Hidden, its space kept, but for a failed turn, the turn the reader points at (data-turn-revealed, - VirtualTranscript) or keyboard focus. */ + VirtualTranscript), keyboard focus, or the turn that ends the chat (App.tsx `acpmux-row--final-footer`): + once the agent is done its Copy shows without a hover, as Claude's and ChatGPT's (Leo, 2026-11-02). */ .acpmux-turnSummary .cv-turn-actions { opacity: 0; } @@ -3794,7 +3795,8 @@ a.cv-sha:focus-visible { .acpmux-turnSummary:has([data-popup-open]) .cv-turn-actions, .acpmux-turnSummary .cv-turn-actions:focus-within, .acpmux-turnSummary .cv-turn-actions--failed, -.acpmux-turnSummary .cv-turn-actions--resumable { +.acpmux-turnSummary .cv-turn-actions--resumable, +.acpmux-row--final-footer .cv-turn-actions { opacity: 1; } /* A reply's own row (MessageActions.tsx ReplyActions: a steer, another agent's message or a tool