// Cosmetic tweaks for the operator shell — theme (dark/light), density // (comfy/compact), env badge (prod/staging/dev). Persisted in localStorage so // the choices survive page reloads. The values are applied to as // data-* attributes; tokens.css picks them up via selector overrides. export type ThemeMode = 'dark' | 'light' export type Density = 'comfy' | 'compact' export type Env = 'prod' | 'staging' | 'dev' interface TweakState { theme: ThemeMode density: Density env: Env } const STORAGE_KEY = 'dezky-operator-tweaks' const DEFAULTS: TweakState = { theme: 'dark', density: 'comfy', env: 'dev' } const state = ref({ ...DEFAULTS }) const hydrated = ref(false) function apply() { if (!import.meta.client) return const root = document.documentElement root.setAttribute('data-theme', state.value.theme) root.setAttribute('data-density', state.value.density) } function persist() { if (!import.meta.client) return try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state.value)) } catch { // localStorage can throw in private mode; tweaks are cosmetic so swallow. } } function hydrate() { if (!import.meta.client || hydrated.value) return try { const raw = localStorage.getItem(STORAGE_KEY) if (raw) { const parsed = JSON.parse(raw) as Partial state.value = { ...DEFAULTS, ...parsed } } } catch { // ignore corrupt JSON } apply() hydrated.value = true } export const useTweaks = () => { if (import.meta.client) hydrate() function set(key: K, value: TweakState[K]) { state.value = { ...state.value, [key]: value } apply() persist() } return { state, setTheme: (v: ThemeMode) => set('theme', v), setDensity: (v: Density) => set('density', v), setEnv: (v: Env) => set('env', v), } }