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

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