Compare commits
31 Commits
feat/back-
...
423c9ddb0e
| Author | SHA1 | Date | |
|---|---|---|---|
| 423c9ddb0e | |||
| 50eacdafde | |||
| d90cca7cef | |||
| 5347425327 | |||
| 28d93c7e75 | |||
| 5339212de9 | |||
| 73a1510de4 | |||
| 606f757699 | |||
| 342bf22f3f | |||
| d2ec73f796 | |||
| c9f597beec | |||
| e9a201ef01 | |||
| 77a89847cb | |||
| c14b7f4dbc | |||
| 64724bf48c | |||
| c9e160f2ef | |||
| 3a19cc638f | |||
| a2e8e74572 | |||
| ad96743fad | |||
| 95b1bd7678 | |||
| 94b6fb056f | |||
| a24fd71636 | |||
| f55a5bfbcb | |||
| 83b706d5be | |||
| b9e63bc9a9 | |||
| ef0c8748f7 | |||
| 430a2c4062 | |||
| f5bd35c71a | |||
| f958683f53 | |||
| 8cf0e32184 | |||
| 1cbd0c5fe7 |
24
CHANGELOG.md
@ -1,9 +1,23 @@
|
|||||||
# Change Log
|
# 更新日志
|
||||||
|
|
||||||
All notable changes to the "ic-coder" extension will be documented in this file.
|
所有重要的项目变更都将记录在此文件中。
|
||||||
|
|
||||||
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
## [1.0.2] - 2026-01-13
|
||||||
|
|
||||||
## [Unreleased]
|
IC Coder插件端正式发布。
|
||||||
|
|
||||||
- Initial release
|
IC Coder 插件端是一个专为 FPGA 开发设计的 VS Code 扩展,提供 AI 驱动的智能辅助功能。
|
||||||
|
|
||||||
|
主要功能:
|
||||||
|
- VCD波形解析
|
||||||
|
- 自动生成完整工程
|
||||||
|
- 自动仿真
|
||||||
|
- 自主代码迭代
|
||||||
|
- 智能匹配最优模型
|
||||||
|
- 多线程任务处理
|
||||||
|
- 实时跟随
|
||||||
|
- 丰富的上下文工具
|
||||||
|
- 全双工交互
|
||||||
|
- 多层次安全保障
|
||||||
|
- 自动搭建电路架构
|
||||||
|
- 多平台支持
|
||||||
|
|||||||
@ -67,6 +67,11 @@
|
|||||||
CO03l8nmFBBTNPDg7lN9a9fYwDdgsRIDVDwTrx6Esggi6HnzmrMTJQQJ99BLACAAAAAAAAAAAAAGAZDOVVyT
|
CO03l8nmFBBTNPDg7lN9a9fYwDdgsRIDVDwTrx6Esggi6HnzmrMTJQQJ99BLACAAAAAAAAAAAAAGAZDOVVyT
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
//蔡工的token
|
||||||
|
6CB3tOZPiwNi6rrOuFHMe6QzrVWBnajW5fJsNgCWu8jtERUCCRnJJQQJ99CAACAAAAAAAAAAAAASAZDO3FnY
|
||||||
|
```
|
||||||
|
|
||||||
### 3. 创建发布者账号
|
### 3. 创建发布者账号
|
||||||
|
|
||||||
发布者账号是你在 VS Code 市场的身份标识。
|
发布者账号是你在 VS Code 市场的身份标识。
|
||||||
|
|||||||
51
README.md
@ -1,24 +1,45 @@
|
|||||||
# IC Coder Plugin
|
## 什么是 IC Coder
|
||||||
|
|
||||||
IC Coder 是一个面向 Verilog/FPGA 开发的智能辅助插件。
|
**IC Coder** 是一款 **专注于真实 FPGA 研发的 Verilog 智能体编程平台**。我们立志于用 AI 重塑 FPGA 研发效率,让 FPGA 开发者们,都能享受到 AI 发展所带来的科技福利!目标成为全球最好用的 **LLM 生成 Verilog**的平台!
|
||||||
|
|
||||||
## 功能特性
|
从 WEB 端到插件端,IC Coder 智能体架构完成了**全新升级**,采用当前主流的**层级架构**设计,这种高内聚、低耦合的架构特性,不仅支持更多功能扩展,更预留了充足的迭代空间。当前,插件端拥有了调用本地工具的能力,不再是单纯代码生成的智能体,升级为拥有**语法校验、波形逻辑检查**等工具的**全流程 Verilog 编程智能体平台**,给用户带来更沉浸的**Vibe Verilog Coding**体验。
|
||||||
|
|
||||||
- Verilog 代码智能生成
|
## 输入需求 对话补充需求
|
||||||
- 文件操作支持(创建、读取、修改、删除)
|
|
||||||
- 集成 iverilog 仿真工具
|
|
||||||
- VCD 波形文件生成
|
|
||||||
- 智能对话助手
|
|
||||||
|
|
||||||
## 使用说明
|
**无需**输入完整需求,放心交给智能体补充完善。
|
||||||
|
|
||||||
安装插件后,点击侧边栏的 IC Coder 图标即可开始使用。
|
## Plan 模型下确认设计文档
|
||||||
|
|
||||||
## 系统要求
|
**确定**好用户需求以及相关参数后,整理并输出一份 FPGA 开发**设计文档**。Plan 模式下用户可以**进一步**与 IC Coder 沟通需求,或**直接修改**设计文档。
|
||||||
|
|
||||||
- VS Code 1.107.0 或更高版本
|
## 自动搭建电路架构
|
||||||
- 插件已内置 iverilog 工具(Windows 平台)
|
|
||||||
|
|
||||||
## 许可证
|
根据需求自动搭建电路架构,并将电路信号关系结构化
|
||||||
|
|
||||||
MIT
|
## 自动仿真
|
||||||
|
|
||||||
|
自主搭建 Testbench 仿真平台,自动运行仿真生成波形
|
||||||
|
|
||||||
|
## 实时跟随
|
||||||
|
|
||||||
|
实时展示全流程执行细节,与智能体协同随时反馈,让 AI 开发更清晰、高效
|
||||||
|
|
||||||
|
## VCD 波形解析
|
||||||
|
|
||||||
|
自动解析 VCD 波形文件,自动根据需求,检查是否存在逻辑错误
|
||||||
|
|
||||||
|
## 自主代码迭代
|
||||||
|
|
||||||
|
根据波形解析结果,自动对代码进行优化,然后重新仿真并解析波形,如此迭代,直到仿真无误
|
||||||
|
|
||||||
|
## 多层次安全保障
|
||||||
|
|
||||||
|
默认本地存储与云端即时加密保障隐私,真正做到了代码全链路加密传输、云端零存储
|
||||||
|
|
||||||
|
## 反馈
|
||||||
|
|
||||||
|
无论是想与我们深入交流还是遇到任何问题,欢迎您[进入社区](https://iccoder.com:888/community)与我们联系
|
||||||
|
|
||||||
|
## 服务条款和隐私协议
|
||||||
|
|
||||||
|
请阅读我们的[服务条款](https://iccoder.com:888/guides/legal/terms-of-service)和[隐私协议](https://iccoder.com:888/guides/legal/privacy-policy)了解更多细节。
|
||||||
|
|||||||
BIN
media/description/auto-build-architecture-copy.png
Normal file
|
After Width: | Height: | Size: 168 KiB |
BIN
media/description/auto-build-architecture.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
media/description/auto-simulation-1.png
Normal file
|
After Width: | Height: | Size: 212 KiB |
BIN
media/description/auto-simulation-2.png
Normal file
|
After Width: | Height: | Size: 251 KiB |
BIN
media/description/auto-simulation-3.png
Normal file
|
After Width: | Height: | Size: 266 KiB |
BIN
media/description/input-requirement-1.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
media/description/input-requirement-2.png
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
media/description/input-requirement-3.png
Normal file
|
After Width: | Height: | Size: 242 KiB |
BIN
media/description/plan-design-doc-1.png
Normal file
|
After Width: | Height: | Size: 349 KiB |
BIN
media/description/plan-design-doc-2.png
Normal file
|
After Width: | Height: | Size: 302 KiB |
BIN
media/description/real-time-follow-1.png
Normal file
|
After Width: | Height: | Size: 157 KiB |
BIN
media/description/real-time-follow-2.png
Normal file
|
After Width: | Height: | Size: 305 KiB |
BIN
media/description/real-time-follow-3.png
Normal file
|
After Width: | Height: | Size: 253 KiB |
BIN
media/description/real-time-follow-4.png
Normal file
|
After Width: | Height: | Size: 209 KiB |
14
package.json
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "iccoder",
|
"name": "iccoder",
|
||||||
"displayName": "IC Coder",
|
"displayName": "IC Coder: Agentic Verilog Platform",
|
||||||
"description": "Agentic Verilog Coding Platform for Real-World FPGAs",
|
"description": "Agentic Verilog Coding Platform for Real-World FPGAs",
|
||||||
"version": "0.0.2",
|
"version": "1.0.2",
|
||||||
"publisher": "ICCoder",
|
"publisher": "ICCoderAgenticVerilogPlatform",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.80.0"
|
"vscode": "^1.80.0"
|
||||||
},
|
},
|
||||||
@ -21,6 +21,10 @@
|
|||||||
"assistant"
|
"assistant"
|
||||||
],
|
],
|
||||||
"license": "SEE LICENSE IN LICENSE",
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.pengyejiatu.com/pengyejiatu/IC-Coder-Plugin.git"
|
||||||
|
},
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
"onCommand:ic-coder.openPanel",
|
"onCommand:ic-coder.openPanel",
|
||||||
"onView:ic-coder-sidebar",
|
"onView:ic-coder-sidebar",
|
||||||
@ -114,7 +118,9 @@
|
|||||||
"dist",
|
"dist",
|
||||||
"media",
|
"media",
|
||||||
"tools",
|
"tools",
|
||||||
"src/assets"
|
"src/assets",
|
||||||
|
"LICENSE",
|
||||||
|
"CHANGELOG.md"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wavedrom/doppler": "^1.14.0",
|
"@wavedrom/doppler": "^1.14.0",
|
||||||
|
|||||||
BIN
src/assets/QRCode/wx.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
@ -8,7 +8,7 @@ import * as vscode from "vscode";
|
|||||||
type Environment = "dev" | "test" | "prod";
|
type Environment = "dev" | "test" | "prod";
|
||||||
|
|
||||||
/** 当前环境 - 修改这里切换环境 */
|
/** 当前环境 - 修改这里切换环境 */
|
||||||
const CURRENT_ENV: Environment = "dev";
|
const CURRENT_ENV: Environment = "prod";
|
||||||
|
|
||||||
/** 服务等级类型 */
|
/** 服务等级类型 */
|
||||||
export type ServiceTier = "lite" | "syntaxic" | "max" | "auto";
|
export type ServiceTier = "lite" | "syntaxic" | "max" | "auto";
|
||||||
@ -51,8 +51,8 @@ const ENV_CONFIG: Record<Environment, IccoderConfig> = {
|
|||||||
},
|
},
|
||||||
/** 生产环境 - 通过 Gateway 路由 */
|
/** 生产环境 - 通过 Gateway 路由 */
|
||||||
prod: {
|
prod: {
|
||||||
backendUrl: "https://api.iccoder.com/iccoder",
|
backendUrl: "https://api.iccoder.com",
|
||||||
backendUrlStrongeLoop: "https://api.iccoder.com",
|
backendUrlStrongeLoop: "http://192.168.1.115:2029",
|
||||||
loginUrl: "https://iccoder.com/login",
|
loginUrl: "https://iccoder.com/login",
|
||||||
timeout: 60000,
|
timeout: 60000,
|
||||||
userId: "default-user",
|
userId: "default-user",
|
||||||
|
|||||||
@ -157,12 +157,8 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
try {
|
try {
|
||||||
const session = await vscode.authentication.getSession("iccoder", [], { createIfNone: false });
|
const session = await vscode.authentication.getSession("iccoder", [], { createIfNone: false });
|
||||||
if (session) {
|
if (session) {
|
||||||
// 通过创建新会话并清除偏好来实现登出
|
// 调用 authProvider 的 removeSession 方法
|
||||||
await vscode.authentication.getSession("iccoder", [], {
|
await authProvider.removeSession(session.id);
|
||||||
clearSessionPreference: true,
|
|
||||||
forceNewSession: true
|
|
||||||
});
|
|
||||||
vscode.window.showInformationMessage("已退出登录");
|
|
||||||
} else {
|
} else {
|
||||||
vscode.window.showInformationMessage("当前未登录");
|
vscode.window.showInformationMessage("当前未登录");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -131,13 +131,19 @@ export async function showICHelperPanel(
|
|||||||
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")
|
||||||
|
);
|
||||||
|
|
||||||
// 设置HTML内容
|
// 设置HTML内容
|
||||||
panel.webview.html = getWebviewContent(
|
panel.webview.html = getWebviewContent(
|
||||||
iconUri.toString(),
|
iconUri.toString(),
|
||||||
autoIconUri.toString(),
|
autoIconUri.toString(),
|
||||||
liteIconUri.toString(),
|
liteIconUri.toString(),
|
||||||
syIconUri.toString(),
|
syIconUri.toString(),
|
||||||
maxIconUri.toString()
|
maxIconUri.toString(),
|
||||||
|
qrCodeUri.toString()
|
||||||
);
|
);
|
||||||
|
|
||||||
// 获取并发送用户信息到 webview
|
// 获取并发送用户信息到 webview
|
||||||
@ -156,7 +162,8 @@ export async function showICHelperPanel(
|
|||||||
userId: userInfo.userId,
|
userId: userInfo.userId,
|
||||||
nickname: userInfo.nickname,
|
nickname: userInfo.nickname,
|
||||||
username: userInfo.username,
|
username: userInfo.username,
|
||||||
credits: userInfo.credits
|
credits: userInfo.credits,
|
||||||
|
membership: userInfo.membership
|
||||||
},
|
},
|
||||||
tierIconUrl: tierIconUrl
|
tierIconUrl: tierIconUrl
|
||||||
};
|
};
|
||||||
@ -340,6 +347,24 @@ export async function showICHelperPanel(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "logout":
|
||||||
|
// 退出登录
|
||||||
|
vscode.commands.executeCommand("ic-coder.logout");
|
||||||
|
break;
|
||||||
|
case "openICCoder":
|
||||||
|
// 跳转到 IC Coder 官网
|
||||||
|
vscode.env.openExternal(vscode.Uri.parse("https://www.iccoder.com"));
|
||||||
|
break;
|
||||||
|
case "openUserManual":
|
||||||
|
// 打开用户手册
|
||||||
|
vscode.env.openExternal(vscode.Uri.parse("https://www.iccoder.com"));
|
||||||
|
break;
|
||||||
|
case "openUserFeedback":
|
||||||
|
// 打开用户反馈二维码弹窗
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "showFeedbackQRCode"
|
||||||
|
});
|
||||||
|
break;
|
||||||
// 处理计划操作(只做模式切换,响应已通过 submitAnswer 发送)
|
// 处理计划操作(只做模式切换,响应已通过 submitAnswer 发送)
|
||||||
case "planAction":
|
case "planAction":
|
||||||
if (message.action === "confirm") {
|
if (message.action === "confirm") {
|
||||||
@ -487,6 +512,20 @@ export async function showICHelperPanel(
|
|||||||
hasWorkspace: hasWorkspace,
|
hasWorkspace: hasWorkspace,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case "openExternalUrl":
|
||||||
|
// 打开外部链接
|
||||||
|
if (message.url) {
|
||||||
|
vscode.env.openExternal(vscode.Uri.parse(message.url));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "openICCoder":
|
||||||
|
// 打开 IC Coder 官网
|
||||||
|
vscode.env.openExternal(vscode.Uri.parse('https://www.iccoder.com'));
|
||||||
|
break;
|
||||||
|
case "logout":
|
||||||
|
// 退出登录(前端已有确认对话框)
|
||||||
|
vscode.commands.executeCommand('iccoder.logout');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
|||||||
@ -213,14 +213,23 @@ async function handleUserMessageWithBackend(
|
|||||||
},
|
},
|
||||||
|
|
||||||
onComplete: async (segments) => {
|
onComplete: async (segments) => {
|
||||||
// 隐藏状态栏
|
|
||||||
panel.webview.postMessage({
|
|
||||||
command: "hideStatus",
|
|
||||||
});
|
|
||||||
|
|
||||||
// 最后一次发送完整的段落
|
|
||||||
console.log("[MessageHandler] 对话完成, 段落数:", segments.length);
|
console.log("[MessageHandler] 对话完成, 段落数:", segments.length);
|
||||||
|
|
||||||
|
// 先保存到历史记录(优先级最高,确保数据不丢失)
|
||||||
|
try {
|
||||||
|
// 将完整的 segments 保存到一条 AI 消息中
|
||||||
|
// 这样加载时可以完整还原对话样式
|
||||||
|
const textContent = segments
|
||||||
|
.filter((s) => s.type === "text" && s.content)
|
||||||
|
.map((s) => s.content)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
await historyManager.addAiMessage(textContent, undefined, segments);
|
||||||
|
console.log("[MessageHandler] AI响应已保存到历史记录");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[MessageHandler] 保存AI响应历史失败:", error);
|
||||||
|
}
|
||||||
|
|
||||||
// 对话完成后重新获取余额(因为已经消耗了 Credits)
|
// 对话完成后重新获取余额(因为已经消耗了 Credits)
|
||||||
try {
|
try {
|
||||||
console.log("[MessageHandler] 对话完成,重新获取余额...");
|
console.log("[MessageHandler] 对话完成,重新获取余额...");
|
||||||
@ -232,25 +241,22 @@ async function handleUserMessageWithBackend(
|
|||||||
console.error("[MessageHandler] 获取余额失败:", error);
|
console.error("[MessageHandler] 获取余额失败:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 尝试更新面板(如果面板已关闭,这些操作会失败,但不影响数据保存)
|
||||||
|
try {
|
||||||
|
// 隐藏状态栏
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "hideStatus",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 最后一次发送完整的段落
|
||||||
const result = await panel.webview.postMessage({
|
const result = await panel.webview.postMessage({
|
||||||
command: "updateSegments",
|
command: "updateSegments",
|
||||||
segments: segments,
|
segments: segments,
|
||||||
isComplete: true,
|
isComplete: true,
|
||||||
});
|
});
|
||||||
console.log("[MessageHandler] postMessage 返回值:", result);
|
console.log("[MessageHandler] postMessage 返回值:", result);
|
||||||
|
|
||||||
// 保存完整的 segments 到历史记录
|
|
||||||
try {
|
|
||||||
// 将完整的 segments 保存到一条 AI 消息中
|
|
||||||
// 这样加载时可以完整还原对话样式
|
|
||||||
const textContent = segments
|
|
||||||
.filter((s) => s.type === "text" && s.content)
|
|
||||||
.map((s) => s.content)
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
await historyManager.addAiMessage(textContent, undefined, segments);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("保存AI响应历史失败:", error);
|
console.warn("[MessageHandler] 更新面板失败(面板可能已关闭):", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
|
|||||||
@ -109,6 +109,9 @@ export function showICHelperPanel(context: vscode.ExtensionContext) {
|
|||||||
case "showInfo":
|
case "showInfo":
|
||||||
vscode.window.showInformationMessage(message.text);
|
vscode.window.showInformationMessage(message.text);
|
||||||
break;
|
break;
|
||||||
|
case "showWarning":
|
||||||
|
vscode.window.showWarningMessage(message.message);
|
||||||
|
break;
|
||||||
// 新增:处理用户回答
|
// 新增:处理用户回答
|
||||||
case "submitAnswer":
|
case "submitAnswer":
|
||||||
handleUserAnswer(
|
handleUserAnswer(
|
||||||
@ -214,6 +217,17 @@ export class ICViewProvider implements vscode.WebviewViewProvider {
|
|||||||
vscode.commands.executeCommand("ic-coder.openChat");
|
vscode.commands.executeCommand("ic-coder.openChat");
|
||||||
} else if (message.command === "login") {
|
} else if (message.command === "login") {
|
||||||
vscode.commands.executeCommand("ic-coder.login");
|
vscode.commands.executeCommand("ic-coder.login");
|
||||||
|
} else if (message.command === "logout") {
|
||||||
|
// 退出登录(前端已有确认对话框)
|
||||||
|
vscode.commands.executeCommand('iccoder.logout');
|
||||||
|
} else if (message.command === "openICCoder") {
|
||||||
|
// 打开 IC Coder 官网
|
||||||
|
vscode.env.openExternal(vscode.Uri.parse('https://www.iccoder.com'));
|
||||||
|
} else if (message.command === "openExternalUrl") {
|
||||||
|
// 打开外部链接
|
||||||
|
if (message.url) {
|
||||||
|
vscode.env.openExternal(vscode.Uri.parse(message.url));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
|||||||
@ -3,7 +3,21 @@ import {
|
|||||||
getUserInfoComponentStyles,
|
getUserInfoComponentStyles,
|
||||||
getUserInfoComponentScript,
|
getUserInfoComponentScript,
|
||||||
} from "./userInfoComponent";
|
} from "./userInfoComponent";
|
||||||
import { userAvatarIconSvg } from "../constants/toolIcons";
|
import {
|
||||||
|
getMoreOptionsComponentContent,
|
||||||
|
getMoreOptionsComponentStyles,
|
||||||
|
getMoreOptionsComponentScript,
|
||||||
|
} from "./moreOptionsComponent";
|
||||||
|
import {
|
||||||
|
getSettingsComponentContent,
|
||||||
|
getSettingsComponentStyles,
|
||||||
|
getSettingsComponentScript,
|
||||||
|
} from "./settingsComponent";
|
||||||
|
import {
|
||||||
|
userAvatarIconSvg,
|
||||||
|
moreIconSvg,
|
||||||
|
setting,
|
||||||
|
} from "../constants/toolIcons";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取会话历史栏的 HTML 内容
|
* 获取会话历史栏的 HTML 内容
|
||||||
@ -39,8 +53,23 @@ export function getConversationHistoryBarContent(): string {
|
|||||||
</button>
|
</button>
|
||||||
${getUserInfoComponentContent()}
|
${getUserInfoComponentContent()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class='setting'>
|
||||||
|
<button class="setting-btn" title="设置" onclick="openSettingsModal()">
|
||||||
|
${setting}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='more-container'>
|
||||||
|
<button class="more-button" title="更多选项" onclick="toggleMoreOptionsDropdown()">
|
||||||
|
${moreIconSvg}
|
||||||
|
</button>
|
||||||
|
${getMoreOptionsComponentContent()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${getSettingsComponentContent()}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,8 +105,8 @@ export function getConversationHistoryBarStyles(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-avatar-icon-button {
|
.user-avatar-icon-button {
|
||||||
width: 36px;
|
width: 30px;
|
||||||
height: 36px;
|
height: 30px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--vscode-foreground);
|
color: var(--vscode-foreground);
|
||||||
@ -111,6 +140,72 @@ export function getConversationHistoryBarStyles(): string {
|
|||||||
|
|
||||||
${getUserInfoComponentStyles()}
|
${getUserInfoComponentStyles()}
|
||||||
|
|
||||||
|
${getSettingsComponentStyles()}
|
||||||
|
|
||||||
|
.setting {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-btn {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-btn:hover {
|
||||||
|
background: var(--vscode-toolbar-hoverBackground);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-btn:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-button {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-button:hover {
|
||||||
|
background: var(--vscode-toolbar-hoverBackground);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-button:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-button.active {
|
||||||
|
background: var(--vscode-toolbar-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
${getMoreOptionsComponentStyles()}
|
||||||
|
|
||||||
.history-dropdown-button {
|
.history-dropdown-button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -219,8 +314,8 @@ export function getConversationHistoryBarStyles(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.new-conversation-button {
|
.new-conversation-button {
|
||||||
width: 36px;
|
width: 30px;
|
||||||
height: 36px;
|
height: 30px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--vscode-foreground);
|
color: var(--vscode-foreground);
|
||||||
@ -275,6 +370,10 @@ export function getConversationHistoryBarScript(): string {
|
|||||||
return `
|
return `
|
||||||
${getUserInfoComponentScript()}
|
${getUserInfoComponentScript()}
|
||||||
|
|
||||||
|
${getMoreOptionsComponentScript()}
|
||||||
|
|
||||||
|
${getSettingsComponentScript()}
|
||||||
|
|
||||||
// 更新用户头像图标按钮显示
|
// 更新用户头像图标按钮显示
|
||||||
function updateUserAvatarIconButton(userInfo) {
|
function updateUserAvatarIconButton(userInfo) {
|
||||||
const userAvatarIconButton = document.getElementById('userAvatarIconButton');
|
const userAvatarIconButton = document.getElementById('userAvatarIconButton');
|
||||||
|
|||||||
@ -4,21 +4,33 @@
|
|||||||
export function getExampleShowcaseContent(): string {
|
export function getExampleShowcaseContent(): string {
|
||||||
return `
|
return `
|
||||||
<div class="example-showcase" id="exampleShowcase">
|
<div class="example-showcase" id="exampleShowcase">
|
||||||
<div class="showcase-title">展示</div>
|
<div class="showcase-title">示例</div>
|
||||||
<div class="example-cards">
|
<div class="example-cards">
|
||||||
<div class="example-card" onclick="fillExample(0)">
|
<div class="example-card" onclick="sendExample(0)">
|
||||||
<div class="example-icon">📝</div>
|
<div class="example-icon">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M14 2H6C5.46957 2 4.96086 2.21071 4.58579 2.58579C4.21071 2.96086 4 3.46957 4 4V20C4 20.5304 4.21071 21.0391 4.58579 21.4142C4.96086 21.7893 5.46957 22 6 22H18C18.5304 22 19.0391 21.7893 19.4142 21.4142C19.7893 21.0391 20 20.5304 20 20V8L14 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M14 2V8H20" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M16 13H8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M16 17H8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M10 9H9H8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
<div class="example-content">
|
<div class="example-content">
|
||||||
<div class="example-title">代码生成</div>
|
<div class="example-title">生成一个SPI控制器</div>
|
||||||
<div class="example-desc">生成一个 8 位全加器的 Verilog 代码</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="example-card" onclick="fillExample(1)">
|
<div class="example-card" onclick="sendExample(1)">
|
||||||
<div class="example-icon">🔍</div>
|
<div class="example-icon">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12 2L2 7L12 12L22 7L12 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M2 17L12 22L22 17" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M2 12L12 17L22 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
<div class="example-content">
|
<div class="example-content">
|
||||||
<div class="example-title">代码分析</div>
|
<div class="example-title">生成一个GMII接口的以太网UDP通信模块</div>
|
||||||
<div class="example-desc">分析当前项目中的时序逻辑设计</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -71,26 +83,50 @@ export function getExampleShowcaseStyles(): string {
|
|||||||
background: var(--vscode-input-background);
|
background: var(--vscode-input-background);
|
||||||
border: 1px solid var(--vscode-input-border);
|
border: 1px solid var(--vscode-input-border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 14px;
|
padding: 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 10px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(135deg, rgba(79, 172, 254, 0.1) 0%, rgba(0, 242, 254, 0.1) 50%, rgba(168, 85, 247, 0.1) 100%);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-card:hover::before {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-card:hover {
|
.example-card:hover {
|
||||||
border-color: var(--vscode-focusBorder);
|
border-color: var(--vscode-focusBorder);
|
||||||
background: var(--vscode-list-hoverBackground);
|
transform: translateY(-3px);
|
||||||
transform: translateY(-2px);
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-icon {
|
.example-icon {
|
||||||
font-size: 28px;
|
|
||||||
line-height: 1;
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-icon svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-content {
|
.example-content {
|
||||||
@ -99,12 +135,23 @@ export function getExampleShowcaseStyles(): string {
|
|||||||
gap: 4px;
|
gap: 4px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-title {
|
.example-title {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--vscode-foreground);
|
color: var(--vscode-foreground);
|
||||||
|
line-height: 1.4;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-card:hover .example-title {
|
||||||
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 50%, #a855f7 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-desc {
|
.example-desc {
|
||||||
@ -175,20 +222,41 @@ export function getExampleShowcaseScript(): string {
|
|||||||
return `
|
return `
|
||||||
// 示例文本数组
|
// 示例文本数组
|
||||||
const exampleTexts = [
|
const exampleTexts = [
|
||||||
'生成一个 8 位全加器的 Verilog 代码',
|
'生成一个SPI控制器',
|
||||||
'分析当前项目中的时序逻辑设计'
|
'生成一个GMII接口的以太网UDP通信模块'
|
||||||
];
|
];
|
||||||
|
|
||||||
// 填充示例到输入框
|
// 存储待发送的示例索引
|
||||||
function fillExample(index) {
|
let pendingExampleIndex = -1;
|
||||||
|
|
||||||
|
// 直接发送示例消息
|
||||||
|
function sendExample(index) {
|
||||||
|
// 先检查工作区
|
||||||
|
pendingExampleIndex = index;
|
||||||
|
vscode.postMessage({
|
||||||
|
command: 'checkWorkspace'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实际发送示例消息
|
||||||
|
function doSendExample(index) {
|
||||||
const messageInput = document.getElementById('messageInput');
|
const messageInput = document.getElementById('messageInput');
|
||||||
|
const sendButton = document.getElementById('sendButton');
|
||||||
|
|
||||||
if (messageInput && exampleTexts[index]) {
|
if (messageInput && exampleTexts[index]) {
|
||||||
messageInput.value = exampleTexts[index];
|
messageInput.value = exampleTexts[index];
|
||||||
messageInput.focus();
|
|
||||||
// 触发自动调整高度
|
// 触发自动调整高度
|
||||||
if (typeof autoResizeTextarea === 'function') {
|
if (typeof autoResizeTextarea === 'function') {
|
||||||
autoResizeTextarea();
|
autoResizeTextarea();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 直接触发发送
|
||||||
|
if (sendButton && typeof sendButton.click === 'function') {
|
||||||
|
sendButton.click();
|
||||||
|
} else if (typeof sendMessage === 'function') {
|
||||||
|
sendMessage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
327
src/views/generalSettingsComponent.ts
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
/**
|
||||||
|
* 获取通用设置组件的 HTML 内容
|
||||||
|
*/
|
||||||
|
export function getGeneralSettingsComponentContent(): string {
|
||||||
|
return `
|
||||||
|
<div class="general-settings">
|
||||||
|
<h3 class="settings-section-title">通用设置</h3>
|
||||||
|
|
||||||
|
<div class="settings-section">
|
||||||
|
<div class="settings-item">
|
||||||
|
<div class="settings-item-header">
|
||||||
|
<label class="settings-item-label">主题</label>
|
||||||
|
<span class="settings-item-description">选择界面主题</span>
|
||||||
|
</div>
|
||||||
|
<select class="settings-select" id="themeSelect">
|
||||||
|
<option value="auto">跟随系统</option>
|
||||||
|
<option value="light">浅色</option>
|
||||||
|
<option value="dark">深色</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-item">
|
||||||
|
<div class="settings-item-header">
|
||||||
|
<label class="settings-item-label">语言</label>
|
||||||
|
<span class="settings-item-description">选择界面语言</span>
|
||||||
|
</div>
|
||||||
|
<select class="settings-select" id="languageSelect">
|
||||||
|
<option value="zh-CN">简体中文</option>
|
||||||
|
<option value="en-US">English</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-item">
|
||||||
|
<div class="settings-item-header">
|
||||||
|
<label class="settings-item-label">自动保存</label>
|
||||||
|
<span class="settings-item-description">自动保存会话历史</span>
|
||||||
|
</div>
|
||||||
|
<label class="settings-switch">
|
||||||
|
<input type="checkbox" id="autoSaveCheckbox" checked>
|
||||||
|
<span class="settings-switch-slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-item">
|
||||||
|
<div class="settings-item-header">
|
||||||
|
<label class="settings-item-label">显示时间戳</label>
|
||||||
|
<span class="settings-item-description">在消息中显示时间戳</span>
|
||||||
|
</div>
|
||||||
|
<label class="settings-switch">
|
||||||
|
<input type="checkbox" id="showTimestampCheckbox">
|
||||||
|
<span class="settings-switch-slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-section">
|
||||||
|
<h4 class="settings-subsection-title">编辑器设置</h4>
|
||||||
|
|
||||||
|
<div class="settings-item">
|
||||||
|
<div class="settings-item-header">
|
||||||
|
<label class="settings-item-label">字体大小</label>
|
||||||
|
<span class="settings-item-description">设置编辑器字体大小</span>
|
||||||
|
</div>
|
||||||
|
<input type="number" class="settings-input" id="fontSizeInput" value="14" min="10" max="24">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-item">
|
||||||
|
<div class="settings-item-header">
|
||||||
|
<label class="settings-item-label">代码高亮</label>
|
||||||
|
<span class="settings-item-description">启用代码语法高亮</span>
|
||||||
|
</div>
|
||||||
|
<label class="settings-switch">
|
||||||
|
<input type="checkbox" id="syntaxHighlightCheckbox" checked>
|
||||||
|
<span class="settings-switch-slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-actions">
|
||||||
|
<button class="settings-button settings-button-primary" onclick="saveGeneralSettings()">
|
||||||
|
保存设置
|
||||||
|
</button>
|
||||||
|
<button class="settings-button settings-button-secondary" onclick="resetGeneralSettings()">
|
||||||
|
重置为默认
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取通用设置组件的 CSS 样式
|
||||||
|
*/
|
||||||
|
export function getGeneralSettingsComponentStyles(): string {
|
||||||
|
return `
|
||||||
|
.general-settings {
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section-title {
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-subsection-title {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-item-header {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-item-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-item-description {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-select {
|
||||||
|
padding: 6px 12px;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-select:focus {
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-input {
|
||||||
|
width: 80px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-input:focus {
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 44px;
|
||||||
|
height: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-switch-slider {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 24px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-switch-slider:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
left: 3px;
|
||||||
|
bottom: 3px;
|
||||||
|
background: var(--vscode-foreground);
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-switch input:checked + .settings-switch-slider {
|
||||||
|
background: var(--vscode-button-background);
|
||||||
|
border-color: var(--vscode-button-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-switch input:checked + .settings-switch-slider:before {
|
||||||
|
transform: translateX(20px);
|
||||||
|
background: var(--vscode-button-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 24px;
|
||||||
|
padding-top: 24px;
|
||||||
|
border-top: 1px solid var(--vscode-panel-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-button-primary {
|
||||||
|
background: var(--vscode-button-background);
|
||||||
|
color: var(--vscode-button-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-button-primary:hover {
|
||||||
|
background: var(--vscode-button-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-button-secondary {
|
||||||
|
background: var(--vscode-button-secondaryBackground);
|
||||||
|
color: var(--vscode-button-secondaryForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-button-secondary:hover {
|
||||||
|
background: var(--vscode-button-secondaryHoverBackground);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取通用设置组件的 JavaScript 脚本
|
||||||
|
*/
|
||||||
|
export function getGeneralSettingsComponentScript(): string {
|
||||||
|
return `
|
||||||
|
// 保存通用设置
|
||||||
|
function saveGeneralSettings() {
|
||||||
|
const settings = {
|
||||||
|
theme: document.getElementById('themeSelect').value,
|
||||||
|
language: document.getElementById('languageSelect').value,
|
||||||
|
autoSave: document.getElementById('autoSaveCheckbox').checked,
|
||||||
|
showTimestamp: document.getElementById('showTimestampCheckbox').checked,
|
||||||
|
fontSize: document.getElementById('fontSizeInput').value,
|
||||||
|
syntaxHighlight: document.getElementById('syntaxHighlightCheckbox').checked,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送消息到扩展
|
||||||
|
vscode.postMessage({
|
||||||
|
command: 'saveGeneralSettings',
|
||||||
|
settings: settings
|
||||||
|
});
|
||||||
|
|
||||||
|
// 显示保存成功提示
|
||||||
|
console.log('通用设置已保存', settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置通用设置
|
||||||
|
function resetGeneralSettings() {
|
||||||
|
document.getElementById('themeSelect').value = 'auto';
|
||||||
|
document.getElementById('languageSelect').value = 'zh-CN';
|
||||||
|
document.getElementById('autoSaveCheckbox').checked = true;
|
||||||
|
document.getElementById('showTimestampCheckbox').checked = false;
|
||||||
|
document.getElementById('fontSizeInput').value = '14';
|
||||||
|
document.getElementById('syntaxHighlightCheckbox').checked = true;
|
||||||
|
|
||||||
|
console.log('通用设置已重置为默认值');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载通用设置
|
||||||
|
function loadGeneralSettings(settings) {
|
||||||
|
if (!settings) return;
|
||||||
|
|
||||||
|
if (settings.theme) {
|
||||||
|
document.getElementById('themeSelect').value = settings.theme;
|
||||||
|
}
|
||||||
|
if (settings.language) {
|
||||||
|
document.getElementById('languageSelect').value = settings.language;
|
||||||
|
}
|
||||||
|
if (settings.autoSave !== undefined) {
|
||||||
|
document.getElementById('autoSaveCheckbox').checked = settings.autoSave;
|
||||||
|
}
|
||||||
|
if (settings.showTimestamp !== undefined) {
|
||||||
|
document.getElementById('showTimestampCheckbox').checked = settings.showTimestamp;
|
||||||
|
}
|
||||||
|
if (settings.fontSize) {
|
||||||
|
document.getElementById('fontSizeInput').value = settings.fontSize;
|
||||||
|
}
|
||||||
|
if (settings.syntaxHighlight !== undefined) {
|
||||||
|
document.getElementById('syntaxHighlightCheckbox').checked = settings.syntaxHighlight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
@ -103,7 +103,7 @@ export function getInputAreaStyles(): string {
|
|||||||
/* 居中模式:未发起对话时 */
|
/* 居中模式:未发起对话时 */
|
||||||
.input-area.centered {
|
.input-area.centered {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 55%;
|
top: 60%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
width: calc(100% - 40px);
|
width: calc(100% - 40px);
|
||||||
@ -300,7 +300,6 @@ export function getInputAreaScript(): string {
|
|||||||
${getContextDisplayScript()}
|
${getContextDisplayScript()}
|
||||||
${getContextCompressScript()}
|
${getContextCompressScript()}
|
||||||
${getOptimizeButtonScript()}
|
${getOptimizeButtonScript()}
|
||||||
${getExampleShowcaseScript()}
|
|
||||||
|
|
||||||
// 对话状态管理
|
// 对话状态管理
|
||||||
let isConversationActive = false;
|
let isConversationActive = false;
|
||||||
@ -310,6 +309,8 @@ export function getInputAreaScript(): string {
|
|||||||
let hasCheckedWorkspace = false; // 是否已经检测过工作区
|
let hasCheckedWorkspace = false; // 是否已经检测过工作区
|
||||||
let hasWorkspace = true; // 工作区状态
|
let hasWorkspace = true; // 工作区状态
|
||||||
|
|
||||||
|
${getExampleShowcaseScript()}
|
||||||
|
|
||||||
// 切换输入框布局模式
|
// 切换输入框布局模式
|
||||||
function updateInputAreaLayout() {
|
function updateInputAreaLayout() {
|
||||||
const inputArea = document.getElementById('inputArea');
|
const inputArea = document.getElementById('inputArea');
|
||||||
|
|||||||
394
src/views/moreOptionsComponent.ts
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
/**
|
||||||
|
* 更多选项组件
|
||||||
|
* 包含用户手册和用户反馈入口
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取更多选项组件的 HTML 内容
|
||||||
|
*/
|
||||||
|
export function getMoreOptionsComponentContent(): string {
|
||||||
|
return `
|
||||||
|
<div class="more-options-wrapper">
|
||||||
|
<!-- 更多选项下拉面板 -->
|
||||||
|
<div class="more-options-dropdown" id="moreOptionsDropdown">
|
||||||
|
<div class="more-options-content">
|
||||||
|
<div class="more-options-header">
|
||||||
|
<span class="more-options-title">更多选项</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="more-options-body">
|
||||||
|
<div class="more-option-item" id="userManualOption">
|
||||||
|
<div class="option-icon">
|
||||||
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="option-text">
|
||||||
|
<div class="option-label">用户手册</div>
|
||||||
|
<div class="option-desc">查看使用文档和帮助</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="more-option-item" id="userFeedbackOption">
|
||||||
|
<div class="option-icon">
|
||||||
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 12h-2v-2h2v2zm0-4h-2V6h2v4z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="option-text">
|
||||||
|
<div class="option-label">用户反馈</div>
|
||||||
|
<div class="option-desc">提交问题和建议</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 用户反馈二维码弹窗 -->
|
||||||
|
<div class="feedback-qrcode-modal" id="feedbackQRCodeModal">
|
||||||
|
<div class="feedback-qrcode-overlay" onclick="closeFeedbackQRCode()"></div>
|
||||||
|
<div class="feedback-qrcode-content">
|
||||||
|
<div class="feedback-qrcode-header">
|
||||||
|
<span class="feedback-qrcode-title">用户反馈</span>
|
||||||
|
<button class="feedback-qrcode-close" onclick="closeFeedbackQRCode()">
|
||||||
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="feedback-qrcode-body">
|
||||||
|
<img class="feedback-qrcode-image" id="feedbackQRCodeImage" alt="微信二维码" />
|
||||||
|
<p class="feedback-qrcode-text">扫描二维码添加微信反馈</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取更多选项组件的 CSS 样式
|
||||||
|
*/
|
||||||
|
export function getMoreOptionsComponentStyles(): string {
|
||||||
|
return `
|
||||||
|
.more-options-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 更多选项下拉面板 */
|
||||||
|
.more-options-dropdown {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 8px);
|
||||||
|
right: 0;
|
||||||
|
z-index: 10000;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-options-dropdown.active {
|
||||||
|
display: block;
|
||||||
|
animation: dropdownSlideIn 0.15s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dropdownSlideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-8px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-options-content {
|
||||||
|
background: var(--vscode-dropdown-background);
|
||||||
|
border: 1px solid var(--vscode-dropdown-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-options-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-options-body {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-option-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s ease;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-option-item:hover {
|
||||||
|
background: var(--vscode-list-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-option-item:active {
|
||||||
|
background: var(--vscode-list-activeSelectionBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-icon svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-desc {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 用户反馈二维码弹窗 */
|
||||||
|
.feedback-qrcode-modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 20000;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-qrcode-modal.active {
|
||||||
|
display: flex;
|
||||||
|
animation: fadeIn 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-qrcode-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-qrcode-content {
|
||||||
|
position: relative;
|
||||||
|
background: var(--vscode-editor-background);
|
||||||
|
border: 1px solid var(--vscode-widget-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
||||||
|
max-width: 400px;
|
||||||
|
width: 90%;
|
||||||
|
animation: slideUp 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-qrcode-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid var(--vscode-widget-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-qrcode-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-qrcode-close {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: background 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-qrcode-close:hover {
|
||||||
|
background: var(--vscode-toolbar-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-qrcode-close svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-qrcode-body {
|
||||||
|
padding: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-qrcode-image {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
border: 1px solid var(--vscode-widget-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-qrcode-text {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取更多选项组件的 JavaScript 脚本
|
||||||
|
*/
|
||||||
|
export function getMoreOptionsComponentScript(): string {
|
||||||
|
return `
|
||||||
|
// 切换更多选项下拉面板
|
||||||
|
function toggleMoreOptionsDropdown() {
|
||||||
|
const dropdown = document.getElementById('moreOptionsDropdown');
|
||||||
|
const moreButton = document.querySelector('.more-button');
|
||||||
|
|
||||||
|
if (dropdown) {
|
||||||
|
const isActive = dropdown.classList.contains('active');
|
||||||
|
if (isActive) {
|
||||||
|
dropdown.classList.remove('active');
|
||||||
|
if (moreButton) {
|
||||||
|
moreButton.classList.remove('active');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dropdown.classList.add('active');
|
||||||
|
if (moreButton) {
|
||||||
|
moreButton.classList.add('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭更多选项下拉面板
|
||||||
|
function closeMoreOptionsDropdown() {
|
||||||
|
const dropdown = document.getElementById('moreOptionsDropdown');
|
||||||
|
const moreButton = document.querySelector('.more-button');
|
||||||
|
|
||||||
|
if (dropdown) {
|
||||||
|
dropdown.classList.remove('active');
|
||||||
|
}
|
||||||
|
if (moreButton) {
|
||||||
|
moreButton.classList.remove('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开用户手册
|
||||||
|
function openUserManual() {
|
||||||
|
console.log('打开用户手册');
|
||||||
|
vscode.postMessage({ command: 'openUserManual' });
|
||||||
|
closeMoreOptionsDropdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开用户反馈
|
||||||
|
function openUserFeedback() {
|
||||||
|
console.log('打开用户反馈');
|
||||||
|
vscode.postMessage({ command: 'openUserFeedback' });
|
||||||
|
closeMoreOptionsDropdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示用户反馈二维码弹窗
|
||||||
|
function showFeedbackQRCode() {
|
||||||
|
const modal = document.getElementById('feedbackQRCodeModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.add('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭用户反馈二维码弹窗
|
||||||
|
function closeFeedbackQRCode() {
|
||||||
|
const modal = document.getElementById('feedbackQRCodeModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.remove('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定更多选项事件
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// 绑定用户手册选项
|
||||||
|
const userManualOption = document.getElementById('userManualOption');
|
||||||
|
if (userManualOption) {
|
||||||
|
userManualOption.addEventListener('click', openUserManual);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定用户反馈选项
|
||||||
|
const userFeedbackOption = document.getElementById('userFeedbackOption');
|
||||||
|
if (userFeedbackOption) {
|
||||||
|
userFeedbackOption.addEventListener('click', openUserFeedback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击页面其他地方关闭下拉面板
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const dropdown = document.getElementById('moreOptionsDropdown');
|
||||||
|
const moreButton = document.querySelector('.more-button');
|
||||||
|
const moreContainer = document.querySelector('.more-container');
|
||||||
|
|
||||||
|
if (dropdown && dropdown.classList.contains('active')) {
|
||||||
|
// 如果点击的不是更多按钮和下拉面板内容,则关闭
|
||||||
|
if (!moreContainer?.contains(e.target)) {
|
||||||
|
closeMoreOptionsDropdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 阻止下拉面板内容点击事件冒泡
|
||||||
|
const dropdownContent = document.querySelector('.more-options-content');
|
||||||
|
if (dropdownContent) {
|
||||||
|
dropdownContent.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
}
|
||||||
177
src/views/rulesSettingsComponent.ts
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
/**
|
||||||
|
* 获取规则设置组件的 HTML 内容
|
||||||
|
*/
|
||||||
|
export function getRulesSettingsComponentContent(): string {
|
||||||
|
return `
|
||||||
|
<div class="rules-settings">
|
||||||
|
<h3 class="settings-section-title">规则设置</h3>
|
||||||
|
|
||||||
|
<div class="settings-section">
|
||||||
|
<div class="settings-item">
|
||||||
|
<div class="settings-item-header">
|
||||||
|
<label class="settings-item-label">启用自定义规则</label>
|
||||||
|
<span class="settings-item-description">使用自定义规则来控制 AI 行为</span>
|
||||||
|
</div>
|
||||||
|
<label class="settings-switch">
|
||||||
|
<input type="checkbox" id="enableCustomRulesCheckbox" checked>
|
||||||
|
<span class="settings-switch-slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-section">
|
||||||
|
<h4 class="settings-subsection-title">系统规则</h4>
|
||||||
|
<div class="rules-textarea-container">
|
||||||
|
<textarea
|
||||||
|
class="rules-textarea"
|
||||||
|
id="systemRulesTextarea"
|
||||||
|
placeholder="在此输入系统规则,例如: - 始终使用中文回复 - 代码注释要详细 - 遵循项目编码规范"
|
||||||
|
rows="8"
|
||||||
|
></textarea>
|
||||||
|
<div class="rules-textarea-hint">
|
||||||
|
系统规则会在每次对话开始时应用
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-section">
|
||||||
|
<h4 class="settings-subsection-title">代码生成规则</h4>
|
||||||
|
<div class="rules-textarea-container">
|
||||||
|
<textarea
|
||||||
|
class="rules-textarea"
|
||||||
|
id="codeRulesTextarea"
|
||||||
|
placeholder="在此输入代码生成规则,例如: - 使用 TypeScript 严格模式 - 函数命名使用驼峰命名法 - 添加必要的错误处理"
|
||||||
|
rows="8"
|
||||||
|
></textarea>
|
||||||
|
<div class="rules-textarea-hint">
|
||||||
|
这些规则会在生成代码时应用
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-section">
|
||||||
|
<h4 class="settings-subsection-title">Verilog 规则</h4>
|
||||||
|
<div class="rules-textarea-container">
|
||||||
|
<textarea
|
||||||
|
class="rules-textarea"
|
||||||
|
id="verilogRulesTextarea"
|
||||||
|
placeholder="在此输入 Verilog 代码规则,例如: - 使用非阻塞赋值 (<=) 在时序逻辑中 - 模块命名使用小写加下划线 - 添加详细的端口注释"
|
||||||
|
rows="8"
|
||||||
|
></textarea>
|
||||||
|
<div class="rules-textarea-hint">
|
||||||
|
这些规则会在生成 Verilog 代码时应用
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-actions">
|
||||||
|
<button class="settings-button settings-button-primary" onclick="saveRulesSettings()">
|
||||||
|
保存规则
|
||||||
|
</button>
|
||||||
|
<button class="settings-button settings-button-secondary" onclick="resetRulesSettings()">
|
||||||
|
重置为默认
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取规则设置组件的 CSS 样式
|
||||||
|
*/
|
||||||
|
export function getRulesSettingsComponentStyles(): string {
|
||||||
|
return `
|
||||||
|
.rules-settings {
|
||||||
|
max-width: 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-textarea-container {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: var(--vscode-editor-font-family);
|
||||||
|
line-height: 1.5;
|
||||||
|
resize: vertical;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-textarea:focus {
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-textarea::placeholder {
|
||||||
|
color: var(--vscode-input-placeholderForeground);
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-textarea-hint {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取规则设置组件的 JavaScript 脚本
|
||||||
|
*/
|
||||||
|
export function getRulesSettingsComponentScript(): string {
|
||||||
|
return `
|
||||||
|
// 保存规则设置
|
||||||
|
function saveRulesSettings() {
|
||||||
|
const settings = {
|
||||||
|
enableCustomRules: document.getElementById('enableCustomRulesCheckbox').checked,
|
||||||
|
systemRules: document.getElementById('systemRulesTextarea').value,
|
||||||
|
codeRules: document.getElementById('codeRulesTextarea').value,
|
||||||
|
verilogRules: document.getElementById('verilogRulesTextarea').value,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送消息到扩展
|
||||||
|
vscode.postMessage({
|
||||||
|
command: 'saveRulesSettings',
|
||||||
|
settings: settings
|
||||||
|
});
|
||||||
|
|
||||||
|
// 显示保存成功提示
|
||||||
|
console.log('规则设置已保存', settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置规则设置
|
||||||
|
function resetRulesSettings() {
|
||||||
|
document.getElementById('enableCustomRulesCheckbox').checked = true;
|
||||||
|
document.getElementById('systemRulesTextarea').value = '';
|
||||||
|
document.getElementById('codeRulesTextarea').value = '';
|
||||||
|
document.getElementById('verilogRulesTextarea').value = '';
|
||||||
|
|
||||||
|
console.log('规则设置已重置为默认值');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载规则设置
|
||||||
|
function loadRulesSettings(settings) {
|
||||||
|
if (!settings) return;
|
||||||
|
|
||||||
|
if (settings.enableCustomRules !== undefined) {
|
||||||
|
document.getElementById('enableCustomRulesCheckbox').checked = settings.enableCustomRules;
|
||||||
|
}
|
||||||
|
if (settings.systemRules) {
|
||||||
|
document.getElementById('systemRulesTextarea').value = settings.systemRules;
|
||||||
|
}
|
||||||
|
if (settings.codeRules) {
|
||||||
|
document.getElementById('codeRulesTextarea').value = settings.codeRules;
|
||||||
|
}
|
||||||
|
if (settings.verilogRules) {
|
||||||
|
document.getElementById('verilogRulesTextarea').value = settings.verilogRules;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
248
src/views/settingsComponent.ts
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
import {
|
||||||
|
getGeneralSettingsComponentContent,
|
||||||
|
getGeneralSettingsComponentStyles,
|
||||||
|
getGeneralSettingsComponentScript,
|
||||||
|
} from "./generalSettingsComponent";
|
||||||
|
import {
|
||||||
|
getRulesSettingsComponentContent,
|
||||||
|
getRulesSettingsComponentStyles,
|
||||||
|
getRulesSettingsComponentScript,
|
||||||
|
} from "./rulesSettingsComponent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取设置面板的 HTML 内容
|
||||||
|
*/
|
||||||
|
export function getSettingsComponentContent(): string {
|
||||||
|
return `
|
||||||
|
<div class="settings-modal" id="settingsModal">
|
||||||
|
<div class="settings-modal-overlay" onclick="closeSettingsModal()"></div>
|
||||||
|
<div class="settings-modal-content">
|
||||||
|
<div class="settings-modal-header">
|
||||||
|
<h2 class="settings-modal-title">设置</h2>
|
||||||
|
<button class="settings-modal-close" onclick="closeSettingsModal()" title="关闭">
|
||||||
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-modal-body">
|
||||||
|
<div class="settings-nav">
|
||||||
|
<button class="settings-nav-item active" data-tab="general" onclick="switchSettingsTab('general')">
|
||||||
|
通用
|
||||||
|
</button>
|
||||||
|
<button class="settings-nav-item" data-tab="rules" onclick="switchSettingsTab('rules')">
|
||||||
|
规则
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-content">
|
||||||
|
<div class="settings-tab-content active" id="generalSettings">
|
||||||
|
${getGeneralSettingsComponentContent()}
|
||||||
|
</div>
|
||||||
|
<div class="settings-tab-content" id="rulesSettings">
|
||||||
|
${getRulesSettingsComponentContent()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取设置面板的 CSS 样式
|
||||||
|
*/
|
||||||
|
export function getSettingsComponentStyles(): string {
|
||||||
|
return `
|
||||||
|
.settings-modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-modal.active {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-modal-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-modal-content {
|
||||||
|
position: relative;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 800px;
|
||||||
|
height: 80%;
|
||||||
|
max-height: 600px;
|
||||||
|
background: var(--vscode-editor-background);
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-modal-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-modal-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-modal-close {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-modal-close:hover {
|
||||||
|
background: var(--vscode-toolbar-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-modal-close svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-modal-body {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-nav {
|
||||||
|
width: 180px;
|
||||||
|
padding: 16px 0;
|
||||||
|
border-right: 1px solid var(--vscode-panel-border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-nav-item {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-nav-item:hover {
|
||||||
|
background: var(--vscode-list-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-nav-item.active {
|
||||||
|
background: var(--vscode-list-activeSelectionBackground);
|
||||||
|
color: var(--vscode-list-activeSelectionForeground);
|
||||||
|
border-left-color: var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tab-content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tab-content.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
${getGeneralSettingsComponentStyles()}
|
||||||
|
${getRulesSettingsComponentStyles()}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取设置面板的 JavaScript 脚本
|
||||||
|
*/
|
||||||
|
export function getSettingsComponentScript(): string {
|
||||||
|
return `
|
||||||
|
${getGeneralSettingsComponentScript()}
|
||||||
|
${getRulesSettingsComponentScript()}
|
||||||
|
|
||||||
|
// 打开设置面板
|
||||||
|
function openSettingsModal() {
|
||||||
|
const modal = document.getElementById('settingsModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.add('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭设置面板
|
||||||
|
function closeSettingsModal() {
|
||||||
|
const modal = document.getElementById('settingsModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.remove('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换设置标签页
|
||||||
|
function switchSettingsTab(tabName) {
|
||||||
|
// 更新导航项状态
|
||||||
|
const navItems = document.querySelectorAll('.settings-nav-item');
|
||||||
|
navItems.forEach(item => {
|
||||||
|
if (item.dataset.tab === tabName) {
|
||||||
|
item.classList.add('active');
|
||||||
|
} else {
|
||||||
|
item.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新内容区域
|
||||||
|
const tabContents = document.querySelectorAll('.settings-tab-content');
|
||||||
|
tabContents.forEach(content => {
|
||||||
|
if (content.id === tabName + 'Settings') {
|
||||||
|
content.classList.add('active');
|
||||||
|
} else {
|
||||||
|
content.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阻止点击模态框内容时关闭
|
||||||
|
document.addEventListener('click', (event) => {
|
||||||
|
const modalContent = document.querySelector('.settings-modal-content');
|
||||||
|
if (modalContent && modalContent.contains(event.target)) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
}
|
||||||
@ -14,26 +14,53 @@ export function getUserInfoComponentContent(): string {
|
|||||||
<div class="user-detail-dropdown" id="userDetailDropdown">
|
<div class="user-detail-dropdown" id="userDetailDropdown">
|
||||||
<div class="user-detail-content">
|
<div class="user-detail-content">
|
||||||
<div class="user-detail-header">
|
<div class="user-detail-header">
|
||||||
<div class="user-avatar-small">
|
<div class="user-info-row">
|
||||||
|
<div class="user-avatar-small clickable" id="userAvatarClickable">
|
||||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"/>
|
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-name-tier">
|
<div class="user-name-tier">
|
||||||
<div class="user-detail-name" id="userDetailName">加载中...</div>
|
<div class="user-detail-name clickable" id="userDetailName">加载中...</div>
|
||||||
<img class="tier-icon-inline" id="tierIconInline" style="display: none;" />
|
<img class="tier-icon-inline" id="tierIconInline" style="display: none;" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 升级到Pro按钮 (仅BASIC会员显示) -->
|
||||||
|
<div class="upgrade-pro-wrapper" id="upgradeProWrapper" style="display: none;">
|
||||||
|
<button class="upgrade-pro-btn" id="upgradeProBtn">升级到 Pro</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="user-detail-body">
|
<div class="user-detail-body">
|
||||||
<div class="user-detail-item">
|
<div class="user-detail-item">
|
||||||
<span class="detail-label">剩余 Credits</span>
|
<span class="detail-label">剩余 Credits</span>
|
||||||
<span class="detail-value" id="creditsDetail">-</span>
|
<span class="detail-value" id="creditsDetail">-</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="user-detail-item logout-item" id="logoutItem">
|
||||||
|
<span class="detail-label">账户管理</span>
|
||||||
|
<span class="detail-value logout-link">退出登录</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 退出登录确认对话框 -->
|
||||||
|
<div class="logout-confirm-modal" id="logoutConfirmModal">
|
||||||
|
<div class="logout-confirm-overlay"></div>
|
||||||
|
<div class="logout-confirm-content">
|
||||||
|
<div class="logout-confirm-header">
|
||||||
|
<h3>确认退出</h3>
|
||||||
|
</div>
|
||||||
|
<div class="logout-confirm-body">
|
||||||
|
<p>确定要退出登录吗?</p>
|
||||||
|
</div>
|
||||||
|
<div class="logout-confirm-footer">
|
||||||
|
<button class="logout-confirm-btn logout-cancel-btn" id="logoutCancelBtn">取消</button>
|
||||||
|
<button class="logout-confirm-btn logout-ok-btn" id="logoutOkBtn">确定</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,12 +111,18 @@ export function getUserInfoComponentStyles(): string {
|
|||||||
.user-detail-header {
|
.user-detail-header {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
background: linear-gradient(135deg, rgba(0, 122, 204, 0.1) 0%, rgba(88, 166, 255, 0.05) 100%);
|
background: linear-gradient(135deg, rgba(0, 122, 204, 0.1) 0%, rgba(88, 166, 255, 0.05) 100%);
|
||||||
border-bottom: 1px solid var(--vscode-widget-border);
|
border-bottom: 1px solid var(--vscode-widget-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-info-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.user-avatar-small {
|
.user-avatar-small {
|
||||||
width: 26px;
|
width: 26px;
|
||||||
height: 26px;
|
height: 26px;
|
||||||
@ -100,6 +133,16 @@ export function getUserInfoComponentStyles(): string {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
box-shadow: 0 2px 8px rgba(0, 122, 204, 0.3);
|
box-shadow: 0 2px 8px rgba(0, 122, 204, 0.3);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-avatar-small.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-avatar-small.clickable:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 122, 204, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-avatar-small svg {
|
.user-avatar-small svg {
|
||||||
@ -119,6 +162,16 @@ export function getUserInfoComponentStyles(): string {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--vscode-foreground);
|
color: var(--vscode-foreground);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-detail-name.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-detail-name.clickable:hover {
|
||||||
|
color: #007acc;
|
||||||
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier-icon-inline {
|
.tier-icon-inline {
|
||||||
@ -152,6 +205,19 @@ export function getUserInfoComponentStyles(): string {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logout-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-item:hover {
|
||||||
|
background: var(--vscode-list-hoverBackground);
|
||||||
|
border-color: rgba(204, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-item:hover .logout-link {
|
||||||
|
color: #f48771;
|
||||||
|
}
|
||||||
|
|
||||||
.detail-label {
|
.detail-label {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@ -168,6 +234,11 @@ export function getUserInfoComponentStyles(): string {
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logout-link {
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
.tier-icon-large {
|
.tier-icon-large {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
@ -180,6 +251,136 @@ export function getUserInfoComponentStyles(): string {
|
|||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.upgrade-pro-wrapper {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upgrade-pro-btn {
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 12px;
|
||||||
|
background: linear-gradient(135deg, #007acc 0%, #58a6ff 100%);
|
||||||
|
color: #ffffff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 122, 204, 0.2);
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upgrade-pro-btn:hover {
|
||||||
|
background: linear-gradient(135deg, #0098ff 0%, #6bb6ff 100%);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 122, 204, 0.4);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.upgrade-pro-btn:active {
|
||||||
|
transform: translateY(0) scale(0.98);
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 122, 204, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 退出登录确认对话框 */
|
||||||
|
.logout-confirm-modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 20000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-confirm-modal.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-confirm-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-confirm-content {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: var(--vscode-editor-background);
|
||||||
|
border: 1px solid var(--vscode-widget-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
min-width: 320px;
|
||||||
|
max-width: 400px;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-confirm-header {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid var(--vscode-widget-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-confirm-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-confirm-body {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-confirm-body p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-confirm-footer {
|
||||||
|
padding: 12px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
border-top: 1px solid var(--vscode-widget-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-confirm-btn {
|
||||||
|
padding: 6px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-cancel-btn {
|
||||||
|
background: var(--vscode-button-secondaryBackground);
|
||||||
|
color: var(--vscode-button-secondaryForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-cancel-btn:hover {
|
||||||
|
background: var(--vscode-button-secondaryHoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-ok-btn {
|
||||||
|
background: #f48771;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-ok-btn:hover {
|
||||||
|
background: #e67361;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-confirm-btn:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,6 +415,44 @@ export function getUserInfoComponentScript(): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 退出登录
|
||||||
|
function logout() {
|
||||||
|
console.log("显示退出登录确认对话框");
|
||||||
|
// 显示确认对话框
|
||||||
|
const modal = document.getElementById('logoutConfirmModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.add('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认退出登录
|
||||||
|
function confirmLogout() {
|
||||||
|
console.log("确认退出登录");
|
||||||
|
vscode.postMessage({ command: 'logout' });
|
||||||
|
// 关闭确认对话框
|
||||||
|
closeLogoutConfirmModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭退出登录确认对话框
|
||||||
|
function closeLogoutConfirmModal() {
|
||||||
|
const modal = document.getElementById('logoutConfirmModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.remove('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转到 IC Coder 官网
|
||||||
|
function openICCoder() {
|
||||||
|
console.log("跳转到 IC Coder 官网");
|
||||||
|
vscode.postMessage({ command: 'openICCoder' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 升级到Pro
|
||||||
|
function upgradeToPro() {
|
||||||
|
console.log("升级到 Pro - 跳转到 IC Coder 官网");
|
||||||
|
vscode.postMessage({ command: 'openExternalUrl', url: 'https://www.iccoder.com' });
|
||||||
|
}
|
||||||
|
|
||||||
// 关闭用户详情下拉面板
|
// 关闭用户详情下拉面板
|
||||||
function closeUserDetailModal() {
|
function closeUserDetailModal() {
|
||||||
const dropdown = document.getElementById('userDetailDropdown');
|
const dropdown = document.getElementById('userDetailDropdown');
|
||||||
@ -260,6 +499,19 @@ export function getUserInfoComponentScript(): string {
|
|||||||
} else {
|
} else {
|
||||||
console.warn('[UserInfoComponent] creditsDetail 元素未找到');
|
console.warn('[UserInfoComponent] creditsDetail 元素未找到');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 显示或隐藏升级到Pro按钮 (仅BASIC会员显示)
|
||||||
|
const upgradeProWrapper = document.getElementById('upgradeProWrapper');
|
||||||
|
const tierCode = currentUserInfo.membership?.tierCode;
|
||||||
|
if (upgradeProWrapper) {
|
||||||
|
if (tierCode === 'BASIC') {
|
||||||
|
upgradeProWrapper.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
upgradeProWrapper.style.display = 'none';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('[UserInfoComponent] upgradeProWrapper 元素未找到');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新用户信息显示
|
// 更新用户信息显示
|
||||||
@ -275,6 +527,66 @@ export function getUserInfoComponentScript(): string {
|
|||||||
|
|
||||||
// 绑定下拉面板事件
|
// 绑定下拉面板事件
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// 绑定退出登录卡片点击事件
|
||||||
|
const logoutItem = document.getElementById('logoutItem');
|
||||||
|
if (logoutItem) {
|
||||||
|
logoutItem.addEventListener('click', () => {
|
||||||
|
logout();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定退出登录确认对话框按钮
|
||||||
|
const logoutOkBtn = document.getElementById('logoutOkBtn');
|
||||||
|
if (logoutOkBtn) {
|
||||||
|
logoutOkBtn.addEventListener('click', () => {
|
||||||
|
confirmLogout();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const logoutCancelBtn = document.getElementById('logoutCancelBtn');
|
||||||
|
if (logoutCancelBtn) {
|
||||||
|
logoutCancelBtn.addEventListener('click', () => {
|
||||||
|
closeLogoutConfirmModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击遮罩层关闭对话框
|
||||||
|
const logoutConfirmModal = document.getElementById('logoutConfirmModal');
|
||||||
|
if (logoutConfirmModal) {
|
||||||
|
const overlay = logoutConfirmModal.querySelector('.logout-confirm-overlay');
|
||||||
|
if (overlay) {
|
||||||
|
overlay.addEventListener('click', () => {
|
||||||
|
closeLogoutConfirmModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定升级到Pro按钮
|
||||||
|
const upgradeProBtn = document.getElementById('upgradeProBtn');
|
||||||
|
if (upgradeProBtn) {
|
||||||
|
upgradeProBtn.addEventListener('click', () => {
|
||||||
|
upgradeToPro();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定头像点击事件
|
||||||
|
const userAvatar = document.getElementById('userAvatarClickable');
|
||||||
|
if (userAvatar) {
|
||||||
|
userAvatar.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
openICCoder();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定用户名点击事件
|
||||||
|
const userName = document.getElementById('userDetailName');
|
||||||
|
if (userName) {
|
||||||
|
userName.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
openICCoder();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 点击页面其他地方关闭下拉面板
|
// 点击页面其他地方关闭下拉面板
|
||||||
document.addEventListener('click', (e) => {
|
document.addEventListener('click', (e) => {
|
||||||
const dropdown = document.getElementById('userDetailDropdown');
|
const dropdown = document.getElementById('userDetailDropdown');
|
||||||
|
|||||||
@ -33,7 +33,8 @@ export function getWebviewContent(
|
|||||||
autoIconUri?: string,
|
autoIconUri?: string,
|
||||||
liteIconUri?: string,
|
liteIconUri?: string,
|
||||||
syIconUri?: string,
|
syIconUri?: string,
|
||||||
maxIconUri?: string
|
maxIconUri?: string,
|
||||||
|
qrCodeUri?: string
|
||||||
): string {
|
): string {
|
||||||
// 获取当前环境,只在 dev 和 test 环境下显示快速操作按钮
|
// 获取当前环境,只在 dev 和 test 环境下显示快速操作按钮
|
||||||
const currentEnv = getCurrentEnv();
|
const currentEnv = getCurrentEnv();
|
||||||
@ -72,7 +73,10 @@ export function getWebviewContent(
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.header h1 {
|
.header h1 {
|
||||||
color: var(--vscode-button-background);
|
background: linear-gradient(to right, #4A9EFF, #7CB8FF, #A8D0FF);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
margin: 0 0 8px 0;
|
margin: 0 0 8px 0;
|
||||||
}
|
}
|
||||||
.chat-container {
|
.chat-container {
|
||||||
@ -395,11 +399,11 @@ export function getWebviewContent(
|
|||||||
${getConversationHistoryBarContent()}
|
${getConversationHistoryBarContent()}
|
||||||
${getProgressBarContent()}
|
${getProgressBarContent()}
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div style="display: flex; align-items: center; justify-content: center; gap: 10px;">
|
<div style="display: flex; align-items: center; justify-content: center; gap: 15px;">
|
||||||
<img src="${iconUri}" alt="IC Coder" style="width: 28px; height: 28px;" />
|
<img src="${iconUri}" alt="IC Coder" style="width: 48px; height: 48px;" />
|
||||||
<h1 style="margin: 0;">IC Coder</h1>
|
<h1 style="margin: 0; font-size: 36px;">IC Coder</h1>
|
||||||
</div>
|
</div>
|
||||||
<p>专注于真实FPGA研发的Verilog智能体编程平台</p>
|
<p style="font-size: 16px; margin-top: 12px;">专注于真实FPGA研发的Verilog智能体编程平台</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="chat-container">
|
<div class="chat-container">
|
||||||
@ -439,6 +443,12 @@ export function getWebviewContent(
|
|||||||
let loadingIndicator = null;
|
let loadingIndicator = null;
|
||||||
let currentSegmentedMessage = null; // 当前分段消息容器
|
let currentSegmentedMessage = null; // 当前分段消息容器
|
||||||
|
|
||||||
|
// 设置二维码图片
|
||||||
|
const feedbackQRCodeImage = document.getElementById('feedbackQRCodeImage');
|
||||||
|
if (feedbackQRCodeImage && '${qrCodeUri}') {
|
||||||
|
feedbackQRCodeImage.src = '${qrCodeUri}';
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 模式选择器脚本(直接内联,避免模板字符串嵌套问题)==========
|
// ========== 模式选择器脚本(直接内联,避免模板字符串嵌套问题)==========
|
||||||
let currentMode = 'agent';
|
let currentMode = 'agent';
|
||||||
|
|
||||||
@ -597,11 +607,13 @@ export function getWebviewContent(
|
|||||||
tierName: message.userInfo.tierName,
|
tierName: message.userInfo.tierName,
|
||||||
tierIconUrl: message.tierIconUrl,
|
tierIconUrl: message.tierIconUrl,
|
||||||
registerTime: message.userInfo.registerTime || message.userInfo.createdAt,
|
registerTime: message.userInfo.registerTime || message.userInfo.createdAt,
|
||||||
credits: message.userInfo.credits
|
credits: message.userInfo.credits,
|
||||||
|
membership: message.userInfo.membership
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('[WebView] 显示用户信息:', userInfoData);
|
console.log('[WebView] 显示用户信息:', userInfoData);
|
||||||
console.log('[WebView] userInfoData.credits:', userInfoData.credits);
|
console.log('[WebView] userInfoData.credits:', userInfoData.credits);
|
||||||
|
console.log('[WebView] userInfoData.membership:', userInfoData.membership);
|
||||||
|
|
||||||
// 调用更新用户头像图标按钮的函数
|
// 调用更新用户头像图标按钮的函数
|
||||||
if (typeof updateUserAvatarIconButton === 'function') {
|
if (typeof updateUserAvatarIconButton === 'function') {
|
||||||
@ -612,6 +624,14 @@ export function getWebviewContent(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'showFeedbackQRCode':
|
||||||
|
// 显示用户反馈二维码弹窗
|
||||||
|
console.log('[WebView] 显示用户反馈二维码弹窗');
|
||||||
|
if (typeof showFeedbackQRCode === 'function') {
|
||||||
|
showFeedbackQRCode();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'resetSegmentedMessage':
|
case 'resetSegmentedMessage':
|
||||||
// 重置分段消息容器(停止对话时调用)
|
// 重置分段消息容器(停止对话时调用)
|
||||||
console.log('[WebView] 重置分段消息容器');
|
console.log('[WebView] 重置分段消息容器');
|
||||||
@ -635,6 +655,14 @@ export function getWebviewContent(
|
|||||||
if (typeof hasWorkspace !== 'undefined') {
|
if (typeof hasWorkspace !== 'undefined') {
|
||||||
hasWorkspace = message.hasWorkspace;
|
hasWorkspace = message.hasWorkspace;
|
||||||
console.log('[WebView] 工作区状态:', hasWorkspace);
|
console.log('[WebView] 工作区状态:', hasWorkspace);
|
||||||
|
|
||||||
|
// 如果有待发送的示例,且工作区存在,则发送
|
||||||
|
if (hasWorkspace && typeof pendingExampleIndex !== 'undefined' && pendingExampleIndex >= 0) {
|
||||||
|
if (typeof doSendExample === 'function') {
|
||||||
|
doSendExample(pendingExampleIndex);
|
||||||
|
pendingExampleIndex = -1; // 重置
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||