From 9118ebd6623942cd7f9d0cc14602a82923f57731 Mon Sep 17 00:00:00 2001 From: Roe-xin Date: Wed, 4 Mar 2026 18:58:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E4=BC=81=E4=B8=9A=E6=AC=A2=E8=BF=8E?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/VSCode-Extension-API-Guide.md | 804 +++++++++++++++++++++++++++++ src/views/welcomeModal.ts | 169 ++++-- 2 files changed, 917 insertions(+), 56 deletions(-) create mode 100644 docs/VSCode-Extension-API-Guide.md diff --git a/docs/VSCode-Extension-API-Guide.md b/docs/VSCode-Extension-API-Guide.md new file mode 100644 index 0000000..636d3a4 --- /dev/null +++ b/docs/VSCode-Extension-API-Guide.md @@ -0,0 +1,804 @@ +# VS Code Extension API 核心知识点 + +## 目录 +- [1. Extension 生命周期](#1-extension-生命周期) ⭐⭐⭐ +- [2. 激活事件 (Activation Events)](#2-激活事件-activation-events) ⭐⭐ +- [3. 命令系统 (Commands)](#3-命令系统-commands) ⭐⭐ +- [4. Webview API](#4-webview-api) ⭐⭐⭐⭐⭐ **面试重点** +- [5. TreeView 和自定义视图](#5-treeview-和自定义视图) ⭐⭐ +- [6. 文件系统操作](#6-文件系统操作) ⭐⭐⭐ +- [7. 配置和存储](#7-配置和存储) ⭐⭐⭐⭐ **面试重点** +- [8. 消息通知](#8-消息通知) ⭐ +- [9. 语言特性支持](#9-语言特性支持) ⭐ +- [10. 调试和诊断](#10-调试和诊断) ⭐ + +--- + +## 1. Extension 生命周期 ⭐⭐⭐ + +### 1.1 核心函数 🔥必考 + +```typescript +// extension.ts +import * as vscode from 'vscode'; + +// 插件激活时调用(只调用一次) +export function activate(context: vscode.ExtensionContext) { + console.log('Extension is now active!'); + + // 注册命令、视图、事件监听等 + // 使用 context.subscriptions 管理资源 +} + +// 插件停用时调用(清理资源) +export function deactivate() { + console.log('Extension is deactivated'); + // 清理资源、关闭连接等 +} +``` + +### 1.2 ExtensionContext 重要属性 🔥必考 + +```typescript +interface ExtensionContext { + // 插件订阅管理(自动清理) + subscriptions: { dispose(): any }[]; + + // 工作区存储路径 + storageUri: vscode.Uri | undefined; + globalStorageUri: vscode.Uri; + + // 插件路径 + extensionUri: vscode.Uri; + extensionPath: string; + + // 状态存储 + workspaceState: Memento; // 工作区级别 + globalState: Memento; // 全局级别 + secrets: SecretStorage; // 敏感信息存储 + + // 环境变量 + environmentVariableCollection: EnvironmentVariableCollection; +} +``` + +### 1.3 资源管理最佳实践 🔥必考 + +```typescript +export function activate(context: vscode.ExtensionContext) { + // ✅ 推荐:使用 context.subscriptions 自动管理 + context.subscriptions.push( + vscode.commands.registerCommand('extension.command', () => {}) + ); + + // ❌ 不推荐:手动管理容易忘记清理 + const disposable = vscode.commands.registerCommand('extension.command', () => {}); + // 需要在 deactivate 中手动调用 disposable.dispose() +} +``` + +--- + +## 2. 激活事件 (Activation Events) ⭐⭐ + +### 2.1 常用激活事件 📌重要 + +```json +// package.json +{ + "activationEvents": [ + // 启动时激活 + "onStartupFinished", + + // 执行命令时激活 + "onCommand:extension.helloWorld", + + // 打开特定语言文件时激活 + "onLanguage:javascript", + "onLanguage:verilog", + + // 打开特定文件类型时激活 + "onFileSystem:sftp", + + // 打开特定视图时激活 + "onView:myCustomView", + + // 调试时激活 + "onDebug", + + // 打开特定 URI 时激活 + "onUri", + + // Webview 恢复时激活 + "onWebviewPanel:myWebview", + + // 任务执行时激活 + "onTaskType:npm" + ] +} +``` + +### 2.2 延迟激活策略 🔥必考 + +```typescript +// ✅ 推荐:使用 onStartupFinished 延迟激活 +"activationEvents": ["onStartupFinished"] + +// ❌ 不推荐:使用 * 会拖慢启动速度 +"activationEvents": ["*"] +``` + +--- + +## 3. 命令系统 (Commands) + +### 3.1 注册命令 + +```typescript +// 注册简单命令 +const disposable = vscode.commands.registerCommand( + 'extension.helloWorld', + () => { + vscode.window.showInformationMessage('Hello World!'); + } +); +context.subscriptions.push(disposable); + +// 注册带参数的命令 +vscode.commands.registerCommand( + 'extension.openFile', + (filePath: string) => { + vscode.workspace.openTextDocument(filePath).then(doc => { + vscode.window.showTextDocument(doc); + }); + } +); +``` + +### 3.2 执行命令 + +```typescript +// 执行内置命令 +await vscode.commands.executeCommand('workbench.action.files.save'); + +// 执行自定义命令 +await vscode.commands.executeCommand('extension.openFile', '/path/to/file'); + +// 获取所有可用命令 +const commands = await vscode.commands.getCommands(); +``` + +### 3.3 常用内置命令 + +```typescript +// 文件操作 +'workbench.action.files.save' +'workbench.action.files.saveAll' +'workbench.action.closeActiveEditor' + +// 编辑器操作 +'editor.action.formatDocument' +'editor.action.commentLine' +'editor.action.selectAll' + +// 窗口操作 +'workbench.action.toggleSidebarVisibility' +'workbench.action.terminal.new' +'workbench.action.quickOpen' + +// Git 操作 +'git.commit' +'git.push' +'git.pull' +``` + +--- + +## 4. Webview API ⭐⭐⭐⭐⭐ **面试重点** + +### 4.1 创建 Webview Panel 🔥必考 + +```typescript +const panel = vscode.window.createWebviewPanel( + 'myWebview', // viewType(唯一标识) + 'My Webview', // 标题 + vscode.ViewColumn.One, // 显示位置 + { + enableScripts: true, // 启用 JavaScript + retainContextWhenHidden: true, // 隐藏时保留状态 + localResourceRoots: [ // 允许访问的本地资源路径 + vscode.Uri.joinPath(context.extensionUri, 'media') + ] + } +); +``` + +### 4.2 设置 Webview 内容 + +```typescript +panel.webview.html = getWebviewContent(); + +function getWebviewContent() { + return ` + + + + + My Webview + + +

