Improve HideUI & Flush controls

This commit is contained in:
2026-04-03 16:20:26 +11:00
parent 20a4f11818
commit 6ea24618d9
+94 -112
View File
@@ -319,30 +319,30 @@ const toggleRadiantLyrics = function (): void {
}; };
// Create buttons // Create buttons
let resyncLocked = false; let flushLocked = false;
const unlockResync = (): void => { const unlockFlush = (): void => {
resyncLocked = false; flushLocked = false;
const btn = document.querySelector(".resync-lyrics-button") as HTMLButtonElement; const btn = document.querySelector(".flush-lyrics-button") as HTMLButtonElement;
if (btn) { if (btn) {
btn.disabled = false; btn.disabled = false;
btn.style.opacity = ""; btn.style.opacity = "";
btn.style.cursor = ""; btn.style.cursor = "";
btn.setAttribute("title", "Resync Lyrics"); btn.setAttribute("title", "Flush Lyrics");
btn.setAttribute("aria-label", "Resync Lyrics"); btn.setAttribute("aria-label", "Flush Lyrics");
} }
}; };
const lockResync = (): void => { const lockFlush = (): void => {
resyncLocked = true; flushLocked = true;
const btn = document.querySelector(".resync-lyrics-button") as HTMLButtonElement; const btn = document.querySelector(".flush-lyrics-button") as HTMLButtonElement;
if (btn) { if (btn) {
btn.disabled = true; btn.disabled = true;
btn.style.opacity = "0.3"; btn.style.opacity = "0.3";
btn.style.cursor = "not-allowed"; btn.style.cursor = "not-allowed";
} }
}; };
const disableResyncNoLyrics = (): void => { const disableFlushNoLyrics = (): void => {
resyncLocked = true; flushLocked = true;
const btn = document.querySelector(".resync-lyrics-button") as HTMLButtonElement; const btn = document.querySelector(".flush-lyrics-button") as HTMLButtonElement;
if (btn) { if (btn) {
btn.disabled = true; btn.disabled = true;
btn.style.opacity = "0.3"; btn.style.opacity = "0.3";
@@ -352,16 +352,16 @@ const disableResyncNoLyrics = (): void => {
} }
}; };
const resyncLyrics = async (): Promise<void> => { const flushLyrics = async (): Promise<void> => {
if (resyncLocked) return; if (flushLocked) return;
const trackInfo = await getTrackInfo(); const trackInfo = await getTrackInfo();
if (!trackInfo) { if (!trackInfo) {
trace.msg.err("Resync: could not get track info"); trace.msg.err("Flush: could not get track info");
return; return;
} }
lockResync(); lockFlush();
let params = `?title=${encodeURIComponent(trackInfo.title)}&artist=${encodeURIComponent(trackInfo.artist)}`; let params = `?title=${encodeURIComponent(trackInfo.title)}&artist=${encodeURIComponent(trackInfo.artist)}`;
if (trackInfo.isrc) params += `&isrc=${encodeURIComponent(trackInfo.isrc)}`; if (trackInfo.isrc) params += `&isrc=${encodeURIComponent(trackInfo.isrc)}`;
@@ -370,18 +370,18 @@ const resyncLyrics = async (): Promise<void> => {
const url = `https://api.atomix.one/rl-api${params}`; const url = `https://api.atomix.one/rl-api${params}`;
try { try {
const clientIP = await getPublicIPv4(); const clientIP = await getPublicIPv4();
const resyncHeaders: Record<string, string> = { const flushHeaders: Record<string, string> = {
"P-Access-Token-Id": "58hy4s86", "P-Access-Token-Id": "58hy4s86",
"P-Access-Token": "xjehy2lfg5h5mjwotoxrcqugam", "P-Access-Token": "xjehy2lfg5h5mjwotoxrcqugam",
}; };
resyncHeaders["x-client-ip"] = clientIP ?? "null"; flushHeaders["x-client-ip"] = clientIP ?? "null";
const res = await fetch(url, { headers: resyncHeaders }); const res = await fetch(url, { headers: flushHeaders });
if (res.status === 404) { if (res.status === 404) {
toast("No lyrics found for this track"); toast("No lyrics found for this track");
return; return;
} }
if (!res.ok) { if (!res.ok) {
toastErr(`Resync failed (${res.status})`); toastErr(`Flush failed (${res.status})`);
return; return;
} }
const data = (await res.json()) as LyricsApiResponse & { _flush?: string }; const data = (await res.json()) as LyricsApiResponse & { _flush?: string };
@@ -391,7 +391,7 @@ const resyncLyrics = async (): Promise<void> => {
if (flush) { if (flush) {
toast(flush); toast(flush);
} else { } else {
toast("Lyrics resynced"); toast("Lyrics flushed");
} }
if (needsReload || !flush) { if (needsReload || !flush) {
cachedLyricsKey = null; cachedLyricsKey = null;
@@ -399,100 +399,82 @@ const resyncLyrics = async (): Promise<void> => {
onTrackChange(); onTrackChange();
} }
} catch (err) { } catch (err) {
toastErr(`Resync error: ${err instanceof Error ? err.message : String(err)}`); toastErr(`Flush error: ${err instanceof Error ? err.message : String(err)}`);
} }
}; };
const createResyncButton = function (): void { const createFlushButton = function (): void {
safeTimeout( const closeButton = document.querySelector(
unloads, '[data-test="new-now-playing-close"]',
() => { ) as HTMLButtonElement;
const closeButton = document.querySelector( if (!closeButton?.parentElement) return;
'[data-test="new-now-playing-close"]', if (document.querySelector(".flush-lyrics-button")) return;
) as HTMLButtonElement; const buttonContainer = closeButton.parentElement;
if (!closeButton || !closeButton.parentElement) {
safeTimeout(unloads, () => createResyncButton(), 1000);
return;
}
if (document.querySelector(".resync-lyrics-button")) return;
const buttonContainer = closeButton.parentElement;
const resyncButton = closeButton.cloneNode(false) as HTMLButtonElement; const flushButton = closeButton.cloneNode(false) as HTMLButtonElement;
resyncButton.className = closeButton.className; flushButton.className = closeButton.className;
resyncButton.classList.add("resync-lyrics-button"); flushButton.classList.add("flush-lyrics-button");
resyncButton.removeAttribute("data-test"); flushButton.removeAttribute("data-test");
resyncButton.setAttribute("type", "button"); flushButton.setAttribute("type", "button");
resyncButton.setAttribute("aria-label", "Resync Lyrics"); flushButton.setAttribute("aria-label", "Flush Lyrics");
resyncButton.setAttribute("title", "Resync Lyrics"); flushButton.setAttribute("title", "Flush Lyrics");
resyncButton.disabled = true; flushButton.disabled = true;
resyncButton.style.opacity = "0.3"; flushButton.style.opacity = "0.3";
resyncButton.style.cursor = "not-allowed"; flushButton.style.cursor = "not-allowed";
const iconSpan = closeButton.querySelector("span"); const iconSpan = closeButton.querySelector("span");
const iconSvg = closeButton.querySelector("svg"); const iconSvg = closeButton.querySelector("svg");
const spanWrapper = document.createElement("span"); const spanWrapper = document.createElement("span");
if (iconSpan) spanWrapper.className = iconSpan.className; if (iconSpan) spanWrapper.className = iconSpan.className;
spanWrapper.setAttribute("aria-hidden", "true"); spanWrapper.setAttribute("aria-hidden", "true");
const svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg"); const svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svgEl.setAttribute("viewBox", "0 0 20 20"); svgEl.setAttribute("viewBox", "0 0 20 20");
if (iconSvg) svgEl.setAttribute("class", iconSvg.className.baseVal); if (iconSvg) svgEl.setAttribute("class", iconSvg.className.baseVal);
const useEl = document.createElementNS("http://www.w3.org/2000/svg", "use"); const useEl = document.createElementNS("http://www.w3.org/2000/svg", "use");
useEl.setAttribute("href", "#general__lyrics-sync"); useEl.setAttribute("href", "#general__lyrics-sync");
svgEl.appendChild(useEl); svgEl.appendChild(useEl);
spanWrapper.appendChild(svgEl); spanWrapper.appendChild(svgEl);
resyncButton.appendChild(spanWrapper); flushButton.appendChild(spanWrapper);
resyncButton.onclick = () => resyncLyrics(); flushButton.onclick = () => flushLyrics();
const hideBtn = buttonContainer.querySelector(".hide-ui-button"); const hideBtn = buttonContainer.querySelector(".hide-ui-button");
buttonContainer.insertBefore( buttonContainer.insertBefore(
resyncButton, flushButton,
hideBtn ?? closeButton, hideBtn ?? closeButton,
);
},
1000,
); );
}; };
const createHideUIButton = function (): void { const createHideUIButton = function (): void {
safeTimeout( if (!settings.hideUIEnabled) return;
unloads, const closeButton = document.querySelector(
() => { '[data-test="new-now-playing-close"]',
if (!settings.hideUIEnabled) return; ) as HTMLButtonElement;
const closeButton = document.querySelector( if (!closeButton?.parentElement) return;
'[data-test="new-now-playing-close"]', if (document.querySelector(".hide-ui-button")) return;
) as HTMLButtonElement; const buttonContainer = closeButton.parentElement;
if (!closeButton || !closeButton.parentElement) {
safeTimeout(unloads, () => createHideUIButton(), 1000);
return;
}
if (document.querySelector(".hide-ui-button")) return;
const buttonContainer = closeButton.parentElement;
const hideUIButton = closeButton.cloneNode(false) as HTMLButtonElement; const hideUIButton = closeButton.cloneNode(false) as HTMLButtonElement;
hideUIButton.className = closeButton.className; hideUIButton.className = closeButton.className;
hideUIButton.classList.add("hide-ui-button"); hideUIButton.classList.add("hide-ui-button");
hideUIButton.removeAttribute("data-test"); hideUIButton.removeAttribute("data-test");
hideUIButton.setAttribute("type", "button"); hideUIButton.setAttribute("type", "button");
hideUIButton.setAttribute("aria-label", isHidden ? "Show UI" : "Hide UI"); hideUIButton.setAttribute("aria-label", isHidden ? "Show UI" : "Hide UI");
hideUIButton.setAttribute("title", isHidden ? "Show UI" : "Hide UI"); hideUIButton.setAttribute("title", isHidden ? "Show UI" : "Hide UI");
const iconSpan = closeButton.querySelector("span"); const iconSpan = closeButton.querySelector("span");
const iconSvg = closeButton.querySelector("svg"); const iconSvg = closeButton.querySelector("svg");
const spanWrapper = document.createElement("span"); const spanWrapper = document.createElement("span");
if (iconSpan) spanWrapper.className = iconSpan.className; if (iconSpan) spanWrapper.className = iconSpan.className;
spanWrapper.setAttribute("aria-hidden", "true"); spanWrapper.setAttribute("aria-hidden", "true");
const svgContent = isHidden ? EYE_ON_SVG : EYE_OFF_SVG; const svgContent = isHidden ? EYE_ON_SVG : EYE_OFF_SVG;
spanWrapper.innerHTML = svgContent; spanWrapper.innerHTML = svgContent;
const svg = spanWrapper.querySelector("svg"); const svg = spanWrapper.querySelector("svg");
if (svg && iconSvg) svg.setAttribute("class", iconSvg.className.baseVal); if (svg && iconSvg) svg.setAttribute("class", iconSvg.className.baseVal);
hideUIButton.appendChild(spanWrapper); hideUIButton.appendChild(spanWrapper);
hideUIButton.onclick = toggleRadiantLyrics; hideUIButton.onclick = toggleRadiantLyrics;
buttonContainer.insertBefore(hideUIButton, closeButton); buttonContainer.insertBefore(hideUIButton, closeButton);
},
1000,
);
}; };
// MARKER: Background Rendering // MARKER: Background Rendering
@@ -1024,7 +1006,7 @@ unloads.add(() => {
} }
// Clean up action buttons // Clean up action buttons
for (const sel of [".hide-ui-button", ".resync-lyrics-button"]) { for (const sel of [".hide-ui-button", ".flush-lyrics-button"]) {
const btn = document.querySelector(sel); const btn = document.querySelector(sel);
if (btn?.parentNode) btn.parentNode.removeChild(btn); if (btn?.parentNode) btn.parentNode.removeChild(btn);
} }
@@ -3931,7 +3913,7 @@ const startTickLoop = (): void => {
// Called by track change or style toggle // Called by track change or style toggle
const onTrackChange = async (): Promise<void> => { const onTrackChange = async (): Promise<void> => {
teardown(); teardown();
lockResync(); lockFlush();
const runId = ++trackChangeRunSeq; const runId = ++trackChangeRunSeq;
isTrackChangeRunning = true; isTrackChangeRunning = true;
@@ -3957,7 +3939,7 @@ const onTrackChange = async (): Promise<void> => {
if (token !== trackChangeToken) return; if (token !== trackChangeToken) return;
if (!response) { if (!response) {
trace.log("RL API: no API lyrics available, falling back to TIDAL lines"); trace.log("RL API: no API lyrics available, falling back to TIDAL lines");
disableResyncNoLyrics(); disableFlushNoLyrics();
const tidalTexts = getTidalLines(); const tidalTexts = getTidalLines();
const romanized = settings.romanizeLyrics const romanized = settings.romanizeLyrics
? await romanizeLines(tidalTexts) ? await romanizeLines(tidalTexts)
@@ -4003,7 +3985,7 @@ const onTrackChange = async (): Promise<void> => {
`[RL-Syllable] Loaded "${trackInfo.title}" by "${trackInfo.artist}" — ${response.data.length} lines`, `[RL-Syllable] Loaded "${trackInfo.title}" by "${trackInfo.artist}" — ${response.data.length} lines`,
); );
unlockResync(); unlockFlush();
lyricsMode = response.type === "Word" ? "word" : "line-api"; lyricsMode = response.type === "Word" ? "word" : "line-api";
if (token !== trackChangeToken) return; if (token !== trackChangeToken) return;
lyricsData = lyricsData =
@@ -4197,15 +4179,15 @@ const setupTrackChangeListener = (): void => {
}; };
function setupHeaderObserver(): void { function setupHeaderObserver(): void {
const existing = document.querySelector('[data-test="header"]'); const injectButtons = () => {
if (existing) { if (!document.querySelector(".flush-lyrics-button")) createFlushButton();
if (!document.querySelector(".resync-lyrics-button")) createResyncButton();
if (!document.querySelector(".hide-ui-button")) createHideUIButton(); if (!document.querySelector(".hide-ui-button")) createHideUIButton();
};
if (document.querySelector('[data-test="new-now-playing-close"]')) {
injectButtons();
} }
observe<HTMLElement>(unloads, '[data-test="header"]', () => { observe<HTMLElement>(unloads, '[data-test="new-now-playing-close"]', injectButtons);
if (!document.querySelector(".resync-lyrics-button")) createResyncButton();
if (!document.querySelector(".hide-ui-button")) createHideUIButton();
});
} }
// Apply seeker color on track change // Apply seeker color on track change