@morscherlab/mint-sdk 1.2.0-beta.8 → 1.2.0

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 (402) hide show
  1. package/README.md +60 -0
  2. package/dist/{ExperimentPopover-CfOdh5Rm.js → ExperimentPopover-CJdNlCZb.js} +24 -15
  3. package/dist/ExperimentPopover-CJdNlCZb.js.map +1 -0
  4. package/dist/{ExperimentSelectorModal-DvIagrxY.js → ExperimentSelectorModal-DCXmwKNS.js} +5 -5
  5. package/dist/ExperimentSelectorModal-DCXmwKNS.js.map +1 -0
  6. package/dist/{SettingsModal-W3TYfnP9.js → SettingsModal-BYR20I4c.js} +5 -5
  7. package/dist/SettingsModal-BYR20I4c.js.map +1 -0
  8. package/dist/{EmptyState-NBqf2_8z.js → Skeleton-BAF3CKY7.js} +111 -93
  9. package/dist/Skeleton-BAF3CKY7.js.map +1 -0
  10. package/dist/__tests__/components/DataStates.test.d.ts +1 -0
  11. package/dist/__tests__/components/ExperimentTimeline.test.d.ts +1 -0
  12. package/dist/__tests__/components/FileBrowserModal.test.d.ts +1 -0
  13. package/dist/__tests__/components/FilePicker.state.test.d.ts +1 -0
  14. package/dist/__tests__/components/FilePicker.test.d.ts +1 -0
  15. package/dist/__tests__/components/PlotlyChart.test.d.ts +1 -0
  16. package/dist/__tests__/components/iconButtonIconSizing.test.d.ts +1 -0
  17. package/dist/__tests__/composables/useFileBrowser.test.d.ts +1 -0
  18. package/dist/__tests__/composables/useListReorder.test.d.ts +1 -0
  19. package/dist/__tests__/composables/usePlatformFilePickerAdapter.test.d.ts +1 -0
  20. package/dist/__tests__/styles/cssTokens.test.d.ts +1 -0
  21. package/dist/apiErrors.d.ts +26 -0
  22. package/dist/components/ActionMenu.vue.d.ts +1 -1
  23. package/dist/components/AppPluginSwitcher.vue.d.ts +2 -0
  24. package/dist/components/AuditTrail.vue.d.ts +13 -0
  25. package/dist/components/BaseInput.vue.d.ts +1 -1
  26. package/dist/components/BaseSelect.vue.d.ts +1 -1
  27. package/dist/components/BaseTextarea.vue.d.ts +1 -1
  28. package/dist/components/BatchProgressList.vue.d.ts +17 -0
  29. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
  30. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +2 -2
  31. package/dist/components/ConcentrationInput.vue.d.ts +1 -1
  32. package/dist/components/DataFrame.vue.d.ts +14 -2
  33. package/dist/components/DatePicker.vue.d.ts +2 -2
  34. package/dist/components/DateTimePicker.vue.d.ts +3 -1
  35. package/dist/components/ExperimentDataViewer.vue.d.ts +26 -1
  36. package/dist/components/ExperimentPopover.vue.d.ts +1 -0
  37. package/dist/components/ExperimentTimeline.vue.d.ts +29 -1
  38. package/dist/components/FileBrowserModal.vue.d.ts +99 -0
  39. package/dist/components/FilePicker.vue.d.ts +32 -0
  40. package/dist/components/FileUploader.vue.d.ts +5 -2
  41. package/dist/components/FormulaInput.vue.d.ts +1 -1
  42. package/dist/components/GeneratedPlotlyResult.vue.d.ts +1 -3
  43. package/dist/components/LcmsSequenceTable.vue.d.ts +29 -1
  44. package/dist/components/MobileSupportGate.vue.d.ts +1 -1
  45. package/dist/components/MoleculeInput.vue.d.ts +1 -1
  46. package/dist/components/NumberInput.vue.d.ts +1 -1
  47. package/dist/components/PlotlyChart.vue.d.ts +59 -0
  48. package/dist/components/ProgressBar.vue.d.ts +1 -1
  49. package/dist/components/ProtocolStepEditor.vue.d.ts +1 -0
  50. package/dist/components/ReagentList.vue.d.ts +29 -1
  51. package/dist/components/ResourceCard.vue.d.ts +1 -1
  52. package/dist/components/SampleHierarchyTree.vue.d.ts +29 -1
  53. package/dist/components/SampleSelector.drag.d.ts +13 -0
  54. package/dist/components/SampleSelector.tree.d.ts +2 -0
  55. package/dist/components/SampleSelector.vue.d.ts +34 -1
  56. package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
  57. package/dist/components/SecretInput.vue.d.ts +1 -1
  58. package/dist/components/SequenceInput.vue.d.ts +1 -1
  59. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +1 -0
  60. package/dist/components/StatusIndicator.vue.d.ts +1 -1
  61. package/dist/components/TagsInput.vue.d.ts +1 -1
  62. package/dist/components/TimePicker.vue.d.ts +2 -1
  63. package/dist/components/TimeRangeInput.vue.d.ts +1 -1
  64. package/dist/components/UnitInput.vue.d.ts +1 -1
  65. package/dist/components/WellPlate.geometry.d.ts +15 -0
  66. package/dist/components/WellPlate.interaction.d.ts +13 -0
  67. package/dist/components/WellPlate.vue.d.ts +30 -1
  68. package/dist/components/fileBrowserPresentation.d.ts +10 -0
  69. package/dist/components/index.d.ts +4 -0
  70. package/dist/components/index.js +8 -8
  71. package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -0
  72. package/dist/components/internal/DataStateInternal.vue.d.ts +43 -0
  73. package/dist/components/internal/FileBrowserDialogInternal.vue.d.ts +34 -0
  74. package/dist/components/internal/FileBrowserListingInternal.vue.d.ts +25 -0
  75. package/dist/components/internal/FileBrowserToolbarInternal.vue.d.ts +26 -0
  76. package/dist/components/internal/FilePickerPreviewInternal.vue.d.ts +18 -0
  77. package/dist/components/internal/FilePickerTreeInternal.vue.d.ts +31 -0
  78. package/dist/{components-B80gV0P6.js → components-DIum5hkx.js} +5415 -2753
  79. package/dist/components-DIum5hkx.js.map +1 -0
  80. package/dist/composables/controlSchemaTypes.d.ts +34 -0
  81. package/dist/composables/filePicker/localFiles.d.ts +3 -0
  82. package/dist/composables/filePicker/usePickerNavigation.d.ts +96 -0
  83. package/dist/composables/filePicker/usePickerSelection.d.ts +31 -0
  84. package/dist/composables/filePicker/validation.d.ts +5 -0
  85. package/dist/composables/index.d.ts +3 -0
  86. package/dist/composables/index.js +10 -10
  87. package/dist/composables/useApi.d.ts +14 -7
  88. package/dist/composables/useDropdownState.d.ts +18 -2
  89. package/dist/composables/useEventStream.d.ts +9 -0
  90. package/dist/composables/useFileBrowser.d.ts +55 -0
  91. package/dist/composables/useFilePicker.d.ts +189 -0
  92. package/dist/composables/useListReorder.d.ts +90 -0
  93. package/dist/composables/useMenuKeyboard.d.ts +2 -2
  94. package/dist/composables/usePlatformFilePickerAdapter.d.ts +11 -0
  95. package/dist/composables/usePluginConfig.d.ts +3 -1
  96. package/dist/composables/useRequestSyncState.d.ts +42 -2
  97. package/dist/composables/useScheduleDrag.d.ts +1 -0
  98. package/dist/{composables-C8mmD5ta.js → composables-mFXsTA4g.js} +307 -74
  99. package/dist/composables-mFXsTA4g.js.map +1 -0
  100. package/dist/{useApi-CDBq6z23.js → errors-BLuqMee5.js} +178 -16
  101. package/dist/errors-BLuqMee5.js.map +1 -0
  102. package/dist/{experiment-Y9Iz7KYi.js → experiment-CVDRAnqp.js} +3 -8
  103. package/dist/experiment-CVDRAnqp.js.map +1 -0
  104. package/dist/{experiment-utils-DV3DrvE_.js → experiment-utils-CV2Wza3z.js} +1 -1
  105. package/dist/experiment-utils-CV2Wza3z.js.map +1 -0
  106. package/dist/index.d.ts +1 -0
  107. package/dist/index.js +15 -15
  108. package/dist/install.js +2 -7
  109. package/dist/install.js.map +1 -1
  110. package/dist/permissions.d.ts +42 -1
  111. package/dist/permissions.js +20 -10
  112. package/dist/permissions.js.map +1 -1
  113. package/dist/stores/index.d.ts +1 -1
  114. package/dist/stores/index.js +3 -3
  115. package/dist/stores/settings.d.ts +31 -1
  116. package/dist/styles.css +15162 -32475
  117. package/dist/templates/index.js +2 -4
  118. package/dist/{templates-CK72aKO1.js → templates-CmGfmBRA.js} +291 -85
  119. package/dist/templates-CmGfmBRA.js.map +1 -0
  120. package/dist/types/fileBrowserTypes.d.ts +77 -0
  121. package/dist/types/filePicker.d.ts +77 -0
  122. package/dist/types/index.d.ts +2 -0
  123. package/dist/{useControlSchema-_OVhavKq.js → useControlSchema-DldpxNOP.js} +234 -1121
  124. package/dist/useControlSchema-DldpxNOP.js.map +1 -0
  125. package/dist/{useDropdownState-D5G4UvWd.js → useDropdownState-C7hRiVQf.js} +22 -6
  126. package/dist/useDropdownState-C7hRiVQf.js.map +1 -0
  127. package/dist/{useExperimentSelector-DSEckf_g.js → useExperimentSelector-CEcIaueJ.js} +4 -4
  128. package/dist/{useExperimentSelector-DSEckf_g.js.map → useExperimentSelector-CEcIaueJ.js.map} +1 -1
  129. package/dist/{useFormBuilder-Bd714C7y.js → useFormBuilder-z5L_dUIy.js} +2 -2
  130. package/dist/{useFormBuilder-Bd714C7y.js.map → useFormBuilder-z5L_dUIy.js.map} +1 -1
  131. package/dist/utils/color.d.ts +13 -0
  132. package/dist/utils/errors.d.ts +9 -0
  133. package/dist/{useProtocolTemplates-Dq5Pzwyd.js → validation-uwoJGEb6.js} +231 -158
  134. package/dist/validation-uwoJGEb6.js.map +1 -0
  135. package/package.json +1 -1
  136. package/src/__tests__/apiErrors.test.ts +81 -0
  137. package/src/__tests__/components/ActionMenu.test.ts +94 -0
  138. package/src/__tests__/components/DataFrame.test.ts +183 -11
  139. package/src/__tests__/components/DataStates.test.ts +176 -0
  140. package/src/__tests__/components/DatePicker.test.ts +76 -0
  141. package/src/__tests__/components/DateTimePicker.test.ts +36 -0
  142. package/src/__tests__/components/ExperimentPopover.test.ts +30 -0
  143. package/src/__tests__/components/ExperimentTimeline.test.ts +63 -0
  144. package/src/__tests__/components/FileBrowserModal.test.ts +460 -0
  145. package/src/__tests__/components/FilePicker.state.test.ts +287 -0
  146. package/src/__tests__/components/FilePicker.test.ts +487 -0
  147. package/src/__tests__/components/GeneratedPlotlyResult.test.ts +52 -5
  148. package/src/__tests__/components/GroupAssigner.test.ts +53 -0
  149. package/src/__tests__/components/LcmsSequenceTable.test.ts +22 -0
  150. package/src/__tests__/components/PlotlyChart.test.ts +305 -0
  151. package/src/__tests__/components/RackEditor.test.ts +35 -0
  152. package/src/__tests__/components/ReagentList.test.ts +64 -2
  153. package/src/__tests__/components/RetiredStyles.test.ts +31 -0
  154. package/src/__tests__/components/SampleSelector.drag.test.ts +51 -1
  155. package/src/__tests__/components/ScheduleCalendar.test.ts +63 -0
  156. package/src/__tests__/components/SegmentedControl.test.ts +107 -0
  157. package/src/__tests__/components/TimePicker.test.ts +61 -0
  158. package/src/__tests__/components/WellPlate.interaction.test.ts +59 -0
  159. package/src/__tests__/components/iconButtonIconSizing.test.ts +18 -0
  160. package/src/__tests__/composables/useApi.test.ts +92 -9
  161. package/src/__tests__/composables/useDropdownState.test.ts +58 -1
  162. package/src/__tests__/composables/useFileBrowser.test.ts +305 -0
  163. package/src/__tests__/composables/useGeneratedAnalysis.test.ts +7 -2
  164. package/src/__tests__/composables/useListReorder.test.ts +145 -0
  165. package/src/__tests__/composables/usePlatformFilePickerAdapter.test.ts +67 -0
  166. package/src/__tests__/composables/usePluginClient.test.ts +3 -0
  167. package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
  168. package/src/__tests__/stores/settings.test.ts +71 -1
  169. package/src/__tests__/styles/cssTokens.test.ts +131 -0
  170. package/src/__tests__/utils/permissions.test.ts +68 -0
  171. package/src/apiErrors.ts +85 -0
  172. package/src/components/ActionMenu.vue +45 -88
  173. package/src/components/AlertBox.vue +0 -4
  174. package/src/components/AppAvatarMenu.vue +2 -14
  175. package/src/components/AppLayout.vue +0 -4
  176. package/src/components/AppPluginSwitcher.story.vue +87 -0
  177. package/src/components/AppPluginSwitcher.vue +2 -5
  178. package/src/components/AppSidebar.vue +0 -4
  179. package/src/components/AppToastContainer.vue +6 -8
  180. package/src/components/AppTopBar.vue +0 -4
  181. package/src/components/ArtifactSaveDialog.vue +0 -4
  182. package/src/components/ArtifactSelectorModal.vue +0 -4
  183. package/src/components/AuditTrail.story.vue +18 -0
  184. package/src/components/AuditTrail.vue +27 -7
  185. package/src/components/AutoGroupModal.vue +0 -4
  186. package/src/components/Avatar.vue +0 -4
  187. package/src/components/BaseButton.vue +0 -4
  188. package/src/components/BaseCheckbox.vue +0 -4
  189. package/src/components/BaseInput.vue +0 -4
  190. package/src/components/BaseModal.vue +0 -4
  191. package/src/components/BasePill.vue +0 -4
  192. package/src/components/BaseRadioGroup.vue +0 -4
  193. package/src/components/BaseSelect.vue +0 -4
  194. package/src/components/BaseSlider.vue +0 -4
  195. package/src/components/BaseTabs.vue +0 -4
  196. package/src/components/BaseTextarea.vue +0 -4
  197. package/src/components/BaseToggle.vue +0 -4
  198. package/src/components/BatchProgressList.story.vue +18 -0
  199. package/src/components/BatchProgressList.vue +32 -4
  200. package/src/components/Breadcrumb.vue +0 -4
  201. package/src/components/Calendar.vue +0 -4
  202. package/src/components/ChartContainer.story.vue +1 -1
  203. package/src/components/ChartContainer.vue +0 -4
  204. package/src/components/ChemicalFormula.vue +0 -4
  205. package/src/components/CollapsibleCard.vue +0 -4
  206. package/src/components/ColorSlider.vue +0 -4
  207. package/src/components/ConcentrationInput.vue +0 -4
  208. package/src/components/ConfirmDialog.vue +0 -4
  209. package/src/components/DataFrame.story.vue +14 -0
  210. package/src/components/DataFrame.vue +97 -23
  211. package/src/components/DatePicker.vue +73 -27
  212. package/src/components/DateTimePicker.vue +45 -17
  213. package/src/components/Divider.vue +0 -4
  214. package/src/components/DoseCalculator.vue +0 -4
  215. package/src/components/DropdownButton.vue +2 -14
  216. package/src/components/EmptyState.vue +0 -4
  217. package/src/components/ExperimentCodeBadge.vue +0 -4
  218. package/src/components/ExperimentDataViewer.story.vue +3 -0
  219. package/src/components/ExperimentDataViewer.vue +33 -13
  220. package/src/components/ExperimentPopover.vue +12 -7
  221. package/src/components/ExperimentSelectorModal.vue +0 -4
  222. package/src/components/ExperimentTimeline.story.vue +18 -0
  223. package/src/components/ExperimentTimeline.vue +81 -75
  224. package/src/components/FileBrowserModal.story.vue +203 -0
  225. package/src/components/FileBrowserModal.vue +384 -0
  226. package/src/components/FilePicker.story.vue +44 -0
  227. package/src/components/FilePicker.vue +284 -0
  228. package/src/components/FileUploader.vue +32 -18
  229. package/src/components/FitPanel.vue +0 -4
  230. package/src/components/FormActions.story.vue +96 -0
  231. package/src/components/FormActions.vue +0 -4
  232. package/src/components/FormBuilder.vue +0 -4
  233. package/src/components/FormField.vue +0 -4
  234. package/src/components/FormulaInput.vue +0 -4
  235. package/src/components/GeneratedPathInput.story.vue +68 -0
  236. package/src/components/GeneratedPlotlyResult.story.vue +96 -0
  237. package/src/components/GeneratedPlotlyResult.vue +34 -69
  238. package/src/components/GeneratedPluginView.story.vue +145 -0
  239. package/src/components/GeneratedPluginView.vue +10 -10
  240. package/src/components/GeneratedResultRenderer.story.vue +117 -0
  241. package/src/components/GeneratedResultRenderer.vue +4 -9
  242. package/src/components/GroupAssigner.vue +67 -5
  243. package/src/components/IconButton.vue +0 -4
  244. package/src/components/InstrumentAlertLog.vue +0 -4
  245. package/src/components/InstrumentStateBadge.vue +0 -4
  246. package/src/components/InstrumentStatusCard.vue +0 -4
  247. package/src/components/JobsStatusTray.vue +0 -4
  248. package/src/components/LcmsSequenceTable.vue +81 -50
  249. package/src/components/LoadingSpinner.vue +0 -4
  250. package/src/components/MobileSupportGate.vue +0 -4
  251. package/src/components/MoleculeInput.vue +0 -4
  252. package/src/components/MultiSelect.vue +0 -4
  253. package/src/components/NumberInput.vue +0 -4
  254. package/src/components/PlateMapEditor.vue +3 -14
  255. package/src/components/PlotlyChart.story.vue +61 -0
  256. package/src/components/PlotlyChart.vue +233 -0
  257. package/src/components/PluginIcon.vue +0 -4
  258. package/src/components/ProgressBar.vue +0 -4
  259. package/src/components/ProtocolStepEditor.vue +2 -4
  260. package/src/components/RackEditor.vue +51 -70
  261. package/src/components/ReagentEditor.vue +0 -4
  262. package/src/components/ReagentList.story.vue +18 -0
  263. package/src/components/ReagentList.vue +127 -82
  264. package/src/components/ResourceCard.vue +0 -4
  265. package/src/components/RuntimeUpdateNotice.story.vue +49 -0
  266. package/src/components/RuntimeUpdateNotice.vue +4 -4
  267. package/src/components/SampleHierarchyTree.story.vue +18 -0
  268. package/src/components/SampleHierarchyTree.vue +32 -9
  269. package/src/components/SampleLegend.vue +2 -8
  270. package/src/components/SampleSelector.drag.ts +51 -0
  271. package/src/components/SampleSelector.story.vue +6 -0
  272. package/src/components/SampleSelector.tree.ts +2 -0
  273. package/src/components/SampleSelector.vue +49 -10
  274. package/src/components/SampleSelectorGroupNode.vue +19 -0
  275. package/src/components/ScheduleCalendar.story.vue +6 -0
  276. package/src/components/ScheduleCalendar.vue +82 -7
  277. package/src/components/ScientificNumber.vue +0 -4
  278. package/src/components/SectionCard.vue +0 -4
  279. package/src/components/SegmentedControl.vue +50 -7
  280. package/src/components/SequenceInput.vue +0 -4
  281. package/src/components/SequenceProgressBar.story.vue +110 -0
  282. package/src/components/SequenceProgressBar.vue +0 -4
  283. package/src/components/SettingsModal.vue +0 -4
  284. package/src/components/Skeleton.vue +0 -4
  285. package/src/components/SmartGroupFieldRecipe.vue +8 -6
  286. package/src/components/SmartGroupManual.vue +0 -4
  287. package/src/components/StatusIndicator.vue +0 -4
  288. package/src/components/StepWizard.vue +0 -4
  289. package/src/components/TagsInput.vue +4 -6
  290. package/src/components/ThemeToggle.vue +0 -4
  291. package/src/components/TimePicker.vue +36 -7
  292. package/src/components/TimeRangeInput.vue +0 -4
  293. package/src/components/Tooltip.story.vue +3 -3
  294. package/src/components/Tooltip.vue +0 -4
  295. package/src/components/UnitInput.vue +0 -4
  296. package/src/components/WellPlate.geometry.ts +60 -1
  297. package/src/components/WellPlate.interaction.ts +44 -0
  298. package/src/components/WellPlate.story.vue +8 -0
  299. package/src/components/WellPlate.vue +53 -7
  300. package/src/components/fileBrowserPresentation.ts +39 -0
  301. package/src/components/index.ts +5 -0
  302. package/src/components/internal/AppTopBarPageSelectorInternal.vue +2 -5
  303. package/src/components/internal/AppTopBarPillNavInternal.vue +0 -4
  304. package/src/components/internal/DataStateInternal.vue +74 -0
  305. package/src/components/internal/FileBrowserDialogInternal.vue +75 -0
  306. package/src/components/internal/FileBrowserListingInternal.vue +197 -0
  307. package/src/components/internal/FileBrowserToolbarInternal.vue +136 -0
  308. package/src/components/internal/FilePickerPreviewInternal.vue +100 -0
  309. package/src/components/internal/FilePickerTreeInternal.vue +238 -0
  310. package/src/components/internal/FormSectionRenderer.vue +0 -4
  311. package/src/components/internal/WellEditPopupInternal.vue +0 -4
  312. package/src/composables/controlSchemaTypes.ts +34 -0
  313. package/src/composables/filePicker/localFiles.ts +58 -0
  314. package/src/composables/filePicker/usePickerNavigation.ts +220 -0
  315. package/src/composables/filePicker/usePickerSelection.ts +259 -0
  316. package/src/composables/filePicker/validation.ts +41 -0
  317. package/src/composables/index.ts +17 -0
  318. package/src/composables/useApi.ts +57 -13
  319. package/src/composables/useDropdownState.ts +35 -5
  320. package/src/composables/useEventStream.ts +14 -2
  321. package/src/composables/useFileBrowser.ts +314 -0
  322. package/src/composables/useFileImport.ts +2 -7
  323. package/src/composables/useFilePicker.ts +366 -0
  324. package/src/composables/useGeneratedAnalysis.ts +18 -26
  325. package/src/composables/useListReorder.story.vue +143 -0
  326. package/src/composables/useListReorder.ts +242 -0
  327. package/src/composables/useMenuKeyboard.ts +2 -2
  328. package/src/composables/useOptimisticMutation.ts +1 -15
  329. package/src/composables/usePlatformFilePickerAdapter.ts +104 -0
  330. package/src/composables/usePluginClient.ts +3 -0
  331. package/src/composables/usePluginConfig.ts +3 -1
  332. package/src/composables/useRackEditor.ts +2 -11
  333. package/src/composables/useRequestSyncState.ts +98 -28
  334. package/src/composables/useResourceCrud.ts +1 -15
  335. package/src/composables/useScheduleDrag.ts +37 -0
  336. package/src/composables/useWellPainting.ts +5 -29
  337. package/src/composables/useWellPlateEditor.ts +1 -11
  338. package/src/index.ts +3 -0
  339. package/src/permissions.ts +48 -9
  340. package/src/stores/experiment.ts +2 -7
  341. package/src/stores/index.ts +9 -1
  342. package/src/stores/settings.ts +93 -1
  343. package/src/styles/components/audit-trail.css +5 -5
  344. package/src/styles/components/batch-progress-list.css +7 -0
  345. package/src/styles/components/color-slider.css +2 -2
  346. package/src/styles/components/data-state.css +30 -0
  347. package/src/styles/components/dataframe.css +29 -35
  348. package/src/styles/components/experiment-data-viewer.css +4 -4
  349. package/src/styles/components/experiment-timeline.css +3 -30
  350. package/src/styles/components/file-browser-modal.css +489 -0
  351. package/src/styles/components/file-picker.css +502 -0
  352. package/src/styles/components/form-builder.css +5 -5
  353. package/src/styles/components/icon-button.css +9 -0
  354. package/src/styles/components/lcms-sequence-table.css +16 -1
  355. package/src/styles/components/multi-select.css +1 -1
  356. package/src/styles/components/reagent-list.css +38 -31
  357. package/src/styles/components/sample-hierarchy-tree.css +5 -19
  358. package/src/styles/components/sample-selector.css +9 -2
  359. package/src/styles/components/schedule-calendar.css +8 -0
  360. package/src/styles/components/scientific-number.css +1 -1
  361. package/src/styles/components/well-plate.css +8 -0
  362. package/src/styles/index.css +11 -0
  363. package/src/styles/variables.css +29 -0
  364. package/src/templates/builderPresetControls.ts +1 -1
  365. package/src/templates/componentPlateHelpers.ts +3 -1
  366. package/src/templates/doseResponseAdapters.ts +2 -13
  367. package/src/templates/elisaAssayCollectionBuilder.ts +2 -2
  368. package/src/templates/plateMapBuilder.ts +2 -2
  369. package/src/templates/qpcrPlateBuilder.ts +1 -1
  370. package/src/templates/templateValidators.ts +1 -1
  371. package/src/templates/timeCourseBuilder.ts +2 -2
  372. package/src/templates/wellPlateScreenCollectionBuilder.ts +2 -2
  373. package/src/types/fileBrowserTypes.ts +85 -0
  374. package/src/types/filePicker.ts +72 -0
  375. package/src/types/index.ts +14 -0
  376. package/src/utils/color.ts +51 -0
  377. package/src/utils/errors.ts +29 -0
  378. package/dist/EmptyState-NBqf2_8z.js.map +0 -1
  379. package/dist/ExperimentPopover-CfOdh5Rm.js.map +0 -1
  380. package/dist/ExperimentSelectorModal-DvIagrxY.js.map +0 -1
  381. package/dist/SettingsModal-W3TYfnP9.js.map +0 -1
  382. package/dist/components-B80gV0P6.js.map +0 -1
  383. package/dist/composables/autoGroupConstants.d.ts +0 -1
  384. package/dist/composables/autoGroupGrouping.d.ts +0 -3
  385. package/dist/composables/useAutoGroupInputSources.d.ts +0 -32
  386. package/dist/composables-C8mmD5ta.js.map +0 -1
  387. package/dist/experiment-Y9Iz7KYi.js.map +0 -1
  388. package/dist/experiment-utils-DV3DrvE_.js.map +0 -1
  389. package/dist/templates/builderDefaults.d.ts +0 -1
  390. package/dist/templates/plateGeometry.d.ts +0 -4
  391. package/dist/templates-CK72aKO1.js.map +0 -1
  392. package/dist/useApi-CDBq6z23.js.map +0 -1
  393. package/dist/useControlSchema-_OVhavKq.js.map +0 -1
  394. package/dist/useDropdownState-D5G4UvWd.js.map +0 -1
  395. package/dist/useProtocolTemplates-Dq5Pzwyd.js.map +0 -1
  396. package/src/__tests__/composables/useAutoGroupInputSources.test.ts +0 -127
  397. package/src/composables/autoGroupConstants.ts +0 -4
  398. package/src/composables/autoGroupGrouping.ts +0 -148
  399. package/src/composables/useAutoGroupInputSources.ts +0 -147
  400. package/src/templates/builderDefaults.ts +0 -11
  401. package/src/templates/plateGeometry.ts +0 -62
  402. /package/dist/__tests__/{composables/useAutoGroupInputSources.test.d.ts → apiErrors.test.d.ts} +0 -0
