@morscherlab/mint-sdk 1.1.25 → 1.2.0-beta.10

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 (403) hide show
  1. package/README.md +44 -12
  2. package/dist/{ExperimentPopover-tLwJ7XF4.js → ExperimentPopover-oL0fg_A0.js} +6 -4
  3. package/dist/ExperimentPopover-oL0fg_A0.js.map +1 -0
  4. package/dist/{ExperimentSelectorModal-B6P2msT4.js → ExperimentSelectorModal-BeEOomw_.js} +5 -5
  5. package/dist/ExperimentSelectorModal-BeEOomw_.js.map +1 -0
  6. package/dist/{SettingsModal-BW3Ar-uy.js → SettingsModal-Dphh3RrI.js} +5 -8
  7. package/dist/SettingsModal-Dphh3RrI.js.map +1 -0
  8. package/dist/{EmptyState-p90quq8Z.js → Skeleton-DxtwJ5gt.js} +111 -93
  9. package/dist/Skeleton-DxtwJ5gt.js.map +1 -0
  10. package/dist/__tests__/components/ExperimentTimeline.test.d.ts +1 -0
  11. package/dist/__tests__/components/PluginWorkspaceView.internal.test.d.ts +1 -0
  12. package/dist/__tests__/components/SecretInput.test.d.ts +1 -0
  13. package/dist/__tests__/components/WellEditPopupInternal.test.d.ts +1 -0
  14. package/dist/__tests__/composables/useEventStream.test.d.ts +1 -0
  15. package/dist/__tests__/composables/useListReorder.test.d.ts +1 -0
  16. package/dist/__tests__/styles/cssTokens.test.d.ts +1 -0
  17. package/dist/components/ActionMenu.vue.d.ts +1 -1
  18. package/dist/components/AppAvatarMenu.vue.d.ts +4 -0
  19. package/dist/components/AppPluginSwitcher.vue.d.ts +2 -0
  20. package/dist/components/AppSidebar.vue.d.ts +1 -1
  21. package/dist/components/AuditTrail.vue.d.ts +13 -0
  22. package/dist/components/BatchProgressList.vue.d.ts +17 -0
  23. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +1 -1
  24. package/dist/components/DataFrame.vue.d.ts +14 -2
  25. package/dist/components/DatePicker.vue.d.ts +1 -1
  26. package/dist/components/DateTimePicker.vue.d.ts +2 -0
  27. package/dist/components/DropdownButton.vue.d.ts +2 -0
  28. package/dist/components/ExperimentDataViewer.vue.d.ts +26 -1
  29. package/dist/components/ExperimentPopover.vue.d.ts +1 -0
  30. package/dist/components/ExperimentTimeline.vue.d.ts +29 -1
  31. package/dist/components/FitPanel.vue.d.ts +1 -1
  32. package/dist/components/LcmsSequenceTable.vue.d.ts +29 -1
  33. package/dist/components/PluginWorkspaceView.props.d.ts +29 -1
  34. package/dist/components/PluginWorkspaceView.vue.d.ts +5 -5
  35. package/dist/components/ProgressBar.vue.d.ts +1 -1
  36. package/dist/components/ProtocolStepEditor.vue.d.ts +1 -0
  37. package/dist/components/ReagentList.vue.d.ts +29 -1
  38. package/dist/components/SampleHierarchyTree.vue.d.ts +29 -1
  39. package/dist/components/SampleSelector.drag.d.ts +13 -0
  40. package/dist/components/SampleSelector.tree.d.ts +2 -0
  41. package/dist/components/SampleSelector.vue.d.ts +34 -1
  42. package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
  43. package/dist/components/SecretInput.vue.d.ts +28 -0
  44. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -1
  45. package/dist/components/TimePicker.vue.d.ts +1 -0
  46. package/dist/components/WellPlate.geometry.d.ts +15 -0
  47. package/dist/components/WellPlate.interaction.d.ts +13 -0
  48. package/dist/components/WellPlate.vue.d.ts +29 -0
  49. package/dist/components/index.d.ts +9 -0
  50. package/dist/components/index.js +8 -8
  51. package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -0
  52. package/dist/components/internal/DataStateInternal.vue.d.ts +43 -0
  53. package/dist/components/internal/WellEditPopupInternal.vue.d.ts +3 -1
  54. package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +23 -0
  55. package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +70 -0
  56. package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +43 -0
  57. package/dist/{components-DQt0tcpY.js → components-CgAUzF8F.js} +2491 -2235
  58. package/dist/components-CgAUzF8F.js.map +1 -0
  59. package/dist/composables/controlSchemaTypes.d.ts +34 -0
  60. package/dist/composables/flattenWorkspaceBindings.d.ts +61 -0
  61. package/dist/composables/index.d.ts +8 -2
  62. package/dist/composables/index.js +10 -10
  63. package/dist/composables/pluginWorkspaceModel.d.ts +164 -0
  64. package/dist/composables/useAsync.d.ts +5 -1
  65. package/dist/composables/useBioTemplateWorkspace.d.ts +4 -20
  66. package/dist/composables/useCommandHistory.d.ts +5 -1
  67. package/dist/composables/useDropdownState.d.ts +18 -2
  68. package/dist/composables/useEventStream.d.ts +52 -0
  69. package/dist/composables/useFocusTrap.d.ts +21 -0
  70. package/dist/composables/useJobsStatusTray.d.ts +1 -1
  71. package/dist/composables/useListReorder.d.ts +90 -0
  72. package/dist/composables/useMenuKeyboard.d.ts +29 -0
  73. package/dist/composables/useOptimisticMutation.d.ts +5 -1
  74. package/dist/composables/usePluginClient.d.ts +4 -17
  75. package/dist/composables/usePluginConfig.d.ts +3 -1
  76. package/dist/composables/usePluginWorkspace.d.ts +51 -0
  77. package/dist/composables/useRequestSyncState.d.ts +42 -2
  78. package/dist/composables/useResourceCrud.d.ts +5 -1
  79. package/dist/composables/useRovingFocus.d.ts +24 -0
  80. package/dist/composables/useScheduleDrag.d.ts +1 -0
  81. package/dist/composables/useWellPainting.d.ts +5 -1
  82. package/dist/composables/useWellPlateAdapter.d.ts +5 -1
  83. package/dist/composables/useWellPlateValidation.d.ts +5 -1
  84. package/dist/composables/workspaceRecipes.d.ts +188 -0
  85. package/dist/{composables-CkXDe7nm.js → composables-B_pia3hO.js} +105 -254
  86. package/dist/composables-B_pia3hO.js.map +1 -0
  87. package/dist/{useApi-CDBq6z23.js → errors-_W0-LbA6.js} +102 -3
  88. package/dist/errors-_W0-LbA6.js.map +1 -0
  89. package/dist/{experiment-Y9Iz7KYi.js → experiment-Cwl9AYxw.js} +3 -8
  90. package/dist/experiment-Cwl9AYxw.js.map +1 -0
  91. package/dist/{experiment-utils-CLnzPPq5.js → experiment-utils-DV3DrvE_.js} +70 -51
  92. package/dist/experiment-utils-DV3DrvE_.js.map +1 -0
  93. package/dist/index.d.ts +1 -0
  94. package/dist/index.js +15 -15
  95. package/dist/install.js +2 -7
  96. package/dist/install.js.map +1 -1
  97. package/dist/permissions.d.ts +42 -1
  98. package/dist/permissions.js +20 -10
  99. package/dist/permissions.js.map +1 -1
  100. package/dist/stores/index.d.ts +1 -1
  101. package/dist/stores/index.js +3 -3
  102. package/dist/stores/settings.d.ts +31 -1
  103. package/dist/styles.css +11247 -29501
  104. package/dist/templates/builderReadUtils.d.ts +1 -1
  105. package/dist/templates/index.js +2 -4
  106. package/dist/{templates-CovvRZ-9.js → templates-Qq113pyZ.js} +303 -106
  107. package/dist/templates-Qq113pyZ.js.map +1 -0
  108. package/dist/types/form-builder.d.ts +1 -1
  109. package/dist/{useControlSchema-BhoJzWzx.js → useControlSchema-aMZlb4Sc.js} +684 -1216
  110. package/dist/useControlSchema-aMZlb4Sc.js.map +1 -0
  111. package/dist/{useDropdownState-DSb3BC3N.js → useDropdownState-DitCtrTT.js} +22 -6
  112. package/dist/useDropdownState-DitCtrTT.js.map +1 -0
  113. package/dist/{useExperimentSelector-D0pjj9WH.js → useExperimentSelector-I8HC8hUu.js} +4 -4
  114. package/dist/{useExperimentSelector-D0pjj9WH.js.map → useExperimentSelector-I8HC8hUu.js.map} +1 -1
  115. package/dist/{useFormBuilder-DiEcDis2.js → useFormBuilder-TCxNnqeb.js} +2 -2
  116. package/dist/{useFormBuilder-DiEcDis2.js.map → useFormBuilder-TCxNnqeb.js.map} +1 -1
  117. package/dist/{useProtocolTemplates-DHwSveE6.js → useProtocolTemplates-BmX_iVBR.js} +660 -197
  118. package/dist/useProtocolTemplates-BmX_iVBR.js.map +1 -0
  119. package/dist/utils/color.d.ts +13 -0
  120. package/dist/utils/errors.d.ts +9 -0
  121. package/dist/utils/pluginSecrets.d.ts +53 -0
  122. package/dist/utils/records.d.ts +4 -0
  123. package/package.json +1 -1
  124. package/src/__tests__/components/ActionMenu.test.ts +94 -0
  125. package/src/__tests__/components/AppAvatarMenu.test.ts +123 -0
  126. package/src/__tests__/components/BaseTabs.test.ts +67 -0
  127. package/src/__tests__/components/DataFrame.test.ts +183 -11
  128. package/src/__tests__/components/DataStates.test.ts +176 -0
  129. package/src/__tests__/components/DatePicker.test.ts +76 -0
  130. package/src/__tests__/components/DateTimePicker.test.ts +36 -0
  131. package/src/__tests__/components/DropdownButton.test.ts +134 -0
  132. package/src/__tests__/components/ExperimentTimeline.test.ts +63 -0
  133. package/src/__tests__/components/GeneratedPluginView.test.ts +0 -5
  134. package/src/__tests__/components/GroupAssigner.test.ts +53 -0
  135. package/src/__tests__/components/JobsStatusTray.test.ts +20 -18
  136. package/src/__tests__/components/LcmsSequenceTable.test.ts +22 -0
  137. package/src/__tests__/components/PluginWorkspaceView.internal.test.ts +230 -0
  138. package/src/__tests__/components/RackEditor.test.ts +35 -0
  139. package/src/__tests__/components/ReagentList.test.ts +64 -2
  140. package/src/__tests__/components/RetiredStyles.test.ts +31 -0
  141. package/src/__tests__/components/SampleSelector.drag.test.ts +51 -1
  142. package/src/__tests__/components/ScheduleCalendar.test.ts +63 -0
  143. package/src/__tests__/components/SecretInput.test.ts +87 -0
  144. package/src/__tests__/components/SegmentedControl.test.ts +88 -0
  145. package/src/__tests__/components/TimePicker.test.ts +61 -0
  146. package/src/__tests__/components/WellEditPopupInternal.test.ts +108 -0
  147. package/src/__tests__/components/WellPlate.interaction.test.ts +59 -0
  148. package/src/__tests__/composables/useAuth.test.ts +25 -0
  149. package/src/__tests__/composables/useDropdownState.test.ts +58 -1
  150. package/src/__tests__/composables/useEventStream.test.ts +140 -0
  151. package/src/__tests__/composables/useGeneratedAnalysis.test.ts +103 -0
  152. package/src/__tests__/composables/useJobsStatusTray.test.ts +25 -161
  153. package/src/__tests__/composables/useListReorder.test.ts +145 -0
  154. package/src/__tests__/composables/usePluginClient.test.ts +148 -13
  155. package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
  156. package/src/__tests__/docs/frontendDocsCatalog.test.ts +55 -2
  157. package/src/__tests__/stores/settings.test.ts +71 -1
  158. package/src/__tests__/styles/cssTokens.test.ts +131 -0
  159. package/src/__tests__/utils/permissions.test.ts +68 -0
  160. package/src/components/ActionMenu.vue +45 -88
  161. package/src/components/AlertBox.vue +0 -4
  162. package/src/components/AppAvatarMenu.vue +26 -7
  163. package/src/components/AppLayout.vue +0 -4
  164. package/src/components/AppPluginSwitcher.story.vue +87 -0
  165. package/src/components/AppPluginSwitcher.vue +2 -5
  166. package/src/components/AppSidebar.vue +0 -4
  167. package/src/components/AppToastContainer.vue +6 -8
  168. package/src/components/AppTopBar.vue +0 -4
  169. package/src/components/ArtifactSaveDialog.vue +0 -4
  170. package/src/components/ArtifactSelectorModal.vue +0 -4
  171. package/src/components/AuditTrail.story.vue +18 -0
  172. package/src/components/AuditTrail.vue +27 -7
  173. package/src/components/AutoGroupModal.vue +0 -4
  174. package/src/components/Avatar.vue +0 -4
  175. package/src/components/BaseButton.vue +0 -4
  176. package/src/components/BaseCheckbox.vue +0 -4
  177. package/src/components/BaseInput.vue +0 -4
  178. package/src/components/BaseModal.vue +8 -60
  179. package/src/components/BasePill.vue +0 -4
  180. package/src/components/BaseRadioGroup.vue +0 -4
  181. package/src/components/BaseSelect.vue +0 -4
  182. package/src/components/BaseSlider.vue +0 -4
  183. package/src/components/BaseTabs.vue +26 -5
  184. package/src/components/BaseTextarea.vue +0 -4
  185. package/src/components/BaseToggle.vue +0 -4
  186. package/src/components/BatchProgressList.story.vue +18 -0
  187. package/src/components/BatchProgressList.vue +32 -4
  188. package/src/components/BioTemplateExperimentWorkspaceView.vue +10 -21
  189. package/src/components/BioTemplatePackWorkspaceView.vue +11 -15
  190. package/src/components/BioTemplatePresetWorkspaceView.vue +8 -13
  191. package/src/components/Breadcrumb.vue +0 -4
  192. package/src/components/Calendar.vue +0 -4
  193. package/src/components/ChartContainer.story.vue +1 -1
  194. package/src/components/ChartContainer.vue +0 -4
  195. package/src/components/ChemicalFormula.vue +0 -4
  196. package/src/components/CollapsibleCard.vue +0 -4
  197. package/src/components/ColorSlider.vue +0 -4
  198. package/src/components/ComponentBindingRenderer.vue +1 -3
  199. package/src/components/ConcentrationInput.vue +0 -4
  200. package/src/components/ConfirmDialog.vue +0 -4
  201. package/src/components/ControlWorkspaceView.vue +11 -22
  202. package/src/components/DataFrame.story.vue +14 -0
  203. package/src/components/DataFrame.vue +97 -23
  204. package/src/components/DatePicker.vue +73 -27
  205. package/src/components/DateTimePicker.vue +45 -17
  206. package/src/components/Divider.vue +0 -4
  207. package/src/components/DoseCalculator.vue +0 -4
  208. package/src/components/DoseDesignWorkspaceView.vue +6 -25
  209. package/src/components/DropdownButton.vue +24 -6
  210. package/src/components/EmptyState.vue +0 -4
  211. package/src/components/ExperimentCodeBadge.vue +0 -4
  212. package/src/components/ExperimentDataViewer.story.vue +3 -0
  213. package/src/components/ExperimentDataViewer.vue +33 -13
  214. package/src/components/ExperimentPopover.vue +2 -5
  215. package/src/components/ExperimentSelectorModal.vue +0 -4
  216. package/src/components/ExperimentTimeline.story.vue +18 -0
  217. package/src/components/ExperimentTimeline.vue +81 -75
  218. package/src/components/FileUploader.vue +0 -4
  219. package/src/components/FitPanel.vue +0 -4
  220. package/src/components/FormActions.story.vue +96 -0
  221. package/src/components/FormActions.vue +0 -4
  222. package/src/components/FormBuilder.vue +0 -4
  223. package/src/components/FormField.vue +0 -4
  224. package/src/components/FormulaInput.vue +0 -4
  225. package/src/components/GeneratedPathInput.story.vue +68 -0
  226. package/src/components/GeneratedPlotlyResult.story.vue +96 -0
  227. package/src/components/GeneratedPlotlyResult.vue +3 -3
  228. package/src/components/GeneratedPluginView.story.vue +145 -0
  229. package/src/components/GeneratedPluginView.vue +10 -10
  230. package/src/components/GeneratedResultRenderer.story.vue +117 -0
  231. package/src/components/GeneratedResultRenderer.vue +4 -4
  232. package/src/components/GroupAssigner.vue +67 -5
  233. package/src/components/IconButton.vue +0 -4
  234. package/src/components/InstrumentAlertLog.vue +0 -4
  235. package/src/components/InstrumentStateBadge.vue +0 -4
  236. package/src/components/InstrumentStatusCard.vue +0 -4
  237. package/src/components/JobsStatusTray.story.vue +6 -10
  238. package/src/components/JobsStatusTray.vue +0 -4
  239. package/src/components/LcmsSequenceTable.vue +81 -50
  240. package/src/components/LoadingSpinner.vue +0 -4
  241. package/src/components/MobileSupportGate.vue +0 -4
  242. package/src/components/MoleculeInput.vue +0 -4
  243. package/src/components/MultiSelect.vue +0 -4
  244. package/src/components/NumberInput.vue +0 -4
  245. package/src/components/PlateMapEditor.vue +3 -14
  246. package/src/components/PluginIcon.vue +0 -4
  247. package/src/components/PluginWorkspaceView.props.ts +38 -0
  248. package/src/components/PluginWorkspaceView.vue +53 -224
  249. package/src/components/ProgressBar.vue +0 -4
  250. package/src/components/ProtocolStepEditor.vue +2 -4
  251. package/src/components/RackEditor.vue +51 -70
  252. package/src/components/ReagentEditor.vue +0 -4
  253. package/src/components/ReagentList.story.vue +18 -0
  254. package/src/components/ReagentList.vue +127 -82
  255. package/src/components/ResourceCard.vue +0 -4
  256. package/src/components/RuntimeUpdateNotice.story.vue +49 -0
  257. package/src/components/RuntimeUpdateNotice.vue +4 -4
  258. package/src/components/SampleHierarchyTree.story.vue +18 -0
  259. package/src/components/SampleHierarchyTree.vue +32 -9
  260. package/src/components/SampleLegend.vue +2 -8
  261. package/src/components/SampleSelector.drag.ts +51 -0
  262. package/src/components/SampleSelector.story.vue +6 -0
  263. package/src/components/SampleSelector.tree.ts +2 -0
  264. package/src/components/SampleSelector.vue +49 -10
  265. package/src/components/SampleSelectorGroupNode.vue +19 -0
  266. package/src/components/ScheduleCalendar.story.vue +6 -0
  267. package/src/components/ScheduleCalendar.vue +82 -7
  268. package/src/components/ScientificNumber.vue +0 -4
  269. package/src/components/SecretInput.story.vue +79 -0
  270. package/src/components/SecretInput.vue +150 -0
  271. package/src/components/SectionCard.vue +0 -4
  272. package/src/components/SegmentedControl.vue +48 -5
  273. package/src/components/SequenceInput.vue +0 -4
  274. package/src/components/SequenceProgressBar.story.vue +110 -0
  275. package/src/components/SequenceProgressBar.vue +0 -4
  276. package/src/components/SettingsModal.vue +0 -4
  277. package/src/components/Skeleton.vue +0 -4
  278. package/src/components/SmartGroupFieldRecipe.vue +8 -6
  279. package/src/components/SmartGroupManual.vue +0 -4
  280. package/src/components/StatusIndicator.vue +0 -4
  281. package/src/components/StepWizard.vue +0 -4
  282. package/src/components/TagsInput.vue +4 -6
  283. package/src/components/ThemeToggle.vue +0 -4
  284. package/src/components/TimePicker.vue +36 -7
  285. package/src/components/TimeRangeInput.vue +0 -4
  286. package/src/components/Tooltip.story.vue +3 -3
  287. package/src/components/Tooltip.vue +0 -4
  288. package/src/components/UnitInput.vue +0 -4
  289. package/src/components/WellPlate.geometry.ts +60 -1
  290. package/src/components/WellPlate.interaction.ts +44 -0
  291. package/src/components/WellPlate.story.vue +8 -0
  292. package/src/components/WellPlate.vue +53 -7
  293. package/src/components/index.ts +9 -0
  294. package/src/components/internal/AppTopBarPageSelectorInternal.vue +2 -5
  295. package/src/components/internal/AppTopBarPillNavInternal.vue +0 -4
  296. package/src/components/internal/DataStateInternal.vue +74 -0
  297. package/src/components/internal/FormSectionRenderer.vue +0 -4
  298. package/src/components/internal/WellEditPopupInternal.vue +20 -5
  299. package/src/components/internal/WorkspaceControlsInternal.vue +20 -0
  300. package/src/components/internal/WorkspaceShellInternal.vue +125 -0
  301. package/src/components/internal/WorkspaceSidebarInternal.vue +82 -0
  302. package/src/composables/controlSchemaTypes.ts +34 -0
  303. package/src/composables/experimentDesignData.ts +1 -3
  304. package/src/composables/flattenWorkspaceBindings.ts +104 -0
  305. package/src/composables/formBuilderRegistry.ts +5 -0
  306. package/src/composables/index.ts +41 -5
  307. package/src/composables/pluginWorkspaceModel.ts +182 -0
  308. package/src/composables/useAsync.ts +5 -1
  309. package/src/composables/useBioTemplatePackWorkspace.ts +15 -9
  310. package/src/composables/useBioTemplatePresetWorkspace.ts +14 -15
  311. package/src/composables/useBioTemplateWorkspace.ts +18 -43
  312. package/src/composables/useCommandHistory.ts +5 -1
  313. package/src/composables/useDropdownState.ts +35 -5
  314. package/src/composables/useEventStream.ts +326 -0
  315. package/src/composables/useFileImport.ts +2 -7
  316. package/src/composables/useFocusTrap.ts +91 -0
  317. package/src/composables/useGeneratedAnalysis.ts +46 -103
  318. package/src/composables/useJobsStatusTray.ts +1 -1
  319. package/src/composables/useListReorder.story.vue +143 -0
  320. package/src/composables/useListReorder.ts +242 -0
  321. package/src/composables/useMenuKeyboard.ts +90 -0
  322. package/src/composables/useOptimisticMutation.ts +6 -16
  323. package/src/composables/usePluginClient.ts +91 -252
  324. package/src/composables/usePluginConfig.ts +3 -1
  325. package/src/composables/usePluginWorkspace.ts +208 -0
  326. package/src/composables/useRackEditor.ts +2 -11
  327. package/src/composables/useRequestSyncState.ts +98 -28
  328. package/src/composables/useResourceCrud.ts +6 -16
  329. package/src/composables/useRovingFocus.ts +66 -0
  330. package/src/composables/useScheduleDrag.ts +37 -0
  331. package/src/composables/useWellPainting.ts +10 -30
  332. package/src/composables/useWellPlateAdapter.ts +5 -1
  333. package/src/composables/useWellPlateEditor.ts +1 -11
  334. package/src/composables/useWellPlateValidation.ts +5 -1
  335. package/src/composables/workspaceRecipes.ts +267 -0
  336. package/src/index.ts +16 -0
  337. package/src/permissions.ts +48 -9
  338. package/src/stores/experiment.ts +2 -7
  339. package/src/stores/index.ts +9 -1
  340. package/src/stores/settings.ts +93 -1
  341. package/src/styles/components/audit-trail.css +5 -5
  342. package/src/styles/components/batch-progress-list.css +7 -0
  343. package/src/styles/components/color-slider.css +2 -2
  344. package/src/styles/components/data-state.css +30 -0
  345. package/src/styles/components/dataframe.css +29 -35
  346. package/src/styles/components/experiment-data-viewer.css +4 -4
  347. package/src/styles/components/experiment-timeline.css +3 -30
  348. package/src/styles/components/form-builder.css +5 -5
  349. package/src/styles/components/lcms-sequence-table.css +16 -1
  350. package/src/styles/components/multi-select.css +1 -1
  351. package/src/styles/components/reagent-list.css +38 -31
  352. package/src/styles/components/sample-hierarchy-tree.css +5 -19
  353. package/src/styles/components/sample-selector.css +9 -2
  354. package/src/styles/components/schedule-calendar.css +8 -0
  355. package/src/styles/components/scientific-number.css +1 -1
  356. package/src/styles/components/secret-input.css +60 -0
  357. package/src/styles/components/well-plate.css +8 -0
  358. package/src/styles/index.css +9 -0
  359. package/src/styles/variables.css +29 -0
  360. package/src/templates/builderPresetControls.ts +1 -1
  361. package/src/templates/builderReadUtils.ts +1 -3
  362. package/src/templates/componentPlateHelpers.ts +3 -1
  363. package/src/templates/componentTargetResolvers.ts +1 -3
  364. package/src/templates/controlSchemaTargets.ts +1 -3
  365. package/src/templates/doseResponseAdapters.ts +2 -13
  366. package/src/templates/elisaAssayCollectionBuilder.ts +2 -2
  367. package/src/templates/plateMapBuilder.ts +2 -2
  368. package/src/templates/qpcrPlateBuilder.ts +1 -1
  369. package/src/templates/templateValidators.ts +1 -1
  370. package/src/templates/timeCourseBuilder.ts +2 -2
  371. package/src/templates/wellPlateScreenCollectionBuilder.ts +2 -2
  372. package/src/types/form-builder.ts +1 -0
  373. package/src/utils/color.ts +51 -0
  374. package/src/utils/errors.ts +29 -0
  375. package/src/utils/formModelSync.ts +1 -3
  376. package/src/utils/pluginIcon.ts +1 -3
  377. package/src/utils/pluginSecrets.ts +102 -0
  378. package/src/utils/records.ts +9 -0
  379. package/dist/EmptyState-p90quq8Z.js.map +0 -1
  380. package/dist/ExperimentPopover-tLwJ7XF4.js.map +0 -1
  381. package/dist/ExperimentSelectorModal-B6P2msT4.js.map +0 -1
  382. package/dist/SettingsModal-BW3Ar-uy.js.map +0 -1
  383. package/dist/components-DQt0tcpY.js.map +0 -1
  384. package/dist/composables/autoGroupConstants.d.ts +0 -1
  385. package/dist/composables/autoGroupGrouping.d.ts +0 -3
  386. package/dist/composables/useAutoGroupInputSources.d.ts +0 -32
  387. package/dist/composables-CkXDe7nm.js.map +0 -1
  388. package/dist/experiment-Y9Iz7KYi.js.map +0 -1
  389. package/dist/experiment-utils-CLnzPPq5.js.map +0 -1
  390. package/dist/templates/builderDefaults.d.ts +0 -1
  391. package/dist/templates/plateGeometry.d.ts +0 -4
  392. package/dist/templates-CovvRZ-9.js.map +0 -1
  393. package/dist/useApi-CDBq6z23.js.map +0 -1
  394. package/dist/useControlSchema-BhoJzWzx.js.map +0 -1
  395. package/dist/useDropdownState-DSb3BC3N.js.map +0 -1
  396. package/dist/useProtocolTemplates-DHwSveE6.js.map +0 -1
  397. package/src/__tests__/composables/useAutoGroupInputSources.test.ts +0 -127
  398. package/src/composables/autoGroupConstants.ts +0 -4
  399. package/src/composables/autoGroupGrouping.ts +0 -148
  400. package/src/composables/useAutoGroupInputSources.ts +0 -147
  401. package/src/templates/builderDefaults.ts +0 -11
  402. package/src/templates/plateGeometry.ts +0 -62
  403. /package/dist/__tests__/{composables/useAutoGroupInputSources.test.d.ts → components/DataStates.test.d.ts} +0 -0
