# IC Coder Plugin 数据流程详解 ## 概述 本文档详细说明从用户输入文本到最终显示响应的完整数据流程。 --- ## 📊 完整数据流程图 ``` 用户输入文本 ↓ 前端 WebView (inputArea.ts) ↓ vscode.postMessage VS Code Extension (ICHelperPanel.ts) ↓ onDidReceiveMessage 消息处理器 (messageHandler.ts) ↓ handleUserMessage 后端服务 (dialogService.ts) ↓ 回调函数 前端 WebView (webviewContent.ts) ↓ window.addEventListener 消息区域渲染 (messageArea.ts) ↓ 显示给用户 ``` --- ## 第一阶段:用户输入 → 前端处理 ### 1. 用户在输入框输入文本并点击发送 **文件位置**: `src/views/inputArea.ts:415-422` ```javascript // 用户点击发送按钮或按下 Enter 键 function sendMessage() { const text = messageInput.value.trim(); if (!text) return; // 获取当前配置 const mode = getCurrentMode(); // 运行模式 (agent/plan等) const model = getCurrentModel(); // 选择的模型 const planMode = document.getElementById('planToggle')?.checked || false; const contextItems = window.getContextItems ? window.getContextItems() : []; // 1. 立即显示用户消息 addMessage(text, 'user'); // 2. 更新 UI 状态 hasMessages = true; updateInputAreaLayout(); setSendButtonState(true); // 切换为暂停按钮 // 3. 发送消息到 VS Code 扩展 vscode.postMessage({ command: 'sendMessage', text: text, mode: mode, model: model, planMode: planMode, contextItems: contextItems }); // 4. 清空输入框 messageInput.value = ''; autoResizeTextarea(); messageInput.focus(); // 5. 重置优化状态 resetOptimizeButton(); } ``` **前端同时做的事情:** - ✅ 显示用户消息到聊天区域 - ✅ 切换发送按钮为暂停状态 - ✅ 清空输入框并重置高度 - ✅ 更新布局(如果是第一条消息) --- ## 第二阶段:VS Code 扩展接收消息 ### 2. ICHelperPanel 接收并处理消息 **文件位置**: `src/panels/ICHelperPanel.ts:112-152` ```javascript // 监听来自 WebView 的消息 panel.webview.onDidReceiveMessage(async (message) => { const historyManager = ChatHistoryManager.getInstance(); const panelId = (panel as any).__uniqueId; switch (message.command) { case "sendMessage": // 步骤 1: 确保面板有任务上下文 (taskId) if (!historyManager.getPanelTask(panelId)) { const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; if (workspacePath) { try { // 创建新任务 const taskMeta = await historyManager.createTask( workspacePath, "新对话" ); // 关联面板和任务 historyManager.setPanelTask( panelId, taskMeta.taskId, workspacePath ); } catch (error) { console.error("创建任务失败:", error); } } } // 步骤 2: 切换到当前面板的任务上下文 historyManager.switchToPanelTask(panelId); // 步骤 3: 显示进度条 panel.webview.postMessage({ type: 'showProgress' }); // 步骤 4: 调用消息处理器 handleUserMessage( panel, message.text, context.extensionPath, message.mode ); break; } }); ``` **关键概念:** - **panelId**: 每个 WebView 面板的唯一标识 - **taskId**: 每个对话任务的唯一标识,用于历史记录管理 - **workspacePath**: 当前工作区路径 --- ## 第三阶段:消息处理器处理 ### 3. handleUserMessage 处理用户消息 **文件位置**: `src/utils/messageHandler.ts:56-117` ```javascript export async function handleUserMessage( panel: vscode.WebviewPanel, text: string, extensionPath?: string, mode?: RunMode ) { console.log("收到用户消息:", text); // 步骤 1: 记录用户消息到历史(允许失败,不阻塞主流程) try { const historyManager = ChatHistoryManager.getInstance(); await historyManager.addUserMessage(text); } catch (error) { console.warn("记录消息历史失败(可能没有打开工作区):", error); } // 步骤 2: 设置 WebView 面板用于用户交互 userInteractionManager.setWebviewPanel(panel); // 步骤 3: 检查是否是 VCD 生成命令(本地处理) if (isVCDGenerationCommand(text)) { await handleVCDGeneration(panel, extensionPath || ""); return; } // 步骤 4: 检查是否是文件操作命令(本地处理) const fileOperation = parseFileOperation(text); if (fileOperation) { console.log("执行文件操作:", fileOperation.type, fileOperation.filePath); await handleFileOperation(panel, fileOperation); return; } // 步骤 5: 使用后端服务处理 if (useBackendService && extensionPath) { try { await handleUserMessageWithBackend(panel, text, extensionPath, mode); return; } catch (error) { console.error("后端服务不可用:", error); panel.webview.postMessage({ command: "updateStatus", text: "后端服务不可用", type: "error", }); // 恢复输入状态 panel.webview.postMessage({ command: "updateSegments", segments: [], isComplete: true, }); throw error; } } } ``` **处理流程:** 1. 记录用户消息到历史数据库 2. 设置用户交互管理器 3. 检查特殊命令(VCD生成、文件操作) 4. 调用后端服务处理 --- ## 第四阶段:后端服务处理 ### 4. handleUserMessageWithBackend 调用后端 **文件位置**: `src/utils/messageHandler.ts:122-149` ```javascript async function handleUserMessageWithBackend( panel: vscode.WebviewPanel, text: string, extensionPath: string, mode?: RunMode, reuseTaskId?: string // 可选,复用现有 taskId ): Promise { const historyManager = ChatHistoryManager.getInstance(); // 步骤 1: 获取或创建会话 const taskIdToUse = reuseTaskId || historyManager.getCurrentTaskId(); if (!currentSession || !currentSession.active) { currentSession = dialogManager.createSession( extensionPath, taskIdToUse || undefined ); // 保存 taskId 用于后续操作(如压缩) lastTaskId = currentSession.getTaskId(); console.log( "[MessageHandler] 创建会话: taskId=", lastTaskId, "来源=", taskIdToUse ? "historyManager" : "新生成" ); } // 步骤 2: 显示状态栏 panel.webview.postMessage({ command: "updateStatus", text: "思考中...", type: "thinking", }); // 步骤 3: 发送消息到后端,并注册回调函数 return new Promise((resolve, reject) => { currentSession!.sendMessage( text, { // 回调 1: 文本更新(已废弃,统一通过 onSegmentUpdate 处理) onText: (fullText, isStreaming) => { // 不再单独处理文本 }, // 回调 2: 段落更新(核心回调) onSegmentUpdate: (segments) => { // 实时发送段落更新到前端 panel.webview.postMessage({ command: "updateSegments", segments: segments, }); }, // 回调 3: 工具开始执行 onToolStart: (toolName) => { panel.webview.postMessage({ command: "updateStatus", text: `正在执行 ${toolName}...`, type: "working", }); }, // 回调 4: 工具执行完成 onToolComplete: (toolName, result) => { // 通过 onSegmentUpdate 统一更新 }, // 回调 5: 工具执行错误 onToolError: (toolName, error) => { // 通过 onSegmentUpdate 统一更新 }, // 回调 6: 用户问题 onQuestion: (askId, question, options) => { panel.webview.postMessage({ command: "updateStatus", text: "等待用户回答...", type: "working", }); }, // 回调 7: 对话完成 onComplete: async (segments) => { // 隐藏状态栏 panel.webview.postMessage({ command: "hideStatus", }); // 最后一次发送完整的段落 panel.webview.postMessage({ command: "updateSegments", segments: segments, isComplete: true, // 标记对话完成 }); // 保存完整的 segments 到历史记录 const textContent = segments .filter((s) => s.type === "text" && s.content) .map((s) => s.content) .join("\n"); await historyManager.addAiMessage(textContent, undefined, segments); resolve(); }, // 回调 8: 错误处理 onError: (error) => { panel.webview.postMessage({ command: "updateStatus", text: `错误: ${error.message}`, type: "error", }); reject(error); }, }, mode ); }); } ``` **关键回调函数:** - `onSegmentUpdate`: 实时更新段落(核心) - `onToolStart/Complete/Error`: 工具执行状态 - `onQuestion`: 用户问题 - `onComplete`: 对话完成 - `onError`: 错误处理 --- ## 第五阶段:前端接收并处理响应 ### 5. WebView 监听消息 **文件位置**: `src/views/webviewContent.ts:523-546` ```javascript // 监听来自插件的消息 window.addEventListener('message', event => { const message = event.data; console.log('[WebView] 收到消息:', message.command, message); switch (message.command) { case 'updateSegments': // 实时更新分段消息(核心处理) console.log('[WebView] 实时更新段落, segments:', message.segments); updateSegmentsRealtime(message.segments, message.isComplete); // 如果对话完成,恢复发送按钮状态 if (message.isComplete && typeof setSendButtonState === 'function') { setSendButtonState(false); } break; case 'updateStatus': // 更新状态栏 const statusBar = document.getElementById('statusBar'); const statusText = document.getElementById('statusText'); if (statusBar && statusText) { statusBar.style.display = 'flex'; statusText.textContent = message.text; statusBar.className = 'status-bar ' + (message.type || ''); } break; case 'hideStatus': // 隐藏状态栏 const statusBarHide = document.getElementById('statusBar'); if (statusBarHide) { statusBarHide.style.display = 'none'; } break; } }); ``` **消息类型:** - `updateSegments`: 更新段落(核心) - `updateStatus`: 更新状态栏 - `hideStatus`: 隐藏状态栏 --- ## 第六阶段:消息区域渲染 ### 6. updateSegmentsRealtime 实时渲染段落 **文件位置**: `src/views/messageArea.ts:903-1171` 这是整个数据流程中最复杂的部分,负责将后端返回的 segments 数据渲染成用户可见的 UI。 ```javascript function updateSegmentsRealtime(segments, isComplete) { console.log('[WebView] updateSegmentsRealtime 被调用, segments:', segments, 'isComplete:', isComplete); if (!segments || segments.length === 0) { return; } // 步骤 1: 创建或复用分段消息容器 if (!currentSegmentedMessage) { // 移除流式消息(如果有) if (currentStreamingMessage) { currentStreamingMessage.remove(); currentStreamingMessage = null; } // 移除所有工具状态消息 const toolStatuses = messagesEl.querySelectorAll('.tool-status'); toolStatuses.forEach(el => el.remove()); // 创建新容器 currentSegmentedMessage = document.createElement('div'); currentSegmentedMessage.className = 'message bot-message segmented-message'; messagesEl.appendChild(currentSegmentedMessage); } // 步骤 2: 保存工具的展开/折叠状态 const toolHeaders = currentSegmentedMessage.querySelectorAll('.tool-segment-header[data-collapsible="true"]'); toolHeaders.forEach((header, idx) => { const isCollapsed = header.classList.contains('collapsed'); toolCollapseStates.set(idx, isCollapsed); }); // 步骤 3: 清空容器并重新渲染所有段落 currentSegmentedMessage.innerHTML = ''; // 步骤 4: 合并连续相同的工具调用 const mergedSegments = mergeConsecutiveTools(segments); // 步骤 5: 遍历每个 segment 并渲染 let toolIndex = 0; mergedSegments.forEach((segment, index) => { const segmentDiv = document.createElement('div'); segmentDiv.className = 'message-segment segment-' + segment.type; // 根据 segment 类型渲染 if (segment.type === 'text') { renderTextSegment(segmentDiv, segment); } else if (segment.type === 'tool') { renderToolSegment(segmentDiv, segment, toolIndex); toolIndex++; } else if (segment.type === 'question') { renderQuestionSegment(segmentDiv, segment); } else if (segment.type === 'agent') { renderAgentCard(segment, segmentDiv); } else if (segment.type === 'plan') { renderPlanCardInSegment(segment, segmentDiv, answeredQuestions); } currentSegmentedMessage.appendChild(segmentDiv); }); // 步骤 6: 如果对话完成,添加操作按钮 if (isComplete) { addMessageActions(currentSegmentedMessage, segments); currentSegmentedMessage = null; } // 步骤 7: 智能滚动到底部 smartScrollToBottom(); } ``` **渲染流程说明:** 1. **容器管理**: 创建或复用 `currentSegmentedMessage` 容器 2. **状态保持**: 保存工具的展开/折叠状态,避免重新渲染时丢失 3. **清空重绘**: 每次更新都清空容器并重新渲染所有段落 4. **工具合并**: 连续相同的工具调用会合并显示(如 "已完成文件读取 x3") 5. **类型渲染**: 根据 segment.type 调用不同的渲染函数 6. **完成处理**: 对话完成时添加操作按钮(复制、点赞、点踩) 7. **智能滚动**: 只在用户位于底部时自动滚动 --- ### 7. Segment 类型详解 #### 7.1 Text Segment (文本段落) **数据结构:** ```javascript { type: 'text', content: '这是 AI 的回复内容...' } ``` **渲染函数**: `formatText()` (第 1359-1432 行) **处理流程:** 1. 提取代码块 (\`\`\`language\ncode\`\`\`) 2. 提取行内代码 (\`code\`) 3. 转义 HTML 特殊字符 4. 处理 Markdown 语法: - 标题: `#`, `##`, `###` - 粗体: `**text**` - 斜体: `*text*` - 列表: `-`, `*`, `1.` - 链接: `[text](url)` 5. 处理换行: `\n` → `
` 6. 恢复代码块和行内代码 **示例:** ```javascript // 输入 "## 标题\n这是**粗体**和*斜体*\n```js\nconst a = 1;\n```" // 输出 "

