s3kit 0.1.0 → 0.1.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.
- package/README.md +15 -1
- package/dist/adapters/express.cjs +99 -3
- package/dist/adapters/express.cjs.map +1 -1
- package/dist/adapters/express.d.cts +2 -2
- package/dist/adapters/express.d.ts +2 -2
- package/dist/adapters/express.js +99 -3
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/fetch.cjs +99 -3
- package/dist/adapters/fetch.cjs.map +1 -1
- package/dist/adapters/fetch.d.cts +2 -2
- package/dist/adapters/fetch.d.ts +2 -2
- package/dist/adapters/fetch.js +99 -3
- package/dist/adapters/fetch.js.map +1 -1
- package/dist/adapters/next.cjs +386 -20
- package/dist/adapters/next.cjs.map +1 -1
- package/dist/adapters/next.d.cts +2 -2
- package/dist/adapters/next.d.ts +2 -2
- package/dist/adapters/next.js +387 -20
- package/dist/adapters/next.js.map +1 -1
- package/dist/client/index.cjs +15 -1
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +12 -2
- package/dist/client/index.d.ts +12 -2
- package/dist/client/index.js +15 -1
- package/dist/client/index.js.map +1 -1
- package/dist/core/index.cjs +300 -19
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +8 -3
- package/dist/core/index.d.ts +8 -3
- package/dist/core/index.js +299 -18
- package/dist/core/index.js.map +1 -1
- package/dist/http/index.cjs +99 -3
- package/dist/http/index.cjs.map +1 -1
- package/dist/http/index.d.cts +5 -2
- package/dist/http/index.d.ts +5 -2
- package/dist/http/index.js +99 -3
- package/dist/http/index.js.map +1 -1
- package/dist/index.cjs +403 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +403 -21
- package/dist/index.js.map +1 -1
- package/dist/{manager-BbmXpgXN.d.ts → manager-BtW1-sC0.d.ts} +11 -1
- package/dist/{manager-gIjo-t8h.d.cts → manager-DSsCYKEz.d.cts} +11 -1
- package/dist/react/index.cjs +334 -31
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +334 -31
- package/dist/react/index.js.map +1 -1
- package/dist/{types-g2IYvH3O.d.cts → types-B0yU5sod.d.cts} +51 -3
- package/dist/{types-g2IYvH3O.d.ts → types-B0yU5sod.d.ts} +51 -3
- package/package.json +1 -1
package/dist/react/index.cjs
CHANGED
|
@@ -123,11 +123,25 @@ var S3FileManagerClient = class {
|
|
|
123
123
|
getPreviewUrl(options) {
|
|
124
124
|
return fetchJson(this.f, this.endpoint("/preview"), options);
|
|
125
125
|
}
|
|
126
|
+
getFolderLock(options) {
|
|
127
|
+
return fetchJson(this.f, this.endpoint("/folder/lock/get"), options);
|
|
128
|
+
}
|
|
129
|
+
getFileAttributes(options) {
|
|
130
|
+
return fetchJson(this.f, this.endpoint("/file/attributes/get"), options);
|
|
131
|
+
}
|
|
132
|
+
setFileAttributes(options) {
|
|
133
|
+
return fetchJson(this.f, this.endpoint("/file/attributes/set"), options);
|
|
134
|
+
}
|
|
126
135
|
async uploadFiles(args) {
|
|
127
136
|
const prepare = {
|
|
128
137
|
items: args.files.map((f) => ({
|
|
129
138
|
path: f.path,
|
|
130
|
-
contentType: f.contentType ?? f.file.type
|
|
139
|
+
contentType: f.contentType ?? f.file.type,
|
|
140
|
+
...f.cacheControl !== void 0 ? { cacheControl: f.cacheControl } : {},
|
|
141
|
+
...f.contentDisposition !== void 0 ? { contentDisposition: f.contentDisposition } : {},
|
|
142
|
+
...f.metadata !== void 0 ? { metadata: f.metadata } : {},
|
|
143
|
+
...f.expiresAt !== void 0 ? { expiresAt: f.expiresAt } : {},
|
|
144
|
+
...f.ifNoneMatch !== void 0 ? { ifNoneMatch: f.ifNoneMatch } : {}
|
|
131
145
|
})),
|
|
132
146
|
...args.expiresInSeconds !== void 0 ? { expiresInSeconds: args.expiresInSeconds } : {}
|
|
133
147
|
};
|
|
@@ -605,10 +619,15 @@ function FileManager({
|
|
|
605
619
|
const [hoverRow, setHoverRow] = (0, import_react6.useState)(null);
|
|
606
620
|
const [previewData, setPreviewData] = (0, import_react6.useState)(null);
|
|
607
621
|
const [previewDisplay, setPreviewDisplay] = (0, import_react6.useState)(null);
|
|
622
|
+
const [fileAttributes, setFileAttributes] = (0, import_react6.useState)(null);
|
|
623
|
+
const [attributesLoading, setAttributesLoading] = (0, import_react6.useState)(false);
|
|
624
|
+
const [attributesError, setAttributesError] = (0, import_react6.useState)(null);
|
|
608
625
|
const [isPreviewClosing, setIsPreviewClosing] = (0, import_react6.useState)(false);
|
|
609
626
|
const [sidebarWidth, setSidebarWidth] = (0, import_react6.useState)(320);
|
|
610
627
|
const [isResizing, setIsResizing] = (0, import_react6.useState)(false);
|
|
611
628
|
const [inlinePreviews, setInlinePreviews] = (0, import_react6.useState)({});
|
|
629
|
+
const [folderLocks, setFolderLocks] = (0, import_react6.useState)({});
|
|
630
|
+
const [pendingFolderMoves, setPendingFolderMoves] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
|
|
612
631
|
const [isSelectionMode, setIsSelectionMode] = (0, import_react6.useState)(false);
|
|
613
632
|
const [searchQuery, setSearchQuery] = (0, import_react6.useState)("");
|
|
614
633
|
const [sortBy, setSortBy] = (0, import_react6.useState)("name");
|
|
@@ -659,7 +678,111 @@ function FileManager({
|
|
|
659
678
|
const longPressTimerRef = (0, import_react6.useRef)(null);
|
|
660
679
|
const suppressClickRef = (0, import_react6.useRef)(false);
|
|
661
680
|
const dragSelectionBaseRef = (0, import_react6.useRef)(/* @__PURE__ */ new Set());
|
|
681
|
+
const requestFolderLock = (0, import_react6.useCallback)(
|
|
682
|
+
(path2) => {
|
|
683
|
+
if (folderLocks[path2]) return;
|
|
684
|
+
setFolderLocks((prev) => ({ ...prev, [path2]: { status: "loading" } }));
|
|
685
|
+
const run = async () => {
|
|
686
|
+
try {
|
|
687
|
+
const out = await client.getFolderLock({ path: path2 });
|
|
688
|
+
const isActive = out?.expiresAt && new Date(out.expiresAt).getTime() > Date.now() ? true : false;
|
|
689
|
+
setFolderLocks((prev) => ({
|
|
690
|
+
...prev,
|
|
691
|
+
[path2]: isActive && out ? { status: "locked", lock: out } : { status: "unlocked" }
|
|
692
|
+
}));
|
|
693
|
+
} catch {
|
|
694
|
+
setFolderLocks((prev) => ({ ...prev, [path2]: { status: "unlocked" } }));
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
void run();
|
|
698
|
+
},
|
|
699
|
+
[client, folderLocks]
|
|
700
|
+
);
|
|
701
|
+
const getFolderLockLabel = (0, import_react6.useCallback)(
|
|
702
|
+
(entry) => {
|
|
703
|
+
if (entry.type !== "folder") return null;
|
|
704
|
+
if (entry.path === `${TRASH_PATH}/`) return null;
|
|
705
|
+
if (pendingFolderMoves.has(entry.path)) return "Renaming";
|
|
706
|
+
const lock = folderLocks[entry.path];
|
|
707
|
+
if (lock?.status === "locked") return "Locked";
|
|
708
|
+
return null;
|
|
709
|
+
},
|
|
710
|
+
[folderLocks, pendingFolderMoves]
|
|
711
|
+
);
|
|
712
|
+
const handleEntryHover = (0, import_react6.useCallback)(
|
|
713
|
+
(entry) => {
|
|
714
|
+
setHoverRow(entry.path);
|
|
715
|
+
if (entry.type === "folder" && entry.path !== `${TRASH_PATH}/`) {
|
|
716
|
+
requestFolderLock(entry.path);
|
|
717
|
+
}
|
|
718
|
+
},
|
|
719
|
+
[requestFolderLock]
|
|
720
|
+
);
|
|
721
|
+
const resolveEntryEtag = (0, import_react6.useCallback)(
|
|
722
|
+
(entry) => {
|
|
723
|
+
if (entry.type !== "file") return void 0;
|
|
724
|
+
if (fileAttributes?.path === entry.path && fileAttributes.etag) return fileAttributes.etag;
|
|
725
|
+
return entry.etag;
|
|
726
|
+
},
|
|
727
|
+
[fileAttributes]
|
|
728
|
+
);
|
|
729
|
+
const parseApiError = (0, import_react6.useCallback)((err) => {
|
|
730
|
+
const fallback = err instanceof Error ? { code: void 0, message: err.message } : { code: void 0, message: "Request failed" };
|
|
731
|
+
if (!(err instanceof Error)) return fallback;
|
|
732
|
+
const raw = err.message;
|
|
733
|
+
if (!raw.trim().startsWith("{")) return fallback;
|
|
734
|
+
try {
|
|
735
|
+
const parsed = JSON.parse(raw);
|
|
736
|
+
if (parsed?.error) {
|
|
737
|
+
return { code: parsed.error.code, message: parsed.error.message ?? raw };
|
|
738
|
+
}
|
|
739
|
+
} catch {
|
|
740
|
+
return fallback;
|
|
741
|
+
}
|
|
742
|
+
return fallback;
|
|
743
|
+
}, []);
|
|
744
|
+
const isConflictError = (0, import_react6.useCallback)(
|
|
745
|
+
(err) => {
|
|
746
|
+
const info = parseApiError(err);
|
|
747
|
+
if (info.code === "conflict") return true;
|
|
748
|
+
if (info.message && /conflict|already in progress/i.test(info.message)) return true;
|
|
749
|
+
return false;
|
|
750
|
+
},
|
|
751
|
+
[parseApiError]
|
|
752
|
+
);
|
|
753
|
+
const showConflictAlert = (0, import_react6.useCallback)(() => {
|
|
754
|
+
window.alert("This item changed elsewhere. Refresh and try again.");
|
|
755
|
+
}, []);
|
|
756
|
+
const renderFolderLockBadge = (0, import_react6.useCallback)(
|
|
757
|
+
(entry, variant) => {
|
|
758
|
+
const label = getFolderLockLabel(entry);
|
|
759
|
+
if (!label) return null;
|
|
760
|
+
const isPending = label === "Renaming";
|
|
761
|
+
const style2 = {
|
|
762
|
+
display: "inline-flex",
|
|
763
|
+
alignItems: "center",
|
|
764
|
+
fontSize: 10,
|
|
765
|
+
fontWeight: 600,
|
|
766
|
+
textTransform: "uppercase",
|
|
767
|
+
letterSpacing: "0.04em",
|
|
768
|
+
padding: "4px 6px",
|
|
769
|
+
borderRadius: 6,
|
|
770
|
+
border: `1px solid ${isPending ? theme.accent : theme.border}`,
|
|
771
|
+
backgroundColor: isPending ? theme.accent : theme.bgSecondary,
|
|
772
|
+
color: isPending ? theme.bg : theme.textSecondary
|
|
773
|
+
};
|
|
774
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
775
|
+
"div",
|
|
776
|
+
{
|
|
777
|
+
style: variant === "grid" ? { ...style2, position: "absolute", top: 10, right: 10 } : { ...style2, marginLeft: 8 },
|
|
778
|
+
children: label
|
|
779
|
+
}
|
|
780
|
+
);
|
|
781
|
+
},
|
|
782
|
+
[getFolderLockLabel, theme]
|
|
783
|
+
);
|
|
662
784
|
const lastSelectionSigRef = (0, import_react6.useRef)("");
|
|
785
|
+
const lastSelectedPathsRef = (0, import_react6.useRef)(/* @__PURE__ */ new Set());
|
|
663
786
|
const [dragSelect, setDragSelect] = (0, import_react6.useState)(null);
|
|
664
787
|
const handleKeyDown = (0, import_react6.useCallback)(
|
|
665
788
|
(e) => {
|
|
@@ -925,6 +1048,9 @@ function FileManager({
|
|
|
925
1048
|
setPath("");
|
|
926
1049
|
}
|
|
927
1050
|
}, [hideTrash, view]);
|
|
1051
|
+
(0, import_react6.useEffect)(() => {
|
|
1052
|
+
lastSelectedPathsRef.current = /* @__PURE__ */ new Set();
|
|
1053
|
+
}, [mode]);
|
|
928
1054
|
(0, import_react6.useEffect)(() => {
|
|
929
1055
|
const timeoutId = setTimeout(() => {
|
|
930
1056
|
performSearch(searchQuery);
|
|
@@ -941,7 +1067,16 @@ function FileManager({
|
|
|
941
1067
|
if (sig === lastSelectionSigRef.current) return;
|
|
942
1068
|
lastSelectionSigRef.current = sig;
|
|
943
1069
|
onSelectionChange?.(selectedEntries);
|
|
944
|
-
|
|
1070
|
+
const nextSelectedPaths = new Set(selectedEntries.map((entry) => entry.path));
|
|
1071
|
+
if (mode === "picker" && onFileSelect) {
|
|
1072
|
+
selectedEntries.forEach((entry) => {
|
|
1073
|
+
if (entry.type === "file" && !lastSelectedPathsRef.current.has(entry.path)) {
|
|
1074
|
+
onFileSelect(entry);
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
lastSelectedPathsRef.current = nextSelectedPaths;
|
|
1079
|
+
}, [entries, searchResults, selected, searchQuery, onSelectionChange, onFileSelect, mode]);
|
|
945
1080
|
(0, import_react6.useEffect)(() => {
|
|
946
1081
|
const source = searchQuery.trim() ? searchResults : entries;
|
|
947
1082
|
const selectedFiles = source.filter((entry) => selected.has(entry.path)).filter((entry) => entry.type === "file");
|
|
@@ -983,6 +1118,45 @@ function FileManager({
|
|
|
983
1118
|
setPreviewData(null);
|
|
984
1119
|
}
|
|
985
1120
|
}, [selected, lastSelected, client]);
|
|
1121
|
+
(0, import_react6.useEffect)(() => {
|
|
1122
|
+
if (selected.size !== 1) return;
|
|
1123
|
+
const selectedPath = Array.from(selected)[0];
|
|
1124
|
+
if (!selectedPath) return;
|
|
1125
|
+
const source = searchQuery.trim() ? searchResults : entries;
|
|
1126
|
+
const entry = source.find((item) => item.path === selectedPath);
|
|
1127
|
+
if (entry?.type === "folder") {
|
|
1128
|
+
requestFolderLock(entry.path);
|
|
1129
|
+
}
|
|
1130
|
+
}, [entries, requestFolderLock, searchQuery, searchResults, selected]);
|
|
1131
|
+
(0, import_react6.useEffect)(() => {
|
|
1132
|
+
const entry = previewData?.entry;
|
|
1133
|
+
if (!entry || entry.type !== "file") {
|
|
1134
|
+
setFileAttributes(null);
|
|
1135
|
+
setAttributesError(null);
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
let cancelled = false;
|
|
1139
|
+
setAttributesLoading(true);
|
|
1140
|
+
setAttributesError(null);
|
|
1141
|
+
const run = async () => {
|
|
1142
|
+
try {
|
|
1143
|
+
const out = await client.getFileAttributes({ path: entry.path });
|
|
1144
|
+
if (cancelled) return;
|
|
1145
|
+
setFileAttributes(out);
|
|
1146
|
+
} catch (e) {
|
|
1147
|
+
if (cancelled) return;
|
|
1148
|
+
const message = e instanceof Error ? e.message : "Failed to load attributes";
|
|
1149
|
+
setAttributesError(message);
|
|
1150
|
+
setFileAttributes(null);
|
|
1151
|
+
} finally {
|
|
1152
|
+
if (!cancelled) setAttributesLoading(false);
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
void run();
|
|
1156
|
+
return () => {
|
|
1157
|
+
cancelled = true;
|
|
1158
|
+
};
|
|
1159
|
+
}, [client, previewData?.entry]);
|
|
986
1160
|
(0, import_react6.useEffect)(() => {
|
|
987
1161
|
if (previewData) {
|
|
988
1162
|
setPreviewDisplay(previewData);
|
|
@@ -1123,6 +1297,7 @@ function FileManager({
|
|
|
1123
1297
|
if (isRenaming) return;
|
|
1124
1298
|
const target = renameTarget ?? lastSelected;
|
|
1125
1299
|
if (!target) return;
|
|
1300
|
+
const isFolder = target.type === "folder";
|
|
1126
1301
|
const oldPath = target.path;
|
|
1127
1302
|
const parent = getParentPath(oldPath);
|
|
1128
1303
|
const existingNames = new Set(
|
|
@@ -1137,16 +1312,40 @@ function FileManager({
|
|
|
1137
1312
|
return;
|
|
1138
1313
|
}
|
|
1139
1314
|
try {
|
|
1315
|
+
if (isFolder) {
|
|
1316
|
+
setPendingFolderMoves((prev) => {
|
|
1317
|
+
const next = new Set(prev);
|
|
1318
|
+
next.add(oldPath);
|
|
1319
|
+
return next;
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1140
1322
|
setIsRenaming(true);
|
|
1141
|
-
|
|
1323
|
+
const renameEtag = target.type === "file" ? resolveEntryEtag(target) : void 0;
|
|
1324
|
+
await client.move({
|
|
1325
|
+
fromPath: oldPath,
|
|
1326
|
+
toPath: newPath,
|
|
1327
|
+
...renameEtag ? { ifMatch: renameEtag } : {}
|
|
1328
|
+
});
|
|
1142
1329
|
setRenameOpen(false);
|
|
1143
1330
|
setRenameTarget(null);
|
|
1144
1331
|
refresh();
|
|
1145
1332
|
} catch (e) {
|
|
1146
1333
|
console.error("Rename failed", e);
|
|
1147
|
-
|
|
1334
|
+
if (isConflictError(e)) {
|
|
1335
|
+
showConflictAlert();
|
|
1336
|
+
} else {
|
|
1337
|
+
alert("Rename failed");
|
|
1338
|
+
}
|
|
1148
1339
|
} finally {
|
|
1149
1340
|
setIsRenaming(false);
|
|
1341
|
+
if (isFolder) {
|
|
1342
|
+
setPendingFolderMoves((prev) => {
|
|
1343
|
+
const next = new Set(prev);
|
|
1344
|
+
next.delete(oldPath);
|
|
1345
|
+
return next;
|
|
1346
|
+
});
|
|
1347
|
+
setFolderLocks((prev) => ({ ...prev, [oldPath]: { status: "unlocked" } }));
|
|
1348
|
+
}
|
|
1150
1349
|
}
|
|
1151
1350
|
}
|
|
1152
1351
|
async function deleteEntries(targets) {
|
|
@@ -1155,12 +1354,26 @@ function FileManager({
|
|
|
1155
1354
|
for (const target of targets) {
|
|
1156
1355
|
if (target.path.startsWith(TRASH_PATH)) continue;
|
|
1157
1356
|
const dest = joinPath(TRASH_PATH, target.path);
|
|
1158
|
-
|
|
1357
|
+
const moveEtag = target.type === "file" ? resolveEntryEtag(target) : void 0;
|
|
1358
|
+
await client.move({
|
|
1359
|
+
fromPath: target.path,
|
|
1360
|
+
toPath: dest,
|
|
1361
|
+
...moveEtag ? { ifMatch: moveEtag } : {}
|
|
1362
|
+
});
|
|
1159
1363
|
}
|
|
1160
1364
|
} else {
|
|
1161
|
-
const files = targets.filter((e) => e.type === "file")
|
|
1365
|
+
const files = targets.filter((e) => e.type === "file");
|
|
1162
1366
|
const folders = targets.filter((e) => e.type === "folder");
|
|
1163
|
-
if (files.length > 0)
|
|
1367
|
+
if (files.length > 0)
|
|
1368
|
+
await client.deleteFiles({
|
|
1369
|
+
items: files.map((file) => {
|
|
1370
|
+
const deleteEtag = resolveEntryEtag(file);
|
|
1371
|
+
return {
|
|
1372
|
+
path: file.path,
|
|
1373
|
+
...deleteEtag ? { ifMatch: deleteEtag } : {}
|
|
1374
|
+
};
|
|
1375
|
+
})
|
|
1376
|
+
});
|
|
1164
1377
|
for (const folder of folders) {
|
|
1165
1378
|
await client.deleteFolder({ path: folder.path, recursive: true });
|
|
1166
1379
|
}
|
|
@@ -1178,7 +1391,11 @@ function FileManager({
|
|
|
1178
1391
|
refresh();
|
|
1179
1392
|
} catch (e) {
|
|
1180
1393
|
console.error("Delete failed", e);
|
|
1181
|
-
|
|
1394
|
+
if (isConflictError(e)) {
|
|
1395
|
+
showConflictAlert();
|
|
1396
|
+
} else {
|
|
1397
|
+
alert("Delete failed");
|
|
1398
|
+
}
|
|
1182
1399
|
} finally {
|
|
1183
1400
|
setIsDeleting(false);
|
|
1184
1401
|
}
|
|
@@ -1189,15 +1406,29 @@ function FileManager({
|
|
|
1189
1406
|
if (!target.path.startsWith(TRASH_PATH)) continue;
|
|
1190
1407
|
const originalPath = target.path.slice(TRASH_PATH.length + 1);
|
|
1191
1408
|
if (!originalPath) continue;
|
|
1192
|
-
|
|
1409
|
+
const restoreEtag = target.type === "file" ? resolveEntryEtag(target) : void 0;
|
|
1410
|
+
await client.move({
|
|
1411
|
+
fromPath: target.path,
|
|
1412
|
+
toPath: originalPath,
|
|
1413
|
+
...restoreEtag ? { ifMatch: restoreEtag } : {}
|
|
1414
|
+
});
|
|
1193
1415
|
}
|
|
1194
1416
|
}
|
|
1195
1417
|
async function onRestore() {
|
|
1196
1418
|
if (!can.restore) return;
|
|
1197
1419
|
const source = searchQuery.trim() ? searchResults : entries;
|
|
1198
1420
|
const targets = source.filter((e) => selected.has(e.path));
|
|
1199
|
-
|
|
1200
|
-
|
|
1421
|
+
try {
|
|
1422
|
+
await restoreEntries(targets);
|
|
1423
|
+
refresh();
|
|
1424
|
+
} catch (e) {
|
|
1425
|
+
console.error("Restore failed", e);
|
|
1426
|
+
if (isConflictError(e)) {
|
|
1427
|
+
showConflictAlert();
|
|
1428
|
+
} else {
|
|
1429
|
+
alert("Restore failed");
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1201
1432
|
}
|
|
1202
1433
|
async function onEmptyTrash() {
|
|
1203
1434
|
if (!can.restore) return;
|
|
@@ -1506,11 +1737,26 @@ function FileManager({
|
|
|
1506
1737
|
const dest = window.prompt("Copy to folder path", path || "");
|
|
1507
1738
|
if (!dest) return;
|
|
1508
1739
|
const baseDest = dest.replace(/\/+$/, "");
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1740
|
+
try {
|
|
1741
|
+
for (const entry of entriesToCopy) {
|
|
1742
|
+
if (entry.path.startsWith(TRASH_PATH)) continue;
|
|
1743
|
+
const targetName = entry.name || entry.path.split("/").pop() || entry.path;
|
|
1744
|
+
const toPath = entry.type === "folder" ? `${joinPath(baseDest, targetName)}/` : joinPath(baseDest, targetName);
|
|
1745
|
+
const copyEtag = entry.type === "file" ? resolveEntryEtag(entry) : void 0;
|
|
1746
|
+
await client.copy({
|
|
1747
|
+
fromPath: entry.path,
|
|
1748
|
+
toPath,
|
|
1749
|
+
...copyEtag ? { ifMatch: copyEtag } : {}
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
} catch (e) {
|
|
1753
|
+
console.error("Copy failed", e);
|
|
1754
|
+
if (isConflictError(e)) {
|
|
1755
|
+
showConflictAlert();
|
|
1756
|
+
} else {
|
|
1757
|
+
alert("Copy failed");
|
|
1758
|
+
}
|
|
1759
|
+
return;
|
|
1514
1760
|
}
|
|
1515
1761
|
refresh();
|
|
1516
1762
|
}
|
|
@@ -1519,11 +1765,26 @@ function FileManager({
|
|
|
1519
1765
|
const dest = window.prompt("Move to folder path", path || "");
|
|
1520
1766
|
if (!dest) return;
|
|
1521
1767
|
const baseDest = dest.replace(/\/+$/, "");
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1768
|
+
try {
|
|
1769
|
+
for (const entry of entriesToMove) {
|
|
1770
|
+
if (entry.path.startsWith(TRASH_PATH)) continue;
|
|
1771
|
+
const targetName = entry.name || entry.path.split("/").pop() || entry.path;
|
|
1772
|
+
const toPath = entry.type === "folder" ? `${joinPath(baseDest, targetName)}/` : joinPath(baseDest, targetName);
|
|
1773
|
+
const moveEtag = entry.type === "file" ? resolveEntryEtag(entry) : void 0;
|
|
1774
|
+
await client.move({
|
|
1775
|
+
fromPath: entry.path,
|
|
1776
|
+
toPath,
|
|
1777
|
+
...moveEtag ? { ifMatch: moveEtag } : {}
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
} catch (e) {
|
|
1781
|
+
console.error("Move failed", e);
|
|
1782
|
+
if (isConflictError(e)) {
|
|
1783
|
+
showConflictAlert();
|
|
1784
|
+
} else {
|
|
1785
|
+
alert("Move failed");
|
|
1786
|
+
}
|
|
1787
|
+
return;
|
|
1527
1788
|
}
|
|
1528
1789
|
refresh();
|
|
1529
1790
|
}
|
|
@@ -2314,7 +2575,7 @@ function FileManager({
|
|
|
2314
2575
|
},
|
|
2315
2576
|
onClick: (e) => handleEntryClickWithSelection(entry, index, filteredEntries, e),
|
|
2316
2577
|
onDoubleClick: () => openEntry(entry),
|
|
2317
|
-
onMouseEnter: () =>
|
|
2578
|
+
onMouseEnter: () => handleEntryHover(entry),
|
|
2318
2579
|
onMouseLeave: () => setHoverRow(null),
|
|
2319
2580
|
onTouchStart: () => {
|
|
2320
2581
|
if (!isMobile) return;
|
|
@@ -2350,7 +2611,8 @@ function FileManager({
|
|
|
2350
2611
|
},
|
|
2351
2612
|
children: entryLabel
|
|
2352
2613
|
}
|
|
2353
|
-
)
|
|
2614
|
+
),
|
|
2615
|
+
renderFolderLockBadge(entry, "grid")
|
|
2354
2616
|
]
|
|
2355
2617
|
},
|
|
2356
2618
|
entry.path
|
|
@@ -2380,7 +2642,7 @@ function FileManager({
|
|
|
2380
2642
|
},
|
|
2381
2643
|
onClick: (e) => handleEntryClickWithSelection(entry, index, filteredEntries, e),
|
|
2382
2644
|
onDoubleClick: () => openEntry(entry),
|
|
2383
|
-
onMouseEnter: () =>
|
|
2645
|
+
onMouseEnter: () => handleEntryHover(entry),
|
|
2384
2646
|
onMouseLeave: () => setHoverRow(null),
|
|
2385
2647
|
onTouchStart: () => {
|
|
2386
2648
|
if (!isMobile) return;
|
|
@@ -2466,7 +2728,7 @@ function FileManager({
|
|
|
2466
2728
|
e.stopPropagation();
|
|
2467
2729
|
}
|
|
2468
2730
|
},
|
|
2469
|
-
onMouseEnter: () =>
|
|
2731
|
+
onMouseEnter: () => handleEntryHover(entry),
|
|
2470
2732
|
onMouseLeave: () => setHoverRow(null),
|
|
2471
2733
|
onTouchStart: () => {
|
|
2472
2734
|
if (!isMobile) return;
|
|
@@ -2502,7 +2764,8 @@ function FileManager({
|
|
|
2502
2764
|
},
|
|
2503
2765
|
children: entryLabel
|
|
2504
2766
|
}
|
|
2505
|
-
)
|
|
2767
|
+
),
|
|
2768
|
+
renderFolderLockBadge(entry, "grid")
|
|
2506
2769
|
]
|
|
2507
2770
|
}
|
|
2508
2771
|
),
|
|
@@ -2743,7 +3006,7 @@ function FileManager({
|
|
|
2743
3006
|
handleEntryClickWithSelection(entry, index, filteredEntries, e);
|
|
2744
3007
|
},
|
|
2745
3008
|
onDoubleClick: () => openEntry(entry),
|
|
2746
|
-
onMouseEnter: () =>
|
|
3009
|
+
onMouseEnter: () => handleEntryHover(entry),
|
|
2747
3010
|
onMouseLeave: () => setHoverRow(null),
|
|
2748
3011
|
onTouchStart: () => {
|
|
2749
3012
|
if (!isMobile) return;
|
|
@@ -2840,7 +3103,7 @@ function FileManager({
|
|
|
2840
3103
|
handleEntryClickWithSelection(entry, index, filteredEntries, e);
|
|
2841
3104
|
},
|
|
2842
3105
|
onDoubleClick: () => openEntry(entry),
|
|
2843
|
-
onMouseEnter: () =>
|
|
3106
|
+
onMouseEnter: () => handleEntryHover(entry),
|
|
2844
3107
|
onMouseLeave: () => setHoverRow(null),
|
|
2845
3108
|
onTouchStart: () => {
|
|
2846
3109
|
if (!isMobile) return;
|
|
@@ -2923,7 +3186,8 @@ function FileManager({
|
|
|
2923
3186
|
}
|
|
2924
3187
|
return getFileIcon(entry.name);
|
|
2925
3188
|
})(),
|
|
2926
|
-
entryLabel
|
|
3189
|
+
entryLabel,
|
|
3190
|
+
renderFolderLockBadge(entry, "list")
|
|
2927
3191
|
]
|
|
2928
3192
|
}
|
|
2929
3193
|
),
|
|
@@ -2998,7 +3262,7 @@ function FileManager({
|
|
|
2998
3262
|
}
|
|
2999
3263
|
e.stopPropagation();
|
|
3000
3264
|
},
|
|
3001
|
-
onMouseEnter: () =>
|
|
3265
|
+
onMouseEnter: () => handleEntryHover(entry),
|
|
3002
3266
|
onMouseLeave: () => setHoverRow(null),
|
|
3003
3267
|
onTouchStart: () => {
|
|
3004
3268
|
if (!isMobile) return;
|
|
@@ -3081,7 +3345,8 @@ function FileManager({
|
|
|
3081
3345
|
}
|
|
3082
3346
|
return getFileIcon(entry.name);
|
|
3083
3347
|
})(),
|
|
3084
|
-
entryLabel
|
|
3348
|
+
entryLabel,
|
|
3349
|
+
renderFolderLockBadge(entry, "list")
|
|
3085
3350
|
]
|
|
3086
3351
|
}
|
|
3087
3352
|
),
|
|
@@ -3735,6 +4000,44 @@ function FileManager({
|
|
|
3735
4000
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaLabel, children: "Modified" }),
|
|
3736
4001
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaValue, children: previewDisplay.entry.type === "file" && previewDisplay.entry.lastModified ? new Date(previewDisplay.entry.lastModified).toLocaleString() : "--" })
|
|
3737
4002
|
] }),
|
|
4003
|
+
attributesLoading && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: FileManager_default.metaItem, children: [
|
|
4004
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaLabel, children: "Attributes" }),
|
|
4005
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaValue, children: "Loading..." })
|
|
4006
|
+
] }),
|
|
4007
|
+
attributesError && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: FileManager_default.metaItem, children: [
|
|
4008
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaLabel, children: "Attributes" }),
|
|
4009
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaValue, children: attributesError })
|
|
4010
|
+
] }),
|
|
4011
|
+
!attributesLoading && !attributesError && previewDisplay.entry.type === "file" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
4012
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: FileManager_default.metaItem, children: [
|
|
4013
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaLabel, children: "Content Type" }),
|
|
4014
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaValue, children: fileAttributes?.contentType ?? "--" })
|
|
4015
|
+
] }),
|
|
4016
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: FileManager_default.metaItem, children: [
|
|
4017
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaLabel, children: "Cache Control" }),
|
|
4018
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaValue, children: fileAttributes?.cacheControl ?? "--" })
|
|
4019
|
+
] }),
|
|
4020
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: FileManager_default.metaItem, children: [
|
|
4021
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaLabel, children: "Disposition" }),
|
|
4022
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaValue, children: fileAttributes?.contentDisposition ?? "--" })
|
|
4023
|
+
] }),
|
|
4024
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: FileManager_default.metaItem, children: [
|
|
4025
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaLabel, children: "ETag" }),
|
|
4026
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaValue, children: fileAttributes?.etag ?? previewDisplay.entry.etag ?? "--" })
|
|
4027
|
+
] }),
|
|
4028
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: FileManager_default.metaItem, children: [
|
|
4029
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaLabel, children: "Expires" }),
|
|
4030
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaValue, children: fileAttributes?.expiresAt ? new Date(fileAttributes.expiresAt).toLocaleString() : "--" })
|
|
4031
|
+
] }),
|
|
4032
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: FileManager_default.metaItem, children: [
|
|
4033
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaLabel, children: "Metadata" }),
|
|
4034
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: FileManager_default.metaValue, children: fileAttributes?.metadata && Object.keys(fileAttributes.metadata).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: Object.entries(fileAttributes.metadata).map(([k, v]) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
4035
|
+
k,
|
|
4036
|
+
": ",
|
|
4037
|
+
v
|
|
4038
|
+
] }, k)) }) : "--" })
|
|
4039
|
+
] })
|
|
4040
|
+
] }),
|
|
3738
4041
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3739
4042
|
"div",
|
|
3740
4043
|
{
|