From 15e643e9a7be86923647c6967154d7cde8194459 Mon Sep 17 00:00:00 2001 From: Moses <338911804+MosesXD@users.noreply.github.com> Date: Mon, 9 Nov 2026 16:36:19 -0400 Subject: [PATCH 1/3] Lock background scrolling while any modal is open --- script.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/script.js b/script.js index a477b7b..c396e04 100644 --- a/script.js +++ b/script.js @@ -431,3 +431,52 @@ if (wipayOpen && wipayDialog) { }); } } + + +// ---------- Shared modal behaviour: lock the page behind every native dialog ---------- +(function () { + const body = document.body; + const root = document.documentElement; + let locked = false; + let savedScrollY = 0; + let originalStyles = null; + + function syncModalScroll() { + const modalOpen = Boolean(document.querySelector("dialog[open]")); + if (modalOpen && !locked) { + savedScrollY = window.scrollY; + originalStyles = { + position: body.style.position, + top: body.style.top, + left: body.style.left, + right: body.style.right, + width: body.style.width, + overflow: body.style.overflow + }; + body.style.position = "fixed"; + body.style.top = "-" + savedScrollY + "px"; + body.style.left = "0"; + body.style.right = "0"; + body.style.width = "100%"; + body.style.overflow = "hidden"; + body.classList.add("modal-scroll-locked"); + locked = true; + } else if (!modalOpen && locked) { + body.classList.remove("modal-scroll-locked"); + Object.keys(originalStyles).forEach(property => { + body.style[property] = originalStyles[property]; + }); + locked = false; + window.scrollTo(0, savedScrollY); + } + } + + const observer = new MutationObserver(syncModalScroll); + observer.observe(root, { + subtree: true, + childList: true, + attributes: true, + attributeFilter: ["open"] + }); + document.addEventListener("close", syncModalScroll, true); +})(); From 8fbc65fc9a8b993334c2cc788fd76457fe4963eb Mon Sep 17 00:00:00 2001 From: Moses <338911804+MosesXD@users.noreply.github.com> Date: Mon, 9 Nov 2026 16:36:21 -0400 Subject: [PATCH 2/3] Centre and contain native modal prompts --- style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/style.css b/style.css index 363527f..956005e 100644 --- a/style.css +++ b/style.css @@ -875,6 +875,9 @@ html[data-bg="grid"] body { .now-playing-artwork { width:52px; height:52px; object-fit:cover; border-radius:10px; } .now-playing-open { color:var(--cyan); } .spotify-confirm { width:min(92vw,390px); margin:auto; padding:1.5rem; color:var(--text); background:var(--surface); border:1px solid var(--border); border-radius:22px; } +/* Keep every native prompt centred, contained, and scrollable without moving the page behind it. */ +dialog[open] { margin:auto; max-height:90vh; max-height:90dvh; overflow-y:auto; overscroll-behavior:contain; } +body.modal-scroll-locked { overscroll-behavior:none; } .spotify-confirm::backdrop { background:rgba(0,0,0,.68); backdrop-filter:blur(4px); } .spotify-confirm h3 { margin-bottom:.5rem; } .spotify-confirm form > p:not(.eyebrow) { color:var(--muted); } From 14c8dbde5259a267f7f45dbfa9aacaca21877f2e Mon Sep 17 00:00:00 2001 From: Moses <338911804+MosesXD@users.noreply.github.com> Date: Mon, 9 Nov 2026 16:36:23 -0400 Subject: [PATCH 3/3] Improve Last.fm chart empty and API error feedback --- music-charts.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/music-charts.js b/music-charts.js index 20cb8e0..d4ec16e 100644 --- a/music-charts.js +++ b/music-charts.js @@ -50,7 +50,8 @@ list.replaceChildren(); if (!period || !Array.isArray(period.tracks) || period.tracks.length === 0) { status.hidden = false; - status.textContent = "No listening history found for this period yet."; + status.textContent = "No scrobbles were returned from your Last.fm history for this date range."; + if (data && data.generatedAt) status.textContent += " Last checked: " + new Date(data.generatedAt).toLocaleString() + "."; return; } status.hidden = false; @@ -103,7 +104,9 @@ const response = await fetch(endpoint, { headers: { Accept: "application/json" }, cache: "no-store" }); const result = await response.json().catch(() => null); if (!response.ok || !result || !result.periods) { - throw new Error(result && result.error ? result.error : "Charts are temporarily unavailable."); + const message = result && result.error ? result.error : "Charts are temporarily unavailable."; + const detail = result && result.detail ? " — " + result.detail : ""; + throw new Error(message + detail); } data = result; lastLoaded = Date.now();