@djangocfg/ui-tools 2.1.381 → 2.1.383

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.
Files changed (178) hide show
  1. package/README.md +132 -899
  2. package/dist/ChatRoot-6IZFM5HM.mjs +5 -0
  3. package/dist/{ChatRoot-EJC5Y2YM.cjs.map → ChatRoot-6IZFM5HM.mjs.map} +1 -1
  4. package/dist/ChatRoot-LW4XNIKP.cjs +14 -0
  5. package/dist/{ChatRoot-QOSKJPM6.mjs.map → ChatRoot-LW4XNIKP.cjs.map} +1 -1
  6. package/dist/DictationField-U25MEYAL.mjs +4 -0
  7. package/dist/DictationField-U25MEYAL.mjs.map +1 -0
  8. package/dist/DictationField-XWR5VOID.cjs +13 -0
  9. package/dist/DictationField-XWR5VOID.cjs.map +1 -0
  10. package/dist/{DocsLayout-2YKPXZYO.mjs → DocsLayout-2P3ONDWJ.mjs} +3 -3
  11. package/dist/{DocsLayout-2YKPXZYO.mjs.map → DocsLayout-2P3ONDWJ.mjs.map} +1 -1
  12. package/dist/{DocsLayout-Q4KS3QWW.cjs → DocsLayout-2YZNS5VK.cjs} +8 -8
  13. package/dist/{DocsLayout-Q4KS3QWW.cjs.map → DocsLayout-2YZNS5VK.cjs.map} +1 -1
  14. package/dist/chunk-4PFW7MIJ.cjs +837 -0
  15. package/dist/chunk-4PFW7MIJ.cjs.map +1 -0
  16. package/dist/chunk-C2YN6WEO.mjs +833 -0
  17. package/dist/chunk-C2YN6WEO.mjs.map +1 -0
  18. package/dist/{chunk-XACCHZH2.cjs → chunk-FIRK5CEH.cjs} +42 -4
  19. package/dist/chunk-FIRK5CEH.cjs.map +1 -0
  20. package/dist/{chunk-NWUT327A.mjs → chunk-HIK6BPL7.mjs} +38 -5
  21. package/dist/chunk-HIK6BPL7.mjs.map +1 -0
  22. package/dist/chunk-OZAU3QWD.cjs +2493 -0
  23. package/dist/chunk-OZAU3QWD.cjs.map +1 -0
  24. package/dist/chunk-UWVP6LCW.mjs +2447 -0
  25. package/dist/chunk-UWVP6LCW.mjs.map +1 -0
  26. package/dist/index.cjs +1668 -99
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +1215 -107
  29. package/dist/index.d.ts +1215 -107
  30. package/dist/index.mjs +1555 -50
  31. package/dist/index.mjs.map +1 -1
  32. package/package.json +16 -15
  33. package/src/audio-assets.d.ts +8 -0
  34. package/src/components/markdown/MarkdownMessage/CollapseToggle.tsx +3 -1
  35. package/src/components/markdown/MarkdownMessage/components.tsx +2 -5
  36. package/src/tools/Chat/README.md +347 -530
  37. package/src/tools/Chat/components/Attachments.tsx +6 -1
  38. package/src/tools/Chat/components/ChatRoot.tsx +30 -2
  39. package/src/tools/Chat/components/Composer.tsx +20 -3
  40. package/src/tools/Chat/components/ErrorBanner.tsx +7 -3
  41. package/src/tools/Chat/components/MessageActions.tsx +3 -1
  42. package/src/tools/Chat/components/MessageBubble.tsx +6 -5
  43. package/src/tools/Chat/components/MessageList.tsx +87 -1
  44. package/src/tools/Chat/components/ToolCalls.tsx +21 -3
  45. package/src/tools/Chat/context/ChatProvider.tsx +21 -3
  46. package/src/tools/Chat/core/audio/audioBus.ts +10 -163
  47. package/src/tools/Chat/core/audio/defaults.ts +43 -0
  48. package/src/tools/Chat/core/audio/index.ts +1 -0
  49. package/src/tools/Chat/core/audio/preferences.ts +5 -59
  50. package/src/tools/Chat/core/audio/sounds/error.mp3 +0 -0
  51. package/src/tools/Chat/core/audio/sounds/mention.mp3 +0 -0
  52. package/src/tools/Chat/core/audio/sounds/notification.mp3 +0 -0
  53. package/src/tools/Chat/core/audio/sounds/received.mp3 +0 -0
  54. package/src/tools/Chat/core/audio/sounds/sent.mp3 +0 -0
  55. package/src/tools/Chat/core/audio/sounds/start.mp3 +0 -0
  56. package/src/tools/Chat/core/audio/types.ts +28 -0
  57. package/src/tools/Chat/core/reducer.ts +33 -0
  58. package/src/tools/Chat/core/transport/index.ts +13 -0
  59. package/src/tools/Chat/core/transport/mappers/index.ts +6 -0
  60. package/src/tools/Chat/core/transport/mappers/pydantic-ai.ts +142 -0
  61. package/src/tools/Chat/core/transport/pydantic-ai-transport.ts +208 -0
  62. package/src/tools/Chat/core/transport/sse.ts +18 -5
  63. package/src/tools/Chat/hooks/index.ts +25 -0
  64. package/src/tools/Chat/hooks/useAutoFocusOnStreamEnd.ts +5 -3
  65. package/src/tools/Chat/hooks/useChat.ts +28 -0
  66. package/src/tools/Chat/hooks/useChatAudio.ts +59 -180
  67. package/src/tools/Chat/hooks/useChatDockPrefs.ts +74 -0
  68. package/src/tools/Chat/hooks/useChatReset.ts +70 -0
  69. package/src/tools/Chat/hooks/useChatUnread.ts +87 -0
  70. package/src/tools/Chat/hooks/useFocusOnEmptyClick.ts +111 -0
  71. package/src/tools/Chat/hooks/useVisitorFingerprint.ts +48 -0
  72. package/src/tools/Chat/index.ts +84 -1
  73. package/src/tools/Chat/launcher/ChatDock.tsx +263 -0
  74. package/src/tools/Chat/launcher/ChatFAB.tsx +349 -0
  75. package/src/tools/Chat/launcher/ChatGreeting.tsx +200 -0
  76. package/src/tools/Chat/launcher/ChatHeader.tsx +76 -0
  77. package/src/tools/Chat/launcher/ChatHeaderActionButton.tsx +87 -0
  78. package/src/tools/Chat/launcher/ChatHeaderAudioToggle.tsx +47 -0
  79. package/src/tools/Chat/launcher/ChatHeaderLanguageButton.tsx +179 -0
  80. package/src/tools/Chat/launcher/ChatHeaderModeToggle.tsx +57 -0
  81. package/src/tools/Chat/launcher/ChatHeaderResetButton.tsx +93 -0
  82. package/src/tools/Chat/launcher/ChatLauncher.tsx +321 -0
  83. package/src/tools/Chat/launcher/ChatUnreadPreview.tsx +197 -0
  84. package/src/tools/Chat/launcher/index.ts +46 -0
  85. package/src/tools/Chat/launcher/useChatPresence.ts +44 -0
  86. package/src/tools/Chat/styles/bubbleTokens.ts +71 -0
  87. package/src/tools/Chat/styles/index.ts +16 -0
  88. package/src/tools/Chat/styles/useChatStyles.ts +101 -0
  89. package/src/tools/Chat/types/attachment.ts +25 -0
  90. package/src/tools/Chat/types/config.ts +48 -0
  91. package/src/tools/Chat/types/events.ts +35 -0
  92. package/src/tools/Chat/types/index.ts +34 -0
  93. package/src/tools/Chat/types/labels.ts +38 -0
  94. package/src/tools/Chat/types/message.ts +32 -0
  95. package/src/tools/Chat/types/persona.ts +31 -0
  96. package/src/tools/Chat/types/session.ts +43 -0
  97. package/src/tools/Chat/types/tool-call.ts +17 -0
  98. package/src/tools/Chat/types/transport.ts +28 -0
  99. package/src/tools/Chat/types.ts +5 -240
  100. package/src/tools/MarkdownEditor/MarkdownEditor.tsx +50 -14
  101. package/src/tools/MarkdownEditor/index.ts +1 -1
  102. package/src/tools/SpeechRecognition/README.md +336 -0
  103. package/src/tools/SpeechRecognition/__tests__/ids.test.ts +15 -0
  104. package/src/tools/SpeechRecognition/__tests__/language.test.ts +59 -0
  105. package/src/tools/SpeechRecognition/__tests__/reducer.test.ts +71 -0
  106. package/src/tools/SpeechRecognition/__tests__/transcript.test.ts +52 -0
  107. package/src/tools/SpeechRecognition/components/DevicePicker.tsx +49 -0
  108. package/src/tools/SpeechRecognition/components/DictationButton.tsx +93 -0
  109. package/src/tools/SpeechRecognition/components/EngineBadge.tsx +30 -0
  110. package/src/tools/SpeechRecognition/components/ErrorBanner.tsx +52 -0
  111. package/src/tools/SpeechRecognition/components/LanguagePicker.tsx +63 -0
  112. package/src/tools/SpeechRecognition/components/MicMeter.tsx +63 -0
  113. package/src/tools/SpeechRecognition/components/PushToTalkHint.tsx +51 -0
  114. package/src/tools/SpeechRecognition/components/TranscriptView.tsx +55 -0
  115. package/src/tools/SpeechRecognition/components/index.ts +16 -0
  116. package/src/tools/SpeechRecognition/context/SpeechRecognitionProvider.tsx +47 -0
  117. package/src/tools/SpeechRecognition/context/index.ts +6 -0
  118. package/src/tools/SpeechRecognition/core/audio/defaults.ts +24 -0
  119. package/src/tools/SpeechRecognition/core/engine/external.ts +222 -0
  120. package/src/tools/SpeechRecognition/core/engine/http.ts +147 -0
  121. package/src/tools/SpeechRecognition/core/engine/index.ts +52 -0
  122. package/src/tools/SpeechRecognition/core/engine/mediarecorder.ts +105 -0
  123. package/src/tools/SpeechRecognition/core/engine/websocket.ts +211 -0
  124. package/src/tools/SpeechRecognition/core/engine/webspeech.ts +188 -0
  125. package/src/tools/SpeechRecognition/core/ids.ts +11 -0
  126. package/src/tools/SpeechRecognition/core/index.ts +14 -0
  127. package/src/tools/SpeechRecognition/core/language.ts +78 -0
  128. package/src/tools/SpeechRecognition/core/languages-catalog.ts +229 -0
  129. package/src/tools/SpeechRecognition/core/logger.ts +3 -0
  130. package/src/tools/SpeechRecognition/core/reducer.ts +105 -0
  131. package/src/tools/SpeechRecognition/core/transcript.ts +36 -0
  132. package/src/tools/SpeechRecognition/hooks/index.ts +14 -0
  133. package/src/tools/SpeechRecognition/hooks/useDictation.ts +59 -0
  134. package/src/tools/SpeechRecognition/hooks/useEnginePrefs.ts +15 -0
  135. package/src/tools/SpeechRecognition/hooks/useMicDevices.ts +57 -0
  136. package/src/tools/SpeechRecognition/hooks/useMicLevel.ts +52 -0
  137. package/src/tools/SpeechRecognition/hooks/usePushToTalk.ts +85 -0
  138. package/src/tools/SpeechRecognition/hooks/useResolvedLanguage.ts +28 -0
  139. package/src/tools/SpeechRecognition/hooks/useSpeechLanguageInfo.ts +108 -0
  140. package/src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts +188 -0
  141. package/src/tools/SpeechRecognition/hooks/useVoiceSupport.ts +78 -0
  142. package/src/tools/SpeechRecognition/index.ts +82 -0
  143. package/src/tools/SpeechRecognition/lazy.tsx +19 -0
  144. package/src/tools/SpeechRecognition/store/index.ts +2 -0
  145. package/src/tools/SpeechRecognition/store/prefsStore.ts +54 -0
  146. package/src/tools/SpeechRecognition/types.ts +133 -0
  147. package/src/tools/SpeechRecognition/widgets/DictationField.tsx +105 -0
  148. package/src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx +305 -0
  149. package/src/tools/SpeechRecognition/widgets/VoiceMessageRecorder.tsx +88 -0
  150. package/src/tools/SpeechRecognition/widgets/index.ts +6 -0
  151. package/dist/ChatRoot-EJC5Y2YM.cjs +0 -14
  152. package/dist/ChatRoot-QOSKJPM6.mjs +0 -5
  153. package/dist/chunk-NWUT327A.mjs.map +0 -1
  154. package/dist/chunk-QLMKCSR6.mjs +0 -2420
  155. package/dist/chunk-QLMKCSR6.mjs.map +0 -1
  156. package/dist/chunk-SI5RD2GD.cjs +0 -2460
  157. package/dist/chunk-SI5RD2GD.cjs.map +0 -1
  158. package/dist/chunk-XACCHZH2.cjs.map +0 -1
  159. package/src/components/markdown/MarkdownMessage/MarkdownMessage.story.tsx +0 -771
  160. package/src/stories/index.ts +0 -33
  161. package/src/tools/AudioPlayer/AudioPlayer.story.tsx +0 -481
  162. package/src/tools/Chat/Chat.story.tsx +0 -1457
  163. package/src/tools/CodeEditor/CodeEditor.story.tsx +0 -202
  164. package/src/tools/CronScheduler/CronScheduler.story.tsx +0 -300
  165. package/src/tools/Gallery/Gallery.story.tsx +0 -237
  166. package/src/tools/ImageViewer/ImageViewer.story.tsx +0 -85
  167. package/src/tools/JsonForm/JsonForm.story.tsx +0 -350
  168. package/src/tools/JsonTree/JsonTree.story.tsx +0 -141
  169. package/src/tools/LottiePlayer/LottiePlayer.story.tsx +0 -95
  170. package/src/tools/Map/Map.story.tsx +0 -458
  171. package/src/tools/MarkdownEditor/MarkdownEditor.story.tsx +0 -225
  172. package/src/tools/Mermaid/Mermaid.story.tsx +0 -251
  173. package/src/tools/OpenapiViewer/OpenapiViewer.story.tsx +0 -230
  174. package/src/tools/PrettyCode/PrettyCode.story.tsx +0 -304
  175. package/src/tools/Tour/Tour.story.tsx +0 -279
  176. package/src/tools/Tree/Tree.story.tsx +0 -620
  177. package/src/tools/Uploader/Uploader.story.tsx +0 -415
  178. package/src/tools/VideoPlayer/VideoPlayer.story.tsx +0 -87
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { useSpeechPrefs, useResolvedLanguage } from './chunk-C2YN6WEO.mjs';
1
2
  export { ArrayFieldItemTemplate, ArrayFieldTemplate, BaseInputTemplate, CheckboxWidget, ColorWidget, ErrorListTemplate, FieldTemplate, JsonSchemaForm, NumberWidget, ObjectFieldTemplate, SelectWidget, SliderWidget, SwitchWidget, TextWidget, evaluateDisabledWhen, getRequiredFields, hasRequiredFields, mergeDefaults, normalizeFormData, safeJsonParse, safeJsonStringify, validateRequiredFields, validateSchema } from './chunk-GYIO7W7M.mjs';
