From c6dfda6eee57c059d1839fdab9fca3fe4e9040dd Mon Sep 17 00:00:00 2001 From: Shakker Date: Wed, 15 Jul 2026 00:59:27 +0100 Subject: [PATCH] fix: announce chat replies from the active pane --- ui/src/pages/chat/chat-pane.ts | 1 + ui/src/pages/chat/chat-view.test.ts | 39 +++++++++++++++++++++ ui/src/pages/chat/chat-view.ts | 2 ++ ui/src/pages/chat/components/chat-thread.ts | 3 +- 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/ui/src/pages/chat/chat-pane.ts b/ui/src/pages/chat/chat-pane.ts index ebd1a6f3d817..0fefd298062f 100644 --- a/ui/src/pages/chat/chat-pane.ts +++ b/ui/src/pages/chat/chat-pane.ts @@ -1944,6 +1944,7 @@ class ChatPane extends OpenClawLightDomElement { transcript: this.transcript, paneId: this.paneId, sessionKey: state.sessionKey, + announceTranscript: this.active, onSessionKeyChange: (next) => { this.onPaneSessionChange?.(this.paneId, next); }, diff --git a/ui/src/pages/chat/chat-view.test.ts b/ui/src/pages/chat/chat-view.test.ts index 7f1749c61d47..c7c0956d336c 100644 --- a/ui/src/pages/chat/chat-view.test.ts +++ b/ui/src/pages/chat/chat-view.test.ts @@ -1103,6 +1103,45 @@ describe("chat transcript rendering", () => { "New answer", ); }); + + it("does not announce appended rows from an inactive split pane", () => { + const transcript = new ChatTranscriptController({ + addController: () => undefined, + removeController: () => undefined, + requestUpdate: () => undefined, + updateComplete: Promise.resolve(true), + } satisfies ReactiveControllerHost); + const container = document.createElement("div"); + const existing = { + __testVirtualRow: true, + __testVirtualKey: "assistant-1", + __testVirtualRole: "assistant", + content: "Existing answer", + }; + const appended = { + __testVirtualRow: true, + __testVirtualKey: "assistant-2", + __testVirtualRole: "assistant", + content: "New answer", + }; + + render( + renderChat(createChatProps({ announceTranscript: false, transcript, messages: [existing] })), + container, + ); + render( + renderChat( + createChatProps({ + announceTranscript: false, + transcript, + messages: [existing, appended], + }), + ), + container, + ); + + expect(container.querySelector(".chat-transcript-announcement")?.textContent).toBe(""); + }); }); describe("chat goal status", () => { diff --git a/ui/src/pages/chat/chat-view.ts b/ui/src/pages/chat/chat-view.ts index 0cf711eeb3f0..ff540768cced 100644 --- a/ui/src/pages/chat/chat-view.ts +++ b/ui/src/pages/chat/chat-view.ts @@ -72,6 +72,7 @@ export type ChatProps = { transcript: ChatTranscriptController; paneId: string; sessionKey: string; + announceTranscript?: boolean; onSessionKeyChange: (next: string) => void; thinkingLevel: string | null; showThinking: boolean; @@ -264,6 +265,7 @@ export function renderChat(props: ChatProps) { { paneId: props.paneId, sessionKey: props.sessionKey, + announceTranscript: props.announceTranscript, loading: props.loading, historyPagination: props.historyPagination, messages: props.messages, diff --git a/ui/src/pages/chat/components/chat-thread.ts b/ui/src/pages/chat/components/chat-thread.ts index 74f32572c0de..0fecee684275 100644 --- a/ui/src/pages/chat/components/chat-thread.ts +++ b/ui/src/pages/chat/components/chat-thread.ts @@ -91,6 +91,7 @@ type ChatThreadState = { type ChatThreadProps = { paneId: string; sessionKey: string; + announceTranscript?: boolean; loading: boolean; historyPagination?: { loading: boolean; @@ -1113,7 +1114,7 @@ function renderChatThreadContents( transcriptRows, (row) => (row.kind === "item" ? renderItem(row.item) : row.content), latestTranscriptAnnouncement(collapsedItems), - !state.searchOpen && !props.loading, + props.announceTranscript !== false && !state.searchOpen && !props.loading, props.historyPagination ? renderHistorySentinel(props.historyPagination.loading) : nothing,