@parallelworks/ui 0.19.0 → 0.19.1

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 (217) hide show
  1. package/dist/{chunks/memoryStore-CC91S5LP.js → ai/adapter/memoryStore.js} +2 -2
  2. package/dist/ai/adapter/mock/index.js +2 -80
  3. package/dist/ai/adapter/mock/mockAdapter.js +81 -0
  4. package/dist/ai/adapter/openai/createOpenAIStream.js +33 -0
  5. package/dist/ai/adapter/openai/errors.js +37 -0
  6. package/dist/ai/adapter/openai/index.js +5 -333
  7. package/dist/ai/adapter/openai/openaiAdapter.js +54 -0
  8. package/dist/ai/adapter/openai/responses.js +108 -0
  9. package/dist/ai/adapter/openai/sse.js +112 -0
  10. package/dist/{chunks/types-Yy0NrerF.js → ai/adapter/types.js} +1 -1
  11. package/dist/ai/agent/classifier.js +12 -0
  12. package/dist/ai/agent/diff.js +223 -0
  13. package/dist/ai/agent/diffHighlight.js +101 -0
  14. package/dist/ai/agent/permissions.js +29 -0
  15. package/dist/ai/agent/toolfmt.js +383 -0
  16. package/dist/ai/agent/transcript.js +127 -0
  17. package/dist/ai/components/AllocationSelector.js +56 -0
  18. package/dist/ai/components/AttachmentManager.js +144 -0
  19. package/dist/ai/components/AttachmentUpload.js +179 -0
  20. package/dist/ai/components/BlockedGroupBanner.js +29 -0
  21. package/dist/ai/components/BranchNavigator.js +57 -0
  22. package/dist/ai/components/ChatEmptyState.js +171 -0
  23. package/dist/ai/components/ChatInput.js +265 -0
  24. package/dist/ai/components/ChatLayout.js +15 -0
  25. package/dist/ai/components/ChatMessage.js +338 -0
  26. package/dist/ai/components/ChatMessageList.js +301 -0
  27. package/dist/ai/components/ChatSidebar.js +345 -0
  28. package/dist/ai/components/ChatThread.js +198 -0
  29. package/dist/ai/components/ComposerChrome.js +50 -0
  30. package/dist/ai/components/ComposerFrame.js +33 -0
  31. package/dist/ai/components/DragOverlay.js +20 -0
  32. package/dist/ai/components/ModelSelector.js +343 -0
  33. package/dist/ai/components/ShareDialog.js +260 -0
  34. package/dist/ai/components/SlashMenu.js +91 -0
  35. package/dist/ai/components/agent/ActivityLine.js +151 -0
  36. package/dist/ai/components/agent/AgentMessageParts.js +721 -0
  37. package/dist/ai/components/agent/PermissionPicker.js +38 -0
  38. package/dist/ai/components/attachmentKeys.js +10 -0
  39. package/dist/ai/core/ChatProvider.js +848 -0
  40. package/dist/ai/core/KeyboardShortcutsProvider.js +9 -0
  41. package/dist/ai/core/chatReducer.js +223 -0
  42. package/dist/ai/core/config.js +45 -0
  43. package/dist/ai/core/events.js +4 -0
  44. package/dist/ai/core/exportConversation.js +42 -0
  45. package/dist/ai/core/greeting.js +38 -0
  46. package/dist/ai/core/parts.js +84 -0
  47. package/dist/ai/core/useDragDrop.js +36 -0
  48. package/dist/ai/core/useKeyboardShortcuts.js +77 -0
  49. package/dist/ai/core/useSSEStream.js +98 -0
  50. package/dist/ai/index.js +43 -6644
  51. package/dist/ai/strings.js +175 -0
  52. package/dist/ai/ui/Avatar.js +35 -0
  53. package/dist/ai/ui/Dropdown.js +110 -0
  54. package/dist/ai/ui/Markdown.js +79 -0
  55. package/dist/ai/ui/Notice.js +77 -0
  56. package/dist/ai/ui/codeBreaks.js +36 -0
  57. package/dist/ai/ui/prose.js +13 -0
  58. package/dist/ai/ui/sidebar.js +148 -0
  59. package/dist/ai/utils.js +23 -0
  60. package/dist/components/AddMenu.js +46 -0
  61. package/dist/components/Avatar.js +46 -0
  62. package/dist/{chunks/BareModal-BtRiQezg.js → components/BareModal.js} +1 -1
  63. package/dist/components/Breadcrumbs.js +83 -0
  64. package/dist/components/Button.js +83 -0
  65. package/dist/components/Callout.js +23 -0
  66. package/dist/components/Card.js +31 -0
  67. package/dist/components/CollapsiblePanel.js +77 -0
  68. package/dist/{chunks/ConfirmModal-aOR4O1QE.js → components/ConfirmModal.js} +4 -9
  69. package/dist/components/CopyToClipboard.js +81 -0
  70. package/dist/{chunks/Markdown-CM-FenNE.js → components/CreateModal.js} +9 -116
  71. package/dist/components/DescriptionList.js +42 -0
  72. package/dist/components/Dropdown.js +349 -0
  73. package/dist/components/EmptyState.js +27 -0
  74. package/dist/components/Filter.js +32 -0
  75. package/dist/components/FilterPill.js +13 -0
  76. package/dist/components/IconButton.js +34 -0
  77. package/dist/{chunks/Indicator-hBSIxOPK.js → components/Indicator.js} +1 -1
  78. package/dist/components/Input.js +88 -0
  79. package/dist/components/LetterBadge.js +13 -0
  80. package/dist/components/Listbox.js +123 -0
  81. package/dist/{chunks/Loader-BXoXTZGc.js → components/Loader.js} +1 -1
  82. package/dist/components/Markdown.js +15 -0
  83. package/dist/components/MultiSelect.js +171 -0
  84. package/dist/components/Overview.js +39 -0
  85. package/dist/components/PageHeader.js +194 -0
  86. package/dist/components/Pagination.js +35 -0
  87. package/dist/{chunks/Provider-CDYurraE.js → components/Provider.js} +1 -1
  88. package/dist/{chunks/RequiredMark-DfLdYbYZ.js → components/RequiredMark.js} +1 -1
  89. package/dist/components/SectionHeader.js +11 -0
  90. package/dist/components/SettingModeOption.js +30 -0
  91. package/dist/components/SettingRow.js +35 -0
  92. package/dist/components/SettingsCard.js +31 -0
  93. package/dist/components/SettingsGroup.js +17 -0
  94. package/dist/components/Sort.js +42 -0
  95. package/dist/components/StatusBadge.js +63 -0
  96. package/dist/components/SwitchToggle.js +59 -0
  97. package/dist/{chunks/SwitchToggleSmall-FcsaYSTs.js → components/SwitchToggleSmall.js} +1 -1
  98. package/dist/{chunks/Table-DU2MW8xz.js → components/Table.js} +3 -3
  99. package/dist/components/Toggle.js +20 -0
  100. package/dist/{chunks/Tooltip-K5HJr4_p.js → components/Tooltip.js} +2 -27
  101. package/dist/{chunks/StatusBadge-C1Blx_cW.js → components/dropdownUtils.js} +2 -63
  102. package/dist/{chunks/focus-D_iZJTW1.js → components/focus.js} +1 -1
  103. package/dist/components/ghostButton.js +7 -0
  104. package/dist/components/inputClasses.js +4 -0
  105. package/dist/components/keys.js +27 -0
  106. package/dist/components/relativeTime.js +32 -0
  107. package/dist/{chunks/useCssIsDark-DHr05dG2.js → components/useCssIsDark.js} +1 -1
  108. package/dist/{chunks/duration-BOv8Phl8.js → duration.js} +1 -1
  109. package/dist/editor/Editor.js +13 -0
  110. package/dist/editor/EditorField.js +53 -0
  111. package/dist/editor/FormCodePanel.js +173 -0
  112. package/dist/{chunks/Monaco-DJ8MW-Dq.js → editor/Monaco.js} +4 -2
  113. package/dist/editor/index.js +6 -1
  114. package/dist/editor/themes.js +91 -0
  115. package/dist/editor/workers.js +29 -0
  116. package/dist/editor/yaml.js +15 -0
  117. package/dist/file-explorer/FileExplorer.js +1402 -0
  118. package/dist/file-explorer/FileExplorerContext.js +370 -0
  119. package/dist/{chunks/CodePreviewEditor-YqQBz1YL.js → file-explorer/FilePreview/CodePreviewEditor.js} +3 -2
  120. package/dist/file-explorer/FilePreview/Inline.js +45 -0
  121. package/dist/file-explorer/FilePreview/StreamedBadge.js +16 -0
  122. package/dist/file-explorer/FilePreview/Viewer.js +526 -0
  123. package/dist/file-explorer/FilePreview/index.js +266 -0
  124. package/dist/file-explorer/FilePreview/parse.js +63 -0
  125. package/dist/file-explorer/FilePreview/previewCache.js +98 -0
  126. package/dist/file-explorer/FilePreview/previewType.js +151 -0
  127. package/dist/file-explorer/FilePreview/readCappedBytes.js +196 -0
  128. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +95 -0
  129. package/dist/file-explorer/FilePreview/useObjectText.js +109 -0
  130. package/dist/file-explorer/FilePreview/usePresignedUrl.js +73 -0
  131. package/dist/file-explorer/FileRow.js +165 -0
  132. package/dist/file-explorer/FileRowsWindow.js +92 -0
  133. package/dist/file-explorer/FileTable.js +172 -0
  134. package/dist/file-explorer/FileTableHeaders.js +49 -0
  135. package/dist/file-explorer/LoadMoreRow.js +46 -0
  136. package/dist/file-explorer/TreeView.js +376 -0
  137. package/dist/file-explorer/helpers.js +537 -0
  138. package/dist/file-explorer/index.js +7 -5671
  139. package/dist/file-explorer/lib/batch.js +39 -0
  140. package/dist/file-explorer/lib/errors.js +9 -0
  141. package/dist/file-explorer/lib/paging.js +124 -0
  142. package/dist/file-explorer/lib/utils.js +322 -0
  143. package/dist/file-explorer/toasts.js +400 -0
  144. package/dist/file-explorer/treeRows.js +73 -0
  145. package/dist/file-explorer/useNearEnd.js +27 -0
  146. package/dist/form/FieldWrapper.js +54 -0
  147. package/dist/form/Fields/BooleanField.js +51 -0
  148. package/dist/form/Fields/CheckboxGroupField.js +72 -0
  149. package/dist/form/Fields/ColorField.js +33 -0
  150. package/dist/form/Fields/DropdownField.js +49 -0
  151. package/dist/form/Fields/DurationField.js +130 -0
  152. package/dist/form/Fields/LabelField.js +19 -0
  153. package/dist/form/Fields/MultiDropdownField.js +40 -0
  154. package/dist/form/Fields/NumberField.js +51 -0
  155. package/dist/form/Fields/ObjectField.js +54 -0
  156. package/dist/form/Fields/RadioField.js +49 -0
  157. package/dist/form/Fields/RangeField.js +58 -0
  158. package/dist/form/Fields/ResourceOptionIcon.js +21 -0
  159. package/dist/form/Fields/StepField.js +6 -0
  160. package/dist/form/Fields/StringField.js +37 -0
  161. package/dist/form/Fields/TextAreaField.js +31 -0
  162. package/dist/form/Fields/durationValidity.js +11 -0
  163. package/dist/form/Form.js +643 -0
  164. package/dist/form/SpecialTypes/DropdownWrapper.js +30 -0
  165. package/dist/form/SpecialTypes/FormikCustomDropdown.js +122 -0
  166. package/dist/form/SpecialTypes/FormikCustomInput.js +111 -0
  167. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +31 -0
  168. package/dist/form/SpecialTypes/FormikNumericInput.js +60 -0
  169. package/dist/form/Wizard/WizardContainer.js +138 -0
  170. package/dist/form/Wizard/WizardNavigation.js +92 -0
  171. package/dist/form/Wizard/WizardStepContent.js +35 -0
  172. package/dist/form/Wizard/WizardStepIndicator.js +108 -0
  173. package/dist/form/Wizard/useWizardState.js +118 -0
  174. package/dist/form/Wizard/utils.js +34 -0
  175. package/dist/form/fieldContext.js +26 -0
  176. package/dist/form/fieldRegistry.js +50 -0
  177. package/dist/form/index.js +17 -2321
  178. package/dist/{chunks/FieldWrapper-Cszwg-4B.js → form/lib.js} +2 -97
  179. package/dist/form/useParsedOpts.js +62 -0
  180. package/dist/form/usePrevious.js +11 -0
  181. package/dist/form/utils/checkShowIfObject.js +14 -0
  182. package/dist/form/utils/duration.js +69 -0
  183. package/dist/form/utils/getParentValue.js +23 -0
  184. package/dist/form/utils/getPlaceholder.js +9 -0
  185. package/dist/form/utils/getValueUsingPath.js +23 -0
  186. package/dist/form/utils/secondaryField.js +22 -0
  187. package/dist/graph/AnnotationBanner.js +141 -0
  188. package/dist/graph/Collapse.js +41 -0
  189. package/dist/graph/DependencyGraph.js +1398 -0
  190. package/dist/graph/JobSummary.js +104 -0
  191. package/dist/graph/MatrixGroup.js +107 -0
  192. package/dist/graph/Reveal.js +57 -0
  193. package/dist/graph/TreeView.js +711 -0
  194. package/dist/graph/index.js +1 -2519
  195. package/dist/graph/types.js +6 -0
  196. package/dist/index.js +46 -599
  197. package/dist/list/ActionToolbar.js +184 -0
  198. package/dist/list/HoverCard.js +73 -0
  199. package/dist/list/ListPage.js +145 -0
  200. package/dist/list/ListPager.js +59 -0
  201. package/dist/list/ListTable.js +163 -0
  202. package/dist/list/ListViewControls.js +372 -0
  203. package/dist/list/RowContextMenu.js +350 -0
  204. package/dist/list/UserHoverCard.js +117 -0
  205. package/dist/list/index.js +11 -2
  206. package/dist/list/listSearch.js +137 -0
  207. package/dist/list/listView.js +210 -0
  208. package/dist/list/textEntry.js +10 -0
  209. package/dist/{chunks/logviewer-BTKuQWzw.js → logviewer/LogViewer.js} +4 -82
  210. package/dist/logviewer/index.js +2 -1
  211. package/dist/logviewer/segments.js +80 -0
  212. package/package.json +1 -1
  213. package/dist/chunks/PageHeader-0kG_iypP.js +0 -692
  214. package/dist/chunks/SwitchToggle-DShNdcao.js +0 -758
  215. package/dist/chunks/editor-Dk1rbV75.js +0 -361
  216. package/dist/chunks/list-CJm875m7.js +0 -1418
  217. /package/dist/{chunks/MarkdownImpl-CyeTVvSt.js → components/MarkdownImpl.js} +0 -0
