@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
@@ -0,0 +1,653 @@
1
+ import type { WellLegendItem } from '../types'
2
+ import {
3
+ extractTemplateCollection,
4
+ getTemplateData,
5
+ } from './builders'
6
+ import {
7
+ toDoseConditions,
8
+ toDoseLayoutState,
9
+ toPlateMapEditorState,
10
+ toProtocolSteps,
11
+ toQpcrWellPlateWells,
12
+ toReagentListItems,
13
+ toSampleOptions,
14
+ toTemplateDataFrame,
15
+ toTimeCourseSteps,
16
+ toWellPlateWells,
17
+ } from './adapters'
18
+ import {
19
+ getBioTemplateInfo,
20
+ } from './catalog'
21
+ import {
22
+ getBioTemplatePresetInfo,
23
+ } from './presets'
24
+ import {
25
+ getBioTemplatePackInfo,
26
+ } from './packs'
27
+ import type {
28
+ BioTemplateEnvelope,
29
+ DataFrameTemplate,
30
+ DoseResponseTemplate,
31
+ PlateMapTemplate,
32
+ QpcrPlateTemplate,
33
+ ReagentListTemplate,
34
+ SampleSheetTemplate,
35
+ TemplateCollectionEnvelope,
36
+ TemplateId,
37
+ TemplatePackId,
38
+ TemplatePresetId,
39
+ TimeCourseTemplate,
40
+ ProtocolStepsTemplate,
41
+ } from './types'
42
+
43
+ export type BioTemplateComponentTarget =
44
+ | TemplateId
45
+ | TemplatePackId
46
+ | TemplatePresetId
47
+ | string
48
+ | BioTemplateEnvelope<unknown>
49
+ | TemplateCollectionEnvelope
50
+
51
+ export interface BioTemplateComponentBinding {
52
+ id: string
53
+ template_id: TemplateId
54
+ component: string
55
+ adapters: readonly string[]
56
+ props: readonly string[]
57
+ description: string
58
+ importPath: '@morscherlab/mint-sdk'
59
+ }
60
+
61
+ export interface BioTemplateComponentPropsBinding extends BioTemplateComponentBinding {
62
+ propsObject: Record<string, unknown>
63
+ }
64
+
65
+ export interface BioTemplateResolvedComponentBinding extends Omit<BioTemplateComponentBinding, 'props'> {
66
+ /** Prop names advertised by the static template/component compatibility catalog. */
67
+ propNames: readonly string[]
68
+ /** Concrete props ready for direct `v-bind="componentBindingsById[id].props"` use. */
69
+ props: Record<string, unknown>
70
+ /** Backward-compatible alias for callers that already consume componentProps entries. */
71
+ propsObject: Record<string, unknown>
72
+ }
73
+
74
+ export type BioTemplateComponentBindingsById = Record<string, BioTemplateResolvedComponentBinding>
75
+ export type BioTemplateComponentPropsById = Record<string, Record<string, unknown>>
76
+ export type BioTemplateComponentPropsByComponent = Record<string, Record<string, unknown>[]>
77
+
78
+ export interface BioTemplateComponentPropsLookupOptions {
79
+ templateId?: TemplateId
80
+ id?: string
81
+ }
82
+
83
+ export interface BioTemplateComponentImport {
84
+ importPath: BioTemplateComponentBinding['importPath']
85
+ components: string[]
86
+ statement: string
87
+ }
88
+
89
+ export interface BioTemplateComponentSnippet extends BioTemplateComponentBinding {
90
+ propsName: string
91
+ propsExpression: string
92
+ template: string
93
+ }
94
+
95
+ export interface BioTemplateComponentSnippetOptions {
96
+ propsVariable?: string
97
+ }
98
+
99
+ export interface BioTemplateComponentUsageOptions extends BioTemplateComponentSnippetOptions {
100
+ targetExpression?: string
101
+ targetPropName?: string
102
+ }
103
+
104
+ export interface BioTemplateComponentUsage {
105
+ imports: BioTemplateComponentImport[]
106
+ componentProps: BioTemplateComponentPropsBinding[]
107
+ snippets: BioTemplateComponentSnippet[]
108
+ propsDeclarations: string[]
109
+ scriptSetup: string
110
+ template: string
111
+ sfc: string
112
+ }
113
+
114
+ const templateComponentBindings = {
115
+ 'plate-map': [
116
+ binding('plate-map', 'PlateMapEditor', ['toPlateMapEditorState'], ['modelValue', 'format', 'samples'], 'Edit plate layouts from plate-map template data.'),
117
+ binding('plate-map', 'WellPlate', ['toWellPlateWells'], ['format', 'wells', 'sampleColors', 'legendItems'], 'Render the active plate as a well grid.'),
118
+ ],
119
+ 'sample-sheet': [
120
+ binding('sample-sheet', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render sample metadata as a sortable table.'),
121
+ binding('sample-sheet', 'SampleSelector', ['toSampleOptions'], ['samples', 'modelValue'], 'Select samples from sample-sheet records.'),
122
+ ],
123
+ 'sample-prep': [
124
+ binding('sample-prep', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render preparation steps as a table.'),
125
+ ],
126
+ 'dose-response': [
127
+ binding('dose-response', 'DoseCalculator', ['toDoseConditions'], ['mode', 'targetWells'], 'Configure serial dilution and apply concentrations to wells.'),
128
+ binding('dose-response', 'WellPlate', ['toDoseLayoutState'], ['format', 'wells', 'sampleColors', 'legendItems'], 'Render the optional dose-response plate layout.'),
129
+ ],
130
+ 'calibration-curve': [
131
+ binding('calibration-curve', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render standards, QC, blanks, and response values.'),
132
+ ],
133
+ 'time-course': [
134
+ binding('time-course', 'ExperimentTimeline', ['toTimeCourseSteps'], ['modelValue', 'orientation'], 'Render time points as an ordered timeline.'),
135
+ binding('time-course', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render longitudinal samples as a table.'),
136
+ ],
137
+ 'protocol-steps': [
138
+ binding('protocol-steps', 'ExperimentTimeline', ['toProtocolSteps'], ['modelValue', 'orientation', 'editable'], 'Render protocol steps as an editable timeline.'),
139
+ binding('protocol-steps', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render protocol steps as a table.'),
140
+ ],
141
+ 'assay-matrix': [
142
+ binding('assay-matrix', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render sample-by-feature measurements.'),
143
+ ],
144
+ 'reagent-list': [
145
+ binding('reagent-list', 'ReagentList', ['toReagentListItems'], ['modelValue'], 'Render reagents with lot, storage, and stock metadata.'),
146
+ binding('reagent-list', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render reagents as a table.'),
147
+ ],
148
+ 'flow-cytometry-panel': [
149
+ binding('flow-cytometry-panel', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render markers, fluorophores, detectors, and controls.'),
150
+ ],
151
+ 'instrument-run': [
152
+ binding('instrument-run', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render acquisition queues, methods, QC, and run status.'),
153
+ ],
154
+ 'qpcr-plate': [
155
+ binding('qpcr-plate', 'WellPlate', ['toQpcrWellPlateWells'], ['format', 'wells', 'sampleColors', 'legendItems'], 'Render qPCR reactions on a well plate.'),
156
+ binding('qpcr-plate', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render qPCR reactions, targets, controls, and Cq values.'),
157
+ ],
158
+ } satisfies Record<TemplateId, readonly BioTemplateComponentBinding[]>
159
+
160
+ export function listBioTemplateComponentBindings(): Record<TemplateId, BioTemplateComponentBinding[]> {
161
+ return cloneBindingMap(templateComponentBindings)
162
+ }
163
+
164
+ export function getBioTemplateComponentBindings(
165
+ target: BioTemplateComponentTarget
166
+ ): BioTemplateComponentBinding[] {
167
+ const templateIds = resolveComponentTemplateIds(target)
168
+ return templateIds.flatMap(templateId => cloneBindings(templateComponentBindings[templateId]))
169
+ }
170
+
171
+ export function toBioTemplateComponentProps(
172
+ target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope
173
+ ): BioTemplateComponentPropsBinding[] {
174
+ if (isTemplateEnvelope(target)) {
175
+ return propsForTemplate(target)
176
+ }
177
+
178
+ const collection = extractTemplateCollection(target)
179
+ return Object.values(collection).flatMap(template => propsForTemplate(template))
180
+ }
181
+
182
+ /** Return resolved component bindings with concrete props for direct slot rendering. */
183
+ export function toBioTemplateComponentBindings(
184
+ target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope,
185
+ ): BioTemplateResolvedComponentBinding[] {
186
+ return toBioTemplateComponentProps(target).map(toResolvedComponentBinding)
187
+ }
188
+
189
+ /** Return resolved component bindings keyed by stable template component id. */
190
+ export function toBioTemplateComponentBindingsById(
191
+ target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope,
192
+ ): BioTemplateComponentBindingsById {
193
+ return Object.fromEntries(
194
+ toBioTemplateComponentBindings(target).map(binding => [binding.id, binding]),
195
+ )
196
+ }
197
+
198
+ /** Return component props keyed by stable binding id for direct `v-bind="componentPropsById[id]"` use. */
199
+ export function toBioTemplateComponentPropsById(
200
+ target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope,
201
+ ): BioTemplateComponentPropsById {
202
+ return Object.fromEntries(
203
+ toBioTemplateComponentProps(target).map(binding => [binding.id, binding.propsObject]),
204
+ )
205
+ }
206
+
207
+ /** Return component props grouped by SDK component name for direct WellPlate/DoseCalculator binding. */
208
+ export function toBioTemplateComponentPropsByComponent(
209
+ target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope,
210
+ ): BioTemplateComponentPropsByComponent {
211
+ const grouped: BioTemplateComponentPropsByComponent = {}
212
+ for (const binding of toBioTemplateComponentProps(target)) {
213
+ const props = grouped[binding.component] ?? []
214
+ props.push(binding.propsObject)
215
+ grouped[binding.component] = props
216
+ }
217
+ return grouped
218
+ }
219
+
220
+ /** Return the first props object for an SDK component, optionally restricted by template id or binding id. */
221
+ export function getBioTemplateComponentProps(
222
+ target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope,
223
+ component: string,
224
+ options: BioTemplateComponentPropsLookupOptions = {},
225
+ ): Record<string, unknown> | undefined {
226
+ return toBioTemplateComponentProps(target).find((binding) => {
227
+ if (binding.component !== component) return false
228
+ if (options.id !== undefined && binding.id !== options.id) return false
229
+ if (options.templateId !== undefined && binding.template_id !== options.templateId) return false
230
+ return true
231
+ })?.propsObject
232
+ }
233
+
234
+ function toResolvedComponentBinding(
235
+ binding: BioTemplateComponentPropsBinding,
236
+ ): BioTemplateResolvedComponentBinding {
237
+ const { props: propNames, propsObject, ...rest } = binding
238
+ return {
239
+ ...rest,
240
+ propNames,
241
+ props: propsObject,
242
+ propsObject,
243
+ }
244
+ }
245
+
246
+ export function toBioTemplateComponentImports(
247
+ target: BioTemplateComponentTarget
248
+ ): BioTemplateComponentImport[] {
249
+ const groups = new Map<BioTemplateComponentBinding['importPath'], string[]>()
250
+
251
+ for (const binding of getBioTemplateComponentBindings(target)) {
252
+ const components = groups.get(binding.importPath) ?? []
253
+ if (!components.includes(binding.component)) components.push(binding.component)
254
+ groups.set(binding.importPath, components)
255
+ }
256
+
257
+ return Array.from(groups.entries()).map(([importPath, components]) => ({
258
+ importPath,
259
+ components,
260
+ statement: `import { ${components.join(', ')} } from '${importPath}'`,
261
+ }))
262
+ }
263
+
264
+ export function toBioTemplateComponentSnippets(
265
+ target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope,
266
+ options: BioTemplateComponentSnippetOptions = {}
267
+ ): BioTemplateComponentSnippet[] {
268
+ const propsVariable = options.propsVariable ?? 'componentProps'
269
+ const usedNames = new Map<string, number>()
270
+
271
+ return toBioTemplateComponentProps(target).map((binding, index) => {
272
+ const propsName = uniquePropsName(binding, usedNames)
273
+ const propsExpression = `${propsVariable}[${index}].propsObject`
274
+ return {
275
+ ...cloneBinding(binding),
276
+ propsName,
277
+ propsExpression,
278
+ template: `<${binding.component} v-bind="${propsExpression}" />`,
279
+ }
280
+ })
281
+ }
282
+
283
+ export function toBioTemplateComponentUsage(
284
+ target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope,
285
+ options: BioTemplateComponentUsageOptions = {}
286
+ ): BioTemplateComponentUsage {
287
+ const propsVariable = options.propsVariable ?? 'componentProps'
288
+ const usesGeneratedTargetProp = options.targetExpression === undefined
289
+ const targetPropName = options.targetPropName ?? 'templateCollection'
290
+ const targetExpression = options.targetExpression ?? `props.${targetPropName}`
291
+ const componentProps = toBioTemplateComponentProps(target)
292
+ const snippets = toBioTemplateComponentSnippets(target, { propsVariable }).map(snippet => ({
293
+ ...snippet,
294
+ propsExpression: snippet.propsName,
295
+ template: `<${snippet.component} v-bind="${snippet.propsName}" />`,
296
+ }))
297
+ const propsDeclarations = snippets.map(
298
+ (snippet, index) => `const ${snippet.propsName} = ${propsVariable}[${index}].propsObject`
299
+ )
300
+ const setupLines = [
301
+ ...(usesGeneratedTargetProp
302
+ ? [`const props = defineProps<{ ${targetPropName}: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope }>()`]
303
+ : []),
304
+ `const ${propsVariable} = toBioTemplateComponentProps(${targetExpression})`,
305
+ ...propsDeclarations,
306
+ ]
307
+ const imports = toBioTemplateComponentImports(target)
308
+ const template = snippets.map(snippet => snippet.template).join('\n')
309
+
310
+ return {
311
+ imports,
312
+ componentProps,
313
+ snippets,
314
+ propsDeclarations,
315
+ scriptSetup: setupLines.join('\n'),
316
+ template,
317
+ sfc: toVueSfc(imports, setupLines, template, usesGeneratedTargetProp),
318
+ }
319
+ }
320
+
321
+ function propsForTemplate(template: BioTemplateEnvelope<unknown>): BioTemplateComponentPropsBinding[] {
322
+ switch (template.template_id) {
323
+ case 'plate-map':
324
+ return plateMapProps(template as PlateMapTemplate)
325
+ case 'sample-sheet':
326
+ return [
327
+ dataFrameProps(template as DataFrameTemplate),
328
+ sampleSelectorProps(template as SampleSheetTemplate),
329
+ ]
330
+ case 'sample-prep':
331
+ case 'calibration-curve':
332
+ case 'assay-matrix':
333
+ case 'flow-cytometry-panel':
334
+ case 'instrument-run':
335
+ return [dataFrameProps(template as DataFrameTemplate)]
336
+ case 'dose-response':
337
+ return doseResponseProps(template as DoseResponseTemplate)
338
+ case 'time-course':
339
+ return [
340
+ timelineProps(template as TimeCourseTemplate, toTimeCourseSteps(template as TimeCourseTemplate)),
341
+ dataFrameProps(template as DataFrameTemplate),
342
+ ]
343
+ case 'protocol-steps':
344
+ return [
345
+ timelineProps(template as ProtocolStepsTemplate, toProtocolSteps(template as ProtocolStepsTemplate), true),
346
+ dataFrameProps(template as DataFrameTemplate),
347
+ ]
348
+ case 'reagent-list':
349
+ return [
350
+ reagentListProps(template as ReagentListTemplate),
351
+ dataFrameProps(template as DataFrameTemplate),
352
+ ]
353
+ case 'qpcr-plate':
354
+ return qpcrPlateProps(template as QpcrPlateTemplate)
355
+ default:
356
+ return []
357
+ }
358
+ }
359
+
360
+ function plateMapProps(template: PlateMapTemplate): BioTemplateComponentPropsBinding[] {
361
+ const state = toPlateMapEditorState(template)
362
+ const plate = activePlateFromState(state)
363
+ return [
364
+ propsBinding('plate-map', 'PlateMapEditor', {
365
+ modelValue: state,
366
+ format: plate.format,
367
+ samples: state.samples,
368
+ }),
369
+ propsBinding('plate-map', 'WellPlate', {
370
+ format: plate.format,
371
+ wells: toWellPlateWells(template),
372
+ sampleColors: sampleColorsFromPlateState(state),
373
+ legendItems: legendItemsFromPlateState(state),
374
+ showLegend: true,
375
+ showSampleTypeIndicator: true,
376
+ }),
377
+ ]
378
+ }
379
+
380
+ function doseResponseProps(template: DoseResponseTemplate): BioTemplateComponentPropsBinding[] {
381
+ const conditions = toDoseConditions(template)
382
+ const layout = toDoseLayoutState(template)
383
+ const targetWells = layout ? Object.keys(activePlateFromState(layout).wells) : []
384
+ const props = [
385
+ propsBinding('dose-response', 'DoseCalculator', {
386
+ mode: 'serial',
387
+ targetWells,
388
+ disabled: conditions.length === 0,
389
+ }),
390
+ ]
391
+
392
+ if (layout) {
393
+ const plate = activePlateFromState(layout)
394
+ props.push(propsBinding('dose-response', 'WellPlate', {
395
+ format: plate.format,
396
+ wells: plate.wells,
397
+ sampleColors: sampleColorsFromPlateState(layout),
398
+ legendItems: conditions.map(condition => ({
399
+ type: condition.label,
400
+ label: condition.label,
401
+ color: condition.color,
402
+ })),
403
+ showLegend: true,
404
+ showSampleTypeIndicator: true,
405
+ }))
406
+ }
407
+
408
+ return props
409
+ }
410
+
411
+ function qpcrPlateProps(template: QpcrPlateTemplate): BioTemplateComponentPropsBinding[] {
412
+ const data = getTemplateData(template, 'qpcr-plate')
413
+ const frame = dataFrameProps(template)
414
+ return [
415
+ propsBinding('qpcr-plate', 'WellPlate', {
416
+ format: data.format,
417
+ wells: toQpcrWellPlateWells(template),
418
+ sampleColors: Object.fromEntries(data.samples.map((sample, index) => [sample.sampleId, colorForIndex(index)])),
419
+ legendItems: data.samples.map((sample, index): WellLegendItem => ({
420
+ type: sample.sampleId,
421
+ label: sample.name ?? sample.sampleId,
422
+ color: colorForIndex(index),
423
+ })),
424
+ showLegend: true,
425
+ showSampleTypeIndicator: true,
426
+ }),
427
+ frame,
428
+ ]
429
+ }
430
+
431
+ function dataFrameProps(template: DataFrameTemplate): BioTemplateComponentPropsBinding {
432
+ const frame = toTemplateDataFrame(template)
433
+ return propsBinding(template.template_id as TemplateId, 'DataFrame', {
434
+ ...frame,
435
+ searchable: true,
436
+ sortable: true,
437
+ size: 'sm',
438
+ })
439
+ }
440
+
441
+ function sampleSelectorProps(template: SampleSheetTemplate): BioTemplateComponentPropsBinding {
442
+ const options = toSampleOptions(template)
443
+ return propsBinding('sample-sheet', 'SampleSelector', {
444
+ samples: options.map(option => option.label),
445
+ modelValue: [],
446
+ })
447
+ }
448
+
449
+ function timelineProps(
450
+ template: TimeCourseTemplate | ProtocolStepsTemplate,
451
+ steps: unknown[],
452
+ editable = false
453
+ ): BioTemplateComponentPropsBinding {
454
+ return propsBinding(template.template_id as TemplateId, 'ExperimentTimeline', {
455
+ modelValue: steps,
456
+ orientation: 'vertical',
457
+ editable,
458
+ })
459
+ }
460
+
461
+ function reagentListProps(template: ReagentListTemplate): BioTemplateComponentPropsBinding {
462
+ return propsBinding('reagent-list', 'ReagentList', {
463
+ modelValue: toReagentListItems(template),
464
+ searchable: true,
465
+ sortable: true,
466
+ })
467
+ }
468
+
469
+ function propsBinding(
470
+ templateId: TemplateId,
471
+ component: string,
472
+ propsObject: Record<string, unknown>
473
+ ): BioTemplateComponentPropsBinding {
474
+ const matched = templateComponentBindings[templateId].find(binding => binding.component === component)
475
+ if (!matched) {
476
+ throw new Error(`No component binding registered for '${templateId}' and '${component}'.`)
477
+ }
478
+ return {
479
+ ...cloneBinding(matched),
480
+ propsObject,
481
+ }
482
+ }
483
+
484
+ function resolveComponentTemplateIds(target: BioTemplateComponentTarget): TemplateId[] {
485
+ if (typeof target === 'string') {
486
+ const preset = getBioTemplatePresetInfo(target)
487
+ if (preset) return [...preset.templates]
488
+ const pack = getBioTemplatePackInfo(target)
489
+ if (pack) return [...pack.templates]
490
+ const template = getBioTemplateInfo(target)
491
+ return template ? [template.template_id] : []
492
+ }
493
+
494
+ if (isTemplateEnvelope(target)) {
495
+ const template = getBioTemplateInfo(target.template_id)
496
+ return template ? [template.template_id] : []
497
+ }
498
+
499
+ const presetName = collectionPresetName(target)
500
+ if (presetName) {
501
+ const preset = getBioTemplatePresetInfo(presetName)
502
+ if (preset) return [...preset.templates]
503
+ }
504
+
505
+ const packName = collectionPackName(target)
506
+ if (packName) {
507
+ const pack = getBioTemplatePackInfo(packName)
508
+ if (pack) return [...pack.templates]
509
+ }
510
+
511
+ return Object.keys(extractTemplateCollection(target))
512
+ .map(templateId => getBioTemplateInfo(templateId)?.template_id)
513
+ .filter((templateId): templateId is TemplateId => templateId !== undefined)
514
+ }
515
+
516
+ function binding(
517
+ templateId: TemplateId,
518
+ component: string,
519
+ adapters: readonly string[],
520
+ props: readonly string[],
521
+ description: string
522
+ ): BioTemplateComponentBinding {
523
+ return {
524
+ id: `${templateId}:${component}`,
525
+ template_id: templateId,
526
+ component,
527
+ adapters,
528
+ props,
529
+ description,
530
+ importPath: '@morscherlab/mint-sdk',
531
+ }
532
+ }
533
+
534
+ function activePlateFromState(state: ReturnType<typeof toPlateMapEditorState>) {
535
+ return state.plates.find(plate => plate.id === state.activePlateId) ?? state.plates[0]
536
+ }
537
+
538
+ function sampleColorsFromPlateState(state: ReturnType<typeof toPlateMapEditorState>): Record<string, string> {
539
+ return Object.fromEntries(
540
+ state.samples
541
+ .filter(sample => sample.color)
542
+ .map(sample => [sample.id, sample.color as string])
543
+ )
544
+ }
545
+
546
+ function legendItemsFromPlateState(state: ReturnType<typeof toPlateMapEditorState>): WellLegendItem[] {
547
+ return state.samples.map((sample, index) => ({
548
+ type: sample.id,
549
+ label: sample.name,
550
+ color: sample.color ?? colorForIndex(index),
551
+ }))
552
+ }
553
+
554
+ function colorForIndex(index: number): string {
555
+ const colors = ['#3B82F6', '#10B981', '#EF4444', '#F59E0B', '#8B5CF6', '#F97316', '#06B6D4', '#14B8A6']
556
+ return colors[index % colors.length]
557
+ }
558
+
559
+ function cloneBindingMap<TName extends string>(
560
+ bindings: Record<TName, readonly BioTemplateComponentBinding[]>
561
+ ): Record<TName, BioTemplateComponentBinding[]> {
562
+ const result = {} as Record<TName, BioTemplateComponentBinding[]>
563
+ for (const name of Object.keys(bindings) as TName[]) {
564
+ result[name] = cloneBindings(bindings[name])
565
+ }
566
+ return result
567
+ }
568
+
569
+ function cloneBindings(bindings: readonly BioTemplateComponentBinding[]): BioTemplateComponentBinding[] {
570
+ return bindings.map(cloneBinding)
571
+ }
572
+
573
+ function cloneBinding(binding: BioTemplateComponentBinding): BioTemplateComponentBinding {
574
+ return {
575
+ ...binding,
576
+ adapters: [...binding.adapters],
577
+ props: [...binding.props],
578
+ }
579
+ }
580
+
581
+ function uniquePropsName(
582
+ binding: BioTemplateComponentBinding,
583
+ usedNames: Map<string, number>
584
+ ): string {
585
+ const base = toCamelIdentifier(`${binding.template_id} ${binding.component} props`)
586
+ const count = usedNames.get(base) ?? 0
587
+ usedNames.set(base, count + 1)
588
+ return count === 0 ? base : `${base}${count + 1}`
589
+ }
590
+
591
+ function toCamelIdentifier(value: string): string {
592
+ const words = value
593
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
594
+ .split(/[^A-Za-z0-9]+/)
595
+ .filter(Boolean)
596
+
597
+ if (words.length === 0) return 'componentProps'
598
+
599
+ return words
600
+ .map((word, index) => {
601
+ const lower = word.toLowerCase()
602
+ return index === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1)
603
+ })
604
+ .join('')
605
+ }
606
+
607
+ function toVueSfc(
608
+ imports: BioTemplateComponentImport[],
609
+ setupLines: string[],
610
+ template: string,
611
+ includeTargetTypeImport: boolean
612
+ ): string {
613
+ const importLines = [
614
+ ...imports.map(item => item.statement),
615
+ "import { toBioTemplateComponentProps } from '@morscherlab/mint-sdk/templates'",
616
+ ...(includeTargetTypeImport
617
+ ? ["import type { BioTemplateEnvelope, TemplateCollectionEnvelope } from '@morscherlab/mint-sdk/templates'"]
618
+ : []),
619
+ ]
620
+ const templateLines = template
621
+ ? template.split('\n').map(line => ` ${line}`).join('\n')
622
+ : ''
623
+
624
+ return [
625
+ '<script setup lang="ts">',
626
+ ...importLines,
627
+ '',
628
+ ...setupLines,
629
+ '</script>',
630
+ '',
631
+ '<template>',
632
+ templateLines,
633
+ '</template>',
634
+ ].join('\n')
635
+ }
636
+
637
+ function isTemplateEnvelope(value: unknown): value is BioTemplateEnvelope<unknown> {
638
+ return isRecord(value) && typeof value.template_id === 'string' && 'data' in value
639
+ }
640
+
641
+ function collectionPresetName(value: unknown): string | undefined {
642
+ if (!isRecord(value) || !isRecord(value.metadata)) return undefined
643
+ return typeof value.metadata.preset === 'string' ? value.metadata.preset : undefined
644
+ }
645
+
646
+ function collectionPackName(value: unknown): string | undefined {
647
+ if (!isRecord(value) || !isRecord(value.metadata)) return undefined
648
+ return typeof value.metadata.pack === 'string' ? value.metadata.pack : undefined
649
+ }
650
+
651
+ function isRecord(value: unknown): value is Record<string, unknown> {
652
+ return typeof value === 'object' && value !== null
653
+ }