feat: WebView 集成运行模式

- webviewContent 集成模式选择器脚本和样式
- inputArea 适配模式传递
- ICViewProvider/ICHelperPanel 传递模式参数
This commit is contained in:
XiaoFeng
2025-12-30 20:42:44 +08:00
parent 42481cd314
commit 023fdb66c3
5 changed files with 105 additions and 6 deletions

View File

@ -17,7 +17,7 @@ export interface IccoderConfig {
/** 默认配置 */ /** 默认配置 */
const DEFAULT_CONFIG: IccoderConfig = { const DEFAULT_CONFIG: IccoderConfig = {
backendUrl: "http://localhost:8080", backendUrl: "http://localhost:8080",
timeout: 60000, timeout: 3600000, // 1小时
userId: "default-user", userId: "default-user",
}; };

View File

@ -9,6 +9,9 @@ import {
handleReplaceInFile, handleReplaceInFile,
handleUserAnswer, handleUserAnswer,
abortCurrentDialog, abortCurrentDialog,
handlePlanAction,
setPendingPlanExecution,
getCurrentTaskId,
} from "../utils/messageHandler"; } from "../utils/messageHandler";
import { VCDViewerPanel } from "./VCDViewerPanel"; import { VCDViewerPanel } from "./VCDViewerPanel";
import { ChatHistoryManager } from "../utils/chatHistoryManager"; import { ChatHistoryManager } from "../utils/chatHistoryManager";
@ -107,7 +110,7 @@ export async function showICHelperPanel(
// 切换到当前面板的任务上下文 // 切换到当前面板的任务上下文
historyManager.switchToPanelTask(panelId); historyManager.switchToPanelTask(panelId);
handleUserMessage(panel, message.text, context.extensionPath); handleUserMessage(panel, message.text, context.extensionPath, message.mode);
break; break;
case "readFile": case "readFile":
handleReadFile(panel, message.filePath); handleReadFile(panel, message.filePath);
@ -181,6 +184,24 @@ export async function showICHelperPanel(
case "abortDialog": case "abortDialog":
abortCurrentDialog(); abortCurrentDialog();
break; 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, undefined,

View File

@ -47,7 +47,7 @@ export function showICHelperPanel(context: vscode.ExtensionContext) {
(message) => { (message) => {
switch (message.command) { switch (message.command) {
case "sendMessage": case "sendMessage":
handleUserMessage(panel, message.text, context.extensionPath); handleUserMessage(panel, message.text, context.extensionPath, message.mode);
break; break;
case "readFile": case "readFile":
handleReadFile(panel, message.filePath); handleReadFile(panel, message.filePath);

View File

@ -261,7 +261,7 @@ export function getInputAreaStyles(): string {
*/ */
export function getInputAreaScript(): string { export function getInputAreaScript(): string {
return ` return `
${getModeSelectorScript()} // 注意:getModeSelectorScript() 已在 webviewContent.ts 开头加载,这里不再重复加载
${getModelSelectorScript()} ${getModelSelectorScript()}
${getContextButtonScript()} ${getContextButtonScript()}
${getContextCompressScript()} ${getContextCompressScript()}
@ -328,13 +328,14 @@ export function getInputAreaScript(): string {
const mode = getCurrentMode(); // 从模式选择器组件获取当前模式 const mode = getCurrentMode(); // 从模式选择器组件获取当前模式
const model = getCurrentModel(); // 从模型选择器组件获取当前模型 const model = getCurrentModel(); // 从模型选择器组件获取当前模型
const planMode = document.getElementById('planToggle')?.checked || false;
addMessage(text, 'user'); addMessage(text, 'user');
// 切换按钮为暂停状态 // 切换按钮为暂停状态
setSendButtonState(true); 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 = ''; messageInput.value = '';
autoResizeTextarea(); // 重置输入框高度 autoResizeTextarea(); // 重置输入框高度
messageInput.focus(); messageInput.focus();

View File

@ -417,6 +417,62 @@ export function getWebviewContent(iconUri?: string): string {
let loadingIndicator = null; let loadingIndicator = null;
let currentSegmentedMessage = 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) { function quickAction(type) {
const questions = { const questions = {
counter: '生成一个4位同步计数器', counter: '生成一个4位同步计数器',
@ -588,6 +644,27 @@ export function getWebviewContent(iconUri?: string): string {
} }
break; 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: default:
console.log('[WebView] 未处理的消息类型:', message.command); console.log('[WebView] 未处理的消息类型:', message.command);
} }