feat:添加上下文功能实现

This commit is contained in:
Roe-xin
2026-01-05 15:59:26 +08:00
parent 9b0d2d5e01
commit 1d7f3d7626
4 changed files with 798 additions and 14 deletions

View File

@ -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 = !!(

View File

@ -7,14 +7,78 @@
*/
export function getContextButtonContent(): string {
return `
<div class="tooltip">
<button class="add-context-button" onclick="handleAddContext()">
<svg t="1766915545722" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4994" width="200" height="200">
<path d="M469.333333 469.333333V170.666667h85.333334v298.666666h298.666666v85.333334h-298.666666v298.666666h-85.333334v-298.666666H170.666667v-85.333334h298.666666z" fill="#8a8a8a" p-id="4995"></path>
</svg>
<span class="add-context-label">添加上下文</span>
</button>
<span class="tooltiptext">添加文件或代码片段作为上下文</span>
<div class="context-selector-wrapper">
<div class="tooltip">
<button class="add-context-button" onclick="toggleContextMenu()">
<svg t="1766915545722" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4994" width="200" height="200">
<path d="M469.333333 469.333333V170.666667h85.333334v298.666666h298.666666v85.333334h-298.666666v298.666666h-85.333334v-298.666666H170.666667v-85.333334h298.666666z" fill="#8a8a8a" p-id="4995"></path>
</svg>
<span class="add-context-label">添加上下文</span>
<svg class="dropdown-arrow" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M512 714.666667L213.333333 416l42.666667-42.666667L512 629.333333l256-256 42.666667 42.666667z" fill="currentColor"/>
</svg>
</button>
<span class="tooltiptext">添加文件、文件夹、图片或文档作为上下文</span>
</div>
<!-- 上拉菜单 -->
<div class="context-menu" id="contextMenu">
<!-- 主菜单 -->
<div class="context-menu-main" id="contextMenuMain">
<div class="context-menu-item" onclick="showFileList()">
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326z m1.8 562H232V136h302v216c0 23.2 18.8 42 42 42h216v494z" fill="currentColor"/>
</svg>
<span>文件</span>
<svg class="arrow-right" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M340.864 149.312l384 384-384 384-45.248-45.248L634.368 533.312 295.616 194.56z" fill="currentColor"/>
</svg>
</div>
<div class="context-menu-item" onclick="showFolderList()">
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M880 298.4H521L403.7 186.2c-1.5-1.4-3.5-2.2-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z" fill="currentColor"/>
</svg>
<span>文件夹</span>
<svg class="arrow-right" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M340.864 149.312l384 384-384 384-45.248-45.248L634.368 533.312 295.616 194.56z" fill="currentColor"/>
</svg>
</div>
<div class="context-menu-item" onclick="handleAddImage()">
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M928 160H96c-17.7 0-32 14.3-32 32v640c0 17.7 14.3 32 32 32h832c17.7 0 32-14.3 32-32V192c0-17.7-14.3-32-32-32z m-40 632H136V232h752v560z m-120-240c0 55.2-44.8 100-100 100s-100-44.8-100-100 44.8-100 100-100 100 44.8 100 100z m-476 0l164 164h476L696 480 536 640l-84-84-160 160z" fill="currentColor"/>
</svg>
<span>图片</span>
</div>
<div class="context-menu-item" onclick="handleAddDocument()">
<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 m-40 824H232V136h560v752z m-120-568H352c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h320c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z m0 144H352c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h320c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z m0 144H352c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h320c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z" fill="currentColor"/>
</svg>
<span>文档库</span>
</div>
</div>
<!-- 文件/文件夹列表视图 -->
<div class="context-menu-list" id="contextMenuList" style="display: none;">
<div class="context-menu-list-header">
<button class="context-menu-back" onclick="backToMainMenu()">
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8c-16.4 12.8-16.4 37.5 0 50.3l450.8 352.1c5.3 4.1 12.9 0.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z" fill="currentColor"/>
</svg>
</button>
<span id="contextMenuListTitle">选择文件</span>
</div>
<div class="context-menu-list-body" id="contextMenuListBody">
<!-- 动态加载列表 -->
</div>
<div class="context-menu-list-footer">
<input type="text" id="contextMenuSearch" placeholder="搜索..." />
<div class="context-menu-list-actions">
<span id="contextMenuListCount">已选择 0 项</span>
<button class="primary" onclick="confirmSelection()">确定</button>
</div>
</div>
</div>
</div>
</div>
`;
}
@ -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) => \`
<div class="context-menu-list-item" onclick="toggleItemSelection(\${index})">
<input type="checkbox" id="item-\${index}" />
<label for="item-\${index}">\${item.relativePath}</label>
</div>
\`).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);
}
});
`;
}