package/README.md CHANGED
@@ -33,6 +33,11 @@ import '@morscherlab/mint-sdk/styles'
33
33
 
34
34
  The SDK includes all necessary CSS utility classes pre-built, so **Tailwind CSS is optional**. The styles work standalone or alongside Tailwind.
35
35
 
36
+ > **Required.** `dist/styles.css` is the SDK's single stylesheet: components no
37
+ > longer `@import` their own CSS from their `<style>` block, so a plugin that
38
+ > skips this import renders unstyled components. `src/styles/index.css` is the
39
+ > sole aggregator — every component stylesheet is imported there exactly once.
40
+
36
41
  ### 2. (Optional) Configure Tailwind
37
42
 
38
43
  If you're using Tailwind CSS and want to extend the MINT theme:
@@ -79,6 +84,28 @@ async function handleSubmit() {
79
84
  </template>
80
85
  ```
81
86
 
87
+ ### Native Plotly charts
88
+
89
+ `PlotlyChart` accepts native Plotly `data`, `layout`, and `config` props and
90
+ loads Plotly lazily. It shares MINT's chart header, loading/empty states, and
91
+ toolbar/legend slots. Explicit layout colors override the MINT defaults.
92
+
93
+ ```vue
94
+ <PlotlyChart
95
+ title="QC overview"
96
+ :data="[{ type: 'scatter', x: [1, 2, 3], y: [3, 4, 2] }]"
97
+ :layout="{ xaxis: { title: { text: 'Sample' } } }"
98
+ :config="{ toImageButtonOptions: { filename: 'qc-overview', format: 'png' } }"
99
+ />
100
+ ```
101
+
102
+ Replace data/layout/config references to redraw; nested in-place mutations do
103
+ not schedule a render. The component isolates Plotly's mutations of data and
104
+ layout, observes container size and the root theme, and cleans up on unmount.
105
+ Use `loading`, `empty`, and `emptyMessage` for explicit states, and the `error`
106
+ slot for a custom failure presentation. Generated Plotly results reuse this
107
+ renderer and retain their static-image fallback.
108
+
82
109
  ### 4. Use Generated Plugin Clients
83
110
 
84
111
  For plugin frontends scaffolded by `mint init`, run this from the plugin root:
@@ -105,6 +132,14 @@ const result = await pluginClient.analyze(
105
132
  )
106
133
  ```
107
134
 
135
+ Generated requests (including uploads and downloads) throw `MintApiError` from
136
+ `@morscherlab/mint-sdk` for HTTP failures. It exposes `code`, `message`, `status`,
137
+ `requestId`, `details`, and the original response `body`; the status comes from
138
+ HTTP, and the request ID falls back to `X-Request-ID`. Legacy FastAPI `detail`
139
+ and SDK plugin error responses are supported. Direct `useApi()` calls retain
140
+ Axios errors unless configured with `{ typedErrors: true }`; cancellation and
141
+ authentication retry behavior are unchanged.
142
+
108
143
  Generated files also expose typed plugin settings when the backend declares
109
144
  `@mint_plugin(config=PluginConfig)`:
110
145
 
@@ -656,6 +691,31 @@ The SDK provides ready-to-use CSS classes:
656
691
  - `.toggle-switch` - Toggle switch
657
692
  - `.checkbox-modern` - Styled checkbox
658
693
 
694
+ ## Icons
695
+
696
+ **[lucide-vue-next](https://lucide.dev) is the icon standard for this SDK.** New
697
+ components should import the icon component they need rather than inlining an
698
+ SVG:
699
+
700
+ ```vue
701
+ <script setup lang="ts">
702
+ import { RefreshCw } from 'lucide-vue-next'
703
+ </script>
704
+
705
+ <template>
706
+ <RefreshCw :size="16" />
707
+ </template>
708
+ ```
709
+
710
+ `lucide-vue-next` is a declared dependency and is **external** to the built
711
+ bundle (like `axios` and `@floating-ui/dom`), so the consumer resolves one
712
+ shared copy instead of the SDK shipping its own.
713
+
714
+ A number of older components still carry hand-written inline `<svg>` markup —
715
+ usually annotated with the Lucide icon name they were traced from. Those are
716
+ converted opportunistically, not in bulk; leave one alone unless you are already
717
+ editing the component.
718
+
659
719
  ## Theming
660
720
 
661
721
  The SDK uses CSS variables for theming. Override them to customize:
@@ -1,6 +1,6 @@
1
1
  import { n as __exportAll } from "./chunk-BoAXSpZd.js";
2
- import { f as BaseModal_default, h as BaseButton_default, l as formatExperimentStatus } from "./experiment-utils-DV3DrvE_.js";
3
- import { t as useDropdownState } from "./useDropdownState-D5G4UvWd.js";
2
+ import { f as BaseModal_default, h as BaseButton_default, l as formatExperimentStatus } from "./experiment-utils-CV2Wza3z.js";
3
+ import { t as useDropdownState } from "./useDropdownState-C7hRiVQf.js";
4
4
  import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createStaticVNode, createTextVNode, createVNode, defineComponent, normalizeClass, onUnmounted, openBlock, ref, renderSlot, toDisplayString, unref, watch, withCtx, withModifiers } from "vue";
5
5
  //#region src/components/ConfirmDialog.vue?vue&type=script&setup=true&lang.ts
6
6
  var _hoisted_1$1 = { class: "mint-confirm" };
@@ -246,9 +246,16 @@ var ExperimentPopover_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
246
246
  return props.experimentName || props.experimentCode || "No experiment";
247
247
  });