@@ -0,0 +1,848 @@
1
+ import { applyPartDelta, finalizeParts } from "./parts.js";
2
+ import { chatReducer, initialState } from "./chatReducer.js";
3
+ import { ChatConfigProvider, resolveChatConfig } from "./config.js";
4
+ import { useSSEStream } from "./useSSEStream.js";
5
+ import { createContext, useCallback, useContext, useEffect, useMemo, useReducer, useRef } from "react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ import { DateTime } from "luxon";
8
+ //#region src/ai/core/ChatProvider.tsx
9
+ var SELECTED_MODEL_STORAGE_KEY = "aiChatSelectedModel";
10
+ var SIDEBAR_COLLAPSED_STORAGE_KEY = "aiChatSidebarCollapsed";
11
+ function readStoredSidebarCollapsed() {
12
+ try {
13
+ return globalThis.localStorage?.getItem(SIDEBAR_COLLAPSED_STORAGE_KEY) === "1";
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
18
+ function storeSidebarCollapsed(collapsed) {
19
+ try {
20
+ globalThis.localStorage?.setItem(SIDEBAR_COLLAPSED_STORAGE_KEY, collapsed ? "1" : "0");
21
+ } catch {}
22
+ }
23
+ function readStoredModel() {
24
+ try {
25
+ return globalThis.localStorage?.getItem(SELECTED_MODEL_STORAGE_KEY) ?? null;
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+ function storeSelectedModel(modelId) {
31
+ try {
32
+ globalThis.localStorage?.setItem(SELECTED_MODEL_STORAGE_KEY, modelId);
33
+ } catch {}
34
+ }
35
+ function generateTitleFromMessage(content) {
36
+ const cleaned = content.replace(/```[\s\S]*?```/g, "").replace(/`[^`]+`/g, "").replace(/[#*_~[\]()]/g, "").replace(/\s+/g, " ").trim();
37
+ if (cleaned.length <= 50) return cleaned;
38
+ const truncated = cleaned.substring(0, 50);
39
+ const lastSpace = truncated.lastIndexOf(" ");
40
+ if (lastSpace > 30) return `${truncated.substring(0, lastSpace)}...`;
41
+ return `${truncated}...`;
42
+ }
43
+ function errorText(error) {
44
+ return error instanceof Error ? error.message : String(error);
45
+ }
46
+ var ChatContext = createContext(void 0);
47
+ function useChat() {
48
+ const context = useContext(ChatContext);
49
+ if (!context) throw new Error("useChat must be used within a ChatProvider");
50
+ return context;
51
+ }
52
+ function ChatProvider({ adapter, currentUser, navigation, notify, activeConversationId = null, config, children }) {
53
+ const [state, dispatch] = useReducer(chatReducer, initialState, (seed) => ({
54
+ ...seed,
55
+ sidebarCollapsed: readStoredSidebarCollapsed()
56
+ }));
57
+ const hasLoadedConversationsRef = useRef(false);
58
+ const { onStreamingChunk, startStreaming, stopAnimationLoop, resetAnimationRefs, targetStreamContentRef, streamActiveRef, streamingConversationIdRef, backgroundStreamsRef, abortControllerRef } = useSSEStream(dispatch);
59
+ const currentConversationIdRef = useRef(null);
60
+ const selectedProviderRef = useRef(state.selectedProvider);
61
+ useEffect(() => {
62
+ selectedProviderRef.current = state.selectedProvider;
63
+ }, [state.selectedProvider]);
64
+ const isSelectedModelAvailable = !!state.selectedProvider && (!state.hasLoadedModels || state.models.some((m) => m.id === state.selectedProvider));
65
+ const isSendingRef = useRef(false);
66
+ const pendingQueueRef = useRef([]);
67
+ const sendMessageRef = useRef(null);
68
+ const loadConversations = useCallback(async () => {
69
+ if (!hasLoadedConversationsRef.current) dispatch({
70
+ type: "SET_LOADING",
71
+ isLoading: true
72
+ });
73
+ try {
74
+ const conversations = await adapter.conversations.list();
75
+ dispatch({
76
+ type: "SET_CONVERSATIONS",
77
+ conversations
78
+ });
79
+ hasLoadedConversationsRef.current = true;
80
+ } catch (error) {
81
+ notify.error(errorText(error));
82
+ } finally {
83
+ dispatch({
84
+ type: "SET_LOADING",
85
+ isLoading: false
86
+ });
87
+ }
88
+ }, [adapter, notify]);
89
+ const streamingToConversationRef = useRef(null);
90
+ const loadSequenceRef = useRef(0);
91
+ const loadConversation = useCallback(async (id) => {
92
+ if (streamingToConversationRef.current === id && currentConversationIdRef.current === id) return;
93
+ if (streamingConversationIdRef.current && streamingConversationIdRef.current !== id) resetAnimationRefs();
94
+ const bgStream = backgroundStreamsRef.current.get(id);
95
+ dispatch({
96
+ type: "SET_LOADING_CONVERSATION",
97
+ isLoadingConversation: true
98
+ });
99
+ const sequence = ++loadSequenceRef.current;
100
+ let conversation;
101
+ try {
102
+ conversation = await adapter.conversations.get(id);
103
+ } catch (error) {
104
+ if (loadSequenceRef.current !== sequence) return;
105
+ dispatch({
106
+ type: "SET_LOADING_CONVERSATION",
107
+ isLoadingConversation: false
108
+ });
109
+ notify.error(errorText(error));
110
+ currentConversationIdRef.current = null;
111
+ dispatch({
112
+ type: "SET_CURRENT_CONVERSATION",
113
+ conversation: null
114
+ });
115
+ return;
116
+ }
117
+ if (loadSequenceRef.current !== sequence) return;
118
+ dispatch({
119
+ type: "SET_LOADING_CONVERSATION",
120
+ isLoadingConversation: false
121
+ });
122
+ currentConversationIdRef.current = id;
123
+ dispatch({
124
+ type: "SET_CURRENT_CONVERSATION",
125
+ conversation
126
+ });
127
+ if (bgStream?.isActive) {
128
+ streamingConversationIdRef.current = id;
129
+ targetStreamContentRef.current = bgStream.content;
130
+ streamActiveRef.current = true;
131
+ dispatch({
132
+ type: "RESTORE_BACKGROUND_STREAM",
133
+ content: "",
134
+ reasoning: bgStream.reasoning,
135
+ parts: bgStream.parts
136
+ });
137
+ startStreaming(id);
138
+ }
139
+ }, [
140
+ adapter,
141
+ notify,
142
+ resetAnimationRefs,
143
+ streamingConversationIdRef,
144
+ backgroundStreamsRef,
145
+ targetStreamContentRef,
146
+ streamActiveRef,
147
+ startStreaming
148
+ ]);
149
+ const hasLoadedProvidersRef = useRef(false);
150
+ const loadProviders = useCallback(async () => {
151
+ if (hasLoadedProvidersRef.current || !adapter.providers) return;
152
+ try {
153
+ const providers = await adapter.providers.list();
154
+ dispatch({
155
+ type: "SET_PROVIDERS",
156
+ providers
157
+ });
158
+ hasLoadedProvidersRef.current = true;
159
+ } catch (error) {
160
+ notify.error(errorText(error));
161
+ }
162
+ }, [adapter, notify]);
163
+ const refreshProviders = useCallback(async () => {
164
+ hasLoadedProvidersRef.current = false;
165
+ await loadProviders();
166
+ }, [loadProviders]);
167
+ const hasLoadedModelsRef = useRef(false);
168
+ const loadModels = useCallback(async () => {
169
+ if (hasLoadedModelsRef.current) return;
170
+ dispatch({
171
+ type: "SET_LOADING_MODELS",
172
+ isLoadingModels: true
173
+ });
174
+ try {
175
+ const { models, unreachableSessions } = await adapter.models.list();
176
+ dispatch({
177
+ type: "SET_MODELS",
178
+ models,
179
+ unreachableSessions
180
+ });
181
+ hasLoadedModelsRef.current = true;
182
+ const current = selectedProviderRef.current;
183
+ const stored = readStoredModel();
184
+ const offers = (id) => !!id && models.some((m) => m.id === id);
185
+ const resolved = offers(current) ? current : offers(stored) ? stored : models[0]?.id ?? null;
186
+ if (resolved !== current) dispatch({
187
+ type: "SET_SELECTED_PROVIDER",
188
+ providerId: resolved
189
+ });
190
+ } catch (error) {
191
+ console.error("Failed to load models:", error);
192
+ dispatch({
193
+ type: "SET_MODELS_ERROR",
194
+ error: errorText(error)
195
+ });
196
+ hasLoadedModelsRef.current = true;
197
+ } finally {
198
+ dispatch({
199
+ type: "SET_LOADING_MODELS",
200
+ isLoadingModels: false
201
+ });
202
+ }
203
+ }, [adapter]);
204
+ const refreshModels = useCallback(async () => {
205
+ hasLoadedModelsRef.current = false;
206
+ dispatch({
207
+ type: "SET_MODELS_ERROR",
208
+ error: null
209
+ });
210
+ await loadModels();
211
+ }, [loadModels]);
212
+ useEffect(() => {
213
+ loadProviders();
214
+ loadModels();
215
+ }, [loadProviders, loadModels]);
216
+ const createConversation = useCallback(async (title) => {
217
+ try {
218
+ const conversation = await adapter.conversations.create(title);
219
+ dispatch({
220
+ type: "ADD_CONVERSATION",
221
+ conversation
222
+ });
223
+ return conversation.id;
224
+ } catch (error) {
225
+ notify.error(errorText(error));
226
+ return null;
227
+ }
228
+ }, [adapter, notify]);
229
+ const deleteConversation = useCallback(async (id) => {
230
+ try {
231
+ await adapter.conversations.remove(id);
232
+ } catch (error) {
233
+ notify.error(errorText(error));
234
+ return false;
235
+ }
236
+ dispatch({
237
+ type: "REMOVE_CONVERSATION",
238
+ conversationId: id
239
+ });
240
+ if (state.currentConversation?.id === id) {
241
+ dispatch({
242
+ type: "SET_CURRENT_CONVERSATION",
243
+ conversation: null
244
+ });
245
+ navigation.toNewChat();
246
+ }
247
+ return true;
248
+ }, [
249
+ adapter,
250
+ notify,
251
+ state.currentConversation,
252
+ navigation
253
+ ]);
254
+ const sendMessage = useCallback(async (content, attachmentIds, parentId, attachmentMeta, _precedingMessages) => {
255
+ if (isSendingRef.current) {
256
+ const convId = currentConversationIdRef.current;
257
+ if (!convId) return false;
258
+ const MAX_QUEUED_MESSAGES = 10;
259
+ if (pendingQueueRef.current.length >= MAX_QUEUED_MESSAGES) {
260
+ notify.error(`You can queue up to ${MAX_QUEUED_MESSAGES} messages while waiting for a response`);
261
+ return false;
262
+ }
263
+ for (const pm of _precedingMessages ?? []) {
264
+ const pmId = crypto.randomUUID();
265
+ pendingQueueRef.current.push({
266
+ id: pmId,
267
+ ...pm
268
+ });
269
+ dispatch({
270
+ type: "ADD_QUEUED_MESSAGE",
271
+ message: {
272
+ id: pmId,
273
+ role: "user",
274
+ content: pm.content,
275
+ timestamp: DateTime.now().toISO(),
276
+ author: currentUser,
277
+ attachments: pm.attachmentIds.length ? pm.attachmentIds.map((id) => ({
278
+ id,
279
+ filename: pm.attachmentMeta[id]?.filename ?? "",
280
+ contentType: pm.attachmentMeta[id]?.contentType ?? "",
281
+ size: pm.attachmentMeta[id]?.size ?? 0,
282
+ uploadedAt: DateTime.now().toISO()
283
+ })) : void 0
284
+ },
285
+ conversationId: convId
286
+ });
287
+ }
288
+ const queuedId = crypto.randomUUID();
289
+ pendingQueueRef.current.push({
290
+ id: queuedId,
291
+ content,
292
+ attachmentIds: attachmentIds ? [...attachmentIds] : [],
293
+ attachmentMeta: attachmentMeta ? { ...attachmentMeta } : {}
294
+ });
295
+ dispatch({
296
+ type: "ADD_QUEUED_MESSAGE",
297
+ message: {
298
+ id: queuedId,
299
+ role: "user",
300
+ content,
301
+ timestamp: DateTime.now().toISO(),
302
+ author: currentUser,
303
+ attachments: attachmentIds?.map((id) => ({
304
+ id,
305
+ filename: attachmentMeta?.[id]?.filename ?? "",
306
+ contentType: attachmentMeta?.[id]?.contentType ?? "",
307
+ size: attachmentMeta?.[id]?.size ?? 0,
308
+ uploadedAt: DateTime.now().toISO()
309
+ }))
310
+ },
311
+ conversationId: convId
312
+ });
313
+ return true;
314
+ }
315
+ if (!isSelectedModelAvailable) {
316
+ notify.error("Please select a provider first");
317
+ return false;
318
+ }
319
+ isSendingRef.current = true;
320
+ if (pendingQueueRef.current.length > 0) {
321
+ const held = pendingQueueRef.current;
322
+ pendingQueueRef.current = [];
323
+ dispatch({ type: "CLEAR_QUEUED_MESSAGES" });
324
+ _precedingMessages = [...held.map(({ id: _id, ...rest }) => rest), ..._precedingMessages ?? []];
325
+ }
326
+ let conversationId = currentConversationIdRef.current;
327
+ let isNewConversation = false;
328
+ if (!conversationId) {
329
+ const newId = await createConversation();
330
+ if (!newId) {
331
+ isSendingRef.current = false;
332
+ return false;
333
+ }
334
+ conversationId = newId;
335
+ isNewConversation = true;
336
+ currentConversationIdRef.current = newId;
337
+ const generatedTitle = content.trim() ? generateTitleFromMessage(content) : null;
338
+ dispatch({
339
+ type: "SET_CURRENT_CONVERSATION",
340
+ conversation: {
341
+ id: newId,
342
+ title: generatedTitle,
343
+ messages: [],
344
+ activeBranchId: null,
345
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
346
+ isOwner: true,
347
+ canCollaborate: true
348
+ }
349
+ });
350
+ navigation.toConversation(newId);
351
+ if (generatedTitle) adapter.conversations.rename(newId, generatedTitle).then(() => {
352
+ loadConversations();
353
+ }).catch((err) => console.error("Failed to set title:", err));
354
+ }
355
+ streamingToConversationRef.current = conversationId;
356
+ const targetConversationId = conversationId;
357
+ startStreaming(targetConversationId);
358
+ let lastBranchId = parentId || state.currentConversation?.activeBranchId || null;
359
+ const precedingApiMessages = [];
360
+ if (_precedingMessages?.length) for (const pm of _precedingMessages) {
361
+ const pmId = crypto.randomUUID();
362
+ const pmMessage = {
363
+ id: pmId,
364
+ parentId: lastBranchId,
365
+ role: "user",
366
+ content: pm.content,
367
+ attachments: pm.attachmentIds?.length ? pm.attachmentIds.map((id) => ({
368
+ id,
369
+ filename: pm.attachmentMeta[id]?.filename ?? "",
370
+ contentType: pm.attachmentMeta[id]?.contentType ?? "",
371
+ size: pm.attachmentMeta[id]?.size ?? 0,
372
+ uploadedAt: DateTime.now().toISO()
373
+ })) : void 0,
374
+ timestamp: DateTime.now().toISO(),
375
+ author: currentUser
376
+ };
377
+ dispatch({
378
+ type: "ADD_MESSAGE",
379
+ message: pmMessage,
380
+ conversationId: targetConversationId
381
+ });
382
+ precedingApiMessages.push({
383
+ role: "user",
384
+ content: pm.content,
385
+ attachment_ids: pm.attachmentIds?.length ? pm.attachmentIds : void 0
386
+ });
387
+ lastBranchId = pmId;
388
+ }
389
+ const userMessageId = crypto.randomUUID();
390
+ const userMessage = {
391
+ id: userMessageId,
392
+ parentId: lastBranchId,
393
+ role: "user",
394
+ content,
395
+ attachments: attachmentIds?.map((id) => ({
396
+ id,
397
+ filename: attachmentMeta?.[id]?.filename ?? "",
398
+ contentType: attachmentMeta?.[id]?.contentType ?? "",
399
+ size: attachmentMeta?.[id]?.size ?? 0,
400
+ uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
401
+ })),
402
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
403
+ author: currentUser
404
+ };
405
+ dispatch({
406
+ type: "ADD_MESSAGE",
407
+ message: userMessage,
408
+ conversationId: targetConversationId
409
+ });
410
+ backgroundStreamsRef.current.set(targetConversationId, {
411
+ content: "",
412
+ reasoning: "",
413
+ parts: [],
414
+ messageId: null,
415
+ isActive: true
416
+ });
417
+ const setMessageError = (errorMsg) => {
418
+ dispatch({
419
+ type: "UPDATE_MESSAGE",
420
+ messageId: userMessageId,
421
+ updates: { error: errorMsg },
422
+ conversationId: targetConversationId
423
+ });
424
+ };
425
+ const apiMessages = [
426
+ ...(isNewConversation ? [] : state.currentConversation?.messages || []).filter((m) => m.content || m.toolCalls?.length).map((m) => {
427
+ const msg = {
428
+ role: m.role,
429
+ content: m.content || null
430
+ };
431
+ if (m.role === "assistant" && m.reasoning) msg.reasoning_content = m.reasoning;
432
+ if (m.role === "assistant" && m.toolCalls?.length) msg.tool_calls = m.toolCalls;
433
+ if (m.role === "assistant" && m.responsesOutput?.length && m.model === state.selectedProvider) msg.responses_output = m.responsesOutput;
434
+ if (m.role === "tool" && m.toolCallId) msg.tool_call_id = m.toolCallId;
435
+ return msg;
436
+ }),
437
+ ...precedingApiMessages,
438
+ {
439
+ role: "user",
440
+ content,
441
+ attachment_ids: attachmentIds?.length ? attachmentIds : void 0
442
+ }
443
+ ];
444
+ dispatch({
445
+ type: "SET_STREAMING",
446
+ isStreaming: true,
447
+ conversationId: targetConversationId
448
+ });
449
+ dispatch({
450
+ type: "CLEAR_STREAMING_CONTENT",
451
+ conversationId: targetConversationId
452
+ });
453
+ const controller = new AbortController();
454
+ abortControllerRef.current = controller;
455
+ let sendSucceeded = false;
456
+ let wasAborted = false;
457
+ try {
458
+ const modelString = state.selectedProvider;
459
+ const useResponses = state.models.find((m) => m.id === modelString)?.supports_responses === true;
460
+ let thinkingStart = null;
461
+ const markStreamStart = () => {
462
+ if (thinkingStart === null) thinkingStart = Date.now();
463
+ };
464
+ let liveParts = [];
465
+ const { content: assistantContent, messageId: assistantMessageId, toolCalls, model: resolvedModel, reasoning: assistantReasoning, responsesOutput } = await adapter.streamCompletion({
466
+ model: modelString,
467
+ messages: apiMessages,
468
+ conversationId,
469
+ parentMessageId: userMessage.parentId ?? null,
470
+ userMessageId,
471
+ allocation: state.selectedAllocation,
472
+ useResponses
473
+ }, {
474
+ onContent: (chunk) => {
475
+ markStreamStart();
476
+ onStreamingChunk(chunk);
477
+ },
478
+ onReasoning: (reasoning) => {
479
+ markStreamStart();
480
+ dispatch({
481
+ type: "APPEND_STREAMING_REASONING",
482
+ reasoning,
483
+ conversationId: targetConversationId
484
+ });
485
+ const bgStream = backgroundStreamsRef.current.get(targetConversationId);
486
+ if (bgStream) bgStream.reasoning += reasoning;
487
+ },
488
+ onMessageId: (messageId) => {
489
+ markStreamStart();
490
+ const bgStream = backgroundStreamsRef.current.get(targetConversationId);
491
+ if (bgStream) bgStream.messageId = messageId;
492
+ },
493
+ onPart: (delta) => {
494
+ markStreamStart();
495
+ liveParts = applyPartDelta(liveParts, delta);
496
+ dispatch({
497
+ type: "APPLY_STREAMING_PART",
498
+ delta,
499
+ conversationId: targetConversationId
500
+ });
501
+ const bgStream = backgroundStreamsRef.current.get(targetConversationId);
502
+ if (bgStream) bgStream.parts = liveParts;
503
+ }
504
+ }, controller.signal);
505
+ const thinkingDuration = assistantReasoning && thinkingStart !== null ? Date.now() - thinkingStart : null;
506
+ const assistantMessage = {
507
+ id: assistantMessageId || crypto.randomUUID(),
508
+ parentId: userMessageId,
509
+ role: "assistant",
510
+ content: assistantContent || (toolCalls.length > 0 ? null : ""),
511
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
512
+ model: useResponses ? modelString : resolvedModel || modelString,
513
+ providerId: state.selectedProvider,
514
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
515
+ reasoning: assistantReasoning || void 0,
516
+ reasoningDuration: thinkingDuration ?? void 0,
517
+ responsesOutput: responsesOutput.length ? responsesOutput : void 0,
518
+ ...liveParts.length ? { parts: finalizeParts(liveParts) } : {}
519
+ };
520
+ dispatch({
521
+ type: "ADD_MESSAGE",
522
+ message: assistantMessage,
523
+ conversationId: targetConversationId
524
+ });
525
+ if (isNewConversation) loadConversations();
526
+ sendSucceeded = true;
527
+ return true;
528
+ } catch (error) {
529
+ if (error instanceof DOMException && error.name === "AbortError") {
530
+ wasAborted = true;
531
+ return false;
532
+ }
533
+ console.error("Failed to send message:", error);
534
+ setMessageError(errorText(error));
535
+ return false;
536
+ } finally {
537
+ abortControllerRef.current = null;
538
+ const { unrevealed } = stopAnimationLoop();
539
+ if (unrevealed) dispatch({
540
+ type: "APPEND_STREAMING_CONTENT",
541
+ content: unrevealed,
542
+ conversationId: targetConversationId
543
+ });
544
+ streamingConversationIdRef.current = null;
545
+ backgroundStreamsRef.current.delete(targetConversationId);
546
+ setTimeout(() => {
547
+ streamingToConversationRef.current = null;
548
+ }, 500);
549
+ dispatch({
550
+ type: "SET_STREAMING",
551
+ isStreaming: false,
552
+ conversationId: targetConversationId
553
+ });
554
+ dispatch({
555
+ type: "CLEAR_STREAMING_CONTENT",
556
+ conversationId: targetConversationId
557
+ });
558
+ if (wasAborted) isSendingRef.current = false;
559
+ else if (sendSucceeded) {
560
+ const queued = pendingQueueRef.current;
561
+ pendingQueueRef.current = [];
562
+ dispatch({ type: "CLEAR_QUEUED_MESSAGES" });
563
+ if (queued.length > 0) {
564
+ const lastQueued = queued[queued.length - 1];
565
+ const preceding = queued.slice(0, -1);
566
+ setTimeout(() => {
567
+ isSendingRef.current = false;
568
+ sendMessageRef.current?.(lastQueued.content, lastQueued.attachmentIds.length > 0 ? lastQueued.attachmentIds : void 0, void 0, Object.keys(lastQueued.attachmentMeta).length > 0 ? lastQueued.attachmentMeta : void 0, preceding.length > 0 ? preceding : void 0);
569
+ }, 0);
570
+ } else isSendingRef.current = false;
571
+ } else isSendingRef.current = false;
572
+ }
573
+ }, [
574
+ adapter,
575
+ notify,
576
+ isSelectedModelAvailable,
577
+ state.selectedProvider,
578
+ state.selectedAllocation,
579
+ state.currentConversation,
580
+ state.models,
581
+ currentUser,
582
+ createConversation,
583
+ loadConversations,
584
+ navigation,
585
+ onStreamingChunk,
586
+ startStreaming,
587
+ stopAnimationLoop,
588
+ streamingConversationIdRef,
589
+ backgroundStreamsRef,
590
+ abortControllerRef
591
+ ]);
592
+ useEffect(() => {
593
+ sendMessageRef.current = sendMessage;
594
+ }, [sendMessage]);
595
+ const updateConversationTitle = useCallback(async (id, title) => {
596
+ try {
597
+ await adapter.conversations.rename(id, title);
598
+ } catch (error) {
599
+ notify.error(errorText(error));
600
+ return false;
601
+ }
602
+ dispatch({
603
+ type: "SET_CONVERSATIONS",
604
+ conversations: state.conversations.map((c) => c.id === id ? {
605
+ ...c,
606
+ title
607
+ } : c)
608
+ });
609
+ if (state.currentConversation?.id === id) dispatch({
610
+ type: "SET_CURRENT_CONVERSATION",
611
+ conversation: {
612
+ ...state.currentConversation,
613
+ title
614
+ }
615
+ });
616
+ return true;
617
+ }, [
618
+ adapter,
619
+ notify,
620
+ state.conversations,
621
+ state.currentConversation
622
+ ]);
623
+ const setSelectedProvider = useCallback((providerId) => {
624
+ if (providerId && state.models.some((m) => m.id === providerId)) storeSelectedModel(providerId);
625
+ dispatch({
626
+ type: "SET_SELECTED_PROVIDER",
627
+ providerId
628
+ });
629
+ }, [state.models]);
630
+ const setSelectedAllocation = useCallback((allocation) => {
631
+ dispatch({
632
+ type: "SET_SELECTED_ALLOCATION",
633
+ allocation
634
+ });
635
+ }, []);
636
+ const retryMessage = useCallback(async (messageId) => {
637
+ const currentConvId = state.currentConversation?.id;
638
+ const messages = state.currentConversation?.messages || [];
639
+ const failedMessage = messages.find((m) => m.id === messageId);
640
+ if (!failedMessage?.error || !currentConvId) return false;
641
+ if (failedMessage.role === "user") {
642
+ dispatch({
643
+ type: "UPDATE_MESSAGE",
644
+ messageId,
645
+ updates: { error: null },
646
+ conversationId: currentConvId
647
+ });
648
+ return sendMessage(failedMessage.content || "", failedMessage.attachments?.map((a) => a.id), failedMessage.parentId || void 0);
649
+ }
650
+ if (failedMessage.role === "assistant") {
651
+ const parentMessage = messages.find((m) => m.id === failedMessage.parentId);
652
+ if (parentMessage && parentMessage.role === "user") {
653
+ dispatch({
654
+ type: "UPDATE_MESSAGE",
655
+ messageId,
656
+ updates: { error: null },
657
+ conversationId: currentConvId
658
+ });
659
+ return sendMessage(parentMessage.content || "", parentMessage.attachments?.map((a) => a.id), parentMessage.parentId || void 0);
660
+ }
661
+ }
662
+ return false;
663
+ }, [
664
+ state.currentConversation?.messages,
665
+ state.currentConversation?.id,
666
+ sendMessage
667
+ ]);
668
+ const stopStreaming = useCallback(() => {
669
+ if (abortControllerRef.current) {
670
+ abortControllerRef.current.abort();
671
+ abortControllerRef.current = null;
672
+ }
673
+ const conversationId = streamingConversationIdRef.current;
674
+ const bgStream = conversationId ? backgroundStreamsRef.current.get(conversationId) : null;
675
+ const stoppedContent = bgStream?.content || "";
676
+ const stoppedReasoning = bgStream?.reasoning || "";
677
+ const stoppedParts = bgStream?.parts ?? [];
678
+ const stoppedMessageId = bgStream?.messageId || null;
679
+ const hasContent = stoppedContent || stoppedReasoning || stoppedParts.length;
680
+ stopAnimationLoop();
681
+ if (conversationId) backgroundStreamsRef.current.delete(conversationId);
682
+ streamingConversationIdRef.current = null;
683
+ streamingToConversationRef.current = null;
684
+ resetAnimationRefs();
685
+ dispatch({
686
+ type: "SET_STREAMING",
687
+ isStreaming: false
688
+ });
689
+ dispatch({ type: "CLEAR_STREAMING_CONTENT" });
690
+ if (!conversationId || !hasContent) return;
691
+ const lastUserMessage = [...state.currentConversation?.messages || []].reverse().find((m) => m.role === "user");
692
+ const messageId = stoppedMessageId || crypto.randomUUID();
693
+ dispatch({
694
+ type: "ADD_MESSAGE",
695
+ message: {
696
+ id: messageId,
697
+ parentId: lastUserMessage?.id || null,
698
+ role: "assistant",
699
+ content: stoppedContent || null,
700
+ reasoning: stoppedReasoning || void 0,
701
+ ...stoppedParts.length ? { parts: finalizeParts(stoppedParts) } : {},
702
+ reasoningDuration: state.thinkingStartTime ? Date.now() - state.thinkingStartTime : void 0,
703
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
704
+ stopped: true
705
+ },
706
+ conversationId
707
+ });
708
+ setTimeout(() => {
709
+ if (currentConversationIdRef.current === conversationId) loadConversation(conversationId);
710
+ }, 500);
711
+ }, [
712
+ resetAnimationRefs,
713
+ streamingConversationIdRef,
714
+ abortControllerRef,
715
+ backgroundStreamsRef,
716
+ stopAnimationLoop,
717
+ loadConversation,
718
+ state.currentConversation?.messages,
719
+ state.thinkingStartTime
720
+ ]);
721
+ const sidebarCollapsedRef = useRef(state.sidebarCollapsed);
722
+ sidebarCollapsedRef.current = state.sidebarCollapsed;
723
+ const toggleSidebar = useCallback(() => {
724
+ storeSidebarCollapsed(!sidebarCollapsedRef.current);
725
+ dispatch({ type: "TOGGLE_SIDEBAR" });
726
+ }, []);
727
+ const navigateToBranch = useCallback((messageId) => {
728
+ if (!state.currentConversation) return;
729
+ dispatch({
730
+ type: "SET_CURRENT_CONVERSATION",
731
+ conversation: {
732
+ ...state.currentConversation,
733
+ activeBranchId: messageId
734
+ }
735
+ });
736
+ }, [state.currentConversation]);
737
+ const removeQueuedMessage = useCallback((id) => {
738
+ pendingQueueRef.current = pendingQueueRef.current.filter((m) => m.id !== id);
739
+ dispatch({
740
+ type: "REMOVE_QUEUED_MESSAGE",
741
+ id
742
+ });
743
+ }, []);
744
+ const flushQueuedMessages = useCallback(() => {
745
+ if (isSendingRef.current) return;
746
+ const held = pendingQueueRef.current;
747
+ if (held.length === 0) return;
748
+ pendingQueueRef.current = [];
749
+ dispatch({ type: "CLEAR_QUEUED_MESSAGES" });
750
+ const last = held[held.length - 1];
751
+ const preceding = held.slice(0, -1).map(({ id: _id, ...rest }) => rest);
752
+ sendMessageRef.current?.(last.content, last.attachmentIds.length > 0 ? last.attachmentIds : void 0, void 0, Object.keys(last.attachmentMeta).length > 0 ? last.attachmentMeta : void 0, preceding.length > 0 ? preceding : void 0);
753
+ }, []);
754
+ const clearCurrentConversation = useCallback(() => {
755
+ currentConversationIdRef.current = null;
756
+ loadSequenceRef.current++;
757
+ streamingConversationIdRef.current = null;
758
+ resetAnimationRefs();
759
+ pendingQueueRef.current = [];
760
+ isSendingRef.current = false;
761
+ dispatch({
762
+ type: "SET_CURRENT_CONVERSATION",
763
+ conversation: null
764
+ });
765
+ }, [resetAnimationRefs, streamingConversationIdRef]);
766
+ const resolvedConfig = useMemo(() => resolveChatConfig(config), [config]);
767
+ const value = useMemo(() => ({
768
+ adapter,
769
+ currentUser,
770
+ navigation,
771
+ notify,
772
+ activeConversationId,
773
+ conversations: state.conversations,
774
+ currentConversation: state.currentConversation,
775
+ providers: state.providers,
776
+ models: state.models,
777
+ unreachableSessions: state.unreachableSessions,
778
+ selectedProvider: state.selectedProvider,
779
+ isSelectedModelAvailable,
780
+ selectedAllocation: state.selectedAllocation,
781
+ isLoading: state.isLoading,
782
+ isLoadingConversation: state.isLoadingConversation,
783
+ isLoadingModels: state.isLoadingModels,
784
+ hasLoadedModels: state.hasLoadedModels,
785
+ modelsError: state.modelsError,
786
+ isStreaming: state.isStreaming,
787
+ isThinking: state.isThinking,
788
+ streamingContent: state.streamingContent,
789
+ streamingReasoning: state.streamingReasoning,
790
+ streamingParts: state.streamingParts,
791
+ thinkingStartTime: state.thinkingStartTime,
792
+ sidebarCollapsed: state.sidebarCollapsed,
793
+ queuedMessages: state.queuedMessages,
794
+ loadConversations,
795
+ loadConversation,
796
+ loadProviders,
797
+ refreshProviders,
798
+ refreshModels,
799
+ createConversation,
800
+ deleteConversation,
801
+ sendMessage,
802
+ retryMessage,
803
+ setSelectedProvider,
804
+ setSelectedAllocation,
805
+ stopStreaming,
806
+ removeQueuedMessage,
807
+ flushQueuedMessages,
808
+ toggleSidebar,
809
+ updateConversationTitle,
810
+ navigateToBranch,
811
+ clearCurrentConversation
812
+ }), [
813
+ adapter,
814
+ currentUser,
815
+ navigation,
816
+ notify,
817
+ activeConversationId,
818
+ state,
819
+ isSelectedModelAvailable,
820
+ loadConversations,
821
+ loadConversation,
822
+ loadProviders,
823
+ refreshProviders,
824
+ refreshModels,
825
+ createConversation,
826
+ deleteConversation,
827
+ sendMessage,
828
+ retryMessage,
829
+ setSelectedProvider,
830
+ setSelectedAllocation,
831
+ stopStreaming,
832
+ removeQueuedMessage,
833
+ flushQueuedMessages,
834
+ toggleSidebar,
835
+ updateConversationTitle,
836
+ navigateToBranch,
837
+ clearCurrentConversation
838
+ ]);
839
+ return /* @__PURE__ */ jsx(ChatConfigProvider, {
840
+ value: resolvedConfig,
841
+ children: /* @__PURE__ */ jsx(ChatContext.Provider, {
842
+ value,
843
+ children
844
+ })
845
+ });
846
+ }
847
+ //#endregion
848
+ export { ChatProvider, useChat };