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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (353) hide show
  1. package/README.md +14 -3
  2. package/dist/{ActionMenu-CScINahU.js → AlertBox-DwgNTEqf.js} +40 -209
  3. package/dist/AlertBox-DwgNTEqf.js.map +1 -0
  4. package/dist/{AppSidebar-BmiiPfz9.js → AppSidebar-CZZaxni0.js} +1249 -239
  5. package/dist/AppSidebar-CZZaxni0.js.map +1 -0
  6. package/dist/{ConfirmDialog-CJ7hqYUP.js → ConfirmDialog-CjWoxAD1.js} +2 -2
  7. package/dist/{ConfirmDialog-CJ7hqYUP.js.map → ConfirmDialog-CjWoxAD1.js.map} +1 -1
  8. package/dist/ExperimentPopover-DujiuQIO.js +337 -0
  9. package/dist/ExperimentPopover-DujiuQIO.js.map +1 -0
  10. package/dist/{ExperimentSelectorModal-D-9z7cNx.js → ExperimentSelectorModal-B4j4eUOJ.js} +244 -211
  11. package/dist/ExperimentSelectorModal-B4j4eUOJ.js.map +1 -0
  12. package/dist/{SegmentedControl-DERBN-Xd.js → SegmentedControl-DyUPH3I5.js} +2 -2
  13. package/dist/{SegmentedControl-DERBN-Xd.js.map → SegmentedControl-DyUPH3I5.js.map} +1 -1
  14. package/dist/{SettingsModal-CSLE31yg.js → SettingsModal-D2MOOtuO.js} +28 -27
  15. package/dist/SettingsModal-D2MOOtuO.js.map +1 -0
  16. package/dist/__stories__/experiment-helpers.d.ts +10 -0
  17. package/dist/__tests__/components/AppTopBarOnboarding.test.d.ts +1 -0
  18. package/dist/__tests__/components/FloatingPanelsInModal.test.d.ts +1 -0
  19. package/dist/__tests__/composables/useFloatingPanel.test.d.ts +1 -0
  20. package/dist/__tests__/composables/useOnboarding.test.d.ts +1 -0
  21. package/dist/__tests__/styles/paletteTokens.test.d.ts +1 -0
  22. package/dist/__tests__/styles/v2AuditRules.test.d.ts +1 -0
  23. package/dist/components/AppSidebar.vue.d.ts +1 -1
  24. package/dist/components/AppTopBar.vue.d.ts +20 -5
  25. package/dist/components/ArtifactSaveDialog.vue.d.ts +9 -1
  26. package/dist/components/AuditTrail.vue.d.ts +1 -1
  27. package/dist/components/BaseButton.vue.d.ts +4 -2
  28. package/dist/components/BaseSelect.vue.d.ts +2 -2
  29. package/dist/components/ChartContainer.vue.d.ts +2 -0
  30. package/dist/components/DatePicker.vue.d.ts +1 -1
  31. package/dist/components/ExperimentPopover.vue.d.ts +18 -0
  32. package/dist/components/ExperimentSelectorModal.vue.d.ts +15 -5
  33. package/dist/components/FilePicker.vue.d.ts +1 -1
  34. package/dist/components/FileUploader.vue.d.ts +2 -2
  35. package/dist/components/JobsStatusTray.vue.d.ts +15 -4
  36. package/dist/components/PluginIcon.vue.d.ts +2 -2
  37. package/dist/components/PluginWorkspaceView.props.d.ts +3 -0
  38. package/dist/components/PluginWorkspaceView.vue.d.ts +3 -0
  39. package/dist/components/SampleSelector.vue.d.ts +6 -6
  40. package/dist/components/SequenceProgressBar.vue.d.ts +3 -0
  41. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +3 -3
  42. package/dist/components/StepWizard.vue.d.ts +3 -0
  43. package/dist/components/internal/AppTopBarOnboardingInternal.vue.d.ts +7 -0
  44. package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -1
  45. package/dist/components/internal/AutoGroupModalInternal.vue.d.ts +3 -3
  46. package/dist/components/internal/DatePickerTimeInternal.vue.d.ts +1 -1
  47. package/dist/components/internal/OnboardingLayerInternal.vue.d.ts +7 -0
  48. package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +2 -7
  49. package/dist/components/internal/SelectMultipleInternal.vue.d.ts +4 -0
  50. package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +8 -0
  51. package/dist/composables/createClient.d.ts +2 -0
  52. package/dist/composables/index.d.ts +1 -0
  53. package/dist/composables/pluginIdentity.d.ts +24 -0
  54. package/dist/composables/pluginWorkspaceModel.d.ts +2 -2
  55. package/dist/composables/useAppExperiment.d.ts +8 -0
  56. package/dist/composables/useCalendarGrid.d.ts +1 -1
  57. package/dist/composables/useExperimentSelector.d.ts +6 -2
  58. package/dist/composables/useFileImport.d.ts +7 -0
  59. package/dist/composables/useFloatingPanel.d.ts +29 -0
  60. package/dist/composables/useJobCenter.d.ts +41 -3
  61. package/dist/composables/useOnboarding.d.ts +119 -0
  62. package/dist/composables/usePlatform.d.ts +1 -0
  63. package/dist/{controlSchemaModel-CP5-_nAj.js → controlSchemaModel-Bsg0t5Ab.js} +2 -2
  64. package/dist/{controlSchemaModel-CP5-_nAj.js.map → controlSchemaModel-Bsg0t5Ab.js.map} +1 -1
  65. package/dist/{data-C2PwI_dn.js → data-Boeh4uPI.js} +180 -125
  66. package/dist/data-Boeh4uPI.js.map +1 -0
  67. package/dist/data.d.ts +1 -1
  68. package/dist/data.js +9 -9
  69. package/dist/{experiment-43OJtH7R.js → experiment-DyO54FQB.js} +25 -61
  70. package/dist/experiment-DyO54FQB.js.map +1 -0
  71. package/dist/{experimentStatus-BZmU4czV.js → experimentStatus-DCn2QKG7.js} +3 -3
  72. package/dist/{experimentStatus-BZmU4czV.js.map → experimentStatus-DCn2QKG7.js.map} +1 -1
  73. package/dist/index.js +20 -20
  74. package/dist/{install-DAID6c2-.js → install-BEHp7Mo-.js} +15 -14
  75. package/dist/{install-DAID6c2-.js.map → install-BEHp7Mo-.js.map} +1 -1
  76. package/dist/install.js +1 -1
  77. package/dist/{lab-DLwRl_Ql.js → lab-D61eaX6f.js} +136 -148
  78. package/dist/lab-D61eaX6f.js.map +1 -0
  79. package/dist/lab.js +4 -3
  80. package/dist/{records-BzTcxekG.js → records-D4aQXyZu.js} +647 -389
  81. package/dist/records-D4aQXyZu.js.map +1 -0
  82. package/dist/{runtime-D6gVat6r.js → runtime-q-R0CNLh.js} +417 -54
  83. package/dist/runtime-q-R0CNLh.js.map +1 -0
  84. package/dist/runtime.d.ts +1 -1
  85. package/dist/runtime.js +7 -7
  86. package/dist/{shell-5ju97nOS.js → shell-6CGFvUUw.js} +10 -5
  87. package/dist/{shell-5ju97nOS.js.map → shell-6CGFvUUw.js.map} +1 -1
  88. package/dist/shell.d.ts +1 -0
  89. package/dist/shell.js +6 -6
  90. package/dist/stores/index.d.ts +1 -1
  91. package/dist/stores/index.js +2 -2
  92. package/dist/stores/settings.d.ts +4 -12
  93. package/dist/styles.css +2753 -2101
  94. package/dist/types/componentWorkflowTypes.d.ts +2 -0
  95. package/dist/types/components.d.ts +2 -0
  96. package/dist/types/generated/platformContext.d.ts +1 -0
  97. package/dist/types/generated-ui.d.ts +4 -0
  98. package/dist/types/index.d.ts +1 -1
  99. package/dist/types/jobs.d.ts +9 -0
  100. package/dist/types/platform.d.ts +1 -1
  101. package/dist/{BaseModal-DfRIuVoj.js → useDropdownState-uFIz6Yv8.js} +75 -5
  102. package/dist/useDropdownState-uFIz6Yv8.js.map +1 -0
  103. package/dist/{useExperimentSamples-CxcnxZ8h.js → useExperimentSamples-QEFoq4tf.js} +48 -6
  104. package/dist/useExperimentSamples-QEFoq4tf.js.map +1 -0
  105. package/dist/{useListReorder-CcbEYxX-.js → useListReorder-DsuzwWGS.js} +219 -437
  106. package/dist/useListReorder-DsuzwWGS.js.map +1 -0
  107. package/dist/{useRackEditor-BD5NIo5s.js → useRackEditor-KpZ4iJ6o.js} +85 -81
  108. package/dist/useRackEditor-KpZ4iJ6o.js.map +1 -0
  109. package/dist/useRealtime-B58Lo-Tm.js +508 -0
  110. package/dist/useRealtime-B58Lo-Tm.js.map +1 -0
  111. package/dist/useRequest-BuDmW8Jc.js +215 -0
  112. package/dist/useRequest-BuDmW8Jc.js.map +1 -0
  113. package/dist/utils/time.d.ts +7 -0
  114. package/package.json +3 -2
  115. package/src/__stories__/experiment-helpers.ts +3 -3
  116. package/src/__tests__/components/ActionMenu.test.ts +2 -2
  117. package/src/__tests__/components/AppTopBar.test.ts +257 -3
  118. package/src/__tests__/components/AppTopBarOnboarding.test.ts +160 -0
  119. package/src/__tests__/components/AppTopBarPageSelector.test.ts +125 -9
  120. package/src/__tests__/components/AppTopBarPillNav.test.ts +35 -0
  121. package/src/__tests__/components/ArtifactSaveDialog.test.ts +78 -0
  122. package/src/__tests__/components/BaseButton.test.ts +19 -0
  123. package/src/__tests__/components/BaseModal.test.ts +13 -0
  124. package/src/__tests__/components/BaseSelect.multiple.test.ts +87 -32
  125. package/src/__tests__/components/BaseSelect.test.ts +2 -1
  126. package/src/__tests__/components/BatchProgressList.test.ts +16 -0
  127. package/src/__tests__/components/CalendarGridPanel.test.ts +2 -2
  128. package/src/__tests__/components/ChartContainer.test.ts +11 -0
  129. package/src/__tests__/components/ChemicalFormula.test.ts +6 -3
  130. package/src/__tests__/components/DatePicker.datetime.test.ts +6 -0
  131. package/src/__tests__/components/DatePicker.test.ts +21 -0
  132. package/src/__tests__/components/DatePicker.time.test.ts +2 -2
  133. package/src/__tests__/components/EmptyState.test.ts +1 -1
  134. package/src/__tests__/components/ExperimentPopover.test.ts +125 -16
  135. package/src/__tests__/components/ExperimentSelectorModal.test.ts +153 -14
  136. package/src/__tests__/components/FloatingPanelsInModal.test.ts +94 -0
  137. package/src/__tests__/components/GeneratedPluginView.test.ts +23 -2
  138. package/src/__tests__/components/InstrumentStateBadge.test.ts +4 -4
  139. package/src/__tests__/components/JobsStatusTray.test.ts +373 -71
  140. package/src/__tests__/components/PluginWorkspaceView.test.ts +63 -1
  141. package/src/__tests__/components/ResourcePickerListInternal.test.ts +0 -20
  142. package/src/__tests__/components/SampleSelector.test.ts +33 -8
  143. package/src/__tests__/components/SequenceProgressBar.test.ts +12 -0
  144. package/src/__tests__/components/SettingsModal.test.ts +9 -2
  145. package/src/__tests__/components/StepWizard.test.ts +11 -0
  146. package/src/__tests__/components/WellPlate.decoration.test.ts +11 -4
  147. package/src/__tests__/components/deprecationTags.test.ts +1 -1
  148. package/src/__tests__/composables/useAppExperiment.test.ts +27 -1
  149. package/src/__tests__/composables/useCalendarGrid.test.ts +4 -2
  150. package/src/__tests__/composables/useExperimentSelector.test.ts +111 -0
  151. package/src/__tests__/composables/useFileImport.test.ts +36 -0
  152. package/src/__tests__/composables/useFloatingPanel.test.ts +68 -0
  153. package/src/__tests__/composables/useJobCenter.test.ts +262 -3
  154. package/src/__tests__/composables/useOnboarding.test.ts +317 -0
  155. package/src/__tests__/docs/frontendDocsCatalog.test.ts +4 -3
  156. package/src/__tests__/fixtures/table-import/cells.xlsx +0 -0
  157. package/src/__tests__/fixtures/table-import/samples.xlsx +0 -0
  158. package/src/__tests__/stores/settings.test.ts +21 -21
  159. package/src/__tests__/styles/baseControlsRules.test.ts +6 -2
  160. package/src/__tests__/styles/controlRefreshRules.test.ts +0 -6
  161. package/src/__tests__/styles/cssTokens.test.ts +6 -3
  162. package/src/__tests__/styles/menusShellRefresh.test.ts +22 -12
  163. package/src/__tests__/styles/paletteTokens.test.ts +44 -0
  164. package/src/__tests__/styles/v2AuditRules.test.ts +104 -0
  165. package/src/__tests__/utils/plotlyTemplate.test.ts +4 -4
  166. package/src/__tests__/utils/time.test.ts +18 -1
  167. package/src/components/ActionMenu.vue +6 -37
  168. package/src/components/AdductText.story.vue +7 -0
  169. package/src/components/AdductText.vue +1 -1
  170. package/src/components/AlertBox.story.vue +2 -2
  171. package/src/components/AlertBox.vue +18 -16
  172. package/src/components/AppSidebar.story.vue +0 -28
  173. package/src/components/AppTopBar.onboarding.story.vue +69 -0
  174. package/src/components/AppTopBar.story.vue +105 -15
  175. package/src/components/AppTopBar.vue +248 -122
  176. package/src/components/ArtifactSaveDialog.story.vue +23 -0
  177. package/src/components/ArtifactSaveDialog.vue +102 -12
  178. package/src/components/AuditTrail.story.vue +2 -1
  179. package/src/components/AuditTrail.vue +2 -21
  180. package/src/components/Avatar.vue +10 -14
  181. package/src/components/BaseButton.vue +16 -2
  182. package/src/components/BaseModal.vue +2 -1
  183. package/src/components/BaseSelect.multiple.story.vue +13 -2
  184. package/src/components/BaseSelect.vue +4 -3
  185. package/src/components/BatchProgressList.vue +35 -25
  186. package/src/components/ChartContainer.story.vue +2 -1
  187. package/src/components/ChartContainer.vue +8 -6
  188. package/src/components/ConfirmDialog.story.vue +1 -1
  189. package/src/components/EmptyState.vue +1 -1
  190. package/src/components/ExperimentPopover.story.vue +165 -53
  191. package/src/components/ExperimentPopover.vue +169 -187
  192. package/src/components/ExperimentSelectorModal.story.vue +73 -31
  193. package/src/components/ExperimentSelectorModal.vue +144 -111
  194. package/src/components/FormField.story.vue +8 -48
  195. package/src/components/GeneratedPluginView.vue +11 -10
  196. package/src/components/GeneratedResultRenderer.vue +3 -4
  197. package/src/components/InstrumentStateBadge.vue +1 -1
  198. package/src/components/JobsStatusTray.story.vue +97 -43
  199. package/src/components/JobsStatusTray.vue +287 -290
  200. package/src/components/LayoutResizeHandle.story.vue +65 -4
  201. package/src/components/LoadingSpinner.vue +1 -1
  202. package/src/components/NumberInput.vue +3 -6
  203. package/src/components/PluginIcon.story.vue +2 -10
  204. package/src/components/PluginIcon.vue +2 -2
  205. package/src/components/PluginWorkspaceView.props.ts +3 -0
  206. package/src/components/PluginWorkspaceView.story.vue +1 -1
  207. package/src/components/PluginWorkspaceView.vue +17 -0
  208. package/src/components/RuntimeUpdateNotice.vue +1 -1
  209. package/src/components/SampleSelector.story.vue +22 -7
  210. package/src/components/SampleSelector.vue +34 -37
  211. package/src/components/SampleSelectorGroupNode.vue +1 -1
  212. package/src/components/SectionCard.story.vue +35 -2
  213. package/src/components/SequenceProgressBar.story.vue +9 -0
  214. package/src/components/SequenceProgressBar.vue +6 -1
  215. package/src/components/SettingsModal.schema.ts +1 -1
  216. package/src/components/SettingsModal.vue +9 -14
  217. package/src/components/SmartGroupFieldRecipe.story.vue +1 -1
  218. package/src/components/SmartGroupManual.story.vue +1 -1
  219. package/src/components/StepWizard.story.vue +33 -0
  220. package/src/components/StepWizard.vue +8 -1
  221. package/src/components/Tooltip.vue +9 -32
  222. package/src/components/WellPlate.story.vue +3 -1
  223. package/src/components/WellPlate.vue +7 -0
  224. package/src/components/internal/AppTopBarOnboardingInternal.vue +172 -0
  225. package/src/components/internal/AppTopBarPageSelectorInternal.vue +140 -72
  226. package/src/components/internal/AppTopBarPillNavInternal.vue +33 -2
  227. package/src/components/internal/DatePickerDateInternal.vue +23 -85
  228. package/src/components/internal/DatePickerDateTimeInternal.vue +10 -14
  229. package/src/components/internal/DatePickerTimeInternal.vue +13 -9
  230. package/src/components/internal/NumberUnitInputInternal.vue +4 -18
  231. package/src/components/internal/OnboardingLayerInternal.vue +173 -0
  232. package/src/components/internal/ResourcePickerListInternal.vue +45 -85
  233. package/src/components/internal/SelectMultipleInternal.vue +187 -65
  234. package/src/components/internal/SelectSearchableInternal.vue +8 -4
  235. package/src/components/internal/WellEditPopupInternal.vue +11 -22
  236. package/src/components/internal/WorkspaceShellInternal.vue +12 -0
  237. package/src/composables/createClient.ts +2 -0
  238. package/src/composables/index.ts +1 -0
  239. package/src/composables/pluginIdentity.ts +31 -0
  240. package/src/composables/pluginWorkspaceModel.ts +2 -2
  241. package/src/composables/useAppExperiment.ts +20 -0
  242. package/src/composables/useCalendarGrid.ts +4 -3
  243. package/src/composables/useExperimentSelector.ts +77 -31
  244. package/src/composables/useFileImport.ts +51 -1
  245. package/src/composables/useFloatingPanel.ts +107 -0
  246. package/src/composables/useJobCenter.ts +188 -6
  247. package/src/composables/useOnboarding.ts +465 -0
  248. package/src/composables/usePluginWorkspace.ts +1 -1
  249. package/src/data.ts +1 -0
  250. package/src/runtime.ts +2 -1
  251. package/src/shell.ts +17 -0
  252. package/src/stores/index.ts +0 -2
  253. package/src/stores/settings.ts +20 -50
  254. package/src/styles/components/action-menu.css +1 -1
  255. package/src/styles/components/adduct-text.css +13 -5
  256. package/src/styles/components/alert-box.css +39 -58
  257. package/src/styles/components/app-layout.css +10 -6
  258. package/src/styles/components/app-page-selector.css +121 -44
  259. package/src/styles/components/app-pill-nav.css +11 -12
  260. package/src/styles/components/app-sidebar.css +9 -18
  261. package/src/styles/components/app-top-bar.css +219 -55
  262. package/src/styles/components/artifact-save-dialog.css +153 -2
  263. package/src/styles/components/audit-trail.css +16 -19
  264. package/src/styles/components/avatar.css +6 -4
  265. package/src/styles/components/batch-progress-list.css +38 -36
  266. package/src/styles/components/button.css +36 -59
  267. package/src/styles/components/calendar.css +12 -12
  268. package/src/styles/components/chart-container.css +34 -23
  269. package/src/styles/components/checkbox.css +2 -12
  270. package/src/styles/components/collapsible-card.css +27 -29
  271. package/src/styles/components/confirm-dialog.css +13 -14
  272. package/src/styles/components/dataframe.css +13 -5
  273. package/src/styles/components/date-picker.css +84 -94
  274. package/src/styles/components/datetime-picker.css +35 -47
  275. package/src/styles/components/divider.css +20 -20
  276. package/src/styles/components/dose-calculator.css +3 -3
  277. package/src/styles/components/empty-state.css +20 -17
  278. package/src/styles/components/experiment-code-badge.css +2 -0
  279. package/src/styles/components/experiment-popover.css +174 -329
  280. package/src/styles/components/experiment-selector-modal.css +88 -56
  281. package/src/styles/components/file-picker.css +3 -3
  282. package/src/styles/components/file-uploader.css +38 -49
  283. package/src/styles/components/form-builder.css +3 -3
  284. package/src/styles/components/formula-input.css +1 -1
  285. package/src/styles/components/icon-button.css +18 -24
  286. package/src/styles/components/input.css +5 -4
  287. package/src/styles/components/instrument-monitor.css +13 -15
  288. package/src/styles/components/jobs-status-tray.css +140 -296
  289. package/src/styles/components/layout-resize-handle.css +59 -16
  290. package/src/styles/components/modal.css +5 -7
  291. package/src/styles/components/molecule-input.css +2 -2
  292. package/src/styles/components/multi-select.css +65 -117
  293. package/src/styles/components/number-input.css +26 -19
  294. package/src/styles/components/onboarding.css +368 -0
  295. package/src/styles/components/pill.css +3 -2
  296. package/src/styles/components/plate-editor.css +0 -1
  297. package/src/styles/components/plugin-icon.css +26 -23
  298. package/src/styles/components/popover.css +2 -2
  299. package/src/styles/components/presence-avatars.css +1 -1
  300. package/src/styles/components/progress-bar.css +3 -3
  301. package/src/styles/components/protocol-step-editor.css +1 -3
  302. package/src/styles/components/resource-card.css +2 -2
  303. package/src/styles/components/resource-picker-list.css +6 -57
  304. package/src/styles/components/sample-hierarchy-tree.css +4 -4
  305. package/src/styles/components/sample-selector.css +138 -79
  306. package/src/styles/components/schedule-calendar.css +8 -8
  307. package/src/styles/components/searchable-select.css +15 -19
  308. package/src/styles/components/section-card.css +54 -49
  309. package/src/styles/components/segmented-control.css +18 -15
  310. package/src/styles/components/select.css +3 -3
  311. package/src/styles/components/sequence-input.css +2 -2
  312. package/src/styles/components/sequence-progress-bar.css +17 -8
  313. package/src/styles/components/settings-modal.css +7 -2
  314. package/src/styles/components/smart-group.css +18 -18
  315. package/src/styles/components/step-wizard.css +52 -16
  316. package/src/styles/components/tabs.css +2 -2
  317. package/src/styles/components/textarea.css +3 -3
  318. package/src/styles/components/time-picker.css +37 -25
  319. package/src/styles/components/toast.css +13 -11
  320. package/src/styles/components/toggle.css +1 -11
  321. package/src/styles/components/tooltip.css +12 -11
  322. package/src/styles/components/unit-input.css +9 -8
  323. package/src/styles/components/well-edit-popup.css +2 -2
  324. package/src/styles/components/well-plate.css +9 -2
  325. package/src/styles/index.css +1 -0
  326. package/src/styles/variables.css +247 -78
  327. package/src/types/componentWorkflowTypes.ts +2 -0
  328. package/src/types/components.ts +2 -0
  329. package/src/types/generated/platformContext.ts +1 -0
  330. package/src/types/generated-ui.ts +4 -0
  331. package/src/types/index.ts +1 -0
  332. package/src/types/jobs.ts +10 -0
  333. package/src/types/platform.ts +1 -1
  334. package/src/utils/time.ts +21 -0
  335. package/dist/ActionMenu-CScINahU.js.map +0 -1
  336. package/dist/AppSidebar-BmiiPfz9.js.map +0 -1
  337. package/dist/BaseModal-DfRIuVoj.js.map +0 -1
  338. package/dist/ExperimentPopover-C4sGw-is.js +0 -359
  339. package/dist/ExperimentPopover-C4sGw-is.js.map +0 -1
  340. package/dist/ExperimentSelectorModal-D-9z7cNx.js.map +0 -1
  341. package/dist/SettingsModal-CSLE31yg.js.map +0 -1
  342. package/dist/data-C2PwI_dn.js.map +0 -1
  343. package/dist/experiment-43OJtH7R.js.map +0 -1
  344. package/dist/lab-DLwRl_Ql.js.map +0 -1
  345. package/dist/records-BzTcxekG.js.map +0 -1
  346. package/dist/runtime-D6gVat6r.js.map +0 -1
  347. package/dist/useExperimentSamples-CxcnxZ8h.js.map +0 -1
  348. package/dist/useListReorder-CcbEYxX-.js.map +0 -1
  349. package/dist/useRackEditor-BD5NIo5s.js.map +0 -1
  350. package/dist/useRealtime-PalKue84.js +0 -696
  351. package/dist/useRealtime-PalKue84.js.map +0 -1
  352. package/dist/useRequest-D5JDfsgZ.js +0 -76
  353. package/dist/useRequest-D5JDfsgZ.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"data-C2PwI_dn.js","names":[],"sources":["../src/composables/useArtifacts.ts","../src/components/ArtifactSelectorModal.vue","../src/components/ArtifactSelectorModal.vue","../src/components/ArtifactSaveDialog.vue","../src/components/ArtifactSaveDialog.vue","../src/components/ExperimentCodeBadge.vue","../src/components/ExperimentCodeBadge.vue","../src/components/internal/filePickerPresentation.ts","../src/composables/filePicker/validation.ts","../src/components/internal/FilePickerListInternal.vue","../src/components/internal/FilePickerListInternal.vue","../src/components/internal/FilePickerPreviewInternal.vue","../src/components/internal/FilePickerPreviewInternal.vue","../src/composables/filePicker/usePickerNavigation.ts","../src/composables/filePicker/usePickerSelection.ts","../src/composables/filePicker/localFiles.ts","../src/composables/useFilePicker.ts","../src/components/FilePicker.vue","../src/components/FilePicker.vue","../src/components/AuditTrail.vue","../src/components/AuditTrail.vue","../src/components/BatchProgressList.vue","../src/components/BatchProgressList.vue","../src/components/VersionConflictDialog.vue","../src/components/VersionConflictDialog.vue","../src/components/StaleFormBanner.vue","../src/components/StaleFormBanner.vue","../src/composables/useVersionedSave.ts","../src/composables/createFilePickerAdapter.ts","../src/composables/usePlatformFilePickerAdapter.ts","../src/composables/useFileBrowser.ts","../src/composables/analysisArtifactTarget.ts","../src/data.ts"],"sourcesContent":["import {\n computed,\n ref,\n toValue,\n watch,\n type ComputedRef,\n type MaybeRefOrGetter,\n type Ref,\n} from 'vue'\nimport { useApi } from './useApi'\nimport { useRequest } from './useRequest'\nimport { getInjectedPlatformContext } from './platformContextHelpers'\nimport { useExperimentStore } from '../stores/experiment'\nimport type {\n AnalysisArtifactDetail,\n AnalysisArtifactListResponse,\n AnalysisArtifactMetadataUpdate,\n AnalysisArtifactStatusFilter,\n AnalysisArtifactSummary,\n AnalysisArtifactTarget,\n} from '../types/analysisArtifactTypes'\n\nexport { ANALYSIS_FILE_ARTIFACT_SCHEMA } from '../types/analysisArtifactTypes'\n\nexport interface UseArtifactsOptions {\n /** Experiment id; defaults to the current experiment (`useExperimentStore`). Refetches when it changes. */\n experimentId?: MaybeRefOrGetter<number | undefined>\n /** Override API base URL. */\n apiBaseUrl?: string\n /** Scope to a single owning plugin; omit or null to keep all plugins. */\n pluginId?: MaybeRefOrGetter<string | null | undefined>\n /** Always request archived artifacts alongside active ones. */\n includeArchived?: MaybeRefOrGetter<boolean | undefined>\n /** Fetch as soon as an experiment id is available. */\n immediate?: boolean\n}\n\nexport interface UseArtifactsReturn {\n /** Fetched artifacts, scoped to pluginId when set. */\n artifacts: ComputedRef<AnalysisArtifactSummary[]>\n /** Artifacts after applying statusFilter, pluginFilter, and search. */\n visibleArtifacts: ComputedRef<AnalysisArtifactSummary[]>\n /** Detail loaded by the last successful getDetail call, or null. */\n selectedDetail: Ref<AnalysisArtifactDetail | null>\n /** Resolved experiment id from options or the current experiment. */\n currentExperimentId: ComputedRef<number | undefined>\n /** Whether an experiment id is available. */\n hasCurrentExperiment: ComputedRef<boolean>\n /** Whether any list or detail fetch is in progress. */\n isLoading: ComputedRef<boolean>\n /** Whether any archive/restore/metadata mutation is in progress. */\n isMutating: ComputedRef<boolean>\n /** Error message from the last failed operation, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful load, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Case-insensitive filter over display_name and artifact_key. */\n search: Ref<string>\n /** Lifecycle filter; widening past \"active\" refetches with include_archived. */\n statusFilter: Ref<AnalysisArtifactStatusFilter>\n /** Plugin filter applied to visibleArtifacts, or null for all plugins. */\n pluginFilter: Ref<string | null>\n /** Fetch the artifact list for the current experiment. */\n list: () => Promise<AnalysisArtifactSummary[]>\n /** Alias of list. */\n refresh: () => Promise<AnalysisArtifactSummary[]>\n /** Fetch one artifact with its result payload; null when it fails or is superseded. */\n getDetail: (artifactId: number) => Promise<AnalysisArtifactDetail | null>\n /** Resolve and fetch the exact stable artifact identity. */\n resolveTarget: (target: AnalysisArtifactTarget) => Promise<AnalysisArtifactDetail | null>\n /** Update display_name and/or note; resolves to the updated summary or null. */\n updateMetadata: (\n artifactId: number,\n updates: AnalysisArtifactMetadataUpdate,\n ) => Promise<AnalysisArtifactSummary | null>\n /** Archive an artifact; resolves to the updated summary or null. */\n archive: (artifactId: number) => Promise<AnalysisArtifactSummary | null>\n /** Restore an archived artifact; resolves to the updated summary or null. */\n restore: (artifactId: number) => Promise<AnalysisArtifactSummary | null>\n /** Whether a key exists in the loaded artifacts (no network call). */\n hasArtifactKey: (artifactKey: string) => boolean\n /** Reset artifacts, selection, and error. */\n clear: () => void\n}\n\n/**\n * Lists and manages analysis artifacts through the user-facing platform API.\n *\n * @example Recommended\n * ```ts\n * const artifacts = useArtifacts({ pluginId: 'LEAF' })\n * await artifacts.list()\n * ```\n */\nexport function useArtifacts(\n options: UseArtifactsOptions = {},\n): UseArtifactsReturn {\n const injectedContext = getInjectedPlatformContext()\n const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl })\n const experimentStore = useExperimentStore()\n\n const currentExperimentId = computed<number | undefined>(\n () => toValue(options.experimentId) ?? experimentStore.currentId ?? undefined,\n )\n const hasCurrentExperiment = computed(() => currentExperimentId.value !== undefined)\n const scopePluginId = computed(() => toValue(options.pluginId) ?? null)\n\n const request = useRequest('Analysis artifact request failed.')\n const lastLoadedAt = ref<Date | null>(null)\n const fetchedArtifacts = ref<AnalysisArtifactSummary[]>([])\n const selectedDetail = ref<AnalysisArtifactDetail | null>(null)\n\n // Counters, not booleans: overlapping requests must not clear each other's flag.\n const loadingCount = ref(0)\n const mutatingCount = ref(0)\n const isLoading = computed(() => loadingCount.value > 0)\n const isMutating = computed(() => mutatingCount.value > 0)\n\n const search = ref('')\n const statusFilter = ref<AnalysisArtifactStatusFilter>('active')\n const pluginFilter = ref<string | null>(null)\n\n const needsArchived = computed(\n () => Boolean(toValue(options.includeArchived)) || statusFilter.value !== 'active',\n )\n\n const artifacts = computed(() => (\n scopePluginId.value\n ? fetchedArtifacts.value.filter((artifact) => artifact.plugin_id === scopePluginId.value)\n : fetchedArtifacts.value\n ))\n\n const visibleArtifacts = computed(() => {\n const query = search.value.trim().toLowerCase()\n return artifacts.value.filter((artifact) => {\n if (statusFilter.value !== 'all' && artifact.status !== statusFilter.value) return false\n if (pluginFilter.value && artifact.plugin_id !== pluginFilter.value) return false\n if (!query) return true\n return (\n artifact.display_name.toLowerCase().includes(query)\n || artifact.artifact_key.toLowerCase().includes(query)\n )\n })\n })\n\n // Generation tokens: a superseded response must commit nothing — not rows, not the\n // shared error, not lastLoadedAt — or a late failure would bury fresh results.\n let listToken = 0\n let detailToken = 0\n const hasRequestedList = ref(false)\n\n function currentExperimentIdOrError(action: string): number | undefined {\n const id = currentExperimentId.value\n if (id === undefined) {\n request.setError(`No current experiment is selected for ${action}`)\n return undefined\n }\n return id\n }\n\n function replaceArtifact(summary: AnalysisArtifactSummary): void {\n const index = fetchedArtifacts.value.findIndex((artifact) => artifact.id === summary.id)\n if (index >= 0) {\n fetchedArtifacts.value.splice(index, 1, summary)\n }\n }\n\n async function list(): Promise<AnalysisArtifactSummary[]> {\n const experimentId = currentExperimentIdOrError('listing analysis artifacts')\n if (experimentId === undefined) {\n fetchedArtifacts.value = []\n return []\n }\n const token = ++listToken\n hasRequestedList.value = true\n loadingCount.value += 1\n request.clearError()\n try {\n const data = await api.get<AnalysisArtifactListResponse>(\n `/experiments/${experimentId}/analysis-artifacts?include_archived=${needsArchived.value}`,\n )\n if (token !== listToken) return artifacts.value\n // Copy: replaceArtifact splices in place, and the response array is not ours.\n fetchedArtifacts.value = [...(data?.artifacts ?? [])]\n lastLoadedAt.value = new Date()\n return artifacts.value\n } catch (err) {\n if (token !== listToken) return []\n request.setError(err)\n fetchedArtifacts.value = []\n return []\n } finally {\n loadingCount.value -= 1\n }\n }\n\n async function getDetail(artifactId: number): Promise<AnalysisArtifactDetail | null> {\n const experimentId = currentExperimentIdOrError('loading an analysis artifact')\n if (experimentId === undefined) return null\n const token = ++detailToken\n loadingCount.value += 1\n request.clearError()\n try {\n const detail = await api.get<AnalysisArtifactDetail>(\n `/experiments/${experimentId}/analysis-artifacts/${artifactId}`,\n )\n if (token !== detailToken) return null\n selectedDetail.value = detail\n lastLoadedAt.value = new Date()\n return detail\n } catch (err) {\n if (token !== detailToken) return null\n request.setError(err)\n return null\n } finally {\n loadingCount.value -= 1\n }\n }\n\n async function resolveTarget(\n target: AnalysisArtifactTarget,\n ): Promise<AnalysisArtifactDetail | null> {\n const experimentId = currentExperimentId.value\n if (experimentId !== target.experimentId) {\n request.setError(\n `Analysis artifact target belongs to experiment ${target.experimentId}, not `\n + `${experimentId ?? 'the current selection'}.`,\n )\n return null\n }\n\n const available = await list()\n const summary = available.find((artifact) => (\n artifact.plugin_id === target.artifactPluginId\n && artifact.artifact_key === target.artifactKey\n && artifact.status === 'active'\n ))\n if (!summary) {\n request.setError(\n `Analysis artifact ${target.artifactPluginId}/${target.artifactKey} is not available.`,\n )\n return null\n }\n\n const detail = await getDetail(summary.id)\n if (\n detail\n && (\n detail.experiment_id !== target.experimentId\n || detail.plugin_id !== target.artifactPluginId\n || detail.artifact_key !== target.artifactKey\n )\n ) {\n selectedDetail.value = null\n request.setError(\n `Analysis artifact ${target.artifactPluginId}/${target.artifactKey} did not `\n + 'match the requested identity.',\n )\n return null\n }\n return detail\n }\n\n async function mutate(\n action: string,\n operation: (experimentId: number) => Promise<AnalysisArtifactSummary>,\n ): Promise<AnalysisArtifactSummary | null> {\n const experimentId = currentExperimentIdOrError(action)\n if (experimentId === undefined) return null\n mutatingCount.value += 1\n request.clearError()\n try {\n const summary = await operation(experimentId)\n replaceArtifact(summary)\n return summary\n } catch (err) {\n request.setError(err)\n return null\n } finally {\n mutatingCount.value -= 1\n }\n }\n\n async function updateMetadata(\n artifactId: number,\n updates: AnalysisArtifactMetadataUpdate,\n ): Promise<AnalysisArtifactSummary | null> {\n return mutate('updating artifact metadata', (experimentId) =>\n api.patch<AnalysisArtifactSummary>(\n `/experiments/${experimentId}/analysis-artifacts/${artifactId}`,\n updates,\n ))\n }\n\n async function archive(artifactId: number): Promise<AnalysisArtifactSummary | null> {\n return mutate('archiving an analysis artifact', (experimentId) =>\n api.post<AnalysisArtifactSummary>(\n `/experiments/${experimentId}/analysis-artifacts/${artifactId}/archive`,\n ))\n }\n\n async function restore(artifactId: number): Promise<AnalysisArtifactSummary | null> {\n return mutate('restoring an analysis artifact', (experimentId) =>\n api.post<AnalysisArtifactSummary>(\n `/experiments/${experimentId}/analysis-artifacts/${artifactId}/restore`,\n ))\n }\n\n function hasArtifactKey(artifactKey: string): boolean {\n return artifacts.value.some((artifact) => artifact.artifact_key === artifactKey)\n }\n\n /** Drop experiment-scoped state and orphan every in-flight response. */\n function clear(): void {\n listToken += 1\n detailToken += 1\n fetchedArtifacts.value = []\n selectedDetail.value = null\n request.clearError()\n }\n\n // Refetch when the scope changes or archived rows are newly needed. `immediate`\n // covers a late-resolving experiment id; otherwise stay quiet until something\n // has asked for a list at least once.\n watch([currentExperimentId, needsArchived], ([experimentId], [previousExperimentId]) => {\n if (experimentId !== previousExperimentId) {\n // Scope changed: the previous experiment's rows and any in-flight reply for\n // them must not survive into the new one.\n clear()\n }\n if (experimentId === undefined) return\n if (options.immediate || hasRequestedList.value) {\n void list()\n }\n }, { immediate: options.immediate })\n\n return {\n artifacts,\n visibleArtifacts,\n selectedDetail,\n currentExperimentId,\n hasCurrentExperiment,\n isLoading,\n isMutating,\n error: request.error,\n lastLoadedAt,\n search,\n statusFilter,\n pluginFilter,\n list,\n refresh: list,\n getDetail,\n resolveTarget,\n updateMetadata,\n archive,\n restore,\n hasArtifactKey,\n clear,\n }\n}\n","<script setup lang=\"ts\">\n/**\n * Modal for browsing and selecting an analysis artifact of an experiment, with filters and keyboard nav.\n *\n * @example Recommended\n * ```vue\n * <ArtifactSelectorModal v-model:open=\"open\" :experiment-id=\"experimentId\" @select=\"selectArtifact\" />\n * ```\n */\nimport { computed, ref, watch } from 'vue'\nimport type { ModalSize } from '../types'\nimport type {\n AnalysisArtifactDetail,\n AnalysisArtifactStatusFilter,\n AnalysisArtifactSummary,\n} from '../types/analysisArtifactTypes'\nimport { useArtifacts } from '../composables/useArtifacts'\nimport { formatExperimentDate } from '../composables/experiment-utils'\nimport { getInjectedPlatformContext } from '../composables/platformContextHelpers'\nimport { hasAllPermissions } from '../permissions'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport ResourcePickerListInternal from './internal/ResourcePickerListInternal.vue'\n\ninterface Props {\n open: boolean\n /** Experiment id override; defaults to the current experiment (`useExperimentStore`). */\n experimentId?: number | null\n /** Scope to a single owning plugin; omit to show all plugins. */\n pluginId?: string | null\n /** Highlights the currently used artifact. */\n currentArtifactId?: number | null\n /** Start with archived artifacts visible. */\n includeArchived?: boolean\n /** Fetch the full detail before emitting selectDetail. */\n fetchDetailOnSelect?: boolean\n title?: string\n size?: ModalSize\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n experimentId: null,\n pluginId: null,\n currentArtifactId: null,\n includeArchived: false,\n fetchDetailOnSelect: false,\n title: 'Select Analysis Artifact',\n size: 'full',\n})\n\nconst emit = defineEmits<{\n 'update:open': [value: boolean]\n select: [artifact: AnalysisArtifactSummary]\n selectDetail: [detail: AnalysisArtifactDetail]\n archive: [artifact: AnalysisArtifactSummary]\n restore: [artifact: AnalysisArtifactSummary]\n}>()\n\nconst {\n artifacts,\n visibleArtifacts,\n isLoading,\n isMutating,\n error,\n search,\n statusFilter,\n pluginFilter,\n list,\n getDetail,\n archive,\n restore,\n} = useArtifacts({\n experimentId: () => props.experimentId ?? undefined,\n pluginId: () => props.pluginId,\n includeArchived: () => props.includeArchived,\n})\n\nconst injectedContext = getInjectedPlatformContext()\n// Without injected user info (standalone/dev mode) the backend stays the authority.\nconst canManage = computed(() => {\n const user = injectedContext?.user\n return user ? hasAllPermissions(user, ['experiments.edit']) : true\n})\n\nconst STATUS_FILTER_OPTIONS: Array<{ value: AnalysisArtifactStatusFilter; label: string }> = [\n { value: 'active', label: 'Active' },\n { value: 'archived', label: 'Archived' },\n { value: 'all', label: 'All statuses' },\n]\n\nconst pluginFilterOptions = computed(() => [\n { value: '', label: 'All plugins' },\n ...[...new Set(artifacts.value.map((artifact) => artifact.plugin_id))]\n .sort()\n .map((pluginId) => ({ value: pluginId, label: pluginId })),\n])\nconst canChoosePlugin = computed(\n () => !props.pluginId && pluginFilterOptions.value.length > 2,\n)\n\n// Re-mounting the list on open clears its keyboard highlight.\nconst openCount = ref(0)\n\nfunction describe(artifact: AnalysisArtifactSummary) {\n const results = artifact.result_keys.length\n return {\n code: artifact.artifact_key,\n title: artifact.display_name,\n meta: [\n artifact.plugin_id,\n `${results} result${results === 1 ? '' : 's'}`,\n ...(artifact.status === 'archived' ? ['Archived'] : []),\n ],\n date: formatArtifactDate(artifact),\n }\n}\n\nfunction formatArtifactDate(artifact: AnalysisArtifactSummary): string {\n const date = artifact.updated_at ?? artifact.created_at\n return date ? formatExperimentDate(date) : ''\n}\n\nasync function handleSelect(artifact: AnalysisArtifactSummary): Promise<void> {\n if (props.fetchDetailOnSelect) {\n const detail = await getDetail(artifact.id)\n // Stay open on failure: closing would look like a successful pick while the\n // consumer waiting on selectDetail got nothing.\n if (!detail) return\n emit('selectDetail', detail)\n }\n emit('select', artifact)\n emit('update:open', false)\n}\n\n// Emit what the backend returned, not the row we started from: the pre-mutation\n// object still carries the old status.\nasync function handleArchive(artifact: AnalysisArtifactSummary): Promise<void> {\n const archived = await archive(artifact.id)\n if (archived) emit('archive', archived)\n}\n\nasync function handleRestore(artifact: AnalysisArtifactSummary): Promise<void> {\n const restored = await restore(artifact.id)\n if (restored) emit('restore', restored)\n}\n\nwatch(\n () => props.open,\n (isOpen) => {\n if (isOpen) {\n openCount.value++\n statusFilter.value = props.includeArchived ? 'all' : 'active'\n void list()\n }\n },\n { immediate: true },\n)\n</script>\n\n<template>\n <BaseModal\n :open=\"open\"\n :title=\"title\"\n :size=\"size\"\n @update:open=\"emit('update:open', $event)\"\n >\n <ResourcePickerListInternal\n :key=\"openCount\"\n v-model:search=\"search\"\n class=\"mint-artifact-selector\"\n label=\"Analysis artifacts\"\n placeholder=\"Search artifacts…\"\n :items=\"visibleArtifacts\"\n :item-key=\"(artifact) => artifact.id\"\n :describe=\"describe\"\n :selected-key=\"currentArtifactId\"\n :loading=\"isLoading\"\n :error=\"error\"\n empty-title=\"No analysis artifacts found\"\n empty-description=\"Artifacts appear here once an analysis saves results for this experiment.\"\n @select=\"handleSelect\"\n >\n <template #filters>\n <div class=\"mint-artifact-selector__filter-select\">\n <BaseSelect\n :model-value=\"statusFilter\"\n :options=\"STATUS_FILTER_OPTIONS\"\n size=\"sm\"\n aria-label=\"Filter by status\"\n @update:model-value=\"v => statusFilter = v as AnalysisArtifactStatusFilter\"\n />\n </div>\n <div v-if=\"canChoosePlugin\" class=\"mint-artifact-selector__filter-select\">\n <BaseSelect\n :model-value=\"pluginFilter ?? ''\"\n :options=\"pluginFilterOptions\"\n size=\"sm\"\n aria-label=\"Filter by plugin\"\n @update:model-value=\"v => pluginFilter = String(v) || null\"\n />\n </div>\n </template>\n <template v-if=\"canManage\" #actions=\"{ item }\">\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n class=\"mint-artifact-selector__action\"\n :disabled=\"isMutating\"\n @click=\"item.status === 'archived' ? handleRestore(item) : handleArchive(item)\"\n >\n {{ item.status === 'archived' ? 'Restore' : 'Archive' }}\n </BaseButton>\n </template>\n </ResourcePickerListInternal>\n </BaseModal>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Modal for browsing and selecting an analysis artifact of an experiment, with filters and keyboard nav.\n *\n * @example Recommended\n * ```vue\n * <ArtifactSelectorModal v-model:open=\"open\" :experiment-id=\"experimentId\" @select=\"selectArtifact\" />\n * ```\n */\nimport { computed, ref, watch } from 'vue'\nimport type { ModalSize } from '../types'\nimport type {\n AnalysisArtifactDetail,\n AnalysisArtifactStatusFilter,\n AnalysisArtifactSummary,\n} from '../types/analysisArtifactTypes'\nimport { useArtifacts } from '../composables/useArtifacts'\nimport { formatExperimentDate } from '../composables/experiment-utils'\nimport { getInjectedPlatformContext } from '../composables/platformContextHelpers'\nimport { hasAllPermissions } from '../permissions'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport ResourcePickerListInternal from './internal/ResourcePickerListInternal.vue'\n\ninterface Props {\n open: boolean\n /** Experiment id override; defaults to the current experiment (`useExperimentStore`). */\n experimentId?: number | null\n /** Scope to a single owning plugin; omit to show all plugins. */\n pluginId?: string | null\n /** Highlights the currently used artifact. */\n currentArtifactId?: number | null\n /** Start with archived artifacts visible. */\n includeArchived?: boolean\n /** Fetch the full detail before emitting selectDetail. */\n fetchDetailOnSelect?: boolean\n title?: string\n size?: ModalSize\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n experimentId: null,\n pluginId: null,\n currentArtifactId: null,\n includeArchived: false,\n fetchDetailOnSelect: false,\n title: 'Select Analysis Artifact',\n size: 'full',\n})\n\nconst emit = defineEmits<{\n 'update:open': [value: boolean]\n select: [artifact: AnalysisArtifactSummary]\n selectDetail: [detail: AnalysisArtifactDetail]\n archive: [artifact: AnalysisArtifactSummary]\n restore: [artifact: AnalysisArtifactSummary]\n}>()\n\nconst {\n artifacts,\n visibleArtifacts,\n isLoading,\n isMutating,\n error,\n search,\n statusFilter,\n pluginFilter,\n list,\n getDetail,\n archive,\n restore,\n} = useArtifacts({\n experimentId: () => props.experimentId ?? undefined,\n pluginId: () => props.pluginId,\n includeArchived: () => props.includeArchived,\n})\n\nconst injectedContext = getInjectedPlatformContext()\n// Without injected user info (standalone/dev mode) the backend stays the authority.\nconst canManage = computed(() => {\n const user = injectedContext?.user\n return user ? hasAllPermissions(user, ['experiments.edit']) : true\n})\n\nconst STATUS_FILTER_OPTIONS: Array<{ value: AnalysisArtifactStatusFilter; label: string }> = [\n { value: 'active', label: 'Active' },\n { value: 'archived', label: 'Archived' },\n { value: 'all', label: 'All statuses' },\n]\n\nconst pluginFilterOptions = computed(() => [\n { value: '', label: 'All plugins' },\n ...[...new Set(artifacts.value.map((artifact) => artifact.plugin_id))]\n .sort()\n .map((pluginId) => ({ value: pluginId, label: pluginId })),\n])\nconst canChoosePlugin = computed(\n () => !props.pluginId && pluginFilterOptions.value.length > 2,\n)\n\n// Re-mounting the list on open clears its keyboard highlight.\nconst openCount = ref(0)\n\nfunction describe(artifact: AnalysisArtifactSummary) {\n const results = artifact.result_keys.length\n return {\n code: artifact.artifact_key,\n title: artifact.display_name,\n meta: [\n artifact.plugin_id,\n `${results} result${results === 1 ? '' : 's'}`,\n ...(artifact.status === 'archived' ? ['Archived'] : []),\n ],\n date: formatArtifactDate(artifact),\n }\n}\n\nfunction formatArtifactDate(artifact: AnalysisArtifactSummary): string {\n const date = artifact.updated_at ?? artifact.created_at\n return date ? formatExperimentDate(date) : ''\n}\n\nasync function handleSelect(artifact: AnalysisArtifactSummary): Promise<void> {\n if (props.fetchDetailOnSelect) {\n const detail = await getDetail(artifact.id)\n // Stay open on failure: closing would look like a successful pick while the\n // consumer waiting on selectDetail got nothing.\n if (!detail) return\n emit('selectDetail', detail)\n }\n emit('select', artifact)\n emit('update:open', false)\n}\n\n// Emit what the backend returned, not the row we started from: the pre-mutation\n// object still carries the old status.\nasync function handleArchive(artifact: AnalysisArtifactSummary): Promise<void> {\n const archived = await archive(artifact.id)\n if (archived) emit('archive', archived)\n}\n\nasync function handleRestore(artifact: AnalysisArtifactSummary): Promise<void> {\n const restored = await restore(artifact.id)\n if (restored) emit('restore', restored)\n}\n\nwatch(\n () => props.open,\n (isOpen) => {\n if (isOpen) {\n openCount.value++\n statusFilter.value = props.includeArchived ? 'all' : 'active'\n void list()\n }\n },\n { immediate: true },\n)\n</script>\n\n<template>\n <BaseModal\n :open=\"open\"\n :title=\"title\"\n :size=\"size\"\n @update:open=\"emit('update:open', $event)\"\n >\n <ResourcePickerListInternal\n :key=\"openCount\"\n v-model:search=\"search\"\n class=\"mint-artifact-selector\"\n label=\"Analysis artifacts\"\n placeholder=\"Search artifacts…\"\n :items=\"visibleArtifacts\"\n :item-key=\"(artifact) => artifact.id\"\n :describe=\"describe\"\n :selected-key=\"currentArtifactId\"\n :loading=\"isLoading\"\n :error=\"error\"\n empty-title=\"No analysis artifacts found\"\n empty-description=\"Artifacts appear here once an analysis saves results for this experiment.\"\n @select=\"handleSelect\"\n >\n <template #filters>\n <div class=\"mint-artifact-selector__filter-select\">\n <BaseSelect\n :model-value=\"statusFilter\"\n :options=\"STATUS_FILTER_OPTIONS\"\n size=\"sm\"\n aria-label=\"Filter by status\"\n @update:model-value=\"v => statusFilter = v as AnalysisArtifactStatusFilter\"\n />\n </div>\n <div v-if=\"canChoosePlugin\" class=\"mint-artifact-selector__filter-select\">\n <BaseSelect\n :model-value=\"pluginFilter ?? ''\"\n :options=\"pluginFilterOptions\"\n size=\"sm\"\n aria-label=\"Filter by plugin\"\n @update:model-value=\"v => pluginFilter = String(v) || null\"\n />\n </div>\n </template>\n <template v-if=\"canManage\" #actions=\"{ item }\">\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n class=\"mint-artifact-selector__action\"\n :disabled=\"isMutating\"\n @click=\"item.status === 'archived' ? handleRestore(item) : handleArchive(item)\"\n >\n {{ item.status === 'archived' ? 'Restore' : 'Archive' }}\n </BaseButton>\n </template>\n </ResourcePickerListInternal>\n </BaseModal>\n</template>\n","<script setup lang=\"ts\">\n/** Save/update dialog for analysis artifacts; delegates the write to a plugin-supplied handler. */\nimport { computed, ref, shallowRef, watch } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport BaseInput from './BaseInput.vue'\nimport BaseButton from './BaseButton.vue'\nimport AlertBox from './AlertBox.vue'\nimport FormField from './FormField.vue'\nimport FileUploader from './FileUploader.vue'\nimport SegmentedControl from './SegmentedControl.vue'\nimport { useRequest } from '../composables/useRequest'\nimport { useExperimentStore } from '../stores/experiment'\nimport {\n ANALYSIS_FILE_ARTIFACT_SCHEMA,\n type AnalysisArtifactDetail,\n type ArtifactFileSource,\n type ArtifactSaveDialogHandler,\n type ArtifactSaveDialogPayload,\n type ArtifactSaveHandler,\n type ArtifactSaveResult,\n type HandlerArtifactSaveHandler,\n} from '../types/analysisArtifactTypes'\nimport type { ModalSize } from '../types'\n\ninterface Props {\n open: boolean\n /** Performs the backend write; the dialog never calls the platform itself. */\n saveHandler: ArtifactSaveDialogHandler\n /** Which artifact kinds the form offers. */\n mode?: 'json' | 'file' | 'both'\n /** Use `handler` when the plugin backend serializes the file content itself. */\n fileSource?: ArtifactFileSource\n /** JSON result payload supplied programmatically by the host. */\n result?: Record<string, unknown>\n /** Experiment id override; defaults to the current experiment (`useExperimentStore`). */\n experimentId?: number | null\n /** Existing artifact detail; when set the dialog updates instead of creating. */\n existingArtifact?: AnalysisArtifactDetail | null\n /** Known artifact keys, used to warn (json) or block (file) on collisions. */\n existingArtifactKeys?: string[]\n title?: string\n size?: ModalSize\n defaultArtifactKey?: string\n defaultDisplayName?: string\n /** Maps to the Python `kind=` argument for new file artifacts. */\n defaultFileKind?: string\n /** Forwarded to the file uploader. */\n accept?: string\n /** Forwarded to the file uploader (bytes). */\n maxFileSize?: number\n /**\n * Result metadata for file artifacts. Backend-side this REPLACES the previous\n * metadata; on replacement the existing artifact's metadata is carried through\n * unless this prop supplies a new map.\n */\n metadata?: Record<string, unknown>\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n mode: 'file',\n fileSource: 'upload',\n experimentId: null,\n existingArtifact: null,\n size: 'md',\n defaultArtifactKey: 'default',\n defaultFileKind: 'file',\n})\n\nconst emit = defineEmits<{\n 'update:open': [value: boolean]\n saved: [result: ArtifactSaveResult & { payload: ArtifactSaveDialogPayload }]\n error: [message: string]\n cancel: []\n}>()\n\nconst ARTIFACT_KEY_PATTERN = /^[a-z0-9][a-z0-9._-]*$/\n\nconst experimentStore = useExperimentStore()\nconst request = useRequest('Failed to save analysis artifact.')\n\nconst isUpdate = computed(() => props.existingArtifact !== null)\nconst existingResult = computed(\n () => (props.existingArtifact?.result ?? {}) as Record<string, unknown>,\n)\nconst existingIsFile = computed(\n () => existingResult.value.schema_version === ANALYSIS_FILE_ARTIFACT_SCHEMA,\n)\nconst existingFileKind = computed(() =>\n typeof existingResult.value.kind === 'string' ? existingResult.value.kind : undefined)\nconst existingFilename = computed(() =>\n typeof existingResult.value.filename === 'string' ? existingResult.value.filename : undefined)\nconst existingMetadata = computed(() => {\n const value = existingResult.value.metadata\n return value && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown>\n : undefined\n})\n\nconst activeKind = ref<'json' | 'file'>('file')\nconst keyInput = ref('')\nconst displayNameInput = ref('')\nconst noteInput = ref('')\nconst selectedFile = shallowRef<File | null>(null)\nlet initialNote = ''\n\nfunction resetForm(): void {\n const existing = props.existingArtifact\n activeKind.value = existing\n ? (existingIsFile.value ? 'file' : 'json')\n : (props.mode === 'json' ? 'json' : 'file')\n keyInput.value = existing?.artifact_key ?? props.defaultArtifactKey\n displayNameInput.value = existing?.display_name ?? props.defaultDisplayName ?? ''\n initialNote = existing?.note ?? ''\n noteInput.value = initialNote\n selectedFile.value = null\n request.clearError()\n}\n\nresetForm()\nwatch(() => props.open, (open) => {\n if (open) resetForm()\n})\n// Key off identity, not object reference: re-fetching the same artifact must not\n// wipe in-progress edits.\nwatch(\n () => (props.existingArtifact ? `${props.existingArtifact.id}:${props.existingArtifact.artifact_key}` : null),\n resetForm,\n)\n\nconst resolvedTitle = computed(() =>\n props.title ?? (isUpdate.value ? 'Update Analysis Artifact' : 'Save Analysis Artifact'))\nconst showKindToggle = computed(() => !isUpdate.value && props.mode === 'both')\nconst showFileUploader = computed(\n () => activeKind.value === 'file' && props.fileSource === 'upload',\n)\n// save_analysis_artifact ignores note=None (it preserves), so a JSON artifact's\n// note cannot be cleared through the save path — that belongs to updateMetadata.\n// update_analysis_file_artifact does honour the not-provided/null/value sentinel.\nconst showNoteField = computed(() => !isUpdate.value || existingIsFile.value)\n\nconst effectiveKey = computed(() => {\n if (isUpdate.value) return props.existingArtifact?.artifact_key ?? ''\n return keyInput.value.trim() || props.defaultArtifactKey\n})\nconst keyValid = computed(() => ARTIFACT_KEY_PATTERN.test(effectiveKey.value))\nconst keyExists = computed(\n () => !isUpdate.value && (props.existingArtifactKeys ?? []).includes(effectiveKey.value),\n)\n// A file artifact is create-only: reusing a key always fails backend-side, and the\n// dialog has no detail to replace it safely. JSON keys upsert natively, so warn only.\nconst keyBlocked = computed(() => keyExists.value && activeKind.value === 'file')\nconst keyError = computed(() => {\n if (!keyValid.value) return 'Use lowercase letters, digits, dots, dashes, or underscores.'\n if (keyBlocked.value) return 'This key is already used. Choose another, or replace the existing artifact.'\n return undefined\n})\n\nconst canSubmit = computed(() => {\n if (request.isLoading.value || !keyValid.value || keyBlocked.value) return false\n if (activeKind.value === 'file') {\n return props.fileSource === 'handler' || selectedFile.value !== null\n }\n return props.result !== undefined\n})\n\nfunction resolveExperimentId(): number | undefined {\n return props.experimentId ?? experimentStore.currentId ?? undefined\n}\n\n/** Set-only note for creates and JSON upserts: an empty field just sends nothing. */\nfunction createNotePayload(): { note?: string } {\n const value = noteInput.value.trim()\n return showNoteField.value && value ? { note: value } : {}\n}\n\n/**\n * Three-state note, honoured only by update_analysis_file_artifact:\n * omitted = preserve, null = clear, string = set.\n */\nfunction updateNotePayload(): { note?: string | null } {\n if (noteInput.value === initialNote) return {}\n const value = noteInput.value.trim()\n return value ? { note: value } : { note: null }\n}\n\nfunction buildPayload(experimentId: number): ArtifactSaveDialogPayload | null {\n const base = {\n experimentId,\n artifactKey: effectiveKey.value,\n }\n if (activeKind.value === 'json') {\n if (props.result === undefined) return null\n // Upsert only sets display_name when provided; pass the existing one through\n // on update so a re-save never blanks it.\n const displayName = isUpdate.value\n ? props.existingArtifact?.display_name\n : displayNameInput.value.trim() || undefined\n return {\n kind: 'json',\n mode: isUpdate.value || keyExists.value ? 'upsert' : 'create',\n ...base,\n ...createNotePayload(),\n ...(displayName ? { displayName } : {}),\n result: props.result,\n }\n }\n if (isUpdate.value) {\n // Metadata replaces rather than merges backend-side, so send the full intended\n // map: the caller's override, else whatever the artifact already carries.\n const metadata = props.metadata ?? existingMetadata.value\n if (props.fileSource === 'handler') {\n return {\n kind: 'file',\n source: 'handler',\n mode: 'update',\n ...base,\n ...updateNotePayload(),\n fileKind: existingFileKind.value ?? props.defaultFileKind,\n ...(metadata ? { metadata } : {}),\n }\n }\n const file = selectedFile.value\n if (!file) return null\n return {\n kind: 'file',\n mode: 'update',\n ...base,\n ...updateNotePayload(),\n file,\n // kind is immutable backend-side; always echo the artifact's own kind.\n fileKind: existingFileKind.value ?? props.defaultFileKind,\n ...(metadata ? { metadata } : {}),\n ...(file.type ? { contentType: file.type } : {}),\n }\n }\n const displayName = displayNameInput.value.trim()\n if (props.fileSource === 'handler') {\n return {\n kind: 'file',\n source: 'handler',\n mode: 'create',\n ...base,\n ...createNotePayload(),\n fileKind: props.defaultFileKind,\n ...(displayName ? { displayName } : {}),\n ...(props.metadata ? { metadata: props.metadata } : {}),\n }\n }\n const file = selectedFile.value\n if (!file) return null\n return {\n kind: 'file',\n mode: 'create',\n ...base,\n ...createNotePayload(),\n file,\n fileKind: props.defaultFileKind,\n filename: file.name,\n ...(displayName ? { displayName } : {}),\n ...(props.metadata ? { metadata: props.metadata } : {}),\n ...(file.type ? { contentType: file.type } : {}),\n }\n}\n\nfunction runSaveHandler(\n payload: ArtifactSaveDialogPayload,\n): Promise<ArtifactSaveResult | void> {\n if (props.fileSource === 'handler') {\n if (payload.kind === 'file' && !('source' in payload)) {\n throw new Error('ArtifactSaveDialog built an upload payload for a handler-owned file.')\n }\n return (props.saveHandler as HandlerArtifactSaveHandler)(payload)\n }\n if (payload.kind === 'file' && 'source' in payload) {\n throw new Error('ArtifactSaveDialog built a handler-owned payload for an uploaded file.')\n }\n return (props.saveHandler as ArtifactSaveHandler)(payload)\n}\n\nfunction handleFileUpload(files: File[]): void {\n selectedFile.value = files[0] ?? null\n}\n\nfunction handleFileError(message: string): void {\n request.setError(message)\n}\n\nasync function handleSubmit(): Promise<void> {\n if (!canSubmit.value) return\n const experimentId = resolveExperimentId()\n if (experimentId === undefined) {\n const message = request.setError('No experiment is available for saving an artifact.')\n emit('error', message)\n return\n }\n const payload = buildPayload(experimentId)\n if (!payload) return\n try {\n const result = await request.run(\n () => runSaveHandler(payload),\n { kind: 'save', errorMessage: 'Failed to save analysis artifact.' },\n )\n emit('saved', { ...(result ?? {}), payload })\n emit('update:open', false)\n resetForm()\n } catch {\n emit('error', request.error.value ?? 'Failed to save analysis artifact.')\n }\n}\n\nfunction handleCancel(): void {\n emit('cancel')\n emit('update:open', false)\n}\n</script>\n\n<template>\n <BaseModal\n :open=\"open\"\n :title=\"resolvedTitle\"\n :size=\"size\"\n :closable=\"!request.isLoading.value\"\n @update:open=\"emit('update:open', $event)\"\n @close=\"emit('cancel')\"\n >\n <div class=\"mint-artifact-save-dialog\">\n <SegmentedControl\n v-if=\"showKindToggle\"\n v-model=\"activeKind\"\n class=\"mint-artifact-save-dialog__kind-toggle\"\n :options=\"[\n { value: 'file', label: 'File' },\n { value: 'json', label: 'JSON' },\n ]\"\n full-width\n />\n\n <AlertBox\n v-if=\"keyExists && !keyBlocked\"\n tone=\"warning\"\n class=\"mint-artifact-save-dialog__warning\"\n >\n An artifact with key \"{{ effectiveKey }}\" already exists. Its results will be\n replaced; its name and note are kept unless you set new ones.\n </AlertBox>\n\n <FormField\n label=\"Artifact key\"\n class=\"mint-artifact-save-dialog__key\"\n :error=\"keyError\"\n :hint=\"isUpdate ? 'Keys are fixed once an artifact exists.' : undefined\"\n >\n <BaseInput\n v-model=\"keyInput\"\n :readonly=\"isUpdate\"\n :invalid=\"Boolean(keyError)\"\n :placeholder=\"defaultArtifactKey\"\n />\n </FormField>\n\n <FormField\n v-if=\"!isUpdate\"\n label=\"Display name\"\n class=\"mint-artifact-save-dialog__name\"\n >\n <BaseInput\n v-model=\"displayNameInput\"\n placeholder=\"Shown in artifact lists\"\n />\n </FormField>\n\n <FormField\n v-if=\"showNoteField\"\n label=\"Note\"\n class=\"mint-artifact-save-dialog__note\"\n >\n <BaseInput\n type=\"textarea\"\n v-model=\"noteInput\"\n placeholder=\"Optional context for this artifact\"\n />\n </FormField>\n\n <template v-if=\"activeKind === 'file'\">\n <FileUploader\n v-if=\"showFileUploader\"\n class=\"mint-artifact-save-dialog__file\"\n :accept=\"accept\"\n :max-size=\"maxFileSize\"\n :multiple=\"false\"\n @upload=\"handleFileUpload\"\n @error=\"handleFileError\"\n />\n <p\n v-if=\"isUpdate && existingFilename\"\n class=\"mint-artifact-save-dialog__immutable\"\n >\n Replaces the content of <code>{{ existingFilename }}</code> (kind\n <code>{{ existingFileKind ?? defaultFileKind }}</code>); filename and kind stay unchanged.\n </p>\n </template>\n\n <AlertBox\n v-if=\"request.error.value\"\n tone=\"error\"\n class=\"mint-artifact-save-dialog__error\"\n role=\"alert\"\n >\n {{ request.error.value }}\n </AlertBox>\n </div>\n\n <template #footer>\n <BaseButton variant=\"secondary\" :disabled=\"request.isLoading.value\" @click=\"handleCancel\">\n Cancel\n </BaseButton>\n <BaseButton\n class=\"mint-artifact-save-dialog__submit\"\n :loading=\"request.isLoading.value\"\n :disabled=\"!canSubmit\"\n @click=\"handleSubmit\"\n >\n {{ isUpdate ? 'Update Artifact' : 'Save Artifact' }}\n </BaseButton>\n </template>\n </BaseModal>\n</template>\n","<script setup lang=\"ts\">\n/** Save/update dialog for analysis artifacts; delegates the write to a plugin-supplied handler. */\nimport { computed, ref, shallowRef, watch } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport BaseInput from './BaseInput.vue'\nimport BaseButton from './BaseButton.vue'\nimport AlertBox from './AlertBox.vue'\nimport FormField from './FormField.vue'\nimport FileUploader from './FileUploader.vue'\nimport SegmentedControl from './SegmentedControl.vue'\nimport { useRequest } from '../composables/useRequest'\nimport { useExperimentStore } from '../stores/experiment'\nimport {\n ANALYSIS_FILE_ARTIFACT_SCHEMA,\n type AnalysisArtifactDetail,\n type ArtifactFileSource,\n type ArtifactSaveDialogHandler,\n type ArtifactSaveDialogPayload,\n type ArtifactSaveHandler,\n type ArtifactSaveResult,\n type HandlerArtifactSaveHandler,\n} from '../types/analysisArtifactTypes'\nimport type { ModalSize } from '../types'\n\ninterface Props {\n open: boolean\n /** Performs the backend write; the dialog never calls the platform itself. */\n saveHandler: ArtifactSaveDialogHandler\n /** Which artifact kinds the form offers. */\n mode?: 'json' | 'file' | 'both'\n /** Use `handler` when the plugin backend serializes the file content itself. */\n fileSource?: ArtifactFileSource\n /** JSON result payload supplied programmatically by the host. */\n result?: Record<string, unknown>\n /** Experiment id override; defaults to the current experiment (`useExperimentStore`). */\n experimentId?: number | null\n /** Existing artifact detail; when set the dialog updates instead of creating. */\n existingArtifact?: AnalysisArtifactDetail | null\n /** Known artifact keys, used to warn (json) or block (file) on collisions. */\n existingArtifactKeys?: string[]\n title?: string\n size?: ModalSize\n defaultArtifactKey?: string\n defaultDisplayName?: string\n /** Maps to the Python `kind=` argument for new file artifacts. */\n defaultFileKind?: string\n /** Forwarded to the file uploader. */\n accept?: string\n /** Forwarded to the file uploader (bytes). */\n maxFileSize?: number\n /**\n * Result metadata for file artifacts. Backend-side this REPLACES the previous\n * metadata; on replacement the existing artifact's metadata is carried through\n * unless this prop supplies a new map.\n */\n metadata?: Record<string, unknown>\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n mode: 'file',\n fileSource: 'upload',\n experimentId: null,\n existingArtifact: null,\n size: 'md',\n defaultArtifactKey: 'default',\n defaultFileKind: 'file',\n})\n\nconst emit = defineEmits<{\n 'update:open': [value: boolean]\n saved: [result: ArtifactSaveResult & { payload: ArtifactSaveDialogPayload }]\n error: [message: string]\n cancel: []\n}>()\n\nconst ARTIFACT_KEY_PATTERN = /^[a-z0-9][a-z0-9._-]*$/\n\nconst experimentStore = useExperimentStore()\nconst request = useRequest('Failed to save analysis artifact.')\n\nconst isUpdate = computed(() => props.existingArtifact !== null)\nconst existingResult = computed(\n () => (props.existingArtifact?.result ?? {}) as Record<string, unknown>,\n)\nconst existingIsFile = computed(\n () => existingResult.value.schema_version === ANALYSIS_FILE_ARTIFACT_SCHEMA,\n)\nconst existingFileKind = computed(() =>\n typeof existingResult.value.kind === 'string' ? existingResult.value.kind : undefined)\nconst existingFilename = computed(() =>\n typeof existingResult.value.filename === 'string' ? existingResult.value.filename : undefined)\nconst existingMetadata = computed(() => {\n const value = existingResult.value.metadata\n return value && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown>\n : undefined\n})\n\nconst activeKind = ref<'json' | 'file'>('file')\nconst keyInput = ref('')\nconst displayNameInput = ref('')\nconst noteInput = ref('')\nconst selectedFile = shallowRef<File | null>(null)\nlet initialNote = ''\n\nfunction resetForm(): void {\n const existing = props.existingArtifact\n activeKind.value = existing\n ? (existingIsFile.value ? 'file' : 'json')\n : (props.mode === 'json' ? 'json' : 'file')\n keyInput.value = existing?.artifact_key ?? props.defaultArtifactKey\n displayNameInput.value = existing?.display_name ?? props.defaultDisplayName ?? ''\n initialNote = existing?.note ?? ''\n noteInput.value = initialNote\n selectedFile.value = null\n request.clearError()\n}\n\nresetForm()\nwatch(() => props.open, (open) => {\n if (open) resetForm()\n})\n// Key off identity, not object reference: re-fetching the same artifact must not\n// wipe in-progress edits.\nwatch(\n () => (props.existingArtifact ? `${props.existingArtifact.id}:${props.existingArtifact.artifact_key}` : null),\n resetForm,\n)\n\nconst resolvedTitle = computed(() =>\n props.title ?? (isUpdate.value ? 'Update Analysis Artifact' : 'Save Analysis Artifact'))\nconst showKindToggle = computed(() => !isUpdate.value && props.mode === 'both')\nconst showFileUploader = computed(\n () => activeKind.value === 'file' && props.fileSource === 'upload',\n)\n// save_analysis_artifact ignores note=None (it preserves), so a JSON artifact's\n// note cannot be cleared through the save path — that belongs to updateMetadata.\n// update_analysis_file_artifact does honour the not-provided/null/value sentinel.\nconst showNoteField = computed(() => !isUpdate.value || existingIsFile.value)\n\nconst effectiveKey = computed(() => {\n if (isUpdate.value) return props.existingArtifact?.artifact_key ?? ''\n return keyInput.value.trim() || props.defaultArtifactKey\n})\nconst keyValid = computed(() => ARTIFACT_KEY_PATTERN.test(effectiveKey.value))\nconst keyExists = computed(\n () => !isUpdate.value && (props.existingArtifactKeys ?? []).includes(effectiveKey.value),\n)\n// A file artifact is create-only: reusing a key always fails backend-side, and the\n// dialog has no detail to replace it safely. JSON keys upsert natively, so warn only.\nconst keyBlocked = computed(() => keyExists.value && activeKind.value === 'file')\nconst keyError = computed(() => {\n if (!keyValid.value) return 'Use lowercase letters, digits, dots, dashes, or underscores.'\n if (keyBlocked.value) return 'This key is already used. Choose another, or replace the existing artifact.'\n return undefined\n})\n\nconst canSubmit = computed(() => {\n if (request.isLoading.value || !keyValid.value || keyBlocked.value) return false\n if (activeKind.value === 'file') {\n return props.fileSource === 'handler' || selectedFile.value !== null\n }\n return props.result !== undefined\n})\n\nfunction resolveExperimentId(): number | undefined {\n return props.experimentId ?? experimentStore.currentId ?? undefined\n}\n\n/** Set-only note for creates and JSON upserts: an empty field just sends nothing. */\nfunction createNotePayload(): { note?: string } {\n const value = noteInput.value.trim()\n return showNoteField.value && value ? { note: value } : {}\n}\n\n/**\n * Three-state note, honoured only by update_analysis_file_artifact:\n * omitted = preserve, null = clear, string = set.\n */\nfunction updateNotePayload(): { note?: string | null } {\n if (noteInput.value === initialNote) return {}\n const value = noteInput.value.trim()\n return value ? { note: value } : { note: null }\n}\n\nfunction buildPayload(experimentId: number): ArtifactSaveDialogPayload | null {\n const base = {\n experimentId,\n artifactKey: effectiveKey.value,\n }\n if (activeKind.value === 'json') {\n if (props.result === undefined) return null\n // Upsert only sets display_name when provided; pass the existing one through\n // on update so a re-save never blanks it.\n const displayName = isUpdate.value\n ? props.existingArtifact?.display_name\n : displayNameInput.value.trim() || undefined\n return {\n kind: 'json',\n mode: isUpdate.value || keyExists.value ? 'upsert' : 'create',\n ...base,\n ...createNotePayload(),\n ...(displayName ? { displayName } : {}),\n result: props.result,\n }\n }\n if (isUpdate.value) {\n // Metadata replaces rather than merges backend-side, so send the full intended\n // map: the caller's override, else whatever the artifact already carries.\n const metadata = props.metadata ?? existingMetadata.value\n if (props.fileSource === 'handler') {\n return {\n kind: 'file',\n source: 'handler',\n mode: 'update',\n ...base,\n ...updateNotePayload(),\n fileKind: existingFileKind.value ?? props.defaultFileKind,\n ...(metadata ? { metadata } : {}),\n }\n }\n const file = selectedFile.value\n if (!file) return null\n return {\n kind: 'file',\n mode: 'update',\n ...base,\n ...updateNotePayload(),\n file,\n // kind is immutable backend-side; always echo the artifact's own kind.\n fileKind: existingFileKind.value ?? props.defaultFileKind,\n ...(metadata ? { metadata } : {}),\n ...(file.type ? { contentType: file.type } : {}),\n }\n }\n const displayName = displayNameInput.value.trim()\n if (props.fileSource === 'handler') {\n return {\n kind: 'file',\n source: 'handler',\n mode: 'create',\n ...base,\n ...createNotePayload(),\n fileKind: props.defaultFileKind,\n ...(displayName ? { displayName } : {}),\n ...(props.metadata ? { metadata: props.metadata } : {}),\n }\n }\n const file = selectedFile.value\n if (!file) return null\n return {\n kind: 'file',\n mode: 'create',\n ...base,\n ...createNotePayload(),\n file,\n fileKind: props.defaultFileKind,\n filename: file.name,\n ...(displayName ? { displayName } : {}),\n ...(props.metadata ? { metadata: props.metadata } : {}),\n ...(file.type ? { contentType: file.type } : {}),\n }\n}\n\nfunction runSaveHandler(\n payload: ArtifactSaveDialogPayload,\n): Promise<ArtifactSaveResult | void> {\n if (props.fileSource === 'handler') {\n if (payload.kind === 'file' && !('source' in payload)) {\n throw new Error('ArtifactSaveDialog built an upload payload for a handler-owned file.')\n }\n return (props.saveHandler as HandlerArtifactSaveHandler)(payload)\n }\n if (payload.kind === 'file' && 'source' in payload) {\n throw new Error('ArtifactSaveDialog built a handler-owned payload for an uploaded file.')\n }\n return (props.saveHandler as ArtifactSaveHandler)(payload)\n}\n\nfunction handleFileUpload(files: File[]): void {\n selectedFile.value = files[0] ?? null\n}\n\nfunction handleFileError(message: string): void {\n request.setError(message)\n}\n\nasync function handleSubmit(): Promise<void> {\n if (!canSubmit.value) return\n const experimentId = resolveExperimentId()\n if (experimentId === undefined) {\n const message = request.setError('No experiment is available for saving an artifact.')\n emit('error', message)\n return\n }\n const payload = buildPayload(experimentId)\n if (!payload) return\n try {\n const result = await request.run(\n () => runSaveHandler(payload),\n { kind: 'save', errorMessage: 'Failed to save analysis artifact.' },\n )\n emit('saved', { ...(result ?? {}), payload })\n emit('update:open', false)\n resetForm()\n } catch {\n emit('error', request.error.value ?? 'Failed to save analysis artifact.')\n }\n}\n\nfunction handleCancel(): void {\n emit('cancel')\n emit('update:open', false)\n}\n</script>\n\n<template>\n <BaseModal\n :open=\"open\"\n :title=\"resolvedTitle\"\n :size=\"size\"\n :closable=\"!request.isLoading.value\"\n @update:open=\"emit('update:open', $event)\"\n @close=\"emit('cancel')\"\n >\n <div class=\"mint-artifact-save-dialog\">\n <SegmentedControl\n v-if=\"showKindToggle\"\n v-model=\"activeKind\"\n class=\"mint-artifact-save-dialog__kind-toggle\"\n :options=\"[\n { value: 'file', label: 'File' },\n { value: 'json', label: 'JSON' },\n ]\"\n full-width\n />\n\n <AlertBox\n v-if=\"keyExists && !keyBlocked\"\n tone=\"warning\"\n class=\"mint-artifact-save-dialog__warning\"\n >\n An artifact with key \"{{ effectiveKey }}\" already exists. Its results will be\n replaced; its name and note are kept unless you set new ones.\n </AlertBox>\n\n <FormField\n label=\"Artifact key\"\n class=\"mint-artifact-save-dialog__key\"\n :error=\"keyError\"\n :hint=\"isUpdate ? 'Keys are fixed once an artifact exists.' : undefined\"\n >\n <BaseInput\n v-model=\"keyInput\"\n :readonly=\"isUpdate\"\n :invalid=\"Boolean(keyError)\"\n :placeholder=\"defaultArtifactKey\"\n />\n </FormField>\n\n <FormField\n v-if=\"!isUpdate\"\n label=\"Display name\"\n class=\"mint-artifact-save-dialog__name\"\n >\n <BaseInput\n v-model=\"displayNameInput\"\n placeholder=\"Shown in artifact lists\"\n />\n </FormField>\n\n <FormField\n v-if=\"showNoteField\"\n label=\"Note\"\n class=\"mint-artifact-save-dialog__note\"\n >\n <BaseInput\n type=\"textarea\"\n v-model=\"noteInput\"\n placeholder=\"Optional context for this artifact\"\n />\n </FormField>\n\n <template v-if=\"activeKind === 'file'\">\n <FileUploader\n v-if=\"showFileUploader\"\n class=\"mint-artifact-save-dialog__file\"\n :accept=\"accept\"\n :max-size=\"maxFileSize\"\n :multiple=\"false\"\n @upload=\"handleFileUpload\"\n @error=\"handleFileError\"\n />\n <p\n v-if=\"isUpdate && existingFilename\"\n class=\"mint-artifact-save-dialog__immutable\"\n >\n Replaces the content of <code>{{ existingFilename }}</code> (kind\n <code>{{ existingFileKind ?? defaultFileKind }}</code>); filename and kind stay unchanged.\n </p>\n </template>\n\n <AlertBox\n v-if=\"request.error.value\"\n tone=\"error\"\n class=\"mint-artifact-save-dialog__error\"\n role=\"alert\"\n >\n {{ request.error.value }}\n </AlertBox>\n </div>\n\n <template #footer>\n <BaseButton variant=\"secondary\" :disabled=\"request.isLoading.value\" @click=\"handleCancel\">\n Cancel\n </BaseButton>\n <BaseButton\n class=\"mint-artifact-save-dialog__submit\"\n :loading=\"request.isLoading.value\"\n :disabled=\"!canSubmit\"\n @click=\"handleSubmit\"\n >\n {{ isUpdate ? 'Update Artifact' : 'Save Artifact' }}\n </BaseButton>\n </template>\n </BaseModal>\n</template>\n","<script setup lang=\"ts\">\n/** Inline badge that displays an experiment code and copies it to the clipboard on click. */\nimport { ref } from 'vue'\n\ninterface Props {\n code: string\n size?: 'sm' | 'md' | 'lg'\n copyable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n copyable: true,\n})\n\nconst emit = defineEmits<{\n copy: [code: string]\n}>()\n\nconst copied = ref(false)\nlet copyTimeout: ReturnType<typeof setTimeout> | null = null\n\nasync function handleCopy() {\n if (!props.copyable) return\n try {\n await navigator.clipboard.writeText(props.code)\n copied.value = true\n emit('copy', props.code)\n if (copyTimeout) clearTimeout(copyTimeout)\n copyTimeout = setTimeout(() => { copied.value = false }, 1500)\n } catch {\n // Clipboard API not available (e.g. insecure context)\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!props.copyable) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleCopy()\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mint-exp-code',\n `mint-exp-code--${size}`,\n { 'mint-exp-code--copyable': copyable, 'mint-exp-code--copied': copied },\n ]\"\n :role=\"copyable ? 'button' : undefined\"\n :tabindex=\"copyable ? 0 : undefined\"\n :title=\"copyable ? (copied ? 'Copied!' : 'Click to copy') : undefined\"\n @click=\"handleCopy\"\n @keydown=\"handleKeydown\"\n >\n {{ copied ? 'Copied!' : code }}\n </span>\n</template>\n","<script setup lang=\"ts\">\n/** Inline badge that displays an experiment code and copies it to the clipboard on click. */\nimport { ref } from 'vue'\n\ninterface Props {\n code: string\n size?: 'sm' | 'md' | 'lg'\n copyable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n copyable: true,\n})\n\nconst emit = defineEmits<{\n copy: [code: string]\n}>()\n\nconst copied = ref(false)\nlet copyTimeout: ReturnType<typeof setTimeout> | null = null\n\nasync function handleCopy() {\n if (!props.copyable) return\n try {\n await navigator.clipboard.writeText(props.code)\n copied.value = true\n emit('copy', props.code)\n if (copyTimeout) clearTimeout(copyTimeout)\n copyTimeout = setTimeout(() => { copied.value = false }, 1500)\n } catch {\n // Clipboard API not available (e.g. insecure context)\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!props.copyable) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleCopy()\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mint-exp-code',\n `mint-exp-code--${size}`,\n { 'mint-exp-code--copyable': copyable, 'mint-exp-code--copied': copied },\n ]\"\n :role=\"copyable ? 'button' : undefined\"\n :tabindex=\"copyable ? 0 : undefined\"\n :title=\"copyable ? (copied ? 'Copied!' : 'Click to copy') : undefined\"\n @click=\"handleCopy\"\n @keydown=\"handleKeydown\"\n >\n {{ copied ? 'Copied!' : code }}\n </span>\n</template>\n","export const FILE_PICKER_ICONS = {\n folder: 'M4 7h6l2 2h8v10H4z',\n file: 'M14 3v5h5M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z',\n lock: 'M7 11V8a5 5 0 0 1 10 0v3M5 11h14v10H5z',\n}\n\nexport function formatBytes(bytes: number | null): string {\n if (bytes === null) return '—'\n const units = ['B', 'KB', 'MB', 'GB', 'TB']\n let value = bytes\n let unit = 0\n while (value >= 1024 && unit < units.length - 1) {\n value /= 1024\n unit += 1\n }\n const rounded = value >= 100 || unit === 0 ? Math.round(value) : Number(value.toFixed(1))\n return `${rounded} ${units[unit]}`\n}\n\nconst pad = (value: number) => String(value).padStart(2, '0')\n\n/** Compact local timestamp for a fixed-width mono column: `MM-DD HH:mm`, or `YYYY-MM-DD` outside this year. */\nexport function formatModified(value: string | null | undefined, now = new Date()): string {\n const date = value ? new Date(value) : null\n if (!date || Number.isNaN(date.getTime())) return ''\n const day = `${pad(date.getMonth() + 1)}-${pad(date.getDate())}`\n return date.getFullYear() === now.getFullYear()\n ? `${day} ${pad(date.getHours())}:${pad(date.getMinutes())}`\n : `${date.getFullYear()}-${day}`\n}\n","import type { PickerCapability, PickerExcluded, PickerNode } from '../../types/filePicker'\n\nexport function normalizePickerFormat(format: string): string {\n return format.trim().replace(/^\\./, '').toLowerCase()\n}\n\nexport function pickerExclusion(\n node: PickerNode,\n capability?: PickerCapability,\n skipBlanks = false,\n): PickerExcluded | null {\n const base = { path: node.path, name: node.name }\n if (node.unavailableReason) return { ...base, reason: 'unreadable' }\n if (skipBlanks && capability?.isBlank?.(node)) return { ...base, reason: 'blank' }\n if (node.kind === 'folder') return null\n const formats = capability?.formats.map(normalizePickerFormat) ?? []\n if (formats.length && !formats.some((format) => node.name.toLowerCase().endsWith(`.${format}`))) {\n return { ...base, reason: 'unsupported' }\n }\n if (capability?.maxBytes != null && node.bytes != null && node.bytes > capability.maxBytes) {\n return { ...base, reason: 'tooLarge' }\n }\n return null\n}\n\nexport function pickerReason(excluded: PickerExcluded | null, node?: PickerNode): string {\n if (!excluded) return ''\n return {\n unsupported: 'This format is not supported by this source.',\n tooLarge: 'This file exceeds the size limit for this source.',\n blank: 'Skipped by the blank-file rule.',\n unreadable: node?.unavailableReason || 'This item cannot be read.',\n deselected: 'Removed from the selection.',\n forbidden: 'This item is not permitted by the system filter.',\n }[excluded.reason]\n}\n\nexport function pickerFormat(name: string): string {\n const compound = /\\.(mzml|mzxml)\\.gz$/i.exec(name)\n return compound ? `${compound[1]}.gz` : name.includes('.') ? name.split('.').pop()! : ''\n}\n","<script setup lang=\"ts\">\nimport { computed, h, nextTick, ref, type FunctionalComponent } from 'vue'\nimport BaseButton from '../BaseButton.vue'\nimport BaseCheckbox from '../BaseCheckbox.vue'\nimport Skeleton from '../Skeleton.vue'\nimport { FILE_PICKER_ICONS as ICON, formatBytes, formatModified } from './filePickerPresentation'\nimport { pickerReason } from '../../composables/filePicker/validation'\nimport type {\n PickerExcluded,\n PickerExclusionReason,\n PickerNode,\n PickerTreeRow,\n PickerSelectionMode,\n PickerView,\n} from '../../types/filePicker'\n\nconst DisclosureTriangle: FunctionalComponent = () =>\n h('svg', { viewBox: '0 0 16 16', fill: 'currentColor' }, [h('path', { d: 'm6 3 5 5-5 5z' })])\n\nconst props = defineProps<{\n emptyTitle?: string\n view: PickerView\n rows: PickerTreeRow[]\n loading: boolean\n error: string\n query: string\n /** Search results stopped at a limit; the rows are the first matches only. */\n truncated?: boolean\n mode: PickerSelectionMode\n selected: (node: PickerNode) => boolean\n selectable: (node: PickerNode) => boolean\n exclusion: (node: PickerNode) => PickerExcluded | null\n progress: (node: PickerNode) => string\n focusPath: string\n}>()\nconst emit = defineEmits<{\n select: [node: PickerNode, event: MouseEvent | KeyboardEvent]\n enter: [node: PickerNode]\n expand: [node: PickerNode]\n up: []\n retry: []\n parent: [node: PickerNode]\n}>()\n\nconst SHORT_REASON: Record<PickerExclusionReason, string> = {\n unsupported: 'Unsupported format',\n tooLarge: 'Over the size limit',\n blank: 'Blank',\n unreadable: 'No read permission',\n forbidden: 'No read permission',\n deselected: '',\n}\nconst tree = computed(() => props.view === 'tree')\nconst multi = computed(() => props.mode === 'multi-file' || props.mode === 'folder+files')\nconst list = ref<HTMLElement | null>(null)\n\nfunction reason(node: PickerNode): string {\n return pickerReason(props.exclusion(node), node)\n}\nfunction shortReason(node: PickerNode): string {\n const excluded = props.exclusion(node)\n if (!excluded) return ''\n return (excluded.reason === 'unreadable' && node.unavailableReason) || SHORT_REASON[excluded.reason]\n}\nfunction locked(node: PickerNode): boolean {\n const excluded = props.exclusion(node)?.reason\n return excluded === 'unreadable' || excluded === 'forbidden'\n}\nfunction partial(node: PickerNode): boolean {\n const progress = props.progress(node)\n return (\n node.kind === 'folder' &&\n props.selected(node) &&\n !!progress &&\n progress.split(' / ')[0] !== String(node.fileCount)\n )\n}\nfunction size(node: PickerNode): string {\n if (node.kind === 'folder' && props.selected(node) && props.progress(node)) return props.progress(node)\n return node.bytes == null ? '' : formatBytes(node.bytes)\n}\n\nconst focusablePath = computed(() => {\n const available = props.rows.filter((row) => !reason(row.node))\n return (\n available.find((row) => row.node.path === props.focusPath)?.node.path ?? available[0]?.node.path\n )\n})\nfunction focusRow(path: string) {\n nextTick(() => {\n const target = [...(list.value?.querySelectorAll<HTMLElement>('[data-path]') ?? [])].find(\n (el) => el.dataset.path === path,\n )\n target?.focus()\n target?.scrollIntoView({ block: 'nearest' })\n })\n}\nfunction keydown(event: KeyboardEvent, row: PickerTreeRow) {\n if (reason(row.node)) return\n const available = props.rows.filter((item) => !reason(item.node))\n const index = available.findIndex((item) => item.node.path === row.node.path)\n if (['ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) {\n event.preventDefault()\n const target =\n event.key === 'Home'\n ? available[0]\n : event.key === 'End'\n ? available[available.length - 1]\n : available[\n Math.max(\n 0,\n Math.min(available.length - 1, index + (event.key === 'ArrowDown' ? 1 : -1)),\n )\n ]\n if (target) {\n emit('select', target.node, event)\n focusRow(target.node.path)\n }\n } else if (event.key === 'ArrowRight') {\n event.preventDefault()\n // A flat list has no disclosure: Right opens the folder, Left goes back up.\n if (!tree.value) {\n if (row.node.kind === 'folder') emit('enter', row.node)\n }\n else if (row.node.kind === 'folder' && !row.expanded) emit('expand', row.node)\n else if (row.expanded) {\n const next = available[index + 1]\n if (next && next.depth > row.depth) {\n emit('select', next.node, event)\n focusRow(next.node.path)\n }\n }\n } else if (event.key === 'ArrowLeft') {\n event.preventDefault()\n if (row.expanded) emit('expand', row.node)\n else {\n const parent = props.rows\n .slice(0, props.rows.indexOf(row))\n .reverse()\n .find((item) => item.depth < row.depth)\n if (parent) {\n emit('select', parent.node, event)\n focusRow(parent.node.path)\n } else emit('up')\n }\n } else if (event.key === 'Enter') {\n event.preventDefault()\n emit('enter', row.node)\n } else if (event.key === ' ') {\n event.preventDefault()\n emit('select', row.node, event)\n }\n}\nfunction parts(name: string) {\n const index = props.query ? name.toLowerCase().indexOf(props.query.toLowerCase()) : -1\n return index < 0\n ? [name, '', '']\n : [\n name.slice(0, index),\n name.slice(index, index + props.query.length),\n name.slice(index + props.query.length),\n ]\n}\n</script>\n\n<template>\n <div\n ref=\"list\"\n class=\"mint-picker__list\"\n :role=\"tree ? 'tree' : 'listbox'\"\n aria-label=\"Folders and files\"\n :aria-multiselectable=\"multi\"\n :aria-busy=\"loading\"\n >\n <div class=\"mint-picker__list-head\" aria-hidden=\"true\">\n <span /><span>Name</span><span>Size</span><span>Modified</span>\n </div>\n <p v-if=\"truncated && !loading && !error && rows.length\" class=\"mint-picker__truncated\" role=\"status\">\n Showing the first {{ rows.length }} matches. Search a smaller folder or use a more specific search to see the rest.\n </p>\n <div v-if=\"loading\" class=\"mint-picker__skeleton\">\n <Skeleton v-for=\"n in 6\" :key=\"n\" width=\"100%\" height=\"1.25rem\" />\n </div>\n <div v-else-if=\"error\" role=\"alert\" class=\"mint-picker__empty\">\n <span>{{ error }}</span\n ><BaseButton variant=\"secondary\" size=\"sm\" @click=\"emit('retry')\">Try again</BaseButton>\n </div>\n <div v-else-if=\"!rows.length\" class=\"mint-picker__empty\" role=\"status\">\n <strong>{{\n truncated\n ? 'Search stopped before finding a match'\n : query ? 'No matching folders or files' : emptyTitle || 'No readable files'\n }}</strong>\n <span>{{\n truncated\n ? 'Search a smaller folder or use a more specific search.'\n : query ? 'Try a different search or include subfolders.' : 'Choose another location.'\n }}</span>\n </div>\n <template v-for=\"row in rows\" v-else :key=\"row.node.path\">\n <div\n :role=\"tree ? 'treeitem' : 'option'\"\n :data-path=\"row.node.path\"\n :aria-level=\"tree ? row.depth + 1 : undefined\"\n :aria-selected=\"selected(row.node)\"\n :aria-disabled=\"!!reason(row.node)\"\n :aria-expanded=\"\n tree && row.node.kind === 'folder' && !reason(row.node) ? row.expanded : undefined\n \"\n :tabindex=\"!reason(row.node) && row.node.path === focusablePath ? 0 : -1\"\n class=\"mint-picker__row\"\n :class=\"{\n 'mint-picker__row--selected': selected(row.node),\n 'mint-picker__row--unreadable': !!reason(row.node),\n }\"\n :style=\"{ '--picker-depth': row.depth }\"\n :title=\"reason(row.node) || row.node.name\"\n @click=\"!reason(row.node) && emit('select', row.node, $event)\"\n @dblclick=\"!reason(row.node) && emit('enter', row.node)\"\n @keydown=\"keydown($event, row)\"\n >\n <span class=\"mint-picker__cell mint-picker__cell--check\">\n <svg\n v-if=\"locked(row.node)\"\n class=\"mint-picker__lock\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.8\"\n aria-hidden=\"true\"\n >\n <path :d=\"ICON.lock\" />\n </svg>\n <!-- The row is the control (aria-selected); the checkbox only mirrors it. -->\n <BaseCheckbox\n v-else-if=\"multi && selectable(row.node)\"\n size=\"sm\"\n inert\n aria-hidden=\"true\"\n :model-value=\"selected(row.node)\"\n :indeterminate=\"partial(row.node)\"\n />\n </span>\n <span class=\"mint-picker__cell mint-picker__cell--name\">\n <template v-if=\"tree\">\n <BaseButton\n v-if=\"row.node.kind === 'folder' && row.node.hasChildren !== false && !reason(row.node)\"\n size=\"sm\"\n variant=\"ghost\"\n :class=\"['mint-picker__disclosure', { 'mint-picker__chevron--open': row.expanded }]\"\n :icon=\"DisclosureTriangle\"\n :label=\"`${row.expanded ? 'Collapse' : 'Expand'} ${row.node.name}`\"\n tabindex=\"-1\"\n :loading=\"row.loading\"\n @click.stop=\"emit('expand', row.node)\"\n @dblclick.stop\n />\n <span v-else class=\"mint-picker__disclosure\" />\n </template>\n <svg\n class=\"mint-picker__node-icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.7\"\n aria-hidden=\"true\"\n >\n <path :d=\"row.node.kind === 'folder' ? ICON.folder : ICON.file\" />\n </svg>\n <span class=\"mint-picker__name\"\n >{{ parts(row.node.name)[0]\n }}<mark v-if=\"parts(row.node.name)[1]\">{{ parts(row.node.name)[1] }}</mark\n >{{ parts(row.node.name)[2] }}</span\n >\n <span v-if=\"reason(row.node)\" class=\"mint-picker__reason\">{{\n shortReason(row.node)\n }}</span>\n <span v-else-if=\"query && row.node.parentName\" class=\"mint-picker__parent-name\">{{\n row.node.parentName\n }}</span>\n <BaseButton\n v-if=\"\n query &&\n mode === 'folder' &&\n row.node.kind === 'file' &&\n row.node.parentPath &&\n !reason(row.node)\n \"\n variant=\"ghost\"\n size=\"sm\"\n @click.stop=\"emit('parent', row.node)\"\n >Select its folder</BaseButton\n >\n </span>\n <span\n class=\"mint-picker__cell mint-picker__cell--size\"\n :class=\"{ 'mint-picker__partial': partial(row.node) }\"\n >{{ size(row.node) }}</span\n >\n <span class=\"mint-picker__cell mint-picker__cell--time\">{{\n formatModified(row.node.modifiedAt)\n }}</span>\n </div>\n <div v-if=\"row.error\" class=\"mint-picker__branch-error\" role=\"alert\">\n {{ row.error\n }}<BaseButton variant=\"ghost\" size=\"sm\" @click=\"emit('expand', row.node)\"\n >Close branch</BaseButton\n >\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, h, nextTick, ref, type FunctionalComponent } from 'vue'\nimport BaseButton from '../BaseButton.vue'\nimport BaseCheckbox from '../BaseCheckbox.vue'\nimport Skeleton from '../Skeleton.vue'\nimport { FILE_PICKER_ICONS as ICON, formatBytes, formatModified } from './filePickerPresentation'\nimport { pickerReason } from '../../composables/filePicker/validation'\nimport type {\n PickerExcluded,\n PickerExclusionReason,\n PickerNode,\n PickerTreeRow,\n PickerSelectionMode,\n PickerView,\n} from '../../types/filePicker'\n\nconst DisclosureTriangle: FunctionalComponent = () =>\n h('svg', { viewBox: '0 0 16 16', fill: 'currentColor' }, [h('path', { d: 'm6 3 5 5-5 5z' })])\n\nconst props = defineProps<{\n emptyTitle?: string\n view: PickerView\n rows: PickerTreeRow[]\n loading: boolean\n error: string\n query: string\n /** Search results stopped at a limit; the rows are the first matches only. */\n truncated?: boolean\n mode: PickerSelectionMode\n selected: (node: PickerNode) => boolean\n selectable: (node: PickerNode) => boolean\n exclusion: (node: PickerNode) => PickerExcluded | null\n progress: (node: PickerNode) => string\n focusPath: string\n}>()\nconst emit = defineEmits<{\n select: [node: PickerNode, event: MouseEvent | KeyboardEvent]\n enter: [node: PickerNode]\n expand: [node: PickerNode]\n up: []\n retry: []\n parent: [node: PickerNode]\n}>()\n\nconst SHORT_REASON: Record<PickerExclusionReason, string> = {\n unsupported: 'Unsupported format',\n tooLarge: 'Over the size limit',\n blank: 'Blank',\n unreadable: 'No read permission',\n forbidden: 'No read permission',\n deselected: '',\n}\nconst tree = computed(() => props.view === 'tree')\nconst multi = computed(() => props.mode === 'multi-file' || props.mode === 'folder+files')\nconst list = ref<HTMLElement | null>(null)\n\nfunction reason(node: PickerNode): string {\n return pickerReason(props.exclusion(node), node)\n}\nfunction shortReason(node: PickerNode): string {\n const excluded = props.exclusion(node)\n if (!excluded) return ''\n return (excluded.reason === 'unreadable' && node.unavailableReason) || SHORT_REASON[excluded.reason]\n}\nfunction locked(node: PickerNode): boolean {\n const excluded = props.exclusion(node)?.reason\n return excluded === 'unreadable' || excluded === 'forbidden'\n}\nfunction partial(node: PickerNode): boolean {\n const progress = props.progress(node)\n return (\n node.kind === 'folder' &&\n props.selected(node) &&\n !!progress &&\n progress.split(' / ')[0] !== String(node.fileCount)\n )\n}\nfunction size(node: PickerNode): string {\n if (node.kind === 'folder' && props.selected(node) && props.progress(node)) return props.progress(node)\n return node.bytes == null ? '' : formatBytes(node.bytes)\n}\n\nconst focusablePath = computed(() => {\n const available = props.rows.filter((row) => !reason(row.node))\n return (\n available.find((row) => row.node.path === props.focusPath)?.node.path ?? available[0]?.node.path\n )\n})\nfunction focusRow(path: string) {\n nextTick(() => {\n const target = [...(list.value?.querySelectorAll<HTMLElement>('[data-path]') ?? [])].find(\n (el) => el.dataset.path === path,\n )\n target?.focus()\n target?.scrollIntoView({ block: 'nearest' })\n })\n}\nfunction keydown(event: KeyboardEvent, row: PickerTreeRow) {\n if (reason(row.node)) return\n const available = props.rows.filter((item) => !reason(item.node))\n const index = available.findIndex((item) => item.node.path === row.node.path)\n if (['ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) {\n event.preventDefault()\n const target =\n event.key === 'Home'\n ? available[0]\n : event.key === 'End'\n ? available[available.length - 1]\n : available[\n Math.max(\n 0,\n Math.min(available.length - 1, index + (event.key === 'ArrowDown' ? 1 : -1)),\n )\n ]\n if (target) {\n emit('select', target.node, event)\n focusRow(target.node.path)\n }\n } else if (event.key === 'ArrowRight') {\n event.preventDefault()\n // A flat list has no disclosure: Right opens the folder, Left goes back up.\n if (!tree.value) {\n if (row.node.kind === 'folder') emit('enter', row.node)\n }\n else if (row.node.kind === 'folder' && !row.expanded) emit('expand', row.node)\n else if (row.expanded) {\n const next = available[index + 1]\n if (next && next.depth > row.depth) {\n emit('select', next.node, event)\n focusRow(next.node.path)\n }\n }\n } else if (event.key === 'ArrowLeft') {\n event.preventDefault()\n if (row.expanded) emit('expand', row.node)\n else {\n const parent = props.rows\n .slice(0, props.rows.indexOf(row))\n .reverse()\n .find((item) => item.depth < row.depth)\n if (parent) {\n emit('select', parent.node, event)\n focusRow(parent.node.path)\n } else emit('up')\n }\n } else if (event.key === 'Enter') {\n event.preventDefault()\n emit('enter', row.node)\n } else if (event.key === ' ') {\n event.preventDefault()\n emit('select', row.node, event)\n }\n}\nfunction parts(name: string) {\n const index = props.query ? name.toLowerCase().indexOf(props.query.toLowerCase()) : -1\n return index < 0\n ? [name, '', '']\n : [\n name.slice(0, index),\n name.slice(index, index + props.query.length),\n name.slice(index + props.query.length),\n ]\n}\n</script>\n\n<template>\n <div\n ref=\"list\"\n class=\"mint-picker__list\"\n :role=\"tree ? 'tree' : 'listbox'\"\n aria-label=\"Folders and files\"\n :aria-multiselectable=\"multi\"\n :aria-busy=\"loading\"\n >\n <div class=\"mint-picker__list-head\" aria-hidden=\"true\">\n <span /><span>Name</span><span>Size</span><span>Modified</span>\n </div>\n <p v-if=\"truncated && !loading && !error && rows.length\" class=\"mint-picker__truncated\" role=\"status\">\n Showing the first {{ rows.length }} matches. Search a smaller folder or use a more specific search to see the rest.\n </p>\n <div v-if=\"loading\" class=\"mint-picker__skeleton\">\n <Skeleton v-for=\"n in 6\" :key=\"n\" width=\"100%\" height=\"1.25rem\" />\n </div>\n <div v-else-if=\"error\" role=\"alert\" class=\"mint-picker__empty\">\n <span>{{ error }}</span\n ><BaseButton variant=\"secondary\" size=\"sm\" @click=\"emit('retry')\">Try again</BaseButton>\n </div>\n <div v-else-if=\"!rows.length\" class=\"mint-picker__empty\" role=\"status\">\n <strong>{{\n truncated\n ? 'Search stopped before finding a match'\n : query ? 'No matching folders or files' : emptyTitle || 'No readable files'\n }}</strong>\n <span>{{\n truncated\n ? 'Search a smaller folder or use a more specific search.'\n : query ? 'Try a different search or include subfolders.' : 'Choose another location.'\n }}</span>\n </div>\n <template v-for=\"row in rows\" v-else :key=\"row.node.path\">\n <div\n :role=\"tree ? 'treeitem' : 'option'\"\n :data-path=\"row.node.path\"\n :aria-level=\"tree ? row.depth + 1 : undefined\"\n :aria-selected=\"selected(row.node)\"\n :aria-disabled=\"!!reason(row.node)\"\n :aria-expanded=\"\n tree && row.node.kind === 'folder' && !reason(row.node) ? row.expanded : undefined\n \"\n :tabindex=\"!reason(row.node) && row.node.path === focusablePath ? 0 : -1\"\n class=\"mint-picker__row\"\n :class=\"{\n 'mint-picker__row--selected': selected(row.node),\n 'mint-picker__row--unreadable': !!reason(row.node),\n }\"\n :style=\"{ '--picker-depth': row.depth }\"\n :title=\"reason(row.node) || row.node.name\"\n @click=\"!reason(row.node) && emit('select', row.node, $event)\"\n @dblclick=\"!reason(row.node) && emit('enter', row.node)\"\n @keydown=\"keydown($event, row)\"\n >\n <span class=\"mint-picker__cell mint-picker__cell--check\">\n <svg\n v-if=\"locked(row.node)\"\n class=\"mint-picker__lock\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.8\"\n aria-hidden=\"true\"\n >\n <path :d=\"ICON.lock\" />\n </svg>\n <!-- The row is the control (aria-selected); the checkbox only mirrors it. -->\n <BaseCheckbox\n v-else-if=\"multi && selectable(row.node)\"\n size=\"sm\"\n inert\n aria-hidden=\"true\"\n :model-value=\"selected(row.node)\"\n :indeterminate=\"partial(row.node)\"\n />\n </span>\n <span class=\"mint-picker__cell mint-picker__cell--name\">\n <template v-if=\"tree\">\n <BaseButton\n v-if=\"row.node.kind === 'folder' && row.node.hasChildren !== false && !reason(row.node)\"\n size=\"sm\"\n variant=\"ghost\"\n :class=\"['mint-picker__disclosure', { 'mint-picker__chevron--open': row.expanded }]\"\n :icon=\"DisclosureTriangle\"\n :label=\"`${row.expanded ? 'Collapse' : 'Expand'} ${row.node.name}`\"\n tabindex=\"-1\"\n :loading=\"row.loading\"\n @click.stop=\"emit('expand', row.node)\"\n @dblclick.stop\n />\n <span v-else class=\"mint-picker__disclosure\" />\n </template>\n <svg\n class=\"mint-picker__node-icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.7\"\n aria-hidden=\"true\"\n >\n <path :d=\"row.node.kind === 'folder' ? ICON.folder : ICON.file\" />\n </svg>\n <span class=\"mint-picker__name\"\n >{{ parts(row.node.name)[0]\n }}<mark v-if=\"parts(row.node.name)[1]\">{{ parts(row.node.name)[1] }}</mark\n >{{ parts(row.node.name)[2] }}</span\n >\n <span v-if=\"reason(row.node)\" class=\"mint-picker__reason\">{{\n shortReason(row.node)\n }}</span>\n <span v-else-if=\"query && row.node.parentName\" class=\"mint-picker__parent-name\">{{\n row.node.parentName\n }}</span>\n <BaseButton\n v-if=\"\n query &&\n mode === 'folder' &&\n row.node.kind === 'file' &&\n row.node.parentPath &&\n !reason(row.node)\n \"\n variant=\"ghost\"\n size=\"sm\"\n @click.stop=\"emit('parent', row.node)\"\n >Select its folder</BaseButton\n >\n </span>\n <span\n class=\"mint-picker__cell mint-picker__cell--size\"\n :class=\"{ 'mint-picker__partial': partial(row.node) }\"\n >{{ size(row.node) }}</span\n >\n <span class=\"mint-picker__cell mint-picker__cell--time\">{{\n formatModified(row.node.modifiedAt)\n }}</span>\n </div>\n <div v-if=\"row.error\" class=\"mint-picker__branch-error\" role=\"alert\">\n {{ row.error\n }}<BaseButton variant=\"ghost\" size=\"sm\" @click=\"emit('expand', row.node)\"\n >Close branch</BaseButton\n >\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport BaseButton from '../BaseButton.vue'\nimport { FILE_PICKER_ICONS as ICON, formatBytes, formatModified } from './filePickerPresentation'\nimport type { PickerNode, PickerExcluded } from '../../types/filePicker'\nimport { pickerReason } from '../../composables/filePicker/validation'\n\ndefineProps<{\n totalCount?: number | null\n node: PickerNode | null\n sourceLabel: string\n exclusions: PickerExcluded[]\n busy: boolean\n error: string\n reviewing: boolean\n}>()\nconst emit = defineEmits<{ review: []; retry: [] }>()\n</script>\n<template>\n <aside class=\"mint-picker__preview\" aria-label=\"Selection preview\">\n <template v-if=\"reviewing\">\n <div class=\"mint-picker__preview-heading\">\n Skipped files\n <BaseButton variant=\"ghost\" size=\"sm\" @click=\"emit('review')\">Back</BaseButton>\n </div>\n <div\n v-for=\"item in exclusions\"\n :key=\"item.path\"\n class=\"mint-picker__exclusion\"\n :title=\"item.path\"\n >\n <strong>{{ item.name }}</strong\n ><span>{{ pickerReason(item) }}</span>\n </div>\n </template>\n <template v-else-if=\"node\">\n <div class=\"mint-picker__preview-head\">\n <span class=\"mint-picker__preview-icon\">\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.7\"\n aria-hidden=\"true\"\n >\n <path :d=\"node.kind === 'folder' ? ICON.folder : ICON.file\" />\n </svg>\n </span>\n <span class=\"mint-picker__preview-title\">\n <strong class=\"mint-picker__preview-name\">{{ node.name }}</strong>\n <span class=\"mint-picker__preview-kind\">{{\n node.kind === 'folder' ? 'Folder' : node.format ? `${node.format.toUpperCase()} file` : 'File'\n }}</span>\n </span>\n </div>\n <dl class=\"mint-picker__metadata\">\n <dt>Size</dt>\n <dd>{{ node.bytes == null ? '—' : formatBytes(node.bytes) }}</dd>\n <template v-if=\"node.modifiedAt\">\n <dt>Modified</dt>\n <dd>{{ formatModified(node.modifiedAt) }}</dd>\n </template>\n <template v-if=\"node.kind === 'folder'\">\n <dt>Files</dt>\n <dd>\n {{ node.fileCount ?? '—'\n }}<template v-if=\"totalCount != null && totalCount !== node.fileCount\">\n of {{ totalCount }}</template\n >\n </dd>\n <template v-if=\"node.formats\">\n <dt>Formats</dt>\n <dd>\n <span v-for=\"(count, format) in node.formats\" :key=\"format\"\n >{{ format.toUpperCase() }} ×{{ count }}<br\n /></span>\n </dd>\n </template>\n </template>\n <dt>Location</dt>\n <dd :title=\"node.parentName || node.path\">{{ node.parentName || node.path }}</dd>\n <dt>Source</dt>\n <dd>{{ sourceLabel }}</dd>\n </dl>\n <BaseButton\n v-if=\"exclusions.length\"\n variant=\"ghost\"\n size=\"sm\"\n class=\"mint-picker__skipped\"\n @click=\"emit('review')\"\n >{{ exclusions.length }} skipped · Review</BaseButton\n >\n <div v-if=\"error\" class=\"mint-picker__preview-error\" role=\"alert\">\n {{ error\n }}<BaseButton variant=\"secondary\" size=\"sm\" @click=\"emit('retry')\">Try again</BaseButton>\n </div>\n <span v-else-if=\"busy\" class=\"mint-picker__preview-status\" role=\"status\"\n >Checking selection…</span\n >\n </template>\n <p v-else class=\"mint-picker__preview-empty\">Select a file to see its details.</p>\n </aside>\n</template>\n","<script setup lang=\"ts\">\nimport BaseButton from '../BaseButton.vue'\nimport { FILE_PICKER_ICONS as ICON, formatBytes, formatModified } from './filePickerPresentation'\nimport type { PickerNode, PickerExcluded } from '../../types/filePicker'\nimport { pickerReason } from '../../composables/filePicker/validation'\n\ndefineProps<{\n totalCount?: number | null\n node: PickerNode | null\n sourceLabel: string\n exclusions: PickerExcluded[]\n busy: boolean\n error: string\n reviewing: boolean\n}>()\nconst emit = defineEmits<{ review: []; retry: [] }>()\n</script>\n<template>\n <aside class=\"mint-picker__preview\" aria-label=\"Selection preview\">\n <template v-if=\"reviewing\">\n <div class=\"mint-picker__preview-heading\">\n Skipped files\n <BaseButton variant=\"ghost\" size=\"sm\" @click=\"emit('review')\">Back</BaseButton>\n </div>\n <div\n v-for=\"item in exclusions\"\n :key=\"item.path\"\n class=\"mint-picker__exclusion\"\n :title=\"item.path\"\n >\n <strong>{{ item.name }}</strong\n ><span>{{ pickerReason(item) }}</span>\n </div>\n </template>\n <template v-else-if=\"node\">\n <div class=\"mint-picker__preview-head\">\n <span class=\"mint-picker__preview-icon\">\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.7\"\n aria-hidden=\"true\"\n >\n <path :d=\"node.kind === 'folder' ? ICON.folder : ICON.file\" />\n </svg>\n </span>\n <span class=\"mint-picker__preview-title\">\n <strong class=\"mint-picker__preview-name\">{{ node.name }}</strong>\n <span class=\"mint-picker__preview-kind\">{{\n node.kind === 'folder' ? 'Folder' : node.format ? `${node.format.toUpperCase()} file` : 'File'\n }}</span>\n </span>\n </div>\n <dl class=\"mint-picker__metadata\">\n <dt>Size</dt>\n <dd>{{ node.bytes == null ? '—' : formatBytes(node.bytes) }}</dd>\n <template v-if=\"node.modifiedAt\">\n <dt>Modified</dt>\n <dd>{{ formatModified(node.modifiedAt) }}</dd>\n </template>\n <template v-if=\"node.kind === 'folder'\">\n <dt>Files</dt>\n <dd>\n {{ node.fileCount ?? '—'\n }}<template v-if=\"totalCount != null && totalCount !== node.fileCount\">\n of {{ totalCount }}</template\n >\n </dd>\n <template v-if=\"node.formats\">\n <dt>Formats</dt>\n <dd>\n <span v-for=\"(count, format) in node.formats\" :key=\"format\"\n >{{ format.toUpperCase() }} ×{{ count }}<br\n /></span>\n </dd>\n </template>\n </template>\n <dt>Location</dt>\n <dd :title=\"node.parentName || node.path\">{{ node.parentName || node.path }}</dd>\n <dt>Source</dt>\n <dd>{{ sourceLabel }}</dd>\n </dl>\n <BaseButton\n v-if=\"exclusions.length\"\n variant=\"ghost\"\n size=\"sm\"\n class=\"mint-picker__skipped\"\n @click=\"emit('review')\"\n >{{ exclusions.length }} skipped · Review</BaseButton\n >\n <div v-if=\"error\" class=\"mint-picker__preview-error\" role=\"alert\">\n {{ error\n }}<BaseButton variant=\"secondary\" size=\"sm\" @click=\"emit('retry')\">Try again</BaseButton>\n </div>\n <span v-else-if=\"busy\" class=\"mint-picker__preview-status\" role=\"status\"\n >Checking selection…</span\n >\n </template>\n <p v-else class=\"mint-picker__preview-empty\">Select a file to see its details.</p>\n </aside>\n</template>\n","import { computed, onScopeDispose, ref, shallowRef } from 'vue'\nimport type {\n PickerAdapter,\n PickerNode,\n PickerRequest,\n PickerSort,\n PickerTreeRow,\n} from '../../types/filePicker'\n\nconst byName = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare\n/** Folders first; undated rows go last in either time order. */\nfunction ordered(items: PickerNode[], sort: PickerSort | undefined): PickerNode[] {\n if (!sort) return items\n // Parse each timestamp once rather than on every comparison.\n const times = new Map(items.map((node) => [node, node.modifiedAt ? Date.parse(node.modifiedAt) : NaN]))\n const time = (node: PickerNode) => times.get(node)!\n return [...items].sort((a, b) => {\n if (a.kind !== b.kind) return a.kind === 'folder' ? -1 : 1\n if (sort === 'name') return byName(a.name, b.name)\n if (sort === 'name-desc') return byName(b.name, a.name)\n const [ta, tb] = [time(a), time(b)]\n if (Number.isNaN(ta) || Number.isNaN(tb))\n return Number.isNaN(ta) === Number.isNaN(tb) ? byName(a.name, b.name) : Number.isNaN(ta) ? 1 : -1\n return (sort === 'newest' ? tb - ta : ta - tb) || byName(a.name, b.name)\n })\n}\n\nexport function usePickerNavigation(\n adapter: () => PickerAdapter,\n request: () => PickerRequest,\n systemFilter: (node: PickerNode) => boolean = () => true,\n sort: () => PickerSort | undefined = () => undefined,\n) {\n const path = shallowRef('')\n const trail = ref<{ path: string; label: string }[]>([])\n const entries = shallowRef<PickerNode[]>([])\n const nodes = ref(new Map<string, PickerNode>())\n const children = shallowRef(new Map<string, PickerNode[]>())\n const expanded = ref(new Set<string>())\n const expanding = ref(new Set<string>())\n const expansionErrors = ref(new Map<string, string>())\n const loading = shallowRef(false)\n const error = shallowRef('')\n /** True when the current search results stopped at an adapter limit. */\n const truncated = shallowRef(false)\n let epoch = 0\n let navigation = 0\n let controller: AbortController | undefined\n const pending = new Map<string, Promise<PickerNode[]>>()\n const childControllers = new Set<AbortController>()\n let prefetchTimer: ReturnType<typeof setTimeout> | undefined\n let prefetchQueue: PickerNode[] = []\n let prefetchActive = 0\n\n function stopPrefetch() {\n clearTimeout(prefetchTimer)\n prefetchQueue = []\n }\n function drainPrefetch() {\n while (prefetchActive < 2 && prefetchQueue.length) {\n const node = prefetchQueue.shift()!\n if (!isAllowed(node) || children.value.has(node.path) || pending.has(node.path)) continue\n prefetchActive++\n const token = epoch\n void loadChildren(node.path).catch(() => {\n // Speculative failures are retried and reported only when the folder is opened.\n }).finally(() => {\n if (token !== epoch) return\n prefetchActive--\n drainPrefetch()\n })\n }\n }\n function prefetch(items: PickerNode[]) {\n stopPrefetch()\n // ponytail: warm at most 20 immediate folders, two at a time; use a server index for full-tree search.\n const candidates = items.filter((node) => node.kind === 'folder' &&\n !node.unavailableReason && node.hasChildren !== false && isAllowed(node)).slice(0, 20)\n prefetchTimer = setTimeout(() => {\n prefetchQueue = candidates\n drainPrefetch()\n }, 100)\n }\n\n function isAllowed(node: PickerNode): boolean {\n const seen = new Set<string>()\n let current: PickerNode | undefined = nodes.value.get(node.path) ?? node\n while (current && !seen.has(current.path)) {\n if (!systemFilter(current)) return false\n seen.add(current.path)\n current = current.parentPath ? nodes.value.get(current.parentPath) : undefined\n }\n return true\n }\n\n function register(items: PickerNode[], parent?: string) {\n for (const item of items)\n nodes.value.set(item.path, {\n ...item,\n parentPath:\n item.parentPath !== undefined\n ? item.parentPath\n : parent !== undefined\n ? parent || null\n : (nodes.value.get(item.path)?.parentPath ?? null),\n })\n }\n function invalidate() {\n adapter().invalidate?.()\n stopPrefetch()\n prefetchActive = 0\n ++epoch\n ++navigation\n controller?.abort()\n childControllers.forEach((item) => item.abort())\n childControllers.clear()\n pending.clear()\n children.value = new Map()\n expanding.value.clear()\n expansionErrors.value.clear()\n }\n function clear() {\n invalidate()\n nodes.value.clear()\n expanded.value.clear()\n expanding.value.clear()\n expansionErrors.value.clear()\n entries.value = []\n loading.value = false\n error.value = ''\n }\n async function loadChildren(\n folder: string,\n ancestors: string[] = [],\n refresh = false,\n ): Promise<PickerNode[]> {\n if (ancestors.includes(folder)) throw new Error('The adapter returned a circular folder tree.')\n const cached = children.value.get(folder)\n if (cached) return cached\n const existing = pending.get(folder)\n if (existing) return existing\n const token = epoch\n const abort = new AbortController()\n childControllers.add(abort)\n const task = adapter()\n .listChildren(folder, { ...request(), ...(refresh && { refresh }), signal: abort.signal })\n .then((items) => {\n if (token !== epoch) throw new DOMException('Cancelled', 'AbortError')\n const retained = new Set(items.map((item) => item.path))\n function remove(path: string, seen = new Set<string>()) {\n if (seen.has(path) || path === folder) return\n seen.add(path)\n for (const node of nodes.value.values())\n if (node.parentPath === path) remove(node.path, seen)\n nodes.value.delete(path)\n children.value.delete(path)\n }\n for (const node of nodes.value.values())\n if (node.parentPath === folder && !retained.has(node.path)) remove(node.path)\n register(items, folder)\n children.value = new Map(children.value).set(folder, items)\n return items\n })\n .finally(() => {\n childControllers.delete(abort)\n if (token === epoch) pending.delete(folder)\n })\n pending.set(folder, task)\n return task\n }\n async function load(query = '', recursive = false, refresh = false) {\n stopPrefetch()\n const token = ++navigation\n controller?.abort()\n controller = new AbortController()\n const options = { ...request(), ...(refresh && { refresh }), signal: controller.signal }\n loading.value = true\n error.value = ''\n truncated.value = false\n entries.value = []\n try {\n const found = query.trim()\n ? await adapter().search(query.trim(), { path: path.value, recursive }, options)\n : path.value\n ? await loadChildren(path.value, [], refresh)\n : await adapter().listRoot(options)\n if (token !== navigation) return\n const items = Array.isArray(found) ? found : found.nodes\n truncated.value = !Array.isArray(found) && found.truncated\n register(items, query.trim() ? undefined : path.value)\n entries.value = items\n if (!query.trim()) prefetch(items)\n } catch (cause) {\n if (token === navigation)\n error.value = cause instanceof Error ? cause.message : 'Could not read this location.'\n } finally {\n if (token === navigation) loading.value = false\n }\n }\n async function expand(node: PickerNode) {\n if (node.kind !== 'folder' || node.unavailableReason || !isAllowed(node)) return\n if (expanded.value.has(node.path)) {\n expanded.value.delete(node.path)\n return\n }\n expanded.value.add(node.path)\n expanding.value.add(node.path)\n expansionErrors.value.delete(node.path)\n const token = epoch\n const view = navigation\n try {\n const items = await loadChildren(node.path)\n if (token === epoch && view === navigation && expanded.value.has(node.path)) prefetch(items)\n } catch (cause) {\n if (token === epoch)\n expansionErrors.value.set(\n node.path,\n cause instanceof Error ? cause.message : 'Could not read this folder.',\n )\n } finally {\n if (token === epoch) expanding.value.delete(node.path)\n }\n }\n function enter(node: PickerNode) {\n if (node.kind !== 'folder' || node.unavailableReason || !isAllowed(node)) return\n const index = trail.value.findIndex((crumb) => crumb.path === node.path)\n trail.value =\n index >= 0\n ? trail.value.slice(0, index + 1)\n : [...trail.value, { path: node.path, label: node.name }]\n path.value = node.path\n }\n function up(target?: string) {\n const index =\n target !== undefined\n ? trail.value.findIndex((crumb) => crumb.path === target)\n : trail.value.length - 2\n trail.value = index < 0 ? [] : trail.value.slice(0, index + 1)\n path.value = trail.value[trail.value.length - 1]?.path ?? ''\n }\n const rows = computed(() => {\n const result: PickerTreeRow[] = []\n const order = sort()\n function visit(items: PickerNode[], depth: number, ancestors: Set<string>) {\n for (const item of ordered(items, order)) {\n if (ancestors.has(item.path) || depth > 30) continue\n const node = nodes.value.get(item.path) ?? item\n if (!isAllowed(node)) continue\n result.push({\n node,\n depth,\n expanded: expanded.value.has(item.path),\n loading: expanding.value.has(item.path),\n error: expansionErrors.value.get(item.path),\n })\n if (expanded.value.has(item.path))\n visit(children.value.get(item.path) ?? [], depth + 1, new Set([...ancestors, item.path]))\n }\n }\n visit(entries.value, 0, new Set())\n return result\n })\n onScopeDispose(clear)\n return {\n path,\n trail,\n entries,\n nodes,\n children,\n expanded,\n expanding,\n expansionErrors,\n loading,\n error,\n truncated,\n rows,\n isAllowed,\n clear,\n invalidate,\n register,\n loadChildren,\n load,\n expand,\n enter,\n up,\n }\n}\n","import { computed, ref, shallowRef, watch } from 'vue'\nimport type {\n PickerCapability,\n PickerExcluded,\n PickerFile,\n PickerNode,\n PickerSelection,\n PickerSelectionMode,\n} from '../../types/filePicker'\nimport type { usePickerNavigation } from './usePickerNavigation'\nimport { pickerExclusion } from './validation'\n\ntype Navigation = ReturnType<typeof usePickerNavigation>\nexport function usePickerSelection(\n nav: Navigation,\n mode: () => PickerSelectionMode,\n capability: () => PickerCapability | undefined,\n skipBlanks: () => boolean,\n) {\n const keys = ref(new Set<string>())\n const dropped = ref(new Set<string>())\n const focusPath = shallowRef('')\n const resolving = shallowRef(false)\n const resolutionError = shallowRef('')\n let anchor = ''\n let generation = 0\n\n function ancestorSelected(node: PickerNode, candidates = keys.value): boolean {\n const seen = new Set<string>()\n let parent = node.parentPath\n while (parent && !seen.has(parent)) {\n if (candidates.has(parent)) return true\n seen.add(parent)\n parent = nav.nodes.value.get(parent)?.parentPath\n }\n return false\n }\n function exclusion(node: PickerNode) {\n if (!nav.isAllowed(node))\n return { path: node.path, name: node.name, reason: 'forbidden' as const }\n const own = pickerExclusion(node, capability(), skipBlanks())\n if (own) return own\n const seen = new Set<string>()\n let parent = node.parentPath\n while (parent && !seen.has(parent)) {\n seen.add(parent)\n const folder = nav.nodes.value.get(parent)\n if (!folder) break\n const inherited = pickerExclusion(folder, capability(), skipBlanks())\n if (inherited) return { ...inherited, path: node.path, name: node.name }\n parent = folder.parentPath\n }\n return null\n }\n function selected(node: PickerNode): boolean {\n return (\n !exclusion(node) &&\n !dropped.value.has(node.path) &&\n !ancestorSelected(node, dropped.value) &&\n (keys.value.has(node.path) || (mode() === 'folder+files' && ancestorSelected(node)))\n )\n }\n function selectable(node: PickerNode): boolean {\n if (exclusion(node)) return false\n return node.kind === 'folder'\n ? mode() === 'folder' || mode() === 'folder+files'\n : mode() !== 'folder'\n }\n function select(\n node: PickerNode,\n modifiers: { metaKey?: boolean; ctrlKey?: boolean; shiftKey?: boolean; toggle?: boolean } = {},\n ) {\n if (exclusion(node)) return\n focusPath.value = node.path\n if (!selectable(node)) return\n const multi = mode() === 'multi-file' || mode() === 'folder+files'\n // Multi modes behave like a checklist: a plain click (or Space) adds or\n // removes one row, Shift extends a range, Ctrl/Cmd is accepted for habit.\n const additive = multi && (modifiers.metaKey || modifiers.ctrlKey || modifiers.toggle)\n if (multi && modifiers.shiftKey && anchor) {\n const rows = nav.rows.value.map((row) => row.node)\n const from = rows.findIndex((item) => item.path === anchor)\n const to = rows.findIndex((item) => item.path === node.path)\n if (from >= 0 && to >= 0) {\n keys.value = new Set([\n ...(additive ? keys.value : []),\n ...rows\n .slice(Math.min(from, to), Math.max(from, to) + 1)\n .filter(selectable)\n .map((item) => item.path),\n ])\n dropped.value.clear()\n return\n }\n }\n if (additive || (modifiers.toggle && (selected(node) || dropped.value.has(node.path)))) {\n if (mode() === 'folder+files' && ancestorSelected(node)) {\n if (dropped.value.has(node.path)) dropped.value.delete(node.path)\n else dropped.value.add(node.path)\n } else if (keys.value.has(node.path)) keys.value.delete(node.path)\n else {\n keys.value.add(node.path)\n dropped.value.delete(node.path)\n }\n } else {\n keys.value = new Set([node.path])\n dropped.value.clear()\n }\n anchor = node.path\n }\n function reset(selection?: PickerSelection | null) {\n ++generation\n keys.value.clear()\n dropped.value.clear()\n resolving.value = false\n resolutionError.value = ''\n focusPath.value = ''\n if (selection?.source === 'server') {\n const files =\n mode() === 'folder'\n ? []\n : mode() === 'single-file'\n ? selection.files.slice(0, 1)\n : selection.files\n const folders =\n mode() === 'folder'\n ? selection.folders.slice(0, 1)\n : mode() === 'folder+files'\n ? selection.folders\n : []\n nav.register(files)\n folders.forEach((path) => {\n if (!nav.nodes.value.has(path))\n nav.register([\n { kind: 'folder', path, name: 'Selected folder', parentName: 'Selected location' },\n ])\n keys.value.add(path)\n })\n files.forEach((file) => keys.value.add(file.path))\n selection.excluded\n .filter((item) => item.reason === 'deselected')\n .forEach((item) => dropped.value.add(item.path))\n }\n }\n const roots = computed(() =>\n [...keys.value]\n .map((key) => nav.nodes.value.get(key))\n .filter((node): node is PickerNode => !!node && !ancestorSelected(node) && nav.isAllowed(node)),\n )\n const chosen = computed(() => {\n const files: PickerFile[] = []\n const excluded: PickerExcluded[] = []\n for (const node of nav.nodes.value.values()) {\n if (!nav.isAllowed(node)) continue\n if (!keys.value.has(node.path) && !ancestorSelected(node)) continue\n const reason =\n exclusion(node) ??\n (dropped.value.has(node.path)\n ? { path: node.path, name: node.name, reason: 'deselected' as const }\n : null)\n // A dropped folder excludes its descendants as well.\n const droppedByParent = ancestorSelected(node, dropped.value)\n if (reason || droppedByParent) {\n if (node.kind === 'file' || reason?.reason === 'unreadable')\n excluded.push(reason ?? { path: node.path, name: node.name, reason: 'deselected' })\n } else if (node.kind === 'file') files.push(node)\n }\n return { files, excluded, bytes: files.reduce((total, node) => total + (node.bytes ?? 0), 0) }\n })\n async function resolve() {\n const token = ++generation\n resolving.value = true\n resolutionError.value = ''\n let count = 0\n const seen = new Set<string>()\n async function walk(node: PickerNode, ancestors: string[] = []): Promise<PickerFile[]> {\n if (token !== generation) throw new DOMException('Cancelled', 'AbortError')\n if (!nav.isAllowed(node)) return []\n if (node.kind === 'file') return [node]\n if (node.unavailableReason) return []\n if (ancestors.includes(node.path))\n throw new Error('The adapter returned a circular folder tree.')\n if (seen.has(node.path)) return []\n seen.add(node.path)\n if (++count > 500 || ancestors.length > 30)\n throw new Error('This selection is too large to validate. Choose a smaller folder.')\n const children = await nav.loadChildren(node.path, ancestors)\n const files: PickerFile[] = []\n for (const child of children) files.push(...(await walk(child, [...ancestors, node.path])))\n if (token !== generation) throw new DOMException('Cancelled', 'AbortError')\n const formats: Record<string, number> = {}\n files.forEach((file) => {\n formats[file.format] = (formats[file.format] ?? 0) + 1\n })\n nav.register([\n {\n ...node,\n fileCount: files.length,\n bytes: files.reduce((sum, file) => sum + (file.bytes ?? 0), 0),\n formats,\n },\n ])\n return files\n }\n try {\n for (const node of roots.value) await walk(node)\n if (token === generation) {\n // Resolved descendants inherit their folder, including restored selections.\n for (const key of [...keys.value]) {\n const node = nav.nodes.value.get(key)\n if (node && ancestorSelected(node)) keys.value.delete(key)\n }\n }\n } catch (cause) {\n if (token === generation)\n resolutionError.value =\n cause instanceof Error ? cause.message : 'Could not validate the selection.'\n } finally {\n if (token === generation) resolving.value = false\n }\n }\n const focused = computed(() => {\n const node = nav.nodes.value.get(focusPath.value)\n return node && nav.isAllowed(node) ? node : roots.value[roots.value.length - 1] ?? null\n })\n watch(\n () => new Map([...nav.nodes.value].map(([path, node]) => [path, nav.isAllowed(node)])),\n (allowed, previous) => {\n if (keys.value.size && [...allowed].some(([path, value]) =>\n previous.has(path) && value !== previous.get(path),\n )) void resolve()\n },\n )\n const selectedFileCounts = computed(() => {\n const counts = new Map<string, number>()\n for (const file of chosen.value.files) {\n const seen = new Set<string>()\n let parent = file.parentPath\n while (parent && !seen.has(parent)) {\n seen.add(parent)\n counts.set(parent, (counts.get(parent) ?? 0) + 1)\n parent = nav.nodes.value.get(parent)?.parentPath\n }\n }\n return counts\n })\n function folderProgress(node: PickerNode): string {\n if (node.kind !== 'folder' || node.fileCount == null) return ''\n const allowed = selected(node) ? (selectedFileCounts.value.get(node.path) ?? 0) : 0\n return `${allowed} / ${node.fileCount}`\n }\n return {\n keys,\n dropped,\n focusPath,\n resolving,\n resolutionError,\n roots,\n chosen,\n focused,\n select,\n selected,\n selectable,\n exclusion,\n reset,\n resolve,\n folderProgress,\n ancestorSelected,\n }\n}\n","import type { PickerAdapter, PickerNode } from '../../types/filePicker'\nimport { pickerFormat } from './validation'\n\n/** Construct metadata only: no FileReader, arrayBuffer or upload. */\nexport function localFilesAdapter(\n files: File[],\n relativePaths?: (string | undefined)[],\n): PickerAdapter {\n const nodes = new Map<string, PickerNode>()\n for (const [index, file] of files.entries()) {\n const relative = relativePaths?.[index] || file.webkitRelativePath || file.name\n const parts = relative.split('/')\n let parent: string | null = null\n for (let i = 0; i < parts.length - 1; i++) {\n const path = parts.slice(0, i + 1).join('/')\n if (!nodes.has(path))\n nodes.set(path, {\n kind: 'folder', name: parts[i]!, path, parentPath: parent,\n fileCount: 0, bytes: 0, formats: {},\n })\n parent = path\n }\n // Plain file picks can have equal names; preserve both File objects.\n let path = relative\n for (let n = 2; nodes.has(path); n++) path = `${relative} (${n})`\n nodes.set(path, {\n kind: 'file',\n name: file.name,\n path,\n parentPath: parent,\n bytes: file.size,\n modifiedAt: new Date(file.lastModified).toISOString(),\n format: pickerFormat(file.name),\n file,\n })\n }\n const children = new Map<string, PickerNode[]>()\n for (const node of nodes.values()) {\n const parent = node.parentPath ?? ''\n const siblings = children.get(parent)\n if (siblings) siblings.push(node)\n else children.set(parent, [node])\n if (node.kind !== 'file') continue\n // Each file contributes once to its ancestors, rather than scanning all\n // files again for every folder in the selection.\n for (let slash = node.path.indexOf('/'); slash >= 0; slash = node.path.indexOf('/', slash + 1)) {\n const folder = nodes.get(node.path.slice(0, slash))\n if (folder?.kind !== 'folder') continue\n folder.fileCount = (folder.fileCount ?? 0) + 1\n folder.bytes = (folder.bytes ?? 0) + (node.bytes ?? 0)\n folder.formats![node.format] = (folder.formats![node.format] ?? 0) + 1\n }\n }\n const list = (path: string) => [...(children.get(path) ?? [])]\n return {\n listRoot: async () => list(''),\n listChildren: async (path) => list(path),\n search: async (query, scope) =>\n [...nodes.values()].filter((node) => {\n const inScope = scope.recursive\n ? !scope.path || node.path.startsWith(`${scope.path}/`)\n : (node.parentPath ?? '') === scope.path\n return inScope && node.name.toLowerCase().includes(query.toLowerCase())\n }),\n }\n}\n","import { computed, nextTick, onScopeDispose, shallowRef, toRaw, watch, type Ref } from 'vue'\nimport type {\n PickerAdapter,\n PickerCapabilities,\n PickerNode,\n PickerSelection,\n PickerSelectionMode,\n PickerSort,\n PickerSource,\n} from '../types/filePicker'\nimport type { OptionPrimitive, SelectOption } from '../types'\nimport { usePickerNavigation } from './filePicker/usePickerNavigation'\nimport { usePickerSelection } from './filePicker/usePickerSelection'\nimport { localFilesAdapter } from './filePicker/localFiles'\n\ninterface PickerStateProps {\n open: boolean\n adapter: PickerAdapter\n selectionMode: PickerSelectionMode\n sources: PickerSource[]\n capabilities: PickerCapabilities\n initialPath: string\n initialSelection: PickerSelection | null\n defaultSort: PickerSort\n systemFilter?: (node: PickerNode, source: PickerSource) => boolean\n}\ntype PickerEmit = {\n (event: 'update:open', value: boolean): void\n (event: 'select', value: PickerSelection): void\n (event: 'cancel'): void\n}\n\n/** Internal orchestration; public consumers use FilePicker's props and events. */\nexport function useFilePicker(\n props: PickerStateProps,\n emit: PickerEmit,\n searchArea: Ref<HTMLElement | null>,\n) {\n const sourceNames: Record<PickerSource, string> = {\n server: 'Server',\n localFile: 'Local files',\n }\n const allowedSources = computed(() => [...new Set(props.sources)])\n // Segment values are namespaced so an adapter's mount path can never collide with a source name.\n const SOURCE = 'source:'\n const MOUNT = 'mount:'\n const mounts = shallowRef<PickerNode[]>([])\n const source = shallowRef<PickerSource>(\n allowedSources.value.includes(props.initialSelection?.source as PickerSource)\n ? props.initialSelection!.source\n : (allowedSources.value[0] ?? 'server'),\n )\n const query = shallowRef('')\n const recursive = shallowRef(false)\n const sort = shallowRef<PickerSort>(props.defaultSort)\n const skipBlanks = shallowRef(false)\n const reviewing = shallowRef(false)\n const localFiles = shallowRef<File[]>([])\n const localPaths = new WeakMap<File, string>()\n const localAdapter = computed(() =>\n localFilesAdapter(localFiles.value, localFiles.value.map((file) => localPaths.get(toRaw(file)))),\n )\n const nav = usePickerNavigation(\n () => (source.value === 'localFile' ? localAdapter.value : props.adapter),\n () => ({ source: source.value }),\n (node) => props.systemFilter?.(node, source.value) !== false,\n () => sort.value,\n )\n const capability = computed(() => props.capabilities[source.value])\n const selection = usePickerSelection(\n nav,\n () => props.selectionMode,\n () => capability.value,\n () => skipBlanks.value,\n )\n const { path, trail, loading, error, rows } = nav\n /** A mount-rooted adapter lists its mounts as header sources; the listing starts inside one. */\n const mountView = computed(\n () => source.value === 'server' && !!props.adapter.mounts && mounts.value.length > 0,\n )\n const segments = computed<SelectOption<OptionPrimitive>[]>(() =>\n allowedSources.value.flatMap((item) =>\n item === 'server' && props.adapter.mounts && mounts.value.length\n ? mounts.value.map((mount) => ({\n value: MOUNT + mount.path,\n label: mount.name,\n disabled: !!mount.unavailableReason,\n }))\n : [{ value: SOURCE + item, label: sourceNames[item] }],\n ),\n )\n const activeSegment = computed(() =>\n mountView.value ? MOUNT + (trail.value[0]?.path ?? '') : SOURCE + source.value,\n )\n const { roots, chosen, focused, resolving, resolutionError } = selection\n const previewNode = computed(() => {\n const node = focused.value\n if (!node || node.kind !== 'folder' || !selection.selected(node) || resolving.value) return node\n const files = chosen.value.files.filter((file) =>\n selection.ancestorSelected(file, new Set([node.path])),\n )\n const formats: Record<string, number> = {}\n files.forEach((file) => {\n formats[file.format] = (formats[file.format] ?? 0) + 1\n })\n return {\n ...node,\n bytes: files.reduce((sum, file) => sum + (file.bytes ?? 0), 0),\n fileCount: files.length,\n formats,\n }\n })\n\n const confirmLabel = computed(\n () =>\n ({\n 'single-file': 'Use file',\n 'multi-file': 'Use files',\n folder: 'Use folder',\n 'folder+files': 'Use selection',\n })[props.selectionMode],\n )\n const folderCount = computed(() => roots.value.filter((node) => node.kind === 'folder').length)\n const canConfirm = computed(\n () =>\n allowedSources.value.includes(source.value) &&\n roots.value.length > 0 &&\n chosen.value.files.length > 0 &&\n !resolving.value &&\n !loading.value &&\n !error.value &&\n !resolutionError.value,\n )\n let lastConfirmed: PickerSelection | null = null\n let initialized = false\n let timer: ReturnType<typeof setTimeout> | undefined\n let openGeneration = 0\n const remembered = new Map<\n PickerSource,\n { path: string; trail: { path: string; label: string }[] }\n >()\n\n const knownFolders = new Map<PickerSource, PickerNode[]>()\n function rememberFolders() {\n knownFolders.set(\n source.value,\n [...nav.nodes.value.values()].filter((node) => node.kind === 'folder'),\n )\n }\n function currentSeed() {\n return props.initialSelection ?? lastConfirmed\n }\n async function restore() {\n const token = ++openGeneration\n const seed = currentSeed()\n nav.register(knownFolders.get(source.value) ?? [])\n selection.reset(seed?.source === source.value ? seed : null)\n reviewing.value = false\n if (!allowedSources.value.includes(source.value)) return\n if (seed?.source === 'localFile' && source.value === 'localFile') {\n seed.files.forEach((file, index) => {\n if (seed.relativePaths?.[index]) localPaths.set(toRaw(file), seed.relativePaths[index]!)\n })\n localFiles.value = seed.files\n }\n if (source.value === 'server' && props.adapter.mounts) {\n await loadMounts()\n if (token !== openGeneration || !props.open) return\n const first = mounts.value.find((mount) => !mount.unavailableReason)\n if (!path.value && first) openMount(first)\n // An initial folder inside a mount gets that mount as its first crumb.\n const holder = mounts.value.find((mount) => mount.path === props.adapter.mountOf?.(path.value))\n if (holder && trail.value[0]?.path !== holder.path)\n trail.value = [{ path: holder.path, label: holder.name }, ...trail.value]\n }\n await nav.load(query.value, recursive.value)\n if (token !== openGeneration || !props.open) return\n if (seed?.source === 'localFile' && source.value === 'localFile') {\n // Restored local File objects are already the confirmed subset.\n await selectLocalFiles()\n }\n if (selection.keys.value.size) await selection.resolve()\n }\n watch(\n () => props.open,\n (open) => {\n clearTimeout(timer)\n if (open) {\n sort.value = props.defaultSort\n if (!initialized) {\n path.value = props.initialPath\n if (path.value) trail.value = [{ path: path.value, label: 'Current folder' }]\n initialized = true\n }\n const seed = currentSeed()\n if (seed && allowedSources.value.includes(seed.source) && seed.source !== source.value)\n void switchSource(seed.source)\n else void restore()\n } else {\n ++openGeneration\n rememberFolders()\n selection.reset()\n nav.clear()\n }\n },\n { immediate: true },\n )\n watch([query, recursive], () => {\n clearTimeout(timer)\n if (props.open)\n timer = setTimeout(() => {\n void nav.load(query.value, recursive.value)\n }, 200)\n })\n watch(\n () => [...selection.keys.value],\n () => {\n if (props.open) void selection.resolve()\n },\n )\n watch(\n () => props.selectionMode,\n () => {\n if (props.open) void restore()\n },\n )\n watch(allowedSources, (sources) => {\n if (!sources.includes(source.value) && sources[0]) void switchSource(sources[0])\n })\n onScopeDispose(() => {\n ++openGeneration\n clearTimeout(timer)\n selection.reset()\n })\n\n async function loadMounts() {\n try {\n mounts.value = (await props.adapter.listRoot({ source: 'server' })).filter(\n (node) => node.kind === 'folder' && props.systemFilter?.(node, 'server') !== false,\n )\n } catch {\n // The root listing reports the failure; the header falls back to one Server source.\n mounts.value = []\n }\n nav.register(mounts.value, '')\n }\n function openMount(mount: PickerNode) {\n trail.value = [{ path: mount.path, label: mount.name }]\n path.value = mount.path\n }\n async function chooseSegment(value: string | number) {\n const key = String(value)\n if (key.startsWith(SOURCE)) return switchSource(key.slice(SOURCE.length) as PickerSource)\n const mount = mounts.value.find((item) => MOUNT + item.path === key)\n if (!mount || mount.unavailableReason) return\n if (source.value !== 'server') {\n remembered.set('server', { path: mount.path, trail: [{ path: mount.path, label: mount.name }] })\n return switchSource('server')\n }\n clearTimeout(timer)\n query.value = ''\n openMount(mount)\n await nav.load()\n }\n function clearSelection() {\n selection.reset()\n reviewing.value = false\n }\n async function switchSource(next: PickerSource) {\n if (next === source.value || !allowedSources.value.includes(next)) return\n rememberFolders()\n remembered.set(source.value, { path: path.value, trail: [...trail.value] })\n clearTimeout(timer)\n nav.clear()\n source.value = next\n query.value = ''\n path.value = remembered.get(next)?.path ?? ''\n trail.value = remembered.get(next)?.trail ?? []\n await restore()\n }\n function selectNode(node: PickerNode, event: MouseEvent | KeyboardEvent) {\n // The second click of a double-click is ignored, except on a folder+files folder:\n // there the double-click opens it, and the second click undoes the first one's toggle.\n const opensFolder = node.kind === 'folder' && props.selectionMode === 'folder+files'\n if (event.type === 'click' && event.detail > 1 && !opensFolder) return\n selection.select(node, {\n ...{ metaKey: event.metaKey, ctrlKey: event.ctrlKey, shiftKey: event.shiftKey },\n toggle: event.type === 'click' || (event instanceof KeyboardEvent && event.key === ' '),\n })\n }\n async function enter(node: PickerNode) {\n const token = openGeneration\n if (selection.exclusion(node)) return\n if (node.kind === 'file') {\n if (props.selectionMode === 'single-file') {\n selection.select(node)\n await nextTick()\n await selection.resolve()\n if (token === openGeneration && props.open) confirm()\n }\n return\n }\n clearTimeout(timer)\n query.value = ''\n nav.enter(node)\n await nav.load()\n }\n async function up(target?: string) {\n // A mount root is the top of its source; the mount list lives in the header.\n // An initial folder inside a mount the adapter cannot name has no mount crumb, so up leads to the mount list.\n const atMountRoot = mounts.value.some((mount) => mount.path === trail.value[0]?.path)\n if (\n mountView.value &&\n (target === '' || (target === undefined && trail.value.length <= 1 && atMountRoot))\n )\n return\n query.value = ''\n clearTimeout(timer)\n nav.up(target)\n await nav.load()\n }\n async function refresh() {\n nav.invalidate()\n await nav.load(query.value, recursive.value, true)\n if (selection.keys.value.size) await selection.resolve()\n }\n function selectParent(node: PickerNode) {\n if (!node.parentPath) return\n const parent = nav.nodes.value.get(node.parentPath) ?? {\n kind: 'folder' as const,\n path: node.parentPath,\n name: node.parentName ?? 'Containing folder',\n }\n nav.register([parent])\n selection.select(parent)\n }\n async function chooseLocal(files: File[], _rootName?: string, relativePaths?: string[]) {\n const token = openGeneration\n if (!files.length || !props.open || source.value !== 'localFile') return\n files.forEach((file, index) => {\n if (relativePaths?.[index]) localPaths.set(toRaw(file), relativePaths[index]!)\n })\n localFiles.value = files\n clearTimeout(timer)\n nav.clear()\n selection.reset()\n path.value = ''\n trail.value = []\n query.value = ''\n await nav.load()\n if (token !== openGeneration || !props.open || source.value !== 'localFile') return\n await selectLocalFiles()\n }\n async function selectLocalFiles() {\n const token = openGeneration\n const found = props.selectionMode === 'single-file' || props.selectionMode === 'multi-file'\n ? await localAdapter.value.search('', { path: '', recursive: true })\n : nav.entries.value\n const items = Array.isArray(found) ? found : found.nodes\n if (token !== openGeneration || !props.open || source.value !== 'localFile') return\n nav.register(items)\n const candidates = items.filter((node) => selection.selectable(node))\n for (const node of props.selectionMode === 'single-file' || props.selectionMode === 'folder'\n ? candidates.slice(0, 1)\n : candidates)\n selection.select(node, { ctrlKey: true })\n }\n function cancel() {\n emit('cancel')\n emit('update:open', false)\n }\n function confirm() {\n if (!canConfirm.value) return\n const result: PickerSelection =\n source.value === 'localFile'\n ? {\n source: 'localFile',\n files: chosen.value.files.flatMap((node) => (node.file ? [toRaw(node.file)] : [])),\n }\n : {\n source: 'server',\n folders: roots.value.filter((node) => node.kind === 'folder').map((node) => node.path),\n files: [...chosen.value.files],\n excluded: [...chosen.value.excluded],\n }\n if (result.source === 'localFile') {\n const relativePaths = result.files.map((file) =>\n localPaths.get(file) || file.webkitRelativePath || file.name,\n )\n if (relativePaths.some((path, index) =>\n path !== (result.files[index]!.webkitRelativePath || result.files[index]!.name),\n )) result.relativePaths = relativePaths\n }\n lastConfirmed = result\n emit('select', result)\n emit('update:open', false)\n }\n function keyboard(event: KeyboardEvent) {\n if (\n event.key !== '/' ||\n event.target instanceof HTMLInputElement ||\n event.target instanceof HTMLTextAreaElement\n )\n return\n event.preventDefault()\n searchArea.value?.querySelector('input')?.focus()\n }\n\n return {\n previewNode,\n sourceNames,\n segments,\n activeSegment,\n mounts,\n mountView,\n allowedSources,\n source,\n query,\n recursive,\n sort,\n skipBlanks,\n reviewing,\n nav,\n selection,\n capability,\n path,\n trail,\n loading,\n error,\n rows,\n roots,\n chosen,\n focused,\n resolving,\n resolutionError,\n confirmLabel,\n folderCount,\n canConfirm,\n chooseSegment,\n clearSelection,\n selectNode,\n enter,\n up,\n refresh,\n selectParent,\n chooseLocal,\n cancel,\n confirm,\n keyboard,\n }\n}\n","<script setup lang=\"ts\">\n/**\n * Adapter-driven file picker: mounts and local files as header sources, a flat\n * (or tree) listing with multi-select, a metadata preview and a selection receipt.\n *\n * @example Recommended\n * ```vue\n * <FilePicker v-model:open=\"open\" :adapter=\"usePlatformFilePickerAdapter()\" @select=\"use\" />\n * ```\n */\nimport { computed, ref, useTemplateRef } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseInput from './BaseInput.vue'\nimport ActionMenu from './ActionMenu.vue'\nimport SegmentedControl from './SegmentedControl.vue'\nimport FileUploader from './FileUploader.vue'\nimport FilePickerListInternal from './internal/FilePickerListInternal.vue'\nimport FilePickerPreviewInternal from './internal/FilePickerPreviewInternal.vue'\nimport { formatBytes } from './internal/filePickerPresentation'\nimport { useFilePicker } from '../composables/useFilePicker'\nimport type {\n PickerAdapter,\n PickerCapabilities,\n PickerNode,\n PickerSelection,\n PickerSelectionMode,\n PickerSort,\n PickerSource,\n PickerView,\n} from '../types/filePicker'\n\ninterface Props {\n open: boolean\n adapter: PickerAdapter\n selectionMode?: PickerSelectionMode\n sources?: PickerSource[]\n capabilities?: PickerCapabilities\n initialPath?: string\n initialSelection?: PickerSelection | null\n title?: string\n /** `list` shows one folder at a time; `tree` adds disclosure triangles. */\n view?: PickerView\n /** Row order on each open; the user can change it from the path bar until closing. */\n defaultSort?: PickerSort\n /** Host-owned visibility/selection filter; backend must still enforce access. */\n systemFilter?: (node: PickerNode, source: PickerSource) => boolean\n}\nconst props = withDefaults(defineProps<Props>(), {\n selectionMode: 'folder+files',\n sources: () => ['server'],\n capabilities: () => ({}),\n initialPath: '',\n initialSelection: null,\n title: 'Choose data',\n view: 'list',\n defaultSort: 'newest',\n})\nconst emit = defineEmits<{\n 'update:open': [open: boolean]\n select: [selection: PickerSelection]\n cancel: []\n}>()\nconst uploadError = ref('')\nconst {\n previewNode,\n sourceNames,\n mounts,\n segments,\n activeSegment,\n mountView,\n allowedSources,\n source,\n query,\n recursive,\n sort,\n skipBlanks,\n reviewing,\n nav,\n selection,\n capability,\n path,\n trail,\n loading,\n error,\n rows,\n roots,\n chosen,\n focused,\n resolving,\n resolutionError,\n confirmLabel,\n folderCount,\n canConfirm,\n chooseSegment,\n clearSelection,\n selectNode,\n enter,\n up,\n refresh,\n selectParent,\n chooseLocal,\n cancel,\n confirm,\n keyboard,\n} = useFilePicker(props, emit, useTemplateRef<HTMLElement>('searchArea'))\n\n// The preview can outlive a mount switch, so name the mount that holds the node.\nconst sourceLabel = computed(() => {\n const node = previewNode.value\n if (!mountView.value || !node) return sourceNames[source.value]\n for (let at: string | null | undefined = node.path; at; at = nav.nodes.value.get(at)?.parentPath) {\n const mount = mounts.value.find((item) => item.path === at)\n if (mount) return mount.name\n }\n return mounts.value.find((item) => item.path === props.adapter.mountOf?.(node.path))?.name ?? sourceNames.server\n})\nconst SORT_LABELS: Record<PickerSort, string> = {\n newest: 'Newest first',\n oldest: 'Oldest first',\n name: 'Name A–Z',\n 'name-desc': 'Name Z–A',\n}\nconst sortItems = computed(() =>\n (Object.keys(SORT_LABELS) as PickerSort[]).map((id) => ({\n id,\n label: SORT_LABELS[id],\n checked: sort.value === id,\n })),\n)\nconst accepts = computed(() =>\n (capability.value?.formats ?? []).map((format) => `.${format.replace(/^\\./, '')}`).join(' '),\n)\nconst summary = computed(() => {\n if (!roots.value.length) return 'No selection'\n const files = chosen.value.files.length\n return [\n ...(folderCount.value\n ? [`${folderCount.value} folder${folderCount.value === 1 ? '' : 's'}`]\n : []),\n `${files} file${files === 1 ? '' : 's'} selected`,\n resolving.value ? 'Checking…' : formatBytes(chosen.value.bytes),\n ].join(' · ')\n})\n</script>\n\n<template>\n <BaseModal :open=\"open\" size=\"xl\" layout=\"plain\" @close=\"cancel\">\n <template #header>\n <div class=\"mint-picker__head\" @keydown=\"keyboard\">\n <h2 class=\"mint-picker__title\">{{ title }}</h2>\n <SegmentedControl\n v-if=\"segments.length > 1\"\n class=\"mint-picker__sources\"\n aria-label=\"Data source\"\n size=\"sm\"\n :options=\"segments\"\n :model-value=\"activeSegment\"\n @update:model-value=\"chooseSegment\"\n />\n <div v-if=\"source === 'server'\" ref=\"searchArea\" class=\"mint-picker__search\">\n <BaseInput\n v-model=\"query\"\n size=\"sm\"\n aria-label=\"Search folders and files\"\n placeholder=\"Search this source\"\n >\n <template #prefix>\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.8\"\n aria-hidden=\"true\"\n >\n <circle cx=\"10.5\" cy=\"10.5\" r=\"7\" />\n <path d=\"m16 16 5 5\" />\n </svg>\n </template>\n <template #suffix><kbd>/</kbd></template>\n </BaseInput>\n </div>\n </div>\n </template>\n <div :key=\"source\" class=\"mint-picker mint-picker__source-panel\" @keydown=\"keyboard\">\n <div v-if=\"source === 'server'\" class=\"mint-picker__pathbar\">\n <nav aria-label=\"Breadcrumb\" class=\"mint-picker__breadcrumbs\">\n <button\n v-if=\"!mountView\"\n type=\"button\"\n class=\"mint-picker__crumb\"\n :aria-current=\"trail.length ? undefined : 'location'\"\n @click=\"up('')\"\n >\n {{ sourceNames[source] }}\n </button>\n <template v-for=\"(crumb, index) in trail\" :key=\"crumb.path\">\n <svg\n v-if=\"index > 0 || !mountView\"\n class=\"mint-picker__crumb-separator\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path d=\"M6 4l4 4-4 4\" />\n </svg>\n <button\n type=\"button\"\n class=\"mint-picker__crumb\"\n :aria-current=\"index === trail.length - 1 ? 'location' : undefined\"\n @click=\"up(crumb.path)\"\n >\n {{ crumb.label }}\n </button>\n </template>\n </nav>\n <span v-if=\"accepts\" class=\"mint-picker__accepts\">Accepts {{ accepts }}</span>\n <ActionMenu\n label=\"Sort\"\n :items=\"sortItems\"\n @select=\"sort = $event.id as PickerSort\"\n >\n <template #trigger=\"{ open: menuOpen, toggle, keydown }\">\n <button\n type=\"button\"\n class=\"mint-picker__sort\"\n aria-haspopup=\"menu\"\n :aria-expanded=\"menuOpen\"\n :aria-label=\"`Sort: ${SORT_LABELS[sort]}`\"\n @click=\"toggle\"\n @keydown=\"keydown\"\n >\n {{ SORT_LABELS[sort] }}\n <svg viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\">\n <path d=\"M4 6l4 4 4-4\" />\n </svg>\n </button>\n </template>\n </ActionMenu>\n <button\n type=\"button\"\n class=\"mint-picker__scope\"\n :aria-pressed=\"recursive\"\n @click=\"recursive = !recursive\"\n >\n {{ recursive ? 'Subfolders' : 'This folder' }}\n </button>\n <button\n type=\"button\"\n class=\"mint-picker__scope mint-picker__refresh\"\n aria-label=\"Refresh location\"\n title=\"Refresh location\"\n :disabled=\"loading\"\n @click=\"refresh()\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" aria-hidden=\"true\">\n <path d=\"M20 7v5h-5M4 17v-5h5M6 8a7 7 0 0 1 12-2l2 3M4 15l2 3a7 7 0 0 0 12-2\" />\n </svg>\n </button>\n </div>\n <div v-if=\"!allowedSources.length\" class=\"mint-picker__empty\">\n No data sources are enabled.\n </div>\n <template v-else>\n <div v-if=\"source === 'localFile'\" class=\"mint-picker__local-upload\">\n <FileUploader\n size=\"lg\"\n :mode=\"\n selectionMode === 'folder'\n ? 'folder'\n : selectionMode === 'single-file'\n ? 'file'\n : 'file+folder'\n \"\n :multiple=\"selectionMode !== 'single-file'\"\n :show-files=\"false\"\n @upload=\"\n (...args) => {\n uploadError = ''\n chooseLocal(...args)\n }\n \"\n @error=\"uploadError = $event\"\n />\n <p v-if=\"uploadError || error || resolutionError\" role=\"alert\">\n {{ uploadError || error || resolutionError }}\n </p>\n </div>\n <div v-else class=\"mint-picker__workspace\">\n <FilePickerListInternal\n :empty-title=\"path ? 'No readable files' : 'No server locations configured'\"\n :view=\"view\"\n :rows=\"rows\"\n :loading=\"loading\"\n :error=\"error\"\n :query=\"query\"\n :truncated=\"nav.truncated.value\"\n :mode=\"selectionMode\"\n :selected=\"selection.selected\"\n :selectable=\"selection.selectable\"\n :exclusion=\"selection.exclusion\"\n :progress=\"selection.folderProgress\"\n :focus-path=\"selection.focusPath.value\"\n @select=\"selectNode\"\n @enter=\"enter\"\n @expand=\"nav.expand\"\n @up=\"up\"\n @retry=\"refresh\"\n @parent=\"selectParent\"\n />\n <FilePickerPreviewInternal\n :node=\"previewNode\"\n :total-count=\"focused?.kind === 'folder' ? focused.fileCount : undefined\"\n :source-label=\"sourceLabel\"\n :exclusions=\"chosen.excluded\"\n :busy=\"resolving\"\n :error=\"resolutionError\"\n :reviewing=\"reviewing\"\n @review=\"reviewing = !reviewing\"\n @retry=\"selection.resolve\"\n />\n </div>\n </template>\n </div>\n <template #footer>\n <div class=\"mint-picker__footer\">\n <div class=\"mint-picker__receipt\" role=\"status\">\n <span class=\"mint-picker__summary\">{{ summary }}</span>\n <button\n v-if=\"roots.length\"\n type=\"button\"\n class=\"mint-picker__link mint-picker__clear\"\n @click=\"clearSelection\"\n >\n Clear\n </button>\n <button\n v-if=\"chosen.excluded.length && source === 'server'\"\n type=\"button\"\n class=\"mint-picker__link\"\n @click=\"reviewing = !reviewing\"\n >\n {{ chosen.excluded.length }} skipped\n </button>\n <span v-else-if=\"chosen.excluded.length\">{{ chosen.excluded.length }} skipped</span>\n <BaseButton\n v-if=\"capability?.isBlank\"\n size=\"sm\"\n variant=\"ghost\"\n :aria-pressed=\"skipBlanks\"\n @click=\"skipBlanks = !skipBlanks\"\n >Skip blanks {{ skipBlanks ? 'on' : 'off' }}</BaseButton\n >\n </div>\n <div class=\"mint-picker__actions\">\n <BaseButton variant=\"ghost\" size=\"sm\" @click=\"cancel\">Cancel</BaseButton\n ><BaseButton size=\"sm\" :disabled=\"!canConfirm\" @click=\"confirm\">{{\n confirmLabel\n }}</BaseButton>\n </div>\n </div>\n </template>\n </BaseModal>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Adapter-driven file picker: mounts and local files as header sources, a flat\n * (or tree) listing with multi-select, a metadata preview and a selection receipt.\n *\n * @example Recommended\n * ```vue\n * <FilePicker v-model:open=\"open\" :adapter=\"usePlatformFilePickerAdapter()\" @select=\"use\" />\n * ```\n */\nimport { computed, ref, useTemplateRef } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseInput from './BaseInput.vue'\nimport ActionMenu from './ActionMenu.vue'\nimport SegmentedControl from './SegmentedControl.vue'\nimport FileUploader from './FileUploader.vue'\nimport FilePickerListInternal from './internal/FilePickerListInternal.vue'\nimport FilePickerPreviewInternal from './internal/FilePickerPreviewInternal.vue'\nimport { formatBytes } from './internal/filePickerPresentation'\nimport { useFilePicker } from '../composables/useFilePicker'\nimport type {\n PickerAdapter,\n PickerCapabilities,\n PickerNode,\n PickerSelection,\n PickerSelectionMode,\n PickerSort,\n PickerSource,\n PickerView,\n} from '../types/filePicker'\n\ninterface Props {\n open: boolean\n adapter: PickerAdapter\n selectionMode?: PickerSelectionMode\n sources?: PickerSource[]\n capabilities?: PickerCapabilities\n initialPath?: string\n initialSelection?: PickerSelection | null\n title?: string\n /** `list` shows one folder at a time; `tree` adds disclosure triangles. */\n view?: PickerView\n /** Row order on each open; the user can change it from the path bar until closing. */\n defaultSort?: PickerSort\n /** Host-owned visibility/selection filter; backend must still enforce access. */\n systemFilter?: (node: PickerNode, source: PickerSource) => boolean\n}\nconst props = withDefaults(defineProps<Props>(), {\n selectionMode: 'folder+files',\n sources: () => ['server'],\n capabilities: () => ({}),\n initialPath: '',\n initialSelection: null,\n title: 'Choose data',\n view: 'list',\n defaultSort: 'newest',\n})\nconst emit = defineEmits<{\n 'update:open': [open: boolean]\n select: [selection: PickerSelection]\n cancel: []\n}>()\nconst uploadError = ref('')\nconst {\n previewNode,\n sourceNames,\n mounts,\n segments,\n activeSegment,\n mountView,\n allowedSources,\n source,\n query,\n recursive,\n sort,\n skipBlanks,\n reviewing,\n nav,\n selection,\n capability,\n path,\n trail,\n loading,\n error,\n rows,\n roots,\n chosen,\n focused,\n resolving,\n resolutionError,\n confirmLabel,\n folderCount,\n canConfirm,\n chooseSegment,\n clearSelection,\n selectNode,\n enter,\n up,\n refresh,\n selectParent,\n chooseLocal,\n cancel,\n confirm,\n keyboard,\n} = useFilePicker(props, emit, useTemplateRef<HTMLElement>('searchArea'))\n\n// The preview can outlive a mount switch, so name the mount that holds the node.\nconst sourceLabel = computed(() => {\n const node = previewNode.value\n if (!mountView.value || !node) return sourceNames[source.value]\n for (let at: string | null | undefined = node.path; at; at = nav.nodes.value.get(at)?.parentPath) {\n const mount = mounts.value.find((item) => item.path === at)\n if (mount) return mount.name\n }\n return mounts.value.find((item) => item.path === props.adapter.mountOf?.(node.path))?.name ?? sourceNames.server\n})\nconst SORT_LABELS: Record<PickerSort, string> = {\n newest: 'Newest first',\n oldest: 'Oldest first',\n name: 'Name A–Z',\n 'name-desc': 'Name Z–A',\n}\nconst sortItems = computed(() =>\n (Object.keys(SORT_LABELS) as PickerSort[]).map((id) => ({\n id,\n label: SORT_LABELS[id],\n checked: sort.value === id,\n })),\n)\nconst accepts = computed(() =>\n (capability.value?.formats ?? []).map((format) => `.${format.replace(/^\\./, '')}`).join(' '),\n)\nconst summary = computed(() => {\n if (!roots.value.length) return 'No selection'\n const files = chosen.value.files.length\n return [\n ...(folderCount.value\n ? [`${folderCount.value} folder${folderCount.value === 1 ? '' : 's'}`]\n : []),\n `${files} file${files === 1 ? '' : 's'} selected`,\n resolving.value ? 'Checking…' : formatBytes(chosen.value.bytes),\n ].join(' · ')\n})\n</script>\n\n<template>\n <BaseModal :open=\"open\" size=\"xl\" layout=\"plain\" @close=\"cancel\">\n <template #header>\n <div class=\"mint-picker__head\" @keydown=\"keyboard\">\n <h2 class=\"mint-picker__title\">{{ title }}</h2>\n <SegmentedControl\n v-if=\"segments.length > 1\"\n class=\"mint-picker__sources\"\n aria-label=\"Data source\"\n size=\"sm\"\n :options=\"segments\"\n :model-value=\"activeSegment\"\n @update:model-value=\"chooseSegment\"\n />\n <div v-if=\"source === 'server'\" ref=\"searchArea\" class=\"mint-picker__search\">\n <BaseInput\n v-model=\"query\"\n size=\"sm\"\n aria-label=\"Search folders and files\"\n placeholder=\"Search this source\"\n >\n <template #prefix>\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.8\"\n aria-hidden=\"true\"\n >\n <circle cx=\"10.5\" cy=\"10.5\" r=\"7\" />\n <path d=\"m16 16 5 5\" />\n </svg>\n </template>\n <template #suffix><kbd>/</kbd></template>\n </BaseInput>\n </div>\n </div>\n </template>\n <div :key=\"source\" class=\"mint-picker mint-picker__source-panel\" @keydown=\"keyboard\">\n <div v-if=\"source === 'server'\" class=\"mint-picker__pathbar\">\n <nav aria-label=\"Breadcrumb\" class=\"mint-picker__breadcrumbs\">\n <button\n v-if=\"!mountView\"\n type=\"button\"\n class=\"mint-picker__crumb\"\n :aria-current=\"trail.length ? undefined : 'location'\"\n @click=\"up('')\"\n >\n {{ sourceNames[source] }}\n </button>\n <template v-for=\"(crumb, index) in trail\" :key=\"crumb.path\">\n <svg\n v-if=\"index > 0 || !mountView\"\n class=\"mint-picker__crumb-separator\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path d=\"M6 4l4 4-4 4\" />\n </svg>\n <button\n type=\"button\"\n class=\"mint-picker__crumb\"\n :aria-current=\"index === trail.length - 1 ? 'location' : undefined\"\n @click=\"up(crumb.path)\"\n >\n {{ crumb.label }}\n </button>\n </template>\n </nav>\n <span v-if=\"accepts\" class=\"mint-picker__accepts\">Accepts {{ accepts }}</span>\n <ActionMenu\n label=\"Sort\"\n :items=\"sortItems\"\n @select=\"sort = $event.id as PickerSort\"\n >\n <template #trigger=\"{ open: menuOpen, toggle, keydown }\">\n <button\n type=\"button\"\n class=\"mint-picker__sort\"\n aria-haspopup=\"menu\"\n :aria-expanded=\"menuOpen\"\n :aria-label=\"`Sort: ${SORT_LABELS[sort]}`\"\n @click=\"toggle\"\n @keydown=\"keydown\"\n >\n {{ SORT_LABELS[sort] }}\n <svg viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\">\n <path d=\"M4 6l4 4 4-4\" />\n </svg>\n </button>\n </template>\n </ActionMenu>\n <button\n type=\"button\"\n class=\"mint-picker__scope\"\n :aria-pressed=\"recursive\"\n @click=\"recursive = !recursive\"\n >\n {{ recursive ? 'Subfolders' : 'This folder' }}\n </button>\n <button\n type=\"button\"\n class=\"mint-picker__scope mint-picker__refresh\"\n aria-label=\"Refresh location\"\n title=\"Refresh location\"\n :disabled=\"loading\"\n @click=\"refresh()\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" aria-hidden=\"true\">\n <path d=\"M20 7v5h-5M4 17v-5h5M6 8a7 7 0 0 1 12-2l2 3M4 15l2 3a7 7 0 0 0 12-2\" />\n </svg>\n </button>\n </div>\n <div v-if=\"!allowedSources.length\" class=\"mint-picker__empty\">\n No data sources are enabled.\n </div>\n <template v-else>\n <div v-if=\"source === 'localFile'\" class=\"mint-picker__local-upload\">\n <FileUploader\n size=\"lg\"\n :mode=\"\n selectionMode === 'folder'\n ? 'folder'\n : selectionMode === 'single-file'\n ? 'file'\n : 'file+folder'\n \"\n :multiple=\"selectionMode !== 'single-file'\"\n :show-files=\"false\"\n @upload=\"\n (...args) => {\n uploadError = ''\n chooseLocal(...args)\n }\n \"\n @error=\"uploadError = $event\"\n />\n <p v-if=\"uploadError || error || resolutionError\" role=\"alert\">\n {{ uploadError || error || resolutionError }}\n </p>\n </div>\n <div v-else class=\"mint-picker__workspace\">\n <FilePickerListInternal\n :empty-title=\"path ? 'No readable files' : 'No server locations configured'\"\n :view=\"view\"\n :rows=\"rows\"\n :loading=\"loading\"\n :error=\"error\"\n :query=\"query\"\n :truncated=\"nav.truncated.value\"\n :mode=\"selectionMode\"\n :selected=\"selection.selected\"\n :selectable=\"selection.selectable\"\n :exclusion=\"selection.exclusion\"\n :progress=\"selection.folderProgress\"\n :focus-path=\"selection.focusPath.value\"\n @select=\"selectNode\"\n @enter=\"enter\"\n @expand=\"nav.expand\"\n @up=\"up\"\n @retry=\"refresh\"\n @parent=\"selectParent\"\n />\n <FilePickerPreviewInternal\n :node=\"previewNode\"\n :total-count=\"focused?.kind === 'folder' ? focused.fileCount : undefined\"\n :source-label=\"sourceLabel\"\n :exclusions=\"chosen.excluded\"\n :busy=\"resolving\"\n :error=\"resolutionError\"\n :reviewing=\"reviewing\"\n @review=\"reviewing = !reviewing\"\n @retry=\"selection.resolve\"\n />\n </div>\n </template>\n </div>\n <template #footer>\n <div class=\"mint-picker__footer\">\n <div class=\"mint-picker__receipt\" role=\"status\">\n <span class=\"mint-picker__summary\">{{ summary }}</span>\n <button\n v-if=\"roots.length\"\n type=\"button\"\n class=\"mint-picker__link mint-picker__clear\"\n @click=\"clearSelection\"\n >\n Clear\n </button>\n <button\n v-if=\"chosen.excluded.length && source === 'server'\"\n type=\"button\"\n class=\"mint-picker__link\"\n @click=\"reviewing = !reviewing\"\n >\n {{ chosen.excluded.length }} skipped\n </button>\n <span v-else-if=\"chosen.excluded.length\">{{ chosen.excluded.length }} skipped</span>\n <BaseButton\n v-if=\"capability?.isBlank\"\n size=\"sm\"\n variant=\"ghost\"\n :aria-pressed=\"skipBlanks\"\n @click=\"skipBlanks = !skipBlanks\"\n >Skip blanks {{ skipBlanks ? 'on' : 'off' }}</BaseButton\n >\n </div>\n <div class=\"mint-picker__actions\">\n <BaseButton variant=\"ghost\" size=\"sm\" @click=\"cancel\">Cancel</BaseButton\n ><BaseButton size=\"sm\" :disabled=\"!canConfirm\" @click=\"confirm\">{{\n confirmLabel\n }}</BaseButton>\n </div>\n </div>\n </template>\n </BaseModal>\n</template>\n","<script setup lang=\"ts\">\n/** Filterable, sortable timeline of audit entries for tracking experiment or resource history. */\nimport { computed, ref } from 'vue'\nimport type { AuditEntry } from '../types'\nimport BaseSelect from './BaseSelect.vue'\nimport DataStateInternal from './internal/DataStateInternal.vue'\n\ninterface Props {\n entries: AuditEntry[]\n maxHeight?: string\n showFilters?: boolean\n emptyText?: string\n order?: 'newest' | 'oldest'\n size?: 'sm' | 'md' | 'lg'\n /** Replace the timeline with a loading placeholder. */\n loading?: boolean\n /**\n * Error message. When set, the timeline is replaced by an error state.\n * `null`/`undefined` renders normally.\n */\n error?: string | null\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showFilters: false,\n emptyText: 'No activity yet',\n order: 'newest',\n size: 'md',\n loading: false,\n error: null,\n})\n\nconst emit = defineEmits<{\n 'entry-click': [entry: AuditEntry]\n}>()\n\nconst filterType = ref('')\nconst filterUser = ref('')\n\nconst uniqueTypes = computed(() => {\n const types = new Set(props.entries.map(e => e.type))\n return Array.from(types)\n})\n\nconst uniqueUsers = computed(() => {\n const users = new Set(props.entries.flatMap(e => e.user ? [e.user] : []))\n return Array.from(users)\n})\n\nconst typeOptions = computed(() => [{ value: '', label: 'All types' }, ...uniqueTypes.value])\nconst userOptions = computed(() => [{ value: '', label: 'All users' }, ...uniqueUsers.value])\n\nconst filteredEntries = computed(() => {\n let result = props.entries\n if (filterType.value) {\n result = result.filter(e => e.type === filterType.value)\n }\n if (filterUser.value) {\n result = result.filter(e => e.user === filterUser.value)\n }\n return result\n})\n\nconst sortedEntries = computed(() => {\n const entries = [...filteredEntries.value]\n entries.sort((a, b) => {\n const timeA = new Date(a.timestamp).getTime()\n const timeB = new Date(b.timestamp).getTime()\n return props.order === 'newest' ? timeB - timeA : timeA - timeB\n })\n return entries\n})\n\nfunction getInitials(name: string): string {\n const words = name.trim().split(/\\s+/)\n if (words.length >= 2) {\n return (words[0][0] + words[1][0]).toUpperCase()\n }\n return (words[0]?.[0] ?? '').toUpperCase()\n}\n\nconst rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })\n\nfunction formatTimestamp(timestamp: Date | string): string {\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp)\n const now = Date.now()\n const diffMs = date.getTime() - now\n const diffSec = Math.round(diffMs / 1000)\n const diffMin = Math.round(diffSec / 60)\n const diffHr = Math.round(diffMin / 60)\n const diffDay = Math.round(diffHr / 24)\n const diffWeek = Math.round(diffDay / 7)\n\n if (Math.abs(diffSec) < 60) return rtf.format(diffSec, 'second')\n if (Math.abs(diffMin) < 60) return rtf.format(diffMin, 'minute')\n if (Math.abs(diffHr) < 24) return rtf.format(diffHr, 'hour')\n if (Math.abs(diffDay) < 7) return rtf.format(diffDay, 'day')\n if (Math.abs(diffWeek) < 5) return rtf.format(diffWeek, 'week')\n return date.toLocaleDateString()\n}\n\nfunction handleEntryClick(entry: AuditEntry) {\n emit('entry-click', entry)\n}\n</script>\n\n<template>\n <div :class=\"['mint-audit-trail', `mint-audit-trail--${size}`]\">\n <div v-if=\"showFilters\" class=\"mint-audit-trail__filters\">\n <BaseSelect\n :model-value=\"filterType\"\n :options=\"typeOptions\"\n size=\"sm\"\n class=\"mint-audit-trail__filter-select\"\n aria-label=\"Filter by type\"\n @update:model-value=\"filterType = String($event)\"\n />\n <BaseSelect\n :model-value=\"filterUser\"\n :options=\"userOptions\"\n size=\"sm\"\n class=\"mint-audit-trail__filter-select\"\n aria-label=\"Filter by user\"\n @update:model-value=\"filterUser = String($event)\"\n />\n </div>\n\n <div\n v-if=\"!loading && !error && sortedEntries.length\"\n class=\"mint-audit-trail__list\"\n :style=\"maxHeight ? { maxHeight, overflowY: 'auto' } : {}\"\n >\n <div class=\"mint-audit-trail__line\" />\n <div\n v-for=\"entry in sortedEntries\"\n :key=\"entry.id\"\n :class=\"['mint-audit-trail__entry', `mint-audit-trail__entry--${entry.type}`]\"\n @click=\"handleEntryClick(entry)\"\n >\n <slot name=\"entry\" :entry=\"entry\">\n <div class=\"mint-audit-trail__dot\" />\n <div class=\"mint-audit-trail__content\">\n <!-- One sentence from the existing fields: who · what (4d) -->\n <div class=\"mint-audit-trail__header\">\n <div v-if=\"entry.user\" class=\"mint-audit-trail__avatar\">{{ getInitials(entry.user) }}</div>\n <span class=\"mint-audit-trail__sentence\">\n <span v-if=\"entry.user\" class=\"mint-audit-trail__user\">{{ entry.user }}</span> <span class=\"mint-audit-trail__action\">{{ entry.action }}</span>\n </span>\n <span class=\"mint-audit-trail__timestamp\">{{ formatTimestamp(entry.timestamp) }}</span>\n </div>\n <div v-if=\"entry.detail\" class=\"mint-audit-trail__detail\">{{ entry.detail }}</div>\n <div v-if=\"entry.metadata && Object.keys(entry.metadata).length\" class=\"mint-audit-trail__metadata\">\n <span\n v-for=\"(value, key) in entry.metadata\"\n :key=\"String(key)\"\n class=\"mint-audit-trail__metadata-item\"\n >\n <span class=\"mint-audit-trail__metadata-key\">{{ key }}:</span>\n {{ value }}\n </span>\n </div>\n </div>\n </slot>\n </div>\n </div>\n\n <div v-if=\"error\" class=\"mint-audit-trail__state\">\n <slot name=\"error\" :error=\"error\">\n <DataStateInternal state=\"error\" size=\"sm\" :title=\"error\" />\n </slot>\n </div>\n\n <div v-else-if=\"loading\" class=\"mint-audit-trail__state\">\n <slot name=\"loading\">\n <DataStateInternal state=\"loading\" size=\"sm\" loading-label=\"Loading activity\" />\n </slot>\n </div>\n\n <div v-else-if=\"!sortedEntries.length\" class=\"mint-audit-trail__state\">\n <slot name=\"empty\">\n <DataStateInternal state=\"empty\" size=\"sm\" :title=\"emptyText\" />\n </slot>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Filterable, sortable timeline of audit entries for tracking experiment or resource history. */\nimport { computed, ref } from 'vue'\nimport type { AuditEntry } from '../types'\nimport BaseSelect from './BaseSelect.vue'\nimport DataStateInternal from './internal/DataStateInternal.vue'\n\ninterface Props {\n entries: AuditEntry[]\n maxHeight?: string\n showFilters?: boolean\n emptyText?: string\n order?: 'newest' | 'oldest'\n size?: 'sm' | 'md' | 'lg'\n /** Replace the timeline with a loading placeholder. */\n loading?: boolean\n /**\n * Error message. When set, the timeline is replaced by an error state.\n * `null`/`undefined` renders normally.\n */\n error?: string | null\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showFilters: false,\n emptyText: 'No activity yet',\n order: 'newest',\n size: 'md',\n loading: false,\n error: null,\n})\n\nconst emit = defineEmits<{\n 'entry-click': [entry: AuditEntry]\n}>()\n\nconst filterType = ref('')\nconst filterUser = ref('')\n\nconst uniqueTypes = computed(() => {\n const types = new Set(props.entries.map(e => e.type))\n return Array.from(types)\n})\n\nconst uniqueUsers = computed(() => {\n const users = new Set(props.entries.flatMap(e => e.user ? [e.user] : []))\n return Array.from(users)\n})\n\nconst typeOptions = computed(() => [{ value: '', label: 'All types' }, ...uniqueTypes.value])\nconst userOptions = computed(() => [{ value: '', label: 'All users' }, ...uniqueUsers.value])\n\nconst filteredEntries = computed(() => {\n let result = props.entries\n if (filterType.value) {\n result = result.filter(e => e.type === filterType.value)\n }\n if (filterUser.value) {\n result = result.filter(e => e.user === filterUser.value)\n }\n return result\n})\n\nconst sortedEntries = computed(() => {\n const entries = [...filteredEntries.value]\n entries.sort((a, b) => {\n const timeA = new Date(a.timestamp).getTime()\n const timeB = new Date(b.timestamp).getTime()\n return props.order === 'newest' ? timeB - timeA : timeA - timeB\n })\n return entries\n})\n\nfunction getInitials(name: string): string {\n const words = name.trim().split(/\\s+/)\n if (words.length >= 2) {\n return (words[0][0] + words[1][0]).toUpperCase()\n }\n return (words[0]?.[0] ?? '').toUpperCase()\n}\n\nconst rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })\n\nfunction formatTimestamp(timestamp: Date | string): string {\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp)\n const now = Date.now()\n const diffMs = date.getTime() - now\n const diffSec = Math.round(diffMs / 1000)\n const diffMin = Math.round(diffSec / 60)\n const diffHr = Math.round(diffMin / 60)\n const diffDay = Math.round(diffHr / 24)\n const diffWeek = Math.round(diffDay / 7)\n\n if (Math.abs(diffSec) < 60) return rtf.format(diffSec, 'second')\n if (Math.abs(diffMin) < 60) return rtf.format(diffMin, 'minute')\n if (Math.abs(diffHr) < 24) return rtf.format(diffHr, 'hour')\n if (Math.abs(diffDay) < 7) return rtf.format(diffDay, 'day')\n if (Math.abs(diffWeek) < 5) return rtf.format(diffWeek, 'week')\n return date.toLocaleDateString()\n}\n\nfunction handleEntryClick(entry: AuditEntry) {\n emit('entry-click', entry)\n}\n</script>\n\n<template>\n <div :class=\"['mint-audit-trail', `mint-audit-trail--${size}`]\">\n <div v-if=\"showFilters\" class=\"mint-audit-trail__filters\">\n <BaseSelect\n :model-value=\"filterType\"\n :options=\"typeOptions\"\n size=\"sm\"\n class=\"mint-audit-trail__filter-select\"\n aria-label=\"Filter by type\"\n @update:model-value=\"filterType = String($event)\"\n />\n <BaseSelect\n :model-value=\"filterUser\"\n :options=\"userOptions\"\n size=\"sm\"\n class=\"mint-audit-trail__filter-select\"\n aria-label=\"Filter by user\"\n @update:model-value=\"filterUser = String($event)\"\n />\n </div>\n\n <div\n v-if=\"!loading && !error && sortedEntries.length\"\n class=\"mint-audit-trail__list\"\n :style=\"maxHeight ? { maxHeight, overflowY: 'auto' } : {}\"\n >\n <div class=\"mint-audit-trail__line\" />\n <div\n v-for=\"entry in sortedEntries\"\n :key=\"entry.id\"\n :class=\"['mint-audit-trail__entry', `mint-audit-trail__entry--${entry.type}`]\"\n @click=\"handleEntryClick(entry)\"\n >\n <slot name=\"entry\" :entry=\"entry\">\n <div class=\"mint-audit-trail__dot\" />\n <div class=\"mint-audit-trail__content\">\n <!-- One sentence from the existing fields: who · what (4d) -->\n <div class=\"mint-audit-trail__header\">\n <div v-if=\"entry.user\" class=\"mint-audit-trail__avatar\">{{ getInitials(entry.user) }}</div>\n <span class=\"mint-audit-trail__sentence\">\n <span v-if=\"entry.user\" class=\"mint-audit-trail__user\">{{ entry.user }}</span> <span class=\"mint-audit-trail__action\">{{ entry.action }}</span>\n </span>\n <span class=\"mint-audit-trail__timestamp\">{{ formatTimestamp(entry.timestamp) }}</span>\n </div>\n <div v-if=\"entry.detail\" class=\"mint-audit-trail__detail\">{{ entry.detail }}</div>\n <div v-if=\"entry.metadata && Object.keys(entry.metadata).length\" class=\"mint-audit-trail__metadata\">\n <span\n v-for=\"(value, key) in entry.metadata\"\n :key=\"String(key)\"\n class=\"mint-audit-trail__metadata-item\"\n >\n <span class=\"mint-audit-trail__metadata-key\">{{ key }}:</span>\n {{ value }}\n </span>\n </div>\n </div>\n </slot>\n </div>\n </div>\n\n <div v-if=\"error\" class=\"mint-audit-trail__state\">\n <slot name=\"error\" :error=\"error\">\n <DataStateInternal state=\"error\" size=\"sm\" :title=\"error\" />\n </slot>\n </div>\n\n <div v-else-if=\"loading\" class=\"mint-audit-trail__state\">\n <slot name=\"loading\">\n <DataStateInternal state=\"loading\" size=\"sm\" loading-label=\"Loading activity\" />\n </slot>\n </div>\n\n <div v-else-if=\"!sortedEntries.length\" class=\"mint-audit-trail__state\">\n <slot name=\"empty\">\n <DataStateInternal state=\"empty\" size=\"sm\" :title=\"emptyText\" />\n </slot>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Scrollable list tracking per-item status for a batch operation with overall progress summary. */\nimport { computed, ref, watch, nextTick } from 'vue'\nimport type { BatchItem, BatchSummary } from '../types'\nimport DataStateInternal from './internal/DataStateInternal.vue'\n\ninterface Props {\n items: BatchItem[]\n showSummary?: boolean\n title?: string\n maxHeight?: string\n autoScroll?: boolean\n /** Replace the item list with a loading placeholder. */\n loading?: boolean\n /**\n * Error message. When set, the item list is replaced by an error state.\n * `null`/`undefined` renders normally.\n */\n error?: string | null\n /** Empty-state headline shown when there are no batch items. */\n emptyText?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showSummary: true,\n autoScroll: true,\n loading: false,\n error: null,\n emptyText: 'No items in this batch',\n})\n\nconst emit = defineEmits<{\n 'retry': [id: string]\n 'cancel': [id: string]\n}>()\n\nconst listRef = ref<HTMLElement | null>(null)\n// 6b: completed items keep full contrast but collapse behind a toggle.\nconst showCompleted = ref(false)\nconst visibleItems = computed(() => (\n showCompleted.value ? props.items : props.items.filter(i => i.status !== 'completed')\n))\n\nconst summary = computed<BatchSummary>(() => {\n const total = props.items.length\n const completed = props.items.filter(i => i.status === 'completed').length\n const processing = props.items.filter(i => i.status === 'processing').length\n const error = props.items.filter(i => i.status === 'error').length\n const pending = props.items.filter(i => i.status === 'pending').length\n const skipped = props.items.filter(i => i.status === 'skipped').length\n const percent = total > 0 ? Math.round(((completed + skipped) / total) * 100) : 0\n return { total, completed, processing, error, pending, skipped, percent }\n})\n\nwatch(\n () => props.items.map(i => i.status),\n (newStatuses, oldStatuses) => {\n if (!props.autoScroll || !listRef.value) return\n for (let i = 0; i < newStatuses.length; i++) {\n if (newStatuses[i] === 'processing' && oldStatuses?.[i] !== 'processing') {\n nextTick(() => {\n const el = listRef.value?.querySelector(`[data-item-id=\"${props.items[i].id}\"]`)\n el?.scrollIntoView({ behavior: 'smooth', block: 'center' })\n })\n break\n }\n }\n },\n)\n</script>\n\n<template>\n <div class=\"mint-batch-progress\">\n <div v-if=\"title\" class=\"mint-batch-progress__header\">\n <span class=\"mint-batch-progress__title\">{{ title }}</span>\n <span class=\"mint-batch-progress__count\">{{ summary.completed }} / {{ summary.total }}</span>\n <span class=\"mint-batch-progress__percent\">{{ summary.percent }}%</span>\n </div>\n\n <div class=\"mint-batch-progress__overall\">\n <div\n class=\"mint-batch-progress__overall-bar\"\n :style=\"{ width: `${summary.percent}%` }\"\n />\n </div>\n\n <slot v-if=\"showSummary\" name=\"summary\" :summary=\"summary\">\n <div class=\"mint-batch-progress__summary\">\n <span v-if=\"summary.completed\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--completed\">\n <svg class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path class=\"mint-batch-progress__check\" d=\"M3.5 8.5l3 3 6-7\" /></svg>\n <span class=\"mint-batch-progress__summary-count\">{{ summary.completed }}</span> completed\n </span>\n <span v-if=\"summary.processing\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--processing\">\n <span class=\"mint-batch-progress__running-dot\" aria-hidden=\"true\" />\n <span class=\"mint-batch-progress__summary-count\">{{ summary.processing }}</span> processing\n </span>\n <span v-if=\"summary.error\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--error\">\n <svg class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\" aria-hidden=\"true\"><circle class=\"mint-batch-progress__error-disc\" cx=\"8\" cy=\"8\" r=\"7\" /><path class=\"mint-batch-progress__error-mark\" d=\"M8 4.5v4M8 11v.01\" /></svg>\n <span class=\"mint-batch-progress__summary-count\">{{ summary.error }}</span> failed\n </span>\n <span v-if=\"summary.pending\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--pending\">\n <span class=\"mint-batch-progress__pending-ring\" aria-hidden=\"true\" />\n <span class=\"mint-batch-progress__summary-count\">{{ summary.pending }}</span> pending\n </span>\n <span v-if=\"summary.skipped\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--skipped\">\n <svg class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path class=\"mint-batch-progress__dash\" d=\"M4 8h8\" /></svg>\n <span class=\"mint-batch-progress__summary-count\">{{ summary.skipped }}</span> skipped\n </span>\n </div>\n </slot>\n\n <div v-if=\"error\" class=\"mint-batch-progress__state\">\n <slot name=\"error\" :error=\"error\">\n <DataStateInternal state=\"error\" size=\"sm\" :title=\"error\" />\n </slot>\n </div>\n\n <div v-else-if=\"loading\" class=\"mint-batch-progress__state\">\n <slot name=\"loading\">\n <DataStateInternal state=\"loading\" size=\"sm\" loading-label=\"Loading batch\" />\n </slot>\n </div>\n\n <div v-else-if=\"items.length === 0\" class=\"mint-batch-progress__state\">\n <slot name=\"empty\">\n <DataStateInternal state=\"empty\" size=\"sm\" :title=\"emptyText\" />\n </slot>\n </div>\n\n <div\n v-else\n ref=\"listRef\"\n class=\"mint-batch-progress__list\"\n :style=\"maxHeight ? { maxHeight, overflowY: 'auto' } : {}\"\n >\n <div\n v-for=\"item in visibleItems\"\n :key=\"item.id\"\n :data-item-id=\"item.id\"\n :class=\"['mint-batch-progress__item', `mint-batch-progress__item--${item.status}`]\"\n >\n <slot name=\"item\" :item=\"item\">\n <div class=\"mint-batch-progress__item-row\">\n <!-- Status by shape: hollow ring / progress arc / check / filled ! / dash -->\n <span :class=\"['mint-batch-progress__item-icon', `mint-batch-progress__item-icon--${item.status}`]\" aria-hidden=\"true\">\n <span v-if=\"item.status === 'pending'\" class=\"mint-batch-progress__pending-ring\" />\n <svg v-else-if=\"item.status === 'processing'\" class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle class=\"mint-batch-progress__arc-track\" cx=\"8\" cy=\"8\" r=\"6\" />\n <path class=\"mint-batch-progress__arc\" d=\"M8 2a6 6 0 0 1 6 6\" />\n </svg>\n <svg v-else-if=\"item.status === 'completed'\" class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path class=\"mint-batch-progress__check\" d=\"M3.5 8.5l3 3 6-7\" />\n </svg>\n <svg v-else-if=\"item.status === 'error'\" class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\">\n <circle class=\"mint-batch-progress__error-disc\" cx=\"8\" cy=\"8\" r=\"7\" />\n <path class=\"mint-batch-progress__error-mark\" d=\"M8 4.5v4M8 11v.01\" />\n </svg>\n <svg v-else class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path class=\"mint-batch-progress__dash\" d=\"M4 8h8\" />\n </svg>\n </span>\n\n <span class=\"mint-batch-progress__item-label\">{{ item.label }}</span>\n\n <span class=\"mint-batch-progress__item-meta\">\n <template v-if=\"item.status === 'processing' && item.progress !== undefined\">\n <span class=\"mint-batch-progress__item-progress\">\n <span class=\"mint-batch-progress__item-progress-bar\" :style=\"{ width: `${item.progress}%` }\" />\n </span>\n {{ item.progress }}%\n </template>\n <button\n v-if=\"item.status === 'error'\"\n type=\"button\"\n class=\"mint-batch-progress__retry-btn\"\n @click.stop=\"emit('retry', item.id)\"\n >\n Retry\n </button>\n <button\n v-if=\"item.status === 'processing'\"\n type=\"button\"\n class=\"mint-batch-progress__cancel-btn\"\n @click.stop=\"emit('cancel', item.id)\"\n >\n Cancel\n </button>\n </span>\n </div>\n\n <div v-if=\"item.status === 'error' && item.message\" class=\"mint-batch-progress__item-message\">\n {{ item.message }}\n </div>\n </slot>\n </div>\n\n <button\n v-if=\"summary.completed\"\n type=\"button\"\n class=\"mint-batch-progress__completed-toggle\"\n @click=\"showCompleted = !showCompleted\"\n >\n {{ showCompleted ? 'Hide completed' : `Show ${summary.completed} completed` }}\n </button>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Scrollable list tracking per-item status for a batch operation with overall progress summary. */\nimport { computed, ref, watch, nextTick } from 'vue'\nimport type { BatchItem, BatchSummary } from '../types'\nimport DataStateInternal from './internal/DataStateInternal.vue'\n\ninterface Props {\n items: BatchItem[]\n showSummary?: boolean\n title?: string\n maxHeight?: string\n autoScroll?: boolean\n /** Replace the item list with a loading placeholder. */\n loading?: boolean\n /**\n * Error message. When set, the item list is replaced by an error state.\n * `null`/`undefined` renders normally.\n */\n error?: string | null\n /** Empty-state headline shown when there are no batch items. */\n emptyText?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showSummary: true,\n autoScroll: true,\n loading: false,\n error: null,\n emptyText: 'No items in this batch',\n})\n\nconst emit = defineEmits<{\n 'retry': [id: string]\n 'cancel': [id: string]\n}>()\n\nconst listRef = ref<HTMLElement | null>(null)\n// 6b: completed items keep full contrast but collapse behind a toggle.\nconst showCompleted = ref(false)\nconst visibleItems = computed(() => (\n showCompleted.value ? props.items : props.items.filter(i => i.status !== 'completed')\n))\n\nconst summary = computed<BatchSummary>(() => {\n const total = props.items.length\n const completed = props.items.filter(i => i.status === 'completed').length\n const processing = props.items.filter(i => i.status === 'processing').length\n const error = props.items.filter(i => i.status === 'error').length\n const pending = props.items.filter(i => i.status === 'pending').length\n const skipped = props.items.filter(i => i.status === 'skipped').length\n const percent = total > 0 ? Math.round(((completed + skipped) / total) * 100) : 0\n return { total, completed, processing, error, pending, skipped, percent }\n})\n\nwatch(\n () => props.items.map(i => i.status),\n (newStatuses, oldStatuses) => {\n if (!props.autoScroll || !listRef.value) return\n for (let i = 0; i < newStatuses.length; i++) {\n if (newStatuses[i] === 'processing' && oldStatuses?.[i] !== 'processing') {\n nextTick(() => {\n const el = listRef.value?.querySelector(`[data-item-id=\"${props.items[i].id}\"]`)\n el?.scrollIntoView({ behavior: 'smooth', block: 'center' })\n })\n break\n }\n }\n },\n)\n</script>\n\n<template>\n <div class=\"mint-batch-progress\">\n <div v-if=\"title\" class=\"mint-batch-progress__header\">\n <span class=\"mint-batch-progress__title\">{{ title }}</span>\n <span class=\"mint-batch-progress__count\">{{ summary.completed }} / {{ summary.total }}</span>\n <span class=\"mint-batch-progress__percent\">{{ summary.percent }}%</span>\n </div>\n\n <div class=\"mint-batch-progress__overall\">\n <div\n class=\"mint-batch-progress__overall-bar\"\n :style=\"{ width: `${summary.percent}%` }\"\n />\n </div>\n\n <slot v-if=\"showSummary\" name=\"summary\" :summary=\"summary\">\n <div class=\"mint-batch-progress__summary\">\n <span v-if=\"summary.completed\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--completed\">\n <svg class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path class=\"mint-batch-progress__check\" d=\"M3.5 8.5l3 3 6-7\" /></svg>\n <span class=\"mint-batch-progress__summary-count\">{{ summary.completed }}</span> completed\n </span>\n <span v-if=\"summary.processing\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--processing\">\n <span class=\"mint-batch-progress__running-dot\" aria-hidden=\"true\" />\n <span class=\"mint-batch-progress__summary-count\">{{ summary.processing }}</span> processing\n </span>\n <span v-if=\"summary.error\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--error\">\n <svg class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\" aria-hidden=\"true\"><circle class=\"mint-batch-progress__error-disc\" cx=\"8\" cy=\"8\" r=\"7\" /><path class=\"mint-batch-progress__error-mark\" d=\"M8 4.5v4M8 11v.01\" /></svg>\n <span class=\"mint-batch-progress__summary-count\">{{ summary.error }}</span> failed\n </span>\n <span v-if=\"summary.pending\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--pending\">\n <span class=\"mint-batch-progress__pending-ring\" aria-hidden=\"true\" />\n <span class=\"mint-batch-progress__summary-count\">{{ summary.pending }}</span> pending\n </span>\n <span v-if=\"summary.skipped\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--skipped\">\n <svg class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path class=\"mint-batch-progress__dash\" d=\"M4 8h8\" /></svg>\n <span class=\"mint-batch-progress__summary-count\">{{ summary.skipped }}</span> skipped\n </span>\n </div>\n </slot>\n\n <div v-if=\"error\" class=\"mint-batch-progress__state\">\n <slot name=\"error\" :error=\"error\">\n <DataStateInternal state=\"error\" size=\"sm\" :title=\"error\" />\n </slot>\n </div>\n\n <div v-else-if=\"loading\" class=\"mint-batch-progress__state\">\n <slot name=\"loading\">\n <DataStateInternal state=\"loading\" size=\"sm\" loading-label=\"Loading batch\" />\n </slot>\n </div>\n\n <div v-else-if=\"items.length === 0\" class=\"mint-batch-progress__state\">\n <slot name=\"empty\">\n <DataStateInternal state=\"empty\" size=\"sm\" :title=\"emptyText\" />\n </slot>\n </div>\n\n <div\n v-else\n ref=\"listRef\"\n class=\"mint-batch-progress__list\"\n :style=\"maxHeight ? { maxHeight, overflowY: 'auto' } : {}\"\n >\n <div\n v-for=\"item in visibleItems\"\n :key=\"item.id\"\n :data-item-id=\"item.id\"\n :class=\"['mint-batch-progress__item', `mint-batch-progress__item--${item.status}`]\"\n >\n <slot name=\"item\" :item=\"item\">\n <div class=\"mint-batch-progress__item-row\">\n <!-- Status by shape: hollow ring / progress arc / check / filled ! / dash -->\n <span :class=\"['mint-batch-progress__item-icon', `mint-batch-progress__item-icon--${item.status}`]\" aria-hidden=\"true\">\n <span v-if=\"item.status === 'pending'\" class=\"mint-batch-progress__pending-ring\" />\n <svg v-else-if=\"item.status === 'processing'\" class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle class=\"mint-batch-progress__arc-track\" cx=\"8\" cy=\"8\" r=\"6\" />\n <path class=\"mint-batch-progress__arc\" d=\"M8 2a6 6 0 0 1 6 6\" />\n </svg>\n <svg v-else-if=\"item.status === 'completed'\" class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path class=\"mint-batch-progress__check\" d=\"M3.5 8.5l3 3 6-7\" />\n </svg>\n <svg v-else-if=\"item.status === 'error'\" class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\">\n <circle class=\"mint-batch-progress__error-disc\" cx=\"8\" cy=\"8\" r=\"7\" />\n <path class=\"mint-batch-progress__error-mark\" d=\"M8 4.5v4M8 11v.01\" />\n </svg>\n <svg v-else class=\"mint-batch-progress__shape\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path class=\"mint-batch-progress__dash\" d=\"M4 8h8\" />\n </svg>\n </span>\n\n <span class=\"mint-batch-progress__item-label\">{{ item.label }}</span>\n\n <span class=\"mint-batch-progress__item-meta\">\n <template v-if=\"item.status === 'processing' && item.progress !== undefined\">\n <span class=\"mint-batch-progress__item-progress\">\n <span class=\"mint-batch-progress__item-progress-bar\" :style=\"{ width: `${item.progress}%` }\" />\n </span>\n {{ item.progress }}%\n </template>\n <button\n v-if=\"item.status === 'error'\"\n type=\"button\"\n class=\"mint-batch-progress__retry-btn\"\n @click.stop=\"emit('retry', item.id)\"\n >\n Retry\n </button>\n <button\n v-if=\"item.status === 'processing'\"\n type=\"button\"\n class=\"mint-batch-progress__cancel-btn\"\n @click.stop=\"emit('cancel', item.id)\"\n >\n Cancel\n </button>\n </span>\n </div>\n\n <div v-if=\"item.status === 'error' && item.message\" class=\"mint-batch-progress__item-message\">\n {{ item.message }}\n </div>\n </slot>\n </div>\n\n <button\n v-if=\"summary.completed\"\n type=\"button\"\n class=\"mint-batch-progress__completed-toggle\"\n @click=\"showCompleted = !showCompleted\"\n >\n {{ showCompleted ? 'Hide completed' : `Show ${summary.completed} completed` }}\n </button>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * The dialog for a save refused with 412 `<entity>.version_conflict` (RFC #439).\n * Show it while `useVersionedSave().conflict` is set: Reload discards the\n * user's edits and refetches, Overwrite saves them on top of the newer version.\n */\nimport { computed } from 'vue'\nimport { RefreshCw } from 'lucide-vue-next'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n open: boolean\n /** The entity line under the title, such as `EXP-042 · Cisplatin dose response`. */\n entity?: string\n /** Labels of the fields the user changed and did not save. */\n changes?: string[]\n /** True while Overwrite saves. */\n loading?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n entity: '',\n changes: () => [],\n loading: false,\n})\n\nconst emit = defineEmits<{\n 'update:open': [value: boolean]\n reload: []\n overwrite: []\n}>()\n\nconst message = computed(() => {\n const count = props.changes.length\n if (count === 0) return 'Your changes are not saved. Pick what to keep.'\n const [noun, verb] = count === 1 ? ['change', 'is'] : ['changes', 'are']\n return `Your ${count} ${noun} (${props.changes.join(', ')}) ${verb} not saved. Pick what to keep.`\n})\n</script>\n\n<template>\n <BaseModal\n :open=\"open\"\n title=\"Changed by someone else\"\n :subtitle=\"entity || undefined\"\n size=\"md\"\n :closable=\"!loading\"\n :close-on-escape=\"!loading\"\n @update:open=\"emit('update:open', $event)\"\n >\n <p class=\"mint-version-conflict__message\">{{ message }}</p>\n\n <template #footer>\n <div class=\"mint-version-conflict__footer\">\n <BaseButton\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n class=\"mint-version-conflict__overwrite\"\n :loading=\"loading\"\n @click=\"emit('overwrite')\"\n >\n Overwrite\n </BaseButton>\n <BaseButton\n type=\"button\"\n variant=\"primary\"\n size=\"sm\"\n :icon=\"RefreshCw\"\n :disabled=\"loading\"\n @click=\"emit('reload')\"\n >\n Reload and discard mine\n </BaseButton>\n </div>\n </template>\n </BaseModal>\n</template>\n","<script setup lang=\"ts\">\n/**\n * The dialog for a save refused with 412 `<entity>.version_conflict` (RFC #439).\n * Show it while `useVersionedSave().conflict` is set: Reload discards the\n * user's edits and refetches, Overwrite saves them on top of the newer version.\n */\nimport { computed } from 'vue'\nimport { RefreshCw } from 'lucide-vue-next'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n open: boolean\n /** The entity line under the title, such as `EXP-042 · Cisplatin dose response`. */\n entity?: string\n /** Labels of the fields the user changed and did not save. */\n changes?: string[]\n /** True while Overwrite saves. */\n loading?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n entity: '',\n changes: () => [],\n loading: false,\n})\n\nconst emit = defineEmits<{\n 'update:open': [value: boolean]\n reload: []\n overwrite: []\n}>()\n\nconst message = computed(() => {\n const count = props.changes.length\n if (count === 0) return 'Your changes are not saved. Pick what to keep.'\n const [noun, verb] = count === 1 ? ['change', 'is'] : ['changes', 'are']\n return `Your ${count} ${noun} (${props.changes.join(', ')}) ${verb} not saved. Pick what to keep.`\n})\n</script>\n\n<template>\n <BaseModal\n :open=\"open\"\n title=\"Changed by someone else\"\n :subtitle=\"entity || undefined\"\n size=\"md\"\n :closable=\"!loading\"\n :close-on-escape=\"!loading\"\n @update:open=\"emit('update:open', $event)\"\n >\n <p class=\"mint-version-conflict__message\">{{ message }}</p>\n\n <template #footer>\n <div class=\"mint-version-conflict__footer\">\n <BaseButton\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n class=\"mint-version-conflict__overwrite\"\n :loading=\"loading\"\n @click=\"emit('overwrite')\"\n >\n Overwrite\n </BaseButton>\n <BaseButton\n type=\"button\"\n variant=\"primary\"\n size=\"sm\"\n :icon=\"RefreshCw\"\n :disabled=\"loading\"\n @click=\"emit('reload')\"\n >\n Reload and discard mine\n </BaseButton>\n </div>\n </template>\n </BaseModal>\n</template>\n","<script setup lang=\"ts\">\n/**\n * The strip at the top of a form whose entity someone else changed while the\n * form held unsaved edits (RFC #439). Show it while `useVersionedSave().stale`\n * is set. It offers Reload and Review, not Overwrite: a save that would\n * overwrite gets the 412 dialog.\n */\nimport { computed, onBeforeUnmount, ref } from 'vue'\nimport { TriangleAlert, X } from 'lucide-vue-next'\nimport type { StaleChange } from '../composables/useVersionedSave'\n\ninterface Props {\n change: StaleChange\n /** The entity's page; Review opens it in a new tab. Without it, there is no Review. */\n reviewHref?: string\n /** What the form edits, for a write that names no fields. */\n entityNoun?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n reviewHref: '',\n entityNoun: 'this record',\n})\n\nconst emit = defineEmits<{\n reload: []\n dismiss: []\n}>()\n\n/** \"A\", \"A and B\", \"A, B and C\". */\nfunction list(items: readonly string[]): string {\n if (items.length <= 1) return items[0] ?? ''\n return `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`\n}\n\nconst summary = computed(() => {\n const { actors, fields } = props.change\n // Up to two named fields read as a list; more read as a count.\n const what = fields.length === 0\n ? props.entityNoun\n : fields.length <= 2 ? list(fields) : `${fields.length} fields`\n return `${list(actors)} changed ${what}`\n})\n\nconst now = ref(Date.now())\nconst timer = setInterval(() => { now.value = Date.now() }, 5_000)\nonBeforeUnmount(() => clearInterval(timer))\n\nconst ago = computed(() => {\n const seconds = Math.max(0, Math.round((now.value - Date.parse(props.change.at)) / 1000))\n if (seconds < 60) return `${seconds} s ago`\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return `${minutes} min ago`\n return `${Math.round(minutes / 60)} h ago`\n})\n</script>\n\n<template>\n <div class=\"mint-stale-banner\" role=\"status\">\n <TriangleAlert class=\"mint-stale-banner__icon\" aria-hidden=\"true\" />\n <p class=\"mint-stale-banner__text\">\n <strong>{{ summary }}</strong>\n {{ ' ' }}<time class=\"mint-stale-banner__time\" :datetime=\"change.at\">{{ ago }}</time>.\n This form shows the older version.\n </p>\n <a\n v-if=\"reviewHref\"\n class=\"mint-stale-banner__action\"\n :href=\"reviewHref\"\n target=\"_blank\"\n rel=\"noopener\"\n >Review</a>\n <button type=\"button\" class=\"mint-stale-banner__action\" @click=\"emit('reload')\">Reload form</button>\n <button\n type=\"button\"\n class=\"mint-stale-banner__dismiss\"\n aria-label=\"Dismiss until the next change\"\n @click=\"emit('dismiss')\"\n >\n <X aria-hidden=\"true\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * The strip at the top of a form whose entity someone else changed while the\n * form held unsaved edits (RFC #439). Show it while `useVersionedSave().stale`\n * is set. It offers Reload and Review, not Overwrite: a save that would\n * overwrite gets the 412 dialog.\n */\nimport { computed, onBeforeUnmount, ref } from 'vue'\nimport { TriangleAlert, X } from 'lucide-vue-next'\nimport type { StaleChange } from '../composables/useVersionedSave'\n\ninterface Props {\n change: StaleChange\n /** The entity's page; Review opens it in a new tab. Without it, there is no Review. */\n reviewHref?: string\n /** What the form edits, for a write that names no fields. */\n entityNoun?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n reviewHref: '',\n entityNoun: 'this record',\n})\n\nconst emit = defineEmits<{\n reload: []\n dismiss: []\n}>()\n\n/** \"A\", \"A and B\", \"A, B and C\". */\nfunction list(items: readonly string[]): string {\n if (items.length <= 1) return items[0] ?? ''\n return `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`\n}\n\nconst summary = computed(() => {\n const { actors, fields } = props.change\n // Up to two named fields read as a list; more read as a count.\n const what = fields.length === 0\n ? props.entityNoun\n : fields.length <= 2 ? list(fields) : `${fields.length} fields`\n return `${list(actors)} changed ${what}`\n})\n\nconst now = ref(Date.now())\nconst timer = setInterval(() => { now.value = Date.now() }, 5_000)\nonBeforeUnmount(() => clearInterval(timer))\n\nconst ago = computed(() => {\n const seconds = Math.max(0, Math.round((now.value - Date.parse(props.change.at)) / 1000))\n if (seconds < 60) return `${seconds} s ago`\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return `${minutes} min ago`\n return `${Math.round(minutes / 60)} h ago`\n})\n</script>\n\n<template>\n <div class=\"mint-stale-banner\" role=\"status\">\n <TriangleAlert class=\"mint-stale-banner__icon\" aria-hidden=\"true\" />\n <p class=\"mint-stale-banner__text\">\n <strong>{{ summary }}</strong>\n {{ ' ' }}<time class=\"mint-stale-banner__time\" :datetime=\"change.at\">{{ ago }}</time>.\n This form shows the older version.\n </p>\n <a\n v-if=\"reviewHref\"\n class=\"mint-stale-banner__action\"\n :href=\"reviewHref\"\n target=\"_blank\"\n rel=\"noopener\"\n >Review</a>\n <button type=\"button\" class=\"mint-stale-banner__action\" @click=\"emit('reload')\">Reload form</button>\n <button\n type=\"button\"\n class=\"mint-stale-banner__dismiss\"\n aria-label=\"Dismiss until the next change\"\n @click=\"emit('dismiss')\"\n >\n <X aria-hidden=\"true\" />\n </button>\n </div>\n</template>\n","import { readonly, ref, toValue, watch, type MaybeRefOrGetter, type Ref } from 'vue'\nimport { MintApiError } from '../apiErrors'\nimport { useRealtime, type RealtimeEvent } from './useRealtime'\n\n/** A save the platform refused with 412 `<entity>.version_conflict`. */\nexport interface VersionConflict {\n /** The entity's current version from the 412 body; `null` when the body has none. */\n version: number | null\n}\n\n/** Writes by other users that reached the open entity while the form held unsaved edits. */\nexport interface StaleChange {\n /** Display names of the writers, in order of arrival, without repeats. */\n actors: readonly string[]\n /** Labels of the changed fields, without repeats; empty when no event named them. */\n fields: readonly string[]\n /** ISO time of the latest write. */\n at: string\n}\n\nexport interface UseVersionedSaveOptions<T> {\n /** The entity version the form was loaded at; sent as `If-Match: \"v<version>\"`. */\n version: MaybeRefOrGetter<number | null | undefined>\n /** Send the write. Pass `headers` on to the request. */\n save: (headers: Record<string, string>) => Promise<T>\n /**\n * Refetch the entity and reset the form to it. The user's edits are discarded.\n * Reject when no fresh record arrives: the form then keeps the edits and the conflict.\n */\n reload: () => unknown\n /**\n * The realtime topic of the open entity (`experiment:42`). `null` turns the\n * stale banner off: pass it while the form is closed or the platform's\n * `realtime` feature flag is off. A change of topic also clears `conflict`.\n */\n topic?: MaybeRefOrGetter<string | null | undefined>\n /** True while the form holds unsaved edits. */\n dirty?: MaybeRefOrGetter<boolean>\n /** The display name of an event's `actor_id`; unknown users show as \"Someone\". */\n userName?: (userId: string) => string | null | undefined\n /** The label of an event field name; defaults to `end_date` → \"End date\". */\n fieldLabel?: (field: string) => string\n}\n\nexport interface UseVersionedSaveReturn<T> {\n /** Save with `If-Match`. Resolves `undefined` on a 412: `conflict` is then set. Other errors reject. */\n save: () => Promise<T | undefined>\n /** Save again on top of the version the 412 reported. Resolves `undefined` on another 412. */\n overwrite: () => Promise<T | undefined>\n /** Run the `reload` option, then clear `conflict` and `stale`. Rejects, and keeps both, when the reload rejects. */\n reload: () => Promise<void>\n /** Close the conflict dialog and keep editing. */\n dismissConflict: () => void\n /** Hide the stale banner until the next write arrives. */\n dismissStale: () => void\n saving: Readonly<Ref<boolean>>\n conflict: Readonly<Ref<VersionConflict | null>>\n stale: Readonly<Ref<StaleChange | null>>\n}\n\n/** `end_date` → \"End date\". */\nexport function fieldNameLabel(field: string): string {\n const words = field.replace(/_/g, ' ').trim()\n return words.charAt(0).toUpperCase() + words.slice(1)\n}\n\nfunction conflictVersion(cause: unknown): VersionConflict | null {\n if (!(cause instanceof MintApiError) || cause.status !== 412) return null\n const version = (cause.details as { version?: unknown } | null)?.version\n return { version: typeof version === 'number' ? version : null }\n}\n\nfunction union(list: readonly string[], items: readonly string[]): string[] {\n return [...new Set([...list, ...items])]\n}\n\n/**\n * Optimistic-concurrency save for a form on a versioned entity (RFC #439).\n *\n * `save()` sends `If-Match` with the loaded version. A 412 sets `conflict`;\n * show `VersionConflictDialog` for it, whose Reload calls `reload()` and\n * whose Overwrite calls `overwrite()` (the same save, with the version from\n * the 412). With a `topic`, a realtime write by someone else on the entity\n * sets `stale` while the form is dirty (show `StaleFormBanner`); on a clean\n * form it runs the `reload` option instead. Several writes merge into one\n * `stale`.\n *\n * @example\n * const versioned = useVersionedSave({\n * version: () => experiment.value?.version,\n * save: headers => api.patch(`/experiments/${id}`, payload(), { headers }),\n * reload: () => refetchAndResetForm(),\n * topic: () => (realtime && open.value ? `experiment:${id}` : null),\n * dirty: () => changeCount.value > 0,\n * })\n */\nexport function useVersionedSave<T>(options: UseVersionedSaveOptions<T>): UseVersionedSaveReturn<T> {\n const saving = ref(false)\n const conflict = ref<VersionConflict | null>(null)\n const stale = ref<StaleChange | null>(null)\n const fieldLabel = options.fieldLabel ?? fieldNameLabel\n\n async function send(version: number | null | undefined): Promise<T | undefined> {\n saving.value = true\n try {\n const headers: Record<string, string> = version == null ? {} : { 'If-Match': `\"v${version}\"` }\n const result = await options.save(headers)\n conflict.value = null\n stale.value = null\n return result\n } catch (cause) {\n const refused = conflictVersion(cause)\n if (!refused) throw cause\n conflict.value = refused\n return undefined\n } finally {\n saving.value = false\n }\n }\n\n async function reload(): Promise<void> {\n await options.reload()\n conflict.value = null\n stale.value = null\n }\n\n function onEvent(event: RealtimeEvent): void {\n // A resync names no writer, and the user's own save echoes back while it runs.\n if (event.type === 'resync' || saving.value || event.topic !== toValue(options.topic)) return\n if (!toValue(options.dirty)) {\n // A failed background refetch leaves a clean form as it is; the host reports its own faults.\n reload().catch(() => {})\n return\n }\n const actor = (event.actor_id && options.userName?.(event.actor_id)) || 'Someone'\n const previous = stale.value\n stale.value = {\n actors: union(previous?.actors ?? [], [actor]),\n fields: union(previous?.fields ?? [], (event.fields ?? []).map(fieldLabel)),\n at: event.at ?? new Date().toISOString(),\n }\n }\n\n useRealtime(() => {\n const topic = toValue(options.topic)\n return topic ? [topic] : []\n }, onEvent)\n // Another entity, or a closed form: the old writes and the old 412 no longer apply.\n watch(() => toValue(options.topic), () => {\n conflict.value = null\n stale.value = null\n })\n\n return {\n save: () => send(toValue(options.version)),\n // Without a version in the 412, a save with no If-Match is the overwrite.\n overwrite: () => send(conflict.value?.version),\n reload,\n dismissConflict: () => { conflict.value = null },\n dismissStale: () => { stale.value = null },\n saving: readonly(saving),\n conflict: readonly(conflict),\n stale: readonly(stale),\n }\n}\n","import type { PickerAdapter, PickerNode, PickerRequest, PickerSearchResult } from '../types/filePicker'\nimport type {\n FileBrowserLocation,\n FileDirectoryListing,\n FileDirectoryTree,\n FileEntry,\n FileSearchResult,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport { pickerFormat } from './filePicker/validation'\n\nexport interface FilePickerTransport {\n listMounts(request?: PickerRequest): Promise<ServerMount[]>\n browse(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryListing>\n /** `GET …/filesystem/tree`: one request fills the cache for the subfolders too. */\n tree?(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryTree>\n /** `GET …/filesystem/search`: replaces the folder-by-folder search walk. */\n search?(\n location: FileBrowserLocation,\n query: string,\n recursive: boolean,\n request?: PickerRequest,\n ): Promise<FileSearchResult>\n}\nexport interface FilePickerAdapterOptions {\n /** Show this directory's contents directly and prevent navigation outside it. */\n rootLocation?: FileBrowserLocation\n}\n\n/** Opaque picker identity; decode before passing a selection to a mount-scoped API. */\nexport function encodePlatformPickerPath(mountId: string, path = ''): string {\n return JSON.stringify([mountId, path])\n}\nexport function decodePlatformPickerPath(path: string): { mountId: string; path: string } {\n const decoded: unknown = JSON.parse(path)\n if (\n !Array.isArray(decoded) || decoded.length !== 2 ||\n !decoded.every((item) => typeof item === 'string') || !decoded[0]\n ) throw new Error('Invalid platform picker path.')\n return { mountId: decoded[0], path: decoded[1] }\n}\n\nfunction isRelativePath(path: string): boolean {\n return path === '' || (!/^[a-z]:/i.test(path) && !/[\\\\\\x00-\\x1f\\x7f]/.test(path) &&\n path.split('/').every((part) => part !== '' && part !== '.' && part !== '..'))\n}\n\n/** Mount browsing and search shared by platform and generated plugin clients. */\nexport function createFilePickerAdapter(\n transport: FilePickerTransport,\n options: FilePickerAdapterOptions = {},\n): PickerAdapter {\n const root = options.rootLocation && { ...options.rootLocation }\n const mountLabels = new Map<string, string>()\n // Per picker instance: never share permission-sensitive listings across clients.\n const listings = new Map<string, PickerNode[]>()\n let generation = 0\n\n function invalidate(): void {\n generation++\n listings.clear()\n mountLabels.clear()\n }\n\n function validateLocation(location: FileBrowserLocation) {\n if (!location.mount_id || !isRelativePath(location.path) || (root && (\n location.mount_id !== root.mount_id ||\n (root.path !== '' && location.path !== root.path && !location.path.startsWith(`${root.path}/`))\n ))) throw new Error('Invalid file picker location.')\n }\n if (root) validateLocation(root)\n function begin(request?: PickerRequest): number {\n request?.signal?.throwIfAborted()\n if (request?.refresh) invalidate()\n return generation\n }\n function check(request: PickerRequest | undefined, token: number) {\n request?.signal?.throwIfAborted()\n if (token !== generation) throw new DOMException('Cancelled', 'AbortError')\n }\n async function read(path: string, request: PickerRequest | undefined, token: number): Promise<PickerNode[]> {\n check(request, token)\n const decoded = path ? decodePlatformPickerPath(path) : undefined\n const location = decoded ? { mount_id: decoded.mountId, path: decoded.path } : root\n if (location) validateLocation(location)\n const key = location ? encodePlatformPickerPath(location.mount_id, location.path) : ''\n const cached = listings.get(key)\n if (cached) return cached\n let nodes: PickerNode[]\n if (!location) {\n const mounts = await transport.listMounts(request)\n check(request, token)\n nodes = mounts.map((mount) => {\n mountLabels.set(mount.id, mount.label)\n return {\n kind: 'folder', path: encodePlatformPickerPath(mount.id), name: mount.label,\n parentPath: null, parentName: 'Server mounts',\n unavailableReason: mount.available === false ? 'This mount is offline.' : undefined,\n }\n })\n } else if (transport.tree) {\n const tree = await transport.tree(location, request)\n check(request, token)\n for (const listing of tree.listings ?? []) {\n const at = { mount_id: listing.mount_id, path: listing.path }\n validateLocation(at)\n if (at.mount_id !== location.mount_id) throw new Error('Invalid file picker directory entry.')\n // A capped subfolder is read on its own when opened, and reports the cap then.\n if (!listing.truncated) listings.set(encodePlatformPickerPath(at.mount_id, at.path), toNodes(at, listing))\n }\n const cached = listings.get(key)\n if (!cached)\n throw new Error('This directory exceeds the server listing limit. Choose a smaller data directory.')\n return cached\n } else {\n const listing = await transport.browse(location, request)\n check(request, token)\n if (listing.truncated)\n throw new Error('This directory exceeds the server listing limit. Choose a smaller data directory.')\n nodes = toNodes(location, listing)\n }\n listings.set(key, nodes)\n return nodes\n }\n function toNodes(location: FileBrowserLocation, listing: FileDirectoryListing): PickerNode[] {\n return (listing.entries ?? []).map((entry) => {\n if (entry.path !== `${location.path ? `${location.path}/` : ''}${entry.name}`)\n throw new Error('Invalid file picker directory entry.')\n return toNode(location.mount_id, entry)\n })\n }\n /** One entry, parented on the folder its mount-relative path names. */\n function toNode(mountId: string, entry: FileEntry): PickerNode {\n if (!isRelativePath(entry.path) || !entry.name || entry.name.includes('/') ||\n (entry.path !== entry.name && !entry.path.endsWith(`/${entry.name}`)))\n throw new Error('Invalid file picker directory entry.')\n const parent = entry.path.slice(0, Math.max(0, entry.path.length - entry.name.length - 1))\n const atRoot = root && mountId === root.mount_id && parent === root.path\n const common = {\n path: encodePlatformPickerPath(mountId, entry.path),\n parentPath: atRoot ? null : encodePlatformPickerPath(mountId, parent),\n parentName: `${mountLabels.get(mountId) ?? mountId}${parent ? ` / ${parent}` : ''}`,\n name: entry.name, bytes: entry.size_bytes, modifiedAt: entry.modified_at,\n }\n return entry.kind === 'directory'\n // Counts may include only matching formats, not nested directories.\n ? { ...common, kind: 'folder' }\n : { ...common, kind: 'file', format: pickerFormat(entry.name) }\n }\n async function serverSearch(\n query: string,\n scope: { path: string; recursive: boolean },\n request: PickerRequest | undefined,\n ): Promise<PickerNode[] | PickerSearchResult> {\n const token = begin(request)\n const decoded = scope.path ? decodePlatformPickerPath(scope.path) : undefined\n const location = decoded ? { mount_id: decoded.mountId, path: decoded.path } : root!\n validateLocation(location)\n const result = await transport.search!(location, query, scope.recursive, request)\n check(request, token)\n const prefix = location.path ? `${location.path}/` : ''\n const nodes = (result.entries ?? []).map((entry) => {\n // \"This folder\" searches may return direct children only.\n if (!entry.path.startsWith(prefix) || (!scope.recursive && entry.path.slice(prefix.length).includes('/')))\n throw new Error('Invalid file picker directory entry.')\n return toNode(location.mount_id, entry)\n })\n return result.truncated ? { nodes, truncated: true } : nodes\n }\n return {\n mounts: !root,\n mountOf(path) {\n try {\n return encodePlatformPickerPath(decodePlatformPickerPath(path).mountId)\n } catch {\n return undefined // initialPath is caller input; the listing reports a bad one.\n }\n },\n invalidate,\n listRoot: async (request) => read('', request, begin(request)),\n listChildren: async (path, request) => read(path, request, begin(request)),\n async search(query, scope, request) {\n // The mount list itself has no server to search, so it keeps the walk.\n if (transport.search && (scope.path || root)) return serverSearch(query, scope, request)\n const token = begin(request)\n const matches: PickerNode[] = []\n const queue = [scope.path]\n const visited = new Set<string>()\n const needle = query.toLowerCase()\n let currentRequest = request\n while (queue.length) {\n check(request, token)\n const path = queue.shift()!\n if (visited.has(path)) continue\n visited.add(path)\n if (visited.size > 500) return { nodes: matches, truncated: true }\n const nodes = await read(path, currentRequest, token)\n check(request, token)\n if (currentRequest?.refresh) {\n currentRequest = { ...currentRequest }\n delete currentRequest.refresh\n }\n for (const node of nodes) {\n if (node.name.toLowerCase().includes(needle)) matches.push(node)\n if (scope.recursive && node.kind === 'folder' && !node.unavailableReason)\n queue.push(node.path)\n }\n if (matches.length > 10000) return { nodes: matches.slice(0, 10000), truncated: true }\n }\n return matches\n },\n }\n}\n","import type { PickerAdapter } from '../types/filePicker'\nimport type {\n FileBrowserLocation,\n FileDirectoryListing,\n FileDirectoryTree,\n FileSearchResult,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport type { PickerRequest } from '../types/filePicker'\nimport { MintApiError } from '../apiErrors'\nimport { useApi } from './useApi'\nimport { getInjectedPlatformContext } from './platformContextHelpers'\nimport { createFilePickerAdapter, type FilePickerAdapterOptions } from './createFilePickerAdapter'\n\nexport { encodePlatformPickerPath, decodePlatformPickerPath } from './createFilePickerAdapter'\n\nfunction query(location: FileBrowserLocation, request?: PickerRequest): URLSearchParams {\n const params = new URLSearchParams({ mount_id: location.mount_id, path: location.path })\n if (request?.refresh) params.set('refresh', 'true')\n return params\n}\n\n/** Adapt the authenticated MINT mount API without exposing absolute host paths. */\nexport function usePlatformFilePickerAdapter(\n options: FilePickerAdapterOptions & { apiBaseUrl?: string } = {},\n): PickerAdapter {\n const api = useApi({\n baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl,\n })\n return createFilePickerAdapter({\n async listMounts(request) {\n try {\n const url = `/filesystem/mounts${request?.refresh ? '?refresh=true' : ''}`\n const { mounts } = await api.get<{ mounts: ServerMount[] }>(url, {\n signal: request?.signal,\n })\n return mounts\n } catch (cause) {\n if (cause instanceof MintApiError && cause.status === 404) return []\n throw cause\n }\n },\n browse(location, request) {\n return api.get<FileDirectoryListing>(`/filesystem/browse?${query(location, request)}`, {\n signal: request?.signal,\n })\n },\n tree(location, request) {\n return api.get<FileDirectoryTree>(`/filesystem/tree?${query(location, request)}`, {\n signal: request?.signal,\n })\n },\n search(location, text, recursive, request) {\n const params = query(location, request)\n params.set('q', text)\n params.set('recursive', String(recursive))\n return api.get<FileSearchResult>(`/filesystem/search?${params}`, { signal: request?.signal })\n },\n }, options)\n}\n","import {\n computed,\n getCurrentScope,\n onScopeDispose,\n ref,\n shallowRef,\n toValue,\n watch,\n type ComputedRef,\n type MaybeRefOrGetter,\n type Ref,\n} from 'vue'\n\nimport type {\n FileBrowserSortKey,\n FileDirectoryListing,\n FileEntry,\n FilePathCrumb,\n FileSelection,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport { MintApiError } from '../apiErrors'\nimport { useApi } from './useApi'\nimport { getInjectedPlatformContext } from './platformContextHelpers'\nimport { useRequest } from './useRequest'\n\nexport interface UseFileBrowserOptions {\n /** Extensions the caller cares about; entries are flagged, never removed. */\n typeRules?: MaybeRefOrGetter<string[] | undefined>\n /** Mount to open first; defaults to the first available one. */\n initialMountId?: MaybeRefOrGetter<string | undefined>\n /** Override the platform API base URL (standalone plugin dev). */\n apiBaseUrl?: string\n}\n\nexport interface UseFileBrowserReturn {\n /** Every configured mount, offline ones included. */\n mounts: Ref<ServerMount[]>\n /** Mount the current listing belongs to. */\n mountId: Ref<string>\n /** Mount-relative path of the current listing. */\n path: Ref<string>\n /** Parent path, or null at a mount root. */\n parent: Ref<string | null>\n entries: Ref<FileEntry[]>\n breadcrumbs: Ref<FilePathCrumb[]>\n /** Directory size before any search filter. */\n totalCount: Ref<number | null>\n /** How many entries satisfy the type rules. */\n matchCount: Ref<number | null>\n /** How many rows this listing carries, after search and any cap. */\n shownCount: Ref<number | null>\n /** True when the server capped the listing. */\n truncated: Ref<boolean>\n search: Ref<string>\n sort: Ref<FileBrowserSortKey>\n /** Paths the user has picked, keyed by mount and path. */\n selected: Ref<FileSelection[]>\n isLoading: ComputedRef<boolean>\n error: Ref<string | null>\n /** Load the mount list and open the first available mount. */\n init: () => Promise<void>\n /** Open a directory, or a mount root when path is empty. */\n navigate: (location: { mount_id: string; path: string }) => Promise<void>\n /** Re-read the current directory. */\n refresh: () => Promise<void>\n /** Take the directory currently being viewed as the input. */\n selectCurrentFolder: () => void\n clear: () => void\n}\n\n/**\n * Browses the platform's `/api/filesystem` routes for a custom file UI. For the\n * ready-made dialog, use `FilePicker` with `usePlatformFilePickerAdapter()`.\n *\n * Requires the `filesystem.browse` permission and a deployment that declares\n * mounts; with none configured the routes are absent and `mounts` stays empty.\n */\nexport function useFileBrowser(options: UseFileBrowserOptions = {}): UseFileBrowserReturn {\n const injectedContext = getInjectedPlatformContext()\n const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl })\n const sync = useRequest('Could not read the server mounts.')\n\n const mounts = ref<ServerMount[]>([])\n const mountId = shallowRef('')\n const path = shallowRef('')\n const parent = ref<string | null>(null)\n const entries = ref<FileEntry[]>([])\n const breadcrumbs = ref<FilePathCrumb[]>([])\n const totalCount = ref<number | null>(null)\n const matchCount = ref<number | null>(null)\n const shownCount = ref<number | null>(null)\n const truncated = ref(false)\n const search = shallowRef('')\n const sort = shallowRef<FileBrowserSortKey>('name')\n const selected = ref<FileSelection[]>([])\n\n const mountsLoading = shallowRef(false)\n const listingLoading = shallowRef(false)\n const listingReady = shallowRef(false)\n const isLoading = computed(() => mountsLoading.value || listingLoading.value)\n let listToken = 0\n let mountsToken = 0\n let disposed = false\n let listingController: AbortController | undefined\n let mountsController: AbortController | undefined\n let searchTimer: ReturnType<typeof setTimeout> | undefined\n\n function resetListing(): void {\n listingReady.value = false\n entries.value = []\n breadcrumbs.value = []\n parent.value = null\n totalCount.value = null\n matchCount.value = null\n shownCount.value = null\n truncated.value = false\n }\n\n function typeRuleParams(): string {\n const rules = toValue(options.typeRules) ?? []\n return rules.map((rule) => `&type_rules=${encodeURIComponent(rule)}`).join('')\n }\n\n function fail(cause: unknown, fallback: string): void {\n sync.setError(new Error(sync.readErrorMessage(cause, fallback)), fallback)\n }\n\n function statusOf(cause: unknown): number | undefined {\n return cause instanceof MintApiError ? cause.status : undefined\n }\n\n async function loadMounts(): Promise<boolean> {\n const token = ++mountsToken\n mountsController?.abort()\n mountsController = new AbortController()\n mountsLoading.value = true\n try {\n const data = await api.get<{ mounts: ServerMount[] }>('/filesystem/mounts', {\n signal: mountsController.signal,\n })\n if (disposed || token !== mountsToken) return false\n mounts.value = data.mounts ?? []\n sync.clearError()\n return true\n } catch (cause) {\n if (disposed || token !== mountsToken) return false\n // The platform only registers these routes once mounts are configured, so\n // a 404 means \"none declared\" — an empty sidebar, not a failure.\n if (statusOf(cause) === 404) {\n mounts.value = []\n sync.clearError()\n return true\n }\n mounts.value = []\n fail(cause, 'Could not read the server mounts.')\n return false\n } finally {\n if (token === mountsToken) mountsLoading.value = false\n }\n }\n\n async function navigate(location: { mount_id: string; path: string }, refresh = false): Promise<void> {\n if (disposed || !location.mount_id) return\n clearTimeout(searchTimer)\n const token = ++listToken\n listingController?.abort()\n listingController = new AbortController()\n listingLoading.value = true\n mountId.value = location.mount_id\n path.value = location.path\n resetListing()\n sync.clearError()\n try {\n const query =\n `mount_id=${encodeURIComponent(location.mount_id)}` +\n `&path=${encodeURIComponent(location.path)}` +\n `&sort=${sort.value}` +\n (refresh ? '&refresh=true' : '') +\n (search.value ? `&search=${encodeURIComponent(search.value)}` : '') +\n typeRuleParams()\n const listing = await api.get<FileDirectoryListing>(`/filesystem/browse?${query}`, {\n signal: listingController.signal,\n })\n if (disposed || token !== listToken) return\n mountId.value = listing.mount_id\n path.value = listing.path\n parent.value = listing.parent ?? null\n entries.value = listing.entries ?? []\n breadcrumbs.value = listing.breadcrumbs ?? []\n totalCount.value = listing.total_count ?? null\n matchCount.value = listing.match_count ?? null\n shownCount.value = listing.shown_count ?? listing.entries?.length ?? null\n truncated.value = listing.truncated ?? false\n listingReady.value = true\n sync.clearError()\n } catch (cause) {\n if (disposed || token !== listToken) return\n entries.value = []\n fail(cause, 'Could not read that folder.')\n } finally {\n if (token === listToken) listingLoading.value = false\n }\n }\n\n async function init(): Promise<void> {\n if (disposed) return\n const navigationToken = listToken\n if (!(await loadMounts()) || disposed || navigationToken !== listToken) return\n const preferred = toValue(options.initialMountId)\n // An offline mount is not navigable, so a remembered preference for one\n // falls through to the first reachable mount instead of opening on an error.\n const reachable = (mount: ServerMount): boolean => mount.available !== false\n const target =\n mounts.value.find((mount) => mount.id === preferred && reachable(mount)) ??\n mounts.value.find(reachable)\n if (target) await navigate({ mount_id: target.id, path: '' })\n else {\n mountId.value = ''\n path.value = ''\n resetListing()\n }\n }\n\n async function refresh(): Promise<void> {\n if (disposed) return\n if (mountId.value) await navigate({ mount_id: mountId.value, path: path.value }, true)\n else await init()\n }\n\n function selectCurrentFolder(): void {\n if (!mountId.value || !listingReady.value || isLoading.value || sync.error.value) return\n const crumb = breadcrumbs.value[breadcrumbs.value.length - 1]\n const key = JSON.stringify([mountId.value, path.value])\n if (selected.value.some((item) => JSON.stringify([item.mount_id, item.path]) === key)) return\n selected.value = [\n ...selected.value,\n {\n mount_id: mountId.value,\n path: path.value,\n name: crumb?.label ?? path.value,\n kind: 'directory',\n child_count: totalCount.value,\n },\n ]\n }\n\n function clear(): void {\n selected.value = []\n }\n\n // Both controls are server-side, so they only take effect on a re-fetch.\n // Without these watchers the search box and sort button look live but are\n // inert until the user happens to navigate.\n watch(search, () => {\n if (!mountId.value) return\n clearTimeout(searchTimer)\n // Debounced: search changes on every keystroke.\n searchTimer = setTimeout(() => {\n void navigate({ mount_id: mountId.value, path: path.value })\n }, 250)\n })\n\n watch(sort, () => {\n if (mountId.value) void navigate({ mount_id: mountId.value, path: path.value })\n })\n\n watch(\n () => toValue(options.typeRules),\n () => {\n if (mountId.value) void navigate({ mount_id: mountId.value, path: path.value })\n },\n { deep: true },\n )\n\n if (getCurrentScope())\n onScopeDispose(() => {\n disposed = true\n ++listToken\n ++mountsToken\n clearTimeout(searchTimer)\n listingController?.abort()\n mountsController?.abort()\n })\n\n return {\n mounts,\n mountId,\n path,\n parent,\n entries,\n breadcrumbs,\n totalCount,\n matchCount,\n shownCount,\n truncated,\n search,\n sort,\n selected,\n isLoading,\n error: sync.error,\n init,\n navigate,\n refresh,\n selectCurrentFolder,\n clear,\n }\n}\n","import type { AnalysisArtifactTarget } from '../types/analysisArtifactTypes'\n\nconst EXPERIMENT_ID = 'experiment_id'\nconst ARTIFACT_PLUGIN_ID = 'artifact_plugin_id'\nconst ARTIFACT_KEY = 'artifact_key'\nconst ARTIFACT_ID = 'artifact_id'\n\nfunction parsePositiveInteger(value: string | null): number | undefined {\n if (value === null || !/^\\d+$/.test(value)) return undefined\n const parsed = Number(value)\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined\n}\n\n/** Build the canonical query used to open one analysis artifact in a plugin. */\nexport function buildAnalysisArtifactTargetQuery(\n target: AnalysisArtifactTarget,\n): URLSearchParams {\n const query = new URLSearchParams()\n query.set(EXPERIMENT_ID, String(target.experimentId))\n query.set(ARTIFACT_PLUGIN_ID, target.artifactPluginId)\n query.set(ARTIFACT_KEY, target.artifactKey)\n if (target.artifactId !== undefined) {\n query.set(ARTIFACT_ID, String(target.artifactId))\n }\n return query\n}\n\n/** Parse a complete canonical target. Incomplete or invalid input is not guessed. */\nexport function parseAnalysisArtifactTarget(\n query: URLSearchParams,\n): AnalysisArtifactTarget | null {\n const experimentId = parsePositiveInteger(query.get(EXPERIMENT_ID))\n const artifactPluginId = query.get(ARTIFACT_PLUGIN_ID)?.trim()\n const artifactKey = query.get(ARTIFACT_KEY)?.trim()\n const rawArtifactId = query.get(ARTIFACT_ID)\n const artifactId = parsePositiveInteger(rawArtifactId)\n\n if (\n experimentId === undefined\n || !artifactPluginId\n || !artifactKey\n || (rawArtifactId !== null && artifactId === undefined)\n ) {\n return null\n }\n\n return {\n experimentId,\n artifactPluginId,\n artifactKey,\n ...(artifactId === undefined ? {} : { artifactId }),\n }\n}\n","// Experiment, artifact, file and job data components (`@morscherlab/mint-sdk/data`).\n\n// Experiment / analysis components\nexport { default as ExperimentSelectorModal } from './components/ExperimentSelectorModal.vue'\nexport { default as ArtifactSelectorModal } from './components/ArtifactSelectorModal.vue'\nexport { default as ArtifactSaveDialog } from './components/ArtifactSaveDialog.vue'\nexport { default as ExperimentPopover } from './components/ExperimentPopover.vue'\nexport { default as ExperimentCodeBadge } from './components/ExperimentCodeBadge.vue'\nexport { default as FilePicker } from './components/FilePicker.vue'\n\n// Workflow components\nexport { default as AuditTrail } from './components/AuditTrail.vue'\nexport { default as BatchProgressList } from './components/BatchProgressList.vue'\nexport { default as JobsStatusTray } from './components/JobsStatusTray.vue'\n\n// Form components\nexport { default as TagsInput } from './components/TagsInput.vue'\nexport { default as SecretInput } from './components/SecretInput.vue'\n\n// Versioned save (RFC #439): the 412 conflict dialog and the stale-form banner\nexport { default as VersionConflictDialog } from './components/VersionConflictDialog.vue'\nexport { default as StaleFormBanner } from './components/StaleFormBanner.vue'\nexport {\n useVersionedSave,\n fieldNameLabel,\n type StaleChange,\n type UseVersionedSaveOptions,\n type UseVersionedSaveReturn,\n type VersionConflict,\n} from './composables/useVersionedSave'\n\n// Experiment and artifact data\nexport {\n useExperimentData,\n type UseExperimentDataOptions,\n type UseExperimentDataReturn,\n} from './composables/useExperimentData'\nexport {\n useArtifacts,\n ANALYSIS_FILE_ARTIFACT_SCHEMA,\n type UseArtifactsOptions,\n type UseArtifactsReturn,\n} from './composables/useArtifacts'\nexport { experimentIdFromRoute } from './composables/experiment-utils'\nexport * as experimentStatus from './utils/experimentStatus'\nexport * as pluginSecret from './utils/pluginSecrets'\nexport type { PluginSecretRef, PluginSecretState } from './utils/pluginSecrets'\n\n// Text import and search\nexport {\n parseDelimitedText,\n type DelimitedTable,\n type ParseDelimitedTextOptions,\n} from './composables/useFileImport'\nexport {\n candidateMatchesSearch,\n normalizeSearchQuery,\n useTextSearch,\n type TextSearchCandidate,\n type TextSearchSource,\n type UseTextSearchOptions,\n type UseTextSearchReturn,\n} from './composables/useTextSearch'\n\n// File picker adapters\nexport {\n usePlatformFilePickerAdapter,\n encodePlatformPickerPath,\n decodePlatformPickerPath,\n} from './composables/usePlatformFilePickerAdapter'\nexport {\n createFilePickerAdapter,\n type FilePickerTransport,\n type FilePickerAdapterOptions,\n} from './composables/createFilePickerAdapter'\n\n// Experiment, sample and file helpers\nexport {\n useExperimentSelector,\n type UseExperimentSelectorOptions,\n type UseExperimentSelectorReturn,\n} from './composables/useExperimentSelector'\nexport {\n useFileBrowser,\n type UseFileBrowserOptions,\n type UseFileBrowserReturn,\n} from './composables/useFileBrowser'\nexport {\n buildAnalysisArtifactTargetQuery,\n parseAnalysisArtifactTarget,\n} from './composables/analysisArtifactTarget'\nexport {\n formatExperimentDate,\n resolveExperimentCode,\n datePresetToISO,\n DATE_PRESET_OPTIONS,\n SORT_OPTIONS,\n} from './composables/experiment-utils'\nexport {\n extractSampleNamesFromDesignData,\n extractSampleOptionsFromDesignData,\n unwrapExperimentDesignData,\n type ExperimentDesignDataResponse,\n type ExtractExperimentSamplesOptions,\n} from './composables/experimentDesignData'\nexport {\n useExperimentSamples,\n type ExperimentDesignDataSource,\n type ExperimentIdSource,\n type UseExperimentSamplesOptions,\n type UseExperimentSamplesReturn,\n} from './composables/useExperimentSamples'\nexport {\n useAppExperiment,\n APP_EXPERIMENT_KEY,\n type UseAppExperimentOptions,\n type UseAppExperimentReturn,\n type AppExperimentState,\n type AppExperimentPopoverBinding,\n type AppExperimentSelectorModalBinding,\n} from './composables/useAppExperiment'\nexport {\n detectDelimiter,\n readFileAsText,\n useFileImport,\n validateImportFile,\n type FileImportOptions,\n type FileImportValidationOptions,\n type UseFileImportReturn,\n} from './composables/useFileImport'\n"],"mappings":"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"}