- 新增完整的数据流程文档,详细说明从用户输入到响应显示的全流程
- 修复 messageArea.ts 中的消息渲染逻辑:
- 移除用户消息时重置分段容器的逻辑
- 移除对话完成时跳过 segments 处理的逻辑
- 确保对话完成时正确渲染最终的 segments
28 KiB
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
// 用户点击发送按钮或按下 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
// 监听来自 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
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;
}
}
}
处理流程:
- 记录用户消息到历史数据库
- 设置用户交互管理器
- 检查特殊命令(VCD生成、文件操作)
- 调用后端服务处理
第四阶段:后端服务处理
4. handleUserMessageWithBackend 调用后端
文件位置: src/utils/messageHandler.ts:122-149
async function handleUserMessageWithBackend(
panel: vscode.WebviewPanel,
text: string,
extensionPath: string,
mode?: RunMode,
reuseTaskId?: string // 可选,复用现有 taskId
): Promise<void> {
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
// 监听来自插件的消息
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。
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();
}
渲染流程说明:
- 容器管理: 创建或复用
currentSegmentedMessage容器 - 状态保持: 保存工具的展开/折叠状态,避免重新渲染时丢失
- 清空重绘: 每次更新都清空容器并重新渲染所有段落
- 工具合并: 连续相同的工具调用会合并显示(如 "已完成文件读取 x3")
- 类型渲染: 根据 segment.type 调用不同的渲染函数
- 完成处理: 对话完成时添加操作按钮(复制、点赞、点踩)
- 智能滚动: 只在用户位于底部时自动滚动
7. Segment 类型详解
7.1 Text Segment (文本段落)
数据结构:
{
type: 'text',
content: '这是 AI 的回复内容...'
}
渲染函数: formatText() (第 1359-1432 行)
处理流程:
- 提取代码块 (```language\ncode```)
- 提取行内代码 (`code`)
- 转义 HTML 特殊字符
- 处理 Markdown 语法:
- 标题:
#,##,### - 粗体:
**text** - 斜体:
*text* - 列表:
-,*,1. - 链接:
[text](url)
- 标题:
- 处理换行:
\n→<br> - 恢复代码块和行内代码
示例:
// 输入
"## 标题\n这是**粗体**和*斜体*\n```js\nconst a = 1;\n```"
// 输出
"<h2>标题</h2><br>这是<strong>粗体</strong>和<em>斜体</em><br><pre><code class='language-js'>const a = 1;</code></pre>"
7.2 Tool Segment (工具调用段落)
数据结构:
{
type: 'tool',
toolName: 'file_read', // 工具名称
toolStatus: 'success', // 状态: success/error
toolResult: '文件内容...', // 工具执行结果
toolCount: 3, // 合并后的计数(可选)
vcdFilePath: '/path/to/file.vcd' // 特殊字段(仿真工具)
}
渲染逻辑 (第 976-1053 行):
- 过滤工具: 跳过不需要显示的工具(如
spawnExplorer) - 低调样式: 所有工具调用使用低调样式(小字体、低透明度)
- 工具图标: 根据
toolName显示对应图标 - 工具名称: 映射为中文显示名称(如
file_read→ "已完成文件读取") - 结果显示:
- 短结果(≤60字符): 直接显示在同一行
- 长结果(>60字符): 默认折叠,点击展开
- 特殊处理: 仿真工具成功后添加波形预览组件
工具名称映射:
const toolNameMap = {
'file_read': '已完成文件读取',
'file_write': '已完成文件写入',
'file_delete': '已完成文件删除',
'file_list': '已检索代码文件',
'syntax_check': '已完成语法检查',
'simulation': '已完成仿真',
'waveform_summary': '已完成波形分析',
'knowledge_save': '已保存知识库',
'addSignal': '信号分析完成',
// ... 更多映射
};
HTML 结构:
<div class="message-segment segment-tool low-profile">
<div class="tool-segment-header" data-collapsible="true">
<span class="tool-collapse-icon">▼</span>
<span class="tool-segment-name">已完成文件读取 x3</span>
</div>
<div class="tool-segment-content collapsed">
<span class="tool-segment-result">文件内容...</span>
</div>
</div>
7.3 Question Segment (用户问题段落)
数据结构:
{
type: 'question',
askId: 'ask_123456', // 问题唯一标识
question: '请选择一个选项:', // 问题文本
options: ['选项1', '选项2', '选项3'] // 选项列表
}
渲染逻辑 (第 1054-1118 行):
- 检查状态: 从
answeredQuestionsMap 中检查是否已回答 - 显示问题: 显示问题文本
- 显示选项: 渲染选项按钮
- 自定义输入: 提供自定义输入框("其他"选项)
- 事件监听:
- 点击选项按钮 → 提交答案
- 输入自定义答案 → 提交答案
- 回车键 → 提交答案
- 状态更新: 提交后标记为已回答,高亮选中选项
交互流程:
用户点击选项/输入自定义答案
↓
handleQuestionAnswerInSegment()
↓
保存答案到 answeredQuestions Map
↓
标记问题为已回答 (添加 .answered 类)
↓
高亮选中选项 (添加 .selected 类)
↓
发送答案到后端 (vscode.postMessage)
HTML 结构:
<div class="message-segment segment-question">
<div class="question-text">请选择一个选项:</div>
<div class="question-options" data-ask-id="ask_123456">
<button class="question-option" data-option="选项1">选项1</button>
<button class="question-option selected" data-option="选项2">选项2</button>
<button class="question-option" data-option="选项3">选项3</button>
</div>
<div class="custom-input-container">
<input type="text" class="custom-input" placeholder="输入其他答案..." />
<button class="custom-submit">提交</button>
</div>
</div>
7.4 Agent Segment (智能体卡片)
数据结构:
{
type: 'agent',
agentName: 'CodeExplorer',
agentStatus: 'running', // running/completed/error
agentMessage: '正在探索代码...'
}
渲染: 调用 renderAgentCard() 函数(定义在 agentCard.ts)
7.5 Plan Segment (计划卡片)
数据结构:
{
type: 'plan',
planTitle: '实现用户认证功能',
planSteps: [
{ step: 1, description: '创建用户模型', status: 'pending' },
{ step: 2, description: '实现登录接口', status: 'pending' }
]
}
渲染: 调用 renderPlanCardInSegment() 函数(定义在 planCard.ts)
第七阶段:关键特性说明
8.1 实时更新机制
核心原理: 每次后端推送新数据时,前端都会清空容器并重新渲染所有段落
为什么这样设计?
- ✅ 简化状态管理,避免复杂的 diff 算法
- ✅ 确保显示内容与后端数据完全一致
- ✅ 支持段落顺序变化和内容更新
性能优化:
- 保存工具的展开/折叠状态(
toolCollapseStatesMap) - 保存问题的回答状态(
answeredQuestionsMap) - 智能滚动(只在用户位于底部时自动滚动)
8.2 工具合并机制
目的: 减少视觉噪音,提升用户体验
实现 (第 946-966 行):
// 合并连续相同的工具调用
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 行):
// 检查用户是否在底部附近(允许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):
// 保存状态(重新渲染前)
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):
// 保存答案
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 类型定义:
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 存储:
// 重新渲染前保存
toolCollapseStates.set(toolIndex, isCollapsed);
// 渲染时恢复
const savedState = toolCollapseStates.get(toolIndex);
Q3: 如何添加新的 Segment 类型?
A: 按以下步骤操作:
- 在
updateSegmentsRealtime()中添加类型判断 - 创建对应的渲染函数
- 在
messageArea.ts中添加样式 - 更新工具图标映射(如果需要)
示例:
// 1. 添加类型判断
else if (segment.type === 'myNewType') {
renderMyNewType(segmentDiv, segment);
}
// 2. 创建渲染函数
function renderMyNewType(container, segment) {
container.innerHTML = `<div>${segment.content}</div>`;
}
Q4: 如何调试数据流程?
A: 在浏览器开发者工具中查看日志:
- 打开 VS Code 开发者工具:
Help→Toggle Developer Tools - 切换到
Console标签 - 查看关键日志:
[WebView] 收到消息:- 前端接收消息[WebView] updateSegmentsRealtime 被调用- 渲染触发[MessageHandler] 创建会话:- 后端会话创建
总结
本文档详细说明了 IC Coder Plugin 从用户输入到显示响应的完整数据流程:
- 用户输入 → 前端显示并发送消息
- VS Code 扩展 → 接收消息并创建任务上下文
- 消息处理器 → 调用后端服务
- 后端服务 → 处理消息并通过回调推送数据
- 前端接收 → 监听消息并触发渲染
- 消息渲染 → 根据 segment 类型渲染 UI
- 用户交互 → 保持状态并响应用户操作
核心设计理念:
- ✅ 实时更新: 支持流式数据推送
- ✅ 状态保持: 避免丢失用户交互
- ✅ 智能滚动: 不打断用户阅读
- ✅ 工具合并: 减少视觉噪音
- ✅ 简化管理: 清空重绘而非复杂 diff
文档版本: v1.0 最后更新: 2026-01-08 作者: IC Coder Team