diff --git a/media/USER_MANUAL.md b/media/USER_MANUAL.md new file mode 100644 index 0000000..4b1eeed --- /dev/null +++ b/media/USER_MANUAL.md @@ -0,0 +1,251 @@ +# IC Coder 插件端用户手册 + +欢迎**宁德时代新能源科技股份有限公司**的各位专家使用 IC Coder 企业版! + +本手册旨在为贵公司提供插件的安装、配置及使用指导,帮助您快速上手并充分发挥工具的功能。 + +在使用过程中,如遇任何功能异常、性能问题或操作疑问,欢迎随时联系我们,我们将在第一时间为您提供支持。 + +若贵司有特定的业务场景或个性化功能需求,也可直接与我们沟通,我们会为贵司进行定制化开发。 + +| 功能 | 说明 | +| ----------------------- | ---------------------------------------------------- | +| 自研微调模型 | IC Coder 自研顶尖 Max 微调模型,专为 FPGA 开发 | +| 高质量 Verilog 代码生成 | 根据自然语言描述生成符合规范的 Verilog 代码 | +| 自动语法检查 | 自动检测代码语法错误并自动修复 | +| 自动仿真 | 内置编译器,自动编译和仿真 | +| 波形检查工具 | 内置 VCD 波形查看器,自动波形解析 | +| 自主检查迭代 | AI 自动检查代码问题并迭代优化 | +| Diff 功能 | 快速比较代码差异,追踪 AI 修改,可一键确认和撤销修改 | +| Code Action 快捷操作 | Ctrl+L 快捷键或右键菜单快速添加代码到对话 | +| 支持上下文连续对话 | 多轮对话,AI 记住之前的交互内容 | +| 会话历史管理 | 自动保存对话记录,随时恢复历史会话 | + +## IC Coder 快速入门指南 + +### 系统要求 + +- **Visual Studio Code**: 版本 >= 1.60.0 + +各位专家也可以通过这个链接下载VSCode [下载链接](https://code.visualstudio.com/) + +--- + +### 安装步骤 + +#### 步骤 1:通过 VSIX 文件安装(推荐) + +1. **获取安装包** + - 确保您已经获得 `iccoder-Trial-1.0.vsix` 文件 + +2. **打开 VS Code** + - 启动 Visual Studio Code + +3. **安装插件** + + 有以下三种安装方式: + + **方式 A:通过命令面板** + - 按 `Ctrl+Shift+P` (Windows/Linux) 或 `Cmd+Shift+P` (macOS) 打开命令面板 + - 输入 `Extensions: Install from VSIX...` + - 选择 `iccoder-Trial-1.0.vsix` 文件 + - 等待安装完成 + + ![安装方式1.png](./manual/安装方式1.png) + + **方式 B:通过扩展视图** + - 点击左侧活动栏的扩展图标(或按 `Ctrl+Shift+X`) + - 点击扩展视图右上角的 `...` (更多操作) + - 选择 `从 VSIX 安装...` + - 选择 `iccoder-Trial-1.0.vsix` 文件 + + ![安装方式2.png](./manual/安装方式2.png) + + **方式 C:通过命令行** + + ```bash + code --install-extension iccoder-Trial-1.0.vsix + ``` + +4. **重启 VS Code** + - 安装完成后,建议重启 VS Code 以确保插件正常加载 + +#### 步骤 2:打开 IC Coder 界面 + +**登录后会自动打开**,手动打开也有以下几种方式: + +**方式 1:通过侧边栏** + +- 点击左侧活动栏的 IC Coder 图标 +- 侧边栏会显示 IC Coder 聊天界面 + +![侧边栏打开.png](./manual/侧边栏打开.png) + +**方式 2:通过命令面板** + +- 按 `Ctrl+Shift+P` (Windows/Linux) 或 `Cmd+Shift+P` (macOS) +- 输入以下命令之一: + - `IC Coder: 打开聊天` - 打开聊天界面 + - `打开 IC Coder 助手` - 打开助手面板 + +![命令面板打开.png](./manual/命令面板打开.png) + +--- + +#### 步骤 3:开始使用 + +插件已预配置好后端服务,安装后即可直接使用,无需手动配置。 + +![聊天界面.png](./manual/聊天界面.png) + +### 故障排除 + +#### 问题 :插件无法安装 + +**症状**:安装 VSIX 文件时报错 + +#### 解决方案: + +- 确认 VS Code 版本 >= 1.60.0 + +- 检查 VSIX 文件是否完整(未损坏) + +- 尝试以管理员权限运行 VS Code + +- 清除 VS Code 缓存后重试 + +### 完整使用流程示例 + +下面通过一个完整的案例,展示如何使用 IC Coder 从需求到代码生成的全过程。 + +#### 步骤 1:输入设计需求 + +在对话框中输入设计需求,例如: + +``` +我需要设计一个 8 位加法器,要求有进位输入和进位输出 +``` + +点击**发送**按钮。 + +![输入需求.png](./manual/输入需求.png) + +#### 步骤 2:AI 询问补充信息 + +AI 会根据需求,询问一些关键的设计细节。例如: + +- 是否需要溢出检测? +- 时钟频率要求是多少? +- 是否需要流水线设计? + +您只需要根据实际需求**选择相应的选项或者直接输入需求**即可,AI 会根据您的选择生成最合适的设计方案。 + +#### 步骤 3:确认 AI 生成的任务列表 + +AI 会根据您的需求和补充信息,生成一个详细的任务列表(Todo List) + +![确认任务.png](./manual/确认任务.png) + +仔细查看任务列表,确认无误后点击**确认**按钮,AI 将开始执行。 + +#### 步骤 4:观察 AI 执行过程 + +AI 开始工作后,您可以在对话框中实时看到所有执行步骤: + +![观察执行过程.png](./manual/观察执行过程.png) + +每个步骤完成后,任务列表中对应的项目会被标记为完成状态。 + +#### 步骤 5:仿真运行与结果查看 + +当 AI 完成代码生成后,会自动运行仿真验证: + +![仿真运行结果.png](./manual/仿真运行结果.png) + +#### 步骤 6:查看生成的文件 + +所有生成的文件会自动保存到您的工作目录中: + +``` +project/ +├── src/ +│ └── tb_adder_8bit.v # RTL 设计文件 +├── sim/ +│ └── tb_adder_8bit # 测试平台文件 +└── tb_adder_8bit.vcd # 波形文件 +``` + +您可以在 VS Code 的文件资源管理器中直接打开这些文件进行查看或修改。 + +#### 步骤8:继续对话 + +如果您对给出的结果不太满意,您可以告诉IC Coder您想具体修改的地方或者文件 + +#### 使用流程总结 + +整个使用流程可以概括为: + +- **输入需求** → 在对话框中描述您的设计需求 +- **回答问题** → 根据 AI 的询问选择合适的选项 +- **确认任务** → 查看并确认 AI 生成的任务列表 +- **观察执行** → 实时查看 AI 的所有执行步骤 +- **查看结果** → 仿真成功后查看生成的文件 + +#### 使用提示 + +**如何描述需求更准确?** + +- **明确功能**:清楚说明模块要实现什么功能 +- **指定参数**:说明位宽、时钟频率等关键参数 +- **特殊要求**:如果有特殊的时序要求或接口规范,请明确说明 + +**示例:** + +``` +好的描述:设计一个 16 位的 FIFO,深度为 256,支持异步读写 +不够清晰:帮我写一个 FIFO +``` + +#### 常见问题 + +**Q: 仿真失败了怎么办?** +A: AI 会根据错误自动修复代码并重新仿真。 + +**Q: 可以修改生成的代码吗?** +A: 可以,可以直接编辑文件,然后告诉 AI 重新运行仿真。 + +**Q: 可以导入已有的代码吗?** +A: 可以,在工作区中打开对应的代码文件夹,然后直接在对话中告诉 AI 您要修改或优化哪个文件,AI 会读取并进行修改。 + +**Q: 如何查看 AI 的思考过程?** +A: 在执行过程中,AI 会实时显示每一步的操作和决策依据。 + +**Q: 如何保存对话历史?** +A: 对话历史会自动保存在本地,可以点击历史对话查看历史会话记录。 + +--- + +### 卸载插件 + +如需卸载插件: + +1. 打开扩展视图 +2. 找到 "IC Coder" 插件 +3. 点击卸载按钮 +4. 重启 VS Code + +--- + +### 注意事项 + +1. **需提前打开一个文件夹作为工作区**,否则会准确的为您服务 + + ![打开文件夹.png](./manual/打开文件夹.png) + +2. **开箱即用** + - 插件已预配置后端服务,无需手动设置 + - 安装后即可直接使用所有功能 + +--- + +**祝您使用愉快!如有问题欢迎反馈。** diff --git a/media/manual/仿真运行结果.png b/media/manual/仿真运行结果.png new file mode 100644 index 0000000..f9b3956 Binary files /dev/null and b/media/manual/仿真运行结果.png differ diff --git a/media/manual/侧边栏打开.png b/media/manual/侧边栏打开.png new file mode 100644 index 0000000..819ab94 Binary files /dev/null and b/media/manual/侧边栏打开.png differ diff --git a/media/manual/命令面板打开.png b/media/manual/命令面板打开.png new file mode 100644 index 0000000..7e8fc4d Binary files /dev/null and b/media/manual/命令面板打开.png differ diff --git a/media/manual/安装方式1.png b/media/manual/安装方式1.png new file mode 100644 index 0000000..4fc25b0 Binary files /dev/null and b/media/manual/安装方式1.png differ diff --git a/media/manual/安装方式2.png b/media/manual/安装方式2.png new file mode 100644 index 0000000..1bc22a7 Binary files /dev/null and b/media/manual/安装方式2.png differ diff --git a/media/manual/打开文件夹.png b/media/manual/打开文件夹.png new file mode 100644 index 0000000..aeb7a63 Binary files /dev/null and b/media/manual/打开文件夹.png differ diff --git a/media/manual/确认任务.png b/media/manual/确认任务.png new file mode 100644 index 0000000..dbd08ea Binary files /dev/null and b/media/manual/确认任务.png differ diff --git a/media/manual/聊天界面.png b/media/manual/聊天界面.png new file mode 100644 index 0000000..7bc0e4e Binary files /dev/null and b/media/manual/聊天界面.png differ diff --git a/media/manual/观察执行过程.png b/media/manual/观察执行过程.png new file mode 100644 index 0000000..70cc524 Binary files /dev/null and b/media/manual/观察执行过程.png differ diff --git a/media/manual/输入需求.png b/media/manual/输入需求.png new file mode 100644 index 0000000..93fbb5e Binary files /dev/null and b/media/manual/输入需求.png differ diff --git a/src/extension.ts b/src/extension.ts index 21ce5cd..6e013b6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,6 +2,7 @@ import * as vscode from "vscode"; import { ICViewProvider } from "./views/ICViewProvider"; import { showICHelperPanel } from "./panels/ICHelperPanel"; import { VCDViewerPanel, VCDViewerEditorProvider } from "./panels/VCDViewerPanel"; +import { UserManualPanel } from "./panels/UserManualPanel"; import { ChatHistoryManager } from "./utils/chatHistoryManager"; import { ICCoderAuthenticationProvider } from "./services/icCoderAuthProvider"; import { VCDFileServer } from "./services/vcdFileServer"; @@ -181,6 +182,14 @@ export async function activate(context: vscode.ExtensionContext) { } ); + // 注册命令:打开用户手册 + const openUserManualCommand = vscode.commands.registerCommand( + "ic-coder.openUserManual", + () => { + UserManualPanel.render(context.extensionUri); + } + ); + // 注册命令:用户登录 const loginCommand = vscode.commands.registerCommand( "ic-coder.login", @@ -425,6 +434,7 @@ export async function activate(context: vscode.ExtensionContext) { openChatCommand, openVCDViewerCommand, openVCDViewerInBrowserCommand, + openUserManualCommand, loginCommand, logoutCommand, changeInvitationCodeCommand, diff --git a/src/panels/ICHelperPanel.ts b/src/panels/ICHelperPanel.ts index a8c0666..ea8f368 100644 --- a/src/panels/ICHelperPanel.ts +++ b/src/panels/ICHelperPanel.ts @@ -492,7 +492,7 @@ export async function showICHelperPanel( break; case "openUserManual": // 打开用户手册 - vscode.env.openExternal(vscode.Uri.parse("https://www.iccoder.com")); + vscode.commands.executeCommand("ic-coder.openUserManual"); break; case "openUserFeedback": // 打开用户反馈二维码弹窗 diff --git a/src/panels/UserManualPanel.ts b/src/panels/UserManualPanel.ts new file mode 100644 index 0000000..e3f35e1 --- /dev/null +++ b/src/panels/UserManualPanel.ts @@ -0,0 +1,181 @@ +/** + * 用户手册只读预览面板 + */ + +import * as vscode from "vscode"; + +export class UserManualPanel { + public static currentPanel: UserManualPanel | undefined; + private readonly _panel: vscode.WebviewPanel; + private _disposables: vscode.Disposable[] = []; + + private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) { + this._panel = panel; + this._panel.onDidDispose(() => this.dispose(), null, this._disposables); + this._update(extensionUri); + } + + public static render(extensionUri: vscode.Uri) { + if (UserManualPanel.currentPanel) { + UserManualPanel.currentPanel._panel.reveal(vscode.ViewColumn.One); + } else { + const panel = vscode.window.createWebviewPanel( + "userManual", + "IC Coder 用户手册", + vscode.ViewColumn.One, + { + enableScripts: true, + localResourceRoots: [vscode.Uri.joinPath(extensionUri, "media")], + }, + ); + UserManualPanel.currentPanel = new UserManualPanel(panel, extensionUri); + } + } + + private async _update(extensionUri: vscode.Uri) { + const manualPath = vscode.Uri.joinPath( + extensionUri, + "media", + "USER_MANUAL.md", + ); + const markdown = await vscode.workspace.fs.readFile(manualPath); + const content = Buffer.from(markdown).toString("utf-8"); + this._panel.webview.html = await this._getHtmlContent( + content, + extensionUri, + ); + } + + private async _getHtmlContent( + markdown: string, + extensionUri: vscode.Uri, + ): Promise { + let inCodeBlock = false; + let inTable = false; + let tableRows: string[] = []; + const lines: string[] = []; + + // 先处理图片 + markdown = markdown.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, src) => { + const imgUri = this._panel.webview.asWebviewUri( + vscode.Uri.joinPath(extensionUri, "media", src), + ); + return `${alt}`; + }); + + markdown.split("\n").forEach((line) => { + // 代码块 + if (line.startsWith("```")) { + if (inCodeBlock) { + lines.push(""); + inCodeBlock = false; + } else { + lines.push("
");
+          inCodeBlock = true;
+        }
+        return;
+      }
+      if (inCodeBlock) {
+        lines.push(line);
+        return;
+      }
+
+      // 表格
+      if (line.startsWith("|")) {
+        if (!inTable) inTable = true;
+        tableRows.push(line);
+        return;
+      } else if (inTable) {
+        // 表格结束
+        const headers = tableRows[0]
+          .split("|")
+          .filter((c) => c.trim())
+          .map((h) => `${h.trim()}`)
+          .join("");
+        const body = tableRows
+          .slice(2)
+          .map(
+            (r) =>
+              "" +
+              r
+                .split("|")
+                .filter((c) => c.trim())
+                .map((c) => `${c.trim()}`)
+                .join("") +
+              "",
+          )
+          .join("");
+        lines.push(
+          `${headers}${body}
`, + ); + tableRows = []; + inTable = false; + } + + // 其他行 + if (line === "---") lines.push("
"); + else if (line.startsWith("#### ")) + lines.push(`

