@parallelworks/ui 0.19.1 → 0.21.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 (172) hide show
  1. package/README.md +1 -1
  2. package/dist/ai/adapter/openai/createOpenAIStream.js +3 -2
  3. package/dist/ai/agent/diff.d.ts +0 -1
  4. package/dist/ai/agent/diff.js +3 -2
  5. package/dist/ai/agent/toolfmt.js +2 -3
  6. package/dist/ai/ai.css +5 -0
  7. package/dist/ai/components/AllocationSelector.js +3 -2
  8. package/dist/ai/components/AttachmentManager.js +5 -2
  9. package/dist/ai/components/BranchNavigator.js +4 -2
  10. package/dist/ai/components/ChatInput.d.ts +2 -5
  11. package/dist/ai/components/ChatInput.js +10 -7
  12. package/dist/ai/components/ChatMessage.js +11 -8
  13. package/dist/ai/components/ChatSidebar.d.ts +0 -1
  14. package/dist/ai/components/ChatSidebar.js +4 -6
  15. package/dist/ai/components/ChatThread.js +5 -8
  16. package/dist/ai/components/ModelSelector.js +3 -2
  17. package/dist/ai/components/ShareDialog.js +6 -3
  18. package/dist/ai/components/SlashMenu.d.ts +0 -1
  19. package/dist/ai/components/agent/ActivityLine.d.ts +0 -1
  20. package/dist/ai/components/agent/ActivityLine.js +2 -2
  21. package/dist/ai/components/agent/AgentMessageParts.d.ts +1 -26
  22. package/dist/ai/components/agent/AgentMessageParts.js +4 -5
  23. package/dist/ai/core/ChatProvider.d.ts +2 -6
  24. package/dist/ai/core/ChatProvider.js +42 -67
  25. package/dist/ai/core/KeyboardShortcutsProvider.js +1 -1
  26. package/dist/ai/core/chatReducer.d.ts +0 -6
  27. package/dist/ai/core/chatReducer.js +1 -15
  28. package/dist/ai/core/config.js +2 -1
  29. package/dist/ai/core/useKeyboardShortcuts.d.ts +3 -1
  30. package/dist/ai/core/useKeyboardShortcuts.js +9 -11
  31. package/dist/ai/core/useSSEStream.d.ts +0 -1
  32. package/dist/ai/core/useSSEStream.js +5 -17
  33. package/dist/ai/strings.d.ts +0 -1
  34. package/dist/ai/strings.js +0 -1
  35. package/dist/ai/types.d.ts +1 -1
  36. package/dist/ai/ui/Avatar.d.ts +1 -2
  37. package/dist/ai/ui/Avatar.js +7 -18
  38. package/dist/ai/ui/Markdown.d.ts +1 -3
  39. package/dist/ai/ui/Markdown.js +1 -2
  40. package/dist/ai/ui/sidebar.d.ts +0 -1
  41. package/dist/ai/ui/sidebar.js +3 -1
  42. package/dist/ai/utils.js +1 -1
  43. package/dist/assets/editor.worker-DfmPPyOL.js +60 -0
  44. package/dist/assets/json.worker-Dqgjpq9F.js +92 -0
  45. package/dist/assets/yaml.worker-DLx0whrM.js +1441 -0
  46. package/dist/components/Avatar.js +4 -2
  47. package/dist/components/BareModal.js +1 -5
  48. package/dist/components/Breadcrumbs.js +21 -14
  49. package/dist/components/CollapsiblePanel.d.ts +1 -3
  50. package/dist/components/CollapsiblePanel.js +5 -5
  51. package/dist/components/Dropdown.d.ts +0 -4
  52. package/dist/components/Dropdown.js +5 -6
  53. package/dist/components/Listbox.js +14 -18
  54. package/dist/components/MultiSelect.js +5 -8
  55. package/dist/components/Pagination.js +1 -1
  56. package/dist/components/Provider.d.ts +0 -8
  57. package/dist/components/Provider.js +12 -4
  58. package/dist/components/SwitchToggle.js +3 -5
  59. package/dist/components/Table.d.ts +1 -3
  60. package/dist/components/dropdownUtils.js +5 -6
  61. package/dist/components/relativeTime.d.ts +0 -7
  62. package/dist/components/relativeTime.js +1 -1
  63. package/dist/duration.js +1 -1
  64. package/dist/editor/Editor.d.ts +1 -1
  65. package/dist/editor/Editor.js +1 -1
  66. package/dist/editor/EditorField.d.ts +0 -1
  67. package/dist/editor/EditorField.js +8 -8
  68. package/dist/editor/FormCodePanel.js +14 -20
  69. package/dist/editor/Monaco.js +2 -25
  70. package/dist/editor/workers.d.ts +5 -0
  71. package/dist/editor/workers.js +14 -8
  72. package/dist/editor/yaml.d.ts +9 -1
  73. package/dist/editor/yaml.worker.d.ts +1 -0
  74. package/dist/editor/yamlLanguage.d.ts +6 -0
  75. package/dist/editor/yamlLanguage.js +312 -0
  76. package/dist/file-explorer/FileExplorer.js +60 -60
  77. package/dist/file-explorer/FileExplorerContext.d.ts +1 -2
  78. package/dist/file-explorer/FileExplorerContext.js +92 -125
  79. package/dist/file-explorer/FilePreview/Inline.d.ts +1 -2
  80. package/dist/file-explorer/FilePreview/Inline.js +2 -2
  81. package/dist/file-explorer/FilePreview/Viewer.js +0 -4
  82. package/dist/file-explorer/FilePreview/index.d.ts +1 -2
  83. package/dist/file-explorer/FilePreview/index.js +3 -3
  84. package/dist/file-explorer/FilePreview/parse.d.ts +2 -1
  85. package/dist/file-explorer/FilePreview/useObjectBlobUrl.d.ts +5 -4
  86. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +23 -36
  87. package/dist/file-explorer/FilePreview/useObjectText.d.ts +1 -2
  88. package/dist/file-explorer/FilePreview/useObjectText.js +5 -7
  89. package/dist/file-explorer/FileRow.js +1 -1
  90. package/dist/file-explorer/FileRowsWindow.d.ts +5 -12
  91. package/dist/file-explorer/FileTable.d.ts +2 -6
  92. package/dist/file-explorer/LoadMoreRow.d.ts +8 -0
  93. package/dist/file-explorer/TreeView.d.ts +0 -1
  94. package/dist/file-explorer/TreeView.js +6 -6
  95. package/dist/file-explorer/helpers.js +2 -4
  96. package/dist/file-explorer/lib/fileExplorer.d.ts +0 -2
  97. package/dist/file-explorer/lib/types.d.ts +0 -1
  98. package/dist/file-explorer/lib/utils.js +15 -20
  99. package/dist/file-explorer/toasts.js +36 -34
  100. package/dist/form/Fields/BooleanField.js +5 -4
  101. package/dist/form/Fields/CheckboxGroupField.js +5 -4
  102. package/dist/form/Fields/ColorField.js +5 -4
  103. package/dist/form/Fields/DropdownField.d.ts +0 -1
  104. package/dist/form/Fields/DropdownField.js +10 -13
  105. package/dist/form/Fields/DurationField.js +2 -2
  106. package/dist/form/Fields/LabelField.js +5 -5
  107. package/dist/form/Fields/MultiDropdownField.d.ts +1 -1
  108. package/dist/form/Fields/MultiDropdownField.js +8 -11
  109. package/dist/form/Fields/NumberField.js +6 -5
  110. package/dist/form/Fields/ObjectField.d.ts +1 -2
  111. package/dist/form/Fields/ObjectField.js +22 -21
  112. package/dist/form/Fields/RadioField.js +4 -3
  113. package/dist/form/Fields/RangeField.js +7 -6
  114. package/dist/form/Fields/StringField.js +2 -1
  115. package/dist/form/Fields/TextAreaField.js +2 -1
  116. package/dist/form/Form.d.ts +7 -8
  117. package/dist/form/Form.js +52 -78
  118. package/dist/form/SpecialTypes/DropdownWrapper.d.ts +0 -2
  119. package/dist/form/SpecialTypes/DropdownWrapper.js +1 -3
  120. package/dist/form/SpecialTypes/FormikCustomDropdown.d.ts +1 -3
  121. package/dist/form/SpecialTypes/FormikCustomDropdown.js +4 -7
  122. package/dist/form/SpecialTypes/FormikCustomInput.js +13 -11
  123. package/dist/form/Wizard/WizardContainer.js +8 -25
  124. package/dist/form/Wizard/WizardNavigation.d.ts +1 -1
  125. package/dist/form/Wizard/WizardNavigation.js +5 -6
  126. package/dist/form/Wizard/WizardStepContent.d.ts +1 -1
  127. package/dist/form/Wizard/WizardStepContent.js +2 -2
  128. package/dist/form/Wizard/WizardStepIndicator.js +7 -17
  129. package/dist/form/Wizard/types.d.ts +4 -46
  130. package/dist/form/Wizard/useWizardState.d.ts +2 -10
  131. package/dist/form/Wizard/useWizardState.js +42 -92
  132. package/dist/form/Wizard/utils.js +6 -11
  133. package/dist/form/lib.js +28 -54
  134. package/dist/form/types/fieldTypes.d.ts +4 -5
  135. package/dist/form/utils/dependentOptions.d.ts +6 -0
  136. package/dist/form/utils/dependentOptions.js +21 -0
  137. package/dist/form/utils/getParentValue.d.ts +2 -1
  138. package/dist/form/utils/getParentValue.js +10 -15
  139. package/dist/form/utils/getValueUsingPath.js +1 -1
  140. package/dist/graph/AnnotationBanner.d.ts +1 -1
  141. package/dist/graph/AnnotationBanner.js +1 -1
  142. package/dist/graph/Collapse.d.ts +3 -3
  143. package/dist/graph/Collapse.js +3 -8
  144. package/dist/graph/DependencyGraph.d.ts +5 -4
  145. package/dist/graph/DependencyGraph.js +172 -222
  146. package/dist/graph/JobSummary.d.ts +10 -8
  147. package/dist/graph/JobSummary.js +13 -13
  148. package/dist/graph/MatrixGroup.d.ts +5 -17
  149. package/dist/graph/MatrixGroup.js +11 -22
  150. package/dist/graph/TreeView.d.ts +1 -3
  151. package/dist/graph/TreeView.js +104 -491
  152. package/dist/graph/types.d.ts +3 -2
  153. package/dist/graph/util.d.ts +5 -0
  154. package/dist/graph/util.js +18 -0
  155. package/dist/icons.d.ts +5 -5
  156. package/dist/icons.js +6 -6
  157. package/dist/index.d.ts +1 -1
  158. package/dist/list/ListViewControls.js +17 -35
  159. package/dist/list/RowContextMenu.js +9 -8
  160. package/dist/list/UserHoverCard.js +5 -5
  161. package/dist/logviewer/LogViewer.js +7 -9
  162. package/dist/logviewer/segments.js +2 -2
  163. package/dist/safeUrl.d.ts +6 -0
  164. package/dist/safeUrl.js +22 -0
  165. package/dist/styles/theme.css +1 -0
  166. package/dist/styles.css +1 -1
  167. package/dist/theme/index.js +5 -5
  168. package/package.json +33 -37
  169. package/dist/ai/ui/buttons.d.ts +0 -1
  170. package/dist/assets/editor.worker-BmHUIVG4.js +0 -14807
  171. package/dist/assets/json.worker-D8bJ92nw.js +0 -22207
  172. package/dist/assets/yaml.worker-C4gyGAko.js +0 -50201
