From f3eeb4f8a641b52be2e6b4b1c2944bda2e70766a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 8 Jan 2026 20:13:30 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Add=20loading=20state?= =?UTF-8?q?=20to=20Generate=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 💡 What: Replaced the full-screen loading overlay with a spinner inside the "Generate Poster" button. The button is now disabled during poster generation. 🎯 Why: The previous full-screen overlay was disruptive and obscured the UI. This change provides a more subtle and contextual loading indicator, improving the user experience by giving feedback directly at the point of interaction without interrupting the user's flow. ♿ Accessibility: The button is now programmatically disabled during the loading state, which prevents multiple clicks and provides a clear state for assistive technologies. --- web_app/static/index.html | 5 ++++- web_app/static/script.js | 24 ++++++++++++++++++------ web_app/static/style.css | 31 ++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/web_app/static/index.html b/web_app/static/index.html index 0d56371..802e1f3 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..6dba927 100644 --- a/web_app/static/script.js +++ b/web_app/static/script.js @@ -613,7 +613,8 @@ function handleLyricLineClick(lineNumber) { async function generatePoster() { if (!currentMetadata) return; - loadingOverlay.style.display = 'flex'; + generateBtn.disabled = true; + generateBtn.classList.add('loading'); const indexingToggle = document.getElementById('indexingToggle'); const accentToggle = document.getElementById('accentToggle'); @@ -635,7 +636,8 @@ async function generatePoster() { const end = endVal; if (isNaN(start) || isNaN(end) || start >= end) { - loadingOverlay.style.display = 'none'; + generateBtn.disabled = false; + generateBtn.classList.remove('loading'); showToast("Please enter a valid range (Start must be less than End).", "error"); return; } @@ -651,7 +653,9 @@ async function generatePoster() { try { const response = await fetch('/api/generate', { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type': 'application/json' + }, body: JSON.stringify(payload) }); @@ -663,19 +667,27 @@ async function generatePoster() { const data = await response.json(); const imageUrl = data.is_base64 ? data.image_data : `${data.image_url}?t=${new Date().getTime()}`; - const img = document.createElement('img'); + const img = new Image(); img.src = imageUrl; + img.onload = () => { posterContainer.innerHTML = ''; posterContainer.appendChild(img); showDownloadButton(imageUrl, data.filename); - loadingOverlay.style.display = 'none'; + generateBtn.disabled = false; + generateBtn.classList.remove('loading'); + }; + img.onerror = () => { + showToast('Error: Failed to load the generated poster image.', 'error'); + generateBtn.disabled = false; + generateBtn.classList.remove('loading'); }; } catch (error) { console.error("Generation failed", error); showToast(`Error: ${error.message}`, "error"); - loadingOverlay.style.display = 'none'; + generateBtn.disabled = false; + generateBtn.classList.remove('loading'); } } diff --git a/web_app/static/style.css b/web_app/static/style.css index 468eb76..08338d0 100644 --- a/web_app/static/style.css +++ b/web_app/static/style.css @@ -161,11 +161,40 @@ header p { width: 100%; } -.primary-btn:hover { +.primary-btn:hover:not(:disabled) { background: var(--accent-hover); transform: translateY(-1px); } +.primary-btn:disabled, +.primary-btn.loading { + background: var(--accent-hover); + cursor: not-allowed; + transform: none; + opacity: 0.7; +} + +.primary-btn .spinner { + display: none; +} + +.primary-btn.loading .btn-text { + display: none; +} + +.primary-btn.loading .spinner { + display: block; + width: 20px; + height: 20px; + border-width: 2px; + border-style: solid; + border-color: rgba(0, 0, 0, 0.2); + border-top-color: #1a1a1f; + margin-bottom: 0; + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + .secondary-btn { background: transparent; border-color: var(--border-color);