import type { ReactNode } from 'react'; import type { ChatContextValue } from '../context'; import type { ChatDockPrefs } from '../hooks/useChatDockPrefs'; /** * Declarative reset slot. The launcher renders the standard * `` wired to `onReset`, defaulting `onSuccess` * to `ctx.clearMessages()`. The button hides itself until a `sessionId` * exists on the chat context. */ export interface ChatHeaderResetSlot { /** Backend reset call. Resolves to `true` on success. */ onReset: () => Promise; /** Show a confirm dialog before calling `onReset`. @default true */ confirm?: boolean; /** Confirm dialog title. */ confirmTitle?: string; /** Confirm dialog message. */ confirmMessage?: string; /** Override tooltip / aria label. */ ariaLabel?: string; /** * Called after a successful reset. Defaults to `ctx.clearMessages()`. * Override to also re-fetch history, navigate, fire analytics, etc. */ onSuccess?: (ctx: ChatContextValue) => void; /** Called on failure (returned `false` or threw). */ onError?: (err?: unknown) => void; } /** Declarative language-picker slot. */ export interface ChatHeaderLanguageSlot { /** Subset of BCP-47 tags to offer. */ allowedTags?: string[]; /** Override aria-label. */ ariaLabel?: string; /** Hide the globe fallback icon when no flag resolves. */ hideFallbackIcon?: boolean; } /** * Declarative mode-toggle slot. The launcher owns `useChatDockPrefs` * internally — set `persistAs` to a localStorage key for persistence * (omit for ephemeral / session-only mode toggling). */ export interface ChatHeaderModeToggleSlot { /** * localStorage key for `useChatDockPrefs`. When provided the toggle * persists across reloads; omit to use an ephemeral in-memory toggle. */ persistAs?: string; /** Override the default `{ mode: 'popover', side: 'right', sideWidth: 420 }`. */ defaults?: Partial; /** Show the toggle even on viewports below `lg` (1024px). @default true */ forceVisible?: boolean; /** Tooltip / aria for popover → side. */ expandLabel?: string; /** Tooltip / aria for side → popover. */ collapseLabel?: string; } /** * Header buttons rendered INSIDE the `` mounted by * ``. Each entry is rendered in the fixed order: * * custom · languagePicker · modeToggle · audio · reset * * (close icon is always last, owned by ``). * * Slots that accept `boolean | object`: * - `true` → render with default config * - `false` → hide explicitly * - object → render with the given options * * Defaults: * - `audio`: rendered automatically when `audio` prop is passed to * `` AND the resolved instance is not silent. * - `modeToggle`, `languagePicker`: off unless opted in. * - `reset`: off unless `onReset` is provided. */ export interface ChatHeaderSlots { /** Auto-mute toggle. Defaults to true when launcher `audio` is configured. */ audio?: boolean; /** Popover ↔ side mode toggle. */ modeToggle?: boolean | ChatHeaderModeToggleSlot; /** Speech-recognition language picker. */ languagePicker?: boolean | ChatHeaderLanguageSlot; /** Reset-conversation button. */ reset?: ChatHeaderResetSlot; /** Arbitrary extra buttons rendered first (left-most). */ custom?: (ctx: ChatContextValue) => ReactNode; } /** * Resolved mode-toggle config used by the launcher to pick between * the dock's mode/side props and the prefs slot. */ export interface ResolvedChatHeaderSlots { audio: boolean; modeToggle: ChatHeaderModeToggleSlot | null; languagePicker: ChatHeaderLanguageSlot | null; reset: ChatHeaderResetSlot | null; custom: ((ctx: ChatContextValue) => ReactNode) | null; } export function resolveHeaderSlots( slots: ChatHeaderSlots | undefined, audioConfigured: boolean, ): ResolvedChatHeaderSlots { const s = slots ?? {}; const audio = s.audio ?? audioConfigured; const modeToggle: ChatHeaderModeToggleSlot | null = s.modeToggle === true ? {} : s.modeToggle && typeof s.modeToggle === 'object' ? s.modeToggle : null; const languagePicker: ChatHeaderLanguageSlot | null = s.languagePicker === true ? {} : s.languagePicker && typeof s.languagePicker === 'object' ? s.languagePicker : null; return { audio, modeToggle, languagePicker, reset: s.reset ?? null, custom: s.custom ?? null, }; }