@@ -1,3 +1,4 @@
1
+ import { safeUrl } from "../safeUrl.js";
1
2
  import { useNotify, useSlots, useStrings } from "../components/Provider.js";
2
3
  import { AccessIcon, AlertIcon, AngleDownIcon, ChevronRightIcon, ClipboardIcon, CollapseIcon, DownloadFileIcon, EyeIcon, FileIcon, FileTextIcon, FolderIcon, LinkIcon, RefreshIcon, ShareIcon, TrashIcon, UploadIcon } from "../icons.js";
3
4
  import { keyedByContent } from "../components/keys.js";
@@ -55,6 +56,10 @@ function UriCard({ value }) {
55
56
  ]
56
57
  });
57
58
  }
59
+ var MAX_UPLOAD_SIZE = 85899345920;
60
+ function isUploadSizeExceeded(size) {
61
+ return size > MAX_UPLOAD_SIZE;
62
+ }
58
63
  function isStorageDirectory(node) {
59
64
  return node?.type === "directory" && !!node.storageId;
60
65
  }
@@ -67,7 +72,7 @@ function getAncestorDirPaths(path) {
67
72
  const segments = (isDir ? path.slice(0, -1) : path).split("/").filter(Boolean);
68
73
  const dirSegmentCount = isDir ? segments.length : segments.length - 1;
69
74
  const ancestors = [];
70
- for (let i = 1; i <= dirSegmentCount; i++) ancestors.push(segments.slice(0, i).join("/") + "/");
75
+ for (let i = 1; i <= dirSegmentCount; i++) ancestors.push(`${segments.slice(0, i).join("/")}/`);
71
76
  return ancestors;
72
77
  }
