From e19eef80a2dec384ef930605eca12a2b7b792f96 Mon Sep 17 00:00:00 2001 From: meowarex Date: Mon, 9 Jun 2025 21:47:46 +1000 Subject: [PATCH] Revert "Code Cleanup (Prettier) & Removed seperated-lyrics.css" This reverts commit 242410884b02ef60f08dbfe176fc1481134234bc. --- plugins/radiant-lyrics-luna/src/index.ts | 1115 ++++++++--------- .../src/separated-lyrics.css | 133 ++ 2 files changed, 667 insertions(+), 581 deletions(-) create mode 100644 plugins/radiant-lyrics-luna/src/separated-lyrics.css diff --git a/plugins/radiant-lyrics-luna/src/index.ts b/plugins/radiant-lyrics-luna/src/index.ts index a670d2d..8c6f7ed 100644 --- a/plugins/radiant-lyrics-luna/src/index.ts +++ b/plugins/radiant-lyrics-luna/src/index.ts @@ -1,9 +1,10 @@ -import { LunaUnload, Tracer } from "@luna/core"; +import { LunaUnload, Tracer, ftch } from "@luna/core"; import { StyleTag, PlayState } from "@luna/lib"; import { settings, Settings } from "./Settings"; // Import CSS files directly using Luna's file:// syntax - Took me a while to figure out <3 import baseStyles from "file://styles.css?minify"; +import separatedLyrics from "file://separated-lyrics.css?minify"; import playerBarHidden from "file://player-bar-hidden.css?minify"; import lyricsGlow from "file://lyrics-glow.css?minify"; import coverEverywhereCss from "file://cover-everywhere.css?minify"; @@ -24,394 +25,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 +412,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 +649,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 diff --git a/plugins/radiant-lyrics-luna/src/separated-lyrics.css b/plugins/radiant-lyrics-luna/src/separated-lyrics.css new file mode 100644 index 0000000..159ca7e --- /dev/null +++ b/plugins/radiant-lyrics-luna/src/separated-lyrics.css @@ -0,0 +1,133 @@ +/* Font imports for lyrics */ +@font-face { + font-family: "AbyssFont"; + font-weight: 400; + src: url("https://excel.lexploits.top/extra/tidal/LyricsRegular.woff2") format("woff2"); +} + +@font-face { + font-family: "AbyssFont"; + font-weight: 500; + src: url("https://excel.lexploits.top/extra/tidal/LyricsMedium.woff2") format("woff2"); +} + +@font-face { + font-family: "AbyssFont"; + font-weight: 600; + src: url("https://excel.lexploits.top/extra/tidal/LyricsSemibold.woff2") format("woff2"); +} + +@font-face { + font-family: "AbyssFont"; + font-weight: 700; + src: url("https://excel.lexploits.top/extra/tidal/LyricsBold.woff2") format("woff2"); +} + +/* Tab and container visibility - only when UI is hidden */ +.radiant-lyrics-ui-hidden [class*="tabItems"] { + opacity: 0 !important; + transition: opacity 0.4s ease-in-out; +} + +/* Remove image container positioning - let it stay where it is */ + +.radiant-lyrics-ui-hidden [class*="_tabItems"]:hover { + opacity: 1 !important; +} + +.radiant-lyrics-ui-hidden [data-test="header-container"] { + opacity: 0 !important; + transition: opacity 0.4s ease-in-out; +} + +/* Keep bottom gradient visible - may be needed for lyrics scrolling */ + +/* Remove credits button repositioning - let it stay where it is */ + +/* Keep lyrics provider visible - don't hide with UI */ + +/* Sync button margin */ +[class^="_syncButton"] { + margin-bottom: 10px; +} + +/* Smooth scrolling for lyrics */ +[class^="_lyricsContainer"] { + scroll-behavior: smooth; + padding-left: 20px !important; + padding-right: 20px !important; + width: 100% !important; + max-width: 100% !important; + box-sizing: border-box !important; +} + +/* Clean view specific styles */ +[class*="_lyricsText"] > div { + padding: 0 !important; + background-color: transparent !important; + transition: none !important; + margin-left: 0 !important; + margin-right: 0 !important; + width: 100% !important; +} + +[class*="_lyricsText"] > div:hover { + background-color: transparent !important; +} + +/* Ensure lyrics don't get cut off */ +[class^="_lyricsContainer"] { + padding-bottom: 200px !important; + margin-bottom: 0 !important; +} + +/* Remove any hover effects on lyrics container */ +[class^="_lyricsContainer"] > div > div { + background-color: transparent !important; + padding-left: 0 !important; + padding-right: 0 !important; + margin-left: 0 !important; + margin-right: 0 !important; + width: 100% !important; +} + +[class^="_lyricsContainer"] > div > div:hover { + background-color: transparent !important; +} + +/* Ensure lyrics text has proper spacing */ +[class*="_lyricsText"] { + padding-left: 0 !important; + padding-right: 0 !important; + margin-left: 0 !important; + margin-right: 0 !important; + width: 100% !important; + max-width: 100% !important; + box-sizing: border-box !important; +} + +/* Lyrics text styling */ +[class^="_lyricsText"] { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + line-height: 1.6; +} + +[class^="_lyricsText"] > div > span { + transition: all 0.3s ease; +} + +[class^="_lyricsText"] > div > span[data-current="true"] { + font-weight: 600; + transform: scale(1.05); +} + +/* Enhanced lyrics visibility */ +[class^="_lyricsText"] > div { + padding: 8px 0; + border-radius: 4px; + transition: all 0.2s ease; +} + +[class^="_lyricsText"] > div:hover { + background-color: rgba(255, 255, 255, 0.05); +} \ No newline at end of file