From b463ddce9491eddf1ad70925c535d7c1efab2efc Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Tue, 23 Dec 2025 20:07:31 +0000
Subject: [PATCH] feat(ui): replace loading overlay with button spinner
Replaces the disruptive full-screen loading overlay with a contextual loading spinner on the "Generate Poster" button.
This improves the user experience by providing immediate feedback at the point of interaction without blocking the entire UI.
---
web_app/static/index.html | 5 ++++-
web_app/static/script.js | 11 +++++++----
web_app/static/style.css | 26 ++++++++++++++++++++++++++
3 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/web_app/static/index.html b/web_app/static/index.html
index 0d56371..4545a76 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..2de78e7 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.classList.add('loading');
+ generateBtn.disabled = true;
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.classList.remove('loading');
+ generateBtn.disabled = false;
showToast("Please enter a valid range (Start must be less than End).", "error");
return;
}
@@ -669,13 +671,14 @@ async function generatePoster() {
posterContainer.innerHTML = '';
posterContainer.appendChild(img);
showDownloadButton(imageUrl, data.filename);
- loadingOverlay.style.display = 'none';
};
} catch (error) {
console.error("Generation failed", error);
showToast(`Error: ${error.message}`, "error");
- loadingOverlay.style.display = 'none';
+ } finally {
+ generateBtn.classList.remove('loading');
+ generateBtn.disabled = false;
}
}
diff --git a/web_app/static/style.css b/web_app/static/style.css
index 468eb76..a69f565 100644
--- a/web_app/static/style.css
+++ b/web_app/static/style.css
@@ -140,6 +140,7 @@ header p {
/* Buttons */
.primary-btn,
.secondary-btn {
+ position: relative; /* For spinner positioning */
display: inline-flex;
align-items: center;
justify-content: center;
@@ -166,6 +167,31 @@ header p {
transform: translateY(-1px);
}
+.primary-btn.loading {
+ cursor: wait;
+ color: transparent;
+ pointer-events: none;
+}
+
+.primary-btn .spinner {
+ display: none;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 20px;
+ height: 20px;
+ margin: 0;
+ border: 2px solid #1a1a1f;
+ border-top-color: transparent;
+ border-radius: 50%;
+ animation: spin 0.6s linear infinite;
+}
+
+.primary-btn.loading .spinner {
+ display: block;
+}
+
.secondary-btn {
background: transparent;
border-color: var(--border-color);