@morscherlab/mint-sdk 1.4.0-beta.2 → 1.4.0-beta.20

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 (330) hide show
  1. package/README.md +14 -3
  2. package/dist/{ActionMenu-CScINahU.js → AlertBox-CzLg0UaL.js} +40 -209
  3. package/dist/AlertBox-CzLg0UaL.js.map +1 -0
  4. package/dist/{AppSidebar-BmiiPfz9.js → AppSidebar-6oTT-gPO.js} +1249 -239
  5. package/dist/AppSidebar-6oTT-gPO.js.map +1 -0
  6. package/dist/{ConfirmDialog-CJ7hqYUP.js → ConfirmDialog-CSls2Nuv.js} +2 -2
  7. package/dist/{ConfirmDialog-CJ7hqYUP.js.map → ConfirmDialog-CSls2Nuv.js.map} +1 -1
  8. package/dist/ExperimentPopover-9l4QZlvQ.js +337 -0
  9. package/dist/ExperimentPopover-9l4QZlvQ.js.map +1 -0
  10. package/dist/{ExperimentSelectorModal-D-9z7cNx.js → ExperimentSelectorModal-pR5fK3pA.js} +244 -211
  11. package/dist/ExperimentSelectorModal-pR5fK3pA.js.map +1 -0
  12. package/dist/{SegmentedControl-DERBN-Xd.js → SegmentedControl-CJ7EAmOq.js} +2 -2
  13. package/dist/{SegmentedControl-DERBN-Xd.js.map → SegmentedControl-CJ7EAmOq.js.map} +1 -1
  14. package/dist/{SettingsModal-CSLE31yg.js → SettingsModal-Dyitgzfp.js} +28 -27
  15. package/dist/SettingsModal-Dyitgzfp.js.map +1 -0
  16. package/dist/__stories__/experiment-helpers.d.ts +10 -0
  17. package/dist/__tests__/components/AppTopBarOnboarding.test.d.ts +1 -0
  18. package/dist/__tests__/composables/useOnboarding.test.d.ts +1 -0
  19. package/dist/__tests__/styles/paletteTokens.test.d.ts +1 -0
  20. package/dist/__tests__/styles/v2AuditRules.test.d.ts +1 -0
  21. package/dist/components/AppSidebar.vue.d.ts +1 -1
  22. package/dist/components/AppTopBar.vue.d.ts +20 -5
  23. package/dist/components/ArtifactSaveDialog.vue.d.ts +9 -1
  24. package/dist/components/AuditTrail.vue.d.ts +1 -1
  25. package/dist/components/BaseButton.vue.d.ts +4 -2
  26. package/dist/components/BaseSelect.vue.d.ts +2 -2
  27. package/dist/components/ChartContainer.vue.d.ts +2 -0
  28. package/dist/components/DatePicker.vue.d.ts +1 -1
  29. package/dist/components/ExperimentPopover.vue.d.ts +18 -0
  30. package/dist/components/ExperimentSelectorModal.vue.d.ts +15 -5
  31. package/dist/components/FilePicker.vue.d.ts +1 -1
  32. package/dist/components/FileUploader.vue.d.ts +2 -2
  33. package/dist/components/JobsStatusTray.vue.d.ts +15 -4
  34. package/dist/components/PluginIcon.vue.d.ts +2 -2
  35. package/dist/components/PluginWorkspaceView.props.d.ts +3 -0
  36. package/dist/components/PluginWorkspaceView.vue.d.ts +3 -0
  37. package/dist/components/SampleSelector.vue.d.ts +6 -6
  38. package/dist/components/SequenceProgressBar.vue.d.ts +3 -0
  39. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +1 -1
  40. package/dist/components/StepWizard.vue.d.ts +3 -0
  41. package/dist/components/internal/AppTopBarOnboardingInternal.vue.d.ts +7 -0
  42. package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -1
  43. package/dist/components/internal/AutoGroupModalInternal.vue.d.ts +1 -1
  44. package/dist/components/internal/DatePickerTimeInternal.vue.d.ts +1 -1
  45. package/dist/components/internal/OnboardingLayerInternal.vue.d.ts +7 -0
  46. package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +2 -7
  47. package/dist/components/internal/SelectMultipleInternal.vue.d.ts +4 -0
  48. package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +8 -0
  49. package/dist/composables/createClient.d.ts +2 -0
  50. package/dist/composables/index.d.ts +1 -0
  51. package/dist/composables/pluginIdentity.d.ts +24 -0
  52. package/dist/composables/pluginWorkspaceModel.d.ts +2 -2
  53. package/dist/composables/useAppExperiment.d.ts +8 -0
  54. package/dist/composables/useCalendarGrid.d.ts +1 -1
  55. package/dist/composables/useExperimentSelector.d.ts +6 -2
  56. package/dist/composables/useJobCenter.d.ts +41 -3
  57. package/dist/composables/useOnboarding.d.ts +119 -0
  58. package/dist/composables/usePlatform.d.ts +1 -0
  59. package/dist/{controlSchemaModel-CP5-_nAj.js → controlSchemaModel-DCUOx8ll.js} +2 -2
  60. package/dist/{controlSchemaModel-CP5-_nAj.js.map → controlSchemaModel-DCUOx8ll.js.map} +1 -1
  61. package/dist/{data-C2PwI_dn.js → data-DBnmeBZX.js} +179 -125
  62. package/dist/data-DBnmeBZX.js.map +1 -0
  63. package/dist/data.js +8 -8
  64. package/dist/{experiment-43OJtH7R.js → experiment-DyO54FQB.js} +25 -61
  65. package/dist/experiment-DyO54FQB.js.map +1 -0
  66. package/dist/{experimentStatus-BZmU4czV.js → experimentStatus-BFrKpHAH.js} +3 -3
  67. package/dist/{experimentStatus-BZmU4czV.js.map → experimentStatus-BFrKpHAH.js.map} +1 -1
  68. package/dist/index.js +20 -20
  69. package/dist/{install-DAID6c2-.js → install-qjELyNwc.js} +15 -14
  70. package/dist/{install-DAID6c2-.js.map → install-qjELyNwc.js.map} +1 -1
  71. package/dist/install.js +1 -1
  72. package/dist/{lab-DLwRl_Ql.js → lab-UyTbokQC.js} +136 -148
  73. package/dist/lab-UyTbokQC.js.map +1 -0
  74. package/dist/lab.js +4 -3
  75. package/dist/{records-BzTcxekG.js → records-CR_pYzIP.js} +533 -335
  76. package/dist/records-CR_pYzIP.js.map +1 -0
  77. package/dist/{runtime-D6gVat6r.js → runtime-CoHhBP10.js} +417 -54
  78. package/dist/runtime-CoHhBP10.js.map +1 -0
  79. package/dist/runtime.d.ts +1 -1
  80. package/dist/runtime.js +7 -7
  81. package/dist/{shell-5ju97nOS.js → shell-BDuZf2zb.js} +10 -5
  82. package/dist/{shell-5ju97nOS.js.map → shell-BDuZf2zb.js.map} +1 -1
  83. package/dist/shell.d.ts +1 -0
  84. package/dist/shell.js +6 -6
  85. package/dist/stores/index.d.ts +1 -1
  86. package/dist/stores/index.js +2 -2
  87. package/dist/stores/settings.d.ts +4 -12
  88. package/dist/styles.css +2732 -2083
  89. package/dist/types/componentWorkflowTypes.d.ts +2 -0
  90. package/dist/types/components.d.ts +2 -0
  91. package/dist/types/generated/platformContext.d.ts +1 -0
  92. package/dist/types/generated-ui.d.ts +4 -0
  93. package/dist/types/index.d.ts +1 -1
  94. package/dist/types/jobs.d.ts +9 -0
  95. package/dist/types/platform.d.ts +1 -1
  96. package/dist/{BaseModal-DfRIuVoj.js → useDropdownState-Bb2N4m40.js} +74 -4
  97. package/dist/useDropdownState-Bb2N4m40.js.map +1 -0
  98. package/dist/{useExperimentSamples-CxcnxZ8h.js → useExperimentSamples-Dno1fFMq.js} +4 -4
  99. package/dist/{useExperimentSamples-CxcnxZ8h.js.map → useExperimentSamples-Dno1fFMq.js.map} +1 -1
  100. package/dist/{useListReorder-CcbEYxX-.js → useListReorder-CQ9OCScc.js} +183 -351
  101. package/dist/useListReorder-CQ9OCScc.js.map +1 -0
  102. package/dist/{useRackEditor-BD5NIo5s.js → useRackEditor-wL3kPrRT.js} +73 -59
  103. package/dist/useRackEditor-wL3kPrRT.js.map +1 -0
  104. package/dist/useRealtime-C3whhVvb.js +508 -0
  105. package/dist/useRealtime-C3whhVvb.js.map +1 -0
  106. package/dist/useRequest-CDkrXNU7.js +244 -0
  107. package/dist/useRequest-CDkrXNU7.js.map +1 -0
  108. package/dist/utils/time.d.ts +7 -0
  109. package/package.json +1 -1
  110. package/src/__stories__/experiment-helpers.ts +3 -3
  111. package/src/__tests__/components/AppTopBar.test.ts +257 -3
  112. package/src/__tests__/components/AppTopBarOnboarding.test.ts +160 -0
  113. package/src/__tests__/components/AppTopBarPageSelector.test.ts +125 -9
  114. package/src/__tests__/components/AppTopBarPillNav.test.ts +35 -0
  115. package/src/__tests__/components/ArtifactSaveDialog.test.ts +78 -0
  116. package/src/__tests__/components/BaseButton.test.ts +19 -0
  117. package/src/__tests__/components/BaseSelect.multiple.test.ts +87 -32
  118. package/src/__tests__/components/BaseSelect.test.ts +2 -1
  119. package/src/__tests__/components/BatchProgressList.test.ts +16 -0
  120. package/src/__tests__/components/CalendarGridPanel.test.ts +2 -2
  121. package/src/__tests__/components/ChartContainer.test.ts +11 -0
  122. package/src/__tests__/components/ChemicalFormula.test.ts +6 -3
  123. package/src/__tests__/components/DatePicker.datetime.test.ts +6 -0
  124. package/src/__tests__/components/DatePicker.test.ts +21 -0
  125. package/src/__tests__/components/DatePicker.time.test.ts +2 -2
  126. package/src/__tests__/components/EmptyState.test.ts +1 -1
  127. package/src/__tests__/components/ExperimentPopover.test.ts +125 -16
  128. package/src/__tests__/components/ExperimentSelectorModal.test.ts +153 -14
  129. package/src/__tests__/components/GeneratedPluginView.test.ts +23 -2
  130. package/src/__tests__/components/InstrumentStateBadge.test.ts +4 -4
  131. package/src/__tests__/components/JobsStatusTray.test.ts +373 -71
  132. package/src/__tests__/components/PluginWorkspaceView.test.ts +63 -1
  133. package/src/__tests__/components/ResourcePickerListInternal.test.ts +0 -20
  134. package/src/__tests__/components/SampleSelector.test.ts +33 -8
  135. package/src/__tests__/components/SequenceProgressBar.test.ts +12 -0
  136. package/src/__tests__/components/SettingsModal.test.ts +9 -2
  137. package/src/__tests__/components/StepWizard.test.ts +11 -0
  138. package/src/__tests__/components/WellPlate.decoration.test.ts +11 -4
  139. package/src/__tests__/components/deprecationTags.test.ts +1 -1
  140. package/src/__tests__/composables/useAppExperiment.test.ts +27 -1
  141. package/src/__tests__/composables/useCalendarGrid.test.ts +4 -2
  142. package/src/__tests__/composables/useExperimentSelector.test.ts +111 -0
  143. package/src/__tests__/composables/useJobCenter.test.ts +262 -3
  144. package/src/__tests__/composables/useOnboarding.test.ts +317 -0
  145. package/src/__tests__/docs/frontendDocsCatalog.test.ts +4 -3
  146. package/src/__tests__/stores/settings.test.ts +21 -21
  147. package/src/__tests__/styles/baseControlsRules.test.ts +6 -2
  148. package/src/__tests__/styles/controlRefreshRules.test.ts +0 -6
  149. package/src/__tests__/styles/cssTokens.test.ts +6 -3
  150. package/src/__tests__/styles/menusShellRefresh.test.ts +22 -12
  151. package/src/__tests__/styles/paletteTokens.test.ts +44 -0
  152. package/src/__tests__/styles/v2AuditRules.test.ts +104 -0
  153. package/src/__tests__/utils/plotlyTemplate.test.ts +4 -4
  154. package/src/__tests__/utils/time.test.ts +18 -1
  155. package/src/components/AdductText.story.vue +7 -0
  156. package/src/components/AdductText.vue +1 -1
  157. package/src/components/AlertBox.story.vue +2 -2
  158. package/src/components/AlertBox.vue +18 -16
  159. package/src/components/AppSidebar.story.vue +0 -28
  160. package/src/components/AppTopBar.onboarding.story.vue +69 -0
  161. package/src/components/AppTopBar.story.vue +105 -15
  162. package/src/components/AppTopBar.vue +248 -122
  163. package/src/components/ArtifactSaveDialog.story.vue +23 -0
  164. package/src/components/ArtifactSaveDialog.vue +102 -12
  165. package/src/components/AuditTrail.story.vue +2 -1
  166. package/src/components/AuditTrail.vue +2 -21
  167. package/src/components/Avatar.vue +10 -14
  168. package/src/components/BaseButton.vue +16 -2
  169. package/src/components/BaseSelect.multiple.story.vue +13 -2
  170. package/src/components/BaseSelect.vue +4 -3
  171. package/src/components/BatchProgressList.vue +35 -25
  172. package/src/components/ChartContainer.story.vue +2 -1
  173. package/src/components/ChartContainer.vue +8 -6
  174. package/src/components/ConfirmDialog.story.vue +1 -1
  175. package/src/components/EmptyState.vue +1 -1
  176. package/src/components/ExperimentPopover.story.vue +165 -53
  177. package/src/components/ExperimentPopover.vue +169 -187
  178. package/src/components/ExperimentSelectorModal.story.vue +73 -31
  179. package/src/components/ExperimentSelectorModal.vue +144 -111
  180. package/src/components/FormField.story.vue +8 -48
  181. package/src/components/GeneratedPluginView.vue +11 -10
  182. package/src/components/GeneratedResultRenderer.vue +3 -4
  183. package/src/components/InstrumentStateBadge.vue +1 -1
  184. package/src/components/JobsStatusTray.story.vue +97 -43
  185. package/src/components/JobsStatusTray.vue +287 -290
  186. package/src/components/LayoutResizeHandle.story.vue +65 -4
  187. package/src/components/LoadingSpinner.vue +1 -1
  188. package/src/components/NumberInput.vue +3 -6
  189. package/src/components/PluginIcon.story.vue +2 -10
  190. package/src/components/PluginIcon.vue +2 -2
  191. package/src/components/PluginWorkspaceView.props.ts +3 -0
  192. package/src/components/PluginWorkspaceView.story.vue +1 -1
  193. package/src/components/PluginWorkspaceView.vue +17 -0
  194. package/src/components/RuntimeUpdateNotice.vue +1 -1
  195. package/src/components/SampleSelector.story.vue +22 -7
  196. package/src/components/SampleSelector.vue +34 -37
  197. package/src/components/SampleSelectorGroupNode.vue +1 -1
  198. package/src/components/SectionCard.story.vue +35 -2
  199. package/src/components/SequenceProgressBar.story.vue +9 -0
  200. package/src/components/SequenceProgressBar.vue +6 -1
  201. package/src/components/SettingsModal.schema.ts +1 -1
  202. package/src/components/SettingsModal.vue +9 -14
  203. package/src/components/SmartGroupFieldRecipe.story.vue +1 -1
  204. package/src/components/SmartGroupManual.story.vue +1 -1
  205. package/src/components/StepWizard.story.vue +33 -0
  206. package/src/components/StepWizard.vue +8 -1
  207. package/src/components/WellPlate.story.vue +3 -1
  208. package/src/components/WellPlate.vue +7 -0
  209. package/src/components/internal/AppTopBarOnboardingInternal.vue +172 -0
  210. package/src/components/internal/AppTopBarPageSelectorInternal.vue +140 -72
  211. package/src/components/internal/AppTopBarPillNavInternal.vue +33 -2
  212. package/src/components/internal/DatePickerDateInternal.vue +16 -40
  213. package/src/components/internal/DatePickerDateTimeInternal.vue +3 -13
  214. package/src/components/internal/DatePickerTimeInternal.vue +5 -8
  215. package/src/components/internal/OnboardingLayerInternal.vue +173 -0
  216. package/src/components/internal/ResourcePickerListInternal.vue +45 -85
  217. package/src/components/internal/SelectMultipleInternal.vue +182 -65
  218. package/src/components/internal/SelectSearchableInternal.vue +2 -3
  219. package/src/components/internal/WorkspaceShellInternal.vue +12 -0
  220. package/src/composables/createClient.ts +2 -0
  221. package/src/composables/index.ts +1 -0
  222. package/src/composables/pluginIdentity.ts +31 -0
  223. package/src/composables/pluginWorkspaceModel.ts +2 -2
  224. package/src/composables/useAppExperiment.ts +20 -0
  225. package/src/composables/useCalendarGrid.ts +4 -3
  226. package/src/composables/useExperimentSelector.ts +77 -31
  227. package/src/composables/useJobCenter.ts +188 -6
  228. package/src/composables/useOnboarding.ts +465 -0
  229. package/src/composables/usePluginWorkspace.ts +1 -1
  230. package/src/runtime.ts +2 -1
  231. package/src/shell.ts +17 -0
  232. package/src/stores/index.ts +0 -2
  233. package/src/stores/settings.ts +20 -50
  234. package/src/styles/components/adduct-text.css +13 -5
  235. package/src/styles/components/alert-box.css +39 -58
  236. package/src/styles/components/app-layout.css +7 -5
  237. package/src/styles/components/app-page-selector.css +121 -44
  238. package/src/styles/components/app-pill-nav.css +11 -12
  239. package/src/styles/components/app-sidebar.css +9 -18
  240. package/src/styles/components/app-top-bar.css +219 -55
  241. package/src/styles/components/artifact-save-dialog.css +153 -2
  242. package/src/styles/components/audit-trail.css +16 -19
  243. package/src/styles/components/avatar.css +6 -4
  244. package/src/styles/components/batch-progress-list.css +38 -36
  245. package/src/styles/components/button.css +36 -59
  246. package/src/styles/components/calendar.css +12 -12
  247. package/src/styles/components/chart-container.css +34 -23
  248. package/src/styles/components/checkbox.css +2 -12
  249. package/src/styles/components/collapsible-card.css +27 -29
  250. package/src/styles/components/confirm-dialog.css +13 -14
  251. package/src/styles/components/dataframe.css +13 -5
  252. package/src/styles/components/date-picker.css +80 -92
  253. package/src/styles/components/datetime-picker.css +31 -43
  254. package/src/styles/components/divider.css +20 -20
  255. package/src/styles/components/dose-calculator.css +3 -3
  256. package/src/styles/components/empty-state.css +20 -17
  257. package/src/styles/components/experiment-code-badge.css +2 -0
  258. package/src/styles/components/experiment-popover.css +174 -329
  259. package/src/styles/components/experiment-selector-modal.css +88 -56
  260. package/src/styles/components/file-picker.css +3 -3
  261. package/src/styles/components/file-uploader.css +38 -49
  262. package/src/styles/components/form-builder.css +3 -3
  263. package/src/styles/components/formula-input.css +1 -1
  264. package/src/styles/components/icon-button.css +18 -24
  265. package/src/styles/components/input.css +5 -4
  266. package/src/styles/components/instrument-monitor.css +13 -15
  267. package/src/styles/components/jobs-status-tray.css +140 -296
  268. package/src/styles/components/layout-resize-handle.css +59 -16
  269. package/src/styles/components/modal.css +4 -6
  270. package/src/styles/components/molecule-input.css +2 -2
  271. package/src/styles/components/multi-select.css +65 -117
  272. package/src/styles/components/number-input.css +26 -19
  273. package/src/styles/components/onboarding.css +368 -0
  274. package/src/styles/components/pill.css +3 -2
  275. package/src/styles/components/plate-editor.css +0 -1
  276. package/src/styles/components/plugin-icon.css +26 -23
  277. package/src/styles/components/popover.css +2 -2
  278. package/src/styles/components/presence-avatars.css +1 -1
  279. package/src/styles/components/progress-bar.css +3 -3
  280. package/src/styles/components/protocol-step-editor.css +1 -3
  281. package/src/styles/components/resource-card.css +2 -2
  282. package/src/styles/components/resource-picker-list.css +6 -57
  283. package/src/styles/components/sample-hierarchy-tree.css +4 -4
  284. package/src/styles/components/sample-selector.css +138 -79
  285. package/src/styles/components/schedule-calendar.css +8 -8
  286. package/src/styles/components/searchable-select.css +12 -12
  287. package/src/styles/components/section-card.css +54 -49
  288. package/src/styles/components/segmented-control.css +18 -15
  289. package/src/styles/components/select.css +3 -3
  290. package/src/styles/components/sequence-input.css +2 -2
  291. package/src/styles/components/sequence-progress-bar.css +17 -8
  292. package/src/styles/components/settings-modal.css +7 -2
  293. package/src/styles/components/smart-group.css +18 -18
  294. package/src/styles/components/step-wizard.css +52 -16
  295. package/src/styles/components/tabs.css +2 -2
  296. package/src/styles/components/textarea.css +3 -3
  297. package/src/styles/components/time-picker.css +36 -21
  298. package/src/styles/components/toast.css +12 -10
  299. package/src/styles/components/toggle.css +1 -11
  300. package/src/styles/components/tooltip.css +10 -9
  301. package/src/styles/components/unit-input.css +8 -7
  302. package/src/styles/components/well-plate.css +9 -2
  303. package/src/styles/index.css +1 -0
  304. package/src/styles/variables.css +241 -78
  305. package/src/types/componentWorkflowTypes.ts +2 -0
  306. package/src/types/components.ts +2 -0
  307. package/src/types/generated/platformContext.ts +1 -0
  308. package/src/types/generated-ui.ts +4 -0
  309. package/src/types/index.ts +1 -0
  310. package/src/types/jobs.ts +10 -0
  311. package/src/types/platform.ts +1 -1
  312. package/src/utils/time.ts +21 -0
  313. package/dist/ActionMenu-CScINahU.js.map +0 -1
  314. package/dist/AppSidebar-BmiiPfz9.js.map +0 -1
  315. package/dist/BaseModal-DfRIuVoj.js.map +0 -1
  316. package/dist/ExperimentPopover-C4sGw-is.js +0 -359
  317. package/dist/ExperimentPopover-C4sGw-is.js.map +0 -1
  318. package/dist/ExperimentSelectorModal-D-9z7cNx.js.map +0 -1
  319. package/dist/SettingsModal-CSLE31yg.js.map +0 -1
  320. package/dist/data-C2PwI_dn.js.map +0 -1
  321. package/dist/experiment-43OJtH7R.js.map +0 -1
  322. package/dist/lab-DLwRl_Ql.js.map +0 -1
  323. package/dist/records-BzTcxekG.js.map +0 -1
  324. package/dist/runtime-D6gVat6r.js.map +0 -1
  325. package/dist/useListReorder-CcbEYxX-.js.map +0 -1
  326. package/dist/useRackEditor-BD5NIo5s.js.map +0 -1
  327. package/dist/useRealtime-PalKue84.js +0 -696
  328. package/dist/useRealtime-PalKue84.js.map +0 -1
  329. package/dist/useRequest-D5JDfsgZ.js +0 -76
  330. package/dist/useRequest-D5JDfsgZ.js.map +0 -1
