From d43cd610a0365683f218eb27f456a3e22f52a401 Mon Sep 17 00:00:00 2001 From: Roe-xin Date: Tue, 30 Dec 2025 22:51:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E4=BD=93=E5=8D=A1=E7=89=87=E5=92=8C=E5=B7=A5=E5=85=B7=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加智能体卡片智能滚动功能:自动滚动到底部,用户向上滚动时停止,滚动到底部恢复 - 过滤 spawnExplorer 工具,不在界面显示 - 添加所有工具的中文名称映射(file_read、file_write、queryRules、setModule 等) - 优化代码结构,移除未使用的导入 --- src/constants/toolIcons.ts | 6 + src/views/agentCard.ts | 366 ++++++++++++------------------------- src/views/messageArea.ts | 171 +++-------------- 3 files changed, 152 insertions(+), 391 deletions(-) diff --git a/src/constants/toolIcons.ts b/src/constants/toolIcons.ts index 8d866f9..18c4e1e 100644 --- a/src/constants/toolIcons.ts +++ b/src/constants/toolIcons.ts @@ -70,3 +70,9 @@ export const stopIconSvg = ` `; + +/** + * 探索智能体图标 SVG + */ +export const agentIconSvg = ` +`; diff --git a/src/views/agentCard.ts b/src/views/agentCard.ts index 1515750..2783cd7 100644 --- a/src/views/agentCard.ts +++ b/src/views/agentCard.ts @@ -2,310 +2,176 @@ * 智能体卡片组件 * * 功能说明: - * - 显示子智能体的执行过程 - * - 支持展开/收起步骤详情 - * - 显示执行状态和统计信息 + * - 提供智能体执行状态的可视化展示 + * - 显示智能体名称、状态和执行步骤 + * - 支持实时更新步骤信息 */ -import type { - AgentStartEvent, - AgentProgressEvent, - AgentCompleteEvent, - AgentErrorEvent -} from '../types/api'; +import { agentIconSvg } from "../constants/toolIcons"; /** - * 获取智能体卡片样式 + * 获取智能体卡片的样式 */ export function getAgentCardStyles(): string { return ` + /* 智能体卡片样式 */ + .segment-agent { + margin: 8px 0; + } .agent-card { - background: var(--vscode-editor-background); border: 1px solid var(--vscode-input-border); border-radius: 8px; - margin: 10px 0; overflow: hidden; + background: var(--vscode-editor-background); } - .agent-card-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 12px; - background: var(--vscode-sideBar-background); - cursor: pointer; - user-select: none; - } - .agent-card-header:hover { - background: var(--vscode-list-hoverBackground); - } - .agent-card-title { + .agent-header { display: flex; align-items: center; gap: 8px; - font-weight: 500; + padding: 8px 12px; + background: var(--vscode-sideBar-background); + border-bottom: 1px solid var(--vscode-input-border); } - .agent-card-icon { + .agent-icon { font-size: 16px; } - .agent-card-status { - font-size: 12px; - padding: 2px 6px; - border-radius: 4px; + .agent-name { + font-weight: 500; + flex: 1; } - .agent-card-status.running { + .agent-status { + font-size: 11px; + padding: 2px 8px; + border-radius: 10px; + } + .agent-status.running { background: var(--vscode-inputValidation-infoBackground); color: var(--vscode-inputValidation-infoForeground); } - .agent-card-status.completed { - background: var(--vscode-testing-iconPassed); + .agent-status.completed { + background: #28a745; color: white; } - .agent-card-status.error { - background: var(--vscode-testing-iconFailed); + .agent-status.error { + background: #dc3545; color: white; } - .agent-card-toggle { + .agent-body { + padding: 8px; + } + .agent-steps-container { + max-height: 150px; + overflow-y: auto; font-size: 12px; - color: var(--vscode-descriptionForeground); - } - .agent-card-body { - padding: 12px; - border-top: 1px solid var(--vscode-input-border); - } - .agent-card-body.collapsed { - display: none; - } - .agent-card-instruction { - font-size: 13px; - color: var(--vscode-descriptionForeground); - margin-bottom: 10px; - padding-bottom: 10px; - border-bottom: 1px dashed var(--vscode-input-border); - } - .agent-steps { - font-size: 13px; } .agent-step { display: flex; - align-items: flex-start; - gap: 8px; - padding: 6px 0; - border-left: 2px solid var(--vscode-input-border); - padding-left: 12px; - margin-left: 6px; + align-items: center; + gap: 6px; + padding: 4px 8px; + border-radius: 4px; + margin-bottom: 4px; + background: var(--vscode-list-hoverBackground); } .agent-step:last-child { - border-left-color: transparent; + margin-bottom: 0; } - .agent-step-icon { + .step-icon { flex-shrink: 0; } - .agent-step-content { - flex: 1; - min-width: 0; - } - .agent-step-name { + .step-name { font-weight: 500; + color: var(--vscode-foreground); } - .agent-step-result { - font-size: 12px; + .step-result { color: var(--vscode-descriptionForeground); - margin-top: 2px; - white-space: nowrap; + font-size: 11px; overflow: hidden; text-overflow: ellipsis; + white-space: nowrap; } - .agent-card-summary { - font-size: 13px; - padding: 8px 12px; - background: var(--vscode-sideBar-background); - border-top: 1px solid var(--vscode-input-border); - } - .agent-card-error { - color: var(--vscode-errorForeground); - padding: 8px 12px; - background: var(--vscode-inputValidation-errorBackground); + .agent-step-placeholder { + color: var(--vscode-descriptionForeground); + font-style: italic; + padding: 8px; + text-align: center; } `; } /** - * 渲染智能体卡片(启动状态) - */ -export function renderAgentCardStart(event: AgentStartEvent): string { - return ` -
-
-
- 🤖 - ${event.agentName} - 执行中 -
- -
-
-
指令:${escapeHtml(event.instruction)}
-
- -
-
-
- `; -} - -/** - * 渲染步骤项(进行中) - */ -export function renderAgentStepRunning(event: AgentProgressEvent): string { - const inputStr = event.toolInput ? JSON.stringify(event.toolInput) : ''; - const shortInput = inputStr.length > 50 ? inputStr.substring(0, 50) + '...' : inputStr; - - return ` -
- 🔄 -
-
${event.toolName}
-
${escapeHtml(shortInput)}
-
-
- `; -} - -/** - * 更新步骤项(完成) - */ -export function getStepCompleteUpdate(event: AgentProgressEvent): { icon: string; result: string } { - const result = event.toolResult || ''; - const shortResult = result.length > 80 ? result.substring(0, 80) + '...' : result; - return { - icon: event.status === 'completed' ? '✅' : '❌', - result: shortResult - }; -} - -/** - * 获取智能体卡片脚本 + * 获取智能体卡片的脚本 */ export function getAgentCardScript(): string { return ` - // 智能体状态存储 - const agentStates = {}; - - // 切换智能体卡片展开/收起 - function toggleAgentCard(agentId) { - const body = document.getElementById('agent-body-' + agentId); - const header = body?.previousElementSibling; - const toggle = header?.querySelector('.agent-card-toggle'); - - if (body && toggle) { - body.classList.toggle('collapsed'); - toggle.textContent = body.classList.contains('collapsed') ? '▶' : '▼'; - } - } - - // 处理智能体启动事件 - function handleAgentStart(event) { - agentStates[event.agentId] = { - status: 'running', - steps: [] + // 工具名称中文映射 + function getAgentToolDisplayName(toolName) { + const toolNameMap = { + 'file_read': '文件读取', + 'file_write': '文件写入', + 'file_delete': '文件删除', + 'file_list': '检索文件', + 'syntax_check': '语法检查', + 'simulation': '仿真', + 'waveform_summary': '波形分析', + 'knowledge_save': '保存知识库', + 'knowledge_load': '加载知识库', + 'queryKnowledgeSummary': '查询知识摘要', + 'queryRules': '查询规则', + 'setModule': '设置模块', + 'addSignal': '添加信号', + 'addSignalExample': '添加信号示例', + 'validateKnowledgeGraph': '验证知识图谱', + 'querySignals': '查询信号', + 'addPlan': '添加计划', + 'addEdge': '添加边', + 'showPlan': '显示计划', + 'spawnExplorer': '代码探索' }; - - // 在当前消息中添加智能体卡片 - const currentMessage = document.querySelector('.bot-message:last-child .message-content'); - if (currentMessage) { - currentMessage.insertAdjacentHTML('beforeend', renderAgentCardStart(event)); - } + return toolNameMap[toolName] || toolName; } - // 处理智能体进度事件 - function handleAgentProgress(event) { - const stepsContainer = document.getElementById('agent-steps-' + event.agentId); - if (!stepsContainer) return; + /** + * 渲染智能体卡片 + * @param {Object} segment - 智能体段落数据 + * @param {HTMLElement} segmentDiv - 段落容器元素 + */ + function renderAgentCard(segment, segmentDiv) { + segmentDiv.className += ' segment-agent'; - if (event.status === 'running') { - // 添加新步骤 - stepsContainer.insertAdjacentHTML('beforeend', renderAgentStepRunning(event)); - } else if (event.status === 'completed') { - // 更新步骤状态 - const stepEl = document.getElementById('agent-step-' + event.agentId + '-' + event.step); - if (stepEl) { - const iconEl = stepEl.querySelector('.agent-step-icon'); - const resultEl = stepEl.querySelector('.agent-step-result'); - if (iconEl) iconEl.textContent = '✅'; - if (resultEl) { - const result = event.toolResult || ''; - resultEl.textContent = result.length > 80 ? result.substring(0, 80) + '...' : result; - } + const statusText = segment.agentStatus === 'completed' ? '完成' + : segment.agentStatus === 'error' ? '错误' : '执行中'; + const statusClass = segment.agentStatus || 'running'; + + const stepsHtml = (segment.agentSteps || []).map(step => { + const icon = step.status === 'completed' ? '✅' : step.status === 'error' ? '❌' : '🔄'; + const displayName = getAgentToolDisplayName(step.toolName); + const result = step.toolResult ? \`: \${step.toolResult.substring(0, 50)}\${step.toolResult.length > 50 ? '...' : ''}\` : ''; + return \`
\${icon}\${displayName}\${result}
\`; + }).join(''); + + segmentDiv.innerHTML = \` +
+
+ ${agentIconSvg} + \${segment.agentName || '智能体'} + \${statusText} +
+
+
+ \${stepsHtml || '
等待执行...
'} +
+
+
+ \`; + + // 自动滚动到最新步骤 + setTimeout(() => { + const container = segmentDiv.querySelector('.agent-steps-container'); + if (container) { + container.scrollTop = container.scrollHeight; } - } - } - - // 处理智能体完成事件 - function handleAgentComplete(event) { - const card = document.getElementById('agent-' + event.agentId); - if (!card) return; - - // 更新状态 - const statusEl = card.querySelector('.agent-card-status'); - if (statusEl) { - statusEl.className = 'agent-card-status completed'; - statusEl.textContent = '完成'; - } - - // 添加摘要 - const body = card.querySelector('.agent-card-body'); - if (body) { - body.insertAdjacentHTML('beforeend', - '
' + escapeHtml(event.summary) + '
' - ); - } - - // 自动收起 - body?.classList.add('collapsed'); - const toggle = card.querySelector('.agent-card-toggle'); - if (toggle) toggle.textContent = '▶'; - } - - // 处理智能体错误事件 - function handleAgentError(event) { - const card = document.getElementById('agent-' + event.agentId); - if (!card) return; - - // 更新状态 - const statusEl = card.querySelector('.agent-card-status'); - if (statusEl) { - statusEl.className = 'agent-card-status error'; - statusEl.textContent = '错误'; - } - - // 添加错误信息 - const body = card.querySelector('.agent-card-body'); - if (body) { - body.insertAdjacentHTML('beforeend', - '
错误:' + escapeHtml(event.error) + '
' - ); - } - } - - // HTML 转义 - function escapeHtml(text) { - if (!text) return ''; - const div = document.createElement('div'); - div.textContent = text; - return div.innerHTML; + }, 0); } `; } - -/** - * HTML 转义(服务端使用) - */ -function escapeHtml(text: string): string { - if (!text) return ''; - return text - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} diff --git a/src/views/messageArea.ts b/src/views/messageArea.ts index 635f8d2..bf5eef2 100644 --- a/src/views/messageArea.ts +++ b/src/views/messageArea.ts @@ -15,11 +15,13 @@ import { fileWriteIconSvg, syntaxCheckIconSvg, SearchCode, + agentIconSvg, } from "../constants/toolIcons"; import { getWaveformPreviewContent, getWaveformPreviewScript, } from "./waveformPreviewContent"; +import { getAgentCardStyles, getAgentCardScript } from "./agentCard"; /** * 获取消息区域的 HTML 内容 @@ -528,88 +530,7 @@ export function getMessageAreaStyles(): string { border-radius: 4px; font-size: 12px;} - /* 智能体卡片样式 */ - .segment-agent { - margin: 8px 0; - } - .agent-card { - border: 1px solid var(--vscode-input-border); - border-radius: 8px; - overflow: hidden; - background: var(--vscode-editor-background); - } - .agent-header { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - background: var(--vscode-sideBar-background); - border-bottom: 1px solid var(--vscode-input-border); - } - .agent-icon { - font-size: 16px; - } - .agent-name { - font-weight: 500; - flex: 1; - } - .agent-status { - font-size: 11px; - padding: 2px 8px; - border-radius: 10px; - } - .agent-status.running { - background: var(--vscode-inputValidation-infoBackground); - color: var(--vscode-inputValidation-infoForeground); - } - .agent-status.completed { - background: #28a745; - color: white; - } - .agent-status.error { - background: #dc3545; - color: white; - } - .agent-body { - padding: 8px; - } - .agent-steps-container { - max-height: 150px; - overflow-y: auto; - font-size: 12px; - } - .agent-step { - display: flex; - align-items: center; - gap: 6px; - padding: 4px 8px; - border-radius: 4px; - margin-bottom: 4px; - background: var(--vscode-list-hoverBackground); - } - .agent-step:last-child { - margin-bottom: 0; - } - .step-icon { - flex-shrink: 0; - } - .step-name { - font-weight: 500; - color: var(--vscode-foreground); - } - .step-result { - color: var(--vscode-descriptionForeground); - font-size: 11px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .agent-step-placeholder { - color: var(--vscode-descriptionForeground); - font-style: italic; - padding: 8px; - text-align: center; - } + ${getAgentCardStyles()} /* 计划卡片样式 */ .segment-plan { @@ -708,15 +629,31 @@ export function getMessageAreaScript(): string { const syntaxCheckIconSvg = \`${syntaxCheckIconSvg}\`; const searchCodeIconSvg = \`${SearchCode}\`; + ${getAgentCardScript()} + // 工具名称映射 function getToolDisplayName(toolName) { const toolNameMap = { 'file_read': '已完成文件读取', 'file_write': '已完成文件写入', + 'file_delete': '已完成文件删除', 'file_list': '已检索代码文件', 'syntax_check': '已完成语法检查', 'simulation': '已完成仿真', - 'waveform_summary': '已完成波形分析' + 'waveform_summary': '已完成波形分析', + 'knowledge_save': '已保存知识库', + 'knowledge_load': '已加载知识库', + 'queryKnowledgeSummary': '已查询知识摘要', + 'queryRules': '已查询规则', + 'setModule': '已设置模块', + 'addSignal': '已添加信号', + 'addSignalExample': '已添加信号示例', + 'validateKnowledgeGraph': '已验证知识图谱', + 'querySignals': '已查询信号', + 'addPlan': '已添加计划', + 'addEdge': '已添加边', + 'showPlan': '已显示计划', + 'spawnExplorer': '代码探索' }; return toolNameMap[toolName] || toolName; } @@ -949,6 +886,10 @@ export function getMessageAreaScript(): string { segmentDiv.className += ' segment-text'; segmentDiv.innerHTML = formatText(segment.content); } else if (segment.type === 'tool') { + // 过滤掉不需要显示的工具 + if (segment.toolName === 'spawnExplorer') { + return; + } const statusIcon = segment.toolStatus === 'error' ? '❌' : '🔧'; const toolResult = segment.toolResult || ''; @@ -1083,39 +1024,7 @@ export function getMessageAreaScript(): string { } } else if (segment.type === 'agent') { // 智能体卡片渲染 - segmentDiv.className += ' segment-agent'; - const statusText = segment.agentStatus === 'completed' ? '完成' - : segment.agentStatus === 'error' ? '错误' : '执行中'; - const statusClass = segment.agentStatus || 'running'; - - const stepsHtml = (segment.agentSteps || []).map(step => { - const icon = step.status === 'completed' ? '✅' : step.status === 'error' ? '❌' : '🔄'; - const result = step.toolResult ? \`: \${step.toolResult.substring(0, 50)}\${step.toolResult.length > 50 ? '...' : ''}\` : ''; - return \`
\${icon}\${step.toolName}\${result}
\`; - }).join(''); - - segmentDiv.innerHTML = \` -
-
- 🤖 - \${segment.agentName || '智能体'} - \${statusText} -
-
-
- \${stepsHtml || '
等待执行...
'} -
-
-
- \`; - - // 自动滚动到最新步骤 - setTimeout(() => { - const container = segmentDiv.querySelector('.agent-steps-container'); - if (container) { - container.scrollTop = container.scrollHeight; - } - }, 0); + renderAgentCard(segment, segmentDiv); } else if (segment.type === 'plan') { // 计划卡片渲染(类似 askUser) segmentDiv.className += ' segment-plan'; @@ -1269,6 +1178,10 @@ export function getMessageAreaScript(): string { segmentDiv.className += ' segment-text'; segmentDiv.innerHTML = formatText(segment.content); } else if (segment.type === 'tool') { + // 过滤掉不需要显示的工具 + if (segment.toolName === 'spawnExplorer') { + return; + } const statusIcon = segment.toolStatus === 'error' ? '❌' : '🔧'; const toolResult = segment.toolResult || ''; @@ -1347,31 +1260,7 @@ export function getMessageAreaScript(): string { \`; } else if (segment.type === 'agent') { // 智能体卡片渲染 - segmentDiv.className += ' segment-agent'; - const statusText = segment.agentStatus === 'completed' ? '完成' - : segment.agentStatus === 'error' ? '错误' : '执行中'; - const statusClass = segment.agentStatus || 'running'; - - const stepsHtml = (segment.agentSteps || []).map(step => { - const icon = step.status === 'completed' ? '✅' : step.status === 'error' ? '❌' : '🔄'; - const result = step.toolResult ? \`: \${step.toolResult.substring(0, 50)}\${step.toolResult.length > 50 ? '...' : ''}\` : ''; - return \`
\${icon}\${step.toolName}\${result}
\`; - }).join(''); - - segmentDiv.innerHTML = \` -
-
- 🤖 - \${segment.agentName || '智能体'} - \${statusText} -
-
-
- \${stepsHtml || '
等待执行...
'} -
-
-
- \`; + renderAgentCard(segment, segmentDiv); } else if (segment.type === 'plan') { // 计划卡片渲染 segmentDiv.className += ' segment-plan';