From 023fdb66c3b53b9b82e3d528bb37618a3f246112 Mon Sep 17 00:00:00 2001 From: XiaoFeng <117837368+Fzhiyu1@users.noreply.github.com> Date: Tue, 30 Dec 2025 20:42:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20WebView=20=E9=9B=86=E6=88=90=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - webviewContent 集成模式选择器脚本和样式 - inputArea 适配模式传递 - ICViewProvider/ICHelperPanel 传递模式参数 --- src/config/settings.ts | 2 +- src/panels/ICHelperPanel.ts | 23 ++++++++++- src/views/ICViewProvider.ts | 2 +- src/views/inputArea.ts | 5 ++- src/views/webviewContent.ts | 79 ++++++++++++++++++++++++++++++++++++- 5 files changed, 105 insertions(+), 6 deletions(-) diff --git a/src/config/settings.ts b/src/config/settings.ts index e6c041f..621be84 100644 --- a/src/config/settings.ts +++ b/src/config/settings.ts @@ -17,7 +17,7 @@ export interface IccoderConfig { /** 默认配置 */ const DEFAULT_CONFIG: IccoderConfig = { backendUrl: "http://localhost:8080", - timeout: 60000, + timeout: 3600000, // 1小时 userId: "default-user", }; diff --git a/src/panels/ICHelperPanel.ts b/src/panels/ICHelperPanel.ts index f9d227e..f5e149f 100644 --- a/src/panels/ICHelperPanel.ts +++ b/src/panels/ICHelperPanel.ts @@ -9,6 +9,9 @@ import { handleReplaceInFile, handleUserAnswer, abortCurrentDialog, + handlePlanAction, + setPendingPlanExecution, + getCurrentTaskId, } from "../utils/messageHandler"; import { VCDViewerPanel } from "./VCDViewerPanel"; import { ChatHistoryManager } from "../utils/chatHistoryManager"; @@ -107,7 +110,7 @@ export async function showICHelperPanel( // 切换到当前面板的任务上下文 historyManager.switchToPanelTask(panelId); - handleUserMessage(panel, message.text, context.extensionPath); + handleUserMessage(panel, message.text, context.extensionPath, message.mode); break; case "readFile": handleReadFile(panel, message.filePath); @@ -181,6 +184,24 @@ export async function showICHelperPanel( case "abortDialog": abortCurrentDialog(); break; + // 处理计划操作(只做模式切换,响应已通过 submitAnswer 发送) + case "planAction": + if (message.action === 'confirm') { + // 确认执行:切换到 Agent 模式 + panel.webview.postMessage({ + command: 'switchMode', + mode: 'agent' + }); + // 获取当前会话的 taskId,用于复用知识图谱数据 + const taskId = getCurrentTaskId(); + if (taskId) { + // 设置待执行的计划,对话结束后自动执行(复用 taskId) + setPendingPlanExecution(panel, message.planTitle || '计划', context.extensionPath, taskId); + } else { + console.warn('[ICHelperPanel] 无法获取当前 taskId,知识图谱数据可能丢失'); + } + } + break; } }, undefined, diff --git a/src/views/ICViewProvider.ts b/src/views/ICViewProvider.ts index 43a537a..286ac2b 100644 --- a/src/views/ICViewProvider.ts +++ b/src/views/ICViewProvider.ts @@ -47,7 +47,7 @@ export function showICHelperPanel(context: vscode.ExtensionContext) { (message) => { switch (message.command) { case "sendMessage": - handleUserMessage(panel, message.text, context.extensionPath); + handleUserMessage(panel, message.text, context.extensionPath, message.mode); break; case "readFile": handleReadFile(panel, message.filePath); diff --git a/src/views/inputArea.ts b/src/views/inputArea.ts index a0ec703..d1596a4 100644 --- a/src/views/inputArea.ts +++ b/src/views/inputArea.ts @@ -261,7 +261,7 @@ export function getInputAreaStyles(): string { */ export function getInputAreaScript(): string { return ` - ${getModeSelectorScript()} + // 注意:getModeSelectorScript() 已在 webviewContent.ts 开头加载,这里不再重复加载 ${getModelSelectorScript()} ${getContextButtonScript()} ${getContextCompressScript()} @@ -328,13 +328,14 @@ export function getInputAreaScript(): string { const mode = getCurrentMode(); // 从模式选择器组件获取当前模式 const model = getCurrentModel(); // 从模型选择器组件获取当前模型 + const planMode = document.getElementById('planToggle')?.checked || false; addMessage(text, 'user'); // 切换按钮为暂停状态 setSendButtonState(true); - vscode.postMessage({ command: 'sendMessage', text: text, mode: mode, model: model }); + vscode.postMessage({ command: 'sendMessage', text: text, mode: mode, model: model, planMode: planMode }); messageInput.value = ''; autoResizeTextarea(); // 重置输入框高度 messageInput.focus(); diff --git a/src/views/webviewContent.ts b/src/views/webviewContent.ts index c586f31..beb7c67 100644 --- a/src/views/webviewContent.ts +++ b/src/views/webviewContent.ts @@ -255,7 +255,7 @@ export function getWebviewContent(iconUri?: string): string { padding: 0; } .message-segment { - padding: 10px 22 px; + padding: 10px 22px; } .segment-text { line-height: 1.6; @@ -417,6 +417,62 @@ export function getWebviewContent(iconUri?: string): string { let loadingIndicator = null; let currentSegmentedMessage = null; // 当前分段消息容器 + // ========== 模式选择器脚本(直接内联,避免模板字符串嵌套问题)========== + let currentMode = 'agent'; + + function toggleModeDropdown() { + const modeSelectEl = document.getElementById('modeSelect'); + const modelSelectEl = document.getElementById('modelSelect'); + if (modeSelectEl) { + modeSelectEl.classList.toggle('active'); + if (modelSelectEl) { + modelSelectEl.classList.remove('active'); + } + } + } + + function selectMode(value, label) { + currentMode = value; + const modeValue = document.getElementById('modeValue'); + const modeTooltip = document.getElementById('modeTooltip'); + if (modeValue) { + modeValue.textContent = label; + } + if (modeTooltip) { + const tooltipMap = { + 'plan': '只读模式 - 只能查询分析', + 'ask': '逐个确认 - 每个写操作需确认', + 'agent': '智能体自主模式', + 'auto': '完全自动 - 所有操作自动执行' + }; + modeTooltip.textContent = tooltipMap[value] || '切换模式'; + } + const options = document.querySelectorAll('.mode-option'); + options.forEach(option => { + if (option.getAttribute('data-value') === value) { + option.classList.add('selected'); + } else { + option.classList.remove('selected'); + } + }); + const modeSelectEl = document.getElementById('modeSelect'); + if (modeSelectEl) { + modeSelectEl.classList.remove('active'); + } + } + + function getCurrentMode() { + return currentMode; + } + + document.addEventListener('click', (event) => { + const modeSelectEl = document.getElementById('modeSelect'); + if (modeSelectEl && !modeSelectEl.contains(event.target)) { + modeSelectEl.classList.remove('active'); + } + }); + // ========== 模式选择器脚本结束 ========== + function quickAction(type) { const questions = { counter: '生成一个4位同步计数器', @@ -588,6 +644,27 @@ export function getWebviewContent(iconUri?: string): string { } break; + case 'switchMode': + // 切换运行模式(Plan 确认后自动切换到 Agent) + if (message.mode && typeof selectMode === 'function') { + const labelMap = { + 'plan': 'Plan', + 'ask': 'Ask', + 'agent': 'Agent', + 'auto': 'Auto' + }; + selectMode(message.mode, labelMap[message.mode] || message.mode); + console.log('[WebView] 模式已切换到:', message.mode); + } + break; + + case 'addMessage': + // 添加消息(通用) + if (message.text && message.sender) { + addMessage(message.text, message.sender); + } + break; + default: console.log('[WebView] 未处理的消息类型:', message.command); }