feat: 优化文件管理器的加载体验,异步加载新鲜度数据并添加加载状态指示

This commit is contained in:
catlog22
2025-12-20 23:46:11 +08:00
parent 775928456d
commit f1ee46e1ac
3 changed files with 89 additions and 8 deletions

View File

@@ -90,7 +90,9 @@
display: grid;
grid-template-columns: 1fr 400px;
gap: 1.5rem;
align-items: start;
align-items: stretch;
flex: 1;
min-height: 0;
}
@media (max-width: 1200px) {

View File

@@ -94,6 +94,8 @@
.claude-manager-column.center {
overflow: hidden;
height: 100%;
display: flex;
flex-direction: column;
}
.claude-manager-column.right {
@@ -286,11 +288,15 @@
flex: 1;
overflow-y: auto;
padding: 1rem;
min-height: 0;
background: hsl(var(--muted) / 0.3);
border-radius: 0 0 0.5rem 0.5rem;
}
.markdown-content {
line-height: 1.6;
color: hsl(var(--foreground));
min-height: 100%;
}
.markdown-content h1 {
@@ -507,10 +513,14 @@
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
flex: 1;
min-height: 200px;
color: hsl(var(--muted-foreground));
text-align: center;
padding: 2rem;
background: hsl(var(--muted) / 0.3);
border-radius: 0.5rem;
margin: 0.5rem;
}
.empty-state i {
@@ -763,6 +773,18 @@
color: hsl(0, 72%, 51%);
}
.freshness-badge.loading {
background: hsl(var(--muted));
color: hsl(var(--muted-foreground));
min-width: 28px;
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.5; }
50% { opacity: 1; }
}
/* File tree item freshness states */
.file-tree-item.freshness-stale {
border-left: 2px solid hsl(0, 72%, 51%);
@@ -846,6 +868,21 @@
color: hsl(38, 92%, 50%);
}
/* Freshness loading state */
.freshness-loading {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 1.5rem;
color: hsl(var(--muted-foreground));
font-size: 0.875rem;
}
.freshness-loading i {
animation: spin 1s linear infinite;
}
/* ========================================
* Responsive Design
* ======================================== */

View File

@@ -37,11 +37,10 @@ async function renderClaudeManager() {
'<p>' + t('common.loading') + '</p>' +
'</div>';
// Load data
// Load file data first (fast operation)
await loadClaudeFiles();
await loadFreshnessData();
// Render layout
// Render layout immediately without waiting for freshness data
container.innerHTML = '<div class="claude-manager-view">' +
'<div class="claude-manager-header">' +
'<div class="claude-manager-header-left">' +
@@ -64,13 +63,34 @@ async function renderClaudeManager() {
'</div>' +
'</div>';
// Render each column
// Render each column immediately (without freshness data)
renderFileTree();
renderFileViewer();
renderFileMetadata();
// Initialize Lucide icons
if (window.lucide) lucide.createIcons();
// Load freshness data asynchronously in the background (non-blocking)
loadFreshnessDataAsync();
}
// Async freshness loader - loads in background and updates UI when ready
function loadFreshnessDataAsync() {
// Use setTimeout to ensure UI is rendered first
setTimeout(async function() {
try {
await loadFreshnessData();
// Re-render file tree and metadata with freshness data
renderFileTree();
if (selectedFile) {
renderFileMetadata();
}
if (window.lucide) lucide.createIcons();
} catch (error) {
console.error('Error loading freshness data in background:', error);
}
}, 100);
}
// ========== Data Loading ==========
@@ -88,9 +108,14 @@ async function loadClaudeFiles() {
async function refreshClaudeFiles() {
await loadClaudeFiles();
await loadFreshnessData();
await renderClaudeManager();
// Re-render file tree immediately
renderFileTree();
renderFileViewer();
renderFileMetadata();
if (window.lucide) lucide.createIcons();
addGlobalNotification('success', t('claudeManager.refreshed'), null, 'CLAUDE.md');
// Load freshness data in background
loadFreshnessDataAsync();
}
// ========== Freshness Data Loading ==========
@@ -241,6 +266,9 @@ function renderFileTreeItem(file, indentLevel) {
var freshnessClass = '';
var freshnessBadge = '';
// Check if freshness data is loaded (freshnessSummary is set after load)
var freshnessLoaded = freshnessSummary !== null || Object.keys(freshnessData).length > 0;
if (fd) {
if (fd.freshness >= 75) {
freshnessClass = ' freshness-good';
@@ -252,6 +280,9 @@ function renderFileTreeItem(file, indentLevel) {
freshnessClass = ' freshness-stale';
freshnessBadge = '<span class="freshness-badge stale">' + fd.freshness + '%</span>';
}
} else if (!freshnessLoaded) {
// Show loading badge while freshness data is being fetched
freshnessBadge = '<span class="freshness-badge loading">...</span>';
}
return '<div class="file-tree-item' + freshnessClass + (isSelected ? ' selected' : '') + '" ' +
@@ -520,6 +551,8 @@ function renderFileMetadata() {
// Freshness section
var fd = freshnessData[selectedFile.path];
var freshnessLoaded = freshnessSummary !== null || Object.keys(freshnessData).length > 0;
if (fd) {
var freshnessBarClass = fd.freshness >= 75 ? 'good' : fd.freshness >= 50 ? 'warn' : 'stale';
html += '<div class="metadata-section freshness-section">' +
@@ -548,6 +581,15 @@ function renderFileMetadata() {
'<i data-lucide="check-circle" class="w-4 h-4"></i> ' + (t('claudeManager.markAsUpdated') || 'Mark as Updated') +
'</button>' +
'</div>';
} else if (!freshnessLoaded) {
// Show loading state while freshness data is being fetched
html += '<div class="metadata-section freshness-section">' +
'<h4><i data-lucide="activity" class="w-4 h-4"></i> ' + (t('claudeManager.freshness') || 'Freshness') + '</h4>' +
'<div class="freshness-loading">' +
'<i data-lucide="loader-2" class="w-5 h-5 animate-spin"></i>' +
'<span>' + (t('claudeManager.loadingFreshness') || 'Loading freshness data...') + '</span>' +
'</div>' +
'</div>';
}
html += '<div class="metadata-section">' +