@parallelworks/ui 0.40.1 → 0.42.0

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 (289) hide show
  1. package/README.md +25 -0
  2. package/dist/ai/adapter/memoryStore.js +7 -0
  3. package/dist/ai/adapter/mock/mockAdapter.js +52 -17
  4. package/dist/ai/adapter/openai/createOpenAIStream.js +1 -1
  5. package/dist/ai/adapter/openai/index.d.ts +1 -1
  6. package/dist/ai/adapter/openai/sse.d.ts +8 -2
  7. package/dist/ai/adapter/openai/sse.js +40 -4
  8. package/dist/ai/adapter/openai/wire.d.ts +6 -0
  9. package/dist/ai/components/AllocationSelector.d.ts +3 -4
  10. package/dist/ai/components/AllocationSelector.js +37 -33
  11. package/dist/ai/components/AttachmentManager.js +7 -7
  12. package/dist/ai/components/AttachmentUpload.js +5 -5
  13. package/dist/ai/components/BranchNavigator.js +1 -1
  14. package/dist/ai/components/ChatEmptyState.js +2 -2
  15. package/dist/ai/components/ChatInput.js +10 -8
  16. package/dist/ai/components/ChatLayout.js +4 -5
  17. package/dist/ai/components/ChatMessage.js +12 -12
  18. package/dist/ai/components/ChatMessageList.js +9 -9
  19. package/dist/ai/components/ChatSidebar.js +8 -9
  20. package/dist/ai/components/ChatThread.js +4 -4
  21. package/dist/ai/components/ComposerChrome.d.ts +2 -2
  22. package/dist/ai/components/ComposerChrome.js +3 -6
  23. package/dist/ai/components/DragOverlay.js +2 -2
  24. package/dist/ai/components/ModelSelector.d.ts +5 -3
  25. package/dist/ai/components/ModelSelector.js +102 -339
  26. package/dist/ai/components/PastedText.js +11 -11
  27. package/dist/ai/components/ProviderIssueBanner.d.ts +2 -0
  28. package/dist/ai/components/ProviderIssueBanner.js +34 -16
  29. package/dist/ai/components/Reasoning.js +4 -4
  30. package/dist/ai/components/ShareDialog.js +43 -35
  31. package/dist/ai/components/SlashMenu.d.ts +12 -2
  32. package/dist/ai/components/SlashMenu.js +29 -13
  33. package/dist/ai/components/agent/ActivityLine.js +3 -3
  34. package/dist/ai/components/agent/AgentMessageParts.js +20 -20
  35. package/dist/ai/components/agent/PermissionPicker.js +13 -11
  36. package/dist/ai/components/conversationActions.d.ts +4 -6
  37. package/dist/ai/components/conversationActions.js +9 -13
  38. package/dist/ai/core/ChatProvider.js +4 -4
  39. package/dist/ai/core/config.js +1 -1
  40. package/dist/ai/core/parts.d.ts +5 -0
  41. package/dist/ai/core/parts.js +28 -1
  42. package/dist/ai/core/providerIssues.d.ts +7 -0
  43. package/dist/ai/core/providerIssues.js +9 -1
  44. package/dist/ai/core/useKeyboardShortcuts.d.ts +4 -0
  45. package/dist/ai/core/useKeyboardShortcuts.js +8 -2
  46. package/dist/ai/index.d.ts +0 -1
  47. package/dist/ai/index.js +1 -2
  48. package/dist/ai/strings.d.ts +19 -0
  49. package/dist/ai/strings.js +18 -1
  50. package/dist/ai/ui/Avatar.js +5 -5
  51. package/dist/ai/ui/ConversationSidebar.d.ts +0 -1
  52. package/dist/ai/ui/ConversationSidebar.js +6 -7
  53. package/dist/ai/ui/Markdown.js +3 -3
  54. package/dist/ai/ui/Notice.js +6 -6
  55. package/dist/ai/ui/chip.d.ts +4 -0
  56. package/dist/ai/ui/chip.js +8 -0
  57. package/dist/ai/ui/sidebar.d.ts +6 -5
  58. package/dist/ai/ui/sidebar.js +39 -43
  59. package/dist/components/ActionMenu.d.ts +13 -0
  60. package/dist/components/ActionMenu.js +30 -0
  61. package/dist/components/Avatar.js +6 -6
  62. package/dist/components/Breadcrumbs.js +1 -1
  63. package/dist/components/Button.js +10 -10
  64. package/dist/components/Callout.js +2 -2
  65. package/dist/components/Card.js +3 -3
  66. package/dist/components/Combobox.d.ts +66 -0
  67. package/dist/components/Combobox.js +393 -0
  68. package/dist/components/ConfirmModal.js +11 -11
  69. package/dist/components/ContextMenu.d.ts +23 -0
  70. package/dist/components/ContextMenu.js +89 -0
  71. package/dist/components/CopyToClipboard.js +2 -2
  72. package/dist/components/CreateModal.d.ts +17 -36
  73. package/dist/components/CreateModal.js +97 -259
  74. package/dist/components/DescriptionList.js +8 -8
  75. package/dist/components/Filter.js +2 -2
  76. package/dist/components/FilterPill.js +2 -2
  77. package/dist/components/HoverCard.d.ts +14 -0
  78. package/dist/components/HoverCard.js +33 -0
  79. package/dist/components/IconButton.d.ts +5 -5
  80. package/dist/components/IconButton.js +18 -22
  81. package/dist/components/InfoTip.d.ts +15 -0
  82. package/dist/components/InfoTip.js +60 -0
  83. package/dist/components/Input.js +5 -5
  84. package/dist/components/LetterBadge.js +2 -2
  85. package/dist/components/Loader.js +3 -3
  86. package/dist/components/Markdown.js +1 -1
  87. package/dist/components/MarkdownImpl.js +2 -2
  88. package/dist/components/Menu.d.ts +33 -0
  89. package/dist/components/Menu.js +86 -0
  90. package/dist/components/MenuEntries.d.ts +56 -0
  91. package/dist/components/MenuEntries.js +123 -0
  92. package/dist/components/Modal.d.ts +29 -0
  93. package/dist/components/Modal.js +47 -0
  94. package/dist/components/Overview.js +11 -10
  95. package/dist/components/PageHeader.d.ts +10 -16
  96. package/dist/components/PageHeader.js +69 -95
  97. package/dist/components/Pagination.js +5 -5
  98. package/dist/components/Provider.d.ts +5 -11
  99. package/dist/components/Provider.js +11 -14
  100. package/dist/components/Radio.d.ts +8 -0
  101. package/dist/components/Radio.js +16 -0
  102. package/dist/components/SectionHeader.js +2 -2
  103. package/dist/components/SegmentedControl.d.ts +6 -5
  104. package/dist/components/SegmentedControl.js +34 -30
  105. package/dist/components/Select.d.ts +27 -0
  106. package/dist/components/Select.js +78 -0
  107. package/dist/components/SettingModeOption.d.ts +5 -8
  108. package/dist/components/SettingModeOption.js +10 -12
  109. package/dist/components/SettingRow.js +4 -4
  110. package/dist/components/SettingsCard.js +4 -4
  111. package/dist/components/SettingsGroup.js +2 -2
  112. package/dist/components/Sort.js +1 -1
  113. package/dist/components/StatusBadge.js +7 -7
  114. package/dist/components/StatusIcons.js +4 -4
  115. package/dist/components/Switch.d.ts +22 -0
  116. package/dist/components/Switch.js +28 -0
  117. package/dist/components/Table.js +11 -11
  118. package/dist/components/Tooltip.d.ts +24 -20
  119. package/dist/components/Tooltip.js +58 -76
  120. package/dist/components/floating.d.ts +14 -0
  121. package/dist/components/floating.js +21 -0
  122. package/dist/components/search.d.ts +2 -0
  123. package/dist/components/search.js +14 -0
  124. package/dist/editor/Editor.js +1 -1
  125. package/dist/editor/EditorField.js +16 -21
  126. package/dist/editor/FormCodePanel.js +19 -19
  127. package/dist/editor/Monaco.js +3 -3
  128. package/dist/file-explorer/FileExplorer.js +77 -81
  129. package/dist/file-explorer/FileExplorerContext.js +2 -2
  130. package/dist/file-explorer/FilePreview/CodePreviewEditor.js +1 -1
  131. package/dist/file-explorer/FilePreview/Inline.js +1 -1
  132. package/dist/file-explorer/FilePreview/StreamedBadge.js +5 -6
  133. package/dist/file-explorer/FilePreview/Viewer.js +8 -8
  134. package/dist/file-explorer/FilePreview/index.d.ts +1 -1
  135. package/dist/file-explorer/FilePreview/index.js +49 -83
  136. package/dist/file-explorer/FileRow.d.ts +4 -3
  137. package/dist/file-explorer/FileRow.js +32 -38
  138. package/dist/file-explorer/FileRowsWindow.d.ts +3 -2
  139. package/dist/file-explorer/FileRowsWindow.js +4 -4
  140. package/dist/file-explorer/FileTable.d.ts +3 -2
  141. package/dist/file-explorer/FileTable.js +3 -3
  142. package/dist/file-explorer/LoadMoreRow.js +1 -1
  143. package/dist/file-explorer/TreeView.js +11 -16
  144. package/dist/file-explorer/helpers.js +11 -12
  145. package/dist/file-explorer/toasts.js +11 -11
  146. package/dist/form/FieldGroup.d.ts +11 -0
  147. package/dist/form/FieldGroup.js +36 -0
  148. package/dist/form/FieldWrapper.js +4 -4
  149. package/dist/form/Fields/BooleanField.js +23 -33
  150. package/dist/form/Fields/CheckboxGroupField.d.ts +1 -1
  151. package/dist/form/Fields/CheckboxGroupField.js +9 -13
  152. package/dist/form/Fields/ColorField.d.ts +1 -2
  153. package/dist/form/Fields/ColorField.js +6 -11
  154. package/dist/form/Fields/DropdownField.d.ts +3 -13
  155. package/dist/form/Fields/DropdownField.js +40 -27
  156. package/dist/form/Fields/DurationField.d.ts +1 -2
  157. package/dist/form/Fields/DurationField.js +25 -32
  158. package/dist/form/Fields/LabelField.js +2 -2
  159. package/dist/form/Fields/MultiDropdownField.d.ts +6 -12
  160. package/dist/form/Fields/MultiDropdownField.js +31 -22
  161. package/dist/form/Fields/NumberField.d.ts +1 -1
  162. package/dist/form/Fields/NumberField.js +65 -35
  163. package/dist/form/Fields/ObjectField.d.ts +1 -1
  164. package/dist/form/Fields/ObjectField.js +12 -43
  165. package/dist/form/Fields/RadioField.d.ts +1 -1
  166. package/dist/form/Fields/RadioField.js +30 -41
  167. package/dist/form/Fields/RangeField.d.ts +1 -2
  168. package/dist/form/Fields/RangeField.js +23 -16
  169. package/dist/form/Fields/StringField.d.ts +1 -2
  170. package/dist/form/Fields/StringField.js +58 -26
  171. package/dist/form/Fields/TextAreaField.js +12 -10
  172. package/dist/form/Form.d.ts +30 -48
  173. package/dist/form/Form.js +306 -412
  174. package/dist/form/FormLayout.js +9 -9
  175. package/dist/form/Wizard/WizardContainer.d.ts +1 -4
  176. package/dist/form/Wizard/WizardContainer.js +40 -56
  177. package/dist/form/Wizard/WizardNavigation.js +1 -1
  178. package/dist/form/Wizard/WizardStepContent.d.ts +1 -5
  179. package/dist/form/Wizard/WizardStepContent.js +11 -16
  180. package/dist/form/Wizard/WizardStepIndicator.js +6 -6
  181. package/dist/form/Wizard/types.d.ts +0 -14
  182. package/dist/form/fieldRegistry.d.ts +1 -1
  183. package/dist/form/fieldRegistry.js +6 -6
  184. package/dist/form/formEditing.js +1 -1
  185. package/dist/form/index.d.ts +5 -4
  186. package/dist/form/index.js +6 -7
  187. package/dist/form/types/fieldComponentTypes.d.ts +8 -10
  188. package/dist/form/useChoiceField.d.ts +20 -0
  189. package/dist/form/useChoiceField.js +49 -0
  190. package/dist/form/useFieldValue.d.ts +7 -0
  191. package/dist/form/useFieldValue.js +20 -0
  192. package/dist/form/useParsedOpts.d.ts +3 -1
  193. package/dist/form/useParsedOpts.js +12 -17
  194. package/dist/form/utils/dependentOptions.d.ts +8 -4
  195. package/dist/form/utils/dependentOptions.js +25 -17
  196. package/dist/form/utils/fieldItems.d.ts +10 -0
  197. package/dist/form/utils/fieldItems.js +11 -0
  198. package/dist/form/utils/fieldPath.d.ts +10 -0
  199. package/dist/form/utils/fieldPath.js +20 -0
  200. package/dist/form/utils/shareEqual.d.ts +6 -0
  201. package/dist/form/utils/shareEqual.js +38 -0
  202. package/dist/graph/AnnotationBanner.js +8 -8
  203. package/dist/graph/Collapse.d.ts +1 -0
  204. package/dist/graph/Collapse.js +6 -27
  205. package/dist/graph/Connectors.js +5 -5
  206. package/dist/graph/DependencyGraph.js +20 -19
  207. package/dist/graph/GraphEditor.js +30 -41
  208. package/dist/graph/GraphEditorDialogs.js +17 -17
  209. package/dist/graph/GraphNode.js +1 -1
  210. package/dist/graph/InputDialog.js +19 -22
  211. package/dist/graph/InputsEditor.js +58 -66
  212. package/dist/graph/JobSummary.js +42 -39
  213. package/dist/graph/MatrixGroup.js +1 -1
  214. package/dist/graph/ProblemsButton.js +18 -19
  215. package/dist/graph/Reveal.js +1 -1
  216. package/dist/graph/ShortcutsButton.js +15 -18
  217. package/dist/graph/SuggestionInput.d.ts +5 -10
  218. package/dist/graph/SuggestionInput.js +14 -18
  219. package/dist/graph/ToolbarPopover.d.ts +11 -6
  220. package/dist/graph/ToolbarPopover.js +35 -67
  221. package/dist/graph/TreeView.js +24 -37
  222. package/dist/graph/WorkflowSettingsDialog.js +4 -4
  223. package/dist/graph/editorChrome.js +13 -12
  224. package/dist/graph/editorFields.js +29 -29
  225. package/dist/graph/editorPrimitives.d.ts +1 -1
  226. package/dist/graph/editorPrimitives.js +10 -7
  227. package/dist/graph/editorRows.js +103 -97
  228. package/dist/graph/fieldProblems.js +2 -2
  229. package/dist/graph/graphEditorState.d.ts +4 -3
  230. package/dist/graph/inputRefs.js +9 -9
  231. package/dist/graph/settingsViews.js +6 -7
  232. package/dist/graph/stepWith.js +6 -6
  233. package/dist/index.d.ts +14 -15
  234. package/dist/index.js +15 -14
  235. package/dist/list/ActionToolbar.d.ts +6 -14
  236. package/dist/list/ActionToolbar.js +48 -96
  237. package/dist/list/ListPage.d.ts +6 -12
  238. package/dist/list/ListPage.js +46 -29
  239. package/dist/list/ListPager.js +4 -4
  240. package/dist/list/ListTable.js +5 -5
  241. package/dist/list/ListViewControls.d.ts +5 -4
  242. package/dist/list/ListViewControls.js +177 -267
  243. package/dist/list/UserHoverCard.d.ts +3 -17
  244. package/dist/list/UserHoverCard.js +21 -45
  245. package/dist/list/index.d.ts +3 -4
  246. package/dist/list/index.js +3 -4
  247. package/dist/list/listSearch.js +12 -11
  248. package/dist/list/rowMenu.d.ts +63 -0
  249. package/dist/list/rowMenu.js +134 -0
  250. package/dist/logviewer/LogViewer.js +26 -26
  251. package/dist/styles.css +1 -1
  252. package/package.json +7 -13
  253. package/dist/ai/ui/Dropdown.d.ts +0 -31
  254. package/dist/ai/ui/Dropdown.js +0 -110
  255. package/dist/components/AddMenu.d.ts +0 -18
  256. package/dist/components/AddMenu.js +0 -46
  257. package/dist/components/BareModal.d.ts +0 -17
  258. package/dist/components/BareModal.js +0 -77
  259. package/dist/components/CollapsiblePanel.d.ts +0 -27
  260. package/dist/components/CollapsiblePanel.js +0 -77
  261. package/dist/components/Dropdown.d.ts +0 -28
  262. package/dist/components/Dropdown.js +0 -355
  263. package/dist/components/Listbox.d.ts +0 -21
  264. package/dist/components/Listbox.js +0 -119
  265. package/dist/components/MultiSelect.d.ts +0 -22
  266. package/dist/components/MultiSelect.js +0 -168
  267. package/dist/components/SwitchToggle.d.ts +0 -15
  268. package/dist/components/SwitchToggle.js +0 -57
  269. package/dist/components/SwitchToggleSmall.d.ts +0 -6
  270. package/dist/components/SwitchToggleSmall.js +0 -14
  271. package/dist/components/Toggle.d.ts +0 -6
  272. package/dist/components/Toggle.js +0 -20
  273. package/dist/components/dropdownUtils.d.ts +0 -37
  274. package/dist/components/dropdownUtils.js +0 -99
  275. package/dist/form/SpecialTypes/DropdownWrapper.d.ts +0 -25
  276. package/dist/form/SpecialTypes/DropdownWrapper.js +0 -28
  277. package/dist/form/SpecialTypes/FormikCustomDropdown.d.ts +0 -51
  278. package/dist/form/SpecialTypes/FormikCustomDropdown.js +0 -119
  279. package/dist/form/SpecialTypes/FormikCustomInput.d.ts +0 -19
  280. package/dist/form/SpecialTypes/FormikCustomInput.js +0 -121
  281. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.d.ts +0 -13
  282. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +0 -31
  283. package/dist/form/SpecialTypes/FormikNumericInput.d.ts +0 -9
  284. package/dist/form/SpecialTypes/FormikNumericInput.js +0 -60
  285. package/dist/form/SpecialTypes/index.d.ts +0 -3
  286. package/dist/list/HoverCard.d.ts +0 -24
  287. package/dist/list/HoverCard.js +0 -73
  288. package/dist/list/RowContextMenu.d.ts +0 -127
  289. package/dist/list/RowContextMenu.js +0 -515