@@ -0,0 +1,208 @@
1
+ import { computed } from 'vue'
2
+ import type { ComputedRef } from 'vue'
3
+ import type { PageSelectorItem, PillNavItem } from '../types'
4
+ import type {
5
+ PluginWorkspaceViewEmit,
6
+ PluginWorkspaceViewProps,
7
+ } from '../components/PluginWorkspaceView.props'
8
+ import { usePluginWorkspaceControls } from '../components/PluginWorkspaceView.controls'
9
+ import { usePluginWorkspaceNavigation } from '../components/PluginWorkspaceView.navigation'
10
+ import {
11
+ getPluginWorkspaceSidebarSlotNames,
12
+ getPluginWorkspaceTopBarSlotNames,
13
+ hasPluginWorkspaceSidebarSurface,
14
+ } from '../components/PluginWorkspaceView.shell'
15
+ import type {
16
+ PluginWorkspaceControlsModel,
17
+ PluginWorkspaceShellModel,
18
+ PluginWorkspaceSidebarModel,
19
+ } from './pluginWorkspaceModel'
20
+
21
+ /**
22
+ * Loosely-typed view of a component's `useSlots()` result: only slot presence and names are
23
+ * read here, so this accepts any `defineSlots()` shape without fighting Vue's `Slot` render
24
+ * function type. Mirrors the `SlotKeyMap` helper type in PluginWorkspaceView.shell.ts.
25
+ */
26
+ export type PluginWorkspaceSlotsLike = Readonly<Record<string, unknown>>
27
+
28
+ export interface UsePluginWorkspaceOptions {
29
+ /** PluginWorkspaceView's own reactive props object (from `defineProps`). */
30
+ props: PluginWorkspaceViewProps
31
+ /** PluginWorkspaceView's own slots (from `useSlots()`), used to detect provided passthrough slots. */
32
+ slots: PluginWorkspaceSlotsLike
33
+ /** PluginWorkspaceView's own typed `emit` function (from `defineEmits<PluginWorkspaceViewEmits>()`). */
34
+ emit: PluginWorkspaceViewEmit
35
+ }
36
+
37
+ export interface UsePluginWorkspaceReturn {
38
+ /** Model consumed by WorkspaceShellInternal's `shell` prop. */
39
+ shell: ComputedRef<PluginWorkspaceShellModel>
40
+ /** Model consumed by WorkspaceSidebarInternal's `sidebar` prop. */
41
+ sidebar: ComputedRef<PluginWorkspaceSidebarModel>
42
+ /** Model consumed by WorkspaceControlsInternal's `controls` prop, and by the raw `default` slot. */
43
+ controls: ComputedRef<PluginWorkspaceControlsModel>
44
+ /** Whether AppLayout should render a sidebar surface at all. */
45
+ hasSidebarSurface: ComputedRef<boolean>
46
+ /** Applies an AppSidebar `update:model-value` payload to the shared control values and emits `update:modelValue`/`update:values`. */
47
+ handleControlValuesUpdate: (values: Record<string, unknown>) => void
48
+ /** Resolves an AppTopBar `page-selector-select` payload into the active view and emits `page-selector-select`. */
49
+ handlePageSelectorSelect: (page: PageSelectorItem) => void
50
+ /** Resolves an AppTopBar `pill-select` payload into the active view and emits `pill-select`. */
51
+ handlePillSelect: (item: PillNavItem) => void
52
+ }
53
+
54
+ /**
55
+ * Maps PluginWorkspaceView's flat 72-prop surface into the three typed model objects
56
+ * (`shell`, `sidebar`, `controls`) rendered by its internal pieces
57
+ * (WorkspaceShellInternal, WorkspaceSidebarInternal, WorkspaceControlsInternal). Reuses the
58
+ * existing `usePluginWorkspaceControls` / `usePluginWorkspaceNavigation` composables and the
59
+ * PluginWorkspaceView.shell slot-detection helpers, so it changes only how that state is
60
+ * assembled and exposed, not the underlying behavior.
61
+ *
62
+ * @example Recommended
63
+ * ```ts
64
+ * const props = withDefaults(defineProps<PluginWorkspaceViewProps>(), { ... })
65
+ * const emit = defineEmits<PluginWorkspaceViewEmits>()
66
+ * const slots = useSlots()
67
+ * const { shell, sidebar, controls } = usePluginWorkspace({ props, slots, emit })
68
+ * ```
69
+ */
70
+ export function usePluginWorkspace(options: UsePluginWorkspaceOptions): UsePluginWorkspaceReturn {
71
+ const { props, slots, emit } = options
72
+
73
+ const controlsState = usePluginWorkspaceControls({
74
+ model: () => props.model,
75
+ controls: () => props.controls,
76
+ controlOptions: () => props.controlOptions ?? {},
77
+ modelValue: () => props.modelValue,
78
+ values: () => props.values,
79
+ componentBindings: () => props.componentBindings,
80
+ componentProps: () => props.componentProps,
81
+ componentPropsById: () => props.componentPropsById,
82
+ emitValues: (values) => {
83
+ emit('update:modelValue', values)
84
+ emit('update:values', values)
85
+ },
86
+ })
87
+
88
+ const navigationState = usePluginWorkspaceNavigation({
89
+ activeView: () => props.activeView,
90
+ defaultActiveView: () => props.defaultActiveView,
91
+ currentPillId: () => props.currentPillId,
92
+ currentPageSelectorId: () => props.currentPageSelectorId,
93
+ pillNav: () => props.pillNav,
94
+ pageSelector: () => props.pageSelector,
95
+ panelIds: () => Object.keys(props.panels ?? {}),
96
+ generatedPillNav: controlsState.generatedPillNav,
97
+ generatedDefaultView: controlsState.generatedDefaultView,
98
+ emitActiveView: viewId => emit('update:activeView', viewId),
99
+ emitPageSelectorSelect: page => emit('page-selector-select', page),
100
+ emitPillSelect: item => emit('pill-select', item),
101
+ })
102
+
103
+ const topBarSlotNames = computed<string[]>(() => getPluginWorkspaceTopBarSlotNames(slots))
104
+ const sidebarSlotNames = computed<string[]>(() => getPluginWorkspaceSidebarSlotNames(slots))
105
+
106
+ const hasSidebarSurface = computed(() => hasPluginWorkspaceSidebarSurface({
107
+ showSidebar: props.showSidebar ?? true,
108
+ panels: props.panels ?? {},
109
+ forms: props.forms ?? {},
110
+ model: props.model,
111
+ controls: props.controls,
112
+ showSidebarWhenEmpty: props.showSidebarWhenEmpty ?? false,
113
+ sidebarContentId: props.sidebarContentId,
114
+ sidebarTitle: props.sidebarTitle,
115
+ sidebarSubtitle: props.sidebarSubtitle,
116
+ sidebarBadge: props.sidebarBadge,
117
+ hasSidebarSlot: Boolean(slots.sidebar),
118
+ sidebarSlotCount: sidebarSlotNames.value.length,
119
+ }))
120
+
121
+ const controls = computed<PluginWorkspaceControlsModel>(() => ({
122
+ activeView: navigationState.resolvedActiveView.value,
123
+ setActiveView: navigationState.setActiveView,
124
+ values: controlsState.internalControlValues,
125
+ componentBindings: controlsState.resolvedComponentBindings.value,
126
+ componentBindingsById: controlsState.resolvedComponentBindingsById.value,
127
+ componentProps: controlsState.resolvedComponentProps.value,
128
+ componentPropsById: controlsState.resolvedComponentPropsById.value,
129
+ }))
130
+
131
+ const shell = computed<PluginWorkspaceShellModel>(() => ({
132
+ sidebarPosition: props.sidebarPosition ?? 'left',
133
+ sidebarWidth: props.sidebarWidth,
134
+ floating: props.floating ?? true,
135
+ responsiveSidebar: props.responsiveSidebar ?? true,
136
+ sidebarToggleLabel: props.sidebarToggleLabel ?? 'Open workspace sidebar',
137
+ sidebarCloseLabel: props.sidebarCloseLabel ?? 'Close workspace sidebar',
138
+ hasSidebarSurface: hasSidebarSurface.value,
139
+ title: props.title ?? 'Workspace',
140
+ subtitle: props.subtitle,
141
+ topBarVariant: props.topBarVariant ?? 'card',
142
+ homePath: props.homePath ?? '/',
143
+ showLogo: props.showLogo ?? true,
144
+ pageSelector: props.pageSelector,
145
+ currentPageSelectorId: navigationState.resolvedCurrentPageSelectorId.value,
146
+ pluginSwitcher: props.pluginSwitcher,
147
+ pillNav: navigationState.resolvedPillNav.value,
148
+ currentPillId: navigationState.resolvedCurrentPillId.value,
149
+ showThemeToggle: props.showThemeToggle ?? true,
150
+ showSettings: props.showSettings ?? true,
151
+ settingsConfig: props.settingsConfig,
152
+ showStandaloneLabel: props.showStandaloneLabel ?? false,
153
+ standaloneLabel: props.standaloneLabel ?? 'Standalone',
154
+ accountMenu: props.accountMenu,
155
+ showNotifications: props.showNotifications ?? false,
156
+ hasNotificationDot: props.hasNotificationDot ?? false,
157
+ showAdmin: props.showAdmin ?? false,
158
+ adminPath: props.adminPath ?? '/admin',
159
+ userName: props.userName,
160
+ userInitial: props.userInitial,
161
+ userEmail: props.userEmail,
162
+ topBarSlotNames: topBarSlotNames.value,
163
+ controls: controls.value,
164
+ }))
165
+
166
+ const sidebar = computed<PluginWorkspaceSidebarModel>(() => ({
167
+ title: props.sidebarTitle,
168
+ subtitle: props.sidebarSubtitle,
169
+ badge: props.sidebarBadge,
170
+ variant: props.sidebarVariant ?? 'analysis',
171
+ panels: props.panels ?? {},
172
+ activeView: navigationState.resolvedActiveView.value,
173
+ floating: false,
174
+ dense: props.dense ?? true,
175
+ width: props.sidebarWidth,
176
+ side: props.sidebarPosition ?? 'left',
177
+ forms: props.forms ?? {},
178
+ contentId: props.sidebarContentId,
179
+ showWhenEmpty: (props.showSidebarWhenEmpty ?? false) || props.sidebarContentId !== undefined,
180
+ model: props.model,
181
+ controlSchema: props.controls,
182
+ controlOptions: props.controlOptions ?? {},
183
+ values: controlsState.internalControlValues,
184
+ formEnhancements: props.formEnhancements,
185
+ showFormActions: props.showFormActions ?? false,
186
+ formLoading: props.formLoading ?? false,
187
+ formDisabled: props.formDisabled ?? false,
188
+ formReadonly: props.formReadonly ?? false,
189
+ formSize: props.formSize ?? 'sm',
190
+ collapsible: props.sidebarCollapsible,
191
+ collapsed: props.sidebarCollapsed,
192
+ defaultCollapsed: props.defaultSidebarCollapsed ?? false,
193
+ collapsedWidth: props.sidebarCollapsedWidth ?? '3rem',
194
+ collapseButtonLabel: props.sidebarCollapseButtonLabel ?? 'Collapse sidebar',
195
+ expandButtonLabel: props.sidebarExpandButtonLabel ?? 'Expand sidebar',
196
+ slotNames: sidebarSlotNames.value,
197
+ }))
198
+
199
+ return {
200
+ shell,
201
+ sidebar,
202
+ controls,
203
+ hasSidebarSurface,
204
+ handleControlValuesUpdate: controlsState.handleControlValuesUpdate,
205
+ handlePageSelectorSelect: navigationState.handlePageSelectorSelect,
206
+ handlePillSelect: navigationState.handlePillSelect,
207
+ }
208
+ }
@@ -1,5 +1,6 @@
1
1
  import { ref, computed, type Ref, type ComputedRef } from 'vue'
