@morscherlab/mint-sdk 1.2.0-beta.2 → 1.2.0-beta.20

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 (493) hide show
  1. package/README.md +65 -5
  2. package/dist/{ExperimentPopover-tLwJ7XF4.js → ExperimentPopover-CJdNlCZb.js} +24 -15
  3. package/dist/ExperimentPopover-CJdNlCZb.js.map +1 -0
  4. package/dist/{ExperimentSelectorModal-DyPAUCLw.js → ExperimentSelectorModal-DCXmwKNS.js} +5 -5
  5. package/dist/ExperimentSelectorModal-DCXmwKNS.js.map +1 -0
  6. package/dist/{SettingsModal-BgKY93zh.js → SettingsModal-BYR20I4c.js} +5 -8
  7. package/dist/SettingsModal-BYR20I4c.js.map +1 -0
  8. package/dist/{EmptyState-p90quq8Z.js → Skeleton-BAF3CKY7.js} +111 -93
  9. package/dist/Skeleton-BAF3CKY7.js.map +1 -0
  10. package/dist/__tests__/components/DataStates.test.d.ts +1 -0
  11. package/dist/__tests__/components/ExperimentTimeline.test.d.ts +1 -0
  12. package/dist/__tests__/components/FileBrowserModal.test.d.ts +1 -0
  13. package/dist/__tests__/components/FilePicker.state.test.d.ts +1 -0
  14. package/dist/__tests__/components/FilePicker.test.d.ts +1 -0
  15. package/dist/__tests__/components/PlotlyChart.test.d.ts +1 -0
  16. package/dist/__tests__/components/PluginWorkspaceView.internal.test.d.ts +1 -0
  17. package/dist/__tests__/components/SecretInput.test.d.ts +1 -0
  18. package/dist/__tests__/components/WellEditPopupInternal.test.d.ts +1 -0
  19. package/dist/__tests__/components/iconButtonIconSizing.test.d.ts +1 -0
  20. package/dist/__tests__/composables/useFileBrowser.test.d.ts +1 -0
  21. package/dist/__tests__/composables/useListReorder.test.d.ts +1 -0
  22. package/dist/__tests__/composables/usePlatformFilePickerAdapter.test.d.ts +1 -0
  23. package/dist/__tests__/composables/usePluginConfig.test.d.ts +15 -0
  24. package/dist/__tests__/styles/cssTokens.test.d.ts +1 -0
  25. package/dist/apiErrors.d.ts +26 -0
  26. package/dist/components/ActionMenu.vue.d.ts +1 -1
  27. package/dist/components/AppAvatarMenu.vue.d.ts +4 -0
  28. package/dist/components/AppPluginSwitcher.vue.d.ts +2 -0
  29. package/dist/components/AppSidebar.vue.d.ts +1 -1
  30. package/dist/components/AuditTrail.vue.d.ts +13 -0
  31. package/dist/components/AutoGroupModal.vue.d.ts +1 -1
  32. package/dist/components/BaseInput.vue.d.ts +1 -1
  33. package/dist/components/BaseSelect.vue.d.ts +1 -1
  34. package/dist/components/BaseTextarea.vue.d.ts +1 -1
  35. package/dist/components/BatchProgressList.vue.d.ts +17 -0
  36. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +4 -4
  37. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +2 -2
  38. package/dist/components/ConcentrationInput.vue.d.ts +1 -1
  39. package/dist/components/DataFrame.vue.d.ts +14 -2
  40. package/dist/components/DatePicker.vue.d.ts +2 -2
  41. package/dist/components/DateTimePicker.vue.d.ts +3 -1
  42. package/dist/components/DropdownButton.vue.d.ts +2 -0
  43. package/dist/components/ExperimentDataViewer.vue.d.ts +26 -1
  44. package/dist/components/ExperimentPopover.vue.d.ts +1 -0
  45. package/dist/components/ExperimentTimeline.vue.d.ts +29 -1
  46. package/dist/components/FileBrowserModal.vue.d.ts +99 -0
  47. package/dist/components/FilePicker.vue.d.ts +32 -0
  48. package/dist/components/FileUploader.vue.d.ts +5 -2
  49. package/dist/components/FitPanel.vue.d.ts +1 -1
  50. package/dist/components/FormulaInput.vue.d.ts +1 -1
  51. package/dist/components/GeneratedPlotlyResult.vue.d.ts +1 -3
  52. package/dist/components/JobsStatusTray.vue.d.ts +9 -2
  53. package/dist/components/LcmsSequenceTable.vue.d.ts +29 -1
  54. package/dist/components/MobileSupportGate.vue.d.ts +1 -1
  55. package/dist/components/MoleculeInput.vue.d.ts +1 -1
  56. package/dist/components/NumberInput.vue.d.ts +1 -1
  57. package/dist/components/PlotlyChart.vue.d.ts +59 -0
  58. package/dist/components/PluginWorkspaceView.props.d.ts +29 -1
  59. package/dist/components/PluginWorkspaceView.vue.d.ts +5 -5
  60. package/dist/components/ProgressBar.vue.d.ts +1 -1
  61. package/dist/components/ProtocolStepEditor.vue.d.ts +1 -0
  62. package/dist/components/ReagentList.vue.d.ts +29 -1
  63. package/dist/components/ResourceCard.vue.d.ts +1 -1
  64. package/dist/components/SampleHierarchyTree.vue.d.ts +29 -1
  65. package/dist/components/SampleLegend.vue.d.ts +1 -1
  66. package/dist/components/SampleSelector.drag.d.ts +13 -0
  67. package/dist/components/SampleSelector.tree.d.ts +2 -0
  68. package/dist/components/SampleSelector.vue.d.ts +35 -2
  69. package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
  70. package/dist/components/SecretInput.vue.d.ts +28 -0
  71. package/dist/components/SequenceInput.vue.d.ts +1 -1
  72. package/dist/components/SettingsModal.vue.d.ts +1 -1
  73. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -1
  74. package/dist/components/StatusIndicator.vue.d.ts +1 -1
  75. package/dist/components/TagsInput.vue.d.ts +1 -1
  76. package/dist/components/TimePicker.vue.d.ts +2 -1
  77. package/dist/components/TimeRangeInput.vue.d.ts +1 -1
  78. package/dist/components/UnitInput.vue.d.ts +1 -1
  79. package/dist/components/WellPlate.geometry.d.ts +15 -0
  80. package/dist/components/WellPlate.interaction.d.ts +13 -0
  81. package/dist/components/WellPlate.vue.d.ts +30 -1
  82. package/dist/components/fileBrowserPresentation.d.ts +10 -0
  83. package/dist/components/index.d.ts +13 -0
  84. package/dist/components/index.js +8 -8
  85. package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -0
  86. package/dist/components/internal/DataStateInternal.vue.d.ts +43 -0
  87. package/dist/components/internal/FileBrowserDialogInternal.vue.d.ts +34 -0
  88. package/dist/components/internal/FileBrowserListingInternal.vue.d.ts +25 -0
  89. package/dist/components/internal/FileBrowserToolbarInternal.vue.d.ts +26 -0
  90. package/dist/components/internal/FilePickerPreviewInternal.vue.d.ts +18 -0
  91. package/dist/components/internal/FilePickerTreeInternal.vue.d.ts +31 -0
  92. package/dist/components/internal/WellEditPopupInternal.vue.d.ts +3 -1
  93. package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +23 -0
  94. package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +70 -0
  95. package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +43 -0
  96. package/dist/{components-DkHddRMl.js → components-DIum5hkx.js} +5882 -3200
  97. package/dist/components-DIum5hkx.js.map +1 -0
  98. package/dist/composables/controlSchemaTypes.d.ts +34 -0
  99. package/dist/composables/filePicker/localFiles.d.ts +3 -0
  100. package/dist/composables/filePicker/usePickerNavigation.d.ts +96 -0
  101. package/dist/composables/filePicker/usePickerSelection.d.ts +31 -0
  102. package/dist/composables/filePicker/validation.d.ts +5 -0
  103. package/dist/composables/flattenWorkspaceBindings.d.ts +61 -0
  104. package/dist/composables/index.d.ts +11 -2
  105. package/dist/composables/index.js +10 -10
  106. package/dist/composables/pluginWorkspaceModel.d.ts +164 -0
  107. package/dist/composables/useApi.d.ts +14 -7
  108. package/dist/composables/useAsync.d.ts +5 -1
  109. package/dist/composables/useBioTemplateWorkspace.d.ts +4 -20
  110. package/dist/composables/useCommandHistory.d.ts +5 -1
  111. package/dist/composables/useDropdownState.d.ts +18 -2
  112. package/dist/composables/useEventStream.d.ts +9 -0
  113. package/dist/composables/useFileBrowser.d.ts +55 -0
  114. package/dist/composables/useFilePicker.d.ts +189 -0
  115. package/dist/composables/useFocusTrap.d.ts +21 -0
  116. package/dist/composables/useGeneratedAnalysis.d.ts +3 -1
  117. package/dist/composables/useJobsStatusTray.d.ts +25 -1
  118. package/dist/composables/useListReorder.d.ts +90 -0
  119. package/dist/composables/useMenuKeyboard.d.ts +29 -0
  120. package/dist/composables/useOptimisticMutation.d.ts +5 -1
  121. package/dist/composables/usePlatformFilePickerAdapter.d.ts +11 -0
  122. package/dist/composables/usePluginClient.d.ts +2 -0
  123. package/dist/composables/usePluginConfig.d.ts +27 -0
  124. package/dist/composables/usePluginWorkspace.d.ts +51 -0
  125. package/dist/composables/useRequestSyncState.d.ts +42 -2
  126. package/dist/composables/useResourceCrud.d.ts +5 -1
  127. package/dist/composables/useRovingFocus.d.ts +24 -0
  128. package/dist/composables/useScheduleDrag.d.ts +1 -0
  129. package/dist/composables/useWellPainting.d.ts +5 -1
  130. package/dist/composables/useWellPlateAdapter.d.ts +5 -1
  131. package/dist/composables/useWellPlateValidation.d.ts +5 -1
  132. package/dist/composables/workspaceRecipes.d.ts +188 -0
  133. package/dist/{composables-C_i90sGS.js → composables-mFXsTA4g.js} +502 -86
  134. package/dist/composables-mFXsTA4g.js.map +1 -0
  135. package/dist/{useApi-J6zFkHmn.js → errors-BLuqMee5.js} +179 -16
  136. package/dist/errors-BLuqMee5.js.map +1 -0
  137. package/dist/{experiment-Cq145oEW.js → experiment-CVDRAnqp.js} +3 -8
  138. package/dist/experiment-CVDRAnqp.js.map +1 -0
  139. package/dist/{experiment-utils-CLnzPPq5.js → experiment-utils-CV2Wza3z.js} +70 -51
  140. package/dist/experiment-utils-CV2Wza3z.js.map +1 -0
  141. package/dist/index.d.ts +2 -0
  142. package/dist/index.js +15 -15
  143. package/dist/install.js +2 -7
  144. package/dist/install.js.map +1 -1
  145. package/dist/permissions.d.ts +42 -1
  146. package/dist/permissions.js +20 -10
  147. package/dist/permissions.js.map +1 -1
  148. package/dist/stores/auth.d.ts +6 -0
  149. package/dist/stores/index.d.ts +1 -1
  150. package/dist/stores/index.js +3 -3
  151. package/dist/stores/settings.d.ts +31 -1
  152. package/dist/styles.css +15135 -32397
  153. package/dist/templates/builderReadUtils.d.ts +1 -1
  154. package/dist/templates/index.js +2 -4
  155. package/dist/{templates-DsP51i-S.js → templates-CmGfmBRA.js} +303 -106
  156. package/dist/templates-CmGfmBRA.js.map +1 -0
  157. package/dist/types/auth.d.ts +1 -0
  158. package/dist/types/fileBrowserTypes.d.ts +77 -0
  159. package/dist/types/filePicker.d.ts +77 -0
  160. package/dist/types/form-builder.d.ts +1 -1
  161. package/dist/types/index.d.ts +2 -0
  162. package/dist/{useControlSchema-BDx2Olha.js → useControlSchema-DldpxNOP.js} +499 -1162
  163. package/dist/useControlSchema-DldpxNOP.js.map +1 -0
  164. package/dist/{useDropdownState-DSb3BC3N.js → useDropdownState-C7hRiVQf.js} +22 -6
  165. package/dist/useDropdownState-C7hRiVQf.js.map +1 -0
  166. package/dist/{useExperimentSelector-CV4iNfMS.js → useExperimentSelector-CEcIaueJ.js} +4 -4
  167. package/dist/{useExperimentSelector-CV4iNfMS.js.map → useExperimentSelector-CEcIaueJ.js.map} +1 -1
  168. package/dist/{useFormBuilder-BLxG3BOX.js → useFormBuilder-z5L_dUIy.js} +2 -2
  169. package/dist/{useFormBuilder-BLxG3BOX.js.map → useFormBuilder-z5L_dUIy.js.map} +1 -1
  170. package/dist/utils/color.d.ts +13 -0
  171. package/dist/utils/errors.d.ts +9 -0
  172. package/dist/utils/pluginSecrets.d.ts +53 -0
  173. package/dist/utils/records.d.ts +4 -0
  174. package/dist/{useProtocolTemplates-CS_SQl6U.js → validation-uwoJGEb6.js} +811 -212
  175. package/dist/validation-uwoJGEb6.js.map +1 -0
  176. package/package.json +1 -1
  177. package/src/__tests__/apiErrors.test.ts +81 -0
  178. package/src/__tests__/components/ActionMenu.test.ts +94 -0
  179. package/src/__tests__/components/AppAvatarMenu.test.ts +123 -0
  180. package/src/__tests__/components/BaseTabs.test.ts +67 -0
  181. package/src/__tests__/components/DataFrame.test.ts +183 -11
  182. package/src/__tests__/components/DataStates.test.ts +176 -0
  183. package/src/__tests__/components/DatePicker.test.ts +76 -0
  184. package/src/__tests__/components/DateTimePicker.test.ts +36 -0
  185. package/src/__tests__/components/DropdownButton.test.ts +134 -0
  186. package/src/__tests__/components/ExperimentPopover.test.ts +30 -0
  187. package/src/__tests__/components/ExperimentTimeline.test.ts +63 -0
  188. package/src/__tests__/components/FileBrowserModal.test.ts +460 -0
  189. package/src/__tests__/components/FilePicker.state.test.ts +287 -0
  190. package/src/__tests__/components/FilePicker.test.ts +487 -0
  191. package/src/__tests__/components/GeneratedPlotlyResult.test.ts +52 -5
  192. package/src/__tests__/components/GroupAssigner.test.ts +53 -0
  193. package/src/__tests__/components/LcmsSequenceTable.test.ts +22 -0
  194. package/src/__tests__/components/PlotlyChart.test.ts +305 -0
  195. package/src/__tests__/components/PluginWorkspaceView.internal.test.ts +230 -0
  196. package/src/__tests__/components/RackEditor.test.ts +35 -0
  197. package/src/__tests__/components/ReagentList.test.ts +64 -2
  198. package/src/__tests__/components/RetiredStyles.test.ts +31 -0
  199. package/src/__tests__/components/SampleSelector.drag.test.ts +51 -1
  200. package/src/__tests__/components/ScheduleCalendar.test.ts +63 -0
  201. package/src/__tests__/components/SecretInput.test.ts +87 -0
  202. package/src/__tests__/components/SegmentedControl.test.ts +107 -0
  203. package/src/__tests__/components/TimePicker.test.ts +61 -0
  204. package/src/__tests__/components/WellEditPopupInternal.test.ts +108 -0
  205. package/src/__tests__/components/WellPlate.interaction.test.ts +59 -0
  206. package/src/__tests__/components/iconButtonIconSizing.test.ts +18 -0
  207. package/src/__tests__/composables/useApi.test.ts +92 -9
  208. package/src/__tests__/composables/useAuth.test.ts +2 -0
  209. package/src/__tests__/composables/useDropdownState.test.ts +58 -1
  210. package/src/__tests__/composables/useFileBrowser.test.ts +305 -0
  211. package/src/__tests__/composables/useGeneratedAnalysis.test.ts +7 -2
  212. package/src/__tests__/composables/useListReorder.test.ts +145 -0
  213. package/src/__tests__/composables/usePlatformFilePickerAdapter.test.ts +67 -0
  214. package/src/__tests__/composables/usePluginClient.test.ts +143 -0
  215. package/src/__tests__/composables/usePluginConfig.test.ts +497 -0
  216. package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
  217. package/src/__tests__/docs/frontendDocsCatalog.test.ts +39 -7
  218. package/src/__tests__/stores/settings.test.ts +71 -1
  219. package/src/__tests__/styles/cssTokens.test.ts +131 -0
  220. package/src/__tests__/utils/permissions.test.ts +68 -0
  221. package/src/apiErrors.ts +85 -0
  222. package/src/components/ActionMenu.vue +45 -88
  223. package/src/components/AlertBox.vue +0 -4
  224. package/src/components/AppAvatarMenu.vue +26 -7
  225. package/src/components/AppLayout.vue +0 -4
  226. package/src/components/AppPluginSwitcher.story.vue +87 -0
  227. package/src/components/AppPluginSwitcher.vue +2 -5
  228. package/src/components/AppSidebar.vue +0 -4
  229. package/src/components/AppToastContainer.vue +6 -8
  230. package/src/components/AppTopBar.vue +0 -4
  231. package/src/components/ArtifactSaveDialog.vue +0 -4
  232. package/src/components/ArtifactSelectorModal.vue +0 -4
  233. package/src/components/AuditTrail.story.vue +18 -0
  234. package/src/components/AuditTrail.vue +27 -7
  235. package/src/components/AutoGroupModal.vue +0 -4
  236. package/src/components/Avatar.vue +0 -4
  237. package/src/components/BaseButton.vue +0 -4
  238. package/src/components/BaseCheckbox.vue +0 -4
  239. package/src/components/BaseInput.vue +0 -4
  240. package/src/components/BaseModal.vue +8 -60
  241. package/src/components/BasePill.vue +0 -4
  242. package/src/components/BaseRadioGroup.vue +0 -4
  243. package/src/components/BaseSelect.vue +0 -4
  244. package/src/components/BaseSlider.vue +0 -4
  245. package/src/components/BaseTabs.vue +26 -5
  246. package/src/components/BaseTextarea.vue +0 -4
  247. package/src/components/BaseToggle.vue +0 -4
  248. package/src/components/BatchProgressList.story.vue +18 -0
  249. package/src/components/BatchProgressList.vue +32 -4
  250. package/src/components/BioTemplateExperimentWorkspaceView.vue +10 -21
  251. package/src/components/BioTemplatePackWorkspaceView.vue +11 -15
  252. package/src/components/BioTemplatePresetWorkspaceView.vue +8 -13
  253. package/src/components/Breadcrumb.vue +0 -4
  254. package/src/components/Calendar.vue +0 -4
  255. package/src/components/ChartContainer.story.vue +1 -1
  256. package/src/components/ChartContainer.vue +0 -4
  257. package/src/components/ChemicalFormula.vue +0 -4
  258. package/src/components/CollapsibleCard.vue +0 -4
  259. package/src/components/ColorSlider.vue +0 -4
  260. package/src/components/ComponentBindingRenderer.vue +1 -3
  261. package/src/components/ConcentrationInput.vue +0 -4
  262. package/src/components/ConfirmDialog.vue +0 -4
  263. package/src/components/ControlWorkspaceView.vue +11 -22
  264. package/src/components/DataFrame.story.vue +14 -0
  265. package/src/components/DataFrame.vue +97 -23
  266. package/src/components/DatePicker.vue +73 -27
  267. package/src/components/DateTimePicker.vue +45 -17
  268. package/src/components/Divider.vue +0 -4
  269. package/src/components/DoseCalculator.vue +0 -4
  270. package/src/components/DoseDesignWorkspaceView.vue +6 -25
  271. package/src/components/DropdownButton.vue +24 -6
  272. package/src/components/EmptyState.vue +0 -4
  273. package/src/components/ExperimentCodeBadge.vue +0 -4
  274. package/src/components/ExperimentDataViewer.story.vue +3 -0
  275. package/src/components/ExperimentDataViewer.vue +33 -13
  276. package/src/components/ExperimentPopover.vue +12 -7
  277. package/src/components/ExperimentSelectorModal.vue +0 -4
  278. package/src/components/ExperimentTimeline.story.vue +18 -0
  279. package/src/components/ExperimentTimeline.vue +81 -75
  280. package/src/components/FileBrowserModal.story.vue +203 -0
  281. package/src/components/FileBrowserModal.vue +384 -0
  282. package/src/components/FilePicker.story.vue +44 -0
  283. package/src/components/FilePicker.vue +284 -0
  284. package/src/components/FileUploader.vue +32 -18
  285. package/src/components/FitPanel.vue +0 -4
  286. package/src/components/FormActions.story.vue +96 -0
  287. package/src/components/FormActions.vue +0 -4
  288. package/src/components/FormBuilder.vue +0 -4
  289. package/src/components/FormField.vue +0 -4
  290. package/src/components/FormulaInput.vue +0 -4
  291. package/src/components/GeneratedPathInput.story.vue +68 -0
  292. package/src/components/GeneratedPlotlyResult.story.vue +96 -0
  293. package/src/components/GeneratedPlotlyResult.vue +34 -69
  294. package/src/components/GeneratedPluginView.story.vue +145 -0
  295. package/src/components/GeneratedPluginView.vue +10 -10
  296. package/src/components/GeneratedResultRenderer.story.vue +117 -0
  297. package/src/components/GeneratedResultRenderer.vue +4 -9
  298. package/src/components/GroupAssigner.vue +67 -5
  299. package/src/components/IconButton.vue +0 -4
  300. package/src/components/InstrumentAlertLog.vue +0 -4
  301. package/src/components/InstrumentStateBadge.vue +0 -4
  302. package/src/components/InstrumentStatusCard.vue +0 -4
  303. package/src/components/JobsStatusTray.vue +16 -5
  304. package/src/components/LcmsSequenceTable.vue +81 -50
  305. package/src/components/LoadingSpinner.vue +0 -4
  306. package/src/components/MobileSupportGate.vue +0 -4
  307. package/src/components/MoleculeInput.vue +0 -4
  308. package/src/components/MultiSelect.vue +0 -4
  309. package/src/components/NumberInput.vue +0 -4
  310. package/src/components/PlateMapEditor.vue +3 -14
  311. package/src/components/PlotlyChart.story.vue +61 -0
  312. package/src/components/PlotlyChart.vue +233 -0
  313. package/src/components/PluginIcon.vue +0 -4
  314. package/src/components/PluginWorkspaceView.props.ts +38 -0
  315. package/src/components/PluginWorkspaceView.vue +53 -224
  316. package/src/components/ProgressBar.vue +0 -4
  317. package/src/components/ProtocolStepEditor.vue +2 -4
  318. package/src/components/RackEditor.vue +51 -70
  319. package/src/components/ReagentEditor.vue +0 -4
  320. package/src/components/ReagentList.story.vue +18 -0
  321. package/src/components/ReagentList.vue +127 -82
  322. package/src/components/ResourceCard.vue +0 -4
  323. package/src/components/RuntimeUpdateNotice.story.vue +49 -0
  324. package/src/components/RuntimeUpdateNotice.vue +4 -4
  325. package/src/components/SampleHierarchyTree.story.vue +18 -0
  326. package/src/components/SampleHierarchyTree.vue +32 -9
  327. package/src/components/SampleLegend.vue +2 -8
  328. package/src/components/SampleSelector.drag.ts +51 -0
  329. package/src/components/SampleSelector.story.vue +6 -0
  330. package/src/components/SampleSelector.tree.ts +2 -0
  331. package/src/components/SampleSelector.vue +49 -10
  332. package/src/components/SampleSelectorGroupNode.vue +19 -0
  333. package/src/components/ScheduleCalendar.story.vue +6 -0
  334. package/src/components/ScheduleCalendar.vue +82 -7
  335. package/src/components/ScientificNumber.vue +0 -4
  336. package/src/components/SecretInput.story.vue +79 -0
  337. package/src/components/SecretInput.vue +150 -0
  338. package/src/components/SectionCard.vue +0 -4
  339. package/src/components/SegmentedControl.vue +50 -7
  340. package/src/components/SequenceInput.vue +0 -4
  341. package/src/components/SequenceProgressBar.story.vue +110 -0
  342. package/src/components/SequenceProgressBar.vue +0 -4
  343. package/src/components/SettingsModal.vue +0 -4
  344. package/src/components/Skeleton.vue +0 -4
  345. package/src/components/SmartGroupFieldRecipe.vue +8 -6
  346. package/src/components/SmartGroupManual.vue +0 -4
  347. package/src/components/StatusIndicator.vue +0 -4
  348. package/src/components/StepWizard.vue +0 -4
  349. package/src/components/TagsInput.vue +4 -6
  350. package/src/components/ThemeToggle.vue +0 -4
  351. package/src/components/TimePicker.vue +36 -7
  352. package/src/components/TimeRangeInput.vue +0 -4
  353. package/src/components/Tooltip.story.vue +3 -3
  354. package/src/components/Tooltip.vue +0 -4
  355. package/src/components/UnitInput.vue +0 -4
  356. package/src/components/WellPlate.geometry.ts +60 -1
  357. package/src/components/WellPlate.interaction.ts +44 -0
  358. package/src/components/WellPlate.story.vue +8 -0
  359. package/src/components/WellPlate.vue +53 -7
  360. package/src/components/fileBrowserPresentation.ts +39 -0
  361. package/src/components/index.ts +14 -0
  362. package/src/components/internal/AppTopBarPageSelectorInternal.vue +2 -5
  363. package/src/components/internal/AppTopBarPillNavInternal.vue +0 -4
  364. package/src/components/internal/DataStateInternal.vue +74 -0
  365. package/src/components/internal/FileBrowserDialogInternal.vue +75 -0
  366. package/src/components/internal/FileBrowserListingInternal.vue +197 -0
  367. package/src/components/internal/FileBrowserToolbarInternal.vue +136 -0
  368. package/src/components/internal/FilePickerPreviewInternal.vue +100 -0
  369. package/src/components/internal/FilePickerTreeInternal.vue +238 -0
  370. package/src/components/internal/FormSectionRenderer.vue +0 -4
  371. package/src/components/internal/WellEditPopupInternal.vue +20 -5
  372. package/src/components/internal/WorkspaceControlsInternal.vue +20 -0
  373. package/src/components/internal/WorkspaceShellInternal.vue +125 -0
  374. package/src/components/internal/WorkspaceSidebarInternal.vue +82 -0
  375. package/src/composables/controlSchemaTypes.ts +34 -0
  376. package/src/composables/experimentDesignData.ts +1 -3
  377. package/src/composables/filePicker/localFiles.ts +58 -0
  378. package/src/composables/filePicker/usePickerNavigation.ts +220 -0
  379. package/src/composables/filePicker/usePickerSelection.ts +259 -0
  380. package/src/composables/filePicker/validation.ts +41 -0
  381. package/src/composables/flattenWorkspaceBindings.ts +104 -0
  382. package/src/composables/formBuilderRegistry.ts +5 -0
  383. package/src/composables/index.ts +50 -0
  384. package/src/composables/pluginWorkspaceModel.ts +182 -0
  385. package/src/composables/useApi.ts +57 -13
  386. package/src/composables/useAsync.ts +5 -1
  387. package/src/composables/useAuth.ts +3 -0
  388. package/src/composables/useBioTemplatePackWorkspace.ts +15 -9
  389. package/src/composables/useBioTemplatePresetWorkspace.ts +14 -15
  390. package/src/composables/useBioTemplateWorkspace.ts +18 -43
  391. package/src/composables/useCommandHistory.ts +5 -1
  392. package/src/composables/useDropdownState.ts +35 -5
  393. package/src/composables/useEventStream.ts +14 -2
  394. package/src/composables/useFileBrowser.ts +314 -0
  395. package/src/composables/useFileImport.ts +2 -7
  396. package/src/composables/useFilePicker.ts +366 -0
  397. package/src/composables/useFocusTrap.ts +91 -0
  398. package/src/composables/useGeneratedAnalysis.ts +32 -27
  399. package/src/composables/useJobsStatusTray.ts +184 -17
  400. package/src/composables/useListReorder.story.vue +143 -0
  401. package/src/composables/useListReorder.ts +242 -0
  402. package/src/composables/useMenuKeyboard.ts +90 -0
  403. package/src/composables/useOptimisticMutation.ts +6 -16
  404. package/src/composables/usePlatformFilePickerAdapter.ts +104 -0
  405. package/src/composables/usePluginClient.ts +50 -4
  406. package/src/composables/usePluginConfig.ts +252 -0
  407. package/src/composables/usePluginWorkspace.ts +208 -0
  408. package/src/composables/useRackEditor.ts +2 -11
  409. package/src/composables/useRequestSyncState.ts +98 -28
  410. package/src/composables/useResourceCrud.ts +6 -16
  411. package/src/composables/useRovingFocus.ts +66 -0
  412. package/src/composables/useScheduleDrag.ts +37 -0
  413. package/src/composables/useWellPainting.ts +10 -30
  414. package/src/composables/useWellPlateAdapter.ts +5 -1
  415. package/src/composables/useWellPlateEditor.ts +1 -11
  416. package/src/composables/useWellPlateValidation.ts +5 -1
  417. package/src/composables/workspaceRecipes.ts +267 -0
  418. package/src/index.ts +19 -0
  419. package/src/permissions.ts +48 -9
  420. package/src/stores/auth.ts +1 -0
  421. package/src/stores/experiment.ts +2 -7
  422. package/src/stores/index.ts +9 -1
  423. package/src/stores/settings.ts +93 -1
  424. package/src/styles/components/audit-trail.css +5 -5
  425. package/src/styles/components/batch-progress-list.css +7 -0
  426. package/src/styles/components/color-slider.css +2 -2
  427. package/src/styles/components/data-state.css +30 -0
  428. package/src/styles/components/dataframe.css +29 -35
  429. package/src/styles/components/experiment-data-viewer.css +4 -4
  430. package/src/styles/components/experiment-timeline.css +3 -30
  431. package/src/styles/components/file-browser-modal.css +489 -0
  432. package/src/styles/components/file-picker.css +502 -0
  433. package/src/styles/components/form-builder.css +5 -5
  434. package/src/styles/components/icon-button.css +9 -0
  435. package/src/styles/components/lcms-sequence-table.css +16 -1
  436. package/src/styles/components/multi-select.css +1 -1
  437. package/src/styles/components/reagent-list.css +38 -31
  438. package/src/styles/components/sample-hierarchy-tree.css +5 -19
  439. package/src/styles/components/sample-selector.css +9 -2
  440. package/src/styles/components/schedule-calendar.css +8 -0
  441. package/src/styles/components/scientific-number.css +1 -1
  442. package/src/styles/components/secret-input.css +60 -0
  443. package/src/styles/components/well-plate.css +8 -0
  444. package/src/styles/index.css +12 -0
  445. package/src/styles/variables.css +29 -0
  446. package/src/templates/builderPresetControls.ts +1 -1
  447. package/src/templates/builderReadUtils.ts +1 -3
  448. package/src/templates/componentPlateHelpers.ts +3 -1
  449. package/src/templates/componentTargetResolvers.ts +1 -3
  450. package/src/templates/controlSchemaTargets.ts +1 -3
  451. package/src/templates/doseResponseAdapters.ts +2 -13
  452. package/src/templates/elisaAssayCollectionBuilder.ts +2 -2
  453. package/src/templates/plateMapBuilder.ts +2 -2
  454. package/src/templates/qpcrPlateBuilder.ts +1 -1
  455. package/src/templates/templateValidators.ts +1 -1
  456. package/src/templates/timeCourseBuilder.ts +2 -2
  457. package/src/templates/wellPlateScreenCollectionBuilder.ts +2 -2
  458. package/src/types/auth.ts +1 -0
  459. package/src/types/fileBrowserTypes.ts +85 -0
  460. package/src/types/filePicker.ts +72 -0
  461. package/src/types/form-builder.ts +1 -0
  462. package/src/types/index.ts +14 -0
  463. package/src/utils/color.ts +51 -0
  464. package/src/utils/errors.ts +29 -0
  465. package/src/utils/formModelSync.ts +1 -3
  466. package/src/utils/pluginIcon.ts +1 -3
  467. package/src/utils/pluginSecrets.ts +102 -0
  468. package/src/utils/records.ts +9 -0
  469. package/dist/EmptyState-p90quq8Z.js.map +0 -1
  470. package/dist/ExperimentPopover-tLwJ7XF4.js.map +0 -1
  471. package/dist/ExperimentSelectorModal-DyPAUCLw.js.map +0 -1
  472. package/dist/SettingsModal-BgKY93zh.js.map +0 -1
  473. package/dist/components-DkHddRMl.js.map +0 -1
  474. package/dist/composables/autoGroupConstants.d.ts +0 -1
  475. package/dist/composables/autoGroupGrouping.d.ts +0 -3
  476. package/dist/composables/useAutoGroupInputSources.d.ts +0 -32
  477. package/dist/composables-C_i90sGS.js.map +0 -1
  478. package/dist/experiment-Cq145oEW.js.map +0 -1
  479. package/dist/experiment-utils-CLnzPPq5.js.map +0 -1
  480. package/dist/templates/builderDefaults.d.ts +0 -1
  481. package/dist/templates/plateGeometry.d.ts +0 -4
  482. package/dist/templates-DsP51i-S.js.map +0 -1
  483. package/dist/useApi-J6zFkHmn.js.map +0 -1
  484. package/dist/useControlSchema-BDx2Olha.js.map +0 -1
  485. package/dist/useDropdownState-DSb3BC3N.js.map +0 -1
  486. package/dist/useProtocolTemplates-CS_SQl6U.js.map +0 -1
  487. package/src/__tests__/composables/useAutoGroupInputSources.test.ts +0 -127
  488. package/src/composables/autoGroupConstants.ts +0 -4
  489. package/src/composables/autoGroupGrouping.ts +0 -148
  490. package/src/composables/useAutoGroupInputSources.ts +0 -147
  491. package/src/templates/builderDefaults.ts +0 -11
  492. package/src/templates/plateGeometry.ts +0 -62
  493. /package/dist/__tests__/{composables/useAutoGroupInputSources.test.d.ts → apiErrors.test.d.ts} +0 -0
