diff --git a/src/config/settings.ts b/src/config/settings.ts index f357e5a..769ba9c 100644 --- a/src/config/settings.ts +++ b/src/config/settings.ts @@ -31,28 +31,28 @@ export interface IccoderConfig { /** 环境配置 */ const ENV_CONFIG: Record = { - /** 本地开发环境 */ + /** 本地开发环境 - 通过 Gateway 路由 */ dev: { - backendUrl: "http://localhost:2233", - backendUrlStrongeLoop: "http://192.168.1.108:2029", + backendUrl: "http://localhost:8080/iccoder", + backendUrlStrongeLoop: "http://localhost:8080", loginUrl: "http://localhost/login", timeout: 300000, userId: "default-user", serviceTier: "max", // 默认使用 max }, - /** 测试服务器环境 */ + /** 测试服务器环境 - 通过 Gateway 路由 */ test: { - backendUrl: "http://192.168.1.108:2233", + backendUrl: "http://192.168.1.108:2029/iccoder", backendUrlStrongeLoop: "http://192.168.1.108:2029", loginUrl: "http://192.168.1.108:2005/login", timeout: 60000, userId: "default-user", serviceTier: "max", }, - /** 生产环境 */ + /** 生产环境 - 通过 Gateway 路由 */ prod: { - backendUrl: "https://api.iccoder.com", - backendUrlStrongeLoop: "http://api.iccoder.com:2029", + backendUrl: "https://api.iccoder.com/iccoder", + backendUrlStrongeLoop: "https://api.iccoder.com", loginUrl: "https://iccoder.com/login", timeout: 60000, userId: "default-user", diff --git a/src/constants/toolIcons.ts b/src/constants/toolIcons.ts index f551a16..dc24f97 100644 --- a/src/constants/toolIcons.ts +++ b/src/constants/toolIcons.ts @@ -180,3 +180,8 @@ export const userQuestionIconSvg = ``; + +/** + * 更新阶段图标 SVG + */ +export const updateStageIconSvg = ``; diff --git a/src/extension.ts b/src/extension.ts index 935026c..e6c5f74 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,6 +6,7 @@ import { ChatHistoryManager } from "./utils/chatHistoryManager"; import { ICCoderAuthenticationProvider } from "./services/icCoderAuthProvider"; import { VCDFileServer } from "./services/vcdFileServer"; import { initUserService } from "./services/userService"; +import { initCreditsService } from "./services/creditsService"; export function activate(context: vscode.ExtensionContext) { console.log("🎉 IC Coder 插件已激活!"); @@ -13,6 +14,9 @@ export function activate(context: vscode.ExtensionContext) { // 初始化用户服务 initUserService(context); + // 初始化 Credits 服务 + initCreditsService(context); + // 初始化 VCD 文件服务器 const vcdFileServer = new VCDFileServer(context.extensionUri); vcdFileServer.start().then((port) => { @@ -128,15 +132,18 @@ export function activate(context: vscode.ExtensionContext) { "ic-coder.login", async () => { try { - // 检查是否有现有 session - const existingSession = await vscode.authentication.getSession("iccoder", [], { createIfNone: false }); - if (existingSession) { - // 有旧 session,使用 forceNewSession 强制创建新 session - await vscode.authentication.getSession("iccoder", [], { forceNewSession: true }); - } else { - // 没有旧 session,使用 createIfNone 创建新 session - await vscode.authentication.getSession("iccoder", [], { createIfNone: true }); + // 先清除 session 偏好,避免 VSCode 弹出"账户不一致"确认框 + try { + await vscode.authentication.getSession("iccoder", [], { + clearSessionPreference: true, + createIfNone: false + }); + } catch { + // 忽略错误 } + + // 创建新 session + await vscode.authentication.getSession("iccoder", [], { createIfNone: true }); } catch (error) { vscode.window.showErrorMessage(`登录失败: ${error}`); } diff --git a/src/panels/ICHelperPanel.ts b/src/panels/ICHelperPanel.ts index c8f2007..3a239d9 100644 --- a/src/panels/ICHelperPanel.ts +++ b/src/panels/ICHelperPanel.ts @@ -147,16 +147,20 @@ 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); - panel.webview.postMessage({ + const messageData = { command: 'updateUserInfo', userInfo: { userId: userInfo.userId, nickname: userInfo.nickname, - username: userInfo.username + username: userInfo.username, + credits: userInfo.credits }, tierIconUrl: tierIconUrl - }); + }; + console.log('[ICHelperPanel] 发送用户信息到前端:', messageData); + panel.webview.postMessage(messageData); } else { // 如果没有缓存,从 session 中获取 const session = await vscode.authentication.getSession("iccoder", [], { diff --git a/src/services/creditsService.ts b/src/services/creditsService.ts index 09a3a9f..475d379 100644 --- a/src/services/creditsService.ts +++ b/src/services/creditsService.ts @@ -22,6 +22,33 @@ let lastUpdateTime: number = 0; /** 缓存有效期(5分钟) */ const CACHE_TTL_MS = 5 * 60 * 1000; +/** ExtensionContext 用于持久化存储 */ +let extensionContext: vscode.ExtensionContext | null = null; + +/** + * 初始化 Credits 服务(设置 context) + */ +export function initCreditsService(context: vscode.ExtensionContext): void { + extensionContext = context; + // 从持久化存储加载余额 + const savedBalance = extensionContext.globalState.get('icCoderCreditsBalance'); + if (savedBalance !== undefined) { + cachedBalance = savedBalance; + lastUpdateTime = Date.now(); + console.log('[CreditsService] 从持久化存储加载余额:', savedBalance); + } +} + +/** + * 保存余额到持久化存储 + */ +async function saveBalance(balance: number): Promise { + if (extensionContext) { + await extensionContext.globalState.update('icCoderCreditsBalance', balance); + console.log('[CreditsService] 余额已保存到持久化存储:', balance); + } +} + /** * 更新缓存的余额(从 SSE credit_update 事件调用) */ @@ -29,6 +56,10 @@ export function updateCachedBalance(balance: number): void { cachedBalance = balance; lastUpdateTime = Date.now(); console.log('[CreditsService] 余额已更新:', balance); + // 异步保存到持久化存储 + saveBalance(balance).catch(err => { + console.error('[CreditsService] 保存余额失败:', err); + }); } /** @@ -203,8 +234,11 @@ export async function checkBalanceBeforeSend(): Promise<{ /** * 清除缓存(登出时调用) */ -export function clearBalanceCache(): void { +export async function clearBalanceCache(): Promise { cachedBalance = null; lastUpdateTime = 0; + if (extensionContext) { + await extensionContext.globalState.update('icCoderCreditsBalance', undefined); + } console.log('[CreditsService] 余额缓存已清除'); } diff --git a/src/services/sseHandler.ts b/src/services/sseHandler.ts index 1858965..e5828eb 100644 --- a/src/services/sseHandler.ts +++ b/src/services/sseHandler.ts @@ -238,6 +238,25 @@ export async function startStreamDialog( res.on('data', (chunk: string) => { if (!controller.aborted) { console.log('[SSE] 收到原始数据块:', chunk.substring(0, 200)); + + // 检查是否是业务错误码(Gateway 返回 HTTP 200 但响应体是错误 JSON) + try { + const trimmed = chunk.trim(); + if (trimmed.startsWith('{') && trimmed.includes('"code"')) { + const json = JSON.parse(trimmed); + if (json.code === 401 || json.msg?.includes('登录状态已过期')) { + console.log('[SSE] 检测到登录过期业务错误'); + const error = new Error('LOGIN_EXPIRED:登录状态已过期,请重新登录'); + callbacks.onError?.({ message: error.message }); + controller.abort(); + reject(error); + return; + } + } + } catch { + // 不是 JSON 格式,继续正常处理 + } + parser.feed(chunk); } }); diff --git a/src/services/userService.ts b/src/services/userService.ts index c02a2e6..e91d641 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -8,6 +8,7 @@ import { URL } from 'url'; import * as vscode from 'vscode'; import { getStrangeLoopApiUrl, getConfig } from '../config/settings'; import type { UserInfoResponse, MembershipResponse, MultiMembershipVO, MembershipItemVO } from '../types/api'; +import { fetchBalance, getCachedBalance } from './creditsService'; /** * HTTP 请求选项 @@ -114,6 +115,8 @@ export interface UserInfo { remainingDays?: number; monthlyCredits?: number; }; + // Credits 余额 + credits?: number; } /** @@ -221,12 +224,13 @@ function getHighestTierMembership(allMemberships?: MembershipItemVO[]): Membersh */ export async function onTokenReceived(token: string): Promise { try { - console.log('[UserService] Token 已获取,正在获取用户信息和会员信息...'); + console.log('[UserService] Token 已获取,正在获取用户信息、会员信息和余额...'); - // 并行获取用户信息和会员信息 - const [userInfo, membershipInfo] = await Promise.all([ + // 并行获取用户信息、会员信息和余额 + const [userInfo, membershipInfo, credits] = await Promise.all([ getUserInfo(token), - getMembershipInfo(token) + getMembershipInfo(token), + fetchBalance() ]); if (!userInfo) { @@ -234,6 +238,15 @@ export async function onTokenReceived(token: string): Promise { return null; } + // 添加 Credits 余额到用户信息 + console.log('[UserService] 获取到的 Credits 余额:', credits); + if (credits !== null) { + userInfo.credits = credits; + console.log('[UserService] Credits 已添加到用户信息'); + } else { + console.warn('[UserService] Credits 余额为 null,未添加到用户信息'); + } + // 打印用户信息到控制台 console.log('='.repeat(60)); console.log('用户信息详情:'); @@ -286,6 +299,15 @@ export async function onTokenReceived(token: string): Promise { } } + // 打印 Credits 余额 + console.log(''); + console.log('资源点余额:'); + if (userInfo.credits !== undefined) { + console.log(`当前余额: ${userInfo.credits} Credits`); + } else { + console.log('当前余额: 未获取到余额信息'); + } + console.log('='.repeat(60)); // 保存到持久化存储 @@ -329,7 +351,18 @@ export function getCachedUserInfo(): UserInfo | null { console.warn('[UserService] ExtensionContext 未初始化'); return null; } - return extensionContext.globalState.get('icCoderUserInfo') || null; + const userInfo = extensionContext.globalState.get('icCoderUserInfo') || null; + + // 从 creditsService 加载余额并合并到用户信息中 + if (userInfo) { + const cachedCredits = getCachedBalance(); + if (cachedCredits !== null) { + userInfo.credits = cachedCredits; + console.log('[UserService] 从 creditsService 加载余额:', cachedCredits); + } + } + + return userInfo; } /** diff --git a/src/utils/messageHandler.ts b/src/utils/messageHandler.ts index b0ee6b1..adfc75c 100644 --- a/src/utils/messageHandler.ts +++ b/src/utils/messageHandler.ts @@ -18,7 +18,10 @@ import { ChatHistoryManager } from "./chatHistoryManager"; import { dialogManager, DialogSession } from "../services/dialogService"; import { userInteractionManager } from "../services/userInteraction"; import { healthCheck } from "../services/apiClient"; -import { checkBalanceBeforeSend } from "../services/creditsService"; +import { + checkBalanceBeforeSend, + fetchBalance, +} from "../services/creditsService"; import type { RunMode, ServiceTier } from "../types/api"; @@ -39,7 +42,7 @@ export async function handleUserMessage( text: string, extensionPath?: string, mode?: RunMode, - serviceTier?: ServiceTier // 新增:服务等级参数 + serviceTier?: ServiceTier // 新增:服务等级参数 ) { console.log("收到用户消息:", text); @@ -78,7 +81,9 @@ export async function handleUserMessage( "去充值" ); if (selection === "去充值") { - vscode.env.openExternal(vscode.Uri.parse("https://iccoder.com/recharge")); + vscode.env.openExternal( + vscode.Uri.parse("https://iccoder.com/memberCenter") + ); } // 恢复输入状态 panel.webview.postMessage({ @@ -92,7 +97,14 @@ export async function handleUserMessage( // 尝试使用后端服务 if (useBackendService && extensionPath) { try { - await handleUserMessageWithBackend(panel, text, extensionPath, mode, undefined, serviceTier); + await handleUserMessageWithBackend( + panel, + text, + extensionPath, + mode, + undefined, + serviceTier + ); return; } catch (error) { console.error("后端服务不可用:", error); @@ -128,7 +140,7 @@ async function handleUserMessageWithBackend( extensionPath: string, mode?: RunMode, reuseTaskId?: string, // 可选,复用现有 taskId(用于 Plan 模式确认后继续执行) - serviceTier?: ServiceTier // 新增:服务等级参数 + serviceTier?: ServiceTier // 新增:服务等级参数 ): Promise { const historyManager = ChatHistoryManager.getInstance(); @@ -137,10 +149,18 @@ async function handleUserMessageWithBackend( const taskIdToUse = reuseTaskId || historyManager.getCurrentTaskId(); // 创建会话(dialogManager 会自动处理旧会话的中止) - currentSession = dialogManager.createSession(extensionPath, taskIdToUse || undefined); + currentSession = dialogManager.createSession( + extensionPath, + taskIdToUse || undefined + ); // 保存 taskId 用于后续操作(如压缩) lastTaskId = currentSession.getTaskId(); - console.log("[MessageHandler] 创建会话: taskId=", lastTaskId, "来源=", taskIdToUse ? "historyManager" : "新生成"); + console.log( + "[MessageHandler] 创建会话: taskId=", + lastTaskId, + "来源=", + taskIdToUse ? "historyManager" : "新生成" + ); // 显示状态栏 panel.webview.postMessage({ @@ -200,6 +220,17 @@ async function handleUserMessageWithBackend( // 最后一次发送完整的段落 console.log("[MessageHandler] 对话完成, 段落数:", segments.length); + // 对话完成后重新获取余额(因为已经消耗了 Credits) + try { + console.log("[MessageHandler] 对话完成,重新获取余额..."); + const newBalance = await fetchBalance(); + if (newBalance !== null) { + console.log("[MessageHandler] 余额已更新:", newBalance); + } + } catch (error) { + console.error("[MessageHandler] 获取余额失败:", error); + } + const result = await panel.webview.postMessage({ command: "updateSegments", segments: segments, @@ -286,7 +317,7 @@ async function handleUserMessageWithBackend( }, }, mode, - serviceTier // 传递服务等级 + serviceTier // 传递服务等级 ); }); } @@ -369,7 +400,14 @@ export async function handlePlanAction( extensionPath: string, serviceTier?: ServiceTier ): Promise { - console.log("[handlePlanAction] action:", action, "planTitle:", planTitle, "serviceTier:", serviceTier); + console.log( + "[handlePlanAction] action:", + action, + "planTitle:", + planTitle, + "serviceTier:", + serviceTier + ); switch (action) { case "confirm": diff --git a/src/views/ICViewProvider.ts b/src/views/ICViewProvider.ts index c028a2a..02eb3b6 100644 --- a/src/views/ICViewProvider.ts +++ b/src/views/ICViewProvider.ts @@ -128,10 +128,34 @@ export function showICHelperPanel(context: vscode.ExtensionContext) { * 侧边栏视图提供者 */ export class ICViewProvider implements vscode.WebviewViewProvider { + private _view?: vscode.WebviewView; + constructor( private readonly extensionUri: vscode.Uri, private readonly context: vscode.ExtensionContext - ) {} + ) { + // 监听认证状态变化 + this.context.subscriptions.push( + vscode.authentication.onDidChangeSessions((e) => { + if (e.provider.id === "iccoder") { + this.refreshLoginStatus(); + } + }) + ); + } + + /** + * 刷新登录状态并更新视图 + */ + private async refreshLoginStatus(): Promise { + if (this._view) { + const isLoggedIn = await this.checkLoginStatus(); + this._view.webview.html = this.getWebviewContent( + this._view.webview, + isLoggedIn + ); + } + } /** * 检查登录状态(使用 Authentication API) @@ -139,24 +163,29 @@ export class ICViewProvider implements vscode.WebviewViewProvider { private async checkLoginStatus(): Promise { try { const session = await vscode.authentication.getSession("iccoder", [], { createIfNone: false }); + console.log("[ICViewProvider] 检查登录状态, session:", session ? "存在" : "不存在"); if (!session) { return false; } // 检查 token 是否过期 const expired = isTokenExpired(session.accessToken); - // 如果已过期或无法判断(null),都认为未登录 - if (expired === true || expired === null) { - console.log("Token 已过期或无法判断过期状态"); + console.log("[ICViewProvider] token 过期检查结果:", expired); + // 只有明确过期才认为未登录,无法判断时认为已登录 + if (expired === true) { + console.log("[ICViewProvider] Token 已过期"); return false; } return true; } catch (error) { - console.log("检查登录状态失败:", error); + console.log("[ICViewProvider] 检查登录状态失败:", error); return false; } } resolveWebviewView(webviewView: vscode.WebviewView) { + // 保存引用以便后续刷新 + this._view = webviewView; + webviewView.webview.options = { enableScripts: true, localResourceRoots: [vscode.Uri.joinPath(this.extensionUri, "media")], diff --git a/src/views/contextButton.ts b/src/views/contextButton.ts index 621069b..20bb559 100644 --- a/src/views/contextButton.ts +++ b/src/views/contextButton.ts @@ -14,9 +14,7 @@ export function getContextButtonContent(): string { 添加上下文 - - - + 添加文件、文件夹、图片或文档作为上下文 diff --git a/src/views/exampleShowcase.ts b/src/views/exampleShowcase.ts new file mode 100644 index 0000000..1d8ff39 --- /dev/null +++ b/src/views/exampleShowcase.ts @@ -0,0 +1,216 @@ +/** + * 获取展示区域的 HTML 内容 + */ +export function getExampleShowcaseContent(): string { + return ` +
+
展示
+
+
+
📝
+
+
代码生成
+
生成一个 8 位全加器的 Verilog 代码
+
+
+ +
+
🔍
+
+
代码分析
+
分析当前项目中的时序逻辑设计
+
+
+
+ + +
+ `; +} + +/** + * 获取展示区域的样式 + */ +export function getExampleShowcaseStyles(): string { + return ` + .example-showcase { + margin-top: 24px; + padding: 0; + opacity: 1; + transition: opacity 0.3s ease; + } + + .example-showcase.hidden { + display: none; + } + + .showcase-title { + font-size: 14px; + font-weight: 600; + color: var(--vscode-foreground); + margin-bottom: 12px; + text-align: left; + } + + .example-cards { + display: flex; + flex-direction: row; + gap: 12px; + margin-bottom: 20px; + } + + .example-card { + flex: 1; + min-width: 0; + background: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); + border-radius: 8px; + padding: 14px; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + flex-direction: row; + align-items: center; + gap: 12px; + } + + .example-card:hover { + border-color: var(--vscode-focusBorder); + background: var(--vscode-list-hoverBackground); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + } + + .example-icon { + font-size: 28px; + line-height: 1; + flex-shrink: 0; + } + + .example-content { + display: flex; + flex-direction: column; + gap: 4px; + flex: 1; + min-width: 0; + } + + .example-title { + font-size: 13px; + font-weight: 600; + color: var(--vscode-foreground); + } + + .example-desc { + font-size: 11px; + color: var(--vscode-descriptionForeground); + line-height: 1.4; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + } + + .web-link { + display: flex; + justify-content: center; + padding-top: 20px; + border-top: 1px solid var(--vscode-panel-border); + margin-top: 8px; + } + + .web-link-button { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 20px; + background: transparent; + border: none; + text-decoration: none; + font-size: 14px; + font-weight: 600; + transition: all 0.2s ease; + background: linear-gradient(135deg, #4facfe 0%, #00f2fe 50%, #a855f7 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + outline: none; + } + + .web-link-button:focus { + outline: none; + } + + .web-link-button:hover { + transform: translateY(-1px); + opacity: 0.8; + } + + .link-icon { + font-size: 16px; + } + + .link-arrow { + font-size: 16px; + transition: transform 0.2s ease; + } + + .web-link-button:hover .link-arrow { + transform: translateX(3px); + } + `; +} + +/** + * 获取展示区域的脚本 + */ +export function getExampleShowcaseScript(): string { + return ` + // 示例文本数组 + const exampleTexts = [ + '生成一个 8 位全加器的 Verilog 代码', + '分析当前项目中的时序逻辑设计' + ]; + + // 填充示例到输入框 + function fillExample(index) { + const messageInput = document.getElementById('messageInput'); + if (messageInput && exampleTexts[index]) { + messageInput.value = exampleTexts[index]; + messageInput.focus(); + // 触发自动调整高度 + if (typeof autoResizeTextarea === 'function') { + autoResizeTextarea(); + } + } + } + + // 监听消息变化,自动隐藏/显示展示区域 + function updateShowcaseVisibility() { + const showcase = document.getElementById('exampleShowcase'); + if (showcase) { + if (hasMessages) { + showcase.classList.add('hidden'); + } else { + showcase.classList.remove('hidden'); + } + } + } + + // 扩展原有的布局更新函数 + const originalUpdateInputAreaLayout = updateInputAreaLayout; + updateInputAreaLayout = function() { + if (originalUpdateInputAreaLayout) { + originalUpdateInputAreaLayout(); + } + updateShowcaseVisibility(); + }; + `; +} diff --git a/src/views/inputArea.ts b/src/views/inputArea.ts index 8aa6815..9baa1bd 100644 --- a/src/views/inputArea.ts +++ b/src/views/inputArea.ts @@ -29,16 +29,21 @@ import { getOptimizeButtonStyles, getOptimizeButtonScript, } from "./optimizeButton"; +import { + getExampleShowcaseContent, + getExampleShowcaseStyles, + getExampleShowcaseScript, +} from "./exampleShowcase"; import { sendIconSvg, stopIconSvg } from "../constants/toolIcons"; /** * 获取输入区域的 HTML 内容 */ export function getInputAreaContent( - autoIcon: string = '', - liteIcon: string = '', - syIcon: string = '', - maxIcon: string = '' + autoIcon: string = "", + liteIcon: string = "", + syIcon: string = "", + maxIcon: string = "" ): string { return `
@@ -71,6 +76,8 @@ export function getInputAreaContent(
+ + ${getExampleShowcaseContent()} `; } @@ -86,6 +93,7 @@ export function getInputAreaStyles(): string { ${getContextDisplayStyles()} ${getContextCompressStyles()} ${getOptimizeButtonStyles()} + ${getExampleShowcaseStyles()} .input-area { border-top: 1px solid var(--vscode-panel-border); padding-top: 15px; @@ -95,7 +103,7 @@ export function getInputAreaStyles(): string { /* 居中模式:未发起对话时 */ .input-area.centered { position: absolute; - top: 50%; + top: 55%; left: 50%; transform: translate(-50%, -50%); width: calc(100% - 40px); @@ -292,6 +300,7 @@ export function getInputAreaScript(): string { ${getContextDisplayScript()} ${getContextCompressScript()} ${getOptimizeButtonScript()} + ${getExampleShowcaseScript()} // 对话状态管理 let isConversationActive = false; diff --git a/src/views/messageArea.ts b/src/views/messageArea.ts index 8350e2c..698ee4f 100644 --- a/src/views/messageArea.ts +++ b/src/views/messageArea.ts @@ -24,6 +24,7 @@ import { knowledgeLoadIconSvg, stateTransitionIconSvg, userQuestionIconSvg, + updateStageIconSvg, } from "../constants/toolIcons"; import { getWaveformPreviewContent, @@ -670,6 +671,7 @@ export function getMessageAreaScript(): string { const knowledgeLoadIconSvg = \`${knowledgeLoadIconSvg}\`; const stateTransitionIconSvg = \`${stateTransitionIconSvg}\`; const userQuestionIconSvg = \`${userQuestionIconSvg}\`; + const updateStageIconSvg = \`${updateStageIconSvg}\`; ${getAgentCardScript()} @@ -724,6 +726,7 @@ export function getMessageAreaScript(): string { 'updateNode': fileWriteIconSvg, 'addStateTransition': stateTransitionIconSvg, 'askUser': userQuestionIconSvg, + 'updatePhase': updateStageIconSvg, }; return iconMap[toolName] || ''; } @@ -756,6 +759,8 @@ export function getMessageAreaScript(): string { 'spawnExplorer': '代码探索', 'spawnDebugger': '波形调试', 'askUser': '用户提问', + 'updatePhase': '已更新阶段', + 'iverilog': '已完成编译', }; return toolNameMap[toolName] || toolName; } diff --git a/src/views/planCard.ts b/src/views/planCard.ts index c83f6f4..706ec37 100644 --- a/src/views/planCard.ts +++ b/src/views/planCard.ts @@ -62,9 +62,9 @@ export function getPlanCardStyles(): string { .plan-summary p { margin: 8px 0; } .plan-summary ul, .plan-summary ol { margin: 8px 0; - padding-left: 24px; + padding-left: 0; } - .plan-summary li { margin: 4px 0; } + .plan-summary li { margin: 4px 0 4px 27px; } .plan-summary code { background: var(--vscode-textCodeBlock-background); padding: 2px 6px; @@ -403,6 +403,12 @@ export function getPlanCardScript(): string { .replace(//g, '>'); + // 标题(必须在转义之后、其他处理之前) + html = html.replace(/^#### (.+)$/gm, '

$1

'); + html = html.replace(/^### (.+)$/gm, '

$1

'); + html = html.replace(/^## (.+)$/gm, '

$1

'); + html = html.replace(/^# (.+)$/gm, '

$1

'); + // 代码块 (\`\`\`code\`\`\`) html = html.replace(/\\x60\\x60\\x60([\\s\\S]*?)\\x60\\x60\\x60/g, '
$1
'); @@ -428,12 +434,6 @@ export function getPlanCardScript(): string { return table; }); - // 标题 - html = html.replace(/^#### (.+)$/gm, '

$1

'); - html = html.replace(/^### (.+)$/gm, '

$1

'); - html = html.replace(/^## (.+)$/gm, '

$1

'); - html = html.replace(/^# (.+)$/gm, '

$1

'); - // 粗体和斜体 html = html.replace(/\\*\\*(.+?)\\*\\*/g, '$1'); html = html.replace(/\\*(.+?)\\*/g, '$1'); diff --git a/src/views/userInfoComponent.ts b/src/views/userInfoComponent.ts index 444ad59..95bb043 100644 --- a/src/views/userInfoComponent.ts +++ b/src/views/userInfoComponent.ts @@ -250,14 +250,27 @@ export function getUserInfoComponentScript(): string { // 更新剩余 Credits const creditsDetail = document.getElementById('creditsDetail'); + console.log('[UserInfoComponent] 更新 Credits 显示'); + console.log('[UserInfoComponent] currentUserInfo.credits:', currentUserInfo.credits); + console.log('[UserInfoComponent] creditsDetail 元素:', creditsDetail); if (creditsDetail) { - creditsDetail.textContent = currentUserInfo.credits !== undefined ? currentUserInfo.credits.toString() : '-'; + const creditsText = currentUserInfo.credits !== undefined ? currentUserInfo.credits.toString() : '-'; + creditsDetail.textContent = creditsText; + console.log('[UserInfoComponent] Credits 已更新为:', creditsText); + } else { + console.warn('[UserInfoComponent] creditsDetail 元素未找到'); } } // 更新用户信息显示 function updateUserInfoDisplay(userInfo) { currentUserInfo = userInfo; + console.log('[UserInfoComponent] 更新用户信息:', userInfo); + // 如果下拉面板已打开,立即更新显示 + const dropdown = document.getElementById('userDetailDropdown'); + if (dropdown && dropdown.classList.contains('active')) { + updateUserDetailModal(); + } } // 绑定下拉面板事件 diff --git a/src/views/waveformPreviewContent.ts b/src/views/waveformPreviewContent.ts index 73135ea..83568dd 100644 --- a/src/views/waveformPreviewContent.ts +++ b/src/views/waveformPreviewContent.ts @@ -174,7 +174,7 @@ export function getWaveformPreviewScript(): string { const content = document.createElement('div'); content.className = 'waveform-preview-content'; - const miniViewerId = 'waveform-mini-' + Date.now(); + const miniViewerId = 'waveform-mini-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9); const miniViewer = document.createElement('div'); miniViewer.id = miniViewerId; miniViewer.className = 'waveform-mini-viewer'; diff --git a/src/views/webviewContent.ts b/src/views/webviewContent.ts index 9cd5e20..a4d03be 100644 --- a/src/views/webviewContent.ts +++ b/src/views/webviewContent.ts @@ -588,20 +588,25 @@ export function getWebviewContent( case 'updateUserInfo': // 更新用户信息 console.log('[WebView] 收到用户信息:', message.userInfo); + console.log('[WebView] Credits 字段值:', message.userInfo?.credits); if (message.userInfo) { const userInfoData = { nickname: message.userInfo.nickname || message.userInfo.username || '用户', userId: message.userInfo.userId || message.userInfo.id, tierName: message.userInfo.tierName, tierIconUrl: message.tierIconUrl, - registerTime: message.userInfo.registerTime || message.userInfo.createdAt + registerTime: message.userInfo.registerTime || message.userInfo.createdAt, + credits: message.userInfo.credits }; console.log('[WebView] 显示用户信息:', userInfoData); + console.log('[WebView] userInfoData.credits:', userInfoData.credits); // 调用更新用户头像图标按钮的函数 if (typeof updateUserAvatarIconButton === 'function') { updateUserAvatarIconButton(userInfoData); + } else { + console.warn('[WebView] updateUserAvatarIconButton 函数不存在'); } } break;