diff --git a/webview/src/components/ChatPanel.vue b/webview/src/components/ChatPanel.vue index 058a817..6a73fa3 100644 --- a/webview/src/components/ChatPanel.vue +++ b/webview/src/components/ChatPanel.vue @@ -370,6 +370,9 @@ function checkAndFixLoadingState() { if (shouldBeLoading !== chatState.loading) { log(`[checkAndFixLoadingState] 状态不一致,修复 - 应该loading=${shouldBeLoading}, 当前=${chatState.loading}`); chatStore.setLoading(shouldBeLoading); + + // 强制更新senderKey,确保a-x-sender组件重新渲染 + chatState.senderKey = Date.now(); } // 更新本地状态,只在需要时更新 @@ -381,8 +384,8 @@ function checkAndFixLoadingState() { lastLoadingState.value = shouldBeLoading; lastPendingState.value = hasPendingMessage; - // 仅在需要时更新senderKey - if (shouldBeLoading !== actualLoadingState.value || Date.now() - chatState.senderKey > 5000) { + // 仅在需要时更新senderKey (此条件可能不再需要,但保留为了防止其他情况) + if (shouldBeLoading !== actualLoadingState.value) { chatState.senderKey = Date.now(); } } diff --git a/webview/src/store/chat.ts b/webview/src/store/chat.ts index e9925f4..caca84d 100644 --- a/webview/src/store/chat.ts +++ b/webview/src/store/chat.ts @@ -41,18 +41,24 @@ try { // 添加常量定义 const REQUEST_TIMEOUT = 5000; // 请求超时时间(毫秒) +const LOCAL_STORAGE_REQUEST_ID_KEY = 'vscode_ai_chat_request_id'; // 保存requestId的localStorage键名 // WebSocket服务- 浏览器环境下实现 export class WebSocketService { private socket: WebSocket | null = null; private backoffTime = 1000; - private nextRequestId = 1; + // 使用静态变量确保在同一个会话中所有的WebSocketService实例共享同一个requestId序列 + private static _nextRequestId = 1; private requestHandlers: Map void> = new Map(); private reconnectAttempts = 0; private reconnectTimer: NodeJS.Timeout | null = null; private readonly MAX_RECONNECT_ATTEMPTS = 5; private readonly RECONNECT_DELAY = 2000; private isReconnecting = false; + private _hasLoggedRequestId = false; // 用于跟踪是否已记录过requestId + + // 内存存储,用于在localStorage不可用时的备用 + private static memoryStorage: Record = {}; public isConnected = false; public isLoading = false; @@ -62,7 +68,77 @@ export class WebSocketService { public messages: ChatMessage[] = []; // 聊天消息历史 private eventListeners: Map void>> = new Map(); - constructor(private apiUrl: string, private apiKey: string, private logger: (message: string) => void = console.log) {} + /** + * 安全地获取存储值,优先使用localStorage,失败时使用内存存储 + * @param key 存储键 + * @returns 存储的值或null + */ + private safeGetItem(key: string): string | null { + try { + // 首先尝试从localStorage获取 + if (typeof localStorage !== 'undefined') { + return localStorage.getItem(key); + } + } catch (error) { + this.logger(`无法从localStorage读取数据: ${error}`); + } + + // 回退到内存存储 + return WebSocketService.memoryStorage[key] || null; + } + + /** + * 安全地设置存储值,优先使用localStorage,失败时使用内存存储 + * @param key 存储键 + * @param value 要存储的值 + */ + private safeSetItem(key: string, value: string): void { + try { + // 首先尝试存储到localStorage + if (typeof localStorage !== 'undefined') { + localStorage.setItem(key, value); + return; + } + } catch (error) { + this.logger(`无法写入数据到localStorage: ${error}`); + } + + // 回退到内存存储 + WebSocketService.memoryStorage[key] = value; + } + + constructor(private apiUrl: string, private apiKey: string, private logger: (message: string) => void = console.log) { + // 仅在类的静态变量_nextRequestId还是初始值时才从存储中恢复 + if (WebSocketService._nextRequestId === 1) { + // 从存储中恢复requestId,确保在同一VSCode会话中保持一致 + const savedRequestId = this.safeGetItem(LOCAL_STORAGE_REQUEST_ID_KEY); + + if (savedRequestId) { + const parsedId = parseInt(savedRequestId, 10); + if (!isNaN(parsedId) && parsedId > 0) { + WebSocketService._nextRequestId = parsedId; + this.logger(`从存储中恢复requestId: ${WebSocketService._nextRequestId}`); + } else { + // 如果存储的值无效,生成一个新的随机ID并保存 + // 使用时间戳作为基础,确保唯一性 + const timestamp = Date.now(); + WebSocketService._nextRequestId = timestamp % 100000000 + Math.floor(Math.random() * 10000); + this.safeSetItem(LOCAL_STORAGE_REQUEST_ID_KEY, WebSocketService._nextRequestId.toString()); + this.logger(`创建新的随机requestId: ${WebSocketService._nextRequestId} (基于时间戳)`); + } + } else { + // 首次运行,创建新的随机ID + // 使用时间戳作为基础,确保唯一性 + const timestamp = Date.now(); + WebSocketService._nextRequestId = timestamp % 100000000 + Math.floor(Math.random() * 10000); + this.safeSetItem(LOCAL_STORAGE_REQUEST_ID_KEY, WebSocketService._nextRequestId.toString()); + this.logger(`创建新的随机requestId: ${WebSocketService._nextRequestId} (基于时间戳)`); + } + } else { + // 静态变量已初始化,记录当前值 + this.logger(`使用已初始化的会话requestId: ${WebSocketService._nextRequestId}`); + } + } /** * 连接到WebSocket服务 @@ -500,10 +576,17 @@ export class WebSocketService { } /** - * 生成唯一的请求ID + * 生成唯一的请求ID - 在同一会话内保持不变,确保所有请求使用相同的ID */ private generateRequestId(): number { - return this.nextRequestId++; + // 不再自增,每次返回相同的请求ID + // 仅在初始请求时记录日志,避免日志过多 + if (!this._hasLoggedRequestId) { + this.logger(`使用固定请求ID: ${WebSocketService._nextRequestId} (会话中保持一致)`); + this._hasLoggedRequestId = true; + } + + return WebSocketService._nextRequestId; } /**