@morscherlab/mint-sdk 1.0.0-beta.2 → 1.0.0-beta.4

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 (427) hide show
  1. package/README.md +225 -6
  2. package/dist/__tests__/components/ActionItem.test.d.ts +1 -0
  3. package/dist/__tests__/components/AppAvatarMenu.test.d.ts +1 -0
  4. package/dist/__tests__/components/AppPageSelector.test.d.ts +1 -0
  5. package/dist/__tests__/components/AppPillNav.test.d.ts +1 -0
  6. package/dist/__tests__/components/AppPluginSwitcher.test.d.ts +1 -0
  7. package/dist/__tests__/components/AppToastContainer.test.d.ts +1 -0
  8. package/dist/__tests__/components/BaseRadioGroup.test.d.ts +1 -0
  9. package/dist/__tests__/components/BaseSelect.test.d.ts +1 -0
  10. package/dist/__tests__/components/BaseTabs.test.d.ts +1 -0
  11. package/dist/__tests__/components/BatchProgressList.test.d.ts +1 -0
  12. package/dist/__tests__/components/BioTemplateExperimentWorkspaceView.test.d.ts +1 -0
  13. package/dist/__tests__/components/BioTemplatePackWorkspaceView.test.d.ts +1 -0
  14. package/dist/__tests__/components/BioTemplatePresetWorkspaceView.test.d.ts +1 -0
  15. package/dist/__tests__/components/BioTemplateRenderer.test.d.ts +1 -0
  16. package/dist/__tests__/components/Breadcrumb.test.d.ts +1 -0
  17. package/dist/__tests__/components/CalendarGridPanel.test.d.ts +1 -0
  18. package/dist/__tests__/components/ComponentBindingRenderer.test.d.ts +1 -0
  19. package/dist/__tests__/components/ConcentrationInput.test.d.ts +1 -0
  20. package/dist/__tests__/components/ControlWorkspaceView.test.d.ts +1 -0
  21. package/dist/__tests__/components/DatePicker.test.d.ts +1 -0
  22. package/dist/__tests__/components/DateTimePicker.test.d.ts +1 -0
  23. package/dist/__tests__/components/DoseDesignWorkspaceView.test.d.ts +1 -0
  24. package/dist/__tests__/components/EmptyState.test.d.ts +1 -0
  25. package/dist/__tests__/components/ExperimentPopover.test.d.ts +1 -0
  26. package/dist/__tests__/components/FormBuilder.test.d.ts +1 -0
  27. package/dist/__tests__/components/GroupAssigner.test.d.ts +1 -0
  28. package/dist/__tests__/components/MultiSelect.test.d.ts +1 -0
  29. package/dist/__tests__/components/PluginWorkspaceView.test.d.ts +1 -0
  30. package/dist/__tests__/components/ProtocolStepEditor.test.d.ts +1 -0
  31. package/dist/__tests__/components/ReagentList.test.d.ts +1 -0
  32. package/dist/__tests__/components/SampleHierarchyTree.test.d.ts +1 -0
  33. package/dist/__tests__/components/SampleSelector.test.d.ts +1 -0
  34. package/dist/__tests__/components/SegmentedControl.test.d.ts +1 -0
  35. package/dist/__tests__/components/SettingsModal.test.d.ts +1 -0
  36. package/dist/__tests__/components/TagsInput.test.d.ts +1 -0
  37. package/dist/__tests__/components/ThemeToggle.test.d.ts +1 -0
  38. package/dist/__tests__/components/TimePicker.test.d.ts +1 -0
  39. package/dist/__tests__/composables/experiment-utils.test.d.ts +1 -0
  40. package/dist/__tests__/composables/useApi.test.d.ts +1 -0
  41. package/dist/__tests__/composables/useBioTemplatePackWorkspace.test.d.ts +1 -0
  42. package/dist/__tests__/composables/useBioTemplatePresetWorkspace.test.d.ts +1 -0
  43. package/dist/__tests__/composables/useBioTemplateWorkspace.test.d.ts +1 -0
  44. package/dist/__tests__/composables/useCalendarGrid.test.d.ts +1 -0
  45. package/dist/__tests__/composables/useControlSchema.test.d.ts +1 -0
  46. package/dist/__tests__/composables/useDebouncedWatch.test.d.ts +1 -0
  47. package/dist/__tests__/composables/useDropdownState.test.d.ts +1 -0
  48. package/dist/__tests__/composables/useEventListener.test.d.ts +1 -0
  49. package/dist/__tests__/composables/useExpansionSet.test.d.ts +1 -0
  50. package/dist/__tests__/composables/useExperimentData.test.d.ts +1 -0
  51. package/dist/__tests__/composables/useExperimentSelector.test.d.ts +1 -0
  52. package/dist/__tests__/composables/useGroupAssignment.test.d.ts +1 -0
  53. package/dist/__tests__/composables/useListSelection.test.d.ts +1 -0
  54. package/dist/__tests__/composables/usePluginClient.test.d.ts +1 -0
  55. package/dist/__tests__/composables/usePluginConfig.test.d.ts +1 -0
  56. package/dist/__tests__/composables/useRequestSyncState.test.d.ts +1 -0
  57. package/dist/__tests__/composables/useSampleGroups.test.d.ts +1 -0
  58. package/dist/__tests__/composables/useSelectionLimit.test.d.ts +1 -0
  59. package/dist/__tests__/composables/useSortedItems.test.d.ts +1 -0
  60. package/dist/__tests__/composables/useTemplateCollection.test.d.ts +1 -0
  61. package/dist/__tests__/composables/useTextSearch.test.d.ts +1 -0
  62. package/dist/__tests__/composables/useTheme.test.d.ts +1 -0
  63. package/dist/__tests__/composables/useTimeUtils.test.d.ts +1 -0
  64. package/dist/__tests__/docs/frontendDocsCatalog.test.d.ts +1 -0
  65. package/dist/__tests__/templates/templates.test.d.ts +1 -0
  66. package/dist/{auth-DsI0rQ7_.js → auth-QQj2kkze.js} +12 -5
  67. package/dist/auth-QQj2kkze.js.map +1 -0
  68. package/dist/components/AppAvatarMenu.vue.d.ts +2 -7
  69. package/dist/components/AppContainer.vue.d.ts +1 -1
  70. package/dist/components/AppLayout.vue.d.ts +20 -1
  71. package/dist/components/AppSidebar.vue.d.ts +111 -6
  72. package/dist/components/AppTopBar.vue.d.ts +35 -22
  73. package/dist/components/BaseButton.vue.d.ts +1 -1
  74. package/dist/components/BaseCheckbox.vue.d.ts +1 -1
  75. package/dist/components/BaseInput.vue.d.ts +2 -2
  76. package/dist/components/BasePill.vue.d.ts +2 -2
  77. package/dist/components/BaseRadioGroup.vue.d.ts +3 -3
  78. package/dist/components/BaseSelect.vue.d.ts +3 -3
  79. package/dist/components/BaseTabs.vue.d.ts +2 -2
  80. package/dist/components/BaseTextarea.vue.d.ts +1 -1
  81. package/dist/components/BaseToggle.vue.d.ts +1 -1
  82. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +119 -0
  83. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +93 -0
  84. package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +87 -0
  85. package/dist/components/BioTemplateRenderer.vue.d.ts +29 -0
  86. package/dist/components/Breadcrumb.vue.d.ts +2 -2
  87. package/dist/components/Calendar.vue.d.ts +1 -1
  88. package/dist/components/CollapsibleCard.vue.d.ts +1 -1
  89. package/dist/components/ComponentBindingRenderer.vue.d.ts +44 -0
  90. package/dist/components/ConcentrationInput.vue.d.ts +2 -2
  91. package/dist/components/ConfirmDialog.vue.d.ts +2 -2
  92. package/dist/components/ControlWorkspaceView.vue.d.ts +147 -0
  93. package/dist/components/DatePicker.vue.d.ts +1 -1
  94. package/dist/components/DateTimePicker.vue.d.ts +3 -3
  95. package/dist/components/Divider.vue.d.ts +1 -1
  96. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +149 -0
  97. package/dist/components/DropdownButton.vue.d.ts +3 -3
  98. package/dist/components/EmptyState.vue.d.ts +1 -2
  99. package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
  100. package/dist/components/ExperimentTimeline.vue.d.ts +2 -2
  101. package/dist/components/FileUploader.vue.d.ts +1 -1
  102. package/dist/components/FitPanel.vue.d.ts +1 -1
  103. package/dist/components/FormActions.vue.d.ts +4 -4
  104. package/dist/components/FormBuilder.vue.d.ts +31 -17
  105. package/dist/components/FormulaInput.vue.d.ts +2 -2
  106. package/dist/components/MoleculeInput.vue.d.ts +2 -2
  107. package/dist/components/MultiSelect.vue.d.ts +3 -3
  108. package/dist/components/NumberInput.vue.d.ts +1 -1
  109. package/dist/components/PlateMapEditor.vue.d.ts +1 -1
  110. package/dist/components/PluginWorkspaceView.vue.d.ts +310 -0
  111. package/dist/components/ProgressBar.vue.d.ts +1 -1
  112. package/dist/components/ProtocolStepEditor.vue.d.ts +3 -1
  113. package/dist/components/RackEditor.vue.d.ts +2 -2
  114. package/dist/components/SampleLegend.vue.d.ts +2 -2
  115. package/dist/components/ScheduleCalendar.vue.d.ts +2 -2
  116. package/dist/components/SegmentedControl.vue.d.ts +2 -2
  117. package/dist/components/SequenceInput.vue.d.ts +3 -3
  118. package/dist/components/SettingsModal.vue.d.ts +14 -6
  119. package/dist/components/StatusIndicator.vue.d.ts +1 -1
  120. package/dist/components/TagsInput.vue.d.ts +3 -2
  121. package/dist/components/TimePicker.vue.d.ts +3 -3
  122. package/dist/components/TimeRangeInput.vue.d.ts +1 -1
  123. package/dist/components/UnitInput.vue.d.ts +2 -2
  124. package/dist/components/WellPlate.vue.d.ts +6 -6
  125. package/dist/components/index.d.ts +9 -8
  126. package/dist/components/index.js +3 -3
  127. package/dist/components/{SettingsButton.vue.d.ts → internal/ActionItemInternal.vue.d.ts} +11 -9
  128. package/dist/components/{AppPageSelector.vue.d.ts → internal/AppPageSelectorInternal.vue.d.ts} +3 -6
  129. package/dist/components/{AppPillNav.vue.d.ts → internal/AppPillNavInternal.vue.d.ts} +4 -2
  130. package/dist/components/internal/CalendarGridPanelInternal.vue.d.ts +25 -0
  131. package/dist/components/{FormFieldRenderer.vue.d.ts → internal/FormFieldRendererInternal.vue.d.ts} +2 -2
  132. package/dist/components/{FormSection.vue.d.ts → internal/FormSectionRenderer.vue.d.ts} +7 -7
  133. package/dist/components/{WellEditPopup.vue.d.ts → internal/WellEditPopupInternal.vue.d.ts} +1 -1
  134. package/dist/{components-_XqPEhP9.js → components-BkGF4B4y.js} +9760 -8471
  135. package/dist/components-BkGF4B4y.js.map +1 -0
  136. package/dist/composables/experiment-utils.d.ts +8 -0
  137. package/dist/composables/index.d.ts +22 -5
  138. package/dist/composables/index.js +4 -3
  139. package/dist/composables/platformContextHelpers.d.ts +14 -0
  140. package/dist/composables/useAppExperiment.d.ts +31 -2
  141. package/dist/composables/useBioTemplateComponents.d.ts +22 -0
  142. package/dist/composables/useBioTemplateControls.d.ts +6 -0
  143. package/dist/composables/useBioTemplatePackWorkspace.d.ts +46 -0
  144. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +75 -0
  145. package/dist/composables/useBioTemplateWorkspace.d.ts +51 -0
  146. package/dist/composables/useCalendarGrid.d.ts +26 -0
  147. package/dist/composables/useControlSchema.d.ts +343 -0
  148. package/dist/composables/useDebouncedWatch.d.ts +20 -0
  149. package/dist/composables/useDropdownState.d.ts +19 -0
  150. package/dist/composables/useEventListener.d.ts +13 -0
  151. package/dist/composables/useExpansionSet.d.ts +21 -0
  152. package/dist/composables/useExperimentData.d.ts +10 -0
  153. package/dist/composables/useExperimentSave.d.ts +31 -2
  154. package/dist/composables/useExperimentSelector.d.ts +20 -0
  155. package/dist/composables/useForm.d.ts +2 -0
  156. package/dist/composables/useGroupAssignment.d.ts +31 -0
  157. package/dist/composables/useListSelection.d.ts +35 -0
  158. package/dist/composables/usePlatformContext.d.ts +21 -3
  159. package/dist/composables/usePluginClient.d.ts +112 -0
  160. package/dist/composables/usePluginConfig.d.ts +12 -0
  161. package/dist/composables/useRequestSyncState.d.ts +34 -0
  162. package/dist/composables/useSampleGroups.d.ts +32 -0
  163. package/dist/composables/useSelectionLimit.d.ts +17 -0
  164. package/dist/composables/useSortedItems.d.ts +32 -0
  165. package/dist/composables/useTemplateCollection.d.ts +58 -0
  166. package/dist/composables/useTextSearch.d.ts +18 -0
  167. package/dist/composables/useTimeUtils.d.ts +8 -0
  168. package/dist/{composables-tiZqLu1M.js → composables-CHsME9H1.js} +240 -146
  169. package/dist/composables-CHsME9H1.js.map +1 -0
  170. package/dist/index.d.ts +6 -4
  171. package/dist/index.js +6 -5
  172. package/dist/install.d.ts +7 -2
  173. package/dist/install.js +2 -2
  174. package/dist/install.js.map +1 -1
  175. package/dist/stores/index.js +1 -1
  176. package/dist/stores/settings.d.ts +4 -1
  177. package/dist/styles.css +4746 -5514
  178. package/dist/templates/adapters.d.ts +43 -0
  179. package/dist/templates/builders.d.ts +63 -0
  180. package/dist/templates/catalog.d.ts +188 -0
  181. package/dist/templates/componentBindings.d.ts +71 -0
  182. package/dist/templates/controlSchemas.d.ts +25 -0
  183. package/dist/templates/index.d.ts +15 -0
  184. package/dist/templates/index.js +2 -0
  185. package/dist/templates/lookup.d.ts +4 -0
  186. package/dist/templates/packs.d.ts +18 -0
  187. package/dist/templates/presets.d.ts +90 -0
  188. package/dist/templates/types.d.ts +531 -0
  189. package/dist/templates-B5jmTWuk.js +9388 -0
  190. package/dist/templates-B5jmTWuk.js.map +1 -0
  191. package/dist/types/components.d.ts +26 -23
  192. package/dist/types/form-builder.d.ts +6 -8
  193. package/dist/types/index.d.ts +2 -2
  194. package/dist/types/platform.d.ts +7 -1
  195. package/dist/useScheduleDrag-BgzpQT53.js +4414 -0
  196. package/dist/useScheduleDrag-BgzpQT53.js.map +1 -0
  197. package/dist/utils/formModelSync.d.ts +5 -0
  198. package/dist/utils/items.d.ts +8 -0
  199. package/dist/utils/options.d.ts +6 -0
  200. package/dist/utils/pluginIcon.d.ts +9 -0
  201. package/package.json +7 -2
  202. package/src/__tests__/components/ActionItem.test.ts +99 -0
  203. package/src/__tests__/components/AppAvatarMenu.test.ts +27 -0
  204. package/src/__tests__/components/AppLayout.test.ts +44 -0
  205. package/src/__tests__/components/AppPageSelector.test.ts +134 -0
  206. package/src/__tests__/components/AppPillNav.test.ts +125 -0
  207. package/src/__tests__/components/AppPluginSwitcher.test.ts +44 -0
  208. package/src/__tests__/components/AppSidebar.test.ts +496 -0
  209. package/src/__tests__/components/AppToastContainer.test.ts +37 -0
  210. package/src/__tests__/components/AppTopBar.test.ts +455 -9
  211. package/src/__tests__/components/BaseRadioGroup.test.ts +25 -0
  212. package/src/__tests__/components/BaseSelect.test.ts +21 -0
  213. package/src/__tests__/components/BaseTabs.test.ts +25 -0
  214. package/src/__tests__/components/BatchProgressList.test.ts +52 -0
  215. package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +159 -0
  216. package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +175 -0
  217. package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +306 -0
  218. package/src/__tests__/components/BioTemplateRenderer.test.ts +71 -0
  219. package/src/__tests__/components/Breadcrumb.test.ts +23 -0
  220. package/src/__tests__/components/CalendarGridPanel.test.ts +36 -0
  221. package/src/__tests__/components/ComponentBindingRenderer.test.ts +161 -0
  222. package/src/__tests__/components/ConcentrationInput.test.ts +45 -0
  223. package/src/__tests__/components/ControlWorkspaceView.test.ts +1102 -0
  224. package/src/__tests__/components/DataFrame.test.ts +11 -0
  225. package/src/__tests__/components/DatePicker.test.ts +45 -0
  226. package/src/__tests__/components/DateTimePicker.test.ts +48 -0
  227. package/src/__tests__/components/DoseDesignWorkspaceView.test.ts +185 -0
  228. package/src/__tests__/components/DropdownButton.test.ts +23 -0
  229. package/src/__tests__/components/EmptyState.test.ts +23 -0
  230. package/src/__tests__/components/ExperimentPopover.test.ts +56 -0
  231. package/src/__tests__/components/FormBuilder.test.ts +296 -0
  232. package/src/__tests__/components/GroupAssigner.test.ts +30 -0
  233. package/src/__tests__/components/MultiSelect.test.ts +48 -0
  234. package/src/__tests__/components/PluginWorkspaceView.test.ts +548 -0
  235. package/src/__tests__/components/ProtocolStepEditor.test.ts +33 -0
  236. package/src/__tests__/components/ReagentList.test.ts +82 -0
  237. package/src/__tests__/components/SampleHierarchyTree.test.ts +53 -0
  238. package/src/__tests__/components/SampleSelector.test.ts +60 -0
  239. package/src/__tests__/components/SegmentedControl.test.ts +24 -0
  240. package/src/__tests__/components/SettingsModal.test.ts +296 -0
  241. package/src/__tests__/components/TagsInput.test.ts +75 -0
  242. package/src/__tests__/components/ThemeToggle.test.ts +47 -0
  243. package/src/__tests__/components/TimePicker.test.ts +38 -0
  244. package/src/__tests__/composables/experiment-utils.test.ts +30 -0
  245. package/src/__tests__/composables/useApi.test.ts +30 -0
  246. package/src/__tests__/composables/useAppExperiment.test.ts +100 -1
  247. package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +125 -0
  248. package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +199 -0
  249. package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +104 -0
  250. package/src/__tests__/composables/useCalendarGrid.test.ts +38 -0
  251. package/src/__tests__/composables/useControlSchema.test.ts +1033 -0
  252. package/src/__tests__/composables/useDebouncedWatch.test.ts +93 -0
  253. package/src/__tests__/composables/useDropdownState.test.ts +95 -0
  254. package/src/__tests__/composables/useEventListener.test.ts +116 -0
  255. package/src/__tests__/composables/useExpansionSet.test.ts +62 -0
  256. package/src/__tests__/composables/useExperimentData.test.ts +4 -0
  257. package/src/__tests__/composables/useExperimentSave.test.ts +203 -8
  258. package/src/__tests__/composables/useExperimentSelector.test.ts +164 -0
  259. package/src/__tests__/composables/useForm.test.ts +58 -0
  260. package/src/__tests__/composables/useFormBuilder.test.ts +77 -0
  261. package/src/__tests__/composables/useGroupAssignment.test.ts +73 -0
  262. package/src/__tests__/composables/useListSelection.test.ts +66 -0
  263. package/src/__tests__/composables/usePluginClient.test.ts +541 -0
  264. package/src/__tests__/composables/usePluginConfig.test.ts +5 -0
  265. package/src/__tests__/composables/useRequestSyncState.test.ts +92 -0
  266. package/src/__tests__/composables/useSampleGroups.test.ts +66 -0
  267. package/src/__tests__/composables/useSelectionLimit.test.ts +41 -0
  268. package/src/__tests__/composables/useSortedItems.test.ts +87 -0
  269. package/src/__tests__/composables/useTemplateCollection.test.ts +147 -0
  270. package/src/__tests__/composables/useTextSearch.test.ts +55 -0
  271. package/src/__tests__/composables/useTheme.test.ts +91 -0
  272. package/src/__tests__/composables/useTimeUtils.test.ts +35 -0
  273. package/src/__tests__/docs/frontendDocsCatalog.test.ts +324 -0
  274. package/src/__tests__/fixtures/templates/dose-response.json +81 -0
  275. package/src/__tests__/fixtures/templates/plate-map.json +54 -0
  276. package/src/__tests__/fixtures/templates/qpcr-plate.json +96 -0
  277. package/src/__tests__/fixtures/templates/sample-sheet.json +71 -0
  278. package/src/__tests__/templates/templates.test.ts +1055 -0
  279. package/src/components/AppAvatarMenu.vue +15 -69
  280. package/src/components/AppLayout.story.vue +64 -25
  281. package/src/components/AppLayout.vue +83 -2
  282. package/src/components/AppPluginSwitcher.vue +41 -145
  283. package/src/components/AppSidebar.story.vue +203 -1
  284. package/src/components/AppSidebar.vue +320 -25
  285. package/src/components/{ToastNotification.story.vue → AppToastContainer.story.vue} +6 -6
  286. package/src/components/{ToastNotification.vue → AppToastContainer.vue} +1 -1
  287. package/src/components/AppTopBar.story.vue +7 -33
  288. package/src/components/AppTopBar.vue +104 -300
  289. package/src/components/BaseModal.vue +3 -5
  290. package/src/components/BaseRadioGroup.vue +7 -3
  291. package/src/components/BaseSelect.vue +11 -7
  292. package/src/components/BaseTabs.vue +6 -4
  293. package/src/components/BatchProgressList.vue +5 -8
  294. package/src/components/BioTemplateExperimentWorkspaceView.story.vue +123 -0
  295. package/src/components/BioTemplateExperimentWorkspaceView.vue +343 -0
  296. package/src/components/BioTemplatePackWorkspaceView.story.vue +107 -0
  297. package/src/components/BioTemplatePackWorkspaceView.vue +177 -0
  298. package/src/components/BioTemplatePresetWorkspaceView.story.vue +163 -0
  299. package/src/components/BioTemplatePresetWorkspaceView.vue +401 -0
  300. package/src/components/BioTemplateRenderer.story.vue +57 -0
  301. package/src/components/BioTemplateRenderer.vue +57 -0
  302. package/src/components/Breadcrumb.vue +14 -8
  303. package/src/components/ComponentBindingRenderer.story.vue +57 -0
  304. package/src/components/ComponentBindingRenderer.vue +308 -0
  305. package/src/components/ConcentrationInput.vue +27 -64
  306. package/src/components/ControlWorkspaceView.story.vue +347 -0
  307. package/src/components/ControlWorkspaceView.vue +378 -0
  308. package/src/components/DataFrame.vue +34 -50
  309. package/src/components/DatePicker.vue +59 -192
  310. package/src/components/DateTimePicker.vue +50 -171
  311. package/src/components/DoseDesignWorkspaceView.story.vue +77 -0
  312. package/src/components/DoseDesignWorkspaceView.vue +255 -0
  313. package/src/components/DropdownButton.vue +14 -32
  314. package/src/components/EmptyState.vue +4 -2
  315. package/src/components/ExperimentPopover.vue +7 -28
  316. package/src/components/ExperimentSelectorModal.vue +6 -5
  317. package/src/components/FormBuilder.story.vue +190 -0
  318. package/src/components/FormBuilder.vue +124 -27
  319. package/src/components/GroupAssigner.vue +24 -56
  320. package/src/components/MultiSelect.vue +17 -12
  321. package/src/components/PlateMapEditor.vue +3 -8
  322. package/src/components/PluginIcon.vue +2 -22
  323. package/src/components/PluginWorkspaceView.story.vue +334 -0
  324. package/src/components/PluginWorkspaceView.vue +708 -0
  325. package/src/components/ProtocolStepEditor.vue +13 -22
  326. package/src/components/ReagentList.vue +25 -33
  327. package/src/components/SampleHierarchyTree.vue +12 -23
  328. package/src/components/SampleSelector.vue +42 -122
  329. package/src/components/SegmentedControl.vue +7 -3
  330. package/src/components/SettingsModal.story.vue +88 -1
  331. package/src/components/SettingsModal.vue +120 -29
  332. package/src/components/TagsInput.vue +29 -14
  333. package/src/components/ThemeToggle.vue +9 -7
  334. package/src/components/TimePicker.vue +19 -41
  335. package/src/components/Tooltip.vue +7 -12
  336. package/src/components/WellPlate.vue +6 -12
  337. package/src/components/index.ts +9 -8
  338. package/src/components/internal/ActionItemInternal.vue +82 -0
  339. package/src/components/internal/AppPageSelectorInternal.vue +128 -0
  340. package/src/components/internal/AppPillNavInternal.vue +194 -0
  341. package/src/components/internal/CalendarGridPanelInternal.vue +120 -0
  342. package/src/components/{FormFieldRenderer.vue → internal/FormFieldRendererInternal.vue} +4 -12
  343. package/src/components/{FormSection.vue → internal/FormSectionRenderer.vue} +6 -18
  344. package/src/components/{WellEditPopup.vue → internal/WellEditPopupInternal.vue} +5 -10
  345. package/src/composables/experiment-utils.ts +26 -0
  346. package/src/composables/index.ts +229 -3
  347. package/src/composables/platformContextHelpers.ts +74 -0
  348. package/src/composables/useApi.ts +9 -2
  349. package/src/composables/useAppExperiment.ts +85 -13
  350. package/src/composables/useBioTemplateComponents.ts +105 -0
  351. package/src/composables/useBioTemplateControls.ts +41 -0
  352. package/src/composables/useBioTemplatePackWorkspace.ts +185 -0
  353. package/src/composables/useBioTemplatePresetWorkspace.ts +326 -0
  354. package/src/composables/useBioTemplateWorkspace.ts +141 -0
  355. package/src/composables/useCalendarGrid.ts +140 -0
  356. package/src/composables/useControlSchema.ts +1362 -0
  357. package/src/composables/useDebouncedWatch.ts +119 -0
  358. package/src/composables/useDropdownState.ts +83 -0
  359. package/src/composables/useEventListener.ts +111 -0
  360. package/src/composables/useExpansionSet.ts +117 -0
  361. package/src/composables/useExperimentData.ts +20 -11
  362. package/src/composables/useExperimentSave.ts +202 -50
  363. package/src/composables/useExperimentSelector.ts +86 -72
  364. package/src/composables/useForm.ts +49 -4
  365. package/src/composables/useFormBuilder.ts +93 -42
  366. package/src/composables/useGroupAssignment.ts +148 -0
  367. package/src/composables/useListSelection.ts +158 -0
  368. package/src/composables/usePluginClient.ts +466 -0
  369. package/src/composables/usePluginConfig.ts +34 -13
  370. package/src/composables/useRequestSyncState.ts +126 -0
  371. package/src/composables/useSampleGroups.ts +126 -0
  372. package/src/composables/useSelectionLimit.ts +57 -0
  373. package/src/composables/useSortedItems.ts +118 -0
  374. package/src/composables/useTemplateCollection.ts +229 -0
  375. package/src/composables/useTextSearch.ts +60 -0
  376. package/src/composables/useTheme.ts +2 -28
  377. package/src/composables/useTimeUtils.ts +26 -2
  378. package/src/composables/useWellPlateEditor.ts +13 -9
  379. package/src/index.ts +11 -348
  380. package/src/install.ts +11 -4
  381. package/src/stores/settings.ts +13 -9
  382. package/src/styles/components/app-layout.css +82 -0
  383. package/src/styles/components/app-page-selector.css +23 -0
  384. package/src/styles/components/app-pill-nav.css +77 -0
  385. package/src/styles/components/app-sidebar.css +119 -0
  386. package/src/styles/components/app-top-bar.css +0 -201
  387. package/src/styles/components/concentration-input.css +3 -142
  388. package/src/styles/components/empty-state.css +0 -16
  389. package/src/styles/components/theme-toggle.css +3 -66
  390. package/src/styles/index.css +0 -2
  391. package/src/templates/adapters.ts +785 -0
  392. package/src/templates/builders.ts +2149 -0
  393. package/src/templates/catalog.ts +245 -0
  394. package/src/templates/componentBindings.ts +653 -0
  395. package/src/templates/controlSchemas.ts +718 -0
  396. package/src/templates/index.ts +318 -0
  397. package/src/templates/lookup.ts +18 -0
  398. package/src/templates/packs.ts +156 -0
  399. package/src/templates/presets.ts +146 -0
  400. package/src/templates/types.ts +668 -0
  401. package/src/types/components.ts +39 -27
  402. package/src/types/form-builder.ts +7 -2
  403. package/src/types/index.ts +13 -3
  404. package/src/types/platform.ts +7 -1
  405. package/src/utils/formModelSync.ts +52 -0
  406. package/src/utils/items.ts +28 -0
  407. package/src/utils/options.ts +23 -0
  408. package/src/utils/pluginIcon.ts +30 -0
  409. package/dist/__tests__/composables/usePluginApi.test.d.ts +0 -13
  410. package/dist/auth-DsI0rQ7_.js.map +0 -1
  411. package/dist/components/GroupingModal.vue.d.ts +0 -12
  412. package/dist/components-_XqPEhP9.js.map +0 -1
  413. package/dist/composables/usePluginApi.d.ts +0 -29
  414. package/dist/composables-tiZqLu1M.js.map +0 -1
  415. package/dist/useScheduleDrag-CA9sGNJG.js +0 -7181
  416. package/dist/useScheduleDrag-CA9sGNJG.js.map +0 -1
  417. package/src/__tests__/composables/usePluginApi.test.ts +0 -81
  418. package/src/components/AppPageSelector.vue +0 -159
  419. package/src/components/AppPillNav.vue +0 -66
  420. package/src/components/GroupingModal.story.vue +0 -52
  421. package/src/components/GroupingModal.vue +0 -422
  422. package/src/components/SettingsButton.story.vue +0 -58
  423. package/src/components/SettingsButton.vue +0 -76
  424. package/src/composables/usePluginApi.ts +0 -39
  425. package/src/styles/components/grouping-modal.css +0 -323
  426. package/src/styles/components/settings-button.css +0 -94
  427. /package/dist/components/{ToastNotification.vue.d.ts → AppToastContainer.vue.d.ts} +0 -0
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
- import { ref, watch, computed, onMounted, onUnmounted } from 'vue'
3
- import type { Well, WellEditData, WellEditField } from '../types'
2
+ import { ref, watch, computed } from 'vue'
3
+ import type { Well, WellEditData, WellEditField } from '../../types'
4
+ import { useEventListener } from '../../composables/useEventListener'
4
5
 
