Updated Settings

This commit is contained in:
2025-08-13 21:08:30 +10:00
parent c0255acb4c
commit 40ed89dd34
3 changed files with 51 additions and 60 deletions
+39 -43
View File
@@ -2,7 +2,7 @@ import { ReactiveStore } from "@luna/core";
import { LunaSettings, LunaNumberSetting, LunaSwitchSetting, LunaTextSetting } from "@luna/ui"; import { LunaSettings, LunaNumberSetting, LunaSwitchSetting, LunaTextSetting } from "@luna/ui";
import React from "react"; import React from "react";
export type ColoramaMode = "single" | "gradient" | "auto-single" | "auto-gradient" | "rainbow"; export type ColoramaMode = "single" | "gradient-experimental" | "cover" | "cover-gradient";
export const settings = await ReactiveStore.getPluginStorage("ColoramaLyrics", { export const settings = await ReactiveStore.getPluginStorage("ColoramaLyrics", {
enabled: true, enabled: true,
@@ -15,7 +15,6 @@ export const settings = await ReactiveStore.getPluginStorage("ColoramaLyrics", {
gradientEnd: "#AAFFFF", gradientEnd: "#AAFFFF",
gradientEndAlpha: 100, gradientEndAlpha: 100,
gradientAngle: 0, gradientAngle: 0,
rainbowSpeed: 8,
customColors: [] as string[], customColors: [] as string[],
excludeInactive: false excludeInactive: false
}); });
@@ -30,7 +29,6 @@ export const Settings = () => {
const [gradientEnd, setGradientEnd] = React.useState(settings.gradientEnd); const [gradientEnd, setGradientEnd] = React.useState(settings.gradientEnd);
const [gradientEndAlpha, setGradientEndAlpha] = React.useState<number>(settings.gradientEndAlpha ?? 100); const [gradientEndAlpha, setGradientEndAlpha] = React.useState<number>(settings.gradientEndAlpha ?? 100);
const [gradientAngle, setGradientAngle] = React.useState(settings.gradientAngle); const [gradientAngle, setGradientAngle] = React.useState(settings.gradientAngle);
const [rainbowSpeed, setRainbowSpeed] = React.useState<number>(settings.rainbowSpeed ?? 8);
const [customInput, setCustomInput] = React.useState(settings.singleColor); const [customInput, setCustomInput] = React.useState(settings.singleColor);
const [customColors, setCustomColors] = React.useState(settings.customColors); const [customColors, setCustomColors] = React.useState(settings.customColors);
const [showPicker, setShowPicker] = React.useState(false); const [showPicker, setShowPicker] = React.useState(false);
@@ -129,8 +127,10 @@ export const Settings = () => {
{/* Mode selection via dropdown (aligned right) */} {/* Mode selection via dropdown (aligned right) */}
<div style={{ padding: "8px 0", display: "flex", alignItems: "center", gap: 12 }}> <div style={{ padding: "8px 0", display: "flex", alignItems: "center", gap: 12 }}>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<div style={{ fontWeight: "normal", fontSize: "1.075rem" }}>Mode</div> <div style={{ fontWeight: "normal", fontSize: "1.075rem" }}>Mode</div>
<div style={{ opacity: 0.7, fontSize: 14 }}>Choose how lyrics are colored</div> <div style={{ opacity: 0.7, fontSize: 14 }}>Choose how lyrics are colored</div>
</div>
<select <select
value={mode} value={mode}
onChange={(e) => { onChange={(e) => {
@@ -150,10 +150,9 @@ export const Settings = () => {
}} }}
> >
<option value="single" style={{ color: '#000', background: '#fff' }}>Single</option> <option value="single" style={{ color: '#000', background: '#fff' }}>Single</option>
<option value="gradient" style={{ color: '#000', background: '#fff' }}>Gradient</option> <option value="gradient-experimental" style={{ color: '#000', background: '#fff' }}>Gradient - Experimental</option>
<option value="auto-single" style={{ color: '#000', background: '#fff' }}>Auto (Cover)</option> <option value="cover" style={{ color: '#000', background: '#fff' }}>Cover - Experimental</option>
<option value="auto-gradient" style={{ color: '#000', background: '#fff' }}>Auto Gradient</option> <option value="cover-gradient" style={{ color: '#000', background: '#fff' }}>Cover (Gradient) - Experimental</option>
<option value="rainbow" style={{ color: '#000', background: '#fff' }}>Rainbow</option>
</select> </select>
</div> </div>
@@ -161,7 +160,7 @@ export const Settings = () => {
<div style={{ padding: "8px 0", display: mode === "single" ? "flex" : "none", justifyContent: "space-between", alignItems: "center" }}> <div style={{ padding: "8px 0", display: mode === "single" ? "flex" : "none", justifyContent: "space-between", alignItems: "center" }}>
<div> <div>
<div style={{ fontWeight: "normal", fontSize: "1.075rem", marginBottom: 4 }}>Lyrics Color</div> <div style={{ fontWeight: "normal", fontSize: "1.075rem", marginBottom: 4 }}>Lyrics Color</div>
<div style={{ opacity: 0.7, fontSize: 14 }}>Solid color (configure inside picker)</div> <div style={{ opacity: 0.7, fontSize: 14 }}>Set lyrics color</div>
</div> </div>
<div style={{ display: "flex", gap: 8, alignItems: "center", position: "relative" }}> <div style={{ display: "flex", gap: 8, alignItems: "center", position: "relative" }}>
<button <button
@@ -179,35 +178,32 @@ export const Settings = () => {
</div> </div>
{/* Gradient controls (triggers only) */} {/* Gradient controls (open picker) */}
<div style={{ padding: "8px 0", display: mode === "gradient" ? "block" : "none" }}> <div style={{ padding: "8px 0", display: mode === "gradient-experimental" ? "flex" : "none", justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ fontWeight: "normal", fontSize: "1.075rem", marginBottom: 4 }}>Gradient</div> <div>
<div style={{ opacity: 0.7, fontSize: 14, marginBottom: 8 }}>Pick start/end and angle (inside picker)</div> <div style={{ fontWeight: "normal", fontSize: "1.075rem", marginBottom: 4 }}>Gradient (Experimental)</div>
<div style={{ display: "flex", gap: 12, alignItems: "center" }}> <div style={{ opacity: 0.7, fontSize: 14 }}>Set colors & angle</div>
<button
onClick={() => {
setCustomInput(gradientStart);
openPicker('start');
}}
title="Start Color"
style={{ width: 32, height: 32, border: "1px solid rgba(255,255,255,0.15)", borderRadius: 6, background: normalizeToRGB(gradientStart) }}
/>
<button
onClick={() => {
setCustomInput(gradientEnd);
openPicker('end');
}}
title="End Color"
style={{ width: 32, height: 32, border: "1px solid rgba(255,255,255,0.15)", borderRadius: 6, background: normalizeToRGB(gradientEnd) }}
/>
</div> </div>
<button
onClick={() => { setCustomInput(gradientStart); openPicker('start'); }}
style={{
padding: '8px 12px',
borderRadius: 8,
border: '1px solid rgba(255,255,255,0.2)',
background: 'rgba(255,255,255,0.08)',
color: '#fff',
cursor: 'pointer'
}}
>
Configure
</button>
</div> </div>
{/* Auto gradient controls (open picker for angle) */} {/* Cover gradient controls (open picker for angle) */}
<div style={{ padding: "8px 0", display: mode === "auto-gradient" ? "flex" : "none", justifyContent: 'space-between', alignItems: 'center' }}> <div style={{ padding: "8px 0", display: mode === "cover-gradient" ? "flex" : "none", justifyContent: 'space-between', alignItems: 'center' }}>
<div> <div>
<div style={{ fontWeight: "normal", fontSize: "1.075rem", marginBottom: 4 }}>Auto Gradient</div> <div style={{ fontWeight: "normal", fontSize: "1.075rem", marginBottom: 4 }}>Cover (Gradient) - Experimental</div>
<div style={{ opacity: 0.7, fontSize: 14 }}>Configure angle inside the picker</div> <div style={{ opacity: 0.7, fontSize: 14 }}>Set angle</div>
</div> </div>
<button <button
onClick={() => openPicker('start')} onClick={() => openPicker('start')}
@@ -224,7 +220,7 @@ export const Settings = () => {
</button> </button>
</div> </div>
{/* Rainbow controls removed: mode exists but has no UI */} {/* Rainbow mode removed */}
{/* Modal for picking and managing colors (reused) */} {/* Modal for picking and managing colors (reused) */}
{shouldRender && ( {shouldRender && (
@@ -267,7 +263,7 @@ export const Settings = () => {
<div style={{ marginBottom: 12, color: "#fff", fontWeight: "bold", fontSize: 14 }}> <div style={{ marginBottom: 12, color: "#fff", fontWeight: "bold", fontSize: 14 }}>
{mode === 'single' ? 'Single Color' : 'Gradient Colors'} {mode === 'single' ? 'Single Color' : 'Gradient Colors'}
</div> </div>
{mode === 'gradient' && ( {mode === 'gradient-experimental' && (
<div style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: 12 }}> <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: 12 }}>
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: 12 }}>Editing</div> <div style={{ color: 'rgba(255,255,255,0.7)', fontSize: 12 }}>Editing</div>
<button <button
@@ -296,7 +292,7 @@ export const Settings = () => {
</button> </button>
</div> </div>
)} )}
{mode !== 'auto-gradient' && ( {mode !== 'cover-gradient' && (
<div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 8, marginBottom: 16 }}> <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 8, marginBottom: 16 }}>
{allColors.map((color, index) => ( {allColors.map((color, index) => (
<button <button
@@ -305,7 +301,7 @@ export const Settings = () => {
if (mode === "single") { if (mode === "single") {
const next = normalizeToRGB(color); const next = normalizeToRGB(color);
setSingleColor((settings.singleColor = next)); setSingleColor((settings.singleColor = next));
} else if (mode === "gradient") { } else if (mode === "gradient-experimental") {
if (activeEndpoint === 'end') { if (activeEndpoint === 'end') {
setGradientEnd((settings.gradientEnd = normalizeToRGB(color))); setGradientEnd((settings.gradientEnd = normalizeToRGB(color)));
} else { } else {
@@ -327,7 +323,7 @@ export const Settings = () => {
))} ))}
</div> </div>
)} )}
{mode !== 'auto-gradient' && ( {mode !== 'cover-gradient' && (
<div style={{ marginBottom: 12 }}> <div style={{ marginBottom: 12 }}>
<div style={{ color: "rgba(255,255,255,0.7)", fontSize: 12, marginBottom: 6 }}>Custom Hex (#RRGGBB)</div> <div style={{ color: "rgba(255,255,255,0.7)", fontSize: 12, marginBottom: 6 }}>Custom Hex (#RRGGBB)</div>
<div style={{ display: "flex", gap: 8, alignItems: "center" }}> <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
@@ -343,7 +339,7 @@ export const Settings = () => {
const next = normalizeToRGB(trimmed); const next = normalizeToRGB(trimmed);
setSingleColor((settings.singleColor = next)); setSingleColor((settings.singleColor = next));
setCustomInput(next); setCustomInput(next);
} else if (mode === "gradient") { } else if (mode === "gradient-experimental") {
const norm = normalizeToRGB(trimmed); const norm = normalizeToRGB(trimmed);
if (activeEndpoint === 'end') { if (activeEndpoint === 'end') {
setGradientEnd((settings.gradientEnd = norm)); setGradientEnd((settings.gradientEnd = norm));
@@ -376,7 +372,7 @@ export const Settings = () => {
if (hexColorRegex.test(trimmed)) { if (hexColorRegex.test(trimmed)) {
if (mode === "single") { if (mode === "single") {
setSingleColor((settings.singleColor = normalizeToRGB(trimmed))); setSingleColor((settings.singleColor = normalizeToRGB(trimmed)));
} else if (mode === "gradient") { } else if (mode === "gradient-experimental") {
const norm = normalizeToRGB(trimmed); const norm = normalizeToRGB(trimmed);
if (activeEndpoint === 'end') { if (activeEndpoint === 'end') {
setGradientEnd((settings.gradientEnd = norm)); setGradientEnd((settings.gradientEnd = norm));
@@ -428,7 +424,7 @@ export const Settings = () => {
</div> </div>
)} )}
{mode === 'gradient' && ( {mode === 'gradient-experimental' && (
<div style={{ marginBottom: 16, display: 'grid', gap: 16 }}> <div style={{ marginBottom: 16, display: 'grid', gap: 16 }}>
<div> <div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
@@ -490,7 +486,7 @@ export const Settings = () => {
</div> </div>
)} )}
{mode === 'auto-gradient' && ( {mode === 'cover-gradient' && (
<div style={{ marginBottom: 16 }}> <div style={{ marginBottom: 16 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
<div style={{ color: 'rgba(255,255,255,0.8)', fontSize: 12 }}>Angle</div> <div style={{ color: 'rgba(255,255,255,0.8)', fontSize: 12 }}>Angle</div>
@@ -531,7 +527,7 @@ export const Settings = () => {
</> </>
)} )}
<AnySwitch <AnySwitch
title="Exclude Inactive | Experimental" title="Exclude Inactive"
desc="Apply color/gradient only to the currently active lyric line" desc="Apply color/gradient only to the currently active lyric line"
checked={excludeInactive} checked={excludeInactive}
onChange={(_: unknown, checked: boolean) => { onChange={(_: unknown, checked: boolean) => {
+10 -14
View File
@@ -124,10 +124,10 @@ function applyGradient(start: string, end: string, angle: number) {
} }
function resetModeClasses(): void { function resetModeClasses(): void {
document.body.classList.remove('colorama-single', 'colorama-gradient', 'colorama-rainbow'); document.body.classList.remove('colorama-single', 'colorama-gradient');
} }
async function applyAutoColors(gradient: boolean) { async function applyCoverColors(gradient: boolean) {
const img = await getCoverArtElement(); const img = await getCoverArtElement();
if (!img) return; if (!img) return;
const colors = getDominantColorsFromImage(img, gradient ? 2 : 1); const colors = getDominantColorsFromImage(img, gradient ? 2 : 1);
@@ -143,7 +143,7 @@ async function applyAutoColors(gradient: boolean) {
function applyColoramaLyrics(): void { function applyColoramaLyrics(): void {
if (!settings.enabled) { if (!settings.enabled) {
document.body.classList.remove('colorama-single', 'colorama-gradient', 'colorama-rainbow'); document.body.classList.remove('colorama-single', 'colorama-gradient');
return; return;
} }
@@ -158,17 +158,14 @@ function applyColoramaLyrics(): void {
case "single": case "single":
applySingleColor(settings.singleColor); applySingleColor(settings.singleColor);
break; break;
case "gradient": case "gradient-experimental":
applyGradient(settings.gradientStart, settings.gradientEnd, settings.gradientAngle); applyGradient(settings.gradientStart, settings.gradientEnd, settings.gradientAngle);
break; break;
case "rainbow": case "cover":
// no-op: rainbow mode disabled applyCoverColors(false);
break; break;
case "auto-single": case "cover-gradient":
applyAutoColors(false); applyCoverColors(true);
break;
case "auto-gradient":
applyAutoColors(true);
break; break;
} }
} }
@@ -182,7 +179,7 @@ function observeTrackChanges(): void {
const currentTrackId = PlayState.playbackContext?.actualProductId; const currentTrackId = PlayState.playbackContext?.actualProductId;
if (currentTrackId && currentTrackId !== lastTrackId) { if (currentTrackId && currentTrackId !== lastTrackId) {
lastTrackId = currentTrackId; lastTrackId = currentTrackId;
if (settings.mode.startsWith("auto")) { if (settings.mode === 'cover' || settings.mode === 'cover-gradient') {
setTimeout(() => applyColoramaLyrics(), 200); setTimeout(() => applyColoramaLyrics(), 200);
} }
} }
@@ -220,7 +217,6 @@ function hookRadiantUpdates(): void {
setTimeout(() => hookRadiantUpdates(), 0); setTimeout(() => hookRadiantUpdates(), 0);
// Observe active lyric span changes and restart rainbow animation to avoid freezes // Rainbow mode removed
// Rainbow mode disabled: no lyrics observer needed
@@ -32,7 +32,6 @@
-webkit-text-fill-color: transparent !important; -webkit-text-fill-color: transparent !important;
} }
/* Ensure active line keeps rainbow in only-active mode */
/* Only-active: apply container class only on the active line via JS */ /* Only-active: apply container class only on the active line via JS */
/* Slight emphasis on current line (uniform to single mode) */ /* Slight emphasis on current line (uniform to single mode) */