From 8203f6582ff534597cdb341efbe8bc5e10dde4d6 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Fri, 5 Dec 2025 02:45:44 +0800 Subject: [PATCH] feat(vscode-ide-companion/extension): relay diff accept/cancel events to chat webview Added functionality to relay diff accepted/cancelled events from the IDE to the chat webview so users get immediate feedback when they accept or cancel diffs in the Claude Code style. --- .../vscode-ide-companion/src/extension.ts | 40 +++++++++++++++++++ .../webview/components/PermissionDrawer.tsx | 21 +++++----- .../components/messages/UserMessage.tsx | 2 +- .../toolcalls/Search/SearchToolCall.tsx | 27 ++++++++++++- .../toolcalls/shared/LayoutComponents.css | 3 +- 5 files changed, 78 insertions(+), 15 deletions(-) diff --git a/packages/vscode-ide-companion/src/extension.ts b/packages/vscode-ide-companion/src/extension.ts index 2342e466..224613c1 100644 --- a/packages/vscode-ide-companion/src/extension.ts +++ b/packages/vscode-ide-companion/src/extension.ts @@ -5,6 +5,7 @@ */ import * as vscode from 'vscode'; +import * as path from 'node:path'; import { IDEServer } from './ide-server.js'; import semver from 'semver'; import { DiffContentProvider, DiffManager } from './diff-manager.js'; @@ -166,6 +167,45 @@ export async function activate(context: vscode.ExtensionContext) { createWebViewProvider, ); + // Relay diff accept/cancel events to the chat webview as assistant notices + // so the user sees immediate feedback in the chat thread (Claude Code style). + context.subscriptions.push( + diffManager.onDidChange((notification) => { + try { + const method = (notification as { method?: string }).method; + if (method !== 'ide/diffAccepted' && method !== 'ide/diffClosed') { + return; + } + + const params = ( + notification as unknown as { + params?: { filePath?: string }; + } + ).params; + const filePath = params?.filePath ?? ''; + const fileBase = filePath ? path.basename(filePath) : ''; + const text = + method === 'ide/diffAccepted' + ? `Accepted changes${fileBase ? ` to ${fileBase}` : ''}.` + : `Cancelled changes${fileBase ? ` to ${fileBase}` : ''}.`; + + for (const provider of webViewProviders) { + const panel = provider.getPanel(); + panel?.webview.postMessage({ + type: 'message', + data: { + role: 'assistant', + content: text, + timestamp: Date.now(), + }, + }); + } + } catch (e) { + console.warn('[Extension] Failed to relay diff event to chat:', e); + } + }), + ); + context.subscriptions.push( vscode.workspace.onDidCloseTextDocument((doc) => { if (doc.uri.scheme === DIFF_SCHEME) { diff --git a/packages/vscode-ide-companion/src/webview/components/PermissionDrawer.tsx b/packages/vscode-ide-companion/src/webview/components/PermissionDrawer.tsx index 752ce8bd..e9abed85 100644 --- a/packages/vscode-ide-companion/src/webview/components/PermissionDrawer.tsx +++ b/packages/vscode-ide-companion/src/webview/components/PermissionDrawer.tsx @@ -145,16 +145,13 @@ export const PermissionDrawer: React.FC = ({ > {/* Background layer */}
{/* Title */} -
-
+
+
{getTitle()}
@@ -169,20 +166,20 @@ export const PermissionDrawer: React.FC = ({ key={option.optionId} className={`flex items-center gap-2 px-2 py-1.5 text-left w-full box-border rounded-[4px] border-0 shadow-[inset_0_0_0_1px_var(--app-transparent-inner-border)] transition-colors duration-150 text-[var(--app-primary-foreground)] ${ isFocused - ? 'bg-[var(--app-list-active-background)] text-[var(--app-list-active-foreground)]' - : 'hover:bg-[var(--app-list-hover-background)]' + ? 'bg-[var(--app-list-active-background)] text-[var(--app-list-active-foreground)] hover:bg-[var(--app-button-background)] hover:text-[var(--app-button-foreground)] hover:font-bold hover:relative hover:border-0 hover:border-[var(--app-button-background)]' + : 'hover:bg-[var(--app-button-background)] hover:text-[var(--app-button-foreground)] hover:font-bold hover:relative hover:border-0 hover:border-[var(--app-button-background)]' }`} onClick={() => onResponse(option.optionId)} onMouseEnter={() => setFocusedIndex(index)} > {/* Number badge */} {/* Plain number badge without hover background */} - + {index + 1} {/* Option text */} - {option.name} + {option.name} {/* Always badge */} {/* {isAlways && } */} @@ -197,8 +194,8 @@ export const PermissionDrawer: React.FC = ({
setFocusedIndex(options.length)} onClick={() => customInputRef.current?.focus()} diff --git a/packages/vscode-ide-companion/src/webview/components/messages/UserMessage.tsx b/packages/vscode-ide-companion/src/webview/components/messages/UserMessage.tsx index f69c43fe..69086197 100644 --- a/packages/vscode-ide-companion/src/webview/components/messages/UserMessage.tsx +++ b/packages/vscode-ide-companion/src/webview/components/messages/UserMessage.tsx @@ -63,7 +63,7 @@ export const UserMessage: React.FC = ({ {/* File context indicator */} {fileContextDisplay && ( -
+
= ({ toolCall }) => { const queryText = safeTitle(title); // Group content by type - const { errors } = groupContent(content); + const { errors, textOutputs } = groupContent(content); // Error case: show search query + error in card layout if (errors.length > 0) { @@ -77,6 +77,31 @@ export const SearchToolCall: React.FC = ({ toolCall }) => { ); } + // Show content text if available (e.g., "Listed 4 item(s).") + if (textOutputs.length > 0) { + const containerStatus = mapToolStatusToContainerStatus(toolCall.status); + return ( + +
+ {textOutputs.map((text, index) => ( +
+ + {text} +
+ ))} +
+
+ ); + } + // No results - show query only if (queryText) { const containerStatus = mapToolStatusToContainerStatus(toolCall.status); diff --git a/packages/vscode-ide-companion/src/webview/components/toolcalls/shared/LayoutComponents.css b/packages/vscode-ide-companion/src/webview/components/toolcalls/shared/LayoutComponents.css index 769d2127..5a055684 100644 --- a/packages/vscode-ide-companion/src/webview/components/toolcalls/shared/LayoutComponents.css +++ b/packages/vscode-ide-companion/src/webview/components/toolcalls/shared/LayoutComponents.css @@ -63,7 +63,8 @@ content: '\25cf'; position: absolute; left: 8px; - padding-top: 2px; + /* TODO: */ + /* padding-top: 2px; */ font-size: 10px; color: #c74e39; z-index: 1;