import { Injectable, signal, effect } from '@angular/core'; export type Theme = 'light' | 'dark' | 'system'; @Injectable({ providedIn: 'root' }) export class ThemeService { theme = signal(this.loadTheme()); constructor() { effect(() => { this.applyTheme(this.theme()); }); // Listen for system preference changes if (typeof window !== 'undefined') { window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { if (this.theme() === 'system') { this.applyTheme('system'); } }); } } private loadTheme(): Theme { if (typeof localStorage === 'undefined') return 'system'; const stored = localStorage.getItem('onebox-theme'); if (stored === 'light' || stored === 'dark' || stored === 'system') { return stored; } return 'system'; } setTheme(theme: Theme): void { this.theme.set(theme); if (typeof localStorage !== 'undefined') { localStorage.setItem('onebox-theme', theme); } } toggle(): void { const resolved = this.resolvedTheme(); this.setTheme(resolved === 'dark' ? 'light' : 'dark'); } isDark(): boolean { return this.resolvedTheme() === 'dark'; } resolvedTheme(): 'light' | 'dark' { if (this.theme() === 'system') { if (typeof window === 'undefined') return 'light'; return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } return this.theme() as 'light' | 'dark'; } private applyTheme(theme: Theme): void { if (typeof document === 'undefined') return; const resolved = theme === 'system' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme; document.documentElement.classList.toggle('dark', resolved === 'dark'); } }