@morscherlab/mint-sdk 1.0.0-alpha.2

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 (491) hide show
  1. package/README.md +326 -0
  2. package/dist/__stories__/experiment-helpers.d.ts +25 -0
  3. package/dist/__tests__/components/AppLayout.test.d.ts +1 -0
  4. package/dist/__tests__/components/AppSidebar.test.d.ts +1 -0
  5. package/dist/__tests__/components/AppTopBar.test.d.ts +1 -0
  6. package/dist/__tests__/components/BaseInput.test.d.ts +1 -0
  7. package/dist/__tests__/components/BasePill.test.d.ts +1 -0
  8. package/dist/__tests__/components/Calendar.test.d.ts +1 -0
  9. package/dist/__tests__/components/CollapsibleCard.test.d.ts +1 -0
  10. package/dist/__tests__/components/DataFrame.test.d.ts +1 -0
  11. package/dist/__tests__/components/DropdownButton.test.d.ts +1 -0
  12. package/dist/__tests__/composables/formBuilderRegistry.test.d.ts +1 -0
  13. package/dist/__tests__/composables/useAppExperiment.test.d.ts +1 -0
  14. package/dist/__tests__/composables/useAuth.test.d.ts +1 -0
  15. package/dist/__tests__/composables/useAutoGroup.test.d.ts +1 -0
  16. package/dist/__tests__/composables/useExperimentData.test.d.ts +13 -0
  17. package/dist/__tests__/composables/useExperimentSave.test.d.ts +1 -0
  18. package/dist/__tests__/composables/useForm.test.d.ts +1 -0
  19. package/dist/__tests__/composables/useFormBuilder.test.d.ts +1 -0
  20. package/dist/__tests__/composables/usePlatformContext.test.d.ts +1 -0
  21. package/dist/__tests__/composables/usePluginApi.test.d.ts +13 -0
  22. package/dist/__tests__/composables/usePluginConfig.test.d.ts +14 -0
  23. package/dist/__tests__/utils/color.test.d.ts +1 -0
  24. package/dist/auth-BYmxZdJl.js +297 -0
  25. package/dist/auth-BYmxZdJl.js.map +1 -0
  26. package/dist/components/AlertBox.vue.d.ts +34 -0
  27. package/dist/components/AppAvatarMenu.vue.d.ts +58 -0
  28. package/dist/components/AppContainer.vue.d.ts +28 -0
  29. package/dist/components/AppLayout.vue.d.ts +31 -0
  30. package/dist/components/AppPageSelector.vue.d.ts +43 -0
  31. package/dist/components/AppPillNav.vue.d.ts +11 -0
  32. package/dist/components/AppPluginSwitcher.vue.d.ts +38 -0
  33. package/dist/components/AppSidebar.vue.d.ts +47 -0
  34. package/dist/components/AppTopBar.vue.d.ts +111 -0
  35. package/dist/components/AuditTrail.vue.d.ts +38 -0
  36. package/dist/components/AutoGroupModal.vue.d.ts +124 -0
  37. package/dist/components/Avatar.vue.d.ts +14 -0
  38. package/dist/components/BaseButton.vue.d.ts +37 -0
  39. package/dist/components/BaseCheckbox.vue.d.ts +17 -0
  40. package/dist/components/BaseInput.vue.d.ts +34 -0
  41. package/dist/components/BaseModal.vue.d.ts +46 -0
  42. package/dist/components/BasePill.vue.d.ts +57 -0
  43. package/dist/components/BaseRadioGroup.vue.d.ts +21 -0
  44. package/dist/components/BaseSelect.vue.d.ts +20 -0
  45. package/dist/components/BaseSlider.vue.d.ts +22 -0
  46. package/dist/components/BaseTabs.vue.d.ts +14 -0
  47. package/dist/components/BaseTextarea.vue.d.ts +30 -0
  48. package/dist/components/BaseToggle.vue.d.ts +19 -0
  49. package/dist/components/BatchProgressList.vue.d.ts +43 -0
  50. package/dist/components/Breadcrumb.vue.d.ts +33 -0
  51. package/dist/components/Calendar.vue.d.ts +107 -0
  52. package/dist/components/ChartContainer.vue.d.ts +31 -0
  53. package/dist/components/ChemicalFormula.vue.d.ts +8 -0
  54. package/dist/components/CollapsibleCard.vue.d.ts +41 -0
  55. package/dist/components/ColorSlider.vue.d.ts +34 -0
  56. package/dist/components/ConcentrationInput.vue.d.ts +25 -0
  57. package/dist/components/ConfirmDialog.vue.d.ts +42 -0
  58. package/dist/components/DataFrame.vue.d.ts +107 -0
  59. package/dist/components/DatePicker.vue.d.ts +25 -0
  60. package/dist/components/DateTimePicker.vue.d.ts +30 -0
  61. package/dist/components/Divider.vue.d.ts +14 -0
  62. package/dist/components/DoseCalculator.vue.d.ts +19 -0
  63. package/dist/components/DropdownButton.vue.d.ts +47 -0
  64. package/dist/components/EmptyState.vue.d.ts +36 -0
  65. package/dist/components/ExperimentCodeBadge.vue.d.ts +14 -0
  66. package/dist/components/ExperimentDataViewer.vue.d.ts +29 -0
  67. package/dist/components/ExperimentPopover.vue.d.ts +32 -0
  68. package/dist/components/ExperimentSelectorModal.vue.d.ts +28 -0
  69. package/dist/components/ExperimentTimeline.vue.d.ts +44 -0
  70. package/dist/components/FileUploader.vue.d.ts +40 -0
  71. package/dist/components/FitPanel.vue.d.ts +46 -0
  72. package/dist/components/FormActions.vue.d.ts +33 -0
  73. package/dist/components/FormBuilder.vue.d.ts +287 -0
  74. package/dist/components/FormField.vue.d.ts +28 -0
  75. package/dist/components/FormFieldRenderer.vue.d.ts +31 -0
  76. package/dist/components/FormSection.vue.d.ts +43 -0
  77. package/dist/components/FormulaInput.vue.d.ts +25 -0
  78. package/dist/components/GroupAssigner.vue.d.ts +25 -0
  79. package/dist/components/GroupingModal.vue.d.ts +12 -0
  80. package/dist/components/IconButton.vue.d.ts +34 -0
  81. package/dist/components/LoadingSpinner.vue.d.ts +12 -0
  82. package/dist/components/MoleculeInput.vue.d.ts +27 -0
  83. package/dist/components/MultiSelect.vue.d.ts +19 -0
  84. package/dist/components/NumberInput.vue.d.ts +22 -0
  85. package/dist/components/PlateMapEditor.vue.d.ts +50 -0
  86. package/dist/components/ProgressBar.vue.d.ts +23 -0
  87. package/dist/components/ProtocolStepEditor.vue.d.ts +24 -0
  88. package/dist/components/RackEditor.vue.d.ts +40 -0
  89. package/dist/components/ReagentEditor.vue.d.ts +30 -0
  90. package/dist/components/ReagentList.vue.d.ts +32 -0
  91. package/dist/components/ResourceCard.vue.d.ts +50 -0
  92. package/dist/components/SampleHierarchyTree.vue.d.ts +26 -0
  93. package/dist/components/SampleLegend.vue.d.ts +32 -0
  94. package/dist/components/SampleSelector.vue.d.ts +29 -0
  95. package/dist/components/ScheduleCalendar.vue.d.ts +110 -0
  96. package/dist/components/ScientificNumber.vue.d.ts +14 -0
  97. package/dist/components/SegmentedControl.vue.d.ts +20 -0
  98. package/dist/components/SequenceInput.vue.d.ts +54 -0
  99. package/dist/components/SettingsButton.vue.d.ts +30 -0
  100. package/dist/components/SettingsModal.vue.d.ts +36 -0
  101. package/dist/components/Skeleton.vue.d.ts +11 -0
  102. package/dist/components/StatusIndicator.vue.d.ts +13 -0
  103. package/dist/components/StepWizard.vue.d.ts +65 -0
  104. package/dist/components/TagsInput.vue.d.ts +39 -0
  105. package/dist/components/ThemeToggle.vue.d.ts +7 -0
  106. package/dist/components/TimePicker.vue.d.ts +29 -0
  107. package/dist/components/TimeRangeInput.vue.d.ts +27 -0
  108. package/dist/components/ToastNotification.vue.d.ts +2 -0
  109. package/dist/components/Tooltip.vue.d.ts +35 -0
  110. package/dist/components/UnitInput.vue.d.ts +39 -0
  111. package/dist/components/WellEditPopup.vue.d.ts +25 -0
  112. package/dist/components/WellPlate.vue.d.ts +73 -0
  113. package/dist/components/index.d.ts +87 -0
  114. package/dist/components/index.js +3 -0
  115. package/dist/components-CKf-UpGi.js +15089 -0
  116. package/dist/components-CKf-UpGi.js.map +1 -0
  117. package/dist/composables/experiment-utils.d.ts +8 -0
  118. package/dist/composables/formBuilderRegistry.d.ts +13 -0
  119. package/dist/composables/index.d.ts +28 -0
  120. package/dist/composables/index.js +3 -0
  121. package/dist/composables/useApi.d.ts +20 -0
  122. package/dist/composables/useAppExperiment.d.ts +37 -0
  123. package/dist/composables/useAsync.d.ts +128 -0
  124. package/dist/composables/useAuth.d.ts +47 -0
  125. package/dist/composables/useAutoGroup.d.ts +106 -0
  126. package/dist/composables/useChemicalFormula.d.ts +21 -0
  127. package/dist/composables/useConcentrationUnits.d.ts +29 -0
  128. package/dist/composables/useDoseCalculator.d.ts +58 -0
  129. package/dist/composables/useExperimentData.d.ts +18 -0
  130. package/dist/composables/useExperimentSave.d.ts +36 -0
  131. package/dist/composables/useExperimentSelector.d.ts +30 -0
  132. package/dist/composables/useForm.d.ts +92 -0
  133. package/dist/composables/useFormBuilder.d.ts +24 -0
  134. package/dist/composables/usePasskey.d.ts +10 -0
  135. package/dist/composables/usePlatformContext.d.ts +131 -0
  136. package/dist/composables/usePluginApi.d.ts +29 -0
  137. package/dist/composables/usePluginConfig.d.ts +13 -0
  138. package/dist/composables/useProtocolTemplates.d.ts +44 -0
  139. package/dist/composables/useRackEditor.d.ts +31 -0
  140. package/dist/composables/useReagentSeries.d.ts +23 -0
  141. package/dist/composables/useScheduleDrag.d.ts +78 -0
  142. package/dist/composables/useSequenceUtils.d.ts +14 -0
  143. package/dist/composables/useTheme.d.ts +8 -0
  144. package/dist/composables/useTimeUtils.d.ts +29 -0
  145. package/dist/composables/useToast.d.ts +22 -0
  146. package/dist/composables/useWellPlateEditor.d.ts +33 -0
  147. package/dist/composables-D0QfFzq1.js +805 -0
  148. package/dist/composables-D0QfFzq1.js.map +1 -0
  149. package/dist/histoire.setup.d.ts +1 -0
  150. package/dist/index.d.ts +6 -0
  151. package/dist/index.js +7 -0
  152. package/dist/install.d.ts +16 -0
  153. package/dist/install.js +23 -0
  154. package/dist/install.js.map +1 -0
  155. package/dist/stores/auth.d.ts +146 -0
  156. package/dist/stores/index.d.ts +2 -0
  157. package/dist/stores/index.js +2 -0
  158. package/dist/stores/settings.d.ts +75 -0
  159. package/dist/styles.css +29728 -0
  160. package/dist/tailwind.preset.d.ts +58 -0
  161. package/dist/tailwind.preset.js +66 -0
  162. package/dist/tailwind.preset.js.map +1 -0
  163. package/dist/types/auth.d.ts +42 -0
  164. package/dist/types/auto-group.d.ts +34 -0
  165. package/dist/types/components.d.ts +528 -0
  166. package/dist/types/form-builder.d.ts +167 -0
  167. package/dist/types/index.d.ts +5 -0
  168. package/dist/types/index.js +0 -0
  169. package/dist/types/platform.d.ts +75 -0
  170. package/dist/useScheduleDrag-DAJueTbK.js +7181 -0
  171. package/dist/useScheduleDrag-DAJueTbK.js.map +1 -0
  172. package/dist/utils/color.d.ts +24 -0
  173. package/package.json +114 -0
  174. package/src/__stories__/experiment-helpers.ts +83 -0
  175. package/src/__tests__/components/AppLayout.test.ts +163 -0
  176. package/src/__tests__/components/AppSidebar.test.ts +292 -0
  177. package/src/__tests__/components/AppTopBar.test.ts +683 -0
  178. package/src/__tests__/components/BaseInput.test.ts +99 -0
  179. package/src/__tests__/components/BasePill.test.ts +291 -0
  180. package/src/__tests__/components/Calendar.test.ts +566 -0
  181. package/src/__tests__/components/CollapsibleCard.test.ts +524 -0
  182. package/src/__tests__/components/DataFrame.test.ts +767 -0
  183. package/src/__tests__/components/DropdownButton.test.ts +471 -0
  184. package/src/__tests__/composables/formBuilderRegistry.test.ts +187 -0
  185. package/src/__tests__/composables/useAppExperiment.test.ts +560 -0
  186. package/src/__tests__/composables/useAuth.test.ts +188 -0
  187. package/src/__tests__/composables/useAutoGroup.test.ts +860 -0
  188. package/src/__tests__/composables/useExperimentData.test.ts +127 -0
  189. package/src/__tests__/composables/useExperimentSave.test.ts +347 -0
  190. package/src/__tests__/composables/useForm.test.ts +205 -0
  191. package/src/__tests__/composables/useFormBuilder.test.ts +917 -0
  192. package/src/__tests__/composables/usePlatformContext.test.ts +116 -0
  193. package/src/__tests__/composables/usePluginApi.test.ts +81 -0
  194. package/src/__tests__/composables/usePluginConfig.test.ts +176 -0
  195. package/src/__tests__/utils/color.test.ts +96 -0
  196. package/src/components/AlertBox.story.vue +204 -0
  197. package/src/components/AlertBox.vue +88 -0
  198. package/src/components/AppAvatarMenu.story.vue +155 -0
  199. package/src/components/AppAvatarMenu.vue +184 -0
  200. package/src/components/AppContainer.story.vue +104 -0
  201. package/src/components/AppContainer.vue +34 -0
  202. package/src/components/AppLayout.story.vue +292 -0
  203. package/src/components/AppLayout.vue +75 -0
  204. package/src/components/AppPageSelector.vue +159 -0
  205. package/src/components/AppPillNav.vue +66 -0
  206. package/src/components/AppPluginSwitcher.vue +241 -0
  207. package/src/components/AppSidebar.story.vue +309 -0
  208. package/src/components/AppSidebar.vue +119 -0
  209. package/src/components/AppTopBar.story.vue +304 -0
  210. package/src/components/AppTopBar.vue +661 -0
  211. package/src/components/AuditTrail.story.vue +163 -0
  212. package/src/components/AuditTrail.vue +151 -0
  213. package/src/components/AutoGroupModal.story.vue +273 -0
  214. package/src/components/AutoGroupModal.vue +566 -0
  215. package/src/components/Avatar.story.vue +115 -0
  216. package/src/components/Avatar.vue +79 -0
  217. package/src/components/BaseButton.story.vue +96 -0
  218. package/src/components/BaseButton.vue +73 -0
  219. package/src/components/BaseCheckbox.story.vue +73 -0
  220. package/src/components/BaseCheckbox.vue +69 -0
  221. package/src/components/BaseInput.story.vue +98 -0
  222. package/src/components/BaseInput.vue +74 -0
  223. package/src/components/BaseModal.story.vue +237 -0
  224. package/src/components/BaseModal.vue +182 -0
  225. package/src/components/BasePill.story.vue +142 -0
  226. package/src/components/BasePill.vue +89 -0
  227. package/src/components/BaseRadioGroup.story.vue +145 -0
  228. package/src/components/BaseRadioGroup.vue +124 -0
  229. package/src/components/BaseSelect.story.vue +120 -0
  230. package/src/components/BaseSelect.vue +71 -0
  231. package/src/components/BaseSlider.story.vue +122 -0
  232. package/src/components/BaseSlider.vue +126 -0
  233. package/src/components/BaseTabs.story.vue +127 -0
  234. package/src/components/BaseTabs.vue +59 -0
  235. package/src/components/BaseTextarea.story.vue +91 -0
  236. package/src/components/BaseTextarea.vue +62 -0
  237. package/src/components/BaseToggle.story.vue +81 -0
  238. package/src/components/BaseToggle.vue +76 -0
  239. package/src/components/BatchProgressList.story.vue +92 -0
  240. package/src/components/BatchProgressList.vue +184 -0
  241. package/src/components/Breadcrumb.story.vue +106 -0
  242. package/src/components/Breadcrumb.vue +75 -0
  243. package/src/components/Calendar.story.vue +106 -0
  244. package/src/components/Calendar.vue +363 -0
  245. package/src/components/ChartContainer.story.vue +113 -0
  246. package/src/components/ChartContainer.vue +64 -0
  247. package/src/components/ChemicalFormula.story.vue +102 -0
  248. package/src/components/ChemicalFormula.vue +39 -0
  249. package/src/components/CollapsibleCard.story.vue +135 -0
  250. package/src/components/CollapsibleCard.vue +167 -0
  251. package/src/components/ColorSlider.story.vue +120 -0
  252. package/src/components/ColorSlider.vue +164 -0
  253. package/src/components/ConcentrationInput.story.vue +77 -0
  254. package/src/components/ConcentrationInput.vue +185 -0
  255. package/src/components/ConfirmDialog.story.vue +248 -0
  256. package/src/components/ConfirmDialog.vue +93 -0
  257. package/src/components/DataFrame.story.vue +148 -0
  258. package/src/components/DataFrame.vue +419 -0
  259. package/src/components/DatePicker.story.vue +119 -0
  260. package/src/components/DatePicker.vue +330 -0
  261. package/src/components/DateTimePicker.story.vue +112 -0
  262. package/src/components/DateTimePicker.vue +392 -0
  263. package/src/components/Divider.story.vue +80 -0
  264. package/src/components/Divider.vue +49 -0
  265. package/src/components/DoseCalculator.story.vue +68 -0
  266. package/src/components/DoseCalculator.vue +476 -0
  267. package/src/components/DropdownButton.story.vue +102 -0
  268. package/src/components/DropdownButton.vue +181 -0
  269. package/src/components/EmptyState.story.vue +135 -0
  270. package/src/components/EmptyState.vue +69 -0
  271. package/src/components/ExperimentCodeBadge.story.vue +77 -0
  272. package/src/components/ExperimentCodeBadge.vue +64 -0
  273. package/src/components/ExperimentDataViewer.story.vue +174 -0
  274. package/src/components/ExperimentDataViewer.vue +288 -0
  275. package/src/components/ExperimentPopover.story.vue +384 -0
  276. package/src/components/ExperimentPopover.vue +241 -0
  277. package/src/components/ExperimentSelectorModal.story.vue +391 -0
  278. package/src/components/ExperimentSelectorModal.vue +387 -0
  279. package/src/components/ExperimentTimeline.story.vue +161 -0
  280. package/src/components/ExperimentTimeline.vue +382 -0
  281. package/src/components/FileUploader.story.vue +107 -0
  282. package/src/components/FileUploader.vue +386 -0
  283. package/src/components/FitPanel.story.vue +125 -0
  284. package/src/components/FitPanel.vue +120 -0
  285. package/src/components/FormActions.vue +92 -0
  286. package/src/components/FormBuilder.vue +214 -0
  287. package/src/components/FormField.story.vue +132 -0
  288. package/src/components/FormField.vue +59 -0
  289. package/src/components/FormFieldRenderer.vue +58 -0
  290. package/src/components/FormSection.vue +90 -0
  291. package/src/components/FormulaInput.story.vue +96 -0
  292. package/src/components/FormulaInput.vue +125 -0
  293. package/src/components/GroupAssigner.story.vue +83 -0
  294. package/src/components/GroupAssigner.vue +284 -0
  295. package/src/components/GroupingModal.story.vue +52 -0
  296. package/src/components/GroupingModal.vue +422 -0
  297. package/src/components/IconButton.story.vue +135 -0
  298. package/src/components/IconButton.vue +73 -0
  299. package/src/components/LoadingSpinner.story.vue +70 -0
  300. package/src/components/LoadingSpinner.vue +50 -0
  301. package/src/components/MoleculeInput.story.vue +66 -0
  302. package/src/components/MoleculeInput.vue +426 -0
  303. package/src/components/MultiSelect.story.vue +132 -0
  304. package/src/components/MultiSelect.vue +118 -0
  305. package/src/components/NumberInput.story.vue +122 -0
  306. package/src/components/NumberInput.vue +160 -0
  307. package/src/components/PlateMapEditor.story.vue +92 -0
  308. package/src/components/PlateMapEditor.vue +513 -0
  309. package/src/components/ProgressBar.story.vue +148 -0
  310. package/src/components/ProgressBar.vue +114 -0
  311. package/src/components/ProtocolStepEditor.story.vue +69 -0
  312. package/src/components/ProtocolStepEditor.vue +522 -0
  313. package/src/components/RackEditor.story.vue +100 -0
  314. package/src/components/RackEditor.vue +371 -0
  315. package/src/components/ReagentEditor.story.vue +153 -0
  316. package/src/components/ReagentEditor.vue +418 -0
  317. package/src/components/ReagentList.story.vue +137 -0
  318. package/src/components/ReagentList.vue +463 -0
  319. package/src/components/ResourceCard.story.vue +150 -0
  320. package/src/components/ResourceCard.vue +161 -0
  321. package/src/components/SampleHierarchyTree.story.vue +161 -0
  322. package/src/components/SampleHierarchyTree.vue +256 -0
  323. package/src/components/SampleLegend.story.vue +91 -0
  324. package/src/components/SampleLegend.vue +119 -0
  325. package/src/components/SampleSelector.story.vue +111 -0
  326. package/src/components/SampleSelector.vue +1033 -0
  327. package/src/components/ScheduleCalendar.story.vue +195 -0
  328. package/src/components/ScheduleCalendar.vue +569 -0
  329. package/src/components/ScientificNumber.story.vue +127 -0
  330. package/src/components/ScientificNumber.vue +197 -0
  331. package/src/components/SegmentedControl.story.vue +132 -0
  332. package/src/components/SegmentedControl.vue +79 -0
  333. package/src/components/SequenceInput.story.vue +119 -0
  334. package/src/components/SequenceInput.vue +209 -0
  335. package/src/components/SettingsButton.story.vue +58 -0
  336. package/src/components/SettingsButton.vue +76 -0
  337. package/src/components/SettingsModal.story.vue +145 -0
  338. package/src/components/SettingsModal.vue +146 -0
  339. package/src/components/Skeleton.story.vue +141 -0
  340. package/src/components/Skeleton.vue +74 -0
  341. package/src/components/StatusIndicator.story.vue +99 -0
  342. package/src/components/StatusIndicator.vue +40 -0
  343. package/src/components/StepWizard.story.vue +155 -0
  344. package/src/components/StepWizard.vue +223 -0
  345. package/src/components/TagsInput.story.vue +155 -0
  346. package/src/components/TagsInput.vue +265 -0
  347. package/src/components/ThemeToggle.story.vue +36 -0
  348. package/src/components/ThemeToggle.vue +54 -0
  349. package/src/components/TimePicker.story.vue +96 -0
  350. package/src/components/TimePicker.vue +273 -0
  351. package/src/components/TimeRangeInput.story.vue +104 -0
  352. package/src/components/TimeRangeInput.vue +122 -0
  353. package/src/components/ToastNotification.story.vue +157 -0
  354. package/src/components/ToastNotification.vue +62 -0
  355. package/src/components/Tooltip.story.vue +138 -0
  356. package/src/components/Tooltip.vue +119 -0
  357. package/src/components/UnitInput.story.vue +194 -0
  358. package/src/components/UnitInput.vue +213 -0
  359. package/src/components/WellEditPopup.vue +234 -0
  360. package/src/components/WellPlate.story.vue +282 -0
  361. package/src/components/WellPlate.vue +830 -0
  362. package/src/components/index.ts +118 -0
  363. package/src/composables/experiment-utils.ts +57 -0
  364. package/src/composables/formBuilderRegistry.ts +79 -0
  365. package/src/composables/index.ts +140 -0
  366. package/src/composables/useApi.ts +167 -0
  367. package/src/composables/useAppExperiment.ts +159 -0
  368. package/src/composables/useAsync.ts +323 -0
  369. package/src/composables/useAuth.ts +445 -0
  370. package/src/composables/useAutoGroup.ts +641 -0
  371. package/src/composables/useChemicalFormula.ts +275 -0
  372. package/src/composables/useConcentrationUnits.ts +246 -0
  373. package/src/composables/useDoseCalculator.ts +370 -0
  374. package/src/composables/useExperimentData.ts +86 -0
  375. package/src/composables/useExperimentSave.ts +192 -0
  376. package/src/composables/useExperimentSelector.ts +292 -0
  377. package/src/composables/useForm.ts +416 -0
  378. package/src/composables/useFormBuilder.ts +383 -0
  379. package/src/composables/usePasskey.ts +216 -0
  380. package/src/composables/usePlatformContext.ts +299 -0
  381. package/src/composables/usePluginApi.ts +39 -0
  382. package/src/composables/usePluginConfig.ts +93 -0
  383. package/src/composables/useProtocolTemplates.ts +518 -0
  384. package/src/composables/useRackEditor.ts +222 -0
  385. package/src/composables/useReagentSeries.ts +91 -0
  386. package/src/composables/useScheduleDrag.ts +245 -0
  387. package/src/composables/useSequenceUtils.ts +105 -0
  388. package/src/composables/useTheme.ts +58 -0
  389. package/src/composables/useTimeUtils.ts +131 -0
  390. package/src/composables/useToast.ts +40 -0
  391. package/src/composables/useWellPlateEditor.ts +421 -0
  392. package/src/histoire.setup.ts +17 -0
  393. package/src/index.ts +367 -0
  394. package/src/install.ts +32 -0
  395. package/src/stores/auth.ts +152 -0
  396. package/src/stores/index.ts +2 -0
  397. package/src/stores/settings.ts +218 -0
  398. package/src/styles/components/alert-box.css +150 -0
  399. package/src/styles/components/app-avatar-menu.css +155 -0
  400. package/src/styles/components/app-container.css +33 -0
  401. package/src/styles/components/app-layout.css +98 -0
  402. package/src/styles/components/app-page-selector.css +191 -0
  403. package/src/styles/components/app-pill-nav.css +57 -0
  404. package/src/styles/components/app-plugin-switcher.css +209 -0
  405. package/src/styles/components/app-sidebar.css +145 -0
  406. package/src/styles/components/app-top-bar.css +492 -0
  407. package/src/styles/components/audit-trail.css +143 -0
  408. package/src/styles/components/auto-group-modal.css +644 -0
  409. package/src/styles/components/avatar.css +73 -0
  410. package/src/styles/components/batch-progress-list.css +196 -0
  411. package/src/styles/components/breadcrumb.css +64 -0
  412. package/src/styles/components/button.css +188 -0
  413. package/src/styles/components/calendar.css +192 -0
  414. package/src/styles/components/chart-container.css +69 -0
  415. package/src/styles/components/checkbox.css +123 -0
  416. package/src/styles/components/chemical-formula.css +46 -0
  417. package/src/styles/components/collapsible-card.css +253 -0
  418. package/src/styles/components/color-slider.css +110 -0
  419. package/src/styles/components/concentration-input.css +156 -0
  420. package/src/styles/components/confirm-dialog.css +183 -0
  421. package/src/styles/components/dataframe.css +382 -0
  422. package/src/styles/components/date-picker.css +243 -0
  423. package/src/styles/components/datetime-picker.css +229 -0
  424. package/src/styles/components/divider.css +63 -0
  425. package/src/styles/components/dose-calculator.css +301 -0
  426. package/src/styles/components/dropdown-button.css +280 -0
  427. package/src/styles/components/empty-state.css +151 -0
  428. package/src/styles/components/experiment-code-badge.css +33 -0
  429. package/src/styles/components/experiment-data-viewer.css +138 -0
  430. package/src/styles/components/experiment-popover.css +562 -0
  431. package/src/styles/components/experiment-selector-modal.css +285 -0
  432. package/src/styles/components/experiment-timeline.css +529 -0
  433. package/src/styles/components/file-uploader.css +310 -0
  434. package/src/styles/components/fit-panel.css +67 -0
  435. package/src/styles/components/form-builder.css +69 -0
  436. package/src/styles/components/form-field.css +48 -0
  437. package/src/styles/components/formula-input.css +103 -0
  438. package/src/styles/components/group-assigner.css +200 -0
  439. package/src/styles/components/grouping-modal.css +323 -0
  440. package/src/styles/components/icon-button.css +192 -0
  441. package/src/styles/components/input.css +66 -0
  442. package/src/styles/components/loading-spinner.css +67 -0
  443. package/src/styles/components/modal.css +350 -0
  444. package/src/styles/components/molecule-input.css +186 -0
  445. package/src/styles/components/multi-select.css +131 -0
  446. package/src/styles/components/number-input.css +199 -0
  447. package/src/styles/components/pill.css +188 -0
  448. package/src/styles/components/plate-map-editor.css +464 -0
  449. package/src/styles/components/progress-bar.css +133 -0
  450. package/src/styles/components/protocol-step-editor.css +449 -0
  451. package/src/styles/components/rack-editor.css +265 -0
  452. package/src/styles/components/radio-group.css +240 -0
  453. package/src/styles/components/reagent-editor.css +510 -0
  454. package/src/styles/components/reagent-list.css +407 -0
  455. package/src/styles/components/resource-card.css +360 -0
  456. package/src/styles/components/sample-hierarchy-tree.css +314 -0
  457. package/src/styles/components/sample-legend.css +201 -0
  458. package/src/styles/components/sample-selector.css +751 -0
  459. package/src/styles/components/schedule-calendar.css +478 -0
  460. package/src/styles/components/scientific-number.css +63 -0
  461. package/src/styles/components/segmented-control.css +197 -0
  462. package/src/styles/components/select.css +77 -0
  463. package/src/styles/components/sequence-input.css +184 -0
  464. package/src/styles/components/settings-button.css +94 -0
  465. package/src/styles/components/settings-modal.css +95 -0
  466. package/src/styles/components/skeleton.css +49 -0
  467. package/src/styles/components/slider.css +74 -0
  468. package/src/styles/components/status-indicator.css +66 -0
  469. package/src/styles/components/step-wizard.css +192 -0
  470. package/src/styles/components/tabs.css +95 -0
  471. package/src/styles/components/tags-input.css +195 -0
  472. package/src/styles/components/textarea.css +82 -0
  473. package/src/styles/components/theme-toggle.css +69 -0
  474. package/src/styles/components/time-picker.css +171 -0
  475. package/src/styles/components/time-range-input.css +42 -0
  476. package/src/styles/components/toast.css +91 -0
  477. package/src/styles/components/toggle.css +146 -0
  478. package/src/styles/components/tooltip.css +91 -0
  479. package/src/styles/components/unit-input.css +123 -0
  480. package/src/styles/components/well-edit-popup.css +252 -0
  481. package/src/styles/components/well-plate.css +307 -0
  482. package/src/styles/index.css +87 -0
  483. package/src/styles/variables.css +1117 -0
  484. package/src/tailwind.preset.ts +61 -0
  485. package/src/types/auth.ts +55 -0
  486. package/src/types/auto-group.ts +40 -0
  487. package/src/types/components.ts +710 -0
  488. package/src/types/form-builder.ts +197 -0
  489. package/src/types/index.ts +207 -0
  490. package/src/types/platform.ts +116 -0
  491. package/src/utils/color.ts +96 -0
