@morscherlab/mint-sdk 1.3.11 → 1.4.0-beta.10

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 (1056) hide show
  1. package/README.md +152 -177
  2. package/dist/AlertBox-B2Bgr3ei.js +276 -0
  3. package/dist/AlertBox-B2Bgr3ei.js.map +1 -0
  4. package/dist/AppSidebar-mvm--GxN.js +3565 -0
  5. package/dist/AppSidebar-mvm--GxN.js.map +1 -0
  6. package/dist/ConfirmDialog-nbJj51is.js +102 -0
  7. package/dist/ConfirmDialog-nbJj51is.js.map +1 -0
  8. package/dist/ExperimentPopover-5T1fAq2L.js +337 -0
  9. package/dist/ExperimentPopover-5T1fAq2L.js.map +1 -0
  10. package/dist/ExperimentSelectorModal-65peG4Up.js +544 -0
  11. package/dist/ExperimentSelectorModal-65peG4Up.js.map +1 -0
  12. package/dist/SegmentedControl-BtnnjWzs.js +129 -0
  13. package/dist/SegmentedControl-BtnnjWzs.js.map +1 -0
  14. package/dist/SettingsModal-DO3IZ7qz.js +666 -0
  15. package/dist/SettingsModal-DO3IZ7qz.js.map +1 -0
  16. package/dist/analysisArtifactTypes-Cy3D0Ipg.js.map +1 -1
  17. package/dist/auth-SliLnFre.js +65 -0
  18. package/dist/auth-SliLnFre.js.map +1 -0
  19. package/dist/components/ActionMenu.vue.d.ts +1 -1
  20. package/dist/components/AdductText.vue.d.ts +1 -1
  21. package/dist/components/AlertBox.vue.d.ts +3 -3
  22. package/dist/components/AppContainer.vue.d.ts +1 -1
  23. package/dist/components/AppSidebar.vue.d.ts +15 -95
  24. package/dist/components/AppTopBar.vue.d.ts +16 -70
  25. package/dist/components/ArtifactSaveDialog.vue.d.ts +13 -5
  26. package/dist/components/ArtifactSelectorModal.vue.d.ts +4 -4
  27. package/dist/components/AuditTrail.vue.d.ts +2 -2
  28. package/dist/components/BaseButton.vue.d.ts +11 -4
  29. package/dist/components/BaseCheckbox.vue.d.ts +9 -3
  30. package/dist/components/BaseInput.vue.d.ts +13 -9
  31. package/dist/components/BaseModal.vue.d.ts +4 -4
  32. package/dist/components/BasePill.vue.d.ts +15 -8
  33. package/dist/components/BaseRadioGroup.vue.d.ts +2 -2
  34. package/dist/components/BaseSelect.vue.d.ts +51 -19
  35. package/dist/components/BaseTabs.vue.d.ts +6 -5
  36. package/dist/components/BatchProgressList.vue.d.ts +2 -2
  37. package/dist/components/ChartContainer.vue.d.ts +4 -2
  38. package/dist/components/ChemicalFormula.vue.d.ts +2 -2
  39. package/dist/components/CollapsibleCard.vue.d.ts +3 -3
  40. package/dist/components/ComponentBindingRenderer.vue.d.ts +2 -3
  41. package/dist/components/ConcentrationInput.vue.d.ts +2 -2
  42. package/dist/components/ConfirmDialog.vue.d.ts +8 -7
  43. package/dist/components/DataFrame.vue.d.ts +32 -74
  44. package/dist/components/DatePicker.vue.d.ts +24 -2
  45. package/dist/components/DoseCalculator.vue.d.ts +1 -1
  46. package/dist/components/DoseCalculatorVolumeField.vue.d.ts +1 -1
  47. package/dist/components/EmptyState.vue.d.ts +3 -2
  48. package/dist/components/ExperimentPopover.vue.d.ts +18 -0
  49. package/dist/components/ExperimentSelectorModal.vue.d.ts +3 -7
  50. package/dist/components/FilePicker.vue.d.ts +1 -1
  51. package/dist/components/FileUploader.vue.d.ts +2 -2
  52. package/dist/components/FormBuilder.vue.d.ts +17 -74
  53. package/dist/components/FormulaInput.vue.d.ts +2 -2
  54. package/dist/components/GeneratedPluginView.vue.d.ts +1 -1
  55. package/dist/components/GeneratedResultRenderer.vue.d.ts +5 -1
  56. package/dist/components/{StatusIndicator.vue.d.ts → InstrumentStatusPanels.vue.d.ts} +8 -9
  57. package/dist/components/JobsStatusTray.vue.d.ts +2 -9
  58. package/dist/components/LoadingSpinner.vue.d.ts +3 -3
  59. package/dist/components/MoleculeInput.vue.d.ts +2 -2
  60. package/dist/components/NumberInput.vue.d.ts +19 -5
  61. package/dist/components/PlateEditor.vue.d.ts +11 -61
  62. package/dist/components/PlotlyChart.vue.d.ts +2 -2
  63. package/dist/components/PluginIcon.vue.d.ts +5 -1
  64. package/dist/components/PluginWorkspaceView.controls.d.ts +2 -9
  65. package/dist/components/PluginWorkspaceView.navigation.d.ts +1 -7
  66. package/dist/components/PluginWorkspaceView.props.d.ts +18 -137
  67. package/dist/components/PluginWorkspaceView.shell.d.ts +2 -10
  68. package/dist/components/PluginWorkspaceView.vue.d.ts +14 -84
  69. package/dist/components/PresenceAvatars.vue.d.ts +31 -0
  70. package/dist/components/ProgressBar.vue.d.ts +3 -2
  71. package/dist/components/ProtocolStep.presentation.d.ts +0 -1
  72. package/dist/components/SampleHierarchyTree.vue.d.ts +3 -3
  73. package/dist/components/SampleSelector.vue.d.ts +8 -8
  74. package/dist/components/ScheduleCalendar.vue.d.ts +1 -1
  75. package/dist/components/ScientificNumber.vue.d.ts +1 -1
  76. package/dist/components/SecretInput.vue.d.ts +2 -2
  77. package/dist/components/SegmentedControl.vue.d.ts +2 -2
  78. package/dist/components/SequenceInput.vue.d.ts +2 -2
  79. package/dist/components/SequenceProgressBar.vue.d.ts +3 -0
  80. package/dist/components/SettingsModal.vue.d.ts +12 -20
  81. package/dist/components/SmartGroup.types.d.ts +0 -1
  82. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +1 -1
  83. package/dist/components/StaleFormBanner.vue.d.ts +20 -0
  84. package/dist/components/StepWizard.vue.d.ts +3 -0
  85. package/dist/components/TagsInput.vue.d.ts +4 -3
  86. package/dist/components/TimeRangeInput.vue.d.ts +2 -2
  87. package/dist/components/VersionConflictDialog.vue.d.ts +24 -0
  88. package/dist/components/WellPlate.geometry.d.ts +0 -6
  89. package/dist/components/WellPlate.rendering.d.ts +0 -2
  90. package/dist/components/WellPlate.vue.d.ts +7 -52
  91. package/dist/components/index.d.ts +0 -90
  92. package/dist/components/{AutoGroupModal.adapter.d.ts → internal/AutoGroupModalInternal.adapter.d.ts} +6 -6
  93. package/dist/components/{AutoGroupModal.vue.d.ts → internal/AutoGroupModalInternal.vue.d.ts} +6 -6
  94. package/dist/components/{BaseToggle.vue.d.ts → internal/CheckboxSwitchInternal.vue.d.ts} +2 -2
  95. package/dist/components/{MultiSelect.vue.d.ts → internal/DatePickerDateInternal.vue.d.ts} +11 -9
  96. package/dist/components/{DateTimePicker.vue.d.ts → internal/DatePickerDateTimeInternal.vue.d.ts} +2 -2
  97. package/dist/components/{TimePicker.vue.d.ts → internal/DatePickerTimeInternal.vue.d.ts} +4 -4
  98. package/dist/components/internal/FilePickerListInternal.vue.d.ts +2 -2
  99. package/dist/components/internal/FormSchemaInternal.vue.d.ts +95 -0
  100. package/dist/components/{UnitInput.vue.d.ts → internal/NumberUnitInputInternal.vue.d.ts} +6 -14
  101. package/dist/components/internal/PlateEditorBindingInternal.vue.d.ts +3 -2
  102. package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +5 -10
  103. package/dist/components/internal/SelectMultipleInternal.vue.d.ts +27 -0
  104. package/dist/components/{SearchableSelect.vue.d.ts → internal/SelectSearchableInternal.vue.d.ts} +5 -2
  105. package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +1 -15
  106. package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +4 -4
  107. package/dist/{composables → components/internal}/autoGroup/classKey.d.ts +1 -1
  108. package/dist/{composables → components/internal}/autoGroup/classify.d.ts +1 -1
  109. package/dist/{composables → components/internal}/autoGroup/columns.d.ts +1 -1
  110. package/dist/{composables → components/internal}/autoGroup/compose.d.ts +1 -1
  111. package/dist/{composables → components/internal}/autoGroup/cooccurrence.d.ts +1 -1
  112. package/dist/{composables → components/internal}/autoGroup/csv-shim.d.ts +1 -1
  113. package/dist/{composables → components/internal}/autoGroup/fingerprint.d.ts +1 -1
  114. package/dist/{composables → components/internal}/autoGroup/template.d.ts +1 -1
  115. package/dist/{composables → components/internal}/autoGroup/tokenLength.d.ts +1 -1
  116. package/dist/components/internal/generatedJobsApi.d.ts +1 -1
  117. package/dist/{composables → components/internal}/useAutoGroup.d.ts +9 -18
  118. package/dist/composables/componentSpecs.d.ts +41 -0
  119. package/dist/composables/controlComponentBindings.d.ts +6 -0
  120. package/dist/composables/controlSchemaAdapters.d.ts +28 -2
  121. package/dist/composables/controlSchemaDoseDesign.d.ts +1 -1
  122. package/dist/composables/controlSchemaModel.d.ts +1 -1
  123. package/dist/composables/controlSchemaTypes.d.ts +35 -23
  124. package/dist/composables/{usePluginClient.d.ts → createClient.d.ts} +22 -49
  125. package/dist/composables/{useEventStream.d.ts → eventStream.d.ts} +2 -2
  126. package/dist/composables/experiment-utils.d.ts +1 -6
  127. package/dist/composables/index.d.ts +7 -59
  128. package/dist/composables/platformContextHelpers.d.ts +0 -11
  129. package/dist/composables/pluginEndpointBuilder.d.ts +2 -2
  130. package/dist/composables/pluginIdentity.d.ts +24 -0
  131. package/dist/composables/pluginWorkspaceModel.d.ts +16 -99
  132. package/dist/composables/useApi.d.ts +1 -3
  133. package/dist/composables/useAppExperiment.d.ts +9 -1
  134. package/dist/composables/{useAnalysisArtifacts.d.ts → useArtifacts.d.ts} +6 -6
  135. package/dist/composables/useCalendarGrid.d.ts +1 -1
  136. package/dist/composables/useExperiment.d.ts +30 -0
  137. package/dist/composables/useExperimentSelector.d.ts +6 -2
  138. package/dist/composables/useFilePicker.d.ts +2 -2
  139. package/dist/composables/useJobCenter.d.ts +63 -0
  140. package/dist/composables/{useGeneratedAnalysis.d.ts → useJobsEndpoint.d.ts} +22 -23
  141. package/dist/composables/useMenuKeyboard.d.ts +1 -1
  142. package/dist/composables/usePlatform.d.ts +64 -0
  143. package/dist/composables/usePluginWorkspace.d.ts +6 -11
  144. package/dist/composables/usePresence.d.ts +41 -0
  145. package/dist/composables/useRackEditor.d.ts +2 -2
  146. package/dist/composables/useRealtime.d.ts +71 -0
  147. package/dist/composables/{useRequestSyncState.d.ts → useRequest.d.ts} +12 -17
  148. package/dist/composables/useToast.d.ts +7 -5
  149. package/dist/composables/useVersionedSave.d.ts +76 -0
  150. package/dist/controlSchemaModel-B8CTXagP.js +95 -0
  151. package/dist/controlSchemaModel-B8CTXagP.js.map +1 -0
  152. package/dist/data-CgILxDLW.js +3865 -0
  153. package/dist/data-CgILxDLW.js.map +1 -0
  154. package/dist/data.d.ts +32 -0
  155. package/dist/data.js +10 -0
  156. package/dist/deprecatedRootExports.d.ts +191 -0
  157. package/dist/experiment-43OJtH7R.js +673 -0
  158. package/dist/experiment-43OJtH7R.js.map +1 -0
  159. package/dist/experimentStatus-Cqp6qNrR.js +60 -0
  160. package/dist/experimentStatus-Cqp6qNrR.js.map +1 -0
  161. package/dist/host.d.ts +10 -0
  162. package/dist/host.js +26 -0
  163. package/dist/host.js.map +1 -0
  164. package/dist/index.d.ts +1 -12
  165. package/dist/index.js +65 -277
  166. package/dist/index.js.map +1 -1
  167. package/dist/install-CsPFJSx3.js +669 -0
  168. package/dist/install-CsPFJSx3.js.map +1 -0
  169. package/dist/install.d.ts +2 -1
  170. package/dist/install.js +1 -21
  171. package/dist/instrument.d.ts +0 -6
  172. package/dist/lab-MHzp-_J8.js +7494 -0
  173. package/dist/lab-MHzp-_J8.js.map +1 -0
  174. package/dist/lab.d.ts +42 -0
  175. package/dist/lab.js +5 -0
  176. package/dist/permissions.d.ts +0 -4
  177. package/dist/permissions.js +1 -20
  178. package/dist/permissions.js.map +1 -1
  179. package/dist/records-B6gpM_3e.js +3268 -0
  180. package/dist/records-B6gpM_3e.js.map +1 -0
  181. package/dist/runtime-8bs8EogX.js +1491 -0
  182. package/dist/runtime-8bs8EogX.js.map +1 -0
  183. package/dist/runtime.d.ts +25 -0
  184. package/dist/runtime.js +8 -0
  185. package/dist/shell-CywENwBT.js +197 -0
  186. package/dist/shell-CywENwBT.js.map +1 -0
  187. package/dist/shell.d.ts +16 -0
  188. package/dist/shell.js +8 -0
  189. package/dist/stores/auth.d.ts +13 -35
  190. package/dist/stores/experiment.d.ts +4 -0
  191. package/dist/stores/index.js +2 -2
  192. package/dist/styles.css +12161 -15036
  193. package/dist/types/analysisArtifactTypes.d.ts +1 -1
  194. package/dist/types/auth.d.ts +0 -8
  195. package/dist/types/componentLabTypes.d.ts +98 -34
  196. package/dist/types/componentWorkflowTypes.d.ts +2 -27
  197. package/dist/types/components.d.ts +145 -74
  198. package/dist/types/form-builder.d.ts +4 -0
  199. package/dist/types/generated/platformContext.d.ts +53 -0
  200. package/dist/types/generated-ui.d.ts +6 -2
  201. package/dist/types/index.d.ts +4 -5
  202. package/dist/types/instrument.d.ts +41 -0
  203. package/dist/types/platform.d.ts +20 -58
  204. package/dist/useDebouncedWatch-BPwyl6eS.js +73 -0
  205. package/dist/useDebouncedWatch-BPwyl6eS.js.map +1 -0
  206. package/dist/{experiment-utils-DTANyVKV.js → useDropdownState-8yWMu-7_.js} +127 -144
  207. package/dist/useDropdownState-8yWMu-7_.js.map +1 -0
  208. package/dist/useExpansionSet-CvoNMZJi.js +68 -0
  209. package/dist/useExpansionSet-CvoNMZJi.js.map +1 -0
  210. package/dist/useExperimentSamples-DHnxKFAF.js +366 -0
  211. package/dist/useExperimentSamples-DHnxKFAF.js.map +1 -0
  212. package/dist/useListReorder-BitdUhxn.js +5294 -0
  213. package/dist/useListReorder-BitdUhxn.js.map +1 -0
  214. package/dist/useRackEditor-DiwCt77M.js +5930 -0
  215. package/dist/useRackEditor-DiwCt77M.js.map +1 -0
  216. package/dist/useRealtime-C3iy9QkU.js +699 -0
  217. package/dist/useRealtime-C3iy9QkU.js.map +1 -0
  218. package/dist/useRequest-D506eHi8.js +244 -0
  219. package/dist/useRequest-D506eHi8.js.map +1 -0
  220. package/dist/utils/color.d.ts +0 -5
  221. package/dist/{composables/useDoseCalculator.d.ts → utils/dose.d.ts} +10 -13
  222. package/dist/utils/experimentStatus.d.ts +9 -0
  223. package/dist/{composables/useChemicalFormula.d.ts → utils/formula.d.ts} +3 -6
  224. package/dist/utils/integrationHost.d.ts +18 -0
  225. package/dist/utils/plotlyTemplate.d.ts +2 -0
  226. package/dist/utils/pluginSecrets.d.ts +12 -12
  227. package/dist/utils/presenceAvatars.d.ts +30 -0
  228. package/dist/utils/records.d.ts +0 -2
  229. package/dist/{composables/useTimeUtils.d.ts → utils/time.d.ts} +7 -18
  230. package/package.json +20 -17
  231. package/src/__tests__/components/ActionMenu.test.ts +0 -13
  232. package/src/__tests__/components/AlertBox.test.ts +1 -1
  233. package/src/__tests__/components/AppSidebar.test.ts +119 -167
  234. package/src/__tests__/components/AppToastContainer.test.ts +2 -2
  235. package/src/__tests__/components/AppTopBar.test.ts +230 -176
  236. package/src/__tests__/components/AppTopBarPillNav.test.ts +35 -0
  237. package/src/__tests__/components/ArtifactSaveDialog.test.ts +83 -3
  238. package/src/__tests__/components/ArtifactSelectorModal.test.ts +6 -4
  239. package/src/__tests__/components/AuditTrail.test.ts +1 -1
  240. package/src/__tests__/components/AutoGroupModal.adapter.test.ts +1 -1
  241. package/src/__tests__/components/AutoGroupModal.anatomy.test.ts +2 -2
  242. package/src/__tests__/components/AutoGroupModal.wiring.test.ts +2 -2
  243. package/src/__tests__/components/BaseButton.test.ts +51 -0
  244. package/src/__tests__/components/{BaseToggle.test.ts → BaseCheckbox.switch.test.ts} +8 -6
  245. package/src/__tests__/components/BaseInput.test.ts +13 -34
  246. package/src/__tests__/components/BaseModal.test.ts +6 -6
  247. package/src/__tests__/components/BasePill.test.ts +33 -39
  248. package/src/__tests__/components/BaseSelect.multiple.test.ts +118 -0
  249. package/src/__tests__/components/{SearchableSelect.test.ts → BaseSelect.searchable.test.ts} +19 -9
  250. package/src/__tests__/components/BaseSelect.test.ts +11 -0
  251. package/src/__tests__/components/BaseTabs.test.ts +13 -13
  252. package/src/__tests__/components/BatchProgressList.test.ts +16 -0
  253. package/src/__tests__/components/CalendarGridPanel.test.ts +2 -2
  254. package/src/__tests__/components/ChartContainer.test.ts +11 -0
  255. package/src/__tests__/components/ChemicalFormula.test.ts +6 -3
  256. package/src/__tests__/components/CollapsibleCard.test.ts +1 -1
  257. package/src/__tests__/components/ComponentBindingRenderer.test.ts +25 -73
  258. package/src/__tests__/components/ConcentrationInput.test.ts +3 -3
  259. package/src/__tests__/components/ConfirmDialog.test.ts +23 -0
  260. package/src/__tests__/components/DataFrame.test.ts +140 -142
  261. package/src/__tests__/components/DataStates.test.ts +6 -22
  262. package/src/__tests__/components/{DateTimePicker.test.ts → DatePicker.datetime.test.ts} +15 -7
  263. package/src/__tests__/components/DatePicker.test.ts +21 -0
  264. package/src/__tests__/components/{TimePicker.test.ts → DatePicker.time.test.ts} +20 -19
  265. package/src/__tests__/components/DoseCalculator.test.ts +12 -7
  266. package/src/__tests__/components/DoseCalculatorVolumeField.test.ts +6 -6
  267. package/src/__tests__/components/EmptyState.test.ts +1 -1
  268. package/src/__tests__/components/ExperimentPopover.test.ts +125 -16
  269. package/src/__tests__/components/ExperimentSelectorModal.test.ts +134 -28
  270. package/src/__tests__/components/FormBuilder.test.ts +49 -167
  271. package/src/__tests__/components/FormSchemaInternal.test.ts +91 -0
  272. package/src/__tests__/components/GeneratedPathInput.test.ts +18 -13
  273. package/src/__tests__/components/GeneratedPluginView.test.ts +60 -25
  274. package/src/__tests__/components/GeneratedResultRenderer.test.ts +6 -4
  275. package/src/__tests__/components/InstrumentStateBadge.test.ts +4 -4
  276. package/src/__tests__/components/InstrumentStatusPanels.test.ts +79 -0
  277. package/src/__tests__/components/JobsStatusTray.test.ts +4 -4
  278. package/src/__tests__/components/NumberInput.test.ts +28 -0
  279. package/src/__tests__/components/{UnitInput.test.ts → NumberInput.units.test.ts} +6 -6
  280. package/src/__tests__/components/PlateEditor.test.ts +108 -108
  281. package/src/__tests__/components/PlateEditorBindingInternal.test.ts +13 -13
  282. package/src/__tests__/components/PlotlyChart.test.ts +1 -1
  283. package/src/__tests__/components/PluginIcon.test.ts +9 -4
  284. package/src/__tests__/components/PluginWorkspaceView.controls.test.ts +16 -29
  285. package/src/__tests__/components/PluginWorkspaceView.internal.test.ts +28 -34
  286. package/src/__tests__/components/PluginWorkspaceView.navigation.test.ts +4 -19
  287. package/src/__tests__/components/PluginWorkspaceView.shell.test.ts +1 -6
  288. package/src/__tests__/components/PluginWorkspaceView.test.ts +183 -155
  289. package/src/__tests__/components/PresenceAvatars.test.ts +90 -0
  290. package/src/__tests__/components/ProtocolStep.presentation.test.ts +1 -3
  291. package/src/__tests__/components/ProtocolStepEditor.test.ts +10 -11
  292. package/src/__tests__/components/ProtocolStepParameterField.test.ts +8 -7
  293. package/src/__tests__/components/ResourcePickerListInternal.test.ts +2 -22
  294. package/src/__tests__/components/RetiredStyles.test.ts +0 -18
  295. package/src/__tests__/components/SampleSelector.test.ts +30 -25
  296. package/src/__tests__/components/SecretInput.test.ts +14 -19
  297. package/src/__tests__/components/SequenceProgressBar.test.ts +12 -0
  298. package/src/__tests__/components/SettingsModal.test.ts +76 -84
  299. package/src/__tests__/components/StaleFormBanner.test.ts +48 -0
  300. package/src/__tests__/components/StepWizard.test.ts +11 -0
  301. package/src/__tests__/components/ThemeToggle.test.ts +1 -1
  302. package/src/__tests__/components/WellPlate.decoration.test.ts +20 -11
  303. package/src/__tests__/components/WellPlate.interaction.test.ts +6 -7
  304. package/src/__tests__/components/WellPlate.keyboard.test.ts +2 -2
  305. package/src/__tests__/components/deprecationTags.test.ts +90 -0
  306. package/src/__tests__/composables/analysisArtifactTarget.test.ts +1 -12
  307. package/src/__tests__/composables/autoGroup/classKey.test.ts +1 -1
  308. package/src/__tests__/composables/autoGroup/classify.test.ts +1 -1
  309. package/src/__tests__/composables/autoGroup/columns.test.ts +1 -1
  310. package/src/__tests__/composables/autoGroup/compose.test.ts +1 -1
  311. package/src/__tests__/composables/autoGroup/cooccurrence.test.ts +1 -1
  312. package/src/__tests__/composables/autoGroup/fieldEdits.test.ts +1 -1
  313. package/src/__tests__/composables/autoGroup/fingerprint.test.ts +1 -1
  314. package/src/__tests__/composables/autoGroup/golden.test.ts +1 -1
  315. package/src/__tests__/composables/autoGroup/groupByOrder.test.ts +1 -1
  316. package/src/__tests__/composables/autoGroup/groupTree.test.ts +1 -1
  317. package/src/__tests__/composables/autoGroup/integration.test.ts +1 -1
  318. package/src/__tests__/composables/autoGroup/template.test.ts +1 -1
  319. package/src/__tests__/composables/autoGroup/tokenLength.test.ts +1 -1
  320. package/src/__tests__/composables/autoGroup/tokenize.test.ts +1 -1
  321. package/src/__tests__/composables/{usePluginClient.test.ts → createClient.test.ts} +84 -222
  322. package/src/__tests__/composables/{useEventStream.test.ts → eventStream.test.ts} +6 -6
  323. package/src/__tests__/composables/experiment-utils.test.ts +13 -14
  324. package/src/__tests__/composables/useApi.test.ts +32 -21
  325. package/src/__tests__/composables/useAppExperiment.test.ts +46 -3
  326. package/src/__tests__/composables/{useAnalysisArtifacts.test.ts → useArtifacts.test.ts} +19 -27
  327. package/src/__tests__/composables/useAutoGroup.test.ts +3 -13
  328. package/src/__tests__/composables/useCalendarGrid.test.ts +4 -2
  329. package/src/__tests__/composables/useControlSchema.test.ts +41 -99
  330. package/src/__tests__/composables/useExperiment.test.ts +63 -0
  331. package/src/__tests__/composables/useExperimentSelector.test.ts +111 -0
  332. package/src/__tests__/composables/useFileBrowser.test.ts +4 -6
  333. package/src/__tests__/composables/{useJobsStatusTray.test.ts → useJobCenter.test.ts} +31 -31
  334. package/src/__tests__/composables/{useGeneratedAnalysis.test.ts → useJobsEndpoint.test.ts} +213 -139
  335. package/src/__tests__/composables/usePlatform.test.ts +102 -0
  336. package/src/__tests__/composables/usePresence.test.ts +164 -0
  337. package/src/__tests__/composables/useRackEditor.test.ts +2 -2
  338. package/src/__tests__/composables/useRealtime.test.ts +196 -0
  339. package/src/__tests__/composables/{useRequestSyncState.test.ts → useRequest.test.ts} +32 -47
  340. package/src/__tests__/composables/useVersionedSave.test.ts +194 -0
  341. package/src/__tests__/docs/extractDocsComponents.test.ts +36 -0
  342. package/src/__tests__/docs/extractDocsParsing.test.ts +2 -2
  343. package/src/__tests__/docs/extractDocsTheme.test.ts +1 -46
  344. package/src/__tests__/docs/frontendDocsCatalog.test.ts +164 -84
  345. package/src/__tests__/styles/baseControlsRules.test.ts +7 -3
  346. package/src/__tests__/styles/controlRefreshRules.test.ts +0 -6
  347. package/src/__tests__/styles/cssTokens.test.ts +2 -2
  348. package/src/__tests__/styles/darkModeRules.test.ts +0 -6
  349. package/src/__tests__/styles/menusShellRefresh.test.ts +14 -10
  350. package/src/__tests__/{composables/useChemicalFormula.test.ts → utils/formula.test.ts} +3 -5
  351. package/src/__tests__/utils/permissions.test.ts +1 -36
  352. package/src/__tests__/utils/plotlyTemplate.test.ts +20 -5
  353. package/src/__tests__/{composables/useTimeUtils.test.ts → utils/time.test.ts} +18 -8
  354. package/src/components/ActionMenu.vue +7 -13
  355. package/src/components/AdductText.story.vue +7 -0
  356. package/src/components/AdductText.vue +1 -1
  357. package/src/components/AlertBox.story.vue +27 -27
  358. package/src/components/AlertBox.vue +22 -20
  359. package/src/components/AppLayout.story.vue +33 -44
  360. package/src/components/AppLayout.vue +1 -1
  361. package/src/components/AppSidebar.story.vue +40 -101
  362. package/src/components/AppSidebar.vue +66 -147
  363. package/src/components/AppToastContainer.story.vue +1 -1
  364. package/src/components/AppTopBar.story.vue +97 -111
  365. package/src/components/AppTopBar.vue +170 -290
  366. package/src/components/ArtifactSaveDialog.story.vue +32 -9
  367. package/src/components/ArtifactSaveDialog.vue +125 -38
  368. package/src/components/ArtifactSelectorModal.story.vue +4 -4
  369. package/src/components/ArtifactSelectorModal.vue +10 -10
  370. package/src/components/AuditTrail.story.vue +6 -5
  371. package/src/components/AuditTrail.vue +5 -24
  372. package/src/components/Avatar.story.vue +2 -3
  373. package/src/components/Avatar.vue +11 -16
  374. package/src/components/BaseButton.story.vue +25 -0
  375. package/src/components/BaseButton.vue +39 -10
  376. package/src/components/BaseCheckbox.story.vue +2 -2
  377. package/src/components/{BaseToggle.story.vue → BaseCheckbox.switch.story.vue} +22 -19
  378. package/src/components/BaseCheckbox.vue +39 -7
  379. package/src/components/BaseInput.story.vue +3 -15
  380. package/src/components/{BaseTextarea.story.vue → BaseInput.textarea.story.vue} +14 -10
  381. package/src/components/BaseInput.vue +53 -26
  382. package/src/components/BaseModal.story.vue +11 -11
  383. package/src/components/BaseModal.vue +8 -8
  384. package/src/components/BasePill.story.vue +48 -28
  385. package/src/components/BasePill.vue +22 -11
  386. package/src/components/BaseRadioGroup.story.vue +4 -4
  387. package/src/components/BaseRadioGroup.vue +3 -3
  388. package/src/components/{MultiSelect.story.vue → BaseSelect.multiple.story.vue} +33 -16
  389. package/src/components/{SearchableSelect.story.vue → BaseSelect.searchable.story.vue} +4 -4
  390. package/src/components/BaseSelect.story.vue +2 -2
  391. package/src/components/BaseSelect.vue +118 -22
  392. package/src/components/BaseTabs.story.vue +20 -20
  393. package/src/components/BaseTabs.vue +17 -9
  394. package/src/components/BatchProgressList.vue +38 -28
  395. package/src/components/ChartContainer.story.vue +9 -13
  396. package/src/components/ChartContainer.vue +10 -8
  397. package/src/components/ChemicalFormula.vue +1 -2
  398. package/src/components/CollapsibleCard.vue +5 -4
  399. package/src/components/ComponentBindingRenderer.story.vue +0 -29
  400. package/src/components/ComponentBindingRenderer.vue +15 -39
  401. package/src/components/ConcentrationInput.story.vue +2 -2
  402. package/src/components/ConcentrationInput.vue +9 -9
  403. package/src/components/ConfirmDialog.story.vue +22 -22
  404. package/src/components/ConfirmDialog.vue +15 -14
  405. package/src/components/DataFrame.story.vue +28 -40
  406. package/src/components/DataFrame.vue +112 -128
  407. package/src/components/{DateTimePicker.story.vue → DatePicker.datetime.story.vue} +19 -14
  408. package/src/components/DatePicker.story.vue +2 -2
  409. package/src/components/{TimePicker.story.vue → DatePicker.time.story.vue} +15 -12
  410. package/src/components/DatePicker.vue +82 -220
  411. package/src/components/DoseCalculator.vue +13 -19
  412. package/src/components/DoseCalculatorVolumeField.vue +6 -6
  413. package/src/components/EmptyState.story.vue +17 -17
  414. package/src/components/EmptyState.vue +6 -5
  415. package/src/components/ExperimentPopover.story.vue +165 -53
  416. package/src/components/ExperimentPopover.vue +172 -190
  417. package/src/components/ExperimentSelectorModal.story.vue +43 -39
  418. package/src/components/ExperimentSelectorModal.vue +134 -122
  419. package/src/components/FilePicker.vue +1 -1
  420. package/src/components/FormBuilder.story.vue +9 -4
  421. package/src/components/FormBuilder.vue +46 -253
  422. package/src/components/FormField.story.vue +9 -50
  423. package/src/components/FormulaInput.story.vue +2 -2
  424. package/src/components/FormulaInput.vue +5 -6
  425. package/src/components/GeneratedPathInput.vue +8 -9
  426. package/src/components/GeneratedPluginView.story.vue +1 -1
  427. package/src/components/GeneratedPluginView.vue +29 -22
  428. package/src/components/GeneratedResultRenderer.vue +16 -17
  429. package/src/components/InstrumentStateBadge.vue +1 -1
  430. package/src/components/InstrumentStatusPanels.story.vue +64 -0
  431. package/src/components/InstrumentStatusPanels.vue +133 -0
  432. package/src/components/JobsStatusTray.story.vue +2 -2
  433. package/src/components/JobsStatusTray.vue +5 -20
  434. package/src/components/LoadingSpinner.story.vue +11 -11
  435. package/src/components/LoadingSpinner.vue +7 -5
  436. package/src/components/MoleculeInput.story.vue +2 -2
  437. package/src/components/MoleculeInput.vue +3 -3
  438. package/src/components/NumberInput.story.vue +55 -15
  439. package/src/components/NumberInput.vue +83 -24
  440. package/src/components/PlateEditor.story.vue +51 -43
  441. package/src/components/PlateEditor.vue +97 -128
  442. package/src/components/PlotlyChart.story.vue +1 -1
  443. package/src/components/PlotlyChart.vue +6 -4
  444. package/src/components/PluginIcon.story.vue +6 -11
  445. package/src/components/PluginIcon.vue +7 -2
  446. package/src/components/PluginWorkspaceView.controls.ts +9 -29
  447. package/src/components/PluginWorkspaceView.navigation.ts +2 -23
  448. package/src/components/PluginWorkspaceView.props.ts +17 -151
  449. package/src/components/PluginWorkspaceView.shell.ts +13 -24
  450. package/src/components/PluginWorkspaceView.story.vue +65 -63
  451. package/src/components/PluginWorkspaceView.vue +21 -78
  452. package/src/components/PresenceAvatars.story.vue +75 -0
  453. package/src/components/PresenceAvatars.vue +114 -0
  454. package/src/components/ProgressBar.story.vue +7 -7
  455. package/src/components/ProgressBar.vue +4 -3
  456. package/src/components/ProtocolStep.presentation.ts +0 -11
  457. package/src/components/ProtocolStepEditor.vue +6 -5
  458. package/src/components/ProtocolStepParameterField.vue +15 -12
  459. package/src/components/RuntimeUpdateNotice.vue +1 -1
  460. package/src/components/SampleSelector.colors.ts +1 -1
  461. package/src/components/SampleSelector.story.vue +1 -1
  462. package/src/components/SampleSelector.vue +33 -43
  463. package/src/components/SampleSelectorGroupNode.vue +1 -1
  464. package/src/components/ScheduleCalendar.vue +4 -4
  465. package/src/components/SecretInput.story.vue +1 -1
  466. package/src/components/SecretInput.vue +16 -25
  467. package/src/components/SectionCard.story.vue +2 -2
  468. package/src/components/SegmentedControl.story.vue +7 -7
  469. package/src/components/SegmentedControl.vue +5 -3
  470. package/src/components/SequenceInput.story.vue +2 -2
  471. package/src/components/SequenceInput.vue +3 -3
  472. package/src/components/SequenceProgressBar.story.vue +9 -0
  473. package/src/components/SequenceProgressBar.vue +6 -1
  474. package/src/components/SettingsModal.schema.ts +1 -1
  475. package/src/components/SettingsModal.story.vue +13 -14
  476. package/src/components/SettingsModal.vue +31 -49
  477. package/src/components/SmartGroup.types.ts +4 -6
  478. package/src/components/SmartGroupFieldRecipe.story.vue +1 -1
  479. package/src/components/SmartGroupFieldRecipe.vue +7 -5
  480. package/src/components/SmartGroupManual.story.vue +1 -1
  481. package/src/components/StaleFormBanner.story.vue +90 -0
  482. package/src/components/StaleFormBanner.vue +83 -0
  483. package/src/components/StepWizard.story.vue +33 -0
  484. package/src/components/StepWizard.vue +8 -1
  485. package/src/components/TagsInput.story.vue +7 -7
  486. package/src/components/TagsInput.vue +7 -6
  487. package/src/components/ThemeToggle.vue +5 -30
  488. package/src/components/TimeRangeInput.story.vue +1 -1
  489. package/src/components/TimeRangeInput.vue +10 -8
  490. package/src/components/VersionConflictDialog.story.vue +60 -0
  491. package/src/components/VersionConflictDialog.vue +79 -0
  492. package/src/components/WellPlate.geometry.ts +0 -38
  493. package/src/components/WellPlate.rendering.ts +0 -13
  494. package/src/components/WellPlate.story.vue +64 -51
  495. package/src/components/WellPlate.vue +68 -87
  496. package/src/components/index.ts +3 -105
  497. package/src/components/internal/AppTopBarPillNavInternal.vue +33 -2
  498. package/src/components/{AutoGroupModal.adapter.ts → internal/AutoGroupModalInternal.adapter.ts} +8 -8
  499. package/src/components/{AutoGroupModal.vue → internal/AutoGroupModalInternal.vue} +23 -23
  500. package/src/components/{BaseToggle.vue → internal/CheckboxSwitchInternal.vue} +8 -8
  501. package/src/components/internal/DataStateInternal.vue +1 -1
  502. package/src/components/internal/DatePickerDateInternal.vue +219 -0
  503. package/src/components/{DateTimePicker.vue → internal/DatePickerDateTimeInternal.vue} +12 -22
  504. package/src/components/{TimePicker.vue → internal/DatePickerTimeInternal.vue} +12 -15
  505. package/src/components/internal/FilePickerListInternal.vue +8 -14
  506. package/src/components/{FormActions.vue → internal/FormActionsInternal.vue} +2 -2
  507. package/src/components/internal/FormFieldRendererInternal.vue +1 -1
  508. package/src/components/internal/FormSchemaInternal.vue +253 -0
  509. package/src/components/{UnitInput.vue → internal/NumberUnitInputInternal.vue} +28 -28
  510. package/src/components/internal/PlateEditorBindingInternal.vue +8 -7
  511. package/src/components/internal/ResourcePickerListInternal.vue +48 -88
  512. package/src/components/internal/SelectMultipleInternal.vue +242 -0
  513. package/src/components/{SearchableSelect.vue → internal/SelectSearchableInternal.vue} +26 -11
  514. package/src/components/internal/WellEditPopupInternal.vue +4 -9
  515. package/src/components/internal/WorkspaceShellInternal.vue +3 -36
  516. package/src/components/internal/WorkspaceSidebarInternal.vue +4 -25
  517. package/src/{composables → components/internal}/autoGroup/classKey.ts +1 -1
  518. package/src/{composables → components/internal}/autoGroup/classify.ts +1 -1
  519. package/src/{composables → components/internal}/autoGroup/columns.ts +1 -1
  520. package/src/{composables → components/internal}/autoGroup/compose.ts +3 -3
  521. package/src/{composables → components/internal}/autoGroup/cooccurrence.ts +1 -1
  522. package/src/{composables → components/internal}/autoGroup/csv-shim.ts +1 -1
  523. package/src/{composables → components/internal}/autoGroup/fingerprint.ts +1 -1
  524. package/src/{composables → components/internal}/autoGroup/index.ts +1 -2
  525. package/src/{composables → components/internal}/autoGroup/template.ts +1 -1
  526. package/src/{composables → components/internal}/autoGroup/tokenLength.ts +1 -1
  527. package/src/components/internal/generatedJobsApi.ts +1 -1
  528. package/src/{composables → components/internal}/useAutoGroup.ts +3 -57
  529. package/src/composables/componentSpecs.ts +62 -0
  530. package/src/composables/controlComponentBindings.ts +7 -0
  531. package/src/composables/controlSchemaAdapters.ts +45 -7
  532. package/src/composables/controlSchemaDoseDesign.ts +16 -5
  533. package/src/composables/controlSchemaModel.ts +1 -1
  534. package/src/composables/controlSchemaTypes.ts +31 -22
  535. package/src/composables/{usePluginClient.ts → createClient.ts} +54 -190
  536. package/src/composables/{useEventStream.ts → eventStream.ts} +3 -3
  537. package/src/composables/experiment-utils.ts +1 -39
  538. package/src/composables/formBuilderRegistry.ts +6 -12
  539. package/src/composables/index.ts +17 -480
  540. package/src/composables/platformContextHelpers.ts +0 -57
  541. package/src/composables/pluginEndpointBuilder.ts +8 -10
  542. package/src/composables/pluginIdentity.ts +31 -0
  543. package/src/composables/pluginWorkspaceModel.ts +16 -107
  544. package/src/composables/useApi.ts +18 -30
  545. package/src/composables/useAppExperiment.ts +26 -5
  546. package/src/composables/{useAnalysisArtifacts.ts → useArtifacts.ts} +18 -19
  547. package/src/composables/useCalendarGrid.ts +4 -3
  548. package/src/composables/useControlSchema.ts +5 -9
  549. package/src/composables/useControlWorkspace.ts +4 -7
  550. package/src/composables/useExperiment.ts +49 -0
  551. package/src/composables/useExperimentData.ts +4 -4
  552. package/src/composables/useExperimentSelector.ts +82 -36
  553. package/src/composables/useFileBrowser.ts +5 -13
  554. package/src/composables/useFilePicker.ts +2 -2
  555. package/src/composables/useFormBuilder.ts +3 -4
  556. package/src/composables/useJobCenter.ts +324 -0
  557. package/src/composables/{useGeneratedAnalysis.ts → useJobsEndpoint.ts} +53 -67
  558. package/src/composables/useMenuKeyboard.ts +1 -1
  559. package/src/composables/usePlatform.ts +95 -0
  560. package/src/composables/usePlatformFilePickerAdapter.ts +2 -1
  561. package/src/composables/usePluginWorkspace.ts +62 -107
  562. package/src/composables/usePresence.ts +110 -0
  563. package/src/composables/useRackEditor.ts +3 -3
  564. package/src/composables/useRealtime.ts +194 -0
  565. package/src/composables/{useRequestSyncState.ts → useRequest.ts} +25 -48
  566. package/src/composables/useScheduleCalendarLayout.ts +1 -1
  567. package/src/composables/useToast.ts +14 -11
  568. package/src/composables/useVersionedSave.ts +165 -0
  569. package/src/data.ts +130 -0
  570. package/src/deprecatedRootExports.ts +226 -0
  571. package/src/generated/schema.ts +1 -0
  572. package/src/host.ts +27 -0
  573. package/src/index.ts +2 -71
  574. package/src/install.ts +15 -2
  575. package/src/instrument.ts +1 -6
  576. package/src/lab.ts +128 -0
  577. package/src/permissions.ts +0 -27
  578. package/src/runtime.ts +145 -0
  579. package/src/shell.ts +103 -0
  580. package/src/stores/auth.ts +26 -162
  581. package/src/stores/experiment.ts +27 -1
  582. package/src/styles/components/action-menu.css +0 -5
  583. package/src/styles/components/adduct-text.css +13 -5
  584. package/src/styles/components/alert-box.css +39 -40
  585. package/src/styles/components/app-layout.css +6 -5
  586. package/src/styles/components/app-page-selector.css +3 -1
  587. package/src/styles/components/app-pill-nav.css +11 -12
  588. package/src/styles/components/app-sidebar.css +9 -18
  589. package/src/styles/components/app-top-bar.css +64 -103
  590. package/src/styles/components/artifact-save-dialog.css +153 -2
  591. package/src/styles/components/audit-trail.css +16 -19
  592. package/src/styles/components/avatar.css +6 -4
  593. package/src/styles/components/batch-progress-list.css +38 -36
  594. package/src/styles/components/button.css +40 -59
  595. package/src/styles/components/calendar.css +12 -12
  596. package/src/styles/components/chart-container.css +34 -23
  597. package/src/styles/components/checkbox.css +3 -13
  598. package/src/styles/components/collapsible-card.css +28 -30
  599. package/src/styles/components/confirm-dialog.css +31 -16
  600. package/src/styles/components/dataframe.css +12 -4
  601. package/src/styles/components/date-picker.css +80 -92
  602. package/src/styles/components/datetime-picker.css +31 -43
  603. package/src/styles/components/divider.css +20 -20
  604. package/src/styles/components/dose-calculator.css +3 -3
  605. package/src/styles/components/empty-state.css +23 -18
  606. package/src/styles/components/experiment-code-badge.css +2 -0
  607. package/src/styles/components/experiment-popover.css +174 -329
  608. package/src/styles/components/experiment-selector-modal.css +75 -56
  609. package/src/styles/components/file-picker.css +4 -4
  610. package/src/styles/components/file-uploader.css +38 -49
  611. package/src/styles/components/form-builder.css +3 -3
  612. package/src/styles/components/formula-input.css +1 -1
  613. package/src/styles/components/icon-button.css +19 -25
  614. package/src/styles/components/input.css +5 -4
  615. package/src/styles/components/instrument-monitor.css +13 -15
  616. package/src/styles/components/jobs-status-tray.css +31 -25
  617. package/src/styles/components/loading-spinner.css +9 -12
  618. package/src/styles/components/modal.css +2 -4
  619. package/src/styles/components/molecule-input.css +2 -2
  620. package/src/styles/components/multi-select.css +65 -112
  621. package/src/styles/components/number-input.css +26 -19
  622. package/src/styles/components/pill.css +43 -37
  623. package/src/styles/components/plate-editor.css +0 -1
  624. package/src/styles/components/plugin-icon.css +34 -22
  625. package/src/styles/components/popover.css +2 -2
  626. package/src/styles/components/presence-avatars.css +194 -0
  627. package/src/styles/components/progress-bar.css +2 -0
  628. package/src/styles/components/protocol-step-editor.css +1 -3
  629. package/src/styles/components/resource-card.css +2 -2
  630. package/src/styles/components/resource-picker-list.css +6 -57
  631. package/src/styles/components/sample-hierarchy-tree.css +4 -4
  632. package/src/styles/components/sample-selector.css +95 -79
  633. package/src/styles/components/schedule-calendar.css +8 -8
  634. package/src/styles/components/searchable-select.css +15 -12
  635. package/src/styles/components/section-card.css +28 -47
  636. package/src/styles/components/segmented-control.css +18 -15
  637. package/src/styles/components/select.css +3 -3
  638. package/src/styles/components/sequence-input.css +2 -2
  639. package/src/styles/components/sequence-progress-bar.css +17 -8
  640. package/src/styles/components/smart-group.css +19 -24
  641. package/src/styles/components/step-wizard.css +52 -16
  642. package/src/styles/components/tabs.css +2 -2
  643. package/src/styles/components/textarea.css +4 -4
  644. package/src/styles/components/time-picker.css +36 -21
  645. package/src/styles/components/toast.css +12 -10
  646. package/src/styles/components/toggle.css +3 -13
  647. package/src/styles/components/tooltip.css +10 -9
  648. package/src/styles/components/unit-input.css +8 -7
  649. package/src/styles/components/version-conflict.css +122 -0
  650. package/src/styles/components/well-plate.css +8 -8
  651. package/src/styles/index.css +2 -11
  652. package/src/styles/variables.css +107 -602
  653. package/src/types/analysisArtifactTypes.ts +1 -1
  654. package/src/types/auth.ts +0 -10
  655. package/src/types/componentLabTypes.ts +103 -53
  656. package/src/types/componentWorkflowTypes.ts +4 -33
  657. package/src/types/components.ts +148 -114
  658. package/src/types/form-builder.ts +4 -0
  659. package/src/types/generated/platformContext.ts +60 -0
  660. package/src/types/generated-ui.ts +6 -2
  661. package/src/types/index.ts +15 -53
  662. package/src/types/instrument.ts +27 -0
  663. package/src/types/platform.ts +25 -89
  664. package/src/utils/color.ts +1 -12
  665. package/src/utils/dose.ts +335 -0
  666. package/src/utils/experimentStatus.ts +43 -0
  667. package/src/utils/formula.ts +292 -0
  668. package/src/utils/integrationHost.ts +27 -0
  669. package/src/utils/plotlyTemplate.ts +6 -0
  670. package/src/utils/pluginSecrets.ts +23 -23
  671. package/src/utils/presenceAvatars.ts +53 -0
  672. package/src/utils/records.ts +0 -5
  673. package/src/{composables/useTimeUtils.ts → utils/time.ts} +21 -21
  674. package/dist/ResourcePickerListInternal-CFAw4vTZ.js +0 -605
  675. package/dist/ResourcePickerListInternal-CFAw4vTZ.js.map +0 -1
  676. package/dist/__tests__/composables/useBioTemplatePackWorkspace.test.d.ts +0 -1
  677. package/dist/__tests__/composables/useBioTemplatePresetWorkspace.test.d.ts +0 -1
  678. package/dist/__tests__/composables/useBioTemplateWorkspace.test.d.ts +0 -1
  679. package/dist/__tests__/composables/useChemicalFormula.test.d.ts +0 -1
  680. package/dist/__tests__/composables/useEventStream.test.d.ts +0 -1
  681. package/dist/__tests__/composables/useExperimentSave.test.d.ts +0 -1
  682. package/dist/__tests__/composables/useGeneratedAnalysis.test.d.ts +0 -1
  683. package/dist/__tests__/composables/useGroupAssignment.test.d.ts +0 -1
  684. package/dist/__tests__/composables/useJobsStatusTray.test.d.ts +0 -1
  685. package/dist/__tests__/composables/useMobileSupportGate.test.d.ts +0 -1
  686. package/dist/__tests__/composables/usePlatformContext.test.d.ts +0 -1
  687. package/dist/__tests__/composables/usePluginClient.test.d.ts +0 -1
  688. package/dist/__tests__/composables/usePresenceHeartbeat.test.d.ts +0 -1
  689. package/dist/__tests__/composables/useRequestSyncState.test.d.ts +0 -1
  690. package/dist/__tests__/composables/useRuntimeAlignment.test.d.ts +0 -1
  691. package/dist/__tests__/composables/useTemplateCollection.test.d.ts +0 -1
  692. package/dist/__tests__/composables/useTimeUtils.test.d.ts +0 -1
  693. package/dist/__tests__/docs/extractDocsTemplates.test.d.ts +0 -1
  694. package/dist/__tests__/templates/templates.test.d.ts +0 -1
  695. package/dist/__tests__/templates/templatesDeprecation.test.d.ts +0 -1
  696. package/dist/__tests__/utils/lcms.test.d.ts +0 -1
  697. package/dist/__tests__/utils/rack.test.d.ts +0 -1
  698. package/dist/components/AppAvatarMenu.vue.d.ts +0 -47
  699. package/dist/components/AppTopBar.navigation.d.ts +0 -10
  700. package/dist/components/BaseTextarea.vue.d.ts +0 -31
  701. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +0 -112
  702. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +0 -86
  703. package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +0 -80
  704. package/dist/components/BioTemplateRenderer.vue.d.ts +0 -30
  705. package/dist/components/ControlWorkspaceView.vue.d.ts +0 -138
  706. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +0 -140
  707. package/dist/components/ExperimentDataViewer.vue.d.ts +0 -54
  708. package/dist/components/ExperimentTimeline.vue.d.ts +0 -70
  709. package/dist/components/FitPanel.vue.d.ts +0 -56
  710. package/dist/components/GroupAssigner.vue.d.ts +0 -26
  711. package/dist/components/IconButton.vue.d.ts +0 -30
  712. package/dist/components/MobileSupportGate.vue.d.ts +0 -32
  713. package/dist/components/ReagentEditor.vue.d.ts +0 -31
  714. package/dist/components/ReagentList.presentation.d.ts +0 -17
  715. package/dist/components/ReagentList.vue.d.ts +0 -58
  716. package/dist/components/SampleLegend.vue.d.ts +0 -33
  717. package/dist/components/SmartGroupModal.vue.d.ts +0 -29
  718. package/dist/components/index.js +0 -5
  719. package/dist/components-BOA5e7kb.js +0 -23137
  720. package/dist/components-BOA5e7kb.js.map +0 -1
  721. package/dist/composables/flattenWorkspaceBindings.d.ts +0 -61
  722. package/dist/composables/index.js +0 -7
  723. package/dist/composables/useBioTemplateComponents.d.ts +0 -34
  724. package/dist/composables/useBioTemplateControls.d.ts +0 -10
  725. package/dist/composables/useBioTemplatePackWorkspace.d.ts +0 -56
  726. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +0 -79
  727. package/dist/composables/useBioTemplateWorkspace.d.ts +0 -39
  728. package/dist/composables/useExperimentSave.d.ts +0 -69
  729. package/dist/composables/useGroupAssignment.d.ts +0 -35
  730. package/dist/composables/useJobsStatusTray.d.ts +0 -87
  731. package/dist/composables/useMobileSupportGate.d.ts +0 -14
  732. package/dist/composables/usePlatformContext.d.ts +0 -218
  733. package/dist/composables/usePresenceHeartbeat.d.ts +0 -49
  734. package/dist/composables/useRuntimeAlignment.d.ts +0 -44
  735. package/dist/composables/useTemplateCollection.d.ts +0 -62
  736. package/dist/composables/workspaceRecipes.d.ts +0 -189
  737. package/dist/composables-izSQH8Ky.js +0 -591
  738. package/dist/composables-izSQH8Ky.js.map +0 -1
  739. package/dist/experiment-D49y_Kro.js +0 -459
  740. package/dist/experiment-D49y_Kro.js.map +0 -1
  741. package/dist/experiment-utils-DTANyVKV.js.map +0 -1
  742. package/dist/install.js.map +0 -1
  743. package/dist/lcms.d.ts +0 -38
  744. package/dist/settings-BG6v13Zq.js +0 -341
  745. package/dist/settings-BG6v13Zq.js.map +0 -1
  746. package/dist/tailwind.preset.d.ts +0 -63
  747. package/dist/tailwind.preset.js +0 -71
  748. package/dist/tailwind.preset.js.map +0 -1
  749. package/dist/templates/adapters.d.ts +0 -19
  750. package/dist/templates/assayLookups.d.ts +0 -3
  751. package/dist/templates/assayMatrixAdapters.d.ts +0 -13
  752. package/dist/templates/assayMatrixBuilder.d.ts +0 -3
  753. package/dist/templates/assayNormalizers.d.ts +0 -4
  754. package/dist/templates/builderIdUtils.d.ts +0 -4
  755. package/dist/templates/builderPresetControls.d.ts +0 -11
  756. package/dist/templates/builderReadUtils.d.ts +0 -8
  757. package/dist/templates/builders.d.ts +0 -26
  758. package/dist/templates/calibrationCurveAdapters.d.ts +0 -7
  759. package/dist/templates/calibrationCurveBuilder.d.ts +0 -3
  760. package/dist/templates/calibrationNormalizers.d.ts +0 -5
  761. package/dist/templates/catalog.d.ts +0 -192
  762. package/dist/templates/componentBindingCatalog.d.ts +0 -25
  763. package/dist/templates/componentBindingHelpers.d.ts +0 -17
  764. package/dist/templates/componentBindings.d.ts +0 -97
  765. package/dist/templates/componentDoseResponseProps.d.ts +0 -3
  766. package/dist/templates/componentGenericProps.d.ts +0 -8
  767. package/dist/templates/componentPlateHelpers.d.ts +0 -11
  768. package/dist/templates/componentPlateMapProps.d.ts +0 -3
  769. package/dist/templates/componentPropsFactory.d.ts +0 -3
  770. package/dist/templates/componentQpcrPlateProps.d.ts +0 -3
  771. package/dist/templates/componentTargetResolvers.d.ts +0 -5
  772. package/dist/templates/componentTemplateProps.d.ts +0 -3
  773. package/dist/templates/controlSchemaClone.d.ts +0 -4
  774. package/dist/templates/controlSchemaConstants.d.ts +0 -10
  775. package/dist/templates/controlSchemaMerge.d.ts +0 -3
  776. package/dist/templates/controlSchemaTargets.d.ts +0 -17
  777. package/dist/templates/controlSchemaTypes.d.ts +0 -4
  778. package/dist/templates/controlSchemas.d.ts +0 -34
  779. package/dist/templates/defaultBioTemplateBuilder.d.ts +0 -3
  780. package/dist/templates/doseResponseAdapters.d.ts +0 -6
  781. package/dist/templates/doseResponseBuilder.d.ts +0 -3
  782. package/dist/templates/elisaAssayCollectionBuilder.d.ts +0 -3
  783. package/dist/templates/flowCytometryAssayCollectionBuilder.d.ts +0 -3
  784. package/dist/templates/flowCytometryPanelBuilder.d.ts +0 -3
  785. package/dist/templates/flowNormalizers.d.ts +0 -8
  786. package/dist/templates/flowPanelAdapters.d.ts +0 -7
  787. package/dist/templates/index.d.ts +0 -15
  788. package/dist/templates/index.js +0 -2
  789. package/dist/templates/instrumentRunAdapterHelpers.d.ts +0 -8
  790. package/dist/templates/instrumentRunAdapters.d.ts +0 -19
  791. package/dist/templates/instrumentRunBuilder.d.ts +0 -3
  792. package/dist/templates/lcmsBatchCollectionBuilder.d.ts +0 -3
  793. package/dist/templates/lookup.d.ts +0 -4
  794. package/dist/templates/packs.d.ts +0 -21
  795. package/dist/templates/plateMapAdapters.d.ts +0 -5
  796. package/dist/templates/plateMapBuilder.d.ts +0 -3
  797. package/dist/templates/presetControlSchemas.d.ts +0 -534
  798. package/dist/templates/presets.d.ts +0 -106
  799. package/dist/templates/protocolAdapters.d.ts +0 -9
  800. package/dist/templates/protocolNormalizers.d.ts +0 -6
  801. package/dist/templates/protocolStepsBuilder.d.ts +0 -3
  802. package/dist/templates/qpcrAdapters.d.ts +0 -9
  803. package/dist/templates/qpcrExpressionCollectionBuilder.d.ts +0 -3
  804. package/dist/templates/qpcrPlateBuilder.d.ts +0 -3
  805. package/dist/templates/reagentAdapters.d.ts +0 -9
  806. package/dist/templates/reagentListBuilder.d.ts +0 -3
  807. package/dist/templates/runNormalizers.d.ts +0 -10
  808. package/dist/templates/sampleNormalizers.d.ts +0 -4
  809. package/dist/templates/samplePrepAdapters.d.ts +0 -7
  810. package/dist/templates/samplePrepBuilder.d.ts +0 -3
  811. package/dist/templates/sampleSheetAdapters.d.ts +0 -9
  812. package/dist/templates/sampleSheetBuilder.d.ts +0 -3
  813. package/dist/templates/targetedMetabolomicsCollectionBuilder.d.ts +0 -3
  814. package/dist/templates/targetedMetabolomicsHelpers.d.ts +0 -5
  815. package/dist/templates/templateAdapterTypes.d.ts +0 -48
  816. package/dist/templates/templateControlSchemas.d.ts +0 -400
  817. package/dist/templates/templateCreateOptions.d.ts +0 -165
  818. package/dist/templates/templateEnvelopes.d.ts +0 -17
  819. package/dist/templates/templatePackCollectionBuilder.d.ts +0 -3
  820. package/dist/templates/templatePresetCollectionBuilder.d.ts +0 -20
  821. package/dist/templates/templateQpcrTypes.d.ts +0 -42
  822. package/dist/templates/templateValidators.d.ts +0 -25
  823. package/dist/templates/timeCourseAdapters.d.ts +0 -9
  824. package/dist/templates/timeCourseBuilder.d.ts +0 -3
  825. package/dist/templates/types.d.ts +0 -304
  826. package/dist/templates/wellPlateScreenCollectionBuilder.d.ts +0 -3
  827. package/dist/templates/westernBlotAssayCollectionBuilder.d.ts +0 -3
  828. package/dist/templates-B2DB0MCM.js +0 -12304
  829. package/dist/templates-B2DB0MCM.js.map +0 -1
  830. package/dist/utils/rack.d.ts +0 -63
  831. package/dist/validation-C_eH_vIg.js +0 -7999
  832. package/dist/validation-C_eH_vIg.js.map +0 -1
  833. package/src/__tests__/components/AppAvatarMenu.test.ts +0 -186
  834. package/src/__tests__/components/AppTopBar.navigation.test.ts +0 -68
  835. package/src/__tests__/components/AppTopBarPresence.test.ts +0 -47
  836. package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +0 -159
  837. package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +0 -192
  838. package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +0 -307
  839. package/src/__tests__/components/BioTemplateRenderer.test.ts +0 -96
  840. package/src/__tests__/components/ControlWorkspaceView.test.ts +0 -1100
  841. package/src/__tests__/components/DoseDesignWorkspaceView.test.ts +0 -183
  842. package/src/__tests__/components/ExperimentTimeline.test.ts +0 -108
  843. package/src/__tests__/components/FitPanel.test.ts +0 -96
  844. package/src/__tests__/components/GroupAssigner.test.ts +0 -101
  845. package/src/__tests__/components/MobileSupportGate.test.ts +0 -120
  846. package/src/__tests__/components/MultiSelect.test.ts +0 -54
  847. package/src/__tests__/components/ReagentList.presentation.test.ts +0 -73
  848. package/src/__tests__/components/ReagentList.test.ts +0 -167
  849. package/src/__tests__/components/SampleLegend.test.ts +0 -18
  850. package/src/__tests__/components/SmartGroupModal.test.ts +0 -69
  851. package/src/__tests__/components/WellPlate.sampleTypeIndicator.test.ts +0 -58
  852. package/src/__tests__/components/iconButtonIconSizing.test.ts +0 -18
  853. package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +0 -125
  854. package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +0 -199
  855. package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +0 -104
  856. package/src/__tests__/composables/useExperimentSave.test.ts +0 -542
  857. package/src/__tests__/composables/useGroupAssignment.test.ts +0 -73
  858. package/src/__tests__/composables/useMobileSupportGate.test.ts +0 -74
  859. package/src/__tests__/composables/usePlatformContext.test.ts +0 -148
  860. package/src/__tests__/composables/usePresenceHeartbeat.test.ts +0 -164
  861. package/src/__tests__/composables/useRuntimeAlignment.test.ts +0 -145
  862. package/src/__tests__/composables/useTemplateCollection.test.ts +0 -147
  863. package/src/__tests__/docs/extractDocsTemplates.test.ts +0 -53
  864. package/src/__tests__/fixtures/templates/dose-response.json +0 -81
  865. package/src/__tests__/fixtures/templates/plate-map.json +0 -54
  866. package/src/__tests__/fixtures/templates/qpcr-plate.json +0 -96
  867. package/src/__tests__/fixtures/templates/sample-sheet.json +0 -71
  868. package/src/__tests__/templates/templates.test.ts +0 -1235
  869. package/src/__tests__/templates/templatesDeprecation.test.ts +0 -78
  870. package/src/__tests__/utils/lcms.test.ts +0 -73
  871. package/src/__tests__/utils/rack.test.ts +0 -120
  872. package/src/components/AppAvatarMenu.story.vue +0 -155
  873. package/src/components/AppAvatarMenu.vue +0 -154
  874. package/src/components/AppTopBar.navigation.ts +0 -60
  875. package/src/components/AutoGroupModal.story.vue +0 -458
  876. package/src/components/BaseTextarea.vue +0 -58
  877. package/src/components/BioTemplateExperimentWorkspaceView.story.vue +0 -123
  878. package/src/components/BioTemplateExperimentWorkspaceView.vue +0 -339
  879. package/src/components/BioTemplatePackWorkspaceView.story.vue +0 -107
  880. package/src/components/BioTemplatePackWorkspaceView.vue +0 -180
  881. package/src/components/BioTemplatePresetWorkspaceView.story.vue +0 -163
  882. package/src/components/BioTemplatePresetWorkspaceView.vue +0 -403
  883. package/src/components/BioTemplateRenderer.story.vue +0 -57
  884. package/src/components/BioTemplateRenderer.vue +0 -64
  885. package/src/components/ControlWorkspaceView.story.vue +0 -347
  886. package/src/components/ControlWorkspaceView.vue +0 -370
  887. package/src/components/DoseDesignWorkspaceView.story.vue +0 -77
  888. package/src/components/DoseDesignWorkspaceView.vue +0 -243
  889. package/src/components/ExperimentDataViewer.story.vue +0 -177
  890. package/src/components/ExperimentDataViewer.vue +0 -313
  891. package/src/components/ExperimentTimeline.story.vue +0 -208
  892. package/src/components/ExperimentTimeline.vue +0 -383
  893. package/src/components/FitPanel.story.vue +0 -191
  894. package/src/components/FitPanel.vue +0 -168
  895. package/src/components/FormActions.story.vue +0 -96
  896. package/src/components/GroupAssigner.story.vue +0 -83
  897. package/src/components/GroupAssigner.vue +0 -319
  898. package/src/components/IconButton.story.vue +0 -140
  899. package/src/components/IconButton.vue +0 -69
  900. package/src/components/MobileSupportGate.story.vue +0 -52
  901. package/src/components/MobileSupportGate.vue +0 -111
  902. package/src/components/MultiSelect.vue +0 -119
  903. package/src/components/ReagentEditor.story.vue +0 -153
  904. package/src/components/ReagentEditor.vue +0 -414
  905. package/src/components/ReagentList.presentation.ts +0 -106
  906. package/src/components/ReagentList.story.vue +0 -155
  907. package/src/components/ReagentList.vue +0 -438
  908. package/src/components/SampleLegend.story.vue +0 -91
  909. package/src/components/SampleLegend.vue +0 -121
  910. package/src/components/SmartGroupModal.story.vue +0 -61
  911. package/src/components/SmartGroupModal.vue +0 -61
  912. package/src/components/StatusIndicator.story.vue +0 -99
  913. package/src/components/StatusIndicator.vue +0 -36
  914. package/src/components/UnitInput.story.vue +0 -194
  915. package/src/composables/flattenWorkspaceBindings.ts +0 -104
  916. package/src/composables/useBioTemplateComponents.ts +0 -117
  917. package/src/composables/useBioTemplateControls.ts +0 -45
  918. package/src/composables/useBioTemplatePackWorkspace.ts +0 -201
  919. package/src/composables/useBioTemplatePresetWorkspace.ts +0 -329
  920. package/src/composables/useBioTemplateWorkspace.ts +0 -120
  921. package/src/composables/useChemicalFormula.ts +0 -301
  922. package/src/composables/useDoseCalculator.ts +0 -370
  923. package/src/composables/useExperimentSave.ts +0 -348
  924. package/src/composables/useGroupAssignment.ts +0 -152
  925. package/src/composables/useJobsStatusTray.ts +0 -493
  926. package/src/composables/useMobileSupportGate.ts +0 -80
  927. package/src/composables/usePlatformContext.ts +0 -299
  928. package/src/composables/usePresenceHeartbeat.ts +0 -201
  929. package/src/composables/useRuntimeAlignment.ts +0 -184
  930. package/src/composables/useTemplateCollection.ts +0 -233
  931. package/src/composables/workspaceRecipes.ts +0 -268
  932. package/src/lcms.ts +0 -119
  933. package/src/styles/components/app-avatar-menu.css +0 -121
  934. package/src/styles/components/experiment-data-viewer.css +0 -138
  935. package/src/styles/components/experiment-timeline.css +0 -572
  936. package/src/styles/components/fit-panel.css +0 -153
  937. package/src/styles/components/group-assigner.css +0 -198
  938. package/src/styles/components/mobile-support-gate.css +0 -117
  939. package/src/styles/components/reagent-editor.css +0 -501
  940. package/src/styles/components/reagent-list.css +0 -408
  941. package/src/styles/components/sample-legend.css +0 -212
  942. package/src/styles/components/status-indicator.css +0 -66
  943. package/src/styles/components/theme-toggle.css +0 -6
  944. package/src/tailwind.preset.ts +0 -66
  945. package/src/templates/adapters.ts +0 -141
  946. package/src/templates/assayLookups.ts +0 -33
  947. package/src/templates/assayMatrixAdapters.ts +0 -85
  948. package/src/templates/assayMatrixBuilder.ts +0 -60
  949. package/src/templates/assayNormalizers.ts +0 -34
  950. package/src/templates/builderIdUtils.ts +0 -20
  951. package/src/templates/builderPresetControls.ts +0 -166
  952. package/src/templates/builderReadUtils.ts +0 -55
  953. package/src/templates/builders.ts +0 -122
  954. package/src/templates/calibrationCurveAdapters.ts +0 -62
  955. package/src/templates/calibrationCurveBuilder.ts +0 -100
  956. package/src/templates/calibrationNormalizers.ts +0 -60
  957. package/src/templates/catalog.ts +0 -249
  958. package/src/templates/componentBindingCatalog.ts +0 -90
  959. package/src/templates/componentBindingHelpers.ts +0 -93
  960. package/src/templates/componentBindings.ts +0 -279
  961. package/src/templates/componentDoseResponseProps.ts +0 -41
  962. package/src/templates/componentGenericProps.ts +0 -77
  963. package/src/templates/componentPlateHelpers.ts +0 -52
  964. package/src/templates/componentPlateMapProps.ts +0 -34
  965. package/src/templates/componentPropsFactory.ts +0 -21
  966. package/src/templates/componentQpcrPlateProps.ts +0 -27
  967. package/src/templates/componentTargetResolvers.ts +0 -67
  968. package/src/templates/componentTemplateProps.ts +0 -78
  969. package/src/templates/controlSchemaClone.ts +0 -32
  970. package/src/templates/controlSchemaConstants.ts +0 -11
  971. package/src/templates/controlSchemaMerge.ts +0 -40
  972. package/src/templates/controlSchemaTargets.ts +0 -85
  973. package/src/templates/controlSchemaTypes.ts +0 -20
  974. package/src/templates/controlSchemas.ts +0 -113
  975. package/src/templates/defaultBioTemplateBuilder.ts +0 -125
  976. package/src/templates/doseResponseAdapters.ts +0 -36
  977. package/src/templates/doseResponseBuilder.ts +0 -45
  978. package/src/templates/elisaAssayCollectionBuilder.ts +0 -63
  979. package/src/templates/flowCytometryAssayCollectionBuilder.ts +0 -42
  980. package/src/templates/flowCytometryPanelBuilder.ts +0 -54
  981. package/src/templates/flowNormalizers.ts +0 -58
  982. package/src/templates/flowPanelAdapters.ts +0 -61
  983. package/src/templates/index.ts +0 -330
  984. package/src/templates/instrumentRunAdapterHelpers.ts +0 -94
  985. package/src/templates/instrumentRunAdapters.ts +0 -174
  986. package/src/templates/instrumentRunBuilder.ts +0 -98
  987. package/src/templates/lcmsBatchCollectionBuilder.ts +0 -39
  988. package/src/templates/lookup.ts +0 -18
  989. package/src/templates/packs.ts +0 -166
  990. package/src/templates/plateMapAdapters.ts +0 -38
  991. package/src/templates/plateMapBuilder.ts +0 -44
  992. package/src/templates/presetControlSchemas.ts +0 -258
  993. package/src/templates/presets.ts +0 -183
  994. package/src/templates/protocolAdapters.ts +0 -73
  995. package/src/templates/protocolNormalizers.ts +0 -37
  996. package/src/templates/protocolStepsBuilder.ts +0 -37
  997. package/src/templates/qpcrAdapters.ts +0 -108
  998. package/src/templates/qpcrExpressionCollectionBuilder.ts +0 -34
  999. package/src/templates/qpcrPlateBuilder.ts +0 -97
  1000. package/src/templates/reagentAdapters.ts +0 -81
  1001. package/src/templates/reagentListBuilder.ts +0 -31
  1002. package/src/templates/runNormalizers.ts +0 -63
  1003. package/src/templates/sampleNormalizers.ts +0 -58
  1004. package/src/templates/samplePrepAdapters.ts +0 -66
  1005. package/src/templates/samplePrepBuilder.ts +0 -52
  1006. package/src/templates/sampleSheetAdapters.ts +0 -79
  1007. package/src/templates/sampleSheetBuilder.ts +0 -24
  1008. package/src/templates/targetedMetabolomicsCollectionBuilder.ts +0 -80
  1009. package/src/templates/targetedMetabolomicsHelpers.ts +0 -102
  1010. package/src/templates/templateAdapterTypes.ts +0 -58
  1011. package/src/templates/templateControlSchemas.ts +0 -320
  1012. package/src/templates/templateCreateOptions.ts +0 -208
  1013. package/src/templates/templateEnvelopes.ts +0 -145
  1014. package/src/templates/templatePackCollectionBuilder.ts +0 -24
  1015. package/src/templates/templatePresetCollectionBuilder.ts +0 -141
  1016. package/src/templates/templateQpcrTypes.ts +0 -48
  1017. package/src/templates/templateValidators.ts +0 -426
  1018. package/src/templates/timeCourseAdapters.ts +0 -77
  1019. package/src/templates/timeCourseBuilder.ts +0 -65
  1020. package/src/templates/types.ts +0 -493
  1021. package/src/templates/wellPlateScreenCollectionBuilder.ts +0 -37
  1022. package/src/templates/westernBlotAssayCollectionBuilder.ts +0 -69
  1023. package/src/utils/rack.ts +0 -225
  1024. /package/dist/__tests__/components/{AppAvatarMenu.test.d.ts → BaseButton.test.d.ts} +0 -0
  1025. /package/dist/__tests__/components/{AppTopBar.navigation.test.d.ts → BaseCheckbox.switch.test.d.ts} +0 -0
  1026. /package/dist/__tests__/components/{AppTopBarPresence.test.d.ts → BaseSelect.multiple.test.d.ts} +0 -0
  1027. /package/dist/__tests__/components/{BaseToggle.test.d.ts → BaseSelect.searchable.test.d.ts} +0 -0
  1028. /package/dist/__tests__/components/{BioTemplateExperimentWorkspaceView.test.d.ts → ConfirmDialog.test.d.ts} +0 -0
  1029. /package/dist/__tests__/components/{BioTemplatePackWorkspaceView.test.d.ts → DatePicker.datetime.test.d.ts} +0 -0
  1030. /package/dist/__tests__/components/{BioTemplatePresetWorkspaceView.test.d.ts → DatePicker.time.test.d.ts} +0 -0
  1031. /package/dist/__tests__/components/{BioTemplateRenderer.test.d.ts → FormSchemaInternal.test.d.ts} +0 -0
  1032. /package/dist/__tests__/components/{ControlWorkspaceView.test.d.ts → InstrumentStatusPanels.test.d.ts} +0 -0
  1033. /package/dist/__tests__/components/{DateTimePicker.test.d.ts → NumberInput.units.test.d.ts} +0 -0
  1034. /package/dist/__tests__/components/{DoseDesignWorkspaceView.test.d.ts → PresenceAvatars.test.d.ts} +0 -0
  1035. /package/dist/__tests__/components/{ExperimentTimeline.test.d.ts → StaleFormBanner.test.d.ts} +0 -0
  1036. /package/dist/__tests__/components/{FitPanel.test.d.ts → deprecationTags.test.d.ts} +0 -0
  1037. /package/dist/__tests__/{components/GroupAssigner.test.d.ts → composables/createClient.test.d.ts} +0 -0
  1038. /package/dist/__tests__/{components/MobileSupportGate.test.d.ts → composables/eventStream.test.d.ts} +0 -0
  1039. /package/dist/__tests__/{components/MultiSelect.test.d.ts → composables/useArtifacts.test.d.ts} +0 -0
  1040. /package/dist/__tests__/{components/ReagentList.presentation.test.d.ts → composables/useExperiment.test.d.ts} +0 -0
  1041. /package/dist/__tests__/{components/ReagentList.test.d.ts → composables/useJobCenter.test.d.ts} +0 -0
  1042. /package/dist/__tests__/{components/SampleLegend.test.d.ts → composables/useJobsEndpoint.test.d.ts} +0 -0
  1043. /package/dist/__tests__/{components/SearchableSelect.test.d.ts → composables/usePlatform.test.d.ts} +0 -0
  1044. /package/dist/__tests__/{components/SmartGroupModal.test.d.ts → composables/usePresence.test.d.ts} +0 -0
  1045. /package/dist/__tests__/{components/TimePicker.test.d.ts → composables/useRealtime.test.d.ts} +0 -0
  1046. /package/dist/__tests__/{components/UnitInput.test.d.ts → composables/useRequest.test.d.ts} +0 -0
  1047. /package/dist/__tests__/{components/WellPlate.sampleTypeIndicator.test.d.ts → composables/useVersionedSave.test.d.ts} +0 -0
  1048. /package/dist/__tests__/{components/iconButtonIconSizing.test.d.ts → utils/formula.test.d.ts} +0 -0
  1049. /package/dist/__tests__/{composables/useAnalysisArtifacts.test.d.ts → utils/time.test.d.ts} +0 -0
  1050. /package/dist/components/{FormActions.vue.d.ts → internal/FormActionsInternal.vue.d.ts} +0 -0
  1051. /package/dist/{composables → components/internal}/autoGroup/colors.d.ts +0 -0
  1052. /package/dist/{composables → components/internal}/autoGroup/index.d.ts +0 -0
  1053. /package/dist/{composables → components/internal}/autoGroup/tokenize.d.ts +0 -0
  1054. /package/src/{composables → components/internal}/autoGroup/colors.ts +0 -0
  1055. /package/src/{composables → components/internal}/autoGroup/tokenize.ts +0 -0
  1056. /package/src/{composables → components/internal}/autoGroup/vocab.json +0 -0