@@ -1,12 +1,14 @@
1
1
  import { BiSolidTrashAlt } from "../../icons/bi/BiSolidTrashAlt.js";
2
2
  import { PiShareBold } from "../../icons/pi/PiShareBold.js";
3
+ import { Tooltip } from "../../components/Tooltip.js";
4
+ import { Combobox } from "../../components/Combobox.js";
3
5
  import { ConfirmModal } from "../../components/ConfirmModal.js";
4
6
  import Loader from "../../components/Loader.js";
7
+ import { Select } from "../../components/Select.js";
5
8
  import { useChatConfig } from "../core/config.js";
6
9
  import { useChat } from "../core/ChatProvider.js";
7
- import Dropdown from "../ui/Dropdown.js";
10
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
11
  import { useCallback, useEffect, useState, useTransition } from "react";
9
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
12
  //#region src/ai/components/ShareDialog.tsx
11
13
  function isTeamPermission(p) {
12
14
  return !!p.team && !p.entireOrganization;
@@ -122,6 +124,13 @@ function ShareDialog({ conversationId, isOpen, onClose }) {
122
124
  }, []);
123
125
  if (!sharing) return null;
124
126
  const teamPermissions = permissions.filter(isTeamPermission);
127
+ const permissionItems = [{
128
+ label: t.viewOnly,
129
+ value: "view"
130
+ }, {
131
+ label: t.canCollaborate,
132
+ value: "collaborate"
133
+ }];
125
134
  const availableTeams = teams.filter((team) => !permissions.some((p) => p.team === team.id));
