@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,62 @@
1
+ <script setup lang="ts">
2
+ /** Preferred toast container driven by useToast, with auto-dismiss progress bar and click-to-close on each notification. */
3
+ import { useToast } from '../composables/useToast'
4
+ import type { Toast } from '../types'
5
+
6
+ const { toasts, dismiss } = useToast()
7
+
8
+ const accentColors: Record<Toast['type'], string> = {
9
+ success: '#10B981',
10
+ error: '#EF4444',
11
+ warning: '#F59E0B',
12
+ info: '#3B82F6',
13
+ }
14
+ </script>
15
+
16
+ <template>
17
+ <Teleport to="body">
18
+ <div class="mint-toast__container">
19
+ <TransitionGroup name="toast">
20
+ <div
21
+ v-for="toast in toasts"
22
+ :key="toast.id"
23
+ :class="['mint-toast__item', `mint-toast__item--${toast.type}`]"
24
+ :style="{
25
+ borderLeftColor: accentColors[toast.type],
26
+ }"
27
+ role="alert"
28
+ @click="dismiss(toast.id)"
29
+ >
30
+ <!-- Lucide circle-check -->
31
+ <svg v-if="toast.type === 'success'" class="mint-toast__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" :style="{ color: accentColors[toast.type] }">
32
+ <circle cx="12" cy="12" r="10" /><path d="m9 12 2 2 4-4" />
33
+ </svg>
34
+ <!-- Lucide circle-x -->
35
+ <svg v-else-if="toast.type === 'error'" class="mint-toast__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" :style="{ color: accentColors[toast.type] }">
36
+ <circle cx="12" cy="12" r="10" /><path d="m15 9-6 6" /><path d="m9 9 6 6" />
37
+ </svg>
38
+ <!-- Lucide triangle-alert -->
39
+ <svg v-else-if="toast.type === 'warning'" class="mint-toast__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" :style="{ color: accentColors[toast.type] }">
40
+ <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /><path d="M12 9v4" /><path d="M12 17h.01" />
41
+ </svg>
42
+ <!-- Lucide info -->
43
+ <svg v-else class="mint-toast__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" :style="{ color: accentColors[toast.type] }">
44
+ <circle cx="12" cy="12" r="10" /><path d="M12 16v-4" /><path d="M12 8h.01" />
45
+ </svg>
46
+ <span class="mint-toast__message">{{ toast.message }}</span>
47
+ <span
48
+ class="mint-toast__progress"
49
+ :style="{
50
+ backgroundColor: accentColors[toast.type],
51
+ animationDuration: `${toast.duration ?? 3500}ms`,
52
+ }"
53
+ />
54
+ </div>
55
+ </TransitionGroup>
56
+ </div>
57
+ </Teleport>
58
+ </template>
59
+
60
+ <style>
61
+ @import '../styles/components/toast.css';
62
+ </style>
@@ -3,8 +3,6 @@ import AppTopBar from './AppTopBar.vue'
3
3
  import { ExperimentProvider } from '../__stories__/experiment-helpers'