248
248
  const emit = __emit;
249
- const { isOpen, rootRef: popoverRef, close, toggle } = useDropdownState({ closeOnEscape: false });
249
+ const { isOpen, rootRef: popoverRef, triggerRef, close, toggle } = useDropdownState({ closeOnEscape: false });
250
250
  const showSuccess = ref(false);
251
251
  const showConfirm = ref(false);
252
+ function handleTriggerClick() {
253
+ if (!hasExperiment.value && !isUnresolved.value) {
254
+ handleSelect();
255
+ return;
256
+ }
257
+ toggle();
258
+ }
252
259
  function handleSelect() {
253
260
  emit("select");
254
261
  close();
@@ -287,6 +294,8 @@ var ExperimentPopover_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
287
294
  class: "mint-experiment-popover"
288
295
  }, [
289
296
  createElementVNode("div", { class: normalizeClass(["mint-experiment-popover__split", { "mint-experiment-popover__split--with-save": __props.showSave && __props.experimentName }]) }, [createElementVNode("button", {
297
+ ref_key: "triggerRef",
298
+ ref: triggerRef,
290
299
  type: "button",
291
300
  class: normalizeClass([
292
301
  "mint-experiment-popover__trigger",
@@ -299,9 +308,9 @@ var ExperimentPopover_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
299
308
  "aria-label": isUnresolved.value ? triggerLabel.value : void 0,
300
309
  "aria-busy": __props.isResolving || void 0,
301
310
  "aria-expanded": unref(isOpen),
302
- onClick: _cache[0] || (_cache[0] = withModifiers((...args) => unref(toggle) && unref(toggle)(...args), ["stop"]))
311
+ onClick: withModifiers(handleTriggerClick, ["stop"])
303
312
  }, [
304
- _cache[2] || (_cache[2] = createElementVNode("svg", {
313
+ _cache[1] || (_cache[1] = createElementVNode("svg", {
305
314
  class: "mint-experiment-popover__trigger-icon",
306
315
  "aria-hidden": "true",
307
316
  fill: "none",
@@ -314,7 +323,7 @@ var ExperimentPopover_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
314
323
  d: "M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"
315
324
  })], -1)),
316
325
  __props.isResolving ? (openBlock(), createElementBlock("span", _hoisted_2)) : __props.error ? (openBlock(), createElementBlock("span", _hoisted_3, "Experiment unavailable")) : __props.experimentName ? (openBlock(), createElementBlock("span", _hoisted_4, toDisplayString(__props.experimentName), 1)) : __props.experimentCode ? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(__props.experimentCode), 1)) : (openBlock(), createElementBlock("span", _hoisted_6, "No experiment")),
317
- _cache[3] || (_cache[3] = createElementVNode("svg", {
326
+ _cache[2] || (_cache[2] = createElementVNode("svg", {
318
327
  class: "mint-experiment-popover__trigger-chevron",
319
328
  "aria-hidden": "true",
320
329
  viewBox: "0 0 24 24",
@@ -337,18 +346,18 @@ var ExperimentPopover_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
337
346
  title: __props.saveDisabled && __props.saveDisabledMessage ? __props.saveDisabledMessage : showSuccess.value && __props.saveSuccessMessage ? __props.saveSuccessMessage : "Save to Experiment",
338
347
  "aria-label": "Save to experiment",
339
348
  onClick: withModifiers(handleSave, ["stop"])
340
- }, [__props.saveLoading ? (openBlock(), createElementBlock("span", _hoisted_8)) : showSuccess.value ? (openBlock(), createElementBlock("svg", _hoisted_9, [..._cache[4] || (_cache[4] = [createElementVNode("path", {
349
+ }, [__props.saveLoading ? (openBlock(), createElementBlock("span", _hoisted_8)) : showSuccess.value ? (openBlock(), createElementBlock("svg", _hoisted_9, [..._cache[3] || (_cache[3] = [createElementVNode("path", {
341
350
  "stroke-linecap": "round",
342
351
  "stroke-linejoin": "round",
343
352
  "stroke-width": "2.5",
344
353
  d: "M5 13l4 4L19 7"
345
- }, null, -1)])])) : (openBlock(), createElementBlock("svg", _hoisted_10, [..._cache[5] || (_cache[5] = [createElementVNode("path", {
354
+ }, null, -1)])])) : (openBlock(), createElementBlock("svg", _hoisted_10, [..._cache[4] || (_cache[4] = [createElementVNode("path", {
346
355
  "stroke-linecap": "round",
347
356
  "stroke-linejoin": "round",
348
357
  "stroke-width": "2",
349
358
  d: "M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"
350
359
  }, null, -1)])]))], 10, _hoisted_7)) : createCommentVNode("", true)], 2),
351
- unref(isOpen) ? (openBlock(), createElementBlock("div", _hoisted_11, [createElementVNode("div", _hoisted_12, [_cache[6] || (_cache[6] = createElementVNode("div", { class: "mint-experiment-popover__title" }, "Experiment", -1)), createElementVNode("div", _hoisted_13, toDisplayString(panelSubtitle.value), 1)]), __props.isResolving ? (openBlock(), createElementBlock("div", _hoisted_14, [_cache[7] || (_cache[7] = createStaticVNode("<div class=\"mint-experiment-popover__card\" aria-busy=\"true\"><div class=\"mint-experiment-popover__card-icon\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.75\" d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"></path></svg></div><div class=\"mint-experiment-popover__card-info\"><span class=\"mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--code\"></span><span class=\"mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--name\"></span><span class=\"mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--status\"></span></div></div>", 1)), createElementVNode("div", _hoisted_15, [createElementVNode("button", {
360
+ unref(isOpen) ? (openBlock(), createElementBlock("div", _hoisted_11, [createElementVNode("div", _hoisted_12, [_cache[5] || (_cache[5] = createElementVNode("div", { class: "mint-experiment-popover__title" }, "Experiment", -1)), createElementVNode("div", _hoisted_13, toDisplayString(panelSubtitle.value), 1)]), __props.isResolving ? (openBlock(), createElementBlock("div", _hoisted_14, [_cache[6] || (_cache[6] = createStaticVNode("<div class=\"mint-experiment-popover__card\" aria-busy=\"true\"><div class=\"mint-experiment-popover__card-icon\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.75\" d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"></path></svg></div><div class=\"mint-experiment-popover__card-info\"><span class=\"mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--code\"></span><span class=\"mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--name\"></span><span class=\"mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--status\"></span></div></div>", 1)), createElementVNode("div", _hoisted_15, [createElementVNode("button", {
352
361
  type: "button",
353
362
  class: "mint-experiment-popover__change-btn",
354
363
  onClick: handleSelect
@@ -357,7 +366,7 @@ var ExperimentPopover_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
357
366
  type: "button",
358
367
  class: "mint-experiment-popover__detach-btn",
359
368
  onClick: handleDetach
360
- }, " Detach ")) : createCommentVNode("", true)])])) : __props.error ? (openBlock(), createElementBlock("div", _hoisted_16, [createElementVNode("div", _hoisted_17, [_cache[9] || (_cache[9] = createElementVNode("div", { class: "mint-experiment-popover__card-icon mint-experiment-popover__card-icon--error" }, [createElementVNode("svg", {
369
+ }, " Detach ")) : createCommentVNode("", true)])])) : __props.error ? (openBlock(), createElementBlock("div", _hoisted_16, [createElementVNode("div", _hoisted_17, [_cache[8] || (_cache[8] = createElementVNode("div", { class: "mint-experiment-popover__card-icon mint-experiment-popover__card-icon--error" }, [createElementVNode("svg", {
361
370
  fill: "none",
362
371
  stroke: "currentColor",
363
372
  viewBox: "0 0 24 24"
@@ -366,7 +375,7 @@ var ExperimentPopover_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
366
375
  "stroke-linejoin": "round",
367
376
  "stroke-width": "1.75",
368
377
  d: "M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z"
369
- })])], -1)), createElementVNode("div", _hoisted_18, [_cache[8] || (_cache[8] = createElementVNode("div", { class: "mint-experiment-popover__card-name" }, "Experiment unavailable", -1)), createElementVNode("div", _hoisted_19, toDisplayString(__props.error), 1)])]), createElementVNode("div", _hoisted_20, [createElementVNode("button", {
378
+ })])], -1)), createElementVNode("div", _hoisted_18, [_cache[7] || (_cache[7] = createElementVNode("div", { class: "mint-experiment-popover__card-name" }, "Experiment unavailable", -1)), createElementVNode("div", _hoisted_19, toDisplayString(__props.error), 1)])]), createElementVNode("div", _hoisted_20, [createElementVNode("button", {
370
379
  type: "button",
371
380
  class: "mint-experiment-popover__change-btn",
372
381
  onClick: handleSelect
@@ -379,7 +388,7 @@ var ExperimentPopover_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
379
388
  type: "button",
380
389
  class: "mint-experiment-popover__select-btn",
381
390
  onClick: handleSelect
382
- }, [..._cache[10] || (_cache[10] = [createElementVNode("svg", {
391
+ }, [..._cache[9] || (_cache[9] = [createElementVNode("svg", {
383
392
  width: "14",
384
393
  height: "14",
385
394
  fill: "none",
@@ -390,7 +399,7 @@ var ExperimentPopover_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
390
399
  "stroke-linejoin": "round",
391
400
  "stroke-width": "2",
392
401
  d: "M12 4v16m8-8H4"
393
- })], -1), createTextVNode(" Select Experiment ", -1)])])])) : (openBlock(), createElementBlock("div", _hoisted_22, [createElementVNode("div", _hoisted_23, [_cache[11] || (_cache[11] = createElementVNode("div", { class: "mint-experiment-popover__card-icon" }, [createElementVNode("svg", {
402
+ })], -1), createTextVNode(" Select Experiment ", -1)])])])) : (openBlock(), createElementBlock("div", _hoisted_22, [createElementVNode("div", _hoisted_23, [_cache[10] || (_cache[10] = createElementVNode("div", { class: "mint-experiment-popover__card-icon" }, [createElementVNode("svg", {
394
403
  fill: "none",
395
404
  stroke: "currentColor",
396
405
  viewBox: "0 0 24 24"
@@ -415,7 +424,7 @@ var ExperimentPopover_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
415
424
  }, " Detach ")) : createCommentVNode("", true)])]))])) : createCommentVNode("", true),
416
425
  createVNode(ConfirmDialog_default, {
417
426
  modelValue: showConfirm.value,
418
- "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => showConfirm.value = $event),
427
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => showConfirm.value = $event),
419
428
  title: __props.confirmTitle ?? "Save to Experiment",
420
429
  message: __props.confirmMessage ?? `Save current data to ${__props.experimentName}?`,
421
430
  variant: "info",
@@ -439,4 +448,4 @@ var ExperimentPopover_default = ExperimentPopover_vue_vue_type_script_setup_true
439
448
  //#endregion
440
449
  export { ExperimentPopover_exports as n, ConfirmDialog_default as r, ExperimentPopover_default as t };
441
450
 
442
- //# sourceMappingURL=ExperimentPopover-CfOdh5Rm.js.map
451
+ //# sourceMappingURL=ExperimentPopover-CJdNlCZb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExperimentPopover-CJdNlCZb.js","names":["$slots"],"sources":["../src/components/ConfirmDialog.vue","../src/components/ConfirmDialog.vue","../src/components/ExperimentPopover.vue","../src/components/ExperimentPopover.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/** Confirm/cancel dialog with danger, warning, and info variants; blocks close while loading. */\nimport { computed } from 'vue'\nimport type { ButtonVariant } from '../types'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n modelValue: boolean\n title?: string\n subtitle?: string\n message?: string\n variant?: 'danger' | 'warning' | 'info'\n confirmLabel?: string\n cancelLabel?: string\n loading?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: 'Confirm',\n variant: 'danger',\n confirmLabel: 'Confirm',\n cancelLabel: 'Cancel',\n loading: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n confirm: []\n cancel: []\n}>()\n\nfunction handleCancel() {\n emit('update:modelValue', false)\n emit('cancel')\n}\n\nfunction handleConfirm() {\n emit('confirm')\n}\n\nconst confirmVariant = computed<ButtonVariant>(() => {\n if (props.variant === 'info') return 'primary'\n if (props.variant === 'warning') return 'cta'\n return 'danger'\n})\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :subtitle=\"subtitle\"\n size=\"sm\"\n :closable=\"!loading\"\n :close-on-overlay=\"!loading\"\n :close-on-escape=\"!loading\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mint-confirm\">\n <!-- Icon is now opt-in via slot. Refresh design keeps the body focused on\n the message; variant intent is carried by the confirm button's color. -->\n <div\n v-if=\"$slots.icon\"\n :class=\"['mint-confirm__icon', `mint-confirm__icon--${variant}`]\"\n >\n <slot name=\"icon\" />\n </div>\n <p v-if=\"message\" class=\"mint-confirm__message\">{{ message }}</p>\n <slot />\n </div>\n\n <template #footer>\n <div class=\"mint-confirm__footer\">\n <BaseButton\n type=\"button\"\n variant=\"secondary\"\n size=\"md\"\n :disabled=\"loading\"\n @click=\"handleCancel\"\n >\n {{ cancelLabel }}\n </BaseButton>\n <BaseButton\n type=\"button\"\n :variant=\"confirmVariant\"\n size=\"md\"\n :loading=\"loading\"\n @click=\"handleConfirm\"\n >\n {{ confirmLabel }}\n </BaseButton>\n </div>\n </template>\n </BaseModal>\n</template>\n","<script setup lang=\"ts\">\n/** Confirm/cancel dialog with danger, warning, and info variants; blocks close while loading. */\nimport { computed } from 'vue'\nimport type { ButtonVariant } from '../types'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n modelValue: boolean\n title?: string\n subtitle?: string\n message?: string\n variant?: 'danger' | 'warning' | 'info'\n confirmLabel?: string\n cancelLabel?: string\n loading?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: 'Confirm',\n variant: 'danger',\n confirmLabel: 'Confirm',\n cancelLabel: 'Cancel',\n loading: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n confirm: []\n cancel: []\n}>()\n\nfunction handleCancel() {\n emit('update:modelValue', false)\n emit('cancel')\n}\n\nfunction handleConfirm() {\n emit('confirm')\n}\n\nconst confirmVariant = computed<ButtonVariant>(() => {\n if (props.variant === 'info') return 'primary'\n if (props.variant === 'warning') return 'cta'\n return 'danger'\n})\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :subtitle=\"subtitle\"\n size=\"sm\"\n :closable=\"!loading\"\n :close-on-overlay=\"!loading\"\n :close-on-escape=\"!loading\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mint-confirm\">\n <!-- Icon is now opt-in via slot. Refresh design keeps the body focused on\n the message; variant intent is carried by the confirm button's color. -->\n <div\n v-if=\"$slots.icon\"\n :class=\"['mint-confirm__icon', `mint-confirm__icon--${variant}`]\"\n >\n <slot name=\"icon\" />\n </div>\n <p v-if=\"message\" class=\"mint-confirm__message\">{{ message }}</p>\n <slot />\n </div>\n\n <template #footer>\n <div class=\"mint-confirm__footer\">\n <BaseButton\n type=\"button\"\n variant=\"secondary\"\n size=\"md\"\n :disabled=\"loading\"\n @click=\"handleCancel\"\n >\n {{ cancelLabel }}\n </BaseButton>\n <BaseButton\n type=\"button\"\n :variant=\"confirmVariant\"\n size=\"md\"\n :loading=\"loading\"\n @click=\"handleConfirm\"\n >\n {{ confirmLabel }}\n </BaseButton>\n </div>\n </template>\n </BaseModal>\n</template>\n","<script setup lang=\"ts\">\n/** Floating popover showing the active experiment with save, detach, and select actions. */\nimport { ref, computed, watch, onUnmounted } from 'vue'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport { formatExperimentStatus } from '../composables/experiment-utils'\nimport ConfirmDialog from './ConfirmDialog.vue'\n\ninterface Props {\n experimentName?: string\n experimentCode?: string\n experimentStatus?: string\n /** An experiment is bound but the platform has not returned its record yet. */\n isResolving?: boolean\n /** Message explaining why the bound experiment could not be resolved. */\n error?: string\n showSave?: boolean\n showDetach?: boolean\n saveDisabled?: boolean\n saveLoading?: boolean\n saveSuccessMessage?: string\n saveDisabledMessage?: string\n confirmSave?: boolean\n confirmTitle?: string\n confirmMessage?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n isResolving: false,\n showSave: false,\n showDetach: false,\n saveDisabled: false,\n saveLoading: false,\n confirmSave: true,\n})\n\n// An experiment is bound but has no name to show — either still resolving or\n// resolution failed. The id is never substituted: a name may only come from the\n// platform, so these states say what is happening instead of inventing a label.\nconst isUnresolved = computed(() => props.isResolving || !!props.error)\nconst hasExperiment = computed(() => !!props.experimentName || !!props.experimentCode)\n\nconst panelSubtitle = computed(() => {\n if (props.isResolving) return 'Loading experiment…'\n if (props.error) return 'Could not load this experiment'\n return hasExperiment.value ? 'Linked experiment context' : 'Link to an MINT experiment'\n})\n\nconst triggerLabel = computed(() => {\n if (props.isResolving) return 'Loading experiment'\n if (props.error) return 'Experiment unavailable'\n return props.experimentName || props.experimentCode || 'No experiment'\n})\n\nconst emit = defineEmits<{\n select: []\n save: []\n detach: []\n}>()\n\nconst { isOpen, rootRef: popoverRef, triggerRef, close, toggle } = useDropdownState({\n closeOnEscape: false,\n})\nconst showSuccess = ref(false)\nconst showConfirm = ref(false)\n\nfunction handleTriggerClick() {\n if (!hasExperiment.value && !isUnresolved.value) {\n handleSelect()\n return\n }\n toggle()\n}\n\nfunction handleSelect() {\n emit('select')\n close()\n}\n\nfunction handleSave() {\n if (props.saveDisabled || props.saveLoading) return\n if (props.confirmSave) {\n showConfirm.value = true\n } else {\n emit('save')\n }\n}\n\nfunction handleConfirmSave() {\n showConfirm.value = false\n emit('save')\n}\n\nfunction handleDetach() {\n emit('detach')\n close()\n}\n\nlet successTimer: ReturnType<typeof setTimeout> | null = null\n\n// Show success state when saveSuccessMessage changes from empty to a value\nwatch(() => props.saveSuccessMessage, (msg) => {\n if (successTimer) clearTimeout(successTimer)\n if (msg) {\n showSuccess.value = true\n successTimer = setTimeout(() => {\n showSuccess.value = false\n successTimer = null\n }, 3000)\n }\n})\n\nonUnmounted(() => {\n if (successTimer) clearTimeout(successTimer)\n})\n</script>\n\n<template>\n <div ref=\"popoverRef\" class=\"mint-experiment-popover\">\n <!-- Split trigger: experiment pill + inline save -->\n <div\n :class=\"[\n 'mint-experiment-popover__split',\n { 'mint-experiment-popover__split--with-save': showSave && experimentName },\n ]\"\n >\n <!-- Left: experiment trigger (selects when empty, otherwise opens popover). Prefer the human-readable\n name; the formal code, when present, stays in the detail panel. -->\n <button\n ref=\"triggerRef\"\n type=\"button\"\n :class=\"[\n 'mint-experiment-popover__trigger',\n { 'mint-experiment-popover__trigger--active': isOpen },\n { 'mint-experiment-popover__trigger--empty': !isUnresolved && !hasExperiment },\n { 'mint-experiment-popover__trigger--loading': isResolving },\n { 'mint-experiment-popover__trigger--error': !isResolving && !!error },\n ]\"\n :title=\"isUnresolved ? triggerLabel : experimentName || experimentCode || undefined\"\n :aria-label=\"isUnresolved ? triggerLabel : undefined\"\n :aria-busy=\"isResolving || undefined\"\n :aria-expanded=\"isOpen\"\n @click.stop=\"handleTriggerClick\"\n >\n <!-- Flask icon -->\n <svg class=\"mint-experiment-popover__trigger-icon\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n <!-- Resolving and error come first: with an experiment bound but no name\n yet, \"No experiment\" would be a lie. -->\n <span v-if=\"isResolving\" class=\"mint-experiment-popover__trigger-skeleton\" aria-hidden=\"true\" />\n <span v-else-if=\"error\" class=\"mint-experiment-popover__trigger-text\">Experiment unavailable</span>\n <!-- Name preferred, code as fallback, \"No experiment\" as empty state -->\n <span v-else-if=\"experimentName\" class=\"mint-experiment-popover__trigger-text\">{{ experimentName }}</span>\n <span v-else-if=\"experimentCode\" class=\"mint-experiment-popover__trigger-code\">{{ experimentCode }}</span>\n <span v-else class=\"mint-experiment-popover__trigger-text\">No experiment</span>\n <!-- Chevron -->\n <svg class=\"mint-experiment-popover__trigger-chevron\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <!-- Right: inline save button (direct action) -->\n <button\n v-if=\"showSave && experimentName\"\n type=\"button\"\n :class=\"[\n 'mint-experiment-popover__save-trigger',\n { 'mint-experiment-popover__save-trigger--loading': saveLoading },\n { 'mint-experiment-popover__save-trigger--success': showSuccess },\n { 'mint-experiment-popover__save-trigger--disabled': saveDisabled && !showSuccess },\n ]\"\n :disabled=\"saveDisabled && !showSuccess\"\n :title=\"saveDisabled && saveDisabledMessage ? saveDisabledMessage : showSuccess && saveSuccessMessage ? saveSuccessMessage : 'Save to Experiment'\"\n aria-label=\"Save to experiment\"\n @click.stop=\"handleSave\"\n >\n <!-- Loading spinner -->\n <span v-if=\"saveLoading\" class=\"mint-experiment-popover__spinner--inline\" />\n <!-- Success check -->\n <svg v-else-if=\"showSuccess\" class=\"mint-experiment-popover__save-trigger-icon\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2.5\" d=\"M5 13l4 4L19 7\" />\n </svg>\n <!-- Save icon -->\n <svg v-else class=\"mint-experiment-popover__save-trigger-icon\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4\" />\n </svg>\n </button>\n </div>\n\n <!-- Popover panel -->\n <div v-if=\"isOpen\" class=\"mint-experiment-popover__panel\">\n <!-- Header -->\n <div class=\"mint-experiment-popover__header\">\n <div class=\"mint-experiment-popover__title\">Experiment</div>\n <div class=\"mint-experiment-popover__subtitle\">\n {{ panelSubtitle }}\n </div>\n </div>\n\n <!-- Bound, still resolving — placeholders stand in for the record the\n platform has not returned yet. -->\n <div v-if=\"isResolving\" class=\"mint-experiment-popover__body\">\n <div class=\"mint-experiment-popover__card\" aria-busy=\"true\">\n <div class=\"mint-experiment-popover__card-icon\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n </div>\n <div class=\"mint-experiment-popover__card-info\">\n <span class=\"mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--code\" />\n <span class=\"mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--name\" />\n <span class=\"mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--status\" />\n </div>\n </div>\n <div class=\"mint-experiment-popover__card-actions\">\n <button type=\"button\" class=\"mint-experiment-popover__change-btn\" @click=\"handleSelect\">\n Change\n </button>\n <button v-if=\"showDetach\" type=\"button\" class=\"mint-experiment-popover__detach-btn\" @click=\"handleDetach\">\n Detach\n </button>\n </div>\n </div>\n\n <!-- Bound, resolution failed — Detach stays reachable so the user is not\n stuck on an experiment the platform cannot describe. -->\n <div v-else-if=\"error\" class=\"mint-experiment-popover__body\">\n <div class=\"mint-experiment-popover__card mint-experiment-popover__card--error\" role=\"alert\">\n <div class=\"mint-experiment-popover__card-icon mint-experiment-popover__card-icon--error\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z\"\n />\n </svg>\n </div>\n <div class=\"mint-experiment-popover__card-info\">\n <div class=\"mint-experiment-popover__card-name\">Experiment unavailable</div>\n <div class=\"mint-experiment-popover__card-message\">{{ error }}</div>\n </div>\n </div>\n <div class=\"mint-experiment-popover__card-actions\">\n <button type=\"button\" class=\"mint-experiment-popover__change-btn\" @click=\"handleSelect\">\n Change\n </button>\n <button v-if=\"showDetach\" type=\"button\" class=\"mint-experiment-popover__detach-btn\" @click=\"handleDetach\">\n Detach\n </button>\n </div>\n </div>\n\n <!-- No experiment selected -->\n <div v-else-if=\"!experimentName\" class=\"mint-experiment-popover__body\">\n <button type=\"button\" class=\"mint-experiment-popover__select-btn\" @click=\"handleSelect\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n Select Experiment\n </button>\n </div>\n\n <!-- Experiment selected -->\n <div v-else class=\"mint-experiment-popover__body\">\n <div class=\"mint-experiment-popover__card\">\n <div class=\"mint-experiment-popover__card-icon\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n </div>\n <div class=\"mint-experiment-popover__card-info\">\n <span v-if=\"experimentCode\" class=\"mint-experiment-popover__card-code\">{{ experimentCode }}</span>\n <div class=\"mint-experiment-popover__card-name\">{{ experimentName }}</div>\n <div v-if=\"experimentStatus\" class=\"mint-experiment-popover__card-status\">\n {{ formatExperimentStatus(experimentStatus) }}\n </div>\n </div>\n </div>\n <div class=\"mint-experiment-popover__card-actions\">\n <button type=\"button\" class=\"mint-experiment-popover__change-btn\" @click=\"handleSelect\">\n Change\n </button>\n <button v-if=\"showDetach\" type=\"button\" class=\"mint-experiment-popover__detach-btn\" @click=\"handleDetach\">\n Detach\n </button>\n </div>\n </div>\n </div>\n\n <!-- Save confirmation dialog -->\n <ConfirmDialog\n v-model=\"showConfirm\"\n :title=\"confirmTitle ?? 'Save to Experiment'\"\n :message=\"confirmMessage ?? `Save current data to ${experimentName}?`\"\n variant=\"info\"\n confirm-label=\"Save\"\n :loading=\"saveLoading\"\n @confirm=\"handleConfirmSave\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Floating popover showing the active experiment with save, detach, and select actions. */\nimport { ref, computed, watch, onUnmounted } from 'vue'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport { formatExperimentStatus } from '../composables/experiment-utils'\nimport ConfirmDialog from './ConfirmDialog.vue'\n\ninterface Props {\n experimentName?: string\n experimentCode?: string\n experimentStatus?: string\n /** An experiment is bound but the platform has not returned its record yet. */\n isResolving?: boolean\n /** Message explaining why the bound experiment could not be resolved. */\n error?: string\n showSave?: boolean\n showDetach?: boolean\n saveDisabled?: boolean\n saveLoading?: boolean\n saveSuccessMessage?: string\n saveDisabledMessage?: string\n confirmSave?: boolean\n confirmTitle?: string\n confirmMessage?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n isResolving: false,\n showSave: false,\n showDetach: false,\n saveDisabled: false,\n saveLoading: false,\n confirmSave: true,\n})\n\n// An experiment is bound but has no name to show — either still resolving or\n// resolution failed. The id is never substituted: a name may only come from the\n// platform, so these states say what is happening instead of inventing a label.\nconst isUnresolved = computed(() => props.isResolving || !!props.error)\nconst hasExperiment = computed(() => !!props.experimentName || !!props.experimentCode)\n\nconst panelSubtitle = computed(() => {\n if (props.isResolving) return 'Loading experiment…'\n if (props.error) return 'Could not load this experiment'\n return hasExperiment.value ? 'Linked experiment context' : 'Link to an MINT experiment'\n})\n\nconst triggerLabel = computed(() => {\n if (props.isResolving) return 'Loading experiment'\n if (props.error) return 'Experiment unavailable'\n return props.experimentName || props.experimentCode || 'No experiment'\n})\n\nconst emit = defineEmits<{\n select: []\n save: []\n detach: []\n}>()\n\nconst { isOpen, rootRef: popoverRef, triggerRef, close, toggle } = useDropdownState({\n closeOnEscape: false,\n})\nconst showSuccess = ref(false)\nconst showConfirm = ref(false)\n\nfunction handleTriggerClick() {\n if (!hasExperiment.value && !isUnresolved.value) {\n handleSelect()\n return\n }\n toggle()\n}\n\nfunction handleSelect() {\n emit('select')\n close()\n}\n\nfunction handleSave() {\n if (props.saveDisabled || props.saveLoading) return\n if (props.confirmSave) {\n showConfirm.value = true\n } else {\n emit('save')\n }\n}\n\nfunction handleConfirmSave() {\n showConfirm.value = false\n emit('save')\n}\n\nfunction handleDetach() {\n emit('detach')\n close()\n}\n\nlet successTimer: ReturnType<typeof setTimeout> | null = null\n\n// Show success state when saveSuccessMessage changes from empty to a value\nwatch(() => props.saveSuccessMessage, (msg) => {\n if (successTimer) clearTimeout(successTimer)\n if (msg) {\n showSuccess.value = true\n successTimer = setTimeout(() => {\n showSuccess.value = false\n successTimer = null\n }, 3000)\n }\n})\n\nonUnmounted(() => {\n if (successTimer) clearTimeout(successTimer)\n})\n</script>\n\n<template>\n <div ref=\"popoverRef\" class=\"mint-experiment-popover\">\n <!-- Split trigger: experiment pill + inline save -->\n <div\n :class=\"[\n 'mint-experiment-popover__split',\n { 'mint-experiment-popover__split--with-save': showSave && experimentName },\n ]\"\n >\n <!-- Left: experiment trigger (selects when empty, otherwise opens popover). Prefer the human-readable\n name; the formal code, when present, stays in the detail panel. -->\n <button\n ref=\"triggerRef\"\n type=\"button\"\n :class=\"[\n 'mint-experiment-popover__trigger',\n { 'mint-experiment-popover__trigger--active': isOpen },\n { 'mint-experiment-popover__trigger--empty': !isUnresolved && !hasExperiment },\n { 'mint-experiment-popover__trigger--loading': isResolving },\n { 'mint-experiment-popover__trigger--error': !isResolving && !!error },\n ]\"\n :title=\"isUnresolved ? triggerLabel : experimentName || experimentCode || undefined\"\n :aria-label=\"isUnresolved ? triggerLabel : undefined\"\n :aria-busy=\"isResolving || undefined\"\n :aria-expanded=\"isOpen\"\n @click.stop=\"handleTriggerClick\"\n >\n <!-- Flask icon -->\n <svg class=\"mint-experiment-popover__trigger-icon\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n <!-- Resolving and error come first: with an experiment bound but no name\n yet, \"No experiment\" would be a lie. -->\n <span v-if=\"isResolving\" class=\"mint-experiment-popover__trigger-skeleton\" aria-hidden=\"true\" />\n <span v-else-if=\"error\" class=\"mint-experiment-popover__trigger-text\">Experiment unavailable</span>\n <!-- Name preferred, code as fallback, \"No experiment\" as empty state -->\n <span v-else-if=\"experimentName\" class=\"mint-experiment-popover__trigger-text\">{{ experimentName }}</span>\n <span v-else-if=\"experimentCode\" class=\"mint-experiment-popover__trigger-code\">{{ experimentCode }}</span>\n <span v-else class=\"mint-experiment-popover__trigger-text\">No experiment</span>\n <!-- Chevron -->\n <svg class=\"mint-experiment-popover__trigger-chevron\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <!-- Right: inline save button (direct action) -->\n <button\n v-if=\"showSave && experimentName\"\n type=\"button\"\n :class=\"[\n 'mint-experiment-popover__save-trigger',\n { 'mint-experiment-popover__save-trigger--loading': saveLoading },\n { 'mint-experiment-popover__save-trigger--success': showSuccess },\n { 'mint-experiment-popover__save-trigger--disabled': saveDisabled && !showSuccess },\n ]\"\n :disabled=\"saveDisabled && !showSuccess\"\n :title=\"saveDisabled && saveDisabledMessage ? saveDisabledMessage : showSuccess && saveSuccessMessage ? saveSuccessMessage : 'Save to Experiment'\"\n aria-label=\"Save to experiment\"\n @click.stop=\"handleSave\"\n >\n <!-- Loading spinner -->\n <span v-if=\"saveLoading\" class=\"mint-experiment-popover__spinner--inline\" />\n <!-- Success check -->\n <svg v-else-if=\"showSuccess\" class=\"mint-experiment-popover__save-trigger-icon\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2.5\" d=\"M5 13l4 4L19 7\" />\n </svg>\n <!-- Save icon -->\n <svg v-else class=\"mint-experiment-popover__save-trigger-icon\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4\" />\n </svg>\n </button>\n </div>\n\n <!-- Popover panel -->\n <div v-if=\"isOpen\" class=\"mint-experiment-popover__panel\">\n <!-- Header -->\n <div class=\"mint-experiment-popover__header\">\n <div class=\"mint-experiment-popover__title\">Experiment</div>\n <div class=\"mint-experiment-popover__subtitle\">\n {{ panelSubtitle }}\n </div>\n </div>\n\n <!-- Bound, still resolving — placeholders stand in for the record the\n platform has not returned yet. -->\n <div v-if=\"isResolving\" class=\"mint-experiment-popover__body\">\n <div class=\"mint-experiment-popover__card\" aria-busy=\"true\">\n <div class=\"mint-experiment-popover__card-icon\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n </div>\n <div class=\"mint-experiment-popover__card-info\">\n <span class=\"mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--code\" />\n <span class=\"mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--name\" />\n <span class=\"mint-experiment-popover__skeleton-line mint-experiment-popover__skeleton-line--status\" />\n </div>\n </div>\n <div class=\"mint-experiment-popover__card-actions\">\n <button type=\"button\" class=\"mint-experiment-popover__change-btn\" @click=\"handleSelect\">\n Change\n </button>\n <button v-if=\"showDetach\" type=\"button\" class=\"mint-experiment-popover__detach-btn\" @click=\"handleDetach\">\n Detach\n </button>\n </div>\n </div>\n\n <!-- Bound, resolution failed — Detach stays reachable so the user is not\n stuck on an experiment the platform cannot describe. -->\n <div v-else-if=\"error\" class=\"mint-experiment-popover__body\">\n <div class=\"mint-experiment-popover__card mint-experiment-popover__card--error\" role=\"alert\">\n <div class=\"mint-experiment-popover__card-icon mint-experiment-popover__card-icon--error\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z\"\n />\n </svg>\n </div>\n <div class=\"mint-experiment-popover__card-info\">\n <div class=\"mint-experiment-popover__card-name\">Experiment unavailable</div>\n <div class=\"mint-experiment-popover__card-message\">{{ error }}</div>\n </div>\n </div>\n <div class=\"mint-experiment-popover__card-actions\">\n <button type=\"button\" class=\"mint-experiment-popover__change-btn\" @click=\"handleSelect\">\n Change\n </button>\n <button v-if=\"showDetach\" type=\"button\" class=\"mint-experiment-popover__detach-btn\" @click=\"handleDetach\">\n Detach\n </button>\n </div>\n </div>\n\n <!-- No experiment selected -->\n <div v-else-if=\"!experimentName\" class=\"mint-experiment-popover__body\">\n <button type=\"button\" class=\"mint-experiment-popover__select-btn\" @click=\"handleSelect\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n Select Experiment\n </button>\n </div>\n\n <!-- Experiment selected -->\n <div v-else class=\"mint-experiment-popover__body\">\n <div class=\"mint-experiment-popover__card\">\n <div class=\"mint-experiment-popover__card-icon\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n </div>\n <div class=\"mint-experiment-popover__card-info\">\n <span v-if=\"experimentCode\" class=\"mint-experiment-popover__card-code\">{{ experimentCode }}</span>\n <div class=\"mint-experiment-popover__card-name\">{{ experimentName }}</div>\n <div v-if=\"experimentStatus\" class=\"mint-experiment-popover__card-status\">\n {{ formatExperimentStatus(experimentStatus) }}\n </div>\n </div>\n </div>\n <div class=\"mint-experiment-popover__card-actions\">\n <button type=\"button\" class=\"mint-experiment-popover__change-btn\" @click=\"handleSelect\">\n Change\n </button>\n <button v-if=\"showDetach\" type=\"button\" class=\"mint-experiment-popover__detach-btn\" @click=\"handleDetach\">\n Detach\n </button>\n </div>\n </div>\n </div>\n\n <!-- Save confirmation dialog -->\n <ConfirmDialog\n v-model=\"showConfirm\"\n :title=\"confirmTitle ?? 'Save to Experiment'\"\n :message=\"confirmMessage ?? `Save current data to ${experimentName}?`\"\n variant=\"info\"\n confirm-label=\"Save\"\n :loading=\"saveLoading\"\n @confirm=\"handleConfirmSave\"\n />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECkBA,MAAM,QAAQ;EAQd,MAAM,OAAO;EAMb,SAAS,eAAe;AACtB,QAAK,qBAAqB,MAAK;AAC/B,QAAK,SAAQ;;EAGf,SAAS,gBAAgB;AACvB,QAAK,UAAS;;EAGhB,MAAM,iBAAiB,eAA8B;AACnD,OAAI,MAAM,YAAY,OAAQ,QAAO;AACrC,OAAI,MAAM,YAAY,UAAW,QAAO;AACxC,UAAO;IACR;;uBAIC,YA6CY,mBAAA;IA5CT,eAAa,QAAA;IACb,OAAO,QAAA;IACP,UAAU,QAAA;IACX,MAAK;IACJ,UAAQ,CAAG,QAAA;IACX,oBAAgB,CAAG,QAAA;IACnB,mBAAe,CAAG,QAAA;IAClB,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,qBAAsB,OAAM;;IAe1C,QAAM,cAoBT,CAnBN,mBAmBM,OAnBN,cAmBM,CAlBJ,YAQa,oBAAA;KAPX,MAAK;KACL,SAAQ;KACR,MAAK;KACJ,UAAU,QAAA;KACV,SAAO;;4BAES,CAAA,gBAAA,gBAAd,QAAA,YAAW,EAAA,EAAA,CAAA,CAAA;;yBAEhB,YAQa,oBAAA;KAPX,MAAK;KACJ,SAAS,eAAA;KACV,MAAK;KACJ,SAAS,QAAA;KACT,SAAO;;4BAEU,CAAA,gBAAA,gBAAf,QAAA,aAAY,EAAA,EAAA,CAAA,CAAA;;;2BApBf,CAXN,mBAWM,OAXN,cAWM;KAPIA,KAAAA,OAAO,QAAA,WAAA,EADf,mBAKM,OAAA;;MAHH,OAAK,eAAA,CAAA,sBAAA,uBAAgD,QAAA,UAAO,CAAA;SAE7D,WAAoB,KAAA,QAAA,OAAA,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAEb,QAAA,WAAA,WAAA,EAAT,mBAAiE,KAAjE,cAAiE,gBAAd,QAAA,QAAO,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAC1D,WAAQ,KAAA,QAAA,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE3Cd,MAAM,QAAQ;EAYd,MAAM,eAAe,eAAe,MAAM,eAAe,CAAC,CAAC,MAAM,MAAK;EACtE,MAAM,gBAAgB,eAAe,CAAC,CAAC,MAAM,kBAAkB,CAAC,CAAC,MAAM,eAAc;EAErF,MAAM,gBAAgB,eAAe;AACnC,OAAI,MAAM,YAAa,QAAO;AAC9B,OAAI,MAAM,MAAO,QAAO;AACxB,UAAO,cAAc,QAAQ,8BAA8B;IAC5D;EAED,MAAM,eAAe,eAAe;AAClC,OAAI,MAAM,YAAa,QAAO;AAC9B,OAAI,MAAM,MAAO,QAAO;AACxB,UAAO,MAAM,kBAAkB,MAAM,kBAAkB;IACxD;EAED,MAAM,OAAO;EAMb,MAAM,EAAE,QAAQ,SAAS,YAAY,YAAY,OAAO,WAAW,iBAAiB,EAClF,eAAe,OAChB,CAAA;EACD,MAAM,cAAc,IAAI,MAAK;EAC7B,MAAM,cAAc,IAAI,MAAK;EAE7B,SAAS,qBAAqB;AAC5B,OAAI,CAAC,cAAc,SAAS,CAAC,aAAa,OAAO;AAC/C,kBAAa;AACb;;AAEF,WAAO;;EAGT,SAAS,eAAe;AACtB,QAAK,SAAQ;AACb,UAAM;;EAGR,SAAS,aAAa;AACpB,OAAI,MAAM,gBAAgB,MAAM,YAAa;AAC7C,OAAI,MAAM,YACR,aAAY,QAAQ;OAEpB,MAAK,OAAM;;EAIf,SAAS,oBAAoB;AAC3B,eAAY,QAAQ;AACpB,QAAK,OAAM;;EAGb,SAAS,eAAe;AACtB,QAAK,SAAQ;AACb,UAAM;;EAGR,IAAI,eAAqD;AAGzD,cAAY,MAAM,qBAAqB,QAAQ;AAC7C,OAAI,aAAc,cAAa,aAAY;AAC3C,OAAI,KAAK;AACP,gBAAY,QAAQ;AACpB,mBAAe,iBAAiB;AAC9B,iBAAY,QAAQ;AACpB,oBAAe;OACd,IAAI;;IAEV;AAED,oBAAkB;AAChB,OAAI,aAAc,cAAa,aAAY;IAC5C;;uBAIC,mBAsMM,OAAA;aAtMG;IAAJ,KAAI;IAAa,OAAM;;IAE1B,mBAyEM,OAAA,EAxEH,OAAK,eAAA,CAAA,kCAAA,EAAA,6CAAqG,QAAA,YAAY,QAAA,gBAAc,CAAA,CAAA,EAAA,EAAA,CAOrI,mBAqCS,UAAA;cApCH;KAAJ,KAAI;KACJ,MAAK;KACJ,OAAK,eAAA;;oDAA0G,MAAA,OAAM,EAAA;oDAA4D,aAAA,SAAY,CAAK,cAAA,OAAa;qDAA6D,QAAA,aAAW;oDAA4D,QAAA,eAAW,CAAA,CAAM,QAAA,OAAK;;KAOzW,OAAO,aAAA,QAAe,aAAA,QAAe,QAAA,kBAAkB,QAAA,kBAAkB,KAAA;KACzE,cAAY,aAAA,QAAe,aAAA,QAAe,KAAA;KAC1C,aAAW,QAAA,eAAe,KAAA;KAC1B,iBAAe,MAAA,OAAM;KACrB,SAAK,cAAO,oBAAkB,CAAA,OAAA,CAAA;;+BAG/B,mBAOM,OAAA;MAPD,OAAM;MAAwC,eAAY;MAAO,MAAK;MAAO,QAAO;MAAe,SAAQ;SAC9G,mBAKE,QAAA;MAJA,kBAAe;MACf,mBAAgB;MAChB,gBAAa;MACb,GAAE;;KAKM,QAAA,eAAA,WAAA,EAAZ,mBAAgG,QAAhG,WAAgG,IAC/E,QAAA,SAAA,WAAA,EAAjB,mBAAmG,QAAnG,YAAsE,yBAAsB,IAE3E,QAAA,kBAAA,WAAA,EAAjB,mBAA0G,QAA1G,YAA0G,gBAAxB,QAAA,eAAc,EAAA,EAAA,IAC/E,QAAA,kBAAA,WAAA,EAAjB,mBAA0G,QAA1G,YAA0G,gBAAxB,QAAA,eAAc,EAAA,EAAA,KAAA,WAAA,EAChG,mBAA+E,QAA/E,YAA2D,gBAAa;+BAExE,mBAEM,OAAA;MAFD,OAAM;MAA2C,eAAY;MAAO,SAAQ;MAAY,MAAK;MAAO,QAAO;MAAe,gBAAa;MAAI,kBAAe;MAAQ,mBAAgB;SACrL,mBAAyB,QAAA,EAAnB,GAAE,gBAAc,CAAA,CAAA,EAAA,GAAA;wBAMlB,QAAA,YAAY,QAAA,kBAAA,WAAA,EADpB,mBAwBS,UAAA;;KAtBP,MAAK;KACJ,OAAK,eAAA;;0DAAqH,QAAA,aAAW;0DAAkE,YAAA,OAAW;2DAAmE,QAAA,gBAAY,CAAK,YAAA,OAAW;;KAMjT,UAAU,QAAA,gBAAY,CAAK,YAAA;KAC3B,OAAO,QAAA,gBAAgB,QAAA,sBAAsB,QAAA,sBAAsB,YAAA,SAAe,QAAA,qBAAqB,QAAA,qBAAkB;KAC1H,cAAW;KACV,SAAK,cAAO,YAAU,CAAA,OAAA,CAAA;QAGX,QAAA,eAAA,WAAA,EAAZ,mBAA4E,QAA5E,WAA4E,IAE5D,YAAA,SAAA,WAAA,EAAhB,mBAEM,OAFN,YAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAA6F,QAAA;KAAvF,kBAAe;KAAQ,mBAAgB;KAAQ,gBAAa;KAAM,GAAE;sCAG5E,mBAEM,OAFN,aAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAAwK,QAAA;KAAlK,kBAAe;KAAQ,mBAAgB;KAAQ,gBAAa;KAAI,GAAE;;IAMnE,MAAA,OAAM,IAAA,WAAA,EAAjB,mBA4GM,OA5GN,aA4GM,CA1GJ,mBAKM,OALN,aAKM,CAAA,OAAA,OAAA,OAAA,KAJJ,mBAA4D,OAAA,EAAvD,OAAM,kCAAgC,EAAC,cAAU,GAAA,GACtD,mBAEM,OAFN,aAEM,gBADD,cAAA,MAAa,EAAA,EAAA,CAAA,CAAA,EAMT,QAAA,eAAA,WAAA,EAAX,mBA0BM,OA1BN,aA0BM,CAAA,OAAA,OAAA,OAAA,KAAA,kBAAA,s6BAAA,EAAA,GARJ,mBAOM,OAPN,aAOM,CANJ,mBAES,UAAA;KAFD,MAAK;KAAS,OAAM;KAAuC,SAAO;OAAc,WAExF,EACc,QAAA,cAAA,WAAA,EAAd,mBAES,UAAA;;KAFiB,MAAK;KAAS,OAAM;KAAuC,SAAO;OAAc,WAE1G,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA,IAMY,QAAA,SAAA,WAAA,EAAhB,mBAyBM,OAzBN,aAyBM,CAxBJ,mBAeM,OAfN,aAeM,CAAA,OAAA,OAAA,OAAA,KAdJ,mBASM,OAAA,EATD,OAAM,gFAA8E,EAAA,CACvF,mBAOM,OAAA;KAPD,MAAK;KAAO,QAAO;KAAe,SAAQ;QAC7C,mBAKE,QAAA;KAJA,kBAAe;KACf,mBAAgB;KAChB,gBAAa;KACb,GAAE;iBAIR,mBAGM,OAHN,aAGM,CAAA,OAAA,OAAA,OAAA,KAFJ,mBAA4E,OAAA,EAAvE,OAAM,sCAAoC,EAAC,0BAAsB,GAAA,GACtE,mBAAoE,OAApE,aAAoE,gBAAd,QAAA,MAAK,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,EAG/D,mBAOM,OAPN,aAOM,CANJ,mBAES,UAAA;KAFD,MAAK;KAAS,OAAM;KAAuC,SAAO;OAAc,WAExF,EACc,QAAA,cAAA,WAAA,EAAd,mBAES,UAAA;;KAFiB,MAAK;KAAS,OAAM;KAAuC,SAAO;OAAc,WAE1G,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA,IAAA,CAKa,QAAA,kBAAA,WAAA,EAAjB,mBAOM,OAPN,aAOM,CANJ,mBAKS,UAAA;KALD,MAAK;KAAS,OAAM;KAAuC,SAAO;sCACxE,mBAEM,OAAA;KAFD,OAAM;KAAK,QAAO;KAAK,MAAK;KAAO,QAAO;KAAe,SAAQ;QACpE,mBAA2F,QAAA;KAArF,kBAAe;KAAQ,mBAAgB;KAAQ,gBAAa;KAAI,GAAE;8BACpE,uBAER,GAAA,CAAA,EAAA,CAAA,CAAA,CAAA,KAAA,WAAA,EAIF,mBA4BM,OA5BN,aA4BM,CA3BJ,mBAkBM,OAlBN,aAkBM,CAAA,OAAA,QAAA,OAAA,MAjBJ,mBASM,OAAA,EATD,OAAM,sCAAoC,EAAA,CAC7C,mBAOM,OAAA;KAPD,MAAK;KAAO,QAAO;KAAe,SAAQ;QAC7C,mBAKE,QAAA;KAJA,kBAAe;KACf,mBAAgB;KAChB,gBAAa;KACb,GAAE;iBAIR,mBAMM,OANN,aAMM;KALQ,QAAA,kBAAA,WAAA,EAAZ,mBAAkG,QAAlG,aAAkG,gBAAxB,QAAA,eAAc,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KACxF,mBAA0E,OAA1E,aAA0E,gBAAvB,QAAA,eAAc,EAAA,EAAA;KACtD,QAAA,oBAAA,WAAA,EAAX,mBAEM,OAFN,aAEM,gBADD,MAAA,uBAAsB,CAAC,QAAA,iBAAgB,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;UAIhD,mBAOM,OAPN,aAOM,CANJ,mBAES,UAAA;KAFD,MAAK;KAAS,OAAM;KAAuC,SAAO;OAAc,WAExF,EACc,QAAA,cAAA,WAAA,EAAd,mBAES,UAAA;;KAFiB,MAAK;KAAS,OAAM;KAAuC,SAAO;OAAc,WAE1G,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAMN,YAQE,uBAAA;iBAPS,YAAA;8EAAW,QAAA;KACnB,OAAO,QAAA,gBAAY;KACnB,SAAS,QAAA,kBAAc,wBAA4B,QAAA,eAAc;KAClE,SAAQ;KACR,iBAAc;KACb,SAAS,QAAA;KACT,WAAS"}
@@ -1,8 +1,8 @@
1
1
  import { n as __exportAll } from "./chunk-BoAXSpZd.js";
2
- import { a as SORT_OPTIONS, c as formatExperimentDate, f as BaseModal_default, l as formatExperimentStatus, r as EXPERIMENT_STATUS_OPTIONS, t as DATE_PRESET_OPTIONS, u as getExperimentStatusVariant } from "./experiment-utils-DV3DrvE_.js";
3
- import { a as BaseSelect_default, i as BasePill_default, n as Skeleton_default, s as BaseInput_default, t as EmptyState_default } from "./EmptyState-NBqf2_8z.js";
4
- import { t as useExperimentStore } from "./experiment-Y9Iz7KYi.js";
5
- import { t as useExperimentSelector } from "./useExperimentSelector-DSEckf_g.js";
2
+ import { a as SORT_OPTIONS, c as formatExperimentDate, f as BaseModal_default, l as formatExperimentStatus, r as EXPERIMENT_STATUS_OPTIONS, t as DATE_PRESET_OPTIONS, u as getExperimentStatusVariant } from "./experiment-utils-CV2Wza3z.js";
3
+ import { a as BaseSelect_default, i as BasePill_default, r as EmptyState_default, s as BaseInput_default, t as Skeleton_default } from "./Skeleton-BAF3CKY7.js";
4
+ import { t as useExperimentStore } from "./experiment-CVDRAnqp.js";
5
+ import { t as useExperimentSelector } from "./useExperimentSelector-CEcIaueJ.js";
6
6
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, nextTick, normalizeClass, openBlock, reactive, ref, renderList, toDisplayString, unref, vModelCheckbox, watch, withCtx, withDirectives } from "vue";
7
7
  //#region src/components/ExperimentCodeBadge.vue?vue&type=script&setup=true&lang.ts
8
8
  var _hoisted_1$1 = [
@@ -504,4 +504,4 @@ var ExperimentSelectorModal_default = ExperimentSelectorModal_vue_vue_type_scrip
504
504
  //#endregion
505
505
  export { ExperimentSelectorModal_exports as n, ExperimentCodeBadge_default as r, ExperimentSelectorModal_default as t };
506
506
 
507
- //# sourceMappingURL=ExperimentSelectorModal-DvIagrxY.js.map
507
+ //# sourceMappingURL=ExperimentSelectorModal-DCXmwKNS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExperimentSelectorModal-DCXmwKNS.js","names":[],"sources":["../src/components/ExperimentCodeBadge.vue","../src/components/ExperimentCodeBadge.vue","../src/components/ExperimentSelectorModal.vue","../src/components/ExperimentSelectorModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/** Inline badge that displays an experiment code and copies it to the clipboard on click. */\nimport { ref } from 'vue'\n\ninterface Props {\n code: string\n size?: 'sm' | 'md' | 'lg'\n copyable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n copyable: true,\n})\n\nconst emit = defineEmits<{\n copy: [code: string]\n}>()\n\nconst copied = ref(false)\nlet copyTimeout: ReturnType<typeof setTimeout> | null = null\n\nasync function handleCopy() {\n if (!props.copyable) return\n try {\n await navigator.clipboard.writeText(props.code)\n copied.value = true\n emit('copy', props.code)\n if (copyTimeout) clearTimeout(copyTimeout)\n copyTimeout = setTimeout(() => { copied.value = false }, 1500)\n } catch {\n // Clipboard API not available (e.g. insecure context)\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!props.copyable) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleCopy()\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mint-exp-code',\n `mint-exp-code--${size}`,\n { 'mint-exp-code--copyable': copyable, 'mint-exp-code--copied': copied },\n ]\"\n :role=\"copyable ? 'button' : undefined\"\n :tabindex=\"copyable ? 0 : undefined\"\n :title=\"copyable ? (copied ? 'Copied!' : 'Click to copy') : undefined\"\n @click=\"handleCopy\"\n @keydown=\"handleKeydown\"\n >\n {{ copied ? 'Copied!' : code }}\n </span>\n</template>\n","<script setup lang=\"ts\">\n/** Inline badge that displays an experiment code and copies it to the clipboard on click. */\nimport { ref } from 'vue'\n\ninterface Props {\n code: string\n size?: 'sm' | 'md' | 'lg'\n copyable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n copyable: true,\n})\n\nconst emit = defineEmits<{\n copy: [code: string]\n}>()\n\nconst copied = ref(false)\nlet copyTimeout: ReturnType<typeof setTimeout> | null = null\n\nasync function handleCopy() {\n if (!props.copyable) return\n try {\n await navigator.clipboard.writeText(props.code)\n copied.value = true\n emit('copy', props.code)\n if (copyTimeout) clearTimeout(copyTimeout)\n copyTimeout = setTimeout(() => { copied.value = false }, 1500)\n } catch {\n // Clipboard API not available (e.g. insecure context)\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!props.copyable) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleCopy()\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mint-exp-code',\n `mint-exp-code--${size}`,\n { 'mint-exp-code--copyable': copyable, 'mint-exp-code--copied': copied },\n ]\"\n :role=\"copyable ? 'button' : undefined\"\n :tabindex=\"copyable ? 0 : undefined\"\n :title=\"copyable ? (copied ? 'Copied!' : 'Click to copy') : undefined\"\n @click=\"handleCopy\"\n @keydown=\"handleKeydown\"\n >\n {{ copied ? 'Copied!' : code }}\n </span>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Modal for searching and selecting an experiment from the platform list with filters and keyboard nav.\n *\n * @example Recommended\n * ```vue\n * <ExperimentSelectorModal v-model=\"open\" :allowed-experiment-types=\"['mass-spectrometry']\" />\n * ```\n */\nimport { ref, reactive, computed, watch, nextTick } from 'vue'\nimport type { ModalSize, ExperimentSummary, ExperimentFilters } from '../types'\nimport { useExperimentSelector } from '../composables/useExperimentSelector'\nimport { useExperimentStore } from '../stores/experiment'\nimport {\n formatExperimentDate,\n formatExperimentStatus,\n getExperimentStatusVariant,\n EXPERIMENT_STATUS_OPTIONS,\n DATE_PRESET_OPTIONS,\n SORT_OPTIONS,\n} from '../composables/experiment-utils'\nimport BaseModal from './BaseModal.vue'\nimport BaseInput from './BaseInput.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport BasePill from './BasePill.vue'\nimport Skeleton from './Skeleton.vue'\nimport EmptyState from './EmptyState.vue'\nimport ExperimentCodeBadge from './ExperimentCodeBadge.vue'\n\ninterface Props {\n modelValue: boolean\n experimentType?: string\n allowedExperimentTypes?: string[] | null\n currentExperimentId?: number | null\n title?: string\n size?: ModalSize\n groupByProject?: boolean\n showFilters?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n currentExperimentId: null,\n title: 'Select Experiment',\n size: 'full',\n groupByProject: false,\n showFilters: false,\n})\n\n// Selection is committed to the shared experiment store rather than emitted.\n// A single owner of \"the current experiment\" is what stops callers holding —\n// or inventing — a record of their own.\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n}>()\n\nconst experimentStore = useExperimentStore()\n\nconst {\n experiments,\n filters,\n isLoading,\n error,\n sortKey,\n experimentTypes,\n projects,\n groupedByProject,\n fetch: fetchExperiments,\n fetchFilterOptions,\n} = useExperimentSelector({\n experimentType: props.experimentType,\n allowedExperimentTypes: props.allowedExperimentTypes,\n})\n\nconst activeIndex = ref(-1)\nconst listRef = ref<HTMLElement | null>(null)\nconst showAdvanced = ref(props.showFilters)\nconst groupToggle = ref(props.groupByProject)\n\n// Track whether any advanced filter is active (for badge dot)\nconst hasActiveAdvancedFilters = computed(() =>\n !!(filters.project || filters.experimentType || filters.datePreset || sortKey.value !== 'created_at:desc'),\n)\n\n// Build type filter options from fetched experiment types\nconst typeFilterOptions = computed(() => [\n { value: '', label: 'All types' },\n ...experimentTypes.value.map(t => ({ value: t.value, label: t.label })),\n])\nconst canChooseExperimentType = computed(() =>\n !props.experimentType && typeFilterOptions.value.length > 2,\n)\n\n// Build project filter options from fetched projects\nconst projectFilterOptions = computed(() => [\n { value: '', label: 'All projects' },\n ...projects.value,\n])\n\n// Flat list of experiments for keyboard navigation (works across groups too)\nconst flatExperiments = computed(() => {\n if (!groupToggle.value) return experiments.value\n return groupedByProject.value.flatMap(([, exps]) => exps)\n})\n\n// Stable ids so the search field (role=\"combobox\") can expose the currently\n// arrow-highlighted row to assistive tech via aria-activedescendant. This\n// surfaces the existing keyboard model to screen readers without changing it.\nconst LISTBOX_ID = 'mint-experiment-selector-listbox'\nfunction optionId(experiment: ExperimentSummary): string {\n return `mint-experiment-option-${experiment.id}`\n}\nconst activeDescendantId = computed(() => {\n const exp = flatExperiments.value[activeIndex.value]\n return activeIndex.value >= 0 && exp ? optionId(exp) : undefined\n})\nconst hasResults = computed(() => !isLoading.value && !error.value && experiments.value.length > 0)\n\nfunction setFilter<K extends keyof ExperimentFilters>(key: K, value: string | number) {\n ;(filters as Record<string, unknown>)[key] = String(value) || undefined\n}\n\nfunction handleSortChange(value: string | number) {\n sortKey.value = String(value) || 'created_at:desc'\n}\n\nfunction handleSelect(experiment: ExperimentSummary) {\n experimentStore.select(experiment)\n emit('update:modelValue', false)\n}\n\nfunction handleDeselect() {\n experimentStore.clear()\n emit('update:modelValue', false)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n const list = flatExperiments.value\n if (!list.length) return\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n activeIndex.value = Math.min(activeIndex.value + 1, list.length - 1)\n scrollActiveIntoView()\n break\n case 'ArrowUp':\n event.preventDefault()\n activeIndex.value = Math.max(activeIndex.value - 1, 0)\n scrollActiveIntoView()\n break\n case 'Enter':\n event.preventDefault()\n if (activeIndex.value >= 0 && activeIndex.value < list.length) {\n handleSelect(list[activeIndex.value])\n }\n break\n }\n}\n\nfunction scrollActiveIntoView() {\n nextTick(() => {\n const row = listRef.value?.querySelector('.mint-experiment-selector__row--focused')\n row?.scrollIntoView({ block: 'nearest' })\n })\n}\n\n// Precomputed id → flat index for O(1) lookup in grouped mode\nconst flatIndexMap = computed(() => {\n const map = new Map<number, number>()\n flatExperiments.value.forEach((exp, i) => map.set(exp.id, i))\n return map\n})\n\nfunction getFlatIndex(experiment: ExperimentSummary): number {\n return flatIndexMap.value.get(experiment.id) ?? -1\n}\n\n// Track collapsed groups (reactive Set tracks .add/.delete/.has automatically)\nconst collapsedGroups = reactive(new Set<string>())\n\nfunction toggleGroup(groupName: string) {\n if (collapsedGroups.has(groupName)) {\n collapsedGroups.delete(groupName)\n } else {\n collapsedGroups.add(groupName)\n }\n}\n\n// Reset active index when experiments change\nwatch(experiments, () => { activeIndex.value = -1 })\n\n// Fetch on open\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (isOpen) {\n activeIndex.value = -1\n collapsedGroups.clear()\n fetchFilterOptions()\n fetchExperiments()\n }\n },\n { immediate: true },\n)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :size=\"size\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mint-experiment-selector\" @keydown=\"handleKeydown\">\n <!-- Filter bar row 1 -->\n <div class=\"mint-experiment-selector__filters-row\">\n <div class=\"mint-experiment-selector__search\">\n <BaseInput\n v-model=\"filters.search\"\n placeholder=\"Search experiments...\"\n size=\"sm\"\n type=\"search\"\n role=\"combobox\"\n aria-label=\"Search experiments\"\n :aria-expanded=\"hasResults\"\n :aria-controls=\"LISTBOX_ID\"\n :aria-activedescendant=\"activeDescendantId\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.status ?? ''\"\n :options=\"EXPERIMENT_STATUS_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('status', v)\"\n />\n </div>\n <div v-if=\"canChooseExperimentType\" class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.experimentType ?? ''\"\n :options=\"typeFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('experimentType', v)\"\n />\n </div>\n <button\n class=\"mint-experiment-selector__filters-toggle\"\n :class=\"{ 'mint-experiment-selector__filters-toggle--active': hasActiveAdvancedFilters }\"\n type=\"button\"\n :aria-expanded=\"showAdvanced\"\n @click=\"showAdvanced = !showAdvanced\"\n >\n <svg aria-hidden=\"true\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\" /><line x1=\"8\" y1=\"12\" x2=\"20\" y2=\"12\" /><line x1=\"12\" y1=\"18\" x2=\"20\" y2=\"18\" />\n <circle cx=\"6\" cy=\"12\" r=\"2\" /><circle cx=\"10\" cy=\"18\" r=\"2\" /><circle cx=\"6\" cy=\"6\" r=\"2\" />\n </svg>\n Filters\n <span v-if=\"hasActiveAdvancedFilters\" class=\"mint-experiment-selector__filters-dot\" />\n </button>\n </div>\n\n <!-- Filter bar row 2 (advanced, collapsible) -->\n <div v-if=\"showAdvanced\" class=\"mint-experiment-selector__filters-advanced\">\n <div v-if=\"projectFilterOptions.length > 1\" class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.project ?? ''\"\n :options=\"projectFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('project', v)\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.datePreset ?? ''\"\n :options=\"DATE_PRESET_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('datePreset', v)\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"sortKey\"\n :options=\"SORT_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"handleSortChange\"\n />\n </div>\n <label class=\"mint-experiment-selector__group-toggle\">\n <input\n v-model=\"groupToggle\"\n type=\"checkbox\"\n class=\"mint-experiment-selector__group-checkbox\"\n />\n Group by project\n </label>\n </div>\n\n <!-- Loading skeleton -->\n <div v-if=\"isLoading\" class=\"mint-experiment-selector__skeleton\">\n <div v-for=\"n in 4\" :key=\"n\" class=\"mint-experiment-selector__skeleton-row\">\n <div class=\"mint-experiment-selector__skeleton-content\">\n <Skeleton :width=\"120 + n * 20\" height=\"14px\" />\n <Skeleton width=\"80px\" height=\"10px\" />\n </div>\n <Skeleton width=\"60px\" height=\"20px\" variant=\"rounded\" />\n </div>\n </div>\n\n <!-- Error -->\n <div v-else-if=\"error\" class=\"mint-experiment-selector__error\">\n {{ error }}\n </div>\n\n <!-- Empty -->\n <EmptyState\n v-else-if=\"experiments.length === 0\"\n title=\"No experiments found\"\n description=\"Try adjusting your search or filters.\"\n size=\"sm\"\n />\n\n <!-- Experiment list: grouped mode -->\n <div v-else-if=\"groupToggle\" :id=\"LISTBOX_ID\" ref=\"listRef\" class=\"mint-experiment-selector__list\" role=\"listbox\" aria-label=\"Experiments\">\n <template v-for=\"([groupName, groupExps]) in groupedByProject\" :key=\"groupName\">\n <button\n type=\"button\"\n class=\"mint-experiment-selector__group-header\"\n :aria-expanded=\"!collapsedGroups.has(groupName)\"\n @click=\"toggleGroup(groupName)\"\n >\n <svg\n class=\"mint-experiment-selector__group-chevron\"\n :class=\"{ 'mint-experiment-selector__group-chevron--collapsed': collapsedGroups.has(groupName) }\"\n aria-hidden=\"true\"\n width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n <span class=\"mint-experiment-selector__group-name\">{{ groupName }}</span>\n <span class=\"mint-experiment-selector__group-count\">{{ groupExps.length }}</span>\n </button>\n <template v-if=\"!collapsedGroups.has(groupName)\">\n <div\n v-for=\"exp in groupExps\"\n :id=\"optionId(exp)\"\n :key=\"exp.id\"\n class=\"mint-experiment-selector__row\"\n role=\"option\"\n :aria-selected=\"exp.id === currentExperimentId\"\n :class=\"{\n 'mint-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mint-experiment-selector__row--focused': getFlatIndex(exp) === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = getFlatIndex(exp)\"\n >\n <div class=\"mint-experiment-selector__row-content\">\n <div class=\"mint-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mint-experiment-selector__meta\">\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"getExperimentStatusVariant(exp.status)\" size=\"sm\">\n {{ formatExperimentStatus(exp.status) }}\n </BasePill>\n </div>\n </template>\n </template>\n </div>\n\n <!-- Experiment list: flat mode -->\n <div v-else :id=\"LISTBOX_ID\" ref=\"listRef\" class=\"mint-experiment-selector__list\" role=\"listbox\" aria-label=\"Experiments\">\n <div\n v-for=\"(exp, idx) in experiments\"\n :id=\"optionId(exp)\"\n :key=\"exp.id\"\n class=\"mint-experiment-selector__row\"\n role=\"option\"\n :aria-selected=\"exp.id === currentExperimentId\"\n :class=\"{\n 'mint-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mint-experiment-selector__row--focused': idx === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <div class=\"mint-experiment-selector__row-content\">\n <div class=\"mint-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mint-experiment-selector__meta\">\n <span v-if=\"exp.project_name || exp.project\">{{ exp.project_name || exp.project }}</span>\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"getExperimentStatusVariant(exp.status)\" size=\"sm\">\n {{ formatExperimentStatus(exp.status) }}\n </BasePill>\n </div>\n </div>\n\n <!-- Footer: clear selection -->\n <div v-if=\"currentExperimentId != null\" class=\"mint-experiment-selector__footer\">\n <button\n type=\"button\"\n class=\"mint-experiment-selector__clear-btn\"\n @click=\"handleDeselect\"\n >\n Clear selection\n </button>\n </div>\n </div>\n </BaseModal>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Modal for searching and selecting an experiment from the platform list with filters and keyboard nav.\n *\n * @example Recommended\n * ```vue\n * <ExperimentSelectorModal v-model=\"open\" :allowed-experiment-types=\"['mass-spectrometry']\" />\n * ```\n */\nimport { ref, reactive, computed, watch, nextTick } from 'vue'\nimport type { ModalSize, ExperimentSummary, ExperimentFilters } from '../types'\nimport { useExperimentSelector } from '../composables/useExperimentSelector'\nimport { useExperimentStore } from '../stores/experiment'\nimport {\n formatExperimentDate,\n formatExperimentStatus,\n getExperimentStatusVariant,\n EXPERIMENT_STATUS_OPTIONS,\n DATE_PRESET_OPTIONS,\n SORT_OPTIONS,\n} from '../composables/experiment-utils'\nimport BaseModal from './BaseModal.vue'\nimport BaseInput from './BaseInput.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport BasePill from './BasePill.vue'\nimport Skeleton from './Skeleton.vue'\nimport EmptyState from './EmptyState.vue'\nimport ExperimentCodeBadge from './ExperimentCodeBadge.vue'\n\ninterface Props {\n modelValue: boolean\n experimentType?: string\n allowedExperimentTypes?: string[] | null\n currentExperimentId?: number | null\n title?: string\n size?: ModalSize\n groupByProject?: boolean\n showFilters?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n currentExperimentId: null,\n title: 'Select Experiment',\n size: 'full',\n groupByProject: false,\n showFilters: false,\n})\n\n// Selection is committed to the shared experiment store rather than emitted.\n// A single owner of \"the current experiment\" is what stops callers holding —\n// or inventing — a record of their own.\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n}>()\n\nconst experimentStore = useExperimentStore()\n\nconst {\n experiments,\n filters,\n isLoading,\n error,\n sortKey,\n experimentTypes,\n projects,\n groupedByProject,\n fetch: fetchExperiments,\n fetchFilterOptions,\n} = useExperimentSelector({\n experimentType: props.experimentType,\n allowedExperimentTypes: props.allowedExperimentTypes,\n})\n\nconst activeIndex = ref(-1)\nconst listRef = ref<HTMLElement | null>(null)\nconst showAdvanced = ref(props.showFilters)\nconst groupToggle = ref(props.groupByProject)\n\n// Track whether any advanced filter is active (for badge dot)\nconst hasActiveAdvancedFilters = computed(() =>\n !!(filters.project || filters.experimentType || filters.datePreset || sortKey.value !== 'created_at:desc'),\n)\n\n// Build type filter options from fetched experiment types\nconst typeFilterOptions = computed(() => [\n { value: '', label: 'All types' },\n ...experimentTypes.value.map(t => ({ value: t.value, label: t.label })),\n])\nconst canChooseExperimentType = computed(() =>\n !props.experimentType && typeFilterOptions.value.length > 2,\n)\n\n// Build project filter options from fetched projects\nconst projectFilterOptions = computed(() => [\n { value: '', label: 'All projects' },\n ...projects.value,\n])\n\n// Flat list of experiments for keyboard navigation (works across groups too)\nconst flatExperiments = computed(() => {\n if (!groupToggle.value) return experiments.value\n return groupedByProject.value.flatMap(([, exps]) => exps)\n})\n\n// Stable ids so the search field (role=\"combobox\") can expose the currently\n// arrow-highlighted row to assistive tech via aria-activedescendant. This\n// surfaces the existing keyboard model to screen readers without changing it.\nconst LISTBOX_ID = 'mint-experiment-selector-listbox'\nfunction optionId(experiment: ExperimentSummary): string {\n return `mint-experiment-option-${experiment.id}`\n}\nconst activeDescendantId = computed(() => {\n const exp = flatExperiments.value[activeIndex.value]\n return activeIndex.value >= 0 && exp ? optionId(exp) : undefined\n})\nconst hasResults = computed(() => !isLoading.value && !error.value && experiments.value.length > 0)\n\nfunction setFilter<K extends keyof ExperimentFilters>(key: K, value: string | number) {\n ;(filters as Record<string, unknown>)[key] = String(value) || undefined\n}\n\nfunction handleSortChange(value: string | number) {\n sortKey.value = String(value) || 'created_at:desc'\n}\n\nfunction handleSelect(experiment: ExperimentSummary) {\n experimentStore.select(experiment)\n emit('update:modelValue', false)\n}\n\nfunction handleDeselect() {\n experimentStore.clear()\n emit('update:modelValue', false)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n const list = flatExperiments.value\n if (!list.length) return\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n activeIndex.value = Math.min(activeIndex.value + 1, list.length - 1)\n scrollActiveIntoView()\n break\n case 'ArrowUp':\n event.preventDefault()\n activeIndex.value = Math.max(activeIndex.value - 1, 0)\n scrollActiveIntoView()\n break\n case 'Enter':\n event.preventDefault()\n if (activeIndex.value >= 0 && activeIndex.value < list.length) {\n handleSelect(list[activeIndex.value])\n }\n break\n }\n}\n\nfunction scrollActiveIntoView() {\n nextTick(() => {\n const row = listRef.value?.querySelector('.mint-experiment-selector__row--focused')\n row?.scrollIntoView({ block: 'nearest' })\n })\n}\n\n// Precomputed id → flat index for O(1) lookup in grouped mode\nconst flatIndexMap = computed(() => {\n const map = new Map<number, number>()\n flatExperiments.value.forEach((exp, i) => map.set(exp.id, i))\n return map\n})\n\nfunction getFlatIndex(experiment: ExperimentSummary): number {\n return flatIndexMap.value.get(experiment.id) ?? -1\n}\n\n// Track collapsed groups (reactive Set tracks .add/.delete/.has automatically)\nconst collapsedGroups = reactive(new Set<string>())\n\nfunction toggleGroup(groupName: string) {\n if (collapsedGroups.has(groupName)) {\n collapsedGroups.delete(groupName)\n } else {\n collapsedGroups.add(groupName)\n }\n}\n\n// Reset active index when experiments change\nwatch(experiments, () => { activeIndex.value = -1 })\n\n// Fetch on open\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (isOpen) {\n activeIndex.value = -1\n collapsedGroups.clear()\n fetchFilterOptions()\n fetchExperiments()\n }\n },\n { immediate: true },\n)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :size=\"size\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mint-experiment-selector\" @keydown=\"handleKeydown\">\n <!-- Filter bar row 1 -->\n <div class=\"mint-experiment-selector__filters-row\">\n <div class=\"mint-experiment-selector__search\">\n <BaseInput\n v-model=\"filters.search\"\n placeholder=\"Search experiments...\"\n size=\"sm\"\n type=\"search\"\n role=\"combobox\"\n aria-label=\"Search experiments\"\n :aria-expanded=\"hasResults\"\n :aria-controls=\"LISTBOX_ID\"\n :aria-activedescendant=\"activeDescendantId\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.status ?? ''\"\n :options=\"EXPERIMENT_STATUS_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('status', v)\"\n />\n </div>\n <div v-if=\"canChooseExperimentType\" class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.experimentType ?? ''\"\n :options=\"typeFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('experimentType', v)\"\n />\n </div>\n <button\n class=\"mint-experiment-selector__filters-toggle\"\n :class=\"{ 'mint-experiment-selector__filters-toggle--active': hasActiveAdvancedFilters }\"\n type=\"button\"\n :aria-expanded=\"showAdvanced\"\n @click=\"showAdvanced = !showAdvanced\"\n >\n <svg aria-hidden=\"true\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\" /><line x1=\"8\" y1=\"12\" x2=\"20\" y2=\"12\" /><line x1=\"12\" y1=\"18\" x2=\"20\" y2=\"18\" />\n <circle cx=\"6\" cy=\"12\" r=\"2\" /><circle cx=\"10\" cy=\"18\" r=\"2\" /><circle cx=\"6\" cy=\"6\" r=\"2\" />\n </svg>\n Filters\n <span v-if=\"hasActiveAdvancedFilters\" class=\"mint-experiment-selector__filters-dot\" />\n </button>\n </div>\n\n <!-- Filter bar row 2 (advanced, collapsible) -->\n <div v-if=\"showAdvanced\" class=\"mint-experiment-selector__filters-advanced\">\n <div v-if=\"projectFilterOptions.length > 1\" class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.project ?? ''\"\n :options=\"projectFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('project', v)\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.datePreset ?? ''\"\n :options=\"DATE_PRESET_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('datePreset', v)\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"sortKey\"\n :options=\"SORT_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"handleSortChange\"\n />\n </div>\n <label class=\"mint-experiment-selector__group-toggle\">\n <input\n v-model=\"groupToggle\"\n type=\"checkbox\"\n class=\"mint-experiment-selector__group-checkbox\"\n />\n Group by project\n </label>\n </div>\n\n <!-- Loading skeleton -->\n <div v-if=\"isLoading\" class=\"mint-experiment-selector__skeleton\">\n <div v-for=\"n in 4\" :key=\"n\" class=\"mint-experiment-selector__skeleton-row\">\n <div class=\"mint-experiment-selector__skeleton-content\">\n <Skeleton :width=\"120 + n * 20\" height=\"14px\" />\n <Skeleton width=\"80px\" height=\"10px\" />\n </div>\n <Skeleton width=\"60px\" height=\"20px\" variant=\"rounded\" />\n </div>\n </div>\n\n <!-- Error -->\n <div v-else-if=\"error\" class=\"mint-experiment-selector__error\">\n {{ error }}\n </div>\n\n <!-- Empty -->\n <EmptyState\n v-else-if=\"experiments.length === 0\"\n title=\"No experiments found\"\n description=\"Try adjusting your search or filters.\"\n size=\"sm\"\n />\n\n <!-- Experiment list: grouped mode -->\n <div v-else-if=\"groupToggle\" :id=\"LISTBOX_ID\" ref=\"listRef\" class=\"mint-experiment-selector__list\" role=\"listbox\" aria-label=\"Experiments\">\n <template v-for=\"([groupName, groupExps]) in groupedByProject\" :key=\"groupName\">\n <button\n type=\"button\"\n class=\"mint-experiment-selector__group-header\"\n :aria-expanded=\"!collapsedGroups.has(groupName)\"\n @click=\"toggleGroup(groupName)\"\n >\n <svg\n class=\"mint-experiment-selector__group-chevron\"\n :class=\"{ 'mint-experiment-selector__group-chevron--collapsed': collapsedGroups.has(groupName) }\"\n aria-hidden=\"true\"\n width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n <span class=\"mint-experiment-selector__group-name\">{{ groupName }}</span>\n <span class=\"mint-experiment-selector__group-count\">{{ groupExps.length }}</span>\n </button>\n <template v-if=\"!collapsedGroups.has(groupName)\">\n <div\n v-for=\"exp in groupExps\"\n :id=\"optionId(exp)\"\n :key=\"exp.id\"\n class=\"mint-experiment-selector__row\"\n role=\"option\"\n :aria-selected=\"exp.id === currentExperimentId\"\n :class=\"{\n 'mint-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mint-experiment-selector__row--focused': getFlatIndex(exp) === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = getFlatIndex(exp)\"\n >\n <div class=\"mint-experiment-selector__row-content\">\n <div class=\"mint-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mint-experiment-selector__meta\">\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"getExperimentStatusVariant(exp.status)\" size=\"sm\">\n {{ formatExperimentStatus(exp.status) }}\n </BasePill>\n </div>\n </template>\n </template>\n </div>\n\n <!-- Experiment list: flat mode -->\n <div v-else :id=\"LISTBOX_ID\" ref=\"listRef\" class=\"mint-experiment-selector__list\" role=\"listbox\" aria-label=\"Experiments\">\n <div\n v-for=\"(exp, idx) in experiments\"\n :id=\"optionId(exp)\"\n :key=\"exp.id\"\n class=\"mint-experiment-selector__row\"\n role=\"option\"\n :aria-selected=\"exp.id === currentExperimentId\"\n :class=\"{\n 'mint-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mint-experiment-selector__row--focused': idx === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <div class=\"mint-experiment-selector__row-content\">\n <div class=\"mint-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mint-experiment-selector__meta\">\n <span v-if=\"exp.project_name || exp.project\">{{ exp.project_name || exp.project }}</span>\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"getExperimentStatusVariant(exp.status)\" size=\"sm\">\n {{ formatExperimentStatus(exp.status) }}\n </BasePill>\n </div>\n </div>\n\n <!-- Footer: clear selection -->\n <div v-if=\"currentExperimentId != null\" class=\"mint-experiment-selector__footer\">\n <button\n type=\"button\"\n class=\"mint-experiment-selector__clear-btn\"\n @click=\"handleDeselect\"\n >\n Clear selection\n </button>\n </div>\n </div>\n </BaseModal>\n</template>\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { n as __exportAll } from "./chunk-BoAXSpZd.js";
2
- import { f as BaseModal_default } from "./experiment-utils-DV3DrvE_.js";
3
- import { E as getFieldRegistryEntry, b as controlsToSettingsSchema, f as resolveControlModel, g as mergeControlWorkspaceOptions, rt as isRecord } from "./useControlSchema-_OVhavKq.js";
4
- import { a as colorPalettes, i as useAuthStore, o as useSettingsStore } from "./useApi-CDBq6z23.js";
5
- import { i as usePlatformContext, t as useFormBuilder } from "./useFormBuilder-Bd714C7y.js";
2
+ import { a as useAuthStore, d as useSettingsStore, s as colorPalettes } from "./errors-BLuqMee5.js";
3
+ import { f as BaseModal_default } from "./experiment-utils-CV2Wza3z.js";
4
+ import { E as getFieldRegistryEntry, b as controlsToSettingsSchema, f as resolveControlModel, g as mergeControlWorkspaceOptions, rt as isRecord } from "./useControlSchema-DldpxNOP.js";
5
+ import { i as usePlatformContext, t as useFormBuilder } from "./useFormBuilder-z5L_dUIy.js";
6
6
  import { canAccessByPolicy } from "./permissions.js";
7
7
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, mergeProps, normalizeClass, normalizeProps, normalizeStyle, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, vShow, watch, withCtx, withDirectives } from "vue";
8
8
  //#region src/utils/items.ts
@@ -466,4 +466,4 @@ var SettingsModal_default = SettingsModal_vue_vue_type_script_setup_true_lang_de
466
466
  //#endregion
467
467
  export { pickExistingRecordKeys as a, normalizeItemInput as c, formSchemaFieldNames as i, normalizeLabelItemInput as l, SettingsModal_exports as n, recordValuesEqualForKeys as o, FormFieldRendererInternal_default as r, FormField_default as s, SettingsModal_default as t };
468
468
 
469
- //# sourceMappingURL=SettingsModal-W3TYfnP9.js.map
469
+ //# sourceMappingURL=SettingsModal-BYR20I4c.js.map