534 lines
16 KiB
TypeScript
534 lines
16 KiB
TypeScript
/**
|
|
* 添加上下文按钮组件
|
|
*/
|
|
|
|
/**
|
|
* 获取添加上下文按钮的 HTML 内容
|
|
*/
|
|
export function getContextButtonContent(): string {
|
|
return `
|
|
<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>
|
|
|
|
</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>
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* 获取添加上下文按钮的样式
|
|
*/
|
|
export function getContextButtonStyles(): string {
|
|
return `
|
|
/* 上下文选择器容器 */
|
|
.context-selector-wrapper {
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
|
|
/* 添加上下文按钮样式 */
|
|
.add-context-button {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 6px 12px;
|
|
background: rgba(128, 128, 128, 0.2);
|
|
border: 1px solid var(--vscode-input-border);
|
|
border-radius: 6px;
|
|
color: var(--vscode-foreground);
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.add-context-button:hover {
|
|
background: rgba(128, 128, 128, 0.3);
|
|
border-color: var(--vscode-focusBorder);
|
|
}
|
|
|
|
.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;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.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);
|
|
}
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* 获取添加上下文按钮的脚本
|
|
*/
|
|
export function getContextButtonScript(): string {
|
|
return `
|
|
// 上下文菜单状态
|
|
let currentListData = [];
|
|
let filteredListData = [];
|
|
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 = [];
|
|
filteredListData = [];
|
|
clearContextSearchInput();
|
|
}
|
|
|
|
function clearContextSearchInput() {
|
|
const searchInput = document.getElementById('contextMenuSearch');
|
|
if (searchInput) {
|
|
searchInput.value = '';
|
|
}
|
|
}
|
|
|
|
// 切换到列表视图
|
|
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 || [];
|
|
filteredListData = currentListData;
|
|
selectedItems.clear();
|
|
|
|
clearContextSearchInput();
|
|
renderList(filteredListData);
|
|
updateSelectedCount();
|
|
}
|
|
}
|
|
|
|
// 渲染列表
|
|
function renderList(data) {
|
|
const body = document.getElementById('contextMenuListBody');
|
|
if (!body) return;
|
|
|
|
filteredListData = data || [];
|
|
|
|
body.innerHTML = filteredListData.map((item, index) => \`
|
|
<div class="context-menu-list-item \${selectedItems.has(item.path) ? 'selected' : ''}" onclick="toggleItemSelection(\${index})">
|
|
<input type="checkbox" id="item-\${index}" \${selectedItems.has(item.path) ? 'checked' : ''} />
|
|
<label>\${item.relativePath || item.path}</label>
|
|
</div>
|
|
\`).join('');
|
|
}
|
|
|
|
// 切换项选择
|
|
function toggleItemSelection(index) {
|
|
const selectedItem = filteredListData[index];
|
|
if (!selectedItem) return;
|
|
|
|
const selectedPath = selectedItem.path;
|
|
const checkbox = document.getElementById('item-' + index);
|
|
const item = document.querySelectorAll('.context-menu-list-item')[index];
|
|
|
|
if (selectedItems.has(selectedPath)) {
|
|
selectedItems.delete(selectedPath);
|
|
if (checkbox) checkbox.checked = false;
|
|
if (item) item.classList.remove('selected');
|
|
} else {
|
|
selectedItems.add(selectedPath);
|
|
if (checkbox) checkbox.checked = true;
|
|
if (item) item.classList.add('selected');
|
|
}
|
|
|
|
updateSelectedCount();
|
|
}
|
|
|
|
// 更新选中数量
|
|
function updateSelectedCount() {
|
|
const countEl = document.getElementById('contextMenuListCount');
|
|
if (countEl) {
|
|
countEl.textContent = '已选择 ' + selectedItems.size + ' 项';
|
|
}
|
|
}
|
|
|
|
// 确认选择
|
|
function confirmSelection() {
|
|
try {
|
|
const selected = currentListData.filter(item => selectedItems.has(item.path));
|
|
|
|
if (selected.length > 0) {
|
|
selected.forEach(item => {
|
|
addContextItem(currentListType, item.path, item.relativePath || item.path);
|
|
});
|
|
}
|
|
} finally {
|
|
const menu = document.getElementById('contextMenu');
|
|
const button = document.querySelector('.add-context-button');
|
|
if (menu) {
|
|
menu.classList.remove('show');
|
|
}
|
|
if (button) {
|
|
button.classList.remove('active');
|
|
}
|
|
backToMainMenu();
|
|
}
|
|
}
|
|
|
|
// 添加图片
|
|
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().trim();
|
|
const filtered = currentListData.filter(item =>
|
|
(item.relativePath || item.path || '').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);
|
|
}
|
|
});
|
|
`;
|
|
}
|