@parallelworks/ui 0.20.0 → 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.
- package/dist/ai/adapter/openai/createOpenAIStream.js +3 -2
- package/dist/ai/agent/diff.d.ts +0 -1
- package/dist/ai/agent/diff.js +3 -2
- package/dist/ai/agent/toolfmt.js +2 -3
- package/dist/ai/ai.css +5 -0
- package/dist/ai/components/AllocationSelector.js +3 -2
- package/dist/ai/components/AttachmentManager.js +1 -1
- package/dist/ai/components/BranchNavigator.js +4 -2
- package/dist/ai/components/ChatInput.d.ts +2 -5
- package/dist/ai/components/ChatInput.js +10 -7
- package/dist/ai/components/ChatMessage.js +7 -7
- package/dist/ai/components/ChatSidebar.d.ts +0 -1
- package/dist/ai/components/ChatSidebar.js +4 -6
- package/dist/ai/components/ChatThread.js +5 -8
- package/dist/ai/components/ModelSelector.js +3 -2
- package/dist/ai/components/ShareDialog.js +6 -3
- package/dist/ai/components/SlashMenu.d.ts +0 -1
- package/dist/ai/components/agent/ActivityLine.d.ts +0 -1
- package/dist/ai/components/agent/ActivityLine.js +2 -2
- package/dist/ai/components/agent/AgentMessageParts.d.ts +1 -26
- package/dist/ai/components/agent/AgentMessageParts.js +4 -5
- package/dist/ai/core/ChatProvider.d.ts +2 -6
- package/dist/ai/core/ChatProvider.js +42 -67
- package/dist/ai/core/KeyboardShortcutsProvider.js +1 -1
- package/dist/ai/core/chatReducer.d.ts +0 -6
- package/dist/ai/core/chatReducer.js +1 -15
- package/dist/ai/core/useKeyboardShortcuts.d.ts +3 -1
- package/dist/ai/core/useKeyboardShortcuts.js +9 -11
- package/dist/ai/core/useSSEStream.d.ts +0 -1
- package/dist/ai/core/useSSEStream.js +5 -17
- package/dist/ai/strings.d.ts +0 -1
- package/dist/ai/strings.js +0 -1
- package/dist/ai/types.d.ts +1 -1
- package/dist/ai/ui/Avatar.d.ts +1 -2
- package/dist/ai/ui/Avatar.js +7 -18
- package/dist/ai/ui/Markdown.d.ts +1 -3
- package/dist/ai/ui/Markdown.js +1 -2
- package/dist/ai/ui/sidebar.d.ts +0 -1
- package/dist/ai/ui/sidebar.js +3 -1
- package/dist/ai/utils.js +1 -1
- package/dist/components/Avatar.js +4 -2
- package/dist/components/BareModal.js +1 -5
- package/dist/components/Breadcrumbs.js +21 -14
- package/dist/components/CollapsiblePanel.d.ts +1 -3
- package/dist/components/CollapsiblePanel.js +5 -5
- package/dist/components/Dropdown.d.ts +0 -4
- package/dist/components/Dropdown.js +5 -6
- package/dist/components/Listbox.js +14 -18
- package/dist/components/MultiSelect.js +5 -8
- package/dist/components/Pagination.js +1 -1
- package/dist/components/Provider.d.ts +0 -8
- package/dist/components/SwitchToggle.js +3 -5
- package/dist/components/Table.d.ts +1 -3
- package/dist/components/dropdownUtils.js +5 -6
- package/dist/components/relativeTime.d.ts +0 -7
- package/dist/components/relativeTime.js +1 -1
- package/dist/duration.js +1 -1
- package/dist/editor/Editor.d.ts +1 -1
- package/dist/editor/Editor.js +1 -1
- package/dist/editor/EditorField.d.ts +0 -1
- package/dist/editor/EditorField.js +8 -8
- package/dist/editor/FormCodePanel.js +14 -20
- package/dist/editor/yamlLanguage.js +2 -2
- package/dist/file-explorer/FileExplorer.js +56 -59
- package/dist/file-explorer/FileExplorerContext.d.ts +1 -2
- package/dist/file-explorer/FileExplorerContext.js +92 -125
- package/dist/file-explorer/FilePreview/Inline.d.ts +1 -2
- package/dist/file-explorer/FilePreview/Inline.js +2 -2
- package/dist/file-explorer/FilePreview/Viewer.js +0 -4
- package/dist/file-explorer/FilePreview/index.d.ts +1 -2
- package/dist/file-explorer/FilePreview/index.js +3 -3
- package/dist/file-explorer/FilePreview/parse.d.ts +2 -1
- package/dist/file-explorer/FilePreview/useObjectBlobUrl.d.ts +5 -4
- package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +23 -36
- package/dist/file-explorer/FilePreview/useObjectText.d.ts +1 -2
- package/dist/file-explorer/FilePreview/useObjectText.js +5 -7
- package/dist/file-explorer/FileRow.js +1 -1
- package/dist/file-explorer/FileRowsWindow.d.ts +5 -12
- package/dist/file-explorer/FileTable.d.ts +2 -6
- package/dist/file-explorer/LoadMoreRow.d.ts +8 -0
- package/dist/file-explorer/TreeView.d.ts +0 -1
- package/dist/file-explorer/TreeView.js +6 -6
- package/dist/file-explorer/helpers.js +2 -4
- package/dist/file-explorer/lib/fileExplorer.d.ts +0 -2
- package/dist/file-explorer/lib/types.d.ts +0 -1
- package/dist/file-explorer/lib/utils.js +15 -20
- package/dist/file-explorer/toasts.js +36 -34
- package/dist/form/Fields/BooleanField.js +5 -4
- package/dist/form/Fields/CheckboxGroupField.js +5 -4
- package/dist/form/Fields/ColorField.js +5 -4
- package/dist/form/Fields/DropdownField.d.ts +0 -1
- package/dist/form/Fields/DropdownField.js +10 -13
- package/dist/form/Fields/DurationField.js +2 -2
- package/dist/form/Fields/LabelField.js +5 -5
- package/dist/form/Fields/MultiDropdownField.d.ts +1 -1
- package/dist/form/Fields/MultiDropdownField.js +8 -11
- package/dist/form/Fields/NumberField.js +6 -5
- package/dist/form/Fields/ObjectField.d.ts +1 -2
- package/dist/form/Fields/ObjectField.js +22 -21
- package/dist/form/Fields/RadioField.js +4 -3
- package/dist/form/Fields/RangeField.js +7 -6
- package/dist/form/Fields/StringField.js +2 -1
- package/dist/form/Fields/TextAreaField.js +2 -1
- package/dist/form/Form.d.ts +7 -8
- package/dist/form/Form.js +52 -78
- package/dist/form/SpecialTypes/DropdownWrapper.d.ts +0 -2
- package/dist/form/SpecialTypes/DropdownWrapper.js +1 -3
- package/dist/form/SpecialTypes/FormikCustomDropdown.d.ts +1 -3
- package/dist/form/SpecialTypes/FormikCustomDropdown.js +4 -7
- package/dist/form/SpecialTypes/FormikCustomInput.js +13 -11
- package/dist/form/Wizard/WizardContainer.js +8 -25
- package/dist/form/Wizard/WizardNavigation.d.ts +1 -1
- package/dist/form/Wizard/WizardNavigation.js +5 -6
- package/dist/form/Wizard/WizardStepContent.d.ts +1 -1
- package/dist/form/Wizard/WizardStepContent.js +2 -2
- package/dist/form/Wizard/WizardStepIndicator.js +7 -17
- package/dist/form/Wizard/types.d.ts +4 -46
- package/dist/form/Wizard/useWizardState.d.ts +2 -10
- package/dist/form/Wizard/useWizardState.js +42 -92
- package/dist/form/Wizard/utils.js +6 -11
- package/dist/form/lib.js +28 -54
- package/dist/form/types/fieldTypes.d.ts +4 -5
- package/dist/form/utils/dependentOptions.d.ts +6 -0
- package/dist/form/utils/dependentOptions.js +21 -0
- package/dist/form/utils/getParentValue.d.ts +2 -1
- package/dist/form/utils/getParentValue.js +10 -15
- package/dist/form/utils/getValueUsingPath.js +1 -1
- package/dist/graph/AnnotationBanner.d.ts +1 -1
- package/dist/graph/AnnotationBanner.js +1 -1
- package/dist/graph/Collapse.d.ts +3 -3
- package/dist/graph/Collapse.js +3 -8
- package/dist/graph/DependencyGraph.d.ts +5 -4
- package/dist/graph/DependencyGraph.js +172 -222
- package/dist/graph/JobSummary.d.ts +10 -8
- package/dist/graph/JobSummary.js +13 -13
- package/dist/graph/MatrixGroup.d.ts +5 -17
- package/dist/graph/MatrixGroup.js +11 -22
- package/dist/graph/TreeView.d.ts +1 -3
- package/dist/graph/TreeView.js +104 -491
- package/dist/graph/types.d.ts +3 -2
- package/dist/graph/util.d.ts +5 -0
- package/dist/graph/util.js +18 -0
- package/dist/icons.d.ts +2 -2
- package/dist/icons.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/list/ListViewControls.js +17 -35
- package/dist/list/RowContextMenu.js +7 -7
- package/dist/logviewer/LogViewer.js +7 -9
- package/dist/logviewer/segments.js +2 -2
- package/dist/styles/theme.css +1 -0
- package/dist/styles.css +1 -1
- package/dist/theme/index.js +5 -5
- package/package.json +28 -30
- package/dist/ai/ui/buttons.d.ts +0 -1
|
@@ -56,6 +56,10 @@ function UriCard({ value }) {
|
|
|
56
56
|
]
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
|
+
var MAX_UPLOAD_SIZE = 85899345920;
|
|
60
|
+
function isUploadSizeExceeded(size) {
|
|
61
|
+
return size > MAX_UPLOAD_SIZE;
|
|
62
|
+
}
|
|
59
63
|
function isStorageDirectory(node) {
|
|
60
64
|
return node?.type === "directory" && !!node.storageId;
|
|
61
65
|
}
|
|
@@ -68,7 +72,7 @@ function getAncestorDirPaths(path) {
|
|
|
68
72
|
const segments = (isDir ? path.slice(0, -1) : path).split("/").filter(Boolean);
|
|
69
73
|
const dirSegmentCount = isDir ? segments.length : segments.length - 1;
|
|
70
74
|
const ancestors = [];
|
|
71
|
-
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("/")}/`);
|
|
72
76
|
return ancestors;
|
|
73
77
|
}
|
|
74
78
|
/** The tree's width is the host's when the tree is embedded on its own, and this
|
|
@@ -157,9 +161,10 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
157
161
|
setCheckedItems(/* @__PURE__ */ new Set());
|
|
158
162
|
}
|
|
159
163
|
const selectedNodeChildren = useMemo(() => selectedNode ? getNodeChildren(parentToChildrenMap, selectedNode.path) : [], [parentToChildrenMap, selectedNode]);
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
const
|
|
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);
|
|
163
168
|
const selectedNodeCanWrite = selectedStorage?.canWrite === true;
|
|
164
169
|
const selectedNodeCanUpload = selectedStorage?.canUpload !== false;
|
|
165
170
|
const selectedNodeCanShare = selectedStorage?.canShare !== false;
|
|
@@ -181,10 +186,6 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
181
186
|
useEffect(() => clearPreviewCache, []);
|
|
182
187
|
const { openMenu, contextMenu } = useRowMenu();
|
|
183
188
|
const [sidebarWidth, setSidebarWidth] = useLocalStorage(SIDEBAR_WIDTH_STORAGE_KEY, 300);
|
|
184
|
-
const MAX_UPLOAD_SIZE = 85899345920;
|
|
185
|
-
const isUploadSizeExceeded = (size) => {
|
|
186
|
-
return size > MAX_UPLOAD_SIZE;
|
|
187
|
-
};
|
|
188
189
|
const handleToggleExpand = useCallback((path) => {
|
|
189
190
|
setExpandedPaths((prev) => {
|
|
190
191
|
const next = new Set(prev);
|
|
@@ -196,10 +197,10 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
196
197
|
setExpandedPaths(/* @__PURE__ */ new Set([""]));
|
|
197
198
|
}, []);
|
|
198
199
|
const getStorageProviderAndClient = async (storageId) => {
|
|
199
|
-
const storage =
|
|
200
|
+
const storage = findStorage(storageId);
|
|
200
201
|
if (!storage) {
|
|
201
202
|
notify.error(t.messages.storageUnavailable);
|
|
202
|
-
return;
|
|
203
|
+
return null;
|
|
203
204
|
}
|
|
204
205
|
const { provider, client } = await getProviderAndClient(storage).catch(() => ({
|
|
205
206
|
provider: null,
|
|
@@ -236,7 +237,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
236
237
|
const factory = await getStorageProviderAndClient(node.storageId);
|
|
237
238
|
if (!factory) throw new Error("Failed to connect to storage provider.");
|
|
238
239
|
const { storage, provider, client } = factory;
|
|
239
|
-
if (provider
|
|
240
|
+
if (provider.listDirectoryInput && provider.convertDataToStorageObjects) {
|
|
240
241
|
const dir_path = node.root ? "" : node.relativePath ?? "";
|
|
241
242
|
const input = provider.listDirectoryInput(storage, dir_path, cursor ? { cursor } : {});
|
|
242
243
|
const objects = await client.listDirectory(input);
|
|
@@ -262,7 +263,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
262
263
|
const errorMessage = isCorsError ? error.message || "CORS error" : error instanceof Error ? error.message : "Unknown error";
|
|
263
264
|
if (owns()) {
|
|
264
265
|
setListings((prev) => recordListingError(options?.replace ? clearListingsUnder(prev, node.path) : prev, node.path, errorMessage));
|
|
265
|
-
updateConnectionIssuesMessage((prev) => new Map(prev).set(nodeStorageId, `Failed to load contents for ${node
|
|
266
|
+
updateConnectionIssuesMessage((prev) => new Map(prev).set(nodeStorageId, `Failed to load contents for ${node.displayName || node.name}: ${errorMessage}`));
|
|
266
267
|
if (isCorsError) setHasCorsIssue((prev) => new Map(prev).set(nodeStorageId, true));
|
|
267
268
|
}
|
|
268
269
|
return { corsError: isCorsError };
|
|
@@ -283,7 +284,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
283
284
|
setDetailTab("details");
|
|
284
285
|
}
|
|
285
286
|
setSelectedPath(path);
|
|
286
|
-
if (node.type === "directory" && node
|
|
287
|
+
if (node.type === "directory" && node.storageId && !isListed(listings, path)) await fetchNodeChildren(node);
|
|
287
288
|
};
|
|
288
289
|
useEffect(() => {
|
|
289
290
|
if (!normalizedSelectedPath) return;
|
|
@@ -430,18 +431,21 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
430
431
|
if (selectedNode && selectedNode.type === "directory") return true;
|
|
431
432
|
return false;
|
|
432
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
|
+
};
|
|
433
438
|
const handleDownload = async () => {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if (filesToDownload.length === 0) {
|
|
439
|
+
const filesToDownload = getFilesToDownload();
|
|
440
|
+
const firstFile = filesToDownload[0];
|
|
441
|
+
if (!firstFile) {
|
|
438
442
|
notify.error(t.messages.noFilesForDownload);
|
|
439
443
|
return;
|
|
440
444
|
}
|
|
441
|
-
const factory = await getStorageProviderAndClient(
|
|
445
|
+
const factory = await getStorageProviderAndClient(firstFile.storageId);
|
|
442
446
|
if (!factory) return;
|
|
443
447
|
const { provider, client } = factory;
|
|
444
|
-
if (!provider
|
|
448
|
+
if (!provider.getFileInput) {
|
|
445
449
|
notify.error(t.messages.downloadUnsupported);
|
|
446
450
|
return;
|
|
447
451
|
}
|
|
@@ -460,7 +464,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
460
464
|
const factory = await getStorageProviderAndClient(node.storageId);
|
|
461
465
|
if (!factory) return;
|
|
462
466
|
const { provider, client } = factory;
|
|
463
|
-
if (!provider
|
|
467
|
+
if (!provider.getFileInput) {
|
|
464
468
|
notify.error(t.messages.sharingUnsupported);
|
|
465
469
|
return;
|
|
466
470
|
}
|
|
@@ -489,14 +493,14 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
489
493
|
const factory = await getStorageProviderAndClient(node.storageId);
|
|
490
494
|
if (!factory) return [null, /* @__PURE__ */ new Error("Failed to connect to storage provider.")];
|
|
491
495
|
const { provider, client } = factory;
|
|
492
|
-
if (!provider
|
|
496
|
+
if (!provider.getFileInput) return [null, /* @__PURE__ */ new Error("Preview is not supported for this storage.")];
|
|
493
497
|
const input = provider.getFileInput(node.storageName ?? "", node, PREVIEW_URL_EXPIRES_IN, downloadFilename);
|
|
494
498
|
return client.getFile(input);
|
|
495
499
|
};
|
|
496
500
|
const previewSiblings = useMemo(() => previewNode ? getNodeChildren(parentToChildrenMap, getParentPath(previewNode.path)).filter((n) => n.type === "file") : [], [previewNode, parentToChildrenMap]);
|
|
497
501
|
const openPreview = (node) => {
|
|
498
502
|
if (node.type !== "file") return;
|
|
499
|
-
if (onOpenFile?.(node,
|
|
503
|
+
if (onOpenFile?.(node, findStorage(node.storageId))) return;
|
|
500
504
|
setPreviewNode(node);
|
|
501
505
|
setPreviewOpen(true);
|
|
502
506
|
};
|
|
@@ -508,7 +512,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
508
512
|
if (!expandedPaths.has(node.path)) handleToggleExpand(node.path);
|
|
509
513
|
};
|
|
510
514
|
const getNodeUris = (node) => {
|
|
511
|
-
const storage =
|
|
515
|
+
const storage = findStorage(node.storageId);
|
|
512
516
|
if (!storage || !objectUris) return [];
|
|
513
517
|
return objectUris(storage, getObjectKeyFromNode(node));
|
|
514
518
|
};
|
|
@@ -534,7 +538,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
534
538
|
triggerBrowserDownload(presignedUrl, node.name);
|
|
535
539
|
};
|
|
536
540
|
const deleteNode = (node) => {
|
|
537
|
-
if (
|
|
541
|
+
if (findStorage(node.storageId)?.canWrite !== true) {
|
|
538
542
|
notify.error(t.messages.noWriteAccess);
|
|
539
543
|
return;
|
|
540
544
|
}
|
|
@@ -556,7 +560,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
556
560
|
}];
|
|
557
561
|
};
|
|
558
562
|
const buildRowMenu = (node) => {
|
|
559
|
-
const storage =
|
|
563
|
+
const storage = findStorage(node.storageId);
|
|
560
564
|
const nodeCanWrite = storage?.canWrite === true;
|
|
561
565
|
const nodeCanShare = storage?.canShare !== false;
|
|
562
566
|
const isFile = node.type === "file";
|
|
@@ -649,13 +653,10 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
649
653
|
});
|
|
650
654
|
};
|
|
651
655
|
const generateDownloadCommand = () => {
|
|
652
|
-
|
|
653
|
-
if (
|
|
654
|
-
else if (selectedNode && selectedNode.type === "file") filesToDownload = [selectedNode];
|
|
655
|
-
if (filesToDownload.length === 0) return "";
|
|
656
|
-
if (!downloadCommand) return "";
|
|
656
|
+
const filesToDownload = getFilesToDownload();
|
|
657
|
+
if (!downloadCommand || filesToDownload.length === 0) return "";
|
|
657
658
|
const objects = filesToDownload.flatMap((node) => {
|
|
658
|
-
const storage =
|
|
659
|
+
const storage = findStorage(node.storageId);
|
|
659
660
|
return storage ? [{
|
|
660
661
|
storage,
|
|
661
662
|
key: getObjectKeyFromNode(node),
|
|
@@ -697,16 +698,17 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
697
698
|
}
|
|
698
699
|
};
|
|
699
700
|
const handleDelete = async () => {
|
|
700
|
-
|
|
701
|
+
const firstPending = pendingDelete?.[0];
|
|
702
|
+
if (!pendingDelete || !firstPending) {
|
|
701
703
|
notify.error(t.messages.noItemsForDeletion);
|
|
702
704
|
return;
|
|
703
705
|
}
|
|
704
|
-
const parentPath = getParentPath(
|
|
706
|
+
const parentPath = getParentPath(firstPending.path);
|
|
705
707
|
const parentNode = treeMap[parentPath];
|
|
706
|
-
const factory = await getStorageProviderAndClient(
|
|
708
|
+
const factory = await getStorageProviderAndClient(firstPending.storageId);
|
|
707
709
|
if (!factory) return;
|
|
708
710
|
const { storage, provider, client } = factory;
|
|
709
|
-
if (!provider
|
|
711
|
+
if (!provider.deleteFilesInput) {
|
|
710
712
|
notify.error(t.messages.deleteUnsupported);
|
|
711
713
|
return;
|
|
712
714
|
}
|
|
@@ -747,7 +749,6 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
747
749
|
notify.warning(t.messages.waitForCurrentUpload);
|
|
748
750
|
return;
|
|
749
751
|
}
|
|
750
|
-
setWaitForUpload(true);
|
|
751
752
|
const uploadNodes = Array.from(files).map((file) => ({
|
|
752
753
|
name: file.name,
|
|
753
754
|
file,
|
|
@@ -760,7 +761,6 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
760
761
|
totalSize
|
|
761
762
|
});
|
|
762
763
|
setUploadModalOpen(true);
|
|
763
|
-
setWaitForUpload(false);
|
|
764
764
|
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
765
765
|
if (folderInputRef.current) folderInputRef.current.value = "";
|
|
766
766
|
};
|
|
@@ -774,7 +774,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
774
774
|
return;
|
|
775
775
|
}
|
|
776
776
|
const targetNode = treeMap[pendingUpload.targetPath];
|
|
777
|
-
if (!targetNode
|
|
777
|
+
if (!targetNode?.storageId) {
|
|
778
778
|
notify.error(t.messages.invalidUploadTarget);
|
|
779
779
|
return;
|
|
780
780
|
}
|
|
@@ -786,7 +786,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
786
786
|
const { storage, provider, client } = factory;
|
|
787
787
|
const storageName = storage.bucketName || storage.name;
|
|
788
788
|
const cleanTargetPath = targetNode.root ? "" : removeLeadingAndTrailingSlashes(targetNode.relativePath || "");
|
|
789
|
-
if (!provider
|
|
789
|
+
if (!provider.uploadFileInput) {
|
|
790
790
|
notify.error(t.messages.uploadUnsupported);
|
|
791
791
|
return;
|
|
792
792
|
}
|
|
@@ -813,7 +813,6 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
813
813
|
}
|
|
814
814
|
setUploadModalOpen(false);
|
|
815
815
|
setPendingUpload(null);
|
|
816
|
-
setWaitForUpload(false);
|
|
817
816
|
};
|
|
818
817
|
const validateFolderName = (name) => {
|
|
819
818
|
if (createFolderTarget?.type !== "directory") return null;
|
|
@@ -861,6 +860,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
861
860
|
if (onRefresh) onRefresh();
|
|
862
861
|
return { corsError: false };
|
|
863
862
|
};
|
|
863
|
+
const downloadCommandText = generateDownloadCommand();
|
|
864
864
|
return /* @__PURE__ */ jsxs("div", {
|
|
865
865
|
className: "w-full h-full flex relative",
|
|
866
866
|
children: [
|
|
@@ -1058,22 +1058,19 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
1058
1058
|
className: "text-sm rounded-sm hover:theme-hover cursor-pointer w-full items-center px-2 py-1.5",
|
|
1059
1059
|
onClick: handleDownload,
|
|
1060
1060
|
children: t.chrome.downloadFromBrowser
|
|
1061
|
-
}) }), downloadCommand &&
|
|
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", {
|
|
1062
1062
|
className: "text-sm w-full text-center pointer-events-none px-2 py-1.5",
|
|
1063
1063
|
children: downloadCommand.label
|
|
1064
|
-
}), (
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
1069
|
-
className: "
|
|
1070
|
-
children:
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
})
|
|
1075
|
-
});
|
|
1076
|
-
})()] }) })] })]
|
|
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
|
+
})] }) })] })]
|
|
1077
1074
|
})] }),
|
|
1078
1075
|
/* @__PURE__ */ jsxs("button", {
|
|
1079
1076
|
type: "button",
|
|
@@ -1081,7 +1078,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
1081
1078
|
className: "btn btn-info cursor-pointer flex items-center justify-center",
|
|
1082
1079
|
disabled: !selectedNode.storageId,
|
|
1083
1080
|
onClick: () => setAccessDrawerOpen(true),
|
|
1084
|
-
hidden:
|
|
1081
|
+
hidden: groups.length === 0 || !selectedNodeCanManageAccess,
|
|
1085
1082
|
children: [/* @__PURE__ */ jsx(AccessIcon, { className: "h-4 w-4 mr-2" }), /* @__PURE__ */ jsx("span", {
|
|
1086
1083
|
className: "hidden md:inline",
|
|
1087
1084
|
children: t.chrome.manageAccess
|
|
@@ -1129,7 +1126,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
1129
1126
|
onSelectAll: handleBatchCheckbox
|
|
1130
1127
|
})
|
|
1131
1128
|
}), /* @__PURE__ */ jsx(NoDataAvailablePreview, {
|
|
1132
|
-
selectedStorage:
|
|
1129
|
+
selectedStorage: selectedStorage ?? null,
|
|
1133
1130
|
canWrite: selectedNodeCanWrite,
|
|
1134
1131
|
onRefresh: handleRefresh,
|
|
1135
1132
|
error: hasCorsIssue.get(selectedNode.storageId ?? "") ? new CorsError(connectionIssuesMessage.get(selectedNode.storageId ?? "") || "") : null,
|
|
@@ -1162,7 +1159,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
1162
1159
|
})]
|
|
1163
1160
|
}), effectiveTab === "preview" && previewable ? /* @__PURE__ */ jsx(FilePreviewInline, {
|
|
1164
1161
|
node: selectedNode,
|
|
1165
|
-
storage:
|
|
1162
|
+
storage: selectedStorage ?? null,
|
|
1166
1163
|
getPresignedUrl,
|
|
1167
1164
|
onOpenFull: openPreview,
|
|
1168
1165
|
onDownload: downloadNode,
|
|
@@ -1289,7 +1286,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
1289
1286
|
open: previewOpen,
|
|
1290
1287
|
node: previewNode,
|
|
1291
1288
|
siblings: previewSiblings,
|
|
1292
|
-
storage:
|
|
1289
|
+
storage: findStorage(previewNode?.storageId) ?? null,
|
|
1293
1290
|
getPresignedUrl,
|
|
1294
1291
|
onNavigate: setPreviewNode,
|
|
1295
1292
|
onClose: closePreview,
|
|
@@ -1396,7 +1393,7 @@ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHie
|
|
|
1396
1393
|
open: accessDrawerOpen,
|
|
1397
1394
|
setOpen: setAccessDrawerOpen,
|
|
1398
1395
|
groups,
|
|
1399
|
-
storage:
|
|
1396
|
+
storage: selectedStorage
|
|
1400
1397
|
})
|
|
1401
1398
|
]
|
|
1402
1399
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IFileExplorerClient, IFileExplorerProvider } from './lib/fileExplorer.js';
|
|
2
|
-
import type { TreeNode,
|
|
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
|
|
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
|
|
34
|
-
|
|
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
|
|
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
|
-
|
|
47
|
-
|
|
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
|
|
69
|
+
totalSizeBytes: calculateUploadNodesTotalSize(uploadNodes),
|
|
81
70
|
allFiles: new Map(uploadNodes.map((file) => [file.relativePath, {
|
|
82
71
|
fileKey: file.relativePath,
|
|
83
|
-
fileSizeBytes: file.file
|
|
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:
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
percentage:
|
|
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
|
-
|
|
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
|
-
|
|
270
|
-
|
|
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
|
|
242
|
+
const abortedCount = failedResults.length - actualFailedResults.length;
|
|
276
243
|
const failedFileNames = actualFailedResults.map((result) => {
|
|
277
|
-
const originalIndex = results.
|
|
244
|
+
const originalIndex = results.indexOf(result);
|
|
278
245
|
return session.uploadNodes[originalIndex]?.name || `File #${originalIndex + 1}`;
|
|
279
|
-
})
|
|
280
|
-
const successfulCount = results
|
|
281
|
-
if (actualFailedResults
|
|
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,
|
|
11
|
+
export declare function FilePreviewInline({ node, storage, getPresignedUrl, onOpenFull, onDownload, active, }: FilePreviewInlineProps): import("react").JSX.Element;
|
|
13
12
|
export {};
|
|
@@ -7,7 +7,7 @@ import { usePresignedUrl } from "./usePresignedUrl.js";
|
|
|
7
7
|
import { FilePreviewViewer } from "./Viewer.js";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
//#region src/file-explorer/FilePreview/Inline.tsx
|
|
10
|
-
function FilePreviewInline({ node, storage, getPresignedUrl, onOpenFull, onDownload, active = true
|
|
10
|
+
function FilePreviewInline({ node, storage, getPresignedUrl, onOpenFull, onDownload, active = true }) {
|
|
11
11
|
const t = useStrings().fileExplorer;
|
|
12
12
|
const { url, loading, error } = usePresignedUrl(node, getPresignedUrl, {
|
|
13
13
|
enabled: active,
|
|
@@ -31,7 +31,7 @@ function FilePreviewInline({ node, storage, getPresignedUrl, onOpenFull, onDownl
|
|
|
31
31
|
className: "flex shrink-0 items-center justify-between gap-3 border-t theme-border px-4 py-2.5",
|
|
32
32
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
33
33
|
className: "flex items-center gap-2 text-xs theme-muted-text",
|
|
34
|
-
children: [/* @__PURE__ */ jsxs("span", { children: [typeof node.size === "number" && formatFileSize(node.size), node.contentType && ` · ${node.contentType}`] }),
|
|
34
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [typeof node.size === "number" && formatFileSize(node.size), node.contentType && ` · ${node.contentType}`] }), url && /* @__PURE__ */ jsx(StreamedBadge, {})]
|
|
35
35
|
}), /* @__PURE__ */ jsxs("button", {
|
|
36
36
|
type: "button",
|
|
37
37
|
className: "btn btn-neutral cursor-pointer",
|