@@ -3,7 +3,11 @@
3
3
  * @import '@morscherlab/mint-sdk/styles';
4
4
  */
5
5
 
6
- @import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600;700&family=Fira+Sans:wght@300;400;500;600;700&display=swap');
6
+ @import url('https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500;600&display=swap');
7
+
8
+ /* Tailwind v4's layer order, declared first so the SDK's base and utilities
9
+ layers sort the same way with or without Tailwind in the host. */
10
+ @layer theme, base, components, utilities;
7
11
 
8
12
  /* ==========================================================================
9
13
  CSS Variables - Light Mode
@@ -21,9 +25,9 @@
21
25
  /* Strong control border — unchecked checkbox/radio, toggle off track, dashed outlines (~3.2:1 light, ~4.2:1 dark). */
22
26
  --_control-border-strong: color-mix(in srgb, var(--text-secondary) 75%, var(--border-color));
23
27
  /* Field border on hover. */
24
- --_field-border-hover: color-mix(in srgb, var(--text-secondary) 60%, var(--border-color));
25
- /* Ink: primary / error text on card and tinted surfaces. The -hover tokens
26
- have no dark value, so html.dark lifts these toward --text-primary. */
28
+ --_field-border-hover: var(--border-strong);
29
+ /* Ink: primary / error text on card and tinted surfaces. html.dark lifts
30
+ these toward --text-primary. */
27
31
  --_ink-primary: var(--color-primary-hover);
