'use client'; /** * Display density — `comfortable` (default) · `compact` · `dense`. Persisted in `localStorage['aia-density']`, applied * before paint by `DENSITY_SCRIPT` as `` and read by globals.css through CSS variables * (`--d-cell-y` table cell padding, `--d-kv-y` key–value rows, `--d-section-y` section padding, `--d-base` font size). * Same pattern as the theme (`components/layout/theme.tsx`). */ import { useCallback, useEffect, useState } from 'react'; import { DENSITY_KEY } from './prepaint'; export type Density = 'comfortable' | 'compact' | 'dense'; export { DENSITY_KEY, DENSITY_SCRIPT } from './prepaint'; export const DENSITIES: Density[] = ['comfortable', 'compact', 'dense']; export const DENSITY_LABELS: Record = { comfortable: 'Comfortable', compact: 'Compact', dense: 'Dense' }; const EVENT = 'aia-density-change'; export function readDensity(): Density { if (typeof window === 'undefined') return 'comfortable'; const d = window.localStorage.getItem(DENSITY_KEY); return d === 'compact' || d === 'dense' ? d : 'comfortable'; } export function applyDensity(d: Density) { if (typeof document === 'undefined') return; if (d === 'comfortable') document.documentElement.removeAttribute('data-density'); else document.documentElement.setAttribute('data-density', d); } export function setDensity(d: Density) { try { if (d === 'comfortable') window.localStorage.removeItem(DENSITY_KEY); else window.localStorage.setItem(DENSITY_KEY, d); } catch { /* storage disabled */ } applyDensity(d); window.dispatchEvent(new CustomEvent(EVENT)); } export function nextDensity(d: Density): Density { return DENSITIES[(DENSITIES.indexOf(d) + 1) % DENSITIES.length] as Density; } /** React binding: `[density, set, cycle]`; `density` is `comfortable` during SSR/hydration. */ export function useDensity(): [Density, (d: Density) => void, () => void] { const [d, setD] = useState('comfortable'); useEffect(() => { const sync = () => setD(readDensity()); sync(); applyDensity(readDensity()); window.addEventListener(EVENT, sync); window.addEventListener('storage', sync); return () => { window.removeEventListener(EVENT, sync); window.removeEventListener('storage', sync); }; }, []); const set = useCallback((n: Density) => { setDensity(n); setD(n); }, []); const cycle = useCallback(() => set(nextDensity(readDensity())), [set]); return [d, set, cycle]; }