Hello from Webview!

+ + + + + `; +} +``` + +### 4.3 Webview 消息通信 🔥必考(项目核心) + +```typescript +// Extension → Webview +panel.webview.postMessage({ + command: 'update', + data: 'some data' +}); + +// Webview → Extension +panel.webview.onDidReceiveMessage( + message => { + switch (message.command) { + case 'alert': + vscode.window.showInformationMessage(message.text); + break; + case 'getData': + // 处理数据请求 + panel.webview.postMessage({ + command: 'dataResponse', + data: fetchData() + }); + break; + } + }, + undefined, + context.subscriptions +); +``` + +### 4.4 Webview 生命周期管理 📌重要 + +```typescript +// 监听 Webview 关闭事件 +panel.onDidDispose( + () => { + // 清理资源 + console.log('Webview disposed'); + }, + null, + context.subscriptions +); + +// 监听 Webview 可见性变化 +panel.onDidChangeViewState( + e => { + if (e.webviewPanel.visible) { + console.log('Webview is now visible'); + } + }, + null, + context.subscriptions +); +``` + +### 4.5 加载本地资源 📌重要 + +```typescript +// 获取本地资源 URI +const scriptUri = panel.webview.asWebviewUri( + vscode.Uri.joinPath(context.extensionUri, 'media', 'script.js') +); + +const styleUri = panel.webview.asWebviewUri( + vscode.Uri.joinPath(context.extensionUri, 'media', 'style.css') +); + +// 在 HTML 中使用 +const html = ` + + +`; +``` + +### 4.6 Webview 状态持久化 📌重要 + +```typescript +// Webview 中保存状态 +const vscode = acquireVsCodeApi(); +const state = vscode.getState() || { count: 0 }; + +// 更新状态 +state.count++; +vscode.setState(state); + +// Extension 中序列化状态 +panel.webview.options = { + enableScripts: true, + retainContextWhenHidden: true +}; + +// 恢复 Webview +vscode.window.registerWebviewPanelSerializer('myWebview', { + async deserializeWebviewPanel(webviewPanel, state) { + webviewPanel.webview.html = getWebviewContent(); + // 恢复状态 + webviewPanel.webview.postMessage({ command: 'restore', state }); + } +}); +``` + +--- + +## 5. TreeView 和自定义视图 + +### 5.1 创建 TreeView Provider + +```typescript +class MyTreeDataProvider implements vscode.TreeDataProvider { + private _onDidChangeTreeData = new vscode.EventEmitter(); + readonly onDidChangeTreeData = this._onDidChangeTreeData.event; + + refresh(): void { + this._onDidChangeTreeData.fire(undefined); + } + + getTreeItem(element: TreeItem): vscode.TreeItem { + return element; + } + + getChildren(element?: TreeItem): Thenable { + if (!element) { + // 返回根节点 + return Promise.resolve([ + new TreeItem('Item 1', vscode.TreeItemCollapsibleState.None), + new TreeItem('Item 2', vscode.TreeItemCollapsibleState.Collapsed) + ]); + } + // 返回子节点 + return Promise.resolve([]); + } +} + +class TreeItem extends vscode.TreeItem { + constructor( + public readonly label: string, + public readonly collapsibleState: vscode.TreeItemCollapsibleState + ) { + super(label, collapsibleState); + this.tooltip = `Tooltip for ${label}`; + this.command = { + command: 'extension.itemClicked', + title: 'Click Item', + arguments: [this] + }; + } +} +``` + +### 5.2 注册 TreeView + +```typescript +const treeDataProvider = new MyTreeDataProvider(); +const treeView = vscode.window.createTreeView('myTreeView', { + treeDataProvider, + showCollapseAll: true +}); + +context.subscriptions.push(treeView); + +// 刷新视图 +treeDataProvider.refresh(); +``` + +### 5.3 WebviewView Provider(侧边栏 Webview) + +```typescript +class MyWebviewViewProvider implements vscode.WebviewViewProvider { + resolveWebviewView( + webviewView: vscode.WebviewView, + context: vscode.WebviewViewResolveContext, + token: vscode.CancellationToken + ) { + webviewView.webview.options = { + enableScripts: true + }; + + webviewView.webview.html = getWebviewContent(); + + webviewView.webview.onDidReceiveMessage(message => { + // 处理消息 + }); + } +} + +// 注册 +vscode.window.registerWebviewViewProvider( + 'myWebviewView', + new MyWebviewViewProvider() +); +``` + +--- + +## 6. 文件系统操作 ⭐⭐⭐ + +### 6.1 读取文件 📌重要 + +```typescript +// 读取文本文件 +const uri = vscode.Uri.file('/path/to/file.txt'); +const content = await vscode.workspace.fs.readFile(uri); +const text = Buffer.from(content).toString('utf8'); + +// 使用 TextDocument API +const document = await vscode.workspace.openTextDocument(uri); +const text = document.getText(); +``` + +### 6.2 写入文件 + +```typescript +// 写入文件 +const uri = vscode.Uri.file('/path/to/file.txt'); +const content = Buffer.from('Hello World', 'utf8'); +await vscode.workspace.fs.writeFile(uri, content); + +// 使用 WorkspaceEdit +const edit = new vscode.WorkspaceEdit(); +edit.createFile(uri, { overwrite: true }); +edit.insert(uri, new vscode.Position(0, 0), 'Hello World'); +await vscode.workspace.applyEdit(edit); +``` + +### 6.3 文件监听 + +```typescript +// 监听文件变化 +const watcher = vscode.workspace.createFileSystemWatcher('**/*.js'); + +watcher.onDidCreate(uri => { + console.log('File created:', uri.fsPath); +}); + +watcher.onDidChange(uri => { + console.log('File changed:', uri.fsPath); +}); + +watcher.onDidDelete(uri => { + console.log('File deleted:', uri.fsPath); +}); + +context.subscriptions.push(watcher); +``` + +### 6.4 工作区操作 + +```typescript +// 获取工作区文件夹 +const workspaceFolders = vscode.workspace.workspaceFolders; +if (workspaceFolders) { + const rootPath = workspaceFolders[0].uri.fsPath; +} + +// 查找文件 +const files = await vscode.workspace.findFiles( + '**/*.ts', // include pattern + '**/node_modules/**' // exclude pattern +); + +// 打开文件 +const document = await vscode.workspace.openTextDocument(uri); +await vscode.window.showTextDocument(document); +``` + +--- + +## 7. 配置和存储 ⭐⭐⭐⭐ **面试重点** + +### 7.1 读取配置 📌重要 + +```typescript +// 读取配置 +const config = vscode.workspace.getConfiguration('myExtension'); +const value = config.get('settingName', 'defaultValue'); + +// 监听配置变化 +vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('myExtension.settingName')) { + console.log('Configuration changed'); + } +}); +``` + +### 7.2 更新配置 + +```typescript +const config = vscode.workspace.getConfiguration('myExtension'); + +// 更新用户配置(全局) +await config.update('settingName', 'newValue', vscode.ConfigurationTarget.Global); + +// 更新工作区配置 +await config.update('settingName', 'newValue', vscode.ConfigurationTarget.Workspace); +``` + +### 7.3 状态存储 🔥必考 + +```typescript +// 工作区状态(仅当前工作区) +await context.workspaceState.update('key', 'value'); +const value = context.workspaceState.get('key'); + +// 全局状态(跨工作区) +await context.globalState.update('key', 'value'); +const value = context.globalState.get('key'); + +// 存储对象 +await context.globalState.update('userData', { name: 'John', age: 30 }); +``` + +### 7.4 敏感信息存储 🔥必考(Token 管理) + +```typescript +// 存储密码、Token 等敏感信息 +await context.secrets.store('apiToken', 'secret-token-value'); + +// 读取 +const token = await context.secrets.get('apiToken'); + +// 删除 +await context.secrets.delete('apiToken'); + +// 监听变化 +context.secrets.onDidChange(e => { + console.log('Secret changed:', e.key); +}); +``` + +--- + +## 8. 消息通知 + +### 8.1 信息提示 + +```typescript +// 普通信息 +vscode.window.showInformationMessage('Operation completed!'); + +// 警告 +vscode.window.showWarningMessage('This action may cause issues'); + +// 错误 +vscode.window.showErrorMessage('Operation failed!'); +``` + +### 8.2 带按钮的提示 + +```typescript +const result = await vscode.window.showInformationMessage( + 'Do you want to continue?', + 'Yes', + 'No', + 'Cancel' +); + +if (result === 'Yes') { + // 用户点击了 Yes +} +``` + +### 8.3 输入框 + +```typescript +// 简单输入 +const input = await vscode.window.showInputBox({ + prompt: 'Enter your name', + placeHolder: 'John Doe', + validateInput: (value) => { + return value.length < 3 ? 'Name too short' : null; + } +}); + +// 快速选择 +const selected = await vscode.window.showQuickPick( + ['Option 1', 'Option 2', 'Option 3'], + { + placeHolder: 'Select an option', + canPickMany: false + } +); +``` + +### 8.4 进度提示 + +```typescript +await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: 'Processing...', + cancellable: true + }, + async (progress, token) => { + token.onCancellationRequested(() => { + console.log('User canceled'); + }); + + progress.report({ increment: 0, message: 'Starting...' }); + await doWork1(); + + progress.report({ increment: 50, message: 'Half done...' }); + await doWork2(); + + progress.report({ increment: 100, message: 'Complete!' }); + } +); +``` + +--- + +## 9. 语言特性支持 + +### 9.1 代码补全 + +```typescript +const provider = vscode.languages.registerCompletionItemProvider( + 'javascript', + { + provideCompletionItems(document, position) { + const item = new vscode.CompletionItem('myFunction'); + item.kind = vscode.CompletionItemKind.Function; + item.detail = 'My custom function'; + item.documentation = 'This is a custom function'; + item.insertText = new vscode.SnippetString('myFunction($1)$0'); + + return [item]; + } + }, + '.' // 触发字符 +); + +context.subscriptions.push(provider); +``` + +### 9.2 悬停提示 + +```typescript +const provider = vscode.languages.registerHoverProvider('javascript', { + provideHover(document, position) { + const range = document.getWordRangeAtPosition(position); + const word = document.getText(range); + + return new vscode.Hover([ + `**${word}**`, + 'This is a hover tooltip' + ]); + } +}); +``` + +### 9.3 诊断(错误提示) + +```typescript +const diagnosticCollection = vscode.languages.createDiagnosticCollection('myExtension'); +context.subscriptions.push(diagnosticCollection); + +function updateDiagnostics(document: vscode.TextDocument) { + const diagnostics: vscode.Diagnostic[] = []; + + const text = document.getText(); + const regex = /TODO/g; + let match; + + while ((match = regex.exec(text))) { + const range = new vscode.Range( + document.positionAt(match.index), + document.positionAt(match.index + match[0].length) + ); + + const diagnostic = new vscode.Diagnostic( + range, + 'TODO found', + vscode.DiagnosticSeverity.Warning + ); + + diagnostics.push(diagnostic); + } + + diagnosticCollection.set(document.uri, diagnostics); +} +``` + +--- + +## 10. 调试和诊断 + +### 10.1 输出通道 + +```typescript +const outputChannel = vscode.window.createOutputChannel('My Extension'); +context.subscriptions.push(outputChannel); + +outputChannel.appendLine('Extension activated'); +outputChannel.show(); // 显示输出面板 +``` + +### 10.2 日志记录 + +```typescript +// 使用 LogOutputChannel(带时间戳) +const logger = vscode.window.createOutputChannel('My Extension', { log: true }); + +logger.trace('Trace message'); +logger.debug('Debug message'); +logger.info('Info message'); +logger.warn('Warning message'); +logger.error('Error message'); +``` + +### 10.3 错误处理 + +```typescript +try { + await riskyOperation(); +} catch (error) { + if (error instanceof Error) { + vscode.window.showErrorMessage(`Error: ${error.message}`); + logger.error(error.stack || error.message); + } +} +``` + +--- + +## 最佳实践总结 + +### ✅ 推荐做法 + +1. **资源管理**:所有 disposable 对象都放入 `context.subscriptions` +2. **延迟激活**:使用 `onStartupFinished` 而不是 `*` +3. **异步操作**:使用 `async/await` 处理异步操作 +4. **错误处理**:捕获异常并给用户友好提示 +5. **类型安全**:充分利用 TypeScript 类型系统 +6. **状态持久化**:使用 `globalState`/`workspaceState` 保存状态 +7. **敏感信息**:使用 `secrets` API 存储 Token、密码等 + +### ❌ 避免做法 + +1. 不要在 `activate` 中执行耗时操作 +2. 不要忘记清理资源(监听器、Webview 等) +3. 不要在 Webview 中直接访问文件系统 +4. 不要在配置中存储敏感信息 +5. 不要阻塞主线程(使用 Worker 或异步操作) + +--- + +## 参考资源 + +- [VS Code Extension API 官方文档](https://code.visualstudio.com/api) +- [Extension Samples](https://github.com/microsoft/vscode-extension-samples) +- [Extension Guidelines](https://code.visualstudio.com/api/references/extension-guidelines) diff --git a/src/views/welcomeModal.ts b/src/views/welcomeModal.ts index a16d64b..b9df61f 100644 --- a/src/views/welcomeModal.ts +++ b/src/views/welcomeModal.ts @@ -19,40 +19,48 @@ export function getWelcomeModalContent(logoUri?: string): string {
🎉

