mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-11 02:33:51 +08:00
feat: 优化文件管理器的加载体验,异步加载新鲜度数据并添加加载状态指示
This commit is contained in:
@@ -90,7 +90,9 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 400px;
|
grid-template-columns: 1fr 400px;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
align-items: start;
|
align-items: stretch;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
|
|||||||
@@ -94,6 +94,8 @@
|
|||||||
.claude-manager-column.center {
|
.claude-manager-column.center {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.claude-manager-column.right {
|
.claude-manager-column.right {
|
||||||
@@ -286,11 +288,15 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
min-height: 0;
|
||||||
|
background: hsl(var(--muted) / 0.3);
|
||||||
|
border-radius: 0 0 0.5rem 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-content {
|
.markdown-content {
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: hsl(var(--foreground));
|
color: hsl(var(--foreground));
|
||||||
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-content h1 {
|
.markdown-content h1 {
|
||||||
@@ -507,10 +513,14 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 100%;
|
flex: 1;
|
||||||
|
min-height: 200px;
|
||||||
color: hsl(var(--muted-foreground));
|
color: hsl(var(--muted-foreground));
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
|
background: hsl(var(--muted) / 0.3);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
margin: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state i {
|
.empty-state i {
|
||||||
@@ -763,6 +773,18 @@
|
|||||||
color: hsl(0, 72%, 51%);
|
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 states */
|
||||||
.file-tree-item.freshness-stale {
|
.file-tree-item.freshness-stale {
|
||||||
border-left: 2px solid hsl(0, 72%, 51%);
|
border-left: 2px solid hsl(0, 72%, 51%);
|
||||||
@@ -846,6 +868,21 @@
|
|||||||
color: hsl(38, 92%, 50%);
|
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
|
* Responsive Design
|
||||||
* ======================================== */
|
* ======================================== */
|
||||||
|
|||||||
@@ -37,11 +37,10 @@ async function renderClaudeManager() {
|
|||||||
'<p>' + t('common.loading') + '</p>' +
|
'<p>' + t('common.loading') + '</p>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
|
|
||||||
// Load data
|
// Load file data first (fast operation)
|
||||||
await loadClaudeFiles();
|
await loadClaudeFiles();
|
||||||
await loadFreshnessData();
|
|
||||||
|
|
||||||
// Render layout
|
// Render layout immediately without waiting for freshness data
|
||||||
container.innerHTML = '<div class="claude-manager-view">' +
|
container.innerHTML = '<div class="claude-manager-view">' +
|
||||||
'<div class="claude-manager-header">' +
|
'<div class="claude-manager-header">' +
|
||||||
'<div class="claude-manager-header-left">' +
|
'<div class="claude-manager-header-left">' +
|
||||||
@@ -64,13 +63,34 @@ async function renderClaudeManager() {
|
|||||||
'</div>' +
|
'</div>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
|
|
||||||
// Render each column
|
// Render each column immediately (without freshness data)
|
||||||
renderFileTree();
|
renderFileTree();
|
||||||
renderFileViewer();
|
renderFileViewer();
|
||||||
renderFileMetadata();
|
renderFileMetadata();
|
||||||
|
|
||||||
// Initialize Lucide icons
|
// Initialize Lucide icons
|
||||||
if (window.lucide) lucide.createIcons();
|
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 ==========
|
// ========== Data Loading ==========
|
||||||
@@ -88,9 +108,14 @@ async function loadClaudeFiles() {
|
|||||||
|
|
||||||
async function refreshClaudeFiles() {
|
async function refreshClaudeFiles() {
|
||||||
await loadClaudeFiles();
|
await loadClaudeFiles();
|
||||||
await loadFreshnessData();
|
// Re-render file tree immediately
|
||||||
await renderClaudeManager();
|
renderFileTree();
|
||||||
|
renderFileViewer();
|
||||||
|
renderFileMetadata();
|
||||||
|
if (window.lucide) lucide.createIcons();
|
||||||
addGlobalNotification('success', t('claudeManager.refreshed'), null, 'CLAUDE.md');
|
addGlobalNotification('success', t('claudeManager.refreshed'), null, 'CLAUDE.md');
|
||||||
|
// Load freshness data in background
|
||||||
|
loadFreshnessDataAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== Freshness Data Loading ==========
|
// ========== Freshness Data Loading ==========
|
||||||
@@ -241,6 +266,9 @@ function renderFileTreeItem(file, indentLevel) {
|
|||||||
var freshnessClass = '';
|
var freshnessClass = '';
|
||||||
var freshnessBadge = '';
|
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) {
|
||||||
if (fd.freshness >= 75) {
|
if (fd.freshness >= 75) {
|
||||||
freshnessClass = ' freshness-good';
|
freshnessClass = ' freshness-good';
|
||||||
@@ -252,6 +280,9 @@ function renderFileTreeItem(file, indentLevel) {
|
|||||||
freshnessClass = ' freshness-stale';
|
freshnessClass = ' freshness-stale';
|
||||||
freshnessBadge = '<span class="freshness-badge stale">' + fd.freshness + '%</span>';
|
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' : '') + '" ' +
|
return '<div class="file-tree-item' + freshnessClass + (isSelected ? ' selected' : '') + '" ' +
|
||||||
@@ -520,6 +551,8 @@ function renderFileMetadata() {
|
|||||||
|
|
||||||
// Freshness section
|
// Freshness section
|
||||||
var fd = freshnessData[selectedFile.path];
|
var fd = freshnessData[selectedFile.path];
|
||||||
|
var freshnessLoaded = freshnessSummary !== null || Object.keys(freshnessData).length > 0;
|
||||||
|
|
||||||
if (fd) {
|
if (fd) {
|
||||||
var freshnessBarClass = fd.freshness >= 75 ? 'good' : fd.freshness >= 50 ? 'warn' : 'stale';
|
var freshnessBarClass = fd.freshness >= 75 ? 'good' : fd.freshness >= 50 ? 'warn' : 'stale';
|
||||||
html += '<div class="metadata-section freshness-section">' +
|
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') +
|
'<i data-lucide="check-circle" class="w-4 h-4"></i> ' + (t('claudeManager.markAsUpdated') || 'Mark as Updated') +
|
||||||
'</button>' +
|
'</button>' +
|
||||||
'</div>';
|
'</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">' +
|
html += '<div class="metadata-section">' +
|
||||||
|
|||||||
Reference in New Issue
Block a user