2
3
  export { useLottie } from './chunk-ODO4GMW7.mjs';
3
4
  export { Player as AudioPlayer } from './chunk-VWQ5WOIL.mjs';
@@ -5,24 +6,24 @@ export { NativeProvider, StreamProvider, VideoControls, VideoErrorFallback, Vide
5
6
  export { ImageViewer } from './chunk-OBRSGM64.mjs';
6
7
  export { generateContentKey, useAudioCache, useBlobUrlCleanup, useImageCache, useMediaCacheStore, useVideoCache, useVideoPlayerSettings } from './chunk-C6GXVH5J.mjs';
7
8
  export { CronSchedulerProvider, CustomInput, DayChips, MonthDayGrid, SchedulePreview, ScheduleTypeSelector, TimeSelector, buildCron, humanizeCron, isValidCron, parseCron, useCronCustom, useCronMonthDays, useCronPreview, useCronScheduler, useCronSchedulerContext, useCronTime, useCronType, useCronWeekDays } from './chunk-PVAX67JG.mjs';
8
- import { LIMITS, createId, useChatContextOptional, useChatAudioPrefs } from './chunk-QLMKCSR6.mjs';
9
- export { Attachments, AttachmentsGrid, AttachmentsList, CHAT_EVENT_NAME, CSS_VARS, ChatProvider, ChatRoot, Composer, DEFAULT_LABELS, DEFAULT_SIDEBAR, DEFAULT_Z_INDEX, EmptyState, ErrorBanner, HOTKEYS, JumpToLatest, LIMITS, MessageActions, MessageBubble, MessageList, STORAGE_KEYS, Sources, StreamingIndicator, ToolCalls, createId, createTokenBuffer, deriveInitials, getChatLogger, initialState, isSubmittableDraft, reducer, resolvePersona, sanitizeDraft, useChat, useChatAudio, useChatAudioPrefs, useChatComposer, useChatContext, useChatContextOptional, useChatLayout } from './chunk-QLMKCSR6.mjs';
9
+ import { LIMITS, createId, useChatContext, useChatContextOptional } from './chunk-UWVP6LCW.mjs';
10
+ export { Attachments, AttachmentsGrid, AttachmentsList, CHAT_EVENT_NAME, CSS_VARS, ChatProvider, ChatRoot, Composer, DEFAULT_CHAT_SOUNDS, DEFAULT_LABELS, DEFAULT_SIDEBAR, DEFAULT_Z_INDEX, EmptyState, ErrorBanner, HOTKEYS, JumpToLatest, LIMITS, MessageActions, MessageBubble, MessageList, STORAGE_KEYS, Sources, StreamingIndicator, ToolCalls, createId, createTokenBuffer, deriveInitials, getChatLogger, initialState, isSubmittableDraft, reducer, resolvePersona, sanitizeDraft, useAutoFocusOnStreamEnd, useChat, useChatAudio, useChatBubbleStyles, useChatComposer, useChatContext, useChatContextOptional, useChatDestructiveStyles, useChatLayout, useChatRoleStyles, useFocusOnEmptyClick, useRegisterComposer } from './chunk-UWVP6LCW.mjs';
10
11
  export { TreeError, TreeSkeleton, createDemoTree } from './chunk-B6IR5KSC.mjs';
11
12
  export { DEFAULT_TREE_APPEARANCE, DEFAULT_TREE_LABELS, TreeRoot as Tree, TreeChevron, TreeContent, TreeEmpty, TreeIcon, TreeIndentGuides, TreeLabel, TreeProvider, TreeRoot, TreeRow, TreeSearchInput, appearanceToStyle, clearTreeState, createChildCache, flattenTree, loadTreeState, resolveAppearance, resolveChildren, saveTreeState, useTreeActions, useTreeContext, useTreeExpansion, useTreeFocus, useTreeKeyboard, useTreeLabels, useTreeRows, useTreeSearch, useTreeSelection, useTreeTypeAhead } from './chunk-ZL7FH4NW.mjs';
12
13
  import { PlaygroundProvider } from './chunk-Y6UTOBF6.mjs';
13
- export { MarkdownMessage, Mermaid_default as Mermaid, PrettyCode_default as PrettyCode, extractTextFromChildren, useCollapsibleContent } from './chunk-NWUT327A.mjs';
14
+ export { ANCHOR, BUBBLE_SURFACE, DESTRUCTIVE_SURFACE, MarkdownMessage, Mermaid_default as Mermaid, PrettyCode_default as PrettyCode, TOGGLE, TOOL_CALL, extractTextFromChildren, useCollapsibleContent } from './chunk-HIK6BPL7.mjs';
14
15
  export { JsonTree_default as JsonTree } from './chunk-ECONRHIG.mjs';
15
16
  import './chunk-KNEQRUBA.mjs';
16
17
  import { __name, __publicField } from './chunk-N2XQF2OL.mjs';
17
18
  import * as React from 'react';
18
- import { lazy, forwardRef, useRef, useState, useCallback, useImperativeHandle, useEffect, createContext, Suspense, useContext, useMemo } from 'react';
19
+ import { forwardRef, createContext, lazy, useRef, useState, useCallback, useImperativeHandle, useEffect, useMemo, Suspense, useContext } from 'react';
19
20
  import { cn } from '@djangocfg/ui-core/lib';
20
21
  import { useAppT } from '@djangocfg/i18n';
21
- import { jsx, jsxs } from 'react/jsx-runtime';
22
- import { VolumeX, Volume2, Bold, Italic, Strikethrough, Code, Heading1, Heading2, Heading3, List, ListOrdered, Quote, Minus } from 'lucide-react';
23
- import { Button } from '@djangocfg/ui-core/components';
24
- import { useResolvedTheme } from '@djangocfg/ui-core/hooks';
25
- import { ReactRenderer, useEditor as useEditor$1, EditorContent } from '@tiptap/react';
22
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
23
+ import { Bold, Italic, Strikethrough, Code, Heading1, Heading2, Heading3, List, ListOrdered, Quote, Minus, Bot, X, PanelRightClose, PanelRightOpen, VolumeX, Volume2, RotateCcw, Globe } from 'lucide-react';
24
+ import { createAudioPrefsStore, useIsPhone, useIsTabletOrBelow, useIsMobile, useHotkey, useLocalStorage, useResolvedTheme } from '@djangocfg/ui-core/hooks';
25
+ import { Button, Portal, Combobox, Flag, Avatar, AvatarImage, AvatarFallback } from '@djangocfg/ui-core/components';
26
+ import { useEditor as useEditor$1, EditorContent, ReactRenderer } from '@tiptap/react';
26
27
  import StarterKit from '@tiptap/starter-kit';
27
28
  import Placeholder from '@tiptap/extension-placeholder';
28
29
  import Mention from '@tiptap/extension-mention';
@@ -243,7 +244,7 @@ function OpenapiLoadingFallback() {
243
244
  }
244
245
  __name(OpenapiLoadingFallback, "OpenapiLoadingFallback");
245
246
  var LazyDocsLayout = createLazyComponent(
246
- () => import('./DocsLayout-2YKPXZYO.mjs').then((mod) => ({ default: mod.DocsLayout })),
247
+ () => import('./DocsLayout-2P3ONDWJ.mjs').then((mod) => ({ default: mod.DocsLayout })),
247
248
  {
248
249
  displayName: "LazyDocsLayout",
249
250
  fallback: /* @__PURE__ */ jsx(OpenapiLoadingFallback, {})
@@ -349,7 +350,7 @@ var LazyTree = createLazyComponent(
349
350
  }
350
351
  );
351
352
  var LazyChat = createLazyComponent(
352
- () => import('./ChatRoot-QOSKJPM6.mjs').then((m) => ({ default: m.ChatRoot })),
353
+ () => import('./ChatRoot-6IZFM5HM.mjs').then((m) => ({ default: m.ChatRoot })),
353
354
  {
354
355
  displayName: "LazyChat",
355
356
  fallback: /* @__PURE__ */ jsx(LoadingFallback, { minHeight: 320, text: "Loading chat\u2026" })
@@ -411,7 +412,13 @@ async function* parseSSE(response, options = {}) {
411
412
  buffer = buffer.slice(separator + 2);
412
413
  const raw = parseEventBlock(rawBlock);
413
414
  const evt = map(raw);
414
- if (evt) yield evt;
415
+ if (evt) {
416
+ if (Array.isArray(evt)) {
417
+ for (const e of evt) yield e;
418
+ } else {
419
+ yield evt;
420
+ }
421
+ }
415
422
  separator = buffer.indexOf("\n\n");
416
423
  }
417
424
  idleCheck();
@@ -419,7 +426,13 @@ async function* parseSSE(response, options = {}) {
419
426
  if (buffer.trim()) {
420
427
  const raw = parseEventBlock(buffer);
421
428
  const evt = map(raw);
422
- if (evt) yield evt;
429
+ if (evt) {
430
+ if (Array.isArray(evt)) {
431
+ for (const e of evt) yield e;
432
+ } else {
433
+ yield evt;
434
+ }
435
+ }
423
436
  }
424
437
  } finally {
425
438
  try {
@@ -651,6 +664,1440 @@ function createMockTransport(opts = {}) {
651
664
  };
652
665
  }
653
666
  __name(createMockTransport, "createMockTransport");
667
+
668
+ // src/tools/Chat/core/transport/mappers/pydantic-ai.ts
669
+ function createToolIdQueue() {
670
+ const queues = /* @__PURE__ */ new Map();
671
+ let counter = 0;
672
+ return {
673
+ push(name) {
674
+ const id = `${name}-${counter++}-${Date.now()}`;
675
+ const q = queues.get(name) ?? [];
676
+ q.push(id);
677
+ queues.set(name, q);
678
+ return id;
679
+ },
680
+ shift(name) {
681
+ return queues.get(name)?.shift() ?? `${name}-orphan-${counter++}`;
682
+ },
683
+ clear() {
684
+ queues.clear();
685
+ }
686
+ };
687
+ }
688
+ __name(createToolIdQueue, "createToolIdQueue");
689
+ function* mapPydanticAIEvent(ev, toolIds) {
690
+ switch (ev.type) {
691
+ case "text_delta":
692
+ if (ev.delta) yield { type: "chunk", delta: ev.delta };
693
+ return;
694
+ case "tool_call": {
695
+ const name = ev.tool ?? "tool";
696
+ const toolId = toolIds.push(name);
697
+ yield { type: "tool_call_start", toolId, name, input: ev.args };
698
+ return;
699
+ }
700
+ case "tool_result": {
701
+ const name = ev.tool ?? "tool";
702
+ const toolId = toolIds.shift(name);
703
+ const output = ev.data !== void 0 ? ev.data : ev.result;
704
+ yield { type: "tool_call_end", toolId, output, status: "success" };
705
+ return;
706
+ }
707
+ case "done":
708
+ yield { type: "message_end", tokensOut: ev.total_tokens };
709
+ return;
710
+ case "error":
711
+ yield { type: "error", code: "backend_error", message: ev.error ?? "Unknown error" };
712
+ return;
713
+ case "approval_required":
714
+ return;
715
+ }
716
+ }
717
+ __name(mapPydanticAIEvent, "mapPydanticAIEvent");
718
+ function createPydanticAISSEMap() {
719
+ const toolIds = createToolIdQueue();
720
+ return (raw) => {
721
+ if (!raw.data) return null;
722
+ let parsed;
723
+ try {
724
+ parsed = JSON.parse(raw.data);
725
+ } catch {
726
+ return null;
727
+ }
728
+ const out = [];
729
+ for (const evt of mapPydanticAIEvent(parsed, toolIds)) {
730
+ out.push(evt);
731
+ }
732
+ if (out.length === 0) return null;
733
+ if (out.length === 1) return out[0];
734
+ return out;
735
+ };
736
+ }
737
+ __name(createPydanticAISSEMap, "createPydanticAISSEMap");
738
+
739
+ // src/tools/Chat/core/transport/pydantic-ai-transport.ts
740
+ var DEFAULT_SESSION_ID = "default";
741
+ function mapStatusToCode2(status) {
742
+ if (status === 401 || status === 403) return "unauthorized";
743
+ if (status === 404) return "not_found";
744
+ if (status === 408) return "timeout";
745
+ if (status === 429) return "rate_limited";
746
+ if (status >= 500) return "server_error";
747
+ return "error";
748
+ }
749
+ __name(mapStatusToCode2, "mapStatusToCode");
750
+ function createPydanticAIChatTransport(opts) {
751
+ const fetchImpl = opts.fetchImpl ?? fetch.bind(globalThis);
752
+ const streamMethod = opts.streamMethod ?? "POST";
753
+ async function resolvedHeaders(extra) {
754
+ const base = opts.buildHeaders ? await opts.buildHeaders() : {};
755
+ const headers = new Headers(base);
756
+ if (extra) {
757
+ for (const [k, v] of Object.entries(extra)) headers.set(k, v);
758
+ }
759
+ return headers;
760
+ }
761
+ __name(resolvedHeaders, "resolvedHeaders");
762
+ return {
763
+ async createSession(createOpts) {
764
+ if (opts.bootstrapSession) return opts.bootstrapSession(createOpts);
765
+ return { sessionId: DEFAULT_SESSION_ID };
766
+ },
767
+ async loadHistory(sessionId, cursor) {
768
+ if (opts.loadHistory) return opts.loadHistory(sessionId, cursor);
769
+ return { messages: [], hasMore: false, nextCursor: null };
770
+ },
771
+ async *stream(sessionId, content, options) {
772
+ const url = opts.buildStreamUrl(sessionId, content);
773
+ const headers = await resolvedHeaders({ Accept: "text/event-stream" });
774
+ const init = {
775
+ method: streamMethod,
776
+ headers,
777
+ signal: options.signal
778
+ };
779
+ if (streamMethod === "POST") {
780
+ headers.set("Content-Type", "application/json");
781
+ init.body = JSON.stringify({
782
+ content,
783
+ attachments: options.attachments ?? [],
784
+ metadata: options.metadata ?? {}
785
+ });
786
+ }
787
+ const res = await fetchImpl(typeof url === "string" ? url : url.toString(), init);
788
+ if (!res.ok) {
789
+ const text = await res.text().catch(() => "");
790
+ throw new TransportError(
791
+ `stream failed (${res.status}): ${text || res.statusText}`,
792
+ mapStatusToCode2(res.status)
793
+ );
794
+ }
795
+ const sideChannel = opts.onPydanticEvent;
796
+ if (!sideChannel) {
797
+ yield* parseSSE(res, {
798
+ signal: options.signal,
799
+ idleTimeoutMs: opts.idleTimeoutMs,
800
+ map: createPydanticAISSEMap()
801
+ });
802
+ return;
803
+ }
804
+ const toolIds = createToolIdQueue();
805
+ yield* parseSSE(res, {
806
+ signal: options.signal,
807
+ idleTimeoutMs: opts.idleTimeoutMs,
808
+ map: /* @__PURE__ */ __name((raw) => {
809
+ if (!raw.data) return null;
810
+ let parsed;
811
+ try {
812
+ parsed = JSON.parse(raw.data);
813
+ } catch {
814
+ return null;
815
+ }
816
+ try {
817
+ sideChannel(parsed);
818
+ } catch {
819
+ }
820
+ const out = [];
821
+ for (const evt of mapPydanticAIEvent(parsed, toolIds)) out.push(evt);
822
+ if (out.length === 0) return null;
823
+ if (out.length === 1) return out[0];
824
+ return out;
825
+ }, "map")
826
+ });
827
+ },
828
+ async send(sessionId, content, sendOpts) {
829
+ if (opts.send) return opts.send(sessionId, content, sendOpts);
830
+ throw new TransportError(
831
+ "Buffered send is not supported by this transport",
832
+ "unsupported"
833
+ );
834
+ },
835
+ async closeSession(sessionId) {
836
+ if (opts.closeSession) await opts.closeSession(sessionId);
837
+ }
838
+ };
839
+ }
840
+ __name(createPydanticAIChatTransport, "createPydanticAIChatTransport");
841
+ var SIZE_PX = { sm: 44, md: 56, lg: 64 };
842
+ var ICON_PX = { sm: 18, md: 22, lg: 26 };
843
+ function useEffectiveFABSize(size, inline) {
844
+ const isPhone = useIsPhone();
845
+ const isBelowDesktop = useIsTabletOrBelow();
846
+ if (size !== "responsive") return size;
847
+ if (inline) return "md";
848
+ if (isPhone) return "sm";
849
+ if (isBelowDesktop) return "md";
850
+ return "lg";
851
+ }
852
+ __name(useEffectiveFABSize, "useEffectiveFABSize");
853
+ function positionStyle(position, offset2) {
854
+ const [vert, horiz] = position.split("-");
855
+ return { [vert]: offset2, [horiz]: offset2 };
856
+ }
857
+ __name(positionStyle, "positionStyle");
858
+ function tooltipSideClasses(position) {
859
+ return position.endsWith("right") ? "right-full mr-3 origin-right" : "left-full ml-3 origin-left";
860
+ }
861
+ __name(tooltipSideClasses, "tooltipSideClasses");
862
+ function Badge({ value }) {
863
+ const display = value > 9 ? "9+" : String(value);
864
+ return /* @__PURE__ */ jsx(
865
+ "span",
866
+ {
867
+ "aria-hidden": "true",
868
+ className: cn(
869
+ "absolute -right-1 -top-1 inline-flex min-w-[18px] h-[18px] items-center justify-center",
870
+ "rounded-full bg-destructive px-1 text-[10px] font-semibold leading-none text-destructive-foreground",
871
+ "ring-2 ring-background"
872
+ ),
873
+ children: display
874
+ }
875
+ );
876
+ }
877
+ __name(Badge, "Badge");
878
+ function PulseDot() {
879
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "absolute right-1 top-1", children: /* @__PURE__ */ jsxs("span", { className: "relative inline-flex h-2.5 w-2.5", children: [
880
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-0 rounded-full bg-destructive opacity-75 animate-ping" }),
881
+ /* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full bg-destructive ring-2 ring-background" })
882
+ ] }) });
883
+ }
884
+ __name(PulseDot, "PulseDot");
885
+ function Tooltip({ text, side }) {
886
+ return /* @__PURE__ */ jsx(
887
+ "span",
888
+ {
889
+ role: "tooltip",
890
+ className: cn(
891
+ "pointer-events-none absolute top-1/2 -translate-y-1/2 whitespace-nowrap",
892
+ "rounded-md bg-popover px-2.5 py-1 text-xs font-medium text-popover-foreground shadow-md",
893
+ "border border-border opacity-0 scale-95 transition-all duration-150",
894
+ "group-hover:opacity-100 group-hover:scale-100",
895
+ "group-focus-within:opacity-100 group-focus-within:scale-100",
896
+ side
897
+ ),
898
+ children: text
899
+ }
900
+ );
901
+ }
902
+ __name(Tooltip, "Tooltip");
903
+ function ChatFAB({
904
+ onClick,
905
+ ariaLabel = "Open chat",
906
+ icon,
907
+ variant = "simple",
908
+ size = "responsive",
909
+ position = "bottom-right",
910
+ offset: offset2 = 24,
911
+ zIndex = 9999,
912
+ pulse = false,
913
+ badge,
914
+ tooltip,
915
+ inline = false,
916
+ className,
917
+ style
918
+ }) {
919
+ const effectiveSize = useEffectiveFABSize(size, inline);
920
+ const px = SIZE_PX[effectiveSize];
921
+ const iconPx = ICON_PX[effectiveSize];
922
+ const renderedIcon = icon ?? /* @__PURE__ */ jsx(Bot, { size: iconPx });
923
+ const baseButton = cn(
924
+ "relative grid place-items-center rounded-full focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
925
+ "transition-transform hover:scale-105"
926
+ );
927
+ return /* @__PURE__ */ jsxs(
928
+ "div",
929
+ {
930
+ className: cn("group", inline ? "relative inline-flex" : "fixed"),
931
+ style: inline ? void 0 : { ...positionStyle(position, offset2), zIndex },
932
+ children: [
933
+ variant === "animated" && /* @__PURE__ */ jsx(
934
+ AnimatedFAB,
935
+ {
936
+ ariaLabel,
937
+ onClick,
938
+ size: px,
939
+ className,
940
+ style,
941
+ children: renderedIcon
942
+ }
943
+ ),
944
+ variant === "glass" && /* @__PURE__ */ jsxs(
945
+ "button",
946
+ {
947
+ type: "button",
948
+ "aria-label": ariaLabel,
949
+ onClick,
950
+ className: cn(
951
+ baseButton,
952
+ "border border-border/40 bg-background/60 text-foreground shadow-lg backdrop-blur-xl",
953
+ "hover:bg-background/80",
954
+ className
955
+ ),
956
+ style: { width: px, height: px, ...style },
957
+ children: [
958
+ renderedIcon,
959
+ badge !== void 0 ? /* @__PURE__ */ jsx(Badge, { value: badge }) : pulse ? /* @__PURE__ */ jsx(PulseDot, {}) : null
960
+ ]
961
+ }
962
+ ),
963
+ variant === "simple" && /* @__PURE__ */ jsxs(
964
+ "button",
965
+ {
966
+ type: "button",
967
+ "aria-label": ariaLabel,
968
+ onClick,
969
+ className: cn(
970
+ baseButton,
971
+ "bg-primary text-primary-foreground hover:bg-primary/90 shadow-2xl",
972
+ className
973
+ ),
974
+ style: { width: px, height: px, ...style },
975
+ children: [
976
+ renderedIcon,
977
+ badge !== void 0 ? /* @__PURE__ */ jsx(Badge, { value: badge }) : pulse ? /* @__PURE__ */ jsx(PulseDot, {}) : null
978
+ ]
979
+ }
980
+ ),
981
+ tooltip && /* @__PURE__ */ jsx(Tooltip, { text: tooltip, side: tooltipSideClasses(position) })
982
+ ]
983
+ }
984
+ );
985
+ }
986
+ __name(ChatFAB, "ChatFAB");
987
+ function AnimatedFAB({ ariaLabel, onClick, size, className, style, children }) {
988
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
989
+ /* @__PURE__ */ jsx("style", { children: ANIMATED_CSS }),
990
+ /* @__PURE__ */ jsx(
991
+ "div",
992
+ {
993
+ className: cn("cmdop-fab-anim", className),
994
+ style: { width: size, height: size, ...style },
995
+ children: /* @__PURE__ */ jsx("div", { className: "cmdop-fab-anim-glow", children: /* @__PURE__ */ jsxs("div", { className: "cmdop-fab-anim-wrap", children: [
996
+ /* @__PURE__ */ jsx("div", { className: "cmdop-fab-anim-grad cmdop-fab-anim-grad-1" }),
997
+ /* @__PURE__ */ jsx("div", { className: "cmdop-fab-anim-grad cmdop-fab-anim-grad-2" }),
998
+ /* @__PURE__ */ jsx("div", { className: "cmdop-fab-anim-inner" }),
999
+ /* @__PURE__ */ jsx(
1000
+ "button",
1001
+ {
1002
+ type: "button",
1003
+ "aria-label": ariaLabel,
1004
+ onClick,
1005
+ className: "cmdop-fab-anim-btn",
1006
+ children: /* @__PURE__ */ jsx("span", { className: "cmdop-fab-anim-icon", children })
1007
+ }
1008
+ )
1009
+ ] }) })
1010
+ }
1011
+ )
1012
+ ] });
1013
+ }
1014
+ __name(AnimatedFAB, "AnimatedFAB");
1015
+ var ANIMATED_CSS = `
1016
+ .cmdop-fab-anim {
1017
+ position: relative;
1018
+ pointer-events: auto;
1019
+ }
1020
+ .cmdop-fab-anim-glow {
1021
+ width: 100%; height: 100%;
1022
+ border-radius: 50%;
1023
+ overflow: hidden;
1024
+ animation:
1025
+ cmdop-fab-entrance 0.6s cubic-bezier(0.34, 1.45, 0.64, 1) forwards,
1026
+ cmdop-fab-glow-shift 8s ease-in-out 0.6s infinite;
1027
+ }
1028
+ .cmdop-fab-anim-wrap {
1029
+ position: relative; width: 100%; height: 100%;
1030
+ border-radius: 50%; overflow: hidden;
1031
+ }
1032
+ .cmdop-fab-anim-grad { position: absolute; inset: 0; border-radius: 50%; }
1033
+ .cmdop-fab-anim-grad-1 {
1034
+ background: conic-gradient(
1035
+ from 0deg,
1036
+ #fbbf24 0%, rgba(251,191,36,0) 15%,
1037
+ rgba(168,85,247,0) 20%, #a855f7 35%, rgba(168,85,247,0) 50%,
1038
+ rgba(20,184,166,0) 55%, #14b8a6 70%, rgba(20,184,166,0) 85%,
1039
+ rgba(236,72,153,0) 88%, #ec4899 97%, #fbbf24 100%
1040
+ );
1041
+ animation: cmdop-fab-rotate 7s linear infinite;
1042
+ filter: blur(1px); opacity: 0.95;
1043
+ }
1044
+ .cmdop-fab-anim-grad-2 {
1045
+ inset: 1px;
1046
+ background: conic-gradient(
1047
+ from 180deg,
1048
+ #a855f7 0%, rgba(168,85,247,0) 20%,
1049
+ rgba(20,184,166,0) 30%, #14b8a6 50%, rgba(20,184,166,0) 70%,
1050
+ rgba(251,191,36,0) 75%, #fbbf24 95%, #a855f7 100%
1051
+ );
1052
+ animation: cmdop-fab-rotate-rev 9s linear infinite;
1053
+ filter: blur(0.75px); opacity: 0.7;
1054
+ }
1055
+ .cmdop-fab-anim-inner {
1056
+ position: absolute; inset: 3px; border-radius: 50%;
1057
+ background: rgba(10, 10, 10, 0.65);
1058
+ backdrop-filter: blur(12px) saturate(1.8);
1059
+ -webkit-backdrop-filter: blur(12px) saturate(1.8);
1060
+ animation: cmdop-fab-inner-glow 5s ease-in-out infinite;
1061
+ }
1062
+ .cmdop-fab-anim-btn {
1063
+ position: absolute; inset: 2px;
1064
+ border-radius: 50%; border: none; background: transparent;
1065
+ cursor: pointer; display: flex; align-items: center; justify-content: center;
1066
+ transition: transform 0.2s;
1067
+ }
1068
+ .cmdop-fab-anim-btn:hover { transform: scale(1.06); }
1069
+ .cmdop-fab-anim-icon {
1070
+ color: #fbbf24; display: flex;
1071
+ filter: drop-shadow(0 0 6px rgba(251,191,36,0.8));
1072
+ animation: cmdop-fab-icon-pulse 2.5s ease-in-out infinite;
1073
+ }
1074
+ @keyframes cmdop-fab-rotate { to { transform: rotate(360deg); } }
1075
+ @keyframes cmdop-fab-rotate-rev { to { transform: rotate(-360deg); } }
1076
+ @keyframes cmdop-fab-entrance {
1077
+ 0% { transform: scale(0); }
1078
+ 50% { transform: scale(1.08); }
1079
+ 70% { transform: scale(0.98); }
1080
+ 100% { transform: scale(1); }
1081
+ }
1082
+ @keyframes cmdop-fab-glow-shift {
1083
+ 0%, 100% { box-shadow: 0 0 20px rgba(251,191,36,0.5), 0 0 40px rgba(168,85,247,0.3); }
1084
+ 33% { box-shadow: 0 0 20px rgba(168,85,247,0.5), 0 0 40px rgba(20,184,166,0.3); }
1085
+ 66% { box-shadow: 0 0 20px rgba(20,184,166,0.5), 0 0 40px rgba(236,72,153,0.3); }
1086
+ }
1087
+ @keyframes cmdop-fab-icon-pulse {
1088
+ 0%, 100% { opacity: 1; transform: scale(1); }
1089
+ 50% { opacity: 0.85; transform: scale(1.15); }
1090
+ }
1091
+ @keyframes cmdop-fab-inner-glow {
1092
+ 0%, 100% { box-shadow: inset 0 0 20px rgba(251,191,36,0.25), inset 0 0 40px rgba(168,85,247,0.15); }
1093
+ 50% { box-shadow: inset 0 0 25px rgba(168,85,247,0.3), inset 0 0 45px rgba(20,184,166,0.2); }
1094
+ }
1095
+ `;
1096
+ function ChatHeader({
1097
+ title,
1098
+ icon,
1099
+ actions,
1100
+ showClose = true,
1101
+ onClose,
1102
+ closeLabel = "Close",
1103
+ closeSlot,
1104
+ className
1105
+ }) {
1106
+ return /* @__PURE__ */ jsxs(
1107
+ "header",
1108
+ {
1109
+ className: cn(
1110
+ "border-border bg-muted/30 flex shrink-0 items-center justify-between border-b px-4 py-2.5",
1111
+ className
1112
+ ),
1113
+ children: [
1114
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2 text-sm font-semibold", children: [
1115
+ icon ?? /* @__PURE__ */ jsx(Bot, { className: "text-primary h-4 w-4 shrink-0" }),
1116
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: title })
1117
+ ] }),
1118
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", children: [
1119
+ actions,
1120
+ closeSlot ?? (showClose && onClose && /* @__PURE__ */ jsx(
1121
+ Button,
1122
+ {
1123
+ variant: "ghost",
1124
+ size: "sm",
1125
+ onClick: onClose,
1126
+ "aria-label": closeLabel,
1127
+ className: "-mr-1 h-7 w-7 p-0",
1128
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
1129
+ }
1130
+ ))
1131
+ ] })
1132
+ ]
1133
+ }
1134
+ );
1135
+ }
1136
+ __name(ChatHeader, "ChatHeader");
1137
+ function useChatPresence(open, exitDurationMs = 200) {
1138
+ const [phase, setPhase] = useState("hidden");
1139
+ const timerRef = useRef(null);
1140
+ useEffect(() => {
1141
+ if (timerRef.current) clearTimeout(timerRef.current);
1142
+ if (open) {
1143
+ setPhase("entering");
1144
+ timerRef.current = setTimeout(() => setPhase("visible"), 16);
1145
+ } else {
1146
+ setPhase("leaving");
1147
+ timerRef.current = setTimeout(() => setPhase("hidden"), exitDurationMs);
1148
+ }
1149
+ return () => {
1150
+ if (timerRef.current) clearTimeout(timerRef.current);
1151
+ };
1152
+ }, [open, exitDurationMs]);
1153
+ return phase;
1154
+ }
1155
+ __name(useChatPresence, "useChatPresence");
1156
+ function dockPositionStyle(position, horizontal, vertical) {
1157
+ const [vert, horiz] = position.split("-");
1158
+ return { [vert]: vertical, [horiz]: horizontal };
1159
+ }
1160
+ __name(dockPositionStyle, "dockPositionStyle");
1161
+ function ChatDock({
1162
+ open,
1163
+ onClose,
1164
+ children,
1165
+ mode = "popover",
1166
+ side = "right",
1167
+ title = "Chat",
1168
+ icon,
1169
+ headerActions,
1170
+ hideHeader = false,
1171
+ closeLabel,
1172
+ width,
1173
+ height = 720,
1174
+ position = "bottom-right",
1175
+ offset: offset2,
1176
+ exitDurationMs = 200,
1177
+ zIndex = 1e4,
1178
+ ariaLabel,
1179
+ className,
1180
+ mobileFullscreen = true,
1181
+ disablePortal = false,
1182
+ inline = false,
1183
+ reserveBodySpace
1184
+ }) {
1185
+ const phase = useChatPresence(open, exitDurationMs);
1186
+ const isMobile = useIsMobile();
1187
+ const isBelowDesktop = useIsTabletOrBelow();
1188
+ const effectiveMode = mode === "side" && !isBelowDesktop ? "side" : "popover";
1189
+ const fullscreen = mobileFullscreen && isMobile;
1190
+ const wantsReserve = !inline && !fullscreen && effectiveMode === "side" && (reserveBodySpace ?? true);
1191
+ const resolvedSideWidth = width ?? 420;
1192
+ useEffect(() => {
1193
+ if (!wantsReserve || phase === "hidden") return;
1194
+ const body = document.body;
1195
+ if (!body) return;
1196
+ const cssVar = `${resolvedSideWidth}px`;
1197
+ const padKey = side === "right" ? "paddingRight" : "paddingLeft";
1198
+ const prevPad = body.style[padKey];
1199
+ const prevVar = body.style.getPropertyValue("--chat-dock-reserve");
1200
+ body.style[padKey] = cssVar;
1201
+ body.style.setProperty("--chat-dock-reserve", cssVar);
1202
+ return () => {
1203
+ body.style[padKey] = prevPad;
1204
+ if (prevVar) body.style.setProperty("--chat-dock-reserve", prevVar);
1205
+ else body.style.removeProperty("--chat-dock-reserve");
1206
+ };
1207
+ }, [wantsReserve, phase, side, resolvedSideWidth]);
1208
+ if (phase === "hidden") return null;
1209
+ const animating = phase === "entering" || phase === "leaving";
1210
+ const horizontal = offset2?.horizontal ?? 24;
1211
+ const vertical = offset2?.vertical ?? 96;
1212
+ const resolvedWidth = width ?? (effectiveMode === "side" ? resolvedSideWidth : 480);
1213
+ let containerStyle;
1214
+ let cornerClass;
1215
+ const dynVH = "100dvh";
1216
+ if (inline) {
1217
+ containerStyle = {
1218
+ position: "relative",
1219
+ width: resolvedWidth,
1220
+ height,
1221
+ maxHeight: `calc(${dynVH} - 16px)`,
1222
+ pointerEvents: phase === "visible" ? "auto" : "none"
1223
+ };
1224
+ cornerClass = "rounded-xl border";
1225
+ } else if (fullscreen) {
1226
+ containerStyle = {
1227
+ position: "fixed",
1228
+ top: 0,
1229
+ [side === "left" ? "left" : "right"]: 0,
1230
+ width: "100vw",
1231
+ height: dynVH,
1232
+ zIndex,
1233
+ pointerEvents: phase === "visible" ? "auto" : "none"
1234
+ };
1235
+ cornerClass = "rounded-none border-0";
1236
+ } else if (effectiveMode === "side") {
1237
+ containerStyle = {
1238
+ position: "fixed",
1239
+ top: 0,
1240
+ [side]: 0,
1241
+ height: dynVH,
1242
+ zIndex,
1243
+ width: `min(${resolvedWidth}px, 100vw)`,
1244
+ pointerEvents: phase === "visible" ? "auto" : "none"
1245
+ };
1246
+ cornerClass = side === "right" ? "rounded-none border-l" : "rounded-none border-r";
1247
+ } else {
1248
+ const heightCap = `calc(${dynVH} - ${vertical + 24}px)`;
1249
+ containerStyle = {
1250
+ position: "fixed",
1251
+ ...dockPositionStyle(position, horizontal, vertical),
1252
+ zIndex,
1253
+ width: `min(${resolvedWidth}px, calc(100vw - 32px))`,
1254
+ height: `min(${height}px, ${heightCap})`,
1255
+ minHeight: `min(320px, ${heightCap})`,
1256
+ pointerEvents: phase === "visible" ? "auto" : "none"
1257
+ };
1258
+ cornerClass = "rounded-xl border";
1259
+ }
1260
+ const enterClass = (() => {
1261
+ if (fullscreen) return "opacity-0";
1262
+ if (effectiveMode === "side") {
1263
+ return side === "right" ? "opacity-0 translate-x-4" : "opacity-0 -translate-x-4";
1264
+ }
1265
+ return "opacity-0 scale-95 translate-y-2";
1266
+ })();
1267
+ const visibleClass = "opacity-100 scale-100 translate-y-0 translate-x-0";
1268
+ return /* @__PURE__ */ jsx(Portal, { disablePortal: disablePortal || inline, children: /* @__PURE__ */ jsxs(
1269
+ "div",
1270
+ {
1271
+ role: "dialog",
1272
+ "aria-label": ariaLabel ?? (typeof title === "string" ? title : "Chat"),
1273
+ "aria-hidden": phase === "leaving",
1274
+ className: cn(
1275
+ "bg-popover text-popover-foreground border-border",
1276
+ "flex flex-col overflow-hidden shadow-2xl",
1277
+ cornerClass,
1278
+ "transition-all duration-200 ease-out",
1279
+ animating ? enterClass : visibleClass,
1280
+ className
1281
+ ),
1282
+ style: containerStyle,
1283
+ children: [
1284
+ !hideHeader && /* @__PURE__ */ jsx(
1285
+ ChatHeader,
1286
+ {
1287
+ title,
1288
+ icon,
1289
+ actions: headerActions,
1290
+ onClose,
1291
+ closeLabel
1292
+ }
1293
+ ),
1294
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 min-w-0 flex-1 overflow-hidden", children })
1295
+ ]
1296
+ }
1297
+ ) });
1298
+ }
1299
+ __name(ChatDock, "ChatDock");
1300
+ var ChatHeaderActionButton = forwardRef(
1301
+ /* @__PURE__ */ __name(function ChatHeaderActionButton2({ icon, ariaLabel, badge, destructive, loading, disabled, className, ...rest }, ref) {
1302
+ return /* @__PURE__ */ jsxs(
1303
+ "button",
1304
+ {
1305
+ ref,
1306
+ type: "button",
1307
+ "aria-label": ariaLabel,
1308
+ title: ariaLabel,
1309
+ disabled: disabled || loading,
1310
+ className: cn(
1311
+ "relative inline-flex h-7 w-7 items-center justify-center rounded-md",
1312
+ "text-muted-foreground transition-colors",
1313
+ "hover:bg-accent hover:text-foreground",
1314
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1315
+ "disabled:opacity-50 disabled:cursor-not-allowed",
1316
+ destructive && "hover:bg-destructive/15 hover:text-destructive",
1317
+ loading && "animate-pulse",
1318
+ className
1319
+ ),
1320
+ ...rest,
1321
+ children: [
1322
+ icon,
1323
+ badge !== void 0 && /* @__PURE__ */ jsx(
1324
+ "span",
1325
+ {
1326
+ "aria-hidden": "true",
1327
+ 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",
1328
+ children: badge > 9 ? "9+" : badge
1329
+ }
1330
+ )
1331
+ ]
1332
+ }
1333
+ );
1334
+ }, "ChatHeaderActionButton")
1335
+ );
1336
+ function ChatHeaderModeToggle({
1337
+ mode,
1338
+ onToggle,
1339
+ expandLabel = "Dock to side",
1340
+ collapseLabel = "Back to popover",
1341
+ forceVisible = false
1342
+ }) {
1343
+ const isBelowDesktop = useIsTabletOrBelow();
1344
+ if (isBelowDesktop && !forceVisible) return null;
1345
+ const isSide = mode === "side";
1346
+ return /* @__PURE__ */ jsx(
1347
+ ChatHeaderActionButton,
1348
+ {
1349
+ icon: isSide ? /* @__PURE__ */ jsx(PanelRightClose, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(PanelRightOpen, { className: "h-3.5 w-3.5" }),
1350
+ ariaLabel: isSide ? collapseLabel : expandLabel,
1351
+ onClick: onToggle
1352
+ }
1353
+ );
1354
+ }
1355
+ __name(ChatHeaderModeToggle, "ChatHeaderModeToggle");
1356
+ function ChatHeaderAudioToggle({
1357
+ muted,
1358
+ onToggle,
1359
+ unmuteLabel = "Unmute notifications",
1360
+ muteLabel = "Mute notifications"
1361
+ }) {
1362
+ return /* @__PURE__ */ jsx(
1363
+ ChatHeaderActionButton,
1364
+ {
1365
+ icon: muted ? /* @__PURE__ */ jsx(VolumeX, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Volume2, { className: "h-3.5 w-3.5" }),
1366
+ ariaLabel: muted ? unmuteLabel : muteLabel,
1367
+ onClick: onToggle
1368
+ }
1369
+ );
1370
+ }
1371
+ __name(ChatHeaderAudioToggle, "ChatHeaderAudioToggle");
1372
+ function useChatReset(opts) {
1373
+ const { onReset, onSuccess, onError } = opts;
1374
+ const [isResetting, setIsResetting] = useState(false);
1375
+ const reset = useCallback(async () => {
1376
+ if (isResetting) return false;
1377
+ setIsResetting(true);
1378
+ try {
1379
+ const ok = await onReset();
1380
+ if (ok) onSuccess?.();
1381
+ else onError?.();
1382
+ return ok;
1383
+ } catch (err) {
1384
+ onError?.(err);
1385
+ return false;
1386
+ } finally {
1387
+ setIsResetting(false);
1388
+ }
1389
+ }, [isResetting, onReset, onSuccess, onError]);
1390
+ return { reset, isResetting };
1391
+ }
1392
+ __name(useChatReset, "useChatReset");
1393
+ var DEFAULT_TITLE = "Clear conversation?";
1394
+ var DEFAULT_MESSAGE = "The assistant will forget this session and start a new one. This cannot be undone.";
1395
+ var DEFAULT_LABEL = "Clear conversation";
1396
+ function ChatHeaderResetButton({
1397
+ onReset,
1398
+ onSuccess,
1399
+ onError,
1400
+ confirm = true,
1401
+ confirmTitle = DEFAULT_TITLE,
1402
+ confirmMessage = DEFAULT_MESSAGE,
1403
+ ariaLabel = DEFAULT_LABEL
1404
+ }) {
1405
+ const { reset, isResetting } = useChatReset({ onReset, onSuccess, onError });
1406
+ const handleClick = /* @__PURE__ */ __name(async () => {
1407
+ if (confirm) {
1408
+ const api = typeof window !== "undefined" ? window.dialog : void 0;
1409
+ if (api?.confirm) {
1410
+ const ok = await api.confirm({
1411
+ title: confirmTitle,
1412
+ message: confirmMessage,
1413
+ variant: "destructive",
1414
+ confirmText: "Clear",
1415
+ cancelText: "Cancel"
1416
+ });
1417
+ if (!ok) return;
1418
+ } else if (typeof window !== "undefined" && typeof window.confirm === "function") {
1419
+ const ok = window.confirm(`${confirmTitle}
1420
+
1421
+ ${confirmMessage}`);
1422
+ if (!ok) return;
1423
+ }
1424
+ }
1425
+ await reset();
1426
+ }, "handleClick");
1427
+ return /* @__PURE__ */ jsx(
1428
+ ChatHeaderActionButton,
1429
+ {
1430
+ icon: /* @__PURE__ */ jsx(RotateCcw, { className: "h-3.5 w-3.5" }),
1431
+ ariaLabel,
1432
+ onClick: handleClick,
1433
+ loading: isResetting,
1434
+ destructive: true
1435
+ }
1436
+ );
1437
+ }
1438
+ __name(ChatHeaderResetButton, "ChatHeaderResetButton");
1439
+
1440
+ // src/tools/SpeechRecognition/core/languages-catalog.ts
1441
+ var WEB_SPEECH_LANGUAGES = [
1442
+ { name: "Afrikaans", iso: "af", englishName: "afrikaans", dialects: [{ code: "af-ZA", region: "South Africa" }] },
1443
+ { name: "\u12A0\u121B\u122D\u129B", iso: "am", englishName: "amharic", dialects: [{ code: "am-ET", region: "Ethiopia" }] },
1444
+ { name: "Az\u0259rbaycanca", iso: "az", englishName: "azerbaijani", dialects: [{ code: "az-AZ", region: "Azerbaijan" }] },
1445
+ {
1446
+ name: "\u09AC\u09BE\u0982\u09B2\u09BE",
1447
+ iso: "bn",
1448
+ englishName: "bengali",
1449
+ dialects: [
1450
+ { code: "bn-BD", region: "Bangladesh" },
1451
+ { code: "bn-IN", region: "India" }
1452
+ ]
1453
+ },
1454
+ { name: "Bahasa Indonesia", iso: "id", englishName: "indonesian", dialects: [{ code: "id-ID", region: "Indonesia" }] },
1455
+ { name: "Bahasa Melayu", iso: "ms", englishName: "malay", dialects: [{ code: "ms-MY", region: "Malaysia" }] },
1456
+ { name: "Catal\xE0", iso: "ca", englishName: "catalan", dialects: [{ code: "ca-ES", region: "Spain" }] },
1457
+ { name: "\u010Ce\u0161tina", iso: "cs", englishName: "czech", dialects: [{ code: "cs-CZ", region: "Czechia" }] },
1458
+ { name: "Dansk", iso: "da", englishName: "danish", dialects: [{ code: "da-DK", region: "Denmark" }] },
1459
+ { name: "Deutsch", iso: "de", englishName: "german", dialects: [{ code: "de-DE", region: "Germany" }] },
1460
+ {
1461
+ name: "English",
1462
+ iso: "en",
1463
+ englishName: "english",
1464
+ dialects: [
1465
+ { code: "en-US", region: "United States" },
1466
+ { code: "en-GB", region: "United Kingdom" },
1467
+ { code: "en-AU", region: "Australia" },
1468
+ { code: "en-CA", region: "Canada" },
1469
+ { code: "en-IN", region: "India" },
1470
+ { code: "en-NZ", region: "New Zealand" },
1471
+ { code: "en-PH", region: "Philippines" },
1472
+ { code: "en-ZA", region: "South Africa" },
1473
+ { code: "en-NG", region: "Nigeria" },
1474
+ { code: "en-GH", region: "Ghana" },
1475
+ { code: "en-KE", region: "Kenya" },
1476
+ { code: "en-TZ", region: "Tanzania" }
1477
+ ]
1478
+ },
1479
+ {
1480
+ name: "Espa\xF1ol",
1481
+ iso: "es",
1482
+ englishName: "spanish",
1483
+ dialects: [
1484
+ { code: "es-ES", region: "Espa\xF1a" },
1485
+ { code: "es-MX", region: "M\xE9xico" },
1486
+ { code: "es-US", region: "Estados Unidos" },
1487
+ { code: "es-AR", region: "Argentina" },
1488
+ { code: "es-CL", region: "Chile" },
1489
+ { code: "es-CO", region: "Colombia" },
1490
+ { code: "es-PE", region: "Per\xFA" },
1491
+ { code: "es-VE", region: "Venezuela" },
1492
+ { code: "es-EC", region: "Ecuador" },
1493
+ { code: "es-GT", region: "Guatemala" },
1494
+ { code: "es-CR", region: "Costa Rica" },
1495
+ { code: "es-PA", region: "Panam\xE1" },
1496
+ { code: "es-DO", region: "Rep. Dominicana" },
1497
+ { code: "es-UY", region: "Uruguay" },
1498
+ { code: "es-PY", region: "Paraguay" },
1499
+ { code: "es-BO", region: "Bolivia" },
1500
+ { code: "es-SV", region: "El Salvador" },
1501
+ { code: "es-HN", region: "Honduras" },
1502
+ { code: "es-NI", region: "Nicaragua" },
1503
+ { code: "es-PR", region: "Puerto Rico" }
1504
+ ]
1505
+ },
1506
+ { name: "Euskara", iso: "eu", englishName: "basque", dialects: [{ code: "eu-ES", region: "Spain" }] },
1507
+ { name: "Filipino", iso: "fil", englishName: "filipino tagalog", dialects: [{ code: "fil-PH", region: "Philippines" }] },
1508
+ { name: "Fran\xE7ais", iso: "fr", englishName: "french", dialects: [{ code: "fr-FR", region: "France" }] },
1509
+ { name: "Basa Jawa", iso: "jv", englishName: "javanese", dialects: [{ code: "jv-ID", region: "Indonesia" }] },
1510
+ { name: "Galego", iso: "gl", englishName: "galician", dialects: [{ code: "gl-ES", region: "Spain" }] },
1511
+ { name: "\u0A97\u0AC1\u0A9C\u0AB0\u0ABE\u0AA4\u0AC0", iso: "gu", englishName: "gujarati", dialects: [{ code: "gu-IN", region: "India" }] },
1512
+ { name: "Hrvatski", iso: "hr", englishName: "croatian", dialects: [{ code: "hr-HR", region: "Croatia" }] },
1513
+ { name: "IsiZulu", iso: "zu", englishName: "zulu", dialects: [{ code: "zu-ZA", region: "South Africa" }] },
1514
+ { name: "\xCDslenska", iso: "is", englishName: "icelandic", dialects: [{ code: "is-IS", region: "Iceland" }] },
1515
+ {
1516
+ name: "Italiano",
1517
+ iso: "it",
1518
+ englishName: "italian",
1519
+ dialects: [
1520
+ { code: "it-IT", region: "Italia" },
1521
+ { code: "it-CH", region: "Svizzera" }
1522
+ ]
1523
+ },
1524
+ { name: "\u0C95\u0CA8\u0CCD\u0CA8\u0CA1", iso: "kn", englishName: "kannada", dialects: [{ code: "kn-IN", region: "India" }] },
1525
+ { name: "\u1797\u17B6\u179F\u17B6\u1781\u17D2\u1798\u17C2\u179A", iso: "km", englishName: "khmer cambodian", dialects: [{ code: "km-KH", region: "Cambodia" }] },
1526
+ { name: "Latvie\u0161u", iso: "lv", englishName: "latvian", dialects: [{ code: "lv-LV", region: "Latvia" }] },
1527
+ { name: "Lietuvi\u0173", iso: "lt", englishName: "lithuanian", dialects: [{ code: "lt-LT", region: "Lithuania" }] },
1528
+ { name: "\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D02", iso: "ml", englishName: "malayalam", dialects: [{ code: "ml-IN", region: "India" }] },
1529
+ { name: "\u092E\u0930\u093E\u0920\u0940", iso: "mr", englishName: "marathi", dialects: [{ code: "mr-IN", region: "India" }] },
1530
+ { name: "Magyar", iso: "hu", englishName: "hungarian", dialects: [{ code: "hu-HU", region: "Hungary" }] },
1531
+ { name: "\u0EA5\u0EB2\u0EA7", iso: "lo", englishName: "lao laotian", dialects: [{ code: "lo-LA", region: "Laos" }] },
1532
+ { name: "Nederlands", iso: "nl", englishName: "dutch", dialects: [{ code: "nl-NL", region: "Netherlands" }] },
1533
+ { name: "\u0928\u0947\u092A\u093E\u0932\u0940 \u092D\u093E\u0937\u093E", iso: "ne", englishName: "nepali", dialects: [{ code: "ne-NP", region: "Nepal" }] },
1534
+ { name: "Norsk bokm\xE5l", iso: "nb", englishName: "norwegian bokmal", dialects: [{ code: "nb-NO", region: "Norway" }] },
1535
+ { name: "Polski", iso: "pl", englishName: "polish", dialects: [{ code: "pl-PL", region: "Poland" }] },
1536
+ {
1537
+ name: "Portugu\xEAs",
1538
+ iso: "pt",
1539
+ englishName: "portuguese",
1540
+ dialects: [
1541
+ { code: "pt-BR", region: "Brasil" },
1542
+ { code: "pt-PT", region: "Portugal" }
1543
+ ]
1544
+ },
1545
+ { name: "Rom\xE2n\u0103", iso: "ro", englishName: "romanian", dialects: [{ code: "ro-RO", region: "Romania" }] },
1546
+ { name: "\u0DC3\u0DD2\u0D82\u0DC4\u0DBD", iso: "si", englishName: "sinhala sinhalese", dialects: [{ code: "si-LK", region: "Sri Lanka" }] },
1547
+ { name: "Sloven\u0161\u010Dina", iso: "sl", englishName: "slovenian", dialects: [{ code: "sl-SI", region: "Slovenia" }] },
1548
+ { name: "Basa Sunda", iso: "su", englishName: "sundanese", dialects: [{ code: "su-ID", region: "Indonesia" }] },
1549
+ { name: "Sloven\u010Dina", iso: "sk", englishName: "slovak", dialects: [{ code: "sk-SK", region: "Slovakia" }] },
1550
+ { name: "Suomi", iso: "fi", englishName: "finnish", dialects: [{ code: "fi-FI", region: "Finland" }] },
1551
+ { name: "Svenska", iso: "sv", englishName: "swedish", dialects: [{ code: "sv-SE", region: "Sweden" }] },
1552
+ {
1553
+ name: "Kiswahili",
1554
+ iso: "sw",
1555
+ englishName: "swahili",
1556
+ dialects: [
1557
+ { code: "sw-TZ", region: "Tanzania" },
1558
+ { code: "sw-KE", region: "Kenya" }
1559
+ ]
1560
+ },
1561
+ { name: "\u10E5\u10D0\u10E0\u10D7\u10E3\u10DA\u10D8", iso: "ka", englishName: "georgian", dialects: [{ code: "ka-GE", region: "Georgia" }] },
1562
+ { name: "\u0540\u0561\u0575\u0565\u0580\u0565\u0576", iso: "hy", englishName: "armenian", dialects: [{ code: "hy-AM", region: "Armenia" }] },
1563
+ {
1564
+ name: "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD",
1565
+ iso: "ta",
1566
+ englishName: "tamil",
1567
+ dialects: [
1568
+ { code: "ta-IN", region: "\u0B87\u0BA8\u0BCD\u0BA4\u0BBF\u0BAF\u0BBE" },
1569
+ { code: "ta-SG", region: "\u0B9A\u0BBF\u0B99\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0BC2\u0BB0\u0BCD" },
1570
+ { code: "ta-LK", region: "\u0B87\u0BB2\u0B99\u0BCD\u0B95\u0BC8" },
1571
+ { code: "ta-MY", region: "\u0BAE\u0BB2\u0BC7\u0B9A\u0BBF\u0BAF\u0BBE" }
1572
+ ]
1573
+ },
1574
+ { name: "\u0C24\u0C46\u0C32\u0C41\u0C17\u0C41", iso: "te", englishName: "telugu", dialects: [{ code: "te-IN", region: "India" }] },
1575
+ { name: "Ti\u1EBFng Vi\u1EC7t", iso: "vi", englishName: "vietnamese", dialects: [{ code: "vi-VN", region: "Vietnam" }] },
1576
+ { name: "T\xFCrk\xE7e", iso: "tr", englishName: "turkish", dialects: [{ code: "tr-TR", region: "T\xFCrkiye" }] },
1577
+ {
1578
+ name: "\u0627\u064F\u0631\u062F\u064F\u0648",
1579
+ iso: "ur",
1580
+ englishName: "urdu",
1581
+ dialects: [
1582
+ { code: "ur-PK", region: "\u067E\u0627\u06A9\u0633\u062A\u0627\u0646" },
1583
+ { code: "ur-IN", region: "\u0628\u06BE\u0627\u0631\u062A" }
1584
+ ]
1585
+ },
1586
+ { name: "\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC", iso: "el", englishName: "greek", dialects: [{ code: "el-GR", region: "Greece" }] },
1587
+ { name: "\u0431\u044A\u043B\u0433\u0430\u0440\u0441\u043A\u0438", iso: "bg", englishName: "bulgarian", dialects: [{ code: "bg-BG", region: "Bulgaria" }] },
1588
+ { name: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439", iso: "ru", englishName: "russian", dialects: [{ code: "ru-RU", region: "Russia" }] },
1589
+ { name: "\u0421\u0440\u043F\u0441\u043A\u0438", iso: "sr", englishName: "serbian", dialects: [{ code: "sr-RS", region: "Serbia" }] },
1590
+ { name: "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430", iso: "uk", englishName: "ukrainian", dialects: [{ code: "uk-UA", region: "Ukraine" }] },
1591
+ { name: "\uD55C\uAD6D\uC5B4", iso: "ko", englishName: "korean", dialects: [{ code: "ko-KR", region: "Korea" }] },
1592
+ {
1593
+ name: "\u4E2D\u6587",
1594
+ iso: "cmn",
1595
+ englishName: "chinese mandarin cantonese",
1596
+ dialects: [
1597
+ { code: "cmn-Hans-CN", region: "\u666E\u901A\u8BDD (\u4E2D\u56FD\u5927\u9646)" },
1598
+ { code: "cmn-Hans-HK", region: "\u666E\u901A\u8BDD (\u9999\u6E2F)" },
1599
+ { code: "cmn-Hant-TW", region: "\u4E2D\u6587 (\u53F0\u7063)" },
1600
+ { code: "yue-Hant-HK", region: "\u7CB5\u8A9E (\u9999\u6E2F)" }
1601
+ ]
1602
+ },
1603
+ { name: "\u65E5\u672C\u8A9E", iso: "ja", englishName: "japanese", dialects: [{ code: "ja-JP", region: "Japan" }] },
1604
+ { name: "\u0939\u093F\u0928\u094D\u0926\u0940", iso: "hi", englishName: "hindi", dialects: [{ code: "hi-IN", region: "India" }] },
1605
+ { name: "\u0E20\u0E32\u0E29\u0E32\u0E44\u0E17\u0E22", iso: "th", englishName: "thai", dialects: [{ code: "th-TH", region: "Thailand" }] }
1606
+ ];
1607
+ WEB_SPEECH_LANGUAGES.flatMap(
1608
+ (l) => l.dialects.map((d) => d.code)
1609
+ );
1610
+ function findSpeechLanguage(tag) {
1611
+ if (!tag) return null;
1612
+ const lower = tag.toLowerCase();
1613
+ for (const language of WEB_SPEECH_LANGUAGES) {
1614
+ for (const dialect of language.dialects) {
1615
+ if (dialect.code.toLowerCase() === lower) return { language, dialect };
1616
+ }
1617
+ }
1618
+ return null;
1619
+ }
1620
+ __name(findSpeechLanguage, "findSpeechLanguage");
1621
+ function countryFromTag(tag) {
1622
+ if (!tag) return null;
1623
+ const parts = tag.split("-");
1624
+ for (let i = parts.length - 1; i >= 0; i -= 1) {
1625
+ const p = parts[i];
1626
+ if (p.length === 2 && /^[A-Za-z]{2}$/.test(p)) return p.toUpperCase();
1627
+ }
1628
+ return null;
1629
+ }
1630
+ __name(countryFromTag, "countryFromTag");
1631
+ createContext(null);
1632
+ createLazyComponent(
1633
+ () => import('./DictationField-U25MEYAL.mjs').then((mod) => ({
1634
+ default: mod.DictationField
1635
+ })),
1636
+ {
1637
+ displayName: "LazyDictationField",
1638
+ fallback: /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border/60 bg-card px-3 py-2 text-xs text-muted-foreground", children: "Loading dictation\u2026" })
1639
+ }
1640
+ );
1641
+ function ChatHeaderLanguageButton({
1642
+ ariaLabel = "Speech language",
1643
+ allowedTags,
1644
+ hideFallbackIcon,
1645
+ className
1646
+ }) {
1647
+ const prefs = useSpeechPrefs();
1648
+ const active = useResolvedLanguage();
1649
+ const options = useMemo(() => {
1650
+ const allow = allowedTags ? new Set(allowedTags) : null;
1651
+ const out = [];
1652
+ for (const lang of WEB_SPEECH_LANGUAGES) {
1653
+ for (const d of lang.dialects) {
1654
+ if (allow && !allow.has(d.code)) continue;
1655
+ out.push({
1656
+ value: d.code,
1657
+ // "Русский" / "Español — Argentina" / "English — United States"
1658
+ label: lang.dialects.length === 1 ? lang.name : `${lang.name} \u2014 ${d.region}`,
1659
+ // Search-only index: English name, BCP-47 tag, ISO, region.
1660
+ // Lets users type "russian" / "ru-RU" / "ru" / "argentina"
1661
+ // and still find the row regardless of native script.
1662
+ description: `${lang.englishName} ${d.code} ${lang.iso} ${d.region}`.toLowerCase()
1663
+ });
1664
+ }
1665
+ }
1666
+ return out;
1667
+ }, [allowedTags]);
1668
+ return /* @__PURE__ */ jsx(
1669
+ Combobox,
1670
+ {
1671
+ options,
1672
+ value: prefs.language ?? active,
1673
+ onValueChange: (v) => prefs.setLanguage(v || null),
1674
+ placeholder: ariaLabel,
1675
+ searchPlaceholder: "Search language\u2026",
1676
+ filterFunction: (opt, search) => {
1677
+ const s = search.toLowerCase();
1678
+ return opt.label.toLowerCase().includes(s) || opt.value.toLowerCase().includes(s) || (opt.description?.includes(s) ?? false);
1679
+ },
1680
+ contentClassName: "w-[280px]",
1681
+ contentStyle: { zIndex: 10001 },
1682
+ renderOption: (option) => {
1683
+ const country = countryFromTag(option.value);
1684
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
1685
+ country ? /* @__PURE__ */ jsx(
1686
+ Flag,
1687
+ {
1688
+ countryCode: country,
1689
+ className: "h-4 w-5 shrink-0 overflow-hidden rounded-[2px] border border-border/60 ring-1 ring-black/5"
1690
+ }
1691
+ ) : /* @__PURE__ */ jsx(Globe, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
1692
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm", children: option.label })
1693
+ ] });
1694
+ },
1695
+ renderTrigger: (selected, open) => {
1696
+ const tag = selected?.value ?? active;
1697
+ const country = countryFromTag(tag);
1698
+ const found = findSpeechLanguage(tag);
1699
+ const tooltipLabel = found ? `${found.language.name}${found.language.dialects.length > 1 ? ` \u2014 ${found.dialect.region}` : ""} \xB7 ${tag}` : tag;
1700
+ return /* @__PURE__ */ jsx(
1701
+ "button",
1702
+ {
1703
+ type: "button",
1704
+ "aria-label": `${ariaLabel}: ${tooltipLabel}`,
1705
+ "aria-expanded": open,
1706
+ title: tooltipLabel,
1707
+ className: cn(
1708
+ "inline-flex h-7 w-7 items-center justify-center rounded-md",
1709
+ "text-muted-foreground transition-colors",
1710
+ "hover:bg-accent hover:text-foreground",
1711
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1712
+ className
1713
+ ),
1714
+ children: country ? /* @__PURE__ */ jsx(
1715
+ Flag,
1716
+ {
1717
+ countryCode: country,
1718
+ className: "h-4 w-5 overflow-hidden rounded-[2px] border border-border/60 ring-1 ring-black/5"
1719
+ }
1720
+ ) : hideFallbackIcon ? null : /* @__PURE__ */ jsx(Globe, { className: "h-3.5 w-3.5", "aria-hidden": true })
1721
+ }
1722
+ );
1723
+ }
1724
+ }
1725
+ );
1726
+ }
1727
+ __name(ChatHeaderLanguageButton, "ChatHeaderLanguageButton");
1728
+ function anchorStyle(position, fabOffset, fabClearance) {
1729
+ const [vert, horiz] = position.split("-");
1730
+ return { [vert]: fabClearance, [horiz]: fabOffset };
1731
+ }
1732
+ __name(anchorStyle, "anchorStyle");
1733
+ function originClass(position) {
1734
+ if (position === "bottom-right") return "origin-bottom-right";
1735
+ if (position === "bottom-left") return "origin-bottom-left";
1736
+ if (position === "top-right") return "origin-top-right";
1737
+ return "origin-top-left";
1738
+ }
1739
+ __name(originClass, "originClass");
1740
+ function ChatGreeting({
1741
+ open,
1742
+ children,
1743
+ onClick,
1744
+ onDismiss,
1745
+ position = "bottom-right",
1746
+ fabOffset = 24,
1747
+ fabClearance = 96,
1748
+ delayMs = 1500,
1749
+ zIndex = 9998,
1750
+ className,
1751
+ style,
1752
+ avatar,
1753
+ senderName,
1754
+ dismissLabel = "Dismiss",
1755
+ inline = false
1756
+ }) {
1757
+ const [delayed, setDelayed] = useState(delayMs <= 0);
1758
+ useEffect(() => {
1759
+ if (!open || delayMs <= 0) return;
1760
+ const t = setTimeout(() => setDelayed(true), delayMs);
1761
+ return () => clearTimeout(t);
1762
+ }, [open, delayMs]);
1763
+ const shouldShow = open && delayed;
1764
+ const phase = useChatPresence(shouldShow, 220);
1765
+ if (phase === "hidden") return null;
1766
+ const animating = phase === "entering" || phase === "leaving";
1767
+ const clickable = !!onClick;
1768
+ return /* @__PURE__ */ jsxs(
1769
+ "div",
1770
+ {
1771
+ role: clickable ? "button" : "status",
1772
+ "aria-live": "polite",
1773
+ tabIndex: clickable ? 0 : -1,
1774
+ onClick: clickable ? onClick : void 0,
1775
+ onKeyDown: clickable ? (e) => {
1776
+ if (e.key === "Enter" || e.key === " ") {
1777
+ e.preventDefault();
1778
+ onClick?.();
1779
+ }
1780
+ } : void 0,
1781
+ className: cn(
1782
+ inline ? "relative inline-flex" : "fixed",
1783
+ "flex items-start gap-2.5 max-w-[280px]",
1784
+ "rounded-2xl border border-border bg-popover text-popover-foreground",
1785
+ "px-3.5 py-2.5 shadow-2xl transition-all duration-200 ease-out",
1786
+ clickable && "cursor-pointer hover:bg-accent/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1787
+ originClass(position),
1788
+ animating ? "opacity-0 scale-95 translate-y-1" : "opacity-100 scale-100 translate-y-0",
1789
+ className
1790
+ ),
1791
+ style: {
1792
+ ...inline ? {} : anchorStyle(position, fabOffset, fabClearance),
1793
+ ...inline ? {} : { zIndex },
1794
+ pointerEvents: phase === "visible" ? "auto" : "none",
1795
+ ...style
1796
+ },
1797
+ children: [
1798
+ avatar && /* @__PURE__ */ jsx("div", { className: "mt-0.5 shrink-0", children: avatar }),
1799
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 text-sm leading-snug", children: [
1800
+ senderName && /* @__PURE__ */ jsx("div", { className: "mb-0.5 text-[11px] font-medium text-muted-foreground", children: senderName }),
1801
+ /* @__PURE__ */ jsx("div", { className: "text-foreground", children })
1802
+ ] }),
1803
+ onDismiss && /* @__PURE__ */ jsx(
1804
+ "button",
1805
+ {
1806
+ type: "button",
1807
+ "aria-label": dismissLabel,
1808
+ onClick: (e) => {
1809
+ e.stopPropagation();
1810
+ onDismiss();
1811
+ },
1812
+ className: cn(
1813
+ "-mr-1 -mt-1 flex h-6 w-6 shrink-0 items-center justify-center rounded-full",
1814
+ "text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
1815
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1816
+ ),
1817
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
1818
+ }
1819
+ )
1820
+ ]
1821
+ }
1822
+ );
1823
+ }
1824
+ __name(ChatGreeting, "ChatGreeting");
1825
+ var TIME_FORMAT = new Intl.DateTimeFormat(void 0, {
1826
+ hour: "2-digit",
1827
+ minute: "2-digit"
1828
+ });
1829
+ function anchorStyle2(position, fabOffset, fabClearance) {
1830
+ const [vert, horiz] = position.split("-");
1831
+ return { [vert]: fabClearance, [horiz]: fabOffset };
1832
+ }
1833
+ __name(anchorStyle2, "anchorStyle");
1834
+ function originClass2(position) {
1835
+ if (position === "bottom-right") return "origin-bottom-right";
1836
+ if (position === "bottom-left") return "origin-bottom-left";
1837
+ if (position === "top-right") return "origin-top-right";
1838
+ return "origin-top-left";
1839
+ }
1840
+ __name(originClass2, "originClass");
1841
+ function deriveAvatar(persona, name) {
1842
+ const initials = persona?.initials ?? (name ?? persona?.name ?? "?").split(/\s+/).map((p) => p[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
1843
+ return /* @__PURE__ */ jsxs(Avatar, { className: "h-9 w-9", children: [
1844
+ persona?.avatarUrl ? /* @__PURE__ */ jsx(AvatarImage, { src: persona.avatarUrl }) : null,
1845
+ /* @__PURE__ */ jsx(AvatarFallback, { children: initials || "?" })
1846
+ ] });
1847
+ }
1848
+ __name(deriveAvatar, "deriveAvatar");
1849
+ function ChatUnreadPreview({
1850
+ open,
1851
+ message,
1852
+ onClick,
1853
+ onDismiss,
1854
+ position = "bottom-right",
1855
+ fabOffset = 24,
1856
+ fabClearance = 96,
1857
+ truncate = 2,
1858
+ zIndex = 9998,
1859
+ inline = false,
1860
+ className,
1861
+ style,
1862
+ dismissLabel = "Mark as read",
1863
+ avatar,
1864
+ senderName
1865
+ }) {
1866
+ const shouldShow = open && !!message;
1867
+ const phase = useChatPresence(shouldShow, 200);
1868
+ if (phase === "hidden" || !message) return null;
1869
+ const animating = phase === "entering" || phase === "leaving";
1870
+ const clickable = !!onClick;
1871
+ const displayName = senderName ?? message.sender?.name ?? "New message";
1872
+ const stamp = TIME_FORMAT.format(new Date(message.createdAt));
1873
+ return /* @__PURE__ */ jsxs(
1874
+ "div",
1875
+ {
1876
+ role: clickable ? "button" : "status",
1877
+ "aria-live": "polite",
1878
+ tabIndex: clickable ? 0 : -1,
1879
+ onClick: clickable ? onClick : void 0,
1880
+ onKeyDown: clickable ? (e) => {
1881
+ if (e.key === "Enter" || e.key === " ") {
1882
+ e.preventDefault();
1883
+ onClick?.();
1884
+ }
1885
+ } : void 0,
1886
+ className: cn(
1887
+ inline ? "relative inline-flex" : "fixed",
1888
+ "flex items-start gap-2.5 max-w-[300px]",
1889
+ "rounded-2xl border border-border bg-popover text-popover-foreground",
1890
+ "px-3.5 py-2.5 shadow-2xl transition-all duration-200 ease-out",
1891
+ clickable && "cursor-pointer hover:bg-accent/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1892
+ originClass2(position),
1893
+ animating ? "opacity-0 scale-95 translate-y-1" : "opacity-100 scale-100 translate-y-0",
1894
+ className
1895
+ ),
1896
+ style: {
1897
+ ...inline ? {} : anchorStyle2(position, fabOffset, fabClearance),
1898
+ ...inline ? {} : { zIndex },
1899
+ pointerEvents: phase === "visible" ? "auto" : "none",
1900
+ ...style
1901
+ },
1902
+ children: [
1903
+ /* @__PURE__ */ jsx("div", { className: "mt-0.5 shrink-0", children: avatar ?? deriveAvatar(message.sender, displayName) }),
1904
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 text-sm leading-snug", children: [
1905
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
1906
+ /* @__PURE__ */ jsx("div", { className: "truncate text-[12px] font-semibold text-foreground", children: displayName }),
1907
+ /* @__PURE__ */ jsx("div", { className: "shrink-0 text-[10px] text-muted-foreground", children: stamp })
1908
+ ] }),
1909
+ /* @__PURE__ */ jsx(
1910
+ "div",
1911
+ {
1912
+ className: "text-foreground/90 mt-0.5 break-words",
1913
+ style: {
1914
+ display: "-webkit-box",
1915
+ WebkitLineClamp: truncate,
1916
+ WebkitBoxOrient: "vertical",
1917
+ overflow: "hidden"
1918
+ },
1919
+ children: message.content
1920
+ }
1921
+ )
1922
+ ] }),
1923
+ onDismiss ? /* @__PURE__ */ jsx(
1924
+ "button",
1925
+ {
1926
+ type: "button",
1927
+ "aria-label": dismissLabel,
1928
+ onClick: (e) => {
1929
+ e.stopPropagation();
1930
+ onDismiss();
1931
+ },
1932
+ className: cn(
1933
+ "-mr-1 -mt-1 flex h-6 w-6 shrink-0 items-center justify-center rounded-full",
1934
+ "text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
1935
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1936
+ ),
1937
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
1938
+ }
1939
+ ) : null
1940
+ ]
1941
+ }
1942
+ );
1943
+ }
1944
+ __name(ChatUnreadPreview, "ChatUnreadPreview");
1945
+ function readDismissed(storageKey) {
1946
+ if (!storageKey) return false;
1947
+ if (typeof window === "undefined") return false;
1948
+ try {
1949
+ return window.localStorage.getItem(storageKey) === "1";
1950
+ } catch {
1951
+ return false;
1952
+ }
1953
+ }
1954
+ __name(readDismissed, "readDismissed");
1955
+ function writeDismissed(storageKey) {
1956
+ if (!storageKey) return;
1957
+ if (typeof window === "undefined") return;
1958
+ try {
1959
+ window.localStorage.setItem(storageKey, "1");
1960
+ } catch {
1961
+ }
1962
+ }
1963
+ __name(writeDismissed, "writeDismissed");
1964
+ function ChatLauncher({
1965
+ children,
1966
+ fab,
1967
+ dock,
1968
+ greeting,
1969
+ hotkey,
1970
+ defaultOpen = false,
1971
+ open: controlledOpen,
1972
+ onOpenChange,
1973
+ autoFocusComposerOnOpen = true,
1974
+ closeOnEscape = true,
1975
+ unreadMessage,
1976
+ onMarkRead,
1977
+ unreadPreview,
1978
+ audio,
1979
+ hideAudioToggle = false
1980
+ }) {
1981
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
1982
+ const isControlled = controlledOpen !== void 0;
1983
+ const open = isControlled ? controlledOpen : uncontrolledOpen;
1984
+ const dockContentRef = useRef(null);
1985
+ useEffect(() => {
1986
+ if (!autoFocusComposerOnOpen || !open) return;
1987
+ const t = setTimeout(() => {
1988
+ const root = dockContentRef.current;
1989
+ if (!root) return;
1990
+ const target = root.querySelector(
1991
+ 'textarea:not([disabled]):not([readonly]), input[type="text"]:not([disabled]):not([readonly])'
1992
+ );
1993
+ target?.focus();
1994
+ }, 120);
1995
+ return () => clearTimeout(t);
1996
+ }, [open, autoFocusComposerOnOpen]);
1997
+ const setOpen = useCallback(
1998
+ (next) => {
1999
+ if (!isControlled) setUncontrolledOpen(next);
2000
+ onOpenChange?.(next);
2001
+ },
2002
+ [isControlled, onOpenChange]
2003
+ );
2004
+ const toggleOpen = useCallback(() => setOpen(!open), [open, setOpen]);
2005
+ useHotkey(
2006
+ "escape",
2007
+ (e) => {
2008
+ const target = e?.target ?? null;
2009
+ const inEditable = !!target && (target.matches?.('input, textarea, [contenteditable="true"]') ?? false);
2010
+ if (inEditable) {
2011
+ target.blur();
2012
+ return;
2013
+ }
2014
+ setOpen(false);
2015
+ },
2016
+ { enabled: closeOnEscape && open }
2017
+ );
2018
+ const greetingConfig = greeting === void 0 ? null : typeof greeting === "string" ? { content: greeting } : greeting;
2019
+ const [dismissed, setDismissed] = useState(
2020
+ () => readDismissed(greetingConfig?.dismissStorageKey)
2021
+ );
2022
+ useEffect(() => {
2023
+ if (!hotkey) return;
2024
+ const handler = /* @__PURE__ */ __name((e) => {
2025
+ const metaOk = hotkey.meta ? e.metaKey || e.ctrlKey : !e.metaKey && !e.ctrlKey;
2026
+ const shiftOk = hotkey.shift ? e.shiftKey : !e.shiftKey;
2027
+ const altOk = hotkey.alt ? e.altKey : !e.altKey;
2028
+ if (!metaOk || !shiftOk || !altOk) return;
2029
+ if (e.key !== hotkey.key) return;
2030
+ e.preventDefault();
2031
+ setOpen(!open);
2032
+ }, "handler");
2033
+ window.addEventListener("keydown", handler);
2034
+ return () => window.removeEventListener("keydown", handler);
2035
+ }, [hotkey?.key, hotkey?.meta, hotkey?.shift, hotkey?.alt, open, setOpen, hotkey]);
2036
+ const greetingOpen = !!greetingConfig && !dismissed && (greetingConfig.hideOnOpen === false || !open);
2037
+ const fabPosition = fab?.position ?? "bottom-right";
2038
+ const fabOffset = fab?.offset ?? 24;
2039
+ const handleGreetingDismiss = /* @__PURE__ */ __name(() => {
2040
+ setDismissed(true);
2041
+ writeDismissed(greetingConfig?.dismissStorageKey);
2042
+ }, "handleGreetingDismiss");
2043
+ const handleGreetingClick = /* @__PURE__ */ __name(() => {
2044
+ setOpen(true);
2045
+ setDismissed(true);
2046
+ writeDismissed(greetingConfig?.dismissStorageKey);
2047
+ }, "handleGreetingClick");
2048
+ useEffect(() => {
2049
+ if (open && unreadMessage) onMarkRead?.();
2050
+ }, [open, unreadMessage, onMarkRead]);
2051
+ const unreadOpen = !open && !!unreadMessage;
2052
+ const handleUnreadClick = /* @__PURE__ */ __name(() => {
2053
+ setOpen(true);
2054
+ onMarkRead?.();
2055
+ }, "handleUnreadClick");
2056
+ const handleUnreadDismiss = /* @__PURE__ */ __name(() => {
2057
+ onMarkRead?.();
2058
+ }, "handleUnreadDismiss");
2059
+ const resolvedFab = unreadMessage && fab?.badge === void 0 ? { ...fab, badge: 1 } : fab;
2060
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2061
+ /* @__PURE__ */ jsx(ChatFAB, { ...resolvedFab, onClick: toggleOpen }),
2062
+ unreadMessage ? /* @__PURE__ */ jsx(
2063
+ ChatUnreadPreview,
2064
+ {
2065
+ ...unreadPreview,
2066
+ open: unreadOpen,
2067
+ message: unreadMessage,
2068
+ onClick: handleUnreadClick,
2069
+ onDismiss: handleUnreadDismiss,
2070
+ position: fabPosition,
2071
+ fabOffset
2072
+ }
2073
+ ) : greetingConfig ? /* @__PURE__ */ jsx(
2074
+ ChatGreeting,
2075
+ {
2076
+ ...greetingConfig,
2077
+ open: greetingOpen,
2078
+ onClick: handleGreetingClick,
2079
+ onDismiss: handleGreetingDismiss,
2080
+ position: fabPosition,
2081
+ fabOffset,
2082
+ children: greetingConfig.content
2083
+ }
2084
+ ) : null,
2085
+ /* @__PURE__ */ jsx(
2086
+ ChatDock,
2087
+ {
2088
+ ...dock,
2089
+ open,
2090
+ onClose: () => setOpen(false),
2091
+ headerActions: audio && !audio.isSilent && !hideAudioToggle || dock?.headerActions ? /* @__PURE__ */ jsxs(Fragment, { children: [
2092
+ dock?.headerActions,
2093
+ audio && !audio.isSilent && !hideAudioToggle ? /* @__PURE__ */ jsx(ChatHeaderAudioToggle, { muted: audio.muted, onToggle: audio.toggleMute }) : null
2094
+ ] }) : void 0,
2095
+ children: /* @__PURE__ */ jsx("div", { ref: dockContentRef, className: "flex h-full min-h-0 min-w-0 flex-col", children })
2096
+ }
2097
+ )
2098
+ ] });
2099
+ }
2100
+ __name(ChatLauncher, "ChatLauncher");
654
2101
  function useChatScroll(options) {
655
2102
  const {
656
2103
  containerRef,
@@ -794,42 +2241,88 @@ function useChatLightbox() {
794
2241
  return { state, open, close };
795
2242
  }
796
2243
  __name(useChatLightbox, "useChatLightbox");
797
- function useAutoFocusOnStreamEnd(options = {}) {
798
- const { isStreaming: isStreamingProp, targetRef, enabled = true, delayMs = 0 } = options;
799
- const ctx = useChatContextOptional();
800
- const isStreaming = isStreamingProp ?? ctx?.isStreaming ?? false;
801
- const composerHandleRef = useRef(null);
802
- composerHandleRef.current = ctx?.composer ?? null;
803
- const prevStreamingRef = useRef(isStreaming);
2244
+ var DEFAULT_STORAGE_KEY = "chat.visitor.fingerprint";
2245
+ function generate() {
2246
+ if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
2247
+ return crypto.randomUUID();
2248
+ }
2249
+ return `v-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
2250
+ }
2251
+ __name(generate, "generate");
2252
+ function useVisitorFingerprint(opts = {}) {
2253
+ const storageKey = opts.storageKey ?? DEFAULT_STORAGE_KEY;
2254
+ const [fp, setFp] = useState(null);
804
2255
  useEffect(() => {
805
- const wasStreaming = prevStreamingRef.current;
806
- prevStreamingRef.current = isStreaming;
807
- if (!enabled) return;
808
- if (!(wasStreaming && !isStreaming)) return;
809
- const focusNow = /* @__PURE__ */ __name(() => {
810
- const explicit = targetRef?.current;
811
- const target = explicit ?? composerHandleRef.current;
812
- target?.focus();
813
- }, "focusNow");
814
- if (delayMs > 0) {
815
- const id = window.setTimeout(focusNow, delayMs);
816
- return () => window.clearTimeout(id);
2256
+ let value = null;
2257
+ try {
2258
+ value = window.localStorage.getItem(storageKey);
2259
+ if (!value) {
2260
+ value = generate();
2261
+ window.localStorage.setItem(storageKey, value);
2262
+ }
2263
+ } catch {
2264
+ value = generate();
817
2265
  }
818
- const raf = requestAnimationFrame(focusNow);
819
- return () => cancelAnimationFrame(raf);
820
- }, [isStreaming, enabled, delayMs, targetRef]);
2266
+ setFp(value);
2267
+ }, [storageKey]);
2268
+ return fp;
821
2269
  }
822
- __name(useAutoFocusOnStreamEnd, "useAutoFocusOnStreamEnd");
823
- function useRegisterComposer(focus) {
824
- const ctx = useChatContextOptional();
825
- const register = ctx?.registerComposer;
2270
+ __name(useVisitorFingerprint, "useVisitorFingerprint");
2271
+ var DEFAULT_DOCK_PREFS = {
2272
+ mode: "popover",
2273
+ side: "right",
2274
+ sideWidth: 420
2275
+ };
2276
+ var DEFAULT_KEY = "chat.dock.prefs";
2277
+ function useChatDockPrefs(opts = {}) {
2278
+ const key = opts.storageKey ?? DEFAULT_KEY;
2279
+ const initial = { ...DEFAULT_DOCK_PREFS, ...opts.defaults };
2280
+ const [prefs, setStored] = useLocalStorage(key, initial);
2281
+ const setPrefs = useCallback(
2282
+ (patch) => {
2283
+ setStored((prev) => ({ ...prev, ...patch }));
2284
+ },
2285
+ [setStored]
2286
+ );
2287
+ const toggleMode = useCallback(() => {
2288
+ setStored((prev) => ({ ...prev, mode: prev.mode === "side" ? "popover" : "side" }));
2289
+ }, [setStored]);
2290
+ const toggleSide = useCallback(() => {
2291
+ setStored((prev) => ({ ...prev, side: prev.side === "right" ? "left" : "right" }));
2292
+ }, [setStored]);
2293
+ const reset = useCallback(() => setStored(initial), [setStored, initial]);
2294
+ return { ...prefs, setPrefs, toggleMode, toggleSide, reset };
2295
+ }
2296
+ __name(useChatDockPrefs, "useChatDockPrefs");
2297
+ function useChatUnread(opts = {}) {
2298
+ const { open = false, countRoles = ["assistant"] } = opts;
2299
+ const ctx = useChatContext();
2300
+ const [lastSeenId, setLastSeenId] = useState(null);
2301
+ const initialized = useRef(false);
826
2302
  useEffect(() => {
827
- if (!register) return;
828
- register({ focus });
829
- return () => register(null);
830
- }, [register, focus]);
2303
+ if (initialized.current) return;
2304
+ initialized.current = true;
2305
+ const tail = ctx.messages[ctx.messages.length - 1];
2306
+ setLastSeenId(tail?.id ?? null);
2307
+ }, [ctx.messages]);
2308
+ useEffect(() => {
2309
+ if (!open) return;
2310
+ const tail = ctx.messages[ctx.messages.length - 1];
2311
+ setLastSeenId(tail?.id ?? null);
2312
+ }, [open, ctx.messages]);
2313
+ const seenIdx = lastSeenId ? ctx.messages.findIndex((m) => m.id === lastSeenId) : -1;
2314
+ const after = seenIdx === -1 ? ctx.messages : ctx.messages.slice(seenIdx + 1);
2315
+ const inbound = after.filter((m) => countRoles.includes(m.role));
2316
+ const unread = inbound.length > 0 ? inbound[inbound.length - 1] : null;
2317
+ const markRead = useCallback(() => {
2318
+ const tail = ctx.messages[ctx.messages.length - 1];
2319
+ setLastSeenId(tail?.id ?? null);
2320
+ }, [ctx.messages]);
2321
+ return { unread, count: inbound.length, markRead };
831
2322
  }
832
- __name(useRegisterComposer, "useRegisterComposer");
2323
+ __name(useChatUnread, "useChatUnread");
2324
+ var STORAGE_KEY = "djangocfg-chat-audio:prefs";
2325
+ var useChatAudioPrefs = createAudioPrefsStore(STORAGE_KEY);
833
2326
 
834
2327
  // src/tools/Chat/core/payload-dispatch.ts
835
2328
  function dispatchToolPayload(matchers, fallback) {
@@ -910,7 +2403,7 @@ function LottiePlayer(props) {
910
2403
  }
911
2404
  __name(LottiePlayer, "LottiePlayer");
912
2405
  var DocsLayout = lazy(
913
- () => import('./DocsLayout-2YKPXZYO.mjs').then((mod) => ({ default: mod.DocsLayout }))
2406
+ () => import('./DocsLayout-2P3ONDWJ.mjs').then((mod) => ({ default: mod.DocsLayout }))
914
2407
  );
915
2408
  var LoadingFallback6 = /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[400px]", children: /* @__PURE__ */ jsx("div", { className: "text-muted-foreground", children: "Loading API Playground..." }) }), "LoadingFallback");
916
2409
  var Playground = /* @__PURE__ */ __name(({ config }) => {
@@ -1849,7 +3342,7 @@ function extractMentionIds(editor) {
1849
3342
  return [...new Set(ids)];
1850
3343
  }
1851
3344
  __name(extractMentionIds, "extractMentionIds");
1852
- function MarkdownEditor({
3345
+ var MarkdownEditor = forwardRef(/* @__PURE__ */ __name(function MarkdownEditor2({
1853
3346
  value,
1854
3347
  onChange,
1855
3348
  placeholder = "Write markdown...",
@@ -1860,7 +3353,7 @@ function MarkdownEditor({
1860
3353
  mentions,
1861
3354
  onMentionIdsChange,
1862
3355
  onSubmit
1863
- }) {
3356
+ }, ref) {
1864
3357
  const onSubmitRef = useRef(onSubmit);
1865
3358
  onSubmitRef.current = onSubmit;
1866
3359
  const isExternalUpdate = useRef(false);
@@ -1940,13 +3433,25 @@ function MarkdownEditor({
1940
3433
  isExternalUpdate.current = false;
1941
3434
  }
1942
3435
  }, [value, editor]);
3436
+ useImperativeHandle(
3437
+ ref,
3438
+ () => ({
3439
+ focus: /* @__PURE__ */ __name(() => {
3440
+ editor?.commands.focus();
3441
+ }, "focus"),
3442
+ moveCursorToEnd: /* @__PURE__ */ __name(() => {
3443
+ editor?.commands.focus("end");
3444
+ }, "moveCursorToEnd"),
3445
+ getEditor: /* @__PURE__ */ __name(() => editor ?? null, "getEditor")
3446
+ }),
3447
+ [editor]
3448
+ );
1943
3449
  const wrapperClass = `markdown-editor rounded-md border border-input bg-background ${disabled ? "opacity-60" : ""} ${className}`.trim();
1944
3450
  return /* @__PURE__ */ jsxs("div", { className: wrapperClass, children: [
1945
3451
  showToolbar && editor && /* @__PURE__ */ jsx(MarkdownToolbar, { editor }),
1946
3452
  /* @__PURE__ */ jsx("div", { className: "px-3 py-2", children: /* @__PURE__ */ jsx(EditorContent, { editor }) })
1947
3453
  ] });
1948
- }
1949
- __name(MarkdownEditor, "MarkdownEditor");
3454
+ }, "MarkdownEditor"));
1950
3455
  function MarkdownToolbar({ editor }) {
1951
3456
  const items = useMemo(() => [
1952
3457
  { icon: Bold, title: "Bold", action: /* @__PURE__ */ __name(() => editor.chain().focus().toggleBold().run(), "action"), active: editor.isActive("bold") },
@@ -1972,6 +3477,6 @@ function MarkdownToolbar({ editor }) {
1972
3477
  }
1973
3478
  __name(MarkdownToolbar, "MarkdownToolbar");
1974
3479
 
1975
- export { AudioToggle, CardLoadingFallback, CronScheduler, DiffEditor, Editor, EditorProvider, LazyPlayer as LazyAudioPlayer, LazyChat, LazyCronScheduler, LazyImageViewer, LazyJsonSchemaForm, LazyJsonTree, LazyLottiePlayer, LazyMapContainer, LazyMapView, LazyMermaid, LazyOpenapiViewer, LazyPrettyCode, LazyTree, LazyVideoPlayer, LazyWrapper, LoadingFallback, LottiePlayer, MapLoadingFallback, MarkdownEditor, OpenapiViewer_default as OpenapiViewer, Spinner, TransportError, collectImageAttachments, createHttpTransport, createLazyComponent, createMockTransport, dispatchToolPayload, isGeoJSONFeatureCollection, isLatLng, isPlainObject, isStringValue, mentionPresets, parseSSE, useAutoFocusOnStreamEnd, useChatHistory, useChatLightbox, useChatScroll, useEditor, useEditorContext, useLanguage, useMonaco, useRegisterComposer };
3480
+ export { AudioToggle, CardLoadingFallback, ChatDock, ChatFAB, ChatGreeting, ChatHeader, ChatHeaderActionButton, ChatHeaderAudioToggle, ChatHeaderLanguageButton, ChatHeaderModeToggle, ChatHeaderResetButton, ChatLauncher, ChatUnreadPreview, CronScheduler, DEFAULT_DOCK_PREFS, DiffEditor, Editor, EditorProvider, LazyPlayer as LazyAudioPlayer, LazyChat, LazyCronScheduler, LazyImageViewer, LazyJsonSchemaForm, LazyJsonTree, LazyLottiePlayer, LazyMapContainer, LazyMapView, LazyMermaid, LazyOpenapiViewer, LazyPrettyCode, LazyTree, LazyVideoPlayer, LazyWrapper, LoadingFallback, LottiePlayer, MapLoadingFallback, MarkdownEditor, OpenapiViewer_default as OpenapiViewer, Spinner, TransportError, collectImageAttachments, createHttpTransport, createLazyComponent, createMockTransport, createPydanticAIChatTransport, createPydanticAISSEMap, createToolIdQueue, dispatchToolPayload, isGeoJSONFeatureCollection, isLatLng, isPlainObject, isStringValue, mapPydanticAIEvent, mentionPresets, parseSSE, useChatAudioPrefs, useChatDockPrefs, useChatHistory, useChatLightbox, useChatPresence, useChatReset, useChatScroll, useChatUnread, useEditor, useEditorContext, useLanguage, useMonaco, useVisitorFingerprint };
1976
3481
  //# sourceMappingURL=index.mjs.map
1977
3482
  //# sourceMappingURL=index.mjs.map