From fe1620cdf569d52fac978cd264cd1b081bda95b6 Mon Sep 17 00:00:00 2001 From: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:39:58 +0000 Subject: [PATCH] fix(chat): always show copy and fork under a finished agent turn (#40474) --- .../messages/actions/NativeChatAgentControls.tsx | 2 +- .../messages/actions/use-native-chat-reveal-latest.ts | 2 +- .../messages/rows/NativeChatAssistantMessageRow.tsx | 9 ++------- .../native-chat/messages/rows/NativeChatMessageRow.tsx | 2 +- .../pane/actions/NativeChatContextMenuItems.tsx | 2 +- .../viewport/find/use-native-chat-find-matches.ts | 2 +- 6 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/renderer/src/components/native-chat/messages/actions/NativeChatAgentControls.tsx b/src/renderer/src/components/native-chat/messages/actions/NativeChatAgentControls.tsx index 34e6b81cbf3..5db891afcaf 100644 --- a/src/renderer/src/components/native-chat/messages/actions/NativeChatAgentControls.tsx +++ b/src/renderer/src/components/native-chat/messages/actions/NativeChatAgentControls.tsx @@ -21,7 +21,7 @@ export function NativeChatAgentControls({ }): React.JSX.Element { const t = useTranslate() return ( - // Hover-only row chrome: find skips it rather than counting what the mouse happens to show. + // Row chrome, not transcript: find skips it.
{/* A visual line means nothing pasted outside Orca, so the copy leaves it out. An image-only response has nothing to copy, so it has no Copy. */} diff --git a/src/renderer/src/components/native-chat/messages/actions/use-native-chat-reveal-latest.ts b/src/renderer/src/components/native-chat/messages/actions/use-native-chat-reveal-latest.ts index 00c476f9091..19b8e0e927f 100644 --- a/src/renderer/src/components/native-chat/messages/actions/use-native-chat-reveal-latest.ts +++ b/src/renderer/src/components/native-chat/messages/actions/use-native-chat-reveal-latest.ts @@ -56,7 +56,7 @@ export type NativeChatMessageListHandle = { readerAtEnd: () => boolean /** Bring a find match into view, clear of the find bar; the reader stops following the latest. */ revealFindMatch: (match: Range, bar: DOMRectReadOnly | null) => void - /** What the row drawing this message copies from its hover button; null when none draws it. */ + /** What the row drawing this message copies from its Copy button; null when none draws it. */ messageCopyText: (messageId: string) => string | null /** Open a subagent's activity where it sits in the conversation and bring it into view. */ revealSubagent: (agentId: string) => void diff --git a/src/renderer/src/components/native-chat/messages/rows/NativeChatAssistantMessageRow.tsx b/src/renderer/src/components/native-chat/messages/rows/NativeChatAssistantMessageRow.tsx index 8f7fc2045ae..9af89d8fddb 100644 --- a/src/renderer/src/components/native-chat/messages/rows/NativeChatAssistantMessageRow.tsx +++ b/src/renderer/src/components/native-chat/messages/rows/NativeChatAssistantMessageRow.tsx @@ -76,16 +76,11 @@ export function NativeChatAssistantMessageRow({ !message.agentStart && !inSubagentSection && isRootAgentJournalItem(message) - // A row whose fork is opening keeps its controls up so the spinner stays visible. - const revealOnHover = - fork?.pendingItemId === message.id - ? 'mt-1 w-fit select-none' - : 'mt-1 w-fit select-none transition-opacity can-hover:pointer-events-none can-hover:opacity-0 group-hover:pointer-events-auto group-hover:opacity-100 [.group:has(:focus-visible)_&]:pointer-events-auto [.group:has(:focus-visible)_&]:opacity-100' return (
0 ? message.id : undefined} data-native-chat-message-tone={isSystem ? 'faint' : undefined} className={cn( @@ -126,7 +121,7 @@ export function NativeChatAssistantMessageRow({ ) : null } className={cn( - revealOnHover, + 'mt-1 w-fit select-none', // They hang into the gap below; a section keeps them inside its border instead. !inSubagentSection && '-mb-5' )} diff --git a/src/renderer/src/components/native-chat/messages/rows/NativeChatMessageRow.tsx b/src/renderer/src/components/native-chat/messages/rows/NativeChatMessageRow.tsx index fbf666c74ab..11c9bbda907 100644 --- a/src/renderer/src/components/native-chat/messages/rows/NativeChatMessageRow.tsx +++ b/src/renderer/src/components/native-chat/messages/rows/NativeChatMessageRow.tsx @@ -55,7 +55,7 @@ export type MessageRowProps = { activeTurnIsWorking?: boolean /** This row's tool run is the turn's last, so it is the one still live. */ trailingRun?: boolean - /** Hover controls would overlap the next row. */ + /** Response controls would overlap the next row. */ continuesTurn?: boolean /** In a turn: whether this row holds the response's actions (its last words, else its last images). */ endsResponse?: boolean diff --git a/src/renderer/src/components/native-chat/pane/actions/NativeChatContextMenuItems.tsx b/src/renderer/src/components/native-chat/pane/actions/NativeChatContextMenuItems.tsx index bd19901886f..c39e388ffc4 100644 --- a/src/renderer/src/components/native-chat/pane/actions/NativeChatContextMenuItems.tsx +++ b/src/renderer/src/components/native-chat/pane/actions/NativeChatContextMenuItems.tsx @@ -9,7 +9,7 @@ import type { NativeChatContextMenuTarget } from './native-chat-context-menu-tar import type { NativeChatRewindSurface } from '../../messages/actions/use-native-chat-rewind-host' function copyText(text: string): void { - // Best-effort, as the hover copy buttons are: the clipboard can reject when unfocused. + // Best-effort, as the row Copy buttons are: the clipboard can reject when unfocused. window.api.ui.writeClipboardText(text).catch(() => {}) } diff --git a/src/renderer/src/components/native-chat/viewport/find/use-native-chat-find-matches.ts b/src/renderer/src/components/native-chat/viewport/find/use-native-chat-find-matches.ts index b0dcadc3168..2801dd93655 100644 --- a/src/renderer/src/components/native-chat/viewport/find/use-native-chat-find-matches.ts +++ b/src/renderer/src/components/native-chat/viewport/find/use-native-chat-find-matches.ts @@ -15,7 +15,7 @@ const chatFindHighlights = createDomTextSearchHighlights({ const TRANSCRIPT_COLUMN_SELECTOR = '[data-native-chat-transcript-column]' const TRANSCRIPT_SCROLL_SELECTOR = '[data-native-chat-scroll]' -/** Row chrome that only shows on hover (timestamps, copy): marked, so counts never follow the mouse. */ +/** Row chrome, not transcript (timestamps, copy): marked, so find counts only message text. */ const SKIPPED_TEXT_SELECTOR = '.sr-only, [hidden], [data-native-chat-find-skip]' /** A streamed reply's text node while its words fade in: each new word is its own element. */ const WORD_GROUP_SELECTOR = '[data-word-group]'