refactor: 优化 ICHelperPanel 组件结构
- 将 1346 行的单文件拆分为 7 个职责单一的模块 - authHelper: 认证和登录检查 - userInfoHelper: 用户信息管理 - conversationHelper: 会话历史加载 - vcdHelper: VCD 文件处理 - contextHelper: 上下文管理 - fileHelper: 文件操作 - messageRouter: 消息路由分发 - 主组件精简至 157 行,提升可维护性
This commit is contained in:
396
src/panels/helpers/messageRouter.ts
Normal file
396
src/panels/helpers/messageRouter.ts
Normal file
@ -0,0 +1,396 @@
|
||||
/**
|
||||
* 消息路由处理模块
|
||||
* 功能:处理 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 { 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user