@djangocfg/ui-tools 2.1.387 → 2.1.389
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ChatRoot-EFNXQXXN.cjs +15 -0
- package/dist/{ChatRoot-4KM2JMGA.mjs.map → ChatRoot-EFNXQXXN.cjs.map} +1 -1
- package/dist/ChatRoot-FITF5RVP.mjs +6 -0
- package/dist/{ChatRoot-OILWMMZ6.cjs.map → ChatRoot-FITF5RVP.mjs.map} +1 -1
- package/dist/{DocsLayout-2P3ONDWJ.mjs → DocsLayout-EKASBSP7.mjs} +3 -3
- package/dist/{DocsLayout-2P3ONDWJ.mjs.map → DocsLayout-EKASBSP7.mjs.map} +1 -1
- package/dist/{DocsLayout-2YZNS5VK.cjs → DocsLayout-OURFYWQE.cjs} +8 -8
- package/dist/{DocsLayout-2YZNS5VK.cjs.map → DocsLayout-OURFYWQE.cjs.map} +1 -1
- package/dist/{chunk-HIK6BPL7.mjs → chunk-2NG4SXEP.mjs} +6 -5
- package/dist/chunk-2NG4SXEP.mjs.map +1 -0
- package/dist/{chunk-TSNRU3UO.cjs → chunk-4LFB7I5K.cjs} +15 -15
- package/dist/{chunk-TSNRU3UO.cjs.map → chunk-4LFB7I5K.cjs.map} +1 -1
- package/dist/{chunk-HNIMIIFR.mjs → chunk-6ZX2G25W.mjs} +3 -3
- package/dist/{chunk-HNIMIIFR.mjs.map → chunk-6ZX2G25W.mjs.map} +1 -1
- package/dist/{chunk-FIRK5CEH.cjs → chunk-7IYXZUJO.cjs} +8 -4
- package/dist/chunk-7IYXZUJO.cjs.map +1 -0
- package/dist/{chunk-BVESQTBM.mjs → chunk-NTVBIIUD.mjs} +2 -2
- package/dist/{chunk-BVESQTBM.mjs.map → chunk-NTVBIIUD.mjs.map} +1 -1
- package/dist/{chunk-L25HA3TM.cjs → chunk-W75B7Y6C.cjs} +2 -2
- package/dist/{chunk-L25HA3TM.cjs.map → chunk-W75B7Y6C.cjs.map} +1 -1
- package/dist/index.cjs +265 -253
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +255 -218
- package/dist/index.d.ts +255 -218
- package/dist/index.mjs +24 -24
- package/dist/index.mjs.map +1 -1
- package/dist/{launcher-5WYPDPEP.mjs → launcher-5Y42OBSN.mjs} +4 -5
- package/dist/{launcher-5WYPDPEP.mjs.map → launcher-5Y42OBSN.mjs.map} +1 -1
- package/dist/{launcher-QAOG2NUI.cjs → launcher-PMW2YB24.cjs} +16 -17
- package/dist/{launcher-QAOG2NUI.cjs.map → launcher-PMW2YB24.cjs.map} +1 -1
- package/package.json +6 -6
- package/src/components/index.ts +2 -2
- package/src/index.ts +20 -2
- package/src/tools/Chat/components/MessageBubble.tsx +1 -1
- package/src/tools/Chat/index.ts +1 -1
- package/src/tools/Chat/lazy.tsx +91 -4
- package/src/{components/markdown → tools}/MarkdownMessage/CodeBlock.tsx +1 -1
- package/src/{components/markdown → tools}/MarkdownMessage/CollapseToggle.tsx +1 -1
- package/src/{components/markdown → tools}/MarkdownMessage/MarkdownMessage.tsx +1 -1
- package/src/{components/markdown → tools}/MarkdownMessage/components.tsx +2 -2
- package/src/tools/OpenapiViewer/components/DocsLayout/ApiIntroSection.tsx +1 -1
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/index.tsx +1 -1
- package/dist/ChatRoot-4KM2JMGA.mjs +0 -6
- package/dist/ChatRoot-OILWMMZ6.cjs +0 -15
- package/dist/chunk-FIRK5CEH.cjs.map +0 -1
- package/dist/chunk-HIK6BPL7.mjs.map +0 -1
- package/dist/launcher-FCI3LTDY.css +0 -7
- package/dist/launcher-FCI3LTDY.css.map +0 -1
- package/src/components/markdown/index.ts +0 -19
- /package/src/{components/markdown → hooks}/useCollapsibleContent.ts +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/ActionRow.tsx +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/ChatMessageRow.tsx +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/README.md +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/index.ts +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/linkRules.ts +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/plainText.ts +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/sanitize.ts +0 -0
- /package/src/{components/markdown → tools}/MarkdownMessage/types.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tools/Chat/launcher/ChatFAB.tsx","../src/tools/Chat/launcher/ChatHeader.tsx","../src/tools/Chat/launcher/useChatPresence.ts","../src/tools/Chat/launcher/ChatDock.tsx","../src/tools/Chat/launcher/ChatHeaderActionButton.tsx","../src/tools/Chat/launcher/ChatHeaderModeToggle.tsx","../src/tools/Chat/launcher/ChatHeaderAudioToggle.tsx","../src/tools/Chat/hooks/useChatReset.ts","../src/tools/Chat/launcher/ChatHeaderResetButton.tsx","../src/tools/SpeechRecognition/core/languages-catalog.ts","../src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx","../src/tools/SpeechRecognition/context/SpeechRecognitionProvider.tsx","../src/components/lazy-wrapper.tsx","../src/tools/SpeechRecognition/lazy.tsx","../src/tools/Chat/launcher/ChatHeaderLanguageButton.tsx","../src/tools/Chat/launcher/ChatGreeting.tsx","../src/tools/Chat/launcher/ChatUnreadPreview.tsx","../src/tools/Chat/launcher/ChatLauncher.tsx"],"names":["jsxs","cn","jsx","Bot","useIsTabletOrBelow","useEffect","ChatHeaderActionButton","useState","useMemo","X","anchorStyle","originClass","useRef","useCallback","Fragment"],"mappings":"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","file":"chunk-BVESQTBM.mjs","sourcesContent":["'use client';\n\nimport { Bot } from 'lucide-react';\nimport type { CSSProperties, ReactNode } from 'react';\n\nimport { useIsPhone, useIsTabletOrBelow } from '@djangocfg/ui-core/hooks';\nimport { cn } from '@djangocfg/ui-core/lib';\n\nexport type ChatFABPosition =\n | 'bottom-right'\n | 'bottom-left'\n | 'top-right'\n | 'top-left';\n\nexport type ChatFABVariant = 'simple' | 'animated' | 'glass';\nexport type ChatFABSize = 'sm' | 'md' | 'lg' | 'responsive';\n\nexport interface ChatFABProps {\n /** Click handler — typically toggles a `ChatDock`. */\n onClick: () => void;\n /** Accessible label. */\n ariaLabel?: string;\n /** Icon inside the FAB. Defaults to a bot glyph. */\n icon?: ReactNode;\n /** Visual style. @default 'simple' */\n variant?: ChatFABVariant;\n /** Button size. @default 'md' */\n size?: ChatFABSize;\n /** Fixed-screen position. @default 'bottom-right' */\n position?: ChatFABPosition;\n /** Pixel offset from screen edges. @default 24 */\n offset?: number;\n /** z-index for the button. @default 9999 */\n zIndex?: number;\n /** Show a small attention dot (unread / new). */\n pulse?: boolean;\n /**\n * Numeric badge — unread count. Numbers > 9 render as \"9+\".\n * Overrides `pulse` when both set.\n */\n badge?: number;\n /** Hover tooltip text. Shows next to the FAB on hover/focus. */\n tooltip?: string;\n /**\n * Render in-place (no fixed positioning) so the FAB sits inline in the\n * normal document flow. Useful for stories, screenshots, and previews\n * inside a contained playground panel. @default false\n */\n inline?: boolean;\n /** Override classes on the button itself. */\n className?: string;\n /** Extra style on the button (caller-controlled overrides). */\n style?: CSSProperties;\n}\n\ntype ChatFABFixedSize = Exclude<ChatFABSize, 'responsive'>;\n\nconst SIZE_PX: Record<ChatFABFixedSize, number> = { sm: 44, md: 56, lg: 64 };\nconst ICON_PX: Record<ChatFABFixedSize, number> = { sm: 18, md: 22, lg: 26 };\n\n/**\n * Resolve `size='responsive'` to a concrete fixed size based on the\n * viewport. Phone → `sm`, tablet → `md`, desktop → `lg`. `inline`\n * previews always collapse to `md` so stories stay stable.\n */\nfunction useEffectiveFABSize(size: ChatFABSize, inline: boolean): ChatFABFixedSize {\n const isPhone = useIsPhone();\n const isBelowDesktop = useIsTabletOrBelow();\n if (size !== 'responsive') return size;\n if (inline) return 'md';\n if (isPhone) return 'sm';\n if (isBelowDesktop) return 'md';\n return 'lg';\n}\n\nfunction positionStyle(position: ChatFABPosition, offset: number): CSSProperties {\n const [vert, horiz] = position.split('-') as ['bottom' | 'top', 'right' | 'left'];\n return { [vert]: offset, [horiz]: offset } as CSSProperties;\n}\n\nfunction tooltipSideClasses(position: ChatFABPosition): string {\n // Tooltip sits opposite-horizontal to the FAB so it doesn't run off-screen.\n return position.endsWith('right')\n ? 'right-full mr-3 origin-right'\n : 'left-full ml-3 origin-left';\n}\n\nfunction Badge({ value }: { value: number }) {\n const display = value > 9 ? '9+' : String(value);\n return (\n <span\n aria-hidden=\"true\"\n className={cn(\n 'absolute -right-1 -top-1 inline-flex min-w-[18px] h-[18px] items-center justify-center',\n 'rounded-full bg-destructive px-1 text-[10px] font-semibold leading-none text-destructive-foreground',\n 'ring-2 ring-background',\n )}\n >\n {display}\n </span>\n );\n}\n\nfunction PulseDot() {\n return (\n <span aria-hidden=\"true\" className=\"absolute right-1 top-1\">\n <span className=\"relative inline-flex h-2.5 w-2.5\">\n <span className=\"absolute inset-0 rounded-full bg-destructive opacity-75 animate-ping\" />\n <span className=\"relative inline-flex h-2.5 w-2.5 rounded-full bg-destructive ring-2 ring-background\" />\n </span>\n </span>\n );\n}\n\nfunction Tooltip({ text, side }: { text: string; side: string }) {\n return (\n <span\n role=\"tooltip\"\n className={cn(\n 'pointer-events-none absolute top-1/2 -translate-y-1/2 whitespace-nowrap',\n 'rounded-md bg-popover px-2.5 py-1 text-xs font-medium text-popover-foreground shadow-md',\n 'border border-border opacity-0 scale-95 transition-all duration-150',\n 'group-hover:opacity-100 group-hover:scale-100',\n 'group-focus-within:opacity-100 group-focus-within:scale-100',\n side,\n )}\n >\n {text}\n </span>\n );\n}\n\n/**\n * Floating action button for opening a chat dock. Pure presentation — owns\n * no state. Wire it to whatever toggles your dock open.\n *\n * For the common \"FAB + dock + hotkey\" composition, use `<ChatLauncher>` —\n * this primitive is only useful when you need custom triggering.\n */\nexport function ChatFAB({\n onClick,\n ariaLabel = 'Open chat',\n icon,\n variant = 'simple',\n size = 'responsive',\n position = 'bottom-right',\n offset = 24,\n zIndex = 9999,\n pulse = false,\n badge,\n tooltip,\n inline = false,\n className,\n style,\n}: ChatFABProps) {\n const effectiveSize = useEffectiveFABSize(size, inline);\n const px = SIZE_PX[effectiveSize];\n const iconPx = ICON_PX[effectiveSize];\n const renderedIcon = icon ?? <Bot size={iconPx} />;\n\n const baseButton = cn(\n 'relative grid place-items-center rounded-full focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n 'transition-transform hover:scale-105',\n );\n\n return (\n <div\n className={cn('group', inline ? 'relative inline-flex' : 'fixed')}\n style={\n inline\n ? undefined\n : { ...positionStyle(position, offset), zIndex }\n }\n >\n {variant === 'animated' && (\n <AnimatedFAB\n ariaLabel={ariaLabel}\n onClick={onClick}\n size={px}\n className={className}\n style={style}\n >\n {renderedIcon}\n </AnimatedFAB>\n )}\n\n {variant === 'glass' && (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n onClick={onClick}\n className={cn(\n baseButton,\n 'border border-border/40 bg-background/60 text-foreground shadow-lg backdrop-blur-xl',\n 'hover:bg-background/80',\n className,\n )}\n style={{ width: px, height: px, ...style }}\n >\n {renderedIcon}\n {badge !== undefined ? <Badge value={badge} /> : pulse ? <PulseDot /> : null}\n </button>\n )}\n\n {variant === 'simple' && (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n onClick={onClick}\n className={cn(\n baseButton,\n 'bg-primary text-primary-foreground hover:bg-primary/90 shadow-2xl',\n className,\n )}\n style={{ width: px, height: px, ...style }}\n >\n {renderedIcon}\n {badge !== undefined ? <Badge value={badge} /> : pulse ? <PulseDot /> : null}\n </button>\n )}\n\n {tooltip && <Tooltip text={tooltip} side={tooltipSideClasses(position)} />}\n </div>\n );\n}\n\n// ── Animated variant ──────────────────────────────────────────────────────\n// Orbital gradient ring + glow + entrance bounce. Self-contained CSS via\n// inline <style>, scoped by a unique class name per instance to avoid\n// collisions when multiple FABs mount.\n\ninterface AnimatedFABProps {\n ariaLabel: string;\n onClick: () => void;\n size: number;\n className?: string;\n style?: CSSProperties;\n children: ReactNode;\n}\n\nfunction AnimatedFAB({ ariaLabel, onClick, size, className, style, children }: AnimatedFABProps) {\n return (\n <>\n <style>{ANIMATED_CSS}</style>\n <div\n className={cn('cmdop-fab-anim', className)}\n style={{ width: size, height: size, ...style }}\n >\n <div className=\"cmdop-fab-anim-glow\">\n <div className=\"cmdop-fab-anim-wrap\">\n <div className=\"cmdop-fab-anim-grad cmdop-fab-anim-grad-1\" />\n <div className=\"cmdop-fab-anim-grad cmdop-fab-anim-grad-2\" />\n <div className=\"cmdop-fab-anim-inner\" />\n <button\n type=\"button\"\n aria-label={ariaLabel}\n onClick={onClick}\n className=\"cmdop-fab-anim-btn\"\n >\n <span className=\"cmdop-fab-anim-icon\">{children}</span>\n </button>\n </div>\n </div>\n </div>\n </>\n );\n}\n\nconst ANIMATED_CSS = `\n.cmdop-fab-anim {\n position: relative;\n pointer-events: auto;\n}\n.cmdop-fab-anim-glow {\n width: 100%; height: 100%;\n border-radius: 50%;\n overflow: hidden;\n animation:\n cmdop-fab-entrance 0.6s cubic-bezier(0.34, 1.45, 0.64, 1) forwards,\n cmdop-fab-glow-shift 8s ease-in-out 0.6s infinite;\n}\n.cmdop-fab-anim-wrap {\n position: relative; width: 100%; height: 100%;\n border-radius: 50%; overflow: hidden;\n}\n.cmdop-fab-anim-grad { position: absolute; inset: 0; border-radius: 50%; }\n.cmdop-fab-anim-grad-1 {\n background: conic-gradient(\n from 0deg,\n #fbbf24 0%, rgba(251,191,36,0) 15%,\n rgba(168,85,247,0) 20%, #a855f7 35%, rgba(168,85,247,0) 50%,\n rgba(20,184,166,0) 55%, #14b8a6 70%, rgba(20,184,166,0) 85%,\n rgba(236,72,153,0) 88%, #ec4899 97%, #fbbf24 100%\n );\n animation: cmdop-fab-rotate 7s linear infinite;\n filter: blur(1px); opacity: 0.95;\n}\n.cmdop-fab-anim-grad-2 {\n inset: 1px;\n background: conic-gradient(\n from 180deg,\n #a855f7 0%, rgba(168,85,247,0) 20%,\n rgba(20,184,166,0) 30%, #14b8a6 50%, rgba(20,184,166,0) 70%,\n rgba(251,191,36,0) 75%, #fbbf24 95%, #a855f7 100%\n );\n animation: cmdop-fab-rotate-rev 9s linear infinite;\n filter: blur(0.75px); opacity: 0.7;\n}\n.cmdop-fab-anim-inner {\n position: absolute; inset: 3px; border-radius: 50%;\n background: rgba(10, 10, 10, 0.65);\n backdrop-filter: blur(12px) saturate(1.8);\n -webkit-backdrop-filter: blur(12px) saturate(1.8);\n animation: cmdop-fab-inner-glow 5s ease-in-out infinite;\n}\n.cmdop-fab-anim-btn {\n position: absolute; inset: 2px;\n border-radius: 50%; border: none; background: transparent;\n cursor: pointer; display: flex; align-items: center; justify-content: center;\n transition: transform 0.2s;\n}\n.cmdop-fab-anim-btn:hover { transform: scale(1.06); }\n.cmdop-fab-anim-icon {\n color: #fbbf24; display: flex;\n filter: drop-shadow(0 0 6px rgba(251,191,36,0.8));\n animation: cmdop-fab-icon-pulse 2.5s ease-in-out infinite;\n}\n@keyframes cmdop-fab-rotate { to { transform: rotate(360deg); } }\n@keyframes cmdop-fab-rotate-rev { to { transform: rotate(-360deg); } }\n@keyframes cmdop-fab-entrance {\n 0% { transform: scale(0); }\n 50% { transform: scale(1.08); }\n 70% { transform: scale(0.98); }\n 100% { transform: scale(1); }\n}\n@keyframes cmdop-fab-glow-shift {\n 0%, 100% { box-shadow: 0 0 20px rgba(251,191,36,0.5), 0 0 40px rgba(168,85,247,0.3); }\n 33% { box-shadow: 0 0 20px rgba(168,85,247,0.5), 0 0 40px rgba(20,184,166,0.3); }\n 66% { box-shadow: 0 0 20px rgba(20,184,166,0.5), 0 0 40px rgba(236,72,153,0.3); }\n}\n@keyframes cmdop-fab-icon-pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.85; transform: scale(1.15); }\n}\n@keyframes cmdop-fab-inner-glow {\n 0%, 100% { box-shadow: inset 0 0 20px rgba(251,191,36,0.25), inset 0 0 40px rgba(168,85,247,0.15); }\n 50% { box-shadow: inset 0 0 25px rgba(168,85,247,0.3), inset 0 0 45px rgba(20,184,166,0.2); }\n}\n`;\n","'use client';\n\nimport { Bot, X } from 'lucide-react';\nimport type { ReactNode } from 'react';\n\nimport { Button } from '@djangocfg/ui-core/components';\nimport { cn } from '@djangocfg/ui-core/lib';\n\nexport interface ChatHeaderProps {\n /** Window title text. */\n title?: ReactNode;\n /** Icon next to the title. Defaults to a bot glyph. */\n icon?: ReactNode;\n /**\n * Action slot — appears to the right of the title, before the close button.\n * Use for reset / settings / minimize / etc. Compose with `ChatHeaderActionButton`.\n */\n actions?: ReactNode;\n /** Show the close (×) button. @default true */\n showClose?: boolean;\n /** Close click handler. */\n onClose?: () => void;\n /** ARIA label for the close button. @default 'Close' */\n closeLabel?: string;\n /** Replace the close button entirely (rare — most hosts want the default). */\n closeSlot?: ReactNode;\n /** Extra classes on the `<header>` element. */\n className?: string;\n}\n\n/**\n * Standalone chat header — title + icon + action slot + close button.\n *\n * Used by `<ChatDock>` automatically, but can be rendered standalone when\n * the host owns the chat container (e.g. embedded inline in a page).\n */\nexport function ChatHeader({\n title,\n icon,\n actions,\n showClose = true,\n onClose,\n closeLabel = 'Close',\n closeSlot,\n className,\n}: ChatHeaderProps) {\n return (\n <header\n className={cn(\n 'border-border bg-muted/30 flex shrink-0 items-center justify-between border-b px-4 py-2.5',\n className,\n )}\n >\n <div className=\"flex min-w-0 items-center gap-2 text-sm font-semibold\">\n {icon ?? <Bot className=\"text-primary h-4 w-4 shrink-0\" />}\n <span className=\"truncate\">{title}</span>\n </div>\n\n <div className=\"flex items-center gap-0.5\">\n {actions}\n {closeSlot ??\n (showClose && onClose && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={onClose}\n aria-label={closeLabel}\n className=\"-mr-1 h-7 w-7 p-0\"\n >\n <X className=\"h-4 w-4\" />\n </Button>\n ))}\n </div>\n </header>\n );\n}\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nexport type ChatPresencePhase = 'hidden' | 'entering' | 'visible' | 'leaving';\n\n/**\n * Presence state machine for floating popovers.\n *\n * Drives a four-phase lifecycle so enter/leave CSS transitions actually fire:\n *\n * hidden → entering (mount, transition class starts) → visible\n * visible → leaving (transition runs) → hidden (unmount)\n *\n * Mounting in `entering` and ticking to `visible` on the next paint is what\n * lets transition classes animate. Without it the element appears already\n * at its final state and CSS transitions never observe a change.\n *\n * @param open - controlled open state\n * @param exitDurationMs - how long the leave transition runs; should match CSS\n */\nexport function useChatPresence(open: boolean, exitDurationMs = 200): ChatPresencePhase {\n const [phase, setPhase] = useState<ChatPresencePhase>('hidden');\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (timerRef.current) clearTimeout(timerRef.current);\n\n if (open) {\n setPhase('entering');\n // One paint later: switch to 'visible' so the CSS transition runs.\n timerRef.current = setTimeout(() => setPhase('visible'), 16);\n } else {\n setPhase('leaving');\n timerRef.current = setTimeout(() => setPhase('hidden'), exitDurationMs);\n }\n\n return () => {\n if (timerRef.current) clearTimeout(timerRef.current);\n };\n }, [open, exitDurationMs]);\n\n return phase;\n}\n","'use client';\n\nimport { useEffect } from 'react';\nimport type { CSSProperties, ReactNode } from 'react';\n\nimport { Portal } from '@djangocfg/ui-core/components';\nimport { useIsMobile, useIsTabletOrBelow } from '@djangocfg/ui-core/hooks';\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport { ChatHeader } from './ChatHeader';\nimport { useChatPresence } from './useChatPresence';\nimport type { ChatFABPosition } from './ChatFAB';\n\nexport type ChatDockMode = 'popover' | 'side';\nexport type ChatDockSide = 'left' | 'right';\n\nexport interface ChatDockProps {\n /** Controlled open state. */\n open: boolean;\n /** Called when the user clicks the close button. */\n onClose: () => void;\n /** Dock contents (typically a `<Chat>` component). */\n children: ReactNode;\n /**\n * Visual mode.\n * - `popover` (default): floating card anchored to a corner, fixed size, FAB-style.\n * - `side`: docked panel pinned to the left/right edge, full viewport height.\n */\n mode?: ChatDockMode;\n /** Side for `mode='side'`. @default 'right' */\n side?: ChatDockSide;\n /** Header title text. */\n title?: ReactNode;\n /** Header icon. Defaults to a bot glyph. */\n icon?: ReactNode;\n /**\n * Header actions slot (right side, before the close button).\n * Use `ChatHeaderActionButton` to keep visual consistency.\n */\n headerActions?: ReactNode;\n /** Hide the header entirely (you render your own inside `children`). */\n hideHeader?: boolean;\n /** ARIA label for the close button. @default 'Close' */\n closeLabel?: string;\n /** Dock width in px. Clamped to viewport. @default 480 (popover) / 420 (side) */\n width?: number;\n /** Dock height in px. Only used in `popover` mode. @default 720 */\n height?: number;\n /** Which screen corner to dock to in `popover` mode. @default 'bottom-right' */\n position?: ChatFABPosition;\n /** Offset from screen edges in px (popover only). @default 24 / 96 */\n offset?: { horizontal?: number; vertical?: number };\n /** Transition duration in ms — should match CSS animation. @default 200 */\n exitDurationMs?: number;\n /** z-index. @default 10000 */\n zIndex?: number;\n /** Accessible dialog label. */\n ariaLabel?: string;\n /** Extra classes on the dock container. */\n className?: string;\n /**\n * Take over the full viewport on mobile (< 768px). Applies to both modes.\n * @default true\n */\n mobileFullscreen?: boolean;\n /**\n * Render in-place (not in `document.body` via a portal). Useful for stories,\n * screenshots, or wrapping the dock inside a custom container. @default false\n */\n disablePortal?: boolean;\n /**\n * Drop fixed positioning entirely — the dock renders as a normal flow\n * element sized by `width`/`height`. Combine with `disablePortal` for\n * stories/previews where the dock should sit inside the panel instead\n * of attaching to the viewport. @default false\n */\n inline?: boolean;\n /**\n * In `mode='side'`, reserve space on the document body so page content\n * isn't covered by the dock. Sets `padding-{side}` on `<body>` while\n * the dock is open and exposes the width via the `--chat-dock-reserve`\n * CSS variable for custom layouts. @default true (when mode='side')\n */\n reserveBodySpace?: boolean;\n}\n\nfunction dockPositionStyle(\n position: ChatFABPosition,\n horizontal: number,\n vertical: number,\n): CSSProperties {\n const [vert, horiz] = position.split('-') as ['bottom' | 'top', 'right' | 'left'];\n return { [vert]: vertical, [horiz]: horizontal } as CSSProperties;\n}\n\n/**\n * Fixed-position chat surface. Two modes:\n *\n * - `popover` — floating card anchored to a corner. Companion to `<ChatFAB>`.\n * - `side` — full-height panel pinned to the left/right edge. App-shell style.\n *\n * Renders only when `open` is true (plus the leave-transition tail). Uses\n * `useChatPresence` for the four-phase mount/animate/unmount cycle.\n */\nexport function ChatDock({\n open,\n onClose,\n children,\n mode = 'popover',\n side = 'right',\n title = 'Chat',\n icon,\n headerActions,\n hideHeader = false,\n closeLabel,\n width,\n height = 720,\n position = 'bottom-right',\n offset,\n exitDurationMs = 200,\n zIndex = 10000,\n ariaLabel,\n className,\n mobileFullscreen = true,\n disablePortal = false,\n inline = false,\n reserveBodySpace,\n}: ChatDockProps) {\n const phase = useChatPresence(open, exitDurationMs);\n const isMobile = useIsMobile();\n // Side mode is desktop-only — narrow viewports fall back to popover so\n // we never cover 33% of a phone/tablet with a chat panel.\n const isBelowDesktop = useIsTabletOrBelow();\n const effectiveMode: ChatDockMode =\n mode === 'side' && !isBelowDesktop ? 'side' : 'popover';\n const fullscreen = mobileFullscreen && isMobile;\n\n // Reserve body padding for side mode so page content stays visible\n // next to the dock. Auto-on when mode='side' unless explicitly disabled.\n const wantsReserve =\n !inline && !fullscreen && effectiveMode === 'side' && (reserveBodySpace ?? true);\n const resolvedSideWidth = width ?? 420;\n useEffect(() => {\n if (!wantsReserve || phase === 'hidden') return;\n const body = document.body;\n if (!body) return;\n const cssVar = `${resolvedSideWidth}px`;\n const padKey = side === 'right' ? 'paddingRight' : 'paddingLeft';\n const prevPad = body.style[padKey as 'paddingRight' | 'paddingLeft'];\n const prevVar = body.style.getPropertyValue('--chat-dock-reserve');\n body.style[padKey as 'paddingRight' | 'paddingLeft'] = cssVar;\n body.style.setProperty('--chat-dock-reserve', cssVar);\n return () => {\n body.style[padKey as 'paddingRight' | 'paddingLeft'] = prevPad;\n if (prevVar) body.style.setProperty('--chat-dock-reserve', prevVar);\n else body.style.removeProperty('--chat-dock-reserve');\n };\n }, [wantsReserve, phase, side, resolvedSideWidth]);\n\n if (phase === 'hidden') return null;\n\n const animating = phase === 'entering' || phase === 'leaving';\n\n const horizontal = offset?.horizontal ?? 24;\n const vertical = offset?.vertical ?? 96;\n const resolvedWidth = width ?? (effectiveMode === 'side' ? resolvedSideWidth : 480);\n\n let containerStyle: CSSProperties;\n let cornerClass: string;\n\n // Dynamic viewport heights — `dvh` follows iOS Safari URL bar (preferred),\n // `svh`/`lvh` are the small/large fallbacks if the dynamic value isn't\n // supported. Min-height keeps the popover usable even on tiny landscape phones.\n const dynVH = '100dvh';\n\n if (inline) {\n containerStyle = {\n position: 'relative',\n width: resolvedWidth,\n height,\n maxHeight: `calc(${dynVH} - 16px)`,\n pointerEvents: phase === 'visible' ? 'auto' : 'none',\n };\n cornerClass = 'rounded-xl border';\n } else if (fullscreen) {\n containerStyle = {\n position: 'fixed',\n top: 0,\n [side === 'left' ? 'left' : 'right']: 0,\n width: '100vw',\n height: dynVH,\n zIndex,\n pointerEvents: phase === 'visible' ? 'auto' : 'none',\n } as CSSProperties;\n cornerClass = 'rounded-none border-0';\n } else if (effectiveMode === 'side') {\n containerStyle = {\n position: 'fixed',\n top: 0,\n [side]: 0,\n height: dynVH,\n zIndex,\n width: `min(${resolvedWidth}px, 100vw)`,\n pointerEvents: phase === 'visible' ? 'auto' : 'none',\n } as CSSProperties;\n cornerClass = side === 'right' ? 'rounded-none border-l' : 'rounded-none border-r';\n } else {\n // popover — anchored to a corner, capped to viewport so it never\n // overlaps the FAB or goes off-screen on small windows.\n const heightCap = `calc(${dynVH} - ${vertical + 24}px)`;\n containerStyle = {\n position: 'fixed',\n ...dockPositionStyle(position, horizontal, vertical),\n zIndex,\n width: `min(${resolvedWidth}px, calc(100vw - 32px))`,\n height: `min(${height}px, ${heightCap})`,\n minHeight: `min(320px, ${heightCap})`,\n pointerEvents: phase === 'visible' ? 'auto' : 'none',\n };\n cornerClass = 'rounded-xl border';\n }\n\n // Per-mode enter/leave transform classes — side slides in horizontally,\n // popover scales + lifts.\n const enterClass = (() => {\n if (fullscreen) return 'opacity-0';\n if (effectiveMode === 'side') {\n return side === 'right' ? 'opacity-0 translate-x-4' : 'opacity-0 -translate-x-4';\n }\n return 'opacity-0 scale-95 translate-y-2';\n })();\n const visibleClass = 'opacity-100 scale-100 translate-y-0 translate-x-0';\n\n return (\n <Portal disablePortal={disablePortal || inline}>\n <div\n role=\"dialog\"\n aria-label={ariaLabel ?? (typeof title === 'string' ? title : 'Chat')}\n aria-hidden={phase === 'leaving'}\n className={cn(\n 'bg-popover text-popover-foreground border-border',\n 'flex flex-col overflow-hidden shadow-2xl',\n cornerClass,\n 'transition-all duration-200 ease-out',\n animating ? enterClass : visibleClass,\n className,\n )}\n style={containerStyle}\n >\n {!hideHeader && (\n <ChatHeader\n title={title}\n icon={icon}\n actions={headerActions}\n onClose={onClose}\n closeLabel={closeLabel}\n />\n )}\n <div className=\"min-h-0 min-w-0 flex-1 overflow-hidden\">{children}</div>\n </div>\n </Portal>\n );\n}\n","'use client';\n\nimport { forwardRef } from 'react';\nimport type { ButtonHTMLAttributes, ReactNode } from 'react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nexport interface ChatHeaderActionButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {\n /** Icon (required). */\n icon: ReactNode;\n /** Accessible label + native tooltip. */\n ariaLabel: string;\n /** Optional unread / status badge — small number on top-right. */\n badge?: number;\n /** Mark as destructive — uses destructive hover tokens. */\n destructive?: boolean;\n /** Optional visual loading state (e.g. while reset is in flight). */\n loading?: boolean;\n}\n\n/**\n * Compact icon button for the chat header actions slot.\n *\n * Standard chrome: 28×28 ghost button, hover bg-accent, focus ring, optional\n * destructive variant, optional numeric badge for unread / pending.\n *\n * @example\n * ```tsx\n * <ChatHeader\n * title=\"Assistant\"\n * onClose={close}\n * actions={\n * <>\n * <ChatHeaderActionButton\n * icon={<RotateCcw className=\"h-3.5 w-3.5\" />}\n * ariaLabel=\"Clear context\"\n * onClick={handleReset}\n * loading={isResetting}\n * />\n * <ChatHeaderActionButton\n * icon={<Settings className=\"h-3.5 w-3.5\" />}\n * ariaLabel=\"Settings\"\n * onClick={openSettings}\n * />\n * </>\n * }\n * />\n * ```\n */\nexport const ChatHeaderActionButton = forwardRef<HTMLButtonElement, ChatHeaderActionButtonProps>(\n function ChatHeaderActionButton(\n { icon, ariaLabel, badge, destructive, loading, disabled, className, ...rest },\n ref,\n ) {\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-label={ariaLabel}\n title={ariaLabel}\n disabled={disabled || loading}\n className={cn(\n 'relative inline-flex h-7 w-7 items-center justify-center rounded-md',\n 'text-muted-foreground transition-colors',\n 'hover:bg-accent hover:text-foreground',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n 'disabled:opacity-50 disabled:cursor-not-allowed',\n destructive && 'hover:bg-destructive/15 hover:text-destructive',\n loading && 'animate-pulse',\n className,\n )}\n {...rest}\n >\n {icon}\n {badge !== undefined && (\n <span\n aria-hidden=\"true\"\n className=\"absolute -right-0.5 -top-0.5 inline-flex min-w-[14px] h-[14px] items-center justify-center rounded-full bg-destructive px-1 text-[9px] font-semibold leading-none text-destructive-foreground ring-2 ring-background\"\n >\n {badge > 9 ? '9+' : badge}\n </span>\n )}\n </button>\n );\n },\n);\n","'use client';\n\nimport { PanelRightOpen, PanelRightClose } from 'lucide-react';\n\nimport { useIsTabletOrBelow } from '@djangocfg/ui-core/hooks';\n\nimport { ChatHeaderActionButton } from './ChatHeaderActionButton';\nimport type { ChatDockMode } from './ChatDock';\n\nexport interface ChatHeaderModeToggleProps {\n /** Current dock mode. */\n mode: ChatDockMode;\n /** Toggle handler. Wire to `useChatDockPrefs().toggleMode`. */\n onToggle: () => void;\n /** Override aria/tooltip label for popover→side. */\n expandLabel?: string;\n /** Override aria/tooltip label for side→popover. */\n collapseLabel?: string;\n /**\n * Always render — useful for stories. By default the toggle hides itself on\n * viewports below `lg` (1024px) since side mode falls back to popover there.\n */\n forceVisible?: boolean;\n}\n\n/**\n * \"Dock to side\" / \"back to popover\" toggle.\n *\n * Side mode is desktop-only — on viewports below `lg` (1024px) `<ChatDock>`\n * silently falls back to popover anyway, so the toggle has nothing to do\n * and auto-hides. Pass `forceVisible` to override (e.g. in stories).\n */\nexport function ChatHeaderModeToggle({\n mode,\n onToggle,\n expandLabel = 'Dock to side',\n collapseLabel = 'Back to popover',\n forceVisible = false,\n}: ChatHeaderModeToggleProps) {\n const isBelowDesktop = useIsTabletOrBelow();\n if (isBelowDesktop && !forceVisible) return null;\n\n const isSide = mode === 'side';\n return (\n <ChatHeaderActionButton\n icon={\n isSide ? (\n <PanelRightClose className=\"h-3.5 w-3.5\" />\n ) : (\n <PanelRightOpen className=\"h-3.5 w-3.5\" />\n )\n }\n ariaLabel={isSide ? collapseLabel : expandLabel}\n onClick={onToggle}\n />\n );\n}\n","'use client';\n\nimport { Volume2, VolumeX } from 'lucide-react';\n\nimport { ChatHeaderActionButton } from './ChatHeaderActionButton';\n\nexport interface ChatHeaderAudioToggleProps {\n /** Current muted state. */\n muted: boolean;\n /** Toggle handler. Wire to `useChatAudio().setMuted` or `toggleMute`. */\n onToggle: () => void;\n /** Override tooltip label for muted → unmuted. */\n unmuteLabel?: string;\n /** Override tooltip label for unmuted → muted. */\n muteLabel?: string;\n}\n\n/**\n * Mute / unmute notification sounds. Drop into a `<ChatHeader>` actions\n * slot or into `<ChatDock headerActions>`.\n *\n * @example\n * ```tsx\n * const audio = useChatAudio({ sounds: {...} });\n * <ChatHeaderAudioToggle muted={audio.muted} onToggle={() => audio.setMuted(!audio.muted)} />\n * ```\n */\nexport function ChatHeaderAudioToggle({\n muted,\n onToggle,\n unmuteLabel = 'Unmute notifications',\n muteLabel = 'Mute notifications',\n}: ChatHeaderAudioToggleProps) {\n return (\n <ChatHeaderActionButton\n icon={\n muted ? (\n <VolumeX className=\"h-3.5 w-3.5\" />\n ) : (\n <Volume2 className=\"h-3.5 w-3.5\" />\n )\n }\n ariaLabel={muted ? unmuteLabel : muteLabel}\n onClick={onToggle}\n />\n );\n}\n","'use client';\n\nimport { useCallback, useState } from 'react';\n\nexport interface UseChatResetOptions {\n /**\n * Backend call that performs the actual reset (e.g. POST /chat/reset).\n * Should resolve to `true` on success, `false` on failure.\n * Throwing also counts as failure — caught and logged.\n */\n onReset: () => Promise<boolean>;\n /**\n * Called after a successful reset (status === true). Use to clear the\n * local message list, navigate, or fire analytics.\n */\n onSuccess?: () => void;\n /**\n * Called when reset fails (returned `false` or threw). Defaults to no-op\n * — wire to a toast/banner if you want to surface it.\n */\n onError?: (error?: unknown) => void;\n}\n\nexport interface UseChatResetReturn {\n /** Trigger the reset. Safe to call multiple times — re-entrant guard. */\n reset: () => Promise<boolean>;\n /** True while the reset is in flight. */\n isResetting: boolean;\n}\n\n/**\n * Generic \"clear chat context\" hook.\n *\n * Stays generic — the backend call lives in the host (it knows the URL,\n * auth, project slug). Provides the in-flight state and success/error\n * callbacks every consumer wires up the same way.\n *\n * @example\n * ```tsx\n * const { reset, isResetting } = useChatReset({\n * onReset: async () => {\n * const res = await fetch('/api/chat/reset', { method: 'POST', credentials: 'include' });\n * return res.ok;\n * },\n * onSuccess: () => chat.clearMessages(),\n * });\n * ```\n */\nexport function useChatReset(opts: UseChatResetOptions): UseChatResetReturn {\n const { onReset, onSuccess, onError } = opts;\n const [isResetting, setIsResetting] = useState(false);\n\n const reset = useCallback(async (): Promise<boolean> => {\n if (isResetting) return false;\n setIsResetting(true);\n try {\n const ok = await onReset();\n if (ok) onSuccess?.();\n else onError?.();\n return ok;\n } catch (err) {\n onError?.(err);\n return false;\n } finally {\n setIsResetting(false);\n }\n }, [isResetting, onReset, onSuccess, onError]);\n\n return { reset, isResetting };\n}\n","'use client';\n\nimport { RotateCcw } from 'lucide-react';\n\nimport { useChatReset } from '../hooks/useChatReset';\nimport { ChatHeaderActionButton } from './ChatHeaderActionButton';\n\nexport interface ChatHeaderResetButtonProps {\n /**\n * Backend reset call. Should resolve to `true` on success.\n * Plugged into `useChatReset` for in-flight state.\n */\n onReset: () => Promise<boolean>;\n /** Called after a successful reset (e.g. clear local messages, refetch). */\n onSuccess?: () => void;\n /** Called when reset fails (returned `false` or threw). */\n onError?: (error?: unknown) => void;\n /**\n * Show a `window.dialog.confirm` before calling `onReset`. @default true\n *\n * Requires the host to mount `<DialogProvider>` from `@djangocfg/ui-core`\n * — it installs the `window.dialog` API used here.\n */\n confirm?: boolean;\n /** Confirm dialog title. */\n confirmTitle?: string;\n /** Confirm dialog message. */\n confirmMessage?: string;\n /** Override tooltip / aria label. */\n ariaLabel?: string;\n}\n\nconst DEFAULT_TITLE = 'Clear conversation?';\nconst DEFAULT_MESSAGE =\n 'The assistant will forget this session and start a new one. This cannot be undone.';\nconst DEFAULT_LABEL = 'Clear conversation';\n\n/**\n * Standard chat-reset action: prompts the user via `window.dialog.confirm`,\n * then runs the backend reset call through `useChatReset` so the button\n * spins while it's in flight.\n *\n * @example\n * ```tsx\n * <ChatHeaderResetButton\n * onReset={api.clearChat}\n * onSuccess={() => chat.clearMessages()}\n * />\n * ```\n */\nexport function ChatHeaderResetButton({\n onReset,\n onSuccess,\n onError,\n confirm = true,\n confirmTitle = DEFAULT_TITLE,\n confirmMessage = DEFAULT_MESSAGE,\n ariaLabel = DEFAULT_LABEL,\n}: ChatHeaderResetButtonProps) {\n const { reset, isResetting } = useChatReset({ onReset, onSuccess, onError });\n\n const handleClick = async () => {\n if (confirm) {\n const api = typeof window !== 'undefined' ? window.dialog : undefined;\n if (api?.confirm) {\n const ok = await api.confirm({\n title: confirmTitle,\n message: confirmMessage,\n variant: 'destructive',\n confirmText: 'Clear',\n cancelText: 'Cancel',\n });\n if (!ok) return;\n } else if (typeof window !== 'undefined' && typeof window.confirm === 'function') {\n // Fallback to the native browser confirm when the dialog service\n // isn't wired (e.g. host forgot to mount DialogProvider).\n const ok = window.confirm(`${confirmTitle}\\n\\n${confirmMessage}`);\n if (!ok) return;\n }\n }\n await reset();\n };\n\n return (\n <ChatHeaderActionButton\n icon={<RotateCcw className=\"h-3.5 w-3.5\" />}\n ariaLabel={ariaLabel}\n onClick={handleClick}\n loading={isResetting}\n destructive\n />\n );\n}\n","/**\n * Canonical list of BCP-47 language tags that the browser Web Speech\n * API is known to accept. Sourced from the official Google Chrome\n * Speech API demo (`google.com/intl/en/chrome/demos/speech.html`),\n * which is the de-facto reference — the spec itself doesn't expose a\n * way to enumerate supported languages, so this list is the\n * best-effort guarantee for what works in Chromium-based browsers.\n *\n * Each entry groups one human-readable language with its dialect\n * variants. The default tag (first in `dialects`) is what `lang` is\n * set to when the user picks the language without a regional dialect.\n *\n * For custom engines (cmdop wails-whisper, Deepgram, …) hosts can\n * pass their own subset via the `availableLanguages` prop on the\n * picker — backend may support more or fewer tags than the browser.\n */\n\nexport interface SpeechLanguageDialect {\n /** BCP-47 tag (e.g. `en-US`). */\n code: string;\n /** Region label in the language's native script (e.g. \"United States\"). */\n region: string;\n}\n\nexport interface SpeechLanguage {\n /** Native-script name (e.g. \"Русский\", \"中文\"). */\n name: string;\n /**\n * English name used as a secondary search key so users typing\n * \"russian\" / \"chinese\" / \"korean\" land on the right row regardless\n * of the native script. Always lowercase.\n */\n englishName: string;\n /**\n * Primary-subtag ISO-639 code (e.g. `en`, `ru`, `cmn`). Used as the\n * map key into the `LanguageSelect` ui-core component.\n */\n iso: string;\n /** One or more region dialects. Length >= 1. */\n dialects: SpeechLanguageDialect[];\n}\n\nexport const WEB_SPEECH_LANGUAGES: SpeechLanguage[] = [\n { name: 'Afrikaans', iso: 'af', englishName: 'afrikaans', dialects: [{ code: 'af-ZA', region: 'South Africa' }] },\n { name: 'አማርኛ', iso: 'am', englishName: 'amharic', dialects: [{ code: 'am-ET', region: 'Ethiopia' }] },\n { name: 'Azərbaycanca', iso: 'az', englishName: 'azerbaijani', dialects: [{ code: 'az-AZ', region: 'Azerbaijan' }] },\n {\n name: 'বাংলা', iso: 'bn', englishName: 'bengali',\n dialects: [\n { code: 'bn-BD', region: 'Bangladesh' },\n { code: 'bn-IN', region: 'India' },\n ],\n },\n { name: 'Bahasa Indonesia', iso: 'id', englishName: 'indonesian', dialects: [{ code: 'id-ID', region: 'Indonesia' }] },\n { name: 'Bahasa Melayu', iso: 'ms', englishName: 'malay', dialects: [{ code: 'ms-MY', region: 'Malaysia' }] },\n { name: 'Català', iso: 'ca', englishName: 'catalan', dialects: [{ code: 'ca-ES', region: 'Spain' }] },\n { name: 'Čeština', iso: 'cs', englishName: 'czech', dialects: [{ code: 'cs-CZ', region: 'Czechia' }] },\n { name: 'Dansk', iso: 'da', englishName: 'danish', dialects: [{ code: 'da-DK', region: 'Denmark' }] },\n { name: 'Deutsch', iso: 'de', englishName: 'german', dialects: [{ code: 'de-DE', region: 'Germany' }] },\n {\n name: 'English', iso: 'en', englishName: 'english',\n dialects: [\n { code: 'en-US', region: 'United States' },\n { code: 'en-GB', region: 'United Kingdom' },\n { code: 'en-AU', region: 'Australia' },\n { code: 'en-CA', region: 'Canada' },\n { code: 'en-IN', region: 'India' },\n { code: 'en-NZ', region: 'New Zealand' },\n { code: 'en-PH', region: 'Philippines' },\n { code: 'en-ZA', region: 'South Africa' },\n { code: 'en-NG', region: 'Nigeria' },\n { code: 'en-GH', region: 'Ghana' },\n { code: 'en-KE', region: 'Kenya' },\n { code: 'en-TZ', region: 'Tanzania' },\n ],\n },\n {\n name: 'Español', iso: 'es', englishName: 'spanish',\n dialects: [\n { code: 'es-ES', region: 'España' },\n { code: 'es-MX', region: 'México' },\n { code: 'es-US', region: 'Estados Unidos' },\n { code: 'es-AR', region: 'Argentina' },\n { code: 'es-CL', region: 'Chile' },\n { code: 'es-CO', region: 'Colombia' },\n { code: 'es-PE', region: 'Perú' },\n { code: 'es-VE', region: 'Venezuela' },\n { code: 'es-EC', region: 'Ecuador' },\n { code: 'es-GT', region: 'Guatemala' },\n { code: 'es-CR', region: 'Costa Rica' },\n { code: 'es-PA', region: 'Panamá' },\n { code: 'es-DO', region: 'Rep. Dominicana' },\n { code: 'es-UY', region: 'Uruguay' },\n { code: 'es-PY', region: 'Paraguay' },\n { code: 'es-BO', region: 'Bolivia' },\n { code: 'es-SV', region: 'El Salvador' },\n { code: 'es-HN', region: 'Honduras' },\n { code: 'es-NI', region: 'Nicaragua' },\n { code: 'es-PR', region: 'Puerto Rico' },\n ],\n },\n { name: 'Euskara', iso: 'eu', englishName: 'basque', dialects: [{ code: 'eu-ES', region: 'Spain' }] },\n { name: 'Filipino', iso: 'fil', englishName: 'filipino tagalog', dialects: [{ code: 'fil-PH', region: 'Philippines' }] },\n { name: 'Français', iso: 'fr', englishName: 'french', dialects: [{ code: 'fr-FR', region: 'France' }] },\n { name: 'Basa Jawa', iso: 'jv', englishName: 'javanese', dialects: [{ code: 'jv-ID', region: 'Indonesia' }] },\n { name: 'Galego', iso: 'gl', englishName: 'galician', dialects: [{ code: 'gl-ES', region: 'Spain' }] },\n { name: 'ગુજરાતી', iso: 'gu', englishName: 'gujarati', dialects: [{ code: 'gu-IN', region: 'India' }] },\n { name: 'Hrvatski', iso: 'hr', englishName: 'croatian', dialects: [{ code: 'hr-HR', region: 'Croatia' }] },\n { name: 'IsiZulu', iso: 'zu', englishName: 'zulu', dialects: [{ code: 'zu-ZA', region: 'South Africa' }] },\n { name: 'Íslenska', iso: 'is', englishName: 'icelandic', dialects: [{ code: 'is-IS', region: 'Iceland' }] },\n {\n name: 'Italiano', iso: 'it', englishName: 'italian',\n dialects: [\n { code: 'it-IT', region: 'Italia' },\n { code: 'it-CH', region: 'Svizzera' },\n ],\n },\n { name: 'ಕನ್ನಡ', iso: 'kn', englishName: 'kannada', dialects: [{ code: 'kn-IN', region: 'India' }] },\n { name: 'ភាសាខ្មែរ', iso: 'km', englishName: 'khmer cambodian', dialects: [{ code: 'km-KH', region: 'Cambodia' }] },\n { name: 'Latviešu', iso: 'lv', englishName: 'latvian', dialects: [{ code: 'lv-LV', region: 'Latvia' }] },\n { name: 'Lietuvių', iso: 'lt', englishName: 'lithuanian', dialects: [{ code: 'lt-LT', region: 'Lithuania' }] },\n { name: 'മലയാളം', iso: 'ml', englishName: 'malayalam', dialects: [{ code: 'ml-IN', region: 'India' }] },\n { name: 'मराठी', iso: 'mr', englishName: 'marathi', dialects: [{ code: 'mr-IN', region: 'India' }] },\n { name: 'Magyar', iso: 'hu', englishName: 'hungarian', dialects: [{ code: 'hu-HU', region: 'Hungary' }] },\n { name: 'ລາວ', iso: 'lo', englishName: 'lao laotian', dialects: [{ code: 'lo-LA', region: 'Laos' }] },\n { name: 'Nederlands', iso: 'nl', englishName: 'dutch', dialects: [{ code: 'nl-NL', region: 'Netherlands' }] },\n { name: 'नेपाली भाषा', iso: 'ne', englishName: 'nepali', dialects: [{ code: 'ne-NP', region: 'Nepal' }] },\n { name: 'Norsk bokmål', iso: 'nb', englishName: 'norwegian bokmal', dialects: [{ code: 'nb-NO', region: 'Norway' }] },\n { name: 'Polski', iso: 'pl', englishName: 'polish', dialects: [{ code: 'pl-PL', region: 'Poland' }] },\n {\n name: 'Português', iso: 'pt', englishName: 'portuguese',\n dialects: [\n { code: 'pt-BR', region: 'Brasil' },\n { code: 'pt-PT', region: 'Portugal' },\n ],\n },\n { name: 'Română', iso: 'ro', englishName: 'romanian', dialects: [{ code: 'ro-RO', region: 'Romania' }] },\n { name: 'සිංහල', iso: 'si', englishName: 'sinhala sinhalese', dialects: [{ code: 'si-LK', region: 'Sri Lanka' }] },\n { name: 'Slovenščina', iso: 'sl', englishName: 'slovenian', dialects: [{ code: 'sl-SI', region: 'Slovenia' }] },\n { name: 'Basa Sunda', iso: 'su', englishName: 'sundanese', dialects: [{ code: 'su-ID', region: 'Indonesia' }] },\n { name: 'Slovenčina', iso: 'sk', englishName: 'slovak', dialects: [{ code: 'sk-SK', region: 'Slovakia' }] },\n { name: 'Suomi', iso: 'fi', englishName: 'finnish', dialects: [{ code: 'fi-FI', region: 'Finland' }] },\n { name: 'Svenska', iso: 'sv', englishName: 'swedish', dialects: [{ code: 'sv-SE', region: 'Sweden' }] },\n {\n name: 'Kiswahili', iso: 'sw', englishName: 'swahili',\n dialects: [\n { code: 'sw-TZ', region: 'Tanzania' },\n { code: 'sw-KE', region: 'Kenya' },\n ],\n },\n { name: 'ქართული', iso: 'ka', englishName: 'georgian', dialects: [{ code: 'ka-GE', region: 'Georgia' }] },\n { name: 'Հայերեն', iso: 'hy', englishName: 'armenian', dialects: [{ code: 'hy-AM', region: 'Armenia' }] },\n {\n name: 'தமிழ்', iso: 'ta', englishName: 'tamil',\n dialects: [\n { code: 'ta-IN', region: 'இந்தியா' },\n { code: 'ta-SG', region: 'சிங்கப்பூர்' },\n { code: 'ta-LK', region: 'இலங்கை' },\n { code: 'ta-MY', region: 'மலேசியா' },\n ],\n },\n { name: 'తెలుగు', iso: 'te', englishName: 'telugu', dialects: [{ code: 'te-IN', region: 'India' }] },\n { name: 'Tiếng Việt', iso: 'vi', englishName: 'vietnamese', dialects: [{ code: 'vi-VN', region: 'Vietnam' }] },\n { name: 'Türkçe', iso: 'tr', englishName: 'turkish', dialects: [{ code: 'tr-TR', region: 'Türkiye' }] },\n {\n name: 'اُردُو', iso: 'ur', englishName: 'urdu',\n dialects: [\n { code: 'ur-PK', region: 'پاکستان' },\n { code: 'ur-IN', region: 'بھارت' },\n ],\n },\n { name: 'Ελληνικά', iso: 'el', englishName: 'greek', dialects: [{ code: 'el-GR', region: 'Greece' }] },\n { name: 'български', iso: 'bg', englishName: 'bulgarian', dialects: [{ code: 'bg-BG', region: 'Bulgaria' }] },\n { name: 'Русский', iso: 'ru', englishName: 'russian', dialects: [{ code: 'ru-RU', region: 'Russia' }] },\n { name: 'Српски', iso: 'sr', englishName: 'serbian', dialects: [{ code: 'sr-RS', region: 'Serbia' }] },\n { name: 'Українська', iso: 'uk', englishName: 'ukrainian', dialects: [{ code: 'uk-UA', region: 'Ukraine' }] },\n { name: '한국어', iso: 'ko', englishName: 'korean', dialects: [{ code: 'ko-KR', region: 'Korea' }] },\n {\n name: '中文', iso: 'cmn', englishName: 'chinese mandarin cantonese',\n dialects: [\n { code: 'cmn-Hans-CN', region: '普通话 (中国大陆)' },\n { code: 'cmn-Hans-HK', region: '普通话 (香港)' },\n { code: 'cmn-Hant-TW', region: '中文 (台灣)' },\n { code: 'yue-Hant-HK', region: '粵語 (香港)' },\n ],\n },\n { name: '日本語', iso: 'ja', englishName: 'japanese', dialects: [{ code: 'ja-JP', region: 'Japan' }] },\n { name: 'हिन्दी', iso: 'hi', englishName: 'hindi', dialects: [{ code: 'hi-IN', region: 'India' }] },\n { name: 'ภาษาไทย', iso: 'th', englishName: 'thai', dialects: [{ code: 'th-TH', region: 'Thailand' }] },\n];\n\n/** Flat list of every supported BCP-47 tag, useful for validation. */\nexport const WEB_SPEECH_TAGS: string[] = WEB_SPEECH_LANGUAGES.flatMap((l) =>\n l.dialects.map((d) => d.code),\n);\n\n/**\n * Find the human-readable language entry that owns a given BCP-47 tag.\n * Returns `null` for unknown / custom tags (custom engines may use\n * codes outside this catalogue).\n */\nexport function findSpeechLanguage(tag: string | null | undefined): {\n language: SpeechLanguage;\n dialect: SpeechLanguageDialect;\n} | null {\n if (!tag) return null;\n const lower = tag.toLowerCase();\n for (const language of WEB_SPEECH_LANGUAGES) {\n for (const dialect of language.dialects) {\n if (dialect.code.toLowerCase() === lower) return { language, dialect };\n }\n }\n return null;\n}\n\n/**\n * Extract the ISO-3166 country code (2 uppercase letters) from a\n * BCP-47 tag, or `null` if the tag has no region subtag. Used by the\n * language flag button to find the right country flag asset.\n */\nexport function countryFromTag(tag: string | null | undefined): string | null {\n if (!tag) return null;\n const parts = tag.split('-');\n for (let i = parts.length - 1; i >= 0; i -= 1) {\n const p = parts[i];\n if (p.length === 2 && /^[A-Za-z]{2}$/.test(p)) return p.toUpperCase();\n }\n return null;\n}\n","'use client';\n\nimport type * as React from 'react';\nimport { useCallback, useEffect, useRef } from 'react';\nimport { Loader2, Mic } from 'lucide-react';\n\nimport { useCountdownFromSeconds, useNotificationSounds } from '@djangocfg/ui-core/hooks';\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport { useChatContextOptional } from '../../Chat/context';\nimport { useSpeechRecognition } from '../hooks/useSpeechRecognition';\nimport { useVoiceSupport } from '../hooks/useVoiceSupport';\nimport { getSpeechLogger } from '../core/logger';\nimport { normaliseFinal } from '../core/transcript';\nimport { DEFAULT_VOICE_SOUNDS, type VoiceSoundEvent } from '../core/audio/defaults';\nimport type { RecognitionEngine } from '../types';\n\nconst log = getSpeechLogger();\n\nexport interface VoiceComposerSlotProps {\n /**\n * Controlled composer value. Optional — when omitted, the slot\n * reads/writes through the Chat context's registered `ComposerHandle`\n * (built-in `<Composer>` and any host that calls\n * `useRegisterComposer({ getValue, setValue })`). Pass explicitly\n * only for standalone usage outside a `<ChatProvider>`.\n */\n value?: string;\n /** Composer setter — see `value`. Optional when used inside a chat. */\n onChange?: (next: string) => void;\n /** Optional custom engine (Deepgram / HTTP / WS). Defaults to Web Speech. */\n engine?: RecognitionEngine;\n /** BCP-47 language override. Otherwise `useSpeechPrefs` decides. */\n language?: string;\n /** Hide the button if the host wants to disable voice on phones. */\n hideOnMobile?: boolean;\n /** Max session length in seconds before we auto-stop. Default 90. */\n maxSeconds?: number;\n /** Auto-stop after this many ms of silence. Default 2500. */\n silenceMs?: number;\n /** Button size. @default 'md' */\n size?: 'sm' | 'md' | 'lg';\n /** Override classes on the button. */\n className?: string;\n /** Fires when dictation finishes — useful for parent-side analytics. */\n onFinish?: (transcript: string) => void;\n /**\n * Start/stop earcons. `true` (default) plays the bundled sounds,\n * `false` disables them, or pass `{ start, stop }` data-URLs to\n * override. Master mute lives in `useNotificationSounds` localStorage\n * — users can silence everything via their own UI.\n */\n sounds?: boolean | { start?: string; stop?: string };\n}\n\nconst SIZE_CLS: Record<NonNullable<VoiceComposerSlotProps['size']>, string> = {\n sm: 'h-8 w-8 [&_svg]:h-4 [&_svg]:w-4',\n md: 'h-9 w-9 [&_svg]:h-4 [&_svg]:w-4',\n lg: 'h-12 w-12 [&_svg]:h-5 [&_svg]:w-5',\n};\n\nconst STORAGE_KEY = 'djangocfg-stt:voice-sounds';\n\n/**\n * Drop-in slot for the `<Composer toolbarEnd>` (or `toolbarStart`) prop.\n *\n * Renders a microphone button — but only when the browser + device\n * combination can actually do speech recognition. Firefox, in-app\n * WebViews, and missing `getUserMedia` all collapse the component to\n * `null`, so the chat composer never shows a broken affordance.\n *\n * While listening, interim+final transcript is pushed live into the\n * composer's `value` — like dictation on iOS / Android keyboards. The\n * user's already-typed prefix is preserved (anchored on press). Cancel\n * with the same button or by pressing Escape (handled upstream).\n *\n * Soft start/stop earcons play by default. Pass `sounds={false}` to\n * mute, or `sounds={{ start, stop }}` to override the audio URLs.\n */\nexport function VoiceComposerSlot({\n value,\n onChange,\n engine,\n language,\n hideOnMobile = false,\n maxSeconds = 90,\n silenceMs = 2500,\n size = 'md',\n className,\n onFinish,\n sounds = true,\n}: VoiceComposerSlotProps): React.ReactElement | null {\n const support = useVoiceSupport(engine);\n\n // Read the composer handle from chat context — works transparently\n // for the built-in `<Composer>` (registers itself) and for TipTap\n // hosts that call `useRegisterComposer({ getValue, setValue, focus,\n // moveCursorToEnd })`. Falls back to a no-op when mounted outside of\n // a chat.\n const chatCtx = useChatContextOptional();\n const composerHandleRef = useRef(chatCtx?.composer ?? null);\n composerHandleRef.current = chatCtx?.composer ?? null;\n\n useEffect(() => {\n log.slot.debug('mount', {\n supported: support.supported,\n reason: support.reason,\n hasComposerHandle: !!chatCtx?.composer,\n hasExplicitValue: value !== undefined,\n hasOnChange: !!onChange,\n });\n }, [support.supported, support.reason, chatCtx?.composer, value, onChange]);\n\n // Resolve value/onChange: prop wins; otherwise pull from the\n // registered composer handle. The slot can therefore be dropped into\n // `composerToolbarEnd` of `ChatRoot` with zero props.\n const resolvedGetValue = useCallback((): string => {\n if (value !== undefined) return value;\n return composerHandleRef.current?.getValue?.() ?? '';\n }, [value]);\n const resolvedSetValue = useCallback(\n (next: string): void => {\n if (onChange) {\n log.composer.debug('setValue → onChange prop', { len: next.length });\n onChange(next);\n return;\n }\n const handle = composerHandleRef.current;\n if (!handle?.setValue) {\n log.composer.warn(\n 'setValue called but no composer handle is registered — text will be lost. ' +\n 'Make sure <VoiceComposerSlot> lives inside a <ChatProvider> (or pass `value`/`onChange` props for standalone use).',\n { len: next.length },\n );\n return;\n }\n log.composer.debug('setValue → composer handle', { len: next.length });\n handle.setValue(next);\n },\n [onChange],\n );\n\n // Anchor: what was already in the textarea when the user pressed the\n // mic. Live transcript is appended to this baseline so manual typing\n // before pressing the button is never overwritten.\n const anchorRef = useRef<string>('');\n const onFinishRef = useRef(onFinish);\n onFinishRef.current = onFinish;\n\n // Push caret to the end on the next frame — after React commits the\n // new `value` into the DOM. Without the rAF the selection lands on\n // the old text length, leaving the cursor mid-string while the live\n // transcript visually keeps growing.\n const pinCaretToEnd = useCallback(() => {\n const handle = composerHandleRef.current;\n if (!handle?.moveCursorToEnd) return;\n requestAnimationFrame(() => {\n composerHandleRef.current?.moveCursorToEnd?.();\n });\n }, []);\n\n const [countdown, startCountdown] = useCountdownFromSeconds();\n\n // Earcon bus. `sounds === false` → pass empty map so the bus stays\n // silent. Object overrides merge with bundled defaults.\n const soundMap =\n sounds === false\n ? undefined\n : sounds === true\n ? DEFAULT_VOICE_SOUNDS\n : { ...DEFAULT_VOICE_SOUNDS, ...sounds };\n const audio = useNotificationSounds<VoiceSoundEvent>({\n storageKey: STORAGE_KEY,\n sounds: soundMap,\n muted: sounds === false,\n // Both earcons stay deliberately quiet — they're self-initiated\n // micro-confirmations, not notifications. Anything louder feels\n // attention-grabbing when the user pressed the button themselves.\n eventVolumes: { start: 0.35, stop: 0.5 },\n });\n\n const handlePartial = useCallback(\n (text: string) => {\n log.dictation.debug('partial', {\n len: text.length,\n anchorLen: anchorRef.current.length,\n });\n const next = anchorRef.current\n ? `${anchorRef.current} ${text}`\n : text;\n resolvedSetValue(next);\n pinCaretToEnd();\n },\n [pinCaretToEnd, resolvedSetValue],\n );\n\n const handleFinal = useCallback(\n (text: string) => {\n const clean = normaliseFinal(text);\n if (!clean) {\n log.dictation.debug('final dropped — empty after normalise', { raw: text });\n return;\n }\n const merged = anchorRef.current ? `${anchorRef.current} ${clean}` : clean;\n log.dictation.info('final merged', {\n len: clean.length,\n totalLen: merged.length,\n });\n anchorRef.current = merged;\n resolvedSetValue(merged);\n pinCaretToEnd();\n },\n [pinCaretToEnd, resolvedSetValue],\n );\n\n const rec = useSpeechRecognition({\n engine,\n language,\n interim: true,\n autoStop: { silenceMs, maxMs: maxSeconds * 1000 },\n onPartial: (text) => handlePartial(text),\n onFinal: (text) => handleFinal(text),\n onStart: () => {\n void audio.play('start');\n // Focus the composer + park caret at the end so the live\n // transcript visibly grows where the user expects it to.\n composerHandleRef.current?.focus();\n pinCaretToEnd();\n },\n onStop: () => {\n void audio.play('stop');\n // Re-focus on stop too — auto-stop on silence happens without\n // a user gesture, and we want the user to keep typing seamlessly.\n composerHandleRef.current?.focus();\n pinCaretToEnd();\n onFinishRef.current?.(resolvedGetValue());\n },\n });\n\n // Drive the countdown alongside the listening state.\n useEffect(() => {\n if (rec.status === 'listening') {\n startCountdown(maxSeconds);\n }\n }, [rec.status, maxSeconds, startCountdown]);\n\n // Hotkeys while listening:\n // Esc — cancel dictation (and stop event propagation so the\n // outer chat doesn't *also* close — same convention as\n // ChatGPT / Slack voice mode).\n // Enter — finish dictation, KEEP what we already pushed into the\n // composer, do NOT submit the chat (avoids accidental\n // sends while the user is still talking). We block the\n // Enter that would otherwise reach the composer textarea\n // by listening in the capture phase.\n const listening = rec.status === 'listening' || rec.status === 'starting';\n useEffect(() => {\n if (!listening) return undefined;\n const onKey = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n rec.abort();\n return;\n }\n if (e.key === 'Enter' && !e.shiftKey) {\n // Block the chat composer's \"Enter to send\" while we're\n // dictating — finish recording instead.\n e.preventDefault();\n e.stopPropagation();\n void rec.stop();\n }\n };\n // `capture: true` so we run before the composer textarea's\n // keydown handler (which would otherwise close the chat on Esc\n // or submit the form on Enter).\n window.addEventListener('keydown', onKey, true);\n return () => {\n window.removeEventListener('keydown', onKey, true);\n };\n }, [listening, rec]);\n\n const toggle = useCallback(() => {\n if (rec.status === 'listening' || rec.status === 'starting') {\n void rec.stop();\n return;\n }\n anchorRef.current = resolvedGetValue().trim();\n void rec.start();\n }, [rec, resolvedGetValue]);\n\n if (!support.supported) return null;\n if (hideOnMobile && support.isMobile) return null;\n\n const stopping = rec.status === 'stopping';\n\n // Tooltip: countdown + hotkey hint while listening, plain copy\n // otherwise. Avoids the absolutely-positioned label that clipped\n // against the composer bottom edge in the previous design.\n const tooltip = listening\n ? `Listening — ${countdown.label || `${maxSeconds}s left`} · Enter to finish · Esc to cancel`\n : 'Dictate message';\n\n return (\n <span className=\"inline-flex items-center gap-1.5 !h-auto\">\n {listening && countdown.label ? (\n <span\n aria-hidden\n className=\"rounded-full bg-destructive/10 px-1.5 py-0.5 font-mono text-[10px] leading-none text-destructive tabular-nums\"\n >\n {countdown.label}\n </span>\n ) : null}\n <button\n type=\"button\"\n onClick={toggle}\n aria-pressed={listening}\n aria-label={listening ? 'Stop dictation' : 'Dictate message'}\n title={tooltip}\n className={cn(\n 'relative inline-flex items-center justify-center rounded-full transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n SIZE_CLS[size],\n listening\n ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground',\n className,\n )}\n >\n {listening && (\n <span\n aria-hidden\n className=\"absolute inset-0 rounded-full bg-destructive/30 animate-ping\"\n />\n )}\n {stopping ? <Loader2 className=\"animate-spin\" /> : <Mic />}\n </button>\n </span>\n );\n}\n","'use client';\n\nimport type * as React from 'react';\nimport { createContext, useContext, useMemo } from 'react';\n\nimport type { UseSpeechRecognitionConfig, UseSpeechRecognitionReturn } from '../types';\nimport { useSpeechRecognition } from '../hooks/useSpeechRecognition';\n\nconst Ctx = createContext<UseSpeechRecognitionReturn | null>(null);\n\nexport interface SpeechRecognitionProviderProps extends UseSpeechRecognitionConfig {\n children?: React.ReactNode;\n}\n\n/**\n * Lifts a single `useSpeechRecognition` instance into a context so any\n * descendant — composer slot, header badge, transcript overlay — can\n * read the same status / transcript / level without each component\n * spinning up its own engine. Mount it once per Chat (or per\n * dictation-aware screen).\n */\nexport function SpeechRecognitionProvider({\n children,\n ...config\n}: SpeechRecognitionProviderProps): React.ReactElement {\n const rec = useSpeechRecognition(config);\n // Memo prevents context-value churn from leaking through to every\n // useContext consumer when only one of {status, level, transcript}\n // changes — React already triggers them via the inner state, the\n // outer object identity should stay stable across renders.\n const value = useMemo(() => rec, [rec]);\n return <Ctx.Provider value={value}>{children}</Ctx.Provider>;\n}\n\nexport function useSpeechRecognitionContext(): UseSpeechRecognitionReturn {\n const ctx = useContext(Ctx);\n if (!ctx) {\n throw new Error(\n 'useSpeechRecognitionContext must be used inside a <SpeechRecognitionProvider>.',\n );\n }\n return ctx;\n}\n\nexport function useSpeechRecognitionContextOptional(): UseSpeechRecognitionReturn | null {\n return useContext(Ctx);\n}\n","'use client';\n\nimport * as React from 'react';\nimport { Suspense, type ReactNode, type ComponentType } from 'react';\nimport { cn } from '@djangocfg/ui-core/lib';\nimport { useAppT } from '@djangocfg/i18n';\n\n// ============================================================================\n// Loading Fallback Components\n// ============================================================================\n\nexport interface LoadingFallbackProps {\n /** Minimum height of the loading container */\n minHeight?: string | number;\n /** Show loading text */\n showText?: boolean;\n /** Custom loading text */\n text?: string;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Spinner - Simple spinning loader\n */\nexport function Spinner({ className }: { className?: string }) {\n const t = useAppT();\n const loadingLabel = t('ui.states.loading');\n\n return (\n <div\n className={cn(\n 'inline-block h-8 w-8 animate-spin rounded-full',\n 'border-4 border-solid border-current border-r-transparent',\n 'align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]',\n className\n )}\n role=\"status\"\n aria-label={loadingLabel}\n />\n );\n}\n\n/**\n * LoadingFallback - Default loading state for lazy components\n */\nexport function LoadingFallback({\n minHeight = 200,\n showText = true,\n text,\n className,\n}: LoadingFallbackProps) {\n const t = useAppT();\n const loadingText = text ?? t('ui.form.loading');\n const height = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n\n return (\n <div\n className={cn(\n 'flex items-center justify-center bg-muted/30 rounded-lg',\n className\n )}\n style={{ minHeight: height }}\n >\n <div className=\"text-center\">\n <Spinner className=\"text-primary\" />\n {showText && (\n <p className=\"mt-3 text-sm text-muted-foreground\">{loadingText}</p>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * CardLoadingFallback - Loading state styled as a card\n */\nexport function CardLoadingFallback({\n title,\n description,\n minHeight = 200,\n className,\n}: {\n title?: string;\n description?: string;\n minHeight?: string | number;\n className?: string;\n}) {\n const t = useAppT();\n const cardTitle = title ?? t('ui.states.loading');\n const height = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n\n return (\n <div\n className={cn(\n 'relative bg-card rounded-lg border border-border overflow-hidden',\n className\n )}\n >\n <div className=\"p-4 border-b border-border bg-muted/50\">\n <h6 className=\"text-sm font-semibold text-foreground\">{cardTitle}</h6>\n {description && (\n <p className=\"text-xs text-muted-foreground mt-1\">{description}</p>\n )}\n </div>\n <div className=\"p-4\">\n <div\n className=\"flex justify-center items-center\"\n style={{ minHeight: height }}\n >\n <Spinner className=\"text-primary\" />\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * MapLoadingFallback - Loading state for map components\n */\nexport function MapLoadingFallback({\n minHeight = 400,\n className,\n}: {\n minHeight?: string | number;\n className?: string;\n}) {\n const t = useAppT();\n const loadingText = t('ui.form.loading');\n const height = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n\n return (\n <div\n className={cn(\n 'relative bg-muted/50 rounded-lg overflow-hidden',\n 'flex items-center justify-center',\n className\n )}\n style={{ minHeight: height }}\n >\n <div className=\"text-center\">\n <div className=\"relative\">\n <Spinner className=\"text-primary h-10 w-10\" />\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <svg\n className=\"h-5 w-5 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"\n />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"\n />\n </svg>\n </div>\n </div>\n <p className=\"mt-3 text-sm text-muted-foreground\">{loadingText}</p>\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// Lazy Wrapper\n// ============================================================================\n\nexport interface LazyWrapperProps {\n children: ReactNode;\n /** Custom fallback component */\n fallback?: ReactNode;\n /** Use card-style fallback */\n card?: boolean;\n /** Card title (when card=true) */\n cardTitle?: string;\n /** Card description (when card=true) */\n cardDescription?: string;\n /** Minimum height for fallback */\n minHeight?: string | number;\n /** Additional CSS classes for fallback */\n className?: string;\n}\n\n/**\n * LazyWrapper - Suspense wrapper with configurable fallbacks\n *\n * @example\n * // Basic usage\n * <LazyWrapper>\n * <LazyComponent />\n * </LazyWrapper>\n *\n * @example\n * // Card style fallback\n * <LazyWrapper card cardTitle=\"Diagram\" minHeight={300}>\n * <Mermaid chart={...} />\n * </LazyWrapper>\n *\n * @example\n * // Custom fallback\n * <LazyWrapper fallback={<MyCustomLoader />}>\n * <HeavyComponent />\n * </LazyWrapper>\n */\nexport function LazyWrapper({\n children,\n fallback,\n card = false,\n cardTitle,\n cardDescription,\n minHeight = 200,\n className,\n}: LazyWrapperProps) {\n const defaultFallback = card ? (\n <CardLoadingFallback\n title={cardTitle}\n description={cardDescription}\n minHeight={minHeight}\n className={className}\n />\n ) : (\n <LoadingFallback minHeight={minHeight} className={className} />\n );\n\n return <Suspense fallback={fallback ?? defaultFallback}>{children}</Suspense>;\n}\n\n// ============================================================================\n// Lazy Component Factory\n// ============================================================================\n\nexport interface CreateLazyComponentOptions<P> {\n /** Loading fallback component */\n fallback?: ReactNode | ((props: P) => ReactNode);\n /** Display name for the component */\n displayName?: string;\n}\n\n/**\n * createLazyComponent - Factory for creating lazy-loaded components\n *\n * @example\n * const LazyMermaid = createLazyComponent(\n * () => import('./Mermaid.client'),\n * {\n * displayName: 'Mermaid',\n * fallback: <CardLoadingFallback title=\"Diagram\" />,\n * }\n * );\n */\nexport function createLazyComponent<P extends object>(\n loader: () => Promise<{ default: ComponentType<P> }>,\n options: CreateLazyComponentOptions<P> = {}\n): ComponentType<P> {\n const LazyComponent = React.lazy(loader);\n\n const WrappedComponent = (props: P) => {\n const fallback =\n typeof options.fallback === 'function'\n ? options.fallback(props)\n : options.fallback ?? <LoadingFallback />;\n\n return (\n <Suspense fallback={fallback}>\n <LazyComponent {...props} />\n </Suspense>\n );\n };\n\n WrappedComponent.displayName = options.displayName ?? 'LazyComponent';\n\n return WrappedComponent;\n}\n","'use client';\n\nimport { createLazyComponent } from '../../components';\nimport type { DictationFieldProps } from './widgets/DictationField';\n\nexport const LazyDictationField = createLazyComponent<DictationFieldProps>(\n () =>\n import('./widgets/DictationField').then((mod) => ({\n default: mod.DictationField,\n })),\n {\n displayName: 'LazyDictationField',\n fallback: (\n <div className=\"rounded-lg border border-border/60 bg-card px-3 py-2 text-xs text-muted-foreground\">\n Loading dictation…\n </div>\n ),\n },\n);\n","'use client';\n\nimport type * as React from 'react';\nimport { useMemo } from 'react';\nimport { Globe } from 'lucide-react';\n\nimport {\n Combobox,\n type ComboboxOption,\n Flag,\n} from '@djangocfg/ui-core/components';\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport { findSpeechLanguage } from '../../SpeechRecognition';\n\nimport {\n WEB_SPEECH_LANGUAGES,\n countryFromTag,\n useResolvedLanguage,\n useSpeechPrefs,\n} from '../../SpeechRecognition';\n\nexport interface ChatHeaderLanguageButtonProps {\n /** Override aria-label. Default \"Speech language\". */\n ariaLabel?: string;\n /**\n * Subset of BCP-47 tags to offer. Default: every entry from the\n * Web Speech catalogue (~66 tags incl. regional variants). Pass a\n * tighter list when your backend STT only supports a subset.\n */\n allowedTags?: string[];\n /** Hide the globe-fallback icon when no flag resolves. */\n hideFallbackIcon?: boolean;\n className?: string;\n}\n\n/**\n * Compact flag-button language picker for the chat header. Built on\n * top of the ui-core `<Combobox>` — searchable autocomplete with\n * flags, ~66 BCP-47 tags from the official Chrome Web Speech demo, and\n * a custom 28×28 trigger via `renderTrigger`.\n *\n * The selection persists into `useSpeechPrefs` (zustand+localStorage)\n * so `useSpeechRecognition` picks it up as the second-priority resolver\n * value (above i18n locale, below an explicit `language` prop).\n */\nexport function ChatHeaderLanguageButton({\n ariaLabel = 'Speech language',\n allowedTags,\n hideFallbackIcon,\n className,\n}: ChatHeaderLanguageButtonProps): React.ReactElement {\n const prefs = useSpeechPrefs();\n const active = useResolvedLanguage();\n\n // Flatten every dialect into one Combobox option. Display name keeps\n // the native language label; we stash the English aliases + BCP-47\n // tag + region in `description` purely as a hidden search index.\n // (We intentionally hide the description from the rendered row in\n // `renderOption` — it would just clutter the dropdown.)\n const options = useMemo<ComboboxOption[]>(() => {\n const allow = allowedTags ? new Set(allowedTags) : null;\n const out: ComboboxOption[] = [];\n for (const lang of WEB_SPEECH_LANGUAGES) {\n for (const d of lang.dialects) {\n if (allow && !allow.has(d.code)) continue;\n out.push({\n value: d.code,\n // \"Русский\" / \"Español — Argentina\" / \"English — United States\"\n label:\n lang.dialects.length === 1\n ? lang.name\n : `${lang.name} — ${d.region}`,\n // Search-only index: English name, BCP-47 tag, ISO, region.\n // Lets users type \"russian\" / \"ru-RU\" / \"ru\" / \"argentina\"\n // and still find the row regardless of native script.\n description: `${lang.englishName} ${d.code} ${lang.iso} ${d.region}`.toLowerCase(),\n });\n }\n }\n return out;\n }, [allowedTags]);\n\n return (\n <Combobox\n options={options}\n value={prefs.language ?? active}\n onValueChange={(v) => prefs.setLanguage(v || null)}\n placeholder={ariaLabel}\n searchPlaceholder=\"Search language…\"\n filterFunction={(opt, search) => {\n const s = search.toLowerCase();\n // Match label (native script), value (BCP-47), and our packed\n // search index in description (English name + tag + region).\n return (\n opt.label.toLowerCase().includes(s) ||\n opt.value.toLowerCase().includes(s) ||\n (opt.description?.includes(s) ?? false)\n );\n }}\n // Popover width follows the trigger by default (28px → narrow).\n // Force a usable width and bump z-index above ChatDock (z-10000).\n contentClassName=\"w-[280px]\"\n contentStyle={{ zIndex: 10001 }}\n // Custom row: country flag + native language label + BCP-47 tag.\n // ui-core Combobox default rendering only shows label/description\n // — feeding the flag here keeps every list row instantly\n // identifiable. Fallback to a neutral globe glyph when the tag\n // has no resolvable country (rare: bn-BD etc. all have flags).\n renderOption={(option) => {\n const country = countryFromTag(option.value);\n return (\n <div className=\"flex min-w-0 flex-1 items-center gap-2\">\n {country ? (\n <Flag\n countryCode={country}\n className=\"h-4 w-5 shrink-0 overflow-hidden rounded-[2px] border border-border/60 ring-1 ring-black/5\"\n />\n ) : (\n <Globe className=\"h-4 w-4 shrink-0 text-muted-foreground\" aria-hidden />\n )}\n {/* Native-script label only — BCP-47 subtitle removed to\n keep the dropdown visually quiet. Tag + region still\n live in `description` for search. */}\n <span className=\"truncate text-sm\">{option.label}</span>\n </div>\n );\n }}\n // Compact icon-only trigger — replaces the default wide outline\n // button. Stays the same 28×28 footprint as ChatHeaderActionButton\n // siblings (audio toggle, reset).\n renderTrigger={(selected, open) => {\n const tag = selected?.value ?? active;\n const country = countryFromTag(tag);\n const found = findSpeechLanguage(tag);\n // Tooltip copy: native language name + dialect region + tag.\n // Falls back to just the tag for unknown / custom-engine codes.\n const tooltipLabel = found\n ? `${found.language.name}${\n found.language.dialects.length > 1 ? ` — ${found.dialect.region}` : ''\n } · ${tag}`\n : tag;\n // Note on tooltip: we intentionally use the native `title`\n // attribute instead of `<Tooltip>` here. The button is already\n // wrapped by Combobox's `PopoverTrigger asChild`, and stacking\n // a second `TooltipTrigger asChild` on the same node makes\n // Radix Slot merge two competing refs/handlers — popover click\n // stops working. Native `title` is \"good enough\" for a single\n // status label and ships zero extra DOM.\n return (\n <button\n type=\"button\"\n aria-label={`${ariaLabel}: ${tooltipLabel}`}\n aria-expanded={open}\n title={tooltipLabel}\n className={cn(\n 'inline-flex h-7 w-7 items-center justify-center rounded-md',\n 'text-muted-foreground transition-colors',\n 'hover:bg-accent hover:text-foreground',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n className,\n )}\n >\n {country ? (\n <Flag\n countryCode={country}\n // Subtle hairline so flags with light edges (Japan,\n // Switzerland, …) don't blend into the header bg.\n className=\"h-4 w-5 overflow-hidden rounded-[2px] border border-border/60 ring-1 ring-black/5\"\n />\n ) : hideFallbackIcon ? null : (\n <Globe className=\"h-3.5 w-3.5\" aria-hidden />\n )}\n </button>\n );\n }}\n />\n );\n}\n","'use client';\n\nimport { X } from 'lucide-react';\nimport { useEffect, useState } from 'react';\nimport type { CSSProperties, ReactNode } from 'react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport { useChatPresence } from './useChatPresence';\nimport type { ChatFABPosition } from './ChatFAB';\n\nexport interface ChatGreetingProps {\n /** Controlled visibility — usually `!chatOpen && !userDismissed`. */\n open: boolean;\n /** Greeting text. Pass a string for the default bubble, or any ReactNode. */\n children: ReactNode;\n /** Click handler — typically opens the chat. Bubble is clickable when set. */\n onClick?: () => void;\n /** Close (×) button handler — typically marks the greeting as dismissed. */\n onDismiss?: () => void;\n /** Anchor relative to a FAB on the same side. @default 'bottom-right' */\n position?: ChatFABPosition;\n /**\n * Horizontal pixel offset matching the FAB's `offset` prop, so the greeting\n * lines up under the FAB. @default 24\n */\n fabOffset?: number;\n /**\n * Vertical pixel offset above/below the FAB centerline. @default 96\n * (room for an `md` FAB plus a small gap).\n */\n fabClearance?: number;\n /** Delay before the greeting appears, in ms. @default 1500 */\n delayMs?: number;\n /** z-index. @default 9998 (just below the default FAB at 9999). */\n zIndex?: number;\n /** Override classes on the bubble. */\n className?: string;\n /** Override styles on the bubble. */\n style?: CSSProperties;\n /** Optional sender avatar / icon shown on the left. */\n avatar?: ReactNode;\n /** Optional sender label rendered above the text. */\n senderName?: string;\n /** ARIA label for the dismiss button. @default 'Dismiss' */\n dismissLabel?: string;\n /**\n * Render in-place (no fixed positioning). Useful for stories and inline previews.\n * @default false\n */\n inline?: boolean;\n}\n\nfunction anchorStyle(\n position: ChatFABPosition,\n fabOffset: number,\n fabClearance: number,\n): CSSProperties {\n const [vert, horiz] = position.split('-') as ['bottom' | 'top', 'right' | 'left'];\n return { [vert]: fabClearance, [horiz]: fabOffset } as CSSProperties;\n}\n\nfunction originClass(position: ChatFABPosition): string {\n // Scale-in origin matches the corner the bubble attaches to.\n if (position === 'bottom-right') return 'origin-bottom-right';\n if (position === 'bottom-left') return 'origin-bottom-left';\n if (position === 'top-right') return 'origin-top-right';\n return 'origin-top-left';\n}\n\n/**\n * Greeting bubble shown next to a `ChatFAB` to invite the user to start a\n * conversation (LiveChat / Intercom-style proactive prompt).\n *\n * Renders fixed-position, anchored to the same corner as the FAB. Owns its\n * own delayed-mount + presence animation. Hide on chat open and/or after\n * user dismissal.\n *\n * @example\n * ```tsx\n * const [open, setOpen] = useState(false);\n * const [dismissed, setDismissed] = useState(false);\n *\n * <ChatLauncher\n * open={open}\n * onOpenChange={setOpen}\n * fab={{ variant: 'animated' }}\n * dock={{ title: 'Support' }}\n * >\n * <SupportChat />\n * </ChatLauncher>\n *\n * <ChatGreeting\n * open={!open && !dismissed}\n * onClick={() => setOpen(true)}\n * onDismiss={() => setDismissed(true)}\n * senderName=\"Anna from Support\"\n * delayMs={2000}\n * >\n * Hi! 👋 Got a question? I'm here to help.\n * </ChatGreeting>\n * ```\n */\nexport function ChatGreeting({\n open,\n children,\n onClick,\n onDismiss,\n position = 'bottom-right',\n fabOffset = 24,\n fabClearance = 96,\n delayMs = 1500,\n zIndex = 9998,\n className,\n style,\n avatar,\n senderName,\n dismissLabel = 'Dismiss',\n inline = false,\n}: ChatGreetingProps) {\n const [delayed, setDelayed] = useState(delayMs <= 0);\n\n useEffect(() => {\n if (!open || delayMs <= 0) return;\n const t = setTimeout(() => setDelayed(true), delayMs);\n return () => clearTimeout(t);\n }, [open, delayMs]);\n\n const shouldShow = open && delayed;\n const phase = useChatPresence(shouldShow, 220);\n\n if (phase === 'hidden') return null;\n\n const animating = phase === 'entering' || phase === 'leaving';\n const clickable = !!onClick;\n\n return (\n <div\n role={clickable ? 'button' : 'status'}\n aria-live=\"polite\"\n tabIndex={clickable ? 0 : -1}\n onClick={clickable ? onClick : undefined}\n onKeyDown={\n clickable\n ? (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick?.();\n }\n }\n : undefined\n }\n className={cn(\n inline ? 'relative inline-flex' : 'fixed',\n 'flex items-start gap-2.5 max-w-[280px]',\n 'rounded-2xl border border-border bg-popover text-popover-foreground',\n 'px-3.5 py-2.5 shadow-2xl transition-all duration-200 ease-out',\n clickable && 'cursor-pointer hover:bg-accent/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n originClass(position),\n animating ? 'opacity-0 scale-95 translate-y-1' : 'opacity-100 scale-100 translate-y-0',\n className,\n )}\n style={{\n ...(inline ? {} : anchorStyle(position, fabOffset, fabClearance)),\n ...(inline ? {} : { zIndex }),\n pointerEvents: phase === 'visible' ? 'auto' : 'none',\n ...style,\n }}\n >\n {avatar && <div className=\"mt-0.5 shrink-0\">{avatar}</div>}\n\n <div className=\"min-w-0 flex-1 text-sm leading-snug\">\n {senderName && (\n <div className=\"mb-0.5 text-[11px] font-medium text-muted-foreground\">\n {senderName}\n </div>\n )}\n <div className=\"text-foreground\">{children}</div>\n </div>\n\n {onDismiss && (\n <button\n type=\"button\"\n aria-label={dismissLabel}\n onClick={(e) => {\n e.stopPropagation();\n onDismiss();\n }}\n className={cn(\n '-mr-1 -mt-1 flex h-6 w-6 shrink-0 items-center justify-center rounded-full',\n 'text-muted-foreground transition-colors hover:bg-accent hover:text-foreground',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n )}\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </div>\n );\n}\n","'use client';\n\nimport { X } from 'lucide-react';\nimport type { CSSProperties, ReactNode } from 'react';\n\nimport { Avatar, AvatarFallback, AvatarImage } from '@djangocfg/ui-core/components';\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport type { ChatMessage, ChatPersona } from '../types';\nimport type { ChatFABPosition } from './ChatFAB';\nimport { useChatPresence } from './useChatPresence';\n\nexport interface ChatUnreadPreviewProps {\n /** Controlled — usually `!dockOpen && !!message`. */\n open: boolean;\n /** Inbound message to preview. `null` hides the bubble. */\n message: ChatMessage | null;\n /** Tap → open chat + mark read. */\n onClick?: () => void;\n /** × → mark read without opening. */\n onDismiss?: () => void;\n /** Anchor corner — match the FAB so the bubble sits above it. @default 'bottom-right' */\n position?: ChatFABPosition;\n /** Horizontal offset from screen edge, matches the FAB. @default 24 */\n fabOffset?: number;\n /** Vertical clearance above/below the FAB. @default 96 */\n fabClearance?: number;\n /** Lines of body text before ellipsis. @default 2 */\n truncate?: number;\n /** z-index. @default 9998 */\n zIndex?: number;\n /** Render in-place (stories / previews). @default false */\n inline?: boolean;\n /** Override classes on the bubble. */\n className?: string;\n /** Override styles on the bubble. */\n style?: CSSProperties;\n /** ARIA label for the dismiss button. @default 'Mark as read' */\n dismissLabel?: string;\n /** Override the avatar — defaults to derived from `message.sender`. */\n avatar?: ReactNode;\n /** Override the sender label — defaults to `message.sender?.name`. */\n senderName?: string;\n}\n\nconst TIME_FORMAT = new Intl.DateTimeFormat(undefined, {\n hour: '2-digit',\n minute: '2-digit',\n});\n\nfunction anchorStyle(\n position: ChatFABPosition,\n fabOffset: number,\n fabClearance: number,\n): CSSProperties {\n const [vert, horiz] = position.split('-') as ['bottom' | 'top', 'right' | 'left'];\n return { [vert]: fabClearance, [horiz]: fabOffset } as CSSProperties;\n}\n\nfunction originClass(position: ChatFABPosition): string {\n if (position === 'bottom-right') return 'origin-bottom-right';\n if (position === 'bottom-left') return 'origin-bottom-left';\n if (position === 'top-right') return 'origin-top-right';\n return 'origin-top-left';\n}\n\nfunction deriveAvatar(persona?: ChatPersona, name?: string): ReactNode {\n const initials =\n persona?.initials ??\n (name ?? persona?.name ?? '?')\n .split(/\\s+/)\n .map((p) => p[0])\n .filter(Boolean)\n .slice(0, 2)\n .join('')\n .toUpperCase();\n return (\n <Avatar className=\"h-9 w-9\">\n {persona?.avatarUrl ? <AvatarImage src={persona.avatarUrl} /> : null}\n <AvatarFallback>{initials || '?'}</AvatarFallback>\n </Avatar>\n );\n}\n\n/**\n * Push-notification bubble next to the chat FAB.\n *\n * Shows the last inbound message while the chat is closed —\n * Intercom / LiveChat-style. Tap → open chat (host wires `onClick`).\n * Dismiss × → keep chat closed but stop nagging.\n *\n * Pair with `useChatUnread()` (inside `<ChatProvider>`) for state.\n */\nexport function ChatUnreadPreview({\n open,\n message,\n onClick,\n onDismiss,\n position = 'bottom-right',\n fabOffset = 24,\n fabClearance = 96,\n truncate = 2,\n zIndex = 9998,\n inline = false,\n className,\n style,\n dismissLabel = 'Mark as read',\n avatar,\n senderName,\n}: ChatUnreadPreviewProps) {\n const shouldShow = open && !!message;\n const phase = useChatPresence(shouldShow, 200);\n if (phase === 'hidden' || !message) return null;\n\n const animating = phase === 'entering' || phase === 'leaving';\n const clickable = !!onClick;\n const displayName = senderName ?? message.sender?.name ?? 'New message';\n const stamp = TIME_FORMAT.format(new Date(message.createdAt));\n\n return (\n <div\n role={clickable ? 'button' : 'status'}\n aria-live=\"polite\"\n tabIndex={clickable ? 0 : -1}\n onClick={clickable ? onClick : undefined}\n onKeyDown={\n clickable\n ? (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick?.();\n }\n }\n : undefined\n }\n className={cn(\n inline ? 'relative inline-flex' : 'fixed',\n 'flex items-start gap-2.5 max-w-[300px]',\n 'rounded-2xl border border-border bg-popover text-popover-foreground',\n 'px-3.5 py-2.5 shadow-2xl transition-all duration-200 ease-out',\n clickable &&\n 'cursor-pointer hover:bg-accent/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n originClass(position),\n animating ? 'opacity-0 scale-95 translate-y-1' : 'opacity-100 scale-100 translate-y-0',\n className,\n )}\n style={{\n ...(inline ? {} : anchorStyle(position, fabOffset, fabClearance)),\n ...(inline ? {} : { zIndex }),\n pointerEvents: phase === 'visible' ? 'auto' : 'none',\n ...style,\n }}\n >\n <div className=\"mt-0.5 shrink-0\">\n {avatar ?? deriveAvatar(message.sender, displayName)}\n </div>\n\n <div className=\"min-w-0 flex-1 text-sm leading-snug\">\n <div className=\"flex items-baseline justify-between gap-2\">\n <div className=\"truncate text-[12px] font-semibold text-foreground\">\n {displayName}\n </div>\n <div className=\"shrink-0 text-[10px] text-muted-foreground\">{stamp}</div>\n </div>\n <div\n className=\"text-foreground/90 mt-0.5 break-words\"\n style={{\n display: '-webkit-box',\n WebkitLineClamp: truncate,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n }}\n >\n {message.content}\n </div>\n </div>\n\n {onDismiss ? (\n <button\n type=\"button\"\n aria-label={dismissLabel}\n onClick={(e) => {\n e.stopPropagation();\n onDismiss();\n }}\n className={cn(\n '-mr-1 -mt-1 flex h-6 w-6 shrink-0 items-center justify-center rounded-full',\n 'text-muted-foreground transition-colors hover:bg-accent hover:text-foreground',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n )}\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n ) : null}\n </div>\n );\n}\n","'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\n\nimport { useHotkey } from '@djangocfg/ui-core/hooks';\n\nimport { ChatFAB, type ChatFABPosition, type ChatFABProps } from './ChatFAB';\nimport { ChatDock, type ChatDockProps } from './ChatDock';\nimport { ChatGreeting, type ChatGreetingProps } from './ChatGreeting';\nimport { ChatHeaderAudioToggle } from './ChatHeaderAudioToggle';\nimport { ChatUnreadPreview, type ChatUnreadPreviewProps } from './ChatUnreadPreview';\nimport type { ChatMessage } from '../types';\n\nexport interface ChatLauncherHotkey {\n /** Key (case-sensitive single char or named like 'Escape'). */\n key: string;\n /** Require Cmd (mac) or Ctrl (other). */\n meta?: boolean;\n /** Require Shift. */\n shift?: boolean;\n /** Require Alt. */\n alt?: boolean;\n}\n\nexport interface ChatLauncherGreeting\n extends Omit<ChatGreetingProps, 'open' | 'onClick' | 'onDismiss' | 'position' | 'fabOffset' | 'children'> {\n /** Greeting body — string for the default style, or any ReactNode. */\n content: ReactNode;\n /** Persistence key for \"user dismissed this greeting\" in `localStorage`. Pass `null` to disable persistence. @default null */\n dismissStorageKey?: string | null;\n /** Hide the greeting once the user opens the chat. @default true */\n hideOnOpen?: boolean;\n}\n\nexport interface ChatLauncherProps {\n /** Dock contents — typically a `<Chat>` instance. */\n children: ReactNode;\n /** FAB customization (icon, position, label, pulse, badge, tooltip, variant, size). */\n fab?: Omit<ChatFABProps, 'onClick'>;\n /** Dock customization (size, title, position, transition, mobileFullscreen). */\n dock?: Omit<ChatDockProps, 'open' | 'onClose' | 'children'>;\n /**\n * Proactive greeting bubble shown next to the FAB before the user opens the chat.\n * Set to a string or full config object. Omit to disable.\n */\n greeting?: string | ChatLauncherGreeting;\n /** Open/close via a keyboard shortcut. */\n hotkey?: ChatLauncherHotkey;\n /** Initial open state for uncontrolled mode. @default false */\n defaultOpen?: boolean;\n /** Controlled open state (pair with `onOpenChange`). */\n open?: boolean;\n /** Controlled open state setter. */\n onOpenChange?: (open: boolean) => void;\n /**\n * Focus the composer textarea when the dock opens. Saves a click for\n * every \"FAB → start typing\" interaction. @default true\n */\n autoFocusComposerOnOpen?: boolean;\n /**\n * Close the dock on `Escape`. Mirrors standard popover / drawer UX.\n * Set to `false` to disable (e.g. if you want Escape to do something\n * else inside the chat). @default true\n */\n closeOnEscape?: boolean;\n /**\n * Last inbound message (admin reply / system notice / agent push) the\n * user hasn't seen yet. Drives the `<ChatUnreadPreview>` bubble next\n * to the FAB and (by default) the FAB badge.\n *\n * Source it from `useChatUnread()` inside your `<ChatProvider>`.\n */\n unreadMessage?: ChatMessage | null;\n /**\n * Called when the user opens the chat via FAB/preview/hotkey or\n * dismisses the preview with ×. Wire to `useChatUnread().markRead`.\n */\n onMarkRead?: () => void;\n /**\n * Customize the unread bubble (`truncate`, `dismissLabel`, …).\n * `open`/`message`/`onClick`/`onDismiss`/`position`/`fabOffset` are\n * wired automatically.\n */\n unreadPreview?: Omit<\n ChatUnreadPreviewProps,\n 'open' | 'message' | 'onClick' | 'onDismiss' | 'position' | 'fabOffset'\n >;\n /**\n * Auto-inject a mute / unmute button into the header. Pass the\n * `useChatAudio()` (or any compatible `{ muted, toggleMute }`)\n * instance — the launcher renders `<ChatHeaderAudioToggle>` in the\n * header's actions slot when audio is actually configured (not silent).\n *\n * Hosts that manage their own header can ignore this prop and render\n * `<ChatHeaderAudioToggle>` directly.\n */\n audio?: { muted: boolean; toggleMute: () => void; isSilent?: boolean } | null;\n /**\n * Suppress the auto-injected audio toggle even when `audio` is passed.\n * @default false\n */\n hideAudioToggle?: boolean;\n}\n\nfunction readDismissed(storageKey: string | null | undefined): boolean {\n if (!storageKey) return false;\n if (typeof window === 'undefined') return false;\n try {\n return window.localStorage.getItem(storageKey) === '1';\n } catch {\n return false;\n }\n}\n\nfunction writeDismissed(storageKey: string | null | undefined): void {\n if (!storageKey) return;\n if (typeof window === 'undefined') return;\n try {\n window.localStorage.setItem(storageKey, '1');\n } catch {\n // private mode / storage full — silently ignore\n }\n}\n\n/**\n * Floating chat launcher = FAB + Dock + presence + optional greeting + hotkey.\n *\n * 99% of hosts use this directly. For non-FAB triggers (e.g. an inline\n * link in the page) compose `<ChatDock>` with your own button.\n */\nexport function ChatLauncher({\n children,\n fab,\n dock,\n greeting,\n hotkey,\n defaultOpen = false,\n open: controlledOpen,\n onOpenChange,\n autoFocusComposerOnOpen = true,\n closeOnEscape = true,\n unreadMessage,\n onMarkRead,\n unreadPreview,\n audio,\n hideAudioToggle = false,\n}: ChatLauncherProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\n const isControlled = controlledOpen !== undefined;\n const open = isControlled ? controlledOpen : uncontrolledOpen;\n const dockContentRef = useRef<HTMLDivElement>(null);\n\n // Auto-focus the composer when the dock opens.\n // We probe the dock subtree for the first textarea/input after the\n // enter-transition settles. Keeps the hook self-contained — no need to\n // bridge into the ChatProvider context which lives inside `children`.\n useEffect(() => {\n if (!autoFocusComposerOnOpen || !open) return;\n const t = setTimeout(() => {\n const root = dockContentRef.current;\n if (!root) return;\n const target = root.querySelector<HTMLElement>(\n 'textarea:not([disabled]):not([readonly]), input[type=\"text\"]:not([disabled]):not([readonly])',\n );\n target?.focus();\n }, 120);\n return () => clearTimeout(t);\n }, [open, autoFocusComposerOnOpen]);\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) setUncontrolledOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange],\n );\n const toggleOpen = useCallback(() => setOpen(!open), [open, setOpen]);\n\n // Two-step Escape (ChatGPT / Slack behaviour):\n // - When focus is inside a textarea / input / contenteditable, first Esc\n // just blurs — drafts survive accidental presses.\n // - When focus is elsewhere (the user already left the composer or never\n // focused it), Esc closes the dock.\n // Disabled when chat is shut so we don't intercept page-level Esc bindings.\n useHotkey(\n 'escape',\n (e) => {\n const target = (e?.target as HTMLElement | null) ?? null;\n const inEditable =\n !!target &&\n (target.matches?.('input, textarea, [contenteditable=\"true\"]') ?? false);\n if (inEditable) {\n target.blur();\n return;\n }\n setOpen(false);\n },\n { enabled: closeOnEscape && open },\n );\n\n // Normalize greeting prop.\n const greetingConfig: ChatLauncherGreeting | null =\n greeting === undefined\n ? null\n : typeof greeting === 'string'\n ? { content: greeting }\n : greeting;\n\n const [dismissed, setDismissed] = useState(() =>\n readDismissed(greetingConfig?.dismissStorageKey),\n );\n\n // Hotkey.\n useEffect(() => {\n if (!hotkey) return;\n const handler = (e: KeyboardEvent) => {\n const metaOk = hotkey.meta ? e.metaKey || e.ctrlKey : !e.metaKey && !e.ctrlKey;\n const shiftOk = hotkey.shift ? e.shiftKey : !e.shiftKey;\n const altOk = hotkey.alt ? e.altKey : !e.altKey;\n if (!metaOk || !shiftOk || !altOk) return;\n if (e.key !== hotkey.key) return;\n e.preventDefault();\n setOpen(!open);\n };\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [hotkey?.key, hotkey?.meta, hotkey?.shift, hotkey?.alt, open, setOpen, hotkey]);\n\n // Greeting visibility: respect dismissal, hideOnOpen, and the actual open state.\n const greetingOpen = !!greetingConfig\n && !dismissed\n && (greetingConfig.hideOnOpen === false || !open);\n\n const fabPosition: ChatFABPosition = fab?.position ?? 'bottom-right';\n const fabOffset = fab?.offset ?? 24;\n\n const handleGreetingDismiss = () => {\n setDismissed(true);\n writeDismissed(greetingConfig?.dismissStorageKey);\n };\n\n const handleGreetingClick = () => {\n setOpen(true);\n // Tap-to-open also clears the proactive bubble — pushing it again on\n // the same visit would feel spammy. Persisted dismissal honours the\n // storage key so it doesn't reappear after navigation either.\n setDismissed(true);\n writeDismissed(greetingConfig?.dismissStorageKey);\n };\n\n // Mark-as-read also fires when the chat opens through any path (FAB,\n // hotkey, controlled state) — symmetric with click-to-open via the\n // preview itself.\n useEffect(() => {\n if (open && unreadMessage) onMarkRead?.();\n }, [open, unreadMessage, onMarkRead]);\n\n // Unread preview replaces the greeting when there's a real inbound\n // message to surface — same anchor, more relevant content.\n const unreadOpen = !open && !!unreadMessage;\n const handleUnreadClick = () => {\n setOpen(true);\n onMarkRead?.();\n };\n const handleUnreadDismiss = () => {\n onMarkRead?.();\n };\n\n // Auto-derive a \"1\" badge from unread when the host didn't set one.\n const resolvedFab = unreadMessage && fab?.badge === undefined\n ? { ...fab, badge: 1 }\n : fab;\n\n return (\n <>\n <ChatFAB {...resolvedFab} onClick={toggleOpen} />\n {unreadMessage ? (\n <ChatUnreadPreview\n {...unreadPreview}\n open={unreadOpen}\n message={unreadMessage}\n onClick={handleUnreadClick}\n onDismiss={handleUnreadDismiss}\n position={fabPosition}\n fabOffset={fabOffset}\n />\n ) : greetingConfig ? (\n <ChatGreeting\n {...greetingConfig}\n open={greetingOpen}\n onClick={handleGreetingClick}\n onDismiss={handleGreetingDismiss}\n position={fabPosition}\n fabOffset={fabOffset}\n >\n {greetingConfig.content}\n </ChatGreeting>\n ) : null}\n <ChatDock\n {...dock}\n open={open}\n onClose={() => setOpen(false)}\n headerActions={\n (audio && !audio.isSilent && !hideAudioToggle) || dock?.headerActions ? (\n <>\n {dock?.headerActions}\n {audio && !audio.isSilent && !hideAudioToggle ? (\n <ChatHeaderAudioToggle muted={audio.muted} onToggle={audio.toggleMute} />\n ) : null}\n </>\n ) : undefined\n }\n >\n <div ref={dockContentRef} className=\"flex h-full min-h-0 min-w-0 flex-col\">\n {children}\n </div>\n </ChatDock>\n </>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/tools/Chat/launcher/ChatFAB.tsx","../src/tools/Chat/launcher/ChatHeader.tsx","../src/tools/Chat/launcher/useChatPresence.ts","../src/tools/Chat/launcher/ChatDock.tsx","../src/tools/Chat/launcher/ChatHeaderActionButton.tsx","../src/tools/Chat/launcher/ChatHeaderModeToggle.tsx","../src/tools/Chat/launcher/ChatHeaderAudioToggle.tsx","../src/tools/Chat/hooks/useChatReset.ts","../src/tools/Chat/launcher/ChatHeaderResetButton.tsx","../src/tools/SpeechRecognition/core/languages-catalog.ts","../src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx","../src/tools/SpeechRecognition/context/SpeechRecognitionProvider.tsx","../src/components/lazy-wrapper.tsx","../src/tools/SpeechRecognition/lazy.tsx","../src/tools/Chat/launcher/ChatHeaderLanguageButton.tsx","../src/tools/Chat/launcher/ChatGreeting.tsx","../src/tools/Chat/launcher/ChatUnreadPreview.tsx","../src/tools/Chat/launcher/ChatLauncher.tsx"],"names":["jsxs","cn","jsx","Bot","useIsTabletOrBelow","useEffect","ChatHeaderActionButton","useState","useMemo","X","anchorStyle","originClass","useRef","useCallback","Fragment"],"mappings":"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","file":"chunk-NTVBIIUD.mjs","sourcesContent":["'use client';\n\nimport { Bot } from 'lucide-react';\nimport type { CSSProperties, ReactNode } from 'react';\n\nimport { useIsPhone, useIsTabletOrBelow } from '@djangocfg/ui-core/hooks';\nimport { cn } from '@djangocfg/ui-core/lib';\n\nexport type ChatFABPosition =\n | 'bottom-right'\n | 'bottom-left'\n | 'top-right'\n | 'top-left';\n\nexport type ChatFABVariant = 'simple' | 'animated' | 'glass';\nexport type ChatFABSize = 'sm' | 'md' | 'lg' | 'responsive';\n\nexport interface ChatFABProps {\n /** Click handler — typically toggles a `ChatDock`. */\n onClick: () => void;\n /** Accessible label. */\n ariaLabel?: string;\n /** Icon inside the FAB. Defaults to a bot glyph. */\n icon?: ReactNode;\n /** Visual style. @default 'simple' */\n variant?: ChatFABVariant;\n /** Button size. @default 'md' */\n size?: ChatFABSize;\n /** Fixed-screen position. @default 'bottom-right' */\n position?: ChatFABPosition;\n /** Pixel offset from screen edges. @default 24 */\n offset?: number;\n /** z-index for the button. @default 9999 */\n zIndex?: number;\n /** Show a small attention dot (unread / new). */\n pulse?: boolean;\n /**\n * Numeric badge — unread count. Numbers > 9 render as \"9+\".\n * Overrides `pulse` when both set.\n */\n badge?: number;\n /** Hover tooltip text. Shows next to the FAB on hover/focus. */\n tooltip?: string;\n /**\n * Render in-place (no fixed positioning) so the FAB sits inline in the\n * normal document flow. Useful for stories, screenshots, and previews\n * inside a contained playground panel. @default false\n */\n inline?: boolean;\n /** Override classes on the button itself. */\n className?: string;\n /** Extra style on the button (caller-controlled overrides). */\n style?: CSSProperties;\n}\n\ntype ChatFABFixedSize = Exclude<ChatFABSize, 'responsive'>;\n\nconst SIZE_PX: Record<ChatFABFixedSize, number> = { sm: 44, md: 56, lg: 64 };\nconst ICON_PX: Record<ChatFABFixedSize, number> = { sm: 18, md: 22, lg: 26 };\n\n/**\n * Resolve `size='responsive'` to a concrete fixed size based on the\n * viewport. Phone → `sm`, tablet → `md`, desktop → `lg`. `inline`\n * previews always collapse to `md` so stories stay stable.\n */\nfunction useEffectiveFABSize(size: ChatFABSize, inline: boolean): ChatFABFixedSize {\n const isPhone = useIsPhone();\n const isBelowDesktop = useIsTabletOrBelow();\n if (size !== 'responsive') return size;\n if (inline) return 'md';\n if (isPhone) return 'sm';\n if (isBelowDesktop) return 'md';\n return 'lg';\n}\n\nfunction positionStyle(position: ChatFABPosition, offset: number): CSSProperties {\n const [vert, horiz] = position.split('-') as ['bottom' | 'top', 'right' | 'left'];\n return { [vert]: offset, [horiz]: offset } as CSSProperties;\n}\n\nfunction tooltipSideClasses(position: ChatFABPosition): string {\n // Tooltip sits opposite-horizontal to the FAB so it doesn't run off-screen.\n return position.endsWith('right')\n ? 'right-full mr-3 origin-right'\n : 'left-full ml-3 origin-left';\n}\n\nfunction Badge({ value }: { value: number }) {\n const display = value > 9 ? '9+' : String(value);\n return (\n <span\n aria-hidden=\"true\"\n className={cn(\n 'absolute -right-1 -top-1 inline-flex min-w-[18px] h-[18px] items-center justify-center',\n 'rounded-full bg-destructive px-1 text-[10px] font-semibold leading-none text-destructive-foreground',\n 'ring-2 ring-background',\n )}\n >\n {display}\n </span>\n );\n}\n\nfunction PulseDot() {\n return (\n <span aria-hidden=\"true\" className=\"absolute right-1 top-1\">\n <span className=\"relative inline-flex h-2.5 w-2.5\">\n <span className=\"absolute inset-0 rounded-full bg-destructive opacity-75 animate-ping\" />\n <span className=\"relative inline-flex h-2.5 w-2.5 rounded-full bg-destructive ring-2 ring-background\" />\n </span>\n </span>\n );\n}\n\nfunction Tooltip({ text, side }: { text: string; side: string }) {\n return (\n <span\n role=\"tooltip\"\n className={cn(\n 'pointer-events-none absolute top-1/2 -translate-y-1/2 whitespace-nowrap',\n 'rounded-md bg-popover px-2.5 py-1 text-xs font-medium text-popover-foreground shadow-md',\n 'border border-border opacity-0 scale-95 transition-all duration-150',\n 'group-hover:opacity-100 group-hover:scale-100',\n 'group-focus-within:opacity-100 group-focus-within:scale-100',\n side,\n )}\n >\n {text}\n </span>\n );\n}\n\n/**\n * Floating action button for opening a chat dock. Pure presentation — owns\n * no state. Wire it to whatever toggles your dock open.\n *\n * For the common \"FAB + dock + hotkey\" composition, use `<ChatLauncher>` —\n * this primitive is only useful when you need custom triggering.\n */\nexport function ChatFAB({\n onClick,\n ariaLabel = 'Open chat',\n icon,\n variant = 'simple',\n size = 'responsive',\n position = 'bottom-right',\n offset = 24,\n zIndex = 9999,\n pulse = false,\n badge,\n tooltip,\n inline = false,\n className,\n style,\n}: ChatFABProps) {\n const effectiveSize = useEffectiveFABSize(size, inline);\n const px = SIZE_PX[effectiveSize];\n const iconPx = ICON_PX[effectiveSize];\n const renderedIcon = icon ?? <Bot size={iconPx} />;\n\n const baseButton = cn(\n 'relative grid place-items-center rounded-full focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n 'transition-transform hover:scale-105',\n );\n\n return (\n <div\n className={cn('group', inline ? 'relative inline-flex' : 'fixed')}\n style={\n inline\n ? undefined\n : { ...positionStyle(position, offset), zIndex }\n }\n >\n {variant === 'animated' && (\n <AnimatedFAB\n ariaLabel={ariaLabel}\n onClick={onClick}\n size={px}\n className={className}\n style={style}\n >\n {renderedIcon}\n </AnimatedFAB>\n )}\n\n {variant === 'glass' && (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n onClick={onClick}\n className={cn(\n baseButton,\n 'border border-border/40 bg-background/60 text-foreground shadow-lg backdrop-blur-xl',\n 'hover:bg-background/80',\n className,\n )}\n style={{ width: px, height: px, ...style }}\n >\n {renderedIcon}\n {badge !== undefined ? <Badge value={badge} /> : pulse ? <PulseDot /> : null}\n </button>\n )}\n\n {variant === 'simple' && (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n onClick={onClick}\n className={cn(\n baseButton,\n 'bg-primary text-primary-foreground hover:bg-primary/90 shadow-2xl',\n className,\n )}\n style={{ width: px, height: px, ...style }}\n >\n {renderedIcon}\n {badge !== undefined ? <Badge value={badge} /> : pulse ? <PulseDot /> : null}\n </button>\n )}\n\n {tooltip && <Tooltip text={tooltip} side={tooltipSideClasses(position)} />}\n </div>\n );\n}\n\n// ── Animated variant ──────────────────────────────────────────────────────\n// Orbital gradient ring + glow + entrance bounce. Self-contained CSS via\n// inline <style>, scoped by a unique class name per instance to avoid\n// collisions when multiple FABs mount.\n\ninterface AnimatedFABProps {\n ariaLabel: string;\n onClick: () => void;\n size: number;\n className?: string;\n style?: CSSProperties;\n children: ReactNode;\n}\n\nfunction AnimatedFAB({ ariaLabel, onClick, size, className, style, children }: AnimatedFABProps) {\n return (\n <>\n <style>{ANIMATED_CSS}</style>\n <div\n className={cn('cmdop-fab-anim', className)}\n style={{ width: size, height: size, ...style }}\n >\n <div className=\"cmdop-fab-anim-glow\">\n <div className=\"cmdop-fab-anim-wrap\">\n <div className=\"cmdop-fab-anim-grad cmdop-fab-anim-grad-1\" />\n <div className=\"cmdop-fab-anim-grad cmdop-fab-anim-grad-2\" />\n <div className=\"cmdop-fab-anim-inner\" />\n <button\n type=\"button\"\n aria-label={ariaLabel}\n onClick={onClick}\n className=\"cmdop-fab-anim-btn\"\n >\n <span className=\"cmdop-fab-anim-icon\">{children}</span>\n </button>\n </div>\n </div>\n </div>\n </>\n );\n}\n\nconst ANIMATED_CSS = `\n.cmdop-fab-anim {\n position: relative;\n pointer-events: auto;\n}\n.cmdop-fab-anim-glow {\n width: 100%; height: 100%;\n border-radius: 50%;\n overflow: hidden;\n animation:\n cmdop-fab-entrance 0.6s cubic-bezier(0.34, 1.45, 0.64, 1) forwards,\n cmdop-fab-glow-shift 8s ease-in-out 0.6s infinite;\n}\n.cmdop-fab-anim-wrap {\n position: relative; width: 100%; height: 100%;\n border-radius: 50%; overflow: hidden;\n}\n.cmdop-fab-anim-grad { position: absolute; inset: 0; border-radius: 50%; }\n.cmdop-fab-anim-grad-1 {\n background: conic-gradient(\n from 0deg,\n #fbbf24 0%, rgba(251,191,36,0) 15%,\n rgba(168,85,247,0) 20%, #a855f7 35%, rgba(168,85,247,0) 50%,\n rgba(20,184,166,0) 55%, #14b8a6 70%, rgba(20,184,166,0) 85%,\n rgba(236,72,153,0) 88%, #ec4899 97%, #fbbf24 100%\n );\n animation: cmdop-fab-rotate 7s linear infinite;\n filter: blur(1px); opacity: 0.95;\n}\n.cmdop-fab-anim-grad-2 {\n inset: 1px;\n background: conic-gradient(\n from 180deg,\n #a855f7 0%, rgba(168,85,247,0) 20%,\n rgba(20,184,166,0) 30%, #14b8a6 50%, rgba(20,184,166,0) 70%,\n rgba(251,191,36,0) 75%, #fbbf24 95%, #a855f7 100%\n );\n animation: cmdop-fab-rotate-rev 9s linear infinite;\n filter: blur(0.75px); opacity: 0.7;\n}\n.cmdop-fab-anim-inner {\n position: absolute; inset: 3px; border-radius: 50%;\n background: rgba(10, 10, 10, 0.65);\n backdrop-filter: blur(12px) saturate(1.8);\n -webkit-backdrop-filter: blur(12px) saturate(1.8);\n animation: cmdop-fab-inner-glow 5s ease-in-out infinite;\n}\n.cmdop-fab-anim-btn {\n position: absolute; inset: 2px;\n border-radius: 50%; border: none; background: transparent;\n cursor: pointer; display: flex; align-items: center; justify-content: center;\n transition: transform 0.2s;\n}\n.cmdop-fab-anim-btn:hover { transform: scale(1.06); }\n.cmdop-fab-anim-icon {\n color: #fbbf24; display: flex;\n filter: drop-shadow(0 0 6px rgba(251,191,36,0.8));\n animation: cmdop-fab-icon-pulse 2.5s ease-in-out infinite;\n}\n@keyframes cmdop-fab-rotate { to { transform: rotate(360deg); } }\n@keyframes cmdop-fab-rotate-rev { to { transform: rotate(-360deg); } }\n@keyframes cmdop-fab-entrance {\n 0% { transform: scale(0); }\n 50% { transform: scale(1.08); }\n 70% { transform: scale(0.98); }\n 100% { transform: scale(1); }\n}\n@keyframes cmdop-fab-glow-shift {\n 0%, 100% { box-shadow: 0 0 20px rgba(251,191,36,0.5), 0 0 40px rgba(168,85,247,0.3); }\n 33% { box-shadow: 0 0 20px rgba(168,85,247,0.5), 0 0 40px rgba(20,184,166,0.3); }\n 66% { box-shadow: 0 0 20px rgba(20,184,166,0.5), 0 0 40px rgba(236,72,153,0.3); }\n}\n@keyframes cmdop-fab-icon-pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.85; transform: scale(1.15); }\n}\n@keyframes cmdop-fab-inner-glow {\n 0%, 100% { box-shadow: inset 0 0 20px rgba(251,191,36,0.25), inset 0 0 40px rgba(168,85,247,0.15); }\n 50% { box-shadow: inset 0 0 25px rgba(168,85,247,0.3), inset 0 0 45px rgba(20,184,166,0.2); }\n}\n`;\n","'use client';\n\nimport { Bot, X } from 'lucide-react';\nimport type { ReactNode } from 'react';\n\nimport { Button } from '@djangocfg/ui-core/components';\nimport { cn } from '@djangocfg/ui-core/lib';\n\nexport interface ChatHeaderProps {\n /** Window title text. */\n title?: ReactNode;\n /** Icon next to the title. Defaults to a bot glyph. */\n icon?: ReactNode;\n /**\n * Action slot — appears to the right of the title, before the close button.\n * Use for reset / settings / minimize / etc. Compose with `ChatHeaderActionButton`.\n */\n actions?: ReactNode;\n /** Show the close (×) button. @default true */\n showClose?: boolean;\n /** Close click handler. */\n onClose?: () => void;\n /** ARIA label for the close button. @default 'Close' */\n closeLabel?: string;\n /** Replace the close button entirely (rare — most hosts want the default). */\n closeSlot?: ReactNode;\n /** Extra classes on the `<header>` element. */\n className?: string;\n}\n\n/**\n * Standalone chat header — title + icon + action slot + close button.\n *\n * Used by `<ChatDock>` automatically, but can be rendered standalone when\n * the host owns the chat container (e.g. embedded inline in a page).\n */\nexport function ChatHeader({\n title,\n icon,\n actions,\n showClose = true,\n onClose,\n closeLabel = 'Close',\n closeSlot,\n className,\n}: ChatHeaderProps) {\n return (\n <header\n className={cn(\n 'border-border bg-muted/30 flex shrink-0 items-center justify-between border-b px-4 py-2.5',\n className,\n )}\n >\n <div className=\"flex min-w-0 items-center gap-2 text-sm font-semibold\">\n {icon ?? <Bot className=\"text-primary h-4 w-4 shrink-0\" />}\n <span className=\"truncate\">{title}</span>\n </div>\n\n <div className=\"flex items-center gap-0.5\">\n {actions}\n {closeSlot ??\n (showClose && onClose && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={onClose}\n aria-label={closeLabel}\n className=\"-mr-1 h-7 w-7 p-0\"\n >\n <X className=\"h-4 w-4\" />\n </Button>\n ))}\n </div>\n </header>\n );\n}\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nexport type ChatPresencePhase = 'hidden' | 'entering' | 'visible' | 'leaving';\n\n/**\n * Presence state machine for floating popovers.\n *\n * Drives a four-phase lifecycle so enter/leave CSS transitions actually fire:\n *\n * hidden → entering (mount, transition class starts) → visible\n * visible → leaving (transition runs) → hidden (unmount)\n *\n * Mounting in `entering` and ticking to `visible` on the next paint is what\n * lets transition classes animate. Without it the element appears already\n * at its final state and CSS transitions never observe a change.\n *\n * @param open - controlled open state\n * @param exitDurationMs - how long the leave transition runs; should match CSS\n */\nexport function useChatPresence(open: boolean, exitDurationMs = 200): ChatPresencePhase {\n const [phase, setPhase] = useState<ChatPresencePhase>('hidden');\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (timerRef.current) clearTimeout(timerRef.current);\n\n if (open) {\n setPhase('entering');\n // One paint later: switch to 'visible' so the CSS transition runs.\n timerRef.current = setTimeout(() => setPhase('visible'), 16);\n } else {\n setPhase('leaving');\n timerRef.current = setTimeout(() => setPhase('hidden'), exitDurationMs);\n }\n\n return () => {\n if (timerRef.current) clearTimeout(timerRef.current);\n };\n }, [open, exitDurationMs]);\n\n return phase;\n}\n","'use client';\n\nimport { useEffect } from 'react';\nimport type { CSSProperties, ReactNode } from 'react';\n\nimport { Portal } from '@djangocfg/ui-core/components';\nimport { useIsMobile, useIsTabletOrBelow } from '@djangocfg/ui-core/hooks';\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport { ChatHeader } from './ChatHeader';\nimport { useChatPresence } from './useChatPresence';\nimport type { ChatFABPosition } from './ChatFAB';\n\nexport type ChatDockMode = 'popover' | 'side';\nexport type ChatDockSide = 'left' | 'right';\n\nexport interface ChatDockProps {\n /** Controlled open state. */\n open: boolean;\n /** Called when the user clicks the close button. */\n onClose: () => void;\n /** Dock contents (typically a `<Chat>` component). */\n children: ReactNode;\n /**\n * Visual mode.\n * - `popover` (default): floating card anchored to a corner, fixed size, FAB-style.\n * - `side`: docked panel pinned to the left/right edge, full viewport height.\n */\n mode?: ChatDockMode;\n /** Side for `mode='side'`. @default 'right' */\n side?: ChatDockSide;\n /** Header title text. */\n title?: ReactNode;\n /** Header icon. Defaults to a bot glyph. */\n icon?: ReactNode;\n /**\n * Header actions slot (right side, before the close button).\n * Use `ChatHeaderActionButton` to keep visual consistency.\n */\n headerActions?: ReactNode;\n /** Hide the header entirely (you render your own inside `children`). */\n hideHeader?: boolean;\n /** ARIA label for the close button. @default 'Close' */\n closeLabel?: string;\n /** Dock width in px. Clamped to viewport. @default 480 (popover) / 420 (side) */\n width?: number;\n /** Dock height in px. Only used in `popover` mode. @default 720 */\n height?: number;\n /** Which screen corner to dock to in `popover` mode. @default 'bottom-right' */\n position?: ChatFABPosition;\n /** Offset from screen edges in px (popover only). @default 24 / 96 */\n offset?: { horizontal?: number; vertical?: number };\n /** Transition duration in ms — should match CSS animation. @default 200 */\n exitDurationMs?: number;\n /** z-index. @default 10000 */\n zIndex?: number;\n /** Accessible dialog label. */\n ariaLabel?: string;\n /** Extra classes on the dock container. */\n className?: string;\n /**\n * Take over the full viewport on mobile (< 768px). Applies to both modes.\n * @default true\n */\n mobileFullscreen?: boolean;\n /**\n * Render in-place (not in `document.body` via a portal). Useful for stories,\n * screenshots, or wrapping the dock inside a custom container. @default false\n */\n disablePortal?: boolean;\n /**\n * Drop fixed positioning entirely — the dock renders as a normal flow\n * element sized by `width`/`height`. Combine with `disablePortal` for\n * stories/previews where the dock should sit inside the panel instead\n * of attaching to the viewport. @default false\n */\n inline?: boolean;\n /**\n * In `mode='side'`, reserve space on the document body so page content\n * isn't covered by the dock. Sets `padding-{side}` on `<body>` while\n * the dock is open and exposes the width via the `--chat-dock-reserve`\n * CSS variable for custom layouts. @default true (when mode='side')\n */\n reserveBodySpace?: boolean;\n}\n\nfunction dockPositionStyle(\n position: ChatFABPosition,\n horizontal: number,\n vertical: number,\n): CSSProperties {\n const [vert, horiz] = position.split('-') as ['bottom' | 'top', 'right' | 'left'];\n return { [vert]: vertical, [horiz]: horizontal } as CSSProperties;\n}\n\n/**\n * Fixed-position chat surface. Two modes:\n *\n * - `popover` — floating card anchored to a corner. Companion to `<ChatFAB>`.\n * - `side` — full-height panel pinned to the left/right edge. App-shell style.\n *\n * Renders only when `open` is true (plus the leave-transition tail). Uses\n * `useChatPresence` for the four-phase mount/animate/unmount cycle.\n */\nexport function ChatDock({\n open,\n onClose,\n children,\n mode = 'popover',\n side = 'right',\n title = 'Chat',\n icon,\n headerActions,\n hideHeader = false,\n closeLabel,\n width,\n height = 720,\n position = 'bottom-right',\n offset,\n exitDurationMs = 200,\n zIndex = 10000,\n ariaLabel,\n className,\n mobileFullscreen = true,\n disablePortal = false,\n inline = false,\n reserveBodySpace,\n}: ChatDockProps) {\n const phase = useChatPresence(open, exitDurationMs);\n const isMobile = useIsMobile();\n // Side mode is desktop-only — narrow viewports fall back to popover so\n // we never cover 33% of a phone/tablet with a chat panel.\n const isBelowDesktop = useIsTabletOrBelow();\n const effectiveMode: ChatDockMode =\n mode === 'side' && !isBelowDesktop ? 'side' : 'popover';\n const fullscreen = mobileFullscreen && isMobile;\n\n // Reserve body padding for side mode so page content stays visible\n // next to the dock. Auto-on when mode='side' unless explicitly disabled.\n const wantsReserve =\n !inline && !fullscreen && effectiveMode === 'side' && (reserveBodySpace ?? true);\n const resolvedSideWidth = width ?? 420;\n useEffect(() => {\n if (!wantsReserve || phase === 'hidden') return;\n const body = document.body;\n if (!body) return;\n const cssVar = `${resolvedSideWidth}px`;\n const padKey = side === 'right' ? 'paddingRight' : 'paddingLeft';\n const prevPad = body.style[padKey as 'paddingRight' | 'paddingLeft'];\n const prevVar = body.style.getPropertyValue('--chat-dock-reserve');\n body.style[padKey as 'paddingRight' | 'paddingLeft'] = cssVar;\n body.style.setProperty('--chat-dock-reserve', cssVar);\n return () => {\n body.style[padKey as 'paddingRight' | 'paddingLeft'] = prevPad;\n if (prevVar) body.style.setProperty('--chat-dock-reserve', prevVar);\n else body.style.removeProperty('--chat-dock-reserve');\n };\n }, [wantsReserve, phase, side, resolvedSideWidth]);\n\n if (phase === 'hidden') return null;\n\n const animating = phase === 'entering' || phase === 'leaving';\n\n const horizontal = offset?.horizontal ?? 24;\n const vertical = offset?.vertical ?? 96;\n const resolvedWidth = width ?? (effectiveMode === 'side' ? resolvedSideWidth : 480);\n\n let containerStyle: CSSProperties;\n let cornerClass: string;\n\n // Dynamic viewport heights — `dvh` follows iOS Safari URL bar (preferred),\n // `svh`/`lvh` are the small/large fallbacks if the dynamic value isn't\n // supported. Min-height keeps the popover usable even on tiny landscape phones.\n const dynVH = '100dvh';\n\n if (inline) {\n containerStyle = {\n position: 'relative',\n width: resolvedWidth,\n height,\n maxHeight: `calc(${dynVH} - 16px)`,\n pointerEvents: phase === 'visible' ? 'auto' : 'none',\n };\n cornerClass = 'rounded-xl border';\n } else if (fullscreen) {\n containerStyle = {\n position: 'fixed',\n top: 0,\n [side === 'left' ? 'left' : 'right']: 0,\n width: '100vw',\n height: dynVH,\n zIndex,\n pointerEvents: phase === 'visible' ? 'auto' : 'none',\n } as CSSProperties;\n cornerClass = 'rounded-none border-0';\n } else if (effectiveMode === 'side') {\n containerStyle = {\n position: 'fixed',\n top: 0,\n [side]: 0,\n height: dynVH,\n zIndex,\n width: `min(${resolvedWidth}px, 100vw)`,\n pointerEvents: phase === 'visible' ? 'auto' : 'none',\n } as CSSProperties;\n cornerClass = side === 'right' ? 'rounded-none border-l' : 'rounded-none border-r';\n } else {\n // popover — anchored to a corner, capped to viewport so it never\n // overlaps the FAB or goes off-screen on small windows.\n const heightCap = `calc(${dynVH} - ${vertical + 24}px)`;\n containerStyle = {\n position: 'fixed',\n ...dockPositionStyle(position, horizontal, vertical),\n zIndex,\n width: `min(${resolvedWidth}px, calc(100vw - 32px))`,\n height: `min(${height}px, ${heightCap})`,\n minHeight: `min(320px, ${heightCap})`,\n pointerEvents: phase === 'visible' ? 'auto' : 'none',\n };\n cornerClass = 'rounded-xl border';\n }\n\n // Per-mode enter/leave transform classes — side slides in horizontally,\n // popover scales + lifts.\n const enterClass = (() => {\n if (fullscreen) return 'opacity-0';\n if (effectiveMode === 'side') {\n return side === 'right' ? 'opacity-0 translate-x-4' : 'opacity-0 -translate-x-4';\n }\n return 'opacity-0 scale-95 translate-y-2';\n })();\n const visibleClass = 'opacity-100 scale-100 translate-y-0 translate-x-0';\n\n return (\n <Portal disablePortal={disablePortal || inline}>\n <div\n role=\"dialog\"\n aria-label={ariaLabel ?? (typeof title === 'string' ? title : 'Chat')}\n aria-hidden={phase === 'leaving'}\n className={cn(\n 'bg-popover text-popover-foreground border-border',\n 'flex flex-col overflow-hidden shadow-2xl',\n cornerClass,\n 'transition-all duration-200 ease-out',\n animating ? enterClass : visibleClass,\n className,\n )}\n style={containerStyle}\n >\n {!hideHeader && (\n <ChatHeader\n title={title}\n icon={icon}\n actions={headerActions}\n onClose={onClose}\n closeLabel={closeLabel}\n />\n )}\n <div className=\"min-h-0 min-w-0 flex-1 overflow-hidden\">{children}</div>\n </div>\n </Portal>\n );\n}\n","'use client';\n\nimport { forwardRef } from 'react';\nimport type { ButtonHTMLAttributes, ReactNode } from 'react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nexport interface ChatHeaderActionButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {\n /** Icon (required). */\n icon: ReactNode;\n /** Accessible label + native tooltip. */\n ariaLabel: string;\n /** Optional unread / status badge — small number on top-right. */\n badge?: number;\n /** Mark as destructive — uses destructive hover tokens. */\n destructive?: boolean;\n /** Optional visual loading state (e.g. while reset is in flight). */\n loading?: boolean;\n}\n\n/**\n * Compact icon button for the chat header actions slot.\n *\n * Standard chrome: 28×28 ghost button, hover bg-accent, focus ring, optional\n * destructive variant, optional numeric badge for unread / pending.\n *\n * @example\n * ```tsx\n * <ChatHeader\n * title=\"Assistant\"\n * onClose={close}\n * actions={\n * <>\n * <ChatHeaderActionButton\n * icon={<RotateCcw className=\"h-3.5 w-3.5\" />}\n * ariaLabel=\"Clear context\"\n * onClick={handleReset}\n * loading={isResetting}\n * />\n * <ChatHeaderActionButton\n * icon={<Settings className=\"h-3.5 w-3.5\" />}\n * ariaLabel=\"Settings\"\n * onClick={openSettings}\n * />\n * </>\n * }\n * />\n * ```\n */\nexport const ChatHeaderActionButton = forwardRef<HTMLButtonElement, ChatHeaderActionButtonProps>(\n function ChatHeaderActionButton(\n { icon, ariaLabel, badge, destructive, loading, disabled, className, ...rest },\n ref,\n ) {\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-label={ariaLabel}\n title={ariaLabel}\n disabled={disabled || loading}\n className={cn(\n 'relative inline-flex h-7 w-7 items-center justify-center rounded-md',\n 'text-muted-foreground transition-colors',\n 'hover:bg-accent hover:text-foreground',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n 'disabled:opacity-50 disabled:cursor-not-allowed',\n destructive && 'hover:bg-destructive/15 hover:text-destructive',\n loading && 'animate-pulse',\n className,\n )}\n {...rest}\n >\n {icon}\n {badge !== undefined && (\n <span\n aria-hidden=\"true\"\n className=\"absolute -right-0.5 -top-0.5 inline-flex min-w-[14px] h-[14px] items-center justify-center rounded-full bg-destructive px-1 text-[9px] font-semibold leading-none text-destructive-foreground ring-2 ring-background\"\n >\n {badge > 9 ? '9+' : badge}\n </span>\n )}\n </button>\n );\n },\n);\n","'use client';\n\nimport { PanelRightOpen, PanelRightClose } from 'lucide-react';\n\nimport { useIsTabletOrBelow } from '@djangocfg/ui-core/hooks';\n\nimport { ChatHeaderActionButton } from './ChatHeaderActionButton';\nimport type { ChatDockMode } from './ChatDock';\n\nexport interface ChatHeaderModeToggleProps {\n /** Current dock mode. */\n mode: ChatDockMode;\n /** Toggle handler. Wire to `useChatDockPrefs().toggleMode`. */\n onToggle: () => void;\n /** Override aria/tooltip label for popover→side. */\n expandLabel?: string;\n /** Override aria/tooltip label for side→popover. */\n collapseLabel?: string;\n /**\n * Always render — useful for stories. By default the toggle hides itself on\n * viewports below `lg` (1024px) since side mode falls back to popover there.\n */\n forceVisible?: boolean;\n}\n\n/**\n * \"Dock to side\" / \"back to popover\" toggle.\n *\n * Side mode is desktop-only — on viewports below `lg` (1024px) `<ChatDock>`\n * silently falls back to popover anyway, so the toggle has nothing to do\n * and auto-hides. Pass `forceVisible` to override (e.g. in stories).\n */\nexport function ChatHeaderModeToggle({\n mode,\n onToggle,\n expandLabel = 'Dock to side',\n collapseLabel = 'Back to popover',\n forceVisible = false,\n}: ChatHeaderModeToggleProps) {\n const isBelowDesktop = useIsTabletOrBelow();\n if (isBelowDesktop && !forceVisible) return null;\n\n const isSide = mode === 'side';\n return (\n <ChatHeaderActionButton\n icon={\n isSide ? (\n <PanelRightClose className=\"h-3.5 w-3.5\" />\n ) : (\n <PanelRightOpen className=\"h-3.5 w-3.5\" />\n )\n }\n ariaLabel={isSide ? collapseLabel : expandLabel}\n onClick={onToggle}\n />\n );\n}\n","'use client';\n\nimport { Volume2, VolumeX } from 'lucide-react';\n\nimport { ChatHeaderActionButton } from './ChatHeaderActionButton';\n\nexport interface ChatHeaderAudioToggleProps {\n /** Current muted state. */\n muted: boolean;\n /** Toggle handler. Wire to `useChatAudio().setMuted` or `toggleMute`. */\n onToggle: () => void;\n /** Override tooltip label for muted → unmuted. */\n unmuteLabel?: string;\n /** Override tooltip label for unmuted → muted. */\n muteLabel?: string;\n}\n\n/**\n * Mute / unmute notification sounds. Drop into a `<ChatHeader>` actions\n * slot or into `<ChatDock headerActions>`.\n *\n * @example\n * ```tsx\n * const audio = useChatAudio({ sounds: {...} });\n * <ChatHeaderAudioToggle muted={audio.muted} onToggle={() => audio.setMuted(!audio.muted)} />\n * ```\n */\nexport function ChatHeaderAudioToggle({\n muted,\n onToggle,\n unmuteLabel = 'Unmute notifications',\n muteLabel = 'Mute notifications',\n}: ChatHeaderAudioToggleProps) {\n return (\n <ChatHeaderActionButton\n icon={\n muted ? (\n <VolumeX className=\"h-3.5 w-3.5\" />\n ) : (\n <Volume2 className=\"h-3.5 w-3.5\" />\n )\n }\n ariaLabel={muted ? unmuteLabel : muteLabel}\n onClick={onToggle}\n />\n );\n}\n","'use client';\n\nimport { useCallback, useState } from 'react';\n\nexport interface UseChatResetOptions {\n /**\n * Backend call that performs the actual reset (e.g. POST /chat/reset).\n * Should resolve to `true` on success, `false` on failure.\n * Throwing also counts as failure — caught and logged.\n */\n onReset: () => Promise<boolean>;\n /**\n * Called after a successful reset (status === true). Use to clear the\n * local message list, navigate, or fire analytics.\n */\n onSuccess?: () => void;\n /**\n * Called when reset fails (returned `false` or threw). Defaults to no-op\n * — wire to a toast/banner if you want to surface it.\n */\n onError?: (error?: unknown) => void;\n}\n\nexport interface UseChatResetReturn {\n /** Trigger the reset. Safe to call multiple times — re-entrant guard. */\n reset: () => Promise<boolean>;\n /** True while the reset is in flight. */\n isResetting: boolean;\n}\n\n/**\n * Generic \"clear chat context\" hook.\n *\n * Stays generic — the backend call lives in the host (it knows the URL,\n * auth, project slug). Provides the in-flight state and success/error\n * callbacks every consumer wires up the same way.\n *\n * @example\n * ```tsx\n * const { reset, isResetting } = useChatReset({\n * onReset: async () => {\n * const res = await fetch('/api/chat/reset', { method: 'POST', credentials: 'include' });\n * return res.ok;\n * },\n * onSuccess: () => chat.clearMessages(),\n * });\n * ```\n */\nexport function useChatReset(opts: UseChatResetOptions): UseChatResetReturn {\n const { onReset, onSuccess, onError } = opts;\n const [isResetting, setIsResetting] = useState(false);\n\n const reset = useCallback(async (): Promise<boolean> => {\n if (isResetting) return false;\n setIsResetting(true);\n try {\n const ok = await onReset();\n if (ok) onSuccess?.();\n else onError?.();\n return ok;\n } catch (err) {\n onError?.(err);\n return false;\n } finally {\n setIsResetting(false);\n }\n }, [isResetting, onReset, onSuccess, onError]);\n\n return { reset, isResetting };\n}\n","'use client';\n\nimport { RotateCcw } from 'lucide-react';\n\nimport { useChatReset } from '../hooks/useChatReset';\nimport { ChatHeaderActionButton } from './ChatHeaderActionButton';\n\nexport interface ChatHeaderResetButtonProps {\n /**\n * Backend reset call. Should resolve to `true` on success.\n * Plugged into `useChatReset` for in-flight state.\n */\n onReset: () => Promise<boolean>;\n /** Called after a successful reset (e.g. clear local messages, refetch). */\n onSuccess?: () => void;\n /** Called when reset fails (returned `false` or threw). */\n onError?: (error?: unknown) => void;\n /**\n * Show a `window.dialog.confirm` before calling `onReset`. @default true\n *\n * Requires the host to mount `<DialogProvider>` from `@djangocfg/ui-core`\n * — it installs the `window.dialog` API used here.\n */\n confirm?: boolean;\n /** Confirm dialog title. */\n confirmTitle?: string;\n /** Confirm dialog message. */\n confirmMessage?: string;\n /** Override tooltip / aria label. */\n ariaLabel?: string;\n}\n\nconst DEFAULT_TITLE = 'Clear conversation?';\nconst DEFAULT_MESSAGE =\n 'The assistant will forget this session and start a new one. This cannot be undone.';\nconst DEFAULT_LABEL = 'Clear conversation';\n\n/**\n * Standard chat-reset action: prompts the user via `window.dialog.confirm`,\n * then runs the backend reset call through `useChatReset` so the button\n * spins while it's in flight.\n *\n * @example\n * ```tsx\n * <ChatHeaderResetButton\n * onReset={api.clearChat}\n * onSuccess={() => chat.clearMessages()}\n * />\n * ```\n */\nexport function ChatHeaderResetButton({\n onReset,\n onSuccess,\n onError,\n confirm = true,\n confirmTitle = DEFAULT_TITLE,\n confirmMessage = DEFAULT_MESSAGE,\n ariaLabel = DEFAULT_LABEL,\n}: ChatHeaderResetButtonProps) {\n const { reset, isResetting } = useChatReset({ onReset, onSuccess, onError });\n\n const handleClick = async () => {\n if (confirm) {\n const api = typeof window !== 'undefined' ? window.dialog : undefined;\n if (api?.confirm) {\n const ok = await api.confirm({\n title: confirmTitle,\n message: confirmMessage,\n variant: 'destructive',\n confirmText: 'Clear',\n cancelText: 'Cancel',\n });\n if (!ok) return;\n } else if (typeof window !== 'undefined' && typeof window.confirm === 'function') {\n // Fallback to the native browser confirm when the dialog service\n // isn't wired (e.g. host forgot to mount DialogProvider).\n const ok = window.confirm(`${confirmTitle}\\n\\n${confirmMessage}`);\n if (!ok) return;\n }\n }\n await reset();\n };\n\n return (\n <ChatHeaderActionButton\n icon={<RotateCcw className=\"h-3.5 w-3.5\" />}\n ariaLabel={ariaLabel}\n onClick={handleClick}\n loading={isResetting}\n destructive\n />\n );\n}\n","/**\n * Canonical list of BCP-47 language tags that the browser Web Speech\n * API is known to accept. Sourced from the official Google Chrome\n * Speech API demo (`google.com/intl/en/chrome/demos/speech.html`),\n * which is the de-facto reference — the spec itself doesn't expose a\n * way to enumerate supported languages, so this list is the\n * best-effort guarantee for what works in Chromium-based browsers.\n *\n * Each entry groups one human-readable language with its dialect\n * variants. The default tag (first in `dialects`) is what `lang` is\n * set to when the user picks the language without a regional dialect.\n *\n * For custom engines (cmdop wails-whisper, Deepgram, …) hosts can\n * pass their own subset via the `availableLanguages` prop on the\n * picker — backend may support more or fewer tags than the browser.\n */\n\nexport interface SpeechLanguageDialect {\n /** BCP-47 tag (e.g. `en-US`). */\n code: string;\n /** Region label in the language's native script (e.g. \"United States\"). */\n region: string;\n}\n\nexport interface SpeechLanguage {\n /** Native-script name (e.g. \"Русский\", \"中文\"). */\n name: string;\n /**\n * English name used as a secondary search key so users typing\n * \"russian\" / \"chinese\" / \"korean\" land on the right row regardless\n * of the native script. Always lowercase.\n */\n englishName: string;\n /**\n * Primary-subtag ISO-639 code (e.g. `en`, `ru`, `cmn`). Used as the\n * map key into the `LanguageSelect` ui-core component.\n */\n iso: string;\n /** One or more region dialects. Length >= 1. */\n dialects: SpeechLanguageDialect[];\n}\n\nexport const WEB_SPEECH_LANGUAGES: SpeechLanguage[] = [\n { name: 'Afrikaans', iso: 'af', englishName: 'afrikaans', dialects: [{ code: 'af-ZA', region: 'South Africa' }] },\n { name: 'አማርኛ', iso: 'am', englishName: 'amharic', dialects: [{ code: 'am-ET', region: 'Ethiopia' }] },\n { name: 'Azərbaycanca', iso: 'az', englishName: 'azerbaijani', dialects: [{ code: 'az-AZ', region: 'Azerbaijan' }] },\n {\n name: 'বাংলা', iso: 'bn', englishName: 'bengali',\n dialects: [\n { code: 'bn-BD', region: 'Bangladesh' },\n { code: 'bn-IN', region: 'India' },\n ],\n },\n { name: 'Bahasa Indonesia', iso: 'id', englishName: 'indonesian', dialects: [{ code: 'id-ID', region: 'Indonesia' }] },\n { name: 'Bahasa Melayu', iso: 'ms', englishName: 'malay', dialects: [{ code: 'ms-MY', region: 'Malaysia' }] },\n { name: 'Català', iso: 'ca', englishName: 'catalan', dialects: [{ code: 'ca-ES', region: 'Spain' }] },\n { name: 'Čeština', iso: 'cs', englishName: 'czech', dialects: [{ code: 'cs-CZ', region: 'Czechia' }] },\n { name: 'Dansk', iso: 'da', englishName: 'danish', dialects: [{ code: 'da-DK', region: 'Denmark' }] },\n { name: 'Deutsch', iso: 'de', englishName: 'german', dialects: [{ code: 'de-DE', region: 'Germany' }] },\n {\n name: 'English', iso: 'en', englishName: 'english',\n dialects: [\n { code: 'en-US', region: 'United States' },\n { code: 'en-GB', region: 'United Kingdom' },\n { code: 'en-AU', region: 'Australia' },\n { code: 'en-CA', region: 'Canada' },\n { code: 'en-IN', region: 'India' },\n { code: 'en-NZ', region: 'New Zealand' },\n { code: 'en-PH', region: 'Philippines' },\n { code: 'en-ZA', region: 'South Africa' },\n { code: 'en-NG', region: 'Nigeria' },\n { code: 'en-GH', region: 'Ghana' },\n { code: 'en-KE', region: 'Kenya' },\n { code: 'en-TZ', region: 'Tanzania' },\n ],\n },\n {\n name: 'Español', iso: 'es', englishName: 'spanish',\n dialects: [\n { code: 'es-ES', region: 'España' },\n { code: 'es-MX', region: 'México' },\n { code: 'es-US', region: 'Estados Unidos' },\n { code: 'es-AR', region: 'Argentina' },\n { code: 'es-CL', region: 'Chile' },\n { code: 'es-CO', region: 'Colombia' },\n { code: 'es-PE', region: 'Perú' },\n { code: 'es-VE', region: 'Venezuela' },\n { code: 'es-EC', region: 'Ecuador' },\n { code: 'es-GT', region: 'Guatemala' },\n { code: 'es-CR', region: 'Costa Rica' },\n { code: 'es-PA', region: 'Panamá' },\n { code: 'es-DO', region: 'Rep. Dominicana' },\n { code: 'es-UY', region: 'Uruguay' },\n { code: 'es-PY', region: 'Paraguay' },\n { code: 'es-BO', region: 'Bolivia' },\n { code: 'es-SV', region: 'El Salvador' },\n { code: 'es-HN', region: 'Honduras' },\n { code: 'es-NI', region: 'Nicaragua' },\n { code: 'es-PR', region: 'Puerto Rico' },\n ],\n },\n { name: 'Euskara', iso: 'eu', englishName: 'basque', dialects: [{ code: 'eu-ES', region: 'Spain' }] },\n { name: 'Filipino', iso: 'fil', englishName: 'filipino tagalog', dialects: [{ code: 'fil-PH', region: 'Philippines' }] },\n { name: 'Français', iso: 'fr', englishName: 'french', dialects: [{ code: 'fr-FR', region: 'France' }] },\n { name: 'Basa Jawa', iso: 'jv', englishName: 'javanese', dialects: [{ code: 'jv-ID', region: 'Indonesia' }] },\n { name: 'Galego', iso: 'gl', englishName: 'galician', dialects: [{ code: 'gl-ES', region: 'Spain' }] },\n { name: 'ગુજરાતી', iso: 'gu', englishName: 'gujarati', dialects: [{ code: 'gu-IN', region: 'India' }] },\n { name: 'Hrvatski', iso: 'hr', englishName: 'croatian', dialects: [{ code: 'hr-HR', region: 'Croatia' }] },\n { name: 'IsiZulu', iso: 'zu', englishName: 'zulu', dialects: [{ code: 'zu-ZA', region: 'South Africa' }] },\n { name: 'Íslenska', iso: 'is', englishName: 'icelandic', dialects: [{ code: 'is-IS', region: 'Iceland' }] },\n {\n name: 'Italiano', iso: 'it', englishName: 'italian',\n dialects: [\n { code: 'it-IT', region: 'Italia' },\n { code: 'it-CH', region: 'Svizzera' },\n ],\n },\n { name: 'ಕನ್ನಡ', iso: 'kn', englishName: 'kannada', dialects: [{ code: 'kn-IN', region: 'India' }] },\n { name: 'ភាសាខ្មែរ', iso: 'km', englishName: 'khmer cambodian', dialects: [{ code: 'km-KH', region: 'Cambodia' }] },\n { name: 'Latviešu', iso: 'lv', englishName: 'latvian', dialects: [{ code: 'lv-LV', region: 'Latvia' }] },\n { name: 'Lietuvių', iso: 'lt', englishName: 'lithuanian', dialects: [{ code: 'lt-LT', region: 'Lithuania' }] },\n { name: 'മലയാളം', iso: 'ml', englishName: 'malayalam', dialects: [{ code: 'ml-IN', region: 'India' }] },\n { name: 'मराठी', iso: 'mr', englishName: 'marathi', dialects: [{ code: 'mr-IN', region: 'India' }] },\n { name: 'Magyar', iso: 'hu', englishName: 'hungarian', dialects: [{ code: 'hu-HU', region: 'Hungary' }] },\n { name: 'ລາວ', iso: 'lo', englishName: 'lao laotian', dialects: [{ code: 'lo-LA', region: 'Laos' }] },\n { name: 'Nederlands', iso: 'nl', englishName: 'dutch', dialects: [{ code: 'nl-NL', region: 'Netherlands' }] },\n { name: 'नेपाली भाषा', iso: 'ne', englishName: 'nepali', dialects: [{ code: 'ne-NP', region: 'Nepal' }] },\n { name: 'Norsk bokmål', iso: 'nb', englishName: 'norwegian bokmal', dialects: [{ code: 'nb-NO', region: 'Norway' }] },\n { name: 'Polski', iso: 'pl', englishName: 'polish', dialects: [{ code: 'pl-PL', region: 'Poland' }] },\n {\n name: 'Português', iso: 'pt', englishName: 'portuguese',\n dialects: [\n { code: 'pt-BR', region: 'Brasil' },\n { code: 'pt-PT', region: 'Portugal' },\n ],\n },\n { name: 'Română', iso: 'ro', englishName: 'romanian', dialects: [{ code: 'ro-RO', region: 'Romania' }] },\n { name: 'සිංහල', iso: 'si', englishName: 'sinhala sinhalese', dialects: [{ code: 'si-LK', region: 'Sri Lanka' }] },\n { name: 'Slovenščina', iso: 'sl', englishName: 'slovenian', dialects: [{ code: 'sl-SI', region: 'Slovenia' }] },\n { name: 'Basa Sunda', iso: 'su', englishName: 'sundanese', dialects: [{ code: 'su-ID', region: 'Indonesia' }] },\n { name: 'Slovenčina', iso: 'sk', englishName: 'slovak', dialects: [{ code: 'sk-SK', region: 'Slovakia' }] },\n { name: 'Suomi', iso: 'fi', englishName: 'finnish', dialects: [{ code: 'fi-FI', region: 'Finland' }] },\n { name: 'Svenska', iso: 'sv', englishName: 'swedish', dialects: [{ code: 'sv-SE', region: 'Sweden' }] },\n {\n name: 'Kiswahili', iso: 'sw', englishName: 'swahili',\n dialects: [\n { code: 'sw-TZ', region: 'Tanzania' },\n { code: 'sw-KE', region: 'Kenya' },\n ],\n },\n { name: 'ქართული', iso: 'ka', englishName: 'georgian', dialects: [{ code: 'ka-GE', region: 'Georgia' }] },\n { name: 'Հայերեն', iso: 'hy', englishName: 'armenian', dialects: [{ code: 'hy-AM', region: 'Armenia' }] },\n {\n name: 'தமிழ்', iso: 'ta', englishName: 'tamil',\n dialects: [\n { code: 'ta-IN', region: 'இந்தியா' },\n { code: 'ta-SG', region: 'சிங்கப்பூர்' },\n { code: 'ta-LK', region: 'இலங்கை' },\n { code: 'ta-MY', region: 'மலேசியா' },\n ],\n },\n { name: 'తెలుగు', iso: 'te', englishName: 'telugu', dialects: [{ code: 'te-IN', region: 'India' }] },\n { name: 'Tiếng Việt', iso: 'vi', englishName: 'vietnamese', dialects: [{ code: 'vi-VN', region: 'Vietnam' }] },\n { name: 'Türkçe', iso: 'tr', englishName: 'turkish', dialects: [{ code: 'tr-TR', region: 'Türkiye' }] },\n {\n name: 'اُردُو', iso: 'ur', englishName: 'urdu',\n dialects: [\n { code: 'ur-PK', region: 'پاکستان' },\n { code: 'ur-IN', region: 'بھارت' },\n ],\n },\n { name: 'Ελληνικά', iso: 'el', englishName: 'greek', dialects: [{ code: 'el-GR', region: 'Greece' }] },\n { name: 'български', iso: 'bg', englishName: 'bulgarian', dialects: [{ code: 'bg-BG', region: 'Bulgaria' }] },\n { name: 'Русский', iso: 'ru', englishName: 'russian', dialects: [{ code: 'ru-RU', region: 'Russia' }] },\n { name: 'Српски', iso: 'sr', englishName: 'serbian', dialects: [{ code: 'sr-RS', region: 'Serbia' }] },\n { name: 'Українська', iso: 'uk', englishName: 'ukrainian', dialects: [{ code: 'uk-UA', region: 'Ukraine' }] },\n { name: '한국어', iso: 'ko', englishName: 'korean', dialects: [{ code: 'ko-KR', region: 'Korea' }] },\n {\n name: '中文', iso: 'cmn', englishName: 'chinese mandarin cantonese',\n dialects: [\n { code: 'cmn-Hans-CN', region: '普通话 (中国大陆)' },\n { code: 'cmn-Hans-HK', region: '普通话 (香港)' },\n { code: 'cmn-Hant-TW', region: '中文 (台灣)' },\n { code: 'yue-Hant-HK', region: '粵語 (香港)' },\n ],\n },\n { name: '日本語', iso: 'ja', englishName: 'japanese', dialects: [{ code: 'ja-JP', region: 'Japan' }] },\n { name: 'हिन्दी', iso: 'hi', englishName: 'hindi', dialects: [{ code: 'hi-IN', region: 'India' }] },\n { name: 'ภาษาไทย', iso: 'th', englishName: 'thai', dialects: [{ code: 'th-TH', region: 'Thailand' }] },\n];\n\n/** Flat list of every supported BCP-47 tag, useful for validation. */\nexport const WEB_SPEECH_TAGS: string[] = WEB_SPEECH_LANGUAGES.flatMap((l) =>\n l.dialects.map((d) => d.code),\n);\n\n/**\n * Find the human-readable language entry that owns a given BCP-47 tag.\n * Returns `null` for unknown / custom tags (custom engines may use\n * codes outside this catalogue).\n */\nexport function findSpeechLanguage(tag: string | null | undefined): {\n language: SpeechLanguage;\n dialect: SpeechLanguageDialect;\n} | null {\n if (!tag) return null;\n const lower = tag.toLowerCase();\n for (const language of WEB_SPEECH_LANGUAGES) {\n for (const dialect of language.dialects) {\n if (dialect.code.toLowerCase() === lower) return { language, dialect };\n }\n }\n return null;\n}\n\n/**\n * Extract the ISO-3166 country code (2 uppercase letters) from a\n * BCP-47 tag, or `null` if the tag has no region subtag. Used by the\n * language flag button to find the right country flag asset.\n */\nexport function countryFromTag(tag: string | null | undefined): string | null {\n if (!tag) return null;\n const parts = tag.split('-');\n for (let i = parts.length - 1; i >= 0; i -= 1) {\n const p = parts[i];\n if (p.length === 2 && /^[A-Za-z]{2}$/.test(p)) return p.toUpperCase();\n }\n return null;\n}\n","'use client';\n\nimport type * as React from 'react';\nimport { useCallback, useEffect, useRef } from 'react';\nimport { Loader2, Mic } from 'lucide-react';\n\nimport { useCountdownFromSeconds, useNotificationSounds } from '@djangocfg/ui-core/hooks';\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport { useChatContextOptional } from '../../Chat/context';\nimport { useSpeechRecognition } from '../hooks/useSpeechRecognition';\nimport { useVoiceSupport } from '../hooks/useVoiceSupport';\nimport { getSpeechLogger } from '../core/logger';\nimport { normaliseFinal } from '../core/transcript';\nimport { DEFAULT_VOICE_SOUNDS, type VoiceSoundEvent } from '../core/audio/defaults';\nimport type { RecognitionEngine } from '../types';\n\nconst log = getSpeechLogger();\n\nexport interface VoiceComposerSlotProps {\n /**\n * Controlled composer value. Optional — when omitted, the slot\n * reads/writes through the Chat context's registered `ComposerHandle`\n * (built-in `<Composer>` and any host that calls\n * `useRegisterComposer({ getValue, setValue })`). Pass explicitly\n * only for standalone usage outside a `<ChatProvider>`.\n */\n value?: string;\n /** Composer setter — see `value`. Optional when used inside a chat. */\n onChange?: (next: string) => void;\n /** Optional custom engine (Deepgram / HTTP / WS). Defaults to Web Speech. */\n engine?: RecognitionEngine;\n /** BCP-47 language override. Otherwise `useSpeechPrefs` decides. */\n language?: string;\n /** Hide the button if the host wants to disable voice on phones. */\n hideOnMobile?: boolean;\n /** Max session length in seconds before we auto-stop. Default 90. */\n maxSeconds?: number;\n /** Auto-stop after this many ms of silence. Default 2500. */\n silenceMs?: number;\n /** Button size. @default 'md' */\n size?: 'sm' | 'md' | 'lg';\n /** Override classes on the button. */\n className?: string;\n /** Fires when dictation finishes — useful for parent-side analytics. */\n onFinish?: (transcript: string) => void;\n /**\n * Start/stop earcons. `true` (default) plays the bundled sounds,\n * `false` disables them, or pass `{ start, stop }` data-URLs to\n * override. Master mute lives in `useNotificationSounds` localStorage\n * — users can silence everything via their own UI.\n */\n sounds?: boolean | { start?: string; stop?: string };\n}\n\nconst SIZE_CLS: Record<NonNullable<VoiceComposerSlotProps['size']>, string> = {\n sm: 'h-8 w-8 [&_svg]:h-4 [&_svg]:w-4',\n md: 'h-9 w-9 [&_svg]:h-4 [&_svg]:w-4',\n lg: 'h-12 w-12 [&_svg]:h-5 [&_svg]:w-5',\n};\n\nconst STORAGE_KEY = 'djangocfg-stt:voice-sounds';\n\n/**\n * Drop-in slot for the `<Composer toolbarEnd>` (or `toolbarStart`) prop.\n *\n * Renders a microphone button — but only when the browser + device\n * combination can actually do speech recognition. Firefox, in-app\n * WebViews, and missing `getUserMedia` all collapse the component to\n * `null`, so the chat composer never shows a broken affordance.\n *\n * While listening, interim+final transcript is pushed live into the\n * composer's `value` — like dictation on iOS / Android keyboards. The\n * user's already-typed prefix is preserved (anchored on press). Cancel\n * with the same button or by pressing Escape (handled upstream).\n *\n * Soft start/stop earcons play by default. Pass `sounds={false}` to\n * mute, or `sounds={{ start, stop }}` to override the audio URLs.\n */\nexport function VoiceComposerSlot({\n value,\n onChange,\n engine,\n language,\n hideOnMobile = false,\n maxSeconds = 90,\n silenceMs = 2500,\n size = 'md',\n className,\n onFinish,\n sounds = true,\n}: VoiceComposerSlotProps): React.ReactElement | null {\n const support = useVoiceSupport(engine);\n\n // Read the composer handle from chat context — works transparently\n // for the built-in `<Composer>` (registers itself) and for TipTap\n // hosts that call `useRegisterComposer({ getValue, setValue, focus,\n // moveCursorToEnd })`. Falls back to a no-op when mounted outside of\n // a chat.\n const chatCtx = useChatContextOptional();\n const composerHandleRef = useRef(chatCtx?.composer ?? null);\n composerHandleRef.current = chatCtx?.composer ?? null;\n\n useEffect(() => {\n log.slot.debug('mount', {\n supported: support.supported,\n reason: support.reason,\n hasComposerHandle: !!chatCtx?.composer,\n hasExplicitValue: value !== undefined,\n hasOnChange: !!onChange,\n });\n }, [support.supported, support.reason, chatCtx?.composer, value, onChange]);\n\n // Resolve value/onChange: prop wins; otherwise pull from the\n // registered composer handle. The slot can therefore be dropped into\n // `composerToolbarEnd` of `ChatRoot` with zero props.\n const resolvedGetValue = useCallback((): string => {\n if (value !== undefined) return value;\n return composerHandleRef.current?.getValue?.() ?? '';\n }, [value]);\n const resolvedSetValue = useCallback(\n (next: string): void => {\n if (onChange) {\n log.composer.debug('setValue → onChange prop', { len: next.length });\n onChange(next);\n return;\n }\n const handle = composerHandleRef.current;\n if (!handle?.setValue) {\n log.composer.warn(\n 'setValue called but no composer handle is registered — text will be lost. ' +\n 'Make sure <VoiceComposerSlot> lives inside a <ChatProvider> (or pass `value`/`onChange` props for standalone use).',\n { len: next.length },\n );\n return;\n }\n log.composer.debug('setValue → composer handle', { len: next.length });\n handle.setValue(next);\n },\n [onChange],\n );\n\n // Anchor: what was already in the textarea when the user pressed the\n // mic. Live transcript is appended to this baseline so manual typing\n // before pressing the button is never overwritten.\n const anchorRef = useRef<string>('');\n const onFinishRef = useRef(onFinish);\n onFinishRef.current = onFinish;\n\n // Push caret to the end on the next frame — after React commits the\n // new `value` into the DOM. Without the rAF the selection lands on\n // the old text length, leaving the cursor mid-string while the live\n // transcript visually keeps growing.\n const pinCaretToEnd = useCallback(() => {\n const handle = composerHandleRef.current;\n if (!handle?.moveCursorToEnd) return;\n requestAnimationFrame(() => {\n composerHandleRef.current?.moveCursorToEnd?.();\n });\n }, []);\n\n const [countdown, startCountdown] = useCountdownFromSeconds();\n\n // Earcon bus. `sounds === false` → pass empty map so the bus stays\n // silent. Object overrides merge with bundled defaults.\n const soundMap =\n sounds === false\n ? undefined\n : sounds === true\n ? DEFAULT_VOICE_SOUNDS\n : { ...DEFAULT_VOICE_SOUNDS, ...sounds };\n const audio = useNotificationSounds<VoiceSoundEvent>({\n storageKey: STORAGE_KEY,\n sounds: soundMap,\n muted: sounds === false,\n // Both earcons stay deliberately quiet — they're self-initiated\n // micro-confirmations, not notifications. Anything louder feels\n // attention-grabbing when the user pressed the button themselves.\n eventVolumes: { start: 0.35, stop: 0.5 },\n });\n\n const handlePartial = useCallback(\n (text: string) => {\n log.dictation.debug('partial', {\n len: text.length,\n anchorLen: anchorRef.current.length,\n });\n const next = anchorRef.current\n ? `${anchorRef.current} ${text}`\n : text;\n resolvedSetValue(next);\n pinCaretToEnd();\n },\n [pinCaretToEnd, resolvedSetValue],\n );\n\n const handleFinal = useCallback(\n (text: string) => {\n const clean = normaliseFinal(text);\n if (!clean) {\n log.dictation.debug('final dropped — empty after normalise', { raw: text });\n return;\n }\n const merged = anchorRef.current ? `${anchorRef.current} ${clean}` : clean;\n log.dictation.info('final merged', {\n len: clean.length,\n totalLen: merged.length,\n });\n anchorRef.current = merged;\n resolvedSetValue(merged);\n pinCaretToEnd();\n },\n [pinCaretToEnd, resolvedSetValue],\n );\n\n const rec = useSpeechRecognition({\n engine,\n language,\n interim: true,\n autoStop: { silenceMs, maxMs: maxSeconds * 1000 },\n onPartial: (text) => handlePartial(text),\n onFinal: (text) => handleFinal(text),\n onStart: () => {\n void audio.play('start');\n // Focus the composer + park caret at the end so the live\n // transcript visibly grows where the user expects it to.\n composerHandleRef.current?.focus();\n pinCaretToEnd();\n },\n onStop: () => {\n void audio.play('stop');\n // Re-focus on stop too — auto-stop on silence happens without\n // a user gesture, and we want the user to keep typing seamlessly.\n composerHandleRef.current?.focus();\n pinCaretToEnd();\n onFinishRef.current?.(resolvedGetValue());\n },\n });\n\n // Drive the countdown alongside the listening state.\n useEffect(() => {\n if (rec.status === 'listening') {\n startCountdown(maxSeconds);\n }\n }, [rec.status, maxSeconds, startCountdown]);\n\n // Hotkeys while listening:\n // Esc — cancel dictation (and stop event propagation so the\n // outer chat doesn't *also* close — same convention as\n // ChatGPT / Slack voice mode).\n // Enter — finish dictation, KEEP what we already pushed into the\n // composer, do NOT submit the chat (avoids accidental\n // sends while the user is still talking). We block the\n // Enter that would otherwise reach the composer textarea\n // by listening in the capture phase.\n const listening = rec.status === 'listening' || rec.status === 'starting';\n useEffect(() => {\n if (!listening) return undefined;\n const onKey = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n rec.abort();\n return;\n }\n if (e.key === 'Enter' && !e.shiftKey) {\n // Block the chat composer's \"Enter to send\" while we're\n // dictating — finish recording instead.\n e.preventDefault();\n e.stopPropagation();\n void rec.stop();\n }\n };\n // `capture: true` so we run before the composer textarea's\n // keydown handler (which would otherwise close the chat on Esc\n // or submit the form on Enter).\n window.addEventListener('keydown', onKey, true);\n return () => {\n window.removeEventListener('keydown', onKey, true);\n };\n }, [listening, rec]);\n\n const toggle = useCallback(() => {\n if (rec.status === 'listening' || rec.status === 'starting') {\n void rec.stop();\n return;\n }\n anchorRef.current = resolvedGetValue().trim();\n void rec.start();\n }, [rec, resolvedGetValue]);\n\n if (!support.supported) return null;\n if (hideOnMobile && support.isMobile) return null;\n\n const stopping = rec.status === 'stopping';\n\n // Tooltip: countdown + hotkey hint while listening, plain copy\n // otherwise. Avoids the absolutely-positioned label that clipped\n // against the composer bottom edge in the previous design.\n const tooltip = listening\n ? `Listening — ${countdown.label || `${maxSeconds}s left`} · Enter to finish · Esc to cancel`\n : 'Dictate message';\n\n return (\n <span className=\"inline-flex items-center gap-1.5 !h-auto\">\n {listening && countdown.label ? (\n <span\n aria-hidden\n className=\"rounded-full bg-destructive/10 px-1.5 py-0.5 font-mono text-[10px] leading-none text-destructive tabular-nums\"\n >\n {countdown.label}\n </span>\n ) : null}\n <button\n type=\"button\"\n onClick={toggle}\n aria-pressed={listening}\n aria-label={listening ? 'Stop dictation' : 'Dictate message'}\n title={tooltip}\n className={cn(\n 'relative inline-flex items-center justify-center rounded-full transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n SIZE_CLS[size],\n listening\n ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground',\n className,\n )}\n >\n {listening && (\n <span\n aria-hidden\n className=\"absolute inset-0 rounded-full bg-destructive/30 animate-ping\"\n />\n )}\n {stopping ? <Loader2 className=\"animate-spin\" /> : <Mic />}\n </button>\n </span>\n );\n}\n","'use client';\n\nimport type * as React from 'react';\nimport { createContext, useContext, useMemo } from 'react';\n\nimport type { UseSpeechRecognitionConfig, UseSpeechRecognitionReturn } from '../types';\nimport { useSpeechRecognition } from '../hooks/useSpeechRecognition';\n\nconst Ctx = createContext<UseSpeechRecognitionReturn | null>(null);\n\nexport interface SpeechRecognitionProviderProps extends UseSpeechRecognitionConfig {\n children?: React.ReactNode;\n}\n\n/**\n * Lifts a single `useSpeechRecognition` instance into a context so any\n * descendant — composer slot, header badge, transcript overlay — can\n * read the same status / transcript / level without each component\n * spinning up its own engine. Mount it once per Chat (or per\n * dictation-aware screen).\n */\nexport function SpeechRecognitionProvider({\n children,\n ...config\n}: SpeechRecognitionProviderProps): React.ReactElement {\n const rec = useSpeechRecognition(config);\n // Memo prevents context-value churn from leaking through to every\n // useContext consumer when only one of {status, level, transcript}\n // changes — React already triggers them via the inner state, the\n // outer object identity should stay stable across renders.\n const value = useMemo(() => rec, [rec]);\n return <Ctx.Provider value={value}>{children}</Ctx.Provider>;\n}\n\nexport function useSpeechRecognitionContext(): UseSpeechRecognitionReturn {\n const ctx = useContext(Ctx);\n if (!ctx) {\n throw new Error(\n 'useSpeechRecognitionContext must be used inside a <SpeechRecognitionProvider>.',\n );\n }\n return ctx;\n}\n\nexport function useSpeechRecognitionContextOptional(): UseSpeechRecognitionReturn | null {\n return useContext(Ctx);\n}\n","'use client';\n\nimport * as React from 'react';\nimport { Suspense, type ReactNode, type ComponentType } from 'react';\nimport { cn } from '@djangocfg/ui-core/lib';\nimport { useAppT } from '@djangocfg/i18n';\n\n// ============================================================================\n// Loading Fallback Components\n// ============================================================================\n\nexport interface LoadingFallbackProps {\n /** Minimum height of the loading container */\n minHeight?: string | number;\n /** Show loading text */\n showText?: boolean;\n /** Custom loading text */\n text?: string;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Spinner - Simple spinning loader\n */\nexport function Spinner({ className }: { className?: string }) {\n const t = useAppT();\n const loadingLabel = t('ui.states.loading');\n\n return (\n <div\n className={cn(\n 'inline-block h-8 w-8 animate-spin rounded-full',\n 'border-4 border-solid border-current border-r-transparent',\n 'align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]',\n className\n )}\n role=\"status\"\n aria-label={loadingLabel}\n />\n );\n}\n\n/**\n * LoadingFallback - Default loading state for lazy components\n */\nexport function LoadingFallback({\n minHeight = 200,\n showText = true,\n text,\n className,\n}: LoadingFallbackProps) {\n const t = useAppT();\n const loadingText = text ?? t('ui.form.loading');\n const height = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n\n return (\n <div\n className={cn(\n 'flex items-center justify-center bg-muted/30 rounded-lg',\n className\n )}\n style={{ minHeight: height }}\n >\n <div className=\"text-center\">\n <Spinner className=\"text-primary\" />\n {showText && (\n <p className=\"mt-3 text-sm text-muted-foreground\">{loadingText}</p>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * CardLoadingFallback - Loading state styled as a card\n */\nexport function CardLoadingFallback({\n title,\n description,\n minHeight = 200,\n className,\n}: {\n title?: string;\n description?: string;\n minHeight?: string | number;\n className?: string;\n}) {\n const t = useAppT();\n const cardTitle = title ?? t('ui.states.loading');\n const height = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n\n return (\n <div\n className={cn(\n 'relative bg-card rounded-lg border border-border overflow-hidden',\n className\n )}\n >\n <div className=\"p-4 border-b border-border bg-muted/50\">\n <h6 className=\"text-sm font-semibold text-foreground\">{cardTitle}</h6>\n {description && (\n <p className=\"text-xs text-muted-foreground mt-1\">{description}</p>\n )}\n </div>\n <div className=\"p-4\">\n <div\n className=\"flex justify-center items-center\"\n style={{ minHeight: height }}\n >\n <Spinner className=\"text-primary\" />\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * MapLoadingFallback - Loading state for map components\n */\nexport function MapLoadingFallback({\n minHeight = 400,\n className,\n}: {\n minHeight?: string | number;\n className?: string;\n}) {\n const t = useAppT();\n const loadingText = t('ui.form.loading');\n const height = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n\n return (\n <div\n className={cn(\n 'relative bg-muted/50 rounded-lg overflow-hidden',\n 'flex items-center justify-center',\n className\n )}\n style={{ minHeight: height }}\n >\n <div className=\"text-center\">\n <div className=\"relative\">\n <Spinner className=\"text-primary h-10 w-10\" />\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <svg\n className=\"h-5 w-5 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"\n />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"\n />\n </svg>\n </div>\n </div>\n <p className=\"mt-3 text-sm text-muted-foreground\">{loadingText}</p>\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// Lazy Wrapper\n// ============================================================================\n\nexport interface LazyWrapperProps {\n children: ReactNode;\n /** Custom fallback component */\n fallback?: ReactNode;\n /** Use card-style fallback */\n card?: boolean;\n /** Card title (when card=true) */\n cardTitle?: string;\n /** Card description (when card=true) */\n cardDescription?: string;\n /** Minimum height for fallback */\n minHeight?: string | number;\n /** Additional CSS classes for fallback */\n className?: string;\n}\n\n/**\n * LazyWrapper - Suspense wrapper with configurable fallbacks\n *\n * @example\n * // Basic usage\n * <LazyWrapper>\n * <LazyComponent />\n * </LazyWrapper>\n *\n * @example\n * // Card style fallback\n * <LazyWrapper card cardTitle=\"Diagram\" minHeight={300}>\n * <Mermaid chart={...} />\n * </LazyWrapper>\n *\n * @example\n * // Custom fallback\n * <LazyWrapper fallback={<MyCustomLoader />}>\n * <HeavyComponent />\n * </LazyWrapper>\n */\nexport function LazyWrapper({\n children,\n fallback,\n card = false,\n cardTitle,\n cardDescription,\n minHeight = 200,\n className,\n}: LazyWrapperProps) {\n const defaultFallback = card ? (\n <CardLoadingFallback\n title={cardTitle}\n description={cardDescription}\n minHeight={minHeight}\n className={className}\n />\n ) : (\n <LoadingFallback minHeight={minHeight} className={className} />\n );\n\n return <Suspense fallback={fallback ?? defaultFallback}>{children}</Suspense>;\n}\n\n// ============================================================================\n// Lazy Component Factory\n// ============================================================================\n\nexport interface CreateLazyComponentOptions<P> {\n /** Loading fallback component */\n fallback?: ReactNode | ((props: P) => ReactNode);\n /** Display name for the component */\n displayName?: string;\n}\n\n/**\n * createLazyComponent - Factory for creating lazy-loaded components\n *\n * @example\n * const LazyMermaid = createLazyComponent(\n * () => import('./Mermaid.client'),\n * {\n * displayName: 'Mermaid',\n * fallback: <CardLoadingFallback title=\"Diagram\" />,\n * }\n * );\n */\nexport function createLazyComponent<P extends object>(\n loader: () => Promise<{ default: ComponentType<P> }>,\n options: CreateLazyComponentOptions<P> = {}\n): ComponentType<P> {\n const LazyComponent = React.lazy(loader);\n\n const WrappedComponent = (props: P) => {\n const fallback =\n typeof options.fallback === 'function'\n ? options.fallback(props)\n : options.fallback ?? <LoadingFallback />;\n\n return (\n <Suspense fallback={fallback}>\n <LazyComponent {...props} />\n </Suspense>\n );\n };\n\n WrappedComponent.displayName = options.displayName ?? 'LazyComponent';\n\n return WrappedComponent;\n}\n","'use client';\n\nimport { createLazyComponent } from '../../components';\nimport type { DictationFieldProps } from './widgets/DictationField';\n\nexport const LazyDictationField = createLazyComponent<DictationFieldProps>(\n () =>\n import('./widgets/DictationField').then((mod) => ({\n default: mod.DictationField,\n })),\n {\n displayName: 'LazyDictationField',\n fallback: (\n <div className=\"rounded-lg border border-border/60 bg-card px-3 py-2 text-xs text-muted-foreground\">\n Loading dictation…\n </div>\n ),\n },\n);\n","'use client';\n\nimport type * as React from 'react';\nimport { useMemo } from 'react';\nimport { Globe } from 'lucide-react';\n\nimport {\n Combobox,\n type ComboboxOption,\n Flag,\n} from '@djangocfg/ui-core/components';\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport { findSpeechLanguage } from '../../SpeechRecognition';\n\nimport {\n WEB_SPEECH_LANGUAGES,\n countryFromTag,\n useResolvedLanguage,\n useSpeechPrefs,\n} from '../../SpeechRecognition';\n\nexport interface ChatHeaderLanguageButtonProps {\n /** Override aria-label. Default \"Speech language\". */\n ariaLabel?: string;\n /**\n * Subset of BCP-47 tags to offer. Default: every entry from the\n * Web Speech catalogue (~66 tags incl. regional variants). Pass a\n * tighter list when your backend STT only supports a subset.\n */\n allowedTags?: string[];\n /** Hide the globe-fallback icon when no flag resolves. */\n hideFallbackIcon?: boolean;\n className?: string;\n}\n\n/**\n * Compact flag-button language picker for the chat header. Built on\n * top of the ui-core `<Combobox>` — searchable autocomplete with\n * flags, ~66 BCP-47 tags from the official Chrome Web Speech demo, and\n * a custom 28×28 trigger via `renderTrigger`.\n *\n * The selection persists into `useSpeechPrefs` (zustand+localStorage)\n * so `useSpeechRecognition` picks it up as the second-priority resolver\n * value (above i18n locale, below an explicit `language` prop).\n */\nexport function ChatHeaderLanguageButton({\n ariaLabel = 'Speech language',\n allowedTags,\n hideFallbackIcon,\n className,\n}: ChatHeaderLanguageButtonProps): React.ReactElement {\n const prefs = useSpeechPrefs();\n const active = useResolvedLanguage();\n\n // Flatten every dialect into one Combobox option. Display name keeps\n // the native language label; we stash the English aliases + BCP-47\n // tag + region in `description` purely as a hidden search index.\n // (We intentionally hide the description from the rendered row in\n // `renderOption` — it would just clutter the dropdown.)\n const options = useMemo<ComboboxOption[]>(() => {\n const allow = allowedTags ? new Set(allowedTags) : null;\n const out: ComboboxOption[] = [];\n for (const lang of WEB_SPEECH_LANGUAGES) {\n for (const d of lang.dialects) {\n if (allow && !allow.has(d.code)) continue;\n out.push({\n value: d.code,\n // \"Русский\" / \"Español — Argentina\" / \"English — United States\"\n label:\n lang.dialects.length === 1\n ? lang.name\n : `${lang.name} — ${d.region}`,\n // Search-only index: English name, BCP-47 tag, ISO, region.\n // Lets users type \"russian\" / \"ru-RU\" / \"ru\" / \"argentina\"\n // and still find the row regardless of native script.\n description: `${lang.englishName} ${d.code} ${lang.iso} ${d.region}`.toLowerCase(),\n });\n }\n }\n return out;\n }, [allowedTags]);\n\n return (\n <Combobox\n options={options}\n value={prefs.language ?? active}\n onValueChange={(v) => prefs.setLanguage(v || null)}\n placeholder={ariaLabel}\n searchPlaceholder=\"Search language…\"\n filterFunction={(opt, search) => {\n const s = search.toLowerCase();\n // Match label (native script), value (BCP-47), and our packed\n // search index in description (English name + tag + region).\n return (\n opt.label.toLowerCase().includes(s) ||\n opt.value.toLowerCase().includes(s) ||\n (opt.description?.includes(s) ?? false)\n );\n }}\n // Popover width follows the trigger by default (28px → narrow).\n // Force a usable width and bump z-index above ChatDock (z-10000).\n contentClassName=\"w-[280px]\"\n contentStyle={{ zIndex: 10001 }}\n // Custom row: country flag + native language label + BCP-47 tag.\n // ui-core Combobox default rendering only shows label/description\n // — feeding the flag here keeps every list row instantly\n // identifiable. Fallback to a neutral globe glyph when the tag\n // has no resolvable country (rare: bn-BD etc. all have flags).\n renderOption={(option) => {\n const country = countryFromTag(option.value);\n return (\n <div className=\"flex min-w-0 flex-1 items-center gap-2\">\n {country ? (\n <Flag\n countryCode={country}\n className=\"h-4 w-5 shrink-0 overflow-hidden rounded-[2px] border border-border/60 ring-1 ring-black/5\"\n />\n ) : (\n <Globe className=\"h-4 w-4 shrink-0 text-muted-foreground\" aria-hidden />\n )}\n {/* Native-script label only — BCP-47 subtitle removed to\n keep the dropdown visually quiet. Tag + region still\n live in `description` for search. */}\n <span className=\"truncate text-sm\">{option.label}</span>\n </div>\n );\n }}\n // Compact icon-only trigger — replaces the default wide outline\n // button. Stays the same 28×28 footprint as ChatHeaderActionButton\n // siblings (audio toggle, reset).\n renderTrigger={(selected, open) => {\n const tag = selected?.value ?? active;\n const country = countryFromTag(tag);\n const found = findSpeechLanguage(tag);\n // Tooltip copy: native language name + dialect region + tag.\n // Falls back to just the tag for unknown / custom-engine codes.\n const tooltipLabel = found\n ? `${found.language.name}${\n found.language.dialects.length > 1 ? ` — ${found.dialect.region}` : ''\n } · ${tag}`\n : tag;\n // Note on tooltip: we intentionally use the native `title`\n // attribute instead of `<Tooltip>` here. The button is already\n // wrapped by Combobox's `PopoverTrigger asChild`, and stacking\n // a second `TooltipTrigger asChild` on the same node makes\n // Radix Slot merge two competing refs/handlers — popover click\n // stops working. Native `title` is \"good enough\" for a single\n // status label and ships zero extra DOM.\n return (\n <button\n type=\"button\"\n aria-label={`${ariaLabel}: ${tooltipLabel}`}\n aria-expanded={open}\n title={tooltipLabel}\n className={cn(\n 'inline-flex h-7 w-7 items-center justify-center rounded-md',\n 'text-muted-foreground transition-colors',\n 'hover:bg-accent hover:text-foreground',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n className,\n )}\n >\n {country ? (\n <Flag\n countryCode={country}\n // Subtle hairline so flags with light edges (Japan,\n // Switzerland, …) don't blend into the header bg.\n className=\"h-4 w-5 overflow-hidden rounded-[2px] border border-border/60 ring-1 ring-black/5\"\n />\n ) : hideFallbackIcon ? null : (\n <Globe className=\"h-3.5 w-3.5\" aria-hidden />\n )}\n </button>\n );\n }}\n />\n );\n}\n","'use client';\n\nimport { X } from 'lucide-react';\nimport { useEffect, useState } from 'react';\nimport type { CSSProperties, ReactNode } from 'react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport { useChatPresence } from './useChatPresence';\nimport type { ChatFABPosition } from './ChatFAB';\n\nexport interface ChatGreetingProps {\n /** Controlled visibility — usually `!chatOpen && !userDismissed`. */\n open: boolean;\n /** Greeting text. Pass a string for the default bubble, or any ReactNode. */\n children: ReactNode;\n /** Click handler — typically opens the chat. Bubble is clickable when set. */\n onClick?: () => void;\n /** Close (×) button handler — typically marks the greeting as dismissed. */\n onDismiss?: () => void;\n /** Anchor relative to a FAB on the same side. @default 'bottom-right' */\n position?: ChatFABPosition;\n /**\n * Horizontal pixel offset matching the FAB's `offset` prop, so the greeting\n * lines up under the FAB. @default 24\n */\n fabOffset?: number;\n /**\n * Vertical pixel offset above/below the FAB centerline. @default 96\n * (room for an `md` FAB plus a small gap).\n */\n fabClearance?: number;\n /** Delay before the greeting appears, in ms. @default 1500 */\n delayMs?: number;\n /** z-index. @default 9998 (just below the default FAB at 9999). */\n zIndex?: number;\n /** Override classes on the bubble. */\n className?: string;\n /** Override styles on the bubble. */\n style?: CSSProperties;\n /** Optional sender avatar / icon shown on the left. */\n avatar?: ReactNode;\n /** Optional sender label rendered above the text. */\n senderName?: string;\n /** ARIA label for the dismiss button. @default 'Dismiss' */\n dismissLabel?: string;\n /**\n * Render in-place (no fixed positioning). Useful for stories and inline previews.\n * @default false\n */\n inline?: boolean;\n}\n\nfunction anchorStyle(\n position: ChatFABPosition,\n fabOffset: number,\n fabClearance: number,\n): CSSProperties {\n const [vert, horiz] = position.split('-') as ['bottom' | 'top', 'right' | 'left'];\n return { [vert]: fabClearance, [horiz]: fabOffset } as CSSProperties;\n}\n\nfunction originClass(position: ChatFABPosition): string {\n // Scale-in origin matches the corner the bubble attaches to.\n if (position === 'bottom-right') return 'origin-bottom-right';\n if (position === 'bottom-left') return 'origin-bottom-left';\n if (position === 'top-right') return 'origin-top-right';\n return 'origin-top-left';\n}\n\n/**\n * Greeting bubble shown next to a `ChatFAB` to invite the user to start a\n * conversation (LiveChat / Intercom-style proactive prompt).\n *\n * Renders fixed-position, anchored to the same corner as the FAB. Owns its\n * own delayed-mount + presence animation. Hide on chat open and/or after\n * user dismissal.\n *\n * @example\n * ```tsx\n * const [open, setOpen] = useState(false);\n * const [dismissed, setDismissed] = useState(false);\n *\n * <ChatLauncher\n * open={open}\n * onOpenChange={setOpen}\n * fab={{ variant: 'animated' }}\n * dock={{ title: 'Support' }}\n * >\n * <SupportChat />\n * </ChatLauncher>\n *\n * <ChatGreeting\n * open={!open && !dismissed}\n * onClick={() => setOpen(true)}\n * onDismiss={() => setDismissed(true)}\n * senderName=\"Anna from Support\"\n * delayMs={2000}\n * >\n * Hi! 👋 Got a question? I'm here to help.\n * </ChatGreeting>\n * ```\n */\nexport function ChatGreeting({\n open,\n children,\n onClick,\n onDismiss,\n position = 'bottom-right',\n fabOffset = 24,\n fabClearance = 96,\n delayMs = 1500,\n zIndex = 9998,\n className,\n style,\n avatar,\n senderName,\n dismissLabel = 'Dismiss',\n inline = false,\n}: ChatGreetingProps) {\n const [delayed, setDelayed] = useState(delayMs <= 0);\n\n useEffect(() => {\n if (!open || delayMs <= 0) return;\n const t = setTimeout(() => setDelayed(true), delayMs);\n return () => clearTimeout(t);\n }, [open, delayMs]);\n\n const shouldShow = open && delayed;\n const phase = useChatPresence(shouldShow, 220);\n\n if (phase === 'hidden') return null;\n\n const animating = phase === 'entering' || phase === 'leaving';\n const clickable = !!onClick;\n\n return (\n <div\n role={clickable ? 'button' : 'status'}\n aria-live=\"polite\"\n tabIndex={clickable ? 0 : -1}\n onClick={clickable ? onClick : undefined}\n onKeyDown={\n clickable\n ? (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick?.();\n }\n }\n : undefined\n }\n className={cn(\n inline ? 'relative inline-flex' : 'fixed',\n 'flex items-start gap-2.5 max-w-[280px]',\n 'rounded-2xl border border-border bg-popover text-popover-foreground',\n 'px-3.5 py-2.5 shadow-2xl transition-all duration-200 ease-out',\n clickable && 'cursor-pointer hover:bg-accent/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n originClass(position),\n animating ? 'opacity-0 scale-95 translate-y-1' : 'opacity-100 scale-100 translate-y-0',\n className,\n )}\n style={{\n ...(inline ? {} : anchorStyle(position, fabOffset, fabClearance)),\n ...(inline ? {} : { zIndex }),\n pointerEvents: phase === 'visible' ? 'auto' : 'none',\n ...style,\n }}\n >\n {avatar && <div className=\"mt-0.5 shrink-0\">{avatar}</div>}\n\n <div className=\"min-w-0 flex-1 text-sm leading-snug\">\n {senderName && (\n <div className=\"mb-0.5 text-[11px] font-medium text-muted-foreground\">\n {senderName}\n </div>\n )}\n <div className=\"text-foreground\">{children}</div>\n </div>\n\n {onDismiss && (\n <button\n type=\"button\"\n aria-label={dismissLabel}\n onClick={(e) => {\n e.stopPropagation();\n onDismiss();\n }}\n className={cn(\n '-mr-1 -mt-1 flex h-6 w-6 shrink-0 items-center justify-center rounded-full',\n 'text-muted-foreground transition-colors hover:bg-accent hover:text-foreground',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n )}\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </div>\n );\n}\n","'use client';\n\nimport { X } from 'lucide-react';\nimport type { CSSProperties, ReactNode } from 'react';\n\nimport { Avatar, AvatarFallback, AvatarImage } from '@djangocfg/ui-core/components';\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport type { ChatMessage, ChatPersona } from '../types';\nimport type { ChatFABPosition } from './ChatFAB';\nimport { useChatPresence } from './useChatPresence';\n\nexport interface ChatUnreadPreviewProps {\n /** Controlled — usually `!dockOpen && !!message`. */\n open: boolean;\n /** Inbound message to preview. `null` hides the bubble. */\n message: ChatMessage | null;\n /** Tap → open chat + mark read. */\n onClick?: () => void;\n /** × → mark read without opening. */\n onDismiss?: () => void;\n /** Anchor corner — match the FAB so the bubble sits above it. @default 'bottom-right' */\n position?: ChatFABPosition;\n /** Horizontal offset from screen edge, matches the FAB. @default 24 */\n fabOffset?: number;\n /** Vertical clearance above/below the FAB. @default 96 */\n fabClearance?: number;\n /** Lines of body text before ellipsis. @default 2 */\n truncate?: number;\n /** z-index. @default 9998 */\n zIndex?: number;\n /** Render in-place (stories / previews). @default false */\n inline?: boolean;\n /** Override classes on the bubble. */\n className?: string;\n /** Override styles on the bubble. */\n style?: CSSProperties;\n /** ARIA label for the dismiss button. @default 'Mark as read' */\n dismissLabel?: string;\n /** Override the avatar — defaults to derived from `message.sender`. */\n avatar?: ReactNode;\n /** Override the sender label — defaults to `message.sender?.name`. */\n senderName?: string;\n}\n\nconst TIME_FORMAT = new Intl.DateTimeFormat(undefined, {\n hour: '2-digit',\n minute: '2-digit',\n});\n\nfunction anchorStyle(\n position: ChatFABPosition,\n fabOffset: number,\n fabClearance: number,\n): CSSProperties {\n const [vert, horiz] = position.split('-') as ['bottom' | 'top', 'right' | 'left'];\n return { [vert]: fabClearance, [horiz]: fabOffset } as CSSProperties;\n}\n\nfunction originClass(position: ChatFABPosition): string {\n if (position === 'bottom-right') return 'origin-bottom-right';\n if (position === 'bottom-left') return 'origin-bottom-left';\n if (position === 'top-right') return 'origin-top-right';\n return 'origin-top-left';\n}\n\nfunction deriveAvatar(persona?: ChatPersona, name?: string): ReactNode {\n const initials =\n persona?.initials ??\n (name ?? persona?.name ?? '?')\n .split(/\\s+/)\n .map((p) => p[0])\n .filter(Boolean)\n .slice(0, 2)\n .join('')\n .toUpperCase();\n return (\n <Avatar className=\"h-9 w-9\">\n {persona?.avatarUrl ? <AvatarImage src={persona.avatarUrl} /> : null}\n <AvatarFallback>{initials || '?'}</AvatarFallback>\n </Avatar>\n );\n}\n\n/**\n * Push-notification bubble next to the chat FAB.\n *\n * Shows the last inbound message while the chat is closed —\n * Intercom / LiveChat-style. Tap → open chat (host wires `onClick`).\n * Dismiss × → keep chat closed but stop nagging.\n *\n * Pair with `useChatUnread()` (inside `<ChatProvider>`) for state.\n */\nexport function ChatUnreadPreview({\n open,\n message,\n onClick,\n onDismiss,\n position = 'bottom-right',\n fabOffset = 24,\n fabClearance = 96,\n truncate = 2,\n zIndex = 9998,\n inline = false,\n className,\n style,\n dismissLabel = 'Mark as read',\n avatar,\n senderName,\n}: ChatUnreadPreviewProps) {\n const shouldShow = open && !!message;\n const phase = useChatPresence(shouldShow, 200);\n if (phase === 'hidden' || !message) return null;\n\n const animating = phase === 'entering' || phase === 'leaving';\n const clickable = !!onClick;\n const displayName = senderName ?? message.sender?.name ?? 'New message';\n const stamp = TIME_FORMAT.format(new Date(message.createdAt));\n\n return (\n <div\n role={clickable ? 'button' : 'status'}\n aria-live=\"polite\"\n tabIndex={clickable ? 0 : -1}\n onClick={clickable ? onClick : undefined}\n onKeyDown={\n clickable\n ? (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick?.();\n }\n }\n : undefined\n }\n className={cn(\n inline ? 'relative inline-flex' : 'fixed',\n 'flex items-start gap-2.5 max-w-[300px]',\n 'rounded-2xl border border-border bg-popover text-popover-foreground',\n 'px-3.5 py-2.5 shadow-2xl transition-all duration-200 ease-out',\n clickable &&\n 'cursor-pointer hover:bg-accent/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n originClass(position),\n animating ? 'opacity-0 scale-95 translate-y-1' : 'opacity-100 scale-100 translate-y-0',\n className,\n )}\n style={{\n ...(inline ? {} : anchorStyle(position, fabOffset, fabClearance)),\n ...(inline ? {} : { zIndex }),\n pointerEvents: phase === 'visible' ? 'auto' : 'none',\n ...style,\n }}\n >\n <div className=\"mt-0.5 shrink-0\">\n {avatar ?? deriveAvatar(message.sender, displayName)}\n </div>\n\n <div className=\"min-w-0 flex-1 text-sm leading-snug\">\n <div className=\"flex items-baseline justify-between gap-2\">\n <div className=\"truncate text-[12px] font-semibold text-foreground\">\n {displayName}\n </div>\n <div className=\"shrink-0 text-[10px] text-muted-foreground\">{stamp}</div>\n </div>\n <div\n className=\"text-foreground/90 mt-0.5 break-words\"\n style={{\n display: '-webkit-box',\n WebkitLineClamp: truncate,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n }}\n >\n {message.content}\n </div>\n </div>\n\n {onDismiss ? (\n <button\n type=\"button\"\n aria-label={dismissLabel}\n onClick={(e) => {\n e.stopPropagation();\n onDismiss();\n }}\n className={cn(\n '-mr-1 -mt-1 flex h-6 w-6 shrink-0 items-center justify-center rounded-full',\n 'text-muted-foreground transition-colors hover:bg-accent hover:text-foreground',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n )}\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n ) : null}\n </div>\n );\n}\n","'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\n\nimport { useHotkey } from '@djangocfg/ui-core/hooks';\n\nimport { ChatFAB, type ChatFABPosition, type ChatFABProps } from './ChatFAB';\nimport { ChatDock, type ChatDockProps } from './ChatDock';\nimport { ChatGreeting, type ChatGreetingProps } from './ChatGreeting';\nimport { ChatHeaderAudioToggle } from './ChatHeaderAudioToggle';\nimport { ChatUnreadPreview, type ChatUnreadPreviewProps } from './ChatUnreadPreview';\nimport type { ChatMessage } from '../types';\n\nexport interface ChatLauncherHotkey {\n /** Key (case-sensitive single char or named like 'Escape'). */\n key: string;\n /** Require Cmd (mac) or Ctrl (other). */\n meta?: boolean;\n /** Require Shift. */\n shift?: boolean;\n /** Require Alt. */\n alt?: boolean;\n}\n\nexport interface ChatLauncherGreeting\n extends Omit<ChatGreetingProps, 'open' | 'onClick' | 'onDismiss' | 'position' | 'fabOffset' | 'children'> {\n /** Greeting body — string for the default style, or any ReactNode. */\n content: ReactNode;\n /** Persistence key for \"user dismissed this greeting\" in `localStorage`. Pass `null` to disable persistence. @default null */\n dismissStorageKey?: string | null;\n /** Hide the greeting once the user opens the chat. @default true */\n hideOnOpen?: boolean;\n}\n\nexport interface ChatLauncherProps {\n /** Dock contents — typically a `<Chat>` instance. */\n children: ReactNode;\n /** FAB customization (icon, position, label, pulse, badge, tooltip, variant, size). */\n fab?: Omit<ChatFABProps, 'onClick'>;\n /** Dock customization (size, title, position, transition, mobileFullscreen). */\n dock?: Omit<ChatDockProps, 'open' | 'onClose' | 'children'>;\n /**\n * Proactive greeting bubble shown next to the FAB before the user opens the chat.\n * Set to a string or full config object. Omit to disable.\n */\n greeting?: string | ChatLauncherGreeting;\n /** Open/close via a keyboard shortcut. */\n hotkey?: ChatLauncherHotkey;\n /** Initial open state for uncontrolled mode. @default false */\n defaultOpen?: boolean;\n /** Controlled open state (pair with `onOpenChange`). */\n open?: boolean;\n /** Controlled open state setter. */\n onOpenChange?: (open: boolean) => void;\n /**\n * Focus the composer textarea when the dock opens. Saves a click for\n * every \"FAB → start typing\" interaction. @default true\n */\n autoFocusComposerOnOpen?: boolean;\n /**\n * Close the dock on `Escape`. Mirrors standard popover / drawer UX.\n * Set to `false` to disable (e.g. if you want Escape to do something\n * else inside the chat). @default true\n */\n closeOnEscape?: boolean;\n /**\n * Last inbound message (admin reply / system notice / agent push) the\n * user hasn't seen yet. Drives the `<ChatUnreadPreview>` bubble next\n * to the FAB and (by default) the FAB badge.\n *\n * Source it from `useChatUnread()` inside your `<ChatProvider>`.\n */\n unreadMessage?: ChatMessage | null;\n /**\n * Called when the user opens the chat via FAB/preview/hotkey or\n * dismisses the preview with ×. Wire to `useChatUnread().markRead`.\n */\n onMarkRead?: () => void;\n /**\n * Customize the unread bubble (`truncate`, `dismissLabel`, …).\n * `open`/`message`/`onClick`/`onDismiss`/`position`/`fabOffset` are\n * wired automatically.\n */\n unreadPreview?: Omit<\n ChatUnreadPreviewProps,\n 'open' | 'message' | 'onClick' | 'onDismiss' | 'position' | 'fabOffset'\n >;\n /**\n * Auto-inject a mute / unmute button into the header. Pass the\n * `useChatAudio()` (or any compatible `{ muted, toggleMute }`)\n * instance — the launcher renders `<ChatHeaderAudioToggle>` in the\n * header's actions slot when audio is actually configured (not silent).\n *\n * Hosts that manage their own header can ignore this prop and render\n * `<ChatHeaderAudioToggle>` directly.\n */\n audio?: { muted: boolean; toggleMute: () => void; isSilent?: boolean } | null;\n /**\n * Suppress the auto-injected audio toggle even when `audio` is passed.\n * @default false\n */\n hideAudioToggle?: boolean;\n}\n\nfunction readDismissed(storageKey: string | null | undefined): boolean {\n if (!storageKey) return false;\n if (typeof window === 'undefined') return false;\n try {\n return window.localStorage.getItem(storageKey) === '1';\n } catch {\n return false;\n }\n}\n\nfunction writeDismissed(storageKey: string | null | undefined): void {\n if (!storageKey) return;\n if (typeof window === 'undefined') return;\n try {\n window.localStorage.setItem(storageKey, '1');\n } catch {\n // private mode / storage full — silently ignore\n }\n}\n\n/**\n * Floating chat launcher = FAB + Dock + presence + optional greeting + hotkey.\n *\n * 99% of hosts use this directly. For non-FAB triggers (e.g. an inline\n * link in the page) compose `<ChatDock>` with your own button.\n */\nexport function ChatLauncher({\n children,\n fab,\n dock,\n greeting,\n hotkey,\n defaultOpen = false,\n open: controlledOpen,\n onOpenChange,\n autoFocusComposerOnOpen = true,\n closeOnEscape = true,\n unreadMessage,\n onMarkRead,\n unreadPreview,\n audio,\n hideAudioToggle = false,\n}: ChatLauncherProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\n const isControlled = controlledOpen !== undefined;\n const open = isControlled ? controlledOpen : uncontrolledOpen;\n const dockContentRef = useRef<HTMLDivElement>(null);\n\n // Auto-focus the composer when the dock opens.\n // We probe the dock subtree for the first textarea/input after the\n // enter-transition settles. Keeps the hook self-contained — no need to\n // bridge into the ChatProvider context which lives inside `children`.\n useEffect(() => {\n if (!autoFocusComposerOnOpen || !open) return;\n const t = setTimeout(() => {\n const root = dockContentRef.current;\n if (!root) return;\n const target = root.querySelector<HTMLElement>(\n 'textarea:not([disabled]):not([readonly]), input[type=\"text\"]:not([disabled]):not([readonly])',\n );\n target?.focus();\n }, 120);\n return () => clearTimeout(t);\n }, [open, autoFocusComposerOnOpen]);\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) setUncontrolledOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange],\n );\n const toggleOpen = useCallback(() => setOpen(!open), [open, setOpen]);\n\n // Two-step Escape (ChatGPT / Slack behaviour):\n // - When focus is inside a textarea / input / contenteditable, first Esc\n // just blurs — drafts survive accidental presses.\n // - When focus is elsewhere (the user already left the composer or never\n // focused it), Esc closes the dock.\n // Disabled when chat is shut so we don't intercept page-level Esc bindings.\n useHotkey(\n 'escape',\n (e) => {\n const target = (e?.target as HTMLElement | null) ?? null;\n const inEditable =\n !!target &&\n (target.matches?.('input, textarea, [contenteditable=\"true\"]') ?? false);\n if (inEditable) {\n target.blur();\n return;\n }\n setOpen(false);\n },\n { enabled: closeOnEscape && open },\n );\n\n // Normalize greeting prop.\n const greetingConfig: ChatLauncherGreeting | null =\n greeting === undefined\n ? null\n : typeof greeting === 'string'\n ? { content: greeting }\n : greeting;\n\n const [dismissed, setDismissed] = useState(() =>\n readDismissed(greetingConfig?.dismissStorageKey),\n );\n\n // Hotkey.\n useEffect(() => {\n if (!hotkey) return;\n const handler = (e: KeyboardEvent) => {\n const metaOk = hotkey.meta ? e.metaKey || e.ctrlKey : !e.metaKey && !e.ctrlKey;\n const shiftOk = hotkey.shift ? e.shiftKey : !e.shiftKey;\n const altOk = hotkey.alt ? e.altKey : !e.altKey;\n if (!metaOk || !shiftOk || !altOk) return;\n if (e.key !== hotkey.key) return;\n e.preventDefault();\n setOpen(!open);\n };\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [hotkey?.key, hotkey?.meta, hotkey?.shift, hotkey?.alt, open, setOpen, hotkey]);\n\n // Greeting visibility: respect dismissal, hideOnOpen, and the actual open state.\n const greetingOpen = !!greetingConfig\n && !dismissed\n && (greetingConfig.hideOnOpen === false || !open);\n\n const fabPosition: ChatFABPosition = fab?.position ?? 'bottom-right';\n const fabOffset = fab?.offset ?? 24;\n\n const handleGreetingDismiss = () => {\n setDismissed(true);\n writeDismissed(greetingConfig?.dismissStorageKey);\n };\n\n const handleGreetingClick = () => {\n setOpen(true);\n // Tap-to-open also clears the proactive bubble — pushing it again on\n // the same visit would feel spammy. Persisted dismissal honours the\n // storage key so it doesn't reappear after navigation either.\n setDismissed(true);\n writeDismissed(greetingConfig?.dismissStorageKey);\n };\n\n // Mark-as-read also fires when the chat opens through any path (FAB,\n // hotkey, controlled state) — symmetric with click-to-open via the\n // preview itself.\n useEffect(() => {\n if (open && unreadMessage) onMarkRead?.();\n }, [open, unreadMessage, onMarkRead]);\n\n // Unread preview replaces the greeting when there's a real inbound\n // message to surface — same anchor, more relevant content.\n const unreadOpen = !open && !!unreadMessage;\n const handleUnreadClick = () => {\n setOpen(true);\n onMarkRead?.();\n };\n const handleUnreadDismiss = () => {\n onMarkRead?.();\n };\n\n // Auto-derive a \"1\" badge from unread when the host didn't set one.\n const resolvedFab = unreadMessage && fab?.badge === undefined\n ? { ...fab, badge: 1 }\n : fab;\n\n return (\n <>\n <ChatFAB {...resolvedFab} onClick={toggleOpen} />\n {unreadMessage ? (\n <ChatUnreadPreview\n {...unreadPreview}\n open={unreadOpen}\n message={unreadMessage}\n onClick={handleUnreadClick}\n onDismiss={handleUnreadDismiss}\n position={fabPosition}\n fabOffset={fabOffset}\n />\n ) : greetingConfig ? (\n <ChatGreeting\n {...greetingConfig}\n open={greetingOpen}\n onClick={handleGreetingClick}\n onDismiss={handleGreetingDismiss}\n position={fabPosition}\n fabOffset={fabOffset}\n >\n {greetingConfig.content}\n </ChatGreeting>\n ) : null}\n <ChatDock\n {...dock}\n open={open}\n onClose={() => setOpen(false)}\n headerActions={\n (audio && !audio.isSilent && !hideAudioToggle) || dock?.headerActions ? (\n <>\n {dock?.headerActions}\n {audio && !audio.isSilent && !hideAudioToggle ? (\n <ChatHeaderAudioToggle muted={audio.muted} onToggle={audio.toggleMute} />\n ) : null}\n </>\n ) : undefined\n }\n >\n <div ref={dockContentRef} className=\"flex h-full min-h-0 min-w-0 flex-col\">\n {children}\n </div>\n </ChatDock>\n </>\n );\n}\n"]}
|