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

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