2
2
  import type { Rack, SlotPosition, WellPlateFormat, Well } from '../types'
3
+ import { PLATE_CONFIGS, rowLabel as plateRowLabel } from '../components/WellPlate.geometry'
3
4
 
4
5
  export interface UseRackEditorOptions {
5
6
  defaultFormat?: WellPlateFormat
@@ -31,15 +32,6 @@ export interface UseRackEditorReturn {
31
32
 
32
33
  const SLOT_CYCLE: SlotPosition[] = ['R', 'G', 'B', 'Y']
33
34
 
34
- const PLATE_CONFIGS: Record<WellPlateFormat, { rows: number; cols: number }> = {
35
- 6: { rows: 2, cols: 3 },
36
- 12: { rows: 3, cols: 4 },
37
- 24: { rows: 4, cols: 6 },
38
- 48: { rows: 6, cols: 8 },
39
- 54: { rows: 6, cols: 9 },
40
- 96: { rows: 8, cols: 12 },
41
- 384: { rows: 16, cols: 24 },
42
- }
43
35
 
44
36
  let globalIdCounter = 0
45
37
 
@@ -156,8 +148,7 @@ export function useRackEditor(
156
148
  let counter = 1
157
149
  for (let row = 0; row < config.rows; row++) {
158
150
  for (let col = 0; col < config.cols; col++) {
159
- const rowLabel = String.fromCharCode(65 + row)
160
- const wellId = `${rowLabel}${col + 1}`
151
+ const wellId = `${plateRowLabel(row)}${col + 1}`
161
152
  // Only fill empty wells
162
153
  if (!rack.wells[wellId]?.sampleType) {
163
154
  const paddedNum = String(counter).padStart(3, '0')
@@ -1,10 +1,28 @@
1
1
  import { ref, type Ref } from 'vue'
2
+ import { readErrorMessage as readSharedErrorMessage } from '../utils/errors'
2
3
 
3
4
  export type RequestSyncSuccessKind = 'load' | 'save' | 'run'
4
5
 
6
+ /**
7
+ * The unit of work `run()` drives.
8
+ *
9
+ * It receives an `AbortSignal` that fires when the run is superseded by a
10
+ * newer `run()` or by `cancel()`. Zero-argument operations stay valid and
11
+ * simply ignore it.
12
+ */
13
+ export type RequestSyncOperation<T> = (signal: AbortSignal) => Promise<T>
14
+
5
15
  export interface RequestSyncRunOptions {
6
16
  success?: RequestSyncSuccessKind
7
17
  errorMessage?: string
18
+ /**
19
+ * Abort the previously started run when this one starts. Default `true`;
20
+ * pass `false` to let overlapping runs finish their own work (the stale
21
+ * guard on shared state still applies either way).
22
+ */
23
+ abortPrevious?: boolean
24
+ /** Caller-owned signal; aborting it aborts this run's signal too. */
25
+ signal?: AbortSignal
8
26
  }
9
27
 
10
28
  export interface UseRequestSyncStateReturn {
@@ -30,8 +48,32 @@ export interface UseRequestSyncStateReturn {
30
48
  markSaved: (date?: Date) => void
31
49
  /** Mark the operation as successfully run. */
32
50
  markRun: (date?: Date) => void
33
- /** Run a request with shared loading/error handling and optional sync marking. */
34
- run: <T>(operation: () => Promise<T>, options?: RequestSyncRunOptions) => Promise<T>
51
+ /**
52
+ * Run a request with shared loading/error handling and optional sync marking.
53
+ *
54
+ * ## Stale-response semantics
55
+ *
56
+ * Every call takes a monotonically increasing token. Starting a run
57
+ * supersedes any run started before it, and a superseded run writes **no**
58
+ * shared state when it finally settles: it does not clear `loading`, does not
59
+ * set `error`, and does not move `lastLoadedAt` / `lastSavedAt` / `lastRunAt`.
60
+ * Only the newest run owns those.
61
+ *
62
+ * The caller's own promise is untouched by the guard — a superseded run still
63
+ * resolves with its value or rejects with its own error, exactly as the
64
+ * operation did. The guard never invents a rejection, so a caller awaiting
65
+ * `run()` never sees a "superseded" error it did not cause.
66
+ *
67
+ * The operation additionally receives an `AbortSignal` that fires when its
68
+ * run is superseded or `cancel()` is called, so an operation that forwards
69
+ * the signal (`fetch(url, { signal })`) stops work instead of racing.
70
+ */
71
+ run: <T>(operation: RequestSyncOperation<T>, options?: RequestSyncRunOptions) => Promise<T>
72
+ /**
73
+ * Abort the in-flight run and drop `loading`. The aborted run's completion
74
+ * is treated as stale and writes no state.
75
+ */
76
+ cancel: () => void
35
77
  }
36
78
 
37
79
  /** Shared loading/error/timestamp state for generated plugin request helpers. */
@@ -49,22 +91,7 @@ export function useRequestSyncState(
49
91
  }
50
92
 
51
93
  function readErrorMessage(value: unknown, fallback = defaultErrorMessage): string {
52
- if (value instanceof Error && value.message) {
53
- return value.message
54
- }
55
- if (typeof value === 'string' && value.trim()) {
56
- return value
57
- }
58
- if (
59
- typeof value === 'object'
60
- && value !== null
61
- && 'message' in value
62
- && typeof (value as { message?: unknown }).message === 'string'
63
- && (value as { message: string }).message
64
- ) {
65
- return (value as { message: string }).message
66
- }
67
- return fallback
94
+ return readSharedErrorMessage(value, fallback)
68
95
  }
69
96
 
70
97
  function setError(value: unknown, fallback?: string): string {
@@ -85,27 +112,69 @@ export function useRequestSyncState(
85
112
  lastRunAt.value = date
86
113
  }
87
114
 
115
+ // Monotonic token guarding every write to the shared loading/error/timestamp
116
+ // state. Overlapping runs settle in any order, and a superseded one must not
117
+ // clear a newer run's loading flag or overwrite its error.
118
+ let runToken = 0
119
+ let activeController: AbortController | null = null
120
+
121
+ function cancel(): void {
122
+ runToken += 1
123
+ activeController?.abort()
124
+ activeController = null
125
+ loading.value = false
126
+ }
127
+
88
128
  async function run<T>(
89
- operation: () => Promise<T>,
129
+ operation: RequestSyncOperation<T>,
90
130
  options: RequestSyncRunOptions = {},
91
131
  ): Promise<T> {
132
+ if (options.abortPrevious !== false) {
133
+ activeController?.abort()
134
+ }
135
+
136
+ const token = (runToken += 1)
137
+ const controller = new AbortController()
138
+ activeController = controller
139
+
140
+ const callerSignal = options.signal
141
+ if (callerSignal) {
142
+ if (callerSignal.aborted) controller.abort(callerSignal.reason)
143
+ else callerSignal.addEventListener(
144
+ 'abort',
145
+ () => controller.abort(callerSignal.reason),
146
+ { once: true },
147
+ )
148
+ }
149
+
150
+ const isCurrent = (): boolean => token === runToken
151
+
92
152
  loading.value = true
93
153
  clearError()
94
154
  try {
95
- const response = await operation()
96
- if (options.success === 'load') {
97
- markLoaded()
98
- } else if (options.success === 'save') {
99
- markSaved()
100
- } else if (options.success === 'run') {
101
- markRun()
155
+ const response = await operation(controller.signal)
156
+ if (isCurrent()) {
157
+ if (options.success === 'load') {
158
+ markLoaded()
159
+ } else if (options.success === 'save') {
160
+ markSaved()
161
+ } else if (options.success === 'run') {
162
+ markRun()
163
+ }
102
164
  }
103
165
  return response
104
166
  } catch (err) {
105
- setError(err, options.errorMessage)
167
+ if (isCurrent()) {
168
+ setError(err, options.errorMessage)
169
+ }
106
170
  throw err
107
171
  } finally {
108
- loading.value = false
172
+ if (isCurrent()) {
173
+ loading.value = false
174
+ }
175
+ if (activeController === controller) {
176
+ activeController = null
177
+ }
109
178
  }
110
179
  }
111
180
 
@@ -122,5 +191,6 @@ export function useRequestSyncState(
122
191
  markSaved,
123
192
  markRun,
124
193
  run,
194
+ cancel,
125
195
  }
126
196
  }
@@ -1,4 +1,5 @@
1
1
  import { computed, ref, shallowRef, type ComputedRef, type Ref, type ShallowRef } from 'vue'
2
+ import { readErrorMessage } from '../utils/errors'
2
3
 
3
4
  export type ResourceKey = string | number
4
5
 
@@ -38,7 +39,11 @@ export interface UseResourceCrudReturn<TItem, TCreate, TUpdate, TKey extends Res
38
39
  clearError: () => void
39
40
  }
40
41
 
41
- /** Common list/create/update/delete state wrapper for generated plugin clients. */
42
+ /**
43
+ * Common list/create/update/delete state wrapper for generated plugin clients.
44
+ *
45
+ * @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useGeneratedPluginClient()` instead.
46
+ */
42
47
  export function useResourceCrud<
43
48
  TItem,
44
49
  TCreate = Partial<TItem>,
@@ -228,18 +233,3 @@ function resolveKey<TItem, TKey extends ResourceKey>(
228
233
  }
229
234
  return getKey(keyOrItem as TItem)
230
235
  }
231
-
232
- function readErrorMessage(value: unknown): string {
233
- if (value instanceof Error && value.message) return value.message
234
- if (typeof value === 'string' && value.trim()) return value
235
- if (
236
- typeof value === 'object'
237
- && value !== null
238
- && 'message' in value
239
- && typeof (value as { message?: unknown }).message === 'string'
240
- && (value as { message: string }).message.trim()
241
- ) {
242
- return (value as { message: string }).message
243
- }
244
- return 'Request failed.'
245
- }
@@ -0,0 +1,66 @@
1
+ import { nextTick } from 'vue'
2
+
3
+ export interface UseRovingFocusOptions {
4
+ /** Selectable item ids, in DOM order. Already excludes items skipped entirely (e.g. disabled tabs). */
5
+ ids: () => string[]
6
+ /** The id that is currently active/selected. */
7
+ activeId: () => string | undefined
8
+ /** Activates an id (e.g. emit `update:modelValue`). The composable focuses the matching element afterward. */
9
+ activate: (id: string) => void
10
+ /** Returns the focusable element for an id, queried lazily so it reflects the post-activation DOM. */
11
+ getElement: (id: string) => HTMLElement | null | undefined
12
+ /** Arrow-key axis: `'horizontal'` (Left/Right, default) or `'vertical'` (Up/Down). */
13
+ orientation?: 'horizontal' | 'vertical'
14
+ }
15
+
16
+ export interface UseRovingFocusReturn {
17
+ /** Bind to each item's `@keydown`. */
18
+ handleKeydown: (event: KeyboardEvent) => void
19
+ }
20
+
21
+ /**
22
+ * WAI-ARIA roving-tabindex navigation with automatic activation (the tabs pattern):
23
+ * Left/Right (or Up/Down when `orientation` is `'vertical'`) move to the adjacent item
24
+ * and activate it immediately, wrapping past the ends; Home/End jump to the first/last
25
+ * item. Only the active item should carry `tabindex="0"`; give every other item
26
+ * `tabindex="-1"`. Used by BaseTabs.
27
+ */
28
+ export function useRovingFocus(options: UseRovingFocusOptions): UseRovingFocusReturn {
29
+ const orientation = options.orientation ?? 'horizontal'
30
+ const prevKey = orientation === 'vertical' ? 'ArrowUp' : 'ArrowLeft'
31
+ const nextKey = orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight'
32
+
33
+ function moveTo(id: string | undefined) {
34
+ if (id === undefined) return
35
+ options.activate(id)
36
+ void nextTick(() => options.getElement(id)?.focus())
37
+ }
38
+
39
+ function handleKeydown(event: KeyboardEvent) {
40
+ const ids = options.ids()
41
+ if (ids.length === 0) return
42
+ const currentIndex = ids.indexOf(options.activeId() ?? '')
43
+
44
+ if (event.key === 'Home') {
45
+ event.preventDefault()
46
+ moveTo(ids[0])
47
+ return
48
+ }
49
+ if (event.key === 'End') {
50
+ event.preventDefault()
51
+ moveTo(ids[ids.length - 1])
52
+ return
53
+ }
54
+ if (event.key === prevKey) {
55
+ event.preventDefault()
56
+ moveTo(ids[currentIndex <= 0 ? ids.length - 1 : currentIndex - 1])
57
+ return
58
+ }
59
+ if (event.key === nextKey) {
60
+ event.preventDefault()
61
+ moveTo(ids[currentIndex === -1 || currentIndex >= ids.length - 1 ? 0 : currentIndex + 1])
62
+ }
63
+ }
64
+
65
+ return { handleKeydown }
66
+ }
@@ -200,6 +200,42 @@ export function useScheduleDrag(options: UseScheduleDragOptions) {
200
200
  cleanup()
201
201
  }
202
202
 
203
+ /**
204
+ * Keyboard alternative to the pointer drag: shifts an event by one slot
205
+ * (`mode: 'move'`) or grows/shrinks its end by one slot (`mode: 'resize'`),
206
+ * staying inside the visible day. Returns whether the nudge was applied.
207
+ */
208
+ function nudgeEvent(
209
+ event: ScheduleEvent,
210
+ direction: -1 | 1,
211
+ mode: 'move' | 'resize' = 'move',
212
+ ): boolean {
213
+ if (options.readonly.value) return false
214
+ if (mode === 'move' && event.draggable === false) return false
215
+ if (mode === 'resize' && event.resizable === false) return false
216
+
217
+ const slotMin = options.slotDuration.value
218
+ const dayStart = options.dayStartHour.value * 60
219
+ const dayEnd = options.dayEndHour.value * 60
220
+ const start = dateToMinutes(new Date(event.start))
221
+ const end = dateToMinutes(new Date(event.end))
222
+
223
+ const nextStart = mode === 'move' ? start + direction * slotMin : start
224
+ const nextEnd = mode === 'move'
225
+ ? end + direction * slotMin
226
+ : Math.max(start + slotMin, end + direction * slotMin)
227
+
228
+ if (nextStart < dayStart || nextEnd > dayEnd || nextEnd <= nextStart) return false
229
+
230
+ const base = new Date(event.start)
231
+ const newStart = minutesToDate(base, nextStart)
232
+ const newEnd = minutesToDate(base, nextEnd)
233
+
234
+ if (mode === 'move') options.onMoveComplete?.(event, newStart, newEnd)
235
+ else options.onResizeComplete?.(event, newStart, newEnd)
236
+ return true
237
+ }
238
+
203
239
  function addListeners() {
204
240
  document.addEventListener('pointermove', onPointerMove)
205
241
  document.addEventListener('pointerup', onPointerUp)
@@ -221,6 +257,7 @@ export function useScheduleDrag(options: UseScheduleDragOptions) {
221
257
  startCreate,
222
258
  startMove,
223
259
  startResize,
260
+ nudgeEvent,
224
261
  }
225
262
  }
226
263
 
@@ -1,5 +1,10 @@
1
1
  import { computed, ref, toValue, type ComputedRef, type Ref } from 'vue'
2
2
  import type { WellPlateFormat } from '../types'
3
+ import {
4
+ PLATE_CONFIGS as PLATE_DIMENSIONS,
5
+ rowIndex as rowLabelToIndex,
6
+ rowLabel as indexToRowLabel,
7
+ } from '../components/WellPlate.geometry'
3
8
 
4
9
  export type WellPlateSource<T> = T | Ref<T> | ComputedRef<T> | (() => T)
5
10
 
@@ -34,19 +39,12 @@ export interface UseWellPaintingReturn<TMode extends string = string, TPayload =
34
39
  cancel: () => void
35
40
  }
36
41
 
37
- const ROW_LABELS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
38
42
 
39
- const PLATE_DIMENSIONS: Record<WellPlateFormat, { rows: number; cols: number }> = {
40
- 6: { rows: 2, cols: 3 },
41
- 12: { rows: 3, cols: 4 },
42
- 24: { rows: 4, cols: 6 },
43
- 48: { rows: 6, cols: 8 },
44
- 54: { rows: 6, cols: 9 },
45
- 96: { rows: 8, cols: 12 },
46
- 384: { rows: 16, cols: 24 },
47
- }
48
-
49
- /** Pointer painting/rectangle selection state for WellPlate-based editors. */
43
+ /**
44
+ * Pointer painting/rectangle selection state for WellPlate-based editors.
45
+ *
46
+ * @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useWellPlateEditor()` instead.
47
+ */
50
48
  export function useWellPainting<TMode extends string = string, TPayload = unknown>(
51
49
  options: UseWellPaintingOptions<TMode, TPayload>,
52
50
  ): UseWellPaintingReturn<TMode, TPayload> {
@@ -166,22 +164,4 @@ export function wellIdsInRectangle(
166
164
  return ids
167
165
  }
168
166
 
169
- function rowLabelToIndex(label: string): number {
170
- let result = 0
171
- for (const char of label) {
172
- result = result * 26 + (char.charCodeAt(0) - 64)
173
- }
174
- return result - 1
175
- }
176
167
 
177
- function indexToRowLabel(index: number): string {
178
- if (index < ROW_LABELS.length) return ROW_LABELS[index]
179
- let value = index + 1
180
- let label = ''
181
- while (value > 0) {
182
- value--
183
- label = ROW_LABELS[value % 26] + label
184
- value = Math.floor(value / 26)
185
- }
186
- return label
187
- }
@@ -28,7 +28,11 @@ export interface UseWellPlateAdapterReturn {
28
28
  wells: ComputedRef<Record<string, Partial<Well>>>
29
29
  }
30
30
 
31
- /** Convert domain rows into WellPlate's wells prop without hand-written adapters. */
31
+ /**
32
+ * Convert domain rows into WellPlate's wells prop without hand-written adapters.
33
+ *
34
+ * @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useWellPlateEditor()` instead.
35
+ */
32
36
  export function useWellPlateAdapter<TEntry>(
33
37
  options: WellPlateAdapterOptions<TEntry>,
34
38
  ): UseWellPlateAdapterReturn {
@@ -5,18 +5,8 @@ import type {
5
5
  SampleType,
6
6
  WellPlateFormat,
7
7
  } from '../types'
8
+ import { SAMPLE_COLOR_SCALE as DEFAULT_PALETTE } from '../utils/color'
8
9
 
9
- const DEFAULT_PALETTE = [
10
- '#3B82F6', // blue
11
- '#10B981', // green
12
- '#EF4444', // red
13
- '#F59E0B', // amber
14
- '#8B5CF6', // purple
15
- '#F97316', // orange
16
- '#06B6D4', // cyan
17
- '#14B8A6', // teal
18
- '#6B7280', // gray
19
- ]
20
10
 
21
11
  const MAX_HISTORY = 50
22
12
 
@@ -33,7 +33,11 @@ export interface UseWellPlateValidationReturn<TType extends string = string> {
33
33
  errorCount: ComputedRef<number>
34
34
  }
35
35
 
36
- /** Rule-driven validation state for WellPlate layout/design screens. */
36
+ /**
37
+ * Rule-driven validation state for WellPlate layout/design screens.
38
+ *
39
+ * @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; use `useWellPlateEditor()` instead.
40
+ */
37
41
  export function useWellPlateValidation<TContext, TType extends string = string>(
38
42
  options: UseWellPlateValidationOptions<TContext, TType>,
39
43
  ): UseWellPlateValidationReturn<TType> {