diff --git a/static/css/components/loading.css b/static/css/components/loading.css index 34c28765..e118c4a9 100644 --- a/static/css/components/loading.css +++ b/static/css/components/loading.css @@ -56,6 +56,53 @@ transition: width 200ms ease-out; } +/* Enhanced progress display */ +.progress-details-container { + margin-top: var(--space-3); + width: 100%; + text-align: left; +} + +.overall-progress-label { + font-size: 0.9rem; + margin-bottom: var(--space-1); + color: var(--text-color); +} + +.current-item-progress { + margin-top: var(--space-2); +} + +.current-item-label { + font-size: 0.9rem; + margin-bottom: var(--space-1); + color: var(--text-color); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.current-item-bar-container { + height: 8px; + background-color: var(--lora-border); + border-radius: 4px; + overflow: hidden; + margin-bottom: var(--space-1); +} + +.current-item-bar { + height: 100%; + background-color: var(--lora-accent); + transition: width 200ms ease-out; + width: 0%; +} + +.current-item-percent { + font-size: 0.8rem; + color: var(--text-color-secondary, var(--text-color)); + opacity: 0.7; +} + @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } @@ -63,7 +110,8 @@ @media (prefers-reduced-motion: reduce) { .lora-card, - .progress-bar { + .progress-bar, + .current-item-bar { transition: none; } } \ No newline at end of file diff --git a/static/js/managers/DownloadManager.js b/static/js/managers/DownloadManager.js index e4b211ac..2e3f5fc8 100644 --- a/static/js/managers/DownloadManager.js +++ b/static/js/managers/DownloadManager.js @@ -270,19 +270,37 @@ export class DownloadManager { throw new Error('No download URL available'); } - // Show loading with progress bar for download - this.loadingManager.show('Downloading LoRA...', 0); + // Show enhanced loading with progress details + const updateProgress = this.loadingManager.showDownloadProgress(1); + updateProgress(0, 0, this.currentVersion.name); // Setup WebSocket for progress updates const wsProtocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'; const ws = new WebSocket(`${wsProtocol}${window.location.host}/ws/fetch-progress`); + ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.status === 'progress') { - this.loadingManager.setProgress(data.progress); - this.loadingManager.setStatus(`Downloading: ${data.progress}%`); + // Update progress display with current progress + updateProgress(data.progress, 0, this.currentVersion.name); + + // Add more detailed status messages based on progress + if (data.progress < 3) { + this.loadingManager.setStatus(`Preparing download...`); + } else if (data.progress === 3) { + this.loadingManager.setStatus(`Downloaded preview image`); + } else if (data.progress > 3 && data.progress < 100) { + this.loadingManager.setStatus(`Downloading LoRA file`); + } else { + this.loadingManager.setStatus(`Finalizing download...`); + } } }; + + ws.onerror = (error) => { + console.error('WebSocket error:', error); + // Continue with download even if WebSocket fails + }; // Start download const response = await fetch('/api/download-lora', { diff --git a/static/js/managers/ImportManager.js b/static/js/managers/ImportManager.js index bee42dbd..bc590183 100644 --- a/static/js/managers/ImportManager.js +++ b/static/js/managers/ImportManager.js @@ -507,27 +507,56 @@ export class ImportManager { const wsProtocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'; const ws = new WebSocket(`${wsProtocol}${window.location.host}/ws/fetch-progress`); - // Download missing LoRAs sequentially - this.loadingManager.show('Downloading LoRAs...', 0); + // Show enhanced loading with progress details for multiple items + const updateProgress = this.loadingManager.showDownloadProgress(this.missingLoras.length); let completedDownloads = 0; + let currentLoraProgress = 0; + + // Set up progress tracking for current download + ws.onmessage = (event) => { + const data = JSON.parse(event.data); + if (data.status === 'progress') { + // Update current LoRA progress + currentLoraProgress = data.progress; + + // Get current LoRA name + const currentLora = this.missingLoras[completedDownloads]; + const loraName = currentLora ? currentLora.name : ''; + + // Update progress display + updateProgress(currentLoraProgress, completedDownloads, loraName); + + // Add more detailed status messages based on progress + if (currentLoraProgress < 3) { + this.loadingManager.setStatus( + `Preparing download for LoRA ${completedDownloads+1}/${this.missingLoras.length}` + ); + } else if (currentLoraProgress === 3) { + this.loadingManager.setStatus( + `Downloaded preview for LoRA ${completedDownloads+1}/${this.missingLoras.length}` + ); + } else if (currentLoraProgress > 3 && currentLoraProgress < 100) { + this.loadingManager.setStatus( + `Downloading LoRA ${completedDownloads+1}/${this.missingLoras.length}` + ); + } else { + this.loadingManager.setStatus( + `Finalizing LoRA ${completedDownloads+1}/${this.missingLoras.length}` + ); + } + } + }; + for (let i = 0; i < this.missingLoras.length; i++) { const lora = this.missingLoras[i]; - // Update overall progress - this.loadingManager.setStatus(`Downloading LoRA ${i+1}/${this.missingLoras.length}: ${lora.name}`); + // Reset current LoRA progress for new download + currentLoraProgress = 0; - // Set up progress tracking for current download - ws.onmessage = (event) => { - const data = JSON.parse(event.data); - if (data.status === 'progress') { - // Calculate overall progress: completed files + current file progress - const overallProgress = Math.floor( - (completedDownloads + data.progress/100) / this.missingLoras.length * 100 - ); - this.loadingManager.setProgress(overallProgress); - } - }; + // Initial status update for new LoRA + this.loadingManager.setStatus(`Starting download for LoRA ${i+1}/${this.missingLoras.length}`); + updateProgress(0, completedDownloads, lora.name); try { // Download the LoRA @@ -547,6 +576,15 @@ export class ImportManager { // Continue with next download } else { completedDownloads++; + + // Update progress to show completion of current LoRA + updateProgress(100, completedDownloads, ''); + + if (completedDownloads < this.missingLoras.length) { + this.loadingManager.setStatus( + `Completed ${completedDownloads}/${this.missingLoras.length} LoRAs. Starting next download...` + ); + } } } catch (downloadError) { console.error(`Error downloading LoRA ${lora.name}:`, downloadError); diff --git a/static/js/managers/LoadingManager.js b/static/js/managers/LoadingManager.js index 9714f4f2..27ea52d5 100644 --- a/static/js/managers/LoadingManager.js +++ b/static/js/managers/LoadingManager.js @@ -4,17 +4,22 @@ export class LoadingManager { this.overlay = document.getElementById('loading-overlay'); this.progressBar = this.overlay.querySelector('.progress-bar'); this.statusText = this.overlay.querySelector('.loading-status'); + this.detailsContainer = null; // Will be created when needed } show(message = 'Loading...', progress = 0) { this.overlay.style.display = 'flex'; this.setProgress(progress); this.setStatus(message); + + // Remove any existing details container + this.removeDetailsContainer(); } hide() { this.overlay.style.display = 'none'; this.reset(); + this.removeDetailsContainer(); } setProgress(percent) { @@ -29,6 +34,101 @@ export class LoadingManager { reset() { this.setProgress(0); this.setStatus(''); + this.removeDetailsContainer(); + } + + // Create a details container for enhanced progress display + createDetailsContainer() { + // Remove existing container if any + this.removeDetailsContainer(); + + // Create new container + this.detailsContainer = document.createElement('div'); + this.detailsContainer.className = 'progress-details-container'; + + // Insert after the main progress bar + const loadingContent = this.overlay.querySelector('.loading-content'); + if (loadingContent) { + loadingContent.appendChild(this.detailsContainer); + } + + return this.detailsContainer; + } + + // Remove details container + removeDetailsContainer() { + if (this.detailsContainer) { + this.detailsContainer.remove(); + this.detailsContainer = null; + } + } + + // Show enhanced progress for downloads + showDownloadProgress(totalItems = 1) { + this.show('Preparing download...', 0); + + // Create details container + const detailsContainer = this.createDetailsContainer(); + + // Create current item progress + const currentItemContainer = document.createElement('div'); + currentItemContainer.className = 'current-item-progress'; + + const currentItemLabel = document.createElement('div'); + currentItemLabel.className = 'current-item-label'; + currentItemLabel.textContent = 'Current file:'; + + const currentItemBar = document.createElement('div'); + currentItemBar.className = 'current-item-bar-container'; + + const currentItemProgress = document.createElement('div'); + currentItemProgress.className = 'current-item-bar'; + currentItemProgress.style.width = '0%'; + + const currentItemPercent = document.createElement('span'); + currentItemPercent.className = 'current-item-percent'; + currentItemPercent.textContent = '0%'; + + currentItemBar.appendChild(currentItemProgress); + currentItemContainer.appendChild(currentItemLabel); + currentItemContainer.appendChild(currentItemBar); + currentItemContainer.appendChild(currentItemPercent); + + // Create overall progress elements if multiple items + let overallLabel = null; + if (totalItems > 1) { + overallLabel = document.createElement('div'); + overallLabel.className = 'overall-progress-label'; + overallLabel.textContent = `Overall progress (0/${totalItems} complete):`; + detailsContainer.appendChild(overallLabel); + } + + // Add current item progress to container + detailsContainer.appendChild(currentItemContainer); + + // Return update function + return (currentProgress, currentIndex = 0, currentName = '') => { + // Update current item progress + currentItemProgress.style.width = `${currentProgress}%`; + currentItemPercent.textContent = `${Math.floor(currentProgress)}%`; + + // Update current item label if name provided + if (currentName) { + currentItemLabel.textContent = `Downloading: ${currentName}`; + } + + // Update overall label if multiple items + if (totalItems > 1 && overallLabel) { + overallLabel.textContent = `Overall progress (${currentIndex}/${totalItems} complete):`; + + // Calculate and update overall progress + const overallProgress = Math.floor((currentIndex + currentProgress/100) / totalItems * 100); + this.setProgress(overallProgress); + } else { + // Single item, just update main progress + this.setProgress(currentProgress); + } + }; } async showWithProgress(callback, options = {}) {