# Chat settings — `tools/Chat/settings/`

The single, centralized home for every **chat-owned persisted setting**.

## Why this exists

Chat features used to each call `useLocalStorage` (or zustand `persist`)
with their own ad-hoc storage key. That made it impossible to reason
about, export, or reset "the chat's settings" as one thing, and
scattered the SSR / migration / coalescing concerns across the codebase.

This module gives chat **one typed object, one storage key, one hook**:

- `ChatSettings` — the typed shape of all chat-owned settings.
- `useChatSettings()` — the only hook a chat feature should use to read or
  write a persisted preference. Built on the improved `useLocalStorage`
  from `@djangocfg/ui-core`, which gives us:
  - **Coalesced writes** — several features patching different slices in
    the same render commit collapse into one `localStorage` write.
  - **Cross-instance / cross-tab sync** — two `useChatSettings()`
    consumers stay in lockstep, same tab and across tabs.
  - **Safe partial updates** — a slice updater that changes nothing is a
    no-op (no write, no re-render).

## API

```ts
const {
  settings,            // full typed ChatSettings object
  patch,               // grouped patch over whole slices
  updateDock,          // updateDock({ side: 'left' })
  updateAudio,
  updateSpeech,
  updatePageContext,
  setAudioMuted,       // shortcut
  setPageContextLinked,// shortcut
  reset,
} = useChatSettings();
```

Storage key: `djc.chat.settings`.

## Migration status

### Migrated

- **Page-context opt-in** — `lib/page-snapshot/react/provider.tsx` now
  reads/writes `ChatSettings.pageContext.linked` via `useChatSettings`
  instead of its own `useLocalStorage('djc.page-snapshot.linked')`.
  Note: this changes the storage key, so an existing user's prior opt-in
  is not carried over — they start from the default (`linked: false`).
  That is acceptable: page-context is opt-in and a fresh default is safe.

### To migrate (safe follow-up — deferred to keep this pass low-risk)

These are mature, working call sites. They each persist correctly today;
moving them into `ChatSettings` is a clear next step but was deliberately
deferred so a single PR does not churn the whole chat. Each migration
also changes a storage key (acceptable — fresh defaults).

- **Dock / layout prefs**
  - `tools/Chat/hooks/useChatDockPrefs.ts` — key `chat.dock.prefs`,
    shape `{ mode, side, sideWidth }` → `ChatSettings.dock`.
  - `tools/Chat/hooks/useChatLayout.ts` — keys `djc-chat-mode`
    (`STORAGE_KEYS.mode`) and `djc-chat-sidebar-width`
    (`STORAGE_KEYS.sidebarWidth`) → `ChatSettings.dock.mode` /
    `ChatSettings.dock.width`. Note `useChatLayout` stores `mode` and
    width as two separate keys; consolidate into the `dock` slice.
  - Constants live in `tools/Chat/constants.ts` (`STORAGE_KEYS`).

- **Audio prefs**
  - `tools/Chat/hooks/useChatAudio.ts` delegates to ui-core's
    `useNotificationSounds`, which persists under
    `djangocfg-chat-audio:prefs`. Migrating means either teaching
    `useNotificationSounds` to accept an external value/setter, or
    syncing `ChatSettings.audio` ⇆ that hook. Non-trivial — leave as is.

- **Speech / language prefs**
  - `tools/SpeechRecognition/store/prefsStore.ts` — a zustand `persist`
    store under `djangocfg-stt:prefs`, shape `{ language, deviceId,
    engineId, earcons }` → `ChatSettings.speech`. This is a different
    persistence mechanism (zustand, not `useLocalStorage`); migrating it
    means reconciling the store with `useChatSettings` and is the
    highest-effort of the three. Leave as is.

When migrating any of the above: update the consuming components, delete
the old key from `STORAGE_KEYS` where applicable, and update this file.
