import { useEffect, useState } from "react"; export type EffectiveTheme = "dark" | "light"; export type ThemePreference = "dark" | "light" | "system"; export const THEME_PREFERENCE_STORAGE_KEY = "dial.theme.preference"; export const THEME_MEDIA_QUERY = "(prefers-color-scheme: dark)"; export function applyInitialThemePreference() { applyThemeMode(resolveEffectiveTheme(readThemePreference(), getSystemPrefersDark())); } export function applyThemeMode(theme: EffectiveTheme, root: HTMLElement = document.documentElement) { root.setAttribute("theme-mode", theme); } export function getSystemPrefersDark(matchMedia: Window["matchMedia"] = window.matchMedia): boolean { try { return matchMedia(THEME_MEDIA_QUERY).matches; } catch { return false; } } export function parseThemePreference(value: unknown): ThemePreference { return value === "dark" || value === "light" || value === "system" ? value : "system"; } export function readThemePreference(storage: Storage = window.localStorage): ThemePreference { try { return parseThemePreference(storage.getItem(THEME_PREFERENCE_STORAGE_KEY)); } catch { return "system"; } } export function resolveEffectiveTheme(preference: ThemePreference, systemPrefersDark: boolean): EffectiveTheme { if (preference === "dark" || preference === "light") return preference; return systemPrefersDark ? "dark" : "light"; } export function useThemePreference() { const [preference, setPreferenceState] = useState(() => readThemePreference()); const [systemPrefersDark, setSystemPrefersDark] = useState(() => getSystemPrefersDark()); const effectiveTheme = resolveEffectiveTheme(preference, systemPrefersDark); useEffect(() => { applyThemeMode(effectiveTheme); }, [effectiveTheme]); useEffect(() => { const mediaQueryList = window.matchMedia(THEME_MEDIA_QUERY); const handleChange = (event: MediaQueryListEvent) => setSystemPrefersDark(event.matches); mediaQueryList.addEventListener("change", handleChange); return () => mediaQueryList.removeEventListener("change", handleChange); }, []); const setPreference = (nextPreference: ThemePreference) => { setPreferenceState(nextPreference); writeThemePreference(nextPreference); }; return { effectiveTheme, preference, setPreference }; } export function writeThemePreference(preference: ThemePreference, storage: Storage = window.localStorage) { try { storage.setItem(THEME_PREFERENCE_STORAGE_KEY, preference); } catch { // 存储不可用时仅使用当前内存状态,避免阻断 Dashboard 渲染。 } }