"use client"; import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react"; export function useDebounced(value: T, ms = 200): T { const [v, setV] = useState(value); useEffect(() => { const t = setTimeout(() => setV(value), ms); return () => clearTimeout(t); }, [value, ms]); return v; } export function useMediaQuery(q: string, initial = false): boolean { const [m, setM] = useState(initial); useEffect(() => { const mq = window.matchMedia(q); const on = () => setM(mq.matches); on(); mq.addEventListener("change", on); return () => mq.removeEventListener("change", on); }, [q]); return m; } const LS_EVENT = "dci:localstorage"; function readRaw(key: string): string | null { try { return window.localStorage.getItem(key); } catch { return null; } } /** localStorage-backed state (JSON), SSR-safe (server snapshot = initial), synced across tabs. */ export function useLocalStorage(key: string, initial: T): [T, (v: T | ((p: T) => T)) => void] { const subscribe = useCallback((cb: () => void) => { window.addEventListener("storage", cb); window.addEventListener(LS_EVENT, cb); return () => { window.removeEventListener("storage", cb); window.removeEventListener(LS_EVENT, cb); }; }, []); const raw = useSyncExternalStore(subscribe, () => readRaw(key), () => null); const value = useMemo(() => { if (raw === null) return initial; try { return JSON.parse(raw) as T; } catch { return initial; } }, [raw, initial]); const set = useCallback( (nv: T | ((p: T) => T)) => { const cur = ((): T => { const r = readRaw(key); if (r === null) return initial; try { return JSON.parse(r) as T; } catch { return initial; } })(); const next = typeof nv === "function" ? (nv as (p: T) => T)(cur) : nv; try { window.localStorage.setItem(key, JSON.stringify(next)); } catch { /* ignore */ } window.dispatchEvent(new Event(LS_EVENT)); }, [key, initial], ); return [value, set]; } export function useInterval(fn: () => void, ms: number | null) { const ref = useRef(fn); useEffect(() => { ref.current = fn; }); useEffect(() => { if (ms === null) return; const t = setInterval(() => ref.current(), ms); return () => clearInterval(t); }, [ms]); }