461 lines
12 KiB
TypeScript
461 lines
12 KiB
TypeScript
/**
|
||
* 消息路由处理模块
|
||
* 功能:处理 webview 消息的路由分发
|
||
* 依赖:各个 helper 模块和 messageHandler
|
||
* 使用场景:webview 消息接收时
|
||
*/
|
||
import * as vscode from "vscode";
|
||
import {
|
||
handleUserMessage,
|
||
insertCodeToEditor,
|
||
handleReadFile,
|
||
handleUpdateFile,
|
||
handleRenameFile,
|
||
handleReplaceInFile,
|
||
handleUserAnswer,
|
||
abortCurrentDialog,
|
||
handleOptimizePrompt,
|
||
handlePlanAction,
|
||
getCurrentTaskId,
|
||
handleAcceptChange,
|
||
handleRejectChange,
|
||
handleOpenFileDiff,
|
||
startChangeSession,
|
||
} from "../../utils/messageHandler";
|
||
import {
|
||
loadPersonalRules,
|
||
savePersonalRule,
|
||
updatePersonalRule,
|
||
deletePersonalRule,
|
||
} from "../../utils/personalRulesManager";
|
||
import { compactDialog } from "../../services/apiClient";
|
||
import { ChatHistoryManager } from "../../utils/chatHistoryManager";
|
||
import { getCachedUserInfo } from "../../services/userService";
|
||
import { loadConversationHistory, selectConversation } from "./conversationHelper";
|
||
import { getVCDFileInfo } from "./vcdHelper";
|
||
import {
|
||
handleAddContextFile,
|
||
handleAddContextFolder,
|
||
handleAddContextImage,
|
||
handleAddContextDocument,
|
||
} from "./contextHelper";
|
||
import { openFile, openFileWithSelection, openFilePathTag } from "./fileHelper";
|
||
|
||
export async function handleWebviewMessage(
|
||
message: any,
|
||
panel: vscode.WebviewPanel,
|
||
context: vscode.ExtensionContext,
|
||
) {
|
||
const historyManager = ChatHistoryManager.getInstance();
|
||
const panelId = (panel as any).__uniqueId;
|
||
|
||
switch (message.command) {
|
||
case "sendMessage":
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
|
||
historyManager.switchToPanelTask(panelId);
|
||
const sessionId = `session_${panelId}_${Date.now()}`;
|
||
startChangeSession(sessionId);
|
||
panel.webview.postMessage({ type: "showProgress" });
|
||
|
||
handleUserMessage(
|
||
panel,
|
||
message.text,
|
||
context.extensionPath,
|
||
message.mode,
|
||
message.model,
|
||
message.contextItems,
|
||
);
|
||
break;
|
||
|
||
case "readFile":
|
||
handleReadFile(panel, message.filePath);
|
||
break;
|
||
|
||
case "updateFile":
|
||
handleUpdateFile(panel, message.filePath, message.content);
|
||
break;
|
||
|
||
case "renameFile":
|
||
handleRenameFile(panel, message.oldPath, message.newPath);
|
||
break;
|
||
|
||
case "replaceInFile":
|
||
handleReplaceInFile(
|
||
panel,
|
||
message.filePath,
|
||
message.searchText,
|
||
message.replaceText,
|
||
);
|
||
break;
|
||
|
||
case "insertCode":
|
||
insertCodeToEditor(message.code);
|
||
break;
|
||
|
||
case "showInfo":
|
||
vscode.window.showInformationMessage(message.text);
|
||
break;
|
||
|
||
case "openWaveformViewer":
|
||
if (message.vcdFilePath) {
|
||
vscode.commands.executeCommand(
|
||
"ic-coder.openVCDViewer",
|
||
message.vcdFilePath,
|
||
);
|
||
}
|
||
break;
|
||
|
||
case "getVCDInfo":
|
||
if (message.vcdFilePath && message.containerId) {
|
||
getVCDFileInfo(panel, message.vcdFilePath, message.containerId);
|
||
}
|
||
break;
|
||
|
||
case "createNewConversation":
|
||
const { showICHelperPanel } = require("../ICHelperPanel");
|
||
showICHelperPanel(context, panel.viewColumn);
|
||
break;
|
||
|
||
case "loadConversationHistory":
|
||
loadConversationHistory(
|
||
panel,
|
||
message.offset || 0,
|
||
message.limit || 10,
|
||
);
|
||
break;
|
||
|
||
case "selectConversation":
|
||
if (message.conversationId) {
|
||
selectConversation(panel, message.conversationId, context.extensionPath);
|
||
}
|
||
break;
|
||
|
||
case "submitAnswer":
|
||
void handleUserAnswer(
|
||
message.askId,
|
||
message.selected,
|
||
message.customInput,
|
||
message.answers,
|
||
);
|
||
break;
|
||
|
||
case "abortDialog":
|
||
void abortCurrentDialog();
|
||
break;
|
||
|
||
case "compressConversation":
|
||
{
|
||
const taskId = getCurrentTaskId();
|
||
if (taskId) {
|
||
compactDialog(taskId)
|
||
.then((result) => {
|
||
panel.webview.postMessage({
|
||
command: "receiveMessage",
|
||
text: result.success
|
||
? "✅ 会话压缩完成"
|
||
: `❌ 压缩失败: ${result.error || "未知错误"}`,
|
||
});
|
||
})
|
||
.catch((err) => {
|
||
panel.webview.postMessage({
|
||
command: "receiveMessage",
|
||
text: `❌ 压缩失败: ${err.message || "网络错误"}`,
|
||
});
|
||
});
|
||
} else {
|
||
panel.webview.postMessage({
|
||
command: "receiveMessage",
|
||
text: "❌ 没有活跃的会话",
|
||
});
|
||
}
|
||
}
|
||
break;
|
||
|
||
case "optimizePrompt":
|
||
if (typeof message.prompt === "string") {
|
||
void handleOptimizePrompt(panel, message.prompt);
|
||
} else {
|
||
panel.webview.postMessage({
|
||
command: "optimizeResult",
|
||
success: false,
|
||
error: "提示词为空或格式错误",
|
||
});
|
||
}
|
||
break;
|
||
|
||
case "logout":
|
||
vscode.commands.executeCommand("ic-coder.logout");
|
||
break;
|
||
|
||
case "openFile":
|
||
if (message.filePath) {
|
||
await openFile(message.filePath);
|
||
}
|
||
break;
|
||
|
||
case "openFileWithSelection":
|
||
if (message.filePath) {
|
||
await openFileWithSelection(
|
||
message.filePath,
|
||
message.startLine,
|
||
message.endLine,
|
||
);
|
||
}
|
||
break;
|
||
|
||
case "openFilePathTag":
|
||
if (message.filePath) {
|
||
await openFilePathTag(
|
||
message.filePath,
|
||
message.startLine,
|
||
message.endLine,
|
||
);
|
||
}
|
||
break;
|
||
|
||
case "acceptChange":
|
||
if (message.changeId) {
|
||
await handleAcceptChange(panel, message.changeId);
|
||
}
|
||
break;
|
||
|
||
case "rejectChange":
|
||
if (message.changeId) {
|
||
await handleRejectChange(panel, message.changeId);
|
||
}
|
||
break;
|
||
|
||
case "openFileDiff":
|
||
if (message.changeId) {
|
||
await handleOpenFileDiff(panel, message.changeId);
|
||
}
|
||
break;
|
||
|
||
case "checkInvitationCode":
|
||
{
|
||
const userInfo = getCachedUserInfo();
|
||
if (userInfo?.isPluginTrial === true) {
|
||
panel.webview.postMessage({
|
||
command: "invitationCodeStatus",
|
||
verified: true,
|
||
});
|
||
} else {
|
||
const { InvitationService } = require("../../services/invitationService");
|
||
const isVerified = await InvitationService.isVerified(context);
|
||
panel.webview.postMessage({
|
||
command: "invitationCodeStatus",
|
||
verified: isVerified,
|
||
});
|
||
}
|
||
}
|
||
break;
|
||
|
||
case "checkWelcomeModal":
|
||
{
|
||
const userInfo = getCachedUserInfo();
|
||
if (userInfo?.isPluginTrial === true) {
|
||
if (userInfo.pluginTrialExpiresAt === undefined) {
|
||
break;
|
||
}
|
||
if (userInfo.pluginTrialExpiresAt !== null) {
|
||
const now = Date.now();
|
||
const isExpired = now >= userInfo.pluginTrialExpiresAt;
|
||
if (isExpired) {
|
||
break;
|
||
}
|
||
}
|
||
panel.webview.postMessage({ command: "showWelcomeModal" });
|
||
}
|
||
}
|
||
break;
|
||
|
||
case "checkTrialExpiration":
|
||
{
|
||
const { TrialExpirationService } = require("../../services/trialExpirationService");
|
||
const trialService = new TrialExpirationService(context, panel);
|
||
await trialService.checkExpiration();
|
||
}
|
||
break;
|
||
|
||
case "verifyInvitationCode":
|
||
{
|
||
const { InvitationService } = require("../../services/invitationService");
|
||
const result = await InvitationService.verifyCode(message.code);
|
||
|
||
if (result.success) {
|
||
await InvitationService.saveVerificationStatus(context, message.code);
|
||
panel.webview.postMessage({
|
||
command: "invitationCodeVerified",
|
||
success: true,
|
||
});
|
||
setTimeout(() => {
|
||
panel.webview.postMessage({ command: "showNdtWelcomeModal" });
|
||
}, 300);
|
||
} else {
|
||
panel.webview.postMessage({
|
||
command: "invitationCodeVerified",
|
||
success: false,
|
||
message: result.message,
|
||
});
|
||
}
|
||
}
|
||
break;
|
||
|
||
case "openICCoder":
|
||
vscode.env.openExternal(vscode.Uri.parse("https://www.iccoder.com"));
|
||
break;
|
||
|
||
case "openTutorial":
|
||
vscode.env.openExternal(
|
||
vscode.Uri.parse(
|
||
"https://www.iccoder.com/guides/quick-start/first-task-plugin",
|
||
),
|
||
);
|
||
break;
|
||
|
||
case "openUserManual":
|
||
vscode.env.openExternal(vscode.Uri.parse("https://www.iccoder.com"));
|
||
break;
|
||
|
||
case "openUserFeedback":
|
||
panel.webview.postMessage({ command: "showFeedbackQRCode" });
|
||
break;
|
||
|
||
case "planAction":
|
||
if (message.action === "confirm") {
|
||
panel.webview.postMessage({ command: "switchMode", mode: "agent" });
|
||
} else if (message.action === "modify" || message.action === "cancel") {
|
||
void handlePlanAction(
|
||
panel,
|
||
message.action,
|
||
message.planTitle || "",
|
||
context.extensionPath,
|
||
message.model,
|
||
);
|
||
}
|
||
break;
|
||
|
||
case "addContextFile":
|
||
await handleAddContextFile(panel);
|
||
break;
|
||
|
||
case "addContextFolder":
|
||
await handleAddContextFolder(panel);
|
||
break;
|
||
|
||
case "addContextImage":
|
||
await handleAddContextImage(panel);
|
||
break;
|
||
|
||
case "addContextDocument":
|
||
await handleAddContextDocument(panel);
|
||
break;
|
||
|
||
case "checkWorkspace":
|
||
const hasWorkspace = !!(
|
||
vscode.workspace.workspaceFolders &&
|
||
vscode.workspace.workspaceFolders.length > 0
|
||
);
|
||
if (!hasWorkspace) {
|
||
vscode.window
|
||
.showWarningMessage(
|
||
"请先打开一个文件夹作为工作区,这样我就能更好地为您服务了 😊",
|
||
"打开文件夹",
|
||
)
|
||
.then((selection) => {
|
||
if (selection === "打开文件夹") {
|
||
vscode.commands.executeCommand("vscode.openFolder");
|
||
}
|
||
});
|
||
}
|
||
panel.webview.postMessage({
|
||
command: "workspaceStatus",
|
||
hasWorkspace: hasWorkspace,
|
||
});
|
||
break;
|
||
|
||
case "openExternalUrl":
|
||
if (message.url) {
|
||
vscode.env.openExternal(vscode.Uri.parse(message.url));
|
||
}
|
||
break;
|
||
|
||
case "loadPersonalRules":
|
||
{
|
||
const data = loadPersonalRules();
|
||
panel.webview.postMessage({
|
||
command: "personalRulesLoaded",
|
||
data: data,
|
||
});
|
||
}
|
||
break;
|
||
|
||
case "savePersonalRule":
|
||
{
|
||
const success = await savePersonalRule(
|
||
message.name,
|
||
message.content,
|
||
message.enabled,
|
||
);
|
||
if (success) {
|
||
const data = loadPersonalRules();
|
||
panel.webview.postMessage({
|
||
command: "personalRulesLoaded",
|
||
data: data,
|
||
});
|
||
}
|
||
}
|
||
break;
|
||
|
||
case "updatePersonalRule":
|
||
{
|
||
const success = await updatePersonalRule(
|
||
message.filename,
|
||
message.name,
|
||
message.content,
|
||
message.enabled,
|
||
);
|
||
if (success) {
|
||
const data = loadPersonalRules();
|
||
panel.webview.postMessage({
|
||
command: "personalRulesLoaded",
|
||
data: data,
|
||
});
|
||
}
|
||
}
|
||
break;
|
||
|
||
case "deletePersonalRule":
|
||
{
|
||
const success = await deletePersonalRule(message.filename);
|
||
if (success) {
|
||
const data = loadPersonalRules();
|
||
panel.webview.postMessage({
|
||
command: "personalRulesLoaded",
|
||
data: data,
|
||
});
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|