diff --git a/ts/packages/chat-ui/README.AUTOGEN.md b/ts/packages/chat-ui/README.AUTOGEN.md index 5aba2c0892..8e699568f4 100644 --- a/ts/packages/chat-ui/README.AUTOGEN.md +++ b/ts/packages/chat-ui/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # chat-ui β€” AI-generated documentation @@ -12,13 +12,13 @@ ## Overview -The `chat-ui` package provides a shared, framework-free chat user interface for TypeAgent applications. It is designed to deliver a consistent and interactive chat experience across multiple platforms, including the VS Code shell extension, the browser extension chat panel, and the Visual Studio extension webview. The package includes components for rendering user and agent messages, handling streaming updates, replaying chat history, managing connection status, and collecting user feedback. +The `chat-ui` package provides a shared, framework-free chat user interface for TypeAgent applications. It is designed to ensure a consistent and interactive chat experience across multiple platforms, including the VS Code shell extension, the browser extension chat panel, and the Visual Studio extension webview. The package includes components for rendering user and agent messages, handling streaming updates, replaying chat history, managing connection status, and collecting user feedback. ## What it does -The `chat-ui` package offers a range of features to support dynamic and interactive chat interfaces: +The `chat-ui` package offers a set of tools and components to build and manage chat interfaces. Its primary features include: -- **ChatPanel**: The primary component for rendering the chat interface. It supports: +- **ChatPanel**: The core component for rendering the chat interface. It supports: - Adding user and agent messages via `addAgentMessage`. - Updating display metadata with `setDisplayInfo`. @@ -35,7 +35,7 @@ The `chat-ui` package offers a range of features to support dynamic and interact - **Shared Styles**: A CSS file (`styles/chat.css`) is included to provide a consistent appearance for the chat UI across all host applications. -The package is used by several TypeAgent components, such as the VS Code shell, the browser extension, and the Visual Studio extension webview. +The package is used by several TypeAgent components, such as the VS Code shell, the browser extension, and the Visual Studio extension webview, ensuring a unified user experience across these platforms. ## Setup @@ -56,7 +56,7 @@ For additional details on usage and integration, refer to the hand-written READM ## Key Files -The `chat-ui` package is structured into several key files, each responsible for specific functionality: +The `chat-ui` package is organized into several key files, each responsible for specific functionality: - **[index.ts](./src/index.ts)**: The main entry point, exporting the package's primary components and utilities. - **[chatPanel.ts](./src/chatPanel.ts)**: Implements the `ChatPanel` component, which is the core of the chat UI. It handles user input, agent messages, and display updates. @@ -120,6 +120,6 @@ External: `ansi_up`, `dompurify`, `markdown-it` --- -_Auto-generated against commit `abfa7f206e497772254557b7d65cf9252a4728b2` on `2026-07-21T21:21:02.587Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter chat-ui docs:verify-links` to spot-check._ +_Auto-generated against commit `0b06d6a1cc9d93888e91e217057d9c148b3cc49f` on `2026-07-22T04:45:01.388Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter chat-ui docs:verify-links` to spot-check._ diff --git a/ts/packages/chat-ui/src/chatPanel.ts b/ts/packages/chat-ui/src/chatPanel.ts index 80319f024a..5d3e944c95 100644 --- a/ts/packages/chat-ui/src/chatPanel.ts +++ b/ts/packages/chat-ui/src/chatPanel.ts @@ -1874,11 +1874,17 @@ export class ChatPanel { */ private clearAgentRunning(threadId: string): void { this.agentRunningRequestIds.delete(threadId); - this.threadContainers.get(threadId)?.clearRunning(); + // The request finished: collapse every step's reasoning "Thinking" + // block (including the last, still-expanded one) so completed reasoning + // minimizes. + const current = this.threadContainers.get(threadId); + current?.clearRunning(); + current?.collapseReasoning(); const all = this.requestAgentContainers.get(threadId); if (all) { for (const container of all) { container.clearRunning(); + container.collapseReasoning(); } } } @@ -2541,6 +2547,10 @@ export class ChatPanel { source.startsWith("dispatcher.reasoningAction") ) { stepContainer.overrideSource(source, sourceIcon); + // Also covers a reused existing bubble (e.g. a generic + // "Executing action" temporary) that never passed through the + // reasoning detection in getOrCreateAgentContainer. + stepContainer.markReasoning(); } if (requestId) { const start = this.requestStartByRequestId.get(requestId); @@ -2560,6 +2570,7 @@ export class ChatPanel { for (const prior of priorSteps) { if (prior !== stepContainer) { prior.clearRunning(); + prior.collapseReasoning(); } } } @@ -2630,8 +2641,17 @@ export class ChatPanel { requestId: string | undefined, ): AgentMessageContainer { const threadId = this.resolveThreadId(requestId); + // A reasoning display carries a "dispatcher.reasoningAction.*" source; + // mark the bubble so its working rail reads as reasoning (purple) + // whether the content streams (temporary), commits as a step, or + // arrives as a block. + const isReasoning = + source?.startsWith("dispatcher.reasoningAction") ?? false; const existing = this.threadContainers.get(threadId); if (existing) { + if (isReasoning) { + existing.markReasoning(); + } return existing; } // sourceIcon="πŸ€–"). Without this, the bubble would be created with @@ -2670,6 +2690,9 @@ export class ChatPanel { anchor, ); this.threadContainers.set(threadId, container); + if (isReasoning) { + container.markReasoning(); + } container.div.dataset.requestId = threadId; if (this.developerMode) { this.attachDeleteControl(container.div, threadId, "agent"); @@ -3722,6 +3745,17 @@ export class ChatPanel { (requestContainers.length > 0 ? requestContainers[requestContainers.length - 1] : undefined); + // For a reasoning thread with a trail of "Thinking"/tool steps, give + // the final answer step a gap separating it from the reasoning above. + // Target the last committed STEP (the answer), not the newest + // container, which can be a post-answer status block (e.g. copilot's + // "Task flow registered"). + const answerStep = requestContainers.find( + (c) => c.div === this.lastStepAnchorByThread.get(threadId), + ); + if (requestContainers.length > 1 && answerStep?.isReasoning()) { + answerStep.setReasoningAnswer(true); + } const firstMessageMs = this.firstMessageMsByRequestId.get(threadId); if (result?.cancelled) { // Mirror Electron's "⚠ Cancelled" status, anchored to the @@ -5188,6 +5222,8 @@ class AgentMessageContainer { private readonly timestampDiv: HTMLDivElement; private feedbackWidget?: FeedbackWidget; private statusRail?: HTMLDivElement; + private reasoning = false; + private readonly collapsedReasoning = new WeakSet(); private lastAppendMode?: DisplayAppendMode; // Mirrors the shell's swapContent pattern: when action JSON is set, // clicking the agent name toggles the message body between the @@ -5593,12 +5629,31 @@ class AgentMessageContainer { bodyDiv: this.messageDiv, headerDiv: this.timestampDiv, messageDiv: this.messageDiv, + openInWindow: this.platformAdapter.openMessageInWindow + ? () => this.openMessageInWindow() + : undefined, }, controller, variant, ); } + // Ask the host to open this message in a new window. Messages backed by an + //