From 9f2289329c5385e0bec5dc04a170d19d6afaa798 Mon Sep 17 00:00:00 2001 From: Will Miao <13051207myq@gmail.com> Date: Sat, 15 Mar 2025 16:25:56 +0800 Subject: [PATCH] Implement enhanced loading progress display in DownloadManager and ImportManager. Introduce detailed progress updates and UI elements for current item and overall progress during downloads. Update LoadingManager to support dynamic progress visualization. --- static/css/components/loading.css | 50 ++++++++++++- static/js/managers/DownloadManager.js | 26 +++++-- static/js/managers/ImportManager.js | 68 ++++++++++++++---- static/js/managers/LoadingManager.js | 100 ++++++++++++++++++++++++++ 4 files changed, 224 insertions(+), 20 deletions(-) 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 = {}) {