@@ -0,0 +1,91 @@
1
+ import { nextTick, watch, type Ref } from 'vue'
2
+ import { useEventListener } from './useEventListener'
3
+
4
+ export interface UseFocusTrapOptions {
5
+ /** Ref to the trap's container element. Focusable elements are queried within it. */
6
+ containerRef: Ref<HTMLElement | null>
7
+ /** Reactive flag — the trap activates when this becomes `true` and releases when it becomes `false`. */
8
+ active: Ref<boolean>
9
+ /** Restore focus to the element that was focused before activation, when the trap releases. Default `true`. */
10
+ restoreFocus?: boolean
11
+ }
12
+
13
+ export interface UseFocusTrapReturn {
14
+ /** Focusable descendants of the container, in DOM order, excluding roving-tabindex (`tabindex="-1"`) elements. */
15
+ getFocusableElements: () => HTMLElement[]
16
+ }
17
+
18
+ const FOCUSABLE_SELECTOR = [
19
+ 'a[href]',
20
+ 'button:not([disabled])',
21
+ 'input:not([disabled])',
22
+ 'select:not([disabled])',
23
+ 'textarea:not([disabled])',
24
+ '[tabindex]:not([tabindex="-1"])',
25
+ ].join(', ')
26
+
27
+ /**
28
+ * Traps Tab focus within a container while `active` is true: Tab wraps from the last
29
+ * focusable element back to the first (and Shift+Tab from the first to the last), the
30
+ * first focusable element (or the container itself, if none) receives focus when the
31
+ * trap activates, and focus returns to whatever was focused beforehand when it releases.
32
+ * Used by BaseModal and WellEditPopupInternal.
33
+ */
34
+ export function useFocusTrap(options: UseFocusTrapOptions): UseFocusTrapReturn {
35
+ const { containerRef, active } = options
36
+ const restoreFocus = options.restoreFocus ?? true
37
+ let previouslyFocused: HTMLElement | null = null
38
+
39
+ function getFocusableElements(): HTMLElement[] {
40
+ if (!containerRef.value) return []
41
+ // Roving tabindex (inactive tabs/rail items) stays out of the Tab cycle. Checking
42
+ // the attribute directly (rather than the `.tabIndex` IDL property) avoids relying
43
+ // on a browser/engine's default-tabindex computation for freshly-patched elements.
44
+ return Array.from(containerRef.value.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR))
45
+ .filter((el) => el.getAttribute('tabindex') !== '-1')
46
+ }
47
+
48
+ function handleKeydown(event: KeyboardEvent) {
49
+ if (event.key !== 'Tab' || !active.value || !containerRef.value) return
50
+
51
+ const focusable = getFocusableElements()
52
+ if (focusable.length === 0) {
53
+ event.preventDefault()
54
+ return
55
+ }
56
+
57
+ const first = focusable[0]
58
+ const last = focusable[focusable.length - 1]
59
+
60
+ if (event.shiftKey && document.activeElement === first) {
61
+ event.preventDefault()
62
+ last.focus()
63
+ } else if (!event.shiftKey && document.activeElement === last) {
64
+ event.preventDefault()
65
+ first.focus()
66
+ }
67
+ }
68
+
69
+ // `flush: 'sync'` so releasing the trap from `onBeforeUnmount` restores focus
70
+ // before the component's reactive scope is torn down — a batched (default
71
+ // 'pre') watcher would be scheduled but never flushed once that scope stops.
72
+ watch(active, async (isActive) => {
73
+ if (isActive) {
74
+ previouslyFocused = document.activeElement as HTMLElement | null
75
+ await nextTick()
76
+ const focusable = getFocusableElements()
77
+ if (focusable.length > 0) {
78
+ focusable[0].focus()
79
+ } else {
80
+ containerRef.value?.focus()
81
+ }
82
+ } else {
83
+ if (restoreFocus) previouslyFocused?.focus()
84
+ previouslyFocused = null
85
+ }
86
+ }, { flush: 'sync' })
87
+
88
+ useEventListener(() => document, 'keydown', handleKeydown)
89
+
90
+ return { getFocusableElements }
91
+ }
@@ -8,9 +8,13 @@ import {
8
8
  type ShallowRef,
9
9
  } from 'vue'
