@morscherlab/mint-sdk 1.2.0-beta.1 → 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 (408) hide show
  1. package/README.md +35 -5
  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-DyPAUCLw.js → ExperimentSelectorModal-BeEOomw_.js} +5 -5
  5. package/dist/ExperimentSelectorModal-BeEOomw_.js.map +1 -0
  6. package/dist/{SettingsModal-BgKY93zh.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/useListReorder.test.d.ts +1 -0
  15. package/dist/__tests__/composables/usePluginConfig.test.d.ts +15 -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/AutoGroupModal.vue.d.ts +1 -1
  23. package/dist/components/BatchProgressList.vue.d.ts +17 -0
  24. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
  25. package/dist/components/DataFrame.vue.d.ts +14 -2
  26. package/dist/components/DatePicker.vue.d.ts +1 -1
  27. package/dist/components/DateTimePicker.vue.d.ts +2 -0
  28. package/dist/components/DropdownButton.vue.d.ts +2 -0
  29. package/dist/components/ExperimentDataViewer.vue.d.ts +26 -1
  30. package/dist/components/ExperimentPopover.vue.d.ts +1 -0
  31. package/dist/components/ExperimentTimeline.vue.d.ts +29 -1
  32. package/dist/components/FitPanel.vue.d.ts +1 -1
  33. package/dist/components/JobsStatusTray.vue.d.ts +9 -2
  34. package/dist/components/LcmsSequenceTable.vue.d.ts +29 -1
  35. package/dist/components/PluginWorkspaceView.props.d.ts +29 -1
  36. package/dist/components/PluginWorkspaceView.vue.d.ts +5 -5
  37. package/dist/components/ProgressBar.vue.d.ts +1 -1
  38. package/dist/components/ProtocolStepEditor.vue.d.ts +1 -0
  39. package/dist/components/ReagentList.vue.d.ts +29 -1
  40. package/dist/components/SampleHierarchyTree.vue.d.ts +29 -1
  41. package/dist/components/SampleLegend.vue.d.ts +1 -1
  42. package/dist/components/SampleSelector.drag.d.ts +13 -0
  43. package/dist/components/SampleSelector.tree.d.ts +2 -0
  44. package/dist/components/SampleSelector.vue.d.ts +35 -2
  45. package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
  46. package/dist/components/SecretInput.vue.d.ts +28 -0
  47. package/dist/components/SettingsModal.vue.d.ts +1 -1
  48. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -1
  49. package/dist/components/TimePicker.vue.d.ts +1 -0
  50. package/dist/components/WellPlate.geometry.d.ts +15 -0
  51. package/dist/components/WellPlate.interaction.d.ts +13 -0
  52. package/dist/components/WellPlate.vue.d.ts +29 -0
  53. package/dist/components/index.d.ts +9 -0
  54. package/dist/components/index.js +8 -8
  55. package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -0
  56. package/dist/components/internal/DataStateInternal.vue.d.ts +43 -0
  57. package/dist/components/internal/WellEditPopupInternal.vue.d.ts +3 -1
  58. package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +23 -0
  59. package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +70 -0
  60. package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +43 -0
  61. package/dist/{components-DkHddRMl.js → components-CgAUzF8F.js} +2497 -2235
  62. package/dist/components-CgAUzF8F.js.map +1 -0
  63. package/dist/composables/controlSchemaTypes.d.ts +34 -0
  64. package/dist/composables/flattenWorkspaceBindings.d.ts +61 -0
  65. package/dist/composables/index.d.ts +9 -2
  66. package/dist/composables/index.js +10 -10
  67. package/dist/composables/pluginWorkspaceModel.d.ts +164 -0
  68. package/dist/composables/useAsync.d.ts +5 -1
  69. package/dist/composables/useBioTemplateWorkspace.d.ts +4 -20
  70. package/dist/composables/useCommandHistory.d.ts +5 -1
  71. package/dist/composables/useDropdownState.d.ts +18 -2
  72. package/dist/composables/useEventStream.d.ts +9 -0
  73. package/dist/composables/useFocusTrap.d.ts +21 -0
  74. package/dist/composables/useGeneratedAnalysis.d.ts +3 -1
  75. package/dist/composables/useJobsStatusTray.d.ts +25 -1
  76. package/dist/composables/useListReorder.d.ts +90 -0
  77. package/dist/composables/useMenuKeyboard.d.ts +29 -0
  78. package/dist/composables/useOptimisticMutation.d.ts +5 -1
  79. package/dist/composables/usePluginClient.d.ts +2 -0
  80. package/dist/composables/usePluginConfig.d.ts +27 -0
  81. package/dist/composables/usePluginWorkspace.d.ts +51 -0
  82. package/dist/composables/useRequestSyncState.d.ts +42 -2
  83. package/dist/composables/useResourceCrud.d.ts +5 -1
  84. package/dist/composables/useRovingFocus.d.ts +24 -0
  85. package/dist/composables/useScheduleDrag.d.ts +1 -0
  86. package/dist/composables/useWellPainting.d.ts +5 -1
  87. package/dist/composables/useWellPlateAdapter.d.ts +5 -1
  88. package/dist/composables/useWellPlateValidation.d.ts +5 -1
  89. package/dist/composables/workspaceRecipes.d.ts +188 -0
  90. package/dist/{composables-C_i90sGS.js → composables-B_pia3hO.js} +209 -82
  91. package/dist/composables-B_pia3hO.js.map +1 -0
  92. package/dist/{useApi-J6zFkHmn.js → errors-_W0-LbA6.js} +103 -3
  93. package/dist/errors-_W0-LbA6.js.map +1 -0
  94. package/dist/{experiment-Cq145oEW.js → experiment-Cwl9AYxw.js} +3 -8
  95. package/dist/experiment-Cwl9AYxw.js.map +1 -0
  96. package/dist/{experiment-utils-CLnzPPq5.js → experiment-utils-DV3DrvE_.js} +70 -51
  97. package/dist/experiment-utils-DV3DrvE_.js.map +1 -0
  98. package/dist/index.d.ts +1 -0
  99. package/dist/index.js +15 -15
  100. package/dist/install.js +2 -7
  101. package/dist/install.js.map +1 -1
  102. package/dist/permissions.d.ts +42 -1
  103. package/dist/permissions.js +20 -10
  104. package/dist/permissions.js.map +1 -1
  105. package/dist/stores/auth.d.ts +6 -0
  106. package/dist/stores/index.d.ts +1 -1
  107. package/dist/stores/index.js +3 -3
  108. package/dist/stores/settings.d.ts +31 -1
  109. package/dist/styles.css +11247 -29501
  110. package/dist/templates/builderReadUtils.d.ts +1 -1
  111. package/dist/templates/index.js +2 -4
  112. package/dist/{templates-DsP51i-S.js → templates-Qq113pyZ.js} +303 -106
  113. package/dist/templates-Qq113pyZ.js.map +1 -0
  114. package/dist/types/auth.d.ts +1 -0
  115. package/dist/types/form-builder.d.ts +1 -1
  116. package/dist/{useControlSchema-BDx2Olha.js → useControlSchema-aMZlb4Sc.js} +462 -1130
  117. package/dist/useControlSchema-aMZlb4Sc.js.map +1 -0
  118. package/dist/{useDropdownState-DSb3BC3N.js → useDropdownState-DitCtrTT.js} +22 -6
  119. package/dist/useDropdownState-DitCtrTT.js.map +1 -0
  120. package/dist/{useExperimentSelector-CV4iNfMS.js → useExperimentSelector-I8HC8hUu.js} +4 -4
  121. package/dist/{useExperimentSelector-CV4iNfMS.js.map → useExperimentSelector-I8HC8hUu.js.map} +1 -1
  122. package/dist/{useFormBuilder-BLxG3BOX.js → useFormBuilder-TCxNnqeb.js} +2 -2
  123. package/dist/{useFormBuilder-BLxG3BOX.js.map → useFormBuilder-TCxNnqeb.js.map} +1 -1
  124. package/dist/{useProtocolTemplates-CS_SQl6U.js → useProtocolTemplates-BmX_iVBR.js} +766 -212
  125. package/dist/useProtocolTemplates-BmX_iVBR.js.map +1 -0
  126. package/dist/utils/color.d.ts +13 -0
  127. package/dist/utils/errors.d.ts +9 -0
  128. package/dist/utils/pluginSecrets.d.ts +53 -0
  129. package/dist/utils/records.d.ts +4 -0
  130. package/package.json +1 -1
  131. package/src/__tests__/components/ActionMenu.test.ts +94 -0
  132. package/src/__tests__/components/AppAvatarMenu.test.ts +123 -0
  133. package/src/__tests__/components/BaseTabs.test.ts +67 -0
  134. package/src/__tests__/components/DataFrame.test.ts +183 -11
  135. package/src/__tests__/components/DataStates.test.ts +176 -0
  136. package/src/__tests__/components/DatePicker.test.ts +76 -0
  137. package/src/__tests__/components/DateTimePicker.test.ts +36 -0
  138. package/src/__tests__/components/DropdownButton.test.ts +134 -0
  139. package/src/__tests__/components/ExperimentTimeline.test.ts +63 -0
  140. package/src/__tests__/components/GroupAssigner.test.ts +53 -0
  141. package/src/__tests__/components/LcmsSequenceTable.test.ts +22 -0
  142. package/src/__tests__/components/PluginWorkspaceView.internal.test.ts +230 -0
  143. package/src/__tests__/components/RackEditor.test.ts +35 -0
  144. package/src/__tests__/components/ReagentList.test.ts +64 -2
  145. package/src/__tests__/components/RetiredStyles.test.ts +31 -0
  146. package/src/__tests__/components/SampleSelector.drag.test.ts +51 -1
  147. package/src/__tests__/components/ScheduleCalendar.test.ts +63 -0
  148. package/src/__tests__/components/SecretInput.test.ts +87 -0
  149. package/src/__tests__/components/SegmentedControl.test.ts +88 -0
  150. package/src/__tests__/components/TimePicker.test.ts +61 -0
  151. package/src/__tests__/components/WellEditPopupInternal.test.ts +108 -0
  152. package/src/__tests__/components/WellPlate.interaction.test.ts +59 -0
  153. package/src/__tests__/composables/useAuth.test.ts +2 -0
  154. package/src/__tests__/composables/useDropdownState.test.ts +58 -1
  155. package/src/__tests__/composables/useListReorder.test.ts +145 -0
  156. package/src/__tests__/composables/usePluginClient.test.ts +140 -0
  157. package/src/__tests__/composables/usePluginConfig.test.ts +497 -0
  158. package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
  159. package/src/__tests__/docs/frontendDocsCatalog.test.ts +39 -7
  160. package/src/__tests__/stores/settings.test.ts +71 -1
  161. package/src/__tests__/styles/cssTokens.test.ts +131 -0
  162. package/src/__tests__/utils/permissions.test.ts +68 -0
  163. package/src/components/ActionMenu.vue +45 -88
  164. package/src/components/AlertBox.vue +0 -4
  165. package/src/components/AppAvatarMenu.vue +26 -7
  166. package/src/components/AppLayout.vue +0 -4
  167. package/src/components/AppPluginSwitcher.story.vue +87 -0
  168. package/src/components/AppPluginSwitcher.vue +2 -5
  169. package/src/components/AppSidebar.vue +0 -4
  170. package/src/components/AppToastContainer.vue +6 -8
  171. package/src/components/AppTopBar.vue +0 -4
  172. package/src/components/ArtifactSaveDialog.vue +0 -4
  173. package/src/components/ArtifactSelectorModal.vue +0 -4
  174. package/src/components/AuditTrail.story.vue +18 -0
  175. package/src/components/AuditTrail.vue +27 -7
  176. package/src/components/AutoGroupModal.vue +0 -4
  177. package/src/components/Avatar.vue +0 -4
  178. package/src/components/BaseButton.vue +0 -4
  179. package/src/components/BaseCheckbox.vue +0 -4
  180. package/src/components/BaseInput.vue +0 -4
  181. package/src/components/BaseModal.vue +8 -60
  182. package/src/components/BasePill.vue +0 -4
  183. package/src/components/BaseRadioGroup.vue +0 -4
  184. package/src/components/BaseSelect.vue +0 -4
  185. package/src/components/BaseSlider.vue +0 -4
  186. package/src/components/BaseTabs.vue +26 -5
  187. package/src/components/BaseTextarea.vue +0 -4
  188. package/src/components/BaseToggle.vue +0 -4
  189. package/src/components/BatchProgressList.story.vue +18 -0
  190. package/src/components/BatchProgressList.vue +32 -4
  191. package/src/components/BioTemplateExperimentWorkspaceView.vue +10 -21
  192. package/src/components/BioTemplatePackWorkspaceView.vue +11 -15
  193. package/src/components/BioTemplatePresetWorkspaceView.vue +8 -13
  194. package/src/components/Breadcrumb.vue +0 -4
  195. package/src/components/Calendar.vue +0 -4
  196. package/src/components/ChartContainer.story.vue +1 -1
  197. package/src/components/ChartContainer.vue +0 -4
  198. package/src/components/ChemicalFormula.vue +0 -4
  199. package/src/components/CollapsibleCard.vue +0 -4
  200. package/src/components/ColorSlider.vue +0 -4
  201. package/src/components/ComponentBindingRenderer.vue +1 -3
  202. package/src/components/ConcentrationInput.vue +0 -4
  203. package/src/components/ConfirmDialog.vue +0 -4
  204. package/src/components/ControlWorkspaceView.vue +11 -22
  205. package/src/components/DataFrame.story.vue +14 -0
  206. package/src/components/DataFrame.vue +97 -23
  207. package/src/components/DatePicker.vue +73 -27
  208. package/src/components/DateTimePicker.vue +45 -17
  209. package/src/components/Divider.vue +0 -4
  210. package/src/components/DoseCalculator.vue +0 -4
  211. package/src/components/DoseDesignWorkspaceView.vue +6 -25
  212. package/src/components/DropdownButton.vue +24 -6
  213. package/src/components/EmptyState.vue +0 -4
  214. package/src/components/ExperimentCodeBadge.vue +0 -4
  215. package/src/components/ExperimentDataViewer.story.vue +3 -0
  216. package/src/components/ExperimentDataViewer.vue +33 -13
  217. package/src/components/ExperimentPopover.vue +2 -5
  218. package/src/components/ExperimentSelectorModal.vue +0 -4
  219. package/src/components/ExperimentTimeline.story.vue +18 -0
  220. package/src/components/ExperimentTimeline.vue +81 -75
  221. package/src/components/FileUploader.vue +0 -4
  222. package/src/components/FitPanel.vue +0 -4
  223. package/src/components/FormActions.story.vue +96 -0
  224. package/src/components/FormActions.vue +0 -4
  225. package/src/components/FormBuilder.vue +0 -4
  226. package/src/components/FormField.vue +0 -4
  227. package/src/components/FormulaInput.vue +0 -4
  228. package/src/components/GeneratedPathInput.story.vue +68 -0
  229. package/src/components/GeneratedPlotlyResult.story.vue +96 -0
  230. package/src/components/GeneratedPlotlyResult.vue +3 -3
  231. package/src/components/GeneratedPluginView.story.vue +145 -0
  232. package/src/components/GeneratedPluginView.vue +10 -10
  233. package/src/components/GeneratedResultRenderer.story.vue +117 -0
  234. package/src/components/GeneratedResultRenderer.vue +4 -4
  235. package/src/components/GroupAssigner.vue +67 -5
  236. package/src/components/IconButton.vue +0 -4
  237. package/src/components/InstrumentAlertLog.vue +0 -4
  238. package/src/components/InstrumentStateBadge.vue +0 -4
  239. package/src/components/InstrumentStatusCard.vue +0 -4
  240. package/src/components/JobsStatusTray.vue +16 -5
  241. package/src/components/LcmsSequenceTable.vue +81 -50
  242. package/src/components/LoadingSpinner.vue +0 -4
  243. package/src/components/MobileSupportGate.vue +0 -4
  244. package/src/components/MoleculeInput.vue +0 -4
  245. package/src/components/MultiSelect.vue +0 -4
  246. package/src/components/NumberInput.vue +0 -4
  247. package/src/components/PlateMapEditor.vue +3 -14
  248. package/src/components/PluginIcon.vue +0 -4
  249. package/src/components/PluginWorkspaceView.props.ts +38 -0
  250. package/src/components/PluginWorkspaceView.vue +53 -224
  251. package/src/components/ProgressBar.vue +0 -4
  252. package/src/components/ProtocolStepEditor.vue +2 -4
  253. package/src/components/RackEditor.vue +51 -70
  254. package/src/components/ReagentEditor.vue +0 -4
  255. package/src/components/ReagentList.story.vue +18 -0
  256. package/src/components/ReagentList.vue +127 -82
  257. package/src/components/ResourceCard.vue +0 -4
  258. package/src/components/RuntimeUpdateNotice.story.vue +49 -0
  259. package/src/components/RuntimeUpdateNotice.vue +4 -4
  260. package/src/components/SampleHierarchyTree.story.vue +18 -0
  261. package/src/components/SampleHierarchyTree.vue +32 -9
  262. package/src/components/SampleLegend.vue +2 -8
  263. package/src/components/SampleSelector.drag.ts +51 -0
  264. package/src/components/SampleSelector.story.vue +6 -0
  265. package/src/components/SampleSelector.tree.ts +2 -0
  266. package/src/components/SampleSelector.vue +49 -10
  267. package/src/components/SampleSelectorGroupNode.vue +19 -0
  268. package/src/components/ScheduleCalendar.story.vue +6 -0
  269. package/src/components/ScheduleCalendar.vue +82 -7
  270. package/src/components/ScientificNumber.vue +0 -4
  271. package/src/components/SecretInput.story.vue +79 -0
  272. package/src/components/SecretInput.vue +150 -0
  273. package/src/components/SectionCard.vue +0 -4
  274. package/src/components/SegmentedControl.vue +48 -5
  275. package/src/components/SequenceInput.vue +0 -4
  276. package/src/components/SequenceProgressBar.story.vue +110 -0
  277. package/src/components/SequenceProgressBar.vue +0 -4
  278. package/src/components/SettingsModal.vue +0 -4
  279. package/src/components/Skeleton.vue +0 -4
  280. package/src/components/SmartGroupFieldRecipe.vue +8 -6
  281. package/src/components/SmartGroupManual.vue +0 -4
  282. package/src/components/StatusIndicator.vue +0 -4
  283. package/src/components/StepWizard.vue +0 -4
  284. package/src/components/TagsInput.vue +4 -6
  285. package/src/components/ThemeToggle.vue +0 -4
  286. package/src/components/TimePicker.vue +36 -7
  287. package/src/components/TimeRangeInput.vue +0 -4
  288. package/src/components/Tooltip.story.vue +3 -3
  289. package/src/components/Tooltip.vue +0 -4
  290. package/src/components/UnitInput.vue +0 -4
  291. package/src/components/WellPlate.geometry.ts +60 -1
  292. package/src/components/WellPlate.interaction.ts +44 -0
  293. package/src/components/WellPlate.story.vue +8 -0
  294. package/src/components/WellPlate.vue +53 -7
  295. package/src/components/index.ts +9 -0
  296. package/src/components/internal/AppTopBarPageSelectorInternal.vue +2 -5
  297. package/src/components/internal/AppTopBarPillNavInternal.vue +0 -4
  298. package/src/components/internal/DataStateInternal.vue +74 -0
  299. package/src/components/internal/FormSectionRenderer.vue +0 -4
  300. package/src/components/internal/WellEditPopupInternal.vue +20 -5
  301. package/src/components/internal/WorkspaceControlsInternal.vue +20 -0
  302. package/src/components/internal/WorkspaceShellInternal.vue +125 -0
  303. package/src/components/internal/WorkspaceSidebarInternal.vue +82 -0
  304. package/src/composables/controlSchemaTypes.ts +34 -0
  305. package/src/composables/experimentDesignData.ts +1 -3
  306. package/src/composables/flattenWorkspaceBindings.ts +104 -0
  307. package/src/composables/formBuilderRegistry.ts +5 -0
  308. package/src/composables/index.ts +43 -0
  309. package/src/composables/pluginWorkspaceModel.ts +182 -0
  310. package/src/composables/useAsync.ts +5 -1
  311. package/src/composables/useAuth.ts +3 -0
  312. package/src/composables/useBioTemplatePackWorkspace.ts +15 -9
  313. package/src/composables/useBioTemplatePresetWorkspace.ts +14 -15
  314. package/src/composables/useBioTemplateWorkspace.ts +18 -43
  315. package/src/composables/useCommandHistory.ts +5 -1
  316. package/src/composables/useDropdownState.ts +35 -5
  317. package/src/composables/useEventStream.ts +14 -2
  318. package/src/composables/useFileImport.ts +2 -7
  319. package/src/composables/useFocusTrap.ts +91 -0
  320. package/src/composables/useGeneratedAnalysis.ts +22 -10
  321. package/src/composables/useJobsStatusTray.ts +184 -17
  322. package/src/composables/useListReorder.story.vue +143 -0
  323. package/src/composables/useListReorder.ts +242 -0
  324. package/src/composables/useMenuKeyboard.ts +90 -0
  325. package/src/composables/useOptimisticMutation.ts +6 -16
  326. package/src/composables/usePluginClient.ts +47 -4
  327. package/src/composables/usePluginConfig.ts +252 -0
  328. package/src/composables/usePluginWorkspace.ts +208 -0
  329. package/src/composables/useRackEditor.ts +2 -11
  330. package/src/composables/useRequestSyncState.ts +98 -28
  331. package/src/composables/useResourceCrud.ts +6 -16
  332. package/src/composables/useRovingFocus.ts +66 -0
  333. package/src/composables/useScheduleDrag.ts +37 -0
  334. package/src/composables/useWellPainting.ts +10 -30
  335. package/src/composables/useWellPlateAdapter.ts +5 -1
  336. package/src/composables/useWellPlateEditor.ts +1 -11
  337. package/src/composables/useWellPlateValidation.ts +5 -1
  338. package/src/composables/workspaceRecipes.ts +267 -0
  339. package/src/index.ts +16 -0
  340. package/src/permissions.ts +48 -9
  341. package/src/stores/auth.ts +1 -0
  342. package/src/stores/experiment.ts +2 -7
  343. package/src/stores/index.ts +9 -1
  344. package/src/stores/settings.ts +93 -1
  345. package/src/styles/components/audit-trail.css +5 -5
  346. package/src/styles/components/batch-progress-list.css +7 -0
  347. package/src/styles/components/color-slider.css +2 -2
  348. package/src/styles/components/data-state.css +30 -0
  349. package/src/styles/components/dataframe.css +29 -35
  350. package/src/styles/components/experiment-data-viewer.css +4 -4
  351. package/src/styles/components/experiment-timeline.css +3 -30
  352. package/src/styles/components/form-builder.css +5 -5
  353. package/src/styles/components/lcms-sequence-table.css +16 -1
  354. package/src/styles/components/multi-select.css +1 -1
  355. package/src/styles/components/reagent-list.css +38 -31
  356. package/src/styles/components/sample-hierarchy-tree.css +5 -19
  357. package/src/styles/components/sample-selector.css +9 -2
  358. package/src/styles/components/schedule-calendar.css +8 -0
  359. package/src/styles/components/scientific-number.css +1 -1
  360. package/src/styles/components/secret-input.css +60 -0
  361. package/src/styles/components/well-plate.css +8 -0
  362. package/src/styles/index.css +9 -0
  363. package/src/styles/variables.css +29 -0
  364. package/src/templates/builderPresetControls.ts +1 -1
  365. package/src/templates/builderReadUtils.ts +1 -3
  366. package/src/templates/componentPlateHelpers.ts +3 -1
  367. package/src/templates/componentTargetResolvers.ts +1 -3
  368. package/src/templates/controlSchemaTargets.ts +1 -3
  369. package/src/templates/doseResponseAdapters.ts +2 -13
  370. package/src/templates/elisaAssayCollectionBuilder.ts +2 -2
  371. package/src/templates/plateMapBuilder.ts +2 -2
  372. package/src/templates/qpcrPlateBuilder.ts +1 -1
  373. package/src/templates/templateValidators.ts +1 -1
  374. package/src/templates/timeCourseBuilder.ts +2 -2
  375. package/src/templates/wellPlateScreenCollectionBuilder.ts +2 -2
  376. package/src/types/auth.ts +1 -0
  377. package/src/types/form-builder.ts +1 -0
  378. package/src/utils/color.ts +51 -0
  379. package/src/utils/errors.ts +29 -0
  380. package/src/utils/formModelSync.ts +1 -3
  381. package/src/utils/pluginIcon.ts +1 -3
  382. package/src/utils/pluginSecrets.ts +102 -0
  383. package/src/utils/records.ts +9 -0
  384. package/dist/EmptyState-p90quq8Z.js.map +0 -1
  385. package/dist/ExperimentPopover-tLwJ7XF4.js.map +0 -1
  386. package/dist/ExperimentSelectorModal-DyPAUCLw.js.map +0 -1
  387. package/dist/SettingsModal-BgKY93zh.js.map +0 -1
  388. package/dist/components-DkHddRMl.js.map +0 -1
  389. package/dist/composables/autoGroupConstants.d.ts +0 -1
  390. package/dist/composables/autoGroupGrouping.d.ts +0 -3
  391. package/dist/composables/useAutoGroupInputSources.d.ts +0 -32
  392. package/dist/composables-C_i90sGS.js.map +0 -1
  393. package/dist/experiment-Cq145oEW.js.map +0 -1
  394. package/dist/experiment-utils-CLnzPPq5.js.map +0 -1
  395. package/dist/templates/builderDefaults.d.ts +0 -1
  396. package/dist/templates/plateGeometry.d.ts +0 -4
  397. package/dist/templates-DsP51i-S.js.map +0 -1
  398. package/dist/useApi-J6zFkHmn.js.map +0 -1
  399. package/dist/useControlSchema-BDx2Olha.js.map +0 -1
  400. package/dist/useDropdownState-DSb3BC3N.js.map +0 -1
  401. package/dist/useProtocolTemplates-CS_SQl6U.js.map +0 -1
  402. package/src/__tests__/composables/useAutoGroupInputSources.test.ts +0 -127
  403. package/src/composables/autoGroupConstants.ts +0 -4
  404. package/src/composables/autoGroupGrouping.ts +0 -148
  405. package/src/composables/useAutoGroupInputSources.ts +0 -147
  406. package/src/templates/builderDefaults.ts +0 -11
  407. package/src/templates/plateGeometry.ts +0 -62
  408. /package/dist/__tests__/{composables/useAutoGroupInputSources.test.d.ts → components/DataStates.test.d.ts} +0 -0
@@ -1,10 +1,114 @@
1
- import { d as resolveExperimentCode } from "./experiment-utils-CLnzPPq5.js";
2
- import { r as useRequestSyncState } from "./EmptyState-p90quq8Z.js";
3
- import { A as useConcentrationUnits, H as parseExperimentId, J as isActiveJobStatus, Q as normalizeJobState, U as resolveCurrentExperimentId, V as getInjectedPlatformContext, u as useControlWorkspace } from "./useControlSchema-BDx2Olha.js";
4
- import { o as useSettingsStore, r as useApi } from "./useApi-J6zFkHmn.js";
5
- import { t as useExperimentStore } from "./experiment-Cq145oEW.js";
6
- import { B as toBioTemplateComponentProps, F as getBioTemplateComponentProps$1, H as toBioTemplateComponentPropsById, Jt as getBioTemplatePresetInfo, L as toBioTemplateComponentBindings, P as getBioTemplateComponentBindings, Qt as getBioTemplatePackInfo, R as toBioTemplateComponentBindingsById, U as toBioTemplateComponentSnippets, V as toBioTemplateComponentPropsByComponent$1, W as toBioTemplateComponentUsage, d as createBioTemplatePresetCollectionFromControls, dn as extractTemplateCollection, i as createBioTemplateControlToolkit, p as createBioTemplatePackCollection, sn as createTemplateCollection, u as createBioTemplatePresetCollection, un as ensureTemplateFromCollection, z as toBioTemplateComponentImports } from "./templates-DsP51i-S.js";
7
- import { computed, effectScope, getCurrentScope, inject, onMounted, onScopeDispose, onUnmounted, provide, reactive, readonly, ref, shallowRef, toRaw, toValue, unref, watch } from "vue";
1
+ import { d as resolveExperimentCode } from "./experiment-utils-DV3DrvE_.js";
2
+ import { n as useRequestSyncState } from "./Skeleton-DxtwJ5gt.js";
3
+ import { A as useConcentrationUnits, H as parseExperimentId, J as isActiveJobStatus, Q as normalizeJobState, T as getDefaultControlView, U as resolveCurrentExperimentId, V as getInjectedPlatformContext, f as resolveControlModel, g as mergeControlWorkspaceOptions, h as controlValuesToComponentProps, m as controlValuesToComponentBindingsById, p as controlValuesToComponentBindings, r as getControlDefaults, rt as isRecord, u as useControlWorkspace, w as controlsToViewItems } from "./useControlSchema-aMZlb4Sc.js";
4
+ import { d as useSettingsStore, i as useApi, t as readErrorMessage } from "./errors-_W0-LbA6.js";
5
+ import { t as useExperimentStore } from "./experiment-Cwl9AYxw.js";
6
+ import { B as toBioTemplateComponentProps, F as getBioTemplateComponentProps$1, H as toBioTemplateComponentPropsById, Jt as getBioTemplatePresetInfo, L as toBioTemplateComponentBindings, P as getBioTemplateComponentBindings, Qt as getBioTemplatePackInfo, R as toBioTemplateComponentBindingsById, U as toBioTemplateComponentSnippets, V as toBioTemplateComponentPropsByComponent$1, W as toBioTemplateComponentUsage, bn as rowLabel, d as createBioTemplatePresetCollectionFromControls, dn as extractTemplateCollection, i as createBioTemplateControlToolkit, p as createBioTemplatePackCollection, pn as PLATE_CONFIGS, sn as createTemplateCollection, u as createBioTemplatePresetCollection, un as ensureTemplateFromCollection, wn as deriveShade, xn as SAMPLE_COLOR_SCALE, z as toBioTemplateComponentImports } from "./templates-Qq113pyZ.js";
7
+ import { computed, effectScope, getCurrentScope, inject, nextTick, onMounted, onScopeDispose, onUnmounted, provide, reactive, readonly, ref, shallowRef, toRaw, toValue, unref, watch } from "vue";
8
+ //#region src/composables/useRovingFocus.ts
9
+ /**
10
+ * WAI-ARIA roving-tabindex navigation with automatic activation (the tabs pattern):
11
+ * Left/Right (or Up/Down when `orientation` is `'vertical'`) move to the adjacent item
12
+ * and activate it immediately, wrapping past the ends; Home/End jump to the first/last
13
+ * item. Only the active item should carry `tabindex="0"`; give every other item
14
+ * `tabindex="-1"`. Used by BaseTabs.
15
+ */
16
+ function useRovingFocus(options) {
17
+ const orientation = options.orientation ?? "horizontal";
18
+ const prevKey = orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
19
+ const nextKey = orientation === "vertical" ? "ArrowDown" : "ArrowRight";
20
+ function moveTo(id) {
21
+ if (id === void 0) return;
22
+ options.activate(id);
23
+ nextTick(() => options.getElement(id)?.focus());
24
+ }
25
+ function handleKeydown(event) {
26
+ const ids = options.ids();
27
+ if (ids.length === 0) return;
28
+ const currentIndex = ids.indexOf(options.activeId() ?? "");
29
+ if (event.key === "Home") {
30
+ event.preventDefault();
31
+ moveTo(ids[0]);
32
+ return;
33
+ }
34
+ if (event.key === "End") {
35
+ event.preventDefault();
36
+ moveTo(ids[ids.length - 1]);
37
+ return;
38
+ }
39
+ if (event.key === prevKey) {
40
+ event.preventDefault();
41
+ moveTo(ids[currentIndex <= 0 ? ids.length - 1 : currentIndex - 1]);
42
+ return;
43
+ }
44
+ if (event.key === nextKey) {
45
+ event.preventDefault();
46
+ moveTo(ids[currentIndex === -1 || currentIndex >= ids.length - 1 ? 0 : currentIndex + 1]);
47
+ }
48
+ }
49
+ return { handleKeydown };
50
+ }
51
+ //#endregion
52
+ //#region src/composables/useMenuKeyboard.ts
53
+ /**
54
+ * Shared keyboard contract for menu- and listbox-style disclosure panels (the
55
+ * WAI-ARIA menu button pattern). Used by ActionMenu, AppAvatarMenu and DropdownButton,
56
+ * each pairing it with `useDropdownState` for the disclosure state itself.
57
+ */
58
+ function useMenuKeyboard(options) {
59
+ function focusItem(position) {
60
+ nextTick(() => {
61
+ const items = options.items();
62
+ (position === "first" ? items[0] : items[items.length - 1])?.focus();
63
+ });
64
+ }
65
+ function handleTriggerKeydown(event) {
66
+ if (options.isOpen()) return;
67
+ if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
68
+ event.preventDefault();
69
+ options.open();
70
+ focusItem("first");
71
+ return;
72
+ }
73
+ if (event.key === "ArrowUp") {
74
+ event.preventDefault();
75
+ options.open();
76
+ focusItem("last");
77
+ }
78
+ }
79
+ function handlePanelKeydown(event) {
80
+ const items = options.items();
81
+ const currentIndex = items.indexOf(document.activeElement);
82
+ if (event.key === "Escape") {
83
+ event.preventDefault();
84
+ options.close(true);
85
+ return;
86
+ }
87
+ if (event.key === "Tab") {
88
+ options.close();
89
+ return;
90
+ }
91
+ if (event.key === "Home") {
92
+ event.preventDefault();
93
+ items[0]?.focus();
94
+ return;
95
+ }
96
+ if (event.key === "End") {
97
+ event.preventDefault();
98
+ items[items.length - 1]?.focus();
99
+ return;
100
+ }
101
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
102
+ event.preventDefault();
103
+ const direction = event.key === "ArrowDown" ? 1 : -1;
104
+ items[(Math.max(currentIndex, 0) + direction + items.length) % items.length]?.focus();
105
+ }
106
+ return {
107
+ handleTriggerKeydown,
108
+ handlePanelKeydown
109
+ };
110
+ }
111
+ //#endregion
8
112
  //#region src/composables/useSortedItems.ts