欢迎使用 IC Coder!

-

您已成功激活 15 天试用期,让我们开始探索 IC Coder 的强大功能吧!

-
-
📝
-
-

步骤 1:打开聊天面板

-

点击侧边栏的 IC Coder 图标,或使用命令面板搜索 "IC Coder: Open Chat"

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

关于 IC Coder

+

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

+ +
+
+ 多智能体架构(Multi-Agent System):多个专业化AI智能体协同工作,分别负责架构设计、代码生成、验证测试等不同环节 +
+
+ 增强上下文引擎:智能理解和管理大规模设计上下文,确保生成代码的一致性和准确性 +
+
+ AI自主仿真:IC Coder提供完全自动化的仿真验证流程,无需手动编写测试代码 +
-
-
💬
-
-

步骤 2:输入您的需求

-

描述您想要生成的 Verilog 代码或需要帮助的问题,AI 将为您提供专业的解决方案

-
+ +
+ +
- -
-
🔬
-
-

步骤 3:运行仿真

-

使用 "生成 VCD" 命令运行 iverilog 仿真,并通过波形查看器查看仿真结果

-
-
- -
@@ -96,7 +104,7 @@ export function getWelcomeModalStyles(): string { border: 1px solid var(--vscode-widget-border); border-radius: 12px; width: 100%; - max-width: 500px; + max-width: 480px; box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5); animation: modalSlideIn 0.3s cubic-bezier(0.2, 0.8, 0.2, 1); overflow: hidden; @@ -124,9 +132,10 @@ export function getWelcomeModalStyles(): string { .welcome-modal-header h2 { margin: 0 0 12px; - font-size: 24px; + font-size: 20px; font-weight: 600; color: var(--vscode-foreground); + line-height: 1.4; } .welcome-modal-subtitle { @@ -140,37 +149,71 @@ export function getWelcomeModalStyles(): string { padding: 0 32px 32px; } - .welcome-step { + /* 试用期横幅 */ + .trial-banner { display: flex; - gap: 16px; - margin: 20px 0; - padding: 16px; + align-items: center; + justify-content: center; + padding: 12px 16px; background: var(--vscode-editor-inactiveSelectionBackground); border-radius: 8px; - border-left: 4px solid var(--vscode-textLink-foreground); - } - - .welcome-step-icon { - font-size: 24px; - flex-shrink: 0; - } - - .welcome-step-content h3 { - margin: 0 0 8px; - font-size: 15px; - font-weight: 600; - color: var(--vscode-textLink-foreground); - } - - .welcome-step-content p { - margin: 0; + margin-bottom: 20px; font-size: 13px; color: var(--vscode-descriptionForeground); - line-height: 1.5; + border-left: 3px solid var(--vscode-textLink-foreground); + } + + .trial-banner strong { + color: var(--vscode-textLink-foreground); + font-weight: 600; + } + + /* IC Coder 简介区域 */ + .intro-section { + margin-bottom: 24px; + } + + .section-title { + margin: 0 0 12px; + font-size: 15px; + font-weight: 600; + color: var(--vscode-foreground); + } + + .intro-text { + margin: 0 0 16px; + font-size: 13px; + color: var(--vscode-descriptionForeground); + line-height: 1.6; + } + + .features { + display: flex; + flex-direction: column; + gap: 10px; + } + + .feature-item { + padding: 10px 12px; + background: var(--vscode-editor-inactiveSelectionBackground); + border-radius: 6px; + font-size: 13px; + color: var(--vscode-foreground); + border-left: 2px solid var(--vscode-textLink-foreground); + } + + .feature-text { + display: block; + } + + /* 按钮组 */ + .button-group { + display: flex; + gap: 12px; } .welcome-btn { - width: 100%; + flex: 1; padding: 12px 16px; font-size: 14px; font-weight: 600; @@ -181,18 +224,32 @@ export function getWelcomeModalStyles(): string { align-items: center; justify-content: center; gap: 8px; - background: var(--vscode-button-background); - color: var(--vscode-button-foreground); transition: all 0.2s; - margin-top: 24px; } - .welcome-btn:hover { + .welcome-btn-primary { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + } + + .welcome-btn-primary:hover { background: var(--vscode-button-hoverBackground); transform: translateY(-1px); box-shadow: 0 2px 8px rgba(0,0,0,0.2); } + .welcome-btn-secondary { + background: var(--vscode-button-secondaryBackground); + color: var(--vscode-button-secondaryForeground); + border: 1px solid var(--vscode-button-border); + } + + .welcome-btn-secondary:hover { + background: var(--vscode-button-secondaryHoverBackground); + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(0,0,0,0.15); + } + .welcome-btn:active { transform: translateY(0); }