import { ReactiveStore } from "@luna/core"; import { LunaSettings, LunaSwitchSetting } from "@luna/ui"; import React from "react"; declare global { interface Window { applyColoramaLyrics?: () => void; } } type SwitchChangeHandler = ( event: React.ChangeEvent | null, checked: boolean, ) => void; export const settings = await ReactiveStore.getPluginStorage("ColoramaLyrics", { enabled: true, singleColor: "#FFFFFF", singleAlpha: 100, customColors: [] as string[], excludeInactive: false, }); export const Settings = () => { const [singleColor, setSingleColor] = React.useState(settings.singleColor); const [singleAlpha, setSingleAlpha] = React.useState( settings.singleAlpha ?? 100, ); const [customInput, setCustomInput] = React.useState(settings.singleColor); const [customColors, setCustomColors] = React.useState(settings.customColors); const [showPicker, setShowPicker] = React.useState(false); const [isAnimatingIn, setIsAnimatingIn] = React.useState(false); const [shouldRender, setShouldRender] = React.useState(false); const [excludeInactive, setExcludeInactive] = React.useState( settings.excludeInactive, ); const AnySwitch = LunaSwitchSetting as unknown as React.ComponentType<{ title: string; desc?: string; checked: boolean; onChange: SwitchChangeHandler; }>; const normalizeToRGB = ( hex: string, fallback: string = "#FFFFFF", ): string => { let v = hex.trim().toLowerCase(); if (!v.startsWith("#")) v = `#${v}`; if (/^#([0-9a-f]{3,4})$/.test(v)) { const m = v.slice(1); const r = m[0]; const g = m[1]; const b = m[2]; return `#${r}${r}${g}${g}${b}${b}`.toUpperCase(); } if (/^#([0-9a-f]{8})$/.test(v)) { const rrggbb = v.slice(3); return `#${rrggbb}`.toUpperCase(); } if (/^#([0-9a-f]{6})$/.test(v)) return v.toUpperCase(); return fallback; }; const colorPresets = [ "#FFFFFF", "#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF", "#FF8800", "#8800FF", "#0088FF", "#88FF00", "#FF0088", "#00FF88", "#444444", "#888888", "#CCCCCC", "#1DB954", "#E22134", "#1976D2", ]; const openPicker = () => { setShowPicker(true); setShouldRender(true); setTimeout(() => setIsAnimatingIn(true), 10); }; const closePicker = () => { setIsAnimatingIn(false); setTimeout(() => { setShowPicker(false); setShouldRender(false); }, 200); }; const hexColorRegex = /^#([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3,4})$/i; const applyCustomInputColor = (raw: string, updateInput: boolean): void => { const trimmed = raw.trim(); if (!hexColorRegex.test(trimmed)) return; const next = normalizeToRGB(trimmed); settings.singleColor = next; setSingleColor(next); if (updateInput) setCustomInput(next); requestApply(); }; const addCustomColor = () => { const trimmed = customInput.trim(); if ( hexColorRegex.test(trimmed) && !colorPresets.includes(trimmed) && !customColors.includes(normalizeToRGB(trimmed)) ) { const updated = [...customColors, normalizeToRGB(trimmed)]; setCustomColors(updated); settings.customColors = updated; } }; const allColors = [...colorPresets, ...customColors]; const requestApply = () => { window.applyColoramaLyrics?.(); }; return ( {/* Single color picker button */}
Lyrics Color
Set lyrics color
{/* Color picker modal */} {shouldRender && ( <>
Alpha
{ const value = Number(e.target.value); settings.singleAlpha = value; setSingleAlpha(value); requestApply(); }} style={{ width: "100%" }} />
)} | null, checked: boolean) => { settings.excludeInactive = checked; setExcludeInactive(checked); requestApply(); }} />
); };