28
32
  --_ink-error: var(--mint-error-hover);
29
33
 
@@ -33,11 +37,12 @@
33
37
  --bg-tertiary: #F1F5F9;
34
38
  --bg-card: #FFFFFF;
35
39
  --bg-hover: #F1F5F9;
36
- --border-color: #E5E7EB;
37
- --border-light: #F3F4F6;
40
+ --border-color: #E2E8F0;
41
+ --border-light: #F1F5F9;
42
+ --border-strong: #CBD5E1;
38
43
 
39
44
  /* Text colors */
40
- --text-primary: #1E293B;
45
+ --text-primary: #0F172A;
41
46
  --text-secondary: #64748B;
42
47
  --text-muted: #94A3B8;
43
48
  /* De-emphasized label for low-emphasis interactive controls (secondary/ghost
@@ -46,25 +51,31 @@
46
51
  where --text-secondary would drop to ~4.35:1. */
47
52
  --text-secondary-strong: #475569;
48
53
 
49
- /* Brand colors */
50
- --color-primary: #14B8A6;
51
- --color-primary-hover: #0F766E;
52
- --color-primary-soft: rgba(20, 184, 166, 0.12);
53
- --color-cta: #06B6D4;
54
- --color-cta-hover: #0891B2;
55
- --color-purple: #8B5CF6;
56
-
57
- /* Entity accent colors — experiments (cyan) and projects (sky), from the
58
- MINT 1.1 card system. -soft backs icon chips and pills, -border outlines
59
- pills that sit on a card surface. */
60
- --color-experiment: #06B6D4;
61
- --color-experiment-hover: #0891B2;
62
- --color-experiment-soft: rgba(6, 182, 212, 0.12);
63
- --color-experiment-border: rgba(6, 182, 212, 0.3);
64
- --color-project: #0EA5E9;
65
- --color-project-hover: #0284C7;
66
- --color-project-soft: rgba(14, 165, 233, 0.12);
67
- --color-project-border: rgba(14, 165, 233, 0.32);
54
+ /* Brand colors: the accent (--color-primary*) comes from the palette
55
+ blocks below the dark block (html[data-palette], default teal). */
56
+ /* Fixed teal scale, the same in both themes. */
57
+ --color-primary-50: #F0FDFA;
58
+ --color-primary-100: #CCFBF1;
59
+ --color-primary-500: #14B8A6;
60
+ --color-primary-600: #0D9488;
61
+ --mint-primary: var(--color-primary);
62
+ /* Text and icons on an accent fill (filled buttons, Save). */
63
+ --text-on-accent: #FFFFFF;
64
+ /* Retired hues, aliased to the accent until 1.5 (mint doctor warns on use). */
65
+ --color-cta: var(--color-primary);
66
+ --color-cta-hover: var(--color-primary-hover);
67
+ --color-purple: var(--color-primary);
68
+
69
+ /* Entity colors — retired hues, aliased to slate until 1.5. Entities are told
70
+ apart by icon and label, not by hue. Only -hover has no neutral token. */
71
+ --color-experiment: var(--text-secondary-strong);
72
+ --color-experiment-hover: #334155;
73
+ --color-experiment-soft: var(--bg-tertiary);
74
+ --color-experiment-border: var(--border-strong);
75
+ --color-project: var(--color-experiment);
76
+ --color-project-hover: var(--color-experiment-hover);
77
+ --color-project-soft: var(--color-experiment-soft);
78
+ --color-project-border: var(--color-experiment-border);
68
79
 
69
80
  /* MINT brand mark — used for browser theme-color, marketing chrome, app icon background */
70
81
  --mint-brand: #7BD0B5;
