From c9e9df38253699a37fc283e6cd2a7a29b3e67b2a Mon Sep 17 00:00:00 2001 From: Roe-xin Date: Thu, 26 Feb 2026 17:27:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E4=BF=AE=E6=94=B9=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E6=A0=B7=E5=BC=8F=20+=20=E6=AC=A2=E8=BF=8E?= =?UTF-8?q?=E5=AE=81=E5=BE=B7=E5=BC=B9=E7=AA=97=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/panels/ICHelperPanel.ts | 274 ++++++++++++++++++++++------------- src/views/messageArea.ts | 4 +- src/views/ndtWelcomeModal.ts | 9 +- 3 files changed, 183 insertions(+), 104 deletions(-) diff --git a/src/panels/ICHelperPanel.ts b/src/panels/ICHelperPanel.ts index 8292195..cd87efc 100644 --- a/src/panels/ICHelperPanel.ts +++ b/src/panels/ICHelperPanel.ts @@ -28,17 +28,17 @@ import { setBalanceUpdateCallback } from "../services/creditsService"; function getTierIconUri( webview: vscode.Webview, context: vscode.ExtensionContext, - tierCode?: string + tierCode?: string, ): string | undefined { if (!tierCode) { return undefined; } const tierIconMap: Record = { - 'BASIC': 'free.png', - 'TRIAL': 'PRO-Try.png', - 'ADVANCED': 'PRO.png', - 'PROFESSIONAL': 'PRO+.png' + BASIC: "free.png", + TRIAL: "PRO-Try.png", + ADVANCED: "PRO.png", + PROFESSIONAL: "PRO+.png", }; const iconFile = tierIconMap[tierCode]; @@ -47,7 +47,13 @@ function getTierIconUri( } const iconUri = webview.asWebviewUri( - vscode.Uri.joinPath(context.extensionUri, 'src', 'assets', 'titleIcon', iconFile) + vscode.Uri.joinPath( + context.extensionUri, + "src", + "assets", + "titleIcon", + iconFile, + ), ); return iconUri.toString(); @@ -58,27 +64,29 @@ function getTierIconUri( */ export async function showICHelperPanel( context: vscode.ExtensionContext, - viewColumn?: vscode.ViewColumn + viewColumn?: vscode.ViewColumn, ) { // 检查 token 是否过期 let token: string | undefined; try { - const session = await vscode.authentication.getSession("iccoder", [], { createIfNone: false }); + const session = await vscode.authentication.getSession("iccoder", [], { + createIfNone: false, + }); token = session?.accessToken; } catch (error) { - console.warn('[ICHelperPanel] 获取 session 失败:', error); + console.warn("[ICHelperPanel] 获取 session 失败:", error); } if (token && isTokenExpired(token)) { // 清除过期的 session - await context.globalState.update('icCoderSessions', []); - await context.globalState.update('icCoderUserInfo', undefined); + await context.globalState.update("icCoderSessions", []); + await context.globalState.update("icCoderUserInfo", undefined); const action = await vscode.window.showWarningMessage( - '登录已过期,请重新登录', - '立即登录' + "登录已过期,请重新登录", + "立即登录", ); - if (action === '立即登录') { + if (action === "立即登录") { vscode.commands.executeCommand("ic-coder.login"); } return; @@ -120,9 +128,9 @@ export async function showICHelperPanel( retainContextWhenHidden: true, localResourceRoots: [ vscode.Uri.joinPath(context.extensionUri, "media"), - vscode.Uri.joinPath(context.extensionUri, "src", "assets") + vscode.Uri.joinPath(context.extensionUri, "src", "assets"), ], - } + }, ); // 为面板生成唯一ID @@ -136,36 +144,66 @@ export async function showICHelperPanel( panel.iconPath = vscode.Uri.joinPath( context.extensionUri, "media", - "icon.png" + "icon.png", ); // 获取页面内图标URI const iconUri = panel.webview.asWebviewUri( - vscode.Uri.joinPath(context.extensionUri, "media", "icon.png") + vscode.Uri.joinPath(context.extensionUri, "media", "icon.png"), ); // 获取模型图标URI const autoIconUri = panel.webview.asWebviewUri( - vscode.Uri.joinPath(context.extensionUri, "src", "assets", "model", "Auto.png") + vscode.Uri.joinPath( + context.extensionUri, + "src", + "assets", + "model", + "Auto.png", + ), ); const liteIconUri = panel.webview.asWebviewUri( - vscode.Uri.joinPath(context.extensionUri, "src", "assets", "model", "lite.png") + vscode.Uri.joinPath( + context.extensionUri, + "src", + "assets", + "model", + "lite.png", + ), ); const syIconUri = panel.webview.asWebviewUri( - vscode.Uri.joinPath(context.extensionUri, "src", "assets", "model", "Sy.png") + vscode.Uri.joinPath( + context.extensionUri, + "src", + "assets", + "model", + "Sy.png", + ), ); const maxIconUri = panel.webview.asWebviewUri( - vscode.Uri.joinPath(context.extensionUri, "src", "assets", "model", "Max.png") + vscode.Uri.joinPath( + context.extensionUri, + "src", + "assets", + "model", + "Max.png", + ), ); // 获取二维码图片URI const qrCodeUri = panel.webview.asWebviewUri( - vscode.Uri.joinPath(context.extensionUri, "src", "assets", "QRCode", "wx.png") + vscode.Uri.joinPath( + context.extensionUri, + "src", + "assets", + "QRCode", + "wx.png", + ), ); // 获取Logo URI const logoUri = panel.webview.asWebviewUri( - vscode.Uri.joinPath(context.extensionUri, "media", "homepage-logo.png") + vscode.Uri.joinPath(context.extensionUri, "media", "homepage-logo.png"), ); // 设置HTML内容 @@ -176,7 +214,7 @@ export async function showICHelperPanel( syIconUri.toString(), maxIconUri.toString(), qrCodeUri.toString(), - logoUri.toString() + logoUri.toString(), ); // 获取并发送用户信息到 webview @@ -186,21 +224,25 @@ export async function showICHelperPanel( if (userInfo) { // 使用缓存的用户信息 - console.log('[ICHelperPanel] 使用缓存的用户信息:', userInfo); - console.log('[ICHelperPanel] Credits 余额:', userInfo.credits); - const tierIconUrl = getTierIconUri(panel.webview, context, userInfo.membership?.tierCode); + console.log("[ICHelperPanel] 使用缓存的用户信息:", userInfo); + console.log("[ICHelperPanel] Credits 余额:", userInfo.credits); + const tierIconUrl = getTierIconUri( + panel.webview, + context, + userInfo.membership?.tierCode, + ); const messageData = { - command: 'updateUserInfo', + command: "updateUserInfo", userInfo: { userId: userInfo.userId, nickname: userInfo.nickname, username: userInfo.username, credits: userInfo.credits, - membership: userInfo.membership + membership: userInfo.membership, }, - tierIconUrl: tierIconUrl + tierIconUrl: tierIconUrl, }; - console.log('[ICHelperPanel] 发送用户信息到前端:', messageData); + console.log("[ICHelperPanel] 发送用户信息到前端:", messageData); panel.webview.postMessage(messageData); } else { // 如果没有缓存,从 session 中获取 @@ -208,19 +250,22 @@ export async function showICHelperPanel( createIfNone: false, }); if (session) { - console.log('[ICHelperPanel] 从 session 获取用户信息, account:', session.account); + console.log( + "[ICHelperPanel] 从 session 获取用户信息, account:", + session.account, + ); panel.webview.postMessage({ - command: 'updateUserInfo', + command: "updateUserInfo", userInfo: { userId: session.account.id, nickname: session.account.label, - username: session.account.label - } + username: session.account.label, + }, }); } } } catch (error) { - console.error('[ICHelperPanel] 获取用户信息失败:', error); + console.error("[ICHelperPanel] 获取用户信息失败:", error); } // 设置余额更新回调 @@ -228,36 +273,40 @@ export async function showICHelperPanel( const userInfo = getCachedUserInfo(); if (userInfo) { userInfo.credits = balance; - const tierIconUrl = getTierIconUri(panel.webview, context, userInfo.membership?.tierCode); + const tierIconUrl = getTierIconUri( + panel.webview, + context, + userInfo.membership?.tierCode, + ); panel.webview.postMessage({ - command: 'updateUserInfo', + command: "updateUserInfo", userInfo: { userId: userInfo.userId, nickname: userInfo.nickname, username: userInfo.username, credits: balance, - membership: userInfo.membership + membership: userInfo.membership, }, - tierIconUrl: tierIconUrl + tierIconUrl: tierIconUrl, }); } }); // 检查是否有待发送的消息 - const pendingMessage = context.globalState.get('pendingMessage') as any; + const pendingMessage = context.globalState.get("pendingMessage") as any; if (pendingMessage) { - console.log('[ICHelperPanel] 检测到待发送消息,准备自动发送'); + console.log("[ICHelperPanel] 检测到待发送消息,准备自动发送"); // 清除待发送消息 - await context.globalState.update('pendingMessage', undefined); + await context.globalState.update("pendingMessage", undefined); // 延迟发送,确保面板已完全初始化 setTimeout(() => { panel.webview.postMessage({ - command: 'autoSendMessage', + command: "autoSendMessage", text: pendingMessage.text, mode: pendingMessage.mode, - serviceTier: pendingMessage.serviceTier + serviceTier: pendingMessage.serviceTier, }); }, 500); } @@ -279,12 +328,12 @@ export async function showICHelperPanel( try { const taskMeta = await historyManager.createTask( workspacePath, - "新对话" + "新对话", ); historyManager.setPanelTask( panelId, taskMeta.taskId, - workspacePath + workspacePath, ); } catch (error) { console.error("创建任务失败:", error); @@ -296,14 +345,15 @@ export async function showICHelperPanel( historyManager.switchToPanelTask(panelId); // 显示进度条 - panel.webview.postMessage({ type: 'showProgress' }); + panel.webview.postMessage({ type: "showProgress" }); handleUserMessage( panel, message.text, context.extensionPath, message.mode, - message.model // 传递服务等级 + message.model, // 传递服务等级 + message.contextItems, // 传递上下文项 ); break; case "readFile": @@ -320,7 +370,7 @@ export async function showICHelperPanel( panel, message.filePath, message.searchText, - message.replaceText + message.replaceText, ); break; case "insertCode": @@ -332,7 +382,10 @@ export async function showICHelperPanel( case "openWaveformViewer": // 在新列中打开波形查看器 if (message.vcdFilePath) { - vscode.commands.executeCommand('ic-coder.openVCDViewer', message.vcdFilePath); + vscode.commands.executeCommand( + "ic-coder.openVCDViewer", + message.vcdFilePath, + ); } break; case "getVCDInfo": @@ -350,7 +403,7 @@ export async function showICHelperPanel( loadConversationHistory( panel, message.offset || 0, - message.limit || 10 + message.limit || 10, ); break; case "selectConversation": @@ -359,7 +412,7 @@ export async function showICHelperPanel( selectConversation( panel, message.conversationId, - context.extensionPath + context.extensionPath, ); } break; @@ -368,7 +421,7 @@ export async function showICHelperPanel( void handleUserAnswer( message.askId, message.selected, - message.customInput + message.customInput, ); break; // 新增:中止对话 @@ -432,18 +485,20 @@ export async function showICHelperPanel( if (userInfo?.isPluginTrial === true) { // 试用用户,跳过邀请码验证,直接返回已验证 - console.log('[ICHelperPanel] 试用用户,跳过邀请码验证'); + console.log("[ICHelperPanel] 试用用户,跳过邀请码验证"); panel.webview.postMessage({ command: "invitationCodeStatus", - verified: true + verified: true, }); } else { // 正式用户,检查邀请码 - const { InvitationService } = require("../services/invitationService"); + const { + InvitationService, + } = require("../services/invitationService"); const isVerified = await InvitationService.isVerified(context); panel.webview.postMessage({ command: "invitationCodeStatus", - verified: isVerified + verified: isVerified, }); } } @@ -451,49 +506,63 @@ export async function showICHelperPanel( case "checkWelcomeModal": // 检查是否需要显示欢迎弹窗 { - console.log('[ICHelperPanel] 收到 checkWelcomeModal 消息'); - const showWelcome = context.globalState.get('showWelcomeModal'); - console.log('[ICHelperPanel] showWelcomeModal 标记值:', showWelcome); + console.log("[ICHelperPanel] 收到 checkWelcomeModal 消息"); + const showWelcome = context.globalState.get("showWelcomeModal"); + console.log( + "[ICHelperPanel] showWelcomeModal 标记值:", + showWelcome, + ); if (showWelcome) { // 清除标记并显示欢迎弹窗 - await context.globalState.update('showWelcomeModal', undefined); - console.log('[ICHelperPanel] ✅ 发送 showWelcomeModal 命令到前端'); + await context.globalState.update("showWelcomeModal", undefined); + console.log( + "[ICHelperPanel] ✅ 发送 showWelcomeModal 命令到前端", + ); panel.webview.postMessage({ - command: "showWelcomeModal" + command: "showWelcomeModal", }); } else { - console.log('[ICHelperPanel] showWelcomeModal 标记为 false,不显示弹窗'); + console.log( + "[ICHelperPanel] showWelcomeModal 标记为 false,不显示弹窗", + ); } } break; case "checkTrialExpiration": // 检查试用期是否过期 { - console.log('[ICHelperPanel] 收到 checkTrialExpiration 消息'); - const { TrialExpirationService } = require("../services/trialExpirationService"); + console.log("[ICHelperPanel] 收到 checkTrialExpiration 消息"); + const { + TrialExpirationService, + } = require("../services/trialExpirationService"); const trialService = new TrialExpirationService(context, panel); const isExpired = await trialService.checkExpiration(); - console.log('[ICHelperPanel] 试用期过期状态:', isExpired); + console.log("[ICHelperPanel] 试用期过期状态:", isExpired); } break; case "verifyInvitationCode": // 验证邀请码 { - const { InvitationService } = require("../services/invitationService"); + const { + InvitationService, + } = require("../services/invitationService"); const result = await InvitationService.verifyCode(message.code); if (result.success) { // 验证成功,保存状态 - await InvitationService.saveVerificationStatus(context, message.code); + await InvitationService.saveVerificationStatus( + context, + message.code, + ); panel.webview.postMessage({ command: "invitationCodeVerified", - success: true + success: true, }); // 延迟显示欢迎弹窗,确保邀请码弹窗已关闭 setTimeout(() => { panel.webview.postMessage({ - command: "showNdtWelcomeModal" + command: "showNdtWelcomeModal", }); }, 300); } else { @@ -501,7 +570,7 @@ export async function showICHelperPanel( panel.webview.postMessage({ command: "invitationCodeVerified", success: false, - message: result.message + message: result.message, }); } } @@ -512,7 +581,11 @@ export async function showICHelperPanel( break; case "openTutorial": // 打开使用教程 - vscode.env.openExternal(vscode.Uri.parse("https://www.iccoder.com/tutorial")); + vscode.env.openExternal( + vscode.Uri.parse( + "https://www.iccoder.com/guides/quick-start/first-task-plugin", + ), + ); break; case "openUserManual": // 打开用户手册 @@ -521,7 +594,7 @@ export async function showICHelperPanel( case "openUserFeedback": // 打开用户反馈二维码弹窗 panel.webview.postMessage({ - command: "showFeedbackQRCode" + command: "showFeedbackQRCode", }); break; // 处理计划操作(只做模式切换,响应已通过 submitAnswer 发送) @@ -533,13 +606,16 @@ export async function showICHelperPanel( mode: "agent", }); // 注意:不再设置待执行计划;后端 LLM 会在同一对话中自动执行计划 - } else if (message.action === "modify" || message.action === "cancel") { + } else if ( + message.action === "modify" || + message.action === "cancel" + ) { void handlePlanAction( panel, message.action, message.planTitle || "", context.extensionPath, - message.model + message.model, ); } break; @@ -555,7 +631,7 @@ export async function showICHelperPanel( // 获取工作区所有文件 const files = await vscode.workspace.findFiles( "**/*", - "**/node_modules/**" + "**/node_modules/**", ); panel.webview.postMessage({ @@ -585,7 +661,11 @@ export async function showICHelperPanel( try { const items = fs.readdirSync(dir, { withFileTypes: true }); for (const item of items) { - if (item.isDirectory() && item.name !== "node_modules" && !item.name.startsWith(".")) { + if ( + item.isDirectory() && + item.name !== "node_modules" && + !item.name.startsWith(".") + ) { const fullPath = path.join(dir, item.name); const relativePath = path.relative(baseDir, fullPath); folders.push({ path: fullPath, relativePath }); @@ -614,7 +694,7 @@ export async function showICHelperPanel( canSelectMany: true, openLabel: "选择图片", filters: { - "图片文件": ["png", "jpg", "jpeg", "gif", "bmp", "svg", "webp"], + 图片文件: ["png", "jpg", "jpeg", "gif", "bmp", "svg", "webp"], }, }); if (imageUris && imageUris.length > 0) { @@ -634,8 +714,8 @@ export async function showICHelperPanel( canSelectMany: true, openLabel: "选择文档", filters: { - "文档文件": ["pdf", "doc", "docx", "txt", "md"], - "所有文件": ["*"], + 文档文件: ["pdf", "doc", "docx", "txt", "md"], + 所有文件: ["*"], }, }); if (docUris && docUris.length > 0) { @@ -657,7 +737,7 @@ export async function showICHelperPanel( vscode.window .showWarningMessage( "请先打开一个文件夹作为工作区,这样我就能更好地为您服务了 😊", - "打开文件夹" + "打开文件夹", ) .then((selection) => { if (selection === "打开文件夹") { @@ -679,16 +759,16 @@ export async function showICHelperPanel( break; case "openICCoder": // 打开 IC Coder 官网 - vscode.env.openExternal(vscode.Uri.parse('https://www.iccoder.com')); + vscode.env.openExternal(vscode.Uri.parse("https://www.iccoder.com")); break; case "logout": // 退出登录(前端已有确认对话框) - vscode.commands.executeCommand('ic-coder.logout'); + vscode.commands.executeCommand("ic-coder.logout"); break; } }, undefined, - context.subscriptions + context.subscriptions, ); // 面板关闭时清理任务映射 @@ -699,7 +779,7 @@ export async function showICHelperPanel( historyManager.removePanelTask(panelId); }, undefined, - context.subscriptions + context.subscriptions, ); } @@ -709,7 +789,7 @@ export async function showICHelperPanel( async function getVCDFileInfo( panel: vscode.WebviewPanel, vcdFilePath: string, - containerId: string + containerId: string, ) { try { const fs = require("fs"); @@ -847,7 +927,7 @@ function parseVCDSignals(content: string, maxSignals: number = 3) { if (signalDef.width === 1) { // 单比特信号 const singleBitMatch = trimmedLine.match( - new RegExp(`^([01xz])${signalDef.identifier}$`) + new RegExp(`^([01xz])${signalDef.identifier}$`), ); if (singleBitMatch) { values.push({ time: currentTime, value: singleBitMatch[1] }); @@ -855,7 +935,7 @@ function parseVCDSignals(content: string, maxSignals: number = 3) { } else { // 多比特信号 const multiBitMatch = trimmedLine.match( - new RegExp(`^b([01xz]+)\\s+${signalDef.identifier}$`) + new RegExp(`^b([01xz]+)\\s+${signalDef.identifier}$`), ); if (multiBitMatch) { values.push({ time: currentTime, value: multiBitMatch[1] }); @@ -888,7 +968,7 @@ function parseVCDSignals(content: string, maxSignals: number = 3) { async function loadConversationHistory( panel: vscode.WebviewPanel, offset: number = 0, - limit: number = 10 + limit: number = 10, ) { try { const historyManager = ChatHistoryManager.getInstance(); @@ -909,7 +989,7 @@ async function loadConversationHistory( const result = await historyManager.getConversationHistoryList( workspacePath, offset, - limit + limit, ); // 发送会话历史到前端 @@ -937,7 +1017,7 @@ async function loadConversationHistory( async function selectConversation( panel: vscode.WebviewPanel, taskId: string, - extensionPath: string + extensionPath: string, ) { try { const historyManager = ChatHistoryManager.getInstance(); @@ -951,12 +1031,12 @@ async function selectConversation( // 加载任务会话 const taskSession = await historyManager.loadTaskSession( workspacePath, - taskId + taskId, ); if (!taskSession) { vscode.window.showErrorMessage( - `加载任务 ${taskId} 失败: 任务不存在或数据损坏` + `加载任务 ${taskId} 失败: 任务不存在或数据损坏`, ); return; } @@ -1117,7 +1197,7 @@ async function selectConversation( } vscode.window.showInformationMessage( - `已加载会话: ${taskSession.meta.taskName}` + `已加载会话: ${taskSession.meta.taskName}`, ); } catch (error) { console.error("选择会话失败:", error); diff --git a/src/views/messageArea.ts b/src/views/messageArea.ts index 103aa58..a8f2bfa 100644 --- a/src/views/messageArea.ts +++ b/src/views/messageArea.ts @@ -544,9 +544,9 @@ export function getMessageAreaStyles(): string { max-height: 0; } .tool-segment-description { - margin: 2px 0 0 0px; + margin: 6px 0 0 0px; font-size: 12px; - color: #fff; + color: #ccc; line-height: 1.4; } /* 低调显示的工具调用样式 */ diff --git a/src/views/ndtWelcomeModal.ts b/src/views/ndtWelcomeModal.ts index 99f67a8..17b11d6 100644 --- a/src/views/ndtWelcomeModal.ts +++ b/src/views/ndtWelcomeModal.ts @@ -18,20 +18,19 @@ export function getNdtWelcomeModalContent(logoUri?: string): string {
🎉
-

欢迎宁德时代新能源科技股份有限公司的各位专家使用 IC Coder!

-

感谢您选择 IC Coder 作为您的芯片设计助手

+

欢迎宁德时代新能源科技股份有限公司的各位专家使用 IC Coder!

- 您已获得 5 天试用期,试用期内可无限制使用所有功能 + 您已获得 5 天企业版试用期,企业版试用期内Credits用量无限,并可无限制使用所有功能

关于 IC Coder

-

IC Coder是一款The Agentic AI Verilog Coding Platform(自主式人工智能 Verilog 编码平台)。我们采用全球顶尖的大语言模型,加上自研的针对芯片设计领域深度优化的微调模型,为代码生成提供强大的AI能力支撑。

+

IC Coder是一款The Agentic AI Verilog Coding Platform(自主式人工智能 Verilog 编码平台)。我们采用全球顶尖的IC Coder自研芯片设计微调模型,为代码生成提供强大的AI能力支撑。

@@ -41,7 +40,7 @@ export function getNdtWelcomeModalContent(logoUri?: string): string { 增强上下文引擎:智能理解和管理大规模设计上下文,确保生成代码的一致性和准确性
- 自研EDA工具集:完整的仿真、综合、时序分析工具链,无缝集成到AI工作流中 + AI自主仿真:IC Coder提供完全自动化的仿真验证流程,无需手动编写测试代码