@djangocfg/ui-tools 2.1.381 → 2.1.382

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 (183) 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-2ZLQWLYV.mjs +4 -0
  7. package/dist/DictationField-2ZLQWLYV.mjs.map +1 -0
  8. package/dist/DictationField-IPPJ54CU.cjs +13 -0
  9. package/dist/DictationField-IPPJ54CU.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-4LXG3NBV.mjs +833 -0
  15. package/dist/chunk-4LXG3NBV.mjs.map +1 -0
  16. package/dist/{chunk-XACCHZH2.cjs → chunk-FIRK5CEH.cjs} +42 -4
  17. package/dist/chunk-FIRK5CEH.cjs.map +1 -0
  18. package/dist/{chunk-NWUT327A.mjs → chunk-HIK6BPL7.mjs} +38 -5
  19. package/dist/chunk-HIK6BPL7.mjs.map +1 -0
  20. package/dist/chunk-KMSBGNVC.cjs +835 -0
  21. package/dist/chunk-KMSBGNVC.cjs.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 +1532 -100
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +1148 -107
  29. package/dist/index.d.ts +1148 -107
  30. package/dist/index.mjs +1421 -51
  31. package/dist/index.mjs.map +1 -1
  32. package/package.json +16 -8
  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/stories/index.ts +32 -2
  37. package/src/tools/Chat/README.md +347 -530
  38. package/src/tools/Chat/components/Attachments.tsx +6 -1
  39. package/src/tools/Chat/components/ChatRoot.tsx +30 -2
  40. package/src/tools/Chat/components/Composer.tsx +20 -3
  41. package/src/tools/Chat/components/ErrorBanner.tsx +7 -3
  42. package/src/tools/Chat/components/MessageActions.tsx +3 -1
  43. package/src/tools/Chat/components/MessageBubble.tsx +6 -5
  44. package/src/tools/Chat/components/MessageList.tsx +87 -1
  45. package/src/tools/Chat/components/ToolCalls.tsx +21 -3
  46. package/src/tools/Chat/context/ChatProvider.tsx +21 -3
  47. package/src/tools/Chat/core/audio/audioBus.ts +10 -163
  48. package/src/tools/Chat/core/audio/defaults.ts +43 -0
  49. package/src/tools/Chat/core/audio/index.ts +1 -0
  50. package/src/tools/Chat/core/audio/preferences.ts +5 -59
  51. package/src/tools/Chat/core/audio/sounds/error.mp3 +0 -0
  52. package/src/tools/Chat/core/audio/sounds/mention.mp3 +0 -0
  53. package/src/tools/Chat/core/audio/sounds/notification.mp3 +0 -0
  54. package/src/tools/Chat/core/audio/sounds/received.mp3 +0 -0
  55. package/src/tools/Chat/core/audio/sounds/sent.mp3 +0 -0
  56. package/src/tools/Chat/core/audio/sounds/start.mp3 +0 -0
  57. package/src/tools/Chat/core/audio/types.ts +28 -0
  58. package/src/tools/Chat/core/reducer.ts +33 -0
  59. package/src/tools/Chat/core/transport/index.ts +13 -0
  60. package/src/tools/Chat/core/transport/mappers/index.ts +6 -0
  61. package/src/tools/Chat/core/transport/mappers/pydantic-ai.ts +142 -0
  62. package/src/tools/Chat/core/transport/pydantic-ai-transport.ts +208 -0
  63. package/src/tools/Chat/core/transport/sse.ts +18 -5
  64. package/src/tools/Chat/hooks/index.ts +25 -0
  65. package/src/tools/Chat/hooks/useAutoFocusOnStreamEnd.ts +5 -3
  66. package/src/tools/Chat/hooks/useChat.ts +28 -0
  67. package/src/tools/Chat/hooks/useChatAudio.ts +59 -180
  68. package/src/tools/Chat/hooks/useChatDockPrefs.ts +74 -0
  69. package/src/tools/Chat/hooks/useChatReset.ts +70 -0
  70. package/src/tools/Chat/hooks/useChatUnread.ts +87 -0
  71. package/src/tools/Chat/hooks/useFocusOnEmptyClick.ts +111 -0
  72. package/src/tools/Chat/hooks/useVisitorFingerprint.ts +48 -0
  73. package/src/tools/Chat/index.ts +69 -1
  74. package/src/tools/Chat/launcher/ChatDock.tsx +263 -0
  75. package/src/tools/Chat/launcher/ChatFAB.tsx +349 -0
  76. package/src/tools/Chat/launcher/ChatGreeting.tsx +200 -0
  77. package/src/tools/Chat/launcher/ChatHeader.tsx +76 -0
  78. package/src/tools/Chat/launcher/ChatHeaderActionButton.tsx +87 -0
  79. package/src/tools/Chat/launcher/ChatHeaderAudioToggle.tsx +47 -0
  80. package/src/tools/Chat/launcher/ChatHeaderLanguageButton.tsx +179 -0
  81. package/src/tools/Chat/launcher/ChatHeaderModeToggle.tsx +57 -0
  82. package/src/tools/Chat/launcher/ChatHeaderResetButton.tsx +93 -0
  83. package/src/tools/Chat/launcher/ChatLauncher.tsx +321 -0
  84. package/src/tools/Chat/launcher/ChatUnreadPreview.tsx +197 -0
  85. package/src/tools/Chat/launcher/index.ts +46 -0
  86. package/src/tools/Chat/launcher/useChatPresence.ts +44 -0
  87. package/src/tools/Chat/stories/01-basic.story.tsx +64 -0
  88. package/src/tools/Chat/stories/02-bubbles.story.tsx +21 -0
  89. package/src/tools/Chat/stories/03-tool-calls.story.tsx +59 -0
  90. package/src/tools/Chat/stories/04-personas.story.tsx +78 -0
  91. package/src/tools/Chat/stories/05-launcher.story.tsx +321 -0
  92. package/src/tools/Chat/stories/06-header.story.tsx +147 -0
  93. package/src/tools/Chat/stories/07-audio-actions.story.tsx +112 -0
  94. package/src/tools/Chat/stories/shared/Frame.tsx +21 -0
  95. package/src/tools/Chat/stories/shared/index.ts +5 -0
  96. package/src/tools/Chat/stories/shared/messages.ts +39 -0
  97. package/src/tools/Chat/stories/shared/personas.ts +13 -0
  98. package/src/tools/Chat/stories/shared/seeds.ts +92 -0
  99. package/src/tools/Chat/stories/shared/transports.ts +36 -0
  100. package/src/tools/Chat/styles/bubbleTokens.ts +71 -0
  101. package/src/tools/Chat/styles/index.ts +16 -0
  102. package/src/tools/Chat/styles/useChatStyles.ts +101 -0
  103. package/src/tools/Chat/types/attachment.ts +25 -0
  104. package/src/tools/Chat/types/config.ts +48 -0
  105. package/src/tools/Chat/types/events.ts +35 -0
  106. package/src/tools/Chat/types/index.ts +34 -0
  107. package/src/tools/Chat/types/labels.ts +38 -0
  108. package/src/tools/Chat/types/message.ts +32 -0
  109. package/src/tools/Chat/types/persona.ts +31 -0
  110. package/src/tools/Chat/types/session.ts +43 -0
  111. package/src/tools/Chat/types/tool-call.ts +17 -0
  112. package/src/tools/Chat/types/transport.ts +28 -0
  113. package/src/tools/Chat/types.ts +5 -240
  114. package/src/tools/MarkdownEditor/MarkdownEditor.tsx +50 -14
  115. package/src/tools/MarkdownEditor/index.ts +1 -1
  116. package/src/tools/SpeechRecognition/README.md +336 -0
  117. package/src/tools/SpeechRecognition/__tests__/ids.test.ts +15 -0
  118. package/src/tools/SpeechRecognition/__tests__/language.test.ts +59 -0
  119. package/src/tools/SpeechRecognition/__tests__/reducer.test.ts +71 -0
  120. package/src/tools/SpeechRecognition/__tests__/transcript.test.ts +52 -0
  121. package/src/tools/SpeechRecognition/components/DevicePicker.tsx +49 -0
  122. package/src/tools/SpeechRecognition/components/DictationButton.tsx +93 -0
  123. package/src/tools/SpeechRecognition/components/EngineBadge.tsx +30 -0
  124. package/src/tools/SpeechRecognition/components/ErrorBanner.tsx +52 -0
  125. package/src/tools/SpeechRecognition/components/LanguagePicker.tsx +63 -0
  126. package/src/tools/SpeechRecognition/components/MicMeter.tsx +63 -0
  127. package/src/tools/SpeechRecognition/components/PushToTalkHint.tsx +51 -0
  128. package/src/tools/SpeechRecognition/components/TranscriptView.tsx +55 -0
  129. package/src/tools/SpeechRecognition/components/index.ts +16 -0
  130. package/src/tools/SpeechRecognition/context/SpeechRecognitionProvider.tsx +47 -0
  131. package/src/tools/SpeechRecognition/context/index.ts +6 -0
  132. package/src/tools/SpeechRecognition/core/audio/defaults.ts +24 -0
  133. package/src/tools/SpeechRecognition/core/engine/external.ts +222 -0
  134. package/src/tools/SpeechRecognition/core/engine/http.ts +147 -0
  135. package/src/tools/SpeechRecognition/core/engine/index.ts +52 -0
  136. package/src/tools/SpeechRecognition/core/engine/mediarecorder.ts +105 -0
  137. package/src/tools/SpeechRecognition/core/engine/websocket.ts +211 -0
  138. package/src/tools/SpeechRecognition/core/engine/webspeech.ts +188 -0
  139. package/src/tools/SpeechRecognition/core/ids.ts +11 -0
  140. package/src/tools/SpeechRecognition/core/index.ts +14 -0
  141. package/src/tools/SpeechRecognition/core/language.ts +78 -0
  142. package/src/tools/SpeechRecognition/core/languages-catalog.ts +229 -0
  143. package/src/tools/SpeechRecognition/core/logger.ts +3 -0
  144. package/src/tools/SpeechRecognition/core/reducer.ts +105 -0
  145. package/src/tools/SpeechRecognition/core/transcript.ts +36 -0
  146. package/src/tools/SpeechRecognition/hooks/index.ts +14 -0
  147. package/src/tools/SpeechRecognition/hooks/useDictation.ts +59 -0
  148. package/src/tools/SpeechRecognition/hooks/useEnginePrefs.ts +15 -0
  149. package/src/tools/SpeechRecognition/hooks/useMicDevices.ts +57 -0
  150. package/src/tools/SpeechRecognition/hooks/useMicLevel.ts +52 -0
  151. package/src/tools/SpeechRecognition/hooks/usePushToTalk.ts +85 -0
  152. package/src/tools/SpeechRecognition/hooks/useResolvedLanguage.ts +28 -0
  153. package/src/tools/SpeechRecognition/hooks/useSpeechLanguageInfo.ts +108 -0
  154. package/src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts +188 -0
  155. package/src/tools/SpeechRecognition/hooks/useVoiceSupport.ts +78 -0
  156. package/src/tools/SpeechRecognition/index.ts +82 -0
  157. package/src/tools/SpeechRecognition/lazy.tsx +19 -0
  158. package/src/tools/SpeechRecognition/store/index.ts +2 -0
  159. package/src/tools/SpeechRecognition/store/prefsStore.ts +54 -0
  160. package/src/tools/SpeechRecognition/stories/01-basic.story.tsx +32 -0
  161. package/src/tools/SpeechRecognition/stories/02-dictation-field.story.tsx +32 -0
  162. package/src/tools/SpeechRecognition/stories/03-push-to-talk.story.tsx +27 -0
  163. package/src/tools/SpeechRecognition/stories/04-mic-meter.story.tsx +35 -0
  164. package/src/tools/SpeechRecognition/stories/05-custom-engine-http.story.tsx +40 -0
  165. package/src/tools/SpeechRecognition/stories/06-custom-engine-ws.story.tsx +48 -0
  166. package/src/tools/SpeechRecognition/stories/07-language-device.story.tsx +57 -0
  167. package/src/tools/SpeechRecognition/stories/08-errors-permissions.story.tsx +25 -0
  168. package/src/tools/SpeechRecognition/stories/09-chat-voice.story.tsx +90 -0
  169. package/src/tools/SpeechRecognition/stories/shared.tsx +123 -0
  170. package/src/tools/SpeechRecognition/types.ts +133 -0
  171. package/src/tools/SpeechRecognition/widgets/DictationField.tsx +105 -0
  172. package/src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx +305 -0
  173. package/src/tools/SpeechRecognition/widgets/VoiceMessageRecorder.tsx +88 -0
  174. package/src/tools/SpeechRecognition/widgets/index.ts +6 -0
  175. package/dist/ChatRoot-EJC5Y2YM.cjs +0 -14
  176. package/dist/ChatRoot-QOSKJPM6.mjs +0 -5
  177. package/dist/chunk-NWUT327A.mjs.map +0 -1
  178. package/dist/chunk-QLMKCSR6.mjs +0 -2420
  179. package/dist/chunk-QLMKCSR6.mjs.map +0 -1
  180. package/dist/chunk-SI5RD2GD.cjs +0 -2460
  181. package/dist/chunk-SI5RD2GD.cjs.map +0 -1
  182. package/dist/chunk-XACCHZH2.cjs.map +0 -1
  183. package/src/tools/Chat/Chat.story.tsx +0 -1457
