"use client"; import { createContext, useCallback, useContext, useEffect, useMemo, useState, useSyncExternalStore, type ReactNode } from "react"; /** * User preferences (spec §32, §98, §110): density (compact / normal / comfortable), live paused, * exact timestamps. Persisted in localStorage; density is mirrored on so CSS * variables apply everywhere without re-rendering the tree. */ export type Density = "compact" | "normal" | "comfortable"; interface Prefs { density: Density; setDensity: (d: Density) => void; paused: boolean; setPaused: (p: boolean) => void; exactTime: boolean; setExactTime: (v: boolean) => void; mounted: boolean; } const Ctx = createContext(null); const KEY = "ws_prefs"; export function PrefsProvider({ children }: { children: ReactNode }) { // Stored preferences are applied once, right after hydration (reading storage during SSR would mismatch). const mounted = useSyncExternalStore(subscribeNoop, () => true, () => false); const stored = useSyncExternalStore(subscribeNoop, readStored, () => null); const [override, setOverride] = useState<{ density?: Density; exactTime?: boolean }>({}); const density = override.density ?? stored?.density ?? "normal"; const exactTime = override.exactTime ?? stored?.exactTime ?? false; const [paused, setPaused] = useState(false); const setDensityState = (d: Density): void => setOverride((o) => ({ ...o, density: d })); const setExactTimeState = (v: boolean): void => setOverride((o) => ({ ...o, exactTime: v })); useEffect(() => { if (!mounted) return; document.documentElement.dataset.density = density; try { window.localStorage.setItem(KEY, JSON.stringify({ density, exactTime })); } catch { // ignore } }, [density, exactTime, mounted]); const setDensity = useCallback((d: Density) => setDensityState(d), []); const setExactTime = useCallback((v: boolean) => setExactTimeState(v), []); const value = useMemo(() => ({ density, setDensity, paused, setPaused, exactTime, setExactTime, mounted }), [density, setDensity, paused, exactTime, setExactTime, mounted]); return {children}; } const subscribeNoop = (): (() => void) => () => {}; let storedCache: { raw: string | null; value: { density?: Density; exactTime?: boolean } | null } = { raw: undefined as unknown as string | null, value: null }; function readStored(): { density?: Density; exactTime?: boolean } | null { try { const raw = window.localStorage.getItem(KEY); if (raw === storedCache.raw) return storedCache.value; const value = raw ? (JSON.parse(raw) as { density?: Density; exactTime?: boolean }) : null; storedCache = { raw, value }; return value; } catch { return null; } } export function usePrefs(): Prefs { const v = useContext(Ctx); if (!v) return { density: "normal", setDensity: () => {}, paused: false, setPaused: () => {}, exactTime: false, setExactTime: () => {}, mounted: false }; return v; } export function DensityToggle({ compact = false }: { compact?: boolean }) { const { density, setDensity } = usePrefs(); const opts: [Density, string][] = [ ["compact", "Compact"], ["normal", "Normal"], ["comfortable", "Comfortable"], ]; return (
{opts.map(([d, label]) => ( ))}
); }