diff --git a/plugins/radiant-lyrics-luna/src/Settings.tsx b/plugins/radiant-lyrics-luna/src/Settings.tsx index 3a7d642..6e5b91e 100644 --- a/plugins/radiant-lyrics-luna/src/Settings.tsx +++ b/plugins/radiant-lyrics-luna/src/Settings.tsx @@ -34,7 +34,6 @@ export const Settings = () => { desc="Enable glowing effect for lyrics & Font Stytling Changes" checked={lyricsGlowEnabled} onChange={(_, checked: boolean) => { - console.log("Lyrics Glow Effect:", checked ? "enabled" : "disabled"); setLyricsGlowEnabled((settings.lyricsGlowEnabled = checked)); // Update styles immediately when setting changes if ((window as any).updateRadiantLyricsStyles) { @@ -47,7 +46,6 @@ export const Settings = () => { desc="Enable hide/unhide UI functionality with toggle buttons" checked={hideUIEnabled} onChange={(_, checked: boolean) => { - console.log("Hide UI Feature:", checked ? "enabled" : "disabled"); setHideUIEnabled((settings.hideUIEnabled = checked)); }} /> @@ -101,7 +99,6 @@ export const Settings = () => { step={1} value={backgroundContrast} onNumber={(value: number) => { - console.log("Background Contrast:", value); setBackgroundContrast((settings.backgroundContrast = value)); if ((window as any).updateRadiantLyricsGlobalBackground) { (window as any).updateRadiantLyricsGlobalBackground(); diff --git a/plugins/radiant-lyrics-luna/src/index.ts b/plugins/radiant-lyrics-luna/src/index.ts index a670d2d..bd5b1ac 100644 --- a/plugins/radiant-lyrics-luna/src/index.ts +++ b/plugins/radiant-lyrics-luna/src/index.ts @@ -1,4 +1,4 @@ -import { LunaUnload, Tracer } from "@luna/core"; +import { LunaUnload, Tracer, ftch } from "@luna/core"; import { StyleTag, PlayState } from "@luna/lib"; import { settings, Settings } from "./Settings"; @@ -24,394 +24,368 @@ let globalSpinningBgStyleTag: StyleTag | null = null; // Apply lyrics glow styles if enabled if (settings.lyricsGlowEnabled) { - lyricsGlowStyleTag.css = lyricsGlow; + lyricsGlowStyleTag.css = lyricsGlow; } var isHidden = false; let unhideButtonAutoFadeTimeout: number | null = null; -const updateButtonStates = function (): void { - const hideButton = document.querySelector(".hide-ui-button") as HTMLElement; - const unhideButton = document.querySelector( - ".unhide-ui-button", - ) as HTMLElement; - - if (hideButton) { - if (settings.hideUIEnabled && !isHidden) { - hideButton.style.display = "flex"; - // Small delay to ensure display is set first, then fade in - setTimeout(() => { - hideButton.style.opacity = "1"; - hideButton.style.visibility = "visible"; - hideButton.style.pointerEvents = "auto"; - }, 50); - } else { - // Hide UI button immediately when clicked - (couldn't get the fade to work) - hideButton.style.display = "none"; - hideButton.style.opacity = "0"; - hideButton.style.visibility = "hidden"; - hideButton.style.pointerEvents = "none"; - } - } - if (unhideButton) { - // Clear any existing auto-fade timeout - if (unhideButtonAutoFadeTimeout) { - window.clearTimeout(unhideButtonAutoFadeTimeout); - unhideButtonAutoFadeTimeout = null; - } - - if (settings.hideUIEnabled && isHidden) { - unhideButton.style.display = "flex"; - // Remove the hide-immediately class and let it fade in smoothly - unhideButton.classList.remove("hide-immediately"); - unhideButton.classList.remove("auto-faded"); - // Small delay to ensure display is set first, then fade in - (Works for unhide button.. but not hide button.. because uhh idk) - setTimeout(() => { - unhideButton.style.opacity = "1"; - unhideButton.style.visibility = "visible"; - unhideButton.style.pointerEvents = "auto"; - - // Set up auto-fade after 2 seconds - unhideButtonAutoFadeTimeout = window.setTimeout(() => { - if (isHidden && unhideButton && !unhideButton.matches(":hover")) { - unhideButton.classList.add("auto-faded"); - } - }, 2000); - }, 50); - } else { - // Smooth fade out for Unhide UI button - unhideButton.style.opacity = "0"; - unhideButton.style.visibility = "hidden"; - unhideButton.style.pointerEvents = "none"; - unhideButton.classList.remove("auto-faded"); - // Keep display: flex to maintain transitions, then hide after fade - setTimeout(() => { - if (unhideButton.style.opacity === "0") { - unhideButton.style.display = "none"; +const updateButtonStates = function(): void { + const hideButton = document.querySelector('.hide-ui-button') as HTMLElement; + const unhideButton = document.querySelector('.unhide-ui-button') as HTMLElement; + + if (hideButton) { + if (settings.hideUIEnabled && !isHidden) { + hideButton.style.display = 'flex'; + // Small delay to ensure display is set first, then fade in + setTimeout(() => { + hideButton.style.opacity = '1'; + hideButton.style.visibility = 'visible'; + hideButton.style.pointerEvents = 'auto'; + }, 50); + } else { + // Hide UI button immediately when clicked - (couldn't get the fade to work) + hideButton.style.display = 'none'; + hideButton.style.opacity = '0'; + hideButton.style.visibility = 'hidden'; + hideButton.style.pointerEvents = 'none'; + } + } + if (unhideButton) { + // Clear any existing auto-fade timeout + if (unhideButtonAutoFadeTimeout) { + window.clearTimeout(unhideButtonAutoFadeTimeout); + unhideButtonAutoFadeTimeout = null; + } + + if (settings.hideUIEnabled && isHidden) { + unhideButton.style.display = 'flex'; + // Remove the hide-immediately class and let it fade in smoothly + unhideButton.classList.remove('hide-immediately'); + unhideButton.classList.remove('auto-faded'); + // Small delay to ensure display is set first, then fade in - (Works for unhide button.. but not hide button.. because uhh idk) + setTimeout(() => { + unhideButton.style.opacity = '1'; + unhideButton.style.visibility = 'visible'; + unhideButton.style.pointerEvents = 'auto'; + + // Set up auto-fade after 2 seconds + unhideButtonAutoFadeTimeout = window.setTimeout(() => { + if (isHidden && unhideButton && !unhideButton.matches(':hover')) { + unhideButton.classList.add('auto-faded'); + } + }, 2000); + }, 50); + } else { + // Smooth fade out for Unhide UI button + unhideButton.style.opacity = '0'; + unhideButton.style.visibility = 'hidden'; + unhideButton.style.pointerEvents = 'none'; + unhideButton.classList.remove('auto-faded'); + // Keep display: flex to maintain transitions, then hide after fade + setTimeout(() => { + if (unhideButton.style.opacity === '0') { + unhideButton.style.display = 'none'; + } + }, 500); // Wait for transition to complete } - }, 500); // Wait for transition to complete } - } }; // Function to update styles when settings change -const updateRadiantLyricsStyles = function (): void { - if (isHidden) { - // Apply only base styles (button hiding), NOT separated lyrics styles - // to avoid affecting lyrics scrolling behavior - baseStyleTag.css = baseStyles; - - // Apply player bar styles based on setting - if (!settings.playerBarVisible) { - playerBarStyleTag.css = playerBarHidden; - } else { - playerBarStyleTag.remove(); +const updateRadiantLyricsStyles = function(): void { + if (isHidden) { + // Apply only base styles (button hiding), NOT separated lyrics styles + // to avoid affecting lyrics scrolling behavior + baseStyleTag.css = baseStyles; + + // Apply player bar styles based on setting + if (!settings.playerBarVisible) { + playerBarStyleTag.css = playerBarHidden; + } else { + playerBarStyleTag.remove(); + } } - } - // Update lyrics glow based on setting (only if UI is not hidden to avoid interference) - const lyricsContainer = document.querySelector('[class^="_lyricsContainer"]'); - if (lyricsContainer && !isHidden) { - if (settings.lyricsGlowEnabled) { - lyricsContainer.classList.remove("lyrics-glow-disabled"); - lyricsGlowStyleTag.css = lyricsGlow; - } else { - lyricsContainer.classList.add("lyrics-glow-disabled"); - lyricsGlowStyleTag.remove(); + // Update lyrics glow based on setting (only if UI is not hidden to avoid interference) + const lyricsContainer = document.querySelector('[class^="_lyricsContainer"]'); + if (lyricsContainer && !isHidden) { + if (settings.lyricsGlowEnabled) { + lyricsContainer.classList.remove('lyrics-glow-disabled'); + lyricsGlowStyleTag.css = lyricsGlow; + } else { + lyricsContainer.classList.add('lyrics-glow-disabled'); + lyricsGlowStyleTag.remove(); + } } - } }; // Function to apply spinning background to the entire app (cover everywhere) const applyGlobalSpinningBackground = (coverArtImageSrc: string): void => { - const appContainer = document.querySelector( - '[data-test="main"]', - ) as HTMLElement; - if (!settings.spinningCoverEverywhere) { - // Remove StyleTag and all background elements - if (globalSpinningBgStyleTag) { - globalSpinningBgStyleTag.remove(); - globalSpinningBgStyleTag = null; + const appContainer = document.querySelector('[data-test="main"]') as HTMLElement; + if (!settings.spinningCoverEverywhere) { + // Remove StyleTag and all background elements + if (globalSpinningBgStyleTag) { + globalSpinningBgStyleTag.remove(); + globalSpinningBgStyleTag = null; + } + if (appContainer) { + appContainer.querySelectorAll('.global-spinning-image, .global-spinning-black-bg').forEach(el => el.remove()); + } + return; } - if (appContainer) { - appContainer - .querySelectorAll(".global-spinning-image, .global-spinning-black-bg") - .forEach((el) => el.remove()); + + // Add StyleTag if not present (Don't know if this is needed.. But it's here) + if (!globalSpinningBgStyleTag) { + globalSpinningBgStyleTag = new StyleTag("RadiantLyrics-global-spinning-bg", unloads, coverEverywhereCss); } - return; - } - // Add StyleTag if not present (Don't know if this is needed.. But it's here) - if (!globalSpinningBgStyleTag) { - globalSpinningBgStyleTag = new StyleTag( - "RadiantLyrics-global-spinning-bg", - unloads, - coverEverywhereCss, - ); - } + if (!appContainer) return; - if (!appContainer) return; + // Remove any existing background elements + appContainer.querySelectorAll('.global-spinning-image, .global-spinning-black-bg').forEach(el => el.remove()); - // Remove any existing background elements - appContainer - .querySelectorAll(".global-spinning-image, .global-spinning-black-bg") - .forEach((el) => el.remove()); + // Add black background (to obscure image edges) + const blackBg = document.createElement('div'); + blackBg.className = 'global-spinning-black-bg'; + appContainer.appendChild(blackBg); - // Add black background (to obscure image edges) - const blackBg = document.createElement("div"); - blackBg.className = "global-spinning-black-bg"; - appContainer.appendChild(blackBg); - - // Add one image for background (spinning or static based on performance mode) - const img = document.createElement("img"); - img.src = coverArtImageSrc; - img.className = "global-spinning-image"; - img.style.animationDelay = "0s"; - img.style.filter = `blur(${settings.backgroundBlur}px) brightness(${settings.backgroundBrightness / 100}) contrast(${settings.backgroundContrast}%)`; - - // Apply or remove animation based on performance mode - if (settings.performanceMode) { - img.style.animation = "none"; - img.classList.add("performance-mode-static"); - } else { - img.style.animation = `spinGlobal ${settings.spinSpeed}s linear infinite`; - img.classList.remove("performance-mode-static"); - } - - appContainer.appendChild(img); + // Add one image for background (spinning or static based on performance mode) + const img = document.createElement('img'); + img.src = coverArtImageSrc; + img.className = 'global-spinning-image'; + img.style.animationDelay = '0s'; + img.style.filter = `blur(${settings.backgroundBlur}px) brightness(${settings.backgroundBrightness / 100}) contrast(${settings.backgroundContrast}%)`; + + // Apply or remove animation based on performance mode + if (settings.performanceMode) { + img.style.animation = 'none'; + img.classList.add('performance-mode-static'); + } else { + img.style.animation = `spinGlobal ${settings.spinSpeed}s linear infinite`; + img.classList.remove('performance-mode-static'); + } + + appContainer.appendChild(img); }; // Function to clean up global spinning background -const cleanUpGlobalSpinningBackground = function (): void { - const globalImages = document.getElementsByClassName("global-spinning-image"); - Array.from(globalImages).forEach((element) => { - element.remove(); - }); - // Also remove the overlay - const overlay = document.querySelector(".global-spinning-overlay"); - if (overlay && overlay.parentNode) { - overlay.parentNode.removeChild(overlay); - } - // Also remove the black bg - const blackBg = document.querySelector(".global-spinning-black-bg"); - if (blackBg && blackBg.parentNode) { - blackBg.parentNode.removeChild(blackBg); - } +const cleanUpGlobalSpinningBackground = function(): void { + const globalImages = document.getElementsByClassName("global-spinning-image"); + Array.from(globalImages).forEach((element) => { + element.remove(); + }); + // Also remove the overlay + const overlay = document.querySelector('.global-spinning-overlay'); + if (overlay && overlay.parentNode) { + overlay.parentNode.removeChild(overlay); + } + // Also remove the black bg + const blackBg = document.querySelector('.global-spinning-black-bg'); + if (blackBg && blackBg.parentNode) { + blackBg.parentNode.removeChild(blackBg); + } }; // Function to update global background when settings change -const updateRadiantLyricsGlobalBackground = function (): void { - if (settings.spinningCoverEverywhere) { - // Get current cover art and apply global background - updateCoverArtBackground(); - } else { - cleanUpGlobalSpinningBackground(); - } +const updateRadiantLyricsGlobalBackground = function(): void { + if (settings.spinningCoverEverywhere) { + // Get current cover art and apply global background + updateCoverArtBackground(); + } else { + cleanUpGlobalSpinningBackground(); + } }; // Function to update Now Playing background when settings change -const updateRadiantLyricsNowPlayingBackground = function (): void { - const nowPlayingBackgroundImages = document.querySelectorAll( - ".now-playing-background-image", - ); - nowPlayingBackgroundImages.forEach((img: Element) => { - const imgElement = img as HTMLElement; - - // Default values when settings don't affect Now Playing - const defaultBlur = 80; - const defaultBrightness = 40; - const defaultContrast = 120; - const defaultSpinSpeed = 45; - - if (settings.settingsAffectNowPlaying) { - // Use settings values - if (settings.performanceMode) { - imgElement.style.animation = "none"; - imgElement.classList.add("performance-mode-static"); - } else { - imgElement.style.animation = `spin ${settings.spinSpeed}s linear infinite`; - imgElement.classList.remove("performance-mode-static"); - } - imgElement.style.filter = `blur(${settings.backgroundBlur}px) brightness(${settings.backgroundBrightness / 100}) contrast(${settings.backgroundContrast}%)`; - } else { - // Reset to default values - if (settings.performanceMode) { - imgElement.style.animation = "none"; - imgElement.classList.add("performance-mode-static"); - } else { - imgElement.style.animation = `spin ${defaultSpinSpeed}s linear infinite`; - imgElement.classList.remove("performance-mode-static"); - } - imgElement.style.filter = `blur(${defaultBlur}px) brightness(${defaultBrightness / 100}) contrast(${defaultContrast}%)`; - } - }); +const updateRadiantLyricsNowPlayingBackground = function(): void { + const nowPlayingBackgroundImages = document.querySelectorAll('.now-playing-background-image'); + nowPlayingBackgroundImages.forEach((img: Element) => { + const imgElement = img as HTMLElement; + + // Default values when settings don't affect Now Playing + const defaultBlur = 80; + const defaultBrightness = 40; + const defaultContrast = 120; + const defaultSpinSpeed = 45; + + if (settings.settingsAffectNowPlaying) { + // Use settings values + if (settings.performanceMode) { + imgElement.style.animation = 'none'; + imgElement.classList.add('performance-mode-static'); + } else { + imgElement.style.animation = `spin ${settings.spinSpeed}s linear infinite`; + imgElement.classList.remove('performance-mode-static'); + } + imgElement.style.filter = `blur(${settings.backgroundBlur}px) brightness(${settings.backgroundBrightness / 100}) contrast(${settings.backgroundContrast}%)`; + } else { + // Reset to default values + if (settings.performanceMode) { + imgElement.style.animation = 'none'; + imgElement.classList.add('performance-mode-static'); + } else { + imgElement.style.animation = `spin ${defaultSpinSpeed}s linear infinite`; + imgElement.classList.remove('performance-mode-static'); + } + imgElement.style.filter = `blur(${defaultBlur}px) brightness(${defaultBrightness / 100}) contrast(${defaultContrast}%)`; + } + }); }; // Make these functions available globally so Settings can call them (window as any).updateRadiantLyricsStyles = updateRadiantLyricsStyles; -(window as any).updateRadiantLyricsGlobalBackground = - updateRadiantLyricsGlobalBackground; -(window as any).updateRadiantLyricsNowPlayingBackground = - updateRadiantLyricsNowPlayingBackground; +(window as any).updateRadiantLyricsGlobalBackground = updateRadiantLyricsGlobalBackground; +(window as any).updateRadiantLyricsNowPlayingBackground = updateRadiantLyricsNowPlayingBackground; -const toggleRadiantLyrics = function (): void { - const nowPlayingContainer = document.querySelector( - '[class*="_nowPlayingContainer"]', - ) as HTMLElement; - - if (isHidden) { - // currently hidden, so we're about to show UI - // Add a class to immediately hide the unhide button with CSS - const unhideButton = document.querySelector( - ".unhide-ui-button", - ) as HTMLElement; - if (unhideButton) { - unhideButton.classList.add("hide-immediately"); // actually uses fade out but.. still +const toggleRadiantLyrics = function(): void { + const nowPlayingContainer = document.querySelector('[class*="_nowPlayingContainer"]') as HTMLElement; + + if (isHidden) { + // currently hidden, so we're about to show UI + // Add a class to immediately hide the unhide button with CSS + const unhideButton = document.querySelector('.unhide-ui-button') as HTMLElement; + if (unhideButton) { + unhideButton.classList.add('hide-immediately'); // actually uses fade out but.. still + } + + // Toggle the state + isHidden = !isHidden; + + // Don't remove StyleTags completely, just remove the class to show elements again + if (nowPlayingContainer) { + nowPlayingContainer.classList.remove('radiant-lyrics-ui-hidden'); + } + document.body.classList.remove('radiant-lyrics-ui-hidden'); + // Remove styles after animation completes (I think this is needed.. but not sure) + setTimeout(() => { + if (!isHidden) { + lyricsStyleTag.remove(); + baseStyleTag.remove(); + playerBarStyleTag.remove(); + } + }, 500); // Wait for fade animation to complete + + // Update button states normally + updateButtonStates(); + } else { + // We're currently visible, so we're about to hide UI + // Toggle the state first + isHidden = !isHidden; + + // Update button states immediately to start Hide UI button fade-out + updateButtonStates(); + + // Delay adding the CSS class to allow Hide UI button to fade out first - (Had issues with the fade out.. so I removed it) + setTimeout(() => { + // Apply clean view styles + updateRadiantLyricsStyles(); + // Add a class to the container to trigger CSS animations + if (nowPlayingContainer) { + nowPlayingContainer.classList.add('radiant-lyrics-ui-hidden'); + } + document.body.classList.add('radiant-lyrics-ui-hidden'); + }, 50); // Small delay to let Hide UI button start its fade transition - (Had issues with the fade out.. so I removed it) } - - // Toggle the state - isHidden = !isHidden; - - // Don't remove StyleTags completely, just remove the class to show elements again - if (nowPlayingContainer) { - nowPlayingContainer.classList.remove("radiant-lyrics-ui-hidden"); - } - document.body.classList.remove("radiant-lyrics-ui-hidden"); - // Remove styles after animation completes (I think this is needed.. but not sure) - setTimeout(() => { - if (!isHidden) { - lyricsStyleTag.remove(); - baseStyleTag.remove(); - playerBarStyleTag.remove(); - } - }, 500); // Wait for fade animation to complete - - // Update button states normally - updateButtonStates(); - } else { - // We're currently visible, so we're about to hide UI - // Toggle the state first - isHidden = !isHidden; - - // Update button states immediately to start Hide UI button fade-out - updateButtonStates(); - - // Delay adding the CSS class to allow Hide UI button to fade out first - (Had issues with the fade out.. so I removed it) - setTimeout(() => { - // Apply clean view styles - updateRadiantLyricsStyles(); - // Add a class to the container to trigger CSS animations - if (nowPlayingContainer) { - nowPlayingContainer.classList.add("radiant-lyrics-ui-hidden"); - } - document.body.classList.add("radiant-lyrics-ui-hidden"); - }, 50); // Small delay to let Hide UI button start its fade transition - (Had issues with the fade out.. so I removed it) - } }; -const createHideUIButton = function (): void { - setTimeout(() => { - // Only create button if Hide UI is enabled in settings - if (!settings.hideUIEnabled) return; - - // Look for the fullscreen button's parent container - const fullscreenButton = document.querySelector( - '[data-test="request-fullscreen"]', - ); - if (!fullscreenButton || !fullscreenButton.parentElement) { - // Retry if fullscreen button not found yet - setTimeout(() => createHideUIButton(), 1000); - return; - } - - // Check if our button already exists - if (document.querySelector(".hide-ui-button")) return; - - const buttonContainer = fullscreenButton.parentElement; - - // Create our hide UI button - const hideUIButton = document.createElement("button"); - hideUIButton.className = "hide-ui-button"; - hideUIButton.setAttribute("aria-label", "Hide UI"); - hideUIButton.setAttribute("title", "Hide UI"); - hideUIButton.textContent = "Hide UI"; - - // Style to match Tidal's buttons - hideUIButton.style.backgroundColor = "var(--wave-color-solid-accent-fill)"; - hideUIButton.style.color = "black"; - hideUIButton.style.border = "none"; - hideUIButton.style.borderRadius = "12px"; - hideUIButton.style.height = "40px"; - hideUIButton.style.padding = "0 12px"; - hideUIButton.style.marginLeft = "8px"; - hideUIButton.style.cursor = "pointer"; - hideUIButton.style.display = "flex"; - hideUIButton.style.alignItems = "center"; - hideUIButton.style.justifyContent = "center"; - hideUIButton.style.fontSize = "12px"; - hideUIButton.style.fontWeight = "600"; - hideUIButton.style.whiteSpace = "nowrap"; - hideUIButton.style.transition = - "opacity 0.5s ease-in-out, visibility 0.5s ease-in-out, background-color 0.2s ease-in-out"; - hideUIButton.style.opacity = "0"; - hideUIButton.style.visibility = "hidden"; - hideUIButton.style.pointerEvents = "none"; - - // Add hover effect - hideUIButton.addEventListener("mouseenter", () => { - hideUIButton.style.backgroundColor = "lightgray"; - }); - - hideUIButton.addEventListener("mouseleave", () => { - hideUIButton.style.backgroundColor = - "var(--wave-color-solid-accent-fill)"; - }); - - hideUIButton.onclick = toggleRadiantLyrics; - - // Insert after the fullscreen button - buttonContainer.insertBefore(hideUIButton, fullscreenButton.nextSibling); - - // Fade in the button after a small delay +const createHideUIButton = function(): void { setTimeout(() => { - if (settings.hideUIEnabled && !isHidden) { - hideUIButton.style.opacity = "1"; - hideUIButton.style.visibility = "visible"; - hideUIButton.style.pointerEvents = "auto"; - } - }, 100); // Small delay to ensure DOM insertion is complete + // Only create button if Hide UI is enabled in settings + if (!settings.hideUIEnabled) return; + + // Look for the fullscreen button's parent container + const fullscreenButton = document.querySelector('[data-test="request-fullscreen"]'); + if (!fullscreenButton || !fullscreenButton.parentElement) { + // Retry if fullscreen button not found yet + setTimeout(() => createHideUIButton(), 1000); + return; + } - //trace.msg.log("Hide UI button added next to fullscreen button"); - }, 1000); + // Check if our button already exists + if (document.querySelector('.hide-ui-button')) return; + + const buttonContainer = fullscreenButton.parentElement; + + // Create our hide UI button + const hideUIButton = document.createElement("button"); + hideUIButton.className = 'hide-ui-button'; + hideUIButton.setAttribute('aria-label', 'Hide UI'); + hideUIButton.setAttribute('title', 'Hide UI'); + hideUIButton.textContent = 'Hide UI'; + + // Style to match Tidal's buttons + hideUIButton.style.backgroundColor = 'var(--wave-color-solid-accent-fill)'; + hideUIButton.style.color = 'black'; + hideUIButton.style.border = 'none'; + hideUIButton.style.borderRadius = '12px'; + hideUIButton.style.height = '40px'; + hideUIButton.style.padding = '0 12px'; + hideUIButton.style.marginLeft = '8px'; + hideUIButton.style.cursor = 'pointer'; + hideUIButton.style.display = 'flex'; + hideUIButton.style.alignItems = 'center'; + hideUIButton.style.justifyContent = 'center'; + hideUIButton.style.fontSize = '12px'; + hideUIButton.style.fontWeight = '600'; + hideUIButton.style.whiteSpace = 'nowrap'; + hideUIButton.style.transition = 'opacity 0.5s ease-in-out, visibility 0.5s ease-in-out, background-color 0.2s ease-in-out'; + hideUIButton.style.opacity = '0'; + hideUIButton.style.visibility = 'hidden'; + hideUIButton.style.pointerEvents = 'none'; + + // Add hover effect + hideUIButton.addEventListener('mouseenter', () => { + hideUIButton.style.backgroundColor = 'lightgray'; + }); + + hideUIButton.addEventListener('mouseleave', () => { + hideUIButton.style.backgroundColor = 'var(--wave-color-solid-accent-fill)'; + }); + + hideUIButton.onclick = toggleRadiantLyrics; + + // Insert after the fullscreen button + buttonContainer.insertBefore(hideUIButton, fullscreenButton.nextSibling); + + // Fade in the button after a small delay + setTimeout(() => { + if (settings.hideUIEnabled && !isHidden) { + hideUIButton.style.opacity = '1'; + hideUIButton.style.visibility = 'visible'; + hideUIButton.style.pointerEvents = 'auto'; + } + }, 100); // Small delay to ensure DOM insertion is complete + + //trace.msg.log("Hide UI button added next to fullscreen button"); + }, 1000); }; -const createUnhideUIButton = function (): void { - setTimeout(() => { - // Only create button if Hide UI is enabled in settings - if (!settings.hideUIEnabled) return; +const createUnhideUIButton = function(): void { + setTimeout(() => { + // Only create button if Hide UI is enabled in settings + if (!settings.hideUIEnabled) return; + + // Check if our button already exists + if (document.querySelector('.unhide-ui-button')) return; - // Check if our button already exists - if (document.querySelector(".unhide-ui-button")) return; + // Find the Now Playing container to place the button within it + const nowPlayingContainer = document.querySelector('[class*="_nowPlayingContainer"]') as HTMLElement; + if (!nowPlayingContainer) { + // Retry if container not found yet + setTimeout(() => createUnhideUIButton(), 1000); + return; + } - // Find the Now Playing container to place the button within it - const nowPlayingContainer = document.querySelector( - '[class*="_nowPlayingContainer"]', - ) as HTMLElement; - if (!nowPlayingContainer) { - // Retry if container not found yet - setTimeout(() => createUnhideUIButton(), 1000); - return; - } - - // Create unhide UI button - const unhideUIButton = document.createElement("button"); - unhideUIButton.className = "unhide-ui-button"; - unhideUIButton.setAttribute("aria-label", "Unhide UI"); - unhideUIButton.setAttribute("title", "Unhide UI"); - unhideUIButton.textContent = "Unhide"; - - // Style for top-right positioning within the Now Playing container (is a pain) - unhideUIButton.style.cssText = ` + // Create unhide UI button + const unhideUIButton = document.createElement("button"); + unhideUIButton.className = 'unhide-ui-button'; + unhideUIButton.setAttribute('aria-label', 'Unhide UI'); + unhideUIButton.setAttribute('title', 'Unhide UI'); + unhideUIButton.textContent = 'Unhide'; + + // Style for top-right positioning within the Now Playing container (is a pain) + unhideUIButton.style.cssText = ` position: absolute; top: 10px; right: 10px; @@ -437,255 +411,233 @@ const createUnhideUIButton = function (): void { visibility: hidden; pointer-events: none; `; + + // Add hover effect with auto-fade handling + unhideUIButton.addEventListener('mouseenter', () => { + unhideUIButton.style.backgroundColor = 'rgba(255, 255, 255, 0.3)'; + unhideUIButton.style.transform = 'scale(1.05)'; + unhideUIButton.classList.remove('auto-faded'); + }); + + unhideUIButton.addEventListener('mouseleave', () => { + unhideUIButton.style.backgroundColor = 'rgba(255, 255, 255, 0.2)'; + unhideUIButton.style.transform = 'scale(1)'; + // Re-add auto-fade after a short delay if still in hidden mode + window.setTimeout(() => { + if (isHidden && !unhideUIButton.matches(':hover')) { + unhideUIButton.classList.add('auto-faded'); + } + }, 2000); + }); + + unhideUIButton.onclick = toggleRadiantLyrics; - // Add hover effect with auto-fade handling - unhideUIButton.addEventListener("mouseenter", () => { - unhideUIButton.style.backgroundColor = "rgba(255, 255, 255, 0.3)"; - unhideUIButton.style.transform = "scale(1.05)"; - unhideUIButton.classList.remove("auto-faded"); - }); - - unhideUIButton.addEventListener("mouseleave", () => { - unhideUIButton.style.backgroundColor = "rgba(255, 255, 255, 0.2)"; - unhideUIButton.style.transform = "scale(1)"; - // Re-add auto-fade after a short delay if still in hidden mode - window.setTimeout(() => { - if (isHidden && !unhideUIButton.matches(":hover")) { - unhideUIButton.classList.add("auto-faded"); - } - }, 2000); - }); - - unhideUIButton.onclick = toggleRadiantLyrics; - - // Append to the Now Playing container so it only shows there - nowPlayingContainer.appendChild(unhideUIButton); - - //trace.msg.log("Unhide UI button added to top-right of Now Playing container"); - updateButtonStates(); - }, 1500); // Slight delay after hide button + // Append to the Now Playing container so it only shows there + nowPlayingContainer.appendChild(unhideUIButton); + + //trace.msg.log("Unhide UI button added to top-right of Now Playing container"); + updateButtonStates(); + }, 1500); // Slight delay after hide button }; // Function to observe track changes using track ID const observeTrackChanges = (): void => { - let lastTrackId: string | null = null; - const interval = setInterval(() => { + let lastTrackId: string | null = null; + const interval = setInterval(() => { + const currentTrackId = PlayState.playbackContext?.actualProductId; + if (currentTrackId && currentTrackId !== lastTrackId) { + //trace.msg.log(`Track changed: ${lastTrackId} -> ${currentTrackId}`); + lastTrackId = currentTrackId; + // delay for cover art to load (to prevent flickering) + setTimeout(() => { + updateCoverArtBackground(); + }, 150); + } + }, 150); // Check every 150ms for better responsiveness + + unloads.add(() => clearInterval(interval)); + + // Initial background application (if a track is already loaded) const currentTrackId = PlayState.playbackContext?.actualProductId; - if (currentTrackId && currentTrackId !== lastTrackId) { - //trace.msg.log(`Track changed: ${lastTrackId} -> ${currentTrackId}`); - lastTrackId = currentTrackId; - // delay for cover art to load (to prevent flickering) - setTimeout(() => { - updateCoverArtBackground(); - }, 150); + if (currentTrackId) { + lastTrackId = currentTrackId; + // Reduced delay for initial load + setTimeout(() => { + updateCoverArtBackground(); + }, 300); } - }, 150); // Check every 150ms for better responsiveness - - unloads.add(() => clearInterval(interval)); - - // Initial background application (if a track is already loaded) - const currentTrackId = PlayState.playbackContext?.actualProductId; - if (currentTrackId) { - lastTrackId = currentTrackId; - // Reduced delay for initial load - setTimeout(() => { - updateCoverArtBackground(); - }, 300); - } }; // Button observer using polling (instead of Stupid Bloated MutationObserver) function observeForButtons(): void { - const buttonCheckInterval = setInterval(() => { - // Only observe for buttons if Hide UI is enabled - if (!settings.hideUIEnabled) return; - - const fullscreenButton = document.querySelector( - '[data-test="request-fullscreen"]', - ); - if (fullscreenButton && !document.querySelector(".hide-ui-button")) { - createHideUIButton(); - } - - // Create unhide button if it doesn't exist - if (!document.querySelector(".unhide-ui-button")) { - createUnhideUIButton(); - } - - // Fix unhide button visibility if UI is hidden but button isn't showing - if (isHidden) { - const unhideButton = document.querySelector( - ".unhide-ui-button", - ) as HTMLElement; - if ( - unhideButton && - (unhideButton.style.display === "none" || - unhideButton.style.opacity === "0") - ) { - // Force update button states to fix visibility - updateButtonStates(); - } - } - }, 500); // Check every 500ms (much more efficient than MutationObserver) - - unloads.add(() => clearInterval(buttonCheckInterval)); + const buttonCheckInterval = setInterval(() => { + // Only observe for buttons if Hide UI is enabled + if (!settings.hideUIEnabled) return; + + const fullscreenButton = document.querySelector('[data-test="request-fullscreen"]'); + if (fullscreenButton && !document.querySelector('.hide-ui-button')) { + createHideUIButton(); + } + + // Create unhide button if it doesn't exist + if (!document.querySelector('.unhide-ui-button')) { + createUnhideUIButton(); + } + + // Fix unhide button visibility if UI is hidden but button isn't showing + if (isHidden) { + const unhideButton = document.querySelector('.unhide-ui-button') as HTMLElement; + if (unhideButton && (unhideButton.style.display === 'none' || unhideButton.style.opacity === '0')) { + // Force update button states to fix visibility + updateButtonStates(); + } + } + }, 500); // Check every 500ms (much more efficient than MutationObserver) + + unloads.add(() => clearInterval(buttonCheckInterval)); } -// Also observe for lyrics container changes to apply the setting +// Also observe for lyrics container changes to apply the setting function observeLyricsContainer(): void { - const observer = new MutationObserver(() => { - const lyricsContainer = document.querySelector( - '[class^="_lyricsContainer"]', - ); - if (lyricsContainer) { - if (settings.lyricsGlowEnabled) { - lyricsContainer.classList.remove("lyrics-glow-disabled"); - } else { - lyricsContainer.classList.add("lyrics-glow-disabled"); - } - } - }); - - observer.observe(document.body, { - childList: true, - subtree: true, - }); - - unloads.add(() => observer.disconnect()); + const observer = new MutationObserver(() => { + const lyricsContainer = document.querySelector('[class^="_lyricsContainer"]'); + if (lyricsContainer) { + if (settings.lyricsGlowEnabled) { + lyricsContainer.classList.remove('lyrics-glow-disabled'); + } else { + lyricsContainer.classList.add('lyrics-glow-disabled'); + } + } + }); + + observer.observe(document.body, { + childList: true, + subtree: true + }); + + unloads.add(() => observer.disconnect()); } const updateCoverArtBackground = function (method: number = 0): void { - if (method === 1) { - setTimeout(() => { - updateCoverArtBackground(); - return; - }, 2000); - } + if (method === 1) { + setTimeout(() => { + updateCoverArtBackground(); + return; + }, 2000); + } - let coverArtImageElement = document.querySelector( - 'figure[class*="_albumImage"] > div > div > div > img', - ) as HTMLImageElement; - let coverArtImageSrc: string | null = null; + let coverArtImageElement = document.querySelector('figure[class*="_albumImage"] > div > div > div > img') as HTMLImageElement; + let coverArtImageSrc: string | null = null; - if (coverArtImageElement) { - coverArtImageSrc = coverArtImageElement.src; - // Set res to 1280x1280 - coverArtImageSrc = coverArtImageSrc.replace(/\d+x\d+/, "1280x1280"); - coverArtImageElement.src = coverArtImageSrc; - } else { - const videoElement = document.querySelector( - 'figure[class*="_albumImage"] > div > div > div > video', - ) as HTMLVideoElement; - if (videoElement) { - coverArtImageSrc = videoElement.getAttribute("poster"); - if (coverArtImageSrc) { + if (coverArtImageElement) { + coverArtImageSrc = coverArtImageElement.src; // Set res to 1280x1280 - coverArtImageSrc = coverArtImageSrc.replace(/\d+x\d+/, "1280x1280"); - } + coverArtImageSrc = coverArtImageSrc.replace(/\d+x\d+/, '1280x1280'); + coverArtImageElement.src = coverArtImageSrc; } else { - cleanUpDynamicArt(); - return; - } - } - - // Update backgrounds when we have a valid cover art source - if (coverArtImageSrc) { - // Apply global spinning background if enabled - if (settings.spinningCoverEverywhere) { - applyGlobalSpinningBackground(coverArtImageSrc); + const videoElement = document.querySelector('figure[class*="_albumImage"] > div > div > div > video') as HTMLVideoElement; + if (videoElement) { + coverArtImageSrc = videoElement.getAttribute("poster"); + if (coverArtImageSrc) { + // Set res to 1280x1280 + coverArtImageSrc = coverArtImageSrc.replace(/\d+x\d+/, '1280x1280'); + } + } else { + cleanUpDynamicArt(); + return; + } } - // Apply spinning CoverArt background to the Now Playing container - const nowPlayingContainerElement = document.querySelector( - '[class*="_nowPlayingContainer"]', - ) as HTMLElement; - if (nowPlayingContainerElement) { - // Remove existing background images if they exist - const existingBackgroundImages = - nowPlayingContainerElement.querySelectorAll( - ".now-playing-background-image, .now-playing-black-bg", - ); - existingBackgroundImages.forEach((img) => img.remove()); + // Update backgrounds when we have a valid cover art source + if (coverArtImageSrc) { + // Apply global spinning background if enabled + if (settings.spinningCoverEverywhere) { + applyGlobalSpinningBackground(coverArtImageSrc); + } + + // Apply spinning CoverArt background to the Now Playing container + const nowPlayingContainerElement = document.querySelector('[class*="_nowPlayingContainer"]') as HTMLElement; + if (nowPlayingContainerElement) { + // Remove existing background images if they exist + const existingBackgroundImages = nowPlayingContainerElement.querySelectorAll('.now-playing-background-image, .now-playing-black-bg'); + existingBackgroundImages.forEach(img => img.remove()); - // Add black background layer (to obscure image edges) - const blackBg = document.createElement("div"); - blackBg.className = "now-playing-black-bg"; - blackBg.style.position = "absolute"; - blackBg.style.left = "0"; - blackBg.style.top = "0"; - blackBg.style.width = "100%"; - blackBg.style.height = "100%"; - blackBg.style.background = "#000"; - blackBg.style.zIndex = "-2"; - blackBg.style.pointerEvents = "none"; - nowPlayingContainerElement.appendChild(blackBg); + // Add black background layer (to obscure image edges) + const blackBg = document.createElement('div'); + blackBg.className = 'now-playing-black-bg'; + blackBg.style.position = 'absolute'; + blackBg.style.left = '0'; + blackBg.style.top = '0'; + blackBg.style.width = '100%'; + blackBg.style.height = '100%'; + blackBg.style.background = '#000'; + blackBg.style.zIndex = '-2'; + blackBg.style.pointerEvents = 'none'; + nowPlayingContainerElement.appendChild(blackBg); - // Create and append single background layer (the cover art) - const backgroundImage = document.createElement("img"); - backgroundImage.src = coverArtImageSrc; - backgroundImage.className = "now-playing-background-image"; - backgroundImage.style.position = "absolute"; - backgroundImage.style.left = "50%"; - backgroundImage.style.top = "50%"; - backgroundImage.style.transform = "translate(-50%, -50%)"; - backgroundImage.style.width = "90vw"; - backgroundImage.style.height = "90vh"; - backgroundImage.style.objectFit = "cover"; - backgroundImage.style.zIndex = "-1"; - backgroundImage.style.filter = `blur(${settings.backgroundBlur}px) brightness(${settings.backgroundBrightness / 100}) contrast(${settings.backgroundContrast}%)`; - backgroundImage.style.willChange = "transform, filter"; - backgroundImage.style.transformOrigin = "center center"; + // Create and append single background layer (the cover art) + const backgroundImage = document.createElement('img'); + backgroundImage.src = coverArtImageSrc; + backgroundImage.className = 'now-playing-background-image'; + backgroundImage.style.position = 'absolute'; + backgroundImage.style.left = '50%'; + backgroundImage.style.top = '50%'; + backgroundImage.style.transform = 'translate(-50%, -50%)'; + backgroundImage.style.width = '90vw'; + backgroundImage.style.height = '90vh'; + backgroundImage.style.objectFit = 'cover'; + backgroundImage.style.zIndex = '-1'; + backgroundImage.style.filter = `blur(${settings.backgroundBlur}px) brightness(${settings.backgroundBrightness / 100}) contrast(${settings.backgroundContrast}%)`; + backgroundImage.style.willChange = 'transform, filter'; + backgroundImage.style.transformOrigin = 'center center'; + + // Apply animation based on performance mode + if (settings.performanceMode) { + backgroundImage.style.animation = 'none'; + backgroundImage.classList.add('performance-mode-static'); + } else { + backgroundImage.style.animation = `spin ${settings.spinSpeed}s linear infinite`; + backgroundImage.classList.remove('performance-mode-static'); + } + nowPlayingContainerElement.appendChild(backgroundImage); - // Apply animation based on performance mode - if (settings.performanceMode) { - backgroundImage.style.animation = "none"; - backgroundImage.classList.add("performance-mode-static"); - } else { - backgroundImage.style.animation = `spin ${settings.spinSpeed}s linear infinite`; - backgroundImage.classList.remove("performance-mode-static"); - } - nowPlayingContainerElement.appendChild(backgroundImage); + // Create subtle gradient overlay to hide edges (Hate this approach but it's the only way I could get it to work) + const gradientOverlay = document.createElement('div'); + gradientOverlay.className = 'now-playing-gradient-overlay'; + gradientOverlay.style.position = 'absolute'; + gradientOverlay.style.left = '0'; + gradientOverlay.style.top = '0'; + gradientOverlay.style.width = '100%'; + gradientOverlay.style.height = '100%'; + gradientOverlay.style.background = 'radial-gradient(circle at center, transparent 0%, rgba(0, 0, 0, 0.3) 60%, rgba(0, 0, 0, 0.8) 90%)'; + gradientOverlay.style.zIndex = '-1'; + gradientOverlay.style.pointerEvents = 'none'; + nowPlayingContainerElement.appendChild(gradientOverlay); - // Create subtle gradient overlay to hide edges (Hate this approach but it's the only way I could get it to work) - const gradientOverlay = document.createElement("div"); - gradientOverlay.className = "now-playing-gradient-overlay"; - gradientOverlay.style.position = "absolute"; - gradientOverlay.style.left = "0"; - gradientOverlay.style.top = "0"; - gradientOverlay.style.width = "100%"; - gradientOverlay.style.height = "100%"; - gradientOverlay.style.background = - "radial-gradient(circle at center, transparent 0%, rgba(0, 0, 0, 0.3) 60%, rgba(0, 0, 0, 0.8) 90%)"; - gradientOverlay.style.zIndex = "-1"; - gradientOverlay.style.pointerEvents = "none"; - nowPlayingContainerElement.appendChild(gradientOverlay); - - // Add keyframe animation if it doesn't exist - if (!document.querySelector("#spinAnimation")) { - const styleSheet = document.createElement("style"); - styleSheet.id = "spinAnimation"; - styleSheet.textContent = ` + // Add keyframe animation if it doesn't exist + if (!document.querySelector('#spinAnimation')) { + const styleSheet = document.createElement('style'); + styleSheet.id = 'spinAnimation'; + styleSheet.textContent = ` @keyframes spin { from { transform: translate(-50%, -50%) rotate(0deg); } to { transform: translate(-50%, -50%) rotate(360deg); } } `; - document.head.appendChild(styleSheet); - } + document.head.appendChild(styleSheet); + } + } } - } }; const cleanUpDynamicArt = function (): void { - const nowPlayingBackgroundImages = document.getElementsByClassName( - "now-playing-background-image", - ); - Array.from(nowPlayingBackgroundImages).forEach((element) => { - element.remove(); - }); - - // Also clean up global spinning backgrounds - cleanUpGlobalSpinningBackground(); + const nowPlayingBackgroundImages = document.getElementsByClassName("now-playing-background-image"); + Array.from(nowPlayingBackgroundImages).forEach((element) => { + element.remove(); + }); + + // Also clean up global spinning backgrounds + cleanUpGlobalSpinningBackground(); }; // Initialize the button creation and observers @@ -696,31 +648,31 @@ updateCoverArtBackground(1); // Add cleanup to unloads unloads.add(() => { - cleanUpDynamicArt(); + cleanUpDynamicArt(); - // Clean up auto-fade timeout - if (unhideButtonAutoFadeTimeout) { - window.clearTimeout(unhideButtonAutoFadeTimeout); - unhideButtonAutoFadeTimeout = null; - } + // Clean up auto-fade timeout + if (unhideButtonAutoFadeTimeout) { + window.clearTimeout(unhideButtonAutoFadeTimeout); + unhideButtonAutoFadeTimeout = null; + } - // Clean up our custom buttons - const hideButton = document.querySelector(".hide-ui-button"); - if (hideButton && hideButton.parentNode) { - hideButton.parentNode.removeChild(hideButton); - } + // Clean up our custom buttons + const hideButton = document.querySelector('.hide-ui-button'); + if (hideButton && hideButton.parentNode) { + hideButton.parentNode.removeChild(hideButton); + } + + const unhideButton = document.querySelector('.unhide-ui-button'); + if (unhideButton && unhideButton.parentNode) { + unhideButton.parentNode.removeChild(unhideButton); + } - const unhideButton = document.querySelector(".unhide-ui-button"); - if (unhideButton && unhideButton.parentNode) { - unhideButton.parentNode.removeChild(unhideButton); - } - - // Clean up spin animations - const spinAnimationStyle = document.querySelector("#spinAnimation"); - if (spinAnimationStyle && spinAnimationStyle.parentNode) { - spinAnimationStyle.parentNode.removeChild(spinAnimationStyle); - } - - // Clean up global spinning backgrounds - cleanUpGlobalSpinningBackground(); -}); + // Clean up spin animations + const spinAnimationStyle = document.querySelector('#spinAnimation'); + if (spinAnimationStyle && spinAnimationStyle.parentNode) { + spinAnimationStyle.parentNode.removeChild(spinAnimationStyle); + } + + // Clean up global spinning backgrounds + cleanUpGlobalSpinningBackground(); +}); \ No newline at end of file