'use client'; import type { ChatDisplayMode } from '../types'; /** * Dock / layout preferences. * * Mirrors the fields the existing `useChatDockPrefs` / `useChatLayout` * persist today. Kept as a nested slice so a feature can `updateDock({...})` * without touching audio / speech / page-context state. */ export interface ChatDockSettings { /** Floating popover vs. side-docked panel vs. embedded, etc. */ mode: ChatDisplayMode; /** Which edge a side dock attaches to. */ side: 'left' | 'right'; /** Width in px when side-docked. */ width: number; } /** * Audio / notification-sound preferences. * * `eventsMuted` is a sparse map: only events the user explicitly toggled * off appear here. An absent key means "use the default for that event". */ export interface ChatAudioSettings { /** Master mute — silences every chat sound. */ muted: boolean; /** Master volume, 0..1. */ volume: number; /** Per-event opt-outs, keyed by chat audio event name. */ eventsMuted: Record; } /** * Speech-recognition / language-picker preferences. * * `language` is a BCP-47 tag the user explicitly picked, or `null` for * "no override" — callers then fall back to the app locale. */ export interface ChatSpeechSettings { /** Explicit language override, or `null` to follow the app locale. */ language: string | null; /** Preferred capture device id, or `null` for the system default. */ deviceId: string | null; /** Preferred STT engine id, or `null` for auto-select. */ engineId: string | null; /** Earcon (audio cue) feedback on dictation start/stop. */ earcons: boolean; } /** Page-context (screen-sharing) opt-in. */ export interface ChatPageContextSettings { /** * Whether the user has opted in to sharing a snapshot of the page they * are viewing with the assistant. Persisted so the choice survives * reloads — the user enables it once. */ linked: boolean; } /** * The single, typed shape for every chat-owned persisted setting. * * Centralization rationale: chat features used to each call * `useLocalStorage` with their own ad-hoc key. That made it impossible to * reason about (or export/import) "the chat's settings" as one thing, and * scattered the SSR/migration concerns. One object, one storage key, one * hook — every feature reads/writes its own slice through it. */ export interface ChatSettings { dock: ChatDockSettings; audio: ChatAudioSettings; speech: ChatSpeechSettings; pageContext: ChatPageContextSettings; } /** Baseline defaults — used for SSR and first-run (nothing stored yet). */ export const DEFAULT_CHAT_SETTINGS: ChatSettings = { dock: { mode: 'closed', side: 'right', width: 420, }, audio: { muted: false, volume: 1, eventsMuted: {}, }, speech: { language: null, deviceId: null, engineId: null, earcons: false, }, pageContext: { linked: false, }, }; /** * Single localStorage key for the whole chat-settings object. * * One key (vs. one-per-feature) so the entire chat configuration is a * single read/write/coalesce unit — see {@link useChatSettings}. */ export const CHAT_SETTINGS_STORAGE_KEY = 'djc.chat.settings';