${line.slice(5)}

`); + else if (line.startsWith("### ")) lines.push(`

${line.slice(4)}

`); + else if (line.startsWith("## ")) lines.push(`

${line.slice(3)}

`); + else if (line.startsWith("# ")) lines.push(`

${line.slice(2)}

`); + else if (line.startsWith("- ")) + lines.push( + `
  • ${line.slice(2).replace(/\*\*(.+?)\*\*/g, "$1")}
  • `, + ); + else if (line.trim() === "") lines.push("

    "); + else + lines.push( + `

    ${line.replace(/\*\*(.+?)\*\*/g, "$1").replace(/\[(.+?)\]\((.+?)\)/g, '$1')}

    `, + ); + }); + + const html = lines + .join("\n") + .replace(/((?:
  • .*<\/li>\n?)+)/g, "
      $1
    "); + + return ` + + + + + +${html} +`; + } + + public dispose() { + UserManualPanel.currentPanel = undefined; + this._panel.dispose(); + while (this._disposables.length) { + this._disposables.pop()?.dispose(); + } + } +} diff --git a/src/utils/messageHandler.ts b/src/utils/messageHandler.ts index d5253b2..ff6e5fd 100644 --- a/src/utils/messageHandler.ts +++ b/src/utils/messageHandler.ts @@ -286,10 +286,18 @@ async function handleUserMessageWithBackend( }, onSegmentUpdate: (segments) => { + // 过滤掉包含 [调用工具:xxx] 的段落 + const filteredSegments = segments.filter(seg => { + if (seg.type === 'text' && typeof seg.content === 'string') { + return !/\[调用工具:.+?\]/.test(seg.content); + } + return true; + }); + // 实时发送段落更新,按后端返回顺序展示 panel.webview.postMessage({ command: "updateSegments", - segments: segments, + segments: filteredSegments, }); // 【离线部署模式】检测代码生成完成消息,模拟仿真流程 diff --git a/src/views/ICViewProvider.ts b/src/views/ICViewProvider.ts index 682f373..c507700 100644 --- a/src/views/ICViewProvider.ts +++ b/src/views/ICViewProvider.ts @@ -125,6 +125,10 @@ export function showICHelperPanel(context: vscode.ExtensionContext) { case "showWarning": vscode.window.showWarningMessage(message.message); break; + // 新增:打开用户手册 + case "openUserManual": + vscode.commands.executeCommand("ic-coder.openUserManual"); + break; // 新增:处理用户回答 case "submitAnswer": handleUserAnswer( @@ -224,6 +228,9 @@ export class ICViewProvider implements vscode.WebviewViewProvider { } else if (message.command === "openICCoder") { // 打开 IC Coder 官网 vscode.env.openExternal(vscode.Uri.parse('https://www.iccoder.com')); + } else if (message.command === "openUserManual") { + // 打开用户手册 + vscode.commands.executeCommand("ic-coder.openUserManual"); } else if (message.command === "openExternalUrl") { // 打开外部链接 if (message.url) { diff --git a/src/views/exampleShowcase.ts b/src/views/exampleShowcase.ts index b457e11..eaa2549 100644 --- a/src/views/exampleShowcase.ts +++ b/src/views/exampleShowcase.ts @@ -34,14 +34,6 @@ export function getExampleShowcaseContent(): string { - - `; } @@ -165,41 +157,6 @@ export function getExampleShowcaseStyles(): string { -webkit-box-orient: vertical; } - .web-link { - display: flex; - justify-content: center; - padding-top: 20px; - border-top: 1px solid var(--vscode-panel-border); - margin-top: 8px; - } - - .web-link-button { - display: flex; - align-items: center; - gap: 8px; - padding: 10px 20px; - background: transparent; - border: none; - text-decoration: none; - font-size: 14px; - font-weight: 600; - transition: all 0.2s ease; - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 50%, #a855f7 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - outline: none; - } - - .web-link-button:focus { - outline: none; - } - - .web-link-button:hover { - transform: translateY(-1px); - opacity: 0.8; - } - .link-icon { font-size: 16px; } @@ -208,10 +165,6 @@ export function getExampleShowcaseStyles(): string { font-size: 16px; transition: transform 0.2s ease; } - - .web-link-button:hover .link-arrow { - transform: translateX(3px); - } `; } diff --git a/src/views/messageArea.ts b/src/views/messageArea.ts index f6e2afd..135c393 100644 --- a/src/views/messageArea.ts +++ b/src/views/messageArea.ts @@ -382,7 +382,7 @@ export function getMessageAreaStyles(): string { } /* 低调显示的工具调用 - 移除边距和背景 */ .segment-tool.low-profile { - margin: 5px 0px; + margin: 25px 0px; padding: 0; background: none; } @@ -549,7 +549,7 @@ export function getMessageAreaStyles(): string { max-height: 0; } .tool-segment-description { - margin: 6px 0 0 0px; + margin: 25px 0 0 0px; font-size: 0.9rem; color: var(--vscode-descriptionForeground); line-height: 1.4; @@ -590,9 +590,9 @@ export function getMessageAreaStyles(): string { } .segment-question .question-option { padding: 8px 16px; - background: #007ACC; + background: #3d3f41; color: #ffffff; - border: 1px solid #007ACC; + border: 1px solid #474747; border-radius: 6px; cursor: pointer; transition: all 0.2s; @@ -1211,7 +1211,7 @@ export function getMessageAreaScript(): string { const optionsHtml = q.options.map(opt => { const isSelected = selectedAnswers.includes(opt); - return \`