// Local-only user preferences for push-to-talk. Stored in localStorage because // the server is zero-knowledge and doesn't need to know input-device details. const STORAGE_KEY = 'chatapp.ptt'; export interface PttSettings { enabled: boolean; // KeyboardEvent.code of the hold-to-talk key (e.g. 'Space', 'KeyV'). key: string; // Human-readable label derived from the key — kept in settings so we don't // re-derive it on every render. Updated together with `key`. keyLabel: string; } const DEFAULTS: PttSettings = { enabled: false, key: 'Space', keyLabel: 'Space', }; type Listener = (s: PttSettings) => void; const listeners = new Set(); let cached: PttSettings | null = null; function read(): PttSettings { if (cached) return cached; try { const raw = window.localStorage.getItem(STORAGE_KEY); if (!raw) { cached = DEFAULTS; return cached; } const parsed = JSON.parse(raw) as Partial; cached = { enabled: typeof parsed.enabled === 'boolean' ? parsed.enabled : DEFAULTS.enabled, key: typeof parsed.key === 'string' && parsed.key ? parsed.key : DEFAULTS.key, keyLabel: typeof parsed.keyLabel === 'string' && parsed.keyLabel ? parsed.keyLabel : DEFAULTS.keyLabel, }; return cached; } catch { cached = DEFAULTS; return cached; } } function write(s: PttSettings): void { cached = s; try { window.localStorage.setItem(STORAGE_KEY, JSON.stringify(s)); } catch { /* quota / private mode */ } for (const l of listeners) l(s); } export function getPttSettings(): PttSettings { return read(); } export function updatePttSettings(patch: Partial): PttSettings { const next = { ...read(), ...patch }; write(next); return next; } export function subscribePttSettings(listener: Listener): () => void { listeners.add(listener); return () => listeners.delete(listener); } // Turns a KeyboardEvent.code into a short human label (best-effort). export function keyCodeToLabel(code: string): string { if (code === 'Space') return 'Space'; if (code.startsWith('Key')) return code.slice(3); if (code.startsWith('Digit')) return code.slice(5); if (code.startsWith('Numpad')) return 'Num' + code.slice(6); if (code.startsWith('Arrow')) return code.slice(5) + ' Arrow'; return code; }