572 lines
18 KiB
TypeScript
572 lines
18 KiB
TypeScript
/**
|
||
* 文档集对话框组件
|
||
* 功能:添加文档集的对话框
|
||
*/
|
||
|
||
export function getDocsetDialogContent(): string {
|
||
return `
|
||
<div class="docset-dialog" id="docsetDialog">
|
||
<div class="docset-dialog-overlay" onclick="closeDocsetDialog()"></div>
|
||
<div class="docset-dialog-content">
|
||
<div class="docset-dialog-header">
|
||
<h3>添加文档集</h3>
|
||
<button onclick="closeDocsetDialog()">×</button>
|
||
</div>
|
||
<div class="docset-dialog-body">
|
||
<div class="docset-form-group">
|
||
<label>名称</label>
|
||
<input type="text" id="docsetName" placeholder="输入文档集名称" />
|
||
</div>
|
||
<div class="docset-form-group">
|
||
<label>文件</label>
|
||
<div class="docset-hint">支持 .md/.txt/.v/.sv/.pdf,单个文件最大 10 MB,文档集最大 50 MB,最多 1000 个文件</div>
|
||
<button class="docset-add-file-btn" id="addFileBtn" onclick="addFileToDocset()">
|
||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M469.333333 469.333333V170.666667h85.333334v298.666666h298.666666v85.333334h-298.666666v298.666666h-85.333334v-298.666666H170.666667v-85.333334h298.666666z" fill="currentColor"/>
|
||
</svg>
|
||
添加文件
|
||
</button>
|
||
<div id="docsetFilesDisplay" style="display: none; margin-top: 8px;">
|
||
<div id="docsetFilesList" style="max-height: 200px; overflow-y: auto; border: 1px solid var(--vscode-input-border); border-radius: 4px; padding: 8px;"></div>
|
||
<div id="docsetFilesSummary" style="margin-top: 8px; font-size: 12px; color: var(--vscode-descriptionForeground);"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="docset-dialog-footer">
|
||
<button class="docset-btn-cancel" onclick="closeDocsetDialog()">取消</button>
|
||
<button class="docset-btn-confirm" onclick="confirmDocset()">确定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="delete-confirm-dialog" id="deleteConfirmDialog">
|
||
<div class="delete-confirm-content">
|
||
<div class="delete-confirm-title">确认删除</div>
|
||
<div class="delete-confirm-message" id="deleteConfirmMessage"></div>
|
||
<div class="delete-confirm-actions">
|
||
<button class="docset-btn-cancel" onclick="closeDeleteConfirm()">取消</button>
|
||
<button class="docset-btn-confirm" onclick="confirmDelete()">确定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="rename-dialog" id="renameDialog">
|
||
<div class="rename-content">
|
||
<div class="rename-title">修改名称</div>
|
||
<input type="text" id="renameInput" class="rename-input" placeholder="输入新名称" />
|
||
<div class="rename-actions">
|
||
<button class="docset-btn-cancel" onclick="closeRenameDialog()">取消</button>
|
||
<button class="docset-btn-confirm" onclick="confirmRename()">确定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
export function getDocsetDialogStyles(): string {
|
||
return `
|
||
.docset-dialog {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
z-index: 10000;
|
||
}
|
||
|
||
.docset-dialog.active {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.docset-dialog-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
}
|
||
|
||
.docset-dialog-content {
|
||
position: relative;
|
||
width: 90%;
|
||
max-width: 500px;
|
||
background: var(--vscode-editor-background);
|
||
border: 1px solid var(--vscode-panel-border);
|
||
border-radius: 8px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
max-height: 80vh;
|
||
}
|
||
|
||
.docset-dialog-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 16px 20px;
|
||
border-bottom: 1px solid var(--vscode-panel-border);
|
||
}
|
||
|
||
.docset-dialog-header h3 {
|
||
margin: 0;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.docset-dialog-header button {
|
||
width: 32px;
|
||
height: 32px;
|
||
background: transparent;
|
||
border: none;
|
||
font-size: 24px;
|
||
cursor: pointer;
|
||
color: var(--vscode-foreground);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.docset-dialog-header button:hover {
|
||
background: var(--vscode-toolbar-hoverBackground);
|
||
}
|
||
|
||
.docset-dialog-body {
|
||
padding: 20px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.docset-form-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.docset-form-group label {
|
||
display: block;
|
||
margin-bottom: 8px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.docset-hint {
|
||
font-size: 11px;
|
||
font-weight: 400;
|
||
color: var(--vscode-descriptionForeground);
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.docset-form-group input {
|
||
width: 100%;
|
||
padding: 8px 12px;
|
||
background: var(--vscode-input-background);
|
||
border: 1px solid var(--vscode-input-border);
|
||
border-radius: 4px;
|
||
color: var(--vscode-input-foreground);
|
||
font-size: 13px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.docset-add-file-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding: 6px 12px;
|
||
background: transparent;
|
||
color: var(--vscode-textLink-foreground);
|
||
border: 1px solid var(--vscode-textLink-foreground);
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.docset-add-file-btn:hover {
|
||
background: var(--vscode-toolbar-hoverBackground);
|
||
}
|
||
|
||
.docset-add-file-btn svg {
|
||
width: 14px;
|
||
height: 14px;
|
||
}
|
||
|
||
.docset-dialog-footer {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 8px;
|
||
padding: 16px 20px;
|
||
border-top: 1px solid var(--vscode-panel-border);
|
||
}
|
||
|
||
.docset-dialog-footer button {
|
||
padding: 6px 16px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.docset-btn-cancel {
|
||
background: var(--vscode-button-secondaryBackground);
|
||
color: var(--vscode-button-secondaryForeground);
|
||
}
|
||
|
||
.docset-btn-cancel:hover {
|
||
background: var(--vscode-button-secondaryHoverBackground);
|
||
}
|
||
|
||
.docset-btn-confirm {
|
||
background: var(--vscode-button-background);
|
||
color: var(--vscode-button-foreground);
|
||
}
|
||
|
||
.docset-btn-confirm:hover {
|
||
background: var(--vscode-button-hoverBackground);
|
||
}
|
||
|
||
.docset-delete-btn, .docset-change-btn {
|
||
position: relative;
|
||
background: transparent;
|
||
border: none;
|
||
cursor: pointer;
|
||
padding: 4px;
|
||
color: var(--vscode-foreground);
|
||
opacity: 0.6;
|
||
}
|
||
|
||
.docset-delete-btn:hover, .docset-change-btn:hover {
|
||
opacity: 1;
|
||
}
|
||
|
||
.docset-delete-btn:hover::after, .docset-change-btn:hover::after {
|
||
content: attr(data-tooltip);
|
||
position: absolute;
|
||
bottom: 100%;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background: var(--vscode-editorHoverWidget-background);
|
||
color: var(--vscode-editorHoverWidget-foreground);
|
||
border: 1px solid var(--vscode-editorHoverWidget-border);
|
||
padding: 4px 8px;
|
||
border-radius: 3px;
|
||
font-size: 12px;
|
||
white-space: nowrap;
|
||
margin-bottom: 4px;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.delete-confirm-dialog {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.4);
|
||
z-index: 10000;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.delete-confirm-dialog.active {
|
||
display: flex;
|
||
}
|
||
|
||
.delete-confirm-content {
|
||
background: var(--vscode-editor-background);
|
||
border: 1px solid var(--vscode-panel-border);
|
||
border-radius: 6px;
|
||
padding: 20px;
|
||
min-width: 300px;
|
||
max-width: 400px;
|
||
}
|
||
|
||
.delete-confirm-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.delete-confirm-message {
|
||
font-size: 13px;
|
||
color: var(--vscode-descriptionForeground);
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.delete-confirm-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 8px;
|
||
}
|
||
|
||
.delete-confirm-actions button {
|
||
padding: 6px 16px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.rename-dialog {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.4);
|
||
z-index: 10000;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.rename-dialog.active {
|
||
display: flex;
|
||
}
|
||
|
||
.rename-content {
|
||
background: var(--vscode-editor-background);
|
||
border: 1px solid var(--vscode-panel-border);
|
||
border-radius: 6px;
|
||
padding: 20px;
|
||
min-width: 300px;
|
||
max-width: 400px;
|
||
}
|
||
|
||
.rename-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.rename-input {
|
||
width: 100%;
|
||
padding: 8px 12px;
|
||
background: var(--vscode-input-background);
|
||
border: 1px solid var(--vscode-input-border);
|
||
border-radius: 4px;
|
||
color: var(--vscode-input-foreground);
|
||
font-size: 13px;
|
||
box-sizing: border-box;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.rename-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 8px;
|
||
}
|
||
|
||
.rename-actions button {
|
||
padding: 6px 16px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
}
|
||
`;
|
||
}
|
||
|
||
export function getDocsetDialogScript(): string {
|
||
return `
|
||
let docsetFiles = [];
|
||
|
||
function openAddDocumentSetDialog() {
|
||
const dialog = document.getElementById('docsetDialog');
|
||
if (dialog) {
|
||
dialog.classList.add('active');
|
||
docsetFiles = [];
|
||
document.getElementById('docsetName').value = '';
|
||
document.getElementById('addFileBtn').style.display = 'flex';
|
||
document.getElementById('docsetFilesDisplay').style.display = 'none';
|
||
}
|
||
}
|
||
|
||
function closeDocsetDialog() {
|
||
const dialog = document.getElementById('docsetDialog');
|
||
if (dialog) {
|
||
dialog.classList.remove('active');
|
||
}
|
||
}
|
||
|
||
function addFileToDocset() {
|
||
vscode.postMessage({ command: 'selectFilesForDocset' });
|
||
}
|
||
|
||
function formatFileSize(bytes) {
|
||
if (bytes < 1024) return bytes + ' B';
|
||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
||
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
||
}
|
||
|
||
function updateDocsetDisplay() {
|
||
if (docsetFiles.length === 0) {
|
||
document.getElementById('addFileBtn').style.display = 'flex';
|
||
document.getElementById('docsetFilesDisplay').style.display = 'none';
|
||
return;
|
||
}
|
||
|
||
document.getElementById('addFileBtn').style.display = 'none';
|
||
document.getElementById('docsetFilesDisplay').style.display = 'block';
|
||
|
||
const listEl = document.getElementById('docsetFilesList');
|
||
const summaryEl = document.getElementById('docsetFilesSummary');
|
||
|
||
listEl.innerHTML = docsetFiles.map((file, index) => \`
|
||
<div style="display: flex; justify-content: space-between; align-items: center; font-size: 12px; padding: 4px 0; color: var(--vscode-foreground);">
|
||
<span>\${file.name || file.absolutePath}</span>
|
||
<button onclick="removeDocsetFile(\${index})" style="background: transparent; border: none; color: var(--vscode-foreground); cursor: pointer; padding: 0 4px; opacity: 0.7;">
|
||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
|
||
<path d="M10 3h3v1h-1v9l-1 1H4l-1-1V4H2V3h3V2a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v1zM9 2H6v1h3V2zM4 13h7V4H4v9zm2-8H5v7h1V5zm1 0h1v7H7V5zm2 0h1v7H9V5z"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
\`).join('');
|
||
|
||
const totalSize = docsetFiles.reduce((sum, f) => sum + (f.size || 0), 0);
|
||
summaryEl.textContent = \`已选择 \${docsetFiles.length} 个文件,总大小 \${formatFileSize(totalSize)}\`;
|
||
}
|
||
|
||
function removeDocsetFile(index) {
|
||
docsetFiles.splice(index, 1);
|
||
updateDocsetDisplay();
|
||
}
|
||
|
||
function confirmDocset() {
|
||
const name = document.getElementById('docsetName').value.trim();
|
||
if (!name) {
|
||
alert('请输入文档集名称');
|
||
return;
|
||
}
|
||
if (docsetFiles.length === 0) {
|
||
alert('请添加至少一个文件');
|
||
return;
|
||
}
|
||
|
||
vscode.postMessage({
|
||
command: 'saveDocumentSet',
|
||
name: name,
|
||
documents: docsetFiles
|
||
});
|
||
|
||
closeDocsetDialog();
|
||
}
|
||
|
||
function renderDocumentSets(documentSets) {
|
||
const listEl = document.getElementById('contextDocsList');
|
||
if (!listEl) return;
|
||
|
||
if (!documentSets || documentSets.length === 0) {
|
||
listEl.innerHTML = '<div class="context-empty"><p>暂无文档集</p></div>';
|
||
return;
|
||
}
|
||
|
||
listEl.innerHTML = documentSets.map(ds => \`
|
||
<div class="docset-item">
|
||
<div class="docset-name">\${ds.name}</div>
|
||
<div class="docset-meta">更新于 \${new Date(ds.updatedAt).toLocaleString('zh-CN')}</div>
|
||
<button class="docset-change-btn" data-tooltip="修改名称" onclick="changeDocsetName('\${ds.id}', '\${ds.name}')">
|
||
<svg t="1773883957219" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7170" width="14" height="14">
|
||
<path d="M745.76 369.86l-451 537.48a18.693 18.693 0 0 1-8.46 5.74l-136.58 45.27c-13.24 4.39-26.46-6.71-24.43-20.5l20.86-142.36c0.5-3.44 1.95-6.67 4.19-9.33l451-537.48c6.65-7.93 18.47-8.96 26.4-2.31l115.71 97.1c7.92 6.64 8.96 18.46 2.31 26.39zM894.53 192.56l-65.9 78.53c-6.65 7.93-18.47 8.96-26.4 2.31l-115.71-97.1c-7.93-6.65-8.96-18.47-2.31-26.4l65.9-78.53c6.65-7.93 18.47-8.96 26.4-2.31l115.71 97.1c7.93 6.65 8.96 18.47 2.31 26.4z" fill="currentColor" p-id="7171"></path>
|
||
</svg>
|
||
</button>
|
||
<button class="docset-delete-btn" data-tooltip="删除" onclick="showDeleteConfirm('\${ds.id}', '\${ds.name}')">
|
||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
|
||
<path d="M10 3h3v1h-1v9l-1 1H4l-1-1V4H2V3h3V2a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v1zM9 2H6v1h3V2zM4 13h7V4H4v9zm2-8H5v7h1V5zm1 0h1v7H7V5zm2 0h1v7H9V5z"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
\`).join('');
|
||
}
|
||
|
||
let deleteTargetId = null;
|
||
let renameTargetId = null;
|
||
let renameOriginalName = null;
|
||
|
||
window.showDeleteConfirm = function(id, name) {
|
||
deleteTargetId = id;
|
||
document.getElementById('deleteConfirmMessage').textContent = \`确定要删除文档集 "\${name}" 吗?此操作不可恢复。\`;
|
||
document.getElementById('deleteConfirmDialog').classList.add('active');
|
||
};
|
||
|
||
window.changeDocsetName = function(id, name) {
|
||
renameTargetId = id;
|
||
renameOriginalName = name;
|
||
document.getElementById('renameInput').value = name;
|
||
document.getElementById('renameDialog').classList.add('active');
|
||
setTimeout(() => document.getElementById('renameInput').focus(), 100);
|
||
};
|
||
|
||
window.closeDeleteConfirm = function() {
|
||
document.getElementById('deleteConfirmDialog').classList.remove('active');
|
||
deleteTargetId = null;
|
||
};
|
||
|
||
window.closeRenameDialog = function() {
|
||
document.getElementById('renameDialog').classList.remove('active');
|
||
renameTargetId = null;
|
||
renameOriginalName = null;
|
||
};
|
||
|
||
window.confirmDelete = function() {
|
||
if (deleteTargetId) {
|
||
vscode.postMessage({ command: 'deleteDocumentSet', id: deleteTargetId });
|
||
closeDeleteConfirm();
|
||
}
|
||
};
|
||
|
||
window.confirmRename = function() {
|
||
const newName = document.getElementById('renameInput').value.trim();
|
||
if (!newName) {
|
||
alert('请输入名称');
|
||
return;
|
||
}
|
||
if (newName !== renameOriginalName) {
|
||
vscode.postMessage({ command: 'changeDocumentSetName', id: renameTargetId, newName: newName });
|
||
}
|
||
closeRenameDialog();
|
||
};
|
||
|
||
window.addEventListener('message', event => {
|
||
const message = event.data;
|
||
if (message.command === 'filesSelectedForDocset') {
|
||
if (message.errors && message.errors.length > 0) {
|
||
alert('部分文件添加失败:\\n' + message.errors.join('\\n'));
|
||
}
|
||
|
||
const MAX_FILE_SIZE = 10 * 1024 * 1024;
|
||
const MAX_TOTAL_SIZE = 50 * 1024 * 1024;
|
||
const MAX_FILE_COUNT = 1000;
|
||
|
||
const vaildFiles = [];
|
||
const errors = [];
|
||
|
||
for (const file of message.files) {
|
||
if (file.size > MAX_FILE_SIZE) {
|
||
errors.push(\`\${file.name || file.absolutePath} 超过单个文件大小限制(\${formatFileSize(MAX_FILE_SIZE)})\`);
|
||
} else {
|
||
vaildFiles.push(file);
|
||
}
|
||
}
|
||
|
||
const newFiles = [...docsetFiles, ...vaildFiles];
|
||
const totalSize = newFiles.reduce((sum, f) => sum + (f.size || 0), 0);
|
||
|
||
if (newFiles.length > MAX_FILE_COUNT) {
|
||
errors.push(\`文档数量超过限制(最多1000个),当前数量(\${newFiles.length} 个)\`);
|
||
return;
|
||
}
|
||
|
||
if (totalSize > MAX_TOTAL_SIZE) {
|
||
errors.push(\`文档集总大小超过 50MB 限制,当前大小为(\${formatFileSize(MAX_TOTAL_SIZE)})\`);
|
||
return;
|
||
}
|
||
|
||
if(errors.length > 0) {
|
||
alert('以下文件被跳过:\\n' + errors.join('\\n'));
|
||
}
|
||
|
||
docsetFiles = newFiles;
|
||
updateDocsetDisplay();
|
||
} else if (message.command === 'documentSetSaved') {
|
||
renderDocumentSets(message.documentSets);
|
||
}
|
||
});
|
||
`;
|
||
}
|