feat(themes): animated-background sliders (density / speed / opacity)
Port the boolab FX controls: localStorage-backed Density, Speed, and Opacity sliders in the theme settings, shown when a canvas-background theme is active (Density is matrix-rain-only). Speed is a multiplier over each field's native base; values feed MatrixRain / NeonField live.
This commit is contained in:
@@ -5,7 +5,15 @@ import { Card } from '@/components/ui/card';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||
import { THEMES, setTheme, useTheme, type ThemeId, type ThemeMode } from '@/lib/theme';
|
||||
import { useAnimBg, setAnimBg } from '@/lib/anim';
|
||||
import {
|
||||
useAnimBg,
|
||||
setAnimBg,
|
||||
useAnimParams,
|
||||
setAnimDensity,
|
||||
setAnimSpeed,
|
||||
setAnimOpacity,
|
||||
ANIM_RANGES,
|
||||
} from '@/lib/anim';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
// v1.9: lifted out of pages/Settings.tsx so the SettingsPane Theme tab and
|
||||
@@ -18,9 +26,51 @@ const MODES: { value: ThemeMode; label: string; hint: string }[] = [
|
||||
{ value: 'system', label: 'System', hint: 'Follow OS preference.' },
|
||||
];
|
||||
|
||||
// Labelled range input for the animated-background params (boolab FX sliders).
|
||||
// Native <input type=range> tinted with the theme accent — no shadcn Slider
|
||||
// primitive exists in ui/.
|
||||
function FxSlider({
|
||||
label,
|
||||
value,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
onChange,
|
||||
}: {
|
||||
label: string;
|
||||
value: number;
|
||||
min: number;
|
||||
max: number;
|
||||
step: number;
|
||||
onChange: (v: number) => void;
|
||||
}) {
|
||||
return (
|
||||
<label className="block space-y-1">
|
||||
<span className="flex items-center justify-between text-xs text-muted-foreground">
|
||||
<span>{label}</span>
|
||||
<span className="font-mono tabular-nums">{value.toFixed(2)}</span>
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
value={value}
|
||||
onChange={(e) => onChange(Number(e.target.value))}
|
||||
className="w-full cursor-pointer"
|
||||
style={{ accentColor: 'var(--primary)' }}
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export function ThemePicker() {
|
||||
const { id: currentId, mode: currentMode } = useTheme();
|
||||
const animOn = useAnimBg();
|
||||
const params = useAnimParams();
|
||||
// The slider set only applies to the canvas-background themes; density is
|
||||
// matrix-rain-only (Classic), speed/opacity apply to both fields.
|
||||
const isCanvasTheme = currentId === 'boocode-classic' || currentId === 'boocode-override';
|
||||
// Track the most recent in-flight pick so the picker can show a subtle
|
||||
// "applying…" state on the targeted card while the PATCH is in flight.
|
||||
const [pending, setPending] = useState<
|
||||
@@ -97,6 +147,36 @@ export function ThemePicker() {
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Disables canvas animations and CSS effects for all themes. Persisted locally.
|
||||
</p>
|
||||
{isCanvasTheme && (
|
||||
<div className={cn('grid gap-x-4 gap-y-3 sm:grid-cols-2 pt-1', !animOn && 'opacity-50')}>
|
||||
{currentId === 'boocode-classic' && (
|
||||
<FxSlider
|
||||
label="Density"
|
||||
value={params.density}
|
||||
min={ANIM_RANGES.density.min}
|
||||
max={ANIM_RANGES.density.max}
|
||||
step={ANIM_RANGES.density.step}
|
||||
onChange={setAnimDensity}
|
||||
/>
|
||||
)}
|
||||
<FxSlider
|
||||
label="Speed"
|
||||
value={params.speed}
|
||||
min={ANIM_RANGES.speed.min}
|
||||
max={ANIM_RANGES.speed.max}
|
||||
step={ANIM_RANGES.speed.step}
|
||||
onChange={setAnimSpeed}
|
||||
/>
|
||||
<FxSlider
|
||||
label="Opacity"
|
||||
value={params.opacity}
|
||||
min={ANIM_RANGES.opacity.min}
|
||||
max={ANIM_RANGES.opacity.max}
|
||||
step={ANIM_RANGES.opacity.step}
|
||||
onChange={setAnimOpacity}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="space-y-3">
|
||||
|
||||
Reference in New Issue
Block a user