diff --git a/web_app/static/index.html b/web_app/static/index.html index 0d56371..d57b35e 100644 --- a/web_app/static/index.html +++ b/web_app/static/index.html @@ -116,7 +116,10 @@

Lyrics

- + diff --git a/web_app/static/script.js b/web_app/static/script.js index 441a4ed..f480a56 100644 --- a/web_app/static/script.js +++ b/web_app/static/script.js @@ -20,7 +20,7 @@ const startLineInput = document.getElementById('startLine'); const endLineInput = document.getElementById('endLine'); const themeInput = document.getElementById('themeInput'); const fontInput = document.getElementById('fontInput'); -const loadingOverlay = document.getElementById('loadingOverlay'); +const loadingOverlay = document.getElementById('loadingOverlay'); // Keep for now, might be used elsewhere let currentMetadata = null; let searchDebounceTimer = null; @@ -611,9 +611,17 @@ function handleLyricLineClick(lineNumber) { } async function generatePoster() { - if (!currentMetadata) return; + if (!currentMetadata || generateBtn.classList.contains('loading')) return; - loadingOverlay.style.display = 'flex'; + const btnText = generateBtn.querySelector('.btn-text'); + const spinner = generateBtn.querySelector('.spinner'); + + // --- 1. Set Loading State --- + generateBtn.classList.add('loading'); + generateBtn.disabled = true; + btnText.textContent = 'Generating...'; // Update text for visual consistency + generateBtn.setAttribute('aria-label', 'Generating poster, please wait'); // Set accessible name + spinner.style.display = 'block'; const indexingToggle = document.getElementById('indexingToggle'); const accentToggle = document.getElementById('accentToggle'); @@ -635,8 +643,8 @@ async function generatePoster() { const end = endVal; if (isNaN(start) || isNaN(end) || start >= end) { - loadingOverlay.style.display = 'none'; showToast("Please enter a valid range (Start must be less than End).", "error"); + // No loading overlay to hide, just return return; } @@ -669,13 +677,20 @@ async function generatePoster() { posterContainer.innerHTML = ''; posterContainer.appendChild(img); showDownloadButton(imageUrl, data.filename); - loadingOverlay.style.display = 'none'; + // No loading overlay to hide }; } catch (error) { console.error("Generation failed", error); showToast(`Error: ${error.message}`, "error"); - loadingOverlay.style.display = 'none'; + // No loading overlay to hide + } finally { + // --- 2. Reset Button State --- + generateBtn.classList.remove('loading'); + generateBtn.disabled = false; + btnText.textContent = 'Generate Poster'; + generateBtn.removeAttribute('aria-label'); // Remove accessible name + spinner.style.display = 'none'; } } diff --git a/web_app/static/style.css b/web_app/static/style.css index 468eb76..eacd76a 100644 --- a/web_app/static/style.css +++ b/web_app/static/style.css @@ -178,6 +178,28 @@ header p { border-color: var(--text-muted); } +.primary-btn.loading { + cursor: not-allowed; + background: var(--accent-hover); +} + +.primary-btn.loading .btn-text { + display: none; +} + +.primary-btn.loading .spinner { + display: block; + margin-bottom: 0; /* Override default margin */ +} + +/* Scoped spinner for button */ +.primary-btn .spinner { + width: 20px; + height: 20px; + border-width: 2px; + border-top-color: #1a1a1f; /* Dark color for contrast */ +} + /* Inputs */ input[type="text"], input[type="number"] {