From 1a41d7f74c6d315740500272545e441f6d6b881d Mon Sep 17 00:00:00 2001 From: ZLY Date: Thu, 24 Jul 2025 15:41:19 +0800 Subject: [PATCH] =?UTF-8?q?feat(extension):=20=E6=B7=BB=E5=8A=A0=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=B5=8F=E8=A7=88=E5=99=A8=E5=8A=9F=E8=83=BD-=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=86=E8=8E=B7=E5=8F=96=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E5=8C=BA=E6=96=87=E4=BB=B6=E5=88=97=E8=A1=A8=E5=B9=B6=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E5=9C=A8=E6=A8=A1=E6=80=81=E6=A1=86=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=20-=20=E6=B7=BB=E5=8A=A0=E4=BA=86=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=90=9C=E7=B4=A2=E5=8A=9F=E8=83=BD=20-=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E4=BA=86=E7=82=B9=E5=87=BB=E6=96=87=E4=BB=B6=E5=90=8E?= =?UTF-8?q?=E5=B0=86=E5=85=B6=E5=86=85=E5=AE=B9=E5=8A=A0=E8=BD=BD=E5=88=B0?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E6=A1=86=E7=9A=84=E5=8A=9F=E8=83=BD-=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=86=E6=A8=A1=E6=80=81=E6=A1=86=E7=9A=84?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E5=92=8C=E4=BA=A4=E4=BA=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- media/webview/script.js | 66 ++++++++++++++++++++++- media/webview/style.css | 115 ++++++++++++++++++++++++++++++++++++++++ src/extension.ts | 106 +++++++++++++++++++++++++++++++++++- 3 files changed, 285 insertions(+), 2 deletions(-) diff --git a/media/webview/script.js b/media/webview/script.js index a16e26d..82c929a 100644 --- a/media/webview/script.js +++ b/media/webview/script.js @@ -86,6 +86,41 @@ document.addEventListener('DOMContentLoaded', () => { userInput.value = message.content; // 插入选中内容到输入框 selectedCodeForContext = message.content; userInput.focus(); // 自动聚焦输入框 + } else if (message.command === 'fileList') { + const fileListContainer = document.getElementById('file-list'); + + if (message.error) { + fileListContainer.innerHTML = `
错误: ${message.error}
`; + return; + } + + if (!message.files || message.files.length === 0) { + fileListContainer.innerHTML = '
未找到文件
'; + return; + } + + // 渲染文件列表 + fileListContainer.innerHTML = ''; + message.files.forEach(file => { + const fileItem = document.createElement('div'); + fileItem.className = 'file-item'; + fileItem.innerHTML = ` ${file.isDirectory ? '📁' : '📄'} + ${file.relativePath} + `; + + fileItem.addEventListener('click', () => { + // 选择文件 + vscode.postMessage({ + command: 'selectFileByPath', + filePath: file.path + }); + + // 关闭模态框 + document.getElementById('file-browser-modal').classList.add('hidden'); + }); + + fileListContainer.appendChild(fileItem); + }); } else if (message.command === 'setContextFile') { const contextPlaceholder = document.getElementById('context-placeholder'); const contextTab = document.getElementById('context-tab'); @@ -104,9 +139,38 @@ document.addEventListener('DOMContentLoaded', () => { }); + // 打开文件浏览器 document.getElementById('select-context-btn').addEventListener('click', () => { + // 显示模态框 + document.getElementById('file-browser-modal').classList.remove('hidden'); + + // 请求文件列表 vscode.postMessage({ - command: 'selectContextFile' + command: 'getFileList' + }); + }); + // 关闭模态框 + document.getElementById('close-modal').addEventListener('click', () => { + document.getElementById('file-browser-modal').classList.add('hidden'); + }); + // 点击模态框外部关闭 + document.getElementById('file-browser-modal').addEventListener('click', (e) => { + if (e.target.id === 'file-browser-modal') { + document.getElementById('file-browser-modal').classList.add('hidden'); + } + }); + // 文件搜索功能 + document.getElementById('file-search-input').addEventListener('input', (e) => { + const searchTerm = e.target.value.toLowerCase(); + const fileItems = document.querySelectorAll('.file-item'); + + fileItems.forEach(item => { + const fileName = item.querySelector('.file-name').textContent.toLowerCase(); + if (fileName.includes(searchTerm)) { + item.style.display = ''; + } else { + item.style.display = 'none'; + } }); }); // 添加关闭按钮事件监听 diff --git a/media/webview/style.css b/media/webview/style.css index def79d0..9f370cb 100644 --- a/media/webview/style.css +++ b/media/webview/style.css @@ -207,3 +207,118 @@ body { .select-context-btn:hover { background-color: #005a9e; } + +/* 模态框样式 */ +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.modal.hidden { + display: none; +} + +.modal-content { + background-color: #282c34; + border-radius: 8px; + width: 80%; + max-width: 600px; + max-height: 80vh; + display: flex; + flex-direction: column; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 15px 20px; + border-bottom: 1px solid #444; +} + +.modal-header h3 { + margin: 0; + color: white; +} + +.close-modal { + font-size: 24px; + cursor: pointer; + color: #aaa; +} + +.close-modal:hover { + color: white; +} + +.modal-body { + flex: 1; + padding: 15px; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.file-search { + flex: 1; + margin-bottom: 15px; +} + +.file-search input { + padding: 8px; + background-color: #3e4451; + border: 1px solid #444; + border-radius: 4px; + color: white; +} + +.file-list { + flex: 1; + overflow-y: auto; + border: 1px solid #444; + border-radius: 4px; + background-color: #3e4451; +} + +.file-item { + padding: 10px; + border-bottom: 1px solid #444; + cursor: pointer; + display: flex; + align-items: center; +} + +.file-item:hover { + background-color: #4e5461; +} + +.file-item:last-child { + border-bottom: none; +} + +.file-icon { + margin-right: 10px; + width: 16px; + text-align: center; +} + +.file-name { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.loading { + text-align: center; + padding: 20px; + color: #abb2bf; +} diff --git a/src/extension.ts b/src/extension.ts index 770ab1b..6e719b9 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -72,8 +72,54 @@ export function activate(context: vscode.ExtensionContext) { } }); + // 注册获取工作区文件列表的命令 + const getFileListCommand = vscode.commands.registerCommand('ai-chat.getFileList', async () => { + if (panel && panel.webview) { + try { + // 获取工作区根路径 + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders || workspaceFolders.length === 0) { + panel.webview.postMessage({ + command: 'fileList', + files: [], + error: '未找到工作区' + }); + return; + } + + const rootPath = workspaceFolders[0].uri; + + // 查找所有文件 + const fileUris = await vscode.workspace.findFiles('**/*', '**/node_modules/**'); + + // 转换为相对路径和基本信息 + const files = fileUris.map(uri => { + const relativePath = vscode.workspace.asRelativePath(uri); + const isDirectory = uri.path.endsWith('/'); + return { + path: uri.fsPath, + relativePath: relativePath, + name: path.basename(uri.fsPath), + isDirectory: isDirectory + }; + }); + + panel.webview.postMessage({ + command: 'fileList', + files: files + }); + } catch (error) { + panel.webview.postMessage({ + command: 'fileList', + files: [], + error: error.message + }); + } + } + }); + // 添加到 subscriptions - context.subscriptions.push(statusBarItem, openWebviewCommand, addToChatCommand,selectFileCommand); + context.subscriptions.push(statusBarItem, openWebviewCommand, addToChatCommand,selectFileCommand,getFileListCommand); } function openWebview( @@ -163,6 +209,45 @@ function openWebview( }); } break; + case 'getFileList': + // 执行获取文件列表逻辑 + const workspaceFolders = vscode.workspace.workspaceFolders; + if (workspaceFolders && workspaceFolders.length > 0) { + const fileUris = await vscode.workspace.findFiles('**/*', '**/node_modules/**'); + + const files = fileUris.map(uri => { + const relativePath = vscode.workspace.asRelativePath(uri); + return { + path: uri.fsPath, + relativePath: relativePath, + name: path.basename(uri.fsPath), + isDirectory: false // 简化处理,实际可以根据扩展名判断 + }; + }); + + panel.webview.postMessage({ + command: 'fileList', + files: files + }); + } + break; + + case 'selectFileByPath': + // 根据路径选择文件 + try { + const fileUri = vscode.Uri.file(message.filePath); + const fileContent = await vscode.workspace.fs.readFile(fileUri); + const decodedContent = new TextDecoder("utf-8").decode(fileContent); + + panel.webview.postMessage({ + command: 'setContextFile', + fileName: fileUri.fsPath, + fileContent: decodedContent + }); + } catch (error) { + vscode.window.showErrorMessage(`无法读取文件: ${error.message}`); + } + break; } }, undefined, @@ -249,6 +334,25 @@ function getWebviewContent(styleUri: vscode.Uri, scriptUri: vscode.Uri,highlight + + + +