diff --git a/internal/assets/static/css/style.css b/internal/assets/static/css/style.css index de3d981..6166ea2 100644 --- a/internal/assets/static/css/style.css +++ b/internal/assets/static/css/style.css @@ -1941,6 +1941,26 @@ a { background: var(--color-surface); } +button.cache-files-more, +.analysis-files-toggle { + width: 100%; + border: none; + cursor: pointer; + font-family: inherit; +} + +.analysis-files-toggle { + justify-content: center; + color: var(--color-text-muted); + background: var(--color-bg-tertiary); +} + +button.cache-files-more:hover, +.analysis-files-toggle:hover { + color: var(--color-text); + background: var(--color-bg-secondary); +} + .cache-detail-actions { display: flex; gap: 12px; diff --git a/internal/assets/static/js/app.js b/internal/assets/static/js/app.js index e41fdd1..dfbdec0 100644 --- a/internal/assets/static/js/app.js +++ b/internal/assets/static/js/app.js @@ -206,6 +206,39 @@ // Store current analysis for wizard let currentAnalysis = null; let hasShownRevisionPicker = false; // Track if we've shown picker for this repo + let analysisFilesExpanded = false; // Toggle for the >20-file truncation on the Analyze card + + function renderFileListRows(files, expanded) { + const visible = expanded ? files : files.slice(0, 20); + const rowsHtml = visible.map(f => ` +
+ ${escapeHtml(f.path || f.name)} + ${f.size_human || formatBytes(f.size)} +
+ `).join(''); + + const remaining = files.length - 20; + const moreFiles = remaining > 0 + ? `` + : ''; + + return rowsHtml + moreFiles; + } + + // Toggle the full file list on the Analyze card and re-render in place. + // Selection filters (quant checkboxes) still apply; updateCLICommandFromSelections + // is the single source of truth for what's currently visible. + window.toggleAnalysisFilesExpanded = function() { + analysisFilesExpanded = !analysisFilesExpanded; + if (typeof updateCLICommandFromSelections === 'function' && currentAnalysis?.selectable_items?.length) { + updateCLICommandFromSelections(); + } else if (currentAnalysis?.files) { + const container = $('#analysisFilesList'); + if (container) container.innerHTML = renderFileListRows(currentAnalysis.files, analysisFilesExpanded); + } + }; async function analyzeRepo(forceType = null, revision = null) { const input = $('#analyzeInput'); @@ -380,18 +413,9 @@ const resultDiv = $('#analyzeResult'); if (!resultDiv) return; - const filesHtml = data.files?.slice(0, 20).map(f => ` -
- ${escapeHtml(f.path || f.name)} - ${f.size_human || formatBytes(f.size)} -
- `).join('') || ''; - - const moreFiles = (data.files?.length || 0) > 20 - ? `
- ... and ${data.files.length - 20} more files -
` - : ''; + analysisFilesExpanded = false; // Fresh analysis always starts collapsed + const filesHtml = renderFileListRows(data.files || [], analysisFilesExpanded); + const moreFiles = ''; // folded into renderFileListRows above // Build type-specific info let typeInfoHtml = ''; @@ -1388,10 +1412,44 @@ `; } + // Cache-detail modal file list state (module-level so the toggle handler can reach it) + let cacheDetailFiles = null; + let cacheDetailFilesExpanded = false; + + function renderCacheFileRows(files, expanded) { + const visible = expanded ? files : files.slice(0, 20); + const rowsHtml = visible.map(f => ` +
+ + ${f.isLfs ? 'LFS' : ''} + ${escapeHtml(f.name)} + + ${escapeHtml(f.sizeHuman)} +
+ `).join(''); + + const remaining = files.length - 20; + const moreFiles = remaining > 0 + ? `` + : ''; + + return rowsHtml + moreFiles; + } + + window.toggleCacheDetailFilesExpanded = function() { + cacheDetailFilesExpanded = !cacheDetailFilesExpanded; + const list = $('.cache-files-list'); + if (list && cacheDetailFiles) list.innerHTML = renderCacheFileRows(cacheDetailFiles, cacheDetailFilesExpanded); + }; + window.showCacheDetails = async function(repo, type) { try { showModal('Repository Details', '
'); const data = await api('GET', `/cache/${repo}`); + cacheDetailFiles = data.files || null; + cacheDetailFilesExpanded = false; const typeIcon = data.type === 'model' ? ` @@ -1407,16 +1465,7 @@ ? `

Files (${data.files.length})

- ${data.files.slice(0, 20).map(f => ` -
- - ${f.isLfs ? 'LFS' : ''} - ${escapeHtml(f.name)} - - ${escapeHtml(f.sizeHuman)} -
- `).join('')} - ${data.files.length > 20 ? `
... and ${data.files.length - 20} more files
` : ''} + ${renderCacheFileRows(data.files, cacheDetailFilesExpanded)}
` : ''; @@ -2636,21 +2685,9 @@ selectedSize = currentAnalysis.files.reduce((sum, f) => sum + (f.size || 0), 0); } - // Render the filtered file list - const filesHtml = filteredFiles.slice(0, 20).map(f => ` -
- ${escapeHtml(f.path || f.name)} - ${f.size_human || formatBytes(f.size)} -
- `).join(''); - - const moreFiles = filteredFiles.length > 20 - ? `
- ... and ${filteredFiles.length - 20} more files -
` - : ''; - - filesContainer.innerHTML = filesHtml + moreFiles; + // Render the filtered file list (respects the same expand/collapse toggle as the + // unfiltered view, so switching quant selections doesn't silently re-collapse it) + filesContainer.innerHTML = renderFileListRows(filteredFiles, analysisFilesExpanded); // Update the count display if (countEl) { @@ -2682,7 +2719,7 @@ function formatBytes(bytes) { if (!bytes || bytes === 0) return '0 B'; const k = 1024; - const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + const sizes = ['B', 'KiB', 'MiB', 'GiB', 'TiB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; }