export type Theme = 'dark' | 'light' const STORAGE_KEY = 'viewer.theme' const listeners = new Set<(theme: Theme) => void>() export function initTheme(): Theme { const isAndroidShell = typeof (window as unknown as { AndroidViewer?: unknown }).AndroidViewer !== 'undefined' let theme: Theme = 'dark' try { const stored = localStorage.getItem(STORAGE_KEY) if (stored === 'light' || stored === 'dark') { theme = stored } else if (!isAndroidShell && window.matchMedia?.('(prefers-color-scheme: light)').matches) { theme = 'light' } } catch { /* ignore */ } applyTheme(theme, false) return theme } export function getTheme(): Theme { return document.documentElement.dataset.theme === 'light' ? 'light' : 'dark' } export function setTheme(theme: Theme): void { applyTheme(theme, true) } export function toggleTheme(): Theme { const next: Theme = getTheme() === 'dark' ? 'light' : 'dark' setTheme(next) return next } function applyTheme(theme: Theme, persist: boolean): void { document.documentElement.dataset.theme = theme document.documentElement.style.colorScheme = theme const meta = document.querySelector('meta[name="theme-color"]') if (meta) meta.setAttribute('content', theme === 'dark' ? '#0b0e14' : '#f2f5fa') if (persist) { try { localStorage.setItem(STORAGE_KEY, theme) } catch { /* ignore */ } } for (const listener of listeners) listener(theme) } export function onThemeChange(listener: (theme: Theme) => void): () => void { listeners.add(listener) return () => listeners.delete(listener) }