@parallelworks/ui 0.18.0 → 0.19.1

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 (349) hide show
  1. package/README.md +86 -6
  2. package/dist/ai/adapter/memoryStore.d.ts +8 -0
  3. package/dist/ai/adapter/memoryStore.js +152 -0
  4. package/dist/ai/adapter/mock/index.d.ts +2 -0
  5. package/dist/ai/adapter/mock/index.js +3 -0
  6. package/dist/ai/adapter/mock/mockAdapter.d.ts +4 -0
  7. package/dist/ai/adapter/mock/mockAdapter.js +81 -0
  8. package/dist/ai/adapter/openai/createOpenAIStream.d.ts +9 -0
  9. package/dist/ai/adapter/openai/createOpenAIStream.js +33 -0
  10. package/dist/ai/adapter/openai/errors.d.ts +3 -0
  11. package/dist/ai/adapter/openai/errors.js +37 -0
  12. package/dist/ai/adapter/openai/index.d.ts +6 -0
  13. package/dist/ai/adapter/openai/index.js +6 -0
  14. package/dist/ai/adapter/openai/openaiAdapter.d.ts +9 -0
  15. package/dist/ai/adapter/openai/openaiAdapter.js +54 -0
  16. package/dist/ai/adapter/openai/responses.d.ts +4 -0
  17. package/dist/ai/adapter/openai/responses.js +108 -0
  18. package/dist/ai/adapter/openai/sse.d.ts +24 -0
  19. package/dist/ai/adapter/openai/sse.js +112 -0
  20. package/dist/ai/adapter/openai/wire.d.ts +40 -0
  21. package/dist/ai/adapter/types.d.ts +131 -0
  22. package/dist/ai/adapter/types.js +11 -0
  23. package/dist/ai/agent/classifier.d.ts +2 -0
  24. package/dist/ai/agent/classifier.js +12 -0
  25. package/dist/ai/agent/diff.d.ts +35 -0
  26. package/dist/ai/agent/diff.js +223 -0
  27. package/dist/ai/agent/diffHighlight.d.ts +6 -0
  28. package/dist/ai/agent/diffHighlight.js +101 -0
  29. package/dist/ai/agent/index.d.ts +6 -0
  30. package/dist/ai/agent/permissions.d.ts +11 -0
  31. package/dist/ai/agent/permissions.js +29 -0
  32. package/dist/ai/agent/toolfmt.d.ts +18 -0
  33. package/dist/ai/agent/toolfmt.js +383 -0
  34. package/dist/ai/agent/transcript.d.ts +38 -0
  35. package/dist/ai/agent/transcript.js +127 -0
  36. package/dist/ai/ai.css +325 -0
  37. package/dist/ai/components/AllocationSelector.d.ts +4 -0
  38. package/dist/ai/components/AllocationSelector.js +56 -0
  39. package/dist/ai/components/AttachmentManager.d.ts +1 -0
  40. package/dist/ai/components/AttachmentManager.js +144 -0
  41. package/dist/ai/components/AttachmentUpload.d.ts +24 -0
  42. package/dist/ai/components/AttachmentUpload.js +179 -0
  43. package/dist/ai/components/BlockedGroupBanner.d.ts +1 -0
  44. package/dist/ai/components/BlockedGroupBanner.js +29 -0
  45. package/dist/ai/components/BranchNavigator.d.ts +8 -0
  46. package/dist/ai/components/BranchNavigator.js +57 -0
  47. package/dist/ai/components/ChatEmptyState.d.ts +3 -0
  48. package/dist/ai/components/ChatEmptyState.js +171 -0
  49. package/dist/ai/components/ChatInput.d.ts +45 -0
  50. package/dist/ai/components/ChatInput.js +265 -0
  51. package/dist/ai/components/ChatLayout.d.ts +5 -0
  52. package/dist/ai/components/ChatLayout.js +15 -0
  53. package/dist/ai/components/ChatMessage.d.ts +24 -0
  54. package/dist/ai/components/ChatMessage.js +338 -0
  55. package/dist/ai/components/ChatMessageList.d.ts +35 -0
  56. package/dist/ai/components/ChatMessageList.js +301 -0
  57. package/dist/ai/components/ChatSidebar.d.ts +5 -0
  58. package/dist/ai/components/ChatSidebar.js +345 -0
  59. package/dist/ai/components/ChatThread.d.ts +3 -0
  60. package/dist/ai/components/ChatThread.js +198 -0
  61. package/dist/ai/components/ComposerChrome.d.ts +18 -0
  62. package/dist/ai/components/ComposerChrome.js +50 -0
  63. package/dist/ai/components/ComposerFrame.d.ts +16 -0
  64. package/dist/ai/components/ComposerFrame.js +33 -0
  65. package/dist/ai/components/DragOverlay.d.ts +5 -0
  66. package/dist/ai/components/DragOverlay.js +20 -0
  67. package/dist/ai/components/ModelSelector.d.ts +6 -0
  68. package/dist/ai/components/ModelSelector.js +343 -0
  69. package/dist/ai/components/ShareDialog.d.ts +11 -0
  70. package/dist/ai/components/ShareDialog.js +260 -0
  71. package/dist/ai/components/SlashMenu.d.ts +28 -0
  72. package/dist/ai/components/SlashMenu.js +91 -0
  73. package/dist/ai/components/agent/ActivityLine.d.ts +21 -0
  74. package/dist/ai/components/agent/ActivityLine.js +151 -0
  75. package/dist/ai/components/agent/AgentMessageParts.d.ts +54 -0
  76. package/dist/ai/components/agent/AgentMessageParts.js +721 -0
  77. package/dist/ai/components/agent/PermissionPicker.d.ts +11 -0
  78. package/dist/ai/components/agent/PermissionPicker.js +38 -0
  79. package/dist/ai/components/attachmentKeys.d.ts +4 -0
  80. package/dist/ai/components/attachmentKeys.js +10 -0
  81. package/dist/ai/core/ChatProvider.d.ts +66 -0
  82. package/dist/ai/core/ChatProvider.js +848 -0
  83. package/dist/ai/core/KeyboardShortcutsProvider.d.ts +5 -0
  84. package/dist/ai/core/KeyboardShortcutsProvider.js +9 -0
  85. package/dist/ai/core/chatReducer.d.ts +114 -0
  86. package/dist/ai/core/chatReducer.js +223 -0
  87. package/dist/ai/core/config.d.ts +47 -0
  88. package/dist/ai/core/config.js +45 -0
  89. package/dist/ai/core/events.d.ts +1 -0
  90. package/dist/ai/core/events.js +4 -0
  91. package/dist/ai/core/exportConversation.d.ts +4 -0
  92. package/dist/ai/core/exportConversation.js +42 -0
  93. package/dist/ai/core/greeting.d.ts +3 -0
  94. package/dist/ai/core/greeting.js +38 -0
  95. package/dist/ai/core/parts.d.ts +11 -0
  96. package/dist/ai/core/parts.js +84 -0
  97. package/dist/ai/core/useDragDrop.d.ts +14 -0
  98. package/dist/ai/core/useDragDrop.js +36 -0
  99. package/dist/ai/core/useKeyboardShortcuts.d.ts +12 -0
  100. package/dist/ai/core/useKeyboardShortcuts.js +77 -0
  101. package/dist/ai/core/useSSEStream.d.ts +34 -0
  102. package/dist/ai/core/useSSEStream.js +98 -0
  103. package/dist/ai/index.d.ts +40 -0
  104. package/dist/ai/index.js +44 -0
  105. package/dist/ai/strings.d.ts +167 -0
  106. package/dist/ai/strings.js +175 -0
  107. package/dist/ai/types.d.ts +236 -0
  108. package/dist/ai/ui/Avatar.d.ts +8 -0
  109. package/dist/ai/ui/Avatar.js +35 -0
  110. package/dist/ai/ui/Dropdown.d.ts +31 -0
  111. package/dist/ai/ui/Dropdown.js +110 -0
  112. package/dist/ai/ui/Markdown.d.ts +8 -0
  113. package/dist/ai/ui/Markdown.js +79 -0
  114. package/dist/ai/ui/Notice.d.ts +21 -0
  115. package/dist/ai/ui/Notice.js +77 -0
  116. package/dist/ai/ui/buttons.d.ts +1 -0
  117. package/dist/ai/ui/codeBreaks.d.ts +16 -0
  118. package/dist/ai/ui/codeBreaks.js +36 -0
  119. package/dist/ai/ui/prose.d.ts +1 -0
  120. package/dist/ai/ui/prose.js +13 -0
  121. package/dist/ai/ui/sidebar.d.ts +81 -0
  122. package/dist/ai/ui/sidebar.js +148 -0
  123. package/dist/ai/utils.d.ts +4 -0
  124. package/dist/ai/utils.js +23 -0
  125. package/dist/assets/{editor.worker-CuBRIwi-.js → editor.worker-BmHUIVG4.js} +194 -163
  126. package/dist/assets/{json.worker-8ytS02vz.js → json.worker-D8bJ92nw.js} +384 -338
  127. package/dist/assets/{yaml.worker-DJC-VUcv.js → yaml.worker-C4gyGAko.js} +15657 -13823
  128. package/dist/components/AddMenu.js +46 -0
  129. package/dist/components/Avatar.d.ts +1 -1
  130. package/dist/components/Avatar.js +46 -0
  131. package/dist/{chunks/BareModal-BtRiQezg.js → components/BareModal.js} +1 -1
  132. package/dist/components/Breadcrumbs.d.ts +1 -1
  133. package/dist/components/Breadcrumbs.js +83 -0
  134. package/dist/components/Button.js +83 -0
  135. package/dist/components/Callout.js +23 -0
  136. package/dist/components/Card.js +31 -0
  137. package/dist/components/CollapsiblePanel.js +77 -0
  138. package/dist/components/ConfirmModal.js +74 -0
  139. package/dist/components/CopyToClipboard.js +81 -0
  140. package/dist/{chunks/CreateModal-BY6DLmur.js → components/CreateModal.js} +10 -189
  141. package/dist/components/DescriptionList.js +42 -0
  142. package/dist/components/Dropdown.d.ts +1 -1
  143. package/dist/components/Dropdown.js +349 -0
  144. package/dist/components/EmptyState.d.ts +1 -1
  145. package/dist/components/EmptyState.js +27 -0
  146. package/dist/components/Filter.js +32 -0
  147. package/dist/components/FilterPill.js +13 -0
  148. package/dist/components/IconButton.js +34 -0
  149. package/dist/components/Indicator.d.ts +2 -2
  150. package/dist/{chunks/Indicator-hBSIxOPK.js → components/Indicator.js} +1 -1
  151. package/dist/components/Input.d.ts +1 -1
  152. package/dist/components/Input.js +88 -0
  153. package/dist/components/LetterBadge.d.ts +1 -1
  154. package/dist/components/LetterBadge.js +13 -0
  155. package/dist/components/Listbox.js +123 -0
  156. package/dist/{chunks/Loader-BXoXTZGc.js → components/Loader.js} +1 -1
  157. package/dist/components/Markdown.js +15 -0
  158. package/dist/components/MarkdownImpl.d.ts +1 -1
  159. package/dist/components/MultiSelect.js +171 -0
  160. package/dist/components/Overview.js +39 -0
  161. package/dist/components/PageHeader.js +194 -0
  162. package/dist/components/Pagination.js +35 -0
  163. package/dist/components/Provider.d.ts +16 -65
  164. package/dist/{chunks/Provider-Ghua9PkN.js → components/Provider.js} +54 -20
  165. package/dist/{chunks/RequiredMark-DfLdYbYZ.js → components/RequiredMark.js} +1 -1
  166. package/dist/components/SectionHeader.js +11 -0
  167. package/dist/components/SettingModeOption.js +30 -0
  168. package/dist/components/SettingRow.d.ts +1 -1
  169. package/dist/components/SettingRow.js +35 -0
  170. package/dist/components/SettingsCard.js +31 -0
  171. package/dist/components/SettingsGroup.d.ts +1 -1
  172. package/dist/components/SettingsGroup.js +17 -0
  173. package/dist/components/Sort.js +42 -0
  174. package/dist/components/StatusBadge.js +63 -0
  175. package/dist/components/SwitchToggle.js +59 -0
  176. package/dist/{chunks/SwitchToggleSmall-FcsaYSTs.js → components/SwitchToggleSmall.js} +1 -1
  177. package/dist/{chunks/Table-4OHjdKdO.js → components/Table.js} +7 -5
  178. package/dist/components/Toggle.js +20 -0
  179. package/dist/{chunks/Tooltip-Crxo_eU_.js → components/Tooltip.js} +6 -4
  180. package/dist/{chunks/StatusBadge-C1Blx_cW.js → components/dropdownUtils.js} +2 -63
  181. package/dist/components/focus.d.ts +2 -0
  182. package/dist/components/focus.js +7 -0
  183. package/dist/components/ghostButton.js +7 -0
  184. package/dist/components/inputClasses.js +4 -0
  185. package/dist/components/keys.d.ts +12 -0
  186. package/dist/components/keys.js +27 -0
  187. package/dist/components/relativeTime.js +32 -0
  188. package/dist/{chunks/useCssIsDark-DHr05dG2.js → components/useCssIsDark.js} +1 -1
  189. package/dist/duration.d.ts +3 -0
  190. package/dist/duration.js +17 -0
  191. package/dist/editor/Editor.js +13 -0
  192. package/dist/editor/EditorField.js +53 -0
  193. package/dist/editor/FormCodePanel.js +173 -0
  194. package/dist/editor/Monaco.d.ts +1 -20
  195. package/dist/editor/Monaco.js +112 -0
  196. package/dist/editor/index.d.ts +2 -1
  197. package/dist/editor/index.js +7 -2
  198. package/dist/editor/themes.js +91 -0
  199. package/dist/editor/workers.js +29 -0
  200. package/dist/editor/yaml.d.ts +5 -0
  201. package/dist/editor/yaml.js +15 -0
  202. package/dist/engine.d.ts +98 -0
  203. package/dist/file-explorer/FileExplorer.d.ts +11 -2
  204. package/dist/file-explorer/FileExplorer.js +1402 -0
  205. package/dist/file-explorer/FileExplorerContext.d.ts +1 -1
  206. package/dist/file-explorer/FileExplorerContext.js +370 -0
  207. package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +1 -1
  208. package/dist/{chunks/CodePreviewEditor-4lx3kE-w.js → file-explorer/FilePreview/CodePreviewEditor.js} +3 -2
  209. package/dist/file-explorer/FilePreview/Inline.js +45 -0
  210. package/dist/file-explorer/FilePreview/StreamedBadge.js +16 -0
  211. package/dist/file-explorer/FilePreview/Viewer.js +526 -0
  212. package/dist/file-explorer/FilePreview/index.js +266 -0
  213. package/dist/file-explorer/FilePreview/parse.js +63 -0
  214. package/dist/file-explorer/FilePreview/previewCache.js +98 -0
  215. package/dist/file-explorer/FilePreview/previewType.d.ts +1 -1
  216. package/dist/file-explorer/FilePreview/previewType.js +151 -0
  217. package/dist/file-explorer/FilePreview/readCappedBytes.js +196 -0
  218. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +95 -0
  219. package/dist/file-explorer/FilePreview/useObjectText.js +109 -0
  220. package/dist/file-explorer/FilePreview/usePresignedUrl.js +73 -0
  221. package/dist/file-explorer/FileRow.d.ts +1 -1
  222. package/dist/file-explorer/FileRow.js +165 -0
  223. package/dist/file-explorer/FileRowsWindow.js +92 -0
  224. package/dist/file-explorer/FileTable.js +172 -0
  225. package/dist/file-explorer/FileTableHeaders.js +49 -0
  226. package/dist/file-explorer/LoadMoreRow.js +46 -0
  227. package/dist/file-explorer/TreeView.js +376 -0
  228. package/dist/file-explorer/helpers.d.ts +4 -4
  229. package/dist/file-explorer/helpers.js +537 -0
  230. package/dist/file-explorer/index.d.ts +3 -3
  231. package/dist/file-explorer/index.js +7 -5705
  232. package/dist/file-explorer/lib/batch.js +39 -0
  233. package/dist/file-explorer/lib/errors.js +9 -0
  234. package/dist/file-explorer/lib/paging.js +124 -0
  235. package/dist/file-explorer/lib/types.d.ts +6 -1
  236. package/dist/file-explorer/lib/utils.d.ts +1 -3
  237. package/dist/file-explorer/lib/utils.js +322 -0
  238. package/dist/file-explorer/toasts.js +400 -0
  239. package/dist/file-explorer/treeRows.js +73 -0
  240. package/dist/file-explorer/useNearEnd.d.ts +1 -1
  241. package/dist/file-explorer/useNearEnd.js +27 -0
  242. package/dist/form/FieldWrapper.d.ts +2 -2
  243. package/dist/form/FieldWrapper.js +54 -0
  244. package/dist/form/Fields/BooleanField.js +51 -0
  245. package/dist/form/Fields/CheckboxGroupField.js +72 -0
  246. package/dist/form/Fields/ColorField.js +33 -0
  247. package/dist/form/Fields/DropdownField.js +49 -0
  248. package/dist/form/Fields/DurationField.js +130 -0
  249. package/dist/form/Fields/LabelField.js +19 -0
  250. package/dist/form/Fields/MultiDropdownField.js +40 -0
  251. package/dist/form/Fields/NumberField.js +51 -0
  252. package/dist/form/Fields/ObjectField.js +54 -0
  253. package/dist/form/Fields/RadioField.js +49 -0
  254. package/dist/form/Fields/RangeField.js +58 -0
  255. package/dist/form/Fields/ResourceOptionIcon.d.ts +1 -1
  256. package/dist/form/Fields/ResourceOptionIcon.js +21 -0
  257. package/dist/form/Fields/StepField.js +6 -0
  258. package/dist/form/Fields/StringField.js +37 -0
  259. package/dist/form/Fields/TextAreaField.js +31 -0
  260. package/dist/form/Fields/durationValidity.js +11 -0
  261. package/dist/form/Form.d.ts +4 -21
  262. package/dist/form/Form.js +643 -0
  263. package/dist/form/SpecialTypes/DropdownWrapper.js +30 -0
  264. package/dist/form/SpecialTypes/FormikCustomDropdown.js +122 -0
  265. package/dist/form/SpecialTypes/FormikCustomInput.js +111 -0
  266. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +31 -0
  267. package/dist/form/SpecialTypes/FormikNumericInput.js +60 -0
  268. package/dist/form/Wizard/WizardContainer.js +138 -0
  269. package/dist/form/Wizard/WizardNavigation.js +92 -0
  270. package/dist/form/Wizard/WizardStepContent.js +35 -0
  271. package/dist/form/Wizard/WizardStepIndicator.js +108 -0
  272. package/dist/form/Wizard/useWizardState.js +118 -0
  273. package/dist/form/Wizard/utils.js +34 -0
  274. package/dist/form/fieldContext.js +26 -0
  275. package/dist/form/fieldRegistry.js +50 -0
  276. package/dist/form/index.d.ts +11 -12
  277. package/dist/form/index.js +18 -2445
  278. package/dist/form/lib.d.ts +6 -1
  279. package/dist/{chunks/FieldWrapper-Bef01EWO.js → form/lib.js} +5 -104
  280. package/dist/form/types/fieldComponentTypes.d.ts +3 -2
  281. package/dist/form/types/fieldTypes.d.ts +6 -20
  282. package/dist/form/useParsedOpts.d.ts +1 -1
  283. package/dist/form/useParsedOpts.js +62 -0
  284. package/dist/form/usePrevious.js +11 -0
  285. package/dist/form/utils/checkShowIfObject.js +14 -0
  286. package/dist/form/utils/{slurmDuration.d.ts → duration.d.ts} +2 -2
  287. package/dist/form/utils/duration.js +69 -0
  288. package/dist/form/utils/getParentValue.js +23 -0
  289. package/dist/form/utils/getPlaceholder.js +9 -0
  290. package/dist/form/utils/getValueUsingPath.js +23 -0
  291. package/dist/form/utils/secondaryField.js +22 -0
  292. package/dist/graph/AnnotationBanner.js +141 -0
  293. package/dist/graph/Collapse.js +41 -0
  294. package/dist/{workflow/index.js → graph/DependencyGraph.js} +76 -1163
  295. package/dist/graph/JobSummary.js +104 -0
  296. package/dist/{workflow → graph}/MatrixGroup.d.ts +2 -2
  297. package/dist/graph/MatrixGroup.js +107 -0
  298. package/dist/graph/Reveal.js +57 -0
  299. package/dist/graph/TreeView.js +711 -0
  300. package/dist/{workflow → graph}/index.d.ts +1 -1
  301. package/dist/graph/index.js +2 -0
  302. package/dist/{workflow → graph}/types.d.ts +3 -3
  303. package/dist/graph/types.js +6 -0
  304. package/dist/icons.d.ts +20 -21
  305. package/dist/index.d.ts +38 -40
  306. package/dist/index.js +47 -599
  307. package/dist/list/ActionToolbar.js +184 -0
  308. package/dist/list/HoverCard.js +73 -0
  309. package/dist/list/ListPage.js +145 -0
  310. package/dist/list/ListPager.js +59 -0
  311. package/dist/list/ListTable.d.ts +1 -1
  312. package/dist/list/ListTable.js +163 -0
  313. package/dist/list/ListViewControls.d.ts +2 -2
  314. package/dist/list/ListViewControls.js +372 -0
  315. package/dist/list/RowContextMenu.js +350 -0
  316. package/dist/list/UserHoverCard.js +117 -0
  317. package/dist/list/index.d.ts +9 -9
  318. package/dist/list/index.js +11 -2
  319. package/dist/list/listSearch.js +137 -0
  320. package/dist/list/listView.js +210 -0
  321. package/dist/list/textEntry.js +10 -0
  322. package/dist/logviewer/LogViewer.d.ts +1 -1
  323. package/dist/logviewer/LogViewer.js +750 -0
  324. package/dist/logviewer/index.d.ts +1 -0
  325. package/dist/logviewer/index.js +3 -742
  326. package/dist/logviewer/segments.d.ts +9 -0
  327. package/dist/logviewer/segments.js +80 -0
  328. package/dist/styles/fonts.css +4 -4
  329. package/dist/styles/theme.css +6 -4
  330. package/dist/styles.css +1 -1
  331. package/dist/theme/index.js +2 -2
  332. package/package.json +38 -14
  333. package/LICENSE +0 -202
  334. package/dist/chunks/Monaco-C1POOdVv.js +0 -1046
  335. package/dist/chunks/PageHeader-l49p1nM9.js +0 -690
  336. package/dist/chunks/SwitchToggle-CHPP8uZQ.js +0 -755
  337. package/dist/chunks/editor-9_uVtUyy.js +0 -347
  338. package/dist/chunks/list-qrojZZNv.js +0 -1411
  339. package/dist/editor/usesGitlab.d.ts +0 -97
  340. package/dist/form/Fields/BucketReadOnlyField.d.ts +0 -7
  341. package/dist/form/types/workflow-schema.d.ts +0 -1145
  342. package/dist/form/utils/pwResolver.d.ts +0 -18
  343. package/dist/{chunks/MarkdownImpl-Bq4QhrtA.js → components/MarkdownImpl.js} +1 -1
  344. /package/dist/{workflow → graph}/AnnotationBanner.d.ts +0 -0
  345. /package/dist/{workflow → graph}/Collapse.d.ts +0 -0
  346. /package/dist/{workflow → graph}/DependencyGraph.d.ts +0 -0
  347. /package/dist/{workflow → graph}/JobSummary.d.ts +0 -0
  348. /package/dist/{workflow → graph}/Reveal.d.ts +0 -0
  349. /package/dist/{workflow → graph}/TreeView.d.ts +0 -0