4
4
  import type {
5
5
  TopBarVariant,
6
- TopBarPage,
7
- TopBarTab,
8
6
  PillNavItem,
9
7
  PageSelectorItem,
10
8
  AccountMenuItem,
@@ -35,27 +33,6 @@ const sampleAccountMenu: AccountMenuItem[] = [
35
33
  { id: 'admin', label: 'Admin', to: '/admin' },
36
34
  ]
37
35
 
38
- const samplePages: TopBarPage[] = [
39
- { id: 'dashboard', label: 'Dashboard', description: 'Overview and statistics' },
40
- { id: 'experiments', label: 'Experiments', description: 'Manage experiments' },
41
- { id: 'analysis', label: 'Analysis', description: 'Run analysis pipelines' },
42
- { id: 'settings', label: 'Settings', disabled: true },
43
- ]
44
-
45
- const sampleTabs: TopBarTab[] = [
46
- { id: 'overview', label: 'Overview' },
47
- { id: 'results', label: 'Results' },
48
- {
49
- id: 'export',
50
- label: 'Export',
51
- children: [
52
- { id: 'export-csv', label: 'CSV', description: 'Comma-separated values' },
53
- { id: 'export-xlsx', label: 'Excel', description: 'Microsoft Excel format' },
54
- { id: 'export-pdf', label: 'PDF', description: 'Portable Document Format' },
55
- ],
56
- },
57
- ]
58
-
59
36
  </script>
60
37
 
61
38
  <template>
@@ -214,7 +191,7 @@ const sampleTabs: TopBarTab[] = [
214
191
  </div>
215
192
  </Variant>
216
193
 
217
- <!-- ─────────────── CLASSIC VARIANTS (classic prop surface) ─────────────── -->
194
+ <!-- ─────────────── CLASSIC VARIANTS ─────────────── -->
218
195
 
219
196
  <Variant title="Classic · Playground">
220
197
  <template #default="{ state }">
@@ -254,13 +231,13 @@ const sampleTabs: TopBarTab[] = [
254
231
  </div>
255
232
  </Variant>
256
233
 
257
- <Variant title="Classic · Breadcrumb with pages">
234
+ <Variant title="Classic · Page selector">
258
235
  <div style="padding: 2rem;">
259
236
  <AppTopBar
260
237
  plugin-name="IC50 Calculator"
261
238
  title="Results"
262
- :pages="samplePages"
263
- current-page-id="analysis"
239
+ :page-selector="samplePageSelector"
240
+ current-page-selector-id="plugins"
264
241
  variant="card"
265
242
  :show-theme-toggle="true"
266
243
  home-path=""
@@ -268,13 +245,13 @@ const sampleTabs: TopBarTab[] = [
268
245
  </div>
269
246
  </Variant>
270
247
 
271
- <Variant title="Classic · Tabs">
248
+ <Variant title="Classic · Pill nav">
272
249
  <div style="padding: 2rem;">
273
250
  <AppTopBar
274
251
  plugin-name="Plate Analyzer"
275
252
  title="Experiment View"
276
- :tabs="sampleTabs"
277
- current-tab-id="overview"
253
+ :pill-nav="samplePillNav.slice(0, 3)"
254
+ current-pill-id="workspace"
278
255
  variant="card"
279
256
  home-path=""
280
257
  />
@@ -1,18 +1,27 @@
1
1
  <script setup lang="ts">
2
2
  /** Full application top bar with brand logo, page selector or plugin switcher, centered pill nav, experiment popover, and avatar menu. */
3
- import { ref, computed, inject, onMounted, onUnmounted } from 'vue'
3
+ import { ref, computed, inject } from 'vue'
4
4
  import type {
5
5
  TopBarPage,
6
+ TopBarPageInput,
6
7
  TopBarTab,
8
+ TopBarTabInput,
7
9
  TopBarTabOption,
10
+ TopBarTabOptionInput,
8
11
  TopBarSettingsConfig,
9
12
  TopBarVariant,
10
13
  PillNavItem,
14
+ PillNavItemInput,
11
15
  PageSelectorItem,
16
+ PageSelectorItemInput,
12
17
  PluginSwitcherInfo,
13
18
  PluginSwitcherPlugin,
14
19
  AccountMenuItem,
20
+ SettingsTab,
15
21
  } from '../types/components'
22
+ import type { PluginNavItem } from '../types/platform'
23
+ import { normalizeItemInput } from '../utils/items'
24
+ import { isPluginIconFormat } from '../utils/pluginIcon'
16
25
  import ThemeToggle from './ThemeToggle.vue'
17
26
  import SettingsModal from './SettingsModal.vue'
18
27
  import ExperimentPopover from './ExperimentPopover.vue'
@@ -24,45 +33,72 @@ import AppPluginSwitcher from './AppPluginSwitcher.vue'
24
33
  import PluginIcon from './PluginIcon.vue'
25
34
  import { usePlatformContext } from '../composables/usePlatformContext'
26
35
  import { APP_EXPERIMENT_KEY } from '../composables/useAppExperiment'
36
+ import { useEventListener } from '../composables/useEventListener'
37
+ import { useDropdownState } from '../composables/useDropdownState'
27
38
 
28
39
  interface Props {
29
- // Classic title & breadcrumb
40
+ /** App or plugin title shown in the left title group when no page selector is present. */
30
41
  title?: string
42
+ /** Secondary title copy shown under the title in title-group layouts. */
31
43
  subtitle?: string
44
+ /** Show the default MINT logo when the icon/logo slot is not provided. */
32
45
  showLogo?: boolean
46
+ /** Top bar visual treatment. */
33
47
  variant?: TopBarVariant
34
- pluginName?: string
35
- pages?: TopBarPage[]
36
- currentPageId?: string
37
- tabs?: TopBarTab[]
38
- currentTabId?: string
48
+ /** Home link used by classic breadcrumb layouts. */
39
49
  homePath?: string
40
50
 
41
- // New layout left page selector / plugin switcher
42
- pageSelector?: PageSelectorItem[]
51
+ /** Preferred route-level page switch entries for plugin and platform pages. */
52
+ pageSelector?: PageSelectorItemInput[]
53
+ /** Active id for the preferred page selector. */
43
54
  currentPageSelectorId?: string
55
+ /** Optional plugin switcher shown in the left navigation position instead of pageSelector. */
44
56
  pluginSwitcher?: PluginSwitcherInfo
45
57
 
46
- // New layout centered pill nav
47
- pillNav?: PillNavItem[]
58
+ /** Preferred centered navigation for local modes inside the current route. */
59
+ pillNav?: PillNavItemInput[]
60
+ /** Active id for the preferred centered pill navigation. */
48
61
  currentPillId?: string
49
62
 
50
- // New right cluster avatar menu + optional notifications
63
+ /** Account dropdown entries. Takes precedence over the classic profile button. */
51
64
  accountMenu?: AccountMenuItem[]
65
+ /** Show the notifications icon button. */
52
66
  showNotifications?: boolean
67
+ /** Draw a notification dot on the notifications icon. */
53
68
  hasNotificationDot?: boolean
54
69
 
55
- // Classic right cluster (still works; AvatarMenu takes precedence over Profile)
70
+ /** Compatibility breadcrumb plugin name. Prefer pageSelector for route-level pages. */
71
+ pluginName?: string
72
+ /** Compatibility page dropdown items. Prefer pageSelector for new route-level navigation. */
73
+ pages?: TopBarPageInput[]
74
+ /** Active id for the compatibility page dropdown. */
75
+ currentPageId?: string
76
+ /** Compatibility center tabs. Prefer pillNav for new in-page modes. */
77
+ tabs?: TopBarTabInput[]
78
+ /** Active id for compatibility center tabs. */
79
+ currentTabId?: string
80
+
81
+ /** Show the theme toggle button. */
56
82
  showThemeToggle?: boolean
83
+ /** Show the settings button and modal. */
57
84
  showSettings?: boolean
85
+ /** Built-in SettingsModal configuration. */
58
86
  settingsConfig?: TopBarSettingsConfig
87
+ /** Show the standalone badge when the plugin is not integrated into the platform. */
59
88
  showStandaloneLabel?: boolean
89
+ /** Custom standalone badge label. */
60
90
  standaloneLabel?: string
91
+ /** Show the classic admin shortcut. */
61
92
  showAdmin?: boolean
93
+ /** Route used by the classic admin shortcut. */
62
94
  adminPath?: string
95
+ /** Show the classic profile button when accountMenu is not provided. */
63
96
  showProfile?: boolean
97
+ /** Classic profile display name. */
64
98
  userName?: string
99
+ /** Explicit classic profile initial. */
65
100
  userInitial?: string
101
+ /** Classic profile email, used by avatar/account layouts. */
66
102
  userEmail?: string
67
103
  }
68
104
 
@@ -111,21 +147,22 @@ const profileInitial = computed(() => {
111
147
  return 'U'
112
148
  })
113
149
 
150
+ const hasPlatformPages = computed(() => props.pages === undefined && !!plugin.value?.nav_items?.length)
151
+ const effectivePluginName = computed(() => props.pluginName ?? (hasPlatformPages.value ? plugin.value?.name : '') ?? '')
114
152
  const hasPageSelector = computed(() => !!props.pageSelector?.length)
115
153
  const hasPluginSwitcher = computed(() => !!props.pluginSwitcher)
116
154
  const hasPillNav = computed(() => !!props.pillNav?.length)
117
155
  const hasAccountMenu = computed(() => !!props.accountMenu?.length)
118
156
  const hasLegacyBreadcrumb = computed(
119
- () => !hasPageSelector.value && !hasPluginSwitcher.value && (!!props.pluginName || !!props.pages?.length),
157
+ () => !hasPageSelector.value && !hasPluginSwitcher.value && (!!effectivePluginName.value || normalizedPages.value.length > 0),
120
158
  )
121
159
  const hasTitleGroup = computed(
122
160
  () =>
123
161
  !hasPageSelector.value &&
124
162
  !hasPluginSwitcher.value &&
163
+ !hasLegacyBreadcrumb.value &&
125
164
  !!props.title &&
126
- !!props.subtitle &&
127
- !props.pluginName &&
128
- !props.pages?.length,
165
+ !!props.subtitle
129
166
  )
130
167
  const hasTitleOnly = computed(
131
168
  () =>
@@ -135,25 +172,104 @@ const hasTitleOnly = computed(
135
172
  !hasLegacyBreadcrumb.value &&
136
173
  !!props.title,
137
174
  )
175
+ const normalizedPages = computed<TopBarPage[]>(() =>
176
+ props.pages !== undefined
177
+ ? props.pages.map(normalizeItemInput)
178
+ : plugin.value?.nav_items?.map(pluginNavItemToPage) ?? [],
179
+ )
180
+ const normalizedTabs = computed<TopBarTab[]>(() => props.tabs?.map(normalizeTopBarTabInput) ?? [])
181
+ const normalizedPageSelector = computed<PageSelectorItem[]>(() => props.pageSelector?.map(normalizeItemInput) ?? [])
182
+ const normalizedPillNav = computed<PillNavItem[]>(() => props.pillNav?.map(normalizeItemInput) ?? [])
183
+ const normalizedSettingsTabs = computed<SettingsTab[]>(() =>
184
+ props.settingsConfig?.tabs?.map(normalizeItemInput) ?? [],
185
+ )
186
+ const effectiveCurrentPageId = computed(() =>
187
+ props.currentPageId
188
+ ?? (hasPlatformPages.value ? currentPageIdFromLocation(normalizedPages.value) : undefined)
189
+ ?? (hasPlatformPages.value ? normalizedPages.value[0]?.id : undefined),
190
+ )
191
+ const currentLegacyPage = computed(() =>
192
+ normalizedPages.value.find((page) => page.id === effectiveCurrentPageId.value),
193
+ )
194
+ const effectiveTitle = computed(() =>
195
+ props.title ?? (hasPlatformPages.value ? currentLegacyPage.value?.label : undefined),
196
+ )
138
197
 
139
- const showPagesDropdown = ref(false)
140
- const dropdownRef = ref<HTMLElement | null>(null)
198
+ const {
199
+ isOpen: showPagesDropdown,
200
+ rootRef: dropdownRef,
201
+ close: closePagesDropdown,
202
+ toggle: togglePagesDropdownBase,
203
+ } = useDropdownState()
141
204
  const openTabDropdown = ref<string | null>(null)
142
205
  const tabDropdownRefs = ref<Map<string, HTMLElement>>(new Map())
143
206
 
144
207
  function togglePagesDropdown() {
145
- showPagesDropdown.value = !showPagesDropdown.value
208
+ togglePagesDropdownBase()
146
209
  openTabDropdown.value = null
147
210
  }
148
211
 
212
+ function normalizeTopBarTabInput(tab: TopBarTabInput): TopBarTab {
213
+ const normalized = normalizeItemInput<Omit<TopBarTab, 'children'> & { children?: TopBarTabOptionInput[] }>(tab)
214
+ return {
215
+ ...normalized,
216
+ children: normalized.children?.map(normalizeItemInput),
217
+ }
218
+ }
219
+
220
+ function isSvgTabIcon(icon?: string): icon is string {
221
+ return !!icon && (icon.startsWith('M') || icon.startsWith('m'))
222
+ }
223
+
224
+ function normalizeNavPath(path?: string): string {
225
+ const raw = path?.trim() || '/'
226
+ const prefixed = raw.startsWith('/') ? raw : `/${raw}`
227
+ return prefixed.replace(/\/+$/, '') || '/'
228
+ }
229
+
230
+ function pageIdFromPath(path: string, fallback: string): string {
231
+ const normalized = normalizeNavPath(path)
232
+ if (normalized === '/') return 'dashboard'
233
+ return normalized.replace(/^\/+/, '').replace(/\/+/g, '-') || fallback
234
+ }
235
+
236
+ function pluginNavItemToPage(item: PluginNavItem, index: number): TopBarPage {
237
+ const path = normalizeNavPath(item.path)
238
+ return {
239
+ id: item.id || pageIdFromPath(path, `page-${index + 1}`),
240
+ label: item.label,
241
+ to: path,
242
+ icon: item.icon || plugin.value?.icon,
243
+ description: item.description,
244
+ }
245
+ }
246
+
247
+ function currentPagePath(): string | undefined {
248
+ if (typeof window === 'undefined') return undefined
249
+ const pathname = normalizeNavPath(window.location.pathname)
250
+ const routePrefix = normalizeNavPath(plugin.value?.route_prefix)
251
+
252
+ if (pathname === routePrefix) return '/'
253
+ if (routePrefix !== '/' && pathname.startsWith(`${routePrefix}/`)) {
254
+ return normalizeNavPath(pathname.slice(routePrefix.length))
255
+ }
256
+ return pathname
257
+ }
258
+
259
+ function currentPageIdFromLocation(pages: TopBarPage[]): string | undefined {
260
+ const path = currentPagePath()
261
+ if (!path) return undefined
262
+ return pages.find((page) => normalizeNavPath(page.to || page.href) === path)?.id
263
+ }
264
+
149
265
  function handlePageClick(page: TopBarPage) {
150
266
  if (page.disabled) return
151
267
  emit('page-select', page)
152
- showPagesDropdown.value = false
268
+ closePagesDropdown()
153
269
  }
154
270
 
155
271
  function toggleTabDropdown(tabId: string) {
156
- showPagesDropdown.value = false
272
+ closePagesDropdown()
157
273
  openTabDropdown.value = openTabDropdown.value === tabId ? null : tabId
158
274
  }
159
275
 
@@ -184,10 +300,6 @@ function setTabDropdownRef(el: HTMLElement | null, tabId: string) {
184
300
  function handleClickOutside(event: MouseEvent) {
185
301
  const target = event.target as Node
186
302
 
187
- if (showPagesDropdown.value && dropdownRef.value && !dropdownRef.value.contains(target)) {
188
- showPagesDropdown.value = false
189
- }
190
-
191
303
  if (openTabDropdown.value !== null) {
192
304
  const clickedInside = Array.from(tabDropdownRefs.value.values()).some((el) => el.contains(target))
193
305
  if (!clickedInside) {
@@ -196,13 +308,7 @@ function handleClickOutside(event: MouseEvent) {
196
308
  }
197
309
  }
198
310
 
199
- onMounted(() => {
200
- document.addEventListener('click', handleClickOutside)
201
- })
202
-
203
- onUnmounted(() => {
204
- document.removeEventListener('click', handleClickOutside)
205
- })
311
+ useEventListener(() => document, 'click', handleClickOutside)
206
312
  </script>
207
313
 
208
314
  <template>
@@ -293,8 +399,8 @@ onUnmounted(() => {
293
399
  @install-click="emit('plugin-switcher-install')"
294
400
  />
295
401
  <AppPageSelector
296
- v-else-if="hasPageSelector && pageSelector"
297
- :pages="pageSelector"
402
+ v-else-if="hasPageSelector"
403
+ :pages="normalizedPageSelector"
298
404
  :current-page-id="currentPageSelectorId"
299
405
  @select="emit('page-selector-select', $event)"
300
406
  >
@@ -314,12 +420,12 @@ onUnmounted(() => {
314
420
 
315
421
  <div v-else-if="hasLegacyBreadcrumb" ref="dropdownRef" class="mint-topbar-breadcrumb">
316
422
  <button
317
- v-if="pages?.length"
423
+ v-if="normalizedPages.length"
318
424
  type="button"
319
425
  class="mint-topbar-plugin-name"
320
426
  @click.stop="togglePagesDropdown"
321
427
  >
322
- {{ pluginName }}
428
+ {{ effectivePluginName }}
323
429
  <svg
324
430
  class="mint-topbar-chevron"
325
431
  :class="{ 'mint-topbar-chevron--open': showPagesDropdown }"
@@ -335,10 +441,10 @@ onUnmounted(() => {
335
441
  <path d="m6 9 6 6 6-6" />
336
442
  </svg>
337
443
  </button>
338
- <span v-else class="mint-topbar-plugin-name--static">{{ pluginName }}</span>
444
+ <span v-else class="mint-topbar-plugin-name--static">{{ effectivePluginName }}</span>
339
445
 
340
446
  <svg
341
- v-if="title"
447
+ v-if="effectiveTitle"
342
448
  class="mint-topbar-separator"
343
449
  width="16"
344
450
  height="16"
@@ -351,36 +457,69 @@ onUnmounted(() => {
351
457
  >
352
458
  <path d="m9 18 6-6-6-6" />
353
459
  </svg>
354
- <span v-if="title" class="mint-topbar-current-page">{{ title }}</span>
460
+ <span v-if="effectiveTitle" class="mint-topbar-current-page">{{ effectiveTitle }}</span>
355
461
 
356
462
  <div v-show="showPagesDropdown" class="mint-topbar-dropdown">
357
- <template v-for="page in pages" :key="page.id">
463
+ <template v-for="page in normalizedPages" :key="page.id">
358
464
  <a
359
465
  v-if="page.href"
360
466
  :href="page.href"
361
- :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === currentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
362
- @click="showPagesDropdown = false"
467
+ :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === effectiveCurrentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
468
+ @click="closePagesDropdown"
363
469
  >
364
- <span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
365
- <span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
470
+ <span class="mint-topbar-dropdown-item__page">
471
+ <PluginIcon
472
+ v-if="isPluginIconFormat(page.icon)"
473
+ class="mint-topbar-dropdown-item__icon"
474
+ :icon="page.icon"
475
+ size="sm"
476
+ variant="tinted"
477
+ />
478
+ <span class="mint-topbar-dropdown-item__copy">
479
+ <span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
480
+ <span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
481
+ </span>
482
+ </span>
366
483
  </a>
367
484
  <router-link
368
485
  v-else-if="page.to"
369
486
  :to="page.to"
370
- :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === currentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
371
- @click="showPagesDropdown = false"
487
+ :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === effectiveCurrentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
488
+ @click="closePagesDropdown"
372
489
  >
373
- <span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
374
- <span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
490
+ <span class="mint-topbar-dropdown-item__page">
491
+ <PluginIcon
492
+ v-if="isPluginIconFormat(page.icon)"
493
+ class="mint-topbar-dropdown-item__icon"
494
+ :icon="page.icon"
495
+ size="sm"
496
+ variant="tinted"
497
+ />
498
+ <span class="mint-topbar-dropdown-item__copy">
499
+ <span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
500
+ <span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
501
+ </span>
502
+ </span>
375
503
  </router-link>
376
504
  <button
377
505
  v-else
378
506
  type="button"
379
- :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === currentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
507
+ :class="['mint-topbar-dropdown-item', { 'mint-topbar-dropdown-item--active': page.id === effectiveCurrentPageId, 'mint-topbar-dropdown-item--disabled': page.disabled }]"
380
508
  @click="handlePageClick(page)"
381
509
  >
382
- <span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
383
- <span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
510
+ <span class="mint-topbar-dropdown-item__page">
511
+ <PluginIcon
512
+ v-if="isPluginIconFormat(page.icon)"
513
+ class="mint-topbar-dropdown-item__icon"
514
+ :icon="page.icon"
515
+ size="sm"
516
+ variant="tinted"
517
+ />
518
+ <span class="mint-topbar-dropdown-item__copy">
519
+ <span class="mint-topbar-dropdown-item__label">{{ page.label }}</span>
520
+ <span v-if="page.description" class="mint-topbar-dropdown-item__description">{{ page.description }}</span>
521
+ </span>
522
+ </span>
384
523
  </button>
385
524
  </template>
386
525
  </div>
@@ -396,7 +535,7 @@ onUnmounted(() => {
396
535
  <slot name="center">
397
536
  <AppPillNav
398
537
  v-if="hasPillNav && pillNav"
399
- :items="pillNav"
538
+ :items="normalizedPillNav"
400
539
  :current-item-id="currentPillId"
401
540
  @select="emit('pill-select', $event)"
402
541
  />
@@ -406,9 +545,9 @@ onUnmounted(() => {
406
545
  <!-- Center: classic tabs (when no pillNav) — wrapped in the same centered
407
546
  container as AppPillNav so classic :tabs consumers get centered pill
408
547
  layout without migrating to :pill-nav. -->
409
- <div v-if="!hasPillNav && tabs?.length" class="mint-topbar__center">
548
+ <div v-if="!hasPillNav && normalizedTabs.length" class="mint-topbar__center">
410
549
  <div class="mint-topbar__tabs">
411
- <template v-for="tab in tabs" :key="tab.id">
550
+ <template v-for="tab in normalizedTabs" :key="tab.id">
412
551
  <div
413
552
  :ref="(el) => tab.children?.length ? setTabDropdownRef(el as HTMLElement, tab.id) : null"
414
553
  class="mint-topbar-tab-wrapper"
@@ -423,6 +562,19 @@ onUnmounted(() => {
423
562
  ]"
424
563
  @click.stop="handleTabClick(tab)"
425
564
  >
565
+ <svg
566
+ v-if="isSvgTabIcon(tab.icon)"
567
+ class="mint-topbar-tab-icon"
568
+ viewBox="0 0 24 24"
569
+ fill="none"
570
+ stroke="currentColor"
571
+ stroke-width="2"
572
+ stroke-linecap="round"
573
+ stroke-linejoin="round"
574
+ aria-hidden="true"
575
+ >
576
+ <path :d="tab.icon" />
577
+ </svg>
426
578
  {{ tab.label }}
427
579
  <svg
428
580
  class="mint-topbar-tab-chevron"
@@ -449,6 +601,19 @@ onUnmounted(() => {
449
601
  { 'mint-topbar-tab--disabled': tab.disabled }
450
602
  ]"
451
603
  >
604
+ <svg
605
+ v-if="isSvgTabIcon(tab.icon)"
606
+ class="mint-topbar-tab-icon"
607
+ viewBox="0 0 24 24"
608
+ fill="none"
609
+ stroke="currentColor"
610
+ stroke-width="2"
611
+ stroke-linecap="round"
612
+ stroke-linejoin="round"
613
+ aria-hidden="true"
614
+ >
615
+ <path :d="tab.icon" />
616
+ </svg>
452
617
  {{ tab.label }}
453
618
  </a>
454
619
  <router-link
@@ -460,6 +625,19 @@ onUnmounted(() => {
460
625
  { 'mint-topbar-tab--disabled': tab.disabled }
461
626
  ]"
462
627
  >
628
+ <svg
629
+ v-if="isSvgTabIcon(tab.icon)"
630
+ class="mint-topbar-tab-icon"
631
+ viewBox="0 0 24 24"
632
+ fill="none"
633
+ stroke="currentColor"
634
+ stroke-width="2"
635
+ stroke-linecap="round"
636
+ stroke-linejoin="round"
637
+ aria-hidden="true"
638
+ >
639
+ <path :d="tab.icon" />
640
+ </svg>
463
641
  {{ tab.label }}
464
642
  </router-link>
465
643
  <button
@@ -472,6 +650,19 @@ onUnmounted(() => {
472
650
  ]"
473
651
  @click="handleTabClick(tab)"
474
652
  >
653
+ <svg
654
+ v-if="isSvgTabIcon(tab.icon)"
655
+ class="mint-topbar-tab-icon"
656
+ viewBox="0 0 24 24"
657
+ fill="none"
658
+ stroke="currentColor"
659
+ stroke-width="2"
660
+ stroke-linecap="round"
661
+ stroke-linejoin="round"
662
+ aria-hidden="true"
663
+ >
664
+ <path :d="tab.icon" />
665
+ </svg>
475
666
  {{ tab.label }}
476
667
  </button>
477
668
 
@@ -634,16 +825,19 @@ onUnmounted(() => {
634
825
  v-if="showSettings"
635
826
  v-model="settingsOpen"
636
827
  :title="settingsConfig?.title"
637
- :tabs="settingsConfig?.tabs"
828
+ :tabs="normalizedSettingsTabs"
638
829
  :show-appearance="settingsConfig?.showAppearance ?? true"
639
830
  :size="settingsConfig?.size"
640
831
  :layout="settingsConfig?.layout"
641
832
  :schema="settingsConfig?.schema"
833
+ :model="settingsConfig?.model"
834
+ :controls="settingsConfig?.controls"
835
+ :control-options="settingsConfig?.controlOptions"
642
836
  :values="settingsConfig?.values"
643
837
  :enhancements="settingsConfig?.enhancements"
644
838
  @update:values="emit('settings-values-change', $event)"
645
839
  >
646
- <template v-for="tab in (settingsConfig?.tabs ?? [])" :key="tab.id" #[`tab-${tab.id}`]>
840
+ <template v-for="tab in normalizedSettingsTabs" :key="tab.id" #[`tab-${tab.id}`]>
647
841
  <slot :name="`settings-tab-${tab.id}`" />
648
842
  </template>
649
843
  <template #appearance>