9
113
  /** Shared sorting for SDK tables and lists with stable empty-value handling. */
10
114
  function useSortedItems(options) {
@@ -528,6 +632,310 @@ function useMobileSupportGate(options = {}) {
528
632
  };
529
633
  }
530
634
  //#endregion
635
+ //#region src/components/PluginWorkspaceView.controls.ts
636
+ function usePluginWorkspaceControls(options) {
637
+ const resolvedModel = computed(() => resolveControlModel(options.model()));
638
+ const resolvedControls = computed(() => options.controls() ?? resolvedModel.value?.controls);
639
+ const resolvedControlOptions = computed(() => mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, options.controlOptions()));
640
+ const externalControlValues = computed(() => options.modelValue() ?? options.values());
641
+ const internalControlValues = reactive({});
642
+ let syncingControlValues = false;
643
+ const generatedControlDefaults = computed(() => {
644
+ if (!resolvedControls.value) return {};
645
+ return {
646
+ ...getControlDefaults(resolvedControls.value),
647
+ ...resolvedControlOptions.value.initialValues ?? {}
648
+ };
649
+ });
650
+ function replaceControlValues(values) {
651
+ for (const key of Object.keys(internalControlValues)) if (!(key in values)) delete internalControlValues[key];
652
+ Object.assign(internalControlValues, values);
653
+ }
654
+ function emitControlValues() {
655
+ options.emitValues({ ...internalControlValues });
656
+ }
657
+ function syncResolvedControlValues() {
658
+ syncingControlValues = true;
659
+ replaceControlValues({
660
+ ...generatedControlDefaults.value,
661
+ ...externalControlValues.value === void 0 ? internalControlValues : externalControlValues.value
662
+ });
663
+ syncingControlValues = false;
664
+ }
665
+ function handleControlValuesUpdate(values) {
666
+ syncingControlValues = true;
667
+ replaceControlValues({
668
+ ...generatedControlDefaults.value,
669
+ ...values
670
+ });
671
+ syncingControlValues = false;
672
+ emitControlValues();
673
+ }
674
+ const resolvedComponentBindings = computed(() => controlValuesToComponentBindings(internalControlValues, options.componentBindings() ?? resolvedModel.value?.componentBindings));
675
+ const resolvedComponentBindingsById = computed(() => controlValuesToComponentBindingsById(internalControlValues, options.componentBindings() ?? resolvedModel.value?.componentBindings));
676
+ const resolvedComponentProps = computed(() => {
677
+ const mapping = options.componentProps() ?? resolvedModel.value?.componentProps;
678
+ return mapping === void 0 ? {} : controlValuesToComponentProps(internalControlValues, mapping);
679
+ });
680
+ const resolvedComponentPropsById = computed(() => {
681
+ const mappings = options.componentPropsById() ?? resolvedModel.value?.componentPropsById;
682
+ if (mappings === void 0) return {};
683
+ return Object.fromEntries(Object.entries(mappings).map(([id, mapping]) => [id, controlValuesToComponentProps(internalControlValues, mapping)]));
684
+ });
685
+ const generatedPillNav = computed(() => {
686
+ if (!resolvedControls.value) return [];
687
+ return controlsToViewItems(resolvedControls.value, resolvedControlOptions.value);
688
+ });
689
+ const generatedDefaultView = computed(() => {
690
+ if (!resolvedControls.value) return "";
691
+ return getDefaultControlView(resolvedControls.value, resolvedControlOptions.value);
692
+ });
693
+ syncResolvedControlValues();
694
+ watch([generatedControlDefaults, externalControlValues], syncResolvedControlValues, { deep: true });
695
+ watch(internalControlValues, () => {
696
+ if (syncingControlValues) return;
697
+ emitControlValues();
698
+ }, {
699
+ deep: true,
700
+ flush: "sync"
701
+ });
702
+ return {
703
+ resolvedModel,
704
+ resolvedControls,
705
+ resolvedControlOptions,
706
+ generatedPillNav,
707
+ generatedDefaultView,
708
+ internalControlValues,
709
+ resolvedComponentBindings,
710
+ resolvedComponentBindingsById,
711
+ resolvedComponentProps,
712
+ resolvedComponentPropsById,
713
+ handleControlValuesUpdate
714
+ };
715
+ }
716
+ //#endregion
717
+ //#region src/components/PluginWorkspaceView.navigation.ts
718
+ function usePluginWorkspaceNavigation(options) {
719
+ const internalActiveView = ref(options.defaultActiveView());
720
+ const resolvedPillNav = computed(() => {
721
+ if (options.pillNav() !== void 0) return options.pillNav();
722
+ return options.generatedPillNav.value.length > 0 ? [...options.generatedPillNav.value] : void 0;
723
+ });
724
+ const firstConfiguredViewId = computed(() => options.defaultActiveView() ?? firstItemId(options.pillNav()) ?? options.generatedDefaultView.value ?? firstItemId(options.pageSelector()) ?? options.panelIds()[0] ?? "");
725
+ const resolvedActiveView = computed({
726
+ get: () => options.activeView() ?? options.currentPillId() ?? options.currentPageSelectorId() ?? internalActiveView.value ?? firstConfiguredViewId.value,
727
+ set: (value) => {
728
+ internalActiveView.value = value;
729
+ options.emitActiveView(value);
730
+ }
731
+ });
732
+ const resolvedCurrentPillId = computed(() => options.currentPillId() ?? resolvedActiveView.value);
733
+ const resolvedCurrentPageSelectorId = computed(() => options.currentPageSelectorId() ?? resolvedActiveView.value);
734
+ function setActiveView(viewId) {
735
+ resolvedActiveView.value = viewId;
736
+ }
737
+ function handlePageSelectorSelect(page) {
738
+ setActiveView(page.id);
739
+ options.emitPageSelectorSelect(page);
740
+ }
741
+ function handlePillSelect(item) {
742
+ setActiveView(item.id);
743
+ options.emitPillSelect(item);
744
+ }
745
+ return {
746
+ resolvedPillNav,
747
+ firstConfiguredViewId,
748
+ resolvedActiveView,
749
+ resolvedCurrentPillId,
750
+ resolvedCurrentPageSelectorId,
751
+ setActiveView,
752
+ handlePageSelectorSelect,
753
+ handlePillSelect
754
+ };
755
+ }
756
+ function firstItemId(items) {
757
+ const item = items?.[0];
758
+ if (item === void 0) return void 0;
759
+ return typeof item === "string" ? item : item.id;
760
+ }
761
+ //#endregion
762
+ //#region src/components/PluginWorkspaceView.shell.ts
763
+ var topBarForwardedSlots = new Set([
764
+ "icon",
765
+ "logo",
766
+ "page-selector-icon",
767
+ "page-selector-item-icon",
768
+ "nav",
769
+ "center",
770
+ "actions",
771
+ "account-menu-items",
772
+ "account-menu-item-icon",
773
+ "settings-appearance"
774
+ ]);
775
+ var sidebarForwardedSlots = new Set([
776
+ "header",
777
+ "collapsed",
778
+ "collapsed-footer",
779
+ "footer"
780
+ ]);
781
+ function getPluginWorkspaceTopBarSlotNames(slots) {
782
+ return Object.keys(slots).filter((name) => topBarForwardedSlots.has(name) || name.startsWith("settings-tab-"));
783
+ }
784
+ function getPluginWorkspaceSidebarSlotNames(slots) {
785
+ return Object.keys(slots).filter((name) => sidebarForwardedSlots.has(name) || name.startsWith("section-"));
786
+ }
787
+ function hasPluginWorkspaceSidebarSurface(options) {
788
+ if (!options.showSidebar) return false;
789
+ return Object.keys(options.panels).length > 0 || Object.keys(options.forms).length > 0 || options.model !== void 0 || options.controls !== void 0 || options.showSidebarWhenEmpty || options.sidebarContentId !== void 0 || options.sidebarTitle !== void 0 || options.sidebarSubtitle !== void 0 || options.sidebarBadge !== void 0 || options.hasSidebarSlot || options.sidebarSlotCount > 0;
790
+ }
791
+ //#endregion
792
+ //#region src/composables/usePluginWorkspace.ts
793
+ /**
794
+ * Maps PluginWorkspaceView's flat 72-prop surface into the three typed model objects
795
+ * (`shell`, `sidebar`, `controls`) rendered by its internal pieces
796
+ * (WorkspaceShellInternal, WorkspaceSidebarInternal, WorkspaceControlsInternal). Reuses the
797
+ * existing `usePluginWorkspaceControls` / `usePluginWorkspaceNavigation` composables and the
798
+ * PluginWorkspaceView.shell slot-detection helpers, so it changes only how that state is
799
+ * assembled and exposed, not the underlying behavior.
800
+ *
801
+ * @example Recommended
802
+ * ```ts
803
+ * const props = withDefaults(defineProps<PluginWorkspaceViewProps>(), { ... })
804
+ * const emit = defineEmits<PluginWorkspaceViewEmits>()
805
+ * const slots = useSlots()
806
+ * const { shell, sidebar, controls } = usePluginWorkspace({ props, slots, emit })
807
+ * ```
808
+ */
809
+ function usePluginWorkspace(options) {
810
+ const { props, slots, emit } = options;
811
+ const controlsState = usePluginWorkspaceControls({
812
+ model: () => props.model,
813
+ controls: () => props.controls,
814
+ controlOptions: () => props.controlOptions ?? {},
815
+ modelValue: () => props.modelValue,
816
+ values: () => props.values,
817
+ componentBindings: () => props.componentBindings,
818
+ componentProps: () => props.componentProps,
819
+ componentPropsById: () => props.componentPropsById,
820
+ emitValues: (values) => {
821
+ emit("update:modelValue", values);
822
+ emit("update:values", values);
823
+ }
824
+ });
825
+ const navigationState = usePluginWorkspaceNavigation({
826
+ activeView: () => props.activeView,
827
+ defaultActiveView: () => props.defaultActiveView,
828
+ currentPillId: () => props.currentPillId,
829
+ currentPageSelectorId: () => props.currentPageSelectorId,
830
+ pillNav: () => props.pillNav,
831
+ pageSelector: () => props.pageSelector,
832
+ panelIds: () => Object.keys(props.panels ?? {}),
833
+ generatedPillNav: controlsState.generatedPillNav,
834
+ generatedDefaultView: controlsState.generatedDefaultView,
835
+ emitActiveView: (viewId) => emit("update:activeView", viewId),
836
+ emitPageSelectorSelect: (page) => emit("page-selector-select", page),
837
+ emitPillSelect: (item) => emit("pill-select", item)
838
+ });
839
+ const topBarSlotNames = computed(() => getPluginWorkspaceTopBarSlotNames(slots));
840
+ const sidebarSlotNames = computed(() => getPluginWorkspaceSidebarSlotNames(slots));
841
+ const hasSidebarSurface = computed(() => hasPluginWorkspaceSidebarSurface({
842
+ showSidebar: props.showSidebar ?? true,
843
+ panels: props.panels ?? {},
844
+ forms: props.forms ?? {},
845
+ model: props.model,
846
+ controls: props.controls,
847
+ showSidebarWhenEmpty: props.showSidebarWhenEmpty ?? false,
848
+ sidebarContentId: props.sidebarContentId,
849
+ sidebarTitle: props.sidebarTitle,
850
+ sidebarSubtitle: props.sidebarSubtitle,
851
+ sidebarBadge: props.sidebarBadge,
852
+ hasSidebarSlot: Boolean(slots.sidebar),
853
+ sidebarSlotCount: sidebarSlotNames.value.length
854
+ }));
855
+ const controls = computed(() => ({
856
+ activeView: navigationState.resolvedActiveView.value,
857
+ setActiveView: navigationState.setActiveView,
858
+ values: controlsState.internalControlValues,
859
+ componentBindings: controlsState.resolvedComponentBindings.value,
860
+ componentBindingsById: controlsState.resolvedComponentBindingsById.value,
861
+ componentProps: controlsState.resolvedComponentProps.value,
862
+ componentPropsById: controlsState.resolvedComponentPropsById.value
863
+ }));
864
+ return {
865
+ shell: computed(() => ({
866
+ sidebarPosition: props.sidebarPosition ?? "left",
867
+ sidebarWidth: props.sidebarWidth,
868
+ floating: props.floating ?? true,
869
+ responsiveSidebar: props.responsiveSidebar ?? true,
870
+ sidebarToggleLabel: props.sidebarToggleLabel ?? "Open workspace sidebar",
871
+ sidebarCloseLabel: props.sidebarCloseLabel ?? "Close workspace sidebar",
872
+ hasSidebarSurface: hasSidebarSurface.value,
873
+ title: props.title ?? "Workspace",
874
+ subtitle: props.subtitle,
875
+ topBarVariant: props.topBarVariant ?? "card",
876
+ homePath: props.homePath ?? "/",
877
+ showLogo: props.showLogo ?? true,
878
+ pageSelector: props.pageSelector,
879
+ currentPageSelectorId: navigationState.resolvedCurrentPageSelectorId.value,
880
+ pluginSwitcher: props.pluginSwitcher,
881
+ pillNav: navigationState.resolvedPillNav.value,
882
+ currentPillId: navigationState.resolvedCurrentPillId.value,
883
+ showThemeToggle: props.showThemeToggle ?? true,
884
+ showSettings: props.showSettings ?? true,
885
+ settingsConfig: props.settingsConfig,
886
+ showStandaloneLabel: props.showStandaloneLabel ?? false,
887
+ standaloneLabel: props.standaloneLabel ?? "Standalone",
888
+ accountMenu: props.accountMenu,
889
+ showNotifications: props.showNotifications ?? false,
890
+ hasNotificationDot: props.hasNotificationDot ?? false,
891
+ showAdmin: props.showAdmin ?? false,
892
+ adminPath: props.adminPath ?? "/admin",
893
+ userName: props.userName,
894
+ userInitial: props.userInitial,
895
+ userEmail: props.userEmail,
896
+ topBarSlotNames: topBarSlotNames.value,
897
+ controls: controls.value
898
+ })),
899
+ sidebar: computed(() => ({
900
+ title: props.sidebarTitle,
901
+ subtitle: props.sidebarSubtitle,
902
+ badge: props.sidebarBadge,
903
+ variant: props.sidebarVariant ?? "analysis",
904
+ panels: props.panels ?? {},
905
+ activeView: navigationState.resolvedActiveView.value,
906
+ floating: false,
907
+ dense: props.dense ?? true,
908
+ width: props.sidebarWidth,
909
+ side: props.sidebarPosition ?? "left",
910
+ forms: props.forms ?? {},
911
+ contentId: props.sidebarContentId,
912
+ showWhenEmpty: (props.showSidebarWhenEmpty ?? false) || props.sidebarContentId !== void 0,
913
+ model: props.model,
914
+ controlSchema: props.controls,
915
+ controlOptions: props.controlOptions ?? {},
916
+ values: controlsState.internalControlValues,
917
+ formEnhancements: props.formEnhancements,
918
+ showFormActions: props.showFormActions ?? false,
919
+ formLoading: props.formLoading ?? false,
920
+ formDisabled: props.formDisabled ?? false,
921
+ formReadonly: props.formReadonly ?? false,
922
+ formSize: props.formSize ?? "sm",
923
+ collapsible: props.sidebarCollapsible,
924
+ collapsed: props.sidebarCollapsed,
925
+ defaultCollapsed: props.defaultSidebarCollapsed ?? false,
926
+ collapsedWidth: props.sidebarCollapsedWidth ?? "3rem",
927
+ collapseButtonLabel: props.sidebarCollapseButtonLabel ?? "Collapse sidebar",
928
+ expandButtonLabel: props.sidebarExpandButtonLabel ?? "Expand sidebar",
929
+ slotNames: sidebarSlotNames.value
930
+ })),
931
+ controls,
932
+ hasSidebarSurface,
933
+ handleControlValuesUpdate: controlsState.handleControlValuesUpdate,
934
+ handlePageSelectorSelect: navigationState.handlePageSelectorSelect,
935
+ handlePillSelect: navigationState.handlePillSelect
936
+ };
937
+ }
938
+ //#endregion
531
939
  //#region src/composables/useJobsStatusTray.ts
