From 64a34a27ea37e8cbb6d746c26c0a3d5e1a0cd045 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Mon, 19 Jan 2026 20:40:19 +0000
Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Add=20loading=20state?=
=?UTF-8?q?=20to=20Generate=20Poster=20button?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
💡 What: Replaced the full-screen loading overlay with an inline spinner on the "Generate Poster" button.
🎯 Why: The previous full-screen overlay was disruptive and didn't provide focused feedback. This change makes the UI more responsive by showing the loading status directly on the button that the user interacted with, preventing duplicate clicks and providing a smoother experience.
📸 Before/After: A full-screen overlay would appear on poster generation. Now, the "Generate Poster" button itself indicates the loading state.
♿ Accessibility: Added an `aria-label` to the button during the loading state to inform screen reader users that the poster is being generated.
---
web_app/static/index.html | 5 ++++-
web_app/static/script.js | 32 ++++++++++++++++++++++++++++----
web_app/static/style.css | 30 ++++++++++++++++++++++++++++++
3 files changed, 62 insertions(+), 5 deletions(-)
diff --git a/web_app/static/index.html b/web_app/static/index.html
index 0d56371..15c8365 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..d1413cc 100644
--- a/web_app/static/script.js
+++ b/web_app/static/script.js
@@ -613,7 +613,15 @@ function handleLyricLineClick(lineNumber) {
async function generatePoster() {
if (!currentMetadata) return;
- loadingOverlay.style.display = 'flex';
+ const btn = document.getElementById('generateBtn');
+ const btnText = btn.querySelector('.btn-text');
+ const spinner = btn.querySelector('.spinner-inline');
+
+ // UX: Disable button and show spinner
+ btn.classList.add('loading');
+ btn.disabled = true;
+ btn.setAttribute('aria-label', 'Generating poster, please wait');
+
const indexingToggle = document.getElementById('indexingToggle');
const accentToggle = document.getElementById('accentToggle');
@@ -635,8 +643,11 @@ 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");
+ // UX: Reset button state on failure
+ btn.classList.remove('loading');
+ btn.disabled = false;
+ btn.removeAttribute('aria-label');
return;
}
@@ -669,13 +680,26 @@ async function generatePoster() {
posterContainer.innerHTML = '';
posterContainer.appendChild(img);
showDownloadButton(imageUrl, data.filename);
- loadingOverlay.style.display = 'none';
+ // UX: Reset button state on success
+ btn.classList.remove('loading');
+ btn.disabled = false;
+ btn.removeAttribute('aria-label');
+ };
+ img.onerror = () => {
+ showToast("Error loading generated image.", "error");
+ // UX: Reset button state on failure
+ btn.classList.remove('loading');
+ btn.disabled = false;
+ btn.removeAttribute('aria-label');
};
} catch (error) {
console.error("Generation failed", error);
showToast(`Error: ${error.message}`, "error");
- loadingOverlay.style.display = 'none';
+ // UX: Reset button state on failure
+ btn.classList.remove('loading');
+ btn.disabled = false;
+ btn.removeAttribute('aria-label');
}
}
diff --git a/web_app/static/style.css b/web_app/static/style.css
index 468eb76..196b796 100644
--- a/web_app/static/style.css
+++ b/web_app/static/style.css
@@ -908,4 +908,34 @@ input:focus {
.result-action svg {
pointer-events: none;
+}
+
+/* --- Inline Spinner for Buttons --- */
+.spinner-inline {
+ display: none; /* Hidden by default */
+ width: 20px;
+ height: 20px;
+ border: 2px solid rgba(0, 0, 0, 0.2);
+ border-top-color: #1a1a1f; /* Match button text color */
+ border-radius: 50%;
+ animation: spin 0.8s linear infinite;
+}
+
+.primary-btn.loading {
+ cursor: not-allowed;
+ background: var(--accent-hover);
+}
+
+.primary-btn.loading .btn-text {
+ display: none;
+}
+
+.primary-btn.loading .spinner-inline {
+ display: block;
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
}
\ No newline at end of file