From 54fd63f04b72a8bf1eead0c6b0b44ae10eebec3e Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Mon, 15 Dec 2025 22:41:38 +0800 Subject: [PATCH] fix(vscode-ide-companion): optimize stream termination handling and fix style layering issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unify stream termination using the `sendStreamEnd` method to avoid duplicate code. Add stream termination reason identification and handling for timeout and session expiration scenarios. Centralize cleanup logic for various stream termination states in the WebView message hooks. Adjust Tailwind CSS styles to resolve component layering display issues. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../scripts/prepackage.js | 2 +- .../webview/handlers/SessionMessageHandler.ts | 41 +++++++++++++++---- .../src/webview/hooks/useWebViewMessages.ts | 21 +++++++--- .../src/webview/styles/tailwind.css | 2 +- 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/packages/vscode-ide-companion/scripts/prepackage.js b/packages/vscode-ide-companion/scripts/prepackage.js index 8db18a69..de76a335 100644 --- a/packages/vscode-ide-companion/scripts/prepackage.js +++ b/packages/vscode-ide-companion/scripts/prepackage.js @@ -35,7 +35,7 @@ function npmBin() { function run(cmd, args, opts = {}) { const res = spawnSync(cmd, args, { stdio: 'inherit', - shell: process.platform === 'win32' ? true : false, + shell: process.platform === 'win32', ...opts, }); if (res.error) { diff --git a/packages/vscode-ide-companion/src/webview/handlers/SessionMessageHandler.ts b/packages/vscode-ide-companion/src/webview/handlers/SessionMessageHandler.ts index 51dfbdd9..130ca107 100644 --- a/packages/vscode-ide-companion/src/webview/handlers/SessionMessageHandler.ts +++ b/packages/vscode-ide-companion/src/webview/handlers/SessionMessageHandler.ts @@ -152,6 +152,24 @@ export class SessionMessageHandler extends BaseMessageHandler { this.currentStreamContent = ''; } + /** + * Notify the webview that streaming has finished. + */ + private sendStreamEnd(reason?: string): void { + const data: { timestamp: number; reason?: string } = { + timestamp: Date.now(), + }; + + if (reason) { + data.reason = reason; + } + + this.sendToWebView({ + type: 'streamEnd', + data, + }); + } + /** * Prompt user to login and invoke the registered login handler/command. * Returns true if a login was initiated. @@ -373,10 +391,7 @@ export class SessionMessageHandler extends BaseMessageHandler { ); } - this.sendToWebView({ - type: 'streamEnd', - data: { timestamp: Date.now() }, - }); + this.sendStreamEnd(); } catch (error) { console.error('[SessionMessageHandler] Error sending message:', error); @@ -398,10 +413,7 @@ export class SessionMessageHandler extends BaseMessageHandler { if (isAbortLike) { // Do not show VS Code error popup for intentional cancellations. // Ensure the webview knows the stream ended due to user action. - this.sendToWebView({ - type: 'streamEnd', - data: { timestamp: Date.now(), reason: 'user_cancelled' }, - }); + this.sendStreamEnd('user_cancelled'); return; } // Check for session not found error and handle it appropriately @@ -423,12 +435,23 @@ export class SessionMessageHandler extends BaseMessageHandler { type: 'sessionExpired', data: { message: 'Session expired. Please login again.' }, }); + this.sendStreamEnd('session_expired'); } else { - vscode.window.showErrorMessage(`Error sending message: ${error}`); + const isTimeoutError = + lower.includes('timeout') || lower.includes('timed out'); + if (!isTimeoutError) { + vscode.window.showErrorMessage(`Error sending message: ${error}`); + } else { + // 超时对用户没有可执行的操作,因此只在面板内重置信息,不弹出 VS Code 错误提醒 + console.warn( + '[SessionMessageHandler] Prompt timed out; suppressing popup', + ); + } this.sendToWebView({ type: 'error', data: { message: errorMsg }, }); + this.sendStreamEnd(isTimeoutError ? 'timeout' : 'error'); } } } diff --git a/packages/vscode-ide-companion/src/webview/hooks/useWebViewMessages.ts b/packages/vscode-ide-companion/src/webview/hooks/useWebViewMessages.ts index c8d507f2..8336825c 100644 --- a/packages/vscode-ide-companion/src/webview/hooks/useWebViewMessages.ts +++ b/packages/vscode-ide-companion/src/webview/hooks/useWebViewMessages.ts @@ -15,6 +15,14 @@ import type { ToolCallUpdate } from '../../types/chatTypes.js'; import type { ApprovalModeValue } from '../../types/approvalModeValueTypes.js'; import type { PlanEntry } from '../../types/chatTypes.js'; +const FORCE_CLEAR_STREAM_END_REASONS = new Set([ + 'user_cancelled', + 'cancelled', + 'timeout', + 'error', + 'session_expired', +]); + interface UseWebViewMessagesProps { // Session management sessionManagement: { @@ -364,12 +372,12 @@ export const useWebViewMessages = ({ ).toLowerCase(); /** - * Handle different types of stream end reasons: - * - 'user_cancelled': User explicitly cancelled operation - * - 'cancelled': General cancellation - * For these cases, immediately clear all active states + * Handle different types of stream end reasons that require a full reset: + * - 'user_cancelled' / 'cancelled': user explicitly cancelled + * - 'timeout' / 'error' / 'session_expired': request failed unexpectedly + * For these cases, immediately clear all active states. */ - if (reason === 'user_cancelled' || reason === 'cancelled') { + if (FORCE_CLEAR_STREAM_END_REASONS.has(reason)) { // Clear active execution tool call tracking, reset state activeExecToolCallsRef.current.clear(); // Clear waiting response state to ensure UI returns to normal @@ -393,6 +401,9 @@ export const useWebViewMessages = ({ } case 'error': + handlers.messageHandling.endStreaming(); + handlers.messageHandling.clearThinking(); + activeExecToolCallsRef.current.clear(); handlers.messageHandling.clearWaitingForResponse(); break; diff --git a/packages/vscode-ide-companion/src/webview/styles/tailwind.css b/packages/vscode-ide-companion/src/webview/styles/tailwind.css index 46d803d5..a48c172f 100644 --- a/packages/vscode-ide-companion/src/webview/styles/tailwind.css +++ b/packages/vscode-ide-companion/src/webview/styles/tailwind.css @@ -43,7 +43,7 @@ /* Composer: form wrapper */ .composer-form { - @apply relative flex flex-col max-w-[680px] mx-auto rounded-large border shadow-sm transition-colors duration-200; + @apply relative flex flex-col max-w-[680px] mx-auto rounded-large border shadow-sm transition-colors duration-200 z-[1]; background: var(--app-input-secondary-background); border-color: var(--app-input-border); color: var(--app-input-foreground);