import { themes, defaultTheme, cloneTokens, type ThemeDefinition, type ThemeTokens, type ThemeTokenKey } from './themes.js'; const STORAGE_KEY = 'app-theme'; const CUSTOM_TOKENS_KEY = 'app-theme-custom'; class ThemeState { activeThemeId = $state('default'); customLightTokens = $state(cloneTokens(defaultTheme.light)); customDarkTokens = $state(cloneTokens(defaultTheme.dark)); activeTheme = $derived.by((): ThemeDefinition => { if (this.activeThemeId === 'custom') { return { id: 'custom', label: 'Custom', light: this.customLightTokens, dark: this.customDarkTokens }; } return themes.find((t) => t.id === this.activeThemeId) ?? themes[0]; }); constructor() { this.load(); } setTheme(id: string) { this.activeThemeId = id; this.persist(); this.apply(); } updateCustomToken(mode: 'light' | 'dark', key: ThemeTokenKey, value: string) { if (mode === 'light') { this.customLightTokens = { ...this.customLightTokens, [key]: value }; } else { this.customDarkTokens = { ...this.customDarkTokens, [key]: value }; } if (this.activeThemeId === 'custom') this.apply(); this.persist(); } /** Copy a preset theme's tokens into the custom slots */ resetCustomToTheme(themeId: string) { const source = themes.find((t) => t.id === themeId) ?? defaultTheme; this.customLightTokens = cloneTokens(source.light); this.customDarkTokens = cloneTokens(source.dark); if (this.activeThemeId === 'custom') this.apply(); this.persist(); } apply() { if (typeof document === 'undefined') return; const theme = this.activeTheme; // Use a single