feat(extension): 添加文件浏览器功能- 实现了获取工作区文件列表并展示在模态框中的功能

- 添加了文件搜索功能
- 实现了点击文件后将其内容加载到聊天框的功能- 优化了模态框的样式和交互
master
钟良源 7 months ago
parent aac6da5ce0
commit 1a41d7f74c

@ -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 = `<div class="loading">错误: ${message.error}</div>`;
return;
}
if (!message.files || message.files.length === 0) {
fileListContainer.innerHTML = '<div class="loading">未找到文件</div>';
return;
}
// 渲染文件列表
fileListContainer.innerHTML = '';
message.files.forEach(file => {
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
fileItem.innerHTML = ` <span class="file-icon">${file.isDirectory ? '📁' : '📄'}</span>
<span class="file-name" title="${file.relativePath}">${file.relativePath}</span>
`;
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';
}
});
});
// 添加关闭按钮事件监听

@ -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;
}

@ -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
<button id="close-context-btn" class="close-btn" title="清除上下文">×</button>
</div>
</div>
<!-- -->
<div id="file-browser-modal" class="modal hidden">
<div class="modal-content">
<div class="modal-header">
<h3></h3>
<span id="close-modal" class="close-modal">&times;</span>
</div>
<div class="modal-body">
<div class="file-search">
<input type="text" id="file-search-input" placeholder="搜索文件..." />
</div>
<div id="file-list" class="file-list">
<div class="loading">...</div>
</div>
</div>
</div>
</div>
<!-- -->
<form id="chat-form">
<input type="text" id="user-input" placeholder="输入你的问题..." required />

Loading…
Cancel
Save