Add help view and core memory styles

- Introduced styles for the help view including tab transitions, accordion animations, search highlighting, and responsive design.
- Implemented core memory styles with modal base styles, memory card designs, and knowledge graph visualization.
- Enhanced dark mode support across various components.
- Added loading states and empty state designs for better user experience.
This commit is contained in:
catlog22
2025-12-18 18:29:45 +08:00
parent 4577be71ce
commit 17af615fe2
46 changed files with 4941 additions and 4326 deletions

View File

@@ -92,6 +92,15 @@ async function loadCodexLensStatus() {
const data = await response.json();
codexLensStatus = data;
// Expose to window for other modules (e.g., codexlens-manager.js)
if (!window.cliToolsStatus) {
window.cliToolsStatus = {};
}
window.cliToolsStatus.codexlens = {
installed: data.ready || false,
version: data.version || null
};
// Update CodexLens badge
updateCodexLensBadge();

View File

@@ -39,7 +39,27 @@ async function loadIndexStats() {
*/
function renderIndexCard() {
const container = document.getElementById('indexCard');
if (!container || !indexData) return;
if (!container) {
console.warn('[IndexManager] Container not found');
return;
}
// Handle case when data is not loaded yet
if (!indexData) {
container.innerHTML = `
<div class="bg-card border border-border rounded-lg overflow-hidden">
<div class="bg-muted/30 border-b border-border px-4 py-3 flex items-center gap-2">
<i data-lucide="database" class="w-4 h-4 text-primary"></i>
<span class="font-medium text-foreground">${t('index.manager') || 'Index Manager'}</span>
</div>
<div class="p-4 text-center text-muted-foreground">
<div class="animate-pulse">${t('common.loading') || 'Loading...'}</div>
</div>
</div>
`;
if (typeof lucide !== 'undefined') lucide.createIcons();
return;
}
const { indexDir, indexes, summary } = indexData;
@@ -64,113 +84,113 @@ function renderIndexCard() {
if (indexes && indexes.length > 0) {
indexes.forEach(function(idx) {
const vectorBadge = idx.hasVectorIndex
? '<span class="text-xs px-1.5 py-0.5 bg-primary/10 text-primary rounded">' + (t('index.vector') || 'Vector') + '</span>'
? `<span class="text-xs px-1.5 py-0.5 bg-primary/10 text-primary rounded">${t('index.vector') || 'Vector'}</span>`
: '';
const normalBadge = idx.hasNormalIndex
? '<span class="text-xs px-1.5 py-0.5 bg-muted text-muted-foreground rounded">' + (t('index.fts') || 'FTS') + '</span>'
? `<span class="text-xs px-1.5 py-0.5 bg-muted text-muted-foreground rounded">${t('index.fts') || 'FTS'}</span>`
: '';
indexRows += '\
<tr class="border-t border-border hover:bg-muted/30 transition-colors">\
<td class="py-2 px-2 text-foreground">\
<div class="flex items-center gap-2">\
<span class="font-mono text-xs truncate max-w-[250px]" title="' + escapeHtml(idx.id) + '">' + escapeHtml(idx.id) + '</span>\
</div>\
</td>\
<td class="py-2 px-2 text-right text-muted-foreground">' + idx.sizeFormatted + '</td>\
<td class="py-2 px-2 text-center">\
<div class="flex items-center justify-center gap-1">' + vectorBadge + normalBadge + '</div>\
</td>\
<td class="py-2 px-2 text-right text-muted-foreground">' + formatTimeAgo(idx.lastModified) + '</td>\
<td class="py-2 px-1 text-center">\
<button onclick="cleanIndexProject(\'' + escapeHtml(idx.id) + '\')" \
class="text-destructive/70 hover:text-destructive p-1 rounded hover:bg-destructive/10 transition-colors" \
title="' + (t('index.cleanProject') || 'Clean Index') + '">\
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i>\
</button>\
</td>\
</tr>\
';
indexRows += `
<tr class="border-t border-border hover:bg-muted/30 transition-colors">
<td class="py-2 px-2 text-foreground">
<div class="flex items-center gap-2">
<span class="font-mono text-xs truncate max-w-[250px]" title="${escapeHtml(idx.id)}">${escapeHtml(idx.id)}</span>
</div>
</td>
<td class="py-2 px-2 text-right text-muted-foreground">${idx.sizeFormatted}</td>
<td class="py-2 px-2 text-center">
<div class="flex items-center justify-center gap-1">${vectorBadge}${normalBadge}</div>
</td>
<td class="py-2 px-2 text-right text-muted-foreground">${formatTimeAgo(idx.lastModified)}</td>
<td class="py-2 px-1 text-center">
<button onclick="cleanIndexProject('${escapeHtml(idx.id)}')"
class="text-destructive/70 hover:text-destructive p-1 rounded hover:bg-destructive/10 transition-colors"
title="${t('index.cleanProject') || 'Clean Index'}">
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
</button>
</td>
</tr>
`;
});
} else {
indexRows = '\
<tr>\
<td colspan="5" class="py-4 text-center text-muted-foreground text-sm">' + (t('index.noIndexes') || 'No indexes yet') + '</td>\
</tr>\
';
indexRows = `
<tr>
<td colspan="5" class="py-4 text-center text-muted-foreground text-sm">${t('index.noIndexes') || 'No indexes yet'}</td>
</tr>
`;
}
container.innerHTML = '\
<div class="bg-card border border-border rounded-lg overflow-hidden">\
<div class="bg-muted/30 border-b border-border px-4 py-3 flex items-center justify-between">\
<div class="flex items-center gap-2">\
<i data-lucide="database" class="w-4 h-4 text-primary"></i>\
<span class="font-medium text-foreground">' + (t('index.manager') || 'Index Manager') + '</span>\
<span class="text-xs px-2 py-0.5 bg-muted rounded-full text-muted-foreground">' + (summary?.totalSizeFormatted || '0 B') + '</span>\
</div>\
<div class="flex items-center gap-2">\
<button onclick="loadIndexStats()" class="text-xs px-2 py-1 text-muted-foreground hover:text-foreground hover:bg-muted rounded transition-colors" title="' + (t('common.refresh') || 'Refresh') + '">\
<i data-lucide="refresh-cw" class="w-3.5 h-3.5"></i>\
</button>\
<button onclick="showCodexLensConfigModal()" class="text-xs px-2 py-1 text-muted-foreground hover:text-foreground hover:bg-muted rounded transition-colors" title="' + (t('common.settings') || 'Settings') + '">\
<i data-lucide="settings" class="w-3.5 h-3.5"></i>\
</button>\
</div>\
</div>\
<div class="p-4">\
<div class="flex items-center gap-2 mb-3 text-xs text-muted-foreground">\
<i data-lucide="folder" class="w-3.5 h-3.5"></i>\
<span class="font-mono truncate" title="' + escapeHtml(indexDir || '') + '">' + escapeHtml(indexDir || t('index.notConfigured') || 'Not configured') + '</span>\
</div>\
<div class="grid grid-cols-4 gap-3 mb-4">\
<div class="bg-muted/30 rounded-lg p-3 text-center">\
<div class="text-lg font-semibold text-foreground">' + (summary?.totalProjects || 0) + '</div>\
<div class="text-xs text-muted-foreground">' + (t('index.projects') || 'Projects') + '</div>\
</div>\
<div class="bg-muted/30 rounded-lg p-3 text-center">\
<div class="text-lg font-semibold text-foreground">' + (summary?.totalSizeFormatted || '0 B') + '</div>\
<div class="text-xs text-muted-foreground">' + (t('index.totalSize') || 'Total Size') + '</div>\
</div>\
<div class="bg-muted/30 rounded-lg p-3 text-center">\
<div class="text-lg font-semibold text-foreground">' + (summary?.vectorIndexCount || 0) + '</div>\
<div class="text-xs text-muted-foreground">' + (t('index.vectorIndexes') || 'Vector') + '</div>\
</div>\
<div class="bg-muted/30 rounded-lg p-3 text-center">\
<div class="text-lg font-semibold text-foreground">' + (summary?.normalIndexCount || 0) + '</div>\
<div class="text-xs text-muted-foreground">' + (t('index.ftsIndexes') || 'FTS') + '</div>\
</div>\
</div>\
<div class="border border-border rounded-lg overflow-hidden">\
<table class="w-full text-sm">\
<thead class="bg-muted/50">\
<tr class="text-xs text-muted-foreground">\
<th class="py-2 px-2 text-left font-medium">' + (t('index.projectId') || 'Project ID') + '</th>\
<th class="py-2 px-2 text-right font-medium">' + (t('index.size') || 'Size') + '</th>\
<th class="py-2 px-2 text-center font-medium">' + (t('index.type') || 'Type') + '</th>\
<th class="py-2 px-2 text-right font-medium">' + (t('index.lastModified') || 'Modified') + '</th>\
<th class="py-2 px-1 w-8"></th>\
</tr>\
</thead>\
<tbody>\
' + indexRows + '\
</tbody>\
</table>\
</div>\
<div class="mt-4 flex justify-between items-center gap-2">\
<button onclick="initCodexLensIndex()" \
class="text-xs px-3 py-1.5 bg-primary/10 text-primary hover:bg-primary/20 rounded transition-colors flex items-center gap-1.5">\
<i data-lucide="database" class="w-3.5 h-3.5"></i>\
' + (t('index.initCurrent') || 'Init Current Project') + '\
</button>\
<button onclick="cleanAllIndexesConfirm()" \
class="text-xs px-3 py-1.5 bg-destructive/10 text-destructive hover:bg-destructive/20 rounded transition-colors flex items-center gap-1.5">\
<i data-lucide="trash" class="w-3.5 h-3.5"></i>\
' + (t('index.cleanAll') || 'Clean All') + '\
</button>\
</div>\
</div>\
</div>\
';
container.innerHTML = `
<div class="bg-card border border-border rounded-lg overflow-hidden">
<div class="bg-muted/30 border-b border-border px-4 py-3 flex items-center justify-between">
<div class="flex items-center gap-2">
<i data-lucide="database" class="w-4 h-4 text-primary"></i>
<span class="font-medium text-foreground">${t('index.manager') || 'Index Manager'}</span>
<span class="text-xs px-2 py-0.5 bg-muted rounded-full text-muted-foreground">${summary?.totalSizeFormatted || '0 B'}</span>
</div>
<div class="flex items-center gap-2">
<button onclick="loadIndexStats()" class="text-xs px-2 py-1 text-muted-foreground hover:text-foreground hover:bg-muted rounded transition-colors" title="${t('common.refresh') || 'Refresh'}">
<i data-lucide="refresh-cw" class="w-3.5 h-3.5"></i>
</button>
<button onclick="showCodexLensConfigModal()" class="text-xs px-2 py-1 text-muted-foreground hover:text-foreground hover:bg-muted rounded transition-colors" title="${t('common.settings') || 'Settings'}">
<i data-lucide="settings" class="w-3.5 h-3.5"></i>
</button>
</div>
</div>
<div class="p-4">
<div class="flex items-center gap-2 mb-3 text-xs text-muted-foreground">
<i data-lucide="folder" class="w-3.5 h-3.5"></i>
<span class="font-mono truncate" title="${escapeHtml(indexDir || '')}">${escapeHtml(indexDir || t('index.notConfigured') || 'Not configured')}</span>
</div>
<div class="grid grid-cols-4 gap-3 mb-4">
<div class="bg-muted/30 rounded-lg p-3 text-center">
<div class="text-lg font-semibold text-foreground">${summary?.totalProjects || 0}</div>
<div class="text-xs text-muted-foreground">${t('index.projects') || 'Projects'}</div>
</div>
<div class="bg-muted/30 rounded-lg p-3 text-center">
<div class="text-lg font-semibold text-foreground">${summary?.totalSizeFormatted || '0 B'}</div>
<div class="text-xs text-muted-foreground">${t('index.totalSize') || 'Total Size'}</div>
</div>
<div class="bg-muted/30 rounded-lg p-3 text-center">
<div class="text-lg font-semibold text-foreground">${summary?.vectorIndexCount || 0}</div>
<div class="text-xs text-muted-foreground">${t('index.vectorIndexes') || 'Vector'}</div>
</div>
<div class="bg-muted/30 rounded-lg p-3 text-center">
<div class="text-lg font-semibold text-foreground">${summary?.normalIndexCount || 0}</div>
<div class="text-xs text-muted-foreground">${t('index.ftsIndexes') || 'FTS'}</div>
</div>
</div>
<div class="border border-border rounded-lg overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-muted/50">
<tr class="text-xs text-muted-foreground">
<th class="py-2 px-2 text-left font-medium">${t('index.projectId') || 'Project ID'}</th>
<th class="py-2 px-2 text-right font-medium">${t('index.size') || 'Size'}</th>
<th class="py-2 px-2 text-center font-medium">${t('index.type') || 'Type'}</th>
<th class="py-2 px-2 text-right font-medium">${t('index.lastModified') || 'Modified'}</th>
<th class="py-2 px-1 w-8"></th>
</tr>
</thead>
<tbody>
${indexRows}
</tbody>
</table>
</div>
<div class="mt-4 flex justify-between items-center gap-2">
<button onclick="initCodexLensIndex()"
class="text-xs px-3 py-1.5 bg-primary/10 text-primary hover:bg-primary/20 rounded transition-colors flex items-center gap-1.5">
<i data-lucide="database" class="w-3.5 h-3.5"></i>
${t('index.initCurrent') || 'Init Current Project'}
</button>
<button onclick="cleanAllIndexesConfirm()"
class="text-xs px-3 py-1.5 bg-destructive/10 text-destructive hover:bg-destructive/20 rounded transition-colors flex items-center gap-1.5">
<i data-lucide="trash" class="w-3.5 h-3.5"></i>
${t('index.cleanAll') || 'Clean All'}
</button>
</div>
</div>
</div>
`;
// Reinitialize Lucide icons
if (typeof lucide !== 'undefined') {
@@ -185,24 +205,24 @@ function renderIndexCardError(errorMessage) {
const container = document.getElementById('indexCard');
if (!container) return;
container.innerHTML = '\
<div class="bg-card border border-border rounded-lg overflow-hidden">\
<div class="bg-muted/30 border-b border-border px-4 py-3 flex items-center gap-2">\
<i data-lucide="database" class="w-4 h-4 text-primary"></i>\
<span class="font-medium text-foreground">' + (t('index.manager') || 'Index Manager') + '</span>\
</div>\
<div class="p-4 text-center">\
<div class="text-destructive mb-2">\
<i data-lucide="alert-circle" class="w-8 h-8 mx-auto"></i>\
</div>\
<p class="text-sm text-muted-foreground mb-3">' + escapeHtml(errorMessage) + '</p>\
<button onclick="loadIndexStats()" \
class="text-xs px-3 py-1.5 bg-primary text-primary-foreground hover:bg-primary/90 rounded transition-colors">\
' + (t('common.retry') || 'Retry') + '\
</button>\
</div>\
</div>\
';
container.innerHTML = `
<div class="bg-card border border-border rounded-lg overflow-hidden">
<div class="bg-muted/30 border-b border-border px-4 py-3 flex items-center gap-2">
<i data-lucide="database" class="w-4 h-4 text-primary"></i>
<span class="font-medium text-foreground">${t('index.manager') || 'Index Manager'}</span>
</div>
<div class="p-4 text-center">
<div class="text-destructive mb-2">
<i data-lucide="alert-circle" class="w-8 h-8 mx-auto"></i>
</div>
<p class="text-sm text-muted-foreground mb-3">${escapeHtml(errorMessage)}</p>
<button onclick="loadIndexStats()"
class="text-xs px-3 py-1.5 bg-primary text-primary-foreground hover:bg-primary/90 rounded transition-colors">
${t('common.retry') || 'Retry'}
</button>
</div>
</div>
`;
// Reinitialize Lucide icons
if (typeof lucide !== 'undefined') {

View File

@@ -45,10 +45,26 @@ function showModal(title, content, options = {}) {
}
function closeModal() {
const overlay = document.querySelector('.generic-modal-overlay');
if (overlay) {
overlay.classList.remove('active');
setTimeout(() => overlay.remove(), 200);
// Try generic modal overlay first
const genericOverlay = document.querySelector('.generic-modal-overlay');
if (genericOverlay) {
genericOverlay.classList.remove('active');
setTimeout(() => genericOverlay.remove(), 200);
return;
}
// Try CodexLens config modal
const codexLensModal = document.getElementById('codexlensConfigModal');
if (codexLensModal) {
codexLensModal.remove();
return;
}
// Try any modal-backdrop
const modalBackdrop = document.querySelector('.modal-backdrop');
if (modalBackdrop) {
modalBackdrop.remove();
return;
}
}