@@ -1,2460 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkXACCHZH2_cjs = require('./chunk-XACCHZH2.cjs');
4
- var chunkOLISEQHS_cjs = require('./chunk-OLISEQHS.cjs');
5
- var react = require('react');
6
- var lib = require('@djangocfg/ui-core/lib');
7
- var consola = require('consola');
8
- var hooks = require('@djangocfg/ui-core/hooks');
9
- var zustand = require('zustand');
10
- var middleware = require('zustand/middleware');
11
- var jsxRuntime = require('react/jsx-runtime');
12
- var lucideReact = require('lucide-react');
13
- var components = require('@djangocfg/ui-core/components');
14
- var reactVirtuoso = require('react-virtuoso');
15
-
16
- // src/tools/Chat/types.ts
17
- var DEFAULT_LABELS = {
18
- send: "Send",
19
- cancel: "Stop",
20
- copy: "Copy",
21
- regenerate: "Regenerate",
22
- edit: "Edit",
23
- delete: "Delete",
24
- retry: "Retry",
25
- newChat: "New chat",
26
- loadMore: "Load more",
27
- jumpToLatest: "Jump to latest",
28
- attach: "Attach files",
29
- voice: "Voice input",
30
- errorGeneric: "Something went wrong",
31
- cancelledSuffix: "[cancelled]"
32
- };
33
-
34
- // src/tools/Chat/config.ts
35
- var STORAGE_KEYS = {
36
- mode: "djc-chat-mode",
37
- sidebarWidth: "djc-chat-sidebar-width",
38
- composerHistory: "djc-chat-composer-history"
39
- };
40
- var CSS_VARS = {
41
- reserve: "--djc-chat-reserve"
42
- };
43
- var DEFAULT_Z_INDEX = 9e3;
44
- var LIMITS = {
45
- /** Max characters per single message. */
46
- messageMaxLength: 8e3,
47
- /** Max attachments per message. */
48
- attachmentsMax: 10,
49
- /** Composer history slots. */
50
- composerHistorySize: 50,
51
- /** Coalesce stream tokens within this window before dispatching. */
52
- streamCoalesceMs: 16,
53
- /** Default history page size. */
54
- pageSize: 50,
55
- /** Virtualize list when >= this many messages (host-controlled threshold). */
56
- virtualizeThreshold: 50,
57
- /** SSE idle timeout. */
58
- sseIdleMs: 45e3
59
- };
60
- var DEFAULT_SIDEBAR = {
61
- width: 420,
62
- min: 320,
63
- max: 720
64
- };
65
- var HOTKEYS = {
66
- send: "mod+enter",
67
- cancel: "esc",
68
- newChat: "mod+shift+n",
69
- toggleOpen: "mod+/",
70
- focusComposer: "mod+l"
71
- };
72
- var CHAT_EVENT_NAME = "djc:chat:send";
73
-
74
- // src/tools/Chat/core/reducer.ts
75
- var initialState = {
76
- sessionId: null,
77
- messages: [],
78
- isLoading: false,
79
- isStreaming: false,
80
- isLoadingMore: false,
81
- hasMore: true,
82
- oldestCursor: null,
83
- error: null
84
- };
85
- function updateLastStreaming(messages, patch) {
86
- const idx = findLastStreamingIndex(messages);
87
- if (idx === -1) return messages;
88
- const next = messages.slice();
89
- next[idx] = patch(messages[idx]);
90
- return next;
91
- }
92
- chunkOLISEQHS_cjs.__name(updateLastStreaming, "updateLastStreaming");
93
- function findLastStreamingIndex(messages) {
94
- for (let i = messages.length - 1; i >= 0; i -= 1) {
95
- if (messages[i].isStreaming) return i;
96
- }
97
- return -1;
98
- }
99
- chunkOLISEQHS_cjs.__name(findLastStreamingIndex, "findLastStreamingIndex");
100
- function patchMessageById(messages, id, patch) {
101
- const idx = messages.findIndex((m) => m.id === id);
102
- if (idx === -1) return messages;
103
- const next = messages.slice();
104
- next[idx] = patch(messages[idx]);
105
- return next;
106
- }
107
- chunkOLISEQHS_cjs.__name(patchMessageById, "patchMessageById");
108
- function reducer(state, action) {
109
- switch (action.type) {
110
- case "SESSION_SET":
111
- return {
112
- ...state,
113
- sessionId: action.sessionId,
114
- messages: action.messages ?? state.messages,
115
- hasMore: action.hasMore ?? state.hasMore,
116
- oldestCursor: action.cursor ?? state.oldestCursor,
117
- error: null
118
- };
119
- case "HISTORY_LOAD_START":
120
- return { ...state, isLoading: true, error: null };
121
- case "HISTORY_LOAD_DONE":
122
- return {
123
- ...state,
124
- isLoading: false,
125
- messages: action.messages,
126
- hasMore: action.hasMore,
127
- oldestCursor: action.cursor
128
- };
129
- case "HISTORY_MORE_START":
130
- return { ...state, isLoadingMore: true };
131
- case "HISTORY_MORE_DONE":
132
- return {
133
- ...state,
134
- isLoadingMore: false,
135
- // Older messages prepend to the top.
136
- messages: [...action.messages, ...state.messages],
137
- hasMore: action.hasMore,
138
- oldestCursor: action.cursor
139
- };
140
- case "MESSAGE_USER_ADD":
141
- return {
142
- ...state,
143
- messages: [...state.messages, action.message],
144
- error: null
145
- };
146
- case "STREAM_START": {
147
- if (state.isStreaming) {
148
- if (typeof console !== "undefined") {
149
- console.warn("[chat] STREAM_START while already streaming, ignoring");
150
- }
151
- return state;
152
- }
153
- const placeholder = {
154
- id: action.id,
155
- role: "assistant",
156
- content: "",
157
- createdAt: action.createdAt ?? Date.now(),
158
- isStreaming: true
159
- };
160
- return {
161
- ...state,
162
- isStreaming: true,
163
- messages: [...state.messages, placeholder]
164
- };
165
- }
166
- case "STREAM_CHUNK": {
167
- const messages = updateLastStreaming(state.messages, (m) => ({
168
- ...m,
169
- content: m.content + action.delta
170
- }));
171
- return { ...state, messages };
172
- }
173
- case "STREAM_TOOL_ACTIVITY": {
174
- const messages = updateLastStreaming(state.messages, (m) => ({
175
- ...m,
176
- toolActivity: action.tool
177
- }));
178
- return { ...state, messages };
179
- }
180
- case "TOOL_CALL_START": {
181
- const messages = patchMessageById(state.messages, action.messageId, (m) => ({
182
- ...m,
183
- toolCalls: [...m.toolCalls ?? [], action.toolCall]
184
- }));
185
- return { ...state, messages };
186
- }
187
- case "TOOL_CALL_DELTA": {
188
- const messages = patchMessageById(state.messages, action.messageId, (m) => {
189
- if (!m.toolCalls) return m;
190
- return {
191
- ...m,
192
- toolCalls: m.toolCalls.map(
193
- (tc) => tc.id === action.toolId ? { ...tc, streamingText: (tc.streamingText ?? "") + action.delta } : tc
194
- )
195
- };
196
- });
197
- return { ...state, messages };
198
- }
199
- case "TOOL_CALL_END": {
200
- const messages = patchMessageById(state.messages, action.messageId, (m) => {
201
- if (!m.toolCalls) return m;
202
- return {
203
- ...m,
204
- toolCalls: m.toolCalls.map(
205
- (tc) => tc.id === action.toolId ? {
206
- ...tc,
207
- output: action.output,
208
- status: action.status,
209
- streamingText: void 0,
210
- endedAt: Date.now()
211
- } : tc
212
- )
213
- };
214
- });
215
- return { ...state, messages };
216
- }
217
- case "STREAM_DONE": {
218
- const patched = patchMessageById(state.messages, action.id, (m) => ({
219
- ...m,
220
- isStreaming: false,
221
- tokensIn: action.tokensIn ?? m.tokensIn,
222
- tokensOut: action.tokensOut ?? m.tokensOut,
223
- sources: action.sources ?? m.sources
224
- }));
225
- const msg = patched.find((m) => m.id === action.id);
226
- const messages = msg && !msg.content && !msg.toolCalls?.length ? patched.filter((m) => m.id !== action.id) : patched;
227
- return { ...state, isStreaming: false, messages };
228
- }
229
- case "STREAM_CANCELLED": {
230
- const suffix = action.label ?? "[cancelled]";
231
- const messages = patchMessageById(state.messages, action.id, (m) => ({
232
- ...m,
233
- isStreaming: false,
234
- content: action.partialText + (action.partialText ? `
235
-
236
- _${suffix}_` : `_${suffix}_`)
237
- }));
238
- return { ...state, isStreaming: false, messages };
239
- }
240
- case "STREAM_ERROR": {
241
- const messages = action.id ? patchMessageById(state.messages, action.id, (m) => ({
242
- ...m,
243
- isStreaming: false,
244
- isError: true
245
- })) : state.messages;
246
- return { ...state, isStreaming: false, error: action.message, messages };
247
- }
248
- case "STREAM_CANCEL_PLACEHOLDER":
249
- return {
250
- ...state,
251
- isStreaming: false,
252
- messages: state.messages.filter((m) => m.id !== action.id)
253
- };
254
- case "STREAM_RESUME_EXISTING": {
255
- const lastIdx = (() => {
256
- for (let i = state.messages.length - 1; i >= 0; i--) {
257
- if (state.messages[i].role === "assistant") return i;
258
- }
259
- return -1;
260
- })();
261
- if (lastIdx === -1) return { ...state, isStreaming: true };
262
- const msgs = state.messages.slice();
263
- msgs[lastIdx] = { ...msgs[lastIdx], isStreaming: true };
264
- return { ...state, isStreaming: true, messages: msgs };
265
- }
266
- case "MESSAGE_EDIT": {
267
- const messages = patchMessageById(state.messages, action.id, (m) => ({
268
- ...m,
269
- content: action.content,
270
- version: (m.version ?? 0) + 1
271
- }));
272
- return { ...state, messages };
273
- }
274
- case "MESSAGE_DELETE":
275
- return {
276
- ...state,
277
- messages: state.messages.filter((m) => m.id !== action.id)
278
- };
279
- case "MESSAGES_CLEAR":
280
- return {
281
- ...state,
282
- messages: [],
283
- isStreaming: false,
284
- error: null
285
- };
286
- case "ERROR_SET":
287
- return { ...state, error: action.error };
288
- case "ATTACHMENT_PROGRESS": {
289
- const messages = patchMessageById(state.messages, action.messageId, (m) => {
290
- if (!m.attachments) return m;
291
- return {
292
- ...m,
293
- attachments: m.attachments.map(
294
- (a) => a.id === action.attachmentId ? {
295
- ...a,
296
- progress: action.progress ?? a.progress,
297
- status: action.status ?? a.status
298
- } : a
299
- )
300
- };
301
- });
302
- return { ...state, messages };
303
- }
304
- default: {
305
- return state;
306
- }
307
- }
308
- }
309
- chunkOLISEQHS_cjs.__name(reducer, "reducer");
310
-
311
- // src/tools/Chat/core/ids.ts
312
- var counter = 0;
313
- function createId(prefix = "m") {
314
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
315
- return `${prefix}_${crypto.randomUUID()}`;
316
- }
317
- counter += 1;
318
- return `${prefix}_${Date.now().toString(36)}_${counter.toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
319
- }
320
- chunkOLISEQHS_cjs.__name(createId, "createId");
321
- var SCOPES = ["bootstrap", "transport", "stream", "lifecycle", "tools", "error"];
322
- var cache = /* @__PURE__ */ new Map();
323
- function buildLogger(enabled) {
324
- const root = consola.consola.withTag("chat");
325
- const subs = Object.fromEntries(
326
- SCOPES.map((scope) => [scope, root.withTag(scope)])
327
- );
328
- if (!enabled) {
329
- for (const scope of SCOPES) {
330
- if (scope === "error") continue;
331
- subs[scope].level = -999;
332
- }
333
- }
334
- return { ...subs, enabled };
335
- }
336
- chunkOLISEQHS_cjs.__name(buildLogger, "buildLogger");
337
- function getChatLogger(debug) {
338
- const enabled = debug ?? lib.isDev;
339
- let logger = cache.get(enabled);
340
- if (!logger) {
341
- logger = buildLogger(enabled);
342
- cache.set(enabled, logger);
343
- }
344
- return logger;
345
- }
346
- chunkOLISEQHS_cjs.__name(getChatLogger, "getChatLogger");
347
-
348
- // src/tools/Chat/core/markdown.ts
349
- function createTokenBuffer(onFlush, windowMs = LIMITS.streamCoalesceMs) {
350
- let pending = "";
351
- let timer = null;
352
- let closed = false;
353
- const flush = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
354
- if (timer) {
355
- clearTimeout(timer);
356
- timer = null;
357
- }
358
- if (pending) {
359
- const out = pending;
360
- pending = "";
361
- onFlush(out);
362
- }
363
- }, "flush");
364
- return {
365
- push(delta) {
366
- if (closed || !delta) return;
367
- pending += delta;
368
- if (windowMs <= 0) {
369
- flush();
370
- return;
371
- }
372
- if (timer === null) {
373
- timer = setTimeout(flush, windowMs);
374
- }
375
- },
376
- flush,
377
- close() {
378
- closed = true;
379
- flush();
380
- }
381
- };
382
- }
383
- chunkOLISEQHS_cjs.__name(createTokenBuffer, "createTokenBuffer");
384
-
385
- // src/tools/Chat/hooks/useChat.ts
386
- function useChat(config) {
387
- const [state, dispatch] = react.useReducer(reducer, initialState);
388
- const stateRef = react.useRef(state);
389
- stateRef.current = state;
390
- const abortRef = react.useRef(null);
391
- const lastErrorRef = react.useRef(null);
392
- const streamingMsgIdRef = react.useRef(null);
393
- const bootstrapRef = react.useRef(null);
394
- const { transport, autoCreateSession = true, streaming = true, pageSize = LIMITS.pageSize } = config;
395
- const log = getChatLogger(config.debug);
396
- react.useEffect(() => {
397
- let cancelled = false;
398
- if (stateRef.current.sessionId) {
399
- return;
400
- }
401
- if (config.initialSessionId || autoCreateSession) {
402
- dispatch({ type: "HISTORY_LOAD_START" });
403
- }
404
- log.bootstrap.info("start", {
405
- mode: config.initialSessionId ? "resume" : autoCreateSession ? "create" : "idle",
406
- initialSessionId: config.initialSessionId
407
- });
408
- const run = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(async () => {
409
- const t0 = performance.now();
410
- try {
411
- if (config.initialSessionId) {
412
- if (!cancelled) {
413
- dispatch({
414
- type: "SESSION_SET",
415
- sessionId: config.initialSessionId
416
- });
417
- }
418
- const page = await transport.loadHistory(config.initialSessionId, null, pageSize);
419
- if (cancelled) {
420
- log.bootstrap.debug("cancelled (post-loadHistory)");
421
- return config.initialSessionId;
422
- }
423
- dispatch({
424
- type: "HISTORY_LOAD_DONE",
425
- messages: page.messages,
426
- hasMore: page.hasMore,
427
- cursor: page.nextCursor
428
- });
429
- log.bootstrap.success("resumed", {
430
- sessionId: config.initialSessionId,
431
- messages: page.messages.length,
432
- hasMore: page.hasMore,
433
- elapsedMs: Math.round(performance.now() - t0)
434
- });
435
- return config.initialSessionId;
436
- }
437
- if (autoCreateSession) {
438
- const info = await transport.createSession({ metadata: config.metadata });
439
- dispatch({
440
- type: "SESSION_SET",
441
- sessionId: info.sessionId,
442
- messages: info.messages ?? [],
443
- hasMore: info.hasMore ?? false,
444
- cursor: info.cursor ?? null
445
- });
446
- dispatch({
447
- type: "HISTORY_LOAD_DONE",
448
- messages: info.messages ?? [],
449
- hasMore: info.hasMore ?? false,
450
- cursor: info.cursor ?? null
451
- });
452
- log.bootstrap.success(cancelled ? "created (post-cancel)" : "created", {
453
- sessionId: info.sessionId,
454
- resumed: info.resumed ?? false,
455
- cancelled,
456
- elapsedMs: Math.round(performance.now() - t0)
457
- });
458
- return info.sessionId;
459
- }
460
- log.bootstrap.debug("idle (no initialSessionId, autoCreateSession=false)");
461
- return null;
462
- } catch (err) {
463
- const e = err instanceof Error ? err : new Error(String(err));
464
- if (cancelled) {
465
- log.bootstrap.debug("cancelled (in catch)", { message: e.message });
466
- return null;
467
- }
468
- lastErrorRef.current = e;
469
- dispatch({ type: "ERROR_SET", error: e.message });
470
- config.onError?.(e);
471
- log.error.error("bootstrap failed", { message: e.message, elapsedMs: Math.round(performance.now() - t0) });
472
- return null;
473
- }
474
- }, "run");
475
- bootstrapRef.current = run();
476
- return () => {
477
- cancelled = true;
478
- };
479
- }, []);
480
- const awaitSession = react.useCallback(async () => {
481
- if (stateRef.current.sessionId) return stateRef.current.sessionId;
482
- if (bootstrapRef.current) {
483
- const id = await bootstrapRef.current;
484
- if (id) return id;
485
- }
486
- return stateRef.current.sessionId;
487
- }, []);
488
- const consumeStream = react.useCallback(
489
- async (sessionId, content, attachments) => {
490
- const ctrl = new AbortController();
491
- abortRef.current = ctrl;
492
- const assistantId = createId("a");
493
- streamingMsgIdRef.current = assistantId;
494
- const iterator = transport.stream(sessionId, content, {
495
- signal: ctrl.signal,
496
- attachments,
497
- metadata: config.metadata
498
- });
499
- let peekedEvent = null;
500
- dispatch({ type: "STREAM_START", id: assistantId });
501
- config.onStreamStart?.(assistantId);
502
- log.stream.info("start", { sessionId, assistantId, chars: content.length });
503
- const tokenBuffer = createTokenBuffer(
504
- (delta) => dispatch({ type: "STREAM_CHUNK", delta })
505
- );
506
- let chunkCount = 0;
507
- let charsReceived = 0;
508
- const t0 = performance.now();
509
- try {
510
- const firstResult = await iterator.next();
511
- if (!firstResult.done) {
512
- const ev = firstResult.value;
513
- if (ev.type === "resume_start") {
514
- dispatch({ type: "STREAM_CANCEL_PLACEHOLDER", id: assistantId });
515
- dispatch({ type: "STREAM_RESUME_EXISTING" });
516
- } else {
517
- peekedEvent = ev;
518
- }
519
- }
520
- if (peekedEvent) handleEvent(peekedEvent);
521
- for await (const ev of iterator) {
522
- if (ctrl.signal.aborted) break;
523
- handleEvent(ev);
524
- }
525
- tokenBuffer.flush();
526
- if (stateRef.current.isStreaming) {
527
- dispatch({ type: "STREAM_DONE", id: assistantId });
528
- }
529
- const finalMsg = stateRef.current.messages.find((m) => m.id === assistantId);
530
- if (finalMsg) config.onMessageEnd?.(finalMsg);
531
- log.stream.success("done", {
532
- assistantId,
533
- chunks: chunkCount,
534
- chars: charsReceived,
535
- elapsedMs: Math.round(performance.now() - t0)
536
- });
537
- } catch (err) {
538
- tokenBuffer.close();
539
- if (ctrl.signal.aborted) {
540
- const partial = stateRef.current.messages.find((m) => m.id === assistantId)?.content ?? "";
541
- dispatch({ type: "STREAM_CANCELLED", id: assistantId, partialText: partial });
542
- log.stream.warn("cancelled", { assistantId, partialChars: partial.length });
543
- return;
544
- }
545
- const e = err instanceof Error ? err : new Error(String(err));
546
- lastErrorRef.current = e;
547
- dispatch({ type: "STREAM_ERROR", id: assistantId, message: e.message });
548
- config.onError?.(e);
549
- log.error.error("stream failed", { assistantId, message: e.message });
550
- } finally {
551
- tokenBuffer.close();
552
- if (abortRef.current === ctrl) abortRef.current = null;
553
- streamingMsgIdRef.current = null;
554
- }
555
- function handleEvent(ev) {
556
- switch (ev.type) {
557
- case "message_start":
558
- ev.messageId;
559
- log.stream.debug("message_start", { messageId: ev.messageId });
560
- return;
561
- case "chunk":
562
- tokenBuffer.push(ev.delta);
563
- chunkCount += 1;
564
- charsReceived += ev.delta.length;
565
- return;
566
- case "tool_activity":
567
- tokenBuffer.flush();
568
- dispatch({ type: "STREAM_TOOL_ACTIVITY", tool: ev.tool });
569
- log.tools.debug("activity", { tool: ev.tool, status: ev.status });
570
- return;
571
- case "tool_call_start": {
572
- tokenBuffer.flush();
573
- const toolCall = {
574
- id: ev.toolId,
575
- name: ev.name,
576
- input: ev.input,
577
- status: "running",
578
- startedAt: Date.now(),
579
- sourceHostname: ev.sourceHostname
580
- };
581
- dispatch({
582
- type: "TOOL_CALL_START",
583
- messageId: assistantId,
584
- toolCall
585
- });
586
- log.tools.info("call_start", { toolId: ev.toolId, name: ev.name });
587
- return;
588
- }
589
- case "tool_call_delta":
590
- dispatch({
591
- type: "TOOL_CALL_DELTA",
592
- messageId: assistantId,
593
- toolId: ev.toolId,
594
- delta: ev.delta
595
- });
596
- return;
597
- case "tool_call_end":
598
- dispatch({
599
- type: "TOOL_CALL_END",
600
- messageId: assistantId,
601
- toolId: ev.toolId,
602
- output: ev.output,
603
- status: ev.status
604
- });
605
- log.tools.info("call_end", { toolId: ev.toolId, status: ev.status });
606
- return;
607
- case "message_end":
608
- tokenBuffer.flush();
609
- dispatch({
610
- type: "STREAM_DONE",
611
- id: assistantId,
612
- tokensIn: ev.tokensIn,
613
- tokensOut: ev.tokensOut,
614
- sources: ev.sources
615
- });
616
- log.stream.debug("message_end", {
617
- tokensIn: ev.tokensIn,
618
- tokensOut: ev.tokensOut,
619
- sources: ev.sources?.length ?? 0
620
- });
621
- return;
622
- case "error":
623
- tokenBuffer.flush();
624
- dispatch({
625
- type: "STREAM_ERROR",
626
- id: assistantId,
627
- message: ev.message
628
- });
629
- log.error.error("stream event error", { code: ev.code, message: ev.message });
630
- return;
631
- }
632
- }
633
- chunkOLISEQHS_cjs.__name(handleEvent, "handleEvent");
634
- },
635
- [transport, config]
636
- );
637
- const consumeBuffered = react.useCallback(
638
- async (sessionId, content, attachments) => {
639
- const ctrl = new AbortController();
640
- abortRef.current = ctrl;
641
- try {
642
- const reply = await transport.send(sessionId, content, {
643
- signal: ctrl.signal,
644
- attachments,
645
- metadata: config.metadata
646
- });
647
- const placeholderId = createId("a");
648
- dispatch({ type: "STREAM_START", id: placeholderId });
649
- config.onStreamStart?.(placeholderId);
650
- dispatch({ type: "STREAM_CHUNK", delta: reply.content });
651
- dispatch({ type: "STREAM_DONE", id: placeholderId });
652
- config.onMessageEnd?.(reply);
653
- } catch (err) {
654
- const e = err instanceof Error ? err : new Error(String(err));
655
- lastErrorRef.current = e;
656
- dispatch({ type: "STREAM_ERROR", message: e.message });
657
- config.onError?.(e);
658
- } finally {
659
- if (abortRef.current === ctrl) abortRef.current = null;
660
- }
661
- },
662
- [transport, config]
663
- );
664
- const sendMessage = react.useCallback(
665
- async (content, attachments) => {
666
- log.lifecycle.info("sendMessage", {
667
- chars: content.length,
668
- attachments: attachments?.length ?? 0,
669
- hasSession: !!stateRef.current.sessionId
670
- });
671
- const sessionId = await awaitSession();
672
- if (!sessionId) {
673
- const e = new Error("No active session");
674
- lastErrorRef.current = e;
675
- dispatch({ type: "ERROR_SET", error: e.message });
676
- config.onError?.(e);
677
- log.error.error("sendMessage aborted: no session");
678
- return;
679
- }
680
- if (!content.trim() && !(attachments && attachments.length > 0)) {
681
- log.lifecycle.debug("sendMessage skipped (empty)");
682
- return;
683
- }
684
- if (stateRef.current.isStreaming) {
685
- log.lifecycle.debug("sendMessage skipped (already streaming)");
686
- return;
687
- }
688
- const userMsg = {
689
- id: createId("u"),
690
- role: "user",
691
- content,
692
- createdAt: Date.now(),
693
- attachments,
694
- sender: config.userPersona
695
- };
696
- dispatch({ type: "MESSAGE_USER_ADD", message: userMsg });
697
- config.onMessageSent?.(userMsg);
698
- let outbound = content;
699
- if (config.onBeforeSend) {
700
- try {
701
- outbound = await config.onBeforeSend(content);
702
- } catch (err) {
703
- log.error.error("onBeforeSend threw \u2014 falling back to original content", err);
704
- }
705
- }
706
- if (streaming) {
707
- await consumeStream(sessionId, outbound, attachments);
708
- } else {
709
- await consumeBuffered(sessionId, outbound, attachments);
710
- }
711
- },
712
- [streaming, consumeStream, consumeBuffered, config, awaitSession, log]
713
- );
714
- const cancelStream = react.useCallback(() => {
715
- abortRef.current?.abort();
716
- }, []);
717
- const regenerate = react.useCallback(
718
- async (messageId) => {
719
- log.lifecycle.info("regenerate", { messageId: messageId ?? "(last)" });
720
- const messages = stateRef.current.messages;
721
- let targetUserIdx = -1;
722
- if (messageId) {
723
- const idx = messages.findIndex((m) => m.id === messageId);
724
- if (idx !== -1) {
725
- targetUserIdx = messages[idx].role === "user" ? idx : findPreviousUserIndex(messages, idx);
726
- }
727
- } else {
728
- targetUserIdx = findLastUserIndex(messages);
729
- }
730
- if (targetUserIdx === -1) return;
731
- const userMsg = messages[targetUserIdx];
732
- for (let i = messages.length - 1; i > targetUserIdx; i -= 1) {
733
- dispatch({ type: "MESSAGE_DELETE", id: messages[i].id });
734
- }
735
- const sessionId = await awaitSession();
736
- if (!sessionId) return;
737
- if (streaming) {
738
- await consumeStream(sessionId, userMsg.content, userMsg.attachments);
739
- } else {
740
- await consumeBuffered(sessionId, userMsg.content, userMsg.attachments);
741
- }
742
- },
743
- [streaming, consumeStream, consumeBuffered, awaitSession]
744
- );
745
- const editMessage = react.useCallback(
746
- async (id, content) => {
747
- dispatch({ type: "MESSAGE_EDIT", id, content });
748
- const msg = stateRef.current.messages.find((m) => m.id === id);
749
- if (msg?.role === "user") {
750
- await regenerate(id);
751
- }
752
- },
753
- [regenerate]
754
- );
755
- const deleteMessage = react.useCallback((id) => {
756
- dispatch({ type: "MESSAGE_DELETE", id });
757
- }, []);
758
- const clearMessages = react.useCallback(() => {
759
- abortRef.current?.abort();
760
- dispatch({ type: "MESSAGES_CLEAR" });
761
- }, []);
762
- const loadMore = react.useCallback(async () => {
763
- const sessionId = stateRef.current.sessionId;
764
- if (!sessionId) return;
765
- if (stateRef.current.isLoadingMore || !stateRef.current.hasMore) return;
766
- dispatch({ type: "HISTORY_MORE_START" });
767
- try {
768
- const page = await transport.loadHistory(
769
- sessionId,
770
- stateRef.current.oldestCursor,
771
- pageSize
772
- );
773
- dispatch({
774
- type: "HISTORY_MORE_DONE",
775
- messages: page.messages,
776
- hasMore: page.hasMore,
777
- cursor: page.nextCursor
778
- });
779
- } catch (err) {
780
- const e = err instanceof Error ? err : new Error(String(err));
781
- lastErrorRef.current = e;
782
- dispatch({ type: "ERROR_SET", error: e.message });
783
- config.onError?.(e);
784
- }
785
- }, [transport, pageSize, config]);
786
- const newSession = react.useCallback(async () => {
787
- log.lifecycle.info("newSession", { previous: stateRef.current.sessionId });
788
- abortRef.current?.abort();
789
- const previous = stateRef.current.sessionId;
790
- if (previous) {
791
- try {
792
- await transport.closeSession(previous);
793
- } catch {
794
- }
795
- }
796
- dispatch({ type: "MESSAGES_CLEAR" });
797
- try {
798
- const info = await transport.createSession({ metadata: config.metadata });
799
- dispatch({
800
- type: "SESSION_SET",
801
- sessionId: info.sessionId,
802
- messages: info.messages ?? [],
803
- hasMore: info.hasMore ?? false,
804
- cursor: info.cursor ?? null
805
- });
806
- log.lifecycle.success("newSession ok", { sessionId: info.sessionId });
807
- } catch (err) {
808
- const e = err instanceof Error ? err : new Error(String(err));
809
- lastErrorRef.current = e;
810
- dispatch({ type: "ERROR_SET", error: e.message });
811
- config.onError?.(e);
812
- log.error.error("newSession failed", { message: e.message });
813
- }
814
- }, [transport, config]);
815
- return {
816
- ...state,
817
- sendMessage,
818
- cancelStream,
819
- regenerate,
820
- editMessage,
821
- deleteMessage,
822
- clearMessages,
823
- loadMore,
824
- newSession,
825
- lastError: lastErrorRef.current
826
- };
827
- }
828
- chunkOLISEQHS_cjs.__name(useChat, "useChat");
829
- function findLastUserIndex(messages) {
830
- for (let i = messages.length - 1; i >= 0; i -= 1) {
831
- if (messages[i].role === "user") return i;
832
- }
833
- return -1;
834
- }
835
- chunkOLISEQHS_cjs.__name(findLastUserIndex, "findLastUserIndex");
836
- function findPreviousUserIndex(messages, from) {
837
- for (let i = from - 1; i >= 0; i -= 1) {
838
- if (messages[i].role === "user") return i;
839
- }
840
- return -1;
841
- }
842
- chunkOLISEQHS_cjs.__name(findPreviousUserIndex, "findPreviousUserIndex");
843
- var DEFAULT_MOBILE_QUERY = "(max-width: 640px)";
844
- function useChatLayout(config = {}) {
845
- const {
846
- defaultMode = "closed",
847
- storageKey = STORAGE_KEYS.mode,
848
- sidebarStorageKey = STORAGE_KEYS.sidebarWidth,
849
- reserveCssVar = CSS_VARS.reserve,
850
- defaultSidebarWidth = DEFAULT_SIDEBAR.width,
851
- minSidebarWidth = DEFAULT_SIDEBAR.min,
852
- maxSidebarWidth = DEFAULT_SIDEBAR.max,
853
- mobileQuery = DEFAULT_MOBILE_QUERY
854
- } = config;
855
- const [mode, setMode] = hooks.useLocalStorage(storageKey, defaultMode);
856
- const [sidebarWidthRaw, setSidebarWidthRaw] = hooks.useLocalStorage(
857
- sidebarStorageKey,
858
- defaultSidebarWidth
859
- );
860
- const isMobile = hooks.useMediaQuery(mobileQuery);
861
- const setSidebarWidth = react.useCallback(
862
- (w) => {
863
- const clamped = Math.max(minSidebarWidth, Math.min(maxSidebarWidth, w));
864
- setSidebarWidthRaw(clamped);
865
- },
866
- [setSidebarWidthRaw, minSidebarWidth, maxSidebarWidth]
867
- );
868
- const [previousOpenMode, setPreviousOpenMode] = react.useState(
869
- mode === "closed" ? "embedded" : mode
870
- );
871
- react.useEffect(() => {
872
- if (mode !== "closed") setPreviousOpenMode(mode);
873
- }, [mode]);
874
- const open = react.useCallback(() => {
875
- setMode(previousOpenMode === "closed" ? "embedded" : previousOpenMode);
876
- }, [setMode, previousOpenMode]);
877
- const close = react.useCallback(() => setMode("closed"), [setMode]);
878
- const toggle = react.useCallback(() => {
879
- setMode(mode === "closed" ? previousOpenMode : "closed");
880
- }, [setMode, mode, previousOpenMode]);
881
- const effectiveMode = react.useMemo(() => {
882
- if (mode === "closed") return "closed";
883
- if (isMobile && (mode === "sidebar" || mode === "floating")) return "fullscreen";
884
- return mode;
885
- }, [mode, isMobile]);
886
- react.useEffect(() => {
887
- if (typeof document === "undefined") return;
888
- const root = document.documentElement;
889
- if (effectiveMode === "sidebar") {
890
- root.style.setProperty(reserveCssVar, `${sidebarWidthRaw}px`);
891
- } else {
892
- root.style.removeProperty(reserveCssVar);
893
- }
894
- return () => {
895
- root.style.removeProperty(reserveCssVar);
896
- };
897
- }, [effectiveMode, sidebarWidthRaw, reserveCssVar]);
898
- return {
899
- mode,
900
- setMode,
901
- open,
902
- close,
903
- toggle,
904
- sidebarWidth: sidebarWidthRaw,
905
- setSidebarWidth,
906
- isMobile,
907
- effectiveMode
908
- };
909
- }
910
- chunkOLISEQHS_cjs.__name(useChatLayout, "useChatLayout");
911
-
912
- // src/tools/Chat/core/audio/audioBus.ts
913
- var unlocked = false;
914
- var unlockListeners = /* @__PURE__ */ new Set();
915
- function setUnlocked(value) {
916
- if (unlocked === value) return;
917
- unlocked = value;
918
- for (const cb of unlockListeners) cb(value);
919
- }
920
- chunkOLISEQHS_cjs.__name(setUnlocked, "setUnlocked");
921
- function createAudioBus(options) {
922
- if (typeof window === "undefined") {
923
- return noopBus();
924
- }
925
- let sounds = options.sounds;
926
- const cache2 = /* @__PURE__ */ new Map();
927
- const getOrCreate = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((url) => {
928
- const hit = cache2.get(url);
929
- if (hit) return hit;
930
- const el = new Audio(url);
931
- el.preload = "auto";
932
- el.crossOrigin = "anonymous";
933
- cache2.set(url, el);
934
- return el;
935
- }, "getOrCreate");
936
- const resolveUrl = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((event) => {
937
- const v = sounds[event];
938
- if (!v) return null;
939
- return v;
940
- }, "resolveUrl");
941
- const play = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((event) => {
942
- if (options.getMuted()) return;
943
- if (!options.isEnabled(event)) return;
944
- const url = resolveUrl(event);
945
- if (!url) return;
946
- getOrCreate(url);
947
- const fresh = new Audio(url);
948
- fresh.preload = "auto";
949
- fresh.volume = options.getVolume();
950
- const p = fresh.play();
951
- if (p && typeof p.catch === "function") {
952
- p.catch(() => {
953
- });
954
- }
955
- }, "play");
956
- const preload = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((event) => {
957
- const url = resolveUrl(event);
958
- if (!url) return;
959
- const el = getOrCreate(url);
960
- try {
961
- el.load();
962
- } catch {
963
- }
964
- }, "preload");
965
- const unlock = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
966
- if (unlocked) return;
967
- for (const el of cache2.values()) {
968
- const wasMuted = el.muted;
969
- el.muted = true;
970
- const p = el.play();
971
- if (p && typeof p.then === "function") {
972
- p.then(() => {
973
- el.pause();
974
- el.currentTime = 0;
975
- el.muted = wasMuted;
976
- }).catch(() => {
977
- el.muted = wasMuted;
978
- });
979
- } else {
980
- el.pause();
981
- el.muted = wasMuted;
982
- }
983
- }
984
- setUnlocked(true);
985
- }, "unlock");
986
- return {
987
- play,
988
- preload,
989
- unlock,
990
- isUnlocked: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => unlocked, "isUnlocked"),
991
- subscribeUnlock(cb) {
992
- unlockListeners.add(cb);
993
- return () => unlockListeners.delete(cb);
994
- },
995
- setSounds(next) {
996
- sounds = next;
997
- },
998
- dispose() {
999
- cache2.clear();
1000
- }
1001
- };
1002
- }
1003
- chunkOLISEQHS_cjs.__name(createAudioBus, "createAudioBus");
1004
- function noopBus() {
1005
- return {
1006
- play: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => void 0, "play"),
1007
- preload: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => void 0, "preload"),
1008
- unlock: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => void 0, "unlock"),
1009
- isUnlocked: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => false, "isUnlocked"),
1010
- subscribeUnlock: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => () => void 0, "subscribeUnlock"),
1011
- setSounds: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => void 0, "setSounds"),
1012
- dispose: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => void 0, "dispose")
1013
- };
1014
- }
1015
- chunkOLISEQHS_cjs.__name(noopBus, "noopBus");
1016
- var STORAGE_KEY = "djangocfg-chat-audio:prefs";
1017
- var clamp01 = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((v) => {
1018
- if (!Number.isFinite(v)) return 1;
1019
- return v < 0 ? 0 : v > 1 ? 1 : v;
1020
- }, "clamp01");
1021
- var useChatAudioPrefs = zustand.create()(
1022
- middleware.persist(
1023
- (set) => ({
1024
- volume: 1,
1025
- muted: false,
1026
- enabled: {},
1027
- setVolume: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((v) => set({ volume: clamp01(v) }), "setVolume"),
1028
- setMuted: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((m) => set({ muted: !!m }), "setMuted"),
1029
- setEventEnabled: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((event, enabled) => set((s) => ({ enabled: { ...s.enabled, [event]: enabled } })), "setEventEnabled")
1030
- }),
1031
- {
1032
- name: STORAGE_KEY,
1033
- storage: middleware.createJSONStorage(() => {
1034
- if (typeof window === "undefined") {
1035
- return {
1036
- getItem: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => null, "getItem"),
1037
- setItem: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => void 0, "setItem"),
1038
- removeItem: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => void 0, "removeItem")
1039
- };
1040
- }
1041
- return window.localStorage;
1042
- }),
1043
- partialize: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((s) => ({ volume: s.volume, muted: s.muted, enabled: s.enabled }), "partialize"),
1044
- version: 1
1045
- }
1046
- )
1047
- );
1048
-
1049
- // src/tools/Chat/hooks/useChatAudio.ts
1050
- var ALL_EVENTS = [
1051
- "messageSent",
1052
- "messageReceived",
1053
- "streamStart",
1054
- "error",
1055
- "mention",
1056
- "notification"
1057
- ];
1058
- function readReducedMotion() {
1059
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
1060
- return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1061
- }
1062
- chunkOLISEQHS_cjs.__name(readReducedMotion, "readReducedMotion");
1063
- function readReducedData() {
1064
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
1065
- return window.matchMedia("(prefers-reduced-data: reduce)").matches;
1066
- }
1067
- chunkOLISEQHS_cjs.__name(readReducedData, "readReducedData");
1068
- function readVisibilityHidden() {
1069
- if (typeof document === "undefined") return false;
1070
- return document.visibilityState === "hidden";
1071
- }
1072
- chunkOLISEQHS_cjs.__name(readVisibilityHidden, "readVisibilityHidden");
1073
- function useChatAudio(config = {}) {
1074
- const {
1075
- sounds = {},
1076
- volume: volumeOverride,
1077
- muted: mutedOverride,
1078
- shouldPlay,
1079
- respectReducedMotion = true,
1080
- respectReducedData = true,
1081
- muteWhenHidden = true
1082
- } = config;
1083
- const volume = useChatAudioPrefs((s) => volumeOverride != null ? volumeOverride : s.volume);
1084
- const mutedPersisted = useChatAudioPrefs((s) => s.muted);
1085
- const muted = mutedOverride != null ? mutedOverride : mutedPersisted;
1086
- const enabledMap = useChatAudioPrefs((s) => s.enabled);
1087
- const setVolumePref = useChatAudioPrefs((s) => s.setVolume);
1088
- const setMutedPref = useChatAudioPrefs((s) => s.setMuted);
1089
- const setEventEnabledPref = useChatAudioPrefs((s) => s.setEventEnabled);
1090
- const volumeRef = react.useRef(volume);
1091
- volumeRef.current = volume;
1092
- const mutedRef = react.useRef(muted);
1093
- mutedRef.current = muted;
1094
- const enabledRef = react.useRef(enabledMap);
1095
- enabledRef.current = enabledMap;
1096
- const reducedMotionRef = react.useRef(readReducedMotion());
1097
- const reducedDataRef = react.useRef(readReducedData());
1098
- const hiddenRef = react.useRef(readVisibilityHidden());
1099
- const shouldPlayRef = react.useRef(shouldPlay);
1100
- shouldPlayRef.current = shouldPlay;
1101
- react.useEffect(() => {
1102
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
1103
- const mqMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
1104
- const mqData = window.matchMedia("(prefers-reduced-data: reduce)");
1105
- const onMotion = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
1106
- reducedMotionRef.current = mqMotion.matches;
1107
- }, "onMotion");
1108
- const onData = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
1109
- reducedDataRef.current = mqData.matches;
1110
- }, "onData");
1111
- mqMotion.addEventListener("change", onMotion);
1112
- mqData.addEventListener("change", onData);
1113
- return () => {
1114
- mqMotion.removeEventListener("change", onMotion);
1115
- mqData.removeEventListener("change", onData);
1116
- };
1117
- }, []);
1118
- react.useEffect(() => {
1119
- if (!muteWhenHidden || typeof document === "undefined") return;
1120
- const onVis = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
1121
- hiddenRef.current = document.visibilityState === "hidden";
1122
- }, "onVis");
1123
- document.addEventListener("visibilitychange", onVis);
1124
- return () => document.removeEventListener("visibilitychange", onVis);
1125
- }, [muteWhenHidden]);
1126
- const busRef = react.useRef(null);
1127
- if (busRef.current === null) {
1128
- busRef.current = createAudioBus({
1129
- sounds,
1130
- getVolume: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => volumeRef.current, "getVolume"),
1131
- getMuted: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => effectiveMuted(), "getMuted"),
1132
- isEnabled: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((event) => isEnabledImpl(event), "isEnabled")
1133
- });
1134
- }
1135
- function effectiveMuted() {
1136
- if (mutedRef.current) return true;
1137
- if (muteWhenHidden && hiddenRef.current) return true;
1138
- if (respectReducedMotion && reducedMotionRef.current) return true;
1139
- if (respectReducedData && reducedDataRef.current) return true;
1140
- return false;
1141
- }
1142
- chunkOLISEQHS_cjs.__name(effectiveMuted, "effectiveMuted");
1143
- function isEnabledImpl(event) {
1144
- if (shouldPlayRef.current && shouldPlayRef.current(event) === false) return false;
1145
- const flag = enabledRef.current[event];
1146
- if (flag === false) return false;
1147
- return true;
1148
- }
1149
- chunkOLISEQHS_cjs.__name(isEnabledImpl, "isEnabledImpl");
1150
- react.useEffect(() => {
1151
- busRef.current?.setSounds(sounds);
1152
- }, [sounds]);
1153
- react.useEffect(() => {
1154
- const bus = busRef.current;
1155
- if (!bus) return;
1156
- for (const event of ALL_EVENTS) {
1157
- bus.preload(event);
1158
- }
1159
- }, [sounds]);
1160
- react.useEffect(() => {
1161
- const bus = busRef.current;
1162
- return () => {
1163
- bus?.dispose();
1164
- };
1165
- }, []);
1166
- const isUnlocked = react.useSyncExternalStore(
1167
- react.useCallback((cb) => busRef.current?.subscribeUnlock(cb) ?? (() => void 0), []),
1168
- () => busRef.current?.isUnlocked() ?? false,
1169
- () => false
1170
- );
1171
- const play = react.useCallback((event) => {
1172
- busRef.current?.play(event);
1173
- }, []);
1174
- const preload = react.useCallback((event) => {
1175
- busRef.current?.preload(event);
1176
- }, []);
1177
- const unlock = react.useCallback(() => {
1178
- busRef.current?.unlock();
1179
- }, []);
1180
- const isEventEnabled = react.useCallback(
1181
- (event) => isEnabledImpl(event),
1182
- // eslint-disable-next-line react-hooks/exhaustive-deps
1183
- []
1184
- );
1185
- const api = react.useMemo(
1186
- () => ({
1187
- play,
1188
- preload,
1189
- unlock,
1190
- isUnlocked,
1191
- muted,
1192
- setMuted: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((m) => setMutedPref(m), "setMuted"),
1193
- volume,
1194
- setVolume: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((v) => setVolumePref(v), "setVolume"),
1195
- isEventEnabled,
1196
- setEventEnabled: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((event, enabled) => setEventEnabledPref(event, enabled), "setEventEnabled")
1197
- }),
1198
- [play, preload, unlock, isUnlocked, muted, volume, isEventEnabled, setMutedPref, setVolumePref, setEventEnabledPref]
1199
- );
1200
- return api;
1201
- }
1202
- chunkOLISEQHS_cjs.__name(useChatAudio, "useChatAudio");
1203
- var Ctx = react.createContext(null);
1204
- function ChatProvider({
1205
- transport,
1206
- config = {},
1207
- initialSessionId,
1208
- autoCreateSession,
1209
- streaming,
1210
- audio,
1211
- debug,
1212
- onBeforeSend,
1213
- children
1214
- }) {
1215
- const audioApi = useChatAudio(audio ?? {});
1216
- const audioRef = react.useRef(audioApi);
1217
- audioRef.current = audioApi;
1218
- const onMessageSent = react.useCallback(() => audioRef.current.play("messageSent"), []);
1219
- const onMessageEnd = react.useCallback(() => audioRef.current.play("messageReceived"), []);
1220
- const onStreamStart = react.useCallback(() => audioRef.current.play("streamStart"), []);
1221
- const onError = react.useCallback(() => audioRef.current.play("error"), []);
1222
- const chat = useChat({
1223
- transport,
1224
- initialSessionId,
1225
- autoCreateSession,
1226
- streaming,
1227
- debug,
1228
- metadata: {
1229
- locale: config.locale ?? config.prefs?.locale,
1230
- slug: config.slug
1231
- },
1232
- userPersona: config.user,
1233
- onMessageSent,
1234
- onMessageEnd,
1235
- onStreamStart,
1236
- onError,
1237
- onBeforeSend
1238
- });
1239
- const layout = useChatLayout({ defaultMode: "embedded" });
1240
- const rootRef = react.useRef(null);
1241
- react.useEffect(() => {
1242
- if (audioApi.isUnlocked) return;
1243
- const root = rootRef.current;
1244
- if (!root) return;
1245
- const handler = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
1246
- audioApi.unlock();
1247
- }, "handler");
1248
- root.addEventListener("pointerdown", handler, { once: true, capture: true });
1249
- root.addEventListener("keydown", handler, { once: true, capture: true });
1250
- return () => {
1251
- root.removeEventListener("pointerdown", handler, { capture: true });
1252
- root.removeEventListener("keydown", handler, { capture: true });
1253
- };
1254
- }, [audioApi]);
1255
- const labels = react.useMemo(
1256
- () => ({ ...DEFAULT_LABELS, ...config.labels ?? {} }),
1257
- [config.labels]
1258
- );
1259
- const hasAudio = react.useMemo(() => {
1260
- const sounds = audio?.sounds;
1261
- if (!sounds) return false;
1262
- return Object.values(sounds).some(
1263
- (v) => typeof v === "string" && v.length > 0
1264
- );
1265
- }, [audio]);
1266
- const [composer, setComposer] = react.useState(null);
1267
- const registerComposer = react.useCallback((handle) => {
1268
- setComposer(handle);
1269
- }, []);
1270
- const value = react.useMemo(
1271
- () => ({ ...chat, layout, config, labels, audio: audioApi, hasAudio, composer, registerComposer }),
1272
- [chat, layout, config, labels, audioApi, hasAudio, composer, registerComposer]
1273
- );
1274
- return /* @__PURE__ */ jsxRuntime.jsx(Ctx.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: rootRef, style: { display: "contents" }, children }) });
1275
- }
1276
- chunkOLISEQHS_cjs.__name(ChatProvider, "ChatProvider");
1277
- function useChatContext() {
1278
- const v = react.useContext(Ctx);
1279
- if (!v) throw new Error("useChatContext must be used inside <ChatProvider>");
1280
- return v;
1281
- }
1282
- chunkOLISEQHS_cjs.__name(useChatContext, "useChatContext");
1283
- function useChatContextOptional() {
1284
- return react.useContext(Ctx);
1285
- }
1286
- chunkOLISEQHS_cjs.__name(useChatContextOptional, "useChatContextOptional");
1287
-
1288
- // src/tools/Chat/utils/sanitizeDraft.ts
1289
- function sanitizeDraft(input) {
1290
- if (!input) return "";
1291
- let s = input.replace(/[​‌‍]/g, "");
1292
- s = s.replace(/\r\n?/g, "\n");
1293
- s = s.trim();
1294
- return s;
1295
- }
1296
- chunkOLISEQHS_cjs.__name(sanitizeDraft, "sanitizeDraft");
1297
- function isSubmittableDraft(input) {
1298
- return sanitizeDraft(input).length > 0;
1299
- }
1300
- chunkOLISEQHS_cjs.__name(isSubmittableDraft, "isSubmittableDraft");
1301
-
1302
- // src/tools/Chat/hooks/useChatComposer.ts
1303
- var MAX_TEXTAREA_HEIGHT = 240;
1304
- function useChatComposer(options) {
1305
- const {
1306
- onSubmit,
1307
- initialValue = "",
1308
- maxLength = LIMITS.messageMaxLength,
1309
- maxAttachments = LIMITS.attachmentsMax,
1310
- disabled = false,
1311
- submitOn = "enter",
1312
- history = { enabled: true, size: LIMITS.composerHistorySize },
1313
- onPasteFiles,
1314
- persistKey,
1315
- preserveExactValue = false
1316
- } = options;
1317
- const initialFromStorage = (() => {
1318
- if (!persistKey || typeof window === "undefined") return initialValue;
1319
- try {
1320
- const stored = window.sessionStorage.getItem(`chat:draft:${persistKey}`);
1321
- return stored && stored.length > 0 ? stored : initialValue;
1322
- } catch {
1323
- return initialValue;
1324
- }
1325
- })();
1326
- const [value, setValueState] = react.useState(initialFromStorage);
1327
- react.useEffect(() => {
1328
- if (!persistKey || typeof window === "undefined") return;
1329
- try {
1330
- const k = `chat:draft:${persistKey}`;
1331
- if (value.length > 0) window.sessionStorage.setItem(k, value);
1332
- else window.sessionStorage.removeItem(k);
1333
- } catch {
1334
- }
1335
- }, [value, persistKey]);
1336
- const [attachments, setAttachments] = react.useState([]);
1337
- const [isSubmitting, setIsSubmitting] = react.useState(false);
1338
- const textareaRef = react.useRef(null);
1339
- const historyRef = react.useRef({ items: [], index: -1 });
1340
- const setValue = react.useCallback(
1341
- (next) => {
1342
- setValueState(next.length > maxLength ? next.slice(0, maxLength) : next);
1343
- },
1344
- [maxLength]
1345
- );
1346
- react.useEffect(() => {
1347
- const el = textareaRef.current;
1348
- if (!el) return;
1349
- el.style.height = "auto";
1350
- el.style.height = `${Math.min(el.scrollHeight, MAX_TEXTAREA_HEIGHT)}px`;
1351
- }, [value]);
1352
- const reset = react.useCallback(() => {
1353
- setValueState("");
1354
- setAttachments([]);
1355
- historyRef.current.index = -1;
1356
- }, []);
1357
- const focus = react.useCallback(() => {
1358
- requestAnimationFrame(() => textareaRef.current?.focus());
1359
- }, []);
1360
- const submit = react.useCallback(async () => {
1361
- const cleaned = preserveExactValue ? value : sanitizeDraft(value);
1362
- if (!cleaned && attachments.length === 0 || isSubmitting || disabled) return;
1363
- setIsSubmitting(true);
1364
- try {
1365
- if (history.enabled !== false && cleaned) {
1366
- const buf = historyRef.current.items;
1367
- if (buf[buf.length - 1] !== cleaned) {
1368
- buf.push(cleaned);
1369
- if (buf.length > (history.size ?? LIMITS.composerHistorySize)) buf.shift();
1370
- }
1371
- historyRef.current.index = -1;
1372
- }
1373
- const snapshot = [...attachments];
1374
- reset();
1375
- await onSubmit(cleaned, snapshot);
1376
- } finally {
1377
- setIsSubmitting(false);
1378
- }
1379
- }, [value, attachments, isSubmitting, disabled, history, onSubmit, reset, preserveExactValue]);
1380
- const addAttachment = react.useCallback(
1381
- (a) => {
1382
- setAttachments((prev) => {
1383
- if (prev.some((p) => p.id === a.id)) {
1384
- return prev.map((p) => p.id === a.id ? a : p);
1385
- }
1386
- if (prev.length >= maxAttachments) return prev;
1387
- return [...prev, a];
1388
- });
1389
- },
1390
- [maxAttachments]
1391
- );
1392
- const removeAttachment = react.useCallback((id) => {
1393
- setAttachments((prev) => prev.filter((a) => a.id !== id));
1394
- }, []);
1395
- const recallPrevious = react.useCallback(() => {
1396
- const { items } = historyRef.current;
1397
- if (!items.length) return;
1398
- const next = historyRef.current.index < 0 ? items.length - 1 : Math.max(0, historyRef.current.index - 1);
1399
- historyRef.current.index = next;
1400
- setValueState(items[next]);
1401
- }, []);
1402
- const recallNext = react.useCallback(() => {
1403
- const { items } = historyRef.current;
1404
- if (!items.length || historyRef.current.index < 0) return;
1405
- const next = historyRef.current.index + 1;
1406
- if (next >= items.length) {
1407
- historyRef.current.index = -1;
1408
- setValueState("");
1409
- return;
1410
- }
1411
- historyRef.current.index = next;
1412
- setValueState(items[next]);
1413
- }, []);
1414
- const onChange = react.useCallback(
1415
- (e) => {
1416
- setValue(e.target.value);
1417
- },
1418
- [setValue]
1419
- );
1420
- const onKeyDown = react.useCallback(
1421
- (e) => {
1422
- if (e.key === "Enter") {
1423
- const isCmd = e.metaKey || e.ctrlKey;
1424
- const shouldSend = submitOn === "cmd+enter" ? isCmd : !e.shiftKey;
1425
- if (shouldSend) {
1426
- e.preventDefault();
1427
- void submit();
1428
- }
1429
- return;
1430
- }
1431
- if (history.enabled !== false && value === "" && attachments.length === 0) {
1432
- if (e.key === "ArrowUp") {
1433
- e.preventDefault();
1434
- recallPrevious();
1435
- } else if (e.key === "ArrowDown" && historyRef.current.index >= 0) {
1436
- e.preventDefault();
1437
- recallNext();
1438
- }
1439
- }
1440
- },
1441
- [submitOn, submit, history, value, attachments.length, recallPrevious, recallNext]
1442
- );
1443
- const onPaste = react.useCallback(
1444
- (e) => {
1445
- const files = Array.from(e.clipboardData?.files ?? []);
1446
- if (files.length && onPasteFiles) {
1447
- e.preventDefault();
1448
- onPasteFiles(files);
1449
- }
1450
- },
1451
- [onPasteFiles]
1452
- );
1453
- const canSubmit = !disabled && !isSubmitting && ((preserveExactValue ? value.trim().length : sanitizeDraft(value).length) > 0 || attachments.length > 0);
1454
- return {
1455
- value,
1456
- setValue,
1457
- attachments,
1458
- addAttachment,
1459
- removeAttachment,
1460
- isSubmitting,
1461
- canSubmit,
1462
- submit,
1463
- reset,
1464
- focus,
1465
- textareaRef,
1466
- textareaProps: {
1467
- ref: textareaRef,
1468
- value,
1469
- disabled,
1470
- onChange,
1471
- onKeyDown,
1472
- onPaste
1473
- },
1474
- recallPrevious,
1475
- recallNext
1476
- };
1477
- }
1478
- chunkOLISEQHS_cjs.__name(useChatComposer, "useChatComposer");
1479
- function AttachmentsGrid({
1480
- attachments,
1481
- maxVisible,
1482
- onClick,
1483
- onRemove,
1484
- isInComposer = false,
1485
- layout = "wrap",
1486
- className
1487
- }) {
1488
- if (!attachments?.length) return null;
1489
- const visible = maxVisible ? attachments.slice(0, maxVisible) : attachments;
1490
- return /* @__PURE__ */ jsxRuntime.jsx(
1491
- "div",
1492
- {
1493
- className: lib.cn(
1494
- layout === "grid" ? "grid grid-cols-3 gap-2" : "flex flex-wrap gap-2",
1495
- className
1496
- ),
1497
- children: visible.map((a) => /* @__PURE__ */ jsxRuntime.jsx(
1498
- AttachmentTile,
1499
- {
1500
- attachment: a,
1501
- isInComposer,
1502
- onClick: onClick ? () => onClick(a) : void 0,
1503
- onRemove: onRemove ? () => onRemove(a) : void 0
1504
- },
1505
- a.id
1506
- ))
1507
- }
1508
- );
1509
- }
1510
- chunkOLISEQHS_cjs.__name(AttachmentsGrid, "AttachmentsGrid");
1511
- function AttachmentsList({
1512
- attachments,
1513
- maxVisible,
1514
- onClick,
1515
- onRemove,
1516
- renderers,
1517
- isInComposer = false,
1518
- className
1519
- }) {
1520
- if (!attachments?.length) return null;
1521
- const visible = maxVisible ? attachments.slice(0, maxVisible) : attachments;
1522
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: lib.cn("flex w-full flex-col gap-2", className), children: visible.map((a) => {
1523
- const renderer = renderers?.[a.type] ?? renderers?.default;
1524
- const args = {
1525
- attachment: a,
1526
- isInComposer,
1527
- onClick: onClick ? () => onClick(a) : void 0,
1528
- onRemove: onRemove ? () => onRemove(a) : void 0
1529
- };
1530
- if (renderer) {
1531
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full min-w-0", children: [
1532
- renderer(args),
1533
- args.onRemove ? /* @__PURE__ */ jsxRuntime.jsx(RemoveBtn, { onRemove: args.onRemove }) : null
1534
- ] }, a.id);
1535
- }
1536
- return /* @__PURE__ */ jsxRuntime.jsx(AttachmentTile, { ...args }, a.id);
1537
- }) });
1538
- }
1539
- chunkOLISEQHS_cjs.__name(AttachmentsList, "AttachmentsList");
1540
- function Attachments(props) {
1541
- const { renderers, layout, ...rest } = props;
1542
- if (renderers) {
1543
- return /* @__PURE__ */ jsxRuntime.jsx(AttachmentsList, { ...rest, renderers });
1544
- }
1545
- return /* @__PURE__ */ jsxRuntime.jsx(AttachmentsGrid, { ...rest, layout: layout === "grid" ? "grid" : "wrap" });
1546
- }
1547
- chunkOLISEQHS_cjs.__name(Attachments, "Attachments");
1548
- function AttachmentTile({ attachment, onClick, onRemove }) {
1549
- const isImage = attachment.type === "image";
1550
- const isUploading = attachment.status === "uploading";
1551
- const inner = isImage ? /* @__PURE__ */ jsxRuntime.jsx(
1552
- "img",
1553
- {
1554
- src: attachment.thumbnailUrl ?? attachment.url,
1555
- alt: attachment.name ?? "attachment",
1556
- className: "h-16 w-16 rounded-md object-cover",
1557
- loading: "lazy"
1558
- }
1559
- ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex max-w-44 items-center gap-2 rounded-md border border-border bg-background/60 px-2 py-1.5 text-xs", children: [
1560
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.File, { "aria-hidden": true, className: "size-4 shrink-0 text-muted-foreground" }),
1561
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: attachment.name ?? "file" })
1562
- ] });
1563
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1564
- onClick ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick, className: "block", children: inner }) : inner,
1565
- isUploading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center rounded-md bg-background/70 text-[10px] font-medium", children: attachment.progress != null ? `${Math.round(attachment.progress * 100)}%` : "\u2026" }) : null,
1566
- onRemove ? /* @__PURE__ */ jsxRuntime.jsx(RemoveBtn, { onRemove }) : null
1567
- ] });
1568
- }
1569
- chunkOLISEQHS_cjs.__name(AttachmentTile, "AttachmentTile");
1570
- function RemoveBtn({ onRemove }) {
1571
- return /* @__PURE__ */ jsxRuntime.jsx(
1572
- "button",
1573
- {
1574
- type: "button",
1575
- "aria-label": "Remove attachment",
1576
- onClick: onRemove,
1577
- className: "absolute -right-1.5 -top-1.5 grid h-4 w-4 place-items-center rounded-full border border-border bg-background text-muted-foreground hover:bg-destructive hover:text-destructive-foreground",
1578
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { "aria-hidden": true, className: "size-2.5" })
1579
- }
1580
- );
1581
- }
1582
- chunkOLISEQHS_cjs.__name(RemoveBtn, "RemoveBtn");
1583
- var SIZE_CLASSES = {
1584
- sm: {
1585
- slot: "[&>:not(textarea)]:h-8",
1586
- button: "h-8 w-8",
1587
- iconButton: "size-3.5",
1588
- textarea: "min-h-8 max-h-48 px-3 py-1.5",
1589
- text: "text-sm",
1590
- padding: "gap-1.5",
1591
- containerPadding: "px-2 pt-1.5 pb-[max(0.375rem,env(safe-area-inset-bottom))]"
1592
- },
1593
- md: {
1594
- slot: "[&>:not(textarea)]:h-9",
1595
- button: "h-9 w-9",
1596
- iconButton: "size-4",
1597
- textarea: "min-h-9 max-h-60 px-3.5 py-2",
1598
- text: "text-base sm:text-sm",
1599
- padding: "gap-1.5",
1600
- containerPadding: "px-2.5 pt-2 pb-[max(0.5rem,env(safe-area-inset-bottom))]"
1601
- },
1602
- lg: {
1603
- slot: "[&>:not(textarea)]:h-12",
1604
- button: "h-12 w-12",
1605
- iconButton: "size-5",
1606
- textarea: "min-h-12 max-h-72 px-4 py-3",
1607
- text: "text-base",
1608
- padding: "gap-2",
1609
- containerPadding: "px-3.5 pt-3 pb-[max(0.875rem,env(safe-area-inset-bottom))]"
1610
- }
1611
- };
1612
- var Composer = react.forwardRef(/* @__PURE__ */ chunkOLISEQHS_cjs.__name(function Composer2({
1613
- composer,
1614
- placeholder = "Type a message...",
1615
- disabled,
1616
- showAttachmentButton = false,
1617
- onPickFiles,
1618
- toolbarStart,
1619
- toolbarEnd,
1620
- attachmentTray,
1621
- className,
1622
- textareaClassName,
1623
- size = "md",
1624
- isStreaming: isStreamingProp,
1625
- onCancel: onCancelProp
1626
- }, ref) {
1627
- const ctx = useChatContextOptional();
1628
- const isStreaming = isStreamingProp ?? ctx?.isStreaming ?? false;
1629
- const onCancel = onCancelProp ?? ctx?.cancelStream;
1630
- const isDisabled = disabled ?? isStreaming;
1631
- const sz = SIZE_CLASSES[size];
1632
- const register = ctx?.registerComposer;
1633
- const composerFocus = composer.focus;
1634
- react.useEffect(() => {
1635
- if (!register) return;
1636
- register({ focus: composerFocus });
1637
- return () => register(null);
1638
- }, [register, composerFocus]);
1639
- return /* @__PURE__ */ jsxRuntime.jsxs(
1640
- "div",
1641
- {
1642
- ref,
1643
- className: lib.cn(
1644
- "border-t border-border bg-background/95",
1645
- sz.containerPadding,
1646
- className
1647
- ),
1648
- children: [
1649
- composer.attachments.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1.5", children: attachmentTray ?? /* @__PURE__ */ jsxRuntime.jsx(
1650
- Attachments,
1651
- {
1652
- attachments: composer.attachments,
1653
- onRemove: (a) => composer.removeAttachment(a.id)
1654
- }
1655
- ) }) : null,
1656
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex items-end [&>:not(textarea)]:shrink-0", sz.padding, sz.slot), children: [
1657
- showAttachmentButton ? /* @__PURE__ */ jsxRuntime.jsx(
1658
- components.Button,
1659
- {
1660
- type: "button",
1661
- variant: "ghost",
1662
- size: "icon",
1663
- onClick: onPickFiles,
1664
- "aria-label": "Attach files",
1665
- disabled: isDisabled,
1666
- className: sz.button,
1667
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { "aria-hidden": true, className: sz.iconButton })
1668
- }
1669
- ) : null,
1670
- toolbarStart,
1671
- /* @__PURE__ */ jsxRuntime.jsx(
1672
- components.Textarea,
1673
- {
1674
- ...composer.textareaProps,
1675
- rows: 1,
1676
- placeholder,
1677
- "aria-label": placeholder,
1678
- "aria-multiline": "true",
1679
- disabled: isDisabled,
1680
- className: lib.cn(
1681
- "flex-1 resize-none rounded-2xl",
1682
- sz.textarea,
1683
- sz.text,
1684
- textareaClassName
1685
- )
1686
- }
1687
- ),
1688
- toolbarEnd,
1689
- isStreaming ? /* @__PURE__ */ jsxRuntime.jsx(
1690
- components.Button,
1691
- {
1692
- type: "button",
1693
- variant: "secondary",
1694
- size: "icon",
1695
- onClick: onCancel,
1696
- "aria-label": "Stop",
1697
- "aria-keyshortcuts": "Escape",
1698
- className: sz.button,
1699
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Square, { "aria-hidden": true, className: sz.iconButton })
1700
- }
1701
- ) : /* @__PURE__ */ jsxRuntime.jsx(
1702
- components.Button,
1703
- {
1704
- type: "button",
1705
- size: "icon",
1706
- onClick: () => void composer.submit(),
1707
- disabled: !composer.canSubmit,
1708
- "aria-label": "Send",
1709
- "aria-keyshortcuts": "Enter",
1710
- className: sz.button,
1711
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { "aria-hidden": true, className: sz.iconButton })
1712
- }
1713
- )
1714
- ] })
1715
- ]
1716
- }
1717
- );
1718
- }, "Composer"));
1719
- function EmptyState({
1720
- greeting,
1721
- description,
1722
- suggestions,
1723
- onPickSuggestion,
1724
- className
1725
- }) {
1726
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col items-center gap-3 px-4 py-12 text-center", className), children: [
1727
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid size-10 place-items-center rounded-full bg-muted", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { "aria-hidden": true, className: "size-5 text-muted-foreground" }) }),
1728
- greeting ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-semibold", children: greeting }) : null,
1729
- description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "max-w-md text-sm text-muted-foreground", children: description }) : null,
1730
- suggestions?.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 grid w-full max-w-md grid-cols-1 gap-2 sm:grid-cols-2", children: suggestions.map((s) => /* @__PURE__ */ jsxRuntime.jsx(
1731
- "button",
1732
- {
1733
- type: "button",
1734
- onClick: () => onPickSuggestion?.(s.prompt),
1735
- className: "rounded-lg border border-border bg-background/60 px-3 py-2 text-left text-xs hover:bg-accent",
1736
- children: s.label
1737
- },
1738
- s.prompt
1739
- )) }) : null
1740
- ] });
1741
- }
1742
- chunkOLISEQHS_cjs.__name(EmptyState, "EmptyState");
1743
- function ErrorBanner({ error, onDismiss, onRetry, className }) {
1744
- if (!error) return null;
1745
- return /* @__PURE__ */ jsxRuntime.jsxs(
1746
- "div",
1747
- {
1748
- role: "alert",
1749
- className: lib.cn(
1750
- "mx-2.5 my-2 flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive",
1751
- className
1752
- ),
1753
- children: [
1754
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { "aria-hidden": true, className: "mt-0.5 size-3.5 shrink-0" }),
1755
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "min-w-0 flex-1 break-words", children: error }),
1756
- onRetry ? /* @__PURE__ */ jsxRuntime.jsxs(
1757
- "button",
1758
- {
1759
- type: "button",
1760
- onClick: onRetry,
1761
- className: "inline-flex items-center gap-1 rounded px-1.5 py-0.5 hover:bg-destructive/15",
1762
- children: [
1763
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { "aria-hidden": true, className: "size-3" }),
1764
- " Retry"
1765
- ]
1766
- }
1767
- ) : null,
1768
- onDismiss ? /* @__PURE__ */ jsxRuntime.jsx(
1769
- "button",
1770
- {
1771
- type: "button",
1772
- "aria-label": "Dismiss",
1773
- onClick: onDismiss,
1774
- className: "rounded p-0.5 hover:bg-destructive/15",
1775
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { "aria-hidden": true, className: "size-3" })
1776
- }
1777
- ) : null
1778
- ]
1779
- }
1780
- );
1781
- }
1782
- chunkOLISEQHS_cjs.__name(ErrorBanner, "ErrorBanner");
1783
- function JumpToLatest({ visible, unreadCount = 0, onClick, className }) {
1784
- if (!visible) return null;
1785
- return /* @__PURE__ */ jsxRuntime.jsxs(
1786
- "button",
1787
- {
1788
- type: "button",
1789
- onClick,
1790
- "aria-live": "polite",
1791
- className: lib.cn(
1792
- "pointer-events-auto inline-flex items-center gap-1.5 rounded-full border border-border bg-background px-3 py-1 text-xs shadow-md hover:bg-accent",
1793
- className
1794
- ),
1795
- children: [
1796
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { "aria-hidden": true, className: "size-3.5" }),
1797
- unreadCount > 0 ? `${unreadCount} new` : "Jump to latest"
1798
- ]
1799
- }
1800
- );
1801
- }
1802
- chunkOLISEQHS_cjs.__name(JumpToLatest, "JumpToLatest");
1803
-
1804
- // src/tools/Chat/core/persona.ts
1805
- var FALLBACK_USER = { name: "You", initials: "You" };
1806
- var FALLBACK_ASSISTANT = { name: "AI", initials: "AI" };
1807
- function resolvePersona(message, user, assistant) {
1808
- if (message.sender) return message.sender;
1809
- if (message.role === "user") return user ?? FALLBACK_USER;
1810
- if (message.role === "assistant") return assistant ?? FALLBACK_ASSISTANT;
1811
- return { name: message.role };
1812
- }
1813
- chunkOLISEQHS_cjs.__name(resolvePersona, "resolvePersona");
1814
- function deriveInitials(persona, role) {
1815
- if (persona.initials) return persona.initials;
1816
- if (persona.name) {
1817
- const parts = persona.name.trim().split(/\s+/);
1818
- if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
1819
- return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
1820
- }
1821
- if (role === "user") return "You";
1822
- if (role === "assistant") return "AI";
1823
- return "?";
1824
- }
1825
- chunkOLISEQHS_cjs.__name(deriveInitials, "deriveInitials");
1826
- function StreamingIndicatorRaw({ variant = "dots", label, className }) {
1827
- return /* @__PURE__ */ jsxRuntime.jsxs(
1828
- "span",
1829
- {
1830
- className: lib.cn("inline-flex items-center gap-1.5 text-xs text-muted-foreground", className),
1831
- "aria-live": "off",
1832
- children: [
1833
- variant === "dots" ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex gap-0.5", "aria-hidden": true, children: [
1834
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-1 animate-bounce rounded-full bg-current [animation-delay:-0.2s]" }),
1835
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-1 animate-bounce rounded-full bg-current [animation-delay:-0.1s]" }),
1836
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-1 animate-bounce rounded-full bg-current" })
1837
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block size-1.5 animate-pulse rounded-full bg-current", "aria-hidden": true }),
1838
- label ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic", children: label }) : null
1839
- ]
1840
- }
1841
- );
1842
- }
1843
- chunkOLISEQHS_cjs.__name(StreamingIndicatorRaw, "StreamingIndicatorRaw");
1844
- var StreamingIndicator = react.memo(StreamingIndicatorRaw);
1845
- function Sources({ sources, layout = "inline", maxVisible, onClick, className }) {
1846
- if (!sources?.length) return null;
1847
- const visible = maxVisible ? sources.slice(0, maxVisible) : sources;
1848
- const remaining = maxVisible ? Math.max(0, sources.length - maxVisible) : 0;
1849
- return /* @__PURE__ */ jsxRuntime.jsxs(
1850
- "div",
1851
- {
1852
- className: lib.cn(
1853
- "mt-2 flex flex-wrap gap-1.5",
1854
- layout === "grid" && "grid grid-cols-2",
1855
- className
1856
- ),
1857
- children: [
1858
- visible.map((s, i) => {
1859
- const handle = onClick ? () => onClick(s) : void 0;
1860
- const Tag = handle ? "button" : "a";
1861
- const props = handle ? { type: "button", onClick: handle } : { href: s.url, target: "_blank", rel: "noopener noreferrer" };
1862
- return /* @__PURE__ */ jsxRuntime.jsxs(
1863
- Tag,
1864
- {
1865
- ...props,
1866
- className: "inline-flex max-w-full items-center gap-1 rounded-md border border-border bg-background/60 px-2 py-1 text-xs text-foreground/80 hover:bg-accent hover:text-foreground",
1867
- title: s.snippet ?? s.title,
1868
- children: [
1869
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: s.title || s.url }),
1870
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { "aria-hidden": true, className: "size-3 shrink-0 opacity-60" })
1871
- ]
1872
- },
1873
- `${s.url}-${i}`
1874
- );
1875
- }),
1876
- remaining > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center rounded-md border border-dashed border-border px-2 py-1 text-xs text-muted-foreground", children: [
1877
- "+",
1878
- remaining
1879
- ] }) : null
1880
- ]
1881
- }
1882
- );
1883
- }
1884
- chunkOLISEQHS_cjs.__name(Sources, "Sources");
1885
- function ToolCalls({
1886
- calls,
1887
- defaultExpanded = false,
1888
- expandWhileStreaming = true,
1889
- renderInput,
1890
- renderOutput,
1891
- renderStreaming,
1892
- renderPayload,
1893
- renderAfterCalls,
1894
- renderToolCall,
1895
- hideToolCalls = false,
1896
- className
1897
- }) {
1898
- if (!calls?.length) return null;
1899
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("mt-2 space-y-1.5", className), children: [
1900
- !hideToolCalls && calls.map(
1901
- (call) => renderToolCall ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderToolCall(call) }, call.id) : /* @__PURE__ */ jsxRuntime.jsx(
1902
- ToolCallItem,
1903
- {
1904
- call,
1905
- defaultExpanded,
1906
- expandWhileStreaming,
1907
- renderInput,
1908
- renderOutput,
1909
- renderStreaming,
1910
- renderPayload
1911
- },
1912
- call.id
1913
- )
1914
- ),
1915
- renderAfterCalls ? renderAfterCalls(calls) : null
1916
- ] });
1917
- }
1918
- chunkOLISEQHS_cjs.__name(ToolCalls, "ToolCalls");
1919
- function ToolCallItem({
1920
- call,
1921
- defaultExpanded,
1922
- expandWhileStreaming,
1923
- renderInput,
1924
- renderOutput,
1925
- renderStreaming,
1926
- renderPayload
1927
- }) {
1928
- const isRunning = call.status === "running";
1929
- const initialOpen = defaultExpanded || expandWhileStreaming && isRunning;
1930
- const [open, setOpen] = react.useState(initialOpen);
1931
- const userToggledRef = react.useRef(false);
1932
- const wasRunningRef = react.useRef(isRunning);
1933
- react.useEffect(() => {
1934
- if (wasRunningRef.current && !isRunning) {
1935
- if (!userToggledRef.current && !defaultExpanded) {
1936
- setOpen(false);
1937
- }
1938
- }
1939
- wasRunningRef.current = isRunning;
1940
- }, [isRunning, defaultExpanded]);
1941
- const handleToggle = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
1942
- userToggledRef.current = true;
1943
- setOpen((v) => !v);
1944
- }, "handleToggle");
1945
- const Icon = open ? lucideReact.ChevronDown : lucideReact.ChevronRight;
1946
- const statusColor = call.status === "success" ? "text-emerald-500" : call.status === "error" ? "text-destructive" : call.status === "cancelled" ? "text-muted-foreground" : "text-amber-500";
1947
- const renderValue = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((value, kind) => {
1948
- if (kind === "input" && renderInput) return renderInput(value, call);
1949
- if (kind === "output" && renderOutput) return renderOutput(value, call);
1950
- if (kind === "streaming" && renderStreaming)
1951
- return renderStreaming(typeof value === "string" ? value : String(value), call);
1952
- if (renderPayload) return renderPayload(value, kind, call);
1953
- return /* @__PURE__ */ jsxRuntime.jsx(DefaultPayload, { value, kind });
1954
- }, "renderValue");
1955
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-hidden rounded-md border border-border bg-muted/30", children: [
1956
- /* @__PURE__ */ jsxRuntime.jsxs(
1957
- "button",
1958
- {
1959
- type: "button",
1960
- onClick: handleToggle,
1961
- "aria-expanded": open,
1962
- className: "flex w-full items-center gap-2 px-2 py-1.5 text-left text-xs hover:bg-muted/60",
1963
- children: [
1964
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { "aria-hidden": true, className: "size-3 shrink-0 text-muted-foreground" }),
1965
- isRunning ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { "aria-hidden": true, className: "size-3 shrink-0 animate-spin text-amber-500" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: lib.cn("size-2 shrink-0 rounded-full", statusColor.replace("text-", "bg-")) }),
1966
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-foreground", children: call.name }),
1967
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: lib.cn("ml-auto", statusColor), children: call.status })
1968
- ]
1969
- }
1970
- ),
1971
- open ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 border-t border-border px-2 py-1.5 text-[11px]", children: [
1972
- call.input != null ? renderValue(call.input, "input") : null,
1973
- call.streamingText != null ? renderValue(call.streamingText, "streaming") : call.output !== void 0 ? renderValue(call.output, "output") : null
1974
- ] }) : null
1975
- ] });
1976
- }
1977
- chunkOLISEQHS_cjs.__name(ToolCallItem, "ToolCallItem");
1978
- function DefaultPayload({ value, kind }) {
1979
- const isStreamingOrString = kind === "streaming" || typeof value === "string";
1980
- const muted = kind === "input";
1981
- return /* @__PURE__ */ jsxRuntime.jsx(
1982
- "pre",
1983
- {
1984
- className: lib.cn(
1985
- "overflow-auto rounded bg-background/60 p-1.5 font-mono",
1986
- kind === "input" ? "max-h-32" : "max-h-48",
1987
- muted ? "text-muted-foreground" : "text-foreground/90"
1988
- ),
1989
- children: isStreamingOrString ? String(value) : safeStringify(value)
1990
- }
1991
- );
1992
- }
1993
- chunkOLISEQHS_cjs.__name(DefaultPayload, "DefaultPayload");
1994
- function safeStringify(value) {
1995
- try {
1996
- return JSON.stringify(value, null, 2);
1997
- } catch {
1998
- return String(value);
1999
- }
2000
- }
2001
- chunkOLISEQHS_cjs.__name(safeStringify, "safeStringify");
2002
- function MessageActionsRaw({
2003
- role,
2004
- onCopy,
2005
- onRegenerate,
2006
- onEdit,
2007
- onDelete,
2008
- hideOn,
2009
- className
2010
- }) {
2011
- if (hideOn?.includes(role)) return null;
2012
- return /* @__PURE__ */ jsxRuntime.jsxs(
2013
- "div",
2014
- {
2015
- className: lib.cn(
2016
- "mt-1 flex items-center gap-0.5 opacity-0 transition-opacity group-hover/msg:opacity-100 focus-within:opacity-100",
2017
- className
2018
- ),
2019
- children: [
2020
- onCopy ? /* @__PURE__ */ jsxRuntime.jsx(ActionButton, { onClick: onCopy, label: "Copy", icon: lucideReact.Copy }) : null,
2021
- onRegenerate && role === "assistant" ? /* @__PURE__ */ jsxRuntime.jsx(ActionButton, { onClick: onRegenerate, label: "Regenerate", icon: lucideReact.RefreshCw }) : null,
2022
- onEdit && role === "user" ? /* @__PURE__ */ jsxRuntime.jsx(ActionButton, { onClick: onEdit, label: "Edit", icon: lucideReact.Pencil }) : null,
2023
- onDelete ? /* @__PURE__ */ jsxRuntime.jsx(ActionButton, { onClick: onDelete, label: "Delete", icon: lucideReact.Trash, destructive: true }) : null
2024
- ]
2025
- }
2026
- );
2027
- }
2028
- chunkOLISEQHS_cjs.__name(MessageActionsRaw, "MessageActionsRaw");
2029
- var ActionButton = react.memo(/* @__PURE__ */ chunkOLISEQHS_cjs.__name(function ActionButton2({
2030
- onClick,
2031
- label,
2032
- icon: Icon,
2033
- destructive
2034
- }) {
2035
- return /* @__PURE__ */ jsxRuntime.jsx(
2036
- "button",
2037
- {
2038
- type: "button",
2039
- onClick,
2040
- "aria-label": label,
2041
- className: lib.cn(
2042
- "rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground",
2043
- destructive && "hover:bg-destructive/15 hover:text-destructive"
2044
- ),
2045
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { "aria-hidden": true, className: "size-3" })
2046
- }
2047
- );
2048
- }, "ActionButton"));
2049
- var MessageActions = react.memo(MessageActionsRaw);
2050
- var MessageBubbleInner = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(({
2051
- message,
2052
- isUser: isUserProp,
2053
- showAvatar = true,
2054
- avatarSrc,
2055
- avatarFallback,
2056
- user,
2057
- assistant,
2058
- showTimestamp = false,
2059
- showActions = true,
2060
- isCompact = false,
2061
- className,
2062
- beforeContent,
2063
- afterContent,
2064
- toolCallsRenderer,
2065
- toolCallsProps,
2066
- sourcesRenderer,
2067
- attachmentsRenderer,
2068
- attachmentRenderers,
2069
- onAttachmentOpen,
2070
- onCopy,
2071
- onRegenerate,
2072
- onEdit,
2073
- onDelete,
2074
- messageActionsExtra,
2075
- streamingIndicator
2076
- }) => {
2077
- const isUser = isUserProp ?? message.role === "user";
2078
- const isStreaming = !!message.isStreaming;
2079
- const isErr = !!message.isError;
2080
- const ctx = useChatContextOptional();
2081
- const persona = resolvePersona(
2082
- message,
2083
- user ?? ctx?.config.user,
2084
- assistant ?? ctx?.config.assistant
2085
- );
2086
- const initials = deriveInitials(persona, message.role);
2087
- const personaName = persona.name ?? (isUser ? "You" : "Assistant");
2088
- return /* @__PURE__ */ jsxRuntime.jsxs(
2089
- "div",
2090
- {
2091
- role: "article",
2092
- "aria-label": `${personaName} said: ${message.content.slice(0, 80)}`,
2093
- "aria-busy": isStreaming || void 0,
2094
- "data-role": message.role,
2095
- className: lib.cn(
2096
- "group/msg flex gap-2.5 px-2.5 py-2",
2097
- isUser ? "flex-row-reverse" : "flex-row",
2098
- className
2099
- ),
2100
- children: [
2101
- showAvatar ? /* @__PURE__ */ jsxRuntime.jsxs(
2102
- components.Avatar,
2103
- {
2104
- className: "size-7 shrink-0",
2105
- title: persona.description ?? personaName,
2106
- children: [
2107
- avatarSrc || persona.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(components.AvatarImage, { src: avatarSrc ?? persona.avatarUrl, alt: personaName }) : null,
2108
- /* @__PURE__ */ jsxRuntime.jsx(components.AvatarFallback, { className: "text-[10px]", children: avatarFallback ?? initials })
2109
- ]
2110
- }
2111
- ) : null,
2112
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("min-w-0 flex-1", isUser && "flex flex-col items-end"), children: [
2113
- beforeContent,
2114
- message.attachments?.length ? attachmentsRenderer ? attachmentsRenderer(message.attachments) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1.5 w-full", children: attachmentRenderers ? /* @__PURE__ */ jsxRuntime.jsx(
2115
- AttachmentsList,
2116
- {
2117
- attachments: message.attachments,
2118
- renderers: attachmentRenderers,
2119
- onClick: onAttachmentOpen,
2120
- className: isUser ? "items-end" : void 0
2121
- }
2122
- ) : /* @__PURE__ */ jsxRuntime.jsx(
2123
- AttachmentsGrid,
2124
- {
2125
- attachments: message.attachments,
2126
- onClick: onAttachmentOpen,
2127
- className: isUser ? "justify-end" : void 0
2128
- }
2129
- ) }) : null,
2130
- /* @__PURE__ */ jsxRuntime.jsxs(
2131
- "div",
2132
- {
2133
- className: lib.cn(
2134
- "inline-block max-w-full rounded-2xl px-3.5 py-2 text-sm",
2135
- isUser ? "bg-primary text-primary-foreground rounded-tr-md" : isErr ? "bg-destructive/10 text-destructive rounded-tl-md border border-destructive/30" : "bg-muted text-foreground rounded-tl-md"
2136
- ),
2137
- children: [
2138
- isStreaming && message.toolActivity ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1.5", children: streamingIndicator ? streamingIndicator(message) : /* @__PURE__ */ jsxRuntime.jsx(StreamingIndicator, { label: message.toolActivity }) }) : null,
2139
- message.content || !isStreaming ? /* @__PURE__ */ jsxRuntime.jsx(
2140
- chunkXACCHZH2_cjs.MarkdownMessage,
2141
- {
2142
- content: message.content || (isErr ? "*Failed to generate a response.*" : ""),
2143
- isUser,
2144
- isCompact,
2145
- plainText: isStreaming
2146
- }
2147
- ) : streamingIndicator ? streamingIndicator(message) : /* @__PURE__ */ jsxRuntime.jsx(StreamingIndicator, {})
2148
- ]
2149
- }
2150
- ),
2151
- message.toolCalls?.length ? toolCallsRenderer ? toolCallsRenderer(message.toolCalls) : /* @__PURE__ */ jsxRuntime.jsx(ToolCalls, { calls: message.toolCalls, ...toolCallsProps }) : null,
2152
- message.sources?.length && !isStreaming ? sourcesRenderer ? sourcesRenderer(message.sources) : /* @__PURE__ */ jsxRuntime.jsx(Sources, { sources: message.sources }) : null,
2153
- showActions && !isStreaming ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
2154
- /* @__PURE__ */ jsxRuntime.jsx(
2155
- MessageActions,
2156
- {
2157
- role: message.role,
2158
- onCopy,
2159
- onRegenerate,
2160
- onEdit,
2161
- onDelete
2162
- }
2163
- ),
2164
- messageActionsExtra ? messageActionsExtra(message) : null
2165
- ] }) : null,
2166
- showTimestamp ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-[10px] text-muted-foreground", children: new Date(message.createdAt).toLocaleTimeString() }) : null,
2167
- afterContent
2168
- ] })
2169
- ]
2170
- }
2171
- );
2172
- }, "MessageBubbleInner");
2173
- var MessageBubble = react.memo(MessageBubbleInner, (prev, next) => {
2174
- const a = prev.message;
2175
- const b = next.message;
2176
- return a.id === b.id && a.content === b.content && a.isStreaming === b.isStreaming && a.isError === b.isError && (a.version ?? 0) === (b.version ?? 0) && a.toolActivity === b.toolActivity && a.toolCalls === b.toolCalls && a.sources === b.sources && a.attachments === b.attachments;
2177
- });
2178
- MessageBubble.displayName = "MessageBubble";
2179
- var MessageList = react.forwardRef(/* @__PURE__ */ chunkOLISEQHS_cjs.__name(function MessageList2({
2180
- messages: messagesProp,
2181
- renderItem,
2182
- renderEmpty,
2183
- isLoadingMore: isLoadingMoreProp,
2184
- onStartReached,
2185
- className,
2186
- itemClassName,
2187
- noVirtualize = false,
2188
- defaultItemHeight: _deprecatedDefaultItemHeight,
2189
- onAtBottomChange
2190
- }, ref) {
2191
- const ctx = useChatContextOptional();
2192
- const messages = messagesProp ?? ctx?.messages ?? [];
2193
- const isLoadingMore = isLoadingMoreProp ?? ctx?.isLoadingMore ?? false;
2194
- const { copyToClipboard } = hooks.useCopy();
2195
- const virtuosoRef = react.useRef(null);
2196
- const didInitialScrollRef = react.useRef(false);
2197
- react.useImperativeHandle(
2198
- ref,
2199
- () => ({
2200
- scrollToBottom: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((smooth = false) => {
2201
- virtuosoRef.current?.scrollToIndex({
2202
- index: "LAST",
2203
- behavior: smooth ? "smooth" : "auto",
2204
- align: "end"
2205
- });
2206
- }, "scrollToBottom"),
2207
- scrollToIndex: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((index, smooth = false) => {
2208
- virtuosoRef.current?.scrollToIndex({
2209
- index,
2210
- behavior: smooth ? "smooth" : "auto"
2211
- });
2212
- }, "scrollToIndex")
2213
- }),
2214
- []
2215
- );
2216
- const defaultRenderItem = react.useCallback(
2217
- (m) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: itemClassName, children: /* @__PURE__ */ jsxRuntime.jsx(
2218
- MessageBubble,
2219
- {
2220
- message: m,
2221
- onCopy: () => void copyToClipboard(m.content),
2222
- onRegenerate: ctx ? () => void ctx.regenerate(m.id) : void 0,
2223
- onDelete: ctx ? () => ctx.deleteMessage(m.id) : void 0
2224
- }
2225
- ) }),
2226
- [itemClassName, ctx, copyToClipboard]
2227
- );
2228
- const itemRenderer = renderItem ?? defaultRenderItem;
2229
- react.useEffect(() => {
2230
- if (didInitialScrollRef.current) return;
2231
- if (messages.length === 0) return;
2232
- didInitialScrollRef.current = true;
2233
- const id = requestAnimationFrame(() => {
2234
- virtuosoRef.current?.scrollToIndex({
2235
- index: "LAST",
2236
- align: "end",
2237
- behavior: "auto"
2238
- });
2239
- });
2240
- return () => cancelAnimationFrame(id);
2241
- }, [messages.length]);
2242
- const computeItemKey = react.useCallback(
2243
- (index, m) => m?.id ?? index,
2244
- []
2245
- );
2246
- const startReachedHandler = react.useMemo(() => {
2247
- if (!onStartReached) return void 0;
2248
- let inFlight = false;
2249
- return () => {
2250
- if (inFlight || isLoadingMore) return;
2251
- inFlight = true;
2252
- try {
2253
- onStartReached();
2254
- } finally {
2255
- queueMicrotask(() => {
2256
- inFlight = false;
2257
- });
2258
- }
2259
- };
2260
- }, [onStartReached, isLoadingMore]);
2261
- if (messages.length === 0) {
2262
- return /* @__PURE__ */ jsxRuntime.jsx(
2263
- "div",
2264
- {
2265
- role: "log",
2266
- "aria-live": "polite",
2267
- "aria-atomic": "false",
2268
- className: lib.cn("flex-1 overflow-y-auto", className),
2269
- children: renderEmpty?.() ?? null
2270
- }
2271
- );
2272
- }
2273
- if (noVirtualize) {
2274
- return /* @__PURE__ */ jsxRuntime.jsxs(
2275
- "div",
2276
- {
2277
- role: "log",
2278
- "aria-live": "polite",
2279
- "aria-atomic": "false",
2280
- className: lib.cn("flex-1 overflow-y-auto", className),
2281
- children: [
2282
- isLoadingMore ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsxRuntime.jsx(components.Spinner, { className: "size-4 text-muted-foreground" }) }) : null,
2283
- messages.map((m, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: itemRenderer(m, i) }, m.id ?? i))
2284
- ]
2285
- }
2286
- );
2287
- }
2288
- return /* @__PURE__ */ jsxRuntime.jsx(
2289
- reactVirtuoso.Virtuoso,
2290
- {
2291
- ref: virtuosoRef,
2292
- role: "log",
2293
- "aria-live": "polite",
2294
- "aria-atomic": "false",
2295
- className: lib.cn("flex-1", className),
2296
- data: messages,
2297
- computeItemKey,
2298
- itemContent: (index, m) => m ? itemRenderer(m, index) : null,
2299
- initialTopMostItemIndex: messages.length > 0 ? messages.length - 1 : 0,
2300
- followOutput: (isAtBottom) => isAtBottom ? "auto" : false,
2301
- atBottomStateChange: onAtBottomChange,
2302
- startReached: startReachedHandler,
2303
- components: isLoadingMore ? {
2304
- Header: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsxRuntime.jsx(components.Spinner, { className: "size-4 text-muted-foreground" }) }), "Header")
2305
- } : EMPTY_COMPONENTS
2306
- }
2307
- );
2308
- }, "MessageList"));
2309
- var EMPTY_COMPONENTS = {};
2310
- function ChatRoot(props) {
2311
- const { transport, config, initialSessionId, autoCreateSession, streaming, audio, debug, className, listClassName, ...slots } = props;
2312
- return /* @__PURE__ */ jsxRuntime.jsx(
2313
- ChatProvider,
2314
- {
2315
- transport,
2316
- config,
2317
- initialSessionId,
2318
- autoCreateSession,
2319
- streaming,
2320
- audio,
2321
- debug,
2322
- children: /* @__PURE__ */ jsxRuntime.jsx(ChatRootShell, { className, listClassName, slots })
2323
- }
2324
- );
2325
- }
2326
- chunkOLISEQHS_cjs.__name(ChatRoot, "ChatRoot");
2327
- function ChatRootShell({ className, listClassName, slots }) {
2328
- const chat = useChatContext();
2329
- const composer = useChatComposer({
2330
- onSubmit: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((content, attachments) => chat.sendMessage(content, attachments), "onSubmit"),
2331
- disabled: chat.isStreaming
2332
- });
2333
- const listRef = react.useRef(null);
2334
- const [isAtBottom, setIsAtBottom] = react.useState(true);
2335
- const handleStartReached = chat.hasMore && !chat.isLoadingMore ? () => void chat.loadMore() : void 0;
2336
- const greeting = chat.config.greeting ?? "How can I help?";
2337
- const description = chat.config.description;
2338
- const suggestions = chat.config.suggestions;
2339
- const headerNode = slots.renderHeader ? slots.renderHeader(chat) : slots.header;
2340
- const emptyNode = slots.empty ?? (slots.renderEmpty ? slots.renderEmpty({ setValue: composer.setValue, focus: composer.focus }) : /* @__PURE__ */ jsxRuntime.jsx(
2341
- EmptyState,
2342
- {
2343
- greeting,
2344
- description,
2345
- suggestions,
2346
- onPickSuggestion: (prompt) => {
2347
- composer.setValue(prompt);
2348
- composer.focus();
2349
- }
2350
- }
2351
- ));
2352
- const renderItem = slots.renderMessage ?? ((m) => /* @__PURE__ */ jsxRuntime.jsx(
2353
- MessageBubble,
2354
- {
2355
- message: m,
2356
- toolCallsProps: slots.toolCallsProps,
2357
- attachmentRenderers: slots.attachmentRenderers,
2358
- onAttachmentOpen: slots.onAttachmentOpen,
2359
- onCopy: () => copy(m.content),
2360
- onRegenerate: () => void chat.regenerate(m.id),
2361
- onDelete: () => chat.deleteMessage(m.id)
2362
- },
2363
- m.id
2364
- ));
2365
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("relative flex h-full min-h-0 flex-col overflow-hidden", className), children: [
2366
- slots.banner ?? null,
2367
- headerNode ?? null,
2368
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [
2369
- /* @__PURE__ */ jsxRuntime.jsx(
2370
- ErrorBanner,
2371
- {
2372
- error: chat.error,
2373
- onDismiss: chat.error ? () => chat.clearMessages() : void 0,
2374
- onRetry: chat.error ? () => void chat.regenerate() : void 0
2375
- }
2376
- ),
2377
- /* @__PURE__ */ jsxRuntime.jsx(
2378
- MessageList,
2379
- {
2380
- ref: listRef,
2381
- renderItem,
2382
- renderEmpty: () => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: emptyNode }),
2383
- className: listClassName,
2384
- onStartReached: handleStartReached,
2385
- onAtBottomChange: setIsAtBottom
2386
- }
2387
- ),
2388
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-2 flex justify-center", children: slots.jumpToLatest ?? /* @__PURE__ */ jsxRuntime.jsx(
2389
- JumpToLatest,
2390
- {
2391
- visible: !isAtBottom,
2392
- onClick: () => listRef.current?.scrollToBottom(true)
2393
- }
2394
- ) })
2395
- ] }),
2396
- !slots.hideComposer && /* @__PURE__ */ jsxRuntime.jsx(
2397
- Composer,
2398
- {
2399
- composer,
2400
- placeholder: chat.config.placeholder,
2401
- showAttachmentButton: slots.showAttachmentButton,
2402
- onPickFiles: slots.onPickFiles,
2403
- toolbarStart: slots.composerToolbarStart,
2404
- toolbarEnd: slots.composerToolbarEnd,
2405
- attachmentTray: slots.composerAttachmentTray,
2406
- size: slots.composerSize
2407
- }
2408
- ),
2409
- slots.footer ?? null
2410
- ] });
2411
- }
2412
- chunkOLISEQHS_cjs.__name(ChatRootShell, "ChatRootShell");
2413
- function copy(text) {
2414
- if (typeof navigator !== "undefined" && navigator.clipboard) {
2415
- void navigator.clipboard.writeText(text);
2416
- }
2417
- }
2418
- chunkOLISEQHS_cjs.__name(copy, "copy");
2419
-
2420
- exports.Attachments = Attachments;
2421
- exports.AttachmentsGrid = AttachmentsGrid;
2422
- exports.AttachmentsList = AttachmentsList;
2423
- exports.CHAT_EVENT_NAME = CHAT_EVENT_NAME;
2424
- exports.CSS_VARS = CSS_VARS;
2425
- exports.ChatProvider = ChatProvider;
2426
- exports.ChatRoot = ChatRoot;
2427
- exports.Composer = Composer;
2428
- exports.DEFAULT_LABELS = DEFAULT_LABELS;
2429
- exports.DEFAULT_SIDEBAR = DEFAULT_SIDEBAR;
2430
- exports.DEFAULT_Z_INDEX = DEFAULT_Z_INDEX;
2431
- exports.EmptyState = EmptyState;
2432
- exports.ErrorBanner = ErrorBanner;
2433
- exports.HOTKEYS = HOTKEYS;
2434
- exports.JumpToLatest = JumpToLatest;
2435
- exports.LIMITS = LIMITS;
2436
- exports.MessageActions = MessageActions;
2437
- exports.MessageBubble = MessageBubble;
2438
- exports.MessageList = MessageList;
2439
- exports.STORAGE_KEYS = STORAGE_KEYS;
2440
- exports.Sources = Sources;
2441
- exports.StreamingIndicator = StreamingIndicator;
2442
- exports.ToolCalls = ToolCalls;
2443
- exports.createId = createId;
2444
- exports.createTokenBuffer = createTokenBuffer;
2445
- exports.deriveInitials = deriveInitials;
2446
- exports.getChatLogger = getChatLogger;
2447
- exports.initialState = initialState;
2448
- exports.isSubmittableDraft = isSubmittableDraft;
2449
- exports.reducer = reducer;
2450
- exports.resolvePersona = resolvePersona;
2451
- exports.sanitizeDraft = sanitizeDraft;
2452
- exports.useChat = useChat;
2453
- exports.useChatAudio = useChatAudio;
2454
- exports.useChatAudioPrefs = useChatAudioPrefs;
2455
- exports.useChatComposer = useChatComposer;
2456
- exports.useChatContext = useChatContext;
2457
- exports.useChatContextOptional = useChatContextOptional;
2458
- exports.useChatLayout = useChatLayout;
2459
- //# sourceMappingURL=chunk-SI5RD2GD.cjs.map
2460
- //# sourceMappingURL=chunk-SI5RD2GD.cjs.map