# 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<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`

```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` → `<br>`
6. 恢复代码块和行内代码

**示例:**
```javascript
// 输入
"## 标题\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 (工具调用段落)

**数据结构:**
```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
<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 (用户问题段落)

**数据结构:**
```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
<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 (智能体卡片)

**数据结构:**
```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 = `<div>${segment.content}</div>`;
}
```

---

### 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