126
135
  return /* @__PURE__ */ jsx(ConfirmModal, {
127
136
  open: isOpen,
@@ -140,7 +149,7 @@ function ShareDialog({ conversationId, isOpen, onClose }) {
140
149
  size: 24,
141
150
  text: strings.modal.loading
142
151
  })
143
- }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
152
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
144
153
  /* @__PURE__ */ jsxs("div", {
145
154
  className: "border theme-border rounded-lg p-3",
146
155
  children: [/* @__PURE__ */ jsx("h3", {
@@ -193,24 +202,25 @@ function ShareDialog({ conversationId, isOpen, onClose }) {
193
202
  children: perm.teamName || perm.team
194
203
  }), /* @__PURE__ */ jsxs("div", {
195
204
  className: "flex items-center gap-2",
196
- children: [/* @__PURE__ */ jsx(Dropdown, {
197
- options: [{
198
- label: t.viewOnly,
199
- value: "view"
200
- }, {
201
- label: t.canCollaborate,
202
- value: "collaborate"
203
- }],
204
- value: perm.permission,
205
- onChange: (val) => handleTeamPermissionChange(perm.team, val),
206
- wrapperClassName: "w-40",
207
- textBoxClassName: "text-xs border theme-border rounded px-2 py-1"
208
- }), /* @__PURE__ */ jsx("button", {
209
- type: "button",
210
- onClick: () => handleRemoveTeam(perm.team),
211
- className: "p-1 rounded hover:bg-red-500/10",
212
- title: t.remove,
213
- children: /* @__PURE__ */ jsx(BiSolidTrashAlt, { className: "w-3 h-3 text-red-500" })
205
+ children: [/* @__PURE__ */ jsx("div", {
206
+ className: "w-40",
207
+ children: /* @__PURE__ */ jsx(Select, {
208
+ items: permissionItems,
209
+ value: perm.permission,
210
+ onValueChange: (level) => {
211
+ if (level !== null) handleTeamPermissionChange(perm.team, level);
212
+ },
213
+ "aria-label": perm.teamName || perm.team
214
+ })
215
+ }), /* @__PURE__ */ jsx(Tooltip, {
216
+ content: t.remove,
217
+ children: /* @__PURE__ */ jsx("button", {
218
+ type: "button",
219
+ onClick: () => handleRemoveTeam(perm.team),
220
+ className: "p-1 rounded hover:bg-red-500/10",
221
+ "aria-label": `${t.remove} ${perm.teamName || perm.team}`,
222
+ children: /* @__PURE__ */ jsx(BiSolidTrashAlt, { className: "w-3 h-3 text-red-500" })
223
+ })
214
224
  })]
215
225
  })]
216
226
  }, perm.team))
@@ -218,19 +228,17 @@ function ShareDialog({ conversationId, isOpen, onClose }) {
218
228
  className: "text-sm theme-muted-text mb-3",
219
229
  children: t.noGroups
220
230
  }),
221
- availableTeams.length > 0 && /* @__PURE__ */ jsx("div", {
222
- className: "flex items-center gap-2",
223
- children: /* @__PURE__ */ jsx(Dropdown, {
224
- options: availableTeams.map((team) => ({
225
- label: team.name,
226
- value: team.id
227
- })),
228
- value: "",
229
- onChange: (val) => handleAddTeam(val),
230
- placeholder: t.addGroups,
231
- wrapperClassName: "flex-1",
232
- textBoxClassName: "text-sm border theme-border rounded px-2 py-1.5 w-full"
233
- })
231
+ availableTeams.length > 0 && /* @__PURE__ */ jsx(Combobox, {
232
+ items: availableTeams.map((team) => ({
233
+ label: team.name,
234
+ value: team.id
235
+ })),
236
+ value: null,
237
+ onValueChange: (teamId) => {
238
+ if (teamId !== null) handleAddTeam(teamId);
239
+ },
240
+ placeholder: t.addGroups,
241
+ "aria-label": t.addGroups
234
242
  })
235
243
  ]
236
244
  }),
@@ -247,7 +255,7 @@ function ShareButton({ conversationId, disabled = false }) {
247
255
  const { adapter } = useChat();
248
256
  const [isOpen, setIsOpen] = useState(false);
249
257
  if (!adapter.sharing) return null;
250
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("button", {
258
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("button", {
251
259
  type: "button",
252
260
  onClick: () => setIsOpen(true),
253
261
  disabled,
@@ -11,16 +11,26 @@ export interface SlashMenuConfig {
11
11
  onOpen?: (() => void) | undefined;
12
12
  }
13
13
  export declare function slashMatches(input: string, commands: SlashCommandOption[]): SlashCommandOption[];
14
- /** The palette's open state and keys; the caller owns the input. */
14
+ /** The palette's open state and keys; the caller owns the input.
15
+ * Base UI's Autocomplete drives a single-line input, and its arrow keys would take the caret
16
+ * from a multi-line composer, so the textarea stays a textbox that points at the list:
17
+ * spread `inputProps` on it. */
15
18
  export declare function useSlashMenu(input: string, config: SlashMenuConfig | undefined, setInput: (value: string) => void): {
16
19
  open: boolean;
17
20
  matches: SlashCommandOption[];
18
21
  active: number;
19
22
  choose: (name: string) => void;
20
23
  handleKey: (e: KeyboardEvent<HTMLTextAreaElement>) => boolean;
24
+ listId: string;
25
+ inputProps: {
26
+ 'aria-autocomplete': 'list';
27
+ 'aria-controls': string | undefined;
28
+ 'aria-activedescendant': string | undefined;
29
+ };
21
30
  };
22
- export declare function SlashMenu({ label, matches, active, onChoose, }: {
31
+ export declare function SlashMenu({ label, listId, matches, active, onChoose, }: {
23
32
  label: string;
33
+ listId: string;
24
34
  matches: SlashCommandOption[];
25
35
  active: number;
26
36
  onChoose: (name: string) => void;
@@ -1,6 +1,7 @@
1
- import cx from "classnames";
2
- import { useEffect, useState } from "react";
1
+ import { panelClassName, panelItemClassName } from "../../components/floating.js";
2
+ import { clsx } from "clsx";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useEffect, useId, useState } from "react";
4
5
  //#region src/ai/components/SlashMenu.tsx
5
6
  var MAX_SHOWN = 8;
6
7
  var TYPING_NAME = /^\/([\w:-]*)$/;
@@ -10,8 +11,12 @@ function slashMatches(input, commands) {
10
11
  const query = (typed[1] ?? "").toLowerCase();
11
12
  return commands.filter((c) => c.name.toLowerCase().startsWith(query)).slice(0, MAX_SHOWN);
12
13
  }
13
- /** The palette's open state and keys; the caller owns the input. */
14
+ /** The palette's open state and keys; the caller owns the input.
15
+ * Base UI's Autocomplete drives a single-line input, and its arrow keys would take the caret
16
+ * from a multi-line composer, so the textarea stays a textbox that points at the list:
17
+ * spread `inputProps` on it. */
14
18
  function useSlashMenu(input, config, setInput) {
19
+ const listId = useId();
15
20
  const [active, setActive] = useState(0);
16
21
  const [dismissed, setDismissed] = useState(false);
17
22
  const matches = config ? slashMatches(input, config.commands) : [];
@@ -30,6 +35,7 @@ function useSlashMenu(input, config, setInput) {
30
35
  setDismissed(false);
31
36
  }
32
37
  const choose = (name) => setInput(`/${name} `);
38
+ const shown = Math.min(active, matches.length - 1);
33
39
  /** True when the key belonged to the menu. */
34
40
  const handleKey = (e) => {
35
41
  if (!open) return false;
@@ -43,7 +49,7 @@ function useSlashMenu(input, config, setInput) {
43
49
  case "Tab":
44
50
  case "Enter": {
45
51
  if (e.shiftKey) return false;
46
- const pick = matches[Math.min(active, matches.length - 1)];
52
+ const pick = matches[shown];
47
53
  if (!pick) return false;
48
54
  choose(pick.name);
49
55
  break;
@@ -59,29 +65,39 @@ function useSlashMenu(input, config, setInput) {
59
65
  return {
60
66
  open,
61
67
  matches,
62
- active,
68
+ active: shown,
63
69
  choose,
64
- handleKey
70
+ handleKey,
71
+ listId,
72
+ inputProps: {
73
+ "aria-autocomplete": "list",
74
+ "aria-controls": open ? listId : void 0,
75
+ "aria-activedescendant": open ? optionId(listId, shown) : void 0
76
+ }
65
77
  };
66
78
  }
67
- function SlashMenu({ label, matches, active, onChoose }) {
79
+ var optionId = (listId, index) => `${listId}-${index}`;
80
+ function SlashMenu({ label, listId, matches, active, onChoose }) {
68
81
  return /* @__PURE__ */ jsx("div", {
82
+ id: listId,
69
83
  role: "listbox",
70
84
  "aria-label": label,
71
85
  "data-testid": "slash-menu",
72
- className: "absolute bottom-full left-0 right-0 mb-2 z-20 overflow-hidden rounded-xl border theme-border bg-(--theme-panel-bg) shadow-lg",
73
- children: matches.map((c, i) => /* @__PURE__ */ jsxs("button", {
74
- type: "button",
86
+ className: clsx("absolute bottom-full left-0 right-0 z-20 mb-2 overflow-hidden py-1", panelClassName),
87
+ children: matches.map((c, i) => /* @__PURE__ */ jsxs("div", {
88
+ id: optionId(listId, i),
75
89
  role: "option",
90
+ tabIndex: -1,
76
91
  "aria-selected": i === active,
92
+ "data-highlighted": i === active || void 0,
77
93
  onMouseDown: (e) => e.preventDefault(),
78
94
  onClick: () => onChoose(c.name),
79
- className: cx("flex w-full items-baseline gap-3 px-3 py-1.5 text-left text-sm", i === active ? "theme-hover" : "hover:theme-hover"),
95
+ className: clsx("items-baseline gap-3", panelItemClassName),
80
96
  children: [/* @__PURE__ */ jsxs("span", {
81
- className: "font-mono theme-text",
97
+ className: "font-mono",
82
98
  children: ["/", c.name]
83
99
  }), c.description && /* @__PURE__ */ jsx("span", {
84
- className: "truncate text-xs theme-muted-text",
100
+ className: "truncate text-xs text-(--theme-muted-text-color)",
85
101
  children: c.description
86
102
  })]
87
103
  }, c.name))
@@ -1,9 +1,9 @@
1
1
  import { FaStop } from "../../../icons/fa/FaStop.js";
2
2
  import { LoaderIcon } from "../../../components/StatusIcons.js";
3
3
  import { useChatConfig } from "../../core/config.js";
4
- import cx from "classnames";
5
- import { useEffect, useRef, useState } from "react";
4
+ import { clsx } from "clsx";
6
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { useEffect, useRef, useState } from "react";
7
7
  import { DateTime } from "luxon";
8
8
  //#region src/ai/components/agent/ActivityLine.tsx
9
9
  /** The agent status words: one is picked per turn and held until it ends. */
@@ -142,7 +142,7 @@ function ActivityLine({ startedAt, inputTokens, outputTokens, onStop, stopping =
142
142
  disabled: stopping,
143
143
  "aria-label": strings.input.stopGenerating,
144
144
  title: strings.input.stopGenerating,
145
- className: cx("ml-auto inline-flex h-7 items-center gap-1.5 rounded-md px-2 text-xs theme-muted-text transition-colors", "hover:theme-hover hover:theme-text disabled:cursor-not-allowed disabled:opacity-50"),
145
+ className: clsx("ml-auto inline-flex h-7 items-center gap-1.5 rounded-md px-2 text-xs theme-muted-text transition-colors", "hover:theme-hover hover:theme-text disabled:cursor-not-allowed disabled:opacity-50"),
146
146
  children: [/* @__PURE__ */ jsx(FaStop, { className: "h-3 w-3" }), strings.activity.stop]
147
147
  })
148
148
  ]
@@ -11,14 +11,14 @@ import { useChatConfig } from "../../core/config.js";
11
11
  import Markdown from "../../ui/Markdown.js";
12
12
  import { chatProseClasses } from "../../ui/prose.js";
13
13
  import { ReasoningBody, ReasoningToggle } from "../Reasoning.js";
14
- import cx from "classnames";
14
+ import { clsx } from "clsx";
15
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
16
  import { useEffect, useMemo, useState } from "react";
16
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
17
17
  //#region src/ai/components/agent/AgentMessageParts.tsx
18
18
  var COLLAPSED_RESULT_LINES = 12;
19
19
  function LineSpans({ text, tokens, emphasis }) {
20
20
  const pieces = tokens ?? [{ content: text }];
21
- if (!emphasis || emphasis.length === 0) return /* @__PURE__ */ jsx(Fragment$1, { children: pieces.map((t, i) => /* @__PURE__ */ jsx("span", {
21
+ if (!emphasis || emphasis.length === 0) return /* @__PURE__ */ jsx(Fragment, { children: pieces.map((t, i) => /* @__PURE__ */ jsx("span", {
22
22
  style: t.color ? { color: t.color } : void 0,
23
23
  children: t.content
24
24
  }, i)) });
@@ -41,7 +41,7 @@ function LineSpans({ text, tokens, emphasis }) {
41
41
  }
42
42
  offset += t.content.length;
43
43
  }
44
- return /* @__PURE__ */ jsx(Fragment$1, { children: out });
44
+ return /* @__PURE__ */ jsx(Fragment, { children: out });
45
45
  }
46
46
  function DiffRows({ rows, lang }) {
47
47
  const isDark = useCssIsDark();
@@ -68,7 +68,7 @@ function DiffRows({ rows, lang }) {
68
68
  return /* @__PURE__ */ jsx("div", {
69
69
  className: "overflow-x-auto rounded-md border theme-border font-mono text-xs leading-5",
70
70
  children: rows.map((row, i) => /* @__PURE__ */ jsxs("div", {
71
- className: cx("flex whitespace-pre", row.kind === "add" && "bg-green-500/15", row.kind === "del" && "bg-red-500/15", row.kind === "gap" && "theme-muted-text"),
71
+ className: clsx("flex whitespace-pre", row.kind === "add" && "bg-green-500/15", row.kind === "del" && "bg-red-500/15", row.kind === "gap" && "theme-muted-text"),
72
72
  children: [
73
73
  numbered && /* @__PURE__ */ jsx("span", {
74
74
  className: "w-10 shrink-0 select-none text-right pr-2 theme-muted-text",
@@ -207,11 +207,11 @@ function ToolCallBlock({ part }) {
207
207
  ]
208
208
  })]
209
209
  }),
210
- /* @__PURE__ */ jsx(LuChevronRight, { className: cx("w-3.5 h-3.5 mt-1 shrink-0 theme-muted-text transition-transform", expanded && "rotate-90") })
210
+ /* @__PURE__ */ jsx(LuChevronRight, { className: clsx("w-3.5 h-3.5 mt-1 shrink-0 theme-muted-text transition-transform", expanded && "rotate-90") })
211
211
  ]
212
212
  }),
213
213
  !isDiff && /* @__PURE__ */ jsx("div", {
214
- className: cx("ml-5 mt-0.5 text-[13px]", part.status === "error" ? "text-red-600" : "theme-muted-text"),
214
+ className: clsx("ml-5 mt-0.5 text-[13px]", part.status === "error" ? "text-red-600" : "theme-muted-text"),
215
215
  children: expanded ? null : summary
216
216
  }),
217
217
  expanded && !isDiff && /* @__PURE__ */ jsxs("div", {
@@ -251,7 +251,7 @@ function SubagentCard({ part }) {
251
251
  className: "flex min-h-6 items-start gap-2 w-full text-left",
252
252
  children: [
253
253
  part.status === "running" ? /* @__PURE__ */ jsx(LoaderIcon, { className: "w-3.5 h-3.5 mt-[3px] animate-spin theme-muted-text" }) : /* @__PURE__ */ jsx("span", {
254
- className: cx("leading-none", part.status === "failed" ? "text-red-500" : "text-green-600"),
254
+ className: clsx("leading-none", part.status === "failed" ? "text-red-500" : "text-green-600"),
255
255
  style: color && part.status !== "failed" ? { color } : void 0,
256
256
  children: "●"
257
257
  }),
@@ -269,10 +269,10 @@ function SubagentCard({ part }) {
269
269
  ]
270
270
  })]
271
271
  }),
272
- canExpand && /* @__PURE__ */ jsx(LuChevronRight, { className: cx("w-3.5 h-3.5 mt-1 shrink-0 theme-muted-text transition-transform", expanded && "rotate-90") })
272
+ canExpand && /* @__PURE__ */ jsx(LuChevronRight, { className: clsx("w-3.5 h-3.5 mt-1 shrink-0 theme-muted-text transition-transform", expanded && "rotate-90") })
273
273
  ]
274
274
  }), /* @__PURE__ */ jsx("div", {
275
- className: cx("ml-5 mt-0.5 text-[13px]", part.status === "failed" ? "text-red-600" : "theme-muted-text"),
275
+ className: clsx("ml-5 mt-0.5 text-[13px]", part.status === "failed" ? "text-red-600" : "theme-muted-text"),
276
276
  children: expanded ? /* @__PURE__ */ jsxs("div", {
277
277
  className: "space-y-1",
278
278
  children: [actions.length > 0 && /* @__PURE__ */ jsxs("ul", {
@@ -297,7 +297,7 @@ function ConfirmApproval({ part, onAnswer }) {
297
297
  path: [t.allowPath, t.denyPath],
298
298
  call: [t.allowCall, t.denyCall]
299
299
  }[part.subject ?? "command"];
300
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
300
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
301
301
  part.command && /* @__PURE__ */ jsx("pre", {
302
302
  className: "overflow-x-auto rounded-md bg-(--theme-muted-panel-bg) p-2 font-mono text-xs whitespace-pre-wrap",
303
303
  children: part.command
@@ -376,7 +376,7 @@ function AskApproval({ part, onAnswer }) {
376
376
  kind: "ask",
377
377
  text
378
378
  });
379
- if (!onAnswer) return /* @__PURE__ */ jsxs(Fragment$1, { children: [
379
+ if (!onAnswer) return /* @__PURE__ */ jsxs(Fragment, { children: [
380
380
  part.question && /* @__PURE__ */ jsx("div", {
381
381
  className: "text-sm",
382
382
  children: part.question
@@ -393,7 +393,7 @@ function AskApproval({ part, onAnswer }) {
393
393
  children: t.approvalPending
394
394
  })
395
395
  ] });
396
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [part.question && /* @__PURE__ */ jsx("div", {
396
+ return /* @__PURE__ */ jsxs(Fragment, { children: [part.question && /* @__PURE__ */ jsx("div", {
397
397
  className: "text-sm",
398
398
  children: part.question
399
399
  }), own === null ? /* @__PURE__ */ jsxs("div", {
@@ -457,7 +457,7 @@ function ApprovalBlock({ part, onApprovalAnswer }) {
457
457
  function PlanApproval({ part }) {
458
458
  const { strings } = useChatConfig();
459
459
  const t = strings.agentTranscript;
460
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
460
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
461
461
  className: "text-xs theme-muted-text",
462
462
  children: t.planPending
463
463
  }), part.plan && /* @__PURE__ */ jsx("div", {
@@ -480,7 +480,7 @@ function TodoSnapshotBlock({ part }) {
480
480
  }), /* @__PURE__ */ jsx("ul", {
481
481
  className: "space-y-0.5 text-sm",
482
482
  children: part.todos.map((todo) => /* @__PURE__ */ jsxs("li", {
483
- className: cx("flex items-start gap-2", todo.status === "completed" && "line-through theme-muted-text", todo.status === "in_progress" && "font-semibold"),
483
+ className: clsx("flex items-start gap-2", todo.status === "completed" && "line-through theme-muted-text", todo.status === "in_progress" && "font-semibold"),
484
484
  children: [/* @__PURE__ */ jsx("span", {
485
485
  className: "select-none font-mono",
486
486
  children: todo.status === "completed" ? "✔" : todo.status === "in_progress" ? "■" : "□"
@@ -508,7 +508,7 @@ function NoticeBlock({ part }) {
508
508
  children: [/* @__PURE__ */ jsx("span", {
509
509
  className: "min-w-0 flex-1 whitespace-pre-wrap",
510
510
  children: part.text
511
- }), /* @__PURE__ */ jsx(LuChevronRight, { className: cx("mt-0.5 h-3.5 w-3.5 shrink-0 transition-transform", expanded && "rotate-90") })]
511
+ }), /* @__PURE__ */ jsx(LuChevronRight, { className: clsx("mt-0.5 h-3.5 w-3.5 shrink-0 transition-transform", expanded && "rotate-90") })]
512
512
  }), expanded && /* @__PURE__ */ jsx("div", {
513
513
  className: "ml-4 mt-1 border-l-2 theme-border pl-3 text-sm theme-muted-text",
514
514
  children: /* @__PURE__ */ jsx(Markdown, { children: part.detail })
@@ -516,7 +516,7 @@ function NoticeBlock({ part }) {
516
516
  });
517
517
  }
518
518
  return /* @__PURE__ */ jsx("div", {
519
- className: cx("my-1 text-xs rounded-md px-2.5 py-1.5", part.kind === "error" ? "bg-red-500/10 text-red-700" : "bg-yellow-500/10 text-yellow-700"),
519
+ className: clsx("my-1 text-xs rounded-md px-2.5 py-1.5", part.kind === "error" ? "bg-red-500/10 text-red-700" : "bg-yellow-500/10 text-yellow-700"),
520
520
  "data-testid": `${part.kind}-part`,
521
521
  children: part.text
522
522
  });
@@ -575,7 +575,7 @@ function ReasoningBlock({ part, isStreaming }) {
575
575
  }
576
576
  function TextBlock({ part, isStreaming }) {
577
577
  return /* @__PURE__ */ jsx("div", {
578
- className: cx(chatProseClasses, "[&:not(:first-child)]:mt-4"),
578
+ className: clsx(chatProseClasses, "[&:not(:first-child)]:mt-4"),
579
579
  children: /* @__PURE__ */ jsx(Markdown, {
580
580
  isStreaming: isStreaming ?? false,
581
581
  children: part.text
@@ -702,10 +702,10 @@ function RollupGroup({ members, message }) {
702
702
  className: "font-mono text-[13px] theme-muted-text min-w-0 truncate",
703
703
  children: sentence
704
704
  }),
705
- /* @__PURE__ */ jsx(LuChevronRight, { className: cx("w-3.5 h-3.5 shrink-0 theme-muted-text transition-transform", expanded && "rotate-90") })
705
+ /* @__PURE__ */ jsx(LuChevronRight, { className: clsx("w-3.5 h-3.5 shrink-0 theme-muted-text transition-transform", expanded && "rotate-90") })
706
706
  ]
707
707
  }), /* @__PURE__ */ jsx("div", {
708
- className: cx("ml-1.5 mt-0.5 rounded-md", expanded ? "bg-(--theme-muted-panel-bg)/40 px-2" : "hidden"),
708
+ className: clsx("ml-1.5 mt-0.5 rounded-md", expanded ? "bg-(--theme-muted-panel-bg)/40 px-2" : "hidden"),
709
709
  children: members.map((m) => /* @__PURE__ */ jsx(PartDispatch, {
710
710
  part: m.part,
711
711
  message
@@ -1,6 +1,7 @@
1
+ import { Select } from "../../../components/Select.js";
1
2
  import { isNamedPermissionMode, permissionModesUpTo } from "../../agent/permissions.js";
2
3
  import { useChatConfig } from "../../core/config.js";
3
- import Dropdown from "../../ui/Dropdown.js";
4
+ import { smallChipClassName } from "../../ui/chip.js";
4
5
  import { jsx } from "react/jsx-runtime";
5
6
  //#region src/ai/components/agent/PermissionPicker.tsx
6
7
  var LABEL_KEY = {
@@ -17,22 +18,23 @@ function PermissionPicker({ mode, onPick, ceiling, accepted, disabled = false })
17
18
  const t = useChatConfig().strings.permissions;
18
19
  const current = mode ?? "accept-edits";
19
20
  const named = isNamedPermissionMode(current) ? current : void 0;
20
- const options = permissionModesUpTo(ceiling, accepted).map((m) => ({
21
+ const items = permissionModesUpTo(ceiling, accepted).map((m) => ({
21
22
  value: m,
22
23
  label: t[LABEL_KEY[m]],
23
24
  description: t[`${LABEL_KEY[m]}Hint`]
24
25
  }));
25
- return /* @__PURE__ */ jsx(Dropdown, {
26
- options,
26
+ return /* @__PURE__ */ jsx(Select, {
27
+ "aria-label": t.choose,
28
+ items,
27
29
  value: current,
28
- onChange: (value) => onPick(value),
30
+ onValueChange: (value) => value && onPick(value),
29
31
  disabled,
30
- variant: "bare",
31
- ariaLabel: t.choose,
32
- ...named ? { hint: t[`${LABEL_KEY[named]}Hint`] } : {},
33
- ...options.some((o) => o.value === current) ? {} : { valueLabel: named ? t[LABEL_KEY[named]] : current },
34
- textBoxClassName: "rounded-md px-1.5 py-0.5 text-[11px] theme-muted-text transition-colors hover:theme-muted-panel hover:theme-text",
35
- listClassName: "w-64"
32
+ triggerTooltip: named ? t[`${LABEL_KEY[named]}Hint`] : void 0,
33
+ trigger: /* @__PURE__ */ jsx("button", {
34
+ type: "button",
35
+ className: smallChipClassName,
36
+ children: named ? t[LABEL_KEY[named]] : current
37
+ })
36
38
  });
37
39
  }
38
40
  //#endregion
@@ -1,5 +1,6 @@
1
1
  import { type ReactNode } from 'react';
2
- import { type OpenMenu, type RowMenuItem } from '../../list/index.js';
2
+ import { type OpenMenu } from '../../components/ContextMenu.js';
3
+ import type { MenuEntry } from '../../components/MenuEntries.js';
3
4
  import type { ConversationSummary } from '../types.js';
4
5
  export type ConversationGroupKey = 'groupToday' | 'groupYesterday' | 'groupThisWeek' | 'groupThisMonth' | 'groupOlder';
5
6
  /** Conversations by how long ago they started, newest group first, leaving
@@ -13,11 +14,8 @@ export declare function groupConversationsByDate<T extends {
13
14
  /** What a conversation row offers behind its menu (rename, share, download,
14
15
  * delete) and the menu and dialogs that carry them out, for any list of
15
16
  * conversations. Render `overlays` once beside the list. */
16
- export declare function useConversationActions({ menuZClassName, }?: {
17
- /** The menu's stacking class, for a list inside a layer above the page. */
18
- menuZClassName?: string;
19
- }): {
20
- menuItems: (conv: ConversationSummary) => RowMenuItem[];
17
+ export declare function useConversationActions(): {
18
+ menuItems: (conv: ConversationSummary) => MenuEntry[];
21
19
  openMenu: OpenMenu;
22
20
  overlays: ReactNode;
23
21
  };
@@ -2,14 +2,14 @@ import { BiSolidTrashAlt } from "../../icons/bi/BiSolidTrashAlt.js";
2
2
  import { FaCloudDownloadAlt } from "../../icons/fa/FaCloudDownloadAlt.js";
3
3
  import { FaEdit } from "../../icons/fa/FaEdit.js";
4
4
  import { PiShareBold } from "../../icons/pi/PiShareBold.js";
5
- import { useRowMenu } from "../../list/RowContextMenu.js";
5
+ import { useContextMenu } from "../../components/ContextMenu.js";
6
6
  import { useChatConfig } from "../core/config.js";
7
7
  import { useChat } from "../core/ChatProvider.js";
8
8
  import { useRowDialogs } from "../ui/sidebar.js";
9
9
  import { conversationToMarkdown, downloadText, exportFilename } from "../core/exportConversation.js";
10
10
  import ShareDialog from "./ShareDialog.js";
11
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
12
  import { useState } from "react";
12
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
13
  import { DateTime } from "luxon";
14
14
  //#region src/ai/components/conversationActions.tsx
15
15
  /** Conversations by how long ago they started, newest group first, leaving
@@ -35,11 +35,11 @@ function groupConversationsByDate(conversations) {
35
35
  /** What a conversation row offers behind its menu (rename, share, download,
36
36
  * delete) and the menu and dialogs that carry them out, for any list of
37
37
  * conversations. Render `overlays` once beside the list. */
38
- function useConversationActions({ menuZClassName } = {}) {
38
+ function useConversationActions() {
39
39
  const { strings } = useChatConfig();
40
40
  const t = strings.sidebar;
41
41
  const { adapter, notify, deleteConversation, updateConversationTitle } = useChat();
42
- const { openMenu, contextMenu } = useRowMenu(menuZClassName);
42
+ const { openMenu, contextMenu } = useContextMenu();
43
43
  const [sharing, setSharing] = useState(null);
44
44
  const { requestDelete, requestRename, dialogs } = useRowDialogs({
45
45
  strings: {
@@ -65,35 +65,31 @@ function useConversationActions({ menuZClassName } = {}) {
65
65
  };
66
66
  const menuItems = (conv) => [
67
67
  {
68
- kind: "action",
69
68
  label: t.rename,
70
69
  icon: /* @__PURE__ */ jsx(FaEdit, {}),
71
- onSelect: () => requestRename(conv)
70
+ onClick: () => requestRename(conv)
72
71
  },
73
72
  ...adapter.sharing && conv.isOwner !== false ? [{
74
- kind: "action",
75
73
  label: t.share,
76
74
  icon: /* @__PURE__ */ jsx(PiShareBold, {}),
77
- onSelect: () => setSharing(conv.id)
75
+ onClick: () => setSharing(conv.id)
78
76
  }] : [],
79
77
  {
80
- kind: "action",
81
78
  label: strings.chrome.download,
82
79
  icon: /* @__PURE__ */ jsx(FaCloudDownloadAlt, {}),
83
- onSelect: () => void download(conv.id)
80
+ onClick: () => void download(conv.id)
84
81
  },
85
82
  {
86
- kind: "action",
87
83
  label: t.delete,
88
84
  icon: /* @__PURE__ */ jsx(BiSolidTrashAlt, {}),
89
85
  destructive: true,
90
- onSelect: () => requestDelete(conv)
86
+ onClick: () => requestDelete(conv)
91
87
  }
92
88
  ];
93
89
  return {
94
90
  menuItems,
95
91
  openMenu,
96
- overlays: /* @__PURE__ */ jsxs(Fragment$1, { children: [
92
+ overlays: /* @__PURE__ */ jsxs(Fragment, { children: [
97
93
  contextMenu,
98
94
  dialogs,
99
95
  sharing && /* @__PURE__ */ jsx(ShareDialog, {
@@ -1,11 +1,11 @@
1
- import { applyPartDelta, finalizeParts } from "./parts.js";
1
+ import { applyPartDelta, finalizeParts, toolTurnMessages } from "./parts.js";
2
2
  import { chatReducer, initialState } from "./chatReducer.js";
3
3
  import { ChatConfigProvider, resolveChatConfig } from "./config.js";
4
4
  import { providerIssueFor } from "./providerIssues.js";
5
5
  import { ReportSidebarPresentationContext, readStoredSidebar, storeSidebar, useControllable } from "./sidebarState.js";
6
6
  import { useSSEStream } from "./useSSEStream.js";
7
- import { createContext, useCallback, useContext, useEffect, useMemo, useReducer, useRef, useState } from "react";
8
7
  import { jsx } from "react/jsx-runtime";
8
+ import { createContext, useCallback, useContext, useEffect, useMemo, useReducer, useRef, useState } from "react";
9
9
  import { DateTime } from "luxon";
10
10
  //#region src/ai/core/ChatProvider.tsx
11
11
  var SELECTED_MODEL_STORAGE_KEY = "aiChatSelectedModel";
@@ -392,7 +392,7 @@ function ChatProvider({ adapter, currentUser, navigation, notify, activeConversa
392
392
  });
393
393
  };
394
394
  const apiMessages = [
395
- ...(isNewConversation ? [] : state.currentConversation?.messages || []).filter((m) => m.content || m.toolCalls?.length).map((m) => {
395
+ ...(isNewConversation ? [] : state.currentConversation?.messages || []).filter((m) => m.content || m.toolCalls?.length || m.parts?.length).flatMap((m) => {
396
396
  const msg = {
397
397
  role: m.role,
398
398
  content: m.content || null
@@ -401,7 +401,7 @@ function ChatProvider({ adapter, currentUser, navigation, notify, activeConversa
401
401
  if (m.role === "assistant" && m.toolCalls?.length) msg.tool_calls = m.toolCalls;
402
402
  if (m.role === "assistant" && m.responsesOutput?.length && m.model === state.selectedProvider) msg.responses_output = m.responsesOutput;
403
403
  if (m.role === "tool" && m.toolCallId) msg.tool_call_id = m.toolCallId;
404
- return msg;
404
+ return m.role === "assistant" ? toolTurnMessages(m.parts, msg) : [msg];
405
405
  }),
406
406
  ...precedingApiMessages,
407
407
  {
@@ -1,7 +1,7 @@
1
1
  import { safeUrl } from "../../safeUrl.js";
2
2
  import { defaultChatStrings } from "../strings.js";
3
- import { createContext, useContext } from "react";
4
3
  import { jsx } from "react/jsx-runtime";
4
+ import { createContext, useContext } from "react";
5
5
  //#region src/ai/core/config.tsx
6
6
  function hrefFor(target) {
7
7
  switch (target.kind) {
@@ -1,3 +1,4 @@
1
+ import type { CompletionMessage } from '../adapter/openai/wire.js';
1
2
  import type { PartDelta } from '../adapter/types.js';
2
3
  import type { MessagePart } from '../types.js';
3
4
  /** Fold one streamed PartDelta into a parts timeline. Pure: returns a new
@@ -5,6 +6,10 @@ import type { MessagePart } from '../types.js';
5
6
  * and the provider's local accumulation. Unknown target ids are dropped
6
7
  * rather than invented — an end without a start has nothing to render. */
7
8
  export declare function applyPartDelta(parts: MessagePart[], delta: PartDelta): MessagePart[];
9
+ /** The wire history for an assistant turn whose tool calls the gateway ran:
10
+ * the calls, each result, then the answer. A turn with no finished tool
11
+ * calls comes back as the answer alone. */
12
+ export declare function toolTurnMessages(parts: MessagePart[] | null | undefined, answer: CompletionMessage): CompletionMessage[];
8
13
  /** A turn can end (completed or stopped) with tool calls still marked
9
14
  * running — the transport died before their tool_end arrived. A stored
10
15
  * message must not spin forever, so close them out as interrupted. */