225
src/views/contextDisplay.ts Normal file
View File

@ -0,0 +1,225 @@
/**
* 上下文显示组件
* 用于显示已选择的文件、文件夹、图片和文档
*/
/**
* 获取上下文显示区域的 HTML 内容
*/
export function getContextDisplayContent(): string {
return `
<div class="context-display-area" id="contextDisplayArea" style="display: none;">
<div class="context-items-container" id="contextItemsContainer">
<!-- 动态添加的上下文项将显示在这里 -->
</div>
</div>
`;
}
/**
* 获取上下文显示区域的样式
*/
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 viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7z" fill="currentColor"/></svg>';
}
// 获取文件夹图标 SVG
function getFolderIcon() {
return '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M880 298.4H521L403.7 186.2c-1.5-1.4-3.5-2.2-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32z" fill="currentColor"/></svg>';
}
// 获取图片图标 SVG
function getImageIcon() {
return '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M928 160H96c-17.7 0-32 14.3-32 32v640c0 17.7 14.3 32 32 32h832c17.7 0 32-14.3 32-32V192c0-17.7-14.3-32-32-32z" fill="currentColor"/></svg>';
}
// 获取文档图标 SVG
function getDocumentIcon() {
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 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>';
}
// 提取文件名
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 \`
<div class="context-item" title="\${item.path}">
\${icon}
<span class="context-item-name">\${getFileName(item.path)}</span>
<span class="context-item-remove" onclick="removeContextItem(\${item.id})">
\${getRemoveIcon()}
</span>
</div>
\`;
}).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();
};
`;
}

View File

@ -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(
<div class="input-top-toolbar">
${getContextButtonContent()}
</div>
<!-- 上下文显示区域 -->
${getContextDisplayContent()}
<textarea
id="messageInput"
placeholder="输入您的问题,按 Enter 发送Shift + Enter 换行..."
@ -76,6 +83,7 @@ export function getInputAreaStyles(): string {
${getModeSelectorStyles()}
${getModelSelectorStyles()}
${getContextButtonStyles()}
${getContextDisplayStyles()}
${getContextCompressStyles()}
${getOptimizeButtonStyles()}
.input-area {
@ -281,6 +289,7 @@ export function getInputAreaScript(): string {
// 注意getModeSelectorScript() 已在 webviewContent.ts 开头加载,这里不再重复加载
${getModelSelectorScript()}
${getContextButtonScript()}
${getContextDisplayScript()}
${getContextCompressScript()}
${getOptimizeButtonScript()}
@ -391,6 +400,9 @@ export function getInputAreaScript(): string {
const model = getCurrentModel(); // 从模型选择器组件获取当前模型
const planMode = document.getElementById('planToggle')?.checked || false;
// 获取上下文项
const contextItems = window.getContextItems ? window.getContextItems() : [];
addMessage(text, 'user');
// 标记已有消息,切换布局到底部
@ -400,7 +412,14 @@ export function getInputAreaScript(): string {
// 切换按钮为暂停状态
setSendButtonState(true);
vscode.postMessage({ command: 'sendMessage', text: text, mode: mode, model: model, planMode: planMode });
vscode.postMessage({
command: 'sendMessage',
text: text,
mode: mode,
model: model,
planMode: planMode,
contextItems: contextItems
});
messageInput.value = '';
autoResizeTextarea(); // 重置输入框高度
messageInput.focus();