@@ -0,0 +1,1402 @@
1
+ import { useNotify, useSlots, useStrings } from "../components/Provider.js";
2
+ import { AccessIcon, AlertIcon, AngleDownIcon, ChevronRightIcon, ClipboardIcon, CollapseIcon, DownloadFileIcon, EyeIcon, FileIcon, FileTextIcon, FolderIcon, LinkIcon, RefreshIcon, ShareIcon, TrashIcon, UploadIcon } from "../icons.js";
3
+ import { keyedByContent } from "../components/keys.js";
4
+ import { TOOLTIP_ID, TooltipInfo } from "../components/Tooltip.js";
5
+ import { ConfirmModal } from "../components/ConfirmModal.js";
6
+ import CopyToClipboard, { CopyCodeBlock } from "../components/CopyToClipboard.js";
7
+ import { CreateModal } from "../components/CreateModal.js";
8
+ import Loader from "../components/Loader.js";
9
+ import { assembleRowMenu, useCopySubmenu, useRowMenu } from "../list/RowContextMenu.js";
10
+ import { StatusBadge } from "../components/StatusBadge.js";
11
+ import { Table } from "../components/Table.js";
12
+ import { ListActionsHeader, ListSkeleton, listTableFixedProps, listTableProps } from "../list/ListTable.js";
13
+ import { calculateUploadNodesTotalSize, createTreeBuilder, formatFileSize, getDirectoryNameError, getDirectoryObjectPath, getNodeChildren, getObjectKeyFromNode, getParentPath, getUploadNodesFromDataTransferItems, getUriScheme, pruneNodesInsideSelectedDirectories, removeLeadingAndTrailingSlashes } from "./lib/utils.js";
14
+ import { useFileExplorer } from "./FileExplorerContext.js";
15
+ import { PREVIEW_URL_EXPIRES_IN, canPreview } from "./FilePreview/previewType.js";
16
+ import { CorsError } from "./lib/errors.js";
17
+ import { clearPreviewCache } from "./FilePreview/previewCache.js";
18
+ import { DragDropZone, NoDataAvailablePreview, NoFileSelectedPreview, NotFoundPreview, SIDEBAR_WIDTH_STORAGE_KEY, ShareFileButton, getNodeIcon, renderUploadNodes } from "./helpers.js";
19
+ import { FilePreview } from "./FilePreview/index.js";
20
+ import { FilePreviewInline } from "./FilePreview/Inline.js";
21
+ import { UserRow } from "./FileRow.js";
22
+ import { FileTableHeaders } from "./FileTableHeaders.js";
23
+ import { FileTable } from "./FileTable.js";
24
+ import { advanceListing, beginLoadingMore, canAutoLoadMore, clearListingError, clearListingsUnder, hasMore, incompletePaths, isListed, isLoadingMore, loadMoreError, nextHydrationStep, recordListingError, replaceListing } from "./lib/paging.js";
25
+ import { TreeView } from "./TreeView.js";
26
+ import { Menu, MenuButton, MenuItem, MenuItems, MenuSeparator } from "@headlessui/react";
27
+ import cx from "classnames";
28
+ import { useCallback, useEffect, useEffectEvent, useLayoutEffect, useMemo, useRef, useState } from "react";
29
+ import { useLocalStorage } from "usehooks-ts";
30
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
31
+ import { DateTime } from "luxon";
32
+ import { Resizable } from "re-resizable";
33
+ //#region src/file-explorer/FileExplorer.tsx
34
+ function UriCard({ value }) {
35
+ const t = useStrings().fileExplorer;
36
+ const scheme = getUriScheme(value);
37
+ return /* @__PURE__ */ jsxs(CopyToClipboard, {
38
+ text: value,
39
+ showIcon: false,
40
+ className: "group flex items-stretch rounded-lg border theme-border bg-(--theme-input-bg) overflow-hidden cursor-copy hover:border-(--theme-link)/50 transition-colors duration-150",
41
+ children: [
42
+ scheme && /* @__PURE__ */ jsx("span", {
43
+ className: "shrink-0 inline-flex items-center px-3 bg-(--theme-muted-panel-bg) border-r theme-border font-mono text-[11px] font-semibold uppercase tracking-widest theme-muted-text",
44
+ children: scheme
45
+ }),
46
+ /* @__PURE__ */ jsx("span", {
47
+ className: "flex-1 min-w-0 inline-flex items-center px-3 py-2 font-mono text-[12.5px] leading-snug break-all text-(--theme-input)",
48
+ children: value
49
+ }),
50
+ /* @__PURE__ */ jsx("span", {
51
+ className: "shrink-0 inline-flex items-center justify-center px-3 border-l theme-border theme-muted-text group-hover:text-(--theme-link) group-hover:bg-(--theme-link)/5 transition-colors duration-150",
52
+ title: t.chrome.copyToClipboard,
53
+ children: /* @__PURE__ */ jsx(ClipboardIcon, { className: "h-3.5 w-3.5" })
54
+ })
55
+ ]
56
+ });
57
+ }
58
+ function isStorageDirectory(node) {
59
+ return node?.type === "directory" && !!node.storageId;
60
+ }
61
+ function getErrorMessage(error) {
62
+ return error instanceof Error ? error.message : String(error);
63
+ }
64
+ /** Directory paths from the storage root down to `path`. */
65
+ function getAncestorDirPaths(path) {
66
+ const isDir = path.endsWith("/");
67
+ const segments = (isDir ? path.slice(0, -1) : path).split("/").filter(Boolean);
68
+ const dirSegmentCount = isDir ? segments.length : segments.length - 1;
69
+ const ancestors = [];
70
+ for (let i = 1; i <= dirSegmentCount; i++) ancestors.push(segments.slice(0, i).join("/") + "/");
71
+ return ancestors;
72
+ }
73
+ /** The tree's width is the host's when the tree is embedded on its own, and this
74
+ * component's when it is showing the details pane beside it. */
75
+ function TreeFrame({ treeOnly, sidebarWidth, onWidthChange, children }) {
76
+ if (treeOnly) return /* @__PURE__ */ jsx("div", {
77
+ className: "h-full w-full min-w-0",
78
+ children
79
+ });
80
+ return /* @__PURE__ */ jsx(Resizable, {
81
+ size: {
82
+ width: sidebarWidth,
83
+ height: "100%"
84
+ },
85
+ minWidth: 200,
86
+ maxWidth: 800,
87
+ enable: {
88
+ top: false,
89
+ right: true,
90
+ bottom: false,
91
+ left: false,
92
+ topRight: false,
93
+ bottomRight: false,
94
+ bottomLeft: false,
95
+ topLeft: false
96
+ },
97
+ onResizeStop: (_e, _direction, _ref, d) => {
98
+ onWidthChange((prev) => prev + d.width);
99
+ },
100
+ children
101
+ });
102
+ }
103
+ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHierarchy = false, groups = [], groupsLoading = false, selectedPath, onPathChange, getProviderAndClient, treeOnly = false, initialExpandedPaths, onExpandedPathsChange, objectUris, downloadCommand }) {
104
+ const copySubmenu = useCopySubmenu();
105
+ const t = useStrings().fileExplorer;
106
+ const notify = useNotify();
107
+ const slots = useSlots();
108
+ const fileExplorerContext = useFileExplorer();
109
+ const [listings, setListings] = useState(() => /* @__PURE__ */ new Map());
110
+ const fetchesInFlightRef = useRef(/* @__PURE__ */ new Map());
111
+ const incompleteDirsRef = useRef(/* @__PURE__ */ new Set());
112
+ const incompleteDirs = useMemo(() => {
113
+ const next = incompletePaths(listings);
114
+ const previous = incompleteDirsRef.current;
115
+ if (previous.size === next.size && [...next].every((p) => previous.has(p))) return previous;
116
+ incompleteDirsRef.current = next;
117
+ return next;
118
+ }, [listings]);
119
+ const [buildTree] = useState(createTreeBuilder);
120
+ const { treeMap, parentToChildrenMap, rootNodes } = useMemo(() => buildTree(storages, {
121
+ listings,
122
+ forceUserHierarchy: showUserHierarchy,
123
+ unsortedParents: incompleteDirs
124
+ }), [
125
+ buildTree,
126
+ storages,
127
+ listings,
128
+ showUserHierarchy,
129
+ incompleteDirs
130
+ ]);
131
+ const [loadingPaths, setLoadingPaths] = useState(/* @__PURE__ */ new Set());
132
+ const normalizedSelectedPath = useMemo(() => {
133
+ if (!selectedPath) return "";
134
+ if (selectedPath.endsWith("/")) return selectedPath;
135
+ if (treeMap[selectedPath]) return selectedPath;
136
+ return `${selectedPath}/`;
137
+ }, [selectedPath, treeMap]);
138
+ const setSelectedPath = onPathChange;
139
+ const [expandedPaths, setExpandedPaths] = useState(() => /* @__PURE__ */ new Set(["", ...initialExpandedPaths ?? []]));
140
+ const onExpandedPathsChangeRef = useRef(onExpandedPathsChange);
141
+ onExpandedPathsChangeRef.current = onExpandedPathsChange;
142
+ useEffect(() => {
143
+ onExpandedPathsChangeRef.current?.([...expandedPaths]);
144
+ }, [expandedPaths]);
145
+ const selectedNode = useMemo(() => normalizedSelectedPath ? treeMap[normalizedSelectedPath] ?? null : null, [normalizedSelectedPath, treeMap]);
146
+ const seenRefreshKey = useRef(refreshKey);
147
+ useEffect(() => {
148
+ if (refreshKey === seenRefreshKey.current) return;
149
+ seenRefreshKey.current = refreshKey;
150
+ if (selectedNode?.type === "directory" && selectedNode.storageId) fetchNodeChildren(selectedNode, { replace: true });
151
+ }, [refreshKey, selectedNode]);
152
+ const [checkedItems, setCheckedItems] = useState(/* @__PURE__ */ new Set());
153
+ const [checkedItemsPath, setCheckedItemsPath] = useState(selectedPath);
154
+ if (checkedItemsPath !== selectedPath) {
155
+ setCheckedItemsPath(selectedPath);
156
+ setCheckedItems(/* @__PURE__ */ new Set());
157
+ }
158
+ const selectedNodeChildren = useMemo(() => selectedNode ? getNodeChildren(parentToChildrenMap, selectedNode.path) : [], [parentToChildrenMap, selectedNode]);
159
+ const storageCanWrite = (storageId) => storages.find((s) => s.id === storageId)?.canWrite === true;
160
+ const storageCanUpload = (storageId) => storages.find((s) => s.id === storageId)?.canUpload !== false;
161
+ const selectedStorage = storages.find((s) => s.id === selectedNode?.storageId);
162
+ const selectedNodeCanWrite = selectedStorage?.canWrite === true;
163
+ const selectedNodeCanUpload = selectedStorage?.canUpload !== false;
164
+ const selectedNodeCanShare = selectedStorage?.canShare !== false;
165
+ const selectedNodeCanManageAccess = selectedStorage?.canManageAccess !== false;
166
+ const [accessDrawerOpen, setAccessDrawerOpen] = useState(false);
167
+ const [connectionIssuesMessage, updateConnectionIssuesMessage] = useState(/* @__PURE__ */ new Map());
168
+ const [hasCorsIssue, setHasCorsIssue] = useState(/* @__PURE__ */ new Map());
169
+ const [uploadModalOpen, setUploadModalOpen] = useState(false);
170
+ const [createFolderTarget, setCreateFolderTarget] = useState(null);
171
+ const [pendingUpload, setPendingUpload] = useState(null);
172
+ const [waitForUpload, setWaitForUpload] = useState(false);
173
+ const fileInputRef = useRef(null);
174
+ const folderInputRef = useRef(null);
175
+ const [deleteModalOpen, setDeleteModalOpen] = useState(false);
176
+ const [pendingDelete, setPendingDelete] = useState(null);
177
+ const [previewNode, setPreviewNode] = useState(null);
178
+ const [previewOpen, setPreviewOpen] = useState(false);
179
+ const [detailTab, setDetailTab] = useState("details");
180
+ useEffect(() => clearPreviewCache, []);
181
+ const { openMenu, contextMenu } = useRowMenu();
182
+ const [sidebarWidth, setSidebarWidth] = useLocalStorage(SIDEBAR_WIDTH_STORAGE_KEY, 300);
183
+ const MAX_UPLOAD_SIZE = 85899345920;
184
+ const isUploadSizeExceeded = (size) => {
185
+ return size > MAX_UPLOAD_SIZE;
186
+ };
187
+ const handleToggleExpand = useCallback((path) => {
188
+ setExpandedPaths((prev) => {
189
+ const next = new Set(prev);
190
+ if (!next.delete(path)) next.add(path);
191
+ return next;
192
+ });
193
+ }, []);
194
+ const collapseAll = useCallback(() => {
195
+ setExpandedPaths(/* @__PURE__ */ new Set([""]));
196
+ }, []);
197
+ const getStorageProviderAndClient = async (storageId) => {
198
+ const storage = storages.find((s) => s.id === storageId);
199
+ if (!storage) {
200
+ notify.error(t.messages.storageUnavailable);
201
+ return;
202
+ }
203
+ const { provider, client } = await getProviderAndClient(storage).catch(() => ({
204
+ provider: null,
205
+ client: null
206
+ }));
207
+ if (!provider || !client) {
208
+ const message = t.messages.connectFailed;
209
+ updateConnectionIssuesMessage((prev) => new Map(prev).set(storage.id, message));
210
+ return null;
211
+ }
212
+ updateConnectionIssuesMessage((prev) => {
213
+ const next = new Map(prev);
214
+ next.delete(storage.id);
215
+ return next;
216
+ });
217
+ return {
218
+ storage,
219
+ provider,
220
+ client
221
+ };
222
+ };
223
+ const fetchNodeChildren = useEffectEvent(async (node, options) => {
224
+ const more = options?.more ?? false;
225
+ const silent = !more && (options?.silent ?? false);
226
+ const nodeStorageId = node.storageId ?? "";
227
+ if (!options?.replace && fetchesInFlightRef.current.has(node.path)) return { corsError: false };
228
+ const request = silent ? void 0 : {};
229
+ if (request) fetchesInFlightRef.current.set(node.path, request);
230
+ const owns = () => request !== void 0 && fetchesInFlightRef.current.get(node.path) === request;
231
+ if (more) setListings((prev) => beginLoadingMore(prev, node.path));
232
+ else if (request) setLoadingPaths((prev) => new Set(prev).add(node.path));
233
+ const cursor = more ? listings.get(node.path)?.cursor : void 0;
234
+ try {
235
+ const factory = await getStorageProviderAndClient(node.storageId);
236
+ if (!factory) throw new Error("Failed to connect to storage provider.");
237
+ const { storage, provider, client } = factory;
238
+ if (provider?.listDirectoryInput && provider?.convertDataToStorageObjects) {
239
+ const dir_path = node.root ? "" : node.relativePath ?? "";
240
+ const input = provider.listDirectoryInput(storage, dir_path, cursor ? { cursor } : {});
241
+ const objects = await client.listDirectory(input);
242
+ const entries = provider.convertDataToStorageObjects(objects);
243
+ const nextCursor = provider.nextPageCursor?.(objects);
244
+ if (owns()) {
245
+ const page = {
246
+ entries,
247
+ cursor: nextCursor
248
+ };
249
+ setListings((prev) => options?.replace ? replaceListing(prev, node.path, page) : advanceListing(prev, node.path, page));
250
+ }
251
+ }
252
+ if (owns()) setHasCorsIssue((prev) => {
253
+ if (!prev.has(nodeStorageId)) return prev;
254
+ const next = new Map(prev);
255
+ next.delete(nodeStorageId);
256
+ return next;
257
+ });
258
+ return { corsError: false };
259
+ } catch (error) {
260
+ const isCorsError = error instanceof CorsError;
261
+ const errorMessage = isCorsError ? error.message || "CORS error" : error instanceof Error ? error.message : "Unknown error";
262
+ if (owns()) {
263
+ setListings((prev) => recordListingError(options?.replace ? clearListingsUnder(prev, node.path) : prev, node.path, errorMessage));
264
+ updateConnectionIssuesMessage((prev) => new Map(prev).set(nodeStorageId, `Failed to load contents for ${node?.displayName || node?.name}: ${errorMessage}`));
265
+ if (isCorsError) setHasCorsIssue((prev) => new Map(prev).set(nodeStorageId, true));
266
+ }
267
+ return { corsError: isCorsError };
268
+ } finally {
269
+ if (owns()) {
270
+ fetchesInFlightRef.current.delete(node.path);
271
+ if (!more) setLoadingPaths((prev) => {
272
+ const next = new Set(prev);
273
+ next.delete(node.path);
274
+ return next;
275
+ });
276
+ }
277
+ }
278
+ });
279
+ const handleSelectTreeItem = async (path, node) => {
280
+ if (normalizedSelectedPath !== path) {
281
+ setCheckedItems(/* @__PURE__ */ new Set());
282
+ setDetailTab("details");
283
+ }
284
+ setSelectedPath(path);
285
+ if (node.type === "directory" && node?.storageId && !isListed(listings, path)) await fetchNodeChildren(node);
286
+ };
287
+ useEffect(() => {
288
+ if (!normalizedSelectedPath) return;
289
+ const ancestors = getAncestorDirPaths(normalizedSelectedPath);
290
+ if (ancestors.length === 0) return;
291
+ setExpandedPaths((prev) => {
292
+ if (ancestors.every((p) => prev.has(p))) return prev;
293
+ const next = new Set(prev);
294
+ for (const p of ancestors) next.add(p);
295
+ return next;
296
+ });
297
+ }, [normalizedSelectedPath]);
298
+ const [stuck, setStuck] = useState(null);
299
+ const hydrationStuck = stuck?.target === normalizedSelectedPath;
300
+ const hydrationPagesRef = useRef({
301
+ path: "",
302
+ pages: 0
303
+ });
304
+ useLayoutEffect(() => {
305
+ if (!normalizedSelectedPath) return;
306
+ if (hydrationPagesRef.current.path !== normalizedSelectedPath) hydrationPagesRef.current = {
307
+ path: normalizedSelectedPath,
308
+ pages: 0
309
+ };
310
+ const step = nextHydrationStep({
311
+ targetResolved: !!treeMap[normalizedSelectedPath],
312
+ ancestors: getAncestorDirPaths(normalizedSelectedPath),
313
+ isListableDir: (path) => {
314
+ const node = treeMap[path];
315
+ return !!node && node.type === "directory" && !!node.storageId;
316
+ },
317
+ listings,
318
+ inFlight: fetchesInFlightRef.current,
319
+ pagesPulled: hydrationPagesRef.current.pages
320
+ });
321
+ if (step.kind === "stuck") {
322
+ setStuck({
323
+ target: normalizedSelectedPath,
324
+ deepest: step.path
325
+ });
326
+ return;
327
+ }
328
+ if (step.kind === "done") return;
329
+ const node = treeMap[step.path];
330
+ if (!node) return;
331
+ if (step.kind === "fetch") {
332
+ fetchNodeChildren(node);
333
+ return;
334
+ }
335
+ hydrationPagesRef.current.pages += 1;
336
+ fetchNodeChildren(node, { more: true });
337
+ }, [
338
+ normalizedSelectedPath,
339
+ treeMap,
340
+ listings
341
+ ]);
342
+ useEffect(() => {
343
+ for (const path of expandedPaths) {
344
+ const node = treeMap[path];
345
+ if (node?.type === "directory" && node.storageId && !listings.has(path) && !fetchesInFlightRef.current.has(path)) {
346
+ fetchNodeChildren(node);
347
+ return;
348
+ }
349
+ }
350
+ }, [
351
+ expandedPaths,
352
+ treeMap,
353
+ listings
354
+ ]);
355
+ const handleBatchCheckbox = () => {
356
+ if (selectedNode?.type !== "directory") return;
357
+ const selectAll = selectedInFolderCount < selectedNodeChildren.length;
358
+ const newCheckedItems = new Set(checkedItems);
359
+ for (const item of selectedNodeChildren) if (selectAll) newCheckedItems.add(item.path);
360
+ else newCheckedItems.delete(item.path);
361
+ setCheckedItems(newCheckedItems);
362
+ };
363
+ const handleItemCheckbox = (node, isChecked) => {
364
+ setCheckedItems((prev) => {
365
+ const newCheckedItems = new Set(prev);
366
+ if (isChecked) newCheckedItems.add(node.path);
367
+ else newCheckedItems.delete(node.path);
368
+ return newCheckedItems;
369
+ });
370
+ };
371
+ const selectedInFolderCount = useMemo(() => selectedNodeChildren.reduce((count, item) => checkedItems.has(item.path) ? count + 1 : count, 0), [selectedNodeChildren, checkedItems]);
372
+ const folderHasMore = selectedNode ? hasMore(listings, selectedNode.path) : false;
373
+ const allItemsSelected = selectedNodeChildren.length > 0 && selectedInFolderCount === selectedNodeChildren.length && !folderHasMore;
374
+ const loadMoreProps = selectedNode && folderHasMore ? {
375
+ loading: isLoadingMore(listings, selectedNode.path),
376
+ error: loadMoreError(listings, selectedNode.path),
377
+ auto: canAutoLoadMore(listings, selectedNode.path),
378
+ onLoadMore: () => {
379
+ fetchNodeChildren(selectedNode, { more: true });
380
+ }
381
+ } : void 0;
382
+ const treePaging = {
383
+ incompletePaths: incompleteDirs,
384
+ isLoading: (path) => isLoadingMore(listings, path),
385
+ hasFailed: (path) => !!loadMoreError(listings, path),
386
+ onLoadMore: (node) => {
387
+ fetchNodeChildren(node, { more: true });
388
+ }
389
+ };
390
+ const onDropFiles = async (targetPath, files) => {
391
+ const targetStorageId = treeMap[targetPath]?.storageId;
392
+ if (!storageCanWrite(targetStorageId)) {
393
+ notify.error(t.messages.noWriteAccess);
394
+ return;
395
+ }
396
+ if (!storageCanUpload(targetStorageId)) {
397
+ notify.error(t.messages.uploadsUnsupported);
398
+ return;
399
+ }
400
+ let uploadNodes = [];
401
+ let totalSize = 0;
402
+ if (waitForUpload) {
403
+ notify.warning(t.messages.waitForCurrentUpload);
404
+ return;
405
+ }
406
+ setWaitForUpload(true);
407
+ if (files instanceof DataTransferItemList && files.length && files[0] && typeof files[0].webkitGetAsEntry === "function") {
408
+ const prepareData = await getUploadNodesFromDataTransferItems(files);
409
+ uploadNodes = prepareData.uploadNodes;
410
+ totalSize = prepareData.totalSize;
411
+ } else {
412
+ uploadNodes = Array.from(files).map((file) => ({
413
+ name: file.name,
414
+ file,
415
+ relativePath: file.name
416
+ }));
417
+ totalSize = calculateUploadNodesTotalSize(uploadNodes);
418
+ }
419
+ setPendingUpload({
420
+ targetPath,
421
+ uploadNodes,
422
+ totalSize
423
+ });
424
+ setUploadModalOpen(true);
425
+ setWaitForUpload(false);
426
+ };
427
+ const hasFolderSelected = () => {
428
+ if (checkedItems.size > 0) return Array.from(checkedItems).map((path) => treeMap[path]).some((node) => node && node.type === "directory");
429
+ if (selectedNode && selectedNode.type === "directory") return true;
430
+ return false;
431
+ };
432
+ const handleDownload = async () => {
433
+ let filesToDownload = [];
434
+ if (checkedItems.size > 0) filesToDownload = Array.from(checkedItems).map((path) => treeMap[path]).filter((node) => !!node && node.type === "file");
435
+ else if (selectedNode && selectedNode.type === "file") filesToDownload = [selectedNode];
436
+ if (filesToDownload.length === 0) {
437
+ notify.error(t.messages.noFilesForDownload);
438
+ return;
439
+ }
440
+ const factory = await getStorageProviderAndClient(filesToDownload[0].storageId);
441
+ if (!factory) return;
442
+ const { provider, client } = factory;
443
+ if (!provider?.getFileInput) {
444
+ notify.error(t.messages.downloadUnsupported);
445
+ return;
446
+ }
447
+ for (const [index, node] of filesToDownload.entries()) {
448
+ const input = provider.getFileInput(node.storageName ?? "", node, void 0, node.name);
449
+ const [presignedUrl, error] = await client.getFile(input);
450
+ if (error || !presignedUrl) {
451
+ notify.error(error?.message ?? t.messages.downloadLinkFailed);
452
+ continue;
453
+ }
454
+ triggerBrowserDownload(presignedUrl, node.name);
455
+ if (index < filesToDownload.length - 1) await new Promise((resolve) => setTimeout(resolve, 150));
456
+ }
457
+ };
458
+ const onShareFileButtonClick = async (node) => {
459
+ const factory = await getStorageProviderAndClient(node.storageId);
460
+ if (!factory) return;
461
+ const { provider, client } = factory;
462
+ if (!provider?.getFileInput) {
463
+ notify.error(t.messages.sharingUnsupported);
464
+ return;
465
+ }
466
+ const input = provider.getFileInput(node.storageName ?? "", node);
467
+ const [presignedUrl, error] = await client.getFile(input);
468
+ if (error || !presignedUrl) {
469
+ notify.error(error?.message ?? t.messages.fileLinkFailed);
470
+ return;
471
+ }
472
+ await navigator.clipboard.writeText(presignedUrl);
473
+ notify.info(t.messages.fileLinkCopied);
474
+ };
475
+ const triggerBrowserDownload = (url, filename) => {
476
+ const a = document.createElement("a");
477
+ a.href = url;
478
+ a.download = filename;
479
+ document.body.appendChild(a);
480
+ a.click();
481
+ setTimeout(() => {
482
+ document.body.removeChild(a);
483
+ }, 300);
484
+ };
485
+ const getPresignedUrl = async (node, downloadFilename) => {
486
+ const factory = await getStorageProviderAndClient(node.storageId);
487
+ if (!factory) return [null, /* @__PURE__ */ new Error("Failed to connect to storage provider.")];
488
+ const { provider, client } = factory;
489
+ if (!provider?.getFileInput) return [null, /* @__PURE__ */ new Error("Preview is not supported for this storage.")];
490
+ const input = provider.getFileInput(node.storageName ?? "", node, PREVIEW_URL_EXPIRES_IN, downloadFilename);
491
+ return client.getFile(input);
492
+ };
493
+ const previewSiblings = useMemo(() => previewNode ? getNodeChildren(parentToChildrenMap, getParentPath(previewNode.path)).filter((n) => n.type === "file") : [], [previewNode, parentToChildrenMap]);
494
+ const openPreview = (node) => {
495
+ if (node.type !== "file") return;
496
+ if (onOpenFile?.(node, storages.find((s) => s.id === node.storageId))) return;
497
+ setPreviewNode(node);
498
+ setPreviewOpen(true);
499
+ };
500
+ const closePreview = () => {
501
+ setPreviewOpen(false);
502
+ };
503
+ const openUserNode = (node) => {
504
+ handleSelectTreeItem(node.path, node);
505
+ if (!expandedPaths.has(node.path)) handleToggleExpand(node.path);
506
+ };
507
+ const getNodeUris = (node) => {
508
+ const storage = storages.find((s) => s.id === node.storageId);
509
+ if (!storage || !objectUris) return [];
510
+ return objectUris(storage, getObjectKeyFromNode(node));
511
+ };
512
+ const copyNodeUri = async (node) => {
513
+ const uri = getNodeUris(node)[0];
514
+ if (!uri) {
515
+ notify.error(t.messages.noUri);
516
+ return;
517
+ }
518
+ try {
519
+ await navigator.clipboard.writeText(uri);
520
+ notify.info(t.messages.uriCopied);
521
+ } catch {
522
+ notify.error(t.messages.uriCopyFailed);
523
+ }
524
+ };
525
+ const downloadNode = async (node) => {
526
+ const [presignedUrl, error] = await getPresignedUrl(node, node.name);
527
+ if (error || !presignedUrl) {
528
+ notify.error(error?.message ?? t.messages.downloadLinkFailed);
529
+ return;
530
+ }
531
+ triggerBrowserDownload(presignedUrl, node.name);
532
+ };
533
+ const deleteNode = (node) => {
534
+ if (storages.find((s) => s.id === node.storageId)?.canWrite !== true) {
535
+ notify.error(t.messages.noWriteAccess);
536
+ return;
537
+ }
538
+ setPendingDelete([node]);
539
+ setDeleteModalOpen(true);
540
+ };
541
+ const openCreateFolder = async (node) => {
542
+ if (!isStorageDirectory(node)) return;
543
+ if (!isListed(listings, node.path)) await fetchNodeChildren(node);
544
+ setCreateFolderTarget(node);
545
+ };
546
+ const getCreateFolderActions = (node, storage) => {
547
+ if (node.type !== "directory" || storage?.canWrite !== true || storage.canUpload === false) return [];
548
+ return [{
549
+ key: "create-folder",
550
+ label: t.createFolder.action,
551
+ icon: /* @__PURE__ */ jsx(FolderIcon, {}),
552
+ onSelect: () => openCreateFolder(node)
553
+ }];
554
+ };
555
+ const buildRowMenu = (node) => {
556
+ const storage = storages.find((s) => s.id === node.storageId);
557
+ const nodeCanWrite = storage?.canWrite === true;
558
+ const nodeCanShare = storage?.canShare !== false;
559
+ const isFile = node.type === "file";
560
+ const uris = getNodeUris(node);
561
+ const actions = [];
562
+ if (isFile && canPreview(node)) actions.push({
563
+ key: "preview",
564
+ label: t.preview.preview,
565
+ icon: /* @__PURE__ */ jsx(EyeIcon, {}),
566
+ onSelect: () => openPreview(node)
567
+ });
568
+ if (isFile) {
569
+ actions.push({
570
+ key: "download",
571
+ label: t.preview.download,
572
+ icon: /* @__PURE__ */ jsx(DownloadFileIcon, {}),
573
+ onSelect: () => downloadNode(node)
574
+ });
575
+ if (nodeCanShare) actions.push({
576
+ key: "share",
577
+ label: t.preview.shareFile,
578
+ icon: /* @__PURE__ */ jsx(ShareIcon, {}),
579
+ onSelect: () => onShareFileButtonClick(node)
580
+ });
581
+ }
582
+ actions.push(...getCreateFolderActions(node, storage));
583
+ if (nodeCanWrite && !node.root) actions.push({
584
+ key: "delete",
585
+ label: t.preview.delete,
586
+ icon: /* @__PURE__ */ jsx(TrashIcon, {}),
587
+ destructive: true,
588
+ onSelect: () => deleteNode(node)
589
+ });
590
+ const lead = [{
591
+ kind: "action",
592
+ label: isFile ? t.preview.viewDetails : t.preview.openFolder,
593
+ icon: isFile ? /* @__PURE__ */ jsx(FileTextIcon, {}) : /* @__PURE__ */ jsx(FolderIcon, {}),
594
+ onSelect: () => handleSelectTreeItem(node.path, node)
595
+ }];
596
+ const uriExtras = uris.map((value) => ({
597
+ label: `${getUriScheme(value).toUpperCase()} URI`,
598
+ value,
599
+ icon: /* @__PURE__ */ jsx(LinkIcon, {})
600
+ }));
601
+ return assembleRowMenu({
602
+ lead,
603
+ actions,
604
+ copy: uriExtras.length > 0 ? copySubmenu({
605
+ name: isFile ? node.name : void 0,
606
+ extra: uriExtras
607
+ }) : void 0
608
+ });
609
+ };
610
+ const openNodeContextMenu = (e, node) => {
611
+ const items = buildRowMenu(node);
612
+ if (items.length === 0) return;
613
+ e.preventDefault();
614
+ e.stopPropagation();
615
+ openMenu(e.clientX, e.clientY, items);
616
+ };
617
+ const renderBreadcrumb = () => {
618
+ const chain = [];
619
+ let cur = selectedNode ?? void 0;
620
+ const seen = /* @__PURE__ */ new Set();
621
+ while (cur && !seen.has(cur.path)) {
622
+ seen.add(cur.path);
623
+ chain.unshift(cur);
624
+ if (cur.root) break;
625
+ cur = treeMap[getParentPath(cur.path)];
626
+ }
627
+ if (chain.length === 0) return null;
628
+ return /* @__PURE__ */ jsx("nav", {
629
+ className: "flex min-w-0 flex-1 items-center gap-1 overflow-hidden text-[13px]",
630
+ children: chain.map((node, i) => {
631
+ const isLast = i === chain.length - 1;
632
+ const label = node.displayName || node.name;
633
+ return /* @__PURE__ */ jsxs("span", {
634
+ className: "flex min-w-0 items-center gap-1",
635
+ children: [i > 0 && /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-3 w-3 shrink-0 text-(--theme-muted-text-color)" }), isLast ? /* @__PURE__ */ jsx("span", {
636
+ className: "truncate font-semibold text-(--theme-app)",
637
+ children: label
638
+ }) : /* @__PURE__ */ jsx("button", {
639
+ type: "button",
640
+ onClick: () => handleSelectTreeItem(node.path, node),
641
+ className: "max-w-[16rem] shrink-0 cursor-pointer truncate text-(--theme-muted-text-color) transition-colors hover:text-(--theme-app)",
642
+ children: label
643
+ })]
644
+ }, node.path);
645
+ })
646
+ });
647
+ };
648
+ const generateDownloadCommand = () => {
649
+ let filesToDownload = [];
650
+ if (checkedItems.size > 0) filesToDownload = Array.from(checkedItems).map((path) => treeMap[path]).filter((node) => !!node && node.type === "file");
651
+ else if (selectedNode && selectedNode.type === "file") filesToDownload = [selectedNode];
652
+ if (filesToDownload.length === 0) return "";
653
+ if (!downloadCommand) return "";
654
+ const objects = filesToDownload.flatMap((node) => {
655
+ const storage = storages.find((s) => s.id === node.storageId);
656
+ return storage ? [{
657
+ storage,
658
+ key: getObjectKeyFromNode(node),
659
+ name: node.name
660
+ }] : [];
661
+ });
662
+ return downloadCommand.build(objects);
663
+ };
664
+ const onDeleteClick = () => {
665
+ if (!selectedNodeCanWrite) {
666
+ notify.error(t.messages.noWriteAccess);
667
+ return;
668
+ }
669
+ let nodesToDelete = [];
670
+ if (checkedItems.size > 0) nodesToDelete = Array.from(checkedItems).map((path) => treeMap[path]).filter((node) => Boolean(node));
671
+ else if (selectedNode) nodesToDelete = [selectedNode];
672
+ setPendingDelete(pruneNodesInsideSelectedDirectories(nodesToDelete));
673
+ setDeleteModalOpen(true);
674
+ };
675
+ const handlePostDelete = (selectedNode) => {
676
+ if (!selectedNode) return;
677
+ if (selectedNode.type === "file") {
678
+ let parentPath = getParentPath(selectedNode.path);
679
+ let parentNode = treeMap[parentPath];
680
+ while (parentNode) {
681
+ if (getNodeChildren(parentToChildrenMap, parentPath).length > 0) break;
682
+ if (parentNode.storageId && parentNode.root) break;
683
+ parentPath = getParentPath(parentPath);
684
+ parentNode = treeMap[parentPath];
685
+ }
686
+ if (parentNode) handleSelectTreeItem(parentPath, parentNode);
687
+ } else if (selectedNode.type === "directory") {
688
+ const currentFolderChildren = getNodeChildren(parentToChildrenMap, selectedNode.path);
689
+ if (currentFolderChildren.length > 0 && currentFolderChildren.every((child) => checkedItems.has(child.path))) {
690
+ const parentPath = getParentPath(selectedNode.path);
691
+ const parentNode = treeMap[parentPath];
692
+ if (parentNode) handleSelectTreeItem(parentPath, parentNode);
693
+ }
694
+ }
695
+ };
696
+ const handleDelete = async () => {
697
+ if (!pendingDelete || pendingDelete.length === 0) {
698
+ notify.error(t.messages.noItemsForDeletion);
699
+ return;
700
+ }
701
+ const parentPath = getParentPath(pendingDelete[0].path);
702
+ const parentNode = treeMap[parentPath];
703
+ const factory = await getStorageProviderAndClient(pendingDelete[0].storageId);
704
+ if (!factory) return;
705
+ const { storage, provider, client } = factory;
706
+ if (!provider?.deleteFilesInput) {
707
+ notify.error(t.messages.deleteUnsupported);
708
+ return;
709
+ }
710
+ try {
711
+ notify.loading(t.messages.deleting);
712
+ const input = provider.deleteFilesInput(storage.bucketName || storage.name, pendingDelete);
713
+ const response = await client.deleteFiles(input);
714
+ notify.dismiss();
715
+ if (parentNode) await fetchNodeChildren(parentNode, { replace: true });
716
+ setPendingDelete(null);
717
+ setCheckedItems(/* @__PURE__ */ new Set());
718
+ setDeleteModalOpen(false);
719
+ if (response?.Errors?.length) {
720
+ notify.error(t.messages.deleteSomeFailed);
721
+ return;
722
+ }
723
+ const deletedPaths = new Set(pendingDelete.map((n) => n.path));
724
+ const deletedDirPrefixes = pendingDelete.filter((n) => n.type === "directory").map((n) => n.path.endsWith("/") ? n.path : `${n.path}/`);
725
+ const selectedRemoved = !!selectedNode && (deletedPaths.has(selectedNode.path) || deletedDirPrefixes.some((p) => selectedNode.path.startsWith(p)));
726
+ if (selectedNode && (selectedNode.type === "directory" || selectedRemoved)) handlePostDelete(selectedNode);
727
+ notify.success(t.messages.deleteSuccess);
728
+ } catch (err) {
729
+ notify.dismiss();
730
+ notify.error(t.messages.deleteFailed(err instanceof Error ? err.message : String(err)));
731
+ }
732
+ };
733
+ const handleUploadInputChange = (files, selectedNode) => {
734
+ if (!files || !selectedNode || selectedNode.type === "file") return;
735
+ if (!storageCanWrite(selectedNode.storageId)) {
736
+ notify.error(t.messages.noWriteAccess);
737
+ return;
738
+ }
739
+ if (!storageCanUpload(selectedNode.storageId)) {
740
+ notify.error(t.messages.uploadsUnsupported);
741
+ return;
742
+ }
743
+ if (waitForUpload) {
744
+ notify.warning(t.messages.waitForCurrentUpload);
745
+ return;
746
+ }
747
+ setWaitForUpload(true);
748
+ const uploadNodes = Array.from(files).map((file) => ({
749
+ name: file.name,
750
+ file,
751
+ relativePath: file.webkitRelativePath || file.name
752
+ }));
753
+ const totalSize = calculateUploadNodesTotalSize(uploadNodes);
754
+ setPendingUpload({
755
+ targetPath: selectedNode.path,
756
+ uploadNodes,
757
+ totalSize
758
+ });
759
+ setUploadModalOpen(true);
760
+ setWaitForUpload(false);
761
+ if (fileInputRef.current) fileInputRef.current.value = "";
762
+ if (folderInputRef.current) folderInputRef.current.value = "";
763
+ };
764
+ const handleUpload = async () => {
765
+ if (!pendingUpload) {
766
+ notify.error(t.messages.noFilesToUpload);
767
+ return;
768
+ }
769
+ if (waitForUpload) {
770
+ notify.info(t.messages.waitForUpload);
771
+ return;
772
+ }
773
+ const targetNode = treeMap[pendingUpload.targetPath];
774
+ if (!targetNode || !targetNode.storageId) {
775
+ notify.error(t.messages.invalidUploadTarget);
776
+ return;
777
+ }
778
+ const factory = await getStorageProviderAndClient(targetNode.storageId);
779
+ if (!factory) {
780
+ notify.error(t.messages.connectFailed);
781
+ return;
782
+ }
783
+ const { storage, provider, client } = factory;
784
+ const storageName = storage.bucketName || storage.name;
785
+ const cleanTargetPath = targetNode.root ? "" : removeLeadingAndTrailingSlashes(targetNode.relativePath || "");
786
+ if (!provider?.uploadFileInput) {
787
+ notify.error(t.messages.uploadUnsupported);
788
+ return;
789
+ }
790
+ const sessionId = fileExplorerContext.addUploadSession(storageName, cleanTargetPath, pendingUpload.uploadNodes, client, provider, targetNode, (node) => fetchNodeChildren(node, { replace: true }));
791
+ const sessionQueueIndex = fileExplorerContext.getUploadQueueIndex(sessionId);
792
+ if (sessionQueueIndex === -1) {
793
+ notify.update(sessionId, {
794
+ render: t.messages.uploadInitFailed,
795
+ type: "error",
796
+ icon: null,
797
+ isLoading: false,
798
+ closeButton: null
799
+ });
800
+ return;
801
+ } else if (sessionQueueIndex > 0 && fileExplorerContext.uploadQueue[sessionQueueIndex - 1] !== sessionId) {
802
+ notify.update(sessionId, {
803
+ render: t.messages.uploadQueuedInfo,
804
+ type: "info",
805
+ icon: null,
806
+ isLoading: false,
807
+ autoClose: false
808
+ });
809
+ return;
810
+ }
811
+ setUploadModalOpen(false);
812
+ setPendingUpload(null);
813
+ setWaitForUpload(false);
814
+ };
815
+ const validateFolderName = (name) => {
816
+ if (createFolderTarget?.type !== "directory") return null;
817
+ const error = getDirectoryNameError(name, getNodeChildren(parentToChildrenMap, createFolderTarget.path));
818
+ return error ? t.createFolder.validation[error] : null;
819
+ };
820
+ const handleCreateFolder = async (name) => {
821
+ if (!isStorageDirectory(createFolderTarget)) {
822
+ notify.error(t.createFolder.invalidTarget);
823
+ return false;
824
+ }
825
+ if (!storageCanWrite(createFolderTarget.storageId)) {
826
+ notify.error(t.createFolder.noWriteAccess);
827
+ return false;
828
+ }
829
+ if (!storageCanUpload(createFolderTarget.storageId)) {
830
+ notify.error(t.createFolder.unsupported);
831
+ return false;
832
+ }
833
+ try {
834
+ const factory = await getStorageProviderAndClient(createFolderTarget.storageId);
835
+ if (!factory) return false;
836
+ const { storage, provider, client } = factory;
837
+ if (!provider.uploadFileInput) {
838
+ notify.error(t.createFolder.unsupported);
839
+ return false;
840
+ }
841
+ const input = provider.uploadFileInput(storage.bucketName || storage.name, getDirectoryObjectPath(createFolderTarget, name), new Blob());
842
+ await client.uploadFile(input);
843
+ await fetchNodeChildren(createFolderTarget, { replace: true });
844
+ notify.success(t.createFolder.success(name));
845
+ return true;
846
+ } catch (error) {
847
+ notify.error(t.createFolder.error(getErrorMessage(error)));
848
+ return false;
849
+ }
850
+ };
851
+ const handleRefresh = async (opts) => {
852
+ const dirs = [...expandedPaths].map((path) => treeMap[path]).filter((node) => !!node && node.type === "directory" && !!node.storageId);
853
+ if (selectedNode?.type === "directory" && selectedNode.storageId && !dirs.some((dir) => dir.path === selectedNode.path)) dirs.push(selectedNode);
854
+ if (dirs.length > 0) return { corsError: (await Promise.all(dirs.map((dir) => fetchNodeChildren(dir, {
855
+ replace: true,
856
+ silent: opts?.silent
857
+ })))).some((result) => result.corsError) };
858
+ if (onRefresh) onRefresh();
859
+ return { corsError: false };
860
+ };
861
+ return /* @__PURE__ */ jsxs("div", {
862
+ className: "w-full h-full flex relative",
863
+ children: [
864
+ /* @__PURE__ */ jsxs("div", {
865
+ className: "w-full h-full flex",
866
+ children: [/* @__PURE__ */ jsx(TreeFrame, {
867
+ treeOnly,
868
+ sidebarWidth,
869
+ onWidthChange: setSidebarWidth,
870
+ children: /* @__PURE__ */ jsxs("div", {
871
+ className: "h-full flex flex-col border-r theme-border bg-(--theme-panel-bg)/60",
872
+ children: [/* @__PURE__ */ jsx("div", {
873
+ className: "flex items-center justify-end h-10 px-3 shrink-0 border-b theme-border",
874
+ children: /* @__PURE__ */ jsxs("div", {
875
+ className: "flex gap-x-0.5",
876
+ children: [/* @__PURE__ */ jsx("button", {
877
+ type: "button",
878
+ "aria-label": t.chrome.refresh,
879
+ "data-tooltip-id": TOOLTIP_ID,
880
+ "data-tooltip-content": "Refresh",
881
+ "data-tooltip-class-name": "z-50",
882
+ className: "inline-flex items-center justify-center h-7 w-7 rounded-md cursor-pointer theme-muted-text hover:theme-text hover:theme-hover transition-colors duration-150 focus:outline-none focus-visible:ring-1 focus-visible:ring-(--theme-link)",
883
+ onClick: () => handleRefresh(),
884
+ children: /* @__PURE__ */ jsx(RefreshIcon, { className: "h-3.5 w-3.5" })
885
+ }), /* @__PURE__ */ jsx("button", {
886
+ type: "button",
887
+ "aria-label": t.chrome.collapse,
888
+ "data-tooltip-id": TOOLTIP_ID,
889
+ "data-tooltip-content": "Collapse All",
890
+ "data-tooltip-class-name": "z-50",
891
+ className: "inline-flex items-center justify-center h-7 w-7 rounded-md cursor-pointer theme-muted-text hover:theme-text hover:theme-hover transition-colors duration-150 focus:outline-none focus-visible:ring-1 focus-visible:ring-(--theme-link)",
892
+ onClick: collapseAll,
893
+ children: /* @__PURE__ */ jsx(CollapseIcon, { className: "h-3.5 w-3.5" })
894
+ })]
895
+ })
896
+ }), /* @__PURE__ */ jsx(TreeView, {
897
+ rootNodes,
898
+ parentToChildrenMap,
899
+ expandedPaths,
900
+ selectedPath: normalizedSelectedPath,
901
+ loadingPaths,
902
+ onToggleExpand: handleToggleExpand,
903
+ onSelect: (path, node) => {
904
+ handleSelectTreeItem(path, node);
905
+ if (treeOnly && node.type === "file") openPreview(node);
906
+ },
907
+ onDropToFolder: onDropFiles,
908
+ onContextMenu: openNodeContextMenu,
909
+ onPreviewFile: openPreview,
910
+ paging: treePaging
911
+ })]
912
+ })
913
+ }), !treeOnly && /* @__PURE__ */ jsx("div", {
914
+ className: "h-full flex flex-col flex-1 min-w-0",
915
+ children: !selectedNode && normalizedSelectedPath ? hydrationStuck ? /* @__PURE__ */ jsx(NotFoundPreview, {
916
+ path: normalizedSelectedPath,
917
+ onRetry: () => {
918
+ const deepest = stuck?.deepest;
919
+ hydrationPagesRef.current = {
920
+ path: "",
921
+ pages: 0
922
+ };
923
+ setStuck(null);
924
+ if (deepest) setListings((prev) => hasMore(prev, deepest) ? clearListingError(prev, deepest) : clearListingsUnder(prev, deepest));
925
+ }
926
+ }) : /* @__PURE__ */ jsx(ListSkeleton, {}) : !selectedNode && showUserHierarchy ? /* @__PURE__ */ jsxs("div", {
927
+ className: "overflow-auto h-full",
928
+ children: [/* @__PURE__ */ jsxs(Table, {
929
+ ...listTableProps,
930
+ children: [
931
+ /* @__PURE__ */ jsx(Table.Header, {
932
+ caps: false,
933
+ className: "py-2",
934
+ style: { minWidth: 200 },
935
+ children: "Name"
936
+ }),
937
+ /* @__PURE__ */ jsx(Table.Header, {
938
+ caps: false,
939
+ className: "w-[22%] py-2",
940
+ children: "Type"
941
+ }),
942
+ /* @__PURE__ */ jsx(Table.Header, {
943
+ caps: false,
944
+ className: "w-[22%] py-2",
945
+ children: "Storages"
946
+ }),
947
+ /* @__PURE__ */ jsx(ListActionsHeader, { actionCount: 1 }),
948
+ rootNodes.map((rootNode) => /* @__PURE__ */ jsx(UserRow, {
949
+ node: rootNode,
950
+ icon: getNodeIcon(rootNode, slots.storageIconUrl, t.chrome.storageIconAlt),
951
+ storageCount: getNodeChildren(parentToChildrenMap, rootNode.path).length,
952
+ getItems: () => [{
953
+ kind: "action",
954
+ label: t.preview.open,
955
+ icon: /* @__PURE__ */ jsx(FolderIcon, {}),
956
+ onSelect: () => openUserNode(rootNode)
957
+ }],
958
+ openMenu,
959
+ onOpen: openUserNode
960
+ }, rootNode.path))
961
+ ]
962
+ }), rootNodes.length === 0 && /* @__PURE__ */ jsx(NoDataAvailablePreview, {
963
+ selectedStorage: null,
964
+ onRefresh: handleRefresh
965
+ })]
966
+ }) : !selectedNode ? /* @__PURE__ */ jsx(NoFileSelectedPreview, {}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
967
+ /* @__PURE__ */ jsxs("div", {
968
+ className: "flex items-center justify-between gap-3 px-4 h-12 shrink-0 border-b theme-border",
969
+ children: [renderBreadcrumb(), /* @__PURE__ */ jsxs("div", {
970
+ className: "flex shrink-0 items-center gap-2",
971
+ children: [
972
+ folderHasMore && /* @__PURE__ */ jsx(StatusBadge, {
973
+ variant: "muted",
974
+ className: "whitespace-nowrap tabular-nums",
975
+ children: t.partialCount(selectedNodeChildren.length)
976
+ }),
977
+ selectedInFolderCount > 0 && /* @__PURE__ */ jsx(StatusBadge, {
978
+ variant: "muted",
979
+ className: "whitespace-nowrap tabular-nums",
980
+ children: t.selectedCount(selectedInFolderCount)
981
+ }),
982
+ !selectedNodeCanWrite && /* @__PURE__ */ jsx(StatusBadge, {
983
+ variant: "muted",
984
+ className: "uppercase tracking-wider",
985
+ children: t.readOnly
986
+ }),
987
+ /* @__PURE__ */ jsx("input", {
988
+ type: "file",
989
+ multiple: true,
990
+ ref: fileInputRef,
991
+ className: "hidden",
992
+ onChange: (e) => {
993
+ handleUploadInputChange(e.target.files, selectedNode);
994
+ }
995
+ }),
996
+ /* @__PURE__ */ jsx("input", {
997
+ type: "file",
998
+ multiple: true,
999
+ webkitdirectory: "true",
1000
+ ref: folderInputRef,
1001
+ className: "hidden",
1002
+ onChange: (e) => {
1003
+ handleUploadInputChange(e.target.files, selectedNode);
1004
+ }
1005
+ }),
1006
+ selectedNodeCanWrite && selectedNodeCanUpload && /* @__PURE__ */ jsxs("button", {
1007
+ type: "button",
1008
+ "aria-label": t.createFolder.action,
1009
+ className: "btn btn-info cursor-pointer flex items-center justify-center",
1010
+ disabled: selectedNode.type === "file" || !selectedNode.storageId,
1011
+ onClick: () => setCreateFolderTarget(selectedNode),
1012
+ children: [/* @__PURE__ */ jsx(FolderIcon, { className: "h-4 w-4 mr-2" }), /* @__PURE__ */ jsx("span", {
1013
+ className: "hidden md:inline",
1014
+ children: t.createFolder.action
1015
+ })]
1016
+ }),
1017
+ selectedNodeCanWrite && selectedNodeCanUpload && /* @__PURE__ */ jsxs(Menu, { children: [/* @__PURE__ */ jsxs(MenuButton, {
1018
+ "aria-label": t.chrome.upload,
1019
+ className: "btn btn-info cursor-pointer flex items-center justify-between pr-0.5!",
1020
+ disabled: selectedNode.type === "file" || !selectedNode.storageId,
1021
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(UploadIcon, { className: "h-4 w-4 mr-2" }), /* @__PURE__ */ jsx("span", {
1022
+ className: "hidden md:inline",
1023
+ children: t.chrome.upload
1024
+ })] }), /* @__PURE__ */ jsx(AngleDownIcon, { className: "h-4 w-4 ml-2" })]
1025
+ }), /* @__PURE__ */ jsxs(MenuItems, {
1026
+ transition: true,
1027
+ anchor: "bottom end",
1028
+ className: "theme-panel w-36 rounded-md border p-1 mt-1 shadow-lg z-50 origin-top-right transition duration-100\n ease-out data-closed:scale-95 data-closed:opacity-0 focus:outline-none",
1029
+ children: [/* @__PURE__ */ jsx(MenuItem, { children: /* @__PURE__ */ jsx("button", {
1030
+ type: "button",
1031
+ className: "text-sm rounded-sm hover:theme-hover cursor-pointer w-full items-center px-2 py-1.5",
1032
+ onClick: () => fileInputRef.current?.click(),
1033
+ children: "Select Files"
1034
+ }) }), /* @__PURE__ */ jsx(MenuItem, { children: /* @__PURE__ */ jsx("button", {
1035
+ type: "button",
1036
+ className: "text-sm rounded-sm hover:theme-hover cursor-pointer w-full items-center px-2 py-1.5",
1037
+ onClick: () => folderInputRef.current?.click(),
1038
+ children: "Select Folder"
1039
+ }) })]
1040
+ })] }),
1041
+ /* @__PURE__ */ jsxs(Menu, { children: [/* @__PURE__ */ jsxs(MenuButton, {
1042
+ "aria-label": t.chrome.download,
1043
+ className: "btn btn-info cursor-pointer flex items-center justify-between pr-0.5!",
1044
+ disabled: checkedItems.size === 0 && selectedNode.type !== "file" || hasFolderSelected() || !selectedNode.storageId,
1045
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(DownloadFileIcon, { className: "h-4 w-4 mr-2" }), /* @__PURE__ */ jsx("span", {
1046
+ className: "hidden md:inline",
1047
+ children: t.chrome.download
1048
+ })] }), /* @__PURE__ */ jsx(AngleDownIcon, { className: "h-4 w-4 ml-2" })]
1049
+ }), /* @__PURE__ */ jsxs(MenuItems, {
1050
+ transition: true,
1051
+ anchor: "bottom end",
1052
+ className: "theme-panel w-72 rounded-md border p-1 mt-1 shadow-lg z-50 origin-top-right transition duration-100 \n ease-out data-closed:scale-95 data-closed:opacity-0 focus:outline-none",
1053
+ children: [/* @__PURE__ */ jsx(MenuItem, { children: /* @__PURE__ */ jsx("button", {
1054
+ type: "button",
1055
+ className: "text-sm rounded-sm hover:theme-hover cursor-pointer w-full items-center px-2 py-1.5",
1056
+ onClick: handleDownload,
1057
+ children: t.chrome.downloadFromBrowser
1058
+ }) }), downloadCommand && generateDownloadCommand() && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(MenuSeparator, { className: "my-0.5 h-px theme-border" }), /* @__PURE__ */ jsx(MenuItem, { children: /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
1059
+ className: "text-sm w-full text-center pointer-events-none px-2 py-1.5",
1060
+ children: downloadCommand.label
1061
+ }), (() => {
1062
+ const downloadCommand = generateDownloadCommand();
1063
+ return /* @__PURE__ */ jsx(CopyCodeBlock, {
1064
+ textToCopy: downloadCommand,
1065
+ children: /* @__PURE__ */ jsx("div", {
1066
+ className: "overflow-x-auto text-xs",
1067
+ children: keyedByContent(downloadCommand.split("\n"), (l) => l).map(({ key, item: line }) => /* @__PURE__ */ jsx("div", {
1068
+ className: "whitespace-nowrap",
1069
+ children: line
1070
+ }, key))
1071
+ })
1072
+ });
1073
+ })()] }) })] })]
1074
+ })] }),
1075
+ /* @__PURE__ */ jsxs("button", {
1076
+ type: "button",
1077
+ "aria-label": t.chrome.access,
1078
+ className: "btn btn-info cursor-pointer flex items-center justify-center",
1079
+ disabled: !selectedNode.storageId,
1080
+ onClick: () => setAccessDrawerOpen(true),
1081
+ hidden: !groups || groups.length === 0 || !selectedNodeCanManageAccess,
1082
+ children: [/* @__PURE__ */ jsx(AccessIcon, { className: "h-4 w-4 mr-2" }), /* @__PURE__ */ jsx("span", {
1083
+ className: "hidden md:inline",
1084
+ children: t.chrome.manageAccess
1085
+ })]
1086
+ }),
1087
+ selectedNodeCanWrite && /* @__PURE__ */ jsxs("button", {
1088
+ type: "button",
1089
+ "aria-label": t.chrome.delete,
1090
+ className: "btn btn-destructive cursor-pointer flex items-center justify-center",
1091
+ disabled: checkedItems.size === 0 && selectedNode.type !== "file" || !selectedNode.storageId,
1092
+ onClick: onDeleteClick,
1093
+ children: [/* @__PURE__ */ jsx(TrashIcon, { className: "h-4 w-4 mr-2" }), /* @__PURE__ */ jsx("span", {
1094
+ className: "hidden md:inline",
1095
+ children: t.chrome.delete
1096
+ })]
1097
+ })
1098
+ ]
1099
+ })]
1100
+ }),
1101
+ selectedNode.type === "directory" && /* @__PURE__ */ jsxs(DragDropZone, {
1102
+ className: "min-h-0 flex-1 overflow-hidden",
1103
+ onDropFiles: selectedNodeCanWrite ? (files) => {
1104
+ onDropFiles(selectedNode.path, files);
1105
+ } : void 0,
1106
+ children: [loadingPaths.has(selectedNode.path) && /* @__PURE__ */ jsx(ListSkeleton, {}), !loadingPaths.has(selectedNode.path) && (selectedNodeChildren.length > 0 || folderHasMore ? /* @__PURE__ */ jsx(FileTable, {
1107
+ nodes: selectedNodeChildren,
1108
+ checkedItems,
1109
+ allSelected: allItemsSelected,
1110
+ someSelected: selectedInFolderCount > 0,
1111
+ onSelectAll: handleBatchCheckbox,
1112
+ getItems: buildRowMenu,
1113
+ openMenu,
1114
+ onOpen: (n) => handleSelectTreeItem(n.path, n),
1115
+ onToggleCheck: handleItemCheckbox,
1116
+ onPreview: openPreview,
1117
+ loadMore: loadMoreProps
1118
+ }, selectedNode.path) : /* @__PURE__ */ jsxs("div", {
1119
+ className: "h-full flex flex-col",
1120
+ children: [/* @__PURE__ */ jsx(Table, {
1121
+ ...listTableFixedProps,
1122
+ wrapperClassName: "overflow-x-auto",
1123
+ children: /* @__PURE__ */ jsx(FileTableHeaders, {
1124
+ allSelected: allItemsSelected,
1125
+ someSelected: selectedInFolderCount > 0,
1126
+ onSelectAll: handleBatchCheckbox
1127
+ })
1128
+ }), /* @__PURE__ */ jsx(NoDataAvailablePreview, {
1129
+ selectedStorage: storages.find((s) => s.id === selectedNode.storageId) || null,
1130
+ canWrite: selectedNodeCanWrite,
1131
+ onRefresh: handleRefresh,
1132
+ error: hasCorsIssue.get(selectedNode.storageId ?? "") ? new CorsError(connectionIssuesMessage.get(selectedNode.storageId ?? "") || "") : null,
1133
+ connectionIssueMessage: selectedNode.storageId ? connectionIssuesMessage.get(selectedNode.storageId) : null
1134
+ })]
1135
+ }))]
1136
+ }),
1137
+ selectedNode.type === "file" && (() => {
1138
+ const uris = getNodeUris(selectedNode);
1139
+ const fileIcon = getNodeIcon(selectedNode, slots.storageIconUrl, t.chrome.storageIconAlt) ?? /* @__PURE__ */ jsx(FileIcon, { className: "h-5 w-5 theme-muted-text" });
1140
+ const modifiedDt = selectedNode.modified ? DateTime.fromISO(selectedNode.modified) : null;
1141
+ const createdDt = selectedNode.created ? DateTime.fromISO(selectedNode.created) : null;
1142
+ const previewable = canPreview(selectedNode);
1143
+ const effectiveTab = previewable ? detailTab : "details";
1144
+ const tabClass = (active) => cx("mx-2.5 border-b-2 px-1 py-2.5 text-[13px] font-medium cursor-pointer transition-colors", active ? "theme-text border-(--theme-element)" : "theme-muted-text border-transparent hover:theme-text");
1145
+ return /* @__PURE__ */ jsxs("div", {
1146
+ className: "flex h-full min-h-0 flex-col",
1147
+ children: [/* @__PURE__ */ jsxs("div", {
1148
+ className: "flex shrink-0 items-center border-b theme-border px-3",
1149
+ children: [/* @__PURE__ */ jsx("button", {
1150
+ type: "button",
1151
+ className: tabClass(effectiveTab === "details"),
1152
+ onClick: () => setDetailTab("details"),
1153
+ children: t.preview.detailsTab
1154
+ }), previewable && /* @__PURE__ */ jsx("button", {
1155
+ type: "button",
1156
+ className: tabClass(effectiveTab === "preview"),
1157
+ onClick: () => setDetailTab("preview"),
1158
+ children: t.preview.previewTab
1159
+ })]
1160
+ }), effectiveTab === "preview" && previewable ? /* @__PURE__ */ jsx(FilePreviewInline, {
1161
+ node: selectedNode,
1162
+ storage: storages.find((s) => s.id === selectedNode.storageId) ?? null,
1163
+ getPresignedUrl,
1164
+ onOpenFull: openPreview,
1165
+ onDownload: downloadNode,
1166
+ active: !previewOpen
1167
+ }) : /* @__PURE__ */ jsxs("div", {
1168
+ className: "min-h-0 flex-1 overflow-auto",
1169
+ children: [
1170
+ /* @__PURE__ */ jsxs("div", {
1171
+ className: "flex items-start gap-4 px-6 pt-6 pb-5 border-b theme-border",
1172
+ children: [/* @__PURE__ */ jsx("div", {
1173
+ className: "shrink-0 inline-flex items-center justify-center w-12 h-12 rounded-lg theme-muted-panel",
1174
+ children: /* @__PURE__ */ jsx("div", {
1175
+ className: "*:h-5 *:w-5 *:mr-0",
1176
+ children: fileIcon
1177
+ })
1178
+ }), /* @__PURE__ */ jsxs("div", {
1179
+ className: "flex-1 min-w-0",
1180
+ children: [/* @__PURE__ */ jsx("h2", {
1181
+ className: "text-lg font-semibold leading-tight break-all",
1182
+ children: selectedNode.name
1183
+ }), /* @__PURE__ */ jsxs("div", {
1184
+ className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 mt-1 text-xs theme-muted-text",
1185
+ children: [
1186
+ selectedNode.contentType && /* @__PURE__ */ jsx("span", {
1187
+ className: "inline-flex items-center rounded-full border theme-border px-2 py-0.5 font-mono text-[10.5px] uppercase tracking-wider",
1188
+ children: selectedNode.contentType
1189
+ }),
1190
+ typeof selectedNode.size === "number" && /* @__PURE__ */ jsx("span", {
1191
+ className: "tabular-nums",
1192
+ children: formatFileSize(selectedNode.size)
1193
+ }),
1194
+ modifiedDt && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
1195
+ "aria-hidden": true,
1196
+ children: "·"
1197
+ }), /* @__PURE__ */ jsxs("span", { children: ["Modified ", modifiedDt.toRelative()] })] })
1198
+ ]
1199
+ })]
1200
+ })]
1201
+ }),
1202
+ uris.length > 0 && /* @__PURE__ */ jsx("div", {
1203
+ className: "px-6 pt-5 space-y-2",
1204
+ children: uris.map((uri) => /* @__PURE__ */ jsx(UriCard, { value: uri }, uri))
1205
+ }),
1206
+ selectedNodeCanShare && /* @__PURE__ */ jsxs("div", {
1207
+ className: "flex items-center gap-2 px-6 pt-4",
1208
+ children: [/* @__PURE__ */ jsx(ShareFileButton, {
1209
+ node: selectedNode,
1210
+ onShare: onShareFileButtonClick
1211
+ }), /* @__PURE__ */ jsx(TooltipInfo, {
1212
+ text: "This will generate a pre-signed URL for this file. Anyone with this link can temporarily access and download the file, even if they don't have an account. The link will automatically expire after 12 hours.",
1213
+ place: "right"
1214
+ })]
1215
+ }),
1216
+ /* @__PURE__ */ jsxs("div", {
1217
+ className: "px-6 py-5 mt-5 border-t theme-border",
1218
+ children: [/* @__PURE__ */ jsx("h3", {
1219
+ className: "text-[11px] font-semibold uppercase tracking-widest theme-muted-text mb-3",
1220
+ children: "Details"
1221
+ }), /* @__PURE__ */ jsxs("dl", {
1222
+ className: "grid grid-cols-[minmax(0,140px)_1fr] gap-x-6 gap-y-2 text-sm",
1223
+ children: [
1224
+ typeof selectedNode.size === "number" && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("dt", {
1225
+ className: "theme-muted-text",
1226
+ children: t.chrome.size
1227
+ }), /* @__PURE__ */ jsx("dd", {
1228
+ className: "tabular-nums",
1229
+ children: formatFileSize(selectedNode.size)
1230
+ })] }),
1231
+ selectedNode.contentType && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("dt", {
1232
+ className: "theme-muted-text",
1233
+ children: "Content Type"
1234
+ }), /* @__PURE__ */ jsx("dd", {
1235
+ className: "font-mono text-xs",
1236
+ children: selectedNode.contentType
1237
+ })] }),
1238
+ createdDt && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("dt", {
1239
+ className: "theme-muted-text",
1240
+ children: t.chrome.created
1241
+ }), /* @__PURE__ */ jsxs("dd", { children: [
1242
+ createdDt.toLocaleString(DateTime.DATETIME_MED),
1243
+ " ",
1244
+ /* @__PURE__ */ jsxs("span", {
1245
+ className: "theme-muted-text",
1246
+ children: ["· ", createdDt.toRelative()]
1247
+ })
1248
+ ] })] }),
1249
+ modifiedDt && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("dt", {
1250
+ className: "theme-muted-text",
1251
+ children: "Last Modified"
1252
+ }), /* @__PURE__ */ jsxs("dd", { children: [
1253
+ modifiedDt.toLocaleString(DateTime.DATETIME_MED),
1254
+ " ",
1255
+ /* @__PURE__ */ jsxs("span", {
1256
+ className: "theme-muted-text",
1257
+ children: ["· ", modifiedDt.toRelative()]
1258
+ })
1259
+ ] })] }),
1260
+ selectedNode.hash && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("dt", {
1261
+ className: "theme-muted-text",
1262
+ children: t.chrome.hash
1263
+ }), /* @__PURE__ */ jsx("dd", {
1264
+ className: "font-mono text-xs break-all",
1265
+ children: selectedNode.hash.replace(/^"|"$/g, "")
1266
+ })] }),
1267
+ selectedNode.storageClass && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("dt", {
1268
+ className: "theme-muted-text",
1269
+ children: "Storage Class"
1270
+ }), /* @__PURE__ */ jsx("dd", {
1271
+ className: "font-mono text-xs uppercase tracking-wider",
1272
+ children: selectedNode.storageClass
1273
+ })] })
1274
+ ]
1275
+ })]
1276
+ })
1277
+ ]
1278
+ })]
1279
+ });
1280
+ })()
1281
+ ] })
1282
+ })]
1283
+ }),
1284
+ contextMenu,
1285
+ /* @__PURE__ */ jsx(FilePreview, {
1286
+ open: previewOpen,
1287
+ node: previewNode,
1288
+ siblings: previewSiblings,
1289
+ storage: storages.find((s) => s.id === previewNode?.storageId) ?? null,
1290
+ getPresignedUrl,
1291
+ onNavigate: setPreviewNode,
1292
+ onClose: closePreview,
1293
+ onDownload: downloadNode,
1294
+ onCopyUri: copyNodeUri
1295
+ }),
1296
+ /* @__PURE__ */ jsx(CreateModal, {
1297
+ open: !!createFolderTarget,
1298
+ onClose: () => setCreateFolderTarget(null),
1299
+ typeIcon: /* @__PURE__ */ jsx(FolderIcon, { className: "w-3 h-3" }),
1300
+ typeLabel: t.createFolder.folder,
1301
+ namePlaceholder: t.createFolder.namePlaceholder,
1302
+ submitLabel: t.createFolder.submit,
1303
+ submittingLabel: t.createFolder.creating,
1304
+ validateName: validateFolderName,
1305
+ onSubmit: handleCreateFolder
1306
+ }),
1307
+ waitForUpload && /* @__PURE__ */ jsx("div", {
1308
+ className: "absolute inset-0 bg-(--theme-app-bg)/75 backdrop-blur-[2px] transition-opacity flex items-center justify-center z-50",
1309
+ children: /* @__PURE__ */ jsx(Loader, { full: true })
1310
+ }),
1311
+ /* @__PURE__ */ jsx(ConfirmModal, {
1312
+ open: uploadModalOpen && !!pendingUpload,
1313
+ onClose: () => {
1314
+ setUploadModalOpen(false);
1315
+ setPendingUpload(null);
1316
+ },
1317
+ align: "center",
1318
+ title: t.confirmUpload.title,
1319
+ description: t.confirmUpload.text(pendingUpload?.targetPath || t.confirmUpload.defaultPath),
1320
+ confirmLabel: t.confirmUpload.confirm,
1321
+ onConfirm: handleUpload,
1322
+ confirmDisabled: pendingUpload ? isUploadSizeExceeded(pendingUpload.totalSize) : false,
1323
+ children: /* @__PURE__ */ jsxs("div", {
1324
+ className: "text-sm flex flex-col gap-2 mt-2 text-left",
1325
+ children: [
1326
+ /* @__PURE__ */ jsx("ul", {
1327
+ className: "list-disc pl-5 max-h-40 overflow-auto theme-muted-panel py-1",
1328
+ children: pendingUpload?.uploadNodes && renderUploadNodes(pendingUpload.uploadNodes)
1329
+ }),
1330
+ /* @__PURE__ */ jsxs("div", { children: [
1331
+ /* @__PURE__ */ jsx("span", {
1332
+ className: "font-semibold",
1333
+ children: t.confirmUpload.target
1334
+ }),
1335
+ " ",
1336
+ pendingUpload?.targetPath || t.confirmUpload.noPath
1337
+ ] }),
1338
+ /* @__PURE__ */ jsxs("div", {
1339
+ className: "text-xs mt-1",
1340
+ children: [
1341
+ /* @__PURE__ */ jsx("span", {
1342
+ className: "font-semibold",
1343
+ children: t.confirmUpload.totalSize
1344
+ }),
1345
+ " ",
1346
+ /* @__PURE__ */ jsx("span", {
1347
+ className: cx(isUploadSizeExceeded(pendingUpload?.totalSize ?? 0) && "text-amber-800 font-semibold"),
1348
+ children: formatFileSize(pendingUpload?.totalSize ?? 0)
1349
+ })
1350
+ ]
1351
+ }),
1352
+ isUploadSizeExceeded(pendingUpload?.totalSize ?? 0) && /* @__PURE__ */ jsx("div", {
1353
+ className: "mt-3 p-3 bg-amber-50 border border-amber-200 rounded-lg",
1354
+ children: /* @__PURE__ */ jsxs("div", {
1355
+ className: "flex items-start gap-2",
1356
+ children: [/* @__PURE__ */ jsx(AlertIcon, { className: "w-4 h-4 text-amber-600 shrink-0 mt-0.5" }), /* @__PURE__ */ jsxs("div", {
1357
+ className: "text-amber-800",
1358
+ children: [/* @__PURE__ */ jsx("div", {
1359
+ className: "font-semibold mb-1",
1360
+ children: t.confirmUpload.sizeLimitTitle
1361
+ }), /* @__PURE__ */ jsxs("div", {
1362
+ className: "text-sm",
1363
+ children: [/* @__PURE__ */ jsx("p", { children: t.confirmUpload.sizeLimitBody(formatFileSize(pendingUpload?.totalSize ?? 0), formatFileSize(MAX_UPLOAD_SIZE)) }), /* @__PURE__ */ jsx("p", { children: t.confirmUpload.sizeLimitHint })]
1364
+ })]
1365
+ })]
1366
+ })
1367
+ })
1368
+ ]
1369
+ })
1370
+ }),
1371
+ /* @__PURE__ */ jsx(ConfirmModal, {
1372
+ open: deleteModalOpen && !!pendingDelete,
1373
+ onClose: () => {
1374
+ setDeleteModalOpen(false);
1375
+ setPendingDelete(null);
1376
+ setCheckedItems(/* @__PURE__ */ new Set());
1377
+ },
1378
+ align: "center",
1379
+ destructive: true,
1380
+ title: t.confirmDelete.title,
1381
+ description: t.confirmDelete.text,
1382
+ confirmLabel: t.confirmDelete.action,
1383
+ onConfirm: handleDelete,
1384
+ children: /* @__PURE__ */ jsx("div", {
1385
+ className: "text-sm flex flex-col gap-2 mt-2 text-left",
1386
+ children: /* @__PURE__ */ jsx("ul", {
1387
+ className: "list-disc pl-5 max-h-40 overflow-auto theme-muted-panel py-1",
1388
+ children: pendingDelete?.map((node) => /* @__PURE__ */ jsx("li", { children: node.path }, node.path))
1389
+ })
1390
+ })
1391
+ }),
1392
+ selectedNode?.storageId && !groupsLoading && slots.accessDrawer?.({
1393
+ open: accessDrawerOpen,
1394
+ setOpen: setAccessDrawerOpen,
1395
+ groups,
1396
+ storage: storages.find((s) => s.id === selectedNode.storageId)
1397
+ })
1398
+ ]
1399
+ });
1400
+ }
1401
+ //#endregion
1402
+ export { FileExplorer as default };