// ========================================== // INDEX MANAGER COMPONENT // ========================================== // Manages CodexLens code indexes (vector and normal) // State let indexData = null; let indexLoading = false; /** * Initialize index manager */ async function initIndexManager() { await loadIndexStats(); } /** * Load index statistics from API */ async function loadIndexStats() { if (indexLoading) return; indexLoading = true; try { const res = await fetch('/api/codexlens/indexes'); if (!res.ok) throw new Error('Failed to load index stats'); indexData = await res.json(); renderIndexCard(); } catch (err) { console.error('Failed to load index stats:', err); renderIndexCardError(err.message); } finally { indexLoading = false; } } /** * Render index card in the dashboard */ function renderIndexCard() { const container = document.getElementById('indexCard'); if (!container) { console.warn('[IndexManager] Container not found'); return; } // Handle case when data is not loaded yet if (!indexData) { container.innerHTML = `
${t('index.manager') || 'Index Manager'}
${t('common.loading') || 'Loading...'}
`; if (typeof lucide !== 'undefined') lucide.createIcons(); return; } const { indexDir, indexes, summary } = indexData; // Format relative time const formatTimeAgo = (isoString) => { if (!isoString) return t('common.never') || 'Never'; const date = new Date(isoString); const now = new Date(); const diffMs = now - date; const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMins / 60); const diffDays = Math.floor(diffHours / 24); if (diffMins < 1) return t('common.justNow') || 'Just now'; if (diffMins < 60) return diffMins + 'm ' + (t('common.ago') || 'ago'); if (diffHours < 24) return diffHours + 'h ' + (t('common.ago') || 'ago'); if (diffDays < 30) return diffDays + 'd ' + (t('common.ago') || 'ago'); return date.toLocaleDateString(); }; // Build index rows let indexRows = ''; if (indexes && indexes.length > 0) { indexes.forEach(function(idx) { const vectorBadge = idx.hasVectorIndex ? `${t('index.vector') || 'Vector'}` : ''; const normalBadge = idx.hasNormalIndex ? `${t('index.fts') || 'FTS'}` : ''; indexRows += `
${escapeHtml(idx.id)}
${idx.sizeFormatted}
${vectorBadge}${normalBadge}
${formatTimeAgo(idx.lastModified)} `; }); } else { indexRows = ` ${t('index.noIndexes') || 'No indexes yet'} `; } container.innerHTML = `
${t('index.manager') || 'Index Manager'} ${summary?.totalSizeFormatted || '0 B'}
${escapeHtml(indexDir || t('index.notConfigured') || 'Not configured')}
${summary?.totalProjects || 0}
${t('index.projects') || 'Projects'}
${summary?.totalSizeFormatted || '0 B'}
${t('index.totalSize') || 'Total Size'}
${summary?.vectorIndexCount || 0}
${t('index.vectorIndexes') || 'Vector'}
${summary?.normalIndexCount || 0}
${t('index.ftsIndexes') || 'FTS'}
${indexRows}
${t('index.projectId') || 'Project ID'} ${t('index.size') || 'Size'} ${t('index.type') || 'Type'} ${t('index.lastModified') || 'Modified'}
`; // Reinitialize Lucide icons if (typeof lucide !== 'undefined') { lucide.createIcons(); } } /** * Render error state for index card */ function renderIndexCardError(errorMessage) { const container = document.getElementById('indexCard'); if (!container) return; container.innerHTML = `
${t('index.manager') || 'Index Manager'}

${escapeHtml(errorMessage)}

`; // Reinitialize Lucide icons if (typeof lucide !== 'undefined') { lucide.createIcons(); } } /** * Clean a specific project's index */ async function cleanIndexProject(projectId) { if (!confirm((t('index.cleanProjectConfirm') || 'Clean index for') + ' ' + projectId + '?')) { return; } try { showRefreshToast(t('index.cleaning') || 'Cleaning index...', 'info'); // The project ID is the directory name in the index folder // We need to construct the full path or use a clean API const response = await fetch('/api/codexlens/clean', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ projectId: projectId }) }); const result = await response.json(); if (result.success) { showRefreshToast(t('index.cleanSuccess') || 'Index cleaned successfully', 'success'); await loadIndexStats(); } else { showRefreshToast((t('index.cleanFailed') || 'Clean failed') + ': ' + result.error, 'error'); } } catch (err) { showRefreshToast((t('common.error') || 'Error') + ': ' + err.message, 'error'); } } /** * Confirm and clean all indexes */ async function cleanAllIndexesConfirm() { if (!confirm(t('index.cleanAllConfirm') || 'Are you sure you want to clean ALL indexes? This cannot be undone.')) { return; } try { showRefreshToast(t('index.cleaning') || 'Cleaning indexes...', 'info'); const response = await fetch('/api/codexlens/clean', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ all: true }) }); const result = await response.json(); if (result.success) { showRefreshToast(t('index.cleanAllSuccess') || 'All indexes cleaned', 'success'); await loadIndexStats(); } else { showRefreshToast((t('index.cleanFailed') || 'Clean failed') + ': ' + result.error, 'error'); } } catch (err) { showRefreshToast((t('common.error') || 'Error') + ': ' + err.message, 'error'); } }