diff --git a/src/panels/ICHelperPanel.ts b/src/panels/ICHelperPanel.ts
index 11c57a7..0a45b08 100644
--- a/src/panels/ICHelperPanel.ts
+++ b/src/panels/ICHelperPanel.ts
@@ -281,6 +281,109 @@ export async function showICHelperPanel(
}
}
break;
+ // 添加文件上下文 - 显示工作区文件列表
+ case "addContextFile":
+ {
+ const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
+ if (!workspaceFolder) {
+ vscode.window.showWarningMessage("请先打开一个工作区");
+ break;
+ }
+
+ // 获取工作区所有文件
+ const files = await vscode.workspace.findFiles(
+ "**/*",
+ "**/node_modules/**"
+ );
+
+ panel.webview.postMessage({
+ command: "showWorkspaceFileList",
+ files: files.map((uri) => ({
+ path: uri.fsPath,
+ relativePath: vscode.workspace.asRelativePath(uri),
+ })),
+ });
+ }
+ break;
+ // 添加文件夹上下文 - 显示工作区文件夹列表
+ case "addContextFolder":
+ {
+ const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
+ if (!workspaceFolder) {
+ vscode.window.showWarningMessage("请先打开一个工作区");
+ break;
+ }
+
+ // 获取工作区所有文件夹
+ const fs = require("fs");
+ const path = require("path");
+ const folders: Array<{ path: string; relativePath: string }> = [];
+
+ function scanFolders(dir: string, baseDir: string) {
+ try {
+ const items = fs.readdirSync(dir, { withFileTypes: true });
+ for (const item of items) {
+ if (item.isDirectory() && item.name !== "node_modules" && !item.name.startsWith(".")) {
+ const fullPath = path.join(dir, item.name);
+ const relativePath = path.relative(baseDir, fullPath);
+ folders.push({ path: fullPath, relativePath });
+ scanFolders(fullPath, baseDir);
+ }
+ }
+ } catch (error) {
+ console.error("扫描文件夹失败:", error);
+ }
+ }
+
+ scanFolders(workspaceFolder.uri.fsPath, workspaceFolder.uri.fsPath);
+
+ panel.webview.postMessage({
+ command: "showWorkspaceFolderList",
+ folders: folders,
+ });
+ }
+ break;
+ // 添加图片上下文
+ case "addContextImage":
+ {
+ const imageUris = await vscode.window.showOpenDialog({
+ canSelectFiles: true,
+ canSelectFolders: false,
+ canSelectMany: true,
+ openLabel: "选择图片",
+ filters: {
+ "图片文件": ["png", "jpg", "jpeg", "gif", "bmp", "svg", "webp"],
+ },
+ });
+ if (imageUris && imageUris.length > 0) {
+ panel.webview.postMessage({
+ command: "contextImagesSelected",
+ images: imageUris.map((uri) => uri.fsPath),
+ });
+ }
+ }
+ break;
+ // 添加文档库上下文
+ case "addContextDocument":
+ {
+ const docUris = await vscode.window.showOpenDialog({
+ canSelectFiles: true,
+ canSelectFolders: false,
+ canSelectMany: true,
+ openLabel: "选择文档",
+ filters: {
+ "文档文件": ["pdf", "doc", "docx", "txt", "md"],
+ "所有文件": ["*"],
+ },
+ });
+ if (docUris && docUris.length > 0) {
+ panel.webview.postMessage({
+ command: "contextDocumentsSelected",
+ documents: docUris.map((uri) => uri.fsPath),
+ });
+ }
+ }
+ break;
// 新增:检查工作区状态
case "checkWorkspace":
const hasWorkspace = !!(
diff --git a/src/views/contextButton.ts b/src/views/contextButton.ts
index b1d6fed..621069b 100644
--- a/src/views/contextButton.ts
+++ b/src/views/contextButton.ts
@@ -7,14 +7,78 @@
*/
export function getContextButtonContent(): string {
return `
-
-
-
添加文件或代码片段作为上下文
+
`;
}
@@ -24,6 +88,12 @@ export function getContextButtonContent(): string {
*/
export function getContextButtonStyles(): string {
return `
+ /* 上下文选择器容器 */
+ .context-selector-wrapper {
+ position: relative;
+ display: inline-block;
+ }
+
/* 添加上下文按钮样式 */
.add-context-button {
display: flex;
@@ -45,15 +115,218 @@ export function getContextButtonStyles(): string {
border-color: var(--vscode-focusBorder);
}
- .add-context-button svg {
+ .add-context-button svg.icon {
width: 16px;
height: 16px;
color: #409eff;
}
+ .add-context-button .dropdown-arrow {
+ width: 12px;
+ height: 12px;
+ transition: transform 0.2s ease;
+ }
+
+ .add-context-button.active .dropdown-arrow {
+ transform: rotate(180deg);
+ }
+
.add-context-label {
white-space: nowrap;
}
+
+ /* 上拉菜单样式 */
+ .context-menu {
+ position: absolute;
+ bottom: calc(100% + 8px);
+ left: 0;
+ background: var(--vscode-dropdown-background);
+ border: 1px solid var(--vscode-dropdown-border);
+ border-radius: 6px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
+ min-width: 180px;
+ z-index: 1000;
+ display: none;
+ overflow: hidden;
+ }
+
+ .context-menu.show {
+ display: block;
+ animation: slideUp 0.2s ease;
+ }
+
+ @keyframes slideUp {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+
+ .context-menu-item {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 10px 16px;
+ cursor: pointer;
+ transition: background 0.2s ease;
+ color: var(--vscode-foreground);
+ }
+
+ .context-menu-item:hover {
+ background: var(--vscode-list-hoverBackground);
+ }
+
+ .context-menu-item svg {
+ width: 18px;
+ height: 18px;
+ flex-shrink: 0;
+ color: var(--vscode-foreground);
+ opacity: 0.8;
+ }
+
+ .context-menu-item span {
+ font-size: 13px;
+ white-space: nowrap;
+ flex: 1;
+ }
+
+ .context-menu-item .arrow-right {
+ width: 14px;
+ height: 14px;
+ opacity: 0.6;
+ margin-left: auto;
+ }
+
+ /* 列表视图样式 */
+ .context-menu-list {
+ display: flex;
+ flex-direction: column;
+ max-height: 350px;
+ }
+
+ .context-menu-list-header {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 10px 12px;
+ border-bottom: 1px solid var(--vscode-panel-border);
+ }
+
+ .context-menu-back {
+ width: 28px;
+ height: 28px;
+ padding: 0;
+ border: none;
+ background: transparent;
+ color: var(--vscode-foreground);
+ cursor: pointer;
+ border-radius: 4px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .context-menu-back:hover {
+ background: var(--vscode-toolbar-hoverBackground);
+ }
+
+ .context-menu-back svg {
+ width: 16px;
+ height: 16px;
+ }
+
+ .context-menu-list-header span {
+ font-size: 14px;
+ font-weight: 500;
+ flex: 1;
+ }
+
+ .context-menu-list-body {
+ flex: 1;
+ overflow-y: auto;
+ padding: 4px;
+ }
+
+ .context-menu-list-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 6px 8px;
+ cursor: pointer;
+ border-radius: 4px;
+ transition: background 0.2s ease;
+ }
+
+ .context-menu-list-item:hover {
+ background: var(--vscode-list-hoverBackground);
+ }
+
+ .context-menu-list-item.selected {
+ background: var(--vscode-list-activeSelectionBackground);
+ }
+
+ .context-menu-list-item input[type="checkbox"] {
+ width: 14px;
+ height: 14px;
+ flex-shrink: 0;
+ }
+
+ .context-menu-list-item label {
+ flex: 1;
+ font-size: 12px;
+ cursor: pointer;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .context-menu-list-footer {
+ padding: 8px 12px;
+ border-top: 1px solid var(--vscode-panel-border);
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+
+ .context-menu-list-footer input {
+ width: 100%;
+ padding: 6px 10px;
+ background: var(--vscode-input-background);
+ border: 1px solid var(--vscode-input-border);
+ border-radius: 4px;
+ color: var(--vscode-input-foreground);
+ font-size: 12px;
+ box-sizing: border-box;
+ }
+
+ .context-menu-list-actions {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .context-menu-list-footer span {
+ font-size: 12px;
+ color: var(--vscode-descriptionForeground);
+ }
+
+ .context-menu-list-footer button {
+ padding: 4px 12px;
+ background: var(--vscode-button-background);
+ color: var(--vscode-button-foreground);
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 12px;
+ }
+
+ .context-menu-list-footer button:hover {
+ background: var(--vscode-button-hoverBackground);
+ }
`;
}
@@ -62,10 +335,174 @@ export function getContextButtonStyles(): string {
*/
export function getContextButtonScript(): string {
return `
- // 添加上下文处理函数
- function handleAddContext() {
- // 发送添加上下文请求到扩展
- vscode.postMessage({ command: 'addContext' });
+ // 上下文菜单状态
+ let currentListData = [];
+ let currentListType = '';
+ let selectedItems = new Set();
+
+ // 切换上下文菜单显示/隐藏
+ function toggleContextMenu() {
+ const menu = document.getElementById('contextMenu');
+ const button = document.querySelector('.add-context-button');
+
+ if (menu && button) {
+ const isShown = menu.classList.contains('show');
+
+ if (isShown) {
+ menu.classList.remove('show');
+ button.classList.remove('active');
+ backToMainMenu(); // 关闭时回到主菜单
+ } else {
+ menu.classList.add('show');
+ button.classList.add('active');
+ }
+ }
}
+
+ // 点击外部关闭菜单
+ document.addEventListener('click', function(event) {
+ const wrapper = document.querySelector('.context-selector-wrapper');
+ const menu = document.getElementById('contextMenu');
+ const button = document.querySelector('.add-context-button');
+
+ if (wrapper && menu && button && !wrapper.contains(event.target)) {
+ menu.classList.remove('show');
+ button.classList.remove('active');
+ backToMainMenu();
+ }
+ });
+
+ // 显示文件列表
+ function showFileList() {
+ vscode.postMessage({ command: 'addContextFile' });
+ }
+
+ // 显示文件夹列表
+ function showFolderList() {
+ vscode.postMessage({ command: 'addContextFolder' });
+ }
+
+ // 返回主菜单
+ function backToMainMenu() {
+ const mainMenu = document.getElementById('contextMenuMain');
+ const listView = document.getElementById('contextMenuList');
+
+ if (mainMenu && listView) {
+ mainMenu.style.display = 'block';
+ listView.style.display = 'none';
+ }
+
+ selectedItems.clear();
+ currentListData = [];
+ }
+
+ // 切换到列表视图
+ function switchToListView(title, type, data) {
+ const mainMenu = document.getElementById('contextMenuMain');
+ const listView = document.getElementById('contextMenuList');
+ const titleEl = document.getElementById('contextMenuListTitle');
+
+ if (mainMenu && listView && titleEl) {
+ mainMenu.style.display = 'none';
+ listView.style.display = 'flex';
+ titleEl.textContent = title;
+
+ currentListType = type;
+ currentListData = data;
+ selectedItems.clear();
+
+ renderList(data);
+ updateSelectedCount();
+ }
+ }
+
+ // 渲染列表
+ function renderList(data) {
+ const body = document.getElementById('contextMenuListBody');
+ if (!body) return;
+
+ body.innerHTML = data.map((item, index) => \`
+
+ \`).join('');
+ }
+
+ // 切换项选择
+ function toggleItemSelection(index) {
+ const checkbox = document.getElementById('item-' + index);
+ const item = document.querySelectorAll('.context-menu-list-item')[index];
+
+ if (checkbox && item) {
+ checkbox.checked = !checkbox.checked;
+
+ if (checkbox.checked) {
+ selectedItems.add(index);
+ item.classList.add('selected');
+ } else {
+ selectedItems.delete(index);
+ item.classList.remove('selected');
+ }
+
+ updateSelectedCount();
+ }
+ }
+
+ // 更新选中数量
+ function updateSelectedCount() {
+ const countEl = document.getElementById('contextMenuListCount');
+ if (countEl) {
+ countEl.textContent = '已选择 ' + selectedItems.size + ' 项';
+ }
+ }
+
+ // 确认选择
+ function confirmSelection() {
+ const selected = Array.from(selectedItems).map(index => currentListData[index]);
+
+ if (selected.length > 0) {
+ selected.forEach(item => {
+ addContextItem(currentListType, item.path);
+ });
+ }
+
+ toggleContextMenu();
+ }
+
+ // 添加图片
+ function handleAddImage() {
+ vscode.postMessage({ command: 'addContextImage' });
+ toggleContextMenu();
+ }
+
+ // 添加文档
+ function handleAddDocument() {
+ vscode.postMessage({ command: 'addContextDocument' });
+ toggleContextMenu();
+ }
+
+ // 搜索功能
+ const searchInput = document.getElementById('contextMenuSearch');
+ if (searchInput) {
+ searchInput.addEventListener('input', function(e) {
+ const keyword = e.target.value.toLowerCase();
+ const filtered = currentListData.filter(item =>
+ item.relativePath.toLowerCase().includes(keyword)
+ );
+ renderList(filtered);
+ });
+ }
+
+ // 处理后端消息
+ window.addEventListener('message', event => {
+ const message = event.data;
+
+ if (message.command === 'showWorkspaceFileList') {
+ switchToListView('选择文件', 'file', message.files);
+ } else if (message.command === 'showWorkspaceFolderList') {
+ switchToListView('选择文件夹', 'folder', message.folders);
+ }
+ });
`;
}
diff --git a/src/views/contextDisplay.ts b/src/views/contextDisplay.ts
new file mode 100644
index 0000000..f1b0f7b
--- /dev/null
+++ b/src/views/contextDisplay.ts
@@ -0,0 +1,225 @@
+/**
+ * 上下文显示组件
+ * 用于显示已选择的文件、文件夹、图片和文档
+ */
+
+/**
+ * 获取上下文显示区域的 HTML 内容
+ */
+export function getContextDisplayContent(): string {
+ return `
+
+ `;
+}
+
+/**
+ * 获取上下文显示区域的样式
+ */
+export function getContextDisplayStyles(): string {
+ return `
+ /* 上下文显示区域 */
+ .context-display-area {
+ margin-bottom: 8px;
+ padding: 8px;
+ background: rgba(128, 128, 128, 0.1);
+ border-radius: 6px;
+ border: 1px solid var(--vscode-input-border);
+ }
+
+ .context-items-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ }
+
+ /* 上下文项样式 */
+ .context-item {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 6px 10px;
+ background: var(--vscode-input-background);
+ border: 1px solid var(--vscode-input-border);
+ border-radius: 4px;
+ font-size: 12px;
+ color: var(--vscode-foreground);
+ max-width: 300px;
+ transition: all 0.2s ease;
+ }
+
+ .context-item:hover {
+ background: var(--vscode-list-hoverBackground);
+ }
+
+ .context-item svg {
+ width: 14px;
+ height: 14px;
+ flex-shrink: 0;
+ opacity: 0.8;
+ }
+
+ .context-item-name {
+ flex: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .context-item-remove {
+ width: 14px;
+ height: 14px;
+ cursor: pointer;
+ opacity: 0.6;
+ transition: opacity 0.2s ease;
+ flex-shrink: 0;
+ }
+
+ .context-item-remove:hover {
+ opacity: 1;
+ color: #f56c6c;
+ }
+
+ /* 图片预览样式 */
+ .context-item.image-item {
+ position: relative;
+ }
+
+ .context-item-preview {
+ width: 40px;
+ height: 40px;
+ object-fit: cover;
+ border-radius: 3px;
+ border: 1px solid var(--vscode-input-border);
+ }
+ `;
+}
+
+/**
+ * 获取上下文显示区域的脚本
+ */
+export function getContextDisplayScript(): string {
+ return `
+ // 存储上下文项
+ let contextItems = [];
+
+ // 获取文件图标 SVG
+ function getFileIcon() {
+ return '
';
+ }
+
+ // 获取文件夹图标 SVG
+ function getFolderIcon() {
+ return '
';
+ }
+
+ // 获取图片图标 SVG
+ function getImageIcon() {
+ return '
';
+ }
+
+ // 获取文档图标 SVG
+ function getDocumentIcon() {
+ return '
';
+ }
+
+ // 获取删除图标 SVG
+ function getRemoveIcon() {
+ return '
';
+ }
+
+ // 提取文件名
+ function getFileName(path) {
+ return path.split(/[\\\\/]/).pop();
+ }
+
+ // 添加上下文项
+ function addContextItem(type, path) {
+ const id = Date.now() + Math.random();
+ contextItems.push({ id, type, path });
+ renderContextItems();
+ }
+
+ // 删除上下文项
+ function removeContextItem(id) {
+ contextItems = contextItems.filter(item => item.id !== id);
+ renderContextItems();
+ }
+
+ // 渲染上下文项
+ function renderContextItems() {
+ const container = document.getElementById('contextItemsContainer');
+ const displayArea = document.getElementById('contextDisplayArea');
+
+ if (!container || !displayArea) return;
+
+ if (contextItems.length === 0) {
+ displayArea.style.display = 'none';
+ return;
+ }
+
+ displayArea.style.display = 'block';
+ container.innerHTML = contextItems.map(item => {
+ let icon = '';
+ switch(item.type) {
+ case 'file': icon = getFileIcon(); break;
+ case 'folder': icon = getFolderIcon(); break;
+ case 'image': icon = getImageIcon(); break;
+ case 'document': icon = getDocumentIcon(); break;
+ }
+
+ return \`
+
+ \${icon}
+ \${getFileName(item.path)}
+
+ \${getRemoveIcon()}
+
+
+ \`;
+ }).join('');
+ }
+
+ // 处理后端返回的文件选择结果
+ window.addEventListener('message', event => {
+ const message = event.data;
+
+ switch(message.command) {
+ case 'contextFilesSelected':
+ if (message.files && message.files.length > 0) {
+ message.files.forEach(file => addContextItem('file', file));
+ }
+ break;
+ case 'contextFoldersSelected':
+ if (message.folders && message.folders.length > 0) {
+ message.folders.forEach(folder => addContextItem('folder', folder));
+ }
+ break;
+ case 'contextImagesSelected':
+ if (message.images && message.images.length > 0) {
+ message.images.forEach(image => addContextItem('image', image));
+ }
+ break;
+ case 'contextDocumentsSelected':
+ if (message.documents && message.documents.length > 0) {
+ message.documents.forEach(doc => addContextItem('document', doc));
+ }
+ break;
+ }
+ });
+
+ // 获取所有上下文项(供发送消息时使用)
+ window.getContextItems = function() {
+ return contextItems;
+ };
+
+ // 清空上下文项(供清空对话时使用)
+ window.clearContextItems = function() {
+ contextItems = [];
+ renderContextItems();
+ };
+ `;
+}
diff --git a/src/views/inputArea.ts b/src/views/inputArea.ts
index 5c90150..8aa6815 100644
--- a/src/views/inputArea.ts
+++ b/src/views/inputArea.ts
@@ -14,6 +14,11 @@ import {
getContextButtonStyles,
getContextButtonScript,
} from "./contextButton";
+import {
+ getContextDisplayContent,
+ getContextDisplayStyles,
+ getContextDisplayScript,
+} from "./contextDisplay";
import {
getContextCompressContent,
getContextCompressStyles,
@@ -43,6 +48,8 @@ export function getInputAreaContent(
${getContextButtonContent()}
+
+ ${getContextDisplayContent()}