Compare commits
11 Commits
feat/Delet
...
06573e37d7
| Author | SHA1 | Date | |
|---|---|---|---|
| 06573e37d7 | |||
| d740f4da44 | |||
| f24bd38ec7 | |||
| 45934baf0a | |||
| 4384ee53c5 | |||
| d89c326be5 | |||
| 2dccb4f871 | |||
| a9ddf3074e | |||
| db087bb184 | |||
| 5e9083041f | |||
| be0555d6bc |
@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
所有重要的项目变更都将记录在此文件中。
|
所有重要的项目变更都将记录在此文件中。
|
||||||
|
|
||||||
|
## [1.0.12] - 2026-03-06
|
||||||
|
|
||||||
|
### 新增
|
||||||
|
|
||||||
|
- 支持 AskUserQuestion 多问题和多选功能
|
||||||
|
|
||||||
## [1.0.9] - 2026-03-04
|
## [1.0.9] - 2026-03-04
|
||||||
|
|
||||||
### 优化
|
### 优化
|
||||||
|
|||||||
42
docs/code-to-chat-feature.md
Normal file
42
docs/code-to-chat-feature.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# 代码快速添加到对话功能
|
||||||
|
|
||||||
|
## 功能说明
|
||||||
|
|
||||||
|
选中代码后,通过右键菜单/小灯泡/快捷键(Ctrl+Shift+I),将代码作为上下文添加到聊天面板输入框上方。
|
||||||
|
|
||||||
|
## 实现方式
|
||||||
|
|
||||||
|
### 1. Code Action Provider
|
||||||
|
`src/providers/codeActionProvider.ts` - 提供小灯泡菜单选项
|
||||||
|
|
||||||
|
### 2. 命令注册
|
||||||
|
`src/extension.ts` - 注册 `ic-coder.addCodeToChat` 命令,发送消息到 webview
|
||||||
|
|
||||||
|
### 3. 全局引用
|
||||||
|
`src/panels/ICHelperPanel.ts` - 保存 panel 到 `(global as any).currentICHelperPanel`
|
||||||
|
|
||||||
|
### 4. 上下文显示
|
||||||
|
`src/views/contextDisplay.ts` - 添加 `code` 类型支持和 `addCodeContext` 消息处理
|
||||||
|
|
||||||
|
### 5. 配置
|
||||||
|
`package.json` - 配置命令、右键菜单、快捷键
|
||||||
|
|
||||||
|
## 用户体验
|
||||||
|
|
||||||
|
1. 选中代码
|
||||||
|
2. 右键/小灯泡/Ctrl+Shift+I
|
||||||
|
3. 代码显示为上下文项:`文件名.v:10-25` 📄
|
||||||
|
4. 输入问题发送(代码自动作为上下文)
|
||||||
|
|
||||||
|
## 数据结构
|
||||||
|
|
||||||
|
代码上下文存储为 JSON:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"fileName": "路径",
|
||||||
|
"startLine": 10,
|
||||||
|
"endLine": 25,
|
||||||
|
"code": "代码内容",
|
||||||
|
"languageId": "verilog"
|
||||||
|
}
|
||||||
|
```
|
||||||
55
docs/webpack-optimization.md
Normal file
55
docs/webpack-optimization.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Webpack 打包优化说明
|
||||||
|
|
||||||
|
## 优化内容
|
||||||
|
|
||||||
|
### 1. 自动模式切换
|
||||||
|
- 开发模式:保持源码可读性
|
||||||
|
- 生产模式:自动压缩代码
|
||||||
|
|
||||||
|
### 2. 性能优化
|
||||||
|
- **Tree Shaking**:移除未使用的代码
|
||||||
|
- **transpileOnly**:跳过类型检查,加快编译速度
|
||||||
|
- **自动清理**:每次打包自动删除旧文件
|
||||||
|
|
||||||
|
### 3. 体积监控
|
||||||
|
- 单文件超过 2MB 会发出警告
|
||||||
|
- 帮助及时发现打包体积问题
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 开发模式
|
||||||
|
```bash
|
||||||
|
# 编译(不压缩)
|
||||||
|
pnpm run compile
|
||||||
|
|
||||||
|
# 监听模式(自动重新编译)
|
||||||
|
pnpm run watch
|
||||||
|
```
|
||||||
|
|
||||||
|
### 生产模式
|
||||||
|
```bash
|
||||||
|
# Windows
|
||||||
|
set NODE_ENV=production && pnpm run package
|
||||||
|
|
||||||
|
# macOS/Linux
|
||||||
|
NODE_ENV=production pnpm run package
|
||||||
|
```
|
||||||
|
|
||||||
|
## 打包结果
|
||||||
|
|
||||||
|
- **输出目录**:`dist/`
|
||||||
|
- **入口文件**:`dist/extension.js`
|
||||||
|
- **静态资源**:`dist/assets/`
|
||||||
|
|
||||||
|
## 性能对比
|
||||||
|
|
||||||
|
| 模式 | 体积 | 编译速度 | Source Map |
|
||||||
|
|------|------|----------|------------|
|
||||||
|
| 开发 | 较大 | 快 | 完整 |
|
||||||
|
| 生产 | 小 | 较慢 | 隐藏 |
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. 开发时使用 `pnpm run watch`,修改代码自动重新编译
|
||||||
|
2. 发布前必须使用生产模式打包
|
||||||
|
3. 如果打包体积超过 2MB,检查是否引入了不必要的依赖
|
||||||
24
package.json
24
package.json
@ -2,7 +2,7 @@
|
|||||||
"name": "iccoder",
|
"name": "iccoder",
|
||||||
"displayName": "IC Coder: Agentic Verilog Platform",
|
"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": "1.0.11",
|
"version": "1.0.12",
|
||||||
"publisher": "ICCoderAgenticVerilogPlatform",
|
"publisher": "ICCoderAgenticVerilogPlatform",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.80.0"
|
"vscode": "^1.80.0"
|
||||||
@ -54,6 +54,28 @@
|
|||||||
"command": "ic-coder.testNotification",
|
"command": "ic-coder.testNotification",
|
||||||
"title": "测试系统通知",
|
"title": "测试系统通知",
|
||||||
"category": "IC Coder"
|
"category": "IC Coder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "ic-coder.addCodeToChat",
|
||||||
|
"title": "添加到 IC Coder 对话",
|
||||||
|
"category": "IC Coder"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"menus": {
|
||||||
|
"editor/context": [
|
||||||
|
{
|
||||||
|
"command": "ic-coder.addCodeToChat",
|
||||||
|
"when": "editorHasSelection",
|
||||||
|
"group": "9_cutcopypaste"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"keybindings": [
|
||||||
|
{
|
||||||
|
"command": "ic-coder.addCodeToChat",
|
||||||
|
"key": "ctrl+l",
|
||||||
|
"mac": "cmd+l",
|
||||||
|
"when": "editorTextFocus && editorHasSelection"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"viewsContainers": {
|
"viewsContainers": {
|
||||||
|
|||||||
118
src/extension.ts
118
src/extension.ts
@ -10,10 +10,42 @@ import { initCreditsService } from "./services/creditsService";
|
|||||||
import { isTokenExpired } from "./utils/jwtUtils";
|
import { isTokenExpired } from "./utils/jwtUtils";
|
||||||
import { NotificationService } from "./services/notificationService";
|
import { NotificationService } from "./services/notificationService";
|
||||||
import { InvitationService } from "./services/invitationService";
|
import { InvitationService } from "./services/invitationService";
|
||||||
|
import { ICCoderCodeActionProvider } from "./providers/codeActionProvider";
|
||||||
|
|
||||||
export async function activate(context: vscode.ExtensionContext) {
|
export async function activate(context: vscode.ExtensionContext) {
|
||||||
console.log("🎉 IC Coder 插件已激活!");
|
console.log("🎉 IC Coder 插件已激活!");
|
||||||
|
|
||||||
|
// 创建装饰类型(代码旁边的提示)
|
||||||
|
const decorationType = vscode.window.createTextEditorDecorationType({
|
||||||
|
after: {
|
||||||
|
contentText: ' Ctrl+L 添加到 IC Coder 对话',
|
||||||
|
color: '#888',
|
||||||
|
fontStyle: 'italic',
|
||||||
|
margin: '0 0 0 1em'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新装饰
|
||||||
|
const updateDecorations = () => {
|
||||||
|
const editor = vscode.window.activeTextEditor;
|
||||||
|
if (!editor) return;
|
||||||
|
|
||||||
|
if (!editor.selection.isEmpty) {
|
||||||
|
const range = new vscode.Range(editor.selection.end, editor.selection.end);
|
||||||
|
const decoration = { range };
|
||||||
|
editor.setDecorations(decorationType, [decoration]);
|
||||||
|
} else {
|
||||||
|
editor.setDecorations(decorationType, []);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
context.subscriptions.push(
|
||||||
|
vscode.window.onDidChangeTextEditorSelection(updateDecorations),
|
||||||
|
vscode.window.onDidChangeActiveTextEditor(updateDecorations)
|
||||||
|
);
|
||||||
|
|
||||||
|
updateDecorations();
|
||||||
|
|
||||||
// 初始化通知服务
|
// 初始化通知服务
|
||||||
const notificationService = NotificationService.getInstance(context);
|
const notificationService = NotificationService.getInstance(context);
|
||||||
console.log('[Extension] 通知服务已初始化');
|
console.log('[Extension] 通知服务已初始化');
|
||||||
@ -250,6 +282,81 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 注册命令:将选中代码添加到对话
|
||||||
|
const addCodeToChat = vscode.commands.registerCommand(
|
||||||
|
"ic-coder.addCodeToChat",
|
||||||
|
async () => {
|
||||||
|
console.log('[addCodeToChat] 命令触发');
|
||||||
|
const editor = vscode.window.activeTextEditor;
|
||||||
|
if (!editor) {
|
||||||
|
console.log('[addCodeToChat] 没有活动编辑器');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection = editor.selection;
|
||||||
|
const selectedText = editor.document.getText(selection);
|
||||||
|
|
||||||
|
if (!selectedText) {
|
||||||
|
vscode.window.showWarningMessage("请先选择代码");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileName = editor.document.fileName;
|
||||||
|
const startLine = selection.start.line + 1;
|
||||||
|
const endLine = selection.end.line + 1;
|
||||||
|
|
||||||
|
// 检查是否已有打开的面板
|
||||||
|
let panel = (global as any).currentICHelperPanel;
|
||||||
|
let needCreatePanel = false;
|
||||||
|
|
||||||
|
if (!panel) {
|
||||||
|
needCreatePanel = true;
|
||||||
|
} else {
|
||||||
|
// 尝试访问 webview,如果抛出异常说明已销毁
|
||||||
|
try {
|
||||||
|
const _ = panel.webview;
|
||||||
|
} catch (e) {
|
||||||
|
needCreatePanel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[addCodeToChat] 需要创建面板:', needCreatePanel);
|
||||||
|
|
||||||
|
if (needCreatePanel) {
|
||||||
|
console.log('[addCodeToChat] 正在打开面板...');
|
||||||
|
await showICHelperPanel(context);
|
||||||
|
panel = (global as any).currentICHelperPanel;
|
||||||
|
console.log('[addCodeToChat] 面板打开后状态:', panel ? '成功' : '失败');
|
||||||
|
|
||||||
|
// 如果面板仍未创建(如未登录),直接返回
|
||||||
|
if (!panel) {
|
||||||
|
console.log('[addCodeToChat] 面板创建失败,退出');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送代码上下文
|
||||||
|
console.log('[addCodeToChat] 准备发送代码到面板');
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
if (panel?.webview) {
|
||||||
|
console.log('[addCodeToChat] 发送 addCodeContext 消息');
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: 'addCodeContext',
|
||||||
|
fileName,
|
||||||
|
startLine,
|
||||||
|
endLine,
|
||||||
|
code: selectedText,
|
||||||
|
languageId: editor.document.languageId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('[addCodeToChat] 发送消息失败:', e);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// 注册命令:查看会话历史
|
// 注册命令:查看会话历史
|
||||||
// TODO: 这些命令需要根据新的任务架构重新实现
|
// TODO: 这些命令需要根据新的任务架构重新实现
|
||||||
// 暂时注释掉,等待重新实现
|
// 暂时注释掉,等待重新实现
|
||||||
@ -312,6 +419,13 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
// 注册 VCD 自定义编辑器
|
// 注册 VCD 自定义编辑器
|
||||||
const vcdEditorProvider = VCDViewerEditorProvider.register(context, vcdFileServer);
|
const vcdEditorProvider = VCDViewerEditorProvider.register(context, vcdFileServer);
|
||||||
|
|
||||||
|
// 注册 Code Action Provider
|
||||||
|
const codeActionProvider = vscode.languages.registerCodeActionsProvider(
|
||||||
|
{ scheme: 'file' },
|
||||||
|
new ICCoderCodeActionProvider(),
|
||||||
|
{ providedCodeActionKinds: [vscode.CodeActionKind.RefactorRewrite] }
|
||||||
|
);
|
||||||
|
|
||||||
// 添加到订阅
|
// 添加到订阅
|
||||||
context.subscriptions.push(
|
context.subscriptions.push(
|
||||||
openPanelCommand,
|
openPanelCommand,
|
||||||
@ -322,6 +436,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
logoutCommand,
|
logoutCommand,
|
||||||
changeInvitationCodeCommand,
|
changeInvitationCodeCommand,
|
||||||
testNotificationCommand,
|
testNotificationCommand,
|
||||||
|
addCodeToChat,
|
||||||
// testTrialUserCommand,
|
// testTrialUserCommand,
|
||||||
// testExpiredUserCommand,
|
// testExpiredUserCommand,
|
||||||
// TODO: 等待重新实现这些命令
|
// TODO: 等待重新实现这些命令
|
||||||
@ -332,7 +447,8 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
// clearHistoryCommand,
|
// clearHistoryCommand,
|
||||||
// searchSessionCommand,
|
// searchSessionCommand,
|
||||||
viewRegistration,
|
viewRegistration,
|
||||||
vcdEditorProvider
|
vcdEditorProvider,
|
||||||
|
codeActionProvider
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -143,6 +143,9 @@ export async function showICHelperPanel(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 保存 panel 引用到全局
|
||||||
|
(global as any).currentICHelperPanel = panel;
|
||||||
|
|
||||||
// 为面板生成唯一ID
|
// 为面板生成唯一ID
|
||||||
const panelId = `panel_${Date.now()}_${Math.random()
|
const panelId = `panel_${Date.now()}_${Math.random()
|
||||||
.toString(36)
|
.toString(36)
|
||||||
@ -491,6 +494,78 @@ export async function showICHelperPanel(
|
|||||||
// 退出登录
|
// 退出登录
|
||||||
vscode.commands.executeCommand("ic-coder.logout");
|
vscode.commands.executeCommand("ic-coder.logout");
|
||||||
break;
|
break;
|
||||||
|
case "openFile":
|
||||||
|
// 打开文件
|
||||||
|
if (message.filePath) {
|
||||||
|
const path = require('path');
|
||||||
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
|
const fullPath = path.isAbsolute(message.filePath) || !workspaceFolder
|
||||||
|
? message.filePath
|
||||||
|
: vscode.Uri.joinPath(workspaceFolder.uri, message.filePath).fsPath;
|
||||||
|
vscode.workspace.openTextDocument(fullPath).then(doc => {
|
||||||
|
vscode.window.showTextDocument(doc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "openFileWithSelection":
|
||||||
|
// 打开文件并选中代码
|
||||||
|
if (message.filePath) {
|
||||||
|
const path = require('path');
|
||||||
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
|
const fullPath = path.isAbsolute(message.filePath) || !workspaceFolder
|
||||||
|
? message.filePath
|
||||||
|
: vscode.Uri.joinPath(workspaceFolder.uri, message.filePath).fsPath;
|
||||||
|
vscode.workspace.openTextDocument(fullPath).then(doc => {
|
||||||
|
vscode.window.showTextDocument(doc).then(editor => {
|
||||||
|
const start = new vscode.Position(message.startLine - 1, 0);
|
||||||
|
const end = new vscode.Position(message.endLine - 1, doc.lineAt(message.endLine - 1).text.length);
|
||||||
|
editor.selection = new vscode.Selection(start, end);
|
||||||
|
editor.revealRange(new vscode.Range(start, end));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "openFilePathTag":
|
||||||
|
// 打开文件路径标签(智能查找)
|
||||||
|
if (message.filePath) {
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
|
|
||||||
|
let fullPath = message.filePath;
|
||||||
|
|
||||||
|
// 如果是相对路径且工作区存在
|
||||||
|
if (!path.isAbsolute(message.filePath) && workspaceFolder) {
|
||||||
|
const candidatePath = vscode.Uri.joinPath(workspaceFolder.uri, message.filePath).fsPath;
|
||||||
|
// 检查文件是否存在
|
||||||
|
if (fs.existsSync(candidatePath)) {
|
||||||
|
fullPath = candidatePath;
|
||||||
|
} else {
|
||||||
|
// 尝试在工作区中搜索该文件
|
||||||
|
const fileName = path.basename(message.filePath);
|
||||||
|
const files = await vscode.workspace.findFiles(`**/${fileName}`, '**/node_modules/**', 1);
|
||||||
|
if (files.length > 0) {
|
||||||
|
fullPath = files[0].fsPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.startLine && message.endLine) {
|
||||||
|
vscode.workspace.openTextDocument(fullPath).then(doc => {
|
||||||
|
vscode.window.showTextDocument(doc).then(editor => {
|
||||||
|
const start = new vscode.Position(message.startLine - 1, 0);
|
||||||
|
const end = new vscode.Position(message.endLine - 1, doc.lineAt(message.endLine - 1).text.length);
|
||||||
|
editor.selection = new vscode.Selection(start, end);
|
||||||
|
editor.revealRange(new vscode.Range(start, end));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
vscode.workspace.openTextDocument(fullPath).then(doc => {
|
||||||
|
vscode.window.showTextDocument(doc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "acceptChange":
|
case "acceptChange":
|
||||||
// 采纳变更
|
// 采纳变更
|
||||||
if (message.changeId) {
|
if (message.changeId) {
|
||||||
|
|||||||
26
src/providers/codeActionProvider.ts
Normal file
26
src/providers/codeActionProvider.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Code Action Provider - 为选中代码提供快捷操作
|
||||||
|
* 功能:在小灯泡菜单中显示"添加到 IC Coder 对话"选项
|
||||||
|
*/
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
|
export class ICCoderCodeActionProvider implements vscode.CodeActionProvider {
|
||||||
|
provideCodeActions(
|
||||||
|
document: vscode.TextDocument,
|
||||||
|
range: vscode.Range
|
||||||
|
): vscode.CodeAction[] {
|
||||||
|
const selectedText = document.getText(range);
|
||||||
|
if (!selectedText) return [];
|
||||||
|
|
||||||
|
const action = new vscode.CodeAction(
|
||||||
|
'💬 添加到 IC Coder 对话',
|
||||||
|
vscode.CodeActionKind.RefactorRewrite
|
||||||
|
);
|
||||||
|
action.command = {
|
||||||
|
command: 'ic-coder.addCodeToChat',
|
||||||
|
title: '添加到对话'
|
||||||
|
};
|
||||||
|
|
||||||
|
return [action];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -51,7 +51,11 @@ export function getContextDisplayStyles(): string {
|
|||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-item:hover {
|
.context-item.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-item.clickable:hover {
|
||||||
background: var(--vscode-list-hoverBackground);
|
background: var(--vscode-list-hoverBackground);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +130,11 @@ export function getContextDisplayScript(): string {
|
|||||||
return '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M832 64H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32z" fill="currentColor"/></svg>';
|
return '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M832 64H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32z" fill="currentColor"/></svg>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取代码图标 SVG
|
||||||
|
function getCodeIcon() {
|
||||||
|
return '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32z m-40 728H184V184h656v656z m-484.7-122.1l39.6-39.5 113.1 113.1-39.6 39.5-113.1-113.1z m226.4-290.2l113.1 113.1-39.6 39.5-113.1-113.1 39.6-39.5z" fill="currentColor"/></svg>';
|
||||||
|
}
|
||||||
|
|
||||||
// 获取删除图标 SVG
|
// 获取删除图标 SVG
|
||||||
function getRemoveIcon() {
|
function getRemoveIcon() {
|
||||||
return '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9c-4.4 5.2-.7 13.1 6.1 13.1h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z" fill="currentColor"/></svg>';
|
return '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9c-4.4 5.2-.7 13.1 6.1 13.1h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z" fill="currentColor"/></svg>';
|
||||||
@ -172,13 +181,17 @@ export function getContextDisplayScript(): string {
|
|||||||
case 'folder': icon = getFolderIcon(); break;
|
case 'folder': icon = getFolderIcon(); break;
|
||||||
case 'image': icon = getImageIcon(); break;
|
case 'image': icon = getImageIcon(); break;
|
||||||
case 'document': icon = getDocumentIcon(); break;
|
case 'document': icon = getDocumentIcon(); break;
|
||||||
|
case 'code': icon = getCodeIcon(); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clickable = item.type !== 'folder' ? 'clickable' : '';
|
||||||
|
const onclick = item.type !== 'folder' ? \`onclick="window.handleContextItemClick(\${item.id})"\` : '';
|
||||||
|
|
||||||
return \`
|
return \`
|
||||||
<div class="context-item" title="\${item.path}">
|
<div class="context-item \${clickable}" title="\${item.path || item.displayPath}" \${onclick}>
|
||||||
\${icon}
|
\${icon}
|
||||||
<span class="context-item-name">\${item.displayPath || getFileName(item.path)}</span>
|
<span class="context-item-name">\${item.displayPath || getFileName(item.path)}</span>
|
||||||
<span class="context-item-remove" onclick="removeContextItem(\${item.id})">
|
<span class="context-item-remove" onclick="event.stopPropagation(); removeContextItem(\${item.id})">
|
||||||
\${getRemoveIcon()}
|
\${getRemoveIcon()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -186,6 +199,27 @@ export function getContextDisplayScript(): string {
|
|||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 全局访问函数
|
||||||
|
window.handleContextItemClick = function(id) {
|
||||||
|
const item = contextItems.find(i => i.id === id);
|
||||||
|
if (!item || item.type === 'folder') return;
|
||||||
|
|
||||||
|
if (item.type === 'code') {
|
||||||
|
const codeData = JSON.parse(item.path);
|
||||||
|
vscode.postMessage({
|
||||||
|
command: 'openFileWithSelection',
|
||||||
|
filePath: codeData.fileName,
|
||||||
|
startLine: codeData.startLine,
|
||||||
|
endLine: codeData.endLine
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
vscode.postMessage({
|
||||||
|
command: 'openFile',
|
||||||
|
filePath: item.path
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 处理后端返回的文件选择结果
|
// 处理后端返回的文件选择结果
|
||||||
window.addEventListener('message', event => {
|
window.addEventListener('message', event => {
|
||||||
const message = event.data;
|
const message = event.data;
|
||||||
@ -211,6 +245,18 @@ export function getContextDisplayScript(): string {
|
|||||||
message.documents.forEach(doc => addContextItem('document', doc));
|
message.documents.forEach(doc => addContextItem('document', doc));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'addCodeContext':
|
||||||
|
// 添加代码上下文
|
||||||
|
const displayName = \`\${message.fileName.split(/[\\\\/]/).pop()}:\${message.startLine}-\${message.endLine}\`;
|
||||||
|
const codeData = {
|
||||||
|
fileName: message.fileName,
|
||||||
|
startLine: message.startLine,
|
||||||
|
endLine: message.endLine,
|
||||||
|
code: message.code,
|
||||||
|
languageId: message.languageId
|
||||||
|
};
|
||||||
|
addContextItem('code', JSON.stringify(codeData), displayName);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -46,10 +46,21 @@ export function getFilePathTagScript(): string {
|
|||||||
return `
|
return `
|
||||||
// 处理文件路径标签点击
|
// 处理文件路径标签点击
|
||||||
function handleFilePathClick(filePath) {
|
function handleFilePathClick(filePath) {
|
||||||
vscode.postMessage({
|
// 解析文件路径,支持 file.v:5-8 格式
|
||||||
command: 'openFile',
|
const match = filePath.match(/^(.+?):(\\d+)-(\\d+)$/);
|
||||||
filePath: filePath
|
if (match) {
|
||||||
});
|
vscode.postMessage({
|
||||||
|
command: 'openFilePathTag',
|
||||||
|
filePath: match[1],
|
||||||
|
startLine: parseInt(match[2]),
|
||||||
|
endLine: parseInt(match[3])
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
vscode.postMessage({
|
||||||
|
command: 'openFilePathTag',
|
||||||
|
filePath: filePath
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建文件路径标签
|
// 创建文件路径标签
|
||||||
|
|||||||
@ -432,15 +432,14 @@ export function getInputAreaScript(): string {
|
|||||||
// 获取上下文项
|
// 获取上下文项
|
||||||
const contextItems = window.getContextItems ? window.getContextItems() : [];
|
const contextItems = window.getContextItems ? window.getContextItems() : [];
|
||||||
|
|
||||||
// 构建显示消息:如果有上下文文件,添加文件路径前缀
|
// 构建显示消息:如果有上下文项,添加路径前缀
|
||||||
let displayText = text;
|
let displayText = text;
|
||||||
if (contextItems.length > 0) {
|
if (contextItems.length > 0) {
|
||||||
const filePaths = contextItems
|
const contextPaths = contextItems
|
||||||
.filter(item => item.type === 'file')
|
|
||||||
.map(item => item.displayPath || item.path)
|
.map(item => item.displayPath || item.path)
|
||||||
.join(' ');
|
.join(' ');
|
||||||
if (filePaths) {
|
if (contextPaths) {
|
||||||
displayText = filePaths + ' ' + text;
|
displayText = contextPaths + ' ' + text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -863,8 +863,8 @@ export function getMessageAreaScript(): string {
|
|||||||
const textParts = [];
|
const textParts = [];
|
||||||
|
|
||||||
parts.forEach(part => {
|
parts.forEach(part => {
|
||||||
// 判断是否为文件路径:包含路径分隔符或文件扩展名
|
// 判断是否为文件路径或代码片段:包含路径分隔符、文件扩展名或代码片段格式(文件名:行号-行号)
|
||||||
if (part.includes('/') || part.includes('\\\\') || /\\.[a-zA-Z0-9]+$/.test(part)) {
|
if (part.includes('/') || part.includes('\\\\') || /\\.[a-zA-Z0-9]+$/.test(part) || /:[0-9]+-[0-9]+$/.test(part)) {
|
||||||
filePaths.push(part);
|
filePaths.push(part);
|
||||||
} else {
|
} else {
|
||||||
textParts.push(part);
|
textParts.push(part);
|
||||||
@ -1211,7 +1211,7 @@ export function getMessageAreaScript(): string {
|
|||||||
|
|
||||||
const optionsHtml = q.options.map(opt => {
|
const optionsHtml = q.options.map(opt => {
|
||||||
const isSelected = selectedAnswers.includes(opt);
|
const isSelected = selectedAnswers.includes(opt);
|
||||||
return \`<label style="display:flex;align-items:center;gap:6px;cursor:pointer;padding:4px 0;">
|
return \`<label class="question-option\${isSelected ? ' selected' : ''}" style="display:flex;align-items:center;gap:6px;cursor:pointer;padding:4px 0;">
|
||||||
<input type="\${inputType}" name="\${inputName}" value="\${opt}" \${isSelected ? 'checked' : ''} \${isAnswered ? 'disabled' : ''}>
|
<input type="\${inputType}" name="\${inputName}" value="\${opt}" \${isSelected ? 'checked' : ''} \${isAnswered ? 'disabled' : ''}>
|
||||||
<span>\${opt}</span>
|
<span>\${opt}</span>
|
||||||
</label>\`;
|
</label>\`;
|
||||||
@ -1729,9 +1729,18 @@ export function getMessageAreaScript(): string {
|
|||||||
// 标记问题已回答
|
// 标记问题已回答
|
||||||
segmentDiv.classList.add('answered');
|
segmentDiv.classList.add('answered');
|
||||||
|
|
||||||
// 禁用所有输入
|
// 禁用所有输入并保持选中状态的高亮
|
||||||
const inputs = segmentDiv.querySelectorAll('input');
|
const inputs = segmentDiv.querySelectorAll('input');
|
||||||
inputs.forEach(input => input.disabled = true);
|
inputs.forEach(input => {
|
||||||
|
input.disabled = true;
|
||||||
|
// 确保选中的选项保持高亮
|
||||||
|
if (input.checked) {
|
||||||
|
const label = input.closest('.question-option');
|
||||||
|
if (label) {
|
||||||
|
label.classList.add('selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 隐藏提交按钮
|
// 隐藏提交按钮
|
||||||
const submitBtn = segmentDiv.querySelector('.custom-submit');
|
const submitBtn = segmentDiv.querySelector('.custom-submit');
|
||||||
|
|||||||
@ -10,24 +10,23 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
|
|||||||
|
|
||||||
/** @type WebpackConfig */
|
/** @type WebpackConfig */
|
||||||
const extensionConfig = {
|
const extensionConfig = {
|
||||||
target: 'node', // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
|
target: 'node',
|
||||||
mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
|
mode: process.env.NODE_ENV === 'production' ? 'production' : 'none',
|
||||||
|
|
||||||
entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
|
entry: './src/extension.ts',
|
||||||
output: {
|
output: {
|
||||||
// the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/
|
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
filename: 'extension.js',
|
filename: 'extension.js',
|
||||||
libraryTarget: 'commonjs2'
|
libraryTarget: 'commonjs2',
|
||||||
|
clean: true // 自动清理旧文件
|
||||||
},
|
},
|
||||||
externals: {
|
externals: {
|
||||||
vscode: 'commonjs vscode', // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
|
vscode: 'commonjs vscode',
|
||||||
'node-notifier': 'commonjs node-notifier' // node-notifier 依赖原生模块,必须排除
|
'node-notifier': 'commonjs node-notifier'
|
||||||
// modules added here also need to be added in the .vscodeignore file
|
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
|
extensions: ['.ts', '.js'],
|
||||||
extensions: ['.ts', '.js']
|
mainFields: ['module', 'main']
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
@ -36,15 +35,21 @@ const extensionConfig = {
|
|||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
loader: 'ts-loader'
|
loader: 'ts-loader',
|
||||||
|
options: {
|
||||||
|
transpileOnly: true, // 加快编译速度
|
||||||
|
compilerOptions: {
|
||||||
|
sourceMap: true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
devtool: 'nosources-source-map',
|
devtool: process.env.NODE_ENV === 'production' ? 'hidden-source-map' : 'nosources-source-map',
|
||||||
infrastructureLogging: {
|
infrastructureLogging: {
|
||||||
level: "log", // enables logging required for problem matchers
|
level: "log",
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new CopyWebpackPlugin({
|
new CopyWebpackPlugin({
|
||||||
@ -52,6 +57,15 @@ const extensionConfig = {
|
|||||||
{ from: 'src/assets', to: 'assets' }
|
{ from: 'src/assets', to: 'assets' }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
]
|
],
|
||||||
|
optimization: {
|
||||||
|
minimize: process.env.NODE_ENV === 'production',
|
||||||
|
usedExports: true // Tree Shaking
|
||||||
|
},
|
||||||
|
performance: {
|
||||||
|
hints: 'warning',
|
||||||
|
maxAssetSize: 2 * 1024 * 1024, // 2MB
|
||||||
|
maxEntrypointSize: 2 * 1024 * 1024
|
||||||
|
}
|
||||||
};
|
};
|
||||||
module.exports = [ extensionConfig ];
|
module.exports = [ extensionConfig ];
|
||||||
Reference in New Issue
Block a user