@@ -76,13 +87,14 @@
76
87
  --scrollbar-thumb: #D1D5DB;
77
88
  --scrollbar-hover: #9CA3AF;
78
89
 
79
- /* Shadows */
80
- --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
81
- --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
82
- --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
83
- --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
84
- /* The MINT 1.1 card system uses exactly one shadow for every card. */
85
- --card-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.04), 0 1px 3px 0 rgb(0 0 0 / 0.06);
90
+ /* Shadows — flat by default (borders, not shadows); only floating layers
91
+ (popovers, menus, dialogs) get one. */
92
+ --shadow-sm: none;
93
+ --shadow: none;
94
+ --card-shadow: none;
95
+ --shadow-md: 0 4px 12px -2px rgba(15, 23, 42, 0.10), 0 1px 3px rgba(15, 23, 42, 0.06);
96
+ --shadow-lg: 0 16px 40px -8px rgba(15, 23, 42, 0.18), 0 2px 6px rgba(15, 23, 42, 0.06);
97
+ --mint-shadow-pop: 0 4px 12px -2px rgba(15, 23, 42, 0.10), 0 1px 3px rgba(15, 23, 42, 0.06);
86
98
 
87
99
  /* Categorical sample scale — mirrored by SAMPLE_COLOR_SCALE in
88
100
  src/utils/color.ts. The JS constant carries the same hexes as a
@@ -106,20 +118,24 @@
106
118
  /* Motion — the SDK's standard easing curve. */
107
119
  --mint-ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
108
120
 
109
- /* Typography — Fira Sans for UI text, Fira Code for data, code and routes. */
110
- --font-sans: 'Fira Sans', system-ui, -apple-system, sans-serif;
111
- --font-mono: 'Fira Code', ui-monospace, SFMono-Regular, Menlo, monospace;
112
-
113
- /* Border radius */
114
- --radius-sm: 0.25rem;
115
- --radius: 0.375rem;
116
- --radius-md: 0.5rem;
117
- --radius-lg: 0.75rem;
118
-
119
- /* Form element heights */
120
- --form-height-sm: 2rem;
121
- --form-height-md: 2.25rem;
122
- --form-height-lg: 3rem;
121
+ /* Typography — Geist for UI text, Geist Mono for codes, m/z, RT, counts and timestamps. */
122
+ --font-sans: 'Geist', system-ui, -apple-system, sans-serif;
123
+ --font-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
124
+
125
+ /* Border radius — code badges, tags, buttons/inputs, cards/dialogs. */
126
+ --radius-sm: 3px;
127
+ --radius: 4px;
128
+ --radius-md: 6px;
129
+ --radius-lg: 8px;
130
+
131
+ /* Form element heights and page gutter — compact (normal and compact
132
+ density); [data-density="comfortable"] below restores the larger set. */
133
+ --form-height-sm: 1.75rem;
134
+ --form-height-md: 2rem;
135
+ --form-height-lg: 2.5rem;
136
+ --mint-gutter: 0.5rem;
137
+ /* Distance from the outermost cards to the window edge, in every density. */
138
+ --mint-page-inset: 1rem;
123
139
 
124
140
  /* Semantic colors */
125
141
  --mint-success: #10B981;
@@ -141,20 +157,20 @@
141
157
  --mint-info-border: rgba(59, 130, 246, 0.3);
142
158
 
143
159
  /* Extended brand tokens */
144
- --color-primary-light: #93C5FD;
160
+ --color-primary-light: #5EEAD4;
145
161
 
146
162
  /* Shared component tokens */
147
163
  --mint-disabled-opacity: 0.6;
148
164
 
149
165
  /* Focus ring tokens */
150
166
  --focus-ring: 0 0 0 2px var(--color-primary);
151
- --focus-ring-offset: 0 0 0 2px white, 0 0 0 4px var(--color-primary);
167
+ --focus-ring-offset: 0 0 0 2px #FFFFFF, 0 0 0 4px var(--color-primary);
152
168
  --focus-ring-error: 0 0 0 2px var(--mint-error);
153
169
 
154
170
  /* Soft focus rings — 3px translucent. Used by all form primitives
155
171
  post-0.16 visual refresh. Solid variants above kept for legacy
156
172
  components that haven't migrated yet. */
157
- --focus-ring-soft: 0 0 0 3px rgba(20, 184, 166, 0.20);
173
+ --focus-ring-soft: 0 0 0 3px color-mix(in srgb, var(--color-primary) 18%, transparent);
158
174
  /* Red is perceptually brighter than teal at equal alpha; drop 2pp to keep visual weight matched. */
159
175
  --focus-ring-soft-error: 0 0 0 3px rgba(239, 68, 68, 0.18);
160
176
 
@@ -174,7 +190,8 @@
174
190
  --mint-text-inverse: #FFFFFF;
175
191
  --mint-border: var(--border-color);
176
192
  --mint-border-focus: var(--color-primary);
177
- --mint-transition: 150ms ease;
193
+ /* Colour-only transitions; no lift, bounce or glow. */
194
+ --mint-transition: 120ms ease;
178
195
  }
179
196
 
180
197
  /* ==========================================================================
@@ -182,24 +199,30 @@
182
199
  ========================================================================== */
183
200
  html.dark {
184
201
  /* Background colors */
185
- --bg-primary: #0F172A;
186
- --bg-secondary: #1E293B;
187
- --bg-tertiary: #334155;
188
- --bg-card: #1E293B;
189
- --bg-hover: #334155;
190
- --border-color: #334155;
191
- --border-light: #1E293B;
202
+ --bg-primary: #0B1120;
203
+ --bg-secondary: #111827;
204
+ --bg-tertiary: #1E293B;
205
+ --bg-card: #111827;
206
+ --bg-hover: #1E293B;
207
+ --border-color: #1F2937;
208
+ --border-light: #172033;
209
+ --border-strong: #334155;
210
+
211
+ /* Brand colors - Dark mode: the accent comes from the palette blocks below. */
212
+ /* Dark ink on the light accent fill (10.1:1 on #2DD4BF). */
213
+ --text-on-accent: #0B1120;
214
+ --color-experiment-hover: #E2E8F0;
192
215
 
193
216
  /* Ink aliases (see :root): ~7:1 on --bg-card, tints included. */
194
217
  --_ink-primary: color-mix(in srgb, var(--color-primary) 50%, var(--text-primary));
195
218
  --_ink-error: color-mix(in srgb, var(--mint-error) 50%, var(--text-primary));
196
219
 
197
220
  /* Text colors */
198
- --text-primary: #F8FAFC;
221
+ --text-primary: #F1F5F9;
199
222
  --text-secondary: #94A3B8;
200
223
  --text-muted: #64748B;
201
- /* Dark-mode analog: soft light-gray instead of near-white, AA-safe (7.2:1)
202
- on the dark secondary hover surface (--bg-tertiary #334155). */
224
+ /* Dark-mode analog: soft light-gray instead of near-white, AA-safe (9.9:1)
225
+ on the dark secondary hover surface (--bg-tertiary #1E293B). */
203
226
  --text-secondary-strong: #CBD5E1;
204
227
 
205
228
  /* Scrollbar */
@@ -207,22 +230,10 @@ html.dark {
207
230
  --scrollbar-thumb: #475569;
208
231
  --scrollbar-hover: #64748B;
209
232
 
210
- /* Shadows - Dark mode */
211
- --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.2);
212
- --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3), 0 1px 2px -1px rgb(0 0 0 / 0.2);
213
- --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.3), 0 2px 4px -2px rgb(0 0 0 / 0.2);
214
- --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.3), 0 4px 6px -4px rgb(0 0 0 / 0.2);
215
- --card-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.24), 0 1px 3px 0 rgb(0 0 0 / 0.32);
216
-
217
- /* Entity accent colors - Dark mode */
218
- --color-experiment: #22D3EE;
219
- --color-experiment-hover: #67E8F9;
220
- --color-experiment-soft: rgba(34, 211, 238, 0.16);
221
- --color-experiment-border: rgba(34, 211, 238, 0.36);
222
- --color-project: #38BDF8;
223
- --color-project-hover: #7DD3FC;
224
- --color-project-soft: rgba(56, 189, 248, 0.16);
225
- --color-project-border: rgba(56, 189, 248, 0.38);
233
+ /* Shadows - Dark mode (floating layers only; --shadow-sm/--shadow/--card-shadow stay none) */
234
+ --shadow-md: 0 6px 16px -4px rgba(0, 0, 0, 0.6);
235
+ --shadow-lg: 0 16px 40px -8px rgba(0, 0, 0, 0.7);
236
+ --mint-shadow-pop: 0 6px 16px -4px rgba(0, 0, 0, 0.6);
226
237
 
227
238
  /* Semantic colors - Dark mode */
228
239
  --mint-success-bg: rgba(16, 185, 129, 0.15);
