diff --git a/plugins/colorama-lyrics-luna/src/Settings.tsx b/plugins/colorama-lyrics-luna/src/Settings.tsx index f70a1de..603f6d0 100644 --- a/plugins/colorama-lyrics-luna/src/Settings.tsx +++ b/plugins/colorama-lyrics-luna/src/Settings.tsx @@ -8,6 +8,12 @@ declare global { } } +// Define a typed onChange signature for the switch +type SwitchChangeHandler = ( + event: React.ChangeEvent | null, + checked: boolean, +) => void; + export type ColoramaMode = | "single" | "gradient-experimental" @@ -60,9 +66,12 @@ export const Settings = () => { const [activeEndpoint, setActiveEndpoint] = React.useState< "single" | "start" | "end" >("single"); - const AnySwitch = LunaSwitchSetting as unknown as React.ComponentType< - Record - >; + const AnySwitch = LunaSwitchSetting as unknown as React.ComponentType<{ + title: string; + desc?: string; + checked: boolean; + onChange: SwitchChangeHandler; + }>; // Helper for HEX normalization const normalizeToRGB = ( @@ -133,19 +142,19 @@ export const Settings = () => { if (!hexColorRegex.test(trimmed)) return; if (mode === "single") { const next = normalizeToRGB(trimmed); - settings.singleColor = next; setSingleColor(next); + settings.singleColor = next; if (updateInput) setCustomInput(next); } else if (mode === "gradient-experimental") { - const norm = normalizeToRGB(trimmed); + const next = normalizeToRGB(trimmed); if (activeEndpoint === "end") { - settings.gradientEnd = norm; - setGradientEnd(norm); + setGradientEnd(next); + settings.gradientEnd = next; } else { - settings.gradientStart = norm; - setGradientStart(norm); + setGradientStart(next); + settings.gradientStart = next; } - if (updateInput) setCustomInput(norm); + if (updateInput) setCustomInput(next); } requestApply(); }; @@ -500,21 +509,20 @@ export const Settings = () => { key={color} type="button" onClick={() => { + const next = normalizeToRGB(color); if (mode === "single") { - const next = normalizeToRGB(color); - settings.singleColor = next; setSingleColor(next); + settings.singleColor = next; } else if (mode === "gradient-experimental") { - const next = normalizeToRGB(color); if (activeEndpoint === "end") { - settings.gradientEnd = next; setGradientEnd(next); + settings.gradientEnd = next; } else { - settings.gradientStart = next; setGradientStart(next); + settings.gradientStart = next; } } - setCustomInput(normalizeToRGB(color)); + setCustomInput(next); requestApply(); }} style={{ @@ -610,8 +618,8 @@ export const Settings = () => { value={singleAlpha} onChange={(e) => { const value = Number(e.target.value); - settings.singleAlpha = value; setSingleAlpha(value); + settings.singleAlpha = value; requestApply(); }} style={{ width: "100%" }} @@ -653,8 +661,8 @@ export const Settings = () => { value={gradientStartAlpha} onChange={(e) => { const value = Number(e.target.value); - settings.gradientStartAlpha = value; setGradientStartAlpha(value); + settings.gradientStartAlpha = value; requestApply(); }} style={{ width: "100%" }} @@ -692,8 +700,8 @@ export const Settings = () => { value={gradientEndAlpha} onChange={(e) => { const value = Number(e.target.value); - settings.gradientEndAlpha = value; setGradientEndAlpha(value); + settings.gradientEndAlpha = value; requestApply(); }} style={{ width: "100%" }} @@ -727,8 +735,8 @@ export const Settings = () => { value={gradientAngle} onChange={(e) => { const value = Number(e.target.value); - settings.gradientAngle = value; setGradientAngle(value); + settings.gradientAngle = value; requestApply(); }} style={{ width: "100%" }} @@ -762,8 +770,8 @@ export const Settings = () => { value={gradientAngle} onChange={(e) => { const value = Number(e.target.value); - settings.gradientAngle = value; setGradientAngle(value); + settings.gradientAngle = value; requestApply(); }} style={{ width: "100%" }} @@ -794,7 +802,7 @@ export const Settings = () => { title="Exclude Inactive" desc="Apply color/gradient only to the currently active lyric line" checked={excludeInactive} - onChange={(_: unknown, checked: boolean) => { + onChange={(_event: React.ChangeEvent | null, checked: boolean) => { settings.excludeInactive = checked; setExcludeInactive(checked); requestApply(); diff --git a/plugins/copy-lyrics-luna/src/index.ts b/plugins/copy-lyrics-luna/src/index.ts index 04c7e52..cc4cbd5 100644 --- a/plugins/copy-lyrics-luna/src/index.ts +++ b/plugins/copy-lyrics-luna/src/index.ts @@ -41,25 +41,39 @@ const onMouseUp = (): void => { if (selection?.toString().length > 0) { const selectedSpans: HTMLSpanElement[] = []; const range = selection.getRangeAt(0); - const container = range.commonAncestorContainer; + let container: Node | null = range.commonAncestorContainer; - // If the container is NOT an element and a document, adjust it. + // Normalize container: if it's a text node, use its parent element/node + if (container && container.nodeType === Node.TEXT_NODE) { + container = (container.parentElement ?? container.parentNode) as Node | null; + } + + // If parent has data-current, treat as single-line copy case if ( - container.nodeType !== Node.ELEMENT_NODE && - container.nodeType !== Node.DOCUMENT_NODE + container && + container.nodeType === Node.ELEMENT_NODE && + (container as Element).hasAttribute("data-current") ) { - // Get the parent element if it's a text node - const parentElement = container.parentElement; - if (parentElement?.hasAttribute("data-current")) { - const text_ = selection.toString().trim(); - SetClipboard(text_); - trace.msg.log("Copied to clipboard!"); - return; - } + const text_ = selection.toString().trim(); + SetClipboard(text_); + trace.msg.log("Copied to clipboard!"); + return; + } + + // Ensure we have an Element or Document before querying + if ( + !container || + (container.nodeType !== Node.ELEMENT_NODE && + container.nodeType !== Node.DOCUMENT_NODE) + ) { + isSelecting = false; + return; } // Get all the spans inside the container. - const spans = (container as Element).getElementsByTagName("span"); + const spans = (container as Element | Document).getElementsByTagName( + "span", + ); for (const span of spans) { if (selection.containsNode(span, true)) { selectedSpans.push(span as HTMLSpanElement); diff --git a/plugins/element-hider-luna/src/index.ts b/plugins/element-hider-luna/src/index.ts index d20bff4..e021570 100644 --- a/plugins/element-hider-luna/src/index.ts +++ b/plugins/element-hider-luna/src/index.ts @@ -400,8 +400,7 @@ document.addEventListener( const eventX = event.clientX; const eventY = event.clientY; - // Prevent default immediately if we plan to handle it - event.preventDefault(); + // Allow native context menu by default; we'll show our custom menu only if needed // Wait to see if the built-in context menu appears contextMenuTimeout = window.setTimeout(() => { diff --git a/plugins/oled-theme-luna/src/index.ts b/plugins/oled-theme-luna/src/index.ts index fb8cdfa..6d6c82f 100644 --- a/plugins/oled-theme-luna/src/index.ts +++ b/plugins/oled-theme-luna/src/index.ts @@ -31,11 +31,17 @@ const getQualityColor = (audioQuality: string): string => { return QUALITY_COLORS.MAX; } else if (quality?.includes("LOSSLESS")) { return QUALITY_COLORS.HIGH; - } else { + } else if (quality?.includes("HIGH")) { + return QUALITY_COLORS.HIGH; + } else if (quality?.includes("LOW")) { return QUALITY_COLORS.LOW; } + return QUALITY_COLORS.LOW; }; +// Interval tracking for quality monitoring +let qualityMonitoringIntervalId: number | null = null; + // Function to Reset Seek Bar Color (if setting gets disabled while playing) const resetSeekBarColor = async (): Promise => { try { @@ -82,17 +88,16 @@ const applyQualityColors = async (): Promise => { // Function to monitor track changes using track ID const setupQualityMonitoring = (): void => { + if (qualityMonitoringIntervalId != null) return; let lastTrackId: string | null = null; - const interval = setInterval(() => { + qualityMonitoringIntervalId = window.setInterval(() => { if (!settings.qualityColorMatchedSeekBar) return; const currentTrackId = PlayState.playbackContext?.actualProductId; if (currentTrackId && currentTrackId !== lastTrackId) { - //trace.msg.log(`[OLED Theme] Track ID changed: ${lastTrackId} -> ${currentTrackId}`); lastTrackId = currentTrackId; applyQualityColors(); } }, 250); - unloads.add(() => clearInterval(interval)); // Initial color application (if a track is already loaded) const currentTrackId = PlayState.playbackContext?.actualProductId; @@ -102,6 +107,13 @@ const setupQualityMonitoring = (): void => { } }; +const stopQualityMonitoring = (): void => { + if (qualityMonitoringIntervalId != null) { + window.clearInterval(qualityMonitoringIntervalId); + qualityMonitoringIntervalId = null; + } +}; + // Function to apply theme styles based on current settings const applyThemeStyles = (): void => { // Choose the appropriate CSS file based on settings @@ -126,8 +138,9 @@ const applyThemeStyles = (): void => { ); setupQualityMonitoring(); } else { - // If disabling, reset the seek bar color + // If disabling, reset the seek bar color and stop monitoring resetSeekBarColor(); + stopQualityMonitoring(); } // Apply the selected theme using StyleTag @@ -145,3 +158,8 @@ window.updateOLEDThemeStyles = applyThemeStyles; // Apply the OLED theme initially applyThemeStyles(); + +// Ensure interval is cleared on unload +unloads.add(() => { + stopQualityMonitoring(); +}); diff --git a/plugins/oled-theme-luna/src/light-theme.css b/plugins/oled-theme-luna/src/light-theme.css index 630593f..38d4935 100644 --- a/plugins/oled-theme-luna/src/light-theme.css +++ b/plugins/oled-theme-luna/src/light-theme.css @@ -145,7 +145,7 @@ [data-test="current-media-imagery"] { border: 0 !important; - margin: none; + margin: 0; } [class^="_imageBorder"] { diff --git a/plugins/oled-theme-luna/src/oled-friendly.css b/plugins/oled-theme-luna/src/oled-friendly.css index 4437e77..2f95e54 100644 --- a/plugins/oled-theme-luna/src/oled-friendly.css +++ b/plugins/oled-theme-luna/src/oled-friendly.css @@ -120,7 +120,7 @@ [data-test="current-media-imagery"] { border: 0 !important; - margin: none; + margin: 0; } [class^="_imageBorder"] { diff --git a/plugins/radiant-lyrics-luna/src/Settings.tsx b/plugins/radiant-lyrics-luna/src/Settings.tsx index d5ef020..b5dbab2 100644 --- a/plugins/radiant-lyrics-luna/src/Settings.tsx +++ b/plugins/radiant-lyrics-luna/src/Settings.tsx @@ -65,7 +65,7 @@ export const Settings = () => { title="Lyrics Glow Effect" desc="Enable glowing effect for lyrics & Font Stytling Changes" checked={lyricsGlowEnabled} - onChange={(_event: unknown, checked: boolean) => { + onChange={(_: void, checked: boolean) => { setLyricsGlowEnabled((settings.lyricsGlowEnabled = checked)); // Update styles immediately when setting changes if ((window as any).updateRadiantLyricsStyles) { @@ -77,7 +77,7 @@ export const Settings = () => { title="Track Title Glow" desc="Apply glow to the track title" checked={trackTitleGlow} - onChange={(_event: unknown, checked: boolean) => { + onChange={(_: void, checked: boolean) => { setTrackTitleGlow((settings.trackTitleGlow = checked)); if ((window as any).updateRadiantLyricsStyles) { (window as any).updateRadiantLyricsStyles(); @@ -88,7 +88,7 @@ export const Settings = () => { title="Hide UI Feature" desc="Enable hide/unhide UI functionality with toggle buttons" checked={hideUIEnabled} - onChange={(_event: unknown, checked: boolean) => { + onChange={(_: void, checked: boolean) => { setHideUIEnabled((settings.hideUIEnabled = checked)); }} /> @@ -96,7 +96,7 @@ export const Settings = () => { title="Player Bar Visibility in Hide UI Mode" desc="Keep player bar visible when UI is hidden" checked={playerBarVisible} - onChange={(_event: unknown, checked: boolean) => { + onChange={(_: void, checked: boolean) => { console.log("Player Bar Visibility:", checked ? "visible" : "hidden"); setPlayerBarVisible((settings.playerBarVisible = checked)); // Update styles immediately when setting changes @@ -109,7 +109,7 @@ export const Settings = () => { title="Cover Everywhere" desc="Apply the spinning Cover Art background to the entire app, not just the Now Playing view, Heavily Inspired by Cover-Theme by @Inrixia" checked={spinningCoverEverywhere} - onChange={(_event: unknown, checked: boolean) => { + onChange={(_: void, checked: boolean) => { console.log( "Spinning Cover Everywhere:", checked ? "enabled" : "disabled", @@ -127,7 +127,7 @@ export const Settings = () => { title="Performance Mode | Experimental" desc="Performance mode: Reduces blur effects & uses smaller image sizes, to optimize GPU usage" checked={performanceMode} - onChange={(_event: unknown, checked: boolean) => { + onChange={(_: void, checked: boolean) => { console.log("Performance Mode:", checked ? "enabled" : "disabled"); setPerformanceMode((settings.performanceMode = checked)); // Update background animations immediately when setting changes @@ -143,7 +143,7 @@ export const Settings = () => { title="Background Cover Spin" // Cheers @Max/n0201 for the idea <3 desc="Enable the spinning cover art background animation" checked={spinningArtEnabled} - onChange={(_event: unknown, checked: boolean) => { + onChange={(_: void, checked: boolean) => { console.log( "Background Cover Spin:", checked ? "enabled" : "disabled", @@ -262,7 +262,7 @@ export const Settings = () => { title="Settings Affect Now Playing" desc="Apply background settings to Now Playing view" checked={settingsAffectNowPlaying} - onChange={(_event: unknown, checked: boolean) => { + onChange={(_: void, checked: boolean) => { console.log( "Settings Affect Now Playing:", checked ? "enabled" : "disabled", diff --git a/plugins/radiant-lyrics-luna/src/index.ts b/plugins/radiant-lyrics-luna/src/index.ts index 883abed..cf86246 100644 --- a/plugins/radiant-lyrics-luna/src/index.ts +++ b/plugins/radiant-lyrics-luna/src/index.ts @@ -38,30 +38,30 @@ const updateRadiantLyricsTextGlow = function (): void { // 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 + // Apply only base styles (button hiding) and optional player bar hiding baseStyleTag.css = baseStyles; - - // Apply player bar styles based on setting if (!settings.playerBarVisible) { playerBarStyleTag.css = playerBarHidden; } else { playerBarStyleTag.remove(); } + // Ensure lyrics glow styles are not applied when hidden + lyricsGlowStyleTag.remove(); + return; } - // Update lyrics glow based on setting (only if UI is not hidden to avoid interference) + // Update lyrics glow based on setting (only when UI is visible) const lyricsContainer = document.querySelector('[class^="_lyricsContainer"]'); - if (lyricsContainer && !isHidden) { + if (lyricsContainer) { if (settings.lyricsGlowEnabled) { - lyricsContainer.classList.remove("lyrics-glow-disabled"); + (lyricsContainer as HTMLElement).classList.remove("lyrics-glow-disabled"); lyricsGlowStyleTag.css = lyricsGlow; updateRadiantLyricsTextGlow(); } else { - lyricsContainer.classList.add("lyrics-glow-disabled"); + (lyricsContainer as HTMLElement).classList.add("lyrics-glow-disabled"); lyricsGlowStyleTag.remove(); } - } else if (!isHidden) { + } else { observePromise(unloads, '[class^="_lyricsContainer"]') .then((el) => { if (!el) return; @@ -95,6 +95,16 @@ const updateRadiantLyricsStyles = function (): void { var isHidden = false; let unhideButtonAutoFadeTimeout: number | null = null; +// Helper to safely create a one-off timeout that clears previous if any +const safelySetAutoFadeTimeout = ( + existingId: number | null, + fn: () => void, + delay: number, +): number => { + if (existingId != null) window.clearTimeout(existingId); + return window.setTimeout(fn, delay); +}; + const updateButtonStates = function (): void { const hideButton = document.querySelector(".hide-ui-button") as HTMLElement; const unhideButton = document.querySelector( @@ -120,7 +130,7 @@ const updateButtonStates = function (): void { } if (unhideButton) { // Clear any existing auto-fade timeout - if (unhideButtonAutoFadeTimeout) { + if (unhideButtonAutoFadeTimeout != null) { window.clearTimeout(unhideButtonAutoFadeTimeout); unhideButtonAutoFadeTimeout = null; } @@ -137,11 +147,15 @@ const updateButtonStates = function (): void { 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); + unhideButtonAutoFadeTimeout = safelySetAutoFadeTimeout( + unhideButtonAutoFadeTimeout, + () => { + if (isHidden && unhideButton && !unhideButton.matches(":hover")) { + unhideButton.classList.add("auto-faded"); + } + }, + 2000, + ); }, 50); } else { // Smooth fade out for Unhide UI button @@ -771,7 +785,7 @@ unloads.add(() => { cleanUpDynamicArt(); // Clean up auto-fade timeout - if (unhideButtonAutoFadeTimeout) { + if (unhideButtonAutoFadeTimeout != null) { window.clearTimeout(unhideButtonAutoFadeTimeout); unhideButtonAutoFadeTimeout = null; } diff --git a/plugins/radiant-lyrics-luna/src/lyrics-glow.css b/plugins/radiant-lyrics-luna/src/lyrics-glow.css index b455ead..cf04fb9 100644 --- a/plugins/radiant-lyrics-luna/src/lyrics-glow.css +++ b/plugins/radiant-lyrics-luna/src/lyrics-glow.css @@ -9,22 +9,19 @@ @font-face { font-family: "AbyssFont"; font-weight: 500; - src: url("https://excel.lexploits.top/extra/tidal/LyricsMedium.woff2") - format("woff2"); + 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"); + 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"); + src: url("https://excel.lexploits.top/extra/tidal/LyricsBold.woff2") format("woff2"); } /* Enhanced lyrics styling with glow effects */