'use client' import * as React from 'react' import { useLocalStorage } from '../../hooks/use-local-storage' import { useMediaQuery } from '../../hooks/use-media-query' import { ThemeScript } from './theme-script' const MEDIA = '(prefers-color-scheme: dark)' const SYSTEM_THEMES = ['light', 'dark'] const DEFAULT_THEMES = ['light', 'dark'] // Theme is stored as a raw string so the inline no-flash script can read it // directly with `localStorage.getItem` (no JSON quotes). const RAW_STRING = { serializer: (v: string) => v, deserializer: (v: string) => v } export interface ThemeContextValue { /** The chosen theme, possibly `'system'`. */ theme: string | undefined setTheme: (value: string | ((prev: string) => string)) => void /** The theme actually applied - `'system'` resolved to `'light'`/`'dark'`. */ resolvedTheme: string | undefined systemTheme: 'light' | 'dark' | undefined themes: string[] forcedTheme: string | undefined /** `false` until the client has mounted - guard theme-dependent UI with this. */ mounted: boolean } const ThemeContext = React.createContext(undefined) const defaultContext: ThemeContextValue = { theme: undefined, setTheme: () => {}, resolvedTheme: undefined, systemTheme: undefined, themes: [], forcedTheme: undefined, mounted: false, } export function useThemeContext(): ThemeContextValue { return React.useContext(ThemeContext) ?? defaultContext } export interface ThemeProviderProps { /** The subtree the theme applies to */ children?: React.ReactNode /** * Available theme names * @default ['light', 'dark'] */ themes?: string[] /** Force a theme for the whole subtree (overrides storage + OS) */ forcedTheme?: string /** * Respect the OS `prefers-color-scheme` * @default true */ enableSystem?: boolean /** * Suppress CSS transitions during a theme switch * @default false */ disableTransitionOnChange?: boolean /** * Set `color-scheme` on `` so native UI matches * @default true */ enableColorScheme?: boolean /** * `localStorage` key for the persisted choice * @default 'theme' */ storageKey?: string /** Theme used before a choice is stored */ defaultTheme?: string /** Map a theme name to a custom class applied on `` */ value?: Record /** CSP nonce forwarded to the inline script */ nonce?: string /** Extra props for the inline `