@@ -233,7 +244,7 @@ html.dark {
233
244
 
234
245
  /* Focus ring - dark mode offset uses bg-primary instead of white */
235
246
  --focus-ring-offset: 0 0 0 2px var(--bg-primary), 0 0 0 4px var(--color-primary);
236
- --focus-ring-soft: 0 0 0 3px rgba(129, 140, 248, 0.28);
247
+ --focus-ring-soft: 0 0 0 3px color-mix(in srgb, var(--color-primary) 22%, transparent);
237
248
  --focus-ring-soft-error: 0 0 0 3px rgba(248, 113, 113, 0.26);
238
249
 
239
250
  /* Legacy aliases */
@@ -243,6 +254,131 @@ html.dark {
243
254
  color-scheme: dark;
244
255
  }
245
256
 
257
+ /* Accent palettes (Settings → Color palette). The settings store writes
258
+ html[data-palette]; the palette swatches set the same attribute on their
259
+ strip to preview a palette. Each palette is one accent in both themes: white
260
+ text on the light-mode fill, dark text (--text-on-accent) on the dark-mode
261
+ fill. Hues come from the biology plot-style palettes. */
262
+
263
+ /* Teal (default): teal-600 fill, teal-700 hover + ink, teal-800 pressed.
264
+ White on #0D9488 is 3.74:1, a known AA exception. */
265
+ :root,
266
+ [data-palette="default"] {
267
+ --color-primary: #0D9488;
268
+ --color-primary-hover: #0F766E;
269
+ --color-primary-pressed: #115E59;
270
+ --color-primary-soft: rgba(13, 148, 136, 0.10);
271
+ --color-primary-border: #99F6E4;
272
+ }
273
+ html.dark,
274
+ html.dark [data-palette="default"] {
275
+ --color-primary: #2DD4BF;
276
+ --color-primary-hover: #5EEAD4;
277
+ --color-primary-pressed: #99F6E4;
278
+ --color-primary-soft: rgba(45, 212, 191, 0.12);
279
+ --color-primary-border: rgba(45, 212, 191, 0.35);
280
+ }
281
+
282
+ /* Colour-blind safe: Okabe-Ito blue accent, and Okabe-Ito bluish green,
283
+ vermilion and orange for status, so success and error differ for every
284
+ colour-vision type. White on #0072B2 is 5.19:1. */
285
+ :root[data-palette="colorblind"],
286
+ [data-palette="colorblind"] {
287
+ --color-primary: #0072B2;
288
+ --color-primary-hover: #005F94;
289
+ --color-primary-pressed: #004C77;
290
+ --color-primary-soft: rgba(0, 114, 178, 0.10);
291
+ --color-primary-border: #B3D5EA;
292
+ --mint-success: #009E73;
293
+ --mint-success-hover: #00805D;
294
+ --mint-success-bg: rgba(0, 158, 115, 0.1);
295
+ --mint-success-border: rgba(0, 158, 115, 0.3);
296
+ --mint-error: #D55E00;
297
+ --mint-error-hover: #B34E00;
298
+ --mint-error-bg: rgba(213, 94, 0, 0.1);
299
+ --mint-error-border: rgba(213, 94, 0, 0.3);
300
+ --mint-warning: #E69F00;
301
+ --mint-warning-hover: #C78800;
302
+ --mint-warning-bg: rgba(230, 159, 0, 0.1);
303
+ --mint-warning-border: rgba(230, 159, 0, 0.3);
304
+ --mint-warning-text: #8F5F00;
305
+ }
306
+ html.dark[data-palette="colorblind"],
307
+ html.dark [data-palette="colorblind"] {
308
+ --color-primary: #56B4E9;
309
+ --color-primary-hover: #7CC5EE;
310
+ --color-primary-pressed: #A3D6F3;
311
+ --color-primary-soft: rgba(86, 180, 233, 0.14);
312
+ --color-primary-border: rgba(86, 180, 233, 0.38);
313
+ --mint-success-bg: rgba(0, 158, 115, 0.15);
314
+ --mint-error-bg: rgba(213, 94, 0, 0.15);
315
+ --mint-warning-bg: rgba(230, 159, 0, 0.15);
316
+ --mint-warning-text: #FCD34D;
317
+ }
318
+
319
+ /* Purple: plot-style purple #8781bd, deepened so white text is 5.15:1. */
320
+ :root[data-palette="purple"],
321
+ [data-palette="purple"] {
322
+ --color-primary: #6B64B0;
323
+ --color-primary-hover: #5A539E;
324
+ --color-primary-pressed: #4A448A;
325
+ --color-primary-soft: rgba(107, 100, 176, 0.10);
326
+ --color-primary-border: #D3D0EC;
327
+ }
328
+ html.dark[data-palette="purple"],
329
+ html.dark [data-palette="purple"] {
330
+ --color-primary: #A9A3DB;
331
+ --color-primary-hover: #C2BDE7;
332
+ --color-primary-pressed: #D8D4F1;
333
+ --color-primary-soft: rgba(169, 163, 219, 0.14);
334
+ --color-primary-border: rgba(169, 163, 219, 0.38);
335
+ }
336
+
337
+ /* Ocean: plot-style ocean ramp, #2171B5 fill (white 5.13:1), #6BAED6 dark. */
338
+ :root[data-palette="ocean"],
339
+ [data-palette="ocean"] {
340
+ --color-primary: #2171B5;
341
+ --color-primary-hover: #1B5F99;
342
+ --color-primary-pressed: #164E7E;
343
+ --color-primary-soft: rgba(33, 113, 181, 0.10);
344
+ --color-primary-border: #BBD7EE;
345
+ }
346
+ html.dark[data-palette="ocean"],
347
+ html.dark [data-palette="ocean"] {
348
+ --color-primary: #6BAED6;
349
+ --color-primary-hover: #8EC2E2;
350
+ --color-primary-pressed: #B0D5EC;
351
+ --color-primary-soft: rgba(107, 174, 214, 0.14);
352
+ --color-primary-border: rgba(107, 174, 214, 0.38);
353
+ }
354
+
355
+ /* Forest: plot-style forest_clay, #2F6B4F fill (white 6.29:1), #7AA874 dark. */
356
+ :root[data-palette="forest"],
357
+ [data-palette="forest"] {
358
+ --color-primary: #2F6B4F;
359
+ --color-primary-hover: #275A42;
360
+ --color-primary-pressed: #1F4935;
361
+ --color-primary-soft: rgba(47, 107, 79, 0.10);
362
+ --color-primary-border: #BFD8C6;
363
+ }
364
+ html.dark[data-palette="forest"],
365
+ html.dark [data-palette="forest"] {
366
+ --color-primary: #7AA874;
367
+ --color-primary-hover: #95BC90;
368
+ --color-primary-pressed: #B1CFAD;
369
+ --color-primary-soft: rgba(122, 168, 116, 0.14);
370
+ --color-primary-border: rgba(122, 168, 116, 0.38);
371
+ }
372
+
373
+ /* Comfortable density (html[data-density], written by the settings store):
374
+ the pre-v2 control heights and a wider gutter. */
375
+ [data-density="comfortable"] {
376
+ --form-height-sm: 2rem;
377
+ --form-height-md: 2.25rem;
378
+ --form-height-lg: 3rem;
379
+ --mint-gutter: 0.75rem;
380
+ }
381
+
246
382
  /* ==========================================================================
247
383
  Base Styles
248
384
  ========================================================================== */
@@ -257,10 +393,33 @@ body {
257
393
  transition: background-color 0.15s, color 0.15s;
258
394
  }
259
395
 
396
+ /* No page-level rubber-band bounce or pull-to-refresh; overflow: hidden does
397
+ not stop it. Panels still scroll, and their overscroll stops at their own
398
+ edge. A browser tab keeps the horizontal swipe for back and forward; an
399
+ installed PWA has no history swipe, so it drops both axes. */
400
+ html,
401
+ body {
402
+ overscroll-behavior-y: none;
403
+ }
404
+
405
+ @media (display-mode: standalone) {
406
+ html,
407
+ body {
408
+ overscroll-behavior: none;
409
+ }
410
+ }
411
+
260
412
  code, pre {
261
413
  font-family: var(--font-mono);
262
414
  }
263
415
 
416
+ /* Links: accent ink. In the base layer, so Tailwind utilities and component
417
+ rules (both win over a layered element rule) keep their own link colours. */
418
+ @layer base {
419
+ a { color: var(--color-primary-hover); }
420
+ a:hover { color: var(--color-primary-pressed); }
421
+ }
422
+
264
423
  /* Native controls do not inherit the page font; Tailwind preflight covers this
265
424
  in the platform, but SDK consumers without it render buttons and selects in
266
425
  the UA font. */
@@ -313,6 +472,9 @@ button, input, select, textarea {
313
472
  /* ==========================================================================
314
473
  MINT Utility Classes (for Tailwind v4 compatibility)
315
474
  ========================================================================== */
475
+ /* In Tailwind's utilities layer: unlayered, these beat every layered rule, so a
476
+ host's responsive variant (`lg:flex-row`) lost to the SDK's `.flex-col`. */
477
+ @layer utilities {
316
478
  .bg-bg-hover {
317
479
  background-color: var(--bg-tertiary);
318
480
  }
@@ -688,6 +850,7 @@ button, input, select, textarea {
688
850
  .max-w-sm {
689
851
  max-width: 24rem;
690
852
  }
853
+ } /* @layer utilities */
691
854
 
692
855
  /* ==========================================================================
693
856
  Reduced Motion
@@ -184,5 +184,7 @@ export interface ExperimentFilters {
184
184
  project?: string | null
185
185
  experimentType?: string | null
186
186
  datePreset?: DatePreset | null
187
+ /** Only experiments the caller authored (`?mine=true`). */
188
+ mine?: boolean
187
189
  }
188
190
 
@@ -317,6 +317,8 @@ export interface PluginWorkspaceViewSpec {
317
317
  disabled?: import('vue').MaybeRefOrGetter<boolean>
318
318
  reason?: import('vue').MaybeRefOrGetter<string | undefined>
319
319
  }
320
+ /** Job source: the top bar shows the viewer's job and opens a JobsStatusTray. Read once at setup, like `experiment`. */
321
+ jobs?: import('vue').MaybeRefOrGetter<import('../composables/useJobCenter').PluginJobCenterSource | undefined>
320
322
  }
321
323
 
322
324
 
@@ -21,6 +21,7 @@ export interface PlatformContextPlugin {
21
21
  route_prefix: string
22
22
  api_prefix: string
23
23
  settings_api: "plugin" | "platform"
24
+ subtitle?: string
24
25
  description?: string
25
26
  icon?: string
26
27
  color?: string
@@ -151,7 +151,11 @@ export interface GeneratedAnalysisManifest {
151
151
  }
152
152
  ui: {
153
153
  title: string
154
+ /** Second top-bar line; the top bar falls back to `description`. */
155
+ subtitle?: string
154
156
  description: string
157
+ /** Plugin icon for the standalone top bar. */
158
+ icon?: string
155
159
  }
156
160
  jobs: GeneratedJobDefinition[]
157
161
  }
@@ -207,6 +207,7 @@ export type {
207
207
  JobStateInput,
208
208
  JobStatus,
209
209
  JobStreamData,
210
+ TopBarJob,
210
211
  } from './jobs'
211
212
 
212
213
  export type {
package/src/types/jobs.ts CHANGED
@@ -68,3 +68,13 @@ export interface JobSnapshotPayload {
68
68
  }
69
69
 
70
70
  export type JobStreamData = JobStateInput | JobSnapshotPayload
71
+
72
+ /** What the top-bar run slot shows for the viewer's own job. The plugin picks the job. */
73
+ export interface TopBarJob {
74
+ /** Lifecycle of the shown job; sets the icon and the tone. */
75
+ status: JobStatus
76
+ /** Tooltip and accessible name, e.g. "Extracting", "Saved to EXP-0142". Not shown as text. */
77
+ label: string
78
+ /** 0-100; drawn as a small bar after the icon. */
79
+ percent?: number | null
80
+ }
@@ -66,5 +66,5 @@ export type PlatformContext = Partial<Omit<PlatformFrontendContext, 'plugin'>> &
66
66
 
67
67
  // Settings state types
68
68
  export type ThemeMode = 'light' | 'dark' | 'system'
69
- export type ColorPalette = 'default' | 'colorblind' | 'viridis' | 'pastel'
69
+ export type ColorPalette = 'default' | 'colorblind' | 'purple' | 'ocean' | 'forest'
70
70
  export type TableDensity = 'compact' | 'normal' | 'comfortable'
package/src/utils/time.ts CHANGED
@@ -122,6 +122,27 @@ export function findNearestTimeSlotIndex(time: string, slots: readonly string[])
122
122
  return bestIndex
123
123
  }
124
124
 
125
+ const relativeTimeFormat = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })
126
+
127
+ /**
128
+ * "in 5 minutes" / "2 hours ago" up to a week, then a locale date.
129
+ * With `weeks`, times up to five weeks away read as a week count first.
130
+ */
131
+ export function formatRelativeTime(value: Date | string, { weeks = false }: { weeks?: boolean } = {}): string {
132
+ const date = value instanceof Date ? value : new Date(value)
133
+ const diffSec = Math.round((date.getTime() - Date.now()) / 1000)
134
+ const diffMin = Math.round(diffSec / 60)
135
+ const diffHr = Math.round(diffMin / 60)
136
+ const diffDay = Math.round(diffHr / 24)
137
+ const diffWeek = Math.round(diffDay / 7)
138
+ if (Math.abs(diffSec) < 60) return relativeTimeFormat.format(diffSec, 'second')
139
+ if (Math.abs(diffMin) < 60) return relativeTimeFormat.format(diffMin, 'minute')
140
+ if (Math.abs(diffHr) < 24) return relativeTimeFormat.format(diffHr, 'hour')
141
+ if (Math.abs(diffDay) < 7) return relativeTimeFormat.format(diffDay, 'day')
142
+ if (weeks && Math.abs(diffWeek) < 5) return relativeTimeFormat.format(diffWeek, 'week')
143
+ return date.toLocaleDateString()
144
+ }
145
+
125
146
  export function toMinutes(time: string): number {
126
147
  const { hour, minute } = parseTime(time)
127
148
  return hour * 60 + minute
@@ -1 +0,0 @@
1
- {"version":3,"file":"ActionMenu-CScINahU.js","names":[],"sources":["../src/composables/useTextSearch.ts","../src/components/LoadingSpinner.vue","../src/components/LoadingSpinner.vue","../src/components/internal/DataStateInternal.vue","../src/components/internal/DataStateInternal.vue","../src/components/AlertBox.vue","../src/components/AlertBox.vue","../src/components/ActionMenu.vue","../src/components/ActionMenu.vue"],"sourcesContent":["import { computed, toValue, type ComputedRef, type Ref } from 'vue'\n\nexport type TextSearchSource<T> =\n | T\n | Ref<T>\n | ComputedRef<T>\n | (() => T)\n\nexport type TextSearchCandidate = unknown | readonly unknown[]\n\nexport interface UseTextSearchOptions<TItem> {\n items: TextSearchSource<readonly TItem[]>\n query: TextSearchSource<string | null | undefined>\n enabled?: TextSearchSource<boolean | null | undefined>\n getText: (item: TItem) => TextSearchCandidate\n}\n\nexport interface UseTextSearchReturn<TItem> {\n query: ComputedRef<string>\n filteredItems: ComputedRef<TItem[]>\n matches: (item: TItem, query?: string | null | undefined) => boolean\n}\n\n/** Shared text-search filtering for tables, lists, selectors, and chip inputs. */\nexport function useTextSearch<TItem>(options: UseTextSearchOptions<TItem>): UseTextSearchReturn<TItem> {\n const query = computed(() => normalizeSearchQuery(toValue(options.query)))\n const enabled = computed(() => toValue(options.enabled) ?? true)\n\n function matches(item: TItem, overrideQuery?: string | null | undefined): boolean {\n const activeQuery = overrideQuery === undefined ? query.value : normalizeSearchQuery(overrideQuery)\n if (!activeQuery) return true\n return candidateMatchesSearch(options.getText(item), activeQuery)\n }\n\n const filteredItems = computed(() => {\n const items = [...toValue(options.items)]\n if (!enabled.value || !query.value) return items\n return items.filter(item => matches(item))\n })\n\n return {\n query,\n filteredItems,\n matches,\n }\n}\n\nexport function normalizeSearchQuery(value: string | null | undefined): string {\n return (value ?? '').trim().toLowerCase()\n}\n\nexport function candidateMatchesSearch(candidate: TextSearchCandidate, query: string): boolean {\n const normalizedQuery = normalizeSearchQuery(query)\n if (!normalizedQuery) return true\n const values = Array.isArray(candidate) ? candidate : [candidate]\n return values.some((value) => {\n if (value === null || value === undefined) return false\n return String(value).toLowerCase().includes(normalizedQuery)\n })\n}\n","<script setup lang=\"ts\">\n/** Animated SVG spinner with size and tone, and a visually hidden status label for screen readers. `tone=\"inherit\"` takes the parent's text color. */\nimport type { Tone } from '../types'\n\ninterface Props {\n size?: 'xs' | 'sm' | 'md' | 'lg'\n tone?: Tone | 'inherit'\n label?: string\n}\n\nwithDefaults(defineProps<Props>(), {\n size: 'md',\n tone: 'primary',\n label: 'Loading',\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-spinner',\n `mint-spinner--${size}`,\n `mint-spinner--${tone}`,\n ]\"\n role=\"status\"\n >\n <svg\n class=\"mint-spinner__svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n style=\"opacity: 0.25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n />\n <path\n style=\"opacity: 0.75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n <span class=\"mint-spinner__label\">{{ label }}</span>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Animated SVG spinner with size and tone, and a visually hidden status label for screen readers. `tone=\"inherit\"` takes the parent's text color. */\nimport type { Tone } from '../types'\n\ninterface Props {\n size?: 'xs' | 'sm' | 'md' | 'lg'\n tone?: Tone | 'inherit'\n label?: string\n}\n\nwithDefaults(defineProps<Props>(), {\n size: 'md',\n tone: 'primary',\n label: 'Loading',\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-spinner',\n `mint-spinner--${size}`,\n `mint-spinner--${tone}`,\n ]\"\n role=\"status\"\n >\n <svg\n class=\"mint-spinner__svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n style=\"opacity: 0.25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n />\n <path\n style=\"opacity: 0.75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n <span class=\"mint-spinner__label\">{{ label }}</span>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Shared loading / error / empty presentation for the SDK data components.\n *\n * Internal: not exported from the package entrypoints. Data components render\n * this instead of hand-rolling their own placeholder markup so that the\n * loading, error and empty states look the same everywhere.\n */\nimport { computed } from 'vue'\nimport EmptyState from '../EmptyState.vue'\nimport LoadingSpinner from '../LoadingSpinner.vue'\n\n/** Single-path warning triangle used for the error state. */\nconst ERROR_ICON_PATH\n = 'M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z'\n + 'M12 9v4M12 17h.01'\n\ninterface Props {\n /** Which of the three placeholder states to render. */\n state: 'loading' | 'error' | 'empty'\n /** Headline for the empty/error state. */\n title?: string\n /** Supporting copy for the empty/error state. */\n description?: string\n /** Custom `<path d>` for the empty state icon (the error state has its own). */\n iconPath?: string\n /** Placeholder scale; also drives the loading spinner size. */\n size?: 'sm' | 'md' | 'lg'\n /** `EmptyState` layout variant. */\n variant?: 'illustrated' | 'inline'\n /** Accessible label (and visible caption) for the loading state. */\n loadingLabel?: string\n /** Optional call-to-action rendered under the empty/error copy. */\n actionLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n variant: 'illustrated',\n loadingLabel: 'Loading',\n})\n\nconst emit = defineEmits<{\n action: []\n}>()\n\nconst spinnerSize = computed(() => (props.size === 'sm' ? 'sm' : props.size === 'lg' ? 'lg' : 'md'))\n</script>\n\n<template>\n <div\n :class=\"['mint-data-state', `mint-data-state--${state}`, `mint-data-state--${size}`]\"\n :data-state=\"state\"\n >\n <template v-if=\"state === 'loading'\">\n <LoadingSpinner :size=\"spinnerSize\" :label=\"loadingLabel\" />\n <p class=\"mint-data-state__loading-text\" aria-hidden=\"true\">{{ loadingLabel }}</p>\n </template>\n <EmptyState\n v-else\n :title=\"title\"\n :description=\"description\"\n :icon-path=\"state === 'error' ? ERROR_ICON_PATH : iconPath\"\n :tone=\"state === 'error' ? 'error' : 'neutral'\"\n :size=\"size\"\n :variant=\"variant\"\n :action-label=\"actionLabel\"\n @action=\"emit('action')\"\n >\n <slot />\n </EmptyState>\n </div>\n</template>\n\n","<script setup lang=\"ts\">\n/**\n * Shared loading / error / empty presentation for the SDK data components.\n *\n * Internal: not exported from the package entrypoints. Data components render\n * this instead of hand-rolling their own placeholder markup so that the\n * loading, error and empty states look the same everywhere.\n */\nimport { computed } from 'vue'\nimport EmptyState from '../EmptyState.vue'\nimport LoadingSpinner from '../LoadingSpinner.vue'\n\n/** Single-path warning triangle used for the error state. */\nconst ERROR_ICON_PATH\n = 'M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z'\n + 'M12 9v4M12 17h.01'\n\ninterface Props {\n /** Which of the three placeholder states to render. */\n state: 'loading' | 'error' | 'empty'\n /** Headline for the empty/error state. */\n title?: string\n /** Supporting copy for the empty/error state. */\n description?: string\n /** Custom `<path d>` for the empty state icon (the error state has its own). */\n iconPath?: string\n /** Placeholder scale; also drives the loading spinner size. */\n size?: 'sm' | 'md' | 'lg'\n /** `EmptyState` layout variant. */\n variant?: 'illustrated' | 'inline'\n /** Accessible label (and visible caption) for the loading state. */\n loadingLabel?: string\n /** Optional call-to-action rendered under the empty/error copy. */\n actionLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n variant: 'illustrated',\n loadingLabel: 'Loading',\n})\n\nconst emit = defineEmits<{\n action: []\n}>()\n\nconst spinnerSize = computed(() => (props.size === 'sm' ? 'sm' : props.size === 'lg' ? 'lg' : 'md'))\n</script>\n\n<template>\n <div\n :class=\"['mint-data-state', `mint-data-state--${state}`, `mint-data-state--${size}`]\"\n :data-state=\"state\"\n >\n <template v-if=\"state === 'loading'\">\n <LoadingSpinner :size=\"spinnerSize\" :label=\"loadingLabel\" />\n <p class=\"mint-data-state__loading-text\" aria-hidden=\"true\">{{ loadingLabel }}</p>\n </template>\n <EmptyState\n v-else\n :title=\"title\"\n :description=\"description\"\n :icon-path=\"state === 'error' ? ERROR_ICON_PATH : iconPath\"\n :tone=\"state === 'error' ? 'error' : 'neutral'\"\n :size=\"size\"\n :variant=\"variant\"\n :action-label=\"actionLabel\"\n @action=\"emit('action')\"\n >\n <slot />\n </EmptyState>\n </div>\n</template>\n\n","<script setup lang=\"ts\">\n/** Inline alert banner with semantic icon, title, message slot, optional action button, and optional dismiss button. */\nimport { useSlots } from 'vue'\nimport type { Tone } from '../types'\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n tone?: Tone\n title?: string\n dismissible?: boolean\n actionLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n tone: 'info',\n dismissible: false,\n})\n\nconst emit = defineEmits<{\n dismiss: []\n action: []\n}>()\n\nconst slots = useSlots()\nconst hasActions = () => !!props.actionLabel || !!slots.actions\n</script>\n\n<template>\n <div\n class=\"mint-alert\"\n :class=\"`mint-alert--${props.tone}`\"\n role=\"alert\"\n >\n <!-- Lucide circle-check -->\n <svg v-if=\"props.tone === 'success'\" class=\"mint-alert__icon mint-alert__icon--success\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m9 12 2 2 4-4\" />\n </svg>\n <!-- Lucide circle-x -->\n <svg v-else-if=\"props.tone === 'error'\" class=\"mint-alert__icon mint-alert__icon--error\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m15 9-6 6\" /><path d=\"m9 9 6 6\" />\n </svg>\n <!-- Lucide triangle-alert -->\n <svg v-else-if=\"props.tone === 'warning'\" class=\"mint-alert__icon mint-alert__icon--warning\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <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\" />\n </svg>\n <!-- Lucide info -->\n <svg v-else :class=\"['mint-alert__icon', `mint-alert__icon--${props.tone}`]\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 16v-4\" /><path d=\"M12 8h.01\" />\n </svg>\n\n <div class=\"mint-alert__content\">\n <h4 v-if=\"props.title\" class=\"mint-alert__title\">\n {{ props.title }}\n </h4>\n <div class=\"mint-alert__message\">\n <slot />\n </div>\n </div>\n\n <div v-if=\"hasActions()\" class=\"mint-alert__actions\">\n <slot name=\"actions\">\n <BaseButton variant=\"secondary\" size=\"sm\" @click=\"emit('action')\">\n {{ props.actionLabel }}\n </BaseButton>\n </slot>\n </div>\n\n <button\n v-if=\"props.dismissible\"\n type=\"button\"\n class=\"mint-alert__dismiss\"\n aria-label=\"Dismiss\"\n @click=\"emit('dismiss')\"\n >\n <svg class=\"mint-alert__dismiss-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Inline alert banner with semantic icon, title, message slot, optional action button, and optional dismiss button. */\nimport { useSlots } from 'vue'\nimport type { Tone } from '../types'\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n tone?: Tone\n title?: string\n dismissible?: boolean\n actionLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n tone: 'info',\n dismissible: false,\n})\n\nconst emit = defineEmits<{\n dismiss: []\n action: []\n}>()\n\nconst slots = useSlots()\nconst hasActions = () => !!props.actionLabel || !!slots.actions\n</script>\n\n<template>\n <div\n class=\"mint-alert\"\n :class=\"`mint-alert--${props.tone}`\"\n role=\"alert\"\n >\n <!-- Lucide circle-check -->\n <svg v-if=\"props.tone === 'success'\" class=\"mint-alert__icon mint-alert__icon--success\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m9 12 2 2 4-4\" />\n </svg>\n <!-- Lucide circle-x -->\n <svg v-else-if=\"props.tone === 'error'\" class=\"mint-alert__icon mint-alert__icon--error\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m15 9-6 6\" /><path d=\"m9 9 6 6\" />\n </svg>\n <!-- Lucide triangle-alert -->\n <svg v-else-if=\"props.tone === 'warning'\" class=\"mint-alert__icon mint-alert__icon--warning\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <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\" />\n </svg>\n <!-- Lucide info -->\n <svg v-else :class=\"['mint-alert__icon', `mint-alert__icon--${props.tone}`]\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 16v-4\" /><path d=\"M12 8h.01\" />\n </svg>\n\n <div class=\"mint-alert__content\">\n <h4 v-if=\"props.title\" class=\"mint-alert__title\">\n {{ props.title }}\n </h4>\n <div class=\"mint-alert__message\">\n <slot />\n </div>\n </div>\n\n <div v-if=\"hasActions()\" class=\"mint-alert__actions\">\n <slot name=\"actions\">\n <BaseButton variant=\"secondary\" size=\"sm\" @click=\"emit('action')\">\n {{ props.actionLabel }}\n </BaseButton>\n </slot>\n </div>\n\n <button\n v-if=\"props.dismissible\"\n type=\"button\"\n class=\"mint-alert__dismiss\"\n aria-label=\"Dismiss\"\n @click=\"emit('dismiss')\"\n >\n <svg class=\"mint-alert__dismiss-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Compact overflow actions with an icon-only BaseButton trigger and a viewport-safe teleported menu. */\nimport { computePosition, flip, offset, shift } from '@floating-ui/dom'\nimport { Check, Ellipsis } from 'lucide-vue-next'\nimport { nextTick, onMounted, ref, watch } from 'vue'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport { useMenuKeyboard } from '../composables/useMenuKeyboard'\nimport type { ActionMenuItem } from '../types/components'\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n label: string\n items: ActionMenuItem[]\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n})\n\nconst emit = defineEmits<{\n select: [item: ActionMenuItem]\n open: []\n close: []\n}>()\n\ndefineSlots<{\n /** Replaces the ellipsis button; `toggle` and `keydown` go on the slotted button. */\n trigger?: (props: { open: boolean; toggle: () => void; keydown: (event: KeyboardEvent) => void }) => unknown\n}>()\n\nconst panelRef = ref<HTMLElement | null>(null)\nconst panelStyle = ref<Record<string, string>>({ visibility: 'hidden' })\n\n// Disclosure state (outside-click, Escape, scroll/resize closing, focus return)\n// comes from the shared composable; only floating-ui placement stays local.\nconst { isOpen, rootRef, triggerRef, open, close } = useDropdownState({\n closeOnScroll: true,\n insideRefs: [panelRef],\n onOpen: () => {\n panelStyle.value = { visibility: 'hidden' }\n emit('open')\n },\n onClose: () => emit('close'),\n})\n\n// BaseButton owns the actual <button>, so the trigger element is resolved from\n// the wrapper once mounted rather than bound with a template ref.\nonMounted(() => {\n triggerRef.value = rootRef.value?.querySelector('button') ?? null\n})\n\nfunction menuItems(): HTMLElement[] {\n if (!panelRef.value) return []\n return Array.from(\n panelRef.value.querySelectorAll<HTMLElement>('.mint-action-menu__item:not(:disabled)'),\n )\n}\n\nasync function positionPanel() {\n const trigger = triggerRef.value\n const panel = panelRef.value\n if (!trigger || !panel) return\n\n const position = await computePosition(trigger, panel, {\n placement: 'bottom-end',\n strategy: 'fixed',\n middleware: [\n offset(4),\n flip(),\n shift({ padding: 8 }),\n ],\n })\n panelStyle.value = {\n left: `${position.x}px`,\n top: `${position.y}px`,\n visibility: 'visible',\n }\n}\n\n/** `focusFirst` is false when the keyboard composable owns the initial focus. */\nfunction openMenu(focusFirst = true) {\n if (props.disabled || isOpen.value) return\n open()\n if (focusFirst) {\n void nextTick(() => menuItems()[0]?.focus())\n }\n}\n\nfunction toggleMenu() {\n if (props.disabled) return\n if (isOpen.value) close()\n else openMenu()\n}\n\nconst { handleTriggerKeydown, handlePanelKeydown } = useMenuKeyboard({\n items: menuItems,\n isOpen: () => isOpen.value,\n open: () => openMenu(false),\n close,\n})\n\nfunction handleSelect(item: ActionMenuItem) {\n if (item.disabled) return\n emit('select', item)\n close()\n}\n\nwatch(isOpen, async (opened) => {\n if (!opened) return\n await nextTick()\n await positionPanel()\n})\n</script>\n\n<template>\n <span ref=\"rootRef\" class=\"mint-action-menu\">\n <!-- A custom trigger must render one <button> and wire toggle + keydown to it. -->\n <slot name=\"trigger\" :open=\"isOpen\" :toggle=\"toggleMenu\" :keydown=\"handleTriggerKeydown\">\n <BaseButton\n variant=\"secondary\"\n size=\"sm\"\n :icon=\"Ellipsis\"\n :label=\"label\"\n :disabled=\"disabled\"\n aria-haspopup=\"menu\"\n :aria-expanded=\"isOpen\"\n @click=\"toggleMenu\"\n @keydown=\"handleTriggerKeydown\"\n />\n </slot>\n </span>\n\n <Teleport to=\"body\">\n <div\n v-if=\"isOpen\"\n ref=\"panelRef\"\n class=\"mint-popover mint-action-menu__panel\"\n :style=\"panelStyle\"\n role=\"menu\"\n @keydown=\"handlePanelKeydown\"\n >\n <template v-for=\"(item, index) in items\" :key=\"item.id\">\n <div\n v-if=\"item.danger && index > 0 && !items[index - 1].danger\"\n class=\"mint-menu-separator\"\n role=\"separator\"\n />\n <a\n v-if=\"item.href !== undefined && !item.disabled\"\n :href=\"item.href\"\n :target=\"item.target\"\n :rel=\"item.target === '_blank' ? 'noopener noreferrer' : undefined\"\n :class=\"['mint-menu-item mint-action-menu__item', { 'mint-menu-item--danger mint-action-menu__item--danger': item.danger }]\"\n role=\"menuitem\"\n @click=\"handleSelect(item)\"\n >\n {{ item.label }}\n </a>\n <button\n v-else\n type=\"button\"\n :class=\"['mint-menu-item mint-action-menu__item', { 'mint-menu-item--danger mint-action-menu__item--danger': item.danger }]\"\n :role=\"item.checked === undefined ? 'menuitem' : 'menuitemradio'\"\n :aria-checked=\"item.checked\"\n :disabled=\"item.disabled\"\n @click=\"handleSelect(item)\"\n >\n <Check\n v-if=\"item.checked !== undefined\"\n class=\"mint-action-menu__check\"\n :class=\"{ 'mint-action-menu__check--off': !item.checked }\"\n :size=\"14\"\n aria-hidden=\"true\"\n />\n {{ item.label }}\n </button>\n </template>\n </div>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\n/** Compact overflow actions with an icon-only BaseButton trigger and a viewport-safe teleported menu. */\nimport { computePosition, flip, offset, shift } from '@floating-ui/dom'\nimport { Check, Ellipsis } from 'lucide-vue-next'\nimport { nextTick, onMounted, ref, watch } from 'vue'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport { useMenuKeyboard } from '../composables/useMenuKeyboard'\nimport type { ActionMenuItem } from '../types/components'\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n label: string\n items: ActionMenuItem[]\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n})\n\nconst emit = defineEmits<{\n select: [item: ActionMenuItem]\n open: []\n close: []\n}>()\n\ndefineSlots<{\n /** Replaces the ellipsis button; `toggle` and `keydown` go on the slotted button. */\n trigger?: (props: { open: boolean; toggle: () => void; keydown: (event: KeyboardEvent) => void }) => unknown\n}>()\n\nconst panelRef = ref<HTMLElement | null>(null)\nconst panelStyle = ref<Record<string, string>>({ visibility: 'hidden' })\n\n// Disclosure state (outside-click, Escape, scroll/resize closing, focus return)\n// comes from the shared composable; only floating-ui placement stays local.\nconst { isOpen, rootRef, triggerRef, open, close } = useDropdownState({\n closeOnScroll: true,\n insideRefs: [panelRef],\n onOpen: () => {\n panelStyle.value = { visibility: 'hidden' }\n emit('open')\n },\n onClose: () => emit('close'),\n})\n\n// BaseButton owns the actual <button>, so the trigger element is resolved from\n// the wrapper once mounted rather than bound with a template ref.\nonMounted(() => {\n triggerRef.value = rootRef.value?.querySelector('button') ?? null\n})\n\nfunction menuItems(): HTMLElement[] {\n if (!panelRef.value) return []\n return Array.from(\n panelRef.value.querySelectorAll<HTMLElement>('.mint-action-menu__item:not(:disabled)'),\n )\n}\n\nasync function positionPanel() {\n const trigger = triggerRef.value\n const panel = panelRef.value\n if (!trigger || !panel) return\n\n const position = await computePosition(trigger, panel, {\n placement: 'bottom-end',\n strategy: 'fixed',\n middleware: [\n offset(4),\n flip(),\n shift({ padding: 8 }),\n ],\n })\n panelStyle.value = {\n left: `${position.x}px`,\n top: `${position.y}px`,\n visibility: 'visible',\n }\n}\n\n/** `focusFirst` is false when the keyboard composable owns the initial focus. */\nfunction openMenu(focusFirst = true) {\n if (props.disabled || isOpen.value) return\n open()\n if (focusFirst) {\n void nextTick(() => menuItems()[0]?.focus())\n }\n}\n\nfunction toggleMenu() {\n if (props.disabled) return\n if (isOpen.value) close()\n else openMenu()\n}\n\nconst { handleTriggerKeydown, handlePanelKeydown } = useMenuKeyboard({\n items: menuItems,\n isOpen: () => isOpen.value,\n open: () => openMenu(false),\n close,\n})\n\nfunction handleSelect(item: ActionMenuItem) {\n if (item.disabled) return\n emit('select', item)\n close()\n}\n\nwatch(isOpen, async (opened) => {\n if (!opened) return\n await nextTick()\n await positionPanel()\n})\n</script>\n\n<template>\n <span ref=\"rootRef\" class=\"mint-action-menu\">\n <!-- A custom trigger must render one <button> and wire toggle + keydown to it. -->\n <slot name=\"trigger\" :open=\"isOpen\" :toggle=\"toggleMenu\" :keydown=\"handleTriggerKeydown\">\n <BaseButton\n variant=\"secondary\"\n size=\"sm\"\n :icon=\"Ellipsis\"\n :label=\"label\"\n :disabled=\"disabled\"\n aria-haspopup=\"menu\"\n :aria-expanded=\"isOpen\"\n @click=\"toggleMenu\"\n @keydown=\"handleTriggerKeydown\"\n />\n </slot>\n </span>\n\n <Teleport to=\"body\">\n <div\n v-if=\"isOpen\"\n ref=\"panelRef\"\n class=\"mint-popover mint-action-menu__panel\"\n :style=\"panelStyle\"\n role=\"menu\"\n @keydown=\"handlePanelKeydown\"\n >\n <template v-for=\"(item, index) in items\" :key=\"item.id\">\n <div\n v-if=\"item.danger && index > 0 && !items[index - 1].danger\"\n class=\"mint-menu-separator\"\n role=\"separator\"\n />\n <a\n v-if=\"item.href !== undefined && !item.disabled\"\n :href=\"item.href\"\n :target=\"item.target\"\n :rel=\"item.target === '_blank' ? 'noopener noreferrer' : undefined\"\n :class=\"['mint-menu-item mint-action-menu__item', { 'mint-menu-item--danger mint-action-menu__item--danger': item.danger }]\"\n role=\"menuitem\"\n @click=\"handleSelect(item)\"\n >\n {{ item.label }}\n </a>\n <button\n v-else\n type=\"button\"\n :class=\"['mint-menu-item mint-action-menu__item', { 'mint-menu-item--danger mint-action-menu__item--danger': item.danger }]\"\n :role=\"item.checked === undefined ? 'menuitem' : 'menuitemradio'\"\n :aria-checked=\"item.checked\"\n :disabled=\"item.disabled\"\n @click=\"handleSelect(item)\"\n >\n <Check\n v-if=\"item.checked !== undefined\"\n class=\"mint-action-menu__check\"\n :class=\"{ 'mint-action-menu__check--off': !item.checked }\"\n :size=\"14\"\n aria-hidden=\"true\"\n />\n {{ item.label }}\n </button>\n </template>\n </div>\n </Teleport>\n</template>\n"],"mappings":"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"}