"use client"; import { useEffect, useState } from "react"; import { useI18n } from "@/lib/i18n"; import { haptic } from "@/lib/haptics"; import { cn } from "@/lib/utils"; import { MoonIcon, SunIcon } from "@/components/icons"; const STORAGE_KEY = "freezmo.theme"; const THEME_COLORS = { light: "#f4f7fd", dark: "#0e1424", aero: "#2fa7e6" } as const; /** The three themes. Aero is not a dark/light variant: it replaces the palette * and the scene, and the light/dark toggles hide while it is on. */ export type Theme = "light" | "dark" | "aero"; /** * Keeps the first `theme-color` meta (the one the app owns) in step with the * in-app theme. The SSR metas follow the OS preference; this one always wins * because it is media-less and first in the head. */ export function syncThemeColor(theme: Theme) { try { let meta = document.querySelector('meta[name="theme-color"][data-app]'); if (!meta) { meta = document.createElement("meta"); meta.setAttribute("name", "theme-color"); meta.setAttribute("data-app", "1"); document.head.insertBefore(meta, document.head.firstChild); } meta.setAttribute("content", THEME_COLORS[theme]); } catch { /* private mode / odd embedded browsers */ } } export function applyTheme(next: Theme) { document.documentElement.classList.toggle("dark", next === "dark"); document.documentElement.classList.toggle("aero", next === "aero"); syncThemeColor(next); try { window.localStorage.setItem(STORAGE_KEY, next); } catch { /* private mode */ } } export function useTheme() { const [theme, setTheme] = useState("light"); useEffect(() => { const stored = window.localStorage.getItem(STORAGE_KEY); const next: Theme = stored === "dark" || stored === "aero" ? stored : stored === "light" ? "light" : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; setTheme(next); syncThemeColor(next); }, []); const apply = (next: Theme) => { setTheme(next); applyTheme(next); }; return { theme, setTheme: apply, /** The light/dark flip; aero is chosen in Settings. */ toggle: () => apply(theme === "dark" ? "light" : "dark"), }; } export function ThemeToggle({ className, plain = false }: { className?: string; plain?: boolean }) { const { theme, toggle } = useTheme(); const { t } = useI18n(); const dark = theme === "dark"; // Frutiger Aero has no light/dark switch (see the theme picker in Settings). if (theme === "aero") return null; return ( ); }