标题


这是粗体斜体
const a = 1;
" ``` --- #### 7.2 Tool Segment (工具调用段落) **数据结构:** ```javascript { type: 'tool', toolName: 'file_read', // 工具名称 toolStatus: 'success', // 状态: success/error toolResult: '文件内容...', // 工具执行结果 toolCount: 3, // 合并后的计数(可选) vcdFilePath: '/path/to/file.vcd' // 特殊字段(仿真工具) } ``` **渲染逻辑** (第 976-1053 行): 1. **过滤工具**: 跳过不需要显示的工具(如 `spawnExplorer`) 2. **低调样式**: 所有工具调用使用低调样式(小字体、低透明度) 3. **工具图标**: 根据 `toolName` 显示对应图标 4. **工具名称**: 映射为中文显示名称(如 `file_read` → "已完成文件读取") 5. **结果显示**: - 短结果(≤60字符): 直接显示在同一行 - 长结果(>60字符): 默认折叠,点击展开 6. **特殊处理**: 仿真工具成功后添加波形预览组件 **工具名称映射:** ```javascript const toolNameMap = { 'file_read': '已完成文件读取', 'file_write': '已完成文件写入', 'file_delete': '已完成文件删除', 'file_list': '已检索代码文件', 'syntax_check': '已完成语法检查', 'simulation': '已完成仿真', 'waveform_summary': '已完成波形分析', 'knowledge_save': '已保存知识库', 'addSignal': '信号分析完成', // ... 更多映射 }; ``` **HTML 结构:** ```html
已完成文件读取 x3
``` --- #### 7.3 Question Segment (用户问题段落) **数据结构:** ```javascript { type: 'question', askId: 'ask_123456', // 问题唯一标识 question: '请选择一个选项:', // 问题文本 options: ['选项1', '选项2', '选项3'] // 选项列表 } ``` **渲染逻辑** (第 1054-1118 行): 1. **检查状态**: 从 `answeredQuestions` Map 中检查是否已回答 2. **显示问题**: 显示问题文本 3. **显示选项**: 渲染选项按钮 4. **自定义输入**: 提供自定义输入框("其他"选项) 5. **事件监听**: - 点击选项按钮 → 提交答案 - 输入自定义答案 → 提交答案 - 回车键 → 提交答案 6. **状态更新**: 提交后标记为已回答,高亮选中选项 **交互流程:** ``` 用户点击选项/输入自定义答案 ↓ handleQuestionAnswerInSegment() ↓ 保存答案到 answeredQuestions Map ↓ 标记问题为已回答 (添加 .answered 类) ↓ 高亮选中选项 (添加 .selected 类) ↓ 发送答案到后端 (vscode.postMessage) ``` **HTML 结构:** ```html
请选择一个选项:
``` --- #### 7.4 Agent Segment (智能体卡片) **数据结构:** ```javascript { type: 'agent', agentName: 'CodeExplorer', agentStatus: 'running', // running/completed/error agentMessage: '正在探索代码...' } ``` **渲染**: 调用 `renderAgentCard()` 函数(定义在 `agentCard.ts`) --- #### 7.5 Plan Segment (计划卡片) **数据结构:** ```javascript { type: 'plan', planTitle: '实现用户认证功能', planSteps: [ { step: 1, description: '创建用户模型', status: 'pending' }, { step: 2, description: '实现登录接口', status: 'pending' } ] } ``` **渲染**: 调用 `renderPlanCardInSegment()` 函数(定义在 `planCard.ts`) --- ## 第七阶段:关键特性说明 ### 8.1 实时更新机制 **核心原理**: 每次后端推送新数据时,前端都会**清空容器并重新渲染所有段落** **为什么这样设计?** - ✅ 简化状态管理,避免复杂的 diff 算法 - ✅ 确保显示内容与后端数据完全一致 - ✅ 支持段落顺序变化和内容更新 **性能优化:** - 保存工具的展开/折叠状态(`toolCollapseStates` Map) - 保存问题的回答状态(`answeredQuestions` Map) - 智能滚动(只在用户位于底部时自动滚动) --- ### 8.2 工具合并机制 **目的**: 减少视觉噪音,提升用户体验 **实现** (第 946-966 行): ```javascript // 合并连续相同的工具调用 const mergedSegments = []; let i = 0; while (i < segments.length) { const segment = segments[i]; if (segment.type === 'tool') { // 统计连续相同的工具调用 let count = 1; while (i + count < segments.length && segments[i + count].type === 'tool' && segments[i + count].toolName === segment.toolName) { count++; } // 添加合并后的段落(带计数) mergedSegments.push({ ...segment, toolCount: count }); i += count; } else { mergedSegments.push(segment); i++; } } ``` **效果:** ``` 原始: [file_read, file_read, file_read, text, file_write, file_write] 合并: [file_read x3, text, file_write x2] ``` --- ### 8.3 智能滚动机制 **目的**: 只在用户位于底部时自动滚动,避免打断用户阅读 **实现** (第 714-724 行): ```javascript // 检查用户是否在底部附近(允许50px的误差) function isUserNearBottom() { const threshold = 50; return messagesEl.scrollHeight - messagesEl.scrollTop - messagesEl.clientHeight < threshold; } // 智能滚动:只有用户在底部附近时才自动滚动 function smartScrollToBottom() { if (isUserNearBottom()) { messagesEl.scrollTop = messagesEl.scrollHeight; } } ``` **用户体验:** - ✅ 用户在底部 → 自动滚动到最新消息 - ✅ 用户在阅读历史消息 → 不自动滚动,避免打断 --- ### 8.4 状态保持机制 **问题**: 每次重新渲染都会清空容器,如何保持用户的交互状态? **解决方案**: 使用 Map 存储状态 **1. 工具折叠状态** (`toolCollapseStates` Map): ```javascript // 保存状态(重新渲染前) const toolHeaders = currentSegmentedMessage.querySelectorAll('.tool-segment-header[data-collapsible="true"]'); toolHeaders.forEach((header, idx) => { const isCollapsed = header.classList.contains('collapsed'); toolCollapseStates.set(idx, isCollapsed); }); // 恢复状态(渲染时) const savedState = toolCollapseStates.get(toolIndex); const isCollapsed = savedState !== undefined ? savedState : shouldCollapse; ``` **2. 问题回答状态** (`answeredQuestions` Map): ```javascript // 保存答案 answeredQuestions.set(askId, answer); // 检查是否已回答 const isAnswered = answeredQuestions.has(segment.askId); const selectedAnswer = answeredQuestions.get(segment.askId); ``` --- ## 完整数据流程总结 ### 时序图 ``` 用户输入 "帮我读取 main.v 文件" ↓ (0ms) 前端显示用户消息 ↓ (1ms) vscode.postMessage({ command: 'sendMessage', text: '...' }) ↓ (2ms) ICHelperPanel.onDidReceiveMessage ↓ (3ms) 创建/获取 taskId ↓ (5ms) handleUserMessage ↓ (10ms) handleUserMessageWithBackend ↓ (15ms) dialogManager.createSession ↓ (20ms) currentSession.sendMessage ↓ (100ms - 后端处理) onSegmentUpdate 回调 (多次) ↓ (101ms, 150ms, 200ms...) panel.webview.postMessage({ command: 'updateSegments', segments: [...] }) ↓ (102ms, 151ms, 201ms...) window.addEventListener('message') ↓ (103ms, 152ms, 202ms...) updateSegmentsRealtime(segments, false) ↓ (105ms, 155ms, 205ms...) 渲染 segments 到 DOM ↓ (3000ms - 对话完成) onComplete 回调 ↓ (3001ms) panel.webview.postMessage({ command: 'updateSegments', segments: [...], isComplete: true }) ↓ (3002ms) updateSegmentsRealtime(segments, true) ↓ (3005ms) 添加操作按钮 (复制、点赞、点踩) ↓ (3010ms) 恢复发送按钮状态 ``` --- ### 关键文件索引 | 文件 | 职责 | 关键函数 | |------|------|----------| | `inputArea.ts` | 输入框组件 | `sendMessage()` | | `ICHelperPanel.ts` | 面板管理 | `onDidReceiveMessage()` | | `messageHandler.ts` | 消息处理核心 | `handleUserMessage()`, `handleUserMessageWithBackend()` | | `webviewContent.ts` | WebView 主入口 | `window.addEventListener('message')` | | `messageArea.ts` | 消息渲染核心 | `updateSegmentsRealtime()`, `formatText()` | | `agentCard.ts` | 智能体卡片 | `renderAgentCard()` | | `planCard.ts` | 计划卡片 | `renderPlanCardInSegment()` | | `waveformPreviewContent.ts` | 波形预览 | `createWaveformPreview()` | --- ### 数据结构总览 **Segment 类型定义:** ```typescript type Segment = | TextSegment | ToolSegment | QuestionSegment | AgentSegment | PlanSegment; interface TextSegment { type: 'text'; content: string; } interface ToolSegment { type: 'tool'; toolName: string; toolStatus: 'success' | 'error'; toolResult?: string; toolCount?: number; vcdFilePath?: string; fileName?: string; } interface QuestionSegment { type: 'question'; askId: string; question: string; options: string[]; } interface AgentSegment { type: 'agent'; agentName: string; agentStatus: 'running' | 'completed' | 'error'; agentMessage: string; } interface PlanSegment { type: 'plan'; planTitle: string; planSteps: Array<{ step: number; description: string; status: 'pending' | 'running' | 'completed' | 'error'; }>; } ``` --- ### 消息通信协议 **前端 → 后端 (vscode.postMessage):** | Command | 参数 | 说明 | |---------|------|------| | `sendMessage` | `text`, `mode`, `model`, `planMode`, `contextItems` | 发送用户消息 | | `submitAnswer` | `askId`, `selected`, `customInput` | 提交问题答案 | | `abortDialog` | - | 中止当前对话 | | `openWaveformViewer` | `vcdFilePath` | 打开波形查看器 | **后端 → 前端 (panel.webview.postMessage):** | Command | 参数 | 说明 | |---------|------|------| | `updateSegments` | `segments`, `isComplete` | 更新段落(核心) | | `updateStatus` | `text`, `type` | 更新状态栏 | | `hideStatus` | - | 隐藏状态栏 | | `showProgress` | - | 显示进度条 | | `resetSegmentedMessage` | - | 重置分段消息容器 | --- ## 常见问题 FAQ ### Q1: 为什么每次更新都要清空容器重新渲染? **A**: 这是一种简化的状态管理策略: - ✅ 避免复杂的 diff 算法 - ✅ 确保显示内容与后端数据完全一致 - ✅ 支持段落顺序变化 - ✅ 代码简单易维护 性能影响很小,因为: - 渲染速度很快(通常 < 10ms) - 用户感知不到闪烁 - 通过状态保持机制避免丢失用户交互 --- ### Q2: 工具折叠状态如何保持? **A**: 使用 `toolCollapseStates` Map 存储: ```javascript // 重新渲染前保存 toolCollapseStates.set(toolIndex, isCollapsed); // 渲染时恢复 const savedState = toolCollapseStates.get(toolIndex); ``` --- ### Q3: 如何添加新的 Segment 类型? **A**: 按以下步骤操作: 1. 在 `updateSegmentsRealtime()` 中添加类型判断 2. 创建对应的渲染函数 3. 在 `messageArea.ts` 中添加样式 4. 更新工具图标映射(如果需要) 示例: ```javascript // 1. 添加类型判断 else if (segment.type === 'myNewType') { renderMyNewType(segmentDiv, segment); } // 2. 创建渲染函数 function renderMyNewType(container, segment) { container.innerHTML = `
${segment.content}
`; } ``` --- ### Q4: 如何调试数据流程? **A**: 在浏览器开发者工具中查看日志: 1. 打开 VS Code 开发者工具: `Help` → `Toggle Developer Tools` 2. 切换到 `Console` 标签 3. 查看关键日志: - `[WebView] 收到消息:` - 前端接收消息 - `[WebView] updateSegmentsRealtime 被调用` - 渲染触发 - `[MessageHandler] 创建会话:` - 后端会话创建 --- ## 总结 本文档详细说明了 IC Coder Plugin 从用户输入到显示响应的完整数据流程: 1. **用户输入** → 前端显示并发送消息 2. **VS Code 扩展** → 接收消息并创建任务上下文 3. **消息处理器** → 调用后端服务 4. **后端服务** → 处理消息并通过回调推送数据 5. **前端接收** → 监听消息并触发渲染 6. **消息渲染** → 根据 segment 类型渲染 UI 7. **用户交互** → 保持状态并响应用户操作 核心设计理念: - ✅ **实时更新**: 支持流式数据推送 - ✅ **状态保持**: 避免丢失用户交互 - ✅ **智能滚动**: 不打断用户阅读 - ✅ **工具合并**: 减少视觉噪音 - ✅ **简化管理**: 清空重绘而非复杂 diff --- **文档版本**: v1.0 **最后更新**: 2026-01-08 **作者**: IC Coder Team