532
940
  /**
533
941
  * Combines independent job runtimes without copying their reactive state.
@@ -601,6 +1009,12 @@ var PluginJobSourceDisposeError = class extends Error {
601
1009
  function duplicatePluginJobId(jobId) {
602
1010
  return /* @__PURE__ */ new Error(`Duplicate plugin job id across combined sources: ${jobId}`);
603
1011
  }
1012
+ function responseJobs(response) {
1013
+ return "jobs" in response ? response.jobs : response;
1014
+ }
1015
+ function isSnapshotPayload(data) {
1016
+ return "jobs" in data && Array.isArray(data.jobs);
1017
+ }
604
1018
  function errorMessage(error, fallback) {
605
1019
  return error instanceof Error ? error.message : fallback;
606
1020
  }
@@ -611,38 +1025,94 @@ function errorMessage(error, fallback) {
611
1025
  * source. The controller never mirrors source jobs into another ref.
612
1026
  */
613
1027
  function usePluginJobCenter(options = {}) {
1028
+ const legacyOverride = shallowRef(null);
614
1029
  const isOpen = ref(false);
615
1030
  const activeTab = ref("active");
616
1031
  const isLoading = ref(false);
617
1032
  const error = shallowRef(null);
618
1033
  const pendingJobIds = ref(/* @__PURE__ */ new Set());
1034
+ const jobRevisions = /* @__PURE__ */ new Map();
1035
+ let stateRevision = 0;
619
1036
  let requestGeneration = 0;
620
1037
  let active = true;
621
1038
  const source = computed(() => toValue(options.source));
622
- const jobs = computed(() => source.value ? [...toValue(source.value.jobs)].map(normalizeJobState) : []);
1039
+ const adapter = computed(() => toValue(options.adapter));
1040
+ const jobs = computed(() => {
1041
+ const currentSource = source.value;
1042
+ if (currentSource) return [...toValue(currentSource.jobs)].map(normalizeJobState);
1043
+ const input = toValue(options.jobs);
1044
+ const override = legacyOverride.value;
1045
+ if (override && override.input === input) return override.jobs;
1046
+ return input ? [...input].map(normalizeJobState) : [];
1047
+ });
623
1048
  const activeJobs = computed(() => jobs.value.filter((job) => isActiveJobStatus(job.status)));
624
1049
  const historyJobs = computed(() => jobs.value.filter((job) => !isActiveJobStatus(job.status)));
625
1050
  const deletableJobs = computed(() => historyJobs.value.filter((job) => job.can_delete));
626
1051
  const featuredJob = computed(() => activeJobs.value[0] ?? historyJobs.value[0] ?? null);
627
- const canRefresh = computed(() => Boolean(source.value?.refreshJobs));
628
- const canCancel = computed(() => Boolean(source.value?.cancelJob));
629
- const canDelete = computed(() => Boolean(source.value?.deleteJob));
630
- const canClearFinished = computed(() => Boolean(source.value?.clearFinishedJobs || source.value?.deleteJob));
1052
+ const canRefresh = computed(() => Boolean(source.value?.refreshJobs || adapter.value?.listJobs));
1053
+ const canCancel = computed(() => Boolean(source.value?.cancelJob || adapter.value?.cancelJob));
1054
+ const canDelete = computed(() => Boolean(source.value?.deleteJob || adapter.value?.deleteJob));
1055
+ const canClearFinished = computed(() => Boolean(source.value?.clearFinishedJobs || source.value?.deleteJob || adapter.value?.clearFinishedJobs || adapter.value?.deleteJob));
1056
+ function bumpJobRevision(jobId) {
1057
+ jobRevisions.set(jobId, (jobRevisions.get(jobId) ?? 0) + 1);
1058
+ }
1059
+ function replaceLegacyJobs(nextJobs) {
1060
+ const normalized = nextJobs.map(normalizeJobState);
1061
+ new Set([...jobs.value.map((job) => job.job_id), ...normalized.map((job) => job.job_id)]).forEach(bumpJobRevision);
1062
+ legacyOverride.value = {
1063
+ input: toValue(options.jobs),
1064
+ jobs: normalized
1065
+ };
1066
+ stateRevision += 1;
1067
+ }
1068
+ function upsertLegacyJob(nextJob) {
1069
+ const normalized = normalizeJobState(nextJob);
1070
+ const existingIndex = jobs.value.findIndex((job) => job.job_id === normalized.job_id);
1071
+ const nextJobs = [...jobs.value];
1072
+ if (existingIndex === -1) nextJobs.unshift(normalized);
1073
+ else nextJobs[existingIndex] = normalized;
1074
+ legacyOverride.value = {
1075
+ input: toValue(options.jobs),
1076
+ jobs: nextJobs
1077
+ };
1078
+ bumpJobRevision(normalized.job_id);
1079
+ stateRevision += 1;
1080
+ }
1081
+ function removeLegacyJobs(jobIds) {
1082
+ legacyOverride.value = {
1083
+ input: toValue(options.jobs),
1084
+ jobs: jobs.value.filter((job) => !jobIds.has(job.job_id))
1085
+ };
1086
+ jobIds.forEach(bumpJobRevision);
1087
+ stateRevision += 1;
1088
+ }
631
1089
  function setPending(jobId, pending) {
632
1090
  const next = new Set(pendingJobIds.value);
633
1091
  if (pending) next.add(jobId);
634
1092
  else next.delete(jobId);
635
1093
  pendingJobIds.value = next;
636
1094
  }
1095
+ function applyStreamMessage(message) {
1096
+ if (!active || source.value) return;
1097
+ if (message.event === "snapshot" && isSnapshotPayload(message.data)) {
1098
+ replaceLegacyJobs(message.data.jobs);
1099
+ return;
1100
+ }
1101
+ if ((message.event === "job" || message.event === "terminal") && !isSnapshotPayload(message.data)) upsertLegacyJob(message.data);
1102
+ }
637
1103
  async function refresh() {
638
- const refreshJobs = source.value?.refreshJobs;
639
- if (!refreshJobs) return false;
1104
+ const currentSource = source.value;
1105
+ const listJobs = currentSource?.refreshJobs ?? adapter.value?.listJobs;
1106
+ if (!listJobs) return false;
640
1107
  const generation = ++requestGeneration;
1108
+ const revision = stateRevision;
641
1109
  isLoading.value = true;
642
1110
  error.value = null;
643
1111
  try {
644
- await refreshJobs();
645
- return active && generation === requestGeneration;
1112
+ const response = await listJobs();
1113
+ if (!active || generation !== requestGeneration || revision !== stateRevision) return false;
1114
+ if (!currentSource && response) replaceLegacyJobs(responseJobs(response));
1115
+ return true;
646
1116
  } catch (cause) {
647
1117
  if (active && generation === requestGeneration) error.value = errorMessage(cause, "Failed to load jobs");
648
1118
  return false;
@@ -652,12 +1122,17 @@ function usePluginJobCenter(options = {}) {
652
1122
  }
653
1123
  async function cancelJob(job) {
654
1124
  const sourceCancel = source.value?.cancelJob;
655
- if (!job.can_cancel || !sourceCancel || pendingJobIds.value.has(job.job_id)) return false;
1125
+ const adapterCancel = adapter.value?.cancelJob;
1126
+ if (!job.can_cancel || !sourceCancel && !adapterCancel || pendingJobIds.value.has(job.job_id)) return false;
1127
+ const revision = jobRevisions.get(job.job_id) ?? 0;
656
1128
  setPending(job.job_id, true);
657
1129
  error.value = null;
658
1130
  try {
659
- await sourceCancel(job.job_id);
660
- return active;
1131
+ const updated = sourceCancel ? await sourceCancel(job.job_id) : await adapterCancel(job);
1132
+ if (!active) return false;
1133
+ if (!sourceCancel && revision === (jobRevisions.get(job.job_id) ?? 0)) if (updated) upsertLegacyJob(updated);
1134
+ else await refresh();
1135
+ return true;
661
1136
  } catch (cause) {
662
1137
  if (active) error.value = errorMessage(cause, "Failed to cancel job");
663
1138
  return false;
@@ -667,12 +1142,16 @@ function usePluginJobCenter(options = {}) {
667
1142
  }
668
1143
  async function deleteJob(job) {
669
1144
  const sourceDelete = source.value?.deleteJob;
670
- if (!job.can_delete || !sourceDelete || pendingJobIds.value.has(job.job_id)) return false;
1145
+ const adapterDelete = adapter.value?.deleteJob;
1146
+ if (!job.can_delete || !sourceDelete && !adapterDelete || pendingJobIds.value.has(job.job_id)) return false;
671
1147
  setPending(job.job_id, true);
672
1148
  error.value = null;
673
1149
  try {
674
- const deleted = await sourceDelete(job.job_id);
675
- return active && deleted !== false;
1150
+ const deleted = sourceDelete ? await sourceDelete(job.job_id) : await adapterDelete(job);
1151
+ if (!active) return false;
1152
+ if (!sourceDelete) if (deleted === false) await refresh();
1153
+ else removeLegacyJobs(new Set([job.job_id]));
1154
+ return deleted !== false;
676
1155
  } catch (cause) {
677
1156
  if (active) error.value = errorMessage(cause, "Failed to delete job");
678
1157
  return false;
@@ -692,12 +1171,27 @@ function usePluginJobCenter(options = {}) {
692
1171
  return typeof deleted !== "number" || deleted === targets.length;
693
1172
  }
694
1173
  if (currentSource?.deleteJob) return (await Promise.all(targets.map((job) => deleteJob(job)))).every(Boolean);
695
- return false;
1174
+ const currentAdapter = adapter.value;
1175
+ if (currentAdapter?.clearFinishedJobs) {
1176
+ const deleted = await currentAdapter.clearFinishedJobs(targets);
1177
+ if (!active) return false;
1178
+ if (typeof deleted === "number" && deleted !== targets.length) {
1179
+ await refresh();
1180
+ return false;
1181
+ }
1182
+ removeLegacyJobs(new Set(targets.map((job) => job.job_id)));
1183
+ return true;
1184
+ }
1185
+ if (!currentAdapter?.deleteJob) return false;
1186
+ return (await Promise.all(targets.map((job) => deleteJob(job)))).every(Boolean);
696
1187
  } catch (cause) {
697
1188
  if (active) error.value = errorMessage(cause, "Failed to clear jobs");
698
1189
  return false;
699
1190
  }
700
1191
  }
1192
+ if (options.eventStream !== void 0) watch(() => toValue(options.eventStream)?.lastMessage.value ?? null, (message) => {
1193
+ if (message) applyStreamMessage(message);
1194
+ }, { immediate: true });
701
1195
  onMounted(() => {
702
1196
  if (options.loadOnMount !== false) refresh();
703
1197
  });
@@ -730,12 +1224,17 @@ function usePluginJobCenter(options = {}) {
730
1224
  isOpen.value = !isOpen.value;
731
1225
  },
732
1226
  refresh,
1227
+ applyStreamMessage,
733
1228
  cancelJob,
734
1229
  deleteJob,
735
1230
  clearFinished,
736
1231
  isPending: (job) => pendingJobIds.value.has(job.job_id)
737
1232
  };
738
1233
  }
1234
+ /** @deprecated Use `usePluginJobCenter()` for the plugin job-center controller. */
1235
+ function useJobsStatusTray(options = {}) {
1236
+ return usePluginJobCenter(options);
1237
+ }
739
1238
  //#endregion
740
1239
  //#region src/composables/useDoseCalculator.ts
741
1240
  var VOLUME_UNITS = [
@@ -983,18 +1482,153 @@ function useDoseCalculator() {
983
1482
  };
984
1483
  }
985
1484
  //#endregion
1485
+ //#region src/composables/useListReorder.ts
1486
+ /** Inline style for a visually hidden `aria-live` region (no global utility class in the SDK). */
1487
+ var REORDER_ANNOUNCER_STYLE = Object.freeze({
1488
+ position: "absolute",
1489
+ width: "1px",
1490
+ height: "1px",
1491
+ padding: "0",
1492
+ margin: "-1px",
1493
+ overflow: "hidden",
1494
+ clip: "rect(0, 0, 0, 0)",
1495
+ whiteSpace: "nowrap",
1496
+ border: "0"
1497
+ });
1498
+ /**
1499
+ * Screen-reader announcements for drag/keyboard move interactions. Bind
1500
+ * `announcement` to a `role="status" aria-live="polite"` element styled with
1501
+ * `announcerStyle`.
1502
+ */
1503
+ function useReorderAnnouncer() {
1504
+ const announcement = ref("");
1505
+ function announce(message) {
1506
+ if (announcement.value === message) announcement.value = "";
1507
+ nextTick(() => {
1508
+ announcement.value = message;
1509
+ });
1510
+ }
1511
+ return {
1512
+ announcement,
1513
+ announce,
1514
+ announcerStyle: REORDER_ANNOUNCER_STYLE
1515
+ };
1516
+ }
1517
+ /** Pure helper: a copy of `items` with the entry at `from` moved to `to`. */
1518
+ function reorderList(items, from, to) {
1519
+ const next = [...items];
1520
+ const [moved] = next.splice(from, 1);
1521
+ next.splice(to, 0, moved);
1522
+ return next;
1523
+ }
1524
+ /**
1525
+ * Keyboard-accessible list reordering shared by the SDK's drag-to-reorder lists.
1526
+ * It owns the HTML5 drag state, the drop-index maths, and an Alt+Arrow keyboard
1527
+ * alternative that announces every move through a polite live region — so a
1528
+ * reorder never depends on a pointer.
1529
+ */
1530
+ function useListReorder(options) {
1531
+ const draggingId = ref(null);
1532
+ const dragOverId = ref(null);
1533
+ const { announcement, announce, announcerStyle } = useReorderAnnouncer();
1534
+ const noun = options.itemNoun ?? "Item";
1535
+ function isDisabled() {
1536
+ return options.disabled?.() === true;
1537
+ }
1538
+ function idFor(item, index) {
1539
+ return options.getId(item, index);
1540
+ }
1541
+ /** Index within `items()`, resolved by id so filtered/sorted renders still map back. */
1542
+ function indexOf(item, renderIndex) {
1543
+ const id = idFor(item, renderIndex);
1544
+ const found = options.items().findIndex((candidate, index) => idFor(candidate, index) === id);
1545
+ return found === -1 ? renderIndex : found;
1546
+ }
1547
+ function labelFor(index) {
1548
+ const item = options.items()[index];
1549
+ if (item === void 0) return noun;
1550
+ return options.getLabel?.(item, index) ?? idFor(item, index);
1551
+ }
1552
+ function clearDragState() {
1553
+ draggingId.value = null;
1554
+ dragOverId.value = null;
1555
+ }
1556
+ function move(fromIndex, toIndex, source = "keyboard") {
1557
+ if (isDisabled()) return;
1558
+ const items = options.items();
1559
+ if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 || fromIndex >= items.length || toIndex >= items.length) return;
1560
+ const movedId = idFor(items[fromIndex], fromIndex);
1561
+ const label = labelFor(fromIndex);
1562
+ options.onReorder({
1563
+ fromIndex,
1564
+ toIndex,
1565
+ items: reorderList(items, fromIndex, toIndex),
1566
+ source
1567
+ });
1568
+ announce(`${label} moved to position ${toIndex + 1} of ${items.length}.`);
1569
+ nextTick(() => options.getElement?.(movedId)?.focus());
1570
+ }
1571
+ function onDragStart(event, item, index) {
1572
+ if (isDisabled()) return;
1573
+ const id = idFor(item, index);
1574
+ draggingId.value = id;
1575
+ if (event.dataTransfer) {
1576
+ event.dataTransfer.effectAllowed = "move";
1577
+ event.dataTransfer.setData("text/plain", id);
1578
+ }
1579
+ }
1580
+ function onDragOver(event, item, index) {
1581
+ if (isDisabled() || draggingId.value === null) return;
1582
+ event.preventDefault();
1583
+ if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
1584
+ const id = idFor(item, index);
1585
+ dragOverId.value = id === draggingId.value ? null : id;
1586
+ }
1587
+ function onDragLeave() {
1588
+ dragOverId.value = null;
1589
+ }
1590
+ function onDrop(event, item, index) {
1591
+ if (isDisabled() || draggingId.value === null) {
1592
+ clearDragState();
1593
+ return;
1594
+ }
1595
+ event.preventDefault();
1596
+ const fromIndex = options.items().findIndex((candidate, i) => idFor(candidate, i) === draggingId.value);
1597
+ const toIndex = indexOf(item, index);
1598
+ clearDragState();
1599
+ if (fromIndex === -1) return;
1600
+ move(fromIndex, toIndex, "drag");
1601
+ }
1602
+ function onDragEnd() {
1603
+ clearDragState();
1604
+ }
1605
+ function onKeydown(event, item, index) {
1606
+ if (isDisabled() || !event.altKey) return;
1607
+ const direction = event.key === "ArrowUp" || event.key === "ArrowLeft" ? -1 : event.key === "ArrowDown" || event.key === "ArrowRight" ? 1 : 0;
1608
+ if (direction === 0) return;
1609
+ event.preventDefault();
1610
+ const fromIndex = indexOf(item, index);
1611
+ move(fromIndex, fromIndex + direction, "keyboard");
1612
+ }
1613
+ return {
1614
+ draggingId,
1615
+ dragOverId,
1616
+ isDragging: (id) => draggingId.value === id,
1617
+ isDragOver: (id) => dragOverId.value === id,
1618
+ announcement,
1619
+ announcerStyle,
1620
+ announce,
1621
+ onDragStart,
1622
+ onDragOver,
1623
+ onDragLeave,
1624
+ onDrop,
1625
+ onDragEnd,
1626
+ onKeydown,
1627
+ move
1628
+ };
1629
+ }
1630
+ //#endregion
986
1631
  //#region src/composables/useWellPlateEditor.ts
987
- var DEFAULT_PALETTE = [
988
- "#3B82F6",
989
- "#10B981",
990
- "#EF4444",
991
- "#F59E0B",
992
- "#8B5CF6",
993
- "#F97316",
994
- "#06B6D4",
995
- "#14B8A6",
996
- "#6B7280"
997
- ];
998
1632
  var MAX_HISTORY = 50;
999
1633
  function createEmptyPlate(name, format, id) {
1000
1634
  return {
@@ -1078,7 +1712,7 @@ function useWellPlateEditor(initialState, options = {}) {
1078
1712
  const sample = {
1079
1713
  id: generateSampleId(),
1080
1714
  name,
1081
- color: color || DEFAULT_PALETTE[internalState.value.samples.length % DEFAULT_PALETTE.length],
1715
+ color: color || SAMPLE_COLOR_SCALE[internalState.value.samples.length % SAMPLE_COLOR_SCALE.length],
1082
1716
  count: 0
1083
1717
  };
1084
1718
  internalState.value.samples.push(sample);
@@ -1186,7 +1820,7 @@ function useWellPlateEditor(initialState, options = {}) {
1186
1820
  if (sampleId && !sampleMap.has(sampleId)) sampleMap.set(sampleId, {
1187
1821
  id: sampleId,
1188
1822
  name: sampleName || sampleId,
1189
- color: DEFAULT_PALETTE[sampleMap.size % DEFAULT_PALETTE.length],
1823
+ color: SAMPLE_COLOR_SCALE[sampleMap.size % SAMPLE_COLOR_SCALE.length],
1190
1824
  count: 0
1191
1825
  });
1192
1826
  const plate = plateMap.get(plateName);
@@ -2179,9 +2813,6 @@ function splitByTokenLength(classes, tokenized) {
2179
2813
  }
2180
2814
  //#endregion
2181
2815
  //#region src/composables/experimentDesignData.ts
2182
- function isRecord(value) {
2183
- return value !== null && typeof value === "object" && !Array.isArray(value);
2184
- }
2185
2816
  function isPlatformDesignDataResponse(value) {
2186
2817
  return "data" in value && ("experiment_id" in value || "plugin_id" in value || "schema_version" in value || "updated_at" in value);
2187
2818
  }
@@ -2895,7 +3526,7 @@ function useFileImport(options = {}) {
2895
3526
  result.value = parsed;
2896
3527
  return parsed;
2897
3528
  } catch (err) {
2898
- error.value = readErrorMessage(err);
3529
+ error.value = readErrorMessage(err, "Failed to import file.");
2899
3530
  throw err;
2900
3531
  } finally {
2901
3532
  loading.value = false;
@@ -3005,108 +3636,6 @@ function formatBytes(bytes) {
3005
3636
  if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
3006
3637
  return `${Math.round(bytes / (1024 * 1024))} MB`;
3007
3638
  }
3008
- function readErrorMessage(value) {
3009
- if (value instanceof Error && value.message) return value.message;
3010
- if (typeof value === "string" && value.trim()) return value;
3011
- return "Failed to import file.";
3012
- }
3013
- //#endregion
3014
- //#region src/utils/color.ts
3015
- var HEX_RE = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i;
3016
- function clamp(n, min, max) {
3017
- return Math.min(max, Math.max(min, n));
3018
- }
3019
- function hexToHsl(hex) {
3020
- const match = HEX_RE.exec(hex.trim());
3021
- if (!match) return {
3022
- h: 0,
3023
- s: 0,
3024
- l: 50
3025
- };
3026
- let raw = match[1];
3027
- if (raw.length === 3) raw = raw.split("").map((c) => c + c).join("");
3028
- const r = parseInt(raw.slice(0, 2), 16) / 255;
3029
- const g = parseInt(raw.slice(2, 4), 16) / 255;
3030
- const b = parseInt(raw.slice(4, 6), 16) / 255;
3031
- const max = Math.max(r, g, b);
3032
- const min = Math.min(r, g, b);
3033
- const delta = max - min;
3034
- let h = 0;
3035
- if (delta !== 0) if (max === r) h = (g - b) / delta % 6;
3036
- else if (max === g) h = (b - r) / delta + 2;
3037
- else h = (r - g) / delta + 4;
3038
- h = (h * 60 + 360) % 360;
3039
- const l = (max + min) / 2;
3040
- const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
3041
- return {
3042
- h,
3043
- s: s * 100,
3044
- l: l * 100
3045
- };
3046
- }
3047
- function hslToHex(h, s, l) {
3048
- const sNorm = clamp(s, 0, 100) / 100;
3049
- const lNorm = clamp(l, 0, 100) / 100;
3050
- const hNorm = (h % 360 + 360) % 360;
3051
- const c = (1 - Math.abs(2 * lNorm - 1)) * sNorm;
3052
- const x = c * (1 - Math.abs(hNorm / 60 % 2 - 1));
3053
- const m = lNorm - c / 2;
3054
- let r = 0, g = 0, b = 0;
3055
- if (hNorm < 60) [r, g, b] = [
3056
- c,
3057
- x,
3058
- 0
3059
- ];
3060
- else if (hNorm < 120) [r, g, b] = [
3061
- x,
3062
- c,
3063
- 0
3064
- ];
3065
- else if (hNorm < 180) [r, g, b] = [
3066
- 0,
3067
- c,
3068
- x
3069
- ];
3070
- else if (hNorm < 240) [r, g, b] = [
3071
- 0,
3072
- x,
3073
- c
3074
- ];
3075
- else if (hNorm < 300) [r, g, b] = [
3076
- x,
3077
- 0,
3078
- c
3079
- ];
3080
- else [r, g, b] = [
3081
- c,
3082
- 0,
3083
- x
3084
- ];
3085
- const toHex = (n) => Math.round((n + m) * 255).toString(16).padStart(2, "0");
3086
- return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
3087
- }
3088
- /**
3089
- * Derive a same-hue-family shade of `parentHex` for child at `index` of `total`.
3090
- *
3091
- * Lightness is distributed evenly within [35, 75] so all children remain
3092
- * readable on light/dark backgrounds. Hue and saturation are inherited from
3093
- * the parent. With `total <= 1`, returns the parent unchanged.
3094
- *
3095
- * Children are ordered light → dark by index, so index 0 is the brightest
3096
- * sibling and index `total - 1` is the darkest.
3097
- */
3098
- function deriveShade(parentHex, index, total) {
3099
- if (total <= 1) return parentHex;
3100
- const { h, s, l } = hexToHsl(parentHex);
3101
- const L_MIN = 35;
3102
- const L_MAX = 75;
3103
- const spread = Math.min(L_MAX - L_MIN, 40);
3104
- const center = clamp(l, L_MIN + spread / 2, L_MAX - spread / 2);
3105
- const top = center + spread / 2;
3106
- const bot = center - spread / 2;
3107
- const childL = top - (total === 1 ? .5 : index / (total - 1)) * (top - bot);
3108
- return hslToHex(h, clamp(s, 35, 100), childL);
3109
- }
3110
3639
  //#endregion
3111
3640
  //#region src/utils/sampleGroupPath.ts
3112
3641
  /** Split a group name into normalized hierarchy segments. */
@@ -3566,6 +4095,30 @@ function useScheduleDrag(options) {
3566
4095
  else if ((state.type === "resize-top" || state.type === "resize-bottom") && state.event && options.onResizeComplete) options.onResizeComplete(state.event, g.start, g.end);
3567
4096
  cleanup();
3568
4097
  }
4098
+ /**
4099
+ * Keyboard alternative to the pointer drag: shifts an event by one slot
4100
+ * (`mode: 'move'`) or grows/shrinks its end by one slot (`mode: 'resize'`),
4101
+ * staying inside the visible day. Returns whether the nudge was applied.
4102
+ */
4103
+ function nudgeEvent(event, direction, mode = "move") {
4104
+ if (options.readonly.value) return false;
4105
+ if (mode === "move" && event.draggable === false) return false;
4106
+ if (mode === "resize" && event.resizable === false) return false;
4107
+ const slotMin = options.slotDuration.value;
4108
+ const dayStart = options.dayStartHour.value * 60;
4109
+ const dayEnd = options.dayEndHour.value * 60;
4110
+ const start = dateToMinutes(new Date(event.start));
4111
+ const end = dateToMinutes(new Date(event.end));
4112
+ const nextStart = mode === "move" ? start + direction * slotMin : start;
4113
+ const nextEnd = mode === "move" ? end + direction * slotMin : Math.max(start + slotMin, end + direction * slotMin);
4114
+ if (nextStart < dayStart || nextEnd > dayEnd || nextEnd <= nextStart) return false;
4115
+ const base = new Date(event.start);
4116
+ const newStart = minutesToDate(base, nextStart);
4117
+ const newEnd = minutesToDate(base, nextEnd);
4118
+ if (mode === "move") options.onMoveComplete?.(event, newStart, newEnd);
4119
+ else options.onResizeComplete?.(event, newStart, newEnd);
4120
+ return true;
4121
+ }
3569
4122
  function addListeners() {
3570
4123
  document.addEventListener("pointermove", onPointerMove);
3571
4124
  document.addEventListener("pointerup", onPointerUp);
@@ -3583,7 +4136,8 @@ function useScheduleDrag(options) {
3583
4136
  ghost,
3584
4137
  startCreate,
3585
4138
  startMove,
3586
- startResize
4139
+ startResize,
4140
+ nudgeEvent
3587
4141
  };
3588
4142
  }
3589
4143
  function snapMinutes(minutes, step) {
@@ -3940,6 +4494,57 @@ function useBioTemplateComponents(target) {
3940
4494
  };
3941
4495
  }
3942
4496
  //#endregion
4497
+ //#region src/composables/flattenWorkspaceBindings.ts
4498
+ /**
4499
+ * Pick the shared template-component binding keys off a resolved bio-template workspace
4500
+ * (or its `components` source directly), so composables that build on `useBioTemplateWorkspace`
4501
+ * spread one object instead of re-listing all 9 keys by hand.
4502
+ *
4503
+ * @example Recommended
4504
+ * ```ts
4505
+ * const bindings = { renderer, ...flattenWorkspaceComponentBindings(components) }
4506
+ * ```
4507
+ */
4508
+ function flattenWorkspaceComponentBindings(source) {
4509
+ return {
4510
+ componentBindings: source.componentBindings,
4511
+ componentBindingsById: source.componentBindingsById,
4512
+ componentImports: source.componentImports,
4513
+ componentProps: source.componentProps,
4514
+ componentPropsById: source.componentPropsById,
4515
+ componentPropsByComponent: source.componentPropsByComponent,
4516
+ componentSnippets: source.componentSnippets,
4517
+ componentUsage: source.componentUsage,
4518
+ getComponentProps: source.getComponentProps
4519
+ };
4520
+ }
4521
+ /**
4522
+ * `computed()`-lifted version of {@link flattenWorkspaceComponentBindings} for composables
4523
+ * that hold their resolved workspace behind a `ComputedRef` (e.g. a regenerated workspace
4524
+ * swapped in via `shallowRef`/`effectScope`), so each key stays independently reactive
4525
+ * instead of forcing consumers to unwrap the whole workspace to read one field.
4526
+ * `getComponentProps` stays a plain callable function, matching the un-lifted composables.
4527
+ *
4528
+ * @example Recommended
4529
+ * ```ts
4530
+ * const workspace = computed(() => workspaceRef.value)
4531
+ * const { componentBindings, componentProps, getComponentProps } = liftWorkspaceComponentBindings(workspace)
4532
+ * ```
4533
+ */
4534
+ function liftWorkspaceComponentBindings(workspace) {
4535
+ return {
4536
+ componentBindings: computed(() => workspace.value.componentBindings),
4537
+ componentBindingsById: computed(() => workspace.value.componentBindingsById),
4538
+ componentImports: computed(() => workspace.value.componentImports),
4539
+ componentProps: computed(() => workspace.value.componentProps),
4540
+ componentPropsById: computed(() => workspace.value.componentPropsById),
4541
+ componentPropsByComponent: computed(() => workspace.value.componentPropsByComponent),
4542
+ componentSnippets: computed(() => workspace.value.componentSnippets),
4543
+ componentUsage: computed(() => workspace.value.componentUsage),
4544
+ getComponentProps: (component, options) => workspace.value.getComponentProps(component, options)
4545
+ };
4546
+ }
4547
+ //#endregion
3943
4548
  //#region src/composables/useBioTemplateWorkspace.ts
3944
4549
  /** One-stop wiring for BioTemplateRenderer, FormBuilder, AppSidebar, and template-aware SDK components. */
3945
4550
  function useBioTemplateWorkspace(target, options = {}) {
@@ -3947,21 +4552,25 @@ function useBioTemplateWorkspace(target, options = {}) {
3947
4552
  const workspace = useControlWorkspace(controls.controls, options);
3948
4553
  const components = useBioTemplateComponents(target);
3949
4554
  const renderer = isTemplateObjectTarget(target) ? { target } : null;
3950
- const bindings = {
3951
- renderer,
3952
- form: workspace.bindings.form,
3953
- sidebar: workspace.bindings.sidebar,
3954
- topBar: workspace.bindings.topBar,
3955
- topBarSettings: workspace.bindings.topBarSettings,
3956
- pillNav: workspace.bindings.pillNav,
4555
+ const componentBindings = flattenWorkspaceComponentBindings({
3957
4556
  componentBindings: components.bindings,
3958
4557
  componentBindingsById: components.componentBindingsById,
4558
+ componentImports: components.imports,
3959
4559
  componentProps: components.componentProps,
3960
4560
  componentPropsById: components.componentPropsById,
3961
4561
  componentPropsByComponent: components.componentPropsByComponent,
3962
4562
  componentSnippets: components.snippets,
3963
4563
  componentUsage: components.usage,
3964
4564
  getComponentProps: components.getComponentProps
4565
+ });
4566
+ const bindings = {
4567
+ renderer,
4568
+ form: workspace.bindings.form,
4569
+ sidebar: workspace.bindings.sidebar,
4570
+ topBar: workspace.bindings.topBar,
4571
+ topBarSettings: workspace.bindings.topBarSettings,
4572
+ pillNav: workspace.bindings.pillNav,
4573
+ ...componentBindings
3965
4574
  };
3966
4575
  return {
3967
4576
  target,
@@ -3978,15 +4587,7 @@ function useBioTemplateWorkspace(target, options = {}) {
3978
4587
  topBarSettings: workspace.topBarSettings,
3979
4588
  bindings,
3980
4589
  renderer,
3981
- componentBindings: components.bindings,
3982
- componentBindingsById: components.componentBindingsById,
3983
- componentImports: components.imports,
3984
- componentProps: components.componentProps,
3985
- componentPropsById: components.componentPropsById,
3986
- componentPropsByComponent: components.componentPropsByComponent,
3987
- componentSnippets: components.snippets,
3988
- componentUsage: components.usage,
3989
- getComponentProps: components.getComponentProps
4590
+ ...componentBindings
3990
4591
  };
3991
4592
  }
3992
4593
  function isTemplateObjectTarget(target) {
@@ -4027,14 +4628,7 @@ function useBioTemplatePackWorkspace(packId, options = {}) {
4027
4628
  const pillNav = computed(() => workspace.value.pillNav);
4028
4629
  const topBarSettings = computed(() => workspace.value.topBarSettings);
4029
4630
  const bindings = computed(() => workspace.value.bindings);
4030
- const componentBindings = computed(() => workspace.value.componentBindings);
4031
- const componentBindingsById = computed(() => workspace.value.componentBindingsById);
4032
- const componentImports = computed(() => workspace.value.componentImports);
4033
- const componentProps = computed(() => workspace.value.componentProps);
4034
- const componentPropsById = computed(() => workspace.value.componentPropsById);
4035
- const componentPropsByComponent = computed(() => workspace.value.componentPropsByComponent);
4036
- const componentSnippets = computed(() => workspace.value.componentSnippets);
4037
- const componentUsage = computed(() => workspace.value.componentUsage);
4631
+ const { componentBindings, componentBindingsById, componentImports, componentProps, componentPropsById, componentPropsByComponent, componentSnippets, componentUsage, getComponentProps: getWorkspaceComponentProps } = liftWorkspaceComponentBindings(workspace);
4038
4632
  watch(collection, (value) => {
4039
4633
  workspaceRef.value = createWorkspace(value);
4040
4634
  });
@@ -4077,7 +4671,7 @@ function useBioTemplatePackWorkspace(packId, options = {}) {
4077
4671
  componentPropsByComponent,
4078
4672
  componentSnippets,
4079
4673
  componentUsage,
4080
- getComponentProps: (component, options) => workspace.value.getComponentProps(component, options),
4674
+ getComponentProps: getWorkspaceComponentProps,
4081
4675
  applyCollection,
4082
4676
  resetToDefaultCollection,
4083
4677
  save: templateCollection.save,
@@ -4108,14 +4702,7 @@ function useBioTemplatePresetWorkspace(presetId, options = {}) {
4108
4702
  const collection = templateCollection.templateCollection;
4109
4703
  const workspace = computed(() => useBioTemplateWorkspace(collection.value, resolvedControlOptions));
4110
4704
  const renderer = computed(() => ({ target: collection.value }));
4111
- const componentBindings = computed(() => workspace.value.componentBindings);
4112
- const componentBindingsById = computed(() => workspace.value.componentBindingsById);
4113
- const componentImports = computed(() => workspace.value.componentImports);
4114
- const componentProps = computed(() => workspace.value.componentProps);
4115
- const componentPropsById = computed(() => workspace.value.componentPropsById);
4116
- const componentPropsByComponent = computed(() => workspace.value.componentPropsByComponent);
4117
- const componentSnippets = computed(() => workspace.value.componentSnippets);
4118
- const componentUsage = computed(() => workspace.value.componentUsage);
4705
+ const { componentBindings, componentBindingsById, componentImports, componentProps, componentPropsById, componentPropsByComponent, componentSnippets, componentUsage, getComponentProps } = liftWorkspaceComponentBindings(workspace);
4119
4706
  const form = reactive({
4120
4707
  ...controls.form,
4121
4708
  modelValue: controlValues.value,
@@ -4148,9 +4735,6 @@ function useBioTemplatePresetWorkspace(presetId, options = {}) {
4148
4735
  onPillSelect: pillNav.onSelect,
4149
4736
  ...topBarSettings
4150
4737
  }));
4151
- function getComponentProps(component, options) {
4152
- return workspace.value.getComponentProps(component, options);
4153
- }
4154
4738
  const bindings = {
4155
4739
  renderer,
4156
4740
  form,
@@ -4276,36 +4860,6 @@ var SLOT_CYCLE = [
4276
4860
  "B",
4277
4861
  "Y"
4278
4862
  ];
4279
- var PLATE_CONFIGS = {
4280
- 6: {
4281
- rows: 2,
4282
- cols: 3
4283
- },
4284
- 12: {
4285
- rows: 3,
4286
- cols: 4
4287
- },
4288
- 24: {
4289
- rows: 4,
4290
- cols: 6
4291
- },
4292
- 48: {
4293
- rows: 6,
4294
- cols: 8
4295
- },
4296
- 54: {
4297
- rows: 6,
4298
- cols: 9
4299
- },
4300
- 96: {
4301
- rows: 8,
4302
- cols: 12
4303
- },
4304
- 384: {
4305
- rows: 16,
4306
- cols: 24
4307
- }
4308
- };
4309
4863
  var globalIdCounter = 0;
4310
4864
  function generateId() {
4311
4865
  globalIdCounter++;
@@ -4389,7 +4943,7 @@ function useRackEditor(initialRacks, options) {
4389
4943
  if (!config) return;
4390
4944
  let counter = 1;
4391
4945
  for (let row = 0; row < config.rows; row++) for (let col = 0; col < config.cols; col++) {
4392
- const wellId = `${String.fromCharCode(65 + row)}${col + 1}`;
4946
+ const wellId = `${rowLabel(row)}${col + 1}`;
4393
4947
  if (!rack.wells[wellId]?.sampleType) {
4394
4948
  const paddedNum = String(counter).padStart(3, "0");
4395
4949
  rack.wells[wellId] = {
@@ -5053,6 +5607,6 @@ function useProtocolTemplates() {
5053
5607
  };
5054
5608
  }
5055
5609
  //#endregion
5056
- export { candidateMatchesSearch as $, useFileImport as A, useDoseCalculator as B, getSampleGroupParentPath as C, detectDelimiter as D, hslToHex as E, extractSampleNamesFromDesignData as F, APP_EXPERIMENT_KEY as G, usePluginJobCenter as H, extractSampleOptionsFromDesignData as I, useRuntimeAlignment as J, useAppExperiment as K, unwrapExperimentDesignData as L, extractSamplesFromDesignData as M, useAutoGroup as N, parseDelimitedText as O, parseCSV as P, useToast as Q, DEFAULT_COLORS as R, getSampleGroupMajorPrefix as S, hexToHsl as T, DEFAULT_MOBILE_VIEWPORT_QUERY as U, combinePluginJobSources as V, useMobileSupportGate as W, usePresenceHeartbeat as X, getPresenceClientId as Y, useTheme as Z, useScheduleDrag as _, useReagentSeries as a, useExpansionSet as b, useBioTemplatePresetWorkspace as c, getBioTemplateComponentProps as d, normalizeSearchQuery as et, toBioTemplateComponentPropsByComponent as f, useExperimentSave as g, useTemplateCollection as h, generateDilutionSeries as i, validateImportFile as j, readFileAsText as k, useBioTemplatePackWorkspace as l, useBioTemplateControls as m, DEFAULT_PRESETS as n, compareSortValues as nt, useGroupAssignment as o, useBioTemplateComponents as p, getInjectedFrontendRevision as q, DEFAULT_UNITS as r, useSortedItems as rt, useRackEditor as s, useProtocolTemplates as t, useTextSearch as tt, useBioTemplateWorkspace as u, useExperimentSamples as v, deriveShade as w, useSampleGroups as x, useExperimentData as y, useWellPlateEditor as z };
5610
+ export { usePresenceHeartbeat as $, useAutoGroup as A, useReorderAnnouncer as B, getSampleGroupParentPath as C, useFileImport as D, readFileAsText as E, DEFAULT_COLORS as F, usePluginWorkspace as G, combinePluginJobSources as H, useWellPlateEditor as I, APP_EXPERIMENT_KEY as J, DEFAULT_MOBILE_VIEWPORT_QUERY as K, REORDER_ANNOUNCER_STYLE as L, extractSampleNamesFromDesignData as M, extractSampleOptionsFromDesignData as N, validateImportFile as O, unwrapExperimentDesignData as P, getPresenceClientId as Q, reorderList as R, getSampleGroupMajorPrefix as S, parseDelimitedText as T, useJobsStatusTray as U, useDoseCalculator as V, usePluginJobCenter as W, getInjectedFrontendRevision as X, useAppExperiment as Y, useRuntimeAlignment as Z, useScheduleDrag as _, useReagentSeries as a, compareSortValues as at, useExpansionSet as b, useBioTemplatePresetWorkspace as c, useRovingFocus as ct, getBioTemplateComponentProps as d, useTheme as et, toBioTemplateComponentPropsByComponent as f, useExperimentSave as g, useTemplateCollection as h, generateDilutionSeries as i, useTextSearch as it, parseCSV as j, extractSamplesFromDesignData as k, useBioTemplatePackWorkspace as l, useBioTemplateControls as m, DEFAULT_PRESETS as n, candidateMatchesSearch as nt, useGroupAssignment as o, useSortedItems as ot, useBioTemplateComponents as p, useMobileSupportGate as q, DEFAULT_UNITS as r, normalizeSearchQuery as rt, useRackEditor as s, useMenuKeyboard as st, useProtocolTemplates as t, useToast as tt, useBioTemplateWorkspace as u, useExperimentSamples as v, detectDelimiter as w, useSampleGroups as x, useExperimentData as y, useListReorder as z };
5057
5611
 
5058
- //# sourceMappingURL=useProtocolTemplates-CS_SQl6U.js.map
5612
+ //# sourceMappingURL=useProtocolTemplates-BmX_iVBR.js.map