@@ -0,0 +1,3865 @@
1
+ import { a as BaseButton_default, n as BaseModal_default, o as __exportAll } from "./useDropdownState-8yWMu-7_.js";
2
+ import { o as useApi, p as MintApiError, t as useExperimentStore } from "./experiment-43OJtH7R.js";
3
+ import { A as formatRelativeTime, C as formatExperimentDate, G as BaseInput_default, I as FormField_default, S as experimentIdFromRoute, T as FileUploader_default, V as BaseSelect_default, _ as APP_EXPERIMENT_KEY, b as SORT_OPTIONS, g as pluginSecrets_exports, i as getInjectedPlatformContext, m as SecretInput_default, n as ResourcePickerListInternal_default, p as TagsInput_default, r as Skeleton_default, v as useAppExperiment, w as resolveExperimentCode, x as datePresetToISO, y as DATE_PRESET_OPTIONS, z as BaseCheckbox_default } from "./records-B6gpM_3e.js";
4
+ import { t as SegmentedControl_default } from "./SegmentedControl-BtnnjWzs.js";
5
+ import { a as normalizeSearchQuery, i as candidateMatchesSearch, n as DataStateInternal_default, o as useTextSearch, t as AlertBox_default } from "./AlertBox-B2Bgr3ei.js";
6
+ import { n as ActionMenu_default, t as useRequest } from "./useRequest-D506eHi8.js";
7
+ import { hasAllPermissions } from "./permissions.js";
8
+ import { r as useExperimentSelector, t as ExperimentSelectorModal_default } from "./ExperimentSelectorModal-65peG4Up.js";
9
+ import { t as experimentStatus_exports } from "./experimentStatus-Cqp6qNrR.js";
10
+ import { t as ANALYSIS_FILE_ARTIFACT_SCHEMA } from "./analysisArtifactTypes-Cy3D0Ipg.js";
11
+ import { t as ExperimentPopover_default } from "./ExperimentPopover-5T1fAq2L.js";
12
+ import { i as JobsStatusTray_default, r as useRealtime } from "./useRealtime-C3iy9QkU.js";
13
+ import { a as useFileImport, c as extractSampleNamesFromDesignData, i as readFileAsText, l as extractSampleOptionsFromDesignData, n as detectDelimiter, o as validateImportFile, r as parseDelimitedText, s as useExperimentData, t as useExperimentSamples, u as unwrapExperimentDesignData } from "./useExperimentSamples-DHnxKFAF.js";
14
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, getCurrentInstance, getCurrentScope, h, isRef, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onScopeDispose, openBlock, readonly, ref, renderList, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, toRaw, toValue, unref, useTemplateRef, watch, withCtx, withModifiers } from "vue";
15
+ import { CircleCheck, CircleDashed, CircleMinus, CircleX, CloudUpload, File, Loader, RefreshCw, TriangleAlert, X } from "lucide-vue-next";
16
+ //#region src/composables/useArtifacts.ts
17
+ /**
18
+ * Lists and manages analysis artifacts through the user-facing platform API.
19
+ *
20
+ * @example Recommended
21
+ * ```ts
22
+ * const artifacts = useArtifacts({ pluginId: 'LEAF' })
23
+ * await artifacts.list()
24
+ * ```
25
+ */
26
+ function useArtifacts(options = {}) {
27
+ const injectedContext = getInjectedPlatformContext();
28
+ const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl });
29
+ const experimentStore = useExperimentStore();
30
+ const currentExperimentId = computed(() => toValue(options.experimentId) ?? experimentStore.currentId ?? void 0);
31
+ const hasCurrentExperiment = computed(() => currentExperimentId.value !== void 0);
32
+ const scopePluginId = computed(() => toValue(options.pluginId) ?? null);
33
+ const request = useRequest("Analysis artifact request failed.");
34
+ const lastLoadedAt = ref(null);
35
+ const fetchedArtifacts = ref([]);
36
+ const selectedDetail = ref(null);
37
+ const loadingCount = ref(0);
38
+ const mutatingCount = ref(0);
39
+ const isLoading = computed(() => loadingCount.value > 0);
40
+ const isMutating = computed(() => mutatingCount.value > 0);
41
+ const search = ref("");
42
+ const statusFilter = ref("active");
43
+ const pluginFilter = ref(null);
44
+ const needsArchived = computed(() => Boolean(toValue(options.includeArchived)) || statusFilter.value !== "active");
45
+ const artifacts = computed(() => scopePluginId.value ? fetchedArtifacts.value.filter((artifact) => artifact.plugin_id === scopePluginId.value) : fetchedArtifacts.value);
46
+ const visibleArtifacts = computed(() => {
47
+ const query = search.value.trim().toLowerCase();
48
+ return artifacts.value.filter((artifact) => {
49
+ if (statusFilter.value !== "all" && artifact.status !== statusFilter.value) return false;
50
+ if (pluginFilter.value && artifact.plugin_id !== pluginFilter.value) return false;
51
+ if (!query) return true;
52
+ return artifact.display_name.toLowerCase().includes(query) || artifact.artifact_key.toLowerCase().includes(query);
53
+ });
54
+ });
55
+ let listToken = 0;
56
+ let detailToken = 0;
57
+ const hasRequestedList = ref(false);
58
+ function currentExperimentIdOrError(action) {
59
+ const id = currentExperimentId.value;
60
+ if (id === void 0) {
61
+ request.setError(`No current experiment is selected for ${action}`);
62
+ return;
63
+ }
64
+ return id;
65
+ }
66
+ function replaceArtifact(summary) {
67
+ const index = fetchedArtifacts.value.findIndex((artifact) => artifact.id === summary.id);
68
+ if (index >= 0) fetchedArtifacts.value.splice(index, 1, summary);
69
+ }
70
+ async function list() {
71
+ const experimentId = currentExperimentIdOrError("listing analysis artifacts");
72
+ if (experimentId === void 0) {
73
+ fetchedArtifacts.value = [];
74
+ return [];
75
+ }
76
+ const token = ++listToken;
77
+ hasRequestedList.value = true;
78
+ loadingCount.value += 1;
79
+ request.clearError();
80
+ try {
81
+ const data = await api.get(`/experiments/${experimentId}/analysis-artifacts?include_archived=${needsArchived.value}`);
82
+ if (token !== listToken) return artifacts.value;
83
+ fetchedArtifacts.value = [...data?.artifacts ?? []];
84
+ lastLoadedAt.value = /* @__PURE__ */ new Date();
85
+ return artifacts.value;
86
+ } catch (err) {
87
+ if (token !== listToken) return [];
88
+ request.setError(err);
89
+ fetchedArtifacts.value = [];
90
+ return [];
91
+ } finally {
92
+ loadingCount.value -= 1;
93
+ }
94
+ }
95
+ async function getDetail(artifactId) {
96
+ const experimentId = currentExperimentIdOrError("loading an analysis artifact");
97
+ if (experimentId === void 0) return null;
98
+ const token = ++detailToken;
99
+ loadingCount.value += 1;
100
+ request.clearError();
101
+ try {
102
+ const detail = await api.get(`/experiments/${experimentId}/analysis-artifacts/${artifactId}`);
103
+ if (token !== detailToken) return null;
104
+ selectedDetail.value = detail;
105
+ lastLoadedAt.value = /* @__PURE__ */ new Date();
106
+ return detail;
107
+ } catch (err) {
108
+ if (token !== detailToken) return null;
109
+ request.setError(err);
110
+ return null;
111
+ } finally {
112
+ loadingCount.value -= 1;
113
+ }
114
+ }
115
+ async function resolveTarget(target) {
116
+ const experimentId = currentExperimentId.value;
117
+ if (experimentId !== target.experimentId) {
118
+ request.setError(`Analysis artifact target belongs to experiment ${target.experimentId}, not ${experimentId ?? "the current selection"}.`);
119
+ return null;
120
+ }
121
+ const summary = (await list()).find((artifact) => artifact.plugin_id === target.artifactPluginId && artifact.artifact_key === target.artifactKey && artifact.status === "active");
122
+ if (!summary) {
123
+ request.setError(`Analysis artifact ${target.artifactPluginId}/${target.artifactKey} is not available.`);
124
+ return null;
125
+ }
126
+ const detail = await getDetail(summary.id);
127
+ if (detail && (detail.experiment_id !== target.experimentId || detail.plugin_id !== target.artifactPluginId || detail.artifact_key !== target.artifactKey)) {
128
+ selectedDetail.value = null;
129
+ request.setError(`Analysis artifact ${target.artifactPluginId}/${target.artifactKey} did not match the requested identity.`);
130
+ return null;
131
+ }
132
+ return detail;
133
+ }
134
+ async function mutate(action, operation) {
135
+ const experimentId = currentExperimentIdOrError(action);
136
+ if (experimentId === void 0) return null;
137
+ mutatingCount.value += 1;
138
+ request.clearError();
139
+ try {
140
+ const summary = await operation(experimentId);
141
+ replaceArtifact(summary);
142
+ return summary;
143
+ } catch (err) {
144
+ request.setError(err);
145
+ return null;
146
+ } finally {
147
+ mutatingCount.value -= 1;
148
+ }
149
+ }
150
+ async function updateMetadata(artifactId, updates) {
151
+ return mutate("updating artifact metadata", (experimentId) => api.patch(`/experiments/${experimentId}/analysis-artifacts/${artifactId}`, updates));
152
+ }
153
+ async function archive(artifactId) {
154
+ return mutate("archiving an analysis artifact", (experimentId) => api.post(`/experiments/${experimentId}/analysis-artifacts/${artifactId}/archive`));
155
+ }
156
+ async function restore(artifactId) {
157
+ return mutate("restoring an analysis artifact", (experimentId) => api.post(`/experiments/${experimentId}/analysis-artifacts/${artifactId}/restore`));
158
+ }
159
+ function hasArtifactKey(artifactKey) {
160
+ return artifacts.value.some((artifact) => artifact.artifact_key === artifactKey);
161
+ }
162
+ /** Drop experiment-scoped state and orphan every in-flight response. */
163
+ function clear() {
164
+ listToken += 1;
165
+ detailToken += 1;
166
+ fetchedArtifacts.value = [];
167
+ selectedDetail.value = null;
168
+ request.clearError();
169
+ }
170
+ watch([currentExperimentId, needsArchived], ([experimentId], [previousExperimentId]) => {
171
+ if (experimentId !== previousExperimentId) clear();
172
+ if (experimentId === void 0) return;
173
+ if (options.immediate || hasRequestedList.value) list();
174
+ }, { immediate: options.immediate });
175
+ return {
176
+ artifacts,
177
+ visibleArtifacts,
178
+ selectedDetail,
179
+ currentExperimentId,
180
+ hasCurrentExperiment,
181
+ isLoading,
182
+ isMutating,
183
+ error: request.error,
184
+ lastLoadedAt,
185
+ search,
186
+ statusFilter,
187
+ pluginFilter,
188
+ list,
189
+ refresh: list,
190
+ getDetail,
191
+ resolveTarget,
192
+ updateMetadata,
193
+ archive,
194
+ restore,
195
+ hasArtifactKey,
196
+ clear
197
+ };
198
+ }
199
+ //#endregion
200
+ //#region src/components/ArtifactSelectorModal.vue?vue&type=script&setup=true&lang.ts
201
+ var _hoisted_1$9 = { class: "mint-artifact-selector__filter-select" };
202
+ var _hoisted_2$8 = {
203
+ key: 0,
204
+ class: "mint-artifact-selector__filter-select"
205
+ };
206
+ //#endregion
207
+ //#region src/components/ArtifactSelectorModal.vue
208
+ var ArtifactSelectorModal_default = /* @__PURE__ */ defineComponent({
209
+ __name: "ArtifactSelectorModal",
210
+ props: {
211
+ open: { type: Boolean },
212
+ experimentId: { default: null },
213
+ pluginId: { default: null },
214
+ currentArtifactId: { default: null },
215
+ includeArchived: {
216
+ type: Boolean,
217
+ default: false
218
+ },
219
+ fetchDetailOnSelect: {
220
+ type: Boolean,
221
+ default: false
222
+ },
223
+ title: { default: "Select Analysis Artifact" },
224
+ size: { default: "full" }
225
+ },
226
+ emits: [
227
+ "update:open",
228
+ "select",
229
+ "selectDetail",
230
+ "archive",
231
+ "restore"
232
+ ],
233
+ setup(__props, { emit: __emit }) {
234
+ /**
235
+ * Modal for browsing and selecting an analysis artifact of an experiment, with filters and keyboard nav.
236
+ *
237
+ * @example Recommended
238
+ * ```vue
239
+ * <ArtifactSelectorModal v-model:open="open" :experiment-id="experimentId" @select="selectArtifact" />
240
+ * ```
241
+ */
242
+ const props = __props;
243
+ const emit = __emit;
244
+ const { artifacts, visibleArtifacts, isLoading, isMutating, error, search, statusFilter, pluginFilter, list, getDetail, archive, restore } = useArtifacts({
245
+ experimentId: () => props.experimentId ?? void 0,
246
+ pluginId: () => props.pluginId,
247
+ includeArchived: () => props.includeArchived
248
+ });
249
+ const injectedContext = getInjectedPlatformContext();
250
+ const canManage = computed(() => {
251
+ const user = injectedContext?.user;
252
+ return user ? hasAllPermissions(user, ["experiments.edit"]) : true;
253
+ });
254
+ const STATUS_FILTER_OPTIONS = [
255
+ {
256
+ value: "active",
257
+ label: "Active"
258
+ },
259
+ {
260
+ value: "archived",
261
+ label: "Archived"
262
+ },
263
+ {
264
+ value: "all",
265
+ label: "All statuses"
266
+ }
267
+ ];
268
+ const pluginFilterOptions = computed(() => [{
269
+ value: "",
270
+ label: "All plugins"
271
+ }, ...[...new Set(artifacts.value.map((artifact) => artifact.plugin_id))].sort().map((pluginId) => ({
272
+ value: pluginId,
273
+ label: pluginId
274
+ }))]);
275
+ const canChoosePlugin = computed(() => !props.pluginId && pluginFilterOptions.value.length > 2);
276
+ const openCount = ref(0);
277
+ function describe(artifact) {
278
+ const results = artifact.result_keys.length;
279
+ return {
280
+ code: artifact.artifact_key,
281
+ title: artifact.display_name,
282
+ meta: [
283
+ artifact.plugin_id,
284
+ `${results} result${results === 1 ? "" : "s"}`,
285
+ ...artifact.status === "archived" ? ["Archived"] : []
286
+ ],
287
+ date: formatArtifactDate(artifact)
288
+ };
289
+ }
290
+ function formatArtifactDate(artifact) {
291
+ const date = artifact.updated_at ?? artifact.created_at;
292
+ return date ? formatExperimentDate(date) : "";
293
+ }
294
+ async function handleSelect(artifact) {
295
+ if (props.fetchDetailOnSelect) {
296
+ const detail = await getDetail(artifact.id);
297
+ if (!detail) return;
298
+ emit("selectDetail", detail);
299
+ }
300
+ emit("select", artifact);
301
+ emit("update:open", false);
302
+ }
303
+ async function handleArchive(artifact) {
304
+ const archived = await archive(artifact.id);
305
+ if (archived) emit("archive", archived);
306
+ }
307
+ async function handleRestore(artifact) {
308
+ const restored = await restore(artifact.id);
309
+ if (restored) emit("restore", restored);
310
+ }
311
+ watch(() => props.open, (isOpen) => {
312
+ if (isOpen) {
313
+ openCount.value++;
314
+ statusFilter.value = props.includeArchived ? "all" : "active";
315
+ list();
316
+ }
317
+ }, { immediate: true });
318
+ return (_ctx, _cache) => {
319
+ return openBlock(), createBlock(BaseModal_default, {
320
+ open: __props.open,
321
+ title: __props.title,
322
+ size: __props.size,
323
+ "onUpdate:open": _cache[3] || (_cache[3] = ($event) => emit("update:open", $event))
324
+ }, {
325
+ default: withCtx(() => [(openBlock(), createBlock(ResourcePickerListInternal_default, {
326
+ key: openCount.value,
327
+ search: unref(search),
328
+ "onUpdate:search": _cache[2] || (_cache[2] = ($event) => isRef(search) ? search.value = $event : null),
329
+ class: "mint-artifact-selector",
330
+ label: "Analysis artifacts",
331
+ placeholder: "Search artifacts…",
332
+ items: unref(visibleArtifacts),
333
+ "item-key": (artifact) => artifact.id,
334
+ describe,
335
+ "selected-key": __props.currentArtifactId,
336
+ loading: unref(isLoading),
337
+ error: unref(error),
338
+ "empty-title": "No analysis artifacts found",
339
+ "empty-description": "Artifacts appear here once an analysis saves results for this experiment.",
340
+ onSelect: handleSelect
341
+ }, createSlots({
342
+ filters: withCtx(() => [createElementVNode("div", _hoisted_1$9, [createVNode(BaseSelect_default, {
343
+ "model-value": unref(statusFilter),
344
+ options: STATUS_FILTER_OPTIONS,
345
+ size: "sm",
346
+ "aria-label": "Filter by status",
347
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = (v) => statusFilter.value = v)
348
+ }, null, 8, ["model-value"])]), canChoosePlugin.value ? (openBlock(), createElementBlock("div", _hoisted_2$8, [createVNode(BaseSelect_default, {
349
+ "model-value": unref(pluginFilter) ?? "",
350
+ options: pluginFilterOptions.value,
351
+ size: "sm",
352
+ "aria-label": "Filter by plugin",
353
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = (v) => pluginFilter.value = String(v) || null)
354
+ }, null, 8, ["model-value", "options"])])) : createCommentVNode("", true)]),
355
+ _: 2
356
+ }, [canManage.value ? {
357
+ name: "actions",
358
+ fn: withCtx(({ item }) => [createVNode(BaseButton_default, {
359
+ variant: "ghost",
360
+ size: "sm",
361
+ class: "mint-artifact-selector__action",
362
+ disabled: unref(isMutating),
363
+ onClick: ($event) => item.status === "archived" ? handleRestore(item) : handleArchive(item)
364
+ }, {
365
+ default: withCtx(() => [createTextVNode(toDisplayString(item.status === "archived" ? "Restore" : "Archive"), 1)]),
366
+ _: 2
367
+ }, 1032, ["disabled", "onClick"])]),
368
+ key: "0"
369
+ } : void 0]), 1032, [
370
+ "search",
371
+ "items",
372
+ "item-key",
373
+ "selected-key",
374
+ "loading",
375
+ "error"
376
+ ]))]),
377
+ _: 1
378
+ }, 8, [
379
+ "open",
380
+ "title",
381
+ "size"
382
+ ]);
383
+ };
384
+ }
385
+ });
386
+ //#endregion
387
+ //#region src/components/ArtifactSaveDialog.vue?vue&type=script&setup=true&lang.ts
388
+ var _hoisted_1$8 = { class: "mint-artifact-save-dialog" };
389
+ var _hoisted_2$7 = {
390
+ key: 0,
391
+ class: "mint-artifact-save-dialog__experiment"
392
+ };
393
+ var _hoisted_3$6 = {
394
+ key: 0,
395
+ class: "mint-exp-code mint-exp-code--sm"
396
+ };
397
+ var _hoisted_4$6 = { class: "mint-artifact-save-dialog__experiment-name" };
398
+ var _hoisted_5$5 = {
399
+ key: 1,
400
+ class: "mint-artifact-save-dialog__modes",
401
+ role: "radiogroup",
402
+ "aria-label": "Save mode"
403
+ };
404
+ var _hoisted_6$5 = ["aria-checked", "onClick"];
405
+ var _hoisted_7$5 = { class: "mint-artifact-save-dialog__mode-text" };
406
+ var _hoisted_8$5 = {
407
+ key: 2,
408
+ class: "mint-artifact-save-dialog__list"
409
+ };
410
+ var _hoisted_9$5 = { class: "mint-artifact-save-dialog__row mint-artifact-save-dialog__row--selected" };
411
+ var _hoisted_10$5 = { class: "mint-artifact-save-dialog__row-name" };
412
+ var _hoisted_11$5 = {
413
+ key: 0,
414
+ class: "mint-artifact-save-dialog__row-meta"
415
+ };
416
+ var _hoisted_12$5 = {
417
+ key: 1,
418
+ class: "mint-artifact-save-dialog__immutable"
419
+ };
420
+ //#endregion
421
+ //#region src/components/ArtifactSaveDialog.vue
422
+ var ArtifactSaveDialog_default = /* @__PURE__ */ defineComponent({
423
+ __name: "ArtifactSaveDialog",
424
+ props: {
425
+ open: { type: Boolean },
426
+ saveHandler: {},
427
+ mode: { default: "file" },
428
+ fileSource: { default: "upload" },
429
+ result: {},
430
+ experimentId: { default: null },
431
+ experiment: { default: null },
432
+ existingArtifact: { default: null },
433
+ existingArtifactKeys: {},
434
+ title: {},
435
+ size: { default: "md" },
436
+ defaultArtifactKey: { default: "default" },
437
+ defaultDisplayName: {},
438
+ defaultFileKind: { default: "file" },
439
+ accept: {},
440
+ maxFileSize: {},
441
+ metadata: {}
442
+ },
443
+ emits: [
444
+ "update:open",
445
+ "saved",
446
+ "error",
447
+ "cancel",
448
+ "change-experiment"
449
+ ],
450
+ setup(__props, { emit: __emit }) {
451
+ /** Save/update dialog for analysis artifacts; delegates the write to a plugin-supplied handler. */
452
+ const props = __props;
453
+ const emit = __emit;
454
+ const canChangeExperiment = Boolean(getCurrentInstance()?.vnode.props?.onChangeExperiment);
455
+ const ARTIFACT_KEY_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
456
+ const experimentStore = useExperimentStore();
457
+ const request = useRequest("Failed to save analysis artifact.");
458
+ /** With an existing artifact the user picks: replace it, or save a separate one. */
459
+ const saveMode = ref("replace");
460
+ const isUpdate = computed(() => props.existingArtifact !== null && saveMode.value === "replace");
461
+ const SAVE_MODES = [{
462
+ value: "replace",
463
+ label: "Replace an existing result",
464
+ description: "Keeps the key; collaborators see the new version."
465
+ }, {
466
+ value: "new",
467
+ label: "Save as a new result",
468
+ description: "Adds a separate artifact next to the others."
469
+ }];
470
+ const existingResult = computed(() => props.existingArtifact?.result ?? {});
471
+ const existingIsFile = computed(() => existingResult.value.schema_version === ANALYSIS_FILE_ARTIFACT_SCHEMA);
472
+ const existingFileKind = computed(() => typeof existingResult.value.kind === "string" ? existingResult.value.kind : void 0);
473
+ const existingFilename = computed(() => typeof existingResult.value.filename === "string" ? existingResult.value.filename : void 0);
474
+ const existingMetadata = computed(() => {
475
+ const value = existingResult.value.metadata;
476
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
477
+ });
478
+ const activeKind = ref("file");
479
+ const keyInput = ref("");
480
+ const displayNameInput = ref("");
481
+ const noteInput = ref("");
482
+ const selectedFile = shallowRef(null);
483
+ let initialNote = "";
484
+ function resetForm() {
485
+ saveMode.value = "replace";
486
+ syncFields();
487
+ }
488
+ /** Fills the fields for the current save mode; a mode switch calls only this. */
489
+ function syncFields() {
490
+ const existing = isUpdate.value ? props.existingArtifact : null;
491
+ activeKind.value = existing ? existingIsFile.value ? "file" : "json" : props.mode === "json" ? "json" : "file";
492
+ keyInput.value = existing?.artifact_key ?? props.defaultArtifactKey;
493
+ displayNameInput.value = existing?.display_name ?? props.defaultDisplayName ?? "";
494
+ initialNote = existing?.note ?? "";
495
+ noteInput.value = initialNote;
496
+ selectedFile.value = null;
497
+ request.clearError();
498
+ }
499
+ resetForm();
500
+ watch(() => props.open, (open) => {
501
+ if (open) resetForm();
502
+ });
503
+ watch(saveMode, syncFields);
504
+ watch(() => props.existingArtifact ? `${props.existingArtifact.id}:${props.existingArtifact.artifact_key}` : null, resetForm);
505
+ const resolvedTitle = computed(() => props.title ?? "Save to experiment");
506
+ const stripExperiment = computed(() => {
507
+ if (props.experiment) return props.experiment;
508
+ const current = experimentStore.current;
509
+ return current && current.id === resolveExperimentId() ? current : null;
510
+ });
511
+ const existingUpdated = computed(() => {
512
+ const date = props.existingArtifact?.updated_at ?? props.existingArtifact?.created_at;
513
+ return date ? formatExperimentDate(date) : "";
514
+ });
515
+ const showKindToggle = computed(() => !isUpdate.value && props.mode === "both");
516
+ const showFileUploader = computed(() => activeKind.value === "file" && props.fileSource === "upload");
517
+ const showNoteField = computed(() => !isUpdate.value || existingIsFile.value);
518
+ const effectiveKey = computed(() => {
519
+ if (isUpdate.value) return props.existingArtifact?.artifact_key ?? "";
520
+ return keyInput.value.trim() || props.defaultArtifactKey;
521
+ });
522
+ const keyValid = computed(() => ARTIFACT_KEY_PATTERN.test(effectiveKey.value));
523
+ const keyExists = computed(() => {
524
+ if (isUpdate.value) return false;
525
+ return [...props.existingArtifactKeys ?? [], props.existingArtifact?.artifact_key].includes(effectiveKey.value);
526
+ });
527
+ const keyBlocked = computed(() => keyExists.value && (activeKind.value === "file" || props.existingArtifact !== null && saveMode.value === "new"));
528
+ const keyError = computed(() => {
529
+ if (!keyValid.value) return "Use lowercase letters, digits, dots, dashes, or underscores.";
530
+ if (keyBlocked.value) return "This key is already used. Choose another, or replace the existing artifact.";
531
+ });
532
+ const canSubmit = computed(() => {
533
+ if (request.isLoading.value || !keyValid.value || keyBlocked.value) return false;
534
+ if (activeKind.value === "file") return props.fileSource === "handler" || selectedFile.value !== null;
535
+ return props.result !== void 0;
536
+ });
537
+ function resolveExperimentId() {
538
+ return props.experimentId ?? experimentStore.currentId ?? void 0;
539
+ }
540
+ /** Set-only note for creates and JSON upserts: an empty field just sends nothing. */
541
+ function createNotePayload() {
542
+ const value = noteInput.value.trim();
543
+ return showNoteField.value && value ? { note: value } : {};
544
+ }
545
+ /**
546
+ * Three-state note, honoured only by update_analysis_file_artifact:
547
+ * omitted = preserve, null = clear, string = set.
548
+ */
549
+ function updateNotePayload() {
550
+ if (noteInput.value === initialNote) return {};
551
+ const value = noteInput.value.trim();
552
+ return value ? { note: value } : { note: null };
553
+ }
554
+ function buildPayload(experimentId) {
555
+ const base = {
556
+ experimentId,
557
+ artifactKey: effectiveKey.value
558
+ };
559
+ if (activeKind.value === "json") {
560
+ if (props.result === void 0) return null;
561
+ const displayName = isUpdate.value ? props.existingArtifact?.display_name : displayNameInput.value.trim() || void 0;
562
+ return {
563
+ kind: "json",
564
+ mode: isUpdate.value || keyExists.value ? "upsert" : "create",
565
+ ...base,
566
+ ...createNotePayload(),
567
+ ...displayName ? { displayName } : {},
568
+ result: props.result
569
+ };
570
+ }
571
+ if (isUpdate.value) {
572
+ const metadata = props.metadata ?? existingMetadata.value;
573
+ if (props.fileSource === "handler") return {
574
+ kind: "file",
575
+ source: "handler",
576
+ mode: "update",
577
+ ...base,
578
+ ...updateNotePayload(),
579
+ fileKind: existingFileKind.value ?? props.defaultFileKind,
580
+ ...metadata ? { metadata } : {}
581
+ };
582
+ const file = selectedFile.value;
583
+ if (!file) return null;
584
+ return {
585
+ kind: "file",
586
+ mode: "update",
587
+ ...base,
588
+ ...updateNotePayload(),
589
+ file,
590
+ fileKind: existingFileKind.value ?? props.defaultFileKind,
591
+ ...metadata ? { metadata } : {},
592
+ ...file.type ? { contentType: file.type } : {}
593
+ };
594
+ }
595
+ const displayName = displayNameInput.value.trim();
596
+ if (props.fileSource === "handler") return {
597
+ kind: "file",
598
+ source: "handler",
599
+ mode: "create",
600
+ ...base,
601
+ ...createNotePayload(),
602
+ fileKind: props.defaultFileKind,
603
+ ...displayName ? { displayName } : {},
604
+ ...props.metadata ? { metadata: props.metadata } : {}
605
+ };
606
+ const file = selectedFile.value;
607
+ if (!file) return null;
608
+ return {
609
+ kind: "file",
610
+ mode: "create",
611
+ ...base,
612
+ ...createNotePayload(),
613
+ file,
614
+ fileKind: props.defaultFileKind,
615
+ filename: file.name,
616
+ ...displayName ? { displayName } : {},
617
+ ...props.metadata ? { metadata: props.metadata } : {},
618
+ ...file.type ? { contentType: file.type } : {}
619
+ };
620
+ }
621
+ function runSaveHandler(payload) {
622
+ if (props.fileSource === "handler") {
623
+ if (payload.kind === "file" && !("source" in payload)) throw new Error("ArtifactSaveDialog built an upload payload for a handler-owned file.");
624
+ return props.saveHandler(payload);
625
+ }
626
+ if (payload.kind === "file" && "source" in payload) throw new Error("ArtifactSaveDialog built a handler-owned payload for an uploaded file.");
627
+ return props.saveHandler(payload);
628
+ }
629
+ function handleFileUpload(files) {
630
+ selectedFile.value = files[0] ?? null;
631
+ }
632
+ function handleFileError(message) {
633
+ request.setError(message);
634
+ }
635
+ async function handleSubmit() {
636
+ if (!canSubmit.value) return;
637
+ const experimentId = resolveExperimentId();
638
+ if (experimentId === void 0) {
639
+ const message = request.setError("No experiment is available for saving an artifact.");
640
+ emit("error", message);
641
+ return;
642
+ }
643
+ const payload = buildPayload(experimentId);
644
+ if (!payload) return;
645
+ try {
646
+ const result = await request.run(() => runSaveHandler(payload), {
647
+ kind: "save",
648
+ errorMessage: "Failed to save analysis artifact."
649
+ });
650
+ emit("saved", {
651
+ ...result ?? {},
652
+ payload
653
+ });
654
+ emit("update:open", false);
655
+ resetForm();
656
+ } catch {
657
+ emit("error", request.error.value ?? "Failed to save analysis artifact.");
658
+ }
659
+ }
660
+ function handleCancel() {
661
+ emit("cancel");
662
+ emit("update:open", false);
663
+ }
664
+ return (_ctx, _cache) => {
665
+ return openBlock(), createBlock(BaseModal_default, {
666
+ open: __props.open,
667
+ title: resolvedTitle.value,
668
+ size: __props.size,
669
+ closable: !unref(request).isLoading.value,
670
+ "onUpdate:open": _cache[5] || (_cache[5] = ($event) => emit("update:open", $event)),
671
+ onClose: _cache[6] || (_cache[6] = ($event) => emit("cancel"))
672
+ }, {
673
+ footer: withCtx(() => [createVNode(BaseButton_default, {
674
+ variant: "secondary",
675
+ disabled: unref(request).isLoading.value,
676
+ onClick: handleCancel
677
+ }, {
678
+ default: withCtx(() => [..._cache[11] || (_cache[11] = [createTextVNode(" Cancel ", -1)])]),
679
+ _: 1
680
+ }, 8, ["disabled"]), createVNode(BaseButton_default, {
681
+ class: "mint-artifact-save-dialog__submit",
682
+ icon: unref(CloudUpload),
683
+ loading: unref(request).isLoading.value,
684
+ disabled: !canSubmit.value,
685
+ onClick: handleSubmit
686
+ }, {
687
+ default: withCtx(() => [createTextVNode(toDisplayString(isUpdate.value ? "Replace result" : "Save result"), 1)]),
688
+ _: 1
689
+ }, 8, [
690
+ "icon",
691
+ "loading",
692
+ "disabled"
693
+ ])]),
694
+ default: withCtx(() => [createElementVNode("div", _hoisted_1$8, [
695
+ stripExperiment.value ? (openBlock(), createElementBlock("div", _hoisted_2$7, [
696
+ stripExperiment.value.experiment_code ? (openBlock(), createElementBlock("span", _hoisted_3$6, toDisplayString(stripExperiment.value.experiment_code), 1)) : createCommentVNode("", true),
697
+ createElementVNode("span", _hoisted_4$6, toDisplayString(stripExperiment.value.name), 1),
698
+ unref(canChangeExperiment) ? (openBlock(), createElementBlock("button", {
699
+ key: 1,
700
+ type: "button",
701
+ class: "mint-artifact-save-dialog__change",
702
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("change-experiment"))
703
+ }, " Change ")) : createCommentVNode("", true)
704
+ ])) : createCommentVNode("", true),
705
+ __props.existingArtifact ? (openBlock(), createElementBlock("div", _hoisted_5$5, [(openBlock(), createElementBlock(Fragment, null, renderList(SAVE_MODES, (option) => {
706
+ return createElementVNode("button", {
707
+ key: option.value,
708
+ type: "button",
709
+ role: "radio",
710
+ "aria-checked": saveMode.value === option.value,
711
+ class: "mint-artifact-save-dialog__mode",
712
+ onClick: ($event) => saveMode.value = option.value
713
+ }, [_cache[7] || (_cache[7] = createElementVNode("span", {
714
+ class: "mint-artifact-save-dialog__mode-dot",
715
+ "aria-hidden": "true"
716
+ }, null, -1)), createElementVNode("span", _hoisted_7$5, [createElementVNode("b", null, toDisplayString(option.label), 1), createElementVNode("span", null, toDisplayString(option.description), 1)])], 8, _hoisted_6$5);
717
+ }), 64))])) : createCommentVNode("", true),
718
+ isUpdate.value && __props.existingArtifact ? (openBlock(), createElementBlock("div", _hoisted_8$5, [createElementVNode("div", _hoisted_9$5, [
719
+ createVNode(unref(File), {
720
+ class: "mint-artifact-save-dialog__row-icon",
721
+ size: 15,
722
+ "aria-hidden": "true"
723
+ }),
724
+ createElementVNode("span", _hoisted_10$5, [createTextVNode(toDisplayString(__props.existingArtifact.display_name) + " ", 1), createElementVNode("span", null, toDisplayString(__props.existingArtifact.artifact_key), 1)]),
725
+ existingUpdated.value ? (openBlock(), createElementBlock("span", _hoisted_11$5, toDisplayString(existingUpdated.value), 1)) : createCommentVNode("", true)
726
+ ])])) : createCommentVNode("", true),
727
+ showKindToggle.value ? (openBlock(), createBlock(SegmentedControl_default, {
728
+ key: 3,
729
+ modelValue: activeKind.value,
730
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => activeKind.value = $event),
731
+ class: "mint-artifact-save-dialog__kind-toggle",
732
+ options: [{
733
+ value: "file",
734
+ label: "File"
735
+ }, {
736
+ value: "json",
737
+ label: "JSON"
738
+ }],
739
+ "full-width": ""
740
+ }, null, 8, ["modelValue"])) : createCommentVNode("", true),
741
+ keyExists.value && !keyBlocked.value ? (openBlock(), createBlock(AlertBox_default, {
742
+ key: 4,
743
+ tone: "warning",
744
+ class: "mint-artifact-save-dialog__warning"
745
+ }, {
746
+ default: withCtx(() => [createTextVNode(" An artifact with key \"" + toDisplayString(effectiveKey.value) + "\" already exists. Its results will be replaced; its name and note are kept unless you set new ones. ", 1)]),
747
+ _: 1
748
+ })) : createCommentVNode("", true),
749
+ createVNode(FormField_default, {
750
+ label: "Artifact key",
751
+ class: "mint-artifact-save-dialog__key",
752
+ error: keyError.value,
753
+ hint: isUpdate.value ? "Keys are fixed once an artifact exists." : void 0
754
+ }, {
755
+ default: withCtx(() => [createVNode(BaseInput_default, {
756
+ modelValue: keyInput.value,
757
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => keyInput.value = $event),
758
+ readonly: isUpdate.value,
759
+ invalid: Boolean(keyError.value),
760
+ placeholder: __props.defaultArtifactKey
761
+ }, null, 8, [
762
+ "modelValue",
763
+ "readonly",
764
+ "invalid",
765
+ "placeholder"
766
+ ])]),
767
+ _: 1
768
+ }, 8, ["error", "hint"]),
769
+ !isUpdate.value ? (openBlock(), createBlock(FormField_default, {
770
+ key: 5,
771
+ label: "Display name",
772
+ class: "mint-artifact-save-dialog__name"
773
+ }, {
774
+ default: withCtx(() => [createVNode(BaseInput_default, {
775
+ modelValue: displayNameInput.value,
776
+ "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => displayNameInput.value = $event),
777
+ placeholder: "Shown in artifact lists"
778
+ }, null, 8, ["modelValue"])]),
779
+ _: 1
780
+ })) : createCommentVNode("", true),
781
+ showNoteField.value ? (openBlock(), createBlock(FormField_default, {
782
+ key: 6,
783
+ label: "Note",
784
+ class: "mint-artifact-save-dialog__note"
785
+ }, {
786
+ default: withCtx(() => [createVNode(BaseInput_default, {
787
+ type: "textarea",
788
+ modelValue: noteInput.value,
789
+ "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => noteInput.value = $event),
790
+ placeholder: "Optional context for this artifact"
791
+ }, null, 8, ["modelValue"])]),
792
+ _: 1
793
+ })) : createCommentVNode("", true),
794
+ activeKind.value === "file" ? (openBlock(), createElementBlock(Fragment, { key: 7 }, [showFileUploader.value ? (openBlock(), createBlock(FileUploader_default, {
795
+ key: 0,
796
+ class: "mint-artifact-save-dialog__file",
797
+ accept: __props.accept,
798
+ "max-size": __props.maxFileSize,
799
+ multiple: false,
800
+ onUpload: handleFileUpload,
801
+ onError: handleFileError
802
+ }, null, 8, ["accept", "max-size"])) : createCommentVNode("", true), isUpdate.value && existingFilename.value ? (openBlock(), createElementBlock("p", _hoisted_12$5, [
803
+ _cache[8] || (_cache[8] = createTextVNode(" Replaces the content of ", -1)),
804
+ createElementVNode("code", null, toDisplayString(existingFilename.value), 1),
805
+ _cache[9] || (_cache[9] = createTextVNode(" (kind ", -1)),
806
+ createElementVNode("code", null, toDisplayString(existingFileKind.value ?? __props.defaultFileKind), 1),
807
+ _cache[10] || (_cache[10] = createTextVNode("); filename and kind stay unchanged. ", -1))
808
+ ])) : createCommentVNode("", true)], 64)) : createCommentVNode("", true),
809
+ unref(request).error.value ? (openBlock(), createBlock(AlertBox_default, {
810
+ key: 8,
811
+ tone: "error",
812
+ class: "mint-artifact-save-dialog__error",
813
+ role: "alert"
814
+ }, {
815
+ default: withCtx(() => [createTextVNode(toDisplayString(unref(request).error.value), 1)]),
816
+ _: 1
817
+ })) : createCommentVNode("", true)
818
+ ])]),
819
+ _: 1
820
+ }, 8, [
821
+ "open",
822
+ "title",
823
+ "size",
824
+ "closable"
825
+ ]);
826
+ };
827
+ }
828
+ });
829
+ //#endregion
830
+ //#region src/components/ExperimentCodeBadge.vue?vue&type=script&setup=true&lang.ts
831
+ var _hoisted_1$7 = [
832
+ "role",
833
+ "tabindex",
834
+ "title"
835
+ ];
836
+ //#endregion
837
+ //#region src/components/ExperimentCodeBadge.vue
838
+ var ExperimentCodeBadge_default = /* @__PURE__ */ defineComponent({
839
+ __name: "ExperimentCodeBadge",
840
+ props: {
841
+ code: {},
842
+ size: { default: "md" },
843
+ copyable: {
844
+ type: Boolean,
845
+ default: true
846
+ }
847
+ },
848
+ emits: ["copy"],
849
+ setup(__props, { emit: __emit }) {
850
+ /** Inline badge that displays an experiment code and copies it to the clipboard on click. */
851
+ const props = __props;
852
+ const emit = __emit;
853
+ const copied = ref(false);
854
+ let copyTimeout = null;
855
+ async function handleCopy() {
856
+ if (!props.copyable) return;
857
+ try {
858
+ await navigator.clipboard.writeText(props.code);
859
+ copied.value = true;
860
+ emit("copy", props.code);
861
+ if (copyTimeout) clearTimeout(copyTimeout);
862
+ copyTimeout = setTimeout(() => {
863
+ copied.value = false;
864
+ }, 1500);
865
+ } catch {}
866
+ }
867
+ function handleKeydown(event) {
868
+ if (!props.copyable) return;
869
+ if (event.key === "Enter" || event.key === " ") {
870
+ event.preventDefault();
871
+ handleCopy();
872
+ }
873
+ }
874
+ return (_ctx, _cache) => {
875
+ return openBlock(), createElementBlock("span", {
876
+ class: normalizeClass([
877
+ "mint-exp-code",
878
+ `mint-exp-code--${__props.size}`,
879
+ {
880
+ "mint-exp-code--copyable": __props.copyable,
881
+ "mint-exp-code--copied": copied.value
882
+ }
883
+ ]),
884
+ role: __props.copyable ? "button" : void 0,
885
+ tabindex: __props.copyable ? 0 : void 0,
886
+ title: __props.copyable ? copied.value ? "Copied!" : "Click to copy" : void 0,
887
+ onClick: handleCopy,
888
+ onKeydown: handleKeydown
889
+ }, toDisplayString(copied.value ? "Copied!" : __props.code), 43, _hoisted_1$7);
890
+ };
891
+ }
892
+ });
893
+ //#endregion
894
+ //#region src/components/internal/filePickerPresentation.ts
895
+ var FILE_PICKER_ICONS = {
896
+ folder: "M4 7h6l2 2h8v10H4z",
897
+ file: "M14 3v5h5M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z",
898
+ lock: "M7 11V8a5 5 0 0 1 10 0v3M5 11h14v10H5z"
899
+ };
900
+ function formatBytes(bytes) {
901
+ if (bytes === null) return "—";
902
+ const units = [
903
+ "B",
904
+ "KB",
905
+ "MB",
906
+ "GB",
907
+ "TB"
908
+ ];
909
+ let value = bytes;
910
+ let unit = 0;
911
+ while (value >= 1024 && unit < units.length - 1) {
912
+ value /= 1024;
913
+ unit += 1;
914
+ }
915
+ return `${value >= 100 || unit === 0 ? Math.round(value) : Number(value.toFixed(1))} ${units[unit]}`;
916
+ }
917
+ var pad = (value) => String(value).padStart(2, "0");
918
+ /** Compact local timestamp for a fixed-width mono column: `MM-DD HH:mm`, or `YYYY-MM-DD` outside this year. */
919
+ function formatModified(value, now = /* @__PURE__ */ new Date()) {
920
+ const date = value ? new Date(value) : null;
921
+ if (!date || Number.isNaN(date.getTime())) return "";
922
+ const day = `${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
923
+ return date.getFullYear() === now.getFullYear() ? `${day} ${pad(date.getHours())}:${pad(date.getMinutes())}` : `${date.getFullYear()}-${day}`;
924
+ }
925
+ //#endregion
926
+ //#region src/composables/filePicker/validation.ts
927
+ function normalizePickerFormat(format) {
928
+ return format.trim().replace(/^\./, "").toLowerCase();
929
+ }
930
+ function pickerExclusion(node, capability, skipBlanks = false) {
931
+ const base = {
932
+ path: node.path,
933
+ name: node.name
934
+ };
935
+ if (node.unavailableReason) return {
936
+ ...base,
937
+ reason: "unreadable"
938
+ };
939
+ if (skipBlanks && capability?.isBlank?.(node)) return {
940
+ ...base,
941
+ reason: "blank"
942
+ };
943
+ if (node.kind === "folder") return null;
944
+ const formats = capability?.formats.map(normalizePickerFormat) ?? [];
945
+ if (formats.length && !formats.some((format) => node.name.toLowerCase().endsWith(`.${format}`))) return {
946
+ ...base,
947
+ reason: "unsupported"
948
+ };
949
+ if (capability?.maxBytes != null && node.bytes != null && node.bytes > capability.maxBytes) return {
950
+ ...base,
951
+ reason: "tooLarge"
952
+ };
953
+ return null;
954
+ }
955
+ function pickerReason(excluded, node) {
956
+ if (!excluded) return "";
957
+ return {
958
+ unsupported: "This format is not supported by this source.",
959
+ tooLarge: "This file exceeds the size limit for this source.",
960
+ blank: "Skipped by the blank-file rule.",
961
+ unreadable: node?.unavailableReason || "This item cannot be read.",
962
+ deselected: "Removed from the selection.",
963
+ forbidden: "This item is not permitted by the system filter."
964
+ }[excluded.reason];
965
+ }
966
+ function pickerFormat(name) {
967
+ const compound = /\.(mzml|mzxml)\.gz$/i.exec(name);
968
+ return compound ? `${compound[1]}.gz` : name.includes(".") ? name.split(".").pop() : "";
969
+ }
970
+ //#endregion
971
+ //#region src/components/internal/FilePickerListInternal.vue?vue&type=script&setup=true&lang.ts
972
+ var _hoisted_1$6 = [
973
+ "role",
974
+ "aria-multiselectable",
975
+ "aria-busy"
976
+ ];
977
+ var _hoisted_2$6 = {
978
+ key: 0,
979
+ class: "mint-picker__truncated",
980
+ role: "status"
981
+ };
982
+ var _hoisted_3$5 = {
983
+ key: 1,
984
+ class: "mint-picker__skeleton"
985
+ };
986
+ var _hoisted_4$5 = {
987
+ key: 2,
988
+ role: "alert",
989
+ class: "mint-picker__empty"
990
+ };
991
+ var _hoisted_5$4 = {
992
+ key: 3,
993
+ class: "mint-picker__empty",
994
+ role: "status"
995
+ };
996
+ var _hoisted_6$4 = [
997
+ "role",
998
+ "data-path",
999
+ "aria-level",
1000
+ "aria-selected",
1001
+ "aria-disabled",
1002
+ "aria-expanded",
1003
+ "tabindex",
1004
+ "title",
1005
+ "onClick",
1006
+ "onDblclick",
1007
+ "onKeydown"
1008
+ ];
1009
+ var _hoisted_7$4 = { class: "mint-picker__cell mint-picker__cell--check" };
1010
+ var _hoisted_8$4 = {
1011
+ key: 0,
1012
+ class: "mint-picker__lock",
1013
+ viewBox: "0 0 24 24",
1014
+ fill: "none",
1015
+ stroke: "currentColor",
1016
+ "stroke-width": "1.8",
1017
+ "aria-hidden": "true"
1018
+ };
1019
+ var _hoisted_9$4 = ["d"];
1020
+ var _hoisted_10$4 = { class: "mint-picker__cell mint-picker__cell--name" };
1021
+ var _hoisted_11$4 = {
1022
+ key: 1,
1023
+ class: "mint-picker__disclosure"
1024
+ };
1025
+ var _hoisted_12$4 = {
1026
+ class: "mint-picker__node-icon",
1027
+ viewBox: "0 0 24 24",
1028
+ fill: "none",
1029
+ stroke: "currentColor",
1030
+ "stroke-width": "1.7",
1031
+ "aria-hidden": "true"
1032
+ };
1033
+ var _hoisted_13$4 = ["d"];
1034
+ var _hoisted_14$4 = { class: "mint-picker__name" };
1035
+ var _hoisted_15$4 = { key: 0 };
1036
+ var _hoisted_16$2 = {
1037
+ key: 1,
1038
+ class: "mint-picker__reason"
1039
+ };
1040
+ var _hoisted_17$2 = {
1041
+ key: 2,
1042
+ class: "mint-picker__parent-name"
1043
+ };
1044
+ var _hoisted_18$2 = { class: "mint-picker__cell mint-picker__cell--time" };
1045
+ var _hoisted_19$2 = {
1046
+ key: 0,
1047
+ class: "mint-picker__branch-error",
1048
+ role: "alert"
1049
+ };
1050
+ //#endregion
1051
+ //#region src/components/internal/FilePickerListInternal.vue
1052
+ var FilePickerListInternal_default = /* @__PURE__ */ defineComponent({
1053
+ __name: "FilePickerListInternal",
1054
+ props: {
1055
+ emptyTitle: {},
1056
+ view: {},
1057
+ rows: {},
1058
+ loading: { type: Boolean },
1059
+ error: {},
1060
+ query: {},
1061
+ truncated: { type: Boolean },
1062
+ mode: {},
1063
+ selected: { type: Function },
1064
+ selectable: { type: Function },
1065
+ exclusion: { type: Function },
1066
+ progress: { type: Function },
1067
+ focusPath: {}
1068
+ },
1069
+ emits: [
1070
+ "select",
1071
+ "enter",
1072
+ "expand",
1073
+ "up",
1074
+ "retry",
1075
+ "parent"
1076
+ ],
1077
+ setup(__props, { emit: __emit }) {
1078
+ const DisclosureTriangle = () => h("svg", {
1079
+ viewBox: "0 0 16 16",
1080
+ fill: "currentColor"
1081
+ }, [h("path", { d: "m6 3 5 5-5 5z" })]);
1082
+ const props = __props;
1083
+ const emit = __emit;
1084
+ const SHORT_REASON = {
1085
+ unsupported: "Unsupported format",
1086
+ tooLarge: "Over the size limit",
1087
+ blank: "Blank",
1088
+ unreadable: "No read permission",
1089
+ forbidden: "No read permission",
1090
+ deselected: ""
1091
+ };
1092
+ const tree = computed(() => props.view === "tree");
1093
+ const multi = computed(() => props.mode === "multi-file" || props.mode === "folder+files");
1094
+ const list = ref(null);
1095
+ function reason(node) {
1096
+ return pickerReason(props.exclusion(node), node);
1097
+ }
1098
+ function shortReason(node) {
1099
+ const excluded = props.exclusion(node);
1100
+ if (!excluded) return "";
1101
+ return excluded.reason === "unreadable" && node.unavailableReason || SHORT_REASON[excluded.reason];
1102
+ }
1103
+ function locked(node) {
1104
+ const excluded = props.exclusion(node)?.reason;
1105
+ return excluded === "unreadable" || excluded === "forbidden";
1106
+ }
1107
+ function partial(node) {
1108
+ const progress = props.progress(node);
1109
+ return node.kind === "folder" && props.selected(node) && !!progress && progress.split(" / ")[0] !== String(node.fileCount);
1110
+ }
1111
+ function size(node) {
1112
+ if (node.kind === "folder" && props.selected(node) && props.progress(node)) return props.progress(node);
1113
+ return node.bytes == null ? "" : formatBytes(node.bytes);
1114
+ }
1115
+ const focusablePath = computed(() => {
1116
+ const available = props.rows.filter((row) => !reason(row.node));
1117
+ return available.find((row) => row.node.path === props.focusPath)?.node.path ?? available[0]?.node.path;
1118
+ });
1119
+ function focusRow(path) {
1120
+ nextTick(() => {
1121
+ const target = [...list.value?.querySelectorAll("[data-path]") ?? []].find((el) => el.dataset.path === path);
1122
+ target?.focus();
1123
+ target?.scrollIntoView({ block: "nearest" });
1124
+ });
1125
+ }
1126
+ function keydown(event, row) {
1127
+ if (reason(row.node)) return;
1128
+ const available = props.rows.filter((item) => !reason(item.node));
1129
+ const index = available.findIndex((item) => item.node.path === row.node.path);
1130
+ if ([
1131
+ "ArrowUp",
1132
+ "ArrowDown",
1133
+ "Home",
1134
+ "End"
1135
+ ].includes(event.key)) {
1136
+ event.preventDefault();
1137
+ const target = event.key === "Home" ? available[0] : event.key === "End" ? available[available.length - 1] : available[Math.max(0, Math.min(available.length - 1, index + (event.key === "ArrowDown" ? 1 : -1)))];
1138
+ if (target) {
1139
+ emit("select", target.node, event);
1140
+ focusRow(target.node.path);
1141
+ }
1142
+ } else if (event.key === "ArrowRight") {
1143
+ event.preventDefault();
1144
+ if (!tree.value) {
1145
+ if (row.node.kind === "folder") emit("enter", row.node);
1146
+ } else if (row.node.kind === "folder" && !row.expanded) emit("expand", row.node);
1147
+ else if (row.expanded) {
1148
+ const next = available[index + 1];
1149
+ if (next && next.depth > row.depth) {
1150
+ emit("select", next.node, event);
1151
+ focusRow(next.node.path);
1152
+ }
1153
+ }
1154
+ } else if (event.key === "ArrowLeft") {
1155
+ event.preventDefault();
1156
+ if (row.expanded) emit("expand", row.node);
1157
+ else {
1158
+ const parent = props.rows.slice(0, props.rows.indexOf(row)).reverse().find((item) => item.depth < row.depth);
1159
+ if (parent) {
1160
+ emit("select", parent.node, event);
1161
+ focusRow(parent.node.path);
1162
+ } else emit("up");
1163
+ }
1164
+ } else if (event.key === "Enter") {
1165
+ event.preventDefault();
1166
+ emit("enter", row.node);
1167
+ } else if (event.key === " ") {
1168
+ event.preventDefault();
1169
+ emit("select", row.node, event);
1170
+ }
1171
+ }
1172
+ function parts(name) {
1173
+ const index = props.query ? name.toLowerCase().indexOf(props.query.toLowerCase()) : -1;
1174
+ return index < 0 ? [
1175
+ name,
1176
+ "",
1177
+ ""
1178
+ ] : [
1179
+ name.slice(0, index),
1180
+ name.slice(index, index + props.query.length),
1181
+ name.slice(index + props.query.length)
1182
+ ];
1183
+ }
1184
+ return (_ctx, _cache) => {
1185
+ return openBlock(), createElementBlock("div", {
1186
+ ref_key: "list",
1187
+ ref: list,
1188
+ class: "mint-picker__list",
1189
+ role: tree.value ? "tree" : "listbox",
1190
+ "aria-label": "Folders and files",
1191
+ "aria-multiselectable": multi.value,
1192
+ "aria-busy": __props.loading
1193
+ }, [
1194
+ _cache[5] || (_cache[5] = createElementVNode("div", {
1195
+ class: "mint-picker__list-head",
1196
+ "aria-hidden": "true"
1197
+ }, [
1198
+ createElementVNode("span"),
1199
+ createElementVNode("span", null, "Name"),
1200
+ createElementVNode("span", null, "Size"),
1201
+ createElementVNode("span", null, "Modified")
1202
+ ], -1)),
1203
+ __props.truncated && !__props.loading && !__props.error && __props.rows.length ? (openBlock(), createElementBlock("p", _hoisted_2$6, " Showing the first " + toDisplayString(__props.rows.length) + " matches. Search a smaller folder or use a more specific search to see the rest. ", 1)) : createCommentVNode("", true),
1204
+ __props.loading ? (openBlock(), createElementBlock("div", _hoisted_3$5, [(openBlock(), createElementBlock(Fragment, null, renderList(6, (n) => {
1205
+ return createVNode(Skeleton_default, {
1206
+ key: n,
1207
+ width: "100%",
1208
+ height: "1.25rem"
1209
+ });
1210
+ }), 64))])) : __props.error ? (openBlock(), createElementBlock("div", _hoisted_4$5, [createElementVNode("span", null, toDisplayString(__props.error), 1), createVNode(BaseButton_default, {
1211
+ variant: "secondary",
1212
+ size: "sm",
1213
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("retry"))
1214
+ }, {
1215
+ default: withCtx(() => [..._cache[2] || (_cache[2] = [createTextVNode("Try again", -1)])]),
1216
+ _: 1
1217
+ })])) : !__props.rows.length ? (openBlock(), createElementBlock("div", _hoisted_5$4, [createElementVNode("strong", null, toDisplayString(__props.truncated ? "Search stopped before finding a match" : __props.query ? "No matching folders or files" : __props.emptyTitle || "No readable files"), 1), createElementVNode("span", null, toDisplayString(__props.truncated ? "Search a smaller folder or use a more specific search." : __props.query ? "Try a different search or include subfolders." : "Choose another location."), 1)])) : (openBlock(true), createElementBlock(Fragment, { key: 4 }, renderList(__props.rows, (row) => {
1218
+ return openBlock(), createElementBlock(Fragment, { key: row.node.path }, [createElementVNode("div", {
1219
+ role: tree.value ? "treeitem" : "option",
1220
+ "data-path": row.node.path,
1221
+ "aria-level": tree.value ? row.depth + 1 : void 0,
1222
+ "aria-selected": __props.selected(row.node),
1223
+ "aria-disabled": !!reason(row.node),
1224
+ "aria-expanded": tree.value && row.node.kind === "folder" && !reason(row.node) ? row.expanded : void 0,
1225
+ tabindex: !reason(row.node) && row.node.path === focusablePath.value ? 0 : -1,
1226
+ class: normalizeClass(["mint-picker__row", {
1227
+ "mint-picker__row--selected": __props.selected(row.node),
1228
+ "mint-picker__row--unreadable": !!reason(row.node)
1229
+ }]),
1230
+ style: normalizeStyle({ "--picker-depth": row.depth }),
1231
+ title: reason(row.node) || row.node.name,
1232
+ onClick: ($event) => !reason(row.node) && emit("select", row.node, $event),
1233
+ onDblclick: ($event) => !reason(row.node) && emit("enter", row.node),
1234
+ onKeydown: ($event) => keydown($event, row)
1235
+ }, [
1236
+ createElementVNode("span", _hoisted_7$4, [locked(row.node) ? (openBlock(), createElementBlock("svg", _hoisted_8$4, [createElementVNode("path", { d: unref(FILE_PICKER_ICONS).lock }, null, 8, _hoisted_9$4)])) : multi.value && __props.selectable(row.node) ? (openBlock(), createBlock(BaseCheckbox_default, {
1237
+ key: 1,
1238
+ size: "sm",
1239
+ inert: "",
1240
+ "aria-hidden": "true",
1241
+ "model-value": __props.selected(row.node),
1242
+ indeterminate: partial(row.node)
1243
+ }, null, 8, ["model-value", "indeterminate"])) : createCommentVNode("", true)]),
1244
+ createElementVNode("span", _hoisted_10$4, [
1245
+ tree.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [row.node.kind === "folder" && row.node.hasChildren !== false && !reason(row.node) ? (openBlock(), createBlock(BaseButton_default, {
1246
+ key: 0,
1247
+ size: "sm",
1248
+ variant: "ghost",
1249
+ class: normalizeClass(["mint-picker__disclosure", { "mint-picker__chevron--open": row.expanded }]),
1250
+ icon: DisclosureTriangle,
1251
+ label: `${row.expanded ? "Collapse" : "Expand"} ${row.node.name}`,
1252
+ tabindex: "-1",
1253
+ loading: row.loading,
1254
+ onClick: withModifiers(($event) => emit("expand", row.node), ["stop"]),
1255
+ onDblclick: _cache[1] || (_cache[1] = withModifiers(() => {}, ["stop"]))
1256
+ }, null, 8, [
1257
+ "class",
1258
+ "label",
1259
+ "loading",
1260
+ "onClick"
1261
+ ])) : (openBlock(), createElementBlock("span", _hoisted_11$4))], 64)) : createCommentVNode("", true),
1262
+ (openBlock(), createElementBlock("svg", _hoisted_12$4, [createElementVNode("path", { d: row.node.kind === "folder" ? unref(FILE_PICKER_ICONS).folder : unref(FILE_PICKER_ICONS).file }, null, 8, _hoisted_13$4)])),
1263
+ createElementVNode("span", _hoisted_14$4, [
1264
+ createTextVNode(toDisplayString(parts(row.node.name)[0]), 1),
1265
+ parts(row.node.name)[1] ? (openBlock(), createElementBlock("mark", _hoisted_15$4, toDisplayString(parts(row.node.name)[1]), 1)) : createCommentVNode("", true),
1266
+ createTextVNode(toDisplayString(parts(row.node.name)[2]), 1)
1267
+ ]),
1268
+ reason(row.node) ? (openBlock(), createElementBlock("span", _hoisted_16$2, toDisplayString(shortReason(row.node)), 1)) : __props.query && row.node.parentName ? (openBlock(), createElementBlock("span", _hoisted_17$2, toDisplayString(row.node.parentName), 1)) : createCommentVNode("", true),
1269
+ __props.query && __props.mode === "folder" && row.node.kind === "file" && row.node.parentPath && !reason(row.node) ? (openBlock(), createBlock(BaseButton_default, {
1270
+ key: 3,
1271
+ variant: "ghost",
1272
+ size: "sm",
1273
+ onClick: withModifiers(($event) => emit("parent", row.node), ["stop"])
1274
+ }, {
1275
+ default: withCtx(() => [..._cache[3] || (_cache[3] = [createTextVNode("Select its folder", -1)])]),
1276
+ _: 1
1277
+ }, 8, ["onClick"])) : createCommentVNode("", true)
1278
+ ]),
1279
+ createElementVNode("span", { class: normalizeClass(["mint-picker__cell mint-picker__cell--size", { "mint-picker__partial": partial(row.node) }]) }, toDisplayString(size(row.node)), 3),
1280
+ createElementVNode("span", _hoisted_18$2, toDisplayString(unref(formatModified)(row.node.modifiedAt)), 1)
1281
+ ], 46, _hoisted_6$4), row.error ? (openBlock(), createElementBlock("div", _hoisted_19$2, [createTextVNode(toDisplayString(row.error), 1), createVNode(BaseButton_default, {
1282
+ variant: "ghost",
1283
+ size: "sm",
1284
+ onClick: ($event) => emit("expand", row.node)
1285
+ }, {
1286
+ default: withCtx(() => [..._cache[4] || (_cache[4] = [createTextVNode("Close branch", -1)])]),
1287
+ _: 1
1288
+ }, 8, ["onClick"])])) : createCommentVNode("", true)], 64);
1289
+ }), 128))
1290
+ ], 8, _hoisted_1$6);
1291
+ };
1292
+ }
1293
+ });
1294
+ //#endregion
1295
+ //#region src/components/internal/FilePickerPreviewInternal.vue?vue&type=script&setup=true&lang.ts
1296
+ var _hoisted_1$5 = {
1297
+ class: "mint-picker__preview",
1298
+ "aria-label": "Selection preview"
1299
+ };
1300
+ var _hoisted_2$5 = { class: "mint-picker__preview-heading" };
1301
+ var _hoisted_3$4 = ["title"];
1302
+ var _hoisted_4$4 = { class: "mint-picker__preview-head" };
1303
+ var _hoisted_5$3 = { class: "mint-picker__preview-icon" };
1304
+ var _hoisted_6$3 = {
1305
+ viewBox: "0 0 24 24",
1306
+ fill: "none",
1307
+ stroke: "currentColor",
1308
+ "stroke-width": "1.7",
1309
+ "aria-hidden": "true"
1310
+ };
1311
+ var _hoisted_7$3 = ["d"];
1312
+ var _hoisted_8$3 = { class: "mint-picker__preview-title" };
1313
+ var _hoisted_9$3 = { class: "mint-picker__preview-name" };
1314
+ var _hoisted_10$3 = { class: "mint-picker__preview-kind" };
1315
+ var _hoisted_11$3 = { class: "mint-picker__metadata" };
1316
+ var _hoisted_12$3 = ["title"];
1317
+ var _hoisted_13$3 = {
1318
+ key: 1,
1319
+ class: "mint-picker__preview-error",
1320
+ role: "alert"
1321
+ };
1322
+ var _hoisted_14$3 = {
1323
+ key: 2,
1324
+ class: "mint-picker__preview-status",
1325
+ role: "status"
1326
+ };
1327
+ var _hoisted_15$3 = {
1328
+ key: 2,
1329
+ class: "mint-picker__preview-empty"
1330
+ };
1331
+ //#endregion
1332
+ //#region src/components/internal/FilePickerPreviewInternal.vue
1333
+ var FilePickerPreviewInternal_default = /* @__PURE__ */ defineComponent({
1334
+ __name: "FilePickerPreviewInternal",
1335
+ props: {
1336
+ totalCount: {},
1337
+ node: {},
1338
+ sourceLabel: {},
1339
+ exclusions: {},
1340
+ busy: { type: Boolean },
1341
+ error: {},
1342
+ reviewing: { type: Boolean }
1343
+ },
1344
+ emits: ["review", "retry"],
1345
+ setup(__props, { emit: __emit }) {
1346
+ const emit = __emit;
1347
+ return (_ctx, _cache) => {
1348
+ return openBlock(), createElementBlock("aside", _hoisted_1$5, [__props.reviewing ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createElementVNode("div", _hoisted_2$5, [_cache[4] || (_cache[4] = createTextVNode(" Skipped files ", -1)), createVNode(BaseButton_default, {
1349
+ variant: "ghost",
1350
+ size: "sm",
1351
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("review"))
1352
+ }, {
1353
+ default: withCtx(() => [..._cache[3] || (_cache[3] = [createTextVNode("Back", -1)])]),
1354
+ _: 1
1355
+ })]), (openBlock(true), createElementBlock(Fragment, null, renderList(__props.exclusions, (item) => {
1356
+ return openBlock(), createElementBlock("div", {
1357
+ key: item.path,
1358
+ class: "mint-picker__exclusion",
1359
+ title: item.path
1360
+ }, [createElementVNode("strong", null, toDisplayString(item.name), 1), createElementVNode("span", null, toDisplayString(unref(pickerReason)(item)), 1)], 8, _hoisted_3$4);
1361
+ }), 128))], 64)) : __props.node ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
1362
+ createElementVNode("div", _hoisted_4$4, [createElementVNode("span", _hoisted_5$3, [(openBlock(), createElementBlock("svg", _hoisted_6$3, [createElementVNode("path", { d: __props.node.kind === "folder" ? unref(FILE_PICKER_ICONS).folder : unref(FILE_PICKER_ICONS).file }, null, 8, _hoisted_7$3)]))]), createElementVNode("span", _hoisted_8$3, [createElementVNode("strong", _hoisted_9$3, toDisplayString(__props.node.name), 1), createElementVNode("span", _hoisted_10$3, toDisplayString(__props.node.kind === "folder" ? "Folder" : __props.node.format ? `${__props.node.format.toUpperCase()} file` : "File"), 1)])]),
1363
+ createElementVNode("dl", _hoisted_11$3, [
1364
+ _cache[9] || (_cache[9] = createElementVNode("dt", null, "Size", -1)),
1365
+ createElementVNode("dd", null, toDisplayString(__props.node.bytes == null ? "—" : unref(formatBytes)(__props.node.bytes)), 1),
1366
+ __props.node.modifiedAt ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[5] || (_cache[5] = createElementVNode("dt", null, "Modified", -1)), createElementVNode("dd", null, toDisplayString(unref(formatModified)(__props.node.modifiedAt)), 1)], 64)) : createCommentVNode("", true),
1367
+ __props.node.kind === "folder" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
1368
+ _cache[8] || (_cache[8] = createElementVNode("dt", null, "Files", -1)),
1369
+ createElementVNode("dd", null, [createTextVNode(toDisplayString(__props.node.fileCount ?? "—"), 1), __props.totalCount != null && __props.totalCount !== __props.node.fileCount ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(" of " + toDisplayString(__props.totalCount), 1)], 64)) : createCommentVNode("", true)]),
1370
+ __props.node.formats ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[7] || (_cache[7] = createElementVNode("dt", null, "Formats", -1)), createElementVNode("dd", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.node.formats, (count, format) => {
1371
+ return openBlock(), createElementBlock("span", { key: format }, [createTextVNode(toDisplayString(format.toUpperCase()) + " ×" + toDisplayString(count), 1), _cache[6] || (_cache[6] = createElementVNode("br", null, null, -1))]);
1372
+ }), 128))])], 64)) : createCommentVNode("", true)
1373
+ ], 64)) : createCommentVNode("", true),
1374
+ _cache[10] || (_cache[10] = createElementVNode("dt", null, "Location", -1)),
1375
+ createElementVNode("dd", { title: __props.node.parentName || __props.node.path }, toDisplayString(__props.node.parentName || __props.node.path), 9, _hoisted_12$3),
1376
+ _cache[11] || (_cache[11] = createElementVNode("dt", null, "Source", -1)),
1377
+ createElementVNode("dd", null, toDisplayString(__props.sourceLabel), 1)
1378
+ ]),
1379
+ __props.exclusions.length ? (openBlock(), createBlock(BaseButton_default, {
1380
+ key: 0,
1381
+ variant: "ghost",
1382
+ size: "sm",
1383
+ class: "mint-picker__skipped",
1384
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("review"))
1385
+ }, {
1386
+ default: withCtx(() => [createTextVNode(toDisplayString(__props.exclusions.length) + " skipped · Review", 1)]),
1387
+ _: 1
1388
+ })) : createCommentVNode("", true),
1389
+ __props.error ? (openBlock(), createElementBlock("div", _hoisted_13$3, [createTextVNode(toDisplayString(__props.error), 1), createVNode(BaseButton_default, {
1390
+ variant: "secondary",
1391
+ size: "sm",
1392
+ onClick: _cache[2] || (_cache[2] = ($event) => emit("retry"))
1393
+ }, {
1394
+ default: withCtx(() => [..._cache[12] || (_cache[12] = [createTextVNode("Try again", -1)])]),
1395
+ _: 1
1396
+ })])) : __props.busy ? (openBlock(), createElementBlock("span", _hoisted_14$3, "Checking selection…")) : createCommentVNode("", true)
1397
+ ], 64)) : (openBlock(), createElementBlock("p", _hoisted_15$3, "Select a file to see its details."))]);
1398
+ };
1399
+ }
1400
+ });
1401
+ //#endregion
1402
+ //#region src/composables/filePicker/usePickerNavigation.ts
1403
+ var byName = new Intl.Collator(void 0, {
1404
+ numeric: true,
1405
+ sensitivity: "base"
1406
+ }).compare;
1407
+ /** Folders first; undated rows go last in either time order. */
1408
+ function ordered(items, sort) {
1409
+ if (!sort) return items;
1410
+ const times = new Map(items.map((node) => [node, node.modifiedAt ? Date.parse(node.modifiedAt) : NaN]));
1411
+ const time = (node) => times.get(node);
1412
+ return [...items].sort((a, b) => {
1413
+ if (a.kind !== b.kind) return a.kind === "folder" ? -1 : 1;
1414
+ if (sort === "name") return byName(a.name, b.name);
1415
+ if (sort === "name-desc") return byName(b.name, a.name);
1416
+ const [ta, tb] = [time(a), time(b)];
1417
+ if (Number.isNaN(ta) || Number.isNaN(tb)) return Number.isNaN(ta) === Number.isNaN(tb) ? byName(a.name, b.name) : Number.isNaN(ta) ? 1 : -1;
1418
+ return (sort === "newest" ? tb - ta : ta - tb) || byName(a.name, b.name);
1419
+ });
1420
+ }
1421
+ function usePickerNavigation(adapter, request, systemFilter = () => true, sort = () => void 0) {
1422
+ const path = shallowRef("");
1423
+ const trail = ref([]);
1424
+ const entries = shallowRef([]);
1425
+ const nodes = ref(/* @__PURE__ */ new Map());
1426
+ const children = shallowRef(/* @__PURE__ */ new Map());
1427
+ const expanded = ref(/* @__PURE__ */ new Set());
1428
+ const expanding = ref(/* @__PURE__ */ new Set());
1429
+ const expansionErrors = ref(/* @__PURE__ */ new Map());
1430
+ const loading = shallowRef(false);
1431
+ const error = shallowRef("");
1432
+ /** True when the current search results stopped at an adapter limit. */
1433
+ const truncated = shallowRef(false);
1434
+ let epoch = 0;
1435
+ let navigation = 0;
1436
+ let controller;
1437
+ const pending = /* @__PURE__ */ new Map();
1438
+ const childControllers = /* @__PURE__ */ new Set();
1439
+ let prefetchTimer;
1440
+ let prefetchQueue = [];
1441
+ let prefetchActive = 0;
1442
+ function stopPrefetch() {
1443
+ clearTimeout(prefetchTimer);
1444
+ prefetchQueue = [];
1445
+ }
1446
+ function drainPrefetch() {
1447
+ while (prefetchActive < 2 && prefetchQueue.length) {
1448
+ const node = prefetchQueue.shift();
1449
+ if (!isAllowed(node) || children.value.has(node.path) || pending.has(node.path)) continue;
1450
+ prefetchActive++;
1451
+ const token = epoch;
1452
+ loadChildren(node.path).catch(() => {}).finally(() => {
1453
+ if (token !== epoch) return;
1454
+ prefetchActive--;
1455
+ drainPrefetch();
1456
+ });
1457
+ }
1458
+ }
1459
+ function prefetch(items) {
1460
+ stopPrefetch();
1461
+ const candidates = items.filter((node) => node.kind === "folder" && !node.unavailableReason && node.hasChildren !== false && isAllowed(node)).slice(0, 20);
1462
+ prefetchTimer = setTimeout(() => {
1463
+ prefetchQueue = candidates;
1464
+ drainPrefetch();
1465
+ }, 100);
1466
+ }
1467
+ function isAllowed(node) {
1468
+ const seen = /* @__PURE__ */ new Set();
1469
+ let current = nodes.value.get(node.path) ?? node;
1470
+ while (current && !seen.has(current.path)) {
1471
+ if (!systemFilter(current)) return false;
1472
+ seen.add(current.path);
1473
+ current = current.parentPath ? nodes.value.get(current.parentPath) : void 0;
1474
+ }
1475
+ return true;
1476
+ }
1477
+ function register(items, parent) {
1478
+ for (const item of items) nodes.value.set(item.path, {
1479
+ ...item,
1480
+ parentPath: item.parentPath !== void 0 ? item.parentPath : parent !== void 0 ? parent || null : nodes.value.get(item.path)?.parentPath ?? null
1481
+ });
1482
+ }
1483
+ function invalidate() {
1484
+ adapter().invalidate?.();
1485
+ stopPrefetch();
1486
+ prefetchActive = 0;
1487
+ ++epoch;
1488
+ ++navigation;
1489
+ controller?.abort();
1490
+ childControllers.forEach((item) => item.abort());
1491
+ childControllers.clear();
1492
+ pending.clear();
1493
+ children.value = /* @__PURE__ */ new Map();
1494
+ expanding.value.clear();
1495
+ expansionErrors.value.clear();
1496
+ }
1497
+ function clear() {
1498
+ invalidate();
1499
+ nodes.value.clear();
1500
+ expanded.value.clear();
1501
+ expanding.value.clear();
1502
+ expansionErrors.value.clear();
1503
+ entries.value = [];
1504
+ loading.value = false;
1505
+ error.value = "";
1506
+ }
1507
+ async function loadChildren(folder, ancestors = [], refresh = false) {
1508
+ if (ancestors.includes(folder)) throw new Error("The adapter returned a circular folder tree.");
1509
+ const cached = children.value.get(folder);
1510
+ if (cached) return cached;
1511
+ const existing = pending.get(folder);
1512
+ if (existing) return existing;
1513
+ const token = epoch;
1514
+ const abort = new AbortController();
1515
+ childControllers.add(abort);
1516
+ const task = adapter().listChildren(folder, {
1517
+ ...request(),
1518
+ ...refresh && { refresh },
1519
+ signal: abort.signal
1520
+ }).then((items) => {
1521
+ if (token !== epoch) throw new DOMException("Cancelled", "AbortError");
1522
+ const retained = new Set(items.map((item) => item.path));
1523
+ function remove(path, seen = /* @__PURE__ */ new Set()) {
1524
+ if (seen.has(path) || path === folder) return;
1525
+ seen.add(path);
1526
+ for (const node of nodes.value.values()) if (node.parentPath === path) remove(node.path, seen);
1527
+ nodes.value.delete(path);
1528
+ children.value.delete(path);
1529
+ }
1530
+ for (const node of nodes.value.values()) if (node.parentPath === folder && !retained.has(node.path)) remove(node.path);
1531
+ register(items, folder);
1532
+ children.value = new Map(children.value).set(folder, items);
1533
+ return items;
1534
+ }).finally(() => {
1535
+ childControllers.delete(abort);
1536
+ if (token === epoch) pending.delete(folder);
1537
+ });
1538
+ pending.set(folder, task);
1539
+ return task;
1540
+ }
1541
+ async function load(query = "", recursive = false, refresh = false) {
1542
+ stopPrefetch();
1543
+ const token = ++navigation;
1544
+ controller?.abort();
1545
+ controller = new AbortController();
1546
+ const options = {
1547
+ ...request(),
1548
+ ...refresh && { refresh },
1549
+ signal: controller.signal
1550
+ };
1551
+ loading.value = true;
1552
+ error.value = "";
1553
+ truncated.value = false;
1554
+ entries.value = [];
1555
+ try {
1556
+ const found = query.trim() ? await adapter().search(query.trim(), {
1557
+ path: path.value,
1558
+ recursive
1559
+ }, options) : path.value ? await loadChildren(path.value, [], refresh) : await adapter().listRoot(options);
1560
+ if (token !== navigation) return;
1561
+ const items = Array.isArray(found) ? found : found.nodes;
1562
+ truncated.value = !Array.isArray(found) && found.truncated;
1563
+ register(items, query.trim() ? void 0 : path.value);
1564
+ entries.value = items;
1565
+ if (!query.trim()) prefetch(items);
1566
+ } catch (cause) {
1567
+ if (token === navigation) error.value = cause instanceof Error ? cause.message : "Could not read this location.";
1568
+ } finally {
1569
+ if (token === navigation) loading.value = false;
1570
+ }
1571
+ }
1572
+ async function expand(node) {
1573
+ if (node.kind !== "folder" || node.unavailableReason || !isAllowed(node)) return;
1574
+ if (expanded.value.has(node.path)) {
1575
+ expanded.value.delete(node.path);
1576
+ return;
1577
+ }
1578
+ expanded.value.add(node.path);
1579
+ expanding.value.add(node.path);
1580
+ expansionErrors.value.delete(node.path);
1581
+ const token = epoch;
1582
+ const view = navigation;
1583
+ try {
1584
+ const items = await loadChildren(node.path);
1585
+ if (token === epoch && view === navigation && expanded.value.has(node.path)) prefetch(items);
1586
+ } catch (cause) {
1587
+ if (token === epoch) expansionErrors.value.set(node.path, cause instanceof Error ? cause.message : "Could not read this folder.");
1588
+ } finally {
1589
+ if (token === epoch) expanding.value.delete(node.path);
1590
+ }
1591
+ }
1592
+ function enter(node) {
1593
+ if (node.kind !== "folder" || node.unavailableReason || !isAllowed(node)) return;
1594
+ const index = trail.value.findIndex((crumb) => crumb.path === node.path);
1595
+ trail.value = index >= 0 ? trail.value.slice(0, index + 1) : [...trail.value, {
1596
+ path: node.path,
1597
+ label: node.name
1598
+ }];
1599
+ path.value = node.path;
1600
+ }
1601
+ function up(target) {
1602
+ const index = target !== void 0 ? trail.value.findIndex((crumb) => crumb.path === target) : trail.value.length - 2;
1603
+ trail.value = index < 0 ? [] : trail.value.slice(0, index + 1);
1604
+ path.value = trail.value[trail.value.length - 1]?.path ?? "";
1605
+ }
1606
+ const rows = computed(() => {
1607
+ const result = [];
1608
+ const order = sort();
1609
+ function visit(items, depth, ancestors) {
1610
+ for (const item of ordered(items, order)) {
1611
+ if (ancestors.has(item.path) || depth > 30) continue;
1612
+ const node = nodes.value.get(item.path) ?? item;
1613
+ if (!isAllowed(node)) continue;
1614
+ result.push({
1615
+ node,
1616
+ depth,
1617
+ expanded: expanded.value.has(item.path),
1618
+ loading: expanding.value.has(item.path),
1619
+ error: expansionErrors.value.get(item.path)
1620
+ });
1621
+ if (expanded.value.has(item.path)) visit(children.value.get(item.path) ?? [], depth + 1, /* @__PURE__ */ new Set([...ancestors, item.path]));
1622
+ }
1623
+ }
1624
+ visit(entries.value, 0, /* @__PURE__ */ new Set());
1625
+ return result;
1626
+ });
1627
+ onScopeDispose(clear);
1628
+ return {
1629
+ path,
1630
+ trail,
1631
+ entries,
1632
+ nodes,
1633
+ children,
1634
+ expanded,
1635
+ expanding,
1636
+ expansionErrors,
1637
+ loading,
1638
+ error,
1639
+ truncated,
1640
+ rows,
1641
+ isAllowed,
1642
+ clear,
1643
+ invalidate,
1644
+ register,
1645
+ loadChildren,
1646
+ load,
1647
+ expand,
1648
+ enter,
1649
+ up
1650
+ };
1651
+ }
1652
+ //#endregion
1653
+ //#region src/composables/filePicker/usePickerSelection.ts
1654
+ function usePickerSelection(nav, mode, capability, skipBlanks) {
1655
+ const keys = ref(/* @__PURE__ */ new Set());
1656
+ const dropped = ref(/* @__PURE__ */ new Set());
1657
+ const focusPath = shallowRef("");
1658
+ const resolving = shallowRef(false);
1659
+ const resolutionError = shallowRef("");
1660
+ let anchor = "";
1661
+ let generation = 0;
1662
+ function ancestorSelected(node, candidates = keys.value) {
1663
+ const seen = /* @__PURE__ */ new Set();
1664
+ let parent = node.parentPath;
1665
+ while (parent && !seen.has(parent)) {
1666
+ if (candidates.has(parent)) return true;
1667
+ seen.add(parent);
1668
+ parent = nav.nodes.value.get(parent)?.parentPath;
1669
+ }
1670
+ return false;
1671
+ }
1672
+ function exclusion(node) {
1673
+ if (!nav.isAllowed(node)) return {
1674
+ path: node.path,
1675
+ name: node.name,
1676
+ reason: "forbidden"
1677
+ };
1678
+ const own = pickerExclusion(node, capability(), skipBlanks());
1679
+ if (own) return own;
1680
+ const seen = /* @__PURE__ */ new Set();
1681
+ let parent = node.parentPath;
1682
+ while (parent && !seen.has(parent)) {
1683
+ seen.add(parent);
1684
+ const folder = nav.nodes.value.get(parent);
1685
+ if (!folder) break;
1686
+ const inherited = pickerExclusion(folder, capability(), skipBlanks());
1687
+ if (inherited) return {
1688
+ ...inherited,
1689
+ path: node.path,
1690
+ name: node.name
1691
+ };
1692
+ parent = folder.parentPath;
1693
+ }
1694
+ return null;
1695
+ }
1696
+ function selected(node) {
1697
+ return !exclusion(node) && !dropped.value.has(node.path) && !ancestorSelected(node, dropped.value) && (keys.value.has(node.path) || mode() === "folder+files" && ancestorSelected(node));
1698
+ }
1699
+ function selectable(node) {
1700
+ if (exclusion(node)) return false;
1701
+ return node.kind === "folder" ? mode() === "folder" || mode() === "folder+files" : mode() !== "folder";
1702
+ }
1703
+ function select(node, modifiers = {}) {
1704
+ if (exclusion(node)) return;
1705
+ focusPath.value = node.path;
1706
+ if (!selectable(node)) return;
1707
+ const multi = mode() === "multi-file" || mode() === "folder+files";
1708
+ const additive = multi && (modifiers.metaKey || modifiers.ctrlKey || modifiers.toggle);
1709
+ if (multi && modifiers.shiftKey && anchor) {
1710
+ const rows = nav.rows.value.map((row) => row.node);
1711
+ const from = rows.findIndex((item) => item.path === anchor);
1712
+ const to = rows.findIndex((item) => item.path === node.path);
1713
+ if (from >= 0 && to >= 0) {
1714
+ keys.value = /* @__PURE__ */ new Set([...additive ? keys.value : [], ...rows.slice(Math.min(from, to), Math.max(from, to) + 1).filter(selectable).map((item) => item.path)]);
1715
+ dropped.value.clear();
1716
+ return;
1717
+ }
1718
+ }
1719
+ if (additive || modifiers.toggle && (selected(node) || dropped.value.has(node.path))) {
1720
+ if (mode() === "folder+files" && ancestorSelected(node)) {
1721
+ if (dropped.value.has(node.path)) dropped.value.delete(node.path);
1722
+ else dropped.value.add(node.path);
1723
+ } else if (keys.value.has(node.path)) keys.value.delete(node.path);
1724
+ else {
1725
+ keys.value.add(node.path);
1726
+ dropped.value.delete(node.path);
1727
+ }
1728
+ } else {
1729
+ keys.value = /* @__PURE__ */ new Set([node.path]);
1730
+ dropped.value.clear();
1731
+ }
1732
+ anchor = node.path;
1733
+ }
1734
+ function reset(selection) {
1735
+ ++generation;
1736
+ keys.value.clear();
1737
+ dropped.value.clear();
1738
+ resolving.value = false;
1739
+ resolutionError.value = "";
1740
+ focusPath.value = "";
1741
+ if (selection?.source === "server") {
1742
+ const files = mode() === "folder" ? [] : mode() === "single-file" ? selection.files.slice(0, 1) : selection.files;
1743
+ const folders = mode() === "folder" ? selection.folders.slice(0, 1) : mode() === "folder+files" ? selection.folders : [];
1744
+ nav.register(files);
1745
+ folders.forEach((path) => {
1746
+ if (!nav.nodes.value.has(path)) nav.register([{
1747
+ kind: "folder",
1748
+ path,
1749
+ name: "Selected folder",
1750
+ parentName: "Selected location"
1751
+ }]);
1752
+ keys.value.add(path);
1753
+ });
1754
+ files.forEach((file) => keys.value.add(file.path));
1755
+ selection.excluded.filter((item) => item.reason === "deselected").forEach((item) => dropped.value.add(item.path));
1756
+ }
1757
+ }
1758
+ const roots = computed(() => [...keys.value].map((key) => nav.nodes.value.get(key)).filter((node) => !!node && !ancestorSelected(node) && nav.isAllowed(node)));
1759
+ const chosen = computed(() => {
1760
+ const files = [];
1761
+ const excluded = [];
1762
+ for (const node of nav.nodes.value.values()) {
1763
+ if (!nav.isAllowed(node)) continue;
1764
+ if (!keys.value.has(node.path) && !ancestorSelected(node)) continue;
1765
+ const reason = exclusion(node) ?? (dropped.value.has(node.path) ? {
1766
+ path: node.path,
1767
+ name: node.name,
1768
+ reason: "deselected"
1769
+ } : null);
1770
+ const droppedByParent = ancestorSelected(node, dropped.value);
1771
+ if (reason || droppedByParent) {
1772
+ if (node.kind === "file" || reason?.reason === "unreadable") excluded.push(reason ?? {
1773
+ path: node.path,
1774
+ name: node.name,
1775
+ reason: "deselected"
1776
+ });
1777
+ } else if (node.kind === "file") files.push(node);
1778
+ }
1779
+ return {
1780
+ files,
1781
+ excluded,
1782
+ bytes: files.reduce((total, node) => total + (node.bytes ?? 0), 0)
1783
+ };
1784
+ });
1785
+ async function resolve() {
1786
+ const token = ++generation;
1787
+ resolving.value = true;
1788
+ resolutionError.value = "";
1789
+ let count = 0;
1790
+ const seen = /* @__PURE__ */ new Set();
1791
+ async function walk(node, ancestors = []) {
1792
+ if (token !== generation) throw new DOMException("Cancelled", "AbortError");
1793
+ if (!nav.isAllowed(node)) return [];
1794
+ if (node.kind === "file") return [node];
1795
+ if (node.unavailableReason) return [];
1796
+ if (ancestors.includes(node.path)) throw new Error("The adapter returned a circular folder tree.");
1797
+ if (seen.has(node.path)) return [];
1798
+ seen.add(node.path);
1799
+ if (++count > 500 || ancestors.length > 30) throw new Error("This selection is too large to validate. Choose a smaller folder.");
1800
+ const children = await nav.loadChildren(node.path, ancestors);
1801
+ const files = [];
1802
+ for (const child of children) files.push(...await walk(child, [...ancestors, node.path]));
1803
+ if (token !== generation) throw new DOMException("Cancelled", "AbortError");
1804
+ const formats = {};
1805
+ files.forEach((file) => {
1806
+ formats[file.format] = (formats[file.format] ?? 0) + 1;
1807
+ });
1808
+ nav.register([{
1809
+ ...node,
1810
+ fileCount: files.length,
1811
+ bytes: files.reduce((sum, file) => sum + (file.bytes ?? 0), 0),
1812
+ formats
1813
+ }]);
1814
+ return files;
1815
+ }
1816
+ try {
1817
+ for (const node of roots.value) await walk(node);
1818
+ if (token === generation) for (const key of [...keys.value]) {
1819
+ const node = nav.nodes.value.get(key);
1820
+ if (node && ancestorSelected(node)) keys.value.delete(key);
1821
+ }
1822
+ } catch (cause) {
1823
+ if (token === generation) resolutionError.value = cause instanceof Error ? cause.message : "Could not validate the selection.";
1824
+ } finally {
1825
+ if (token === generation) resolving.value = false;
1826
+ }
1827
+ }
1828
+ const focused = computed(() => {
1829
+ const node = nav.nodes.value.get(focusPath.value);
1830
+ return node && nav.isAllowed(node) ? node : roots.value[roots.value.length - 1] ?? null;
1831
+ });
1832
+ watch(() => new Map([...nav.nodes.value].map(([path, node]) => [path, nav.isAllowed(node)])), (allowed, previous) => {
1833
+ if (keys.value.size && [...allowed].some(([path, value]) => previous.has(path) && value !== previous.get(path))) resolve();
1834
+ });
1835
+ const selectedFileCounts = computed(() => {
1836
+ const counts = /* @__PURE__ */ new Map();
1837
+ for (const file of chosen.value.files) {
1838
+ const seen = /* @__PURE__ */ new Set();
1839
+ let parent = file.parentPath;
1840
+ while (parent && !seen.has(parent)) {
1841
+ seen.add(parent);
1842
+ counts.set(parent, (counts.get(parent) ?? 0) + 1);
1843
+ parent = nav.nodes.value.get(parent)?.parentPath;
1844
+ }
1845
+ }
1846
+ return counts;
1847
+ });
1848
+ function folderProgress(node) {
1849
+ if (node.kind !== "folder" || node.fileCount == null) return "";
1850
+ return `${selected(node) ? selectedFileCounts.value.get(node.path) ?? 0 : 0} / ${node.fileCount}`;
1851
+ }
1852
+ return {
1853
+ keys,
1854
+ dropped,
1855
+ focusPath,
1856
+ resolving,
1857
+ resolutionError,
1858
+ roots,
1859
+ chosen,
1860
+ focused,
1861
+ select,
1862
+ selected,
1863
+ selectable,
1864
+ exclusion,
1865
+ reset,
1866
+ resolve,
1867
+ folderProgress,
1868
+ ancestorSelected
1869
+ };
1870
+ }
1871
+ //#endregion
1872
+ //#region src/composables/filePicker/localFiles.ts
1873
+ /** Construct metadata only: no FileReader, arrayBuffer or upload. */
1874
+ function localFilesAdapter(files, relativePaths) {
1875
+ const nodes = /* @__PURE__ */ new Map();
1876
+ for (const [index, file] of files.entries()) {
1877
+ const relative = relativePaths?.[index] || file.webkitRelativePath || file.name;
1878
+ const parts = relative.split("/");
1879
+ let parent = null;
1880
+ for (let i = 0; i < parts.length - 1; i++) {
1881
+ const path = parts.slice(0, i + 1).join("/");
1882
+ if (!nodes.has(path)) nodes.set(path, {
1883
+ kind: "folder",
1884
+ name: parts[i],
1885
+ path,
1886
+ parentPath: parent,
1887
+ fileCount: 0,
1888
+ bytes: 0,
1889
+ formats: {}
1890
+ });
1891
+ parent = path;
1892
+ }
1893
+ let path = relative;
1894
+ for (let n = 2; nodes.has(path); n++) path = `${relative} (${n})`;
1895
+ nodes.set(path, {
1896
+ kind: "file",
1897
+ name: file.name,
1898
+ path,
1899
+ parentPath: parent,
1900
+ bytes: file.size,
1901
+ modifiedAt: new Date(file.lastModified).toISOString(),
1902
+ format: pickerFormat(file.name),
1903
+ file
1904
+ });
1905
+ }
1906
+ const children = /* @__PURE__ */ new Map();
1907
+ for (const node of nodes.values()) {
1908
+ const parent = node.parentPath ?? "";
1909
+ const siblings = children.get(parent);
1910
+ if (siblings) siblings.push(node);
1911
+ else children.set(parent, [node]);
1912
+ if (node.kind !== "file") continue;
1913
+ for (let slash = node.path.indexOf("/"); slash >= 0; slash = node.path.indexOf("/", slash + 1)) {
1914
+ const folder = nodes.get(node.path.slice(0, slash));
1915
+ if (folder?.kind !== "folder") continue;
1916
+ folder.fileCount = (folder.fileCount ?? 0) + 1;
1917
+ folder.bytes = (folder.bytes ?? 0) + (node.bytes ?? 0);
1918
+ folder.formats[node.format] = (folder.formats[node.format] ?? 0) + 1;
1919
+ }
1920
+ }
1921
+ const list = (path) => [...children.get(path) ?? []];
1922
+ return {
1923
+ listRoot: async () => list(""),
1924
+ listChildren: async (path) => list(path),
1925
+ search: async (query, scope) => [...nodes.values()].filter((node) => {
1926
+ return (scope.recursive ? !scope.path || node.path.startsWith(`${scope.path}/`) : (node.parentPath ?? "") === scope.path) && node.name.toLowerCase().includes(query.toLowerCase());
1927
+ })
1928
+ };
1929
+ }
1930
+ //#endregion
1931
+ //#region src/composables/useFilePicker.ts
1932
+ /** Internal orchestration; public consumers use FilePicker's props and events. */
1933
+ function useFilePicker(props, emit, searchArea) {
1934
+ const sourceNames = {
1935
+ server: "Server",
1936
+ localFile: "Local files"
1937
+ };
1938
+ const allowedSources = computed(() => [...new Set(props.sources)]);
1939
+ const SOURCE = "source:";
1940
+ const MOUNT = "mount:";
1941
+ const mounts = shallowRef([]);
1942
+ const source = shallowRef(allowedSources.value.includes(props.initialSelection?.source) ? props.initialSelection.source : allowedSources.value[0] ?? "server");
1943
+ const query = shallowRef("");
1944
+ const recursive = shallowRef(false);
1945
+ const sort = shallowRef(props.defaultSort);
1946
+ const skipBlanks = shallowRef(false);
1947
+ const reviewing = shallowRef(false);
1948
+ const localFiles = shallowRef([]);
1949
+ const localPaths = /* @__PURE__ */ new WeakMap();
1950
+ const localAdapter = computed(() => localFilesAdapter(localFiles.value, localFiles.value.map((file) => localPaths.get(toRaw(file)))));
1951
+ const nav = usePickerNavigation(() => source.value === "localFile" ? localAdapter.value : props.adapter, () => ({ source: source.value }), (node) => props.systemFilter?.(node, source.value) !== false, () => sort.value);
1952
+ const capability = computed(() => props.capabilities[source.value]);
1953
+ const selection = usePickerSelection(nav, () => props.selectionMode, () => capability.value, () => skipBlanks.value);
1954
+ const { path, trail, loading, error, rows } = nav;
1955
+ /** A mount-rooted adapter lists its mounts as header sources; the listing starts inside one. */
1956
+ const mountView = computed(() => source.value === "server" && !!props.adapter.mounts && mounts.value.length > 0);
1957
+ const segments = computed(() => allowedSources.value.flatMap((item) => item === "server" && props.adapter.mounts && mounts.value.length ? mounts.value.map((mount) => ({
1958
+ value: MOUNT + mount.path,
1959
+ label: mount.name,
1960
+ disabled: !!mount.unavailableReason
1961
+ })) : [{
1962
+ value: SOURCE + item,
1963
+ label: sourceNames[item]
1964
+ }]));
1965
+ const activeSegment = computed(() => mountView.value ? MOUNT + (trail.value[0]?.path ?? "") : SOURCE + source.value);
1966
+ const { roots, chosen, focused, resolving, resolutionError } = selection;
1967
+ const previewNode = computed(() => {
1968
+ const node = focused.value;
1969
+ if (!node || node.kind !== "folder" || !selection.selected(node) || resolving.value) return node;
1970
+ const files = chosen.value.files.filter((file) => selection.ancestorSelected(file, /* @__PURE__ */ new Set([node.path])));
1971
+ const formats = {};
1972
+ files.forEach((file) => {
1973
+ formats[file.format] = (formats[file.format] ?? 0) + 1;
1974
+ });
1975
+ return {
1976
+ ...node,
1977
+ bytes: files.reduce((sum, file) => sum + (file.bytes ?? 0), 0),
1978
+ fileCount: files.length,
1979
+ formats
1980
+ };
1981
+ });
1982
+ const confirmLabel = computed(() => ({
1983
+ "single-file": "Use file",
1984
+ "multi-file": "Use files",
1985
+ folder: "Use folder",
1986
+ "folder+files": "Use selection"
1987
+ })[props.selectionMode]);
1988
+ const folderCount = computed(() => roots.value.filter((node) => node.kind === "folder").length);
1989
+ const canConfirm = computed(() => allowedSources.value.includes(source.value) && roots.value.length > 0 && chosen.value.files.length > 0 && !resolving.value && !loading.value && !error.value && !resolutionError.value);
1990
+ let lastConfirmed = null;
1991
+ let initialized = false;
1992
+ let timer;
1993
+ let openGeneration = 0;
1994
+ const remembered = /* @__PURE__ */ new Map();
1995
+ const knownFolders = /* @__PURE__ */ new Map();
1996
+ function rememberFolders() {
1997
+ knownFolders.set(source.value, [...nav.nodes.value.values()].filter((node) => node.kind === "folder"));
1998
+ }
1999
+ function currentSeed() {
2000
+ return props.initialSelection ?? lastConfirmed;
2001
+ }
2002
+ async function restore() {
2003
+ const token = ++openGeneration;
2004
+ const seed = currentSeed();
2005
+ nav.register(knownFolders.get(source.value) ?? []);
2006
+ selection.reset(seed?.source === source.value ? seed : null);
2007
+ reviewing.value = false;
2008
+ if (!allowedSources.value.includes(source.value)) return;
2009
+ if (seed?.source === "localFile" && source.value === "localFile") {
2010
+ seed.files.forEach((file, index) => {
2011
+ if (seed.relativePaths?.[index]) localPaths.set(toRaw(file), seed.relativePaths[index]);
2012
+ });
2013
+ localFiles.value = seed.files;
2014
+ }
2015
+ if (source.value === "server" && props.adapter.mounts) {
2016
+ await loadMounts();
2017
+ if (token !== openGeneration || !props.open) return;
2018
+ const first = mounts.value.find((mount) => !mount.unavailableReason);
2019
+ if (!path.value && first) openMount(first);
2020
+ const holder = mounts.value.find((mount) => mount.path === props.adapter.mountOf?.(path.value));
2021
+ if (holder && trail.value[0]?.path !== holder.path) trail.value = [{
2022
+ path: holder.path,
2023
+ label: holder.name
2024
+ }, ...trail.value];
2025
+ }
2026
+ await nav.load(query.value, recursive.value);
2027
+ if (token !== openGeneration || !props.open) return;
2028
+ if (seed?.source === "localFile" && source.value === "localFile") await selectLocalFiles();
2029
+ if (selection.keys.value.size) await selection.resolve();
2030
+ }
2031
+ watch(() => props.open, (open) => {
2032
+ clearTimeout(timer);
2033
+ if (open) {
2034
+ sort.value = props.defaultSort;
2035
+ if (!initialized) {
2036
+ path.value = props.initialPath;
2037
+ if (path.value) trail.value = [{
2038
+ path: path.value,
2039
+ label: "Current folder"
2040
+ }];
2041
+ initialized = true;
2042
+ }
2043
+ const seed = currentSeed();
2044
+ if (seed && allowedSources.value.includes(seed.source) && seed.source !== source.value) switchSource(seed.source);
2045
+ else restore();
2046
+ } else {
2047
+ ++openGeneration;
2048
+ rememberFolders();
2049
+ selection.reset();
2050
+ nav.clear();
2051
+ }
2052
+ }, { immediate: true });
2053
+ watch([query, recursive], () => {
2054
+ clearTimeout(timer);
2055
+ if (props.open) timer = setTimeout(() => {
2056
+ nav.load(query.value, recursive.value);
2057
+ }, 200);
2058
+ });
2059
+ watch(() => [...selection.keys.value], () => {
2060
+ if (props.open) selection.resolve();
2061
+ });
2062
+ watch(() => props.selectionMode, () => {
2063
+ if (props.open) restore();
2064
+ });
2065
+ watch(allowedSources, (sources) => {
2066
+ if (!sources.includes(source.value) && sources[0]) switchSource(sources[0]);
2067
+ });
2068
+ onScopeDispose(() => {
2069
+ ++openGeneration;
2070
+ clearTimeout(timer);
2071
+ selection.reset();
2072
+ });
2073
+ async function loadMounts() {
2074
+ try {
2075
+ mounts.value = (await props.adapter.listRoot({ source: "server" })).filter((node) => node.kind === "folder" && props.systemFilter?.(node, "server") !== false);
2076
+ } catch {
2077
+ mounts.value = [];
2078
+ }
2079
+ nav.register(mounts.value, "");
2080
+ }
2081
+ function openMount(mount) {
2082
+ trail.value = [{
2083
+ path: mount.path,
2084
+ label: mount.name
2085
+ }];
2086
+ path.value = mount.path;
2087
+ }
2088
+ async function chooseSegment(value) {
2089
+ const key = String(value);
2090
+ if (key.startsWith(SOURCE)) return switchSource(key.slice(7));
2091
+ const mount = mounts.value.find((item) => MOUNT + item.path === key);
2092
+ if (!mount || mount.unavailableReason) return;
2093
+ if (source.value !== "server") {
2094
+ remembered.set("server", {
2095
+ path: mount.path,
2096
+ trail: [{
2097
+ path: mount.path,
2098
+ label: mount.name
2099
+ }]
2100
+ });
2101
+ return switchSource("server");
2102
+ }
2103
+ clearTimeout(timer);
2104
+ query.value = "";
2105
+ openMount(mount);
2106
+ await nav.load();
2107
+ }
2108
+ function clearSelection() {
2109
+ selection.reset();
2110
+ reviewing.value = false;
2111
+ }
2112
+ async function switchSource(next) {
2113
+ if (next === source.value || !allowedSources.value.includes(next)) return;
2114
+ rememberFolders();
2115
+ remembered.set(source.value, {
2116
+ path: path.value,
2117
+ trail: [...trail.value]
2118
+ });
2119
+ clearTimeout(timer);
2120
+ nav.clear();
2121
+ source.value = next;
2122
+ query.value = "";
2123
+ path.value = remembered.get(next)?.path ?? "";
2124
+ trail.value = remembered.get(next)?.trail ?? [];
2125
+ await restore();
2126
+ }
2127
+ function selectNode(node, event) {
2128
+ const opensFolder = node.kind === "folder" && props.selectionMode === "folder+files";
2129
+ if (event.type === "click" && event.detail > 1 && !opensFolder) return;
2130
+ selection.select(node, {
2131
+ metaKey: event.metaKey,
2132
+ ctrlKey: event.ctrlKey,
2133
+ shiftKey: event.shiftKey,
2134
+ toggle: event.type === "click" || event instanceof KeyboardEvent && event.key === " "
2135
+ });
2136
+ }
2137
+ async function enter(node) {
2138
+ const token = openGeneration;
2139
+ if (selection.exclusion(node)) return;
2140
+ if (node.kind === "file") {
2141
+ if (props.selectionMode === "single-file") {
2142
+ selection.select(node);
2143
+ await nextTick();
2144
+ await selection.resolve();
2145
+ if (token === openGeneration && props.open) confirm();
2146
+ }
2147
+ return;
2148
+ }
2149
+ clearTimeout(timer);
2150
+ query.value = "";
2151
+ nav.enter(node);
2152
+ await nav.load();
2153
+ }
2154
+ async function up(target) {
2155
+ const atMountRoot = mounts.value.some((mount) => mount.path === trail.value[0]?.path);
2156
+ if (mountView.value && (target === "" || target === void 0 && trail.value.length <= 1 && atMountRoot)) return;
2157
+ query.value = "";
2158
+ clearTimeout(timer);
2159
+ nav.up(target);
2160
+ await nav.load();
2161
+ }
2162
+ async function refresh() {
2163
+ nav.invalidate();
2164
+ await nav.load(query.value, recursive.value, true);
2165
+ if (selection.keys.value.size) await selection.resolve();
2166
+ }
2167
+ function selectParent(node) {
2168
+ if (!node.parentPath) return;
2169
+ const parent = nav.nodes.value.get(node.parentPath) ?? {
2170
+ kind: "folder",
2171
+ path: node.parentPath,
2172
+ name: node.parentName ?? "Containing folder"
2173
+ };
2174
+ nav.register([parent]);
2175
+ selection.select(parent);
2176
+ }
2177
+ async function chooseLocal(files, _rootName, relativePaths) {
2178
+ const token = openGeneration;
2179
+ if (!files.length || !props.open || source.value !== "localFile") return;
2180
+ files.forEach((file, index) => {
2181
+ if (relativePaths?.[index]) localPaths.set(toRaw(file), relativePaths[index]);
2182
+ });
2183
+ localFiles.value = files;
2184
+ clearTimeout(timer);
2185
+ nav.clear();
2186
+ selection.reset();
2187
+ path.value = "";
2188
+ trail.value = [];
2189
+ query.value = "";
2190
+ await nav.load();
2191
+ if (token !== openGeneration || !props.open || source.value !== "localFile") return;
2192
+ await selectLocalFiles();
2193
+ }
2194
+ async function selectLocalFiles() {
2195
+ const token = openGeneration;
2196
+ const found = props.selectionMode === "single-file" || props.selectionMode === "multi-file" ? await localAdapter.value.search("", {
2197
+ path: "",
2198
+ recursive: true
2199
+ }) : nav.entries.value;
2200
+ const items = Array.isArray(found) ? found : found.nodes;
2201
+ if (token !== openGeneration || !props.open || source.value !== "localFile") return;
2202
+ nav.register(items);
2203
+ const candidates = items.filter((node) => selection.selectable(node));
2204
+ for (const node of props.selectionMode === "single-file" || props.selectionMode === "folder" ? candidates.slice(0, 1) : candidates) selection.select(node, { ctrlKey: true });
2205
+ }
2206
+ function cancel() {
2207
+ emit("cancel");
2208
+ emit("update:open", false);
2209
+ }
2210
+ function confirm() {
2211
+ if (!canConfirm.value) return;
2212
+ const result = source.value === "localFile" ? {
2213
+ source: "localFile",
2214
+ files: chosen.value.files.flatMap((node) => node.file ? [toRaw(node.file)] : [])
2215
+ } : {
2216
+ source: "server",
2217
+ folders: roots.value.filter((node) => node.kind === "folder").map((node) => node.path),
2218
+ files: [...chosen.value.files],
2219
+ excluded: [...chosen.value.excluded]
2220
+ };
2221
+ if (result.source === "localFile") {
2222
+ const relativePaths = result.files.map((file) => localPaths.get(file) || file.webkitRelativePath || file.name);
2223
+ if (relativePaths.some((path, index) => path !== (result.files[index].webkitRelativePath || result.files[index].name))) result.relativePaths = relativePaths;
2224
+ }
2225
+ lastConfirmed = result;
2226
+ emit("select", result);
2227
+ emit("update:open", false);
2228
+ }
2229
+ function keyboard(event) {
2230
+ if (event.key !== "/" || event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) return;
2231
+ event.preventDefault();
2232
+ searchArea.value?.querySelector("input")?.focus();
2233
+ }
2234
+ return {
2235
+ previewNode,
2236
+ sourceNames,
2237
+ segments,
2238
+ activeSegment,
2239
+ mounts,
2240
+ mountView,
2241
+ allowedSources,
2242
+ source,
2243
+ query,
2244
+ recursive,
2245
+ sort,
2246
+ skipBlanks,
2247
+ reviewing,
2248
+ nav,
2249
+ selection,
2250
+ capability,
2251
+ path,
2252
+ trail,
2253
+ loading,
2254
+ error,
2255
+ rows,
2256
+ roots,
2257
+ chosen,
2258
+ focused,
2259
+ resolving,
2260
+ resolutionError,
2261
+ confirmLabel,
2262
+ folderCount,
2263
+ canConfirm,
2264
+ chooseSegment,
2265
+ clearSelection,
2266
+ selectNode,
2267
+ enter,
2268
+ up,
2269
+ refresh,
2270
+ selectParent,
2271
+ chooseLocal,
2272
+ cancel,
2273
+ confirm,
2274
+ keyboard
2275
+ };
2276
+ }
2277
+ //#endregion
2278
+ //#region src/components/FilePicker.vue?vue&type=script&setup=true&lang.ts
2279
+ var _hoisted_1$4 = { class: "mint-picker__title" };
2280
+ var _hoisted_2$4 = {
2281
+ key: 1,
2282
+ ref: "searchArea",
2283
+ class: "mint-picker__search"
2284
+ };
2285
+ var _hoisted_3$3 = {
2286
+ key: 0,
2287
+ class: "mint-picker__pathbar"
2288
+ };
2289
+ var _hoisted_4$3 = {
2290
+ "aria-label": "Breadcrumb",
2291
+ class: "mint-picker__breadcrumbs"
2292
+ };
2293
+ var _hoisted_5$2 = ["aria-current"];
2294
+ var _hoisted_6$2 = {
2295
+ key: 0,
2296
+ class: "mint-picker__crumb-separator",
2297
+ viewBox: "0 0 16 16",
2298
+ fill: "none",
2299
+ stroke: "currentColor",
2300
+ "stroke-width": "1.5",
2301
+ "aria-hidden": "true"
2302
+ };
2303
+ var _hoisted_7$2 = ["aria-current", "onClick"];
2304
+ var _hoisted_8$2 = {
2305
+ key: 0,
2306
+ class: "mint-picker__accepts"
2307
+ };
2308
+ var _hoisted_9$2 = [
2309
+ "aria-expanded",
2310
+ "aria-label",
2311
+ "onClick",
2312
+ "onKeydown"
2313
+ ];
2314
+ var _hoisted_10$2 = ["aria-pressed"];
2315
+ var _hoisted_11$2 = ["disabled"];
2316
+ var _hoisted_12$2 = {
2317
+ key: 1,
2318
+ class: "mint-picker__empty"
2319
+ };
2320
+ var _hoisted_13$2 = {
2321
+ key: 0,
2322
+ class: "mint-picker__local-upload"
2323
+ };
2324
+ var _hoisted_14$2 = {
2325
+ key: 0,
2326
+ role: "alert"
2327
+ };
2328
+ var _hoisted_15$2 = {
2329
+ key: 1,
2330
+ class: "mint-picker__workspace"
2331
+ };
2332
+ var _hoisted_16$1 = { class: "mint-picker__footer" };
2333
+ var _hoisted_17$1 = {
2334
+ class: "mint-picker__receipt",
2335
+ role: "status"
2336
+ };
2337
+ var _hoisted_18$1 = { class: "mint-picker__summary" };
2338
+ var _hoisted_19$1 = { key: 2 };
2339
+ var _hoisted_20$1 = { class: "mint-picker__actions" };
2340
+ //#endregion
2341
+ //#region src/components/FilePicker.vue
2342
+ var FilePicker_default = /* @__PURE__ */ defineComponent({
2343
+ __name: "FilePicker",
2344
+ props: {
2345
+ open: { type: Boolean },
2346
+ adapter: {},
2347
+ selectionMode: { default: "folder+files" },
2348
+ sources: { default: () => ["server"] },
2349
+ capabilities: { default: () => ({}) },
2350
+ initialPath: { default: "" },
2351
+ initialSelection: { default: null },
2352
+ title: { default: "Choose data" },
2353
+ view: { default: "list" },
2354
+ defaultSort: { default: "newest" },
2355
+ systemFilter: {}
2356
+ },
2357
+ emits: [
2358
+ "update:open",
2359
+ "select",
2360
+ "cancel"
2361
+ ],
2362
+ setup(__props, { emit: __emit }) {
2363
+ /**
2364
+ * Adapter-driven file picker: mounts and local files as header sources, a flat
2365
+ * (or tree) listing with multi-select, a metadata preview and a selection receipt.
2366
+ *
2367
+ * @example Recommended
2368
+ * ```vue
2369
+ * <FilePicker v-model:open="open" :adapter="usePlatformFilePickerAdapter()" @select="use" />
2370
+ * ```
2371
+ */
2372
+ const props = __props;
2373
+ const emit = __emit;
2374
+ const uploadError = ref("");
2375
+ const { previewNode, sourceNames, mounts, segments, activeSegment, mountView, allowedSources, source, query, recursive, sort, skipBlanks, reviewing, nav, selection, capability, path, trail, loading, error, rows, roots, chosen, focused, resolving, resolutionError, confirmLabel, folderCount, canConfirm, chooseSegment, clearSelection, selectNode, enter, up, refresh, selectParent, chooseLocal, cancel, confirm, keyboard } = useFilePicker(props, emit, useTemplateRef("searchArea"));
2376
+ const sourceLabel = computed(() => {
2377
+ const node = previewNode.value;
2378
+ if (!mountView.value || !node) return sourceNames[source.value];
2379
+ for (let at = node.path; at; at = nav.nodes.value.get(at)?.parentPath) {
2380
+ const mount = mounts.value.find((item) => item.path === at);
2381
+ if (mount) return mount.name;
2382
+ }
2383
+ return mounts.value.find((item) => item.path === props.adapter.mountOf?.(node.path))?.name ?? sourceNames.server;
2384
+ });
2385
+ const SORT_LABELS = {
2386
+ newest: "Newest first",
2387
+ oldest: "Oldest first",
2388
+ name: "Name A–Z",
2389
+ "name-desc": "Name Z–A"
2390
+ };
2391
+ const sortItems = computed(() => Object.keys(SORT_LABELS).map((id) => ({
2392
+ id,
2393
+ label: SORT_LABELS[id],
2394
+ checked: sort.value === id
2395
+ })));
2396
+ const accepts = computed(() => (capability.value?.formats ?? []).map((format) => `.${format.replace(/^\./, "")}`).join(" "));
2397
+ const summary = computed(() => {
2398
+ if (!roots.value.length) return "No selection";
2399
+ const files = chosen.value.files.length;
2400
+ return [
2401
+ ...folderCount.value ? [`${folderCount.value} folder${folderCount.value === 1 ? "" : "s"}`] : [],
2402
+ `${files} file${files === 1 ? "" : "s"} selected`,
2403
+ resolving.value ? "Checking…" : formatBytes(chosen.value.bytes)
2404
+ ].join(" · ");
2405
+ });
2406
+ return (_ctx, _cache) => {
2407
+ return openBlock(), createBlock(BaseModal_default, {
2408
+ open: __props.open,
2409
+ size: "xl",
2410
+ layout: "plain",
2411
+ onClose: unref(cancel)
2412
+ }, {
2413
+ header: withCtx(() => [createElementVNode("div", {
2414
+ class: "mint-picker__head",
2415
+ onKeydown: _cache[1] || (_cache[1] = (...args) => unref(keyboard) && unref(keyboard)(...args))
2416
+ }, [
2417
+ createElementVNode("h2", _hoisted_1$4, toDisplayString(__props.title), 1),
2418
+ unref(segments).length > 1 ? (openBlock(), createBlock(SegmentedControl_default, {
2419
+ key: 0,
2420
+ class: "mint-picker__sources",
2421
+ "aria-label": "Data source",
2422
+ size: "sm",
2423
+ options: unref(segments),
2424
+ "model-value": unref(activeSegment),
2425
+ "onUpdate:modelValue": unref(chooseSegment)
2426
+ }, null, 8, [
2427
+ "options",
2428
+ "model-value",
2429
+ "onUpdate:modelValue"
2430
+ ])) : createCommentVNode("", true),
2431
+ unref(source) === "server" ? (openBlock(), createElementBlock("div", _hoisted_2$4, [createVNode(BaseInput_default, {
2432
+ modelValue: unref(query),
2433
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isRef(query) ? query.value = $event : null),
2434
+ size: "sm",
2435
+ "aria-label": "Search folders and files",
2436
+ placeholder: "Search this source"
2437
+ }, {
2438
+ prefix: withCtx(() => [..._cache[13] || (_cache[13] = [createElementVNode("svg", {
2439
+ viewBox: "0 0 24 24",
2440
+ fill: "none",
2441
+ stroke: "currentColor",
2442
+ "stroke-width": "1.8",
2443
+ "aria-hidden": "true"
2444
+ }, [createElementVNode("circle", {
2445
+ cx: "10.5",
2446
+ cy: "10.5",
2447
+ r: "7"
2448
+ }), createElementVNode("path", { d: "m16 16 5 5" })], -1)])]),
2449
+ suffix: withCtx(() => [..._cache[14] || (_cache[14] = [createElementVNode("kbd", null, "/", -1)])]),
2450
+ _: 1
2451
+ }, 8, ["modelValue"])], 512)) : createCommentVNode("", true)
2452
+ ], 32)]),
2453
+ footer: withCtx(() => [createElementVNode("div", _hoisted_16$1, [createElementVNode("div", _hoisted_17$1, [
2454
+ createElementVNode("span", _hoisted_18$1, toDisplayString(summary.value), 1),
2455
+ unref(roots).length ? (openBlock(), createElementBlock("button", {
2456
+ key: 0,
2457
+ type: "button",
2458
+ class: "mint-picker__link mint-picker__clear",
2459
+ onClick: _cache[10] || (_cache[10] = (...args) => unref(clearSelection) && unref(clearSelection)(...args))
2460
+ }, " Clear ")) : createCommentVNode("", true),
2461
+ unref(chosen).excluded.length && unref(source) === "server" ? (openBlock(), createElementBlock("button", {
2462
+ key: 1,
2463
+ type: "button",
2464
+ class: "mint-picker__link",
2465
+ onClick: _cache[11] || (_cache[11] = ($event) => reviewing.value = !unref(reviewing))
2466
+ }, toDisplayString(unref(chosen).excluded.length) + " skipped ", 1)) : unref(chosen).excluded.length ? (openBlock(), createElementBlock("span", _hoisted_19$1, toDisplayString(unref(chosen).excluded.length) + " skipped", 1)) : createCommentVNode("", true),
2467
+ unref(capability)?.isBlank ? (openBlock(), createBlock(BaseButton_default, {
2468
+ key: 3,
2469
+ size: "sm",
2470
+ variant: "ghost",
2471
+ "aria-pressed": unref(skipBlanks),
2472
+ onClick: _cache[12] || (_cache[12] = ($event) => skipBlanks.value = !unref(skipBlanks))
2473
+ }, {
2474
+ default: withCtx(() => [createTextVNode("Skip blanks " + toDisplayString(unref(skipBlanks) ? "on" : "off"), 1)]),
2475
+ _: 1
2476
+ }, 8, ["aria-pressed"])) : createCommentVNode("", true)
2477
+ ]), createElementVNode("div", _hoisted_20$1, [createVNode(BaseButton_default, {
2478
+ variant: "ghost",
2479
+ size: "sm",
2480
+ onClick: unref(cancel)
2481
+ }, {
2482
+ default: withCtx(() => [..._cache[18] || (_cache[18] = [createTextVNode("Cancel", -1)])]),
2483
+ _: 1
2484
+ }, 8, ["onClick"]), createVNode(BaseButton_default, {
2485
+ size: "sm",
2486
+ disabled: !unref(canConfirm),
2487
+ onClick: unref(confirm)
2488
+ }, {
2489
+ default: withCtx(() => [createTextVNode(toDisplayString(unref(confirmLabel)), 1)]),
2490
+ _: 1
2491
+ }, 8, ["disabled", "onClick"])])])]),
2492
+ default: withCtx(() => [(openBlock(), createElementBlock("div", {
2493
+ key: unref(source),
2494
+ class: "mint-picker mint-picker__source-panel",
2495
+ onKeydown: _cache[9] || (_cache[9] = (...args) => unref(keyboard) && unref(keyboard)(...args))
2496
+ }, [unref(source) === "server" ? (openBlock(), createElementBlock("div", _hoisted_3$3, [
2497
+ createElementVNode("nav", _hoisted_4$3, [!unref(mountView) ? (openBlock(), createElementBlock("button", {
2498
+ key: 0,
2499
+ type: "button",
2500
+ class: "mint-picker__crumb",
2501
+ "aria-current": unref(trail).length ? void 0 : "location",
2502
+ onClick: _cache[2] || (_cache[2] = ($event) => unref(up)(""))
2503
+ }, toDisplayString(unref(sourceNames)[unref(source)]), 9, _hoisted_5$2)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(trail), (crumb, index) => {
2504
+ return openBlock(), createElementBlock(Fragment, { key: crumb.path }, [index > 0 || !unref(mountView) ? (openBlock(), createElementBlock("svg", _hoisted_6$2, [..._cache[15] || (_cache[15] = [createElementVNode("path", { d: "M6 4l4 4-4 4" }, null, -1)])])) : createCommentVNode("", true), createElementVNode("button", {
2505
+ type: "button",
2506
+ class: "mint-picker__crumb",
2507
+ "aria-current": index === unref(trail).length - 1 ? "location" : void 0,
2508
+ onClick: ($event) => unref(up)(crumb.path)
2509
+ }, toDisplayString(crumb.label), 9, _hoisted_7$2)], 64);
2510
+ }), 128))]),
2511
+ accepts.value ? (openBlock(), createElementBlock("span", _hoisted_8$2, "Accepts " + toDisplayString(accepts.value), 1)) : createCommentVNode("", true),
2512
+ createVNode(ActionMenu_default, {
2513
+ label: "Sort",
2514
+ items: sortItems.value,
2515
+ onSelect: _cache[3] || (_cache[3] = ($event) => sort.value = $event.id)
2516
+ }, {
2517
+ trigger: withCtx(({ open: menuOpen, toggle, keydown }) => [createElementVNode("button", {
2518
+ type: "button",
2519
+ class: "mint-picker__sort",
2520
+ "aria-haspopup": "menu",
2521
+ "aria-expanded": menuOpen,
2522
+ "aria-label": `Sort: ${SORT_LABELS[unref(sort)]}`,
2523
+ onClick: toggle,
2524
+ onKeydown: keydown
2525
+ }, [createTextVNode(toDisplayString(SORT_LABELS[unref(sort)]) + " ", 1), _cache[16] || (_cache[16] = createElementVNode("svg", {
2526
+ viewBox: "0 0 16 16",
2527
+ fill: "none",
2528
+ stroke: "currentColor",
2529
+ "stroke-width": "1.5",
2530
+ "aria-hidden": "true"
2531
+ }, [createElementVNode("path", { d: "M4 6l4 4 4-4" })], -1))], 40, _hoisted_9$2)]),
2532
+ _: 1
2533
+ }, 8, ["items"]),
2534
+ createElementVNode("button", {
2535
+ type: "button",
2536
+ class: "mint-picker__scope",
2537
+ "aria-pressed": unref(recursive),
2538
+ onClick: _cache[4] || (_cache[4] = ($event) => recursive.value = !unref(recursive))
2539
+ }, toDisplayString(unref(recursive) ? "Subfolders" : "This folder"), 9, _hoisted_10$2),
2540
+ createElementVNode("button", {
2541
+ type: "button",
2542
+ class: "mint-picker__scope mint-picker__refresh",
2543
+ "aria-label": "Refresh location",
2544
+ title: "Refresh location",
2545
+ disabled: unref(loading),
2546
+ onClick: _cache[5] || (_cache[5] = ($event) => unref(refresh)())
2547
+ }, [..._cache[17] || (_cache[17] = [createElementVNode("svg", {
2548
+ viewBox: "0 0 24 24",
2549
+ fill: "none",
2550
+ stroke: "currentColor",
2551
+ "stroke-width": "1.8",
2552
+ "aria-hidden": "true"
2553
+ }, [createElementVNode("path", { d: "M20 7v5h-5M4 17v-5h5M6 8a7 7 0 0 1 12-2l2 3M4 15l2 3a7 7 0 0 0 12-2" })], -1)])], 8, _hoisted_11$2)
2554
+ ])) : createCommentVNode("", true), !unref(allowedSources).length ? (openBlock(), createElementBlock("div", _hoisted_12$2, " No data sources are enabled. ")) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [unref(source) === "localFile" ? (openBlock(), createElementBlock("div", _hoisted_13$2, [createVNode(FileUploader_default, {
2555
+ size: "lg",
2556
+ mode: __props.selectionMode === "folder" ? "folder" : __props.selectionMode === "single-file" ? "file" : "file+folder",
2557
+ multiple: __props.selectionMode !== "single-file",
2558
+ "show-files": false,
2559
+ onUpload: _cache[6] || (_cache[6] = (...args) => {
2560
+ uploadError.value = "";
2561
+ unref(chooseLocal)(...args);
2562
+ }),
2563
+ onError: _cache[7] || (_cache[7] = ($event) => uploadError.value = $event)
2564
+ }, null, 8, ["mode", "multiple"]), uploadError.value || unref(error) || unref(resolutionError) ? (openBlock(), createElementBlock("p", _hoisted_14$2, toDisplayString(uploadError.value || unref(error) || unref(resolutionError)), 1)) : createCommentVNode("", true)])) : (openBlock(), createElementBlock("div", _hoisted_15$2, [createVNode(FilePickerListInternal_default, {
2565
+ "empty-title": unref(path) ? "No readable files" : "No server locations configured",
2566
+ view: __props.view,
2567
+ rows: unref(rows),
2568
+ loading: unref(loading),
2569
+ error: unref(error),
2570
+ query: unref(query),
2571
+ truncated: unref(nav).truncated.value,
2572
+ mode: __props.selectionMode,
2573
+ selected: unref(selection).selected,
2574
+ selectable: unref(selection).selectable,
2575
+ exclusion: unref(selection).exclusion,
2576
+ progress: unref(selection).folderProgress,
2577
+ "focus-path": unref(selection).focusPath.value,
2578
+ onSelect: unref(selectNode),
2579
+ onEnter: unref(enter),
2580
+ onExpand: unref(nav).expand,
2581
+ onUp: unref(up),
2582
+ onRetry: unref(refresh),
2583
+ onParent: unref(selectParent)
2584
+ }, null, 8, [
2585
+ "empty-title",
2586
+ "view",
2587
+ "rows",
2588
+ "loading",
2589
+ "error",
2590
+ "query",
2591
+ "truncated",
2592
+ "mode",
2593
+ "selected",
2594
+ "selectable",
2595
+ "exclusion",
2596
+ "progress",
2597
+ "focus-path",
2598
+ "onSelect",
2599
+ "onEnter",
2600
+ "onExpand",
2601
+ "onUp",
2602
+ "onRetry",
2603
+ "onParent"
2604
+ ]), createVNode(FilePickerPreviewInternal_default, {
2605
+ node: unref(previewNode),
2606
+ "total-count": unref(focused)?.kind === "folder" ? unref(focused).fileCount : void 0,
2607
+ "source-label": sourceLabel.value,
2608
+ exclusions: unref(chosen).excluded,
2609
+ busy: unref(resolving),
2610
+ error: unref(resolutionError),
2611
+ reviewing: unref(reviewing),
2612
+ onReview: _cache[8] || (_cache[8] = ($event) => reviewing.value = !unref(reviewing)),
2613
+ onRetry: unref(selection).resolve
2614
+ }, null, 8, [
2615
+ "node",
2616
+ "total-count",
2617
+ "source-label",
2618
+ "exclusions",
2619
+ "busy",
2620
+ "error",
2621
+ "reviewing",
2622
+ "onRetry"
2623
+ ])]))], 64))], 32))]),
2624
+ _: 1
2625
+ }, 8, ["open", "onClose"]);
2626
+ };
2627
+ }
2628
+ });
2629
+ //#endregion
2630
+ //#region src/components/AuditTrail.vue?vue&type=script&setup=true&lang.ts
2631
+ var _hoisted_1$3 = {
2632
+ key: 0,
2633
+ class: "mint-audit-trail__filters"
2634
+ };
2635
+ var _hoisted_2$3 = ["onClick"];
2636
+ var _hoisted_3$2 = { class: "mint-audit-trail__content" };
2637
+ var _hoisted_4$2 = { class: "mint-audit-trail__header" };
2638
+ var _hoisted_5$1 = {
2639
+ key: 0,
2640
+ class: "mint-audit-trail__avatar"
2641
+ };
2642
+ var _hoisted_6$1 = { class: "mint-audit-trail__sentence" };
2643
+ var _hoisted_7$1 = {
2644
+ key: 0,
2645
+ class: "mint-audit-trail__user"
2646
+ };
2647
+ var _hoisted_8$1 = { class: "mint-audit-trail__action" };
2648
+ var _hoisted_9$1 = { class: "mint-audit-trail__timestamp" };
2649
+ var _hoisted_10$1 = {
2650
+ key: 0,
2651
+ class: "mint-audit-trail__detail"
2652
+ };
2653
+ var _hoisted_11$1 = {
2654
+ key: 1,
2655
+ class: "mint-audit-trail__metadata"
2656
+ };
2657
+ var _hoisted_12$1 = { class: "mint-audit-trail__metadata-key" };
2658
+ var _hoisted_13$1 = {
2659
+ key: 2,
2660
+ class: "mint-audit-trail__state"
2661
+ };
2662
+ var _hoisted_14$1 = {
2663
+ key: 3,
2664
+ class: "mint-audit-trail__state"
2665
+ };
2666
+ var _hoisted_15$1 = {
2667
+ key: 4,
2668
+ class: "mint-audit-trail__state"
2669
+ };
2670
+ //#endregion
2671
+ //#region src/components/AuditTrail.vue
2672
+ var AuditTrail_default = /* @__PURE__ */ defineComponent({
2673
+ __name: "AuditTrail",
2674
+ props: {
2675
+ entries: {},
2676
+ maxHeight: {},
2677
+ showFilters: {
2678
+ type: Boolean,
2679
+ default: false
2680
+ },
2681
+ emptyText: { default: "No activity yet" },
2682
+ order: { default: "newest" },
2683
+ size: { default: "md" },
2684
+ loading: {
2685
+ type: Boolean,
2686
+ default: false
2687
+ },
2688
+ error: { default: null }
2689
+ },
2690
+ emits: ["entry-click"],
2691
+ setup(__props, { emit: __emit }) {
2692
+ /** Filterable, sortable timeline of audit entries for tracking experiment or resource history. */
2693
+ const props = __props;
2694
+ const emit = __emit;
2695
+ const filterType = ref("");
2696
+ const filterUser = ref("");
2697
+ const uniqueTypes = computed(() => {
2698
+ const types = new Set(props.entries.map((e) => e.type));
2699
+ return Array.from(types);
2700
+ });
2701
+ const uniqueUsers = computed(() => {
2702
+ const users = new Set(props.entries.flatMap((e) => e.user ? [e.user] : []));
2703
+ return Array.from(users);
2704
+ });
2705
+ const typeOptions = computed(() => [{
2706
+ value: "",
2707
+ label: "All types"
2708
+ }, ...uniqueTypes.value]);
2709
+ const userOptions = computed(() => [{
2710
+ value: "",
2711
+ label: "All users"
2712
+ }, ...uniqueUsers.value]);
2713
+ const filteredEntries = computed(() => {
2714
+ let result = props.entries;
2715
+ if (filterType.value) result = result.filter((e) => e.type === filterType.value);
2716
+ if (filterUser.value) result = result.filter((e) => e.user === filterUser.value);
2717
+ return result;
2718
+ });
2719
+ const sortedEntries = computed(() => {
2720
+ const entries = [...filteredEntries.value];
2721
+ entries.sort((a, b) => {
2722
+ const timeA = new Date(a.timestamp).getTime();
2723
+ const timeB = new Date(b.timestamp).getTime();
2724
+ return props.order === "newest" ? timeB - timeA : timeA - timeB;
2725
+ });
2726
+ return entries;
2727
+ });
2728
+ function getInitials(name) {
2729
+ const words = name.trim().split(/\s+/);
2730
+ if (words.length >= 2) return (words[0][0] + words[1][0]).toUpperCase();
2731
+ return (words[0]?.[0] ?? "").toUpperCase();
2732
+ }
2733
+ function handleEntryClick(entry) {
2734
+ emit("entry-click", entry);
2735
+ }
2736
+ return (_ctx, _cache) => {
2737
+ return openBlock(), createElementBlock("div", { class: normalizeClass(["mint-audit-trail", `mint-audit-trail--${__props.size}`]) }, [
2738
+ __props.showFilters ? (openBlock(), createElementBlock("div", _hoisted_1$3, [createVNode(BaseSelect_default, {
2739
+ "model-value": filterType.value,
2740
+ options: typeOptions.value,
2741
+ size: "sm",
2742
+ class: "mint-audit-trail__filter-select",
2743
+ "aria-label": "Filter by type",
2744
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => filterType.value = String($event))
2745
+ }, null, 8, ["model-value", "options"]), createVNode(BaseSelect_default, {
2746
+ "model-value": filterUser.value,
2747
+ options: userOptions.value,
2748
+ size: "sm",
2749
+ class: "mint-audit-trail__filter-select",
2750
+ "aria-label": "Filter by user",
2751
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => filterUser.value = String($event))
2752
+ }, null, 8, ["model-value", "options"])])) : createCommentVNode("", true),
2753
+ !__props.loading && !__props.error && sortedEntries.value.length ? (openBlock(), createElementBlock("div", {
2754
+ key: 1,
2755
+ class: "mint-audit-trail__list",
2756
+ style: normalizeStyle(__props.maxHeight ? {
2757
+ maxHeight: __props.maxHeight,
2758
+ overflowY: "auto"
2759
+ } : {})
2760
+ }, [_cache[4] || (_cache[4] = createElementVNode("div", { class: "mint-audit-trail__line" }, null, -1)), (openBlock(true), createElementBlock(Fragment, null, renderList(sortedEntries.value, (entry) => {
2761
+ return openBlock(), createElementBlock("div", {
2762
+ key: entry.id,
2763
+ class: normalizeClass(["mint-audit-trail__entry", `mint-audit-trail__entry--${entry.type}`]),
2764
+ onClick: ($event) => handleEntryClick(entry)
2765
+ }, [renderSlot(_ctx.$slots, "entry", { entry }, () => [_cache[3] || (_cache[3] = createElementVNode("div", { class: "mint-audit-trail__dot" }, null, -1)), createElementVNode("div", _hoisted_3$2, [
2766
+ createElementVNode("div", _hoisted_4$2, [
2767
+ entry.user ? (openBlock(), createElementBlock("div", _hoisted_5$1, toDisplayString(getInitials(entry.user)), 1)) : createCommentVNode("", true),
2768
+ createElementVNode("span", _hoisted_6$1, [
2769
+ entry.user ? (openBlock(), createElementBlock("span", _hoisted_7$1, toDisplayString(entry.user), 1)) : createCommentVNode("", true),
2770
+ _cache[2] || (_cache[2] = createTextVNode()),
2771
+ createElementVNode("span", _hoisted_8$1, toDisplayString(entry.action), 1)
2772
+ ]),
2773
+ createElementVNode("span", _hoisted_9$1, toDisplayString(unref(formatRelativeTime)(entry.timestamp, { weeks: true })), 1)
2774
+ ]),
2775
+ entry.detail ? (openBlock(), createElementBlock("div", _hoisted_10$1, toDisplayString(entry.detail), 1)) : createCommentVNode("", true),
2776
+ entry.metadata && Object.keys(entry.metadata).length ? (openBlock(), createElementBlock("div", _hoisted_11$1, [(openBlock(true), createElementBlock(Fragment, null, renderList(entry.metadata, (value, key) => {
2777
+ return openBlock(), createElementBlock("span", {
2778
+ key: String(key),
2779
+ class: "mint-audit-trail__metadata-item"
2780
+ }, [createElementVNode("span", _hoisted_12$1, toDisplayString(key) + ":", 1), createTextVNode(" " + toDisplayString(value), 1)]);
2781
+ }), 128))])) : createCommentVNode("", true)
2782
+ ])])], 10, _hoisted_2$3);
2783
+ }), 128))], 4)) : createCommentVNode("", true),
2784
+ __props.error ? (openBlock(), createElementBlock("div", _hoisted_13$1, [renderSlot(_ctx.$slots, "error", { error: __props.error }, () => [createVNode(DataStateInternal_default, {
2785
+ state: "error",
2786
+ size: "sm",
2787
+ title: __props.error
2788
+ }, null, 8, ["title"])])])) : __props.loading ? (openBlock(), createElementBlock("div", _hoisted_14$1, [renderSlot(_ctx.$slots, "loading", {}, () => [createVNode(DataStateInternal_default, {
2789
+ state: "loading",
2790
+ size: "sm",
2791
+ "loading-label": "Loading activity"
2792
+ })])])) : !sortedEntries.value.length ? (openBlock(), createElementBlock("div", _hoisted_15$1, [renderSlot(_ctx.$slots, "empty", {}, () => [createVNode(DataStateInternal_default, {
2793
+ state: "empty",
2794
+ size: "sm",
2795
+ title: __props.emptyText
2796
+ }, null, 8, ["title"])])])) : createCommentVNode("", true)
2797
+ ], 2);
2798
+ };
2799
+ }
2800
+ });
2801
+ //#endregion
2802
+ //#region src/components/BatchProgressList.vue?vue&type=script&setup=true&lang.ts
2803
+ var _hoisted_1$2 = { class: "mint-batch-progress" };
2804
+ var _hoisted_2$2 = {
2805
+ key: 0,
2806
+ class: "mint-batch-progress__header"
2807
+ };
2808
+ var _hoisted_3$1 = { class: "mint-batch-progress__title" };
2809
+ var _hoisted_4$1 = { class: "mint-batch-progress__count" };
2810
+ var _hoisted_5 = { class: "mint-batch-progress__percent" };
2811
+ var _hoisted_6 = { class: "mint-batch-progress__overall" };
2812
+ var _hoisted_7 = { class: "mint-batch-progress__summary" };
2813
+ var _hoisted_8 = {
2814
+ key: 0,
2815
+ class: "mint-batch-progress__summary-item mint-batch-progress__summary-item--completed"
2816
+ };
2817
+ var _hoisted_9 = { class: "mint-batch-progress__summary-count" };
2818
+ var _hoisted_10 = {
2819
+ key: 1,
2820
+ class: "mint-batch-progress__summary-item mint-batch-progress__summary-item--processing"
2821
+ };
2822
+ var _hoisted_11 = { class: "mint-batch-progress__summary-count" };
2823
+ var _hoisted_12 = {
2824
+ key: 2,
2825
+ class: "mint-batch-progress__summary-item mint-batch-progress__summary-item--error"
2826
+ };
2827
+ var _hoisted_13 = { class: "mint-batch-progress__summary-count" };
2828
+ var _hoisted_14 = {
2829
+ key: 3,
2830
+ class: "mint-batch-progress__summary-item mint-batch-progress__summary-item--pending"
2831
+ };
2832
+ var _hoisted_15 = { class: "mint-batch-progress__summary-count" };
2833
+ var _hoisted_16 = {
2834
+ key: 4,
2835
+ class: "mint-batch-progress__summary-item mint-batch-progress__summary-item--skipped"
2836
+ };
2837
+ var _hoisted_17 = { class: "mint-batch-progress__summary-count" };
2838
+ var _hoisted_18 = {
2839
+ key: 2,
2840
+ class: "mint-batch-progress__state"
2841
+ };
2842
+ var _hoisted_19 = {
2843
+ key: 3,
2844
+ class: "mint-batch-progress__state"
2845
+ };
2846
+ var _hoisted_20 = {
2847
+ key: 4,
2848
+ class: "mint-batch-progress__state"
2849
+ };
2850
+ var _hoisted_21 = ["data-item-id"];
2851
+ var _hoisted_22 = { class: "mint-batch-progress__item-row" };
2852
+ var _hoisted_23 = { class: "mint-batch-progress__item-label" };
2853
+ var _hoisted_24 = { class: "mint-batch-progress__item-meta" };
2854
+ var _hoisted_25 = { class: "mint-batch-progress__item-status" };
2855
+ var _hoisted_26 = ["onClick"];
2856
+ var _hoisted_27 = ["onClick"];
2857
+ var _hoisted_28 = {
2858
+ class: "mint-batch-progress__item-progress",
2859
+ "aria-hidden": "true"
2860
+ };
2861
+ var _hoisted_29 = {
2862
+ key: 0,
2863
+ class: "mint-batch-progress__item-message"
2864
+ };
2865
+ //#endregion
2866
+ //#region src/components/BatchProgressList.vue
2867
+ var BatchProgressList_default = /* @__PURE__ */ defineComponent({
2868
+ __name: "BatchProgressList",
2869
+ props: {
2870
+ items: {},
2871
+ showSummary: {
2872
+ type: Boolean,
2873
+ default: true
2874
+ },
2875
+ title: {},
2876
+ maxHeight: {},
2877
+ autoScroll: {
2878
+ type: Boolean,
2879
+ default: true
2880
+ },
2881
+ loading: {
2882
+ type: Boolean,
2883
+ default: false
2884
+ },
2885
+ error: { default: null },
2886
+ emptyText: { default: "No items in this batch" }
2887
+ },
2888
+ emits: ["retry", "cancel"],
2889
+ setup(__props, { emit: __emit }) {
2890
+ /** Scrollable list tracking per-item status for a batch operation with overall progress summary. */
2891
+ const props = __props;
2892
+ const emit = __emit;
2893
+ const STATUS_ICONS = {
2894
+ completed: CircleCheck,
2895
+ processing: Loader,
2896
+ pending: CircleDashed,
2897
+ error: CircleX,
2898
+ skipped: CircleMinus
2899
+ };
2900
+ const STATUS_TEXT = {
2901
+ pending: "Queued",
2902
+ error: "Failed",
2903
+ skipped: "Skipped"
2904
+ };
2905
+ function itemPercent(item) {
2906
+ return item.status === "completed" ? 100 : item.progress ?? 0;
2907
+ }
2908
+ function statusText(item) {
2909
+ return STATUS_TEXT[item.status] ?? `${itemPercent(item)}%`;
2910
+ }
2911
+ const listRef = ref(null);
2912
+ const showCompleted = ref(false);
2913
+ const visibleItems = computed(() => showCompleted.value ? props.items : props.items.filter((i) => i.status !== "completed"));
2914
+ const summary = computed(() => {
2915
+ const total = props.items.length;
2916
+ const completed = props.items.filter((i) => i.status === "completed").length;
2917
+ const processing = props.items.filter((i) => i.status === "processing").length;
2918
+ const error = props.items.filter((i) => i.status === "error").length;
2919
+ const pending = props.items.filter((i) => i.status === "pending").length;
2920
+ const skipped = props.items.filter((i) => i.status === "skipped").length;
2921
+ return {
2922
+ total,
2923
+ completed,
2924
+ processing,
2925
+ error,
2926
+ pending,
2927
+ skipped,
2928
+ percent: total > 0 ? Math.round((completed + skipped) / total * 100) : 0
2929
+ };
2930
+ });
2931
+ watch(() => props.items.map((i) => i.status), (newStatuses, oldStatuses) => {
2932
+ if (!props.autoScroll || !listRef.value) return;
2933
+ for (let i = 0; i < newStatuses.length; i++) if (newStatuses[i] === "processing" && oldStatuses?.[i] !== "processing") {
2934
+ nextTick(() => {
2935
+ (listRef.value?.querySelector(`[data-item-id="${props.items[i].id}"]`))?.scrollIntoView({
2936
+ behavior: "smooth",
2937
+ block: "center"
2938
+ });
2939
+ });
2940
+ break;
2941
+ }
2942
+ });
2943
+ return (_ctx, _cache) => {
2944
+ return openBlock(), createElementBlock("div", _hoisted_1$2, [
2945
+ __props.title ? (openBlock(), createElementBlock("div", _hoisted_2$2, [
2946
+ createElementVNode("span", _hoisted_3$1, toDisplayString(__props.title), 1),
2947
+ createElementVNode("span", _hoisted_4$1, toDisplayString(summary.value.completed) + " / " + toDisplayString(summary.value.total), 1),
2948
+ createElementVNode("span", _hoisted_5, toDisplayString(summary.value.percent) + "%", 1)
2949
+ ])) : createCommentVNode("", true),
2950
+ createElementVNode("div", _hoisted_6, [createElementVNode("div", {
2951
+ class: "mint-batch-progress__overall-bar",
2952
+ style: normalizeStyle({ width: `${summary.value.percent}%` })
2953
+ }, null, 4)]),
2954
+ __props.showSummary ? renderSlot(_ctx.$slots, "summary", { summary: summary.value }, () => [createElementVNode("div", _hoisted_7, [
2955
+ summary.value.completed ? (openBlock(), createElementBlock("span", _hoisted_8, [
2956
+ _cache[1] || (_cache[1] = createElementVNode("svg", {
2957
+ class: "mint-batch-progress__shape",
2958
+ viewBox: "0 0 16 16",
2959
+ fill: "none",
2960
+ "aria-hidden": "true"
2961
+ }, [createElementVNode("path", {
2962
+ class: "mint-batch-progress__check",
2963
+ d: "M3.5 8.5l3 3 6-7"
2964
+ })], -1)),
2965
+ createElementVNode("span", _hoisted_9, toDisplayString(summary.value.completed), 1),
2966
+ _cache[2] || (_cache[2] = createTextVNode(" completed ", -1))
2967
+ ])) : createCommentVNode("", true),
2968
+ summary.value.processing ? (openBlock(), createElementBlock("span", _hoisted_10, [
2969
+ _cache[3] || (_cache[3] = createElementVNode("span", {
2970
+ class: "mint-batch-progress__running-dot",
2971
+ "aria-hidden": "true"
2972
+ }, null, -1)),
2973
+ createElementVNode("span", _hoisted_11, toDisplayString(summary.value.processing), 1),
2974
+ _cache[4] || (_cache[4] = createTextVNode(" processing ", -1))
2975
+ ])) : createCommentVNode("", true),
2976
+ summary.value.error ? (openBlock(), createElementBlock("span", _hoisted_12, [
2977
+ _cache[5] || (_cache[5] = createElementVNode("svg", {
2978
+ class: "mint-batch-progress__shape",
2979
+ viewBox: "0 0 16 16",
2980
+ "aria-hidden": "true"
2981
+ }, [createElementVNode("circle", {
2982
+ class: "mint-batch-progress__error-disc",
2983
+ cx: "8",
2984
+ cy: "8",
2985
+ r: "7"
2986
+ }), createElementVNode("path", {
2987
+ class: "mint-batch-progress__error-mark",
2988
+ d: "M8 4.5v4M8 11v.01"
2989
+ })], -1)),
2990
+ createElementVNode("span", _hoisted_13, toDisplayString(summary.value.error), 1),
2991
+ _cache[6] || (_cache[6] = createTextVNode(" failed ", -1))
2992
+ ])) : createCommentVNode("", true),
2993
+ summary.value.pending ? (openBlock(), createElementBlock("span", _hoisted_14, [
2994
+ _cache[7] || (_cache[7] = createElementVNode("span", {
2995
+ class: "mint-batch-progress__pending-ring",
2996
+ "aria-hidden": "true"
2997
+ }, null, -1)),
2998
+ createElementVNode("span", _hoisted_15, toDisplayString(summary.value.pending), 1),
2999
+ _cache[8] || (_cache[8] = createTextVNode(" pending ", -1))
3000
+ ])) : createCommentVNode("", true),
3001
+ summary.value.skipped ? (openBlock(), createElementBlock("span", _hoisted_16, [
3002
+ _cache[9] || (_cache[9] = createElementVNode("svg", {
3003
+ class: "mint-batch-progress__shape",
3004
+ viewBox: "0 0 16 16",
3005
+ fill: "none",
3006
+ "aria-hidden": "true"
3007
+ }, [createElementVNode("path", {
3008
+ class: "mint-batch-progress__dash",
3009
+ d: "M4 8h8"
3010
+ })], -1)),
3011
+ createElementVNode("span", _hoisted_17, toDisplayString(summary.value.skipped), 1),
3012
+ _cache[10] || (_cache[10] = createTextVNode(" skipped ", -1))
3013
+ ])) : createCommentVNode("", true)
3014
+ ])], void 0, 1) : createCommentVNode("", true),
3015
+ __props.error ? (openBlock(), createElementBlock("div", _hoisted_18, [renderSlot(_ctx.$slots, "error", { error: __props.error }, () => [createVNode(DataStateInternal_default, {
3016
+ state: "error",
3017
+ size: "sm",
3018
+ title: __props.error
3019
+ }, null, 8, ["title"])])])) : __props.loading ? (openBlock(), createElementBlock("div", _hoisted_19, [renderSlot(_ctx.$slots, "loading", {}, () => [createVNode(DataStateInternal_default, {
3020
+ state: "loading",
3021
+ size: "sm",
3022
+ "loading-label": "Loading batch"
3023
+ })])])) : __props.items.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_20, [renderSlot(_ctx.$slots, "empty", {}, () => [createVNode(DataStateInternal_default, {
3024
+ state: "empty",
3025
+ size: "sm",
3026
+ title: __props.emptyText
3027
+ }, null, 8, ["title"])])])) : (openBlock(), createElementBlock("div", {
3028
+ key: 5,
3029
+ ref_key: "listRef",
3030
+ ref: listRef,
3031
+ class: "mint-batch-progress__list",
3032
+ style: normalizeStyle(__props.maxHeight ? {
3033
+ maxHeight: __props.maxHeight,
3034
+ overflowY: "auto"
3035
+ } : {})
3036
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(visibleItems.value, (item) => {
3037
+ return openBlock(), createElementBlock("div", {
3038
+ key: item.id,
3039
+ "data-item-id": item.id,
3040
+ class: normalizeClass(["mint-batch-progress__item", `mint-batch-progress__item--${item.status}`])
3041
+ }, [renderSlot(_ctx.$slots, "item", { item }, () => [createElementVNode("div", _hoisted_22, [
3042
+ (openBlock(), createBlock(resolveDynamicComponent(STATUS_ICONS[item.status]), {
3043
+ class: normalizeClass(["mint-batch-progress__item-icon", `mint-batch-progress__item-icon--${item.status}`]),
3044
+ size: 16,
3045
+ "aria-hidden": "true"
3046
+ }, null, 8, ["class"])),
3047
+ createElementVNode("span", _hoisted_23, toDisplayString(item.label), 1),
3048
+ createElementVNode("span", _hoisted_24, [
3049
+ createElementVNode("span", _hoisted_25, toDisplayString(statusText(item)), 1),
3050
+ item.status === "error" ? (openBlock(), createElementBlock("button", {
3051
+ key: 0,
3052
+ type: "button",
3053
+ class: "mint-batch-progress__retry-btn",
3054
+ onClick: withModifiers(($event) => emit("retry", item.id), ["stop"])
3055
+ }, " Retry ", 8, _hoisted_26)) : createCommentVNode("", true),
3056
+ item.status === "processing" ? (openBlock(), createElementBlock("button", {
3057
+ key: 1,
3058
+ type: "button",
3059
+ class: "mint-batch-progress__cancel-btn",
3060
+ onClick: withModifiers(($event) => emit("cancel", item.id), ["stop"])
3061
+ }, " Cancel ", 8, _hoisted_27)) : createCommentVNode("", true)
3062
+ ]),
3063
+ createElementVNode("span", _hoisted_28, [createElementVNode("span", {
3064
+ class: "mint-batch-progress__item-progress-bar",
3065
+ style: normalizeStyle({ width: `${itemPercent(item)}%` })
3066
+ }, null, 4)])
3067
+ ]), item.status === "error" && item.message ? (openBlock(), createElementBlock("div", _hoisted_29, toDisplayString(item.message), 1)) : createCommentVNode("", true)])], 10, _hoisted_21);
3068
+ }), 128)), summary.value.completed ? (openBlock(), createElementBlock("button", {
3069
+ key: 0,
3070
+ type: "button",
3071
+ class: "mint-batch-progress__completed-toggle",
3072
+ onClick: _cache[0] || (_cache[0] = ($event) => showCompleted.value = !showCompleted.value)
3073
+ }, toDisplayString(showCompleted.value ? "Hide completed" : `Show ${summary.value.completed} completed`), 1)) : createCommentVNode("", true)], 4))
3074
+ ]);
3075
+ };
3076
+ }
3077
+ });
3078
+ //#endregion
3079
+ //#region src/components/VersionConflictDialog.vue?vue&type=script&setup=true&lang.ts
3080
+ var _hoisted_1$1 = { class: "mint-version-conflict__message" };
3081
+ var _hoisted_2$1 = { class: "mint-version-conflict__footer" };
3082
+ //#endregion
3083
+ //#region src/components/VersionConflictDialog.vue
3084
+ var VersionConflictDialog_default = /* @__PURE__ */ defineComponent({
3085
+ __name: "VersionConflictDialog",
3086
+ props: {
3087
+ open: { type: Boolean },
3088
+ entity: { default: "" },
3089
+ changes: { default: () => [] },
3090
+ loading: {
3091
+ type: Boolean,
3092
+ default: false
3093
+ }
3094
+ },
3095
+ emits: [
3096
+ "update:open",
3097
+ "reload",
3098
+ "overwrite"
3099
+ ],
3100
+ setup(__props, { emit: __emit }) {
3101
+ /**
3102
+ * The dialog for a save refused with 412 `<entity>.version_conflict` (RFC #439).
3103
+ * Show it while `useVersionedSave().conflict` is set: Reload discards the
3104
+ * user's edits and refetches, Overwrite saves them on top of the newer version.
3105
+ */
3106
+ const props = __props;
3107
+ const emit = __emit;
3108
+ const message = computed(() => {
3109
+ const count = props.changes.length;
3110
+ if (count === 0) return "Your changes are not saved. Pick what to keep.";
3111
+ const [noun, verb] = count === 1 ? ["change", "is"] : ["changes", "are"];
3112
+ return `Your ${count} ${noun} (${props.changes.join(", ")}) ${verb} not saved. Pick what to keep.`;
3113
+ });
3114
+ return (_ctx, _cache) => {
3115
+ return openBlock(), createBlock(BaseModal_default, {
3116
+ open: __props.open,
3117
+ title: "Changed by someone else",
3118
+ subtitle: __props.entity || void 0,
3119
+ size: "md",
3120
+ closable: !__props.loading,
3121
+ "close-on-escape": !__props.loading,
3122
+ "onUpdate:open": _cache[2] || (_cache[2] = ($event) => emit("update:open", $event))
3123
+ }, {
3124
+ footer: withCtx(() => [createElementVNode("div", _hoisted_2$1, [createVNode(BaseButton_default, {
3125
+ type: "button",
3126
+ variant: "secondary",
3127
+ size: "sm",
3128
+ class: "mint-version-conflict__overwrite",
3129
+ loading: __props.loading,
3130
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("overwrite"))
3131
+ }, {
3132
+ default: withCtx(() => [..._cache[3] || (_cache[3] = [createTextVNode(" Overwrite ", -1)])]),
3133
+ _: 1
3134
+ }, 8, ["loading"]), createVNode(BaseButton_default, {
3135
+ type: "button",
3136
+ variant: "primary",
3137
+ size: "sm",
3138
+ icon: unref(RefreshCw),
3139
+ disabled: __props.loading,
3140
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("reload"))
3141
+ }, {
3142
+ default: withCtx(() => [..._cache[4] || (_cache[4] = [createTextVNode(" Reload and discard mine ", -1)])]),
3143
+ _: 1
3144
+ }, 8, ["icon", "disabled"])])]),
3145
+ default: withCtx(() => [createElementVNode("p", _hoisted_1$1, toDisplayString(message.value), 1)]),
3146
+ _: 1
3147
+ }, 8, [
3148
+ "open",
3149
+ "subtitle",
3150
+ "closable",
3151
+ "close-on-escape"
3152
+ ]);
3153
+ };
3154
+ }
3155
+ });
3156
+ //#endregion
3157
+ //#region src/components/StaleFormBanner.vue?vue&type=script&setup=true&lang.ts
3158
+ var _hoisted_1 = {
3159
+ class: "mint-stale-banner",
3160
+ role: "status"
3161
+ };
3162
+ var _hoisted_2 = { class: "mint-stale-banner__text" };
3163
+ var _hoisted_3 = ["datetime"];
3164
+ var _hoisted_4 = ["href"];
3165
+ //#endregion
3166
+ //#region src/components/StaleFormBanner.vue
3167
+ var StaleFormBanner_default = /* @__PURE__ */ defineComponent({
3168
+ __name: "StaleFormBanner",
3169
+ props: {
3170
+ change: {},
3171
+ reviewHref: { default: "" },
3172
+ entityNoun: { default: "this record" }
3173
+ },
3174
+ emits: ["reload", "dismiss"],
3175
+ setup(__props, { emit: __emit }) {
3176
+ /**
3177
+ * The strip at the top of a form whose entity someone else changed while the
3178
+ * form held unsaved edits (RFC #439). Show it while `useVersionedSave().stale`
3179
+ * is set. It offers Reload and Review, not Overwrite: a save that would
3180
+ * overwrite gets the 412 dialog.
3181
+ */
3182
+ const props = __props;
3183
+ const emit = __emit;
3184
+ /** "A", "A and B", "A, B and C". */
3185
+ function list(items) {
3186
+ if (items.length <= 1) return items[0] ?? "";
3187
+ return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
3188
+ }
3189
+ const summary = computed(() => {
3190
+ const { actors, fields } = props.change;
3191
+ const what = fields.length === 0 ? props.entityNoun : fields.length <= 2 ? list(fields) : `${fields.length} fields`;
3192
+ return `${list(actors)} changed ${what}`;
3193
+ });
3194
+ const now = ref(Date.now());
3195
+ const timer = setInterval(() => {
3196
+ now.value = Date.now();
3197
+ }, 5e3);
3198
+ onBeforeUnmount(() => clearInterval(timer));
3199
+ const ago = computed(() => {
3200
+ const seconds = Math.max(0, Math.round((now.value - Date.parse(props.change.at)) / 1e3));
3201
+ if (seconds < 60) return `${seconds} s ago`;
3202
+ const minutes = Math.round(seconds / 60);
3203
+ if (minutes < 60) return `${minutes} min ago`;
3204
+ return `${Math.round(minutes / 60)} h ago`;
3205
+ });
3206
+ return (_ctx, _cache) => {
3207
+ return openBlock(), createElementBlock("div", _hoisted_1, [
3208
+ createVNode(unref(TriangleAlert), {
3209
+ class: "mint-stale-banner__icon",
3210
+ "aria-hidden": "true"
3211
+ }),
3212
+ createElementVNode("p", _hoisted_2, [
3213
+ createElementVNode("strong", null, toDisplayString(summary.value), 1),
3214
+ _cache[2] || (_cache[2] = createTextVNode(" " + toDisplayString(" "), -1)),
3215
+ createElementVNode("time", {
3216
+ class: "mint-stale-banner__time",
3217
+ datetime: __props.change.at
3218
+ }, toDisplayString(ago.value), 9, _hoisted_3),
3219
+ _cache[3] || (_cache[3] = createTextVNode(". This form shows the older version. ", -1))
3220
+ ]),
3221
+ __props.reviewHref ? (openBlock(), createElementBlock("a", {
3222
+ key: 0,
3223
+ class: "mint-stale-banner__action",
3224
+ href: __props.reviewHref,
3225
+ target: "_blank",
3226
+ rel: "noopener"
3227
+ }, "Review", 8, _hoisted_4)) : createCommentVNode("", true),
3228
+ createElementVNode("button", {
3229
+ type: "button",
3230
+ class: "mint-stale-banner__action",
3231
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("reload"))
3232
+ }, "Reload form"),
3233
+ createElementVNode("button", {
3234
+ type: "button",
3235
+ class: "mint-stale-banner__dismiss",
3236
+ "aria-label": "Dismiss until the next change",
3237
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("dismiss"))
3238
+ }, [createVNode(unref(X), { "aria-hidden": "true" })])
3239
+ ]);
3240
+ };
3241
+ }
3242
+ });
3243
+ //#endregion
3244
+ //#region src/composables/useVersionedSave.ts
3245
+ /** `end_date` → "End date". */
3246
+ function fieldNameLabel(field) {
3247
+ const words = field.replace(/_/g, " ").trim();
3248
+ return words.charAt(0).toUpperCase() + words.slice(1);
3249
+ }
3250
+ function conflictVersion(cause) {
3251
+ if (!(cause instanceof MintApiError) || cause.status !== 412) return null;
3252
+ const version = cause.details?.version;
3253
+ return { version: typeof version === "number" ? version : null };
3254
+ }
3255
+ function union(list, items) {
3256
+ return [.../* @__PURE__ */ new Set([...list, ...items])];
3257
+ }
3258
+ /**
3259
+ * Optimistic-concurrency save for a form on a versioned entity (RFC #439).
3260
+ *
3261
+ * `save()` sends `If-Match` with the loaded version. A 412 sets `conflict`;
3262
+ * show `VersionConflictDialog` for it, whose Reload calls `reload()` and
3263
+ * whose Overwrite calls `overwrite()` (the same save, with the version from
3264
+ * the 412). With a `topic`, a realtime write by someone else on the entity
3265
+ * sets `stale` while the form is dirty (show `StaleFormBanner`); on a clean
3266
+ * form it runs the `reload` option instead. Several writes merge into one
3267
+ * `stale`.
3268
+ *
3269
+ * @example
3270
+ * const versioned = useVersionedSave({
3271
+ * version: () => experiment.value?.version,
3272
+ * save: headers => api.patch(`/experiments/${id}`, payload(), { headers }),
3273
+ * reload: () => refetchAndResetForm(),
3274
+ * topic: () => (realtime && open.value ? `experiment:${id}` : null),
3275
+ * dirty: () => changeCount.value > 0,
3276
+ * })
3277
+ */
3278
+ function useVersionedSave(options) {
3279
+ const saving = ref(false);
3280
+ const conflict = ref(null);
3281
+ const stale = ref(null);
3282
+ const fieldLabel = options.fieldLabel ?? fieldNameLabel;
3283
+ async function send(version) {
3284
+ saving.value = true;
3285
+ try {
3286
+ const headers = version == null ? {} : { "If-Match": `"v${version}"` };
3287
+ const result = await options.save(headers);
3288
+ conflict.value = null;
3289
+ stale.value = null;
3290
+ return result;
3291
+ } catch (cause) {
3292
+ const refused = conflictVersion(cause);
3293
+ if (!refused) throw cause;
3294
+ conflict.value = refused;
3295
+ return;
3296
+ } finally {
3297
+ saving.value = false;
3298
+ }
3299
+ }
3300
+ async function reload() {
3301
+ await options.reload();
3302
+ conflict.value = null;
3303
+ stale.value = null;
3304
+ }
3305
+ function onEvent(event) {
3306
+ if (event.type === "resync" || saving.value || event.topic !== toValue(options.topic)) return;
3307
+ if (!toValue(options.dirty)) {
3308
+ reload().catch(() => {});
3309
+ return;
3310
+ }
3311
+ const actor = event.actor_id && options.userName?.(event.actor_id) || "Someone";
3312
+ const previous = stale.value;
3313
+ stale.value = {
3314
+ actors: union(previous?.actors ?? [], [actor]),
3315
+ fields: union(previous?.fields ?? [], (event.fields ?? []).map(fieldLabel)),
3316
+ at: event.at ?? (/* @__PURE__ */ new Date()).toISOString()
3317
+ };
3318
+ }
3319
+ useRealtime(() => {
3320
+ const topic = toValue(options.topic);
3321
+ return topic ? [topic] : [];
3322
+ }, onEvent);
3323
+ watch(() => toValue(options.topic), () => {
3324
+ conflict.value = null;
3325
+ stale.value = null;
3326
+ });
3327
+ return {
3328
+ save: () => send(toValue(options.version)),
3329
+ overwrite: () => send(conflict.value?.version),
3330
+ reload,
3331
+ dismissConflict: () => {
3332
+ conflict.value = null;
3333
+ },
3334
+ dismissStale: () => {
3335
+ stale.value = null;
3336
+ },
3337
+ saving: readonly(saving),
3338
+ conflict: readonly(conflict),
3339
+ stale: readonly(stale)
3340
+ };
3341
+ }
3342
+ //#endregion
3343
+ //#region src/composables/createFilePickerAdapter.ts
3344
+ /** Opaque picker identity; decode before passing a selection to a mount-scoped API. */
3345
+ function encodePlatformPickerPath(mountId, path = "") {
3346
+ return JSON.stringify([mountId, path]);
3347
+ }
3348
+ function decodePlatformPickerPath(path) {
3349
+ const decoded = JSON.parse(path);
3350
+ if (!Array.isArray(decoded) || decoded.length !== 2 || !decoded.every((item) => typeof item === "string") || !decoded[0]) throw new Error("Invalid platform picker path.");
3351
+ return {
3352
+ mountId: decoded[0],
3353
+ path: decoded[1]
3354
+ };
3355
+ }
3356
+ function isRelativePath(path) {
3357
+ return path === "" || !/^[a-z]:/i.test(path) && !/[\\\x00-\x1f\x7f]/.test(path) && path.split("/").every((part) => part !== "" && part !== "." && part !== "..");
3358
+ }
3359
+ /** Mount browsing and search shared by platform and generated plugin clients. */
3360
+ function createFilePickerAdapter(transport, options = {}) {
3361
+ const root = options.rootLocation && { ...options.rootLocation };
3362
+ const mountLabels = /* @__PURE__ */ new Map();
3363
+ const listings = /* @__PURE__ */ new Map();
3364
+ let generation = 0;
3365
+ function invalidate() {
3366
+ generation++;
3367
+ listings.clear();
3368
+ mountLabels.clear();
3369
+ }
3370
+ function validateLocation(location) {
3371
+ if (!location.mount_id || !isRelativePath(location.path) || root && (location.mount_id !== root.mount_id || root.path !== "" && location.path !== root.path && !location.path.startsWith(`${root.path}/`))) throw new Error("Invalid file picker location.");
3372
+ }
3373
+ if (root) validateLocation(root);
3374
+ function begin(request) {
3375
+ request?.signal?.throwIfAborted();
3376
+ if (request?.refresh) invalidate();
3377
+ return generation;
3378
+ }
3379
+ function check(request, token) {
3380
+ request?.signal?.throwIfAborted();
3381
+ if (token !== generation) throw new DOMException("Cancelled", "AbortError");
3382
+ }
3383
+ async function read(path, request, token) {
3384
+ check(request, token);
3385
+ const decoded = path ? decodePlatformPickerPath(path) : void 0;
3386
+ const location = decoded ? {
3387
+ mount_id: decoded.mountId,
3388
+ path: decoded.path
3389
+ } : root;
3390
+ if (location) validateLocation(location);
3391
+ const key = location ? encodePlatformPickerPath(location.mount_id, location.path) : "";
3392
+ const cached = listings.get(key);
3393
+ if (cached) return cached;
3394
+ let nodes;
3395
+ if (!location) {
3396
+ const mounts = await transport.listMounts(request);
3397
+ check(request, token);
3398
+ nodes = mounts.map((mount) => {
3399
+ mountLabels.set(mount.id, mount.label);
3400
+ return {
3401
+ kind: "folder",
3402
+ path: encodePlatformPickerPath(mount.id),
3403
+ name: mount.label,
3404
+ parentPath: null,
3405
+ parentName: "Server mounts",
3406
+ unavailableReason: mount.available === false ? "This mount is offline." : void 0
3407
+ };
3408
+ });
3409
+ } else if (transport.tree) {
3410
+ const tree = await transport.tree(location, request);
3411
+ check(request, token);
3412
+ for (const listing of tree.listings ?? []) {
3413
+ const at = {
3414
+ mount_id: listing.mount_id,
3415
+ path: listing.path
3416
+ };
3417
+ validateLocation(at);
3418
+ if (at.mount_id !== location.mount_id) throw new Error("Invalid file picker directory entry.");
3419
+ if (!listing.truncated) listings.set(encodePlatformPickerPath(at.mount_id, at.path), toNodes(at, listing));
3420
+ }
3421
+ const cached = listings.get(key);
3422
+ if (!cached) throw new Error("This directory exceeds the server listing limit. Choose a smaller data directory.");
3423
+ return cached;
3424
+ } else {
3425
+ const listing = await transport.browse(location, request);
3426
+ check(request, token);
3427
+ if (listing.truncated) throw new Error("This directory exceeds the server listing limit. Choose a smaller data directory.");
3428
+ nodes = toNodes(location, listing);
3429
+ }
3430
+ listings.set(key, nodes);
3431
+ return nodes;
3432
+ }
3433
+ function toNodes(location, listing) {
3434
+ return (listing.entries ?? []).map((entry) => {
3435
+ if (entry.path !== `${location.path ? `${location.path}/` : ""}${entry.name}`) throw new Error("Invalid file picker directory entry.");
3436
+ return toNode(location.mount_id, entry);
3437
+ });
3438
+ }
3439
+ /** One entry, parented on the folder its mount-relative path names. */
3440
+ function toNode(mountId, entry) {
3441
+ if (!isRelativePath(entry.path) || !entry.name || entry.name.includes("/") || entry.path !== entry.name && !entry.path.endsWith(`/${entry.name}`)) throw new Error("Invalid file picker directory entry.");
3442
+ const parent = entry.path.slice(0, Math.max(0, entry.path.length - entry.name.length - 1));
3443
+ const atRoot = root && mountId === root.mount_id && parent === root.path;
3444
+ const common = {
3445
+ path: encodePlatformPickerPath(mountId, entry.path),
3446
+ parentPath: atRoot ? null : encodePlatformPickerPath(mountId, parent),
3447
+ parentName: `${mountLabels.get(mountId) ?? mountId}${parent ? ` / ${parent}` : ""}`,
3448
+ name: entry.name,
3449
+ bytes: entry.size_bytes,
3450
+ modifiedAt: entry.modified_at
3451
+ };
3452
+ return entry.kind === "directory" ? {
3453
+ ...common,
3454
+ kind: "folder"
3455
+ } : {
3456
+ ...common,
3457
+ kind: "file",
3458
+ format: pickerFormat(entry.name)
3459
+ };
3460
+ }
3461
+ async function serverSearch(query, scope, request) {
3462
+ const token = begin(request);
3463
+ const decoded = scope.path ? decodePlatformPickerPath(scope.path) : void 0;
3464
+ const location = decoded ? {
3465
+ mount_id: decoded.mountId,
3466
+ path: decoded.path
3467
+ } : root;
3468
+ validateLocation(location);
3469
+ const result = await transport.search(location, query, scope.recursive, request);
3470
+ check(request, token);
3471
+ const prefix = location.path ? `${location.path}/` : "";
3472
+ const nodes = (result.entries ?? []).map((entry) => {
3473
+ if (!entry.path.startsWith(prefix) || !scope.recursive && entry.path.slice(prefix.length).includes("/")) throw new Error("Invalid file picker directory entry.");
3474
+ return toNode(location.mount_id, entry);
3475
+ });
3476
+ return result.truncated ? {
3477
+ nodes,
3478
+ truncated: true
3479
+ } : nodes;
3480
+ }
3481
+ return {
3482
+ mounts: !root,
3483
+ mountOf(path) {
3484
+ try {
3485
+ return encodePlatformPickerPath(decodePlatformPickerPath(path).mountId);
3486
+ } catch {
3487
+ return;
3488
+ }
3489
+ },
3490
+ invalidate,
3491
+ listRoot: async (request) => read("", request, begin(request)),
3492
+ listChildren: async (path, request) => read(path, request, begin(request)),
3493
+ async search(query, scope, request) {
3494
+ if (transport.search && (scope.path || root)) return serverSearch(query, scope, request);
3495
+ const token = begin(request);
3496
+ const matches = [];
3497
+ const queue = [scope.path];
3498
+ const visited = /* @__PURE__ */ new Set();
3499
+ const needle = query.toLowerCase();
3500
+ let currentRequest = request;
3501
+ while (queue.length) {
3502
+ check(request, token);
3503
+ const path = queue.shift();
3504
+ if (visited.has(path)) continue;
3505
+ visited.add(path);
3506
+ if (visited.size > 500) return {
3507
+ nodes: matches,
3508
+ truncated: true
3509
+ };
3510
+ const nodes = await read(path, currentRequest, token);
3511
+ check(request, token);
3512
+ if (currentRequest?.refresh) {
3513
+ currentRequest = { ...currentRequest };
3514
+ delete currentRequest.refresh;
3515
+ }
3516
+ for (const node of nodes) {
3517
+ if (node.name.toLowerCase().includes(needle)) matches.push(node);
3518
+ if (scope.recursive && node.kind === "folder" && !node.unavailableReason) queue.push(node.path);
3519
+ }
3520
+ if (matches.length > 1e4) return {
3521
+ nodes: matches.slice(0, 1e4),
3522
+ truncated: true
3523
+ };
3524
+ }
3525
+ return matches;
3526
+ }
3527
+ };
3528
+ }
3529
+ //#endregion
3530
+ //#region src/composables/usePlatformFilePickerAdapter.ts
3531
+ function query(location, request) {
3532
+ const params = new URLSearchParams({
3533
+ mount_id: location.mount_id,
3534
+ path: location.path
3535
+ });
3536
+ if (request?.refresh) params.set("refresh", "true");
3537
+ return params;
3538
+ }
3539
+ /** Adapt the authenticated MINT mount API without exposing absolute host paths. */
3540
+ function usePlatformFilePickerAdapter(options = {}) {
3541
+ const api = useApi({ baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl });
3542
+ return createFilePickerAdapter({
3543
+ async listMounts(request) {
3544
+ try {
3545
+ const url = `/filesystem/mounts${request?.refresh ? "?refresh=true" : ""}`;
3546
+ const { mounts } = await api.get(url, { signal: request?.signal });
3547
+ return mounts;
3548
+ } catch (cause) {
3549
+ if (cause instanceof MintApiError && cause.status === 404) return [];
3550
+ throw cause;
3551
+ }
3552
+ },
3553
+ browse(location, request) {
3554
+ return api.get(`/filesystem/browse?${query(location, request)}`, { signal: request?.signal });
3555
+ },
3556
+ tree(location, request) {
3557
+ return api.get(`/filesystem/tree?${query(location, request)}`, { signal: request?.signal });
3558
+ },
3559
+ search(location, text, recursive, request) {
3560
+ const params = query(location, request);
3561
+ params.set("q", text);
3562
+ params.set("recursive", String(recursive));
3563
+ return api.get(`/filesystem/search?${params}`, { signal: request?.signal });
3564
+ }
3565
+ }, options);
3566
+ }
3567
+ //#endregion
3568
+ //#region src/composables/useFileBrowser.ts
3569
+ /**
3570
+ * Browses the platform's `/api/filesystem` routes for a custom file UI. For the
3571
+ * ready-made dialog, use `FilePicker` with `usePlatformFilePickerAdapter()`.
3572
+ *
3573
+ * Requires the `filesystem.browse` permission and a deployment that declares
3574
+ * mounts; with none configured the routes are absent and `mounts` stays empty.
3575
+ */
3576
+ function useFileBrowser(options = {}) {
3577
+ const injectedContext = getInjectedPlatformContext();
3578
+ const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl });
3579
+ const sync = useRequest("Could not read the server mounts.");
3580
+ const mounts = ref([]);
3581
+ const mountId = shallowRef("");
3582
+ const path = shallowRef("");
3583
+ const parent = ref(null);
3584
+ const entries = ref([]);
3585
+ const breadcrumbs = ref([]);
3586
+ const totalCount = ref(null);
3587
+ const matchCount = ref(null);
3588
+ const shownCount = ref(null);
3589
+ const truncated = ref(false);
3590
+ const search = shallowRef("");
3591
+ const sort = shallowRef("name");
3592
+ const selected = ref([]);
3593
+ const mountsLoading = shallowRef(false);
3594
+ const listingLoading = shallowRef(false);
3595
+ const listingReady = shallowRef(false);
3596
+ const isLoading = computed(() => mountsLoading.value || listingLoading.value);
3597
+ let listToken = 0;
3598
+ let mountsToken = 0;
3599
+ let disposed = false;
3600
+ let listingController;
3601
+ let mountsController;
3602
+ let searchTimer;
3603
+ function resetListing() {
3604
+ listingReady.value = false;
3605
+ entries.value = [];
3606
+ breadcrumbs.value = [];
3607
+ parent.value = null;
3608
+ totalCount.value = null;
3609
+ matchCount.value = null;
3610
+ shownCount.value = null;
3611
+ truncated.value = false;
3612
+ }
3613
+ function typeRuleParams() {
3614
+ return (toValue(options.typeRules) ?? []).map((rule) => `&type_rules=${encodeURIComponent(rule)}`).join("");
3615
+ }
3616
+ function fail(cause, fallback) {
3617
+ sync.setError(new Error(sync.readErrorMessage(cause, fallback)), fallback);
3618
+ }
3619
+ function statusOf(cause) {
3620
+ return cause instanceof MintApiError ? cause.status : void 0;
3621
+ }
3622
+ async function loadMounts() {
3623
+ const token = ++mountsToken;
3624
+ mountsController?.abort();
3625
+ mountsController = new AbortController();
3626
+ mountsLoading.value = true;
3627
+ try {
3628
+ const data = await api.get("/filesystem/mounts", { signal: mountsController.signal });
3629
+ if (disposed || token !== mountsToken) return false;
3630
+ mounts.value = data.mounts ?? [];
3631
+ sync.clearError();
3632
+ return true;
3633
+ } catch (cause) {
3634
+ if (disposed || token !== mountsToken) return false;
3635
+ if (statusOf(cause) === 404) {
3636
+ mounts.value = [];
3637
+ sync.clearError();
3638
+ return true;
3639
+ }
3640
+ mounts.value = [];
3641
+ fail(cause, "Could not read the server mounts.");
3642
+ return false;
3643
+ } finally {
3644
+ if (token === mountsToken) mountsLoading.value = false;
3645
+ }
3646
+ }
3647
+ async function navigate(location, refresh = false) {
3648
+ if (disposed || !location.mount_id) return;
3649
+ clearTimeout(searchTimer);
3650
+ const token = ++listToken;
3651
+ listingController?.abort();
3652
+ listingController = new AbortController();
3653
+ listingLoading.value = true;
3654
+ mountId.value = location.mount_id;
3655
+ path.value = location.path;
3656
+ resetListing();
3657
+ sync.clearError();
3658
+ try {
3659
+ const query = `mount_id=${encodeURIComponent(location.mount_id)}&path=${encodeURIComponent(location.path)}&sort=${sort.value}` + (refresh ? "&refresh=true" : "") + (search.value ? `&search=${encodeURIComponent(search.value)}` : "") + typeRuleParams();
3660
+ const listing = await api.get(`/filesystem/browse?${query}`, { signal: listingController.signal });
3661
+ if (disposed || token !== listToken) return;
3662
+ mountId.value = listing.mount_id;
3663
+ path.value = listing.path;
3664
+ parent.value = listing.parent ?? null;
3665
+ entries.value = listing.entries ?? [];
3666
+ breadcrumbs.value = listing.breadcrumbs ?? [];
3667
+ totalCount.value = listing.total_count ?? null;
3668
+ matchCount.value = listing.match_count ?? null;
3669
+ shownCount.value = listing.shown_count ?? listing.entries?.length ?? null;
3670
+ truncated.value = listing.truncated ?? false;
3671
+ listingReady.value = true;
3672
+ sync.clearError();
3673
+ } catch (cause) {
3674
+ if (disposed || token !== listToken) return;
3675
+ entries.value = [];
3676
+ fail(cause, "Could not read that folder.");
3677
+ } finally {
3678
+ if (token === listToken) listingLoading.value = false;
3679
+ }
3680
+ }
3681
+ async function init() {
3682
+ if (disposed) return;
3683
+ const navigationToken = listToken;
3684
+ if (!await loadMounts() || disposed || navigationToken !== listToken) return;
3685
+ const preferred = toValue(options.initialMountId);
3686
+ const reachable = (mount) => mount.available !== false;
3687
+ const target = mounts.value.find((mount) => mount.id === preferred && reachable(mount)) ?? mounts.value.find(reachable);
3688
+ if (target) await navigate({
3689
+ mount_id: target.id,
3690
+ path: ""
3691
+ });
3692
+ else {
3693
+ mountId.value = "";
3694
+ path.value = "";
3695
+ resetListing();
3696
+ }
3697
+ }
3698
+ async function refresh() {
3699
+ if (disposed) return;
3700
+ if (mountId.value) await navigate({
3701
+ mount_id: mountId.value,
3702
+ path: path.value
3703
+ }, true);
3704
+ else await init();
3705
+ }
3706
+ function selectCurrentFolder() {
3707
+ if (!mountId.value || !listingReady.value || isLoading.value || sync.error.value) return;
3708
+ const crumb = breadcrumbs.value[breadcrumbs.value.length - 1];
3709
+ const key = JSON.stringify([mountId.value, path.value]);
3710
+ if (selected.value.some((item) => JSON.stringify([item.mount_id, item.path]) === key)) return;
3711
+ selected.value = [...selected.value, {
3712
+ mount_id: mountId.value,
3713
+ path: path.value,
3714
+ name: crumb?.label ?? path.value,
3715
+ kind: "directory",
3716
+ child_count: totalCount.value
3717
+ }];
3718
+ }
3719
+ function clear() {
3720
+ selected.value = [];
3721
+ }
3722
+ watch(search, () => {
3723
+ if (!mountId.value) return;
3724
+ clearTimeout(searchTimer);
3725
+ searchTimer = setTimeout(() => {
3726
+ navigate({
3727
+ mount_id: mountId.value,
3728
+ path: path.value
3729
+ });
3730
+ }, 250);
3731
+ });
3732
+ watch(sort, () => {
3733
+ if (mountId.value) navigate({
3734
+ mount_id: mountId.value,
3735
+ path: path.value
3736
+ });
3737
+ });
3738
+ watch(() => toValue(options.typeRules), () => {
3739
+ if (mountId.value) navigate({
3740
+ mount_id: mountId.value,
3741
+ path: path.value
3742
+ });
3743
+ }, { deep: true });
3744
+ if (getCurrentScope()) onScopeDispose(() => {
3745
+ disposed = true;
3746
+ ++listToken;
3747
+ ++mountsToken;
3748
+ clearTimeout(searchTimer);
3749
+ listingController?.abort();
3750
+ mountsController?.abort();
3751
+ });
3752
+ return {
3753
+ mounts,
3754
+ mountId,
3755
+ path,
3756
+ parent,
3757
+ entries,
3758
+ breadcrumbs,
3759
+ totalCount,
3760
+ matchCount,
3761
+ shownCount,
3762
+ truncated,
3763
+ search,
3764
+ sort,
3765
+ selected,
3766
+ isLoading,
3767
+ error: sync.error,
3768
+ init,
3769
+ navigate,
3770
+ refresh,
3771
+ selectCurrentFolder,
3772
+ clear
3773
+ };
3774
+ }
3775
+ //#endregion
3776
+ //#region src/composables/analysisArtifactTarget.ts
3777
+ var EXPERIMENT_ID = "experiment_id";
3778
+ var ARTIFACT_PLUGIN_ID = "artifact_plugin_id";
3779
+ var ARTIFACT_KEY = "artifact_key";
3780
+ var ARTIFACT_ID = "artifact_id";
3781
+ function parsePositiveInteger(value) {
3782
+ if (value === null || !/^\d+$/.test(value)) return void 0;
3783
+ const parsed = Number(value);
3784
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
3785
+ }
3786
+ /** Build the canonical query used to open one analysis artifact in a plugin. */
3787
+ function buildAnalysisArtifactTargetQuery(target) {
3788
+ const query = new URLSearchParams();
3789
+ query.set(EXPERIMENT_ID, String(target.experimentId));
3790
+ query.set(ARTIFACT_PLUGIN_ID, target.artifactPluginId);
3791
+ query.set(ARTIFACT_KEY, target.artifactKey);
3792
+ if (target.artifactId !== void 0) query.set(ARTIFACT_ID, String(target.artifactId));
3793
+ return query;
3794
+ }
3795
+ /** Parse a complete canonical target. Incomplete or invalid input is not guessed. */
3796
+ function parseAnalysisArtifactTarget(query) {
3797
+ const experimentId = parsePositiveInteger(query.get(EXPERIMENT_ID));
3798
+ const artifactPluginId = query.get(ARTIFACT_PLUGIN_ID)?.trim();
3799
+ const artifactKey = query.get(ARTIFACT_KEY)?.trim();
3800
+ const rawArtifactId = query.get(ARTIFACT_ID);
3801
+ const artifactId = parsePositiveInteger(rawArtifactId);
3802
+ if (experimentId === void 0 || !artifactPluginId || !artifactKey || rawArtifactId !== null && artifactId === void 0) return null;
3803
+ return {
3804
+ experimentId,
3805
+ artifactPluginId,
3806
+ artifactKey,
3807
+ ...artifactId === void 0 ? {} : { artifactId }
3808
+ };
3809
+ }
3810
+ //#endregion
3811
+ //#region src/data.ts
3812
+ var data_exports = /* @__PURE__ */ __exportAll({
3813
+ ANALYSIS_FILE_ARTIFACT_SCHEMA: () => ANALYSIS_FILE_ARTIFACT_SCHEMA,
3814
+ APP_EXPERIMENT_KEY: () => APP_EXPERIMENT_KEY,
3815
+ ArtifactSaveDialog: () => ArtifactSaveDialog_default,
3816
+ ArtifactSelectorModal: () => ArtifactSelectorModal_default,
3817
+ AuditTrail: () => AuditTrail_default,
3818
+ BatchProgressList: () => BatchProgressList_default,
3819
+ DATE_PRESET_OPTIONS: () => DATE_PRESET_OPTIONS,
3820
+ ExperimentCodeBadge: () => ExperimentCodeBadge_default,
3821
+ ExperimentPopover: () => ExperimentPopover_default,
3822
+ ExperimentSelectorModal: () => ExperimentSelectorModal_default,
3823
+ FilePicker: () => FilePicker_default,
3824
+ JobsStatusTray: () => JobsStatusTray_default,
3825
+ SORT_OPTIONS: () => SORT_OPTIONS,
3826
+ SecretInput: () => SecretInput_default,
3827
+ StaleFormBanner: () => StaleFormBanner_default,
3828
+ TagsInput: () => TagsInput_default,
3829
+ VersionConflictDialog: () => VersionConflictDialog_default,
3830
+ buildAnalysisArtifactTargetQuery: () => buildAnalysisArtifactTargetQuery,
3831
+ candidateMatchesSearch: () => candidateMatchesSearch,
3832
+ createFilePickerAdapter: () => createFilePickerAdapter,
3833
+ datePresetToISO: () => datePresetToISO,
3834
+ decodePlatformPickerPath: () => decodePlatformPickerPath,
3835
+ detectDelimiter: () => detectDelimiter,
3836
+ encodePlatformPickerPath: () => encodePlatformPickerPath,
3837
+ experimentIdFromRoute: () => experimentIdFromRoute,
3838
+ experimentStatus: () => experimentStatus_exports,
3839
+ extractSampleNamesFromDesignData: () => extractSampleNamesFromDesignData,
3840
+ extractSampleOptionsFromDesignData: () => extractSampleOptionsFromDesignData,
3841
+ fieldNameLabel: () => fieldNameLabel,
3842
+ formatExperimentDate: () => formatExperimentDate,
3843
+ normalizeSearchQuery: () => normalizeSearchQuery,
3844
+ parseAnalysisArtifactTarget: () => parseAnalysisArtifactTarget,
3845
+ parseDelimitedText: () => parseDelimitedText,
3846
+ pluginSecret: () => pluginSecrets_exports,
3847
+ readFileAsText: () => readFileAsText,
3848
+ resolveExperimentCode: () => resolveExperimentCode,
3849
+ unwrapExperimentDesignData: () => unwrapExperimentDesignData,
3850
+ useAppExperiment: () => useAppExperiment,
3851
+ useArtifacts: () => useArtifacts,
3852
+ useExperimentData: () => useExperimentData,
3853
+ useExperimentSamples: () => useExperimentSamples,
3854
+ useExperimentSelector: () => useExperimentSelector,
3855
+ useFileBrowser: () => useFileBrowser,
3856
+ useFileImport: () => useFileImport,
3857
+ usePlatformFilePickerAdapter: () => usePlatformFilePickerAdapter,
3858
+ useTextSearch: () => useTextSearch,
3859
+ useVersionedSave: () => useVersionedSave,
3860
+ validateImportFile: () => validateImportFile
3861
+ });
3862
+ //#endregion
3863
+ export { ArtifactSaveDialog_default as _, usePlatformFilePickerAdapter as a, encodePlatformPickerPath as c, StaleFormBanner_default as d, VersionConflictDialog_default as f, ExperimentCodeBadge_default as g, FilePicker_default as h, useFileBrowser as i, fieldNameLabel as l, AuditTrail_default as m, buildAnalysisArtifactTargetQuery as n, createFilePickerAdapter as o, BatchProgressList_default as p, parseAnalysisArtifactTarget as r, decodePlatformPickerPath as s, data_exports as t, useVersionedSave as u, ArtifactSelectorModal_default as v, useArtifacts as y };
3864
+
3865
+ //# sourceMappingURL=data-CgILxDLW.js.map