feat: 优化工作区索引状态刷新,增强头部徽章更新逻辑

This commit is contained in:
catlog22
2026-01-07 22:28:36 +08:00
parent fae2f7e279
commit 6e93c36b89

View File

@@ -24,22 +24,46 @@ function escapeHtml(str) {
/** /**
* Refresh workspace index status (FTS and Vector coverage) * Refresh workspace index status (FTS and Vector coverage)
* Updates both the detailed panel (if exists) and header badges
*/ */
async function refreshWorkspaceIndexStatus() { async function refreshWorkspaceIndexStatus() {
var container = document.getElementById('workspaceIndexStatusContent'); var container = document.getElementById('workspaceIndexStatusContent');
if (!container) return; var headerFtsEl = document.getElementById('headerFtsPercent');
var headerVectorEl = document.getElementById('headerVectorPercent');
// Show loading state // If neither container nor header elements exist, nothing to update
if (!container && !headerFtsEl) return;
// Show loading state in container
if (container) {
container.innerHTML = '<div class="text-xs text-muted-foreground text-center py-2">' + container.innerHTML = '<div class="text-xs text-muted-foreground text-center py-2">' +
'<i data-lucide="loader-2" class="w-4 h-4 animate-spin inline mr-1"></i> ' + (t('common.loading') || 'Loading...') + '<i data-lucide="loader-2" class="w-4 h-4 animate-spin inline mr-1"></i> ' + (t('common.loading') || 'Loading...') +
'</div>'; '</div>';
if (window.lucide) lucide.createIcons(); if (window.lucide) lucide.createIcons();
}
try { try {
var response = await fetch('/api/codexlens/workspace-status'); var response = await fetch('/api/codexlens/workspace-status');
var result = await response.json(); var result = await response.json();
if (result.success) { if (result.success) {
var ftsPercent = result.hasIndex ? (result.fts.percent || 0) : 0;
var vectorPercent = result.hasIndex ? (result.vector.percent || 0) : 0;
// Update header badges (always update if elements exist)
if (headerFtsEl) {
headerFtsEl.textContent = ftsPercent + '%';
headerFtsEl.className = 'text-sm font-medium ' +
(ftsPercent >= 100 ? 'text-success' : (ftsPercent > 0 ? 'text-blue-500' : 'text-muted-foreground'));
}
if (headerVectorEl) {
headerVectorEl.textContent = vectorPercent.toFixed(1) + '%';
headerVectorEl.className = 'text-sm font-medium ' +
(vectorPercent >= 100 ? 'text-success' : (vectorPercent >= 50 ? 'text-purple-500' : (vectorPercent > 0 ? 'text-purple-400' : 'text-muted-foreground')));
}
// Update detailed container (if exists)
if (container) {
var html = ''; var html = '';
if (!result.hasIndex) { if (!result.hasIndex) {
@@ -55,7 +79,6 @@ async function refreshWorkspaceIndexStatus() {
'</div>'; '</div>';
} else { } else {
// FTS Status // FTS Status
var ftsPercent = result.fts.percent || 0;
var ftsColor = ftsPercent >= 100 ? 'bg-success' : (ftsPercent > 0 ? 'bg-blue-500' : 'bg-muted-foreground'); var ftsColor = ftsPercent >= 100 ? 'bg-success' : (ftsPercent > 0 ? 'bg-blue-500' : 'bg-muted-foreground');
var ftsTextColor = ftsPercent >= 100 ? 'text-success' : (ftsPercent > 0 ? 'text-blue-500' : 'text-muted-foreground'); var ftsTextColor = ftsPercent >= 100 ? 'text-success' : (ftsPercent > 0 ? 'text-blue-500' : 'text-muted-foreground');
@@ -76,7 +99,6 @@ async function refreshWorkspaceIndexStatus() {
'</div>'; '</div>';
// Vector Status // Vector Status
var vectorPercent = result.vector.percent || 0;
var vectorColor = vectorPercent >= 100 ? 'bg-success' : (vectorPercent >= 50 ? 'bg-purple-500' : (vectorPercent > 0 ? 'bg-purple-400' : 'bg-muted-foreground')); var vectorColor = vectorPercent >= 100 ? 'bg-success' : (vectorPercent >= 50 ? 'bg-purple-500' : (vectorPercent > 0 ? 'bg-purple-400' : 'bg-muted-foreground'));
var vectorTextColor = vectorPercent >= 100 ? 'text-success' : (vectorPercent >= 50 ? 'text-purple-500' : (vectorPercent > 0 ? 'text-purple-400' : 'text-muted-foreground')); var vectorTextColor = vectorPercent >= 100 ? 'text-success' : (vectorPercent >= 50 ? 'text-purple-500' : (vectorPercent > 0 ? 'text-purple-400' : 'text-muted-foreground'));
@@ -112,19 +134,29 @@ async function refreshWorkspaceIndexStatus() {
} }
container.innerHTML = html; container.innerHTML = html;
}
} else { } else {
// Error from API
if (headerFtsEl) headerFtsEl.textContent = '--';
if (headerVectorEl) headerVectorEl.textContent = '--';
if (container) {
container.innerHTML = '<div class="text-xs text-destructive text-center py-2">' + container.innerHTML = '<div class="text-xs text-destructive text-center py-2">' +
'<i data-lucide="alert-circle" class="w-4 h-4 inline mr-1"></i> ' + '<i data-lucide="alert-circle" class="w-4 h-4 inline mr-1"></i> ' +
(result.error || t('common.error') || 'Error loading status') + (result.error || t('common.error') || 'Error loading status') +
'</div>'; '</div>';
} }
}
} catch (err) { } catch (err) {
console.error('[CodexLens] Failed to load workspace status:', err); console.error('[CodexLens] Failed to load workspace status:', err);
if (headerFtsEl) headerFtsEl.textContent = '--';
if (headerVectorEl) headerVectorEl.textContent = '--';
if (container) {
container.innerHTML = '<div class="text-xs text-destructive text-center py-2">' + container.innerHTML = '<div class="text-xs text-destructive text-center py-2">' +
'<i data-lucide="alert-circle" class="w-4 h-4 inline mr-1"></i> ' + '<i data-lucide="alert-circle" class="w-4 h-4 inline mr-1"></i> ' +
(t('common.error') || 'Error') + ': ' + err.message + (t('common.error') || 'Error') + ': ' + err.message +
'</div>'; '</div>';
} }
}
if (window.lucide) lucide.createIcons(); if (window.lucide) lucide.createIcons();
} }
@@ -3884,32 +3916,45 @@ async function renderCodexLensManager() {
// Load additional data in parallel (non-blocking) // Load additional data in parallel (non-blocking)
var isInstalled = window.cliToolsStatus?.codexlens?.installed || dashboardData?.installed; var isInstalled = window.cliToolsStatus?.codexlens?.installed || dashboardData?.installed;
// OPTIMIZATION: Load critical status first (workspace index status for header badges)
// This is prioritized as it updates the visible header immediately
if (isInstalled) {
refreshWorkspaceIndexStatus(); // Updates header FTS/Vector badges
}
// Wait for LiteLLM config before loading semantic deps (it may need provider info) // Wait for LiteLLM config before loading semantic deps (it may need provider info)
await litellmPromise; await litellmPromise;
// Load FastEmbed installation status (show/hide install card) // OPTIMIZATION: Batch non-critical loads with requestIdleCallback or setTimeout
loadFastEmbedInstallStatus(); // This prevents blocking the main thread and allows smoother UI updates
var deferredLoads = function() {
// Always load semantic deps status - it needs GPU detection and device list // Load all independent status checks in parallel
// which are not included in the aggregated endpoint Promise.all([
loadSemanticDepsStatus(); // FastEmbed and semantic deps status (independent)
Promise.resolve().then(function() { loadFastEmbedInstallStatus(); }),
loadModelList(); Promise.resolve().then(function() { loadSemanticDepsStatus(); }),
loadRerankerModelList(); // Model lists (independent)
Promise.resolve().then(function() { loadModelList(); }),
// Initialize model mode and semantic status badge Promise.resolve().then(function() { loadRerankerModelList(); }),
// File watcher status (independent)
Promise.resolve().then(function() { initWatcherStatus(); })
]).then(function() {
// After all loads complete, update the semantic status badge
updateSemanticStatusBadge(); updateSemanticStatusBadge();
});
// Initialize file watcher status // Load index stats if installed (independent from above)
initWatcherStatus();
// Load index stats for the Index Manager section
if (isInstalled) { if (isInstalled) {
loadIndexStatsForPage(); loadIndexStatsForPage();
// Check index health based on git history
checkIndexHealth(); checkIndexHealth();
// Load workspace index status (FTS and Vector coverage) }
refreshWorkspaceIndexStatus(); };
// Use requestIdleCallback for deferred loads if available, otherwise use setTimeout
if (typeof requestIdleCallback === 'function') {
requestIdleCallback(deferredLoads, { timeout: 500 });
} else {
setTimeout(deferredLoads, 50);
} }
} catch (err) { } catch (err) {
container.innerHTML = '<div class="text-center py-12 text-destructive"><i data-lucide="alert-circle" class="w-8 h-8 mx-auto mb-2"></i><p>' + t('common.error') + ': ' + escapeHtml(err.message) + '</p></div>'; container.innerHTML = '<div class="text-center py-12 text-destructive"><i data-lucide="alert-circle" class="w-8 h-8 mx-auto mb-2"></i><p>' + t('common.error') + ': ' + escapeHtml(err.message) + '</p></div>';
@@ -3946,6 +3991,21 @@ function buildCodexLensManagerPage(config) {
'<span class="text-sm text-muted-foreground">' + t('codexlens.indexes') + ':</span>' + '<span class="text-sm text-muted-foreground">' + t('codexlens.indexes') + ':</span>' +
'<span class="text-lg font-bold text-primary">' + indexCount + '</span>' + '<span class="text-lg font-bold text-primary">' + indexCount + '</span>' +
'</div>' + '</div>' +
// Workspace Index Status badges (FTS/Vector percentages)
(isInstalled
? '<div id="headerIndexStatusContainer" class="flex items-center gap-2">' +
'<div class="flex items-center gap-1.5 px-2.5 py-1 rounded-lg bg-blue-500/10 border border-blue-500/20" title="' + (t('codexlens.ftsIndex') || 'FTS Index') + '">' +
'<i data-lucide="file-text" class="w-3.5 h-3.5 text-blue-500"></i>' +
'<span class="text-xs text-muted-foreground">FTS:</span>' +
'<span id="headerFtsPercent" class="text-sm font-medium text-blue-500">--</span>' +
'</div>' +
'<div class="flex items-center gap-1.5 px-2.5 py-1 rounded-lg bg-purple-500/10 border border-purple-500/20" title="' + (t('codexlens.vectorIndex') || 'Vector Index') + '">' +
'<i data-lucide="brain" class="w-3.5 h-3.5 text-purple-500"></i>' +
'<span class="text-xs text-muted-foreground">Vector:</span>' +
'<span id="headerVectorPercent" class="text-sm font-medium text-purple-500">--</span>' +
'</div>' +
'</div>'
: '') +
'</div>' + '</div>' +
'</div>' + '</div>' +
'</div>' + '</div>' +