73
78
  /** The tree's width is the host's when the tree is embedded on its own, and this
@@ -156,9 +161,10 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
156
161
  setCheckedItems(/* @__PURE__ */ new Set());
157
162
  }
158
163
  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);
164
+ const findStorage = (storageId) => storages.find((s) => s.id === storageId);
165
+ const storageCanWrite = (storageId) => findStorage(storageId)?.canWrite === true;
166
+ const storageCanUpload = (storageId) => findStorage(storageId)?.canUpload !== false;
167
+ const selectedStorage = findStorage(selectedNode?.storageId);
162
168
  const selectedNodeCanWrite = selectedStorage?.canWrite === true;
163
169
  const selectedNodeCanUpload = selectedStorage?.canUpload !== false;
164
170
  const selectedNodeCanShare = selectedStorage?.canShare !== false;
@@ -180,10 +186,6 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
180
186
  useEffect(() => clearPreviewCache, []);
181
187
  const { openMenu, contextMenu } = useRowMenu();
182
188
  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
189
  const handleToggleExpand = useCallback((path) => {
188
190
  setExpandedPaths((prev) => {
189
191
  const next = new Set(prev);
@@ -195,10 +197,10 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
195
197
  setExpandedPaths(/* @__PURE__ */ new Set([""]));
196
198
  }, []);
197
199
  const getStorageProviderAndClient = async (storageId) => {
198
- const storage = storages.find((s) => s.id === storageId);
200
+ const storage = findStorage(storageId);
199
201
  if (!storage) {
200
202
  notify.error(t.messages.storageUnavailable);
201
- return;
203
+ return null;
202
204
  }
203
205
  const { provider, client } = await getProviderAndClient(storage).catch(() => ({
204
206
  provider: null,
@@ -235,7 +237,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
235
237
  const factory = await getStorageProviderAndClient(node.storageId);
236
238
  if (!factory) throw new Error("Failed to connect to storage provider.");
237
239
  const { storage, provider, client } = factory;
238
- if (provider?.listDirectoryInput && provider?.convertDataToStorageObjects) {
240
+ if (provider.listDirectoryInput && provider.convertDataToStorageObjects) {
239
241
  const dir_path = node.root ? "" : node.relativePath ?? "";
240
242
  const input = provider.listDirectoryInput(storage, dir_path, cursor ? { cursor } : {});
241
243
  const objects = await client.listDirectory(input);
@@ -261,7 +263,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
261
263
  const errorMessage = isCorsError ? error.message || "CORS error" : error instanceof Error ? error.message : "Unknown error";
262
264
  if (owns()) {
263
265
  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}`));
266
+ updateConnectionIssuesMessage((prev) => new Map(prev).set(nodeStorageId, `Failed to load contents for ${node.displayName || node.name}: ${errorMessage}`));
265
267
  if (isCorsError) setHasCorsIssue((prev) => new Map(prev).set(nodeStorageId, true));
266
268
  }
267
269
  return { corsError: isCorsError };
@@ -282,7 +284,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
282
284
  setDetailTab("details");
283
285
  }
284
286
  setSelectedPath(path);
285
- if (node.type === "directory" && node?.storageId && !isListed(listings, path)) await fetchNodeChildren(node);
287
+ if (node.type === "directory" && node.storageId && !isListed(listings, path)) await fetchNodeChildren(node);
286
288
  };
287
289
  useEffect(() => {
288
290
  if (!normalizedSelectedPath) return;
@@ -429,18 +431,21 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
429
431
  if (selectedNode && selectedNode.type === "directory") return true;
430
432
  return false;
431
433
  };
434
+ const getFilesToDownload = () => {
435
+ if (checkedItems.size > 0) return Array.from(checkedItems).map((path) => treeMap[path]).filter((node) => node?.type === "file");
436
+ return selectedNode?.type === "file" ? [selectedNode] : [];
437
+ };
432
438
  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) {
439
+ const filesToDownload = getFilesToDownload();
440
+ const firstFile = filesToDownload[0];
441
+ if (!firstFile) {
437
442
  notify.error(t.messages.noFilesForDownload);
438
443
  return;
439
444
  }
440
- const factory = await getStorageProviderAndClient(filesToDownload[0].storageId);
445
+ const factory = await getStorageProviderAndClient(firstFile.storageId);
441
446
  if (!factory) return;
442
447
  const { provider, client } = factory;
443
- if (!provider?.getFileInput) {
448
+ if (!provider.getFileInput) {
444
449
  notify.error(t.messages.downloadUnsupported);
445
450
  return;
446
451
  }
@@ -459,7 +464,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
459
464
  const factory = await getStorageProviderAndClient(node.storageId);
460
465
  if (!factory) return;
461
466
  const { provider, client } = factory;
462
- if (!provider?.getFileInput) {
467
+ if (!provider.getFileInput) {
463
468
  notify.error(t.messages.sharingUnsupported);
464
469
  return;
465
470
  }
@@ -473,8 +478,10 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
473
478
  notify.info(t.messages.fileLinkCopied);
474
479
  };
475
480
  const triggerBrowserDownload = (url, filename) => {
481
+ const href = safeUrl(url);
482
+ if (!href) return;
476
483
  const a = document.createElement("a");
477
- a.href = url;
484
+ a.href = href;
478
485
  a.download = filename;
479
486
  document.body.appendChild(a);
480
487
  a.click();
@@ -486,14 +493,14 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
486
493
  const factory = await getStorageProviderAndClient(node.storageId);
487
494
  if (!factory) return [null, /* @__PURE__ */ new Error("Failed to connect to storage provider.")];
488
495
  const { provider, client } = factory;
489
- if (!provider?.getFileInput) return [null, /* @__PURE__ */ new Error("Preview is not supported for this storage.")];
496
+ if (!provider.getFileInput) return [null, /* @__PURE__ */ new Error("Preview is not supported for this storage.")];
490
497
  const input = provider.getFileInput(node.storageName ?? "", node, PREVIEW_URL_EXPIRES_IN, downloadFilename);
491
498
  return client.getFile(input);
492
499
  };
493
500
  const previewSiblings = useMemo(() => previewNode ? getNodeChildren(parentToChildrenMap, getParentPath(previewNode.path)).filter((n) => n.type === "file") : [], [previewNode, parentToChildrenMap]);
494
501
  const openPreview = (node) => {
495
502
  if (node.type !== "file") return;
496
- if (onOpenFile?.(node, storages.find((s) => s.id === node.storageId))) return;
503
+ if (onOpenFile?.(node, findStorage(node.storageId))) return;
497
504
  setPreviewNode(node);
498
505
  setPreviewOpen(true);
499
506
  };
@@ -505,7 +512,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
505
512
  if (!expandedPaths.has(node.path)) handleToggleExpand(node.path);
506
513
  };
507
514
  const getNodeUris = (node) => {
508
- const storage = storages.find((s) => s.id === node.storageId);
515
+ const storage = findStorage(node.storageId);
509
516
  if (!storage || !objectUris) return [];
510
517
  return objectUris(storage, getObjectKeyFromNode(node));
511
518
  };
@@ -531,7 +538,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
531
538
  triggerBrowserDownload(presignedUrl, node.name);
532
539
  };
533
540
  const deleteNode = (node) => {
534
- if (storages.find((s) => s.id === node.storageId)?.canWrite !== true) {
541
+ if (findStorage(node.storageId)?.canWrite !== true) {
535
542
  notify.error(t.messages.noWriteAccess);
536
543
  return;
537
544
  }
@@ -553,7 +560,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
553
560
  }];
554
561
  };
555
562
  const buildRowMenu = (node) => {
556
- const storage = storages.find((s) => s.id === node.storageId);
563
+ const storage = findStorage(node.storageId);
557
564
  const nodeCanWrite = storage?.canWrite === true;
558
565
  const nodeCanShare = storage?.canShare !== false;
559
566
  const isFile = node.type === "file";
@@ -646,13 +653,10 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
646
653
  });
647
654
  };
648
655
  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 "";
656
+ const filesToDownload = getFilesToDownload();
657
+ if (!downloadCommand || filesToDownload.length === 0) return "";
654
658
  const objects = filesToDownload.flatMap((node) => {
655
- const storage = storages.find((s) => s.id === node.storageId);
659
+ const storage = findStorage(node.storageId);
656
660
  return storage ? [{
657
661
  storage,
658
662
  key: getObjectKeyFromNode(node),
@@ -694,16 +698,17 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
694
698
  }
695
699
  };
696
700
  const handleDelete = async () => {
697
- if (!pendingDelete || pendingDelete.length === 0) {
701
+ const firstPending = pendingDelete?.[0];
702
+ if (!pendingDelete || !firstPending) {
698
703
  notify.error(t.messages.noItemsForDeletion);
699
704
  return;
700
705
  }
701
- const parentPath = getParentPath(pendingDelete[0].path);
706
+ const parentPath = getParentPath(firstPending.path);
702
707
  const parentNode = treeMap[parentPath];
703
- const factory = await getStorageProviderAndClient(pendingDelete[0].storageId);
708
+ const factory = await getStorageProviderAndClient(firstPending.storageId);
704
709
  if (!factory) return;
705
710
  const { storage, provider, client } = factory;
706
- if (!provider?.deleteFilesInput) {
711
+ if (!provider.deleteFilesInput) {
707
712
  notify.error(t.messages.deleteUnsupported);
708
713
  return;
709
714
  }
@@ -744,7 +749,6 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
744
749
  notify.warning(t.messages.waitForCurrentUpload);
745
750
  return;
746
751
  }
747
- setWaitForUpload(true);
748
752
  const uploadNodes = Array.from(files).map((file) => ({
749
753
  name: file.name,
750
754
  file,
@@ -757,7 +761,6 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
757
761
  totalSize
758
762
  });
759
763
  setUploadModalOpen(true);
760
- setWaitForUpload(false);
761
764
  if (fileInputRef.current) fileInputRef.current.value = "";
762
765
  if (folderInputRef.current) folderInputRef.current.value = "";
763
766
  };
@@ -771,7 +774,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
771
774
  return;
772
775
  }
773
776
  const targetNode = treeMap[pendingUpload.targetPath];
774
- if (!targetNode || !targetNode.storageId) {
777
+ if (!targetNode?.storageId) {
775
778
  notify.error(t.messages.invalidUploadTarget);
776
779
  return;
777
780
  }
@@ -783,7 +786,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
783
786
  const { storage, provider, client } = factory;
784
787
  const storageName = storage.bucketName || storage.name;
785
788
  const cleanTargetPath = targetNode.root ? "" : removeLeadingAndTrailingSlashes(targetNode.relativePath || "");
786
- if (!provider?.uploadFileInput) {
789
+ if (!provider.uploadFileInput) {
787
790
  notify.error(t.messages.uploadUnsupported);
788
791
  return;
789
792
  }
@@ -810,7 +813,6 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
810
813
  }
811
814
  setUploadModalOpen(false);
812
815
  setPendingUpload(null);
813
- setWaitForUpload(false);
814
816
  };
815
817
  const validateFolderName = (name) => {
816
818
  if (createFolderTarget?.type !== "directory") return null;
@@ -858,6 +860,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
858
860
  if (onRefresh) onRefresh();
859
861
  return { corsError: false };
860
862
  };
863
+ const downloadCommandText = generateDownloadCommand();
861
864
  return /* @__PURE__ */ jsxs("div", {
862
865
  className: "w-full h-full flex relative",
863
866
  children: [
@@ -1055,22 +1058,19 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
1055
1058
  className: "text-sm rounded-sm hover:theme-hover cursor-pointer w-full items-center px-2 py-1.5",
1056
1059
  onClick: handleDownload,
1057
1060
  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", {
1061
+ }) }), downloadCommand && downloadCommandText && /* @__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
1062
  className: "text-sm w-full text-center pointer-events-none px-2 py-1.5",
1060
1063
  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
- })()] }) })] })]
1064
+ }), /* @__PURE__ */ jsx(CopyCodeBlock, {
1065
+ textToCopy: downloadCommandText,
1066
+ children: /* @__PURE__ */ jsx("div", {
1067
+ className: "overflow-x-auto text-xs",
1068
+ children: keyedByContent(downloadCommandText.split("\n"), (l) => l).map(({ key, item: line }) => /* @__PURE__ */ jsx("div", {
1069
+ className: "whitespace-nowrap",
1070
+ children: line
1071
+ }, key))
1072
+ })
1073
+ })] }) })] })]
1074
1074
  })] }),
1075
1075
  /* @__PURE__ */ jsxs("button", {
1076
1076
  type: "button",
@@ -1078,7 +1078,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
1078
1078
  className: "btn btn-info cursor-pointer flex items-center justify-center",
1079
1079
  disabled: !selectedNode.storageId,
1080
1080
  onClick: () => setAccessDrawerOpen(true),
1081
- hidden: !groups || groups.length === 0 || !selectedNodeCanManageAccess,
1081
+ hidden: groups.length === 0 || !selectedNodeCanManageAccess,
1082
1082
  children: [/* @__PURE__ */ jsx(AccessIcon, { className: "h-4 w-4 mr-2" }), /* @__PURE__ */ jsx("span", {
1083
1083
  className: "hidden md:inline",
1084
1084
  children: t.chrome.manageAccess
@@ -1126,7 +1126,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
1126
1126
  onSelectAll: handleBatchCheckbox
1127
1127
  })
1128
1128
  }), /* @__PURE__ */ jsx(NoDataAvailablePreview, {
1129
- selectedStorage: storages.find((s) => s.id === selectedNode.storageId) || null,
1129
+ selectedStorage: selectedStorage ?? null,
1130
1130
  canWrite: selectedNodeCanWrite,
1131
1131
  onRefresh: handleRefresh,
1132
1132
  error: hasCorsIssue.get(selectedNode.storageId ?? "") ? new CorsError(connectionIssuesMessage.get(selectedNode.storageId ?? "") || "") : null,
@@ -1159,7 +1159,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
1159
1159
  })]
1160
1160
  }), effectiveTab === "preview" && previewable ? /* @__PURE__ */ jsx(FilePreviewInline, {
1161
1161
  node: selectedNode,
1162
- storage: storages.find((s) => s.id === selectedNode.storageId) ?? null,
1162
+ storage: selectedStorage ?? null,
1163
1163
  getPresignedUrl,
1164
1164
  onOpenFull: openPreview,
1165
1165
  onDownload: downloadNode,
@@ -1286,7 +1286,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
1286
1286
  open: previewOpen,
1287
1287
  node: previewNode,
1288
1288
  siblings: previewSiblings,
1289
- storage: storages.find((s) => s.id === previewNode?.storageId) ?? null,
1289
+ storage: findStorage(previewNode?.storageId) ?? null,
1290
1290
  getPresignedUrl,
1291
1291
  onNavigate: setPreviewNode,
1292
1292
  onClose: closePreview,
@@ -1393,7 +1393,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
1393
1393
  open: accessDrawerOpen,
1394
1394
  setOpen: setAccessDrawerOpen,
1395
1395
  groups,
1396
- storage: storages.find((s) => s.id === selectedNode.storageId)
1396
+ storage: selectedStorage
1397
1397
  })
1398
1398
  ]
1399
1399
  });
@@ -1,5 +1,5 @@
1
1
  import type { IFileExplorerClient, IFileExplorerProvider } from './lib/fileExplorer.js';
2
- import type { TreeNode, UploadFileProgress, UploadNode, UploadSession } from './lib/types.js';
2
+ import type { TreeNode, UploadNode, UploadSession } from './lib/types.js';
3
3
  interface IFileExplorerContextValue {
4
4
  uploadSessionMap: Map<string, UploadSession>;
5
5
  uploadQueue: string[];
@@ -7,7 +7,6 @@ interface IFileExplorerContextValue {
7
7
  getUploadSessionById: (sessionId: string) => UploadSession | undefined;
8
8
  getUploadQueueIndex: (sessionId: string) => number;
9
9
  startUploadSession: (sessionId: string) => Promise<void>;
10
- updateFileUploadProgress?: (sessionId: string, fileKey: string, progress: UploadFileProgress) => void;
11
10
  }
12
11
  interface IFileExplorerProviderProps {
13
12
  children: React.ReactNode;
@@ -1,6 +1,6 @@
1
1
  import { useNotify, useStrings } from "../components/Provider.js";
2
2
  import { keyedByContent } from "../components/keys.js";
3
- import { getUploadSessionETA, removeLeadingSlash } from "./lib/utils.js";
3
+ import { calculateUploadNodesTotalSize, getUploadSessionETA, removeLeadingSlash } from "./lib/utils.js";
4
4
  import { UploadSessionToast } from "./toasts.js";
5
5
  import { createContext, use, useRef } from "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -21,37 +21,26 @@ function FileExplorerProvider({ children }) {
21
21
  const uploadQueueRef = useRef([]);
22
22
  const isProcessingRef = useRef(false);
23
23
  const uploadSessionIdCounter = useRef(0);
24
- const uploadSessionIdPrefix = "upload-session";
25
24
  const generateUploadSessionId = () => {
26
- return `${uploadSessionIdPrefix}-${Date.now()}-${uploadSessionIdCounter.current++}`;
25
+ return `upload-session-${Date.now()}-${uploadSessionIdCounter.current++}`;
27
26
  };
28
27
  const calculateWeightedProgress = (session) => {
29
28
  if (!session.progress?.allFiles || session.progress.allFiles.size === 0) return 0;
30
29
  let totalWeightedProgress = 0;
31
30
  let totalWeight = 0;
32
31
  session.progress.allFiles.forEach((fileProgress) => {
33
- const fileSize = fileProgress.fileSizeBytes || 0;
34
- const filePercentage = fileProgress?.percentage || 0;
35
- totalWeightedProgress += filePercentage / 100 * fileSize;
32
+ const fileSize = fileProgress.fileSizeBytes;
33
+ totalWeightedProgress += (fileProgress.percentage ?? 0) / 100 * fileSize;
36
34
  totalWeight += fileSize;
37
35
  });
38
36
  return totalWeight > 0 ? totalWeightedProgress / totalWeight * 100 : 0;
39
37
  };
40
- const calculateOverallLoadedBytes = (session) => {
41
- if (!session.progress?.allFiles) return {
42
- loaded: 0,
43
- total: session.progress?.totalSizeBytes || 0
44
- };
38
+ const sumLoadedBytes = (session) => {
45
39
  let totalLoaded = 0;
46
- let totalSize = 0;
47
- session.progress.allFiles.forEach((fileProgress) => {
48
- totalLoaded += fileProgress.loadedBytes || 0;
49
- totalSize += fileProgress.fileSizeBytes || 0;
40
+ session.progress?.allFiles?.forEach((fileProgress) => {
41
+ totalLoaded += fileProgress.loadedBytes ?? 0;
50
42
  });
51
- return {
52
- loaded: totalLoaded,
53
- total: totalSize
54
- };
43
+ return totalLoaded;
55
44
  };
56
45
  const updateFileProgressInUploadSession = (session, relativePath, updates) => {
57
46
  if (!session.progress?.allFiles) return;
@@ -77,10 +66,10 @@ function FileExplorerProvider({ children }) {
77
66
  progress: {
78
67
  completedFiles: 0,
79
68
  totalFiles: uploadNodes.length,
80
- totalSizeBytes: uploadNodes.reduce((sum, node) => sum + (node.file?.size || 0), 0),
69
+ totalSizeBytes: calculateUploadNodesTotalSize(uploadNodes),
81
70
  allFiles: new Map(uploadNodes.map((file) => [file.relativePath, {
82
71
  fileKey: file.relativePath,
83
- fileSizeBytes: file.file?.size || 0,
72
+ fileSizeBytes: file.file.size,
84
73
  abortController: new AbortController(),
85
74
  status: "pending",
86
75
  percentage: 0
@@ -154,109 +143,90 @@ function FileExplorerProvider({ children }) {
154
143
  }
155
144
  session.status = "uploading";
156
145
  isProcessingRef.current = true;
146
+ const progressToast = (shown = session) => /* @__PURE__ */ jsx(UploadSessionToast, {
147
+ session: shown,
148
+ queueLength: uploadQueueRef.current.length,
149
+ onCancel: () => handleSessionCancel(session),
150
+ onCancelFile: (path) => cancelFileInSession(sessionId, path)
151
+ });
157
152
  try {
158
153
  notify.update(sessionId, {
159
- render: /* @__PURE__ */ jsx(UploadSessionToast, {
160
- session,
161
- queueLength: uploadQueueRef.current.length,
162
- onCancel: () => handleSessionCancel(session),
163
- onCancelFile: (path) => cancelFileInSession(sessionId, path)
164
- }),
154
+ render: progressToast(),
165
155
  icon: false,
166
156
  isLoading: false,
167
157
  closeButton: false,
168
158
  autoClose: false
169
159
  });
170
160
  const results = await Promise.allSettled(session.uploadNodes.map(async (node) => {
171
- if (node && node.file) {
172
- const fileProgress = session.progress?.allFiles?.get(node.relativePath);
173
- if (!fileProgress) return;
174
- if (fileProgress.status === "cancelled") return {
175
- reason: "Upload cancelled by user",
176
- status: "rejected"
177
- };
161
+ const fileProgress = session.progress?.allFiles?.get(node.relativePath);
162
+ if (!fileProgress) return;
163
+ if (fileProgress.status === "cancelled") throw new Error("Upload cancelled by user");
164
+ updateFileProgressInUploadSession(session, node.relativePath, {
165
+ status: "uploading",
166
+ loadedBytes: 0,
167
+ totalBytes: node.file.size,
168
+ percentage: 0
169
+ });
170
+ const objectPath = session.targetPath ? `${session.targetPath}/${removeLeadingSlash(node.relativePath)}` : removeLeadingSlash(node.relativePath);
171
+ const uploadFileInput = session.provider.uploadFileInput;
172
+ if (!uploadFileInput) {
173
+ updateFileProgressInUploadSession(session, node.relativePath, { status: "failed" });
174
+ throw new Error("Upload is not supported for this storage provider");
175
+ }
176
+ const input = uploadFileInput(session.storageName, objectPath, node.file);
177
+ const onProgress = (progress) => {
178
+ if (session.status === "cancelled" || !session.progress?.allFiles) return;
179
+ const currentFileProgress = session.progress.allFiles.get(node.relativePath);
180
+ if (!currentFileProgress || [
181
+ "failed",
182
+ "cancelled",
183
+ "completed"
184
+ ].includes(currentFileProgress.status ?? "")) return;
178
185
  updateFileProgressInUploadSession(session, node.relativePath, {
179
- status: "uploading",
180
- loadedBytes: 0,
181
- totalBytes: node.file.size,
182
- percentage: 0
186
+ loadedBytes: progress.loadedBytes,
187
+ totalBytes: progress.totalBytes,
188
+ percentage: progress.percentage,
189
+ status: progress.percentage === 100 ? "completed" : "uploading"
190
+ });
191
+ if (progress.percentage === 100) session.progress.completedFiles += 1;
192
+ const totalLoadedBytes = sumLoadedBytes(session);
193
+ session.progress.overallProgress = calculateWeightedProgress(session);
194
+ session.progress.totalLoadedBytes = totalLoadedBytes;
195
+ const { estimatedSecondsLeft, lastProgressTime, lastProgressBytes, shouldUpdateETA } = getUploadSessionETA(totalLoadedBytes, session.progress.totalSizeBytes, session.progress.lastProgressTime, session.progress.lastProgressBytes);
196
+ session.progress.lastProgressTime = lastProgressTime;
197
+ session.progress.lastProgressBytes = lastProgressBytes;
198
+ if (shouldUpdateETA) session.progress.estimatedSecondsLeft = estimatedSecondsLeft;
199
+ notify.update(sessionId, {
200
+ render: progressToast(),
201
+ icon: false,
202
+ isLoading: false,
203
+ closeButton: false,
204
+ autoClose: false
205
+ });
206
+ };
207
+ try {
208
+ await session.client.uploadFile(input, onProgress, fileProgress.abortController);
209
+ updateFileProgressInUploadSession(session, node.relativePath, { status: "completed" });
210
+ } catch (error) {
211
+ const errorMessage = error instanceof Error ? error.message : String(error ?? "");
212
+ const isCancelled = errorMessage.toLowerCase().includes("abort") || errorMessage.toLowerCase().includes("cancel");
213
+ updateFileProgressInUploadSession(session, node.relativePath, { status: isCancelled ? "cancelled" : "failed" });
214
+ notify.update(sessionId, {
215
+ render: progressToast({
216
+ ...session,
217
+ progress: {
218
+ completedFiles: 0,
219
+ totalFiles: 0,
220
+ totalSizeBytes: 0,
221
+ ...session.progress
222
+ }
223
+ }),
224
+ icon: false,
225
+ isLoading: false,
226
+ closeButton: session.status === "failed" || session.status === "cancelled",
227
+ autoClose: false
183
228
  });
184
- const objectPath = session.targetPath ? `${session.targetPath}/${removeLeadingSlash(node.relativePath)}` : removeLeadingSlash(node.relativePath);
185
- const uploadFileInput = session.provider.uploadFileInput;
186
- if (!uploadFileInput) return {
187
- reason: "Upload is not supported for this storage provider",
188
- status: "rejected"
189
- };
190
- const input = uploadFileInput(session.storageName, objectPath, node.file);
191
- const onProgress = (progress) => {
192
- if (session.status === "cancelled") return;
193
- if (session.progress?.allFiles) {
194
- const currentFileProgress = session.progress.allFiles.get(node.relativePath);
195
- if (!currentFileProgress) return;
196
- if ([
197
- "failed",
198
- "cancelled",
199
- "complete"
200
- ].includes(currentFileProgress.status ?? "")) return;
201
- const wasCompleted = currentFileProgress.percentage === 100;
202
- updateFileProgressInUploadSession(session, node.relativePath, {
203
- loadedBytes: progress.loadedBytes,
204
- totalBytes: progress.totalBytes,
205
- percentage: progress.percentage,
206
- status: progress.percentage === 100 ? "completed" : "uploading"
207
- });
208
- if (progress.percentage === 100 && !wasCompleted) session.progress.completedFiles += 1;
209
- const overallProgress = calculateWeightedProgress(session);
210
- const { loaded: totalLoadedBytes } = calculateOverallLoadedBytes(session);
211
- session.progress.overallProgress = overallProgress;
212
- session.progress.totalLoadedBytes = totalLoadedBytes;
213
- const { estimatedSecondsLeft, lastProgressTime, lastProgressBytes, shouldUpdateETA } = getUploadSessionETA(totalLoadedBytes, session.progress.totalSizeBytes, session.progress.lastProgressTime, session.progress.lastProgressBytes);
214
- session.progress.lastProgressTime = lastProgressTime;
215
- session.progress.lastProgressBytes = lastProgressBytes;
216
- if (shouldUpdateETA) session.progress.estimatedSecondsLeft = estimatedSecondsLeft;
217
- notify.update(sessionId, {
218
- render: /* @__PURE__ */ jsx(UploadSessionToast, {
219
- session,
220
- queueLength: uploadQueueRef.current.length,
221
- onCancel: () => handleSessionCancel(session),
222
- onCancelFile: (path) => cancelFileInSession(sessionId, path)
223
- }),
224
- icon: false,
225
- isLoading: false,
226
- closeButton: false,
227
- autoClose: false
228
- });
229
- }
230
- };
231
- try {
232
- await session.client.uploadFile(input, onProgress, fileProgress.abortController);
233
- updateFileProgressInUploadSession(session, node.relativePath, { status: "completed" });
234
- } catch (error) {
235
- const errorMessage = error instanceof Error ? error.message : String(error ?? "");
236
- const isCancelled = errorMessage.toLowerCase().includes("abort") || errorMessage.toLowerCase().includes("cancel");
237
- updateFileProgressInUploadSession(session, node.relativePath, { status: isCancelled ? "cancelled" : "failed" });
238
- notify.update(sessionId, {
239
- render: /* @__PURE__ */ jsx(UploadSessionToast, {
240
- session: {
241
- ...session,
242
- progress: {
243
- completedFiles: 0,
244
- totalFiles: 0,
245
- totalSizeBytes: 0,
246
- ...session.progress
247
- }
248
- },
249
- queueLength: uploadQueueRef.current.length,
250
- onCancel: () => handleSessionCancel(session),
251
- onCancelFile: (path) => cancelFileInSession(sessionId, path)
252
- }),
253
- icon: false,
254
- isLoading: false,
255
- closeButton: session.status === "failed" || session.status === "cancelled",
256
- autoClose: false
257
- });
258
- throw error;
259
- }
229
+ throw error;
260
230
  }
261
231
  }));
262
232
  try {
@@ -266,19 +236,16 @@ function FileExplorerProvider({ children }) {
266
236
  }
267
237
  const failedResults = results.filter((r) => r.status === "rejected");
268
238
  const actualFailedResults = failedResults.filter((result) => {
269
- if (result.status === "rejected") {
270
- const reason = result.reason?.message || result.reason?.toString() || "";
271
- return !reason.toLowerCase().includes("abort") && !reason.toLowerCase().includes("cancel");
272
- }
273
- return false;
239
+ const reason = result.reason?.message || result.reason?.toString() || "";
240
+ return !reason.toLowerCase().includes("abort") && !reason.toLowerCase().includes("cancel");
274
241
  });
275
- const abortedCount = failedResults?.length - actualFailedResults?.length;
242
+ const abortedCount = failedResults.length - actualFailedResults.length;
276
243
  const failedFileNames = actualFailedResults.map((result) => {
277
- const originalIndex = results.findIndex((r) => r === result);
244
+ const originalIndex = results.indexOf(result);
278
245
  return session.uploadNodes[originalIndex]?.name || `File #${originalIndex + 1}`;
279
- }).filter(Boolean);
280
- const successfulCount = results?.length - failedResults?.length;
281
- if (actualFailedResults?.length > 0) notify.update(sessionId, {
246
+ });
247
+ const successfulCount = results.length - failedResults.length;
248
+ if (actualFailedResults.length > 0) notify.update(sessionId, {
282
249
  render: /* @__PURE__ */ jsxs("div", {
283
250
  className: "text-md",
284
251
  children: [
@@ -344,8 +311,8 @@ function FileExplorerProvider({ children }) {
344
311
  }
345
312
  };
346
313
  const processNextUploadSessionInQueue = async () => {
347
- if (isProcessingRef.current || uploadQueueRef.current.length === 0) return;
348
314
  const nextSessionId = uploadQueueRef.current[0];
315
+ if (isProcessingRef.current || nextSessionId === void 0) return;
349
316
  await startUploadSession(nextSessionId);
350
317
  };
351
318
  const contextValue = {
@@ -7,7 +7,6 @@ interface FilePreviewInlineProps {
7
7
  onDownload: (node: TreeNode) => void;
8
8
  /** When false, skip fetching and release any held preview blob. */
9
9
  active?: boolean;
10
- showSecurityIndicator?: boolean;
11
10
  }
12
- export declare function FilePreviewInline({ node, storage, getPresignedUrl, onOpenFull, onDownload, active, showSecurityIndicator, }: FilePreviewInlineProps): import("react").JSX.Element;
11
+ export declare function FilePreviewInline({ node, storage, getPresignedUrl, onOpenFull, onDownload, active, }: FilePreviewInlineProps): import("react").JSX.Element;
13
12
  export {};