feat(extension): 添加选择文件作为上下文功能

- 在 extension.ts 中注册选择文件命令
- 在 Webview 中添加文件选择按钮和上下文显示区域
- 实现文件选择逻辑,将选中文件内容发送到 Webview
- 在 Webview 中处理接收到的文件内容,显示文件名和内容
-
master
钟良源 7 months ago
parent c0e32c9b83
commit aac6da5ce0

@ -8,7 +8,6 @@ document.addEventListener('DOMContentLoaded', () => {
const loading = document.getElementById('loading'); const loading = document.getElementById('loading');
chatForm.addEventListener('submit', (e) => { chatForm.addEventListener('submit', (e) => {
console.log(1111)
e.preventDefault(); e.preventDefault();
const text = userInput.value.trim(); const text = userInput.value.trim();
if (!text) return; if (!text) return;
@ -21,7 +20,6 @@ document.addEventListener('DOMContentLoaded', () => {
// addMessage('user', text); // addMessage('user', text);
showLoading(); showLoading();
userInput.value = ''; userInput.value = '';
}); });
@ -48,9 +46,9 @@ document.addEventListener('DOMContentLoaded', () => {
// 渲染内容 // 渲染内容
if (isMarkdown(content)) { if (isMarkdown(content)) {
console.log("content:",content) console.log("content:", content)
msgDiv.innerHTML = marked.parse(content); msgDiv.innerHTML = marked.parse(content);
console.log("msgDiv.innerHTML:",msgDiv.innerHTML ) console.log("msgDiv.innerHTML:", msgDiv.innerHTML)
} else { } else {
const div = document.createElement('div'); const div = document.createElement('div');
div.textContent = content; div.textContent = content;
@ -79,7 +77,7 @@ document.addEventListener('DOMContentLoaded', () => {
window.addEventListener('message', (event) => { window.addEventListener('message', (event) => {
const message = event.data; const message = event.data;
console.log(message) console.log(message)
if (message.command === 'addMessage') { if (message.command === 'addMessage') {
addMessage(message.role, message.content); addMessage(message.role, message.content);
} else if (message.command === 'hideLoading') { } else if (message.command === 'hideLoading') {
@ -88,7 +86,36 @@ document.addEventListener('DOMContentLoaded', () => {
userInput.value = message.content; // 插入选中内容到输入框 userInput.value = message.content; // 插入选中内容到输入框
selectedCodeForContext = message.content; selectedCodeForContext = message.content;
userInput.focus(); // 自动聚焦输入框 userInput.focus(); // 自动聚焦输入框
} else if (message.command === 'setContextFile') {
const contextPlaceholder = document.getElementById('context-placeholder');
const contextTab = document.getElementById('context-tab');
const fileNameElement = document.getElementById('context-file-name');
const fullPath = message.fileName;
const fileName = fullPath.split(/[\/\\]/).pop();
fileNameElement.innerText = fileName;
fileNameElement.title = fullPath;
selectedCodeForContext = message.fileContent;
// 隐藏占位符,显示文件标签
contextPlaceholder.classList.add('hidden');
contextTab.classList.remove('hidden');
} }
}); });
document.getElementById('select-context-btn').addEventListener('click', () => {
vscode.postMessage({
command: 'selectContextFile'
});
});
// 添加关闭按钮事件监听
document.getElementById('close-context-btn').addEventListener('click', () => {
const contextPlaceholder = document.getElementById('context-placeholder');
const contextTab = document.getElementById('context-tab');
contextTab.classList.add('hidden');
contextPlaceholder.classList.remove('hidden');
selectedCodeForContext = '';
});
}); });

@ -23,7 +23,7 @@ body {
.user, .user,
.ai { .ai {
align-self: flex-end; align-self: flex-end;
color:#000000; color: #000000;
margin: 7px 3px; margin: 7px 3px;
padding: 7px; padding: 7px;
border-radius: 10px; border-radius: 10px;
@ -75,9 +75,9 @@ body {
#chat-form { #chat-form {
display: flex; display: flex;
padding: 10px; padding: 3px 10px;
background-color: #282c34; background-color: #282c34;
border-top: 1px solid #444; /* border-top: 1px solid #444; */
} }
#user-input { #user-input {
@ -111,4 +111,99 @@ body {
.hidden { .hidden {
display: none; display: none;
} }
.context-container {
display: flex;
align-items: center;
margin: 10px 0;
gap: 10px; /* 元素间的间距 */
padding: 1px 10px;
}
.context-tab {
display: flex;
align-items: center;
padding: 6px 10px;
background-color: #e9e9e9;
border-radius: 4px;
font-size: 13px;
min-width: 0; /* 允许收缩 */
flex: 0 1 auto; /* 不要占据多余空间 */
color: #000000;
cursor: default;
}
.context-tab.hidden {
display: none;
}
.file-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 10px;
}
.context-placeholder {
display: flex;
align-items: center;
padding: 6px 2px;
color: #666;
font-size: 13px;
min-width: 0;
}
.placeholder-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.context-placeholder.hidden {
display: none;
}
.close-btn {
background: none;
border: none;
font-size: 18px;
cursor: pointer;
color: #666;
padding: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.close-btn:hover {
color: #ff0000;
background-color: rgba(0,0,0,0.1);
border-radius: 50%;
}
.select-context-btn {
width: 24px;
height: 24px;
padding: 0;
background-color: #007acc;
color: white;
border: none;
border-radius: 50%;
font-size: 16px;
font-weight: bold;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.select-context-btn:hover {
background-color: #005a9e;
}

@ -47,8 +47,33 @@ export function activate(context: vscode.ExtensionContext) {
} }
}); });
// 注册选择文件命令
const selectFileCommand = vscode.commands.registerCommand('ai-chat.selectFileAsContext', async () => {
const uris = await vscode.window.showOpenDialog({
canSelectFiles: true,
canSelectFolders: false,
canSelectMany: false, // 单选
title: "选择一个文件作为上下文"
});
if (uris && uris.length > 0) {
const selectedFileUri = uris[0];
const fileContent = await vscode.workspace.fs.readFile(selectedFileUri);
const decodedContent = new TextDecoder("utf-8").decode(fileContent);
// 发送文件内容到 Webview
if (panel && panel.webview) {
panel.webview.postMessage({
command: 'setContextFile',
fileName: selectedFileUri.fsPath,
fileContent: decodedContent
});
}
}
});
// 添加到 subscriptions // 添加到 subscriptions
context.subscriptions.push(statusBarItem, openWebviewCommand, addToChatCommand); context.subscriptions.push(statusBarItem, openWebviewCommand, addToChatCommand,selectFileCommand);
} }
function openWebview( function openWebview(
@ -117,6 +142,27 @@ function openWebview(
panel.webview.postMessage({ command: 'hideLoading' }); panel.webview.postMessage({ command: 'hideLoading' });
break; break;
case 'selectContextFile':
// 执行文件选择逻辑
const uris = await vscode.window.showOpenDialog({
canSelectFiles: true,
canSelectFolders: false,
canSelectMany: false,
title: "选择一个文件作为上下文"
});
if (uris && uris.length > 0) {
const selectedFileUri = uris[0];
const fileContent = await vscode.workspace.fs.readFile(selectedFileUri);
const decodedContent = new TextDecoder("utf-8").decode(fileContent);
panel.webview.postMessage({
command: 'setContextFile',
fileName: selectedFileUri.fsPath,
fileContent: decodedContent
});
}
break;
} }
}, },
undefined, undefined,
@ -187,8 +233,22 @@ function getWebviewContent(styleUri: vscode.Uri, scriptUri: vscode.Uri,highlight
</head> </head>
<body> <body>
<div class="chat-container"> <div class="chat-container">
<!---->
<div id="chat-box"></div> <div id="chat-box"></div>
<!---->
<div class="context-container">
<button id="select-context-btn" class="select-context-btn" title="选择上下文文件">+</button>
<div id="context-placeholder" class="context-placeholder">
<span class="placeholder-text"></span>
</div>
<div id="context-tab" class="context-tab hidden">
<span id="context-file-name" class="file-name"></span>
<button id="close-context-btn" class="close-btn" title="清除上下文">×</button>
</div>
</div>
<!-- --> <!-- -->
<form id="chat-form"> <form id="chat-form">
<input type="text" id="user-input" placeholder="输入你的问题..." required /> <input type="text" id="user-input" placeholder="输入你的问题..." required />

Loading…
Cancel
Save