5
6
  interface Props {
6
7
  wellId: string
@@ -83,13 +84,7 @@ function handleKeydown(e: KeyboardEvent) {
83
84
  if (e.key === 'Escape') close()
84
85
  }
85
86
 
86
- onMounted(() => {
87
- document.addEventListener('keydown', handleKeydown)
88
- })
89
-
90
- onUnmounted(() => {
91
- document.removeEventListener('keydown', handleKeydown)
92
- })
87
+ useEventListener(() => document, 'keydown', handleKeydown)
93
88
 
94
89
  const sampleTypeButtons = [
95
90
  { type: 'sample', label: 'S', tooltip: 'Sample' },
@@ -230,5 +225,5 @@ const sampleTypeButtons = [
230
225
  </template>
231
226
 
232
227
  <style>
233
- @import '../styles/components/well-edit-popup.css';
228
+ @import '../../styles/components/well-edit-popup.css';
234
229
  </style>
@@ -1,5 +1,10 @@
1
1
  import type { DatePreset, ExperimentStatus, SelectOption, PillVariant } from '../types'
2
2
 
3
+ interface ExperimentCodeSource {
4
+ id?: number | null
5
+ experiment_code?: string | null
6
+ }
7
+
3
8
  export function formatExperimentDate(dateStr: string): string {
4
9
  try {
5
10
  return new Date(dateStr).toLocaleDateString(undefined, {
@@ -41,6 +46,27 @@ export const EXPERIMENT_STATUS_LABELS: Record<ExperimentStatus, string> = {
41
46
  cancelled: 'Cancelled',
42
47
  }
43
48
 
49
+ export function formatExperimentStatus(status?: ExperimentStatus | string | null): string {
50
+ if (!status) return ''
51
+ if (status in EXPERIMENT_STATUS_LABELS) {
52
+ return EXPERIMENT_STATUS_LABELS[status as ExperimentStatus]
53
+ }
54
+ const label = String(status).replace(/[-_]+/g, ' ').trim()
55
+ return label ? label.replace(/^\w/, c => c.toUpperCase()) : ''
56
+ }
57
+
58
+ export function getExperimentStatusVariant(status?: ExperimentStatus | string | null): PillVariant {
59
+ if (status && status in EXPERIMENT_STATUS_VARIANT_MAP) {
60
+ return EXPERIMENT_STATUS_VARIANT_MAP[status as ExperimentStatus]
61
+ }
62
+ return 'default'
63
+ }
64
+
65
+ export function resolveExperimentCode(experiment: ExperimentCodeSource): string | undefined {
66
+ if (experiment.experiment_code) return experiment.experiment_code
67
+ return experiment.id != null ? `EXP-${experiment.id}` : undefined
68
+ }
69
+
44
70
  export const DATE_PRESET_OPTIONS: SelectOption<string>[] = [
45
71
  { value: '', label: 'Any time' },
46
72
  { value: 'last_7_days', label: 'Last 7 days' },
@@ -43,6 +43,7 @@ export {
43
43
  type SerialDilutionParams,
44
44
  type SerialDilutionStep,
45
45
  type SerialDilutionResult,
46
+ type ConversionResult,
46
47
  type WellConcentration,
47
48
  type UseDoseCalculatorReturn,
48
49
  } from './useDoseCalculator'
@@ -75,6 +76,7 @@ export {
75
76
  useTimeUtils,
76
77
  parseTime,
77
78
  formatTime,
79
+ formatTimeSlot,
78
80
  generateTimeSlots,
79
81
  rangesOverlap,
80
82
  durationMinutes,
@@ -84,8 +86,30 @@ export {
84
86
  snapToSlot,
85
87
  addMinutes,
86
88
  compareTime,
89
+ findNearestTimeSlotIndex,
90
+ toMinutes,
91
+ fromMinutes,
87
92
  } from './useTimeUtils'
88
93
  export { useScheduleDrag } from './useScheduleDrag'
94
+ export {
95
+ useEventListener,
96
+ type EventListenerEnabled,
97
+ type EventFor,
98
+ type EventListenerStop,
99
+ type EventMapFor,
100
+ type EventTargetGetter,
101
+ type EventTargetLike,
102
+ type UseEventListenerObjectOptions,
103
+ type UseEventListenerOptions,
104
+ } from './useEventListener'
105
+ export {
106
+ useDebouncedWatch,
107
+ type DebouncedWatchCallback,
108
+ type DebouncedWatchCleanup,
109
+ type DebouncedWatchSource,
110
+ type UseDebouncedWatchOptions,
111
+ type UseDebouncedWatchReturn,
112
+ } from './useDebouncedWatch'
89
113
  export { useFormBuilder, evaluateCondition } from './useFormBuilder'
90
114
  export { useAutoGroup, DEFAULT_COLORS, extractSamplesFromDesignData } from './useAutoGroup'
91
115
  export {
@@ -105,6 +129,9 @@ export {
105
129
  } from './useExperimentSelector'
106
130
  export {
107
131
  formatExperimentDate,
132
+ formatExperimentStatus,
133
+ getExperimentStatusVariant,
134
+ resolveExperimentCode,
108
135
  datePresetToISO,
109
136
  EXPERIMENT_STATUS_OPTIONS,
110
137
  EXPERIMENT_STATUS_VARIANT_MAP,
@@ -125,9 +152,13 @@ export {
125
152
  export {
126
153
  useAppExperiment,
127
154
  APP_EXPERIMENT_KEY,
155
+ type AppExperimentRecord,
156
+ type AppExperimentSource,
128
157
  type UseAppExperimentOptions,
129
158
  type UseAppExperimentReturn,
130
159
  type AppExperimentState,
160
+ type AppExperimentPopoverBinding,
161
+ type AppExperimentSelectorModalBinding,
131
162
  } from './useAppExperiment'
132
163
  export {
133
164
  useExperimentSave,
@@ -135,6 +166,201 @@ export {
135
166
  type UseExperimentSaveReturn,
136
167
  } from './useExperimentSave'
137
168
  export {
138
- usePluginApi,
139
- type UsePluginApiOptions,
140
- } from './usePluginApi'
169
+ useTemplateCollection,
170
+ type TemplateCollectionInput,
171
+ type UseTemplateCollectionOptions,
172
+ type UseTemplateCollectionReturn,
173
+ } from './useTemplateCollection'
174
+ export {
175
+ useRequestSyncState,
176
+ type RequestSyncRunOptions,
177
+ type RequestSyncSuccessKind,
178
+ type UseRequestSyncStateReturn,
179
+ } from './useRequestSyncState'
180
+ export {
181
+ useSelectionLimit,
182
+ type SelectionLimitSource,
183
+ type UseSelectionLimitOptions,
184
+ type UseSelectionLimitReturn,
185
+ } from './useSelectionLimit'
186
+ export {
187
+ useListSelection,
188
+ type ListSelectionSource,
189
+ type ListSelectionValue,
190
+ type UseListSelectionOptions,
191
+ type UseListSelectionReturn,
192
+ type WidenListSelectionValue,
193
+ } from './useListSelection'
194
+ export {
195
+ useGroupAssignment,
196
+ type GroupAssignmentSource,
197
+ type GroupAssignmentState,
198
+ type GroupAssignmentZone,
199
+ type UseGroupAssignmentOptions,
200
+ type UseGroupAssignmentReturn,
201
+ } from './useGroupAssignment'
202
+ export {
203
+ useSampleGroups,
204
+ type DisplaySampleSubGroup,
205
+ type SampleGroupSource,
206
+ type SampleMajorGroup,
207
+ type UseSampleGroupsOptions,
208
+ type UseSampleGroupsReturn,
209
+ } from './useSampleGroups'
210
+ export {
211
+ useExpansionSet,
212
+ type ExpansionSetSource,
213
+ type UseExpansionSetOptions,
214
+ type UseExpansionSetReturn,
215
+ } from './useExpansionSet'
216
+ export {
217
+ compareSortValues,
218
+ useSortedItems,
219
+ type CompareSortValuesOptions,
220
+ type SortComparator,
221
+ type SortComparatorContext,
222
+ type SortDescriptor,
223
+ type SortedItemsSource,
224
+ type SortOrder,
225
+ type UseSortedItemsOptions,
226
+ type UseSortedItemsReturn,
227
+ } from './useSortedItems'
228
+ export {
229
+ candidateMatchesSearch,
230
+ normalizeSearchQuery,
231
+ useTextSearch,
232
+ type TextSearchCandidate,
233
+ type TextSearchSource,
234
+ type UseTextSearchOptions,
235
+ type UseTextSearchReturn,
236
+ } from './useTextSearch'
237
+ export {
238
+ controlsToFormSchema,
239
+ controlsToSectionFormSchema,
240
+ controlsToSectionFormSchemas,
241
+ controlsToSidebarPanels,
242
+ controlsToSettingsSchema,
243
+ controlsToTopBarSettingsConfig,
244
+ controlsToViewIds,
245
+ controlsToViewItems,
246
+ controlValuesToComponentBindings,
247
+ controlValuesToComponentBindingsById,
248
+ controlValuesToComponentProps,
249
+ defineControlComponentBindings,
250
+ defineControlModel,
251
+ defineDoseDesignControlModel,
252
+ defineDoseCalculatorControlProps,
253
+ defineControls,
254
+ defineWellPlateControlProps,
255
+ defineWellPlateDoseComponentBindings,
256
+ defineWellPlateDoseControlProps,
257
+ getDefaultControlView,
258
+ getControlDefaults,
259
+ useControlSchema,
260
+ useControlWorkspace,
261
+ type ControlDefinition,
262
+ type ControlComponentBinding,
263
+ type ControlComponentBindingConfig,
264
+ type ControlComponentBindingRecordConfig,
265
+ type ControlComponentBindingsById,
266
+ type ControlComponentBindingsConfig,
267
+ type ControlComponentPropSource,
268
+ type ControlComponentPropsByIdMap,
269
+ type ControlComponentPropsMap,
270
+ type DoseDesignControlModelOptions,
271
+ type DoseCalculatorControlPropsOptions,
272
+ type ControlFormSchema,
273
+ type ControlInput,
274
+ type ControlModel,
275
+ type ControlModelBinding,
276
+ type ControlModelSectionConfig,
277
+ type ControlModelViewConfig,
278
+ type ControlOption,
279
+ type ControlOptionValue,
280
+ type ControlPrimitive,
281
+ type ControlSchema,
282
+ type ControlSchemaOptions,
283
+ type ControlShorthand,
284
+ type ControlWorkspaceOptions,
285
+ type ControlFormBinding,
286
+ type ControlSettingsBinding,
287
+ type ControlSidebarBinding,
288
+ type ControlSectionConfig,
289
+ type ControlSidebarConfig,
290
+ type ControlTopBarSettingsBinding,
291
+ type ControlViewConfig,
292
+ type ControlValues,
293
+ type UseControlSchemaReturn,
294
+ type ControlWorkspaceAppTopBarPillBinding,
295
+ type ControlWorkspaceComponentBindings,
296
+ type ControlWorkspaceFormBinding,
297
+ type ControlWorkspacePillNavBinding,
298
+ type ControlWorkspaceSidebarBinding,
299
+ type ControlWorkspaceTopBarBinding,
300
+ type ControlWorkspaceTopBarSettingsBinding,
301
+ type UseControlWorkspaceReturn,
302
+ type WellPlateControlPropsOptions,
303
+ type WellPlateDoseControlPropsOptions,
304
+ } from './useControlSchema'
305
+ export {
306
+ getBioTemplateControlSchema,
307
+ requireBioTemplateControlSchema,
308
+ useBioTemplateControls,
309
+ type BioTemplateControlSchema,
310
+ type BioTemplateControlTarget,
311
+ } from './useBioTemplateControls'
312
+ export {
313
+ getBioTemplateComponentProps,
314
+ getBioTemplateComponentBindings,
315
+ toBioTemplateComponentBindings,
316
+ toBioTemplateComponentBindingsById,
317
+ toBioTemplateComponentProps,
318
+ toBioTemplateComponentPropsByComponent,
319
+ toBioTemplateComponentPropsById,
320
+ useBioTemplateComponents,
321
+ type BioTemplateComponentBinding,
322
+ type BioTemplateComponentBindingsById,
323
+ type BioTemplateComponentPropsByComponent,
324
+ type BioTemplateComponentPropsBinding,
325
+ type BioTemplateComponentPropsById,
326
+ type BioTemplateComponentPropsLookupOptions,
327
+ type BioTemplateResolvedComponentBinding,
328
+ type BioTemplateComponentTarget,
329
+ type UseBioTemplateComponentsReturn,
330
+ } from './useBioTemplateComponents'
331
+ export {
332
+ useBioTemplateWorkspace,
333
+ type BioTemplateRendererBinding,
334
+ type BioTemplateWorkspaceBindings,
335
+ type BioTemplateWorkspaceTarget,
336
+ type UseBioTemplateWorkspaceReturn,
337
+ } from './useBioTemplateWorkspace'
338
+ export {
339
+ useBioTemplatePresetWorkspace,
340
+ type BioTemplatePresetWorkspaceBindings,
341
+ type UseBioTemplatePresetWorkspaceOptions,
342
+ type UseBioTemplatePresetWorkspaceReturn,
343
+ } from './useBioTemplatePresetWorkspace'
344
+ export {
345
+ useBioTemplatePackWorkspace,
346
+ type UseBioTemplatePackWorkspaceOptions,
347
+ type UseBioTemplatePackWorkspaceReturn,
348
+ } from './useBioTemplatePackWorkspace'
349
+ export {
350
+ buildPluginEndpointUrl,
351
+ createPluginClient,
352
+ getPluginPageSelectorItems,
353
+ resolvePluginBaseUrl,
354
+ usePluginClient,
355
+ usePluginSettings,
356
+ useCurrentExperiment,
357
+ type BuildPluginEndpointUrlOptions,
358
+ type PluginContract,
359
+ type PluginEndpointContract,
360
+ type PluginEndpointDefinition,
361
+ type PluginHttpMethod,
362
+ type PluginNavItemContract,
363
+ type CreatePluginClientOptions,
364
+ type UseCurrentExperimentOptions,
365
+ type UseCurrentExperimentReturn,
366
+ } from './usePluginClient'
@@ -0,0 +1,74 @@
1
+ import type { PlatformContext } from '../types'
2
+
3
+ export type ExperimentAwarePlatformContext = PlatformContext & {
4
+ currentExperiment?: unknown
5
+ currentExperimentId?: unknown
6
+ experiment?: unknown
7
+ experimentId?: unknown
8
+ }
9
+
10
+ export function getInjectedPlatformContext(): PlatformContext | undefined {
11
+ if (typeof window === 'undefined') return undefined
12
+ return (window as unknown as { __MINT_PLATFORM__?: PlatformContext }).__MINT_PLATFORM__
13
+ }
14
+
15
+ export function getInjectedExperimentContext(): ExperimentAwarePlatformContext | undefined {
16
+ return getInjectedPlatformContext() as ExperimentAwarePlatformContext | undefined
17
+ }
18
+
19
+ export function currentExperimentFromContext<TExperiment>(
20
+ context: ExperimentAwarePlatformContext | undefined = getInjectedExperimentContext(),
21
+ ): TExperiment | undefined {
22
+ const candidate = context?.currentExperiment ?? context?.experiment
23
+ return candidate && typeof candidate === 'object'
24
+ ? candidate as TExperiment
25
+ : undefined
26
+ }
27
+
28
+ export function currentExperimentIdFromContext(
29
+ context: ExperimentAwarePlatformContext | undefined = getInjectedExperimentContext(),
30
+ ): number | undefined {
31
+ return parseExperimentId(
32
+ context?.currentExperimentId ??
33
+ context?.experimentId ??
34
+ context?.currentExperiment ??
35
+ context?.experiment,
36
+ )
37
+ }
38
+
39
+ export function currentExperimentIdFromUrl(): number | undefined {
40
+ if (typeof window === 'undefined') return undefined
41
+ const params = new URLSearchParams(window.location.search)
42
+ return parseExperimentId(params.get('experimentId') ?? params.get('experiment_id'))
43
+ ?? experimentIdFromPath(window.location.pathname)
44
+ ?? experimentIdFromPath(window.location.hash.replace(/^#\/?/, '/'))
45
+ }
46
+
47
+ export function resolveCurrentExperimentId(
48
+ context: ExperimentAwarePlatformContext | undefined = getInjectedExperimentContext(),
49
+ ): number | undefined {
50
+ return currentExperimentIdFromContext(context) ?? currentExperimentIdFromUrl()
51
+ }
52
+
53
+ export function parseExperimentId(value: unknown): number | undefined {
54
+ if (typeof value === 'number' && Number.isFinite(value)) return value
55
+ if (typeof value === 'string' && value.trim()) {
56
+ const parsed = Number(value)
57
+ return Number.isFinite(parsed) ? parsed : undefined
58
+ }
59
+ if (value && typeof value === 'object' && 'id' in value) {
60
+ return parseExperimentId((value as { id?: unknown }).id)
61
+ }
62
+ return undefined
63
+ }
64
+
65
+ function experimentIdFromPath(path: string): number | undefined {
66
+ const segments = path.split('/').filter(Boolean)
67
+ for (let index = 0; index < segments.length - 1; index += 1) {
68
+ const segment = segments[index]?.toLowerCase()
69
+ if (segment === 'experiment' || segment === 'experiments') {
70
+ return parseExperimentId(decodeURIComponent(segments[index + 1] ?? ''))
71
+ }
72
+ }
73
+ return undefined
74
+ }
@@ -5,6 +5,13 @@ import { useAuthStore } from '../stores/auth'
5
5
  let apiClientInstance: AxiosInstance | null = null
6
6
  let interceptorAttached = false
7
7
 
8
+ function joinUrlPath(baseUrl: string, path: string): string {
9
+ if (!path) return baseUrl
10
+ const normalizedBase = baseUrl.replace(/\/+$/, '')
11
+ const normalizedPath = path.replace(/^\/+/, '/')
12
+ return `${normalizedBase}${normalizedPath.startsWith('/') ? normalizedPath : `/${normalizedPath}`}`
13
+ }
14
+
8
15
  function getApiClient(): AxiosInstance {
9
16
  if (!apiClientInstance) {
10
17
  apiClientInstance = axios.create({
@@ -144,12 +151,12 @@ export function useApi(options: ApiClientOptions = {}): UseApiReturn {
144
151
  // Build full URL for external use (e.g., <a href="...">)
145
152
  function buildUrl(path: string): string {
146
153
  const baseUrl = options.baseUrl ?? settingsStore.getApiBaseUrl()
147
- return `${baseUrl}${path}`
154
+ return joinUrlPath(baseUrl, path)
148
155
  }
149
156
 
150
157
  // WebSocket URL builder
151
158
  function buildWsUrl(path: string): string {
152
- return `${settingsStore.getWsBaseUrl()}${path}`
159
+ return joinUrlPath(settingsStore.getWsBaseUrl(), path)
153
160
  }
154
161
 
155
162
  return {
@@ -4,14 +4,28 @@ import {
4
4
  readonly,
5
5
  provide,
6
6
  toValue,
7
+ watch,
7
8
  onScopeDispose,
8
9
  type Ref,
9
10
  type ComputedRef,
10
11
  type InjectionKey,
11
12
  } from 'vue'
12
13
  import type { ExperimentSummary, ExperimentStatus } from '../types'
14
+ import { resolveExperimentCode } from './experiment-utils'
15
+
16
+ export type AppExperimentRecord = Pick<ExperimentSummary, 'id' | 'name' | 'status' | 'experiment_code'>
17
+
18
+ export type AppExperimentSource =
19
+ | AppExperimentRecord
20
+ | null
21
+ | undefined
22
+ | Ref<AppExperimentRecord | null | undefined>
23
+ | ComputedRef<AppExperimentRecord | null | undefined>
24
+ | (() => AppExperimentRecord | null | undefined)
13
25
 
14
26
  export interface UseAppExperimentOptions {
27
+ /** Current experiment source to mirror into the app shell. Accepts a value, ref, computed, or getter. */
28
+ experiment?: AppExperimentSource
15
29
  onSelect?: (experiment: ExperimentSummary) => void | Promise<void>
16
30
  onSave?: () => string | null | Promise<string | null>
17
31
  onDetach?: () => void
@@ -19,12 +33,37 @@ export interface UseAppExperimentOptions {
19
33
  saveDisabledMessage?: string | Ref<string | undefined> | ComputedRef<string | undefined>
20
34
  }
21
35
 
36
+ export interface AppExperimentPopoverBinding {
37
+ experimentName?: string
38
+ experimentCode?: string
39
+ experimentStatus?: ExperimentStatus
40
+ showSave: boolean
41
+ showDetach: boolean
42
+ saveDisabled: boolean
43
+ saveDisabledMessage?: string
44
+ saveLoading: boolean
45
+ saveSuccessMessage?: string
46
+ }
47
+
48
+ export interface AppExperimentSelectorModalBinding {
49
+ modelValue: boolean
50
+ currentExperimentId: number | null
51
+ }
52
+
22
53
  export interface UseAppExperimentReturn {
23
- set: (experiment: Pick<ExperimentSummary, 'id' | 'name' | 'status' | 'experiment_code'>) => void
54
+ set: (experiment: AppExperimentRecord) => void
24
55
  clear: () => void
25
56
  experimentName: Readonly<Ref<string | undefined>>
26
57
  experimentCode: Readonly<Ref<string | undefined>>
58
+ experimentStatus: Readonly<Ref<ExperimentStatus | undefined>>
27
59
  experimentId: Readonly<Ref<number | null>>
60
+ popover: ComputedRef<AppExperimentPopoverBinding>
61
+ selectorModal: ComputedRef<AppExperimentSelectorModalBinding>
62
+ openModal: () => void
63
+ closeModal: () => void
64
+ handleSelect: (experiment: ExperimentSummary) => void
65
+ handleSave: () => Promise<void>
66
+ handleDetach: () => void
28
67
  }
29
68
 
30
69
  export interface AppExperimentState {
@@ -40,6 +79,8 @@ export interface AppExperimentState {
40
79
  closeModal: () => void
41
80
  saveDisabled: ComputedRef<boolean>
42
81
  saveDisabledMessage: ComputedRef<string | undefined>
82
+ popover: ComputedRef<AppExperimentPopoverBinding>
83
+ selectorModal: ComputedRef<AppExperimentSelectorModalBinding>
43
84
  openModal: () => void
44
85
  handleSelect: (experiment: ExperimentSummary) => void
45
86
  handleSave: () => Promise<void>
@@ -48,7 +89,7 @@ export interface AppExperimentState {
48
89
 
49
90
  export const APP_EXPERIMENT_KEY: InjectionKey<AppExperimentState> = Symbol('app-experiment')
50
91
 
51
- /** Manages the active experiment selection, save flow, and detach action for a plugin's app shell. */
92
+ /** Manages the current experiment selection, save flow, and detach action for a plugin's AppTopBar app shell. */
52
93
  export function useAppExperiment(options: UseAppExperimentOptions = {}): UseAppExperimentReturn {
53
94
  const experimentName = ref<string | undefined>()
54
95
  const experimentCode = ref<string | undefined>()
@@ -64,19 +105,26 @@ export function useAppExperiment(options: UseAppExperimentOptions = {}): UseAppE
64
105
  const showDetach = computed(() => experimentId.value !== null)
65
106
  const saveDisabled = computed(() => toValue(options.saveDisabled) ?? false)
66
107
  const saveDisabledMessage = computed(() => toValue(options.saveDisabledMessage))
67
-
68
- function set(experiment: Pick<ExperimentSummary, 'id' | 'name' | 'status' | 'experiment_code'>) {
108
+ const popover = computed<AppExperimentPopoverBinding>(() => ({
109
+ experimentName: experimentName.value,
110
+ experimentCode: experimentCode.value,
111
+ experimentStatus: experimentStatus.value,
112
+ showSave: showSave.value,
113
+ showDetach: showDetach.value,
114
+ saveDisabled: saveDisabled.value,
115
+ saveDisabledMessage: saveDisabledMessage.value,
116
+ saveLoading: saveLoading.value,
117
+ saveSuccessMessage: saveSuccessMessage.value,
118
+ }))
119
+ const selectorModal = computed<AppExperimentSelectorModalBinding>(() => ({
120
+ modelValue: showModal.value,
121
+ currentExperimentId: experimentId.value,
122
+ }))
123
+
124
+ function set(experiment: AppExperimentRecord) {
69
125
  experimentId.value = experiment.id
70
126
  experimentName.value = experiment.name
71
- // Fallback: if no explicit experiment_code, derive `EXP-${id}` so the
72
- // topbar always has a compact identifier to show instead of the long
73
- // name. Matches the `EXP-${id}` format used elsewhere (e.g., queue
74
- // cards in mint-ms-planner). Callers with the real code should pass it
75
- // via `experiment_code` to keep the topbar chip in sync with the
76
- // formal code shown in the popover panel.
77
- experimentCode.value = experiment.experiment_code ?? (
78
- experiment.id != null ? `EXP-${experiment.id}` : undefined
79
- )
127
+ experimentCode.value = resolveExperimentCode(experiment)
80
128
  experimentStatus.value = experiment.status
81
129
  }
82
130
 
@@ -87,6 +135,20 @@ export function useAppExperiment(options: UseAppExperimentOptions = {}): UseAppE
87
135
  experimentStatus.value = undefined
88
136
  }
89
137
 
138
+ if (options.experiment !== undefined) {
139
+ watch(
140
+ () => toValue(options.experiment),
141
+ (experiment) => {
142
+ if (experiment) {
143
+ set(experiment)
144
+ } else {
145
+ clear()
146
+ }
147
+ },
148
+ { immediate: true },
149
+ )
150
+ }
151
+
90
152
  function openModal() {
91
153
  showModal.value = true
92
154
  }
@@ -140,6 +202,8 @@ export function useAppExperiment(options: UseAppExperimentOptions = {}): UseAppE
140
202
  showDetach,
141
203
  saveDisabled,
142
204
  saveDisabledMessage,
205
+ popover,
206
+ selectorModal,
143
207
  openModal,
144
208
  closeModal,
145
209
  handleSelect,
@@ -154,6 +218,14 @@ export function useAppExperiment(options: UseAppExperimentOptions = {}): UseAppE
154
218
  clear,
155
219
  experimentName: readonly(experimentName),
156
220
  experimentCode: readonly(experimentCode),
221
+ experimentStatus: readonly(experimentStatus),
157
222
  experimentId: readonly(experimentId),
223
+ popover,
224
+ selectorModal,
225
+ openModal,
226
+ closeModal,
227
+ handleSelect,
228
+ handleSave,
229
+ handleDetach,
158
230
  }
159
231
  }