import { ReactiveStore } from "@luna/core"; import { LunaSettings, LunaSwitchSetting, LunaNumberSetting } from "@luna/ui"; import React from "react"; declare global { interface Window { updateRadiantLyricsStyles?: () => void; updateRadiantLyricsTextGlow?: () => void; updateStickyLyricsFeature?: () => void; updateStickyLyricsSetting?: (checked: boolean) => void; updateRadiantLyricsPlayerBarTint?: () => void; updateRadiantLyricsGlobalBackground?: () => void; updateRadiantLyricsNowPlayingBackground?: () => void; updateQualityProgressColor?: () => void; updateLyricsStyle?: () => void; updateLyricsStyleSetting?: (value: number) => void; } } export const settings = await ReactiveStore.getPluginStorage("RadiantLyrics", { lyricsGlowEnabled: true, trackTitleGlow: false, hideUIEnabled: true, playerBarVisible: false, qualityProgressColor: true, floatingPlayerBar: true, playerBarTint: 5, playerBarTintColor: "#000000" as string, playerBarTintCustomColors: [] as string[], playerBarRadius: 5, playerBarSpacing: 10, CoverEverywhere: true, performanceMode: false, spinningArt: true, textGlow: 20, backgroundScale: 15, backgroundRadius: 25, backgroundContrast: 120, backgroundBlur: 80, backgroundBrightness: 40, spinSpeed: 45, settingsAffectNowPlaying: true, stickyLyrics: false, stickyLyricsIcon: "sparkle" as string, lyricsStyle: 2, syllableStyle: 0, // MARKER: Syllable animations SETTINGS (WIP coming soon) contextAwareLyrics: true, blurInactive: true, bubbledLyrics: true, syllableLogging: false, }); export const Settings = () => { const [hideUIEnabled, setHideUIEnabled] = React.useState( settings.hideUIEnabled, ); const [playerBarVisible, setPlayerBarVisible] = React.useState( settings.playerBarVisible, ); const [lyricsGlowEnabled, setLyricsGlowEnabled] = React.useState( settings.lyricsGlowEnabled, ); const [textGlow, setTextGlow] = React.useState(settings.textGlow); const [CoverEverywhere, setCoverEverywhere] = React.useState( settings.CoverEverywhere, ); const [performanceMode, setPerformanceMode] = React.useState( settings.performanceMode, ); const [spinningArt, setspinningArt] = React.useState(settings.spinningArt); const [backgroundContrast, setBackgroundContrast] = React.useState( settings.backgroundContrast, ); const [backgroundBlur, setBackgroundBlur] = React.useState( settings.backgroundBlur, ); const [backgroundBrightness, setBackgroundBrightness] = React.useState( settings.backgroundBrightness, ); const [spinSpeed, setSpinSpeed] = React.useState(settings.spinSpeed); const [settingsAffectNowPlaying, setSettingsAffectNowPlaying] = React.useState(settings.settingsAffectNowPlaying); const [trackTitleGlow, setTrackTitleGlow] = React.useState( settings.trackTitleGlow, ); const [backgroundScale, setBackgroundScale] = React.useState( settings.backgroundScale, ); const [backgroundRadius, setBackgroundRadius] = React.useState( settings.backgroundRadius, ); const [floatingPlayerBar, setFloatingPlayerBar] = React.useState( settings.floatingPlayerBar, ); const [playerBarTint, setPlayerBarTint] = React.useState( settings.playerBarTint, ); const [playerBarTintColor, setPlayerBarTintColor] = React.useState( settings.playerBarTintColor, ); const [playerBarRadius, setPlayerBarRadius] = React.useState( settings.playerBarRadius, ); const [playerBarSpacing, setPlayerBarSpacing] = React.useState( settings.playerBarSpacing, ); const [showTintColorPicker, setShowTintColorPicker] = React.useState(false); const [isTintAnimatingIn, setIsTintAnimatingIn] = React.useState(false); const [shouldRenderTintPicker, setShouldRenderTintPicker] = React.useState(false); const [tintCustomInput, setTintCustomInput] = React.useState( settings.playerBarTintColor, ); const [tintCustomColors, setTintCustomColors] = React.useState( settings.playerBarTintCustomColors, ); const [tintHoveredColorIndex, setTintHoveredColorIndex] = React.useState< number | null >(null); const [stickyLyrics, setStickyLyrics] = React.useState(settings.stickyLyrics); React.useEffect(() => { window.updateStickyLyricsSetting = (checked: boolean) => setStickyLyrics(checked); return () => { window.updateStickyLyricsSetting = undefined; }; }, []); const [lyricsStyle, setLyricsStyle] = React.useState(settings.lyricsStyle); React.useEffect(() => { window.updateLyricsStyleSetting = (value: number) => setLyricsStyle(value); return () => { window.updateLyricsStyleSetting = undefined; }; }, []); const [contextAwareLyrics, setContextAwareLyrics] = React.useState( settings.contextAwareLyrics, ); const [blurInactive, setBlurInactive] = React.useState(settings.blurInactive); const [bubbledLyrics, setBubbledLyrics] = React.useState( settings.bubbledLyrics, ); const [qualityProgressColor, setQualityProgressColor] = React.useState( settings.qualityProgressColor, ); // Derive props and override onChange to accept a broader first param type type BaseSwitchProps = React.ComponentProps; type AnySwitchProps = Omit & { onChange: (_: unknown, checked: boolean) => void; checked: boolean; }; const AnySwitch = LunaSwitchSetting as unknown as React.ComponentType; return ( { settings.lyricsGlowEnabled = checked; setLyricsGlowEnabled(checked); // Update styles immediately when setting changes if (window.updateRadiantLyricsStyles) { window.updateRadiantLyricsStyles(); } }} /> { settings.trackTitleGlow = checked; setTrackTitleGlow(checked); if (window.updateRadiantLyricsStyles) { window.updateRadiantLyricsStyles(); } }} /> {(lyricsGlowEnabled || trackTitleGlow) && ( { settings.textGlow = value; setTextGlow(value); // Update variables immediately when setting changes if (window.updateRadiantLyricsTextGlow) { window.updateRadiantLyricsTextGlow(); } }} /> )} { settings.lyricsStyle = value; setLyricsStyle(value); if (window.updateLyricsStyle) { window.updateLyricsStyle(); } }} /> { settings.blurInactive = checked; setBlurInactive(checked); if (window.updateLyricsStyle) { window.updateLyricsStyle(); } }} /> { settings.contextAwareLyrics = checked; setContextAwareLyrics(checked); if (window.updateLyricsStyle) { window.updateLyricsStyle(); } }} /> { settings.bubbledLyrics = checked; setBubbledLyrics(checked); if (window.updateLyricsStyle) { window.updateLyricsStyle(); } }} /> { settings.stickyLyrics = checked; setStickyLyrics(checked); if (window.updateStickyLyricsFeature) { window.updateStickyLyricsFeature(); } }} /> { settings.hideUIEnabled = checked; setHideUIEnabled(checked); }} /> {hideUIEnabled && ( { console.log( "Player Bar Visibility:", checked ? "visible" : "hidden", ); settings.playerBarVisible = checked; setPlayerBarVisible(checked); // Update styles immediately when setting changes if (window.updateRadiantLyricsStyles) { window.updateRadiantLyricsStyles(); } }} /> )} { settings.qualityProgressColor = checked; setQualityProgressColor(checked); if (window.updateQualityProgressColor) { window.updateQualityProgressColor(); } }} /> { settings.floatingPlayerBar = checked; setFloatingPlayerBar(checked); if (window.updateRadiantLyricsStyles) { window.updateRadiantLyricsStyles(); } }} /> {floatingPlayerBar && ( <> { settings.playerBarRadius = value; setPlayerBarRadius(value); window.updateRadiantLyricsPlayerBarTint?.(); }} /> { settings.playerBarSpacing = value; setPlayerBarSpacing(value); window.updateRadiantLyricsPlayerBarTint?.(); }} /> )} {(() => { const closeTintColorPicker = () => { setIsTintAnimatingIn(false); setTimeout(() => { setShowTintColorPicker(false); setShouldRenderTintPicker(false); }, 200); }; const openTintColorPicker = () => { setShowTintColorPicker(true); setShouldRenderTintPicker(true); setTimeout(() => setIsTintAnimatingIn(true), 10); }; const updateTintColor = (color: string) => { setPlayerBarTintColor(color); setTintCustomInput(color); settings.playerBarTintColor = color; window.updateRadiantLyricsPlayerBarTint?.(); }; const addTintCustomColor = () => { if (tintCustomInput) { const trimmedInput = tintCustomInput.trim().toLowerCase(); const hexColorRegex = /^#([0-9a-f]{6}|[0-9a-f]{3})$/i; if ( hexColorRegex.test(trimmedInput) && !tintColorPresets.includes(trimmedInput) && !tintCustomColors.includes(trimmedInput) ) { const newCustomColors = [...tintCustomColors, trimmedInput]; setTintCustomColors(newCustomColors); settings.playerBarTintCustomColors = newCustomColors; } } }; const removeTintCustomColor = (colorToRemove: string) => { const newCustomColors = tintCustomColors.filter( (color) => color !== colorToRemove, ); setTintCustomColors(newCustomColors); settings.playerBarTintCustomColors = newCustomColors; if (playerBarTintColor === colorToRemove) { updateTintColor("#000000"); } }; const tintColorPresets = [ "#000000", "#111111", "#222222", "#333333", "#444444", "#555555", "#666666", "#888888", "#aaaaaa", "#cccccc", "#ffffff", "#0d1117", "#1a1a2e", "#16213e", "#0f3460", "#1b1b2f", "#162447", "#1f4068", "#e94560", ]; const allTintColors = [...tintColorPresets, ...tintCustomColors]; return (
{ settings.playerBarTint = value; setPlayerBarTint(value); window.updateRadiantLyricsPlayerBarTint?.(); }} /> {/* Color swatch — positioned just left of the value box */} {/* Color Picker Modal */} {shouldRenderTintPicker && ( <> )}
); })}
Add Custom Color
setTintCustomInput(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { updateTintColor(tintCustomInput); addTintCustomColor(); } }} placeholder="#000000" style={{ flex: 1, padding: "8px 12px", borderRadius: "6px", border: "1px solid rgba(255,255,255,0.2)", background: "rgba(255,255,255,0.1)", color: "#fff", fontSize: "14px", fontFamily: "monospace", boxSizing: "border-box", }} />
)} ); })()} { console.log( "Spinning Cover Everywhere:", checked ? "enabled" : "disabled", ); settings.CoverEverywhere = checked; setCoverEverywhere(checked); // Update styles immediately when setting changes if (window.updateRadiantLyricsGlobalBackground) { window.updateRadiantLyricsGlobalBackground(); } }} /> {CoverEverywhere && ( { console.log("Performance Mode:", checked ? "enabled" : "disabled"); settings.performanceMode = checked; setPerformanceMode(checked); // Update background animations immediately when setting changes if (window.updateRadiantLyricsGlobalBackground) { window.updateRadiantLyricsGlobalBackground(); } if (window.updateRadiantLyricsNowPlayingBackground) { window.updateRadiantLyricsNowPlayingBackground(); } }} /> )} {CoverEverywhere && ( { console.log( "Background Cover Spin:", checked ? "enabled" : "disabled", ); settings.spinningArt = checked; setspinningArt(checked); if (window.updateRadiantLyricsGlobalBackground) { window.updateRadiantLyricsGlobalBackground(); } if ( settings.settingsAffectNowPlaying && window.updateRadiantLyricsNowPlayingBackground ) { window.updateRadiantLyricsNowPlayingBackground(); } }} /> )} {CoverEverywhere && ( { settings.backgroundScale = value; setBackgroundScale(value); if (window.updateRadiantLyricsGlobalBackground) { window.updateRadiantLyricsGlobalBackground(); } if ( settings.settingsAffectNowPlaying && window.updateRadiantLyricsNowPlayingBackground ) { window.updateRadiantLyricsNowPlayingBackground(); } }} /> )} {CoverEverywhere && ( { settings.backgroundRadius = value; setBackgroundRadius(value); if (window.updateRadiantLyricsGlobalBackground) { window.updateRadiantLyricsGlobalBackground(); } if ( settings.settingsAffectNowPlaying && window.updateRadiantLyricsNowPlayingBackground ) { window.updateRadiantLyricsNowPlayingBackground(); } }} /> )} {CoverEverywhere && ( { settings.backgroundContrast = value; setBackgroundContrast(value); if (window.updateRadiantLyricsGlobalBackground) { window.updateRadiantLyricsGlobalBackground(); } if ( settings.settingsAffectNowPlaying && window.updateRadiantLyricsNowPlayingBackground ) { window.updateRadiantLyricsNowPlayingBackground(); } }} /> )} {CoverEverywhere && ( { console.log("Background Blur:", value); settings.backgroundBlur = value; setBackgroundBlur(value); if (window.updateRadiantLyricsGlobalBackground) { window.updateRadiantLyricsGlobalBackground(); } if ( settings.settingsAffectNowPlaying && window.updateRadiantLyricsNowPlayingBackground ) { window.updateRadiantLyricsNowPlayingBackground(); } }} /> )} {CoverEverywhere && ( { console.log("Background Brightness:", value); settings.backgroundBrightness = value; setBackgroundBrightness(value); if (window.updateRadiantLyricsGlobalBackground) { window.updateRadiantLyricsGlobalBackground(); } if ( settings.settingsAffectNowPlaying && window.updateRadiantLyricsNowPlayingBackground ) { window.updateRadiantLyricsNowPlayingBackground(); } }} /> )} {CoverEverywhere && spinningArt && ( { console.log("Spin Speed:", value); settings.spinSpeed = value; setSpinSpeed(value); if (window.updateRadiantLyricsGlobalBackground) { window.updateRadiantLyricsGlobalBackground(); } if ( settings.settingsAffectNowPlaying && window.updateRadiantLyricsNowPlayingBackground ) { window.updateRadiantLyricsNowPlayingBackground(); } }} /> )} {CoverEverywhere && ( { console.log( "Settings Affect Now Playing:", checked ? "enabled" : "disabled", ); settings.settingsAffectNowPlaying = checked; setSettingsAffectNowPlaying(checked); // Update Now Playing background immediately when setting changes if (window.updateRadiantLyricsNowPlayingBackground) { window.updateRadiantLyricsNowPlayingBackground(); } }} /> )}
); };