10
10
 
11
- import type { PluginJobCenterSource } from './useJobsStatusTray'
11
+ import type {
12
+ JobsStatusTrayAdapter,
13
+ PluginJobCenterSource,
14
+ } from './useJobsStatusTray'
12
15
  import { useEventStream, type EventStreamMessage } from './useEventStream'
13
- import { normalizeJobState } from '../jobs'
16
+ import { isActiveJobStatus, normalizeJobState } from '../jobs'
17
+ import { MintApiError, mintApiErrorFromResponse } from '../apiErrors'
14
18
  import type {
15
19
  GeneratedAnalysisJob,
16
20
  GeneratedAnalysisJobInput,
@@ -102,11 +106,12 @@ export interface UsePluginJobsReturn extends PluginJobCenterSource {
102
106
  pause: () => void
103
107
  resume: () => void
104
108
  dispose: () => void
109
+ /** @deprecated Pass the runtime itself to SDK job UI as its source. */
110
+ jobsAdapter: JobsStatusTrayAdapter
105
111
  }
106
112
 
107
113
  export type UseGeneratedAnalysisReturn = UsePluginJobsReturn
108
114
 
109
- const ACTIVE_STATUSES = new Set(['awaiting_input', 'queued', 'running'])
110
115
  const AUTH_TOKEN_KEY = 'mint-auth-token'
111
116
  const SESSION_NOT_FOUND_DETAIL = 'Session not found'
112
117
  const MAX_RESULT_CACHE_ENTRIES = 32
@@ -125,16 +130,6 @@ type PluginJobStreamData = GeneratedAnalysisJobInput | {
125
130
  jobs: GeneratedAnalysisJobInput[]
126
131
  }
127
132
 
128
- class GeneratedAnalysisRequestError extends Error {
129
- constructor(
130
- readonly status: number,
131
- readonly detail: unknown,
132
- ) {
133
- super(typeof detail === 'string' ? detail : `Request failed (${status})`)
134
- this.name = 'GeneratedAnalysisRequestError'
135
- }
136
- }
137
-
138
133
  export function createGeneratedAnalysisTransport(
139
134
  options: GeneratedAnalysisTransportOptions = {},
140
135
  ): GeneratedAnalysisTransport {
@@ -468,12 +463,12 @@ function createPluginJobs(
468
463
  .then((payload) => {
469
464
  if (disposed) return jobs.value
470
465
  if (revision !== jobsRevision) {
471
- if (jobs.value.some(job => ACTIVE_STATUSES.has(job.status))) schedulePoll()
466
+ if (jobs.value.some(job => isActiveJobStatus(job.status))) schedulePoll()
472
467
  return jobs.value
473
468
  }
474
469
  replaceJobs(payload.jobs)
475
470
  error.value = null
476
- if (!paused && !jobStream.isConnected.value && jobs.value.some(job => ACTIVE_STATUSES.has(job.status))) {
471
+ if (!paused && !jobStream.isConnected.value && jobs.value.some(job => isActiveJobStatus(job.status))) {
477
472
  schedulePoll()
478
473
  }
479
474
  return jobs.value
@@ -525,7 +520,7 @@ function createPluginJobs(
525
520
  const current = typeof job === 'string'
526
521
  ? jobs.value.find(candidate => candidate.job_id === jobId)
527
522
  : jobs.value.find(candidate => candidate.job_id === jobId) ?? job
528
- if (current && !ACTIVE_STATUSES.has(current.status)) {
523
+ if (current && !isActiveJobStatus(current.status)) {
529
524
  return terminalWaitResult(current, options.rejectOnFailure === true)
530
525
  }
531
526
  if (paused) return Promise.reject(new Error('Plugin jobs runtime is paused'))
@@ -562,7 +557,7 @@ function createPluginJobs(
562
557
  }
563
558
 
564
559
  function settleJobWaiters(job: GeneratedAnalysisJob): void {
565
- if (ACTIVE_STATUSES.has(job.status)) return
560
+ if (isActiveJobStatus(job.status)) return
566
561
  const waiting = jobWaiters.get(job.job_id)
567
562
  if (!waiting) return
568
563
  jobWaiters.delete(job.job_id)
@@ -834,7 +829,7 @@ function createPluginJobs(
834
829
  job: GeneratedAnalysisJob,
835
830
  cause: unknown,
836
831
  ): void {
837
- if (cause instanceof GeneratedAnalysisRequestError) {
832
+ if (cause instanceof MintApiError) {
838
833
  if (cause.status === 404 || cause.status === 410) {
839
834
  discardCachedResult(job.job_id)
840
835
  jobs.value = jobs.value.map(candidate => (
@@ -910,7 +905,7 @@ function createPluginJobs(
910
905
  await refreshJobs()
911
906
  } catch (cause) {
912
907
  error.value = errorMessage(cause, 'Failed to refresh analysis jobs')
913
- if (jobs.value.some(job => ACTIVE_STATUSES.has(job.status))) schedulePoll()
908
+ if (jobs.value.some(job => isActiveJobStatus(job.status))) schedulePoll()
914
909
  }
915
910
  }, pollInterval)
916
911
  }
@@ -922,7 +917,7 @@ function createPluginJobs(
922
917
  }
923
918
 
924
919
  function schedulePollForActiveJobs(): void {
925
- if (jobs.value.some(job => ACTIVE_STATUSES.has(job.status))) schedulePoll()
920
+ if (jobs.value.some(job => isActiveJobStatus(job.status))) schedulePoll()
926
921
  }
927
922
 
928
923
  function applyJobStreamMessage(message: EventStreamMessage<PluginJobStreamData>): void {
@@ -972,6 +967,13 @@ function createPluginJobs(
972
967
  return payload.deleted
973
968
  }
974
969
 
970
+ const jobsAdapter: JobsStatusTrayAdapter = {
971
+ listJobs: async () => ({ jobs: await refreshJobs() }),
972
+ cancelJob: async job => cancelJob(job as GeneratedAnalysisJob),
973
+ deleteJob: async job => deleteJob(job as GeneratedAnalysisJob),
974
+ clearFinishedJobs: async () => clearFinishedJobs(),
975
+ }
976
+
975
977
  function pause(): void {
976
978
  if (disposed || paused) return
977
979
  paused = true
@@ -993,7 +995,7 @@ function createPluginJobs(
993
995
  scheduleManagedDownloadRefresh(jobId, managedDownloadRefreshDelay(result))
994
996
  }
995
997
  if (streamEnabled) jobStream.start()
996
- else if (jobs.value.some(job => ACTIVE_STATUSES.has(job.status))) schedulePoll()
998
+ else if (jobs.value.some(job => isActiveJobStatus(job.status))) schedulePoll()
997
999
  }
998
1000
 
999
1001
  function dispose(): void {
@@ -1026,8 +1028,10 @@ function createPluginJobs(
1026
1028
  },
1027
1029
  })
1028
1030
  if (!response.ok) {
1029
- const payload = await response.json().catch(() => null) as { detail?: unknown } | null
1030
- throw new GeneratedAnalysisRequestError(response.status, payload?.detail)
1031
+ const payload: unknown = await response.json().catch(() => null)
1032
+ throw mintApiErrorFromResponse(response.status, payload, {
1033
+ requestId: response.headers.get('X-Request-ID'),
1034
+ })
1031
1035
  }
1032
1036
  return await response.json() as T
1033
1037
  }
@@ -1065,6 +1069,7 @@ function createPluginJobs(
1065
1069
  pause,
1066
1070
  resume,
1067
1071
  dispose,
1072
+ jobsAdapter,
1068
1073
  }
1069
1074
  }
1070
1075
 
@@ -1165,20 +1170,20 @@ function createGeneratedSessionStorage(candidate?: Storage): GeneratedSessionSto
1165
1170
  }
1166
1171
 
1167
1172
  function isSessionNotFoundError(error: unknown): boolean {
1168
- return error instanceof GeneratedAnalysisRequestError
1173
+ return error instanceof MintApiError
1169
1174
  && error.status === 404
1170
- && error.detail === SESSION_NOT_FOUND_DETAIL
1175
+ && (error.detail === SESSION_NOT_FOUND_DETAIL || error.message === SESSION_NOT_FOUND_DETAIL)
1171
1176
  }
1172
1177
 
1173
1178
  function isRetryableResultError(error: unknown): boolean {
1174
- return !(error instanceof GeneratedAnalysisRequestError)
1179
+ return !(error instanceof MintApiError)
1175
1180
  || error.status === 409
1176
1181
  || error.status === 429
1177
1182
  || error.status >= 500
1178
1183
  }
1179
1184
 
1180
1185
  function isRetryableJobLookupError(error: unknown): boolean {
1181
- return !(error instanceof GeneratedAnalysisRequestError)
1186
+ return !(error instanceof MintApiError)
1182
1187
  || error.status === 408
1183
1188
  || error.status === 429
1184
1189
  || error.status >= 500
@@ -5,6 +5,7 @@ import {
5
5
  ref,
6
6
  shallowRef,
7
7
  toValue,
8
+ watch,
8
9
  type ComputedRef,
9
10
  type MaybeRefOrGetter,
10
11
  type Ref,
@@ -16,7 +17,12 @@ import type {
16
17
  JobListPayload,
17
18
  JobState,
18
19
  JobStateInput,
20
+ JobStreamData,
19
21
  } from '../types/jobs'
22
+ import type {
23
+ PluginEventStreamMessage,
24
+ UsePluginEventStreamReturn,
25
+ } from './usePluginClient'
20
26
 
21
27
  export type JobsStatusTrayTab = 'active' | 'history'
22
28
 
@@ -140,11 +146,32 @@ function duplicatePluginJobId(jobId: string): Error {
140
146
  return new Error(`Duplicate plugin job id across combined sources: ${jobId}`)
141
147
  }
142
148
 
149
+ /** @deprecated Prefer a {@link PluginJobCenterSource}. */
150
+ export interface JobsStatusTrayAdapter {
151
+ listJobs?: () => Promise<JobListPayload | readonly JobStateInput[]>
152
+ cancelJob?: (job: JobState) => Promise<JobStateInput | void>
153
+ deleteJob?: (job: JobState) => Promise<boolean | void>
154
+ clearFinishedJobs?: (jobs: readonly JobState[]) => Promise<number | void>
155
+ }
156
+
143
157
  export interface UsePluginJobCenterOptions {
144
158
  source?: MaybeRefOrGetter<PluginJobCenterSource | undefined>
159
+ /** @deprecated Pass a source with its canonical jobs ref. */
160
+ jobs?: MaybeRefOrGetter<readonly JobStateInput[] | undefined>
161
+ /** @deprecated Pass source actions instead. */
162
+ adapter?: MaybeRefOrGetter<JobsStatusTrayAdapter | undefined>
163
+ /** @deprecated Event handling belongs to the source runtime. */
164
+ eventStream?: MaybeRefOrGetter<UsePluginEventStreamReturn<JobStreamData> | undefined>
145
165
  loadOnMount?: boolean
146
166
  }
147
167
 
168
+ export interface UseJobsStatusTrayOptions extends UsePluginJobCenterOptions {}
169
+
170
+ export interface JobStreamMessage {
171
+ event: string
172
+ data: JobStreamData
173
+ }
174
+
148
175
  export interface UsePluginJobCenterReturn {
149
176
  jobs: ComputedRef<JobState[]>
150
177
  activeJobs: ComputedRef<JobState[]>
@@ -164,12 +191,30 @@ export interface UsePluginJobCenterReturn {
164
191
  close: () => void
165
192
  toggle: () => void
166
193
  refresh: () => Promise<boolean>
194
+ applyStreamMessage: (message: JobStreamMessage) => void
167
195
  cancelJob: (job: JobState) => Promise<boolean>
168
196
  deleteJob: (job: JobState) => Promise<boolean>
169
197
  clearFinished: () => Promise<boolean>
170
198
  isPending: (job: JobState) => boolean
171
199
  }
172
200
 
201
+ export type UseJobsStatusTrayReturn = UsePluginJobCenterReturn
202
+
203
+ interface LegacyJobsOverride {
204
+ readonly input: readonly JobStateInput[] | undefined
205
+ readonly jobs: JobState[]
206
+ }
207
+
208
+ function responseJobs(
209
+ response: JobListPayload | readonly JobStateInput[],
210
+ ): readonly JobStateInput[] {
211
+ return 'jobs' in response ? response.jobs : response
212
+ }
213
+
214
+ function isSnapshotPayload(data: JobStreamData): data is { jobs: JobStateInput[] } {
215
+ return 'jobs' in data && Array.isArray(data.jobs)
216
+ }
217
+
173
218
  function errorMessage(error: unknown, fallback: string): string {
174
219
  return error instanceof Error ? error.message : fallback
175
220
  }
@@ -183,30 +228,84 @@ function errorMessage(error: unknown, fallback: string): string {
183
228
  export function usePluginJobCenter(
184
229
  options: UsePluginJobCenterOptions = {},
185
230
  ): UsePluginJobCenterReturn {
231
+ const legacyOverride = shallowRef<LegacyJobsOverride | null>(null)
186
232
  const isOpen = ref(false)
187
233
  const activeTab = ref<JobsStatusTrayTab>('active')
188
234
  const isLoading = ref(false)
189
235
  const error = shallowRef<string | null>(null)
190
236
  const pendingJobIds = ref(new Set<string>())
237
+ const jobRevisions = new Map<string, number>()
238
+ let stateRevision = 0
191
239
  let requestGeneration = 0
192
240
  let active = true
193
241
  const source = computed(() => toValue(options.source))
242
+ const adapter = computed(() => toValue(options.adapter))
194
243
 
195
- const jobs = computed<JobState[]>(() => (
196
- source.value ? [...toValue(source.value.jobs)].map(normalizeJobState) : []
197
- ))
244
+ const jobs = computed<JobState[]>(() => {
245
+ const currentSource = source.value
246
+ if (currentSource) {
247
+ return [...toValue(currentSource.jobs)].map(normalizeJobState)
248
+ }
249
+ const input = toValue(options.jobs)
250
+ const override = legacyOverride.value
251
+ if (override && override.input === input) return override.jobs
252
+ return input ? [...input].map(normalizeJobState) : []
253
+ })
198
254
  const activeJobs = computed(() => jobs.value.filter(job => isActiveJobStatus(job.status)))
199
255
  const historyJobs = computed(() => jobs.value.filter(job => !isActiveJobStatus(job.status)))
200
256
  const deletableJobs = computed(() => historyJobs.value.filter(job => job.can_delete))
201
257
  const featuredJob = computed(() => activeJobs.value[0] ?? historyJobs.value[0] ?? null)
202
- const canRefresh = computed(() => Boolean(source.value?.refreshJobs))
203
- const canCancel = computed(() => Boolean(source.value?.cancelJob))
204
- const canDelete = computed(() => Boolean(source.value?.deleteJob))
258
+ const canRefresh = computed(() => Boolean(source.value?.refreshJobs || adapter.value?.listJobs))
259
+ const canCancel = computed(() => Boolean(source.value?.cancelJob || adapter.value?.cancelJob))
260
+ const canDelete = computed(() => Boolean(source.value?.deleteJob || adapter.value?.deleteJob))
205
261
  const canClearFinished = computed(() => Boolean(
206
262
  source.value?.clearFinishedJobs
207
263
  || source.value?.deleteJob
264
+ || adapter.value?.clearFinishedJobs
265
+ || adapter.value?.deleteJob,
208
266
  ))
209
267
 
268
+ function bumpJobRevision(jobId: string): void {
269
+ jobRevisions.set(jobId, (jobRevisions.get(jobId) ?? 0) + 1)
270
+ }
271
+
272
+ function replaceLegacyJobs(nextJobs: readonly JobStateInput[]): void {
273
+ const normalized = nextJobs.map(normalizeJobState)
274
+ const touchedJobIds = new Set([
275
+ ...jobs.value.map(job => job.job_id),
276
+ ...normalized.map(job => job.job_id),
277
+ ])
278
+ touchedJobIds.forEach(bumpJobRevision)
279
+ legacyOverride.value = {
280
+ input: toValue(options.jobs),
281
+ jobs: normalized,
282
+ }
283
+ stateRevision += 1
284
+ }
285
+
286
+ function upsertLegacyJob(nextJob: JobStateInput): void {
287
+ const normalized = normalizeJobState(nextJob)
288
+ const existingIndex = jobs.value.findIndex(job => job.job_id === normalized.job_id)
289
+ const nextJobs = [...jobs.value]
290
+ if (existingIndex === -1) nextJobs.unshift(normalized)
291
+ else nextJobs[existingIndex] = normalized
292
+ legacyOverride.value = {
293
+ input: toValue(options.jobs),
294
+ jobs: nextJobs,
295
+ }
296
+ bumpJobRevision(normalized.job_id)
297
+ stateRevision += 1
298
+ }
299
+
300
+ function removeLegacyJobs(jobIds: Set<string>): void {
301
+ legacyOverride.value = {
302
+ input: toValue(options.jobs),
303
+ jobs: jobs.value.filter(job => !jobIds.has(job.job_id)),
304
+ }
305
+ jobIds.forEach(bumpJobRevision)
306
+ stateRevision += 1
307
+ }
308
+
210
309
  function setPending(jobId: string, pending: boolean): void {
211
310
  const next = new Set(pendingJobIds.value)
212
311
  if (pending) next.add(jobId)
@@ -214,15 +313,35 @@ export function usePluginJobCenter(
214
313
  pendingJobIds.value = next
215
314
  }
216
315
 
316
+ function applyStreamMessage(message: JobStreamMessage): void {
317
+ if (!active || source.value) return
318
+ if (message.event === 'snapshot' && isSnapshotPayload(message.data)) {
319
+ replaceLegacyJobs(message.data.jobs)
320
+ return
321
+ }
322
+ if (
323
+ (message.event === 'job' || message.event === 'terminal')
324
+ && !isSnapshotPayload(message.data)
325
+ ) {
326
+ upsertLegacyJob(message.data)
327
+ }
328
+ }
329
+
217
330
  async function refresh(): Promise<boolean> {
218
- const refreshJobs = source.value?.refreshJobs
219
- if (!refreshJobs) return false
331
+ const currentSource = source.value
332
+ const listJobs = currentSource?.refreshJobs ?? adapter.value?.listJobs
333
+ if (!listJobs) return false
220
334
  const generation = ++requestGeneration
335
+ const revision = stateRevision
221
336
  isLoading.value = true
222
337
  error.value = null
223
338
  try {
224
- await refreshJobs()
225
- return active && generation === requestGeneration
339
+ const response = await listJobs()
340
+ if (!active || generation !== requestGeneration || revision !== stateRevision) {
341
+ return false
342
+ }
343
+ if (!currentSource && response) replaceLegacyJobs(responseJobs(response))
344
+ return true
226
345
  } catch (cause) {
227
346
  if (active && generation === requestGeneration) {
228
347
  error.value = errorMessage(cause, 'Failed to load jobs')
@@ -235,14 +354,23 @@ export function usePluginJobCenter(
235
354
 
236
355
  async function cancelJob(job: JobState): Promise<boolean> {
237
356
  const sourceCancel = source.value?.cancelJob
238
- if (!job.can_cancel || !sourceCancel || pendingJobIds.value.has(job.job_id)) {
357
+ const adapterCancel = adapter.value?.cancelJob
358
+ if (!job.can_cancel || (!sourceCancel && !adapterCancel) || pendingJobIds.value.has(job.job_id)) {
239
359
  return false
240
360
  }
361
+ const revision = jobRevisions.get(job.job_id) ?? 0
241
362
  setPending(job.job_id, true)
242
363
  error.value = null
243
364
  try {
244
- await sourceCancel(job.job_id)
245
- return active
365
+ const updated = sourceCancel
366
+ ? await sourceCancel(job.job_id)
367
+ : await adapterCancel!(job)
368
+ if (!active) return false
369
+ if (!sourceCancel && revision === (jobRevisions.get(job.job_id) ?? 0)) {
370
+ if (updated) upsertLegacyJob(updated)
371
+ else await refresh()
372
+ }
373
+ return true
246
374
  } catch (cause) {
247
375
  if (active) error.value = errorMessage(cause, 'Failed to cancel job')
248
376
  return false
@@ -253,14 +381,22 @@ export function usePluginJobCenter(
253
381
 
254
382
  async function deleteJob(job: JobState): Promise<boolean> {
255
383
  const sourceDelete = source.value?.deleteJob
256
- if (!job.can_delete || !sourceDelete || pendingJobIds.value.has(job.job_id)) {
384
+ const adapterDelete = adapter.value?.deleteJob
385
+ if (!job.can_delete || (!sourceDelete && !adapterDelete) || pendingJobIds.value.has(job.job_id)) {
257
386
  return false
258
387
  }
259
388
  setPending(job.job_id, true)
260
389
  error.value = null
261
390
  try {
262
- const deleted = await sourceDelete(job.job_id)
263
- return active && deleted !== false
391
+ const deleted = sourceDelete
392
+ ? await sourceDelete(job.job_id)
393
+ : await adapterDelete!(job)
394
+ if (!active) return false
395
+ if (!sourceDelete) {
396
+ if (deleted === false) await refresh()
397
+ else removeLegacyJobs(new Set([job.job_id]))
398
+ }
399
+ return deleted !== false
264
400
  } catch (cause) {
265
401
  if (active) error.value = errorMessage(cause, 'Failed to delete job')
266
402
  return false
@@ -284,13 +420,36 @@ export function usePluginJobCenter(
284
420
  const results = await Promise.all(targets.map(job => deleteJob(job)))
285
421
  return results.every(Boolean)
286
422
  }
287
- return false
423
+ const currentAdapter = adapter.value
424
+ if (currentAdapter?.clearFinishedJobs) {
425
+ const deleted = await currentAdapter.clearFinishedJobs(targets)
426
+ if (!active) return false
427
+ if (typeof deleted === 'number' && deleted !== targets.length) {
428
+ await refresh()
429
+ return false
430
+ }
431
+ removeLegacyJobs(new Set(targets.map(job => job.job_id)))
432
+ return true
433
+ }
434
+ if (!currentAdapter?.deleteJob) return false
435
+ const results = await Promise.all(targets.map(job => deleteJob(job)))
436
+ return results.every(Boolean)
288
437
  } catch (cause) {
289
438
  if (active) error.value = errorMessage(cause, 'Failed to clear jobs')
290
439
  return false
291
440
  }
292
441
  }
293
442
 
443
+ if (options.eventStream !== undefined) {
444
+ watch(
445
+ () => toValue(options.eventStream)?.lastMessage.value ?? null,
446
+ (message: PluginEventStreamMessage<JobStreamData> | null) => {
447
+ if (message) applyStreamMessage(message)
448
+ },
449
+ { immediate: true },
450
+ )
451
+ }
452
+
294
453
  onMounted(() => {
295
454
  if (options.loadOnMount !== false) void refresh()
296
455
  })
@@ -318,9 +477,17 @@ export function usePluginJobCenter(
318
477
  close: () => { isOpen.value = false },
319
478
  toggle: () => { isOpen.value = !isOpen.value },
320
479
  refresh,
480
+ applyStreamMessage,
321
481
  cancelJob,
322
482
  deleteJob,
323
483
  clearFinished,
324
484
  isPending: job => pendingJobIds.value.has(job.job_id),
325
485
  }
326
486
  }
487
+
488
+ /** @deprecated Use `usePluginJobCenter()` for the plugin job-center controller. */
489
+ export function useJobsStatusTray(
490
+ options: UseJobsStatusTrayOptions = {},
491
+ ): UseJobsStatusTrayReturn {
492
+ return usePluginJobCenter(options)
493
+ }