@@ -0,0 +1,384 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import ExperimentPopover from './ExperimentPopover.vue'
4
+
5
+ const saveLog = ref<string[]>([])
6
+ const lastAction = ref<string>('')
7
+
8
+ function logAction(label: string) {
9
+ lastAction.value = label
10
+ saveLog.value = [label, ...saveLog.value].slice(0, 5)
11
+ }
12
+
13
+ const EXPERIMENT_NAMES = [
14
+ 'DRP Dose-Response Screen · March 2026',
15
+ 'LCMS Method Optimization Run',
16
+ 'Kinase Inhibitor Panel — Plate 04',
17
+ 'Untargeted Metabolomics Pilot',
18
+ ]
19
+
20
+ const STATUSES = ['planned', 'ongoing', 'completed', 'ready_to_extract', 'processing']
21
+ </script>
22
+
23
+ <template>
24
+ <Story title="Experiment/ExperimentPopover">
25
+ <!-- Playground — all props wired up -->
26
+ <Variant title="Playground">
27
+ <template #default="{ state }">
28
+ <div style="padding: 3rem; display: flex; align-items: center; justify-content: center;">
29
+ <ExperimentPopover
30
+ :experiment-name="state.experimentName"
31
+ :experiment-code="state.experimentCode"
32
+ :experiment-status="state.experimentStatus"
33
+ :show-save="state.showSave"
34
+ :show-detach="state.showDetach"
35
+ :save-disabled="state.saveDisabled"
36
+ :save-loading="state.saveLoading"
37
+ :save-success-message="state.showSuccess ? state.saveSuccessMessage : ''"
38
+ :save-disabled-message="state.saveDisabledMessage"
39
+ :confirm-save="state.confirmSave"
40
+ @select="logAction('select')"
41
+ @save="logAction('save')"
42
+ @detach="logAction('detach')"
43
+ />
44
+ </div>
45
+ <div v-if="lastAction" style="text-align: center; font-family: 'Fira Code', monospace; font-size: 0.75rem; color: var(--text-muted);">
46
+ Last event: <code>{{ lastAction }}</code>
47
+ </div>
48
+ </template>
49
+
50
+ <template #controls="{ state }">
51
+ <HstText v-model="state.experimentCode" title="Experiment code (shown in trigger)" />
52
+ <HstText v-model="state.experimentName" title="Experiment name (shown in panel)" />
53
+ <HstSelect
54
+ v-model="state.experimentStatus"
55
+ title="Status"
56
+ :options="STATUSES.map(s => ({ value: s, label: s }))"
57
+ />
58
+ <HstCheckbox v-model="state.showSave" title="Show save (split mode)" />
59
+ <HstCheckbox v-model="state.showDetach" title="Show detach action" />
60
+ <HstCheckbox v-model="state.saveDisabled" title="Save disabled" />
61
+ <HstText v-model="state.saveDisabledMessage" title="Save-disabled tooltip" />
62
+ <HstCheckbox v-model="state.saveLoading" title="Save loading" />
63
+ <HstCheckbox v-model="state.showSuccess" title="Show success state" />
64
+ <HstText v-model="state.saveSuccessMessage" title="Save-success message" />
65
+ <HstCheckbox v-model="state.confirmSave" title="Confirm before save" />
66
+ </template>
67
+ </Variant>
68
+
69
+ <!-- No experiment selected -->
70
+ <Variant
71
+ title="Empty"
72
+ :init-state="() => ({})"
73
+ >
74
+ <div style="padding: 3rem; display: flex; align-items: center; justify-content: center;">
75
+ <ExperimentPopover />
76
+ </div>
77
+ <div style="text-align: center; font-size: 0.75rem; color: var(--text-muted); padding: 0 1rem 1rem;">
78
+ Trigger reads "No experiment" in <code>--text-muted</code>. Click to open panel → "Select Experiment" CTA.
79
+ </div>
80
+ </Variant>
81
+
82
+ <!-- Selected, no save action -->
83
+ <Variant
84
+ title="Selected (no save)"
85
+ :init-state="() => ({})"
86
+ >
87
+ <div style="padding: 3rem; display: flex; align-items: center; justify-content: center;">
88
+ <ExperimentPopover
89
+ experiment-name="DRP Dose-Response Screen · March 2026"
90
+ experiment-code="EXP-042"
91
+ experiment-status="ongoing"
92
+ show-detach
93
+ />
94
+ </div>
95
+ <div style="text-align: center; font-size: 0.75rem; color: var(--text-muted); padding: 0 1rem 1rem;">
96
+ Trigger shows only the code (<code>EXP-042</code>). Click to open the panel — full name, status, and actions
97
+ live inside.
98
+ </div>
99
+ </Variant>
100
+
101
+ <!-- With save — the split button pair -->
102
+ <Variant
103
+ title="With save (split)"
104
+ :init-state="() => ({})"
105
+ >
106
+ <div style="padding: 3rem; display: flex; align-items: center; justify-content: center;">
107
+ <ExperimentPopover
108
+ experiment-name="LCMS Method Optimization Run"
109
+ experiment-code="EXP-103"
110
+ experiment-status="ready_to_extract"
111
+ show-save
112
+ show-detach
113
+ :confirm-save="false"
114
+ @save="logAction('save')"
115
+ />
116
+ </div>
117
+ <div style="text-align: center; font-size: 0.75rem; color: var(--text-muted); padding: 0 1rem 1rem;">
118
+ Attached [chip | save] unit. Hover on either half lifts both together; each half keeps its own color-hover to
119
+ signal which action is "hot".
120
+ </div>
121
+ </Variant>
122
+
123
+ <!-- Save in progress -->
124
+ <Variant
125
+ title="Save — loading"
126
+ :init-state="() => ({})"
127
+ >
128
+ <div style="padding: 3rem; display: flex; align-items: center; justify-content: center;">
129
+ <ExperimentPopover
130
+ experiment-name="Kinase Inhibitor Panel — Plate 04"
131
+ experiment-code="EXP-204"
132
+ experiment-status="processing"
133
+ show-save
134
+ save-loading
135
+ />
136
+ </div>
137
+ </Variant>
138
+
139
+ <!-- Save success state -->
140
+ <Variant
141
+ title="Save — success"
142
+ :init-state="() => ({})"
143
+ >
144
+ <div style="padding: 3rem; display: flex; align-items: center; justify-content: center;">
145
+ <ExperimentPopover
146
+ experiment-name="Kinase Inhibitor Panel — Plate 04"
147
+ experiment-code="EXP-204"
148
+ experiment-status="processing"
149
+ show-save
150
+ save-success-message="Saved 42 measurements"
151
+ />
152
+ </div>
153
+ <div style="text-align: center; font-size: 0.75rem; color: var(--text-muted); padding: 0 1rem 1rem;">
154
+ Green surface with check icon. Success state auto-dismisses after 3s in the component itself; the story
155
+ just shows the moment.
156
+ </div>
157
+ </Variant>
158
+
159
+ <!-- End-to-end: save triggers the refresh-aligned ConfirmDialog -->
160
+ <Variant
161
+ title="Save with confirmation"
162
+ :init-state="() => ({})"
163
+ >
164
+ <div style="padding: 3rem; display: flex; align-items: center; justify-content: center;">
165
+ <ExperimentPopover
166
+ experiment-name="DRP Dose-Response Screen · March 2026"
167
+ experiment-code="EXP-042"
168
+ experiment-status="ongoing"
169
+ show-save
170
+ show-detach
171
+ :confirm-save="true"
172
+ @save="logAction('save confirmed')"
173
+ />
174
+ </div>
175
+ <p style="text-align: center; font-size: 0.75rem; color: var(--text-muted); padding: 0 2rem 1rem;">
176
+ Click the save half to open the <code>ConfirmDialog</code> — shows the refresh-aligned info variant with the
177
+ primary-blue icon, refined 15px title, and the full tactile-button system on both Cancel and Save.
178
+ </p>
179
+ </Variant>
180
+
181
+ <!-- Save disabled with tooltip -->
182
+ <Variant
183
+ title="Save — disabled"
184
+ :init-state="() => ({})"
185
+ >
186
+ <div style="padding: 3rem; display: flex; align-items: center; justify-content: center;">
187
+ <ExperimentPopover
188
+ experiment-name="Untargeted Metabolomics Pilot"
189
+ experiment-code="EXP-307"
190
+ experiment-status="planned"
191
+ show-save
192
+ save-disabled
193
+ save-disabled-message="No session data to save yet"
194
+ />
195
+ </div>
196
+ <div style="text-align: center; font-size: 0.75rem; color: var(--text-muted); padding: 0 1rem 1rem;">
197
+ Hover the save half to see the tooltip explaining why it's disabled.
198
+ </div>
199
+ </Variant>
200
+
201
+ <!-- Fallback: experiment without a code -->
202
+ <Variant
203
+ title="Legacy (no code)"
204
+ :init-state="() => ({})"
205
+ >
206
+ <div style="padding: 3rem; display: flex; align-items: center; justify-content: center;">
207
+ <ExperimentPopover
208
+ experiment-name="Legacy experiment from 2024"
209
+ experiment-status="completed"
210
+ show-detach
211
+ />
212
+ </div>
213
+ <div style="text-align: center; font-size: 0.75rem; color: var(--text-muted); padding: 0 1rem 1rem;">
214
+ When no <code>experimentCode</code> is provided (older data), the trigger falls back to showing the name
215
+ so the UI never reads as "unknown".
216
+ </div>
217
+ </Variant>
218
+
219
+ <!-- Topbar context — shows the refresh-design alignment -->
220
+ <Variant
221
+ title="Topbar context"
222
+ :init-state="() => ({})"
223
+ >
224
+ <div style="padding: 2rem; background: var(--bg-primary);">
225
+ <div
226
+ style="
227
+ display: flex;
228
+ align-items: center;
229
+ gap: 0.75rem;
230
+ padding: 0.625rem 1rem;
231
+ background: var(--bg-card);
232
+ border: 1px solid var(--border-light);
233
+ border-radius: var(--radius-lg);
234
+ box-shadow: var(--shadow-sm);
235
+ "
236
+ >
237
+ <!-- Mock: brand + page selector + plugin chip (visual only) -->
238
+ <div
239
+ style="
240
+ width: 1.625rem;
241
+ height: 1.625rem;
242
+ background: linear-gradient(135deg, var(--color-primary-hover) 0%, var(--color-primary) 100%);
243
+ border-radius: 0.375rem;
244
+ display: grid;
245
+ place-items: center;
246
+ color: white;
247
+ font-family: 'Fira Code', monospace;
248
+ font-weight: 700;
249
+ font-size: 0.6875rem;
250
+ "
251
+ >
252
+ MINT
253
+ </div>
254
+ <div style="flex: 1" />
255
+
256
+ <!-- The ExperimentPopover in-situ -->
257
+ <ExperimentPopover
258
+ experiment-name="DRP Dose-Response Screen · March 2026"
259
+ experiment-code="EXP-042"
260
+ experiment-status="ongoing"
261
+ show-save
262
+ show-detach
263
+ :confirm-save="false"
264
+ @save="logAction('save in topbar')"
265
+ />
266
+
267
+ <!-- Mock: avatar (visual only) -->
268
+ <div
269
+ style="
270
+ width: 1.875rem;
271
+ height: 1.875rem;
272
+ background: linear-gradient(135deg, var(--color-primary-hover) 0%, var(--color-primary) 100%);
273
+ border: 1px solid var(--border-color);
274
+ border-radius: 9999px;
275
+ color: white;
276
+ display: grid;
277
+ place-items: center;
278
+ font-size: 0.6875rem;
279
+ font-weight: 600;
280
+ "
281
+ >
282
+ EP
283
+ </div>
284
+ </div>
285
+ <p style="margin-top: 1rem; font-size: 0.75rem; color: var(--text-muted); text-align: center;">
286
+ Same 2.375rem height and refresh-design motion as AppPluginSwitcher, AppPageSelector, AppAvatarMenu.
287
+ </p>
288
+ </div>
289
+ </Variant>
290
+
291
+ <!-- All states side-by-side (visual regression reference) -->
292
+ <Variant
293
+ title="All states"
294
+ :init-state="() => ({})"
295
+ >
296
+ <div style="padding: 2rem; display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem;">
297
+ <div style="display: flex; flex-direction: column; align-items: center; gap: 0.625rem;">
298
+ <ExperimentPopover />
299
+ <span style="font-size: 0.6875rem; color: var(--text-muted); font-family: 'Fira Code', monospace;">empty</span>
300
+ </div>
301
+ <div style="display: flex; flex-direction: column; align-items: center; gap: 0.625rem;">
302
+ <ExperimentPopover
303
+ :experiment-name="EXPERIMENT_NAMES[0]"
304
+ experiment-code="EXP-042"
305
+ experiment-status="ongoing"
306
+ show-detach
307
+ />
308
+ <span style="font-size: 0.6875rem; color: var(--text-muted); font-family: 'Fira Code', monospace;">selected</span>
309
+ </div>
310
+ <div style="display: flex; flex-direction: column; align-items: center; gap: 0.625rem;">
311
+ <ExperimentPopover
312
+ :experiment-name="EXPERIMENT_NAMES[1]"
313
+ experiment-code="EXP-103"
314
+ experiment-status="ready_to_extract"
315
+ show-save
316
+ show-detach
317
+ :confirm-save="false"
318
+ />
319
+ <span style="font-size: 0.6875rem; color: var(--text-muted); font-family: 'Fira Code', monospace;">selected + save</span>
320
+ </div>
321
+ <div style="display: flex; flex-direction: column; align-items: center; gap: 0.625rem;">
322
+ <ExperimentPopover
323
+ :experiment-name="EXPERIMENT_NAMES[2]"
324
+ experiment-code="EXP-204"
325
+ experiment-status="processing"
326
+ show-save
327
+ save-loading
328
+ />
329
+ <span style="font-size: 0.6875rem; color: var(--text-muted); font-family: 'Fira Code', monospace;">save loading</span>
330
+ </div>
331
+ <div style="display: flex; flex-direction: column; align-items: center; gap: 0.625rem;">
332
+ <ExperimentPopover
333
+ :experiment-name="EXPERIMENT_NAMES[2]"
334
+ experiment-code="EXP-204"
335
+ experiment-status="processing"
336
+ show-save
337
+ save-success-message="Saved"
338
+ />
339
+ <span style="font-size: 0.6875rem; color: var(--text-muted); font-family: 'Fira Code', monospace;">save success</span>
340
+ </div>
341
+ <div style="display: flex; flex-direction: column; align-items: center; gap: 0.625rem;">
342
+ <ExperimentPopover
343
+ :experiment-name="EXPERIMENT_NAMES[3]"
344
+ experiment-code="EXP-307"
345
+ experiment-status="planned"
346
+ show-save
347
+ save-disabled
348
+ save-disabled-message="No data yet"
349
+ />
350
+ <span style="font-size: 0.6875rem; color: var(--text-muted); font-family: 'Fira Code', monospace;">save disabled</span>
351
+ </div>
352
+ </div>
353
+ </Variant>
354
+ </Story>
355
+ </template>
356
+
357
+ <docs lang="md">
358
+ ## ExperimentPopover
359
+
360
+ The experiment context chip that lives in `AppTopBar`'s right cluster. Shows the
361
+ currently linked experiment, opens a popover with Change / Detach actions, and
362
+ optionally pairs with an inline Save button as a split unit.
363
+
364
+ ### Refresh-design alignment
365
+
366
+ - **Height** `2.375rem` — matches `AppPluginSwitcher` / `AppPageSelector` so the
367
+ topbar's right cluster reads at one optical baseline.
368
+ - **Surface** `--bg-card` + `--border-color` — consistent with the white-card +
369
+ border language.
370
+ - **Hover** `translateY(-1px) + var(--shadow-sm)`, gated behind `:not(--active)`
371
+ so the chip stays put once its panel opens.
372
+ - **Active** border-primary + `0 0 0 3px var(--color-primary-soft)` ring, same
373
+ treatment as the other refresh triggers.
374
+ - **Split cohesion** — when save is visible, the `[chip | save]` pair lifts
375
+ together on either-half hover while keeping per-half color feedback.
376
+
377
+ ### Events
378
+
379
+ | Event | When |
380
+ |---|---|
381
+ | `select` | User clicked **Select / Change** → parent opens `ExperimentSelectorModal`. |
382
+ | `save` | User clicked save (or confirmed the inline ConfirmDialog). |
383
+ | `detach` | User clicked detach. |
384
+ </docs>
@@ -0,0 +1,241 @@
1
+ <script setup lang="ts">
2
+ /** Floating popover showing the active experiment with save, detach, and select actions. */
3
+ import { ref, watch, onMounted, onUnmounted } from 'vue'
4
+ import ConfirmDialog from './ConfirmDialog.vue'
5
+
6
+ interface Props {
7
+ experimentName?: string
8
+ experimentCode?: string
9
+ experimentStatus?: string
10
+ showSave?: boolean
11
+ showDetach?: boolean
12
+ saveDisabled?: boolean
13
+ saveLoading?: boolean
14
+ saveSuccessMessage?: string
15
+ saveDisabledMessage?: string
16
+ confirmSave?: boolean
17
+ confirmTitle?: string
18
+ confirmMessage?: string
19
+ }
20
+
21
+ const props = withDefaults(defineProps<Props>(), {
22
+ showSave: false,
23
+ showDetach: false,
24
+ saveDisabled: false,
25
+ saveLoading: false,
26
+ confirmSave: true,
27
+ })
28
+
29
+ const emit = defineEmits<{
30
+ select: []
31
+ save: []
32
+ detach: []
33
+ }>()
34
+
35
+ const isOpen = ref(false)
36
+ const popoverRef = ref<HTMLElement | null>(null)
37
+ const showSuccess = ref(false)
38
+ const showConfirm = ref(false)
39
+
40
+ function toggle() {
41
+ isOpen.value = !isOpen.value
42
+ }
43
+
44
+ function close() {
45
+ isOpen.value = false
46
+ }
47
+
48
+ function handleSelect() {
49
+ emit('select')
50
+ close()
51
+ }
52
+
53
+ function handleSave() {
54
+ if (props.saveDisabled || props.saveLoading) return
55
+ if (props.confirmSave) {
56
+ showConfirm.value = true
57
+ } else {
58
+ emit('save')
59
+ }
60
+ }
61
+
62
+ function handleConfirmSave() {
63
+ showConfirm.value = false
64
+ emit('save')
65
+ }
66
+
67
+ function handleDetach() {
68
+ emit('detach')
69
+ close()
70
+ }
71
+
72
+ function handleClickOutside(event: MouseEvent) {
73
+ if (popoverRef.value && !popoverRef.value.contains(event.target as Node)) {
74
+ close()
75
+ }
76
+ }
77
+
78
+ let successTimer: ReturnType<typeof setTimeout> | null = null
79
+
80
+ // Show success state when saveSuccessMessage changes from empty to a value
81
+ watch(() => props.saveSuccessMessage, (msg) => {
82
+ if (successTimer) clearTimeout(successTimer)
83
+ if (msg) {
84
+ showSuccess.value = true
85
+ successTimer = setTimeout(() => {
86
+ showSuccess.value = false
87
+ successTimer = null
88
+ }, 3000)
89
+ }
90
+ })
91
+
92
+ onMounted(() => {
93
+ document.addEventListener('click', handleClickOutside)
94
+ })
95
+
96
+ onUnmounted(() => {
97
+ document.removeEventListener('click', handleClickOutside)
98
+ if (successTimer) clearTimeout(successTimer)
99
+ })
100
+
101
+ // Format status for display (e.g., "ready_to_extract" -> "Ready to extract")
102
+ function formatStatus(status: string): string {
103
+ return status.replace(/_/g, ' ').replace(/^\w/, c => c.toUpperCase())
104
+ }
105
+ </script>
106
+
107
+ <template>
108
+ <div ref="popoverRef" class="mld-experiment-popover">
109
+ <!-- Split trigger: experiment pill + inline save -->
110
+ <div
111
+ :class="[
112
+ 'mld-experiment-popover__split',
113
+ { 'mld-experiment-popover__split--with-save': showSave && experimentName },
114
+ ]"
115
+ >
116
+ <!-- Left: experiment trigger (opens popover) — shows only the code for
117
+ maximum topbar compactness; full name + status live in the panel. -->
118
+ <button
119
+ type="button"
120
+ :class="[
121
+ 'mld-experiment-popover__trigger',
122
+ { 'mld-experiment-popover__trigger--active': isOpen },
123
+ { 'mld-experiment-popover__trigger--empty': !experimentCode && !experimentName },
124
+ ]"
125
+ :title="experimentName || undefined"
126
+ @click.stop="toggle"
127
+ >
128
+ <!-- Flask icon -->
129
+ <svg class="mld-experiment-popover__trigger-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
130
+ <path
131
+ stroke-linecap="round"
132
+ stroke-linejoin="round"
133
+ stroke-width="1.75"
134
+ 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"
135
+ />
136
+ </svg>
137
+ <!-- Code preferred, name as fallback, "No experiment" as empty state -->
138
+ <span v-if="experimentCode" class="mld-experiment-popover__trigger-code">{{ experimentCode }}</span>
139
+ <span v-else-if="experimentName" class="mld-experiment-popover__trigger-text">{{ experimentName }}</span>
140
+ <span v-else class="mld-experiment-popover__trigger-text">No experiment</span>
141
+ <!-- Chevron -->
142
+ <svg class="mld-experiment-popover__trigger-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
143
+ <path d="m6 9 6 6 6-6" />
144
+ </svg>
145
+ </button>
146
+
147
+ <!-- Right: inline save button (direct action) -->
148
+ <button
149
+ v-if="showSave && experimentName"
150
+ type="button"
151
+ :class="[
152
+ 'mld-experiment-popover__save-trigger',
153
+ { 'mld-experiment-popover__save-trigger--loading': saveLoading },
154
+ { 'mld-experiment-popover__save-trigger--success': showSuccess },
155
+ { 'mld-experiment-popover__save-trigger--disabled': saveDisabled && !showSuccess },
156
+ ]"
157
+ :disabled="saveDisabled && !showSuccess"
158
+ :title="saveDisabled && saveDisabledMessage ? saveDisabledMessage : showSuccess && saveSuccessMessage ? saveSuccessMessage : 'Save to Experiment'"
159
+ @click.stop="handleSave"
160
+ >
161
+ <!-- Loading spinner -->
162
+ <span v-if="saveLoading" class="mld-experiment-popover__spinner--inline" />
163
+ <!-- Success check -->
164
+ <svg v-else-if="showSuccess" class="mld-experiment-popover__save-trigger-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
165
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7" />
166
+ </svg>
167
+ <!-- Save icon -->
168
+ <svg v-else class="mld-experiment-popover__save-trigger-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
169
+ <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" />
170
+ </svg>
171
+ </button>
172
+ </div>
173
+
174
+ <!-- Popover panel -->
175
+ <div v-if="isOpen" class="mld-experiment-popover__panel">
176
+ <!-- Header -->
177
+ <div class="mld-experiment-popover__header">
178
+ <div class="mld-experiment-popover__title">Experiment</div>
179
+ <div class="mld-experiment-popover__subtitle">
180
+ {{ experimentName ? 'Linked experiment context' : 'Link to an MLD experiment' }}
181
+ </div>
182
+ </div>
183
+
184
+ <!-- No experiment selected -->
185
+ <div v-if="!experimentName" class="mld-experiment-popover__body">
186
+ <button type="button" class="mld-experiment-popover__select-btn" @click="handleSelect">
187
+ <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24">
188
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
189
+ </svg>
190
+ Select Experiment
191
+ </button>
192
+ </div>
193
+
194
+ <!-- Experiment selected -->
195
+ <div v-else class="mld-experiment-popover__body">
196
+ <div class="mld-experiment-popover__card">
197
+ <div class="mld-experiment-popover__card-icon">
198
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
199
+ <path
200
+ stroke-linecap="round"
201
+ stroke-linejoin="round"
202
+ stroke-width="1.75"
203
+ 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"
204
+ />
205
+ </svg>
206
+ </div>
207
+ <div class="mld-experiment-popover__card-info">
208
+ <span v-if="experimentCode" class="mld-experiment-popover__card-code">{{ experimentCode }}</span>
209
+ <div class="mld-experiment-popover__card-name">{{ experimentName }}</div>
210
+ <div v-if="experimentStatus" class="mld-experiment-popover__card-status">
211
+ {{ formatStatus(experimentStatus) }}
212
+ </div>
213
+ </div>
214
+ </div>
215
+ <div class="mld-experiment-popover__card-actions">
216
+ <button type="button" class="mld-experiment-popover__change-btn" @click="handleSelect">
217
+ Change
218
+ </button>
219
+ <button v-if="showDetach" type="button" class="mld-experiment-popover__detach-btn" @click="handleDetach">
220
+ Detach
221
+ </button>
222
+ </div>
223
+ </div>
224
+ </div>
225
+
226
+ <!-- Save confirmation dialog -->
227
+ <ConfirmDialog
228
+ v-model="showConfirm"
229
+ :title="confirmTitle ?? 'Save to Experiment'"
230
+ :message="confirmMessage ?? `Save current data to ${experimentName}?`"
231
+ variant="info"
232
+ confirm-label="Save"
233
+ :loading="saveLoading"
234
+ @confirm="handleConfirmSave"
235
+ />
236
+ </div>
237
+ </template>
238
+
239
+ <style>
240
+ @import '../styles/components/experiment-popover.css';
241
+ </style>