@parallelworks/ui 0.18.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 (349) hide show
  1. package/README.md +86 -6
  2. package/dist/ai/adapter/memoryStore.d.ts +8 -0
  3. package/dist/ai/adapter/memoryStore.js +152 -0
  4. package/dist/ai/adapter/mock/index.d.ts +2 -0
  5. package/dist/ai/adapter/mock/index.js +3 -0
  6. package/dist/ai/adapter/mock/mockAdapter.d.ts +4 -0
  7. package/dist/ai/adapter/mock/mockAdapter.js +81 -0
  8. package/dist/ai/adapter/openai/createOpenAIStream.d.ts +9 -0
  9. package/dist/ai/adapter/openai/createOpenAIStream.js +33 -0
  10. package/dist/ai/adapter/openai/errors.d.ts +3 -0
  11. package/dist/ai/adapter/openai/errors.js +37 -0
  12. package/dist/ai/adapter/openai/index.d.ts +6 -0
  13. package/dist/ai/adapter/openai/index.js +6 -0
  14. package/dist/ai/adapter/openai/openaiAdapter.d.ts +9 -0
  15. package/dist/ai/adapter/openai/openaiAdapter.js +54 -0
  16. package/dist/ai/adapter/openai/responses.d.ts +4 -0
  17. package/dist/ai/adapter/openai/responses.js +108 -0
  18. package/dist/ai/adapter/openai/sse.d.ts +24 -0
  19. package/dist/ai/adapter/openai/sse.js +112 -0
  20. package/dist/ai/adapter/openai/wire.d.ts +40 -0
  21. package/dist/ai/adapter/types.d.ts +131 -0
  22. package/dist/ai/adapter/types.js +11 -0
  23. package/dist/ai/agent/classifier.d.ts +2 -0
  24. package/dist/ai/agent/classifier.js +12 -0
  25. package/dist/ai/agent/diff.d.ts +35 -0
  26. package/dist/ai/agent/diff.js +223 -0
  27. package/dist/ai/agent/diffHighlight.d.ts +6 -0
  28. package/dist/ai/agent/diffHighlight.js +101 -0
  29. package/dist/ai/agent/index.d.ts +6 -0
  30. package/dist/ai/agent/permissions.d.ts +11 -0
  31. package/dist/ai/agent/permissions.js +29 -0
  32. package/dist/ai/agent/toolfmt.d.ts +18 -0
  33. package/dist/ai/agent/toolfmt.js +383 -0
  34. package/dist/ai/agent/transcript.d.ts +38 -0
  35. package/dist/ai/agent/transcript.js +127 -0
  36. package/dist/ai/ai.css +325 -0
  37. package/dist/ai/components/AllocationSelector.d.ts +4 -0
  38. package/dist/ai/components/AllocationSelector.js +56 -0
  39. package/dist/ai/components/AttachmentManager.d.ts +1 -0
  40. package/dist/ai/components/AttachmentManager.js +144 -0
  41. package/dist/ai/components/AttachmentUpload.d.ts +24 -0
  42. package/dist/ai/components/AttachmentUpload.js +179 -0
  43. package/dist/ai/components/BlockedGroupBanner.d.ts +1 -0
  44. package/dist/ai/components/BlockedGroupBanner.js +29 -0
  45. package/dist/ai/components/BranchNavigator.d.ts +8 -0
  46. package/dist/ai/components/BranchNavigator.js +57 -0
  47. package/dist/ai/components/ChatEmptyState.d.ts +3 -0
  48. package/dist/ai/components/ChatEmptyState.js +171 -0
  49. package/dist/ai/components/ChatInput.d.ts +45 -0
  50. package/dist/ai/components/ChatInput.js +265 -0
  51. package/dist/ai/components/ChatLayout.d.ts +5 -0
  52. package/dist/ai/components/ChatLayout.js +15 -0
  53. package/dist/ai/components/ChatMessage.d.ts +24 -0
  54. package/dist/ai/components/ChatMessage.js +338 -0
  55. package/dist/ai/components/ChatMessageList.d.ts +35 -0
  56. package/dist/ai/components/ChatMessageList.js +301 -0
  57. package/dist/ai/components/ChatSidebar.d.ts +5 -0
  58. package/dist/ai/components/ChatSidebar.js +345 -0
  59. package/dist/ai/components/ChatThread.d.ts +3 -0
  60. package/dist/ai/components/ChatThread.js +198 -0
  61. package/dist/ai/components/ComposerChrome.d.ts +18 -0
  62. package/dist/ai/components/ComposerChrome.js +50 -0
  63. package/dist/ai/components/ComposerFrame.d.ts +16 -0
  64. package/dist/ai/components/ComposerFrame.js +33 -0
  65. package/dist/ai/components/DragOverlay.d.ts +5 -0
  66. package/dist/ai/components/DragOverlay.js +20 -0
  67. package/dist/ai/components/ModelSelector.d.ts +6 -0
  68. package/dist/ai/components/ModelSelector.js +343 -0
  69. package/dist/ai/components/ShareDialog.d.ts +11 -0
  70. package/dist/ai/components/ShareDialog.js +260 -0
  71. package/dist/ai/components/SlashMenu.d.ts +28 -0
  72. package/dist/ai/components/SlashMenu.js +91 -0
  73. package/dist/ai/components/agent/ActivityLine.d.ts +21 -0
  74. package/dist/ai/components/agent/ActivityLine.js +151 -0
  75. package/dist/ai/components/agent/AgentMessageParts.d.ts +54 -0
  76. package/dist/ai/components/agent/AgentMessageParts.js +721 -0
  77. package/dist/ai/components/agent/PermissionPicker.d.ts +11 -0
  78. package/dist/ai/components/agent/PermissionPicker.js +38 -0
  79. package/dist/ai/components/attachmentKeys.d.ts +4 -0
  80. package/dist/ai/components/attachmentKeys.js +10 -0
  81. package/dist/ai/core/ChatProvider.d.ts +66 -0
  82. package/dist/ai/core/ChatProvider.js +848 -0
  83. package/dist/ai/core/KeyboardShortcutsProvider.d.ts +5 -0
  84. package/dist/ai/core/KeyboardShortcutsProvider.js +9 -0
  85. package/dist/ai/core/chatReducer.d.ts +114 -0
  86. package/dist/ai/core/chatReducer.js +223 -0
  87. package/dist/ai/core/config.d.ts +47 -0
  88. package/dist/ai/core/config.js +45 -0
  89. package/dist/ai/core/events.d.ts +1 -0
  90. package/dist/ai/core/events.js +4 -0
  91. package/dist/ai/core/exportConversation.d.ts +4 -0
  92. package/dist/ai/core/exportConversation.js +42 -0
  93. package/dist/ai/core/greeting.d.ts +3 -0
  94. package/dist/ai/core/greeting.js +38 -0
  95. package/dist/ai/core/parts.d.ts +11 -0
  96. package/dist/ai/core/parts.js +84 -0
  97. package/dist/ai/core/useDragDrop.d.ts +14 -0
  98. package/dist/ai/core/useDragDrop.js +36 -0
  99. package/dist/ai/core/useKeyboardShortcuts.d.ts +12 -0
  100. package/dist/ai/core/useKeyboardShortcuts.js +77 -0
  101. package/dist/ai/core/useSSEStream.d.ts +34 -0
  102. package/dist/ai/core/useSSEStream.js +98 -0
  103. package/dist/ai/index.d.ts +40 -0
  104. package/dist/ai/index.js +44 -0
  105. package/dist/ai/strings.d.ts +167 -0
  106. package/dist/ai/strings.js +175 -0
  107. package/dist/ai/types.d.ts +236 -0
  108. package/dist/ai/ui/Avatar.d.ts +8 -0
  109. package/dist/ai/ui/Avatar.js +35 -0
  110. package/dist/ai/ui/Dropdown.d.ts +31 -0
  111. package/dist/ai/ui/Dropdown.js +110 -0
  112. package/dist/ai/ui/Markdown.d.ts +8 -0
  113. package/dist/ai/ui/Markdown.js +79 -0
  114. package/dist/ai/ui/Notice.d.ts +21 -0
  115. package/dist/ai/ui/Notice.js +77 -0
  116. package/dist/ai/ui/buttons.d.ts +1 -0
  117. package/dist/ai/ui/codeBreaks.d.ts +16 -0
  118. package/dist/ai/ui/codeBreaks.js +36 -0
  119. package/dist/ai/ui/prose.d.ts +1 -0
  120. package/dist/ai/ui/prose.js +13 -0
  121. package/dist/ai/ui/sidebar.d.ts +81 -0
  122. package/dist/ai/ui/sidebar.js +148 -0
  123. package/dist/ai/utils.d.ts +4 -0
  124. package/dist/ai/utils.js +23 -0
  125. package/dist/assets/{editor.worker-CuBRIwi-.js → editor.worker-BmHUIVG4.js} +194 -163
  126. package/dist/assets/{json.worker-8ytS02vz.js → json.worker-D8bJ92nw.js} +384 -338
  127. package/dist/assets/{yaml.worker-DJC-VUcv.js → yaml.worker-C4gyGAko.js} +15657 -13823
  128. package/dist/components/AddMenu.js +46 -0
  129. package/dist/components/Avatar.d.ts +1 -1
  130. package/dist/components/Avatar.js +46 -0
  131. package/dist/{chunks/BareModal-BtRiQezg.js → components/BareModal.js} +1 -1
  132. package/dist/components/Breadcrumbs.d.ts +1 -1
  133. package/dist/components/Breadcrumbs.js +83 -0
  134. package/dist/components/Button.js +83 -0
  135. package/dist/components/Callout.js +23 -0
  136. package/dist/components/Card.js +31 -0
  137. package/dist/components/CollapsiblePanel.js +77 -0
  138. package/dist/components/ConfirmModal.js +74 -0
  139. package/dist/components/CopyToClipboard.js +81 -0
  140. package/dist/{chunks/CreateModal-BY6DLmur.js → components/CreateModal.js} +10 -189
  141. package/dist/components/DescriptionList.js +42 -0
  142. package/dist/components/Dropdown.d.ts +1 -1
  143. package/dist/components/Dropdown.js +349 -0
  144. package/dist/components/EmptyState.d.ts +1 -1
  145. package/dist/components/EmptyState.js +27 -0
  146. package/dist/components/Filter.js +32 -0
  147. package/dist/components/FilterPill.js +13 -0
  148. package/dist/components/IconButton.js +34 -0
  149. package/dist/components/Indicator.d.ts +2 -2
  150. package/dist/{chunks/Indicator-hBSIxOPK.js → components/Indicator.js} +1 -1
  151. package/dist/components/Input.d.ts +1 -1
  152. package/dist/components/Input.js +88 -0
  153. package/dist/components/LetterBadge.d.ts +1 -1
  154. package/dist/components/LetterBadge.js +13 -0
  155. package/dist/components/Listbox.js +123 -0
  156. package/dist/{chunks/Loader-BXoXTZGc.js → components/Loader.js} +1 -1
  157. package/dist/components/Markdown.js +15 -0
  158. package/dist/components/MarkdownImpl.d.ts +1 -1
  159. package/dist/components/MultiSelect.js +171 -0
  160. package/dist/components/Overview.js +39 -0
  161. package/dist/components/PageHeader.js +194 -0
  162. package/dist/components/Pagination.js +35 -0
  163. package/dist/components/Provider.d.ts +16 -65
  164. package/dist/{chunks/Provider-Ghua9PkN.js → components/Provider.js} +54 -20
  165. package/dist/{chunks/RequiredMark-DfLdYbYZ.js → components/RequiredMark.js} +1 -1
  166. package/dist/components/SectionHeader.js +11 -0
  167. package/dist/components/SettingModeOption.js +30 -0
  168. package/dist/components/SettingRow.d.ts +1 -1
  169. package/dist/components/SettingRow.js +35 -0
  170. package/dist/components/SettingsCard.js +31 -0
  171. package/dist/components/SettingsGroup.d.ts +1 -1
  172. package/dist/components/SettingsGroup.js +17 -0
  173. package/dist/components/Sort.js +42 -0
  174. package/dist/components/StatusBadge.js +63 -0
  175. package/dist/components/SwitchToggle.js +59 -0
  176. package/dist/{chunks/SwitchToggleSmall-FcsaYSTs.js → components/SwitchToggleSmall.js} +1 -1
  177. package/dist/{chunks/Table-4OHjdKdO.js → components/Table.js} +7 -5
  178. package/dist/components/Toggle.js +20 -0
  179. package/dist/{chunks/Tooltip-Crxo_eU_.js → components/Tooltip.js} +6 -4
  180. package/dist/{chunks/StatusBadge-C1Blx_cW.js → components/dropdownUtils.js} +2 -63
  181. package/dist/components/focus.d.ts +2 -0
  182. package/dist/components/focus.js +7 -0
  183. package/dist/components/ghostButton.js +7 -0
  184. package/dist/components/inputClasses.js +4 -0
  185. package/dist/components/keys.d.ts +12 -0
  186. package/dist/components/keys.js +27 -0
  187. package/dist/components/relativeTime.js +32 -0
  188. package/dist/{chunks/useCssIsDark-DHr05dG2.js → components/useCssIsDark.js} +1 -1
  189. package/dist/duration.d.ts +3 -0
  190. package/dist/duration.js +17 -0
  191. package/dist/editor/Editor.js +13 -0
  192. package/dist/editor/EditorField.js +53 -0
  193. package/dist/editor/FormCodePanel.js +173 -0
  194. package/dist/editor/Monaco.d.ts +1 -20
  195. package/dist/editor/Monaco.js +112 -0
  196. package/dist/editor/index.d.ts +2 -1
  197. package/dist/editor/index.js +7 -2
  198. package/dist/editor/themes.js +91 -0
  199. package/dist/editor/workers.js +29 -0
  200. package/dist/editor/yaml.d.ts +5 -0
  201. package/dist/editor/yaml.js +15 -0
  202. package/dist/engine.d.ts +98 -0
  203. package/dist/file-explorer/FileExplorer.d.ts +11 -2
  204. package/dist/file-explorer/FileExplorer.js +1402 -0
  205. package/dist/file-explorer/FileExplorerContext.d.ts +1 -1
  206. package/dist/file-explorer/FileExplorerContext.js +370 -0
  207. package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +1 -1
  208. package/dist/{chunks/CodePreviewEditor-4lx3kE-w.js → file-explorer/FilePreview/CodePreviewEditor.js} +3 -2
  209. package/dist/file-explorer/FilePreview/Inline.js +45 -0
  210. package/dist/file-explorer/FilePreview/StreamedBadge.js +16 -0
  211. package/dist/file-explorer/FilePreview/Viewer.js +526 -0
  212. package/dist/file-explorer/FilePreview/index.js +266 -0
  213. package/dist/file-explorer/FilePreview/parse.js +63 -0
  214. package/dist/file-explorer/FilePreview/previewCache.js +98 -0
  215. package/dist/file-explorer/FilePreview/previewType.d.ts +1 -1
  216. package/dist/file-explorer/FilePreview/previewType.js +151 -0
  217. package/dist/file-explorer/FilePreview/readCappedBytes.js +196 -0
  218. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +95 -0
  219. package/dist/file-explorer/FilePreview/useObjectText.js +109 -0
  220. package/dist/file-explorer/FilePreview/usePresignedUrl.js +73 -0
  221. package/dist/file-explorer/FileRow.d.ts +1 -1
  222. package/dist/file-explorer/FileRow.js +165 -0
  223. package/dist/file-explorer/FileRowsWindow.js +92 -0
  224. package/dist/file-explorer/FileTable.js +172 -0
  225. package/dist/file-explorer/FileTableHeaders.js +49 -0
  226. package/dist/file-explorer/LoadMoreRow.js +46 -0
  227. package/dist/file-explorer/TreeView.js +376 -0
  228. package/dist/file-explorer/helpers.d.ts +4 -4
  229. package/dist/file-explorer/helpers.js +537 -0
  230. package/dist/file-explorer/index.d.ts +3 -3
  231. package/dist/file-explorer/index.js +7 -5705
  232. package/dist/file-explorer/lib/batch.js +39 -0
  233. package/dist/file-explorer/lib/errors.js +9 -0
  234. package/dist/file-explorer/lib/paging.js +124 -0
  235. package/dist/file-explorer/lib/types.d.ts +6 -1
  236. package/dist/file-explorer/lib/utils.d.ts +1 -3
  237. package/dist/file-explorer/lib/utils.js +322 -0
  238. package/dist/file-explorer/toasts.js +400 -0
  239. package/dist/file-explorer/treeRows.js +73 -0
  240. package/dist/file-explorer/useNearEnd.d.ts +1 -1
  241. package/dist/file-explorer/useNearEnd.js +27 -0
  242. package/dist/form/FieldWrapper.d.ts +2 -2
  243. package/dist/form/FieldWrapper.js +54 -0
  244. package/dist/form/Fields/BooleanField.js +51 -0
  245. package/dist/form/Fields/CheckboxGroupField.js +72 -0
  246. package/dist/form/Fields/ColorField.js +33 -0
  247. package/dist/form/Fields/DropdownField.js +49 -0
  248. package/dist/form/Fields/DurationField.js +130 -0
  249. package/dist/form/Fields/LabelField.js +19 -0
  250. package/dist/form/Fields/MultiDropdownField.js +40 -0
  251. package/dist/form/Fields/NumberField.js +51 -0
  252. package/dist/form/Fields/ObjectField.js +54 -0
  253. package/dist/form/Fields/RadioField.js +49 -0
  254. package/dist/form/Fields/RangeField.js +58 -0
  255. package/dist/form/Fields/ResourceOptionIcon.d.ts +1 -1
  256. package/dist/form/Fields/ResourceOptionIcon.js +21 -0
  257. package/dist/form/Fields/StepField.js +6 -0
  258. package/dist/form/Fields/StringField.js +37 -0
  259. package/dist/form/Fields/TextAreaField.js +31 -0
  260. package/dist/form/Fields/durationValidity.js +11 -0
  261. package/dist/form/Form.d.ts +4 -21
  262. package/dist/form/Form.js +643 -0
  263. package/dist/form/SpecialTypes/DropdownWrapper.js +30 -0
  264. package/dist/form/SpecialTypes/FormikCustomDropdown.js +122 -0
  265. package/dist/form/SpecialTypes/FormikCustomInput.js +111 -0
  266. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +31 -0
  267. package/dist/form/SpecialTypes/FormikNumericInput.js +60 -0
  268. package/dist/form/Wizard/WizardContainer.js +138 -0
  269. package/dist/form/Wizard/WizardNavigation.js +92 -0
  270. package/dist/form/Wizard/WizardStepContent.js +35 -0
  271. package/dist/form/Wizard/WizardStepIndicator.js +108 -0
  272. package/dist/form/Wizard/useWizardState.js +118 -0
  273. package/dist/form/Wizard/utils.js +34 -0
  274. package/dist/form/fieldContext.js +26 -0
  275. package/dist/form/fieldRegistry.js +50 -0
  276. package/dist/form/index.d.ts +11 -12
  277. package/dist/form/index.js +18 -2445
  278. package/dist/form/lib.d.ts +6 -1
  279. package/dist/{chunks/FieldWrapper-Bef01EWO.js → form/lib.js} +5 -104
  280. package/dist/form/types/fieldComponentTypes.d.ts +3 -2
  281. package/dist/form/types/fieldTypes.d.ts +6 -20
  282. package/dist/form/useParsedOpts.d.ts +1 -1
  283. package/dist/form/useParsedOpts.js +62 -0
  284. package/dist/form/usePrevious.js +11 -0
  285. package/dist/form/utils/checkShowIfObject.js +14 -0
  286. package/dist/form/utils/{slurmDuration.d.ts → duration.d.ts} +2 -2
  287. package/dist/form/utils/duration.js +69 -0
  288. package/dist/form/utils/getParentValue.js +23 -0
  289. package/dist/form/utils/getPlaceholder.js +9 -0
  290. package/dist/form/utils/getValueUsingPath.js +23 -0
  291. package/dist/form/utils/secondaryField.js +22 -0
  292. package/dist/graph/AnnotationBanner.js +141 -0
  293. package/dist/graph/Collapse.js +41 -0
  294. package/dist/{workflow/index.js → graph/DependencyGraph.js} +76 -1163
  295. package/dist/graph/JobSummary.js +104 -0
  296. package/dist/{workflow → graph}/MatrixGroup.d.ts +2 -2
  297. package/dist/graph/MatrixGroup.js +107 -0
  298. package/dist/graph/Reveal.js +57 -0
  299. package/dist/graph/TreeView.js +711 -0
  300. package/dist/{workflow → graph}/index.d.ts +1 -1
  301. package/dist/graph/index.js +2 -0
  302. package/dist/{workflow → graph}/types.d.ts +3 -3
  303. package/dist/graph/types.js +6 -0
  304. package/dist/icons.d.ts +20 -21
  305. package/dist/index.d.ts +38 -40
  306. package/dist/index.js +47 -599
  307. package/dist/list/ActionToolbar.js +184 -0
  308. package/dist/list/HoverCard.js +73 -0
  309. package/dist/list/ListPage.js +145 -0
  310. package/dist/list/ListPager.js +59 -0
  311. package/dist/list/ListTable.d.ts +1 -1
  312. package/dist/list/ListTable.js +163 -0
  313. package/dist/list/ListViewControls.d.ts +2 -2
  314. package/dist/list/ListViewControls.js +372 -0
  315. package/dist/list/RowContextMenu.js +350 -0
  316. package/dist/list/UserHoverCard.js +117 -0
  317. package/dist/list/index.d.ts +9 -9
  318. package/dist/list/index.js +11 -2
  319. package/dist/list/listSearch.js +137 -0
  320. package/dist/list/listView.js +210 -0
  321. package/dist/list/textEntry.js +10 -0
  322. package/dist/logviewer/LogViewer.d.ts +1 -1
  323. package/dist/logviewer/LogViewer.js +750 -0
  324. package/dist/logviewer/index.d.ts +1 -0
  325. package/dist/logviewer/index.js +3 -742
  326. package/dist/logviewer/segments.d.ts +9 -0
  327. package/dist/logviewer/segments.js +80 -0
  328. package/dist/styles/fonts.css +4 -4
  329. package/dist/styles/theme.css +6 -4
  330. package/dist/styles.css +1 -1
  331. package/dist/theme/index.js +2 -2
  332. package/package.json +38 -14
  333. package/LICENSE +0 -202
  334. package/dist/chunks/Monaco-C1POOdVv.js +0 -1046
  335. package/dist/chunks/PageHeader-l49p1nM9.js +0 -690
  336. package/dist/chunks/SwitchToggle-CHPP8uZQ.js +0 -755
  337. package/dist/chunks/editor-9_uVtUyy.js +0 -347
  338. package/dist/chunks/list-qrojZZNv.js +0 -1411
  339. package/dist/editor/usesGitlab.d.ts +0 -97
  340. package/dist/form/Fields/BucketReadOnlyField.d.ts +0 -7
  341. package/dist/form/types/workflow-schema.d.ts +0 -1145
  342. package/dist/form/utils/pwResolver.d.ts +0 -18
  343. package/dist/{chunks/MarkdownImpl-Bq4QhrtA.js → components/MarkdownImpl.js} +1 -1
  344. /package/dist/{workflow → graph}/AnnotationBanner.d.ts +0 -0
  345. /package/dist/{workflow → graph}/Collapse.d.ts +0 -0
  346. /package/dist/{workflow → graph}/DependencyGraph.d.ts +0 -0
  347. /package/dist/{workflow → graph}/JobSummary.d.ts +0 -0
  348. /package/dist/{workflow → graph}/Reveal.d.ts +0 -0
  349. /package/dist/{workflow → graph}/TreeView.d.ts +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 };