export type Theme = 'system' | 'light' | 'dark'; export type ResolvedTheme = 'light' | 'dark'; const STORAGE_KEY = 'mangalord-theme'; function readStored(): Theme { if (typeof localStorage === 'undefined') return 'system'; const v = localStorage.getItem(STORAGE_KEY); return v === 'light' || v === 'dark' ? v : 'system'; } function systemPref(): ResolvedTheme { if (typeof window === 'undefined' || typeof window.matchMedia === 'undefined') return 'light'; return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } function resolve(value: Theme): ResolvedTheme { return value === 'system' ? systemPref() : value; } function apply(resolved: ResolvedTheme) { if (typeof document === 'undefined') return; document.documentElement.setAttribute('data-theme', resolved); } class ThemeStore { value: Theme = $state('system'); resolved: ResolvedTheme = $state('light'); private mql: MediaQueryList | null = null; private mqlListener: ((e: MediaQueryListEvent) => void) | null = null; init() { if (typeof window === 'undefined') return; this.value = readStored(); this.resolved = resolve(this.value); apply(this.resolved); this.mql = window.matchMedia('(prefers-color-scheme: dark)'); this.mqlListener = () => { if (this.value === 'system') { this.resolved = systemPref(); apply(this.resolved); } }; this.mql.addEventListener('change', this.mqlListener); } set(next: Theme) { this.value = next; if (next === 'system') { if (typeof localStorage !== 'undefined') localStorage.removeItem(STORAGE_KEY); } else if (typeof localStorage !== 'undefined') { localStorage.setItem(STORAGE_KEY, next); } this.resolved = resolve(next); apply(this.resolved); } destroy() { if (this.mql && this.mqlListener) { this.mql.removeEventListener('change', this.mqlListener); } this.mql = null; this.mqlListener = null; } } export const theme = new ThemeStore();