s3kit 0.1.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 (56) hide show
  1. package/README.md +398 -0
  2. package/dist/adapters/express.cjs +305 -0
  3. package/dist/adapters/express.cjs.map +1 -0
  4. package/dist/adapters/express.d.cts +10 -0
  5. package/dist/adapters/express.d.ts +10 -0
  6. package/dist/adapters/express.js +278 -0
  7. package/dist/adapters/express.js.map +1 -0
  8. package/dist/adapters/fetch.cjs +298 -0
  9. package/dist/adapters/fetch.cjs.map +1 -0
  10. package/dist/adapters/fetch.d.cts +9 -0
  11. package/dist/adapters/fetch.d.ts +9 -0
  12. package/dist/adapters/fetch.js +271 -0
  13. package/dist/adapters/fetch.js.map +1 -0
  14. package/dist/adapters/next.cjs +796 -0
  15. package/dist/adapters/next.cjs.map +1 -0
  16. package/dist/adapters/next.d.cts +28 -0
  17. package/dist/adapters/next.d.ts +28 -0
  18. package/dist/adapters/next.js +775 -0
  19. package/dist/adapters/next.js.map +1 -0
  20. package/dist/client/index.cjs +153 -0
  21. package/dist/client/index.cjs.map +1 -0
  22. package/dist/client/index.d.cts +59 -0
  23. package/dist/client/index.d.ts +59 -0
  24. package/dist/client/index.js +126 -0
  25. package/dist/client/index.js.map +1 -0
  26. package/dist/core/index.cjs +452 -0
  27. package/dist/core/index.cjs.map +1 -0
  28. package/dist/core/index.d.cts +11 -0
  29. package/dist/core/index.d.ts +11 -0
  30. package/dist/core/index.js +430 -0
  31. package/dist/core/index.js.map +1 -0
  32. package/dist/http/index.cjs +270 -0
  33. package/dist/http/index.cjs.map +1 -0
  34. package/dist/http/index.d.cts +49 -0
  35. package/dist/http/index.d.ts +49 -0
  36. package/dist/http/index.js +243 -0
  37. package/dist/http/index.js.map +1 -0
  38. package/dist/index.cjs +808 -0
  39. package/dist/index.cjs.map +1 -0
  40. package/dist/index.d.cts +6 -0
  41. package/dist/index.d.ts +6 -0
  42. package/dist/index.js +784 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/manager-BbmXpgXN.d.ts +29 -0
  45. package/dist/manager-gIjo-t8h.d.cts +29 -0
  46. package/dist/react/index.cjs +4320 -0
  47. package/dist/react/index.cjs.map +1 -0
  48. package/dist/react/index.css +155 -0
  49. package/dist/react/index.css.map +1 -0
  50. package/dist/react/index.d.cts +79 -0
  51. package/dist/react/index.d.ts +79 -0
  52. package/dist/react/index.js +4315 -0
  53. package/dist/react/index.js.map +1 -0
  54. package/dist/types-g2IYvH3O.d.cts +123 -0
  55. package/dist/types-g2IYvH3O.d.ts +123 -0
  56. package/package.json +100 -0
@@ -0,0 +1,4315 @@
1
+ // src/react/FileManager.tsx
2
+ import { useEffect as useEffect2, useMemo, useState as useState2, useCallback, useRef } from "react";
3
+
4
+ // src/client/client.ts
5
+ function normalizeBasePath(basePath) {
6
+ if (!basePath) return "";
7
+ if (basePath === "/") return "";
8
+ return basePath.startsWith("/") ? basePath.replace(/\/+$/, "") : `/${basePath.replace(/\/+$/, "")}`;
9
+ }
10
+ function normalizeApiRootFromParts(baseUrl, basePath) {
11
+ const b = baseUrl.replace(/\/+$/, "");
12
+ const p = normalizeBasePath(basePath);
13
+ return p ? `${b}${p}` : b;
14
+ }
15
+ async function fetchJson(f, url, body) {
16
+ const res = await f(url, {
17
+ method: "POST",
18
+ headers: { "content-type": "application/json" },
19
+ body: JSON.stringify(body)
20
+ });
21
+ if (!res.ok) {
22
+ const text = await res.text().catch(() => "");
23
+ throw new Error(text || `Request failed: ${res.status}`);
24
+ }
25
+ if (res.status === 204) return void 0;
26
+ return await res.json();
27
+ }
28
+ function uploadWithXhr(upload, file, hooks) {
29
+ return new Promise((resolve, reject) => {
30
+ const xhr = new XMLHttpRequest();
31
+ xhr.open(upload.method, upload.url);
32
+ for (const [k, v] of Object.entries(upload.headers ?? {})) {
33
+ xhr.setRequestHeader(k, v);
34
+ }
35
+ xhr.upload.onprogress = (evt) => {
36
+ hooks?.onUploadProgress?.({
37
+ path: upload.path,
38
+ loaded: evt.loaded,
39
+ total: evt.total
40
+ });
41
+ };
42
+ xhr.onload = async () => {
43
+ if (xhr.status >= 200 && xhr.status < 300) {
44
+ await hooks?.onUploadComplete?.({ path: upload.path });
45
+ resolve();
46
+ return;
47
+ }
48
+ const responseText = xhr.responseText ? ` - ${xhr.responseText.slice(0, 200)}` : "";
49
+ const error = new Error(`Upload failed: ${xhr.status}${responseText}`);
50
+ await hooks?.onUploadError?.({ path: upload.path, error });
51
+ reject(error);
52
+ };
53
+ xhr.onerror = async () => {
54
+ const error = new Error("Upload failed: network error");
55
+ await hooks?.onUploadError?.({ path: upload.path, error });
56
+ reject(error);
57
+ };
58
+ xhr.send(file);
59
+ });
60
+ }
61
+ var S3FileManagerClient = class {
62
+ apiRoot;
63
+ f;
64
+ constructor(options) {
65
+ this.apiRoot = "apiUrl" in options ? options.apiUrl.replace(/\/+$/, "") : normalizeApiRootFromParts(options.baseUrl, options.basePath);
66
+ this.f = options.fetch ?? fetch;
67
+ }
68
+ endpoint(path) {
69
+ const p = path.startsWith("/") ? path : `/${path}`;
70
+ return `${this.apiRoot}${p}`;
71
+ }
72
+ list(options) {
73
+ return fetchJson(this.f, this.endpoint("/list"), options);
74
+ }
75
+ search(options) {
76
+ return fetchJson(this.f, this.endpoint("/search"), options);
77
+ }
78
+ createFolder(options) {
79
+ return fetchJson(this.f, this.endpoint("/folder/create"), options);
80
+ }
81
+ deleteFolder(options) {
82
+ return fetchJson(this.f, this.endpoint("/folder/delete"), options);
83
+ }
84
+ deleteFiles(options) {
85
+ return fetchJson(this.f, this.endpoint("/files/delete"), options);
86
+ }
87
+ copy(options) {
88
+ return fetchJson(this.f, this.endpoint("/files/copy"), options);
89
+ }
90
+ move(options) {
91
+ return fetchJson(this.f, this.endpoint("/files/move"), options);
92
+ }
93
+ prepareUploads(options) {
94
+ return fetchJson(this.f, this.endpoint("/upload/prepare"), options);
95
+ }
96
+ getPreviewUrl(options) {
97
+ return fetchJson(this.f, this.endpoint("/preview"), options);
98
+ }
99
+ async uploadFiles(args) {
100
+ const prepare = {
101
+ items: args.files.map((f) => ({
102
+ path: f.path,
103
+ contentType: f.contentType ?? f.file.type
104
+ })),
105
+ ...args.expiresInSeconds !== void 0 ? { expiresInSeconds: args.expiresInSeconds } : {}
106
+ };
107
+ const uploads = await this.prepareUploads(prepare);
108
+ const byPath = new Map(uploads.map((u) => [u.path, u]));
109
+ const tasks = args.files.map(({ file, path }) => {
110
+ const upload = byPath.get(path);
111
+ if (!upload) throw new Error(`Missing presigned upload for: ${path}`);
112
+ return () => uploadWithXhr(upload, file, args.hooks);
113
+ });
114
+ const parallel = Math.max(1, args.parallel ?? 4);
115
+ let i = 0;
116
+ const runWorker = async () => {
117
+ while (true) {
118
+ const idx = i++;
119
+ if (idx >= tasks.length) return;
120
+ await tasks[idx]();
121
+ }
122
+ };
123
+ await Promise.all(Array.from({ length: Math.min(parallel, tasks.length) }, runWorker));
124
+ }
125
+ };
126
+
127
+ // src/react/FileManager.tsx
128
+ import {
129
+ Folder,
130
+ House,
131
+ UploadSimple,
132
+ FolderPlus,
133
+ Trash,
134
+ ArrowRight,
135
+ ArrowUp,
136
+ ArrowDown,
137
+ X as X2,
138
+ CaretRight,
139
+ DotsThree,
140
+ DownloadSimple,
141
+ ArrowCounterClockwise,
142
+ PencilSimple,
143
+ Warning,
144
+ MagnifyingGlass,
145
+ CaretUp,
146
+ CaretDown,
147
+ List,
148
+ SquaresFour,
149
+ Copy,
150
+ FileText as FileText2
151
+ } from "@phosphor-icons/react";
152
+ import { ContextMenu } from "@base-ui/react";
153
+ import { Menu } from "@base-ui/react";
154
+ import { Dialog as Dialog2 } from "@base-ui/react";
155
+
156
+ // src/react/FileManager.module.css
157
+ var FileManager_default = {};
158
+
159
+ // src/react/components/Button.tsx
160
+ import { Button as BaseButton } from "@base-ui/react";
161
+ import { jsx } from "react/jsx-runtime";
162
+ function Button({
163
+ children,
164
+ variant = "secondary",
165
+ onClick,
166
+ style,
167
+ theme,
168
+ disabled = false
169
+ }) {
170
+ const baseStyle = {
171
+ display: "inline-flex",
172
+ alignItems: "center",
173
+ gap: 8,
174
+ padding: "8px 16px",
175
+ border: "1px solid transparent",
176
+ cursor: disabled ? "not-allowed" : "pointer",
177
+ fontSize: 13,
178
+ fontWeight: 500,
179
+ transition: "all 0.15s",
180
+ opacity: disabled ? 0.6 : 1,
181
+ ...style
182
+ };
183
+ const variants = {
184
+ primary: {
185
+ backgroundColor: theme.accent,
186
+ color: theme.bg,
187
+ borderColor: theme.accent
188
+ },
189
+ secondary: {
190
+ backgroundColor: theme.bg,
191
+ color: theme.text,
192
+ borderColor: theme.border
193
+ },
194
+ danger: {
195
+ backgroundColor: theme.bg,
196
+ color: theme.danger,
197
+ borderColor: theme.danger
198
+ }
199
+ };
200
+ return /* @__PURE__ */ jsx(
201
+ BaseButton,
202
+ {
203
+ type: "button",
204
+ style: { ...baseStyle, ...variants[variant] },
205
+ onClick: disabled ? void 0 : onClick,
206
+ disabled,
207
+ children
208
+ }
209
+ );
210
+ }
211
+
212
+ // src/react/components/Modal.tsx
213
+ import { Dialog } from "@base-ui/react";
214
+
215
+ // src/react/components/UiIcon.tsx
216
+ import { jsx as jsx2 } from "react/jsx-runtime";
217
+ function UiIcon(props) {
218
+ const { icon: Icon, size, weight, color, boxed = false, boxStyle, iconStyle } = props;
219
+ const iconProps = {
220
+ size,
221
+ style: { display: "block", ...iconStyle },
222
+ ...weight !== void 0 ? { weight } : {},
223
+ ...color !== void 0 ? { color } : {}
224
+ };
225
+ return /* @__PURE__ */ jsx2(
226
+ "span",
227
+ {
228
+ style: {
229
+ width: size,
230
+ height: size,
231
+ display: "inline-flex",
232
+ alignItems: "center",
233
+ justifyContent: "center",
234
+ flexShrink: 0,
235
+ lineHeight: 0,
236
+ verticalAlign: "middle",
237
+ ...boxed ? {
238
+ backgroundColor: "var(--s3kit-icon-bg, transparent)",
239
+ border: "1px solid var(--s3kit-icon-border, transparent)",
240
+ borderRadius: "var(--s3kit-icon-radius, 8px)",
241
+ boxSizing: "border-box"
242
+ } : {},
243
+ ...boxStyle
244
+ },
245
+ children: /* @__PURE__ */ jsx2(Icon, { ...iconProps })
246
+ }
247
+ );
248
+ }
249
+
250
+ // src/react/components/Modal.tsx
251
+ import { X } from "@phosphor-icons/react";
252
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
253
+ function Modal({
254
+ open,
255
+ onClose,
256
+ title,
257
+ children,
258
+ theme,
259
+ portalContainer,
260
+ closeDisabled = false
261
+ }) {
262
+ return /* @__PURE__ */ jsx3(
263
+ Dialog.Root,
264
+ {
265
+ open,
266
+ onOpenChange: (open2) => {
267
+ if (!open2 && !closeDisabled) onClose();
268
+ },
269
+ children: /* @__PURE__ */ jsxs(Dialog.Portal, { container: portalContainer, children: [
270
+ /* @__PURE__ */ jsx3(
271
+ Dialog.Backdrop,
272
+ {
273
+ style: {
274
+ position: "fixed",
275
+ inset: 0,
276
+ zIndex: 100,
277
+ backgroundColor: theme.bg === "#ffffff" ? "rgba(255,255,255,0.8)" : "rgba(0,0,0,0.8)",
278
+ backdropFilter: "blur(2px)",
279
+ display: "flex",
280
+ alignItems: "center",
281
+ justifyContent: "center"
282
+ }
283
+ }
284
+ ),
285
+ /* @__PURE__ */ jsxs(
286
+ Dialog.Popup,
287
+ {
288
+ style: {
289
+ position: "fixed",
290
+ top: "50%",
291
+ left: "50%",
292
+ transform: "translate(-50%, -50%)",
293
+ zIndex: 101,
294
+ width: "100%",
295
+ maxWidth: 400,
296
+ backgroundColor: theme.bg,
297
+ border: `1px solid ${theme.border}`,
298
+ boxShadow: "0 10px 40px rgba(0,0,0,0.1)",
299
+ outline: "none"
300
+ },
301
+ children: [
302
+ /* @__PURE__ */ jsxs(
303
+ "div",
304
+ {
305
+ style: {
306
+ display: "flex",
307
+ alignItems: "center",
308
+ justifyContent: "space-between",
309
+ padding: "16px 20px",
310
+ borderBottom: `1px solid ${theme.border}`
311
+ },
312
+ children: [
313
+ /* @__PURE__ */ jsx3(
314
+ Dialog.Title,
315
+ {
316
+ style: {
317
+ margin: 0,
318
+ fontSize: 13,
319
+ fontWeight: 600,
320
+ textTransform: "uppercase",
321
+ letterSpacing: "0.05em",
322
+ color: theme.text
323
+ },
324
+ children: title
325
+ }
326
+ ),
327
+ /* @__PURE__ */ jsx3(
328
+ Dialog.Close,
329
+ {
330
+ disabled: closeDisabled,
331
+ style: {
332
+ background: "none",
333
+ border: "none",
334
+ cursor: closeDisabled ? "not-allowed" : "pointer",
335
+ padding: 4,
336
+ display: "flex",
337
+ alignItems: "center",
338
+ justifyContent: "center",
339
+ color: theme.text,
340
+ opacity: closeDisabled ? 0.5 : 1
341
+ },
342
+ children: /* @__PURE__ */ jsx3(UiIcon, { icon: X, size: 18 })
343
+ }
344
+ )
345
+ ]
346
+ }
347
+ ),
348
+ /* @__PURE__ */ jsx3("div", { style: { padding: 24 }, children })
349
+ ]
350
+ }
351
+ )
352
+ ] })
353
+ }
354
+ );
355
+ }
356
+
357
+ // src/react/components/FileIcons.tsx
358
+ import { Code, File, FileText, FilmStrip, Image, MusicNote } from "@phosphor-icons/react";
359
+ import { jsx as jsx4 } from "react/jsx-runtime";
360
+ function getFileIcon(filename, size = 20) {
361
+ const ext = filename.split(".").pop()?.toLowerCase();
362
+ const weight = "light";
363
+ if (["jpg", "jpeg", "png", "gif", "webp", "svg"].includes(ext || "")) {
364
+ return /* @__PURE__ */ jsx4(UiIcon, { icon: Image, size, weight, boxed: true });
365
+ }
366
+ if (["mp4", "webm", "mov"].includes(ext || "")) {
367
+ return /* @__PURE__ */ jsx4(UiIcon, { icon: FilmStrip, size, weight, boxed: true });
368
+ }
369
+ if (["mp3", "wav", "ogg"].includes(ext || "")) {
370
+ return /* @__PURE__ */ jsx4(UiIcon, { icon: MusicNote, size, weight, boxed: true });
371
+ }
372
+ if (["js", "ts", "jsx", "tsx", "json", "html", "css"].includes(ext || "")) {
373
+ return /* @__PURE__ */ jsx4(UiIcon, { icon: Code, size, weight, boxed: true });
374
+ }
375
+ if (["pdf", "txt", "md", "doc", "docx"].includes(ext || "")) {
376
+ return /* @__PURE__ */ jsx4(UiIcon, { icon: FileText, size, weight, boxed: true });
377
+ }
378
+ return /* @__PURE__ */ jsx4(UiIcon, { icon: File, size, weight, boxed: true });
379
+ }
380
+
381
+ // src/react/theme/fileManagerTheme.ts
382
+ import { useEffect, useState } from "react";
383
+ var lightTheme = {
384
+ bg: "#ffffff",
385
+ bgSecondary: "#fafafa",
386
+ text: "#111111",
387
+ textSecondary: "#666666",
388
+ border: "#eaeaea",
389
+ accent: "#000000",
390
+ hover: "#f4f4f4",
391
+ selected: "#eeeeee",
392
+ danger: "#d32f2f"
393
+ };
394
+ var darkTheme = {
395
+ bg: "#0a0a0a",
396
+ bgSecondary: "#1a1a1a",
397
+ text: "#ffffff",
398
+ textSecondary: "#a1a1aa",
399
+ border: "#27272a",
400
+ accent: "#ffffff",
401
+ hover: "#18181b",
402
+ selected: "#27272a",
403
+ danger: "#ef4444"
404
+ };
405
+ function useTheme(themeMode = "light") {
406
+ const [systemTheme, setSystemTheme] = useState("light");
407
+ useEffect(() => {
408
+ if (themeMode === "system") {
409
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
410
+ setSystemTheme(mediaQuery.matches ? "dark" : "light");
411
+ const handleChange = (e) => {
412
+ setSystemTheme(e.matches ? "dark" : "light");
413
+ };
414
+ mediaQuery.addEventListener("change", handleChange);
415
+ return () => mediaQuery.removeEventListener("change", handleChange);
416
+ }
417
+ }, [themeMode]);
418
+ const activeTheme = themeMode === "system" ? systemTheme : themeMode;
419
+ return activeTheme === "dark" ? darkTheme : lightTheme;
420
+ }
421
+
422
+ // src/react/utils/fileManagerConstants.ts
423
+ var TRASH_PATH = ".trash";
424
+
425
+ // src/react/utils/fileManagerFilters.ts
426
+ var MIME_EXTENSION_MAP = {
427
+ "image/jpeg": ["jpg", "jpeg"],
428
+ "image/png": ["png"],
429
+ "image/gif": ["gif"],
430
+ "image/webp": ["webp"],
431
+ "image/svg+xml": ["svg"],
432
+ "image/heic": ["heic"],
433
+ "image/heif": ["heif"],
434
+ "image/avif": ["avif"],
435
+ "application/pdf": ["pdf"],
436
+ "text/plain": ["txt"],
437
+ "text/markdown": ["md"],
438
+ "text/csv": ["csv"],
439
+ "application/json": ["json"],
440
+ "application/zip": ["zip"],
441
+ "application/x-zip-compressed": ["zip"],
442
+ "audio/mpeg": ["mp3"],
443
+ "audio/wav": ["wav"],
444
+ "audio/ogg": ["ogg"],
445
+ "video/mp4": ["mp4"],
446
+ "video/webm": ["webm"],
447
+ "video/quicktime": ["mov"]
448
+ };
449
+ function buildAllowedExtensions(extensions, mimeTypes) {
450
+ const allowed = /* @__PURE__ */ new Set();
451
+ if (extensions) {
452
+ for (const ext of extensions) {
453
+ const trimmed = ext.replace(/^\./, "").toLowerCase();
454
+ if (trimmed) allowed.add(trimmed);
455
+ }
456
+ }
457
+ if (mimeTypes) {
458
+ for (const type of mimeTypes) {
459
+ const exts = MIME_EXTENSION_MAP[type.toLowerCase()] ?? [];
460
+ for (const ext of exts) allowed.add(ext);
461
+ }
462
+ }
463
+ return allowed.size > 0 ? allowed : null;
464
+ }
465
+ function isImageFileName(filename) {
466
+ const ext = filename.split(".").pop()?.toLowerCase();
467
+ return ["jpg", "jpeg", "png", "gif", "webp", "svg"].includes(ext || "");
468
+ }
469
+
470
+ // src/react/utils/fileManagerFormat.ts
471
+ function formatBytes(bytes) {
472
+ if (bytes === 0) return "0 B";
473
+ const k = 1024;
474
+ const sizes = ["B", "KB", "MB", "GB"];
475
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
476
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
477
+ }
478
+ function formatDate(date) {
479
+ const d = new Date(date);
480
+ return d.toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" });
481
+ }
482
+
483
+ // src/react/utils/fileManagerNames.ts
484
+ function splitName(name) {
485
+ const idx = name.lastIndexOf(".");
486
+ if (idx <= 0) return { base: name, ext: "" };
487
+ return { base: name.slice(0, idx), ext: name.slice(idx) };
488
+ }
489
+ function buildUniqueName(name, existing) {
490
+ if (!existing.has(name)) return name;
491
+ const { base, ext } = splitName(name);
492
+ let i = 1;
493
+ while (true) {
494
+ const candidate = `${base}_copy_${i}${ext}`;
495
+ if (!existing.has(candidate)) return candidate;
496
+ i += 1;
497
+ }
498
+ }
499
+
500
+ // src/react/utils/fileManagerPaths.ts
501
+ function joinPath(folder, name) {
502
+ const f = folder.replace(/\/+$/, "");
503
+ if (!f) return name;
504
+ return `${f}/${name}`;
505
+ }
506
+ function normalizeRelativePath(path) {
507
+ return path.replace(/^\/+/, "").replace(/\\/g, "/");
508
+ }
509
+ function getRelativePathFromFile(file) {
510
+ const rel = file.webkitRelativePath;
511
+ if (!rel) return null;
512
+ return normalizeRelativePath(rel);
513
+ }
514
+ function getParentPath(path) {
515
+ const p = path.replace(/\/+$/, "");
516
+ const parts = p.split("/").filter(Boolean);
517
+ parts.pop();
518
+ return parts.join("/");
519
+ }
520
+
521
+ // src/react/FileManager.tsx
522
+ import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
523
+ function FileManager({
524
+ apiUrl,
525
+ fetch: customFetch,
526
+ onFileSelect,
527
+ onSelectionChange,
528
+ onConfirm,
529
+ onNavigate,
530
+ className,
531
+ style,
532
+ theme: themeMode = "light",
533
+ mode = "manager",
534
+ selection = "multiple",
535
+ allowActions,
536
+ confirmLabel = "Select",
537
+ hideTrash = false,
538
+ filterExtensions,
539
+ filterMimeTypes,
540
+ toolbar,
541
+ viewMode: viewModeProp,
542
+ defaultViewMode = "grid",
543
+ onViewModeChange,
544
+ labels
545
+ }) {
546
+ const theme = useTheme(themeMode);
547
+ const showSearch = toolbar?.search ?? true;
548
+ const showViewSwitcher = toolbar?.viewSwitcher ?? true;
549
+ const showSort = toolbar?.sort ?? true;
550
+ const showBreadcrumbs = toolbar?.breadcrumbs ?? true;
551
+ const labelText = {
552
+ upload: "Upload",
553
+ newFolder: "New Folder",
554
+ delete: "Delete",
555
+ deleteForever: "Delete Forever",
556
+ restore: "Restore",
557
+ emptyTrash: "Empty Trash",
558
+ confirm: confirmLabel,
559
+ searchPlaceholder: "Search files and folders...",
560
+ ...labels
561
+ };
562
+ const allowedExtensions = useMemo(
563
+ () => buildAllowedExtensions(filterExtensions, filterMimeTypes),
564
+ [filterExtensions, filterMimeTypes]
565
+ );
566
+ const can = {
567
+ upload: mode === "manager",
568
+ createFolder: mode === "manager",
569
+ delete: mode === "manager",
570
+ rename: mode === "manager",
571
+ move: mode === "manager",
572
+ copy: mode === "manager",
573
+ restore: mode === "manager",
574
+ ...allowActions
575
+ };
576
+ const [isMobile, setIsMobile] = useState2(false);
577
+ const portalContainer = typeof document === "undefined" ? void 0 : document.body;
578
+ useEffect2(() => {
579
+ if (typeof window === "undefined") return;
580
+ const mediaQuery = window.matchMedia("(max-width: 720px)");
581
+ const update = () => setIsMobile(mediaQuery.matches);
582
+ update();
583
+ mediaQuery.addEventListener("change", update);
584
+ return () => mediaQuery.removeEventListener("change", update);
585
+ }, []);
586
+ const client = useMemo(() => {
587
+ return new S3FileManagerClient({
588
+ apiUrl,
589
+ ...customFetch ? { fetch: customFetch } : {}
590
+ });
591
+ }, [apiUrl, customFetch]);
592
+ const [view, setView] = useState2("files");
593
+ const [path, setPath] = useState2("");
594
+ const [entries, setEntries] = useState2([]);
595
+ const [searchResults, setSearchResults] = useState2([]);
596
+ const [loading, setLoading] = useState2(false);
597
+ const [searching, setSearching] = useState2(false);
598
+ const [loadingMore, setLoadingMore] = useState2(false);
599
+ const [selected, setSelected] = useState2(/* @__PURE__ */ new Set());
600
+ const [lastSelected, setLastSelected] = useState2(null);
601
+ const [hoverRow, setHoverRow] = useState2(null);
602
+ const [previewData, setPreviewData] = useState2(null);
603
+ const [previewDisplay, setPreviewDisplay] = useState2(null);
604
+ const [isPreviewClosing, setIsPreviewClosing] = useState2(false);
605
+ const [sidebarWidth, setSidebarWidth] = useState2(320);
606
+ const [isResizing, setIsResizing] = useState2(false);
607
+ const [inlinePreviews, setInlinePreviews] = useState2({});
608
+ const [isSelectionMode, setIsSelectionMode] = useState2(false);
609
+ const [searchQuery, setSearchQuery] = useState2("");
610
+ const [sortBy, setSortBy] = useState2("name");
611
+ const [sortOrder, setSortOrder] = useState2("asc");
612
+ const [internalViewMode, setInternalViewMode] = useState2(defaultViewMode);
613
+ const viewMode = viewModeProp ?? internalViewMode;
614
+ const setViewMode = (next) => {
615
+ if (!viewModeProp) setInternalViewMode(next);
616
+ onViewModeChange?.(next);
617
+ };
618
+ const toolbarControlHeight = 34;
619
+ const headerLabelStyle = {
620
+ display: "flex",
621
+ alignItems: "center",
622
+ gap: 4,
623
+ fontSize: 12,
624
+ fontWeight: 600,
625
+ color: theme.textSecondary,
626
+ textTransform: "uppercase",
627
+ letterSpacing: "0.05em",
628
+ padding: 0
629
+ };
630
+ const [nextCursor, setNextCursor] = useState2(void 0);
631
+ const [searchCursor, setSearchCursor] = useState2(void 0);
632
+ const [hasMore, setHasMore] = useState2(false);
633
+ const [searchHasMore, setSearchHasMore] = useState2(false);
634
+ const [isDragOver, setIsDragOver] = useState2(false);
635
+ const [_dragCounter, setDragCounter] = useState2(0);
636
+ const [createFolderOpen, setCreateFolderOpen] = useState2(false);
637
+ const [newFolderName, setNewFolderName] = useState2("");
638
+ const [deleteOpen, setDeleteOpen] = useState2(false);
639
+ const [emptyTrashOpen, setEmptyTrashOpen] = useState2(false);
640
+ const [renameOpen, setRenameOpen] = useState2(false);
641
+ const [renameName, setRenameName] = useState2("");
642
+ const [renameTarget, setRenameTarget] = useState2(null);
643
+ const [isRenaming, setIsRenaming] = useState2(false);
644
+ const [isDeleting, setIsDeleting] = useState2(false);
645
+ const [isEmptyingTrash, setIsEmptyingTrash] = useState2(false);
646
+ const [uploadItems, setUploadItems] = useState2([]);
647
+ const [uploadCardOpen, setUploadCardOpen] = useState2(false);
648
+ const fileInputRef = useRef(null);
649
+ const rootRef = useRef(null);
650
+ const listRef = useRef(null);
651
+ const previewPortalRef = useRef(null);
652
+ const previewPortalContainer = previewPortalRef.current ?? rootRef.current ?? portalContainer;
653
+ const downloadUrlCacheRef = useRef(/* @__PURE__ */ new Map());
654
+ const selectionAnchorRef = useRef(null);
655
+ const longPressTimerRef = useRef(null);
656
+ const suppressClickRef = useRef(false);
657
+ const dragSelectionBaseRef = useRef(/* @__PURE__ */ new Set());
658
+ const lastSelectionSigRef = useRef("");
659
+ const [dragSelect, setDragSelect] = useState2(null);
660
+ const handleKeyDown = useCallback(
661
+ (e) => {
662
+ if (e.key === "Escape") {
663
+ e.preventDefault();
664
+ e.stopPropagation();
665
+ if (createFolderOpen) {
666
+ setCreateFolderOpen(false);
667
+ setNewFolderName("");
668
+ return;
669
+ }
670
+ if (renameOpen) {
671
+ setRenameOpen(false);
672
+ setRenameTarget(null);
673
+ return;
674
+ }
675
+ if (deleteOpen) {
676
+ setDeleteOpen(false);
677
+ return;
678
+ }
679
+ if (emptyTrashOpen) {
680
+ setEmptyTrashOpen(false);
681
+ return;
682
+ }
683
+ if (isDragOver) {
684
+ setIsDragOver(false);
685
+ setDragCounter(0);
686
+ return;
687
+ }
688
+ if (dragSelect) {
689
+ setDragSelect(null);
690
+ return;
691
+ }
692
+ if (selected.size > 0) {
693
+ setSelected(/* @__PURE__ */ new Set());
694
+ setLastSelected(null);
695
+ setPreviewData(null);
696
+ return;
697
+ }
698
+ if (searchQuery.trim()) {
699
+ setSearchQuery("");
700
+ return;
701
+ }
702
+ if (uploadCardOpen) {
703
+ const hasActiveUploads = uploadItems.some((item) => item.status === "uploading");
704
+ if (!hasActiveUploads) {
705
+ setUploadCardOpen(false);
706
+ }
707
+ }
708
+ return;
709
+ }
710
+ if ((e.ctrlKey || e.metaKey) && e.key === "v" && view === "files") {
711
+ console.log("Paste detected - use drag and drop or upload button for files");
712
+ }
713
+ },
714
+ [
715
+ view,
716
+ createFolderOpen,
717
+ renameOpen,
718
+ deleteOpen,
719
+ emptyTrashOpen,
720
+ isDragOver,
721
+ dragSelect,
722
+ selected,
723
+ searchQuery,
724
+ uploadCardOpen,
725
+ uploadItems
726
+ ]
727
+ );
728
+ const handleDragEnter = useCallback(
729
+ (e) => {
730
+ e.preventDefault();
731
+ e.stopPropagation();
732
+ if (!can.upload || view !== "files") return;
733
+ if (e.dataTransfer.types.includes("Files")) {
734
+ setDragCounter((prev) => prev + 1);
735
+ setIsDragOver(true);
736
+ }
737
+ },
738
+ [can.upload, view]
739
+ );
740
+ const handleDragLeave = useCallback(
741
+ (e) => {
742
+ e.preventDefault();
743
+ e.stopPropagation();
744
+ if (!can.upload || view !== "files") return;
745
+ setDragCounter((prev) => {
746
+ const newCounter = prev - 1;
747
+ if (newCounter <= 0) {
748
+ setIsDragOver(false);
749
+ return 0;
750
+ }
751
+ return newCounter;
752
+ });
753
+ },
754
+ [can.upload, view]
755
+ );
756
+ const handleDragOver = useCallback(
757
+ (e) => {
758
+ e.preventDefault();
759
+ e.stopPropagation();
760
+ if (e.dataTransfer.types.includes("Files")) {
761
+ e.dataTransfer.dropEffect = can.upload && view === "files" ? "copy" : "none";
762
+ }
763
+ },
764
+ [can.upload, view]
765
+ );
766
+ const handleDrop = useCallback(
767
+ async (e) => {
768
+ e.preventDefault();
769
+ e.stopPropagation();
770
+ setIsDragOver(false);
771
+ setDragCounter(0);
772
+ if (!can.upload || view !== "files") return;
773
+ const items = e.dataTransfer.items;
774
+ if (items && items.length > 0) {
775
+ const dropped = await getDroppedItems(items);
776
+ if (dropped.length > 0) {
777
+ await onUpload(dropped);
778
+ return;
779
+ }
780
+ }
781
+ const files = e.dataTransfer.files;
782
+ if (files && files.length > 0) {
783
+ await onUpload(files);
784
+ }
785
+ },
786
+ [can.upload, view]
787
+ );
788
+ const refresh = useCallback(
789
+ async (loadMore = false) => {
790
+ if (!loadMore) {
791
+ setLoading(true);
792
+ setSelected(/* @__PURE__ */ new Set());
793
+ setLastSelected(null);
794
+ setPreviewData(null);
795
+ } else {
796
+ setLoadingMore(true);
797
+ }
798
+ try {
799
+ const fetchPath = view === "trash" ? path ? joinPath(TRASH_PATH, path) : TRASH_PATH : path;
800
+ const out = await client.list({
801
+ path: fetchPath,
802
+ limit: 100,
803
+ ...loadMore && nextCursor ? { cursor: nextCursor } : {}
804
+ });
805
+ let items = out.entries;
806
+ if (allowedExtensions) {
807
+ items = items.filter((entry) => {
808
+ if (entry.type === "folder") return true;
809
+ const ext = entry.name.split(".").pop()?.toLowerCase();
810
+ return ext ? allowedExtensions.has(ext) : false;
811
+ });
812
+ }
813
+ if (hideTrash) {
814
+ items = items.filter((entry) => entry.path !== `${TRASH_PATH}/`);
815
+ }
816
+ if (loadMore) {
817
+ setEntries((prev) => [...prev, ...items]);
818
+ } else {
819
+ setEntries(items);
820
+ }
821
+ setNextCursor(out.nextCursor);
822
+ setHasMore(!!out.nextCursor);
823
+ } catch (e) {
824
+ console.error(e);
825
+ if (!loadMore) {
826
+ setEntries([]);
827
+ }
828
+ } finally {
829
+ setLoading(false);
830
+ setLoadingMore(false);
831
+ }
832
+ },
833
+ [client, path, view, nextCursor, allowedExtensions, hideTrash]
834
+ );
835
+ const performSearch = useCallback(
836
+ async (query, loadMore = false) => {
837
+ if (!query.trim()) {
838
+ setSearchResults([]);
839
+ setSearching(false);
840
+ setSearchCursor(void 0);
841
+ setSearchHasMore(false);
842
+ return;
843
+ }
844
+ if (!loadMore) {
845
+ setSearching(true);
846
+ setSelected(/* @__PURE__ */ new Set());
847
+ setLastSelected(null);
848
+ setPreviewData(null);
849
+ } else {
850
+ setLoadingMore(true);
851
+ }
852
+ try {
853
+ try {
854
+ const searchOptions = {
855
+ query: query.trim(),
856
+ recursive: true,
857
+ limit: 100,
858
+ ...loadMore && searchCursor ? { cursor: searchCursor } : {}
859
+ };
860
+ if (view === "trash") {
861
+ searchOptions.path = TRASH_PATH;
862
+ }
863
+ const out = await client.search(searchOptions);
864
+ let items = out.entries;
865
+ if (view === "files") {
866
+ items = items.filter((e) => {
867
+ if (!e.path.startsWith(TRASH_PATH)) return true;
868
+ return e.path === `${TRASH_PATH}/`;
869
+ });
870
+ }
871
+ if (allowedExtensions) {
872
+ items = items.filter((entry) => {
873
+ if (entry.type === "folder") return true;
874
+ const ext = entry.name.split(".").pop()?.toLowerCase();
875
+ return ext ? allowedExtensions.has(ext) : false;
876
+ });
877
+ }
878
+ if (hideTrash) {
879
+ items = items.filter((entry) => entry.path !== `${TRASH_PATH}/`);
880
+ }
881
+ if (loadMore) {
882
+ setSearchResults((prev) => [...prev, ...items]);
883
+ } else {
884
+ setSearchResults(items);
885
+ }
886
+ setSearchCursor(out.nextCursor);
887
+ setSearchHasMore(!!out.nextCursor);
888
+ } catch (serverError) {
889
+ if (serverError.message?.includes("404") || serverError.message?.includes("Request failed: 404")) {
890
+ console.log("Server search not available, using client-side search");
891
+ const searchTerm = query.trim().toLowerCase();
892
+ const filtered = entries.filter(
893
+ (entry) => entry.name.toLowerCase().includes(searchTerm)
894
+ );
895
+ setSearchResults(filtered);
896
+ setSearchHasMore(false);
897
+ } else {
898
+ throw serverError;
899
+ }
900
+ }
901
+ } catch (e) {
902
+ console.error("Search failed:", e);
903
+ if (!loadMore) {
904
+ setSearchResults([]);
905
+ }
906
+ } finally {
907
+ setSearching(false);
908
+ setLoadingMore(false);
909
+ }
910
+ },
911
+ [client, view, entries, searchCursor, allowedExtensions, hideTrash]
912
+ );
913
+ useEffect2(() => {
914
+ setNextCursor(void 0);
915
+ setHasMore(false);
916
+ refresh();
917
+ }, [client, path, view]);
918
+ useEffect2(() => {
919
+ if (hideTrash && view === "trash") {
920
+ setView("files");
921
+ setPath("");
922
+ }
923
+ }, [hideTrash, view]);
924
+ useEffect2(() => {
925
+ const timeoutId = setTimeout(() => {
926
+ performSearch(searchQuery);
927
+ }, 300);
928
+ return () => clearTimeout(timeoutId);
929
+ }, [searchQuery, view]);
930
+ useEffect2(() => {
931
+ onNavigate?.(path);
932
+ }, [path, onNavigate]);
933
+ useEffect2(() => {
934
+ const source = searchQuery.trim() ? searchResults : entries;
935
+ const selectedEntries = source.filter((entry) => selected.has(entry.path));
936
+ const sig = selectedEntries.map((entry) => entry.path).sort().join("|");
937
+ if (sig === lastSelectionSigRef.current) return;
938
+ lastSelectionSigRef.current = sig;
939
+ onSelectionChange?.(selectedEntries);
940
+ }, [entries, searchResults, selected, searchQuery, onSelectionChange]);
941
+ useEffect2(() => {
942
+ const source = searchQuery.trim() ? searchResults : entries;
943
+ const selectedFiles = source.filter((entry) => selected.has(entry.path)).filter((entry) => entry.type === "file");
944
+ const toPrepare = selectedFiles.slice(0, 25).map((file) => file.path).filter((p) => !downloadUrlCacheRef.current.has(p));
945
+ if (toPrepare.length === 0) return;
946
+ let cancelled = false;
947
+ const run = async () => {
948
+ await Promise.all(
949
+ toPrepare.map(async (p) => {
950
+ try {
951
+ const out = await client.getPreviewUrl({ path: p, inline: false });
952
+ if (cancelled) return;
953
+ downloadUrlCacheRef.current.set(p, out.url);
954
+ } catch {
955
+ }
956
+ })
957
+ );
958
+ };
959
+ void run();
960
+ return () => {
961
+ cancelled = true;
962
+ };
963
+ }, [client, entries, searchResults, searchQuery, selected]);
964
+ useEffect2(() => {
965
+ if (selected.size === 1 && lastSelected?.type === "file" && selected.has(lastSelected.path)) {
966
+ const fetchPreview = async () => {
967
+ try {
968
+ const out = await client.getPreviewUrl({
969
+ path: lastSelected.path,
970
+ inline: true
971
+ });
972
+ setPreviewData({ url: out.url, entry: lastSelected });
973
+ } catch (e) {
974
+ console.error("Failed to load preview", e);
975
+ }
976
+ };
977
+ fetchPreview();
978
+ } else {
979
+ setPreviewData(null);
980
+ }
981
+ }, [selected, lastSelected, client]);
982
+ useEffect2(() => {
983
+ if (previewData) {
984
+ setPreviewDisplay(previewData);
985
+ setIsPreviewClosing(false);
986
+ return;
987
+ }
988
+ if (!previewDisplay) return;
989
+ setIsPreviewClosing(true);
990
+ const timeoutId = window.setTimeout(() => {
991
+ setPreviewDisplay(null);
992
+ setIsPreviewClosing(false);
993
+ }, 200);
994
+ return () => window.clearTimeout(timeoutId);
995
+ }, [previewData, previewDisplay]);
996
+ useEffect2(() => {
997
+ const prevent = (e) => {
998
+ e.preventDefault();
999
+ };
1000
+ window.addEventListener("dragover", prevent);
1001
+ window.addEventListener("drop", prevent);
1002
+ return () => {
1003
+ window.removeEventListener("dragover", prevent);
1004
+ window.removeEventListener("drop", prevent);
1005
+ };
1006
+ }, []);
1007
+ useEffect2(() => {
1008
+ if (selected.size === 0 && isSelectionMode) {
1009
+ setIsSelectionMode(false);
1010
+ selectionAnchorRef.current = null;
1011
+ }
1012
+ }, [selected, isSelectionMode]);
1013
+ useEffect2(() => {
1014
+ if (view !== "files") return;
1015
+ if (!selected.has(`${TRASH_PATH}/`)) return;
1016
+ setSelected((prev) => {
1017
+ const next = new Set(prev);
1018
+ next.delete(`${TRASH_PATH}/`);
1019
+ return next;
1020
+ });
1021
+ }, [selected, view]);
1022
+ useEffect2(() => {
1023
+ if (!dragSelect?.active) return;
1024
+ const handleMove = (e) => {
1025
+ const container = listRef.current;
1026
+ if (!container) return;
1027
+ const rect = container.getBoundingClientRect();
1028
+ const x = e.clientX - rect.left + container.scrollLeft;
1029
+ const y = e.clientY - rect.top + container.scrollTop;
1030
+ setDragSelect((prev) => prev ? { ...prev, x, y } : prev);
1031
+ const left = Math.min(dragSelect.startX, x);
1032
+ const top = Math.min(dragSelect.startY, y);
1033
+ const right = Math.max(dragSelect.startX, x);
1034
+ const bottom = Math.max(dragSelect.startY, y);
1035
+ const elements = Array.from(container.querySelectorAll("[data-entry-path]"));
1036
+ const selectedPaths = /* @__PURE__ */ new Set();
1037
+ for (const el of elements) {
1038
+ const pathAttr = el.getAttribute("data-entry-path");
1039
+ const selectable = el.getAttribute("data-entry-selectable");
1040
+ if (selectable === "false") continue;
1041
+ if (!pathAttr) continue;
1042
+ const elRect = el.getBoundingClientRect();
1043
+ const elLeft = elRect.left - rect.left + container.scrollLeft;
1044
+ const elTop = elRect.top - rect.top + container.scrollTop;
1045
+ const elRight = elLeft + elRect.width;
1046
+ const elBottom = elTop + elRect.height;
1047
+ const intersects = elLeft < right && elRight > left && elTop < bottom && elBottom > top;
1048
+ if (intersects) selectedPaths.add(pathAttr);
1049
+ }
1050
+ setSelected(() => {
1051
+ const base = dragSelect.additive ? new Set(dragSelectionBaseRef.current) : /* @__PURE__ */ new Set();
1052
+ for (const path2 of selectedPaths) base.add(path2);
1053
+ return base;
1054
+ });
1055
+ setLastSelected(null);
1056
+ setIsSelectionMode(true);
1057
+ };
1058
+ const handleUp = () => {
1059
+ setDragSelect(null);
1060
+ };
1061
+ window.addEventListener("mousemove", handleMove);
1062
+ window.addEventListener("mouseup", handleUp);
1063
+ return () => {
1064
+ window.removeEventListener("mousemove", handleMove);
1065
+ window.removeEventListener("mouseup", handleUp);
1066
+ };
1067
+ }, [dragSelect]);
1068
+ const updateSidebarWidth = useCallback((clientX) => {
1069
+ const availableWidth = rootRef.current?.getBoundingClientRect().width ?? window.innerWidth;
1070
+ const maxWidth = Math.min(520, availableWidth * 0.6);
1071
+ const minWidth = Math.min(280, maxWidth);
1072
+ const next = window.innerWidth - clientX;
1073
+ const clamped = Math.max(minWidth, Math.min(maxWidth, next));
1074
+ setSidebarWidth(clamped);
1075
+ }, []);
1076
+ useEffect2(() => {
1077
+ if (!isResizing) return;
1078
+ const onMouseMove = (e) => {
1079
+ e.preventDefault();
1080
+ updateSidebarWidth(e.clientX);
1081
+ };
1082
+ const onTouchMove = (e) => {
1083
+ if (!e.touches[0]) return;
1084
+ e.preventDefault();
1085
+ updateSidebarWidth(e.touches[0].clientX);
1086
+ };
1087
+ const onStop = () => setIsResizing(false);
1088
+ document.addEventListener("mousemove", onMouseMove);
1089
+ document.addEventListener("mouseup", onStop);
1090
+ document.addEventListener("mouseleave", onStop);
1091
+ document.addEventListener("touchmove", onTouchMove, { passive: false });
1092
+ document.addEventListener("touchend", onStop);
1093
+ document.addEventListener("touchcancel", onStop);
1094
+ return () => {
1095
+ document.removeEventListener("mousemove", onMouseMove);
1096
+ document.removeEventListener("mouseup", onStop);
1097
+ document.removeEventListener("mouseleave", onStop);
1098
+ document.removeEventListener("touchmove", onTouchMove);
1099
+ document.removeEventListener("touchend", onStop);
1100
+ document.removeEventListener("touchcancel", onStop);
1101
+ };
1102
+ }, [isResizing, updateSidebarWidth]);
1103
+ function handleNavigate(newPath) {
1104
+ setPath(newPath);
1105
+ }
1106
+ async function onCreateFolder() {
1107
+ if (!can.createFolder || view !== "files") return;
1108
+ if (!newFolderName.trim()) return;
1109
+ const existingNames = new Set(entries.map((entry) => entry.name));
1110
+ const uniqueName = buildUniqueName(newFolderName.trim(), existingNames);
1111
+ await client.createFolder({ path: joinPath(path, uniqueName) });
1112
+ setNewFolderName("");
1113
+ setCreateFolderOpen(false);
1114
+ refresh();
1115
+ }
1116
+ async function onRename() {
1117
+ if (!can.rename || view !== "files") return;
1118
+ if (!renameName.trim()) return;
1119
+ if (isRenaming) return;
1120
+ const target = renameTarget ?? lastSelected;
1121
+ if (!target) return;
1122
+ const oldPath = target.path;
1123
+ const parent = getParentPath(oldPath);
1124
+ const existingNames = new Set(
1125
+ entries.filter((entry) => entry.path !== oldPath).map((entry) => entry.name)
1126
+ );
1127
+ const uniqueName = buildUniqueName(renameName.trim(), existingNames);
1128
+ const nextPath = parent ? joinPath(parent, uniqueName) : uniqueName;
1129
+ const newPath = target.type === "folder" ? `${nextPath.replace(/\/+$/, "")}/` : nextPath;
1130
+ if (newPath === oldPath) {
1131
+ setRenameOpen(false);
1132
+ setRenameTarget(null);
1133
+ return;
1134
+ }
1135
+ try {
1136
+ setIsRenaming(true);
1137
+ await client.move({ fromPath: oldPath, toPath: newPath });
1138
+ setRenameOpen(false);
1139
+ setRenameTarget(null);
1140
+ refresh();
1141
+ } catch (e) {
1142
+ console.error("Rename failed", e);
1143
+ alert("Rename failed");
1144
+ } finally {
1145
+ setIsRenaming(false);
1146
+ }
1147
+ }
1148
+ async function deleteEntries(targets) {
1149
+ if (!can.delete) return;
1150
+ if (view === "files") {
1151
+ for (const target of targets) {
1152
+ if (target.path.startsWith(TRASH_PATH)) continue;
1153
+ const dest = joinPath(TRASH_PATH, target.path);
1154
+ await client.move({ fromPath: target.path, toPath: dest });
1155
+ }
1156
+ } else {
1157
+ const files = targets.filter((e) => e.type === "file").map((e) => e.path);
1158
+ const folders = targets.filter((e) => e.type === "folder");
1159
+ if (files.length > 0) await client.deleteFiles({ paths: files });
1160
+ for (const folder of folders) {
1161
+ await client.deleteFolder({ path: folder.path, recursive: true });
1162
+ }
1163
+ }
1164
+ }
1165
+ async function onDelete() {
1166
+ if (!can.delete) return;
1167
+ if (isDeleting) return;
1168
+ const source = searchQuery.trim() ? searchResults : entries;
1169
+ const targets = source.filter((e) => selected.has(e.path));
1170
+ try {
1171
+ setIsDeleting(true);
1172
+ await deleteEntries(targets);
1173
+ setDeleteOpen(false);
1174
+ refresh();
1175
+ } catch (e) {
1176
+ console.error("Delete failed", e);
1177
+ alert("Delete failed");
1178
+ } finally {
1179
+ setIsDeleting(false);
1180
+ }
1181
+ }
1182
+ async function restoreEntries(targets) {
1183
+ if (!can.restore) return;
1184
+ for (const target of targets) {
1185
+ if (!target.path.startsWith(TRASH_PATH)) continue;
1186
+ const originalPath = target.path.slice(TRASH_PATH.length + 1);
1187
+ if (!originalPath) continue;
1188
+ await client.move({ fromPath: target.path, toPath: originalPath });
1189
+ }
1190
+ }
1191
+ async function onRestore() {
1192
+ if (!can.restore) return;
1193
+ const source = searchQuery.trim() ? searchResults : entries;
1194
+ const targets = source.filter((e) => selected.has(e.path));
1195
+ await restoreEntries(targets);
1196
+ refresh();
1197
+ }
1198
+ async function onEmptyTrash() {
1199
+ if (!can.restore) return;
1200
+ if (isEmptyingTrash) return;
1201
+ try {
1202
+ setIsEmptyingTrash(true);
1203
+ await client.deleteFolder({ path: TRASH_PATH, recursive: true });
1204
+ setEmptyTrashOpen(false);
1205
+ refresh();
1206
+ } catch (e) {
1207
+ console.error("Empty trash failed", e);
1208
+ alert("Empty trash failed");
1209
+ } finally {
1210
+ setIsEmptyingTrash(false);
1211
+ }
1212
+ }
1213
+ async function onUpload(files) {
1214
+ if (!can.upload || view !== "files") return;
1215
+ if (!files || files.length === 0) return;
1216
+ const baseItems = Array.isArray(files) ? files : Array.from(files).map((file) => {
1217
+ const rel = getRelativePathFromFile(file);
1218
+ return {
1219
+ file,
1220
+ path: joinPath(path, rel ?? file.name)
1221
+ };
1222
+ });
1223
+ const existingByFolder = /* @__PURE__ */ new Map();
1224
+ const currentFolderNames = new Set(entries.map((entry) => entry.name));
1225
+ existingByFolder.set(path, currentFolderNames);
1226
+ const items = baseItems.map((item) => {
1227
+ const parent = getParentPath(item.path);
1228
+ const name = item.path.split("/").pop() ?? item.path;
1229
+ const existing = existingByFolder.get(parent) ?? /* @__PURE__ */ new Set();
1230
+ const uniqueName = buildUniqueName(name, existing);
1231
+ existing.add(uniqueName);
1232
+ existingByFolder.set(parent, existing);
1233
+ const uniquePath = parent ? joinPath(parent, uniqueName) : uniqueName;
1234
+ return { ...item, path: uniquePath };
1235
+ });
1236
+ const initialUploadItems = items.map((item) => ({
1237
+ file: item.file,
1238
+ path: item.path,
1239
+ name: item.path.split("/").pop() ?? item.path,
1240
+ loaded: 0,
1241
+ total: item.file.size ?? 0,
1242
+ status: "uploading",
1243
+ error: void 0
1244
+ }));
1245
+ setUploadCardOpen(true);
1246
+ setUploadItems(initialUploadItems);
1247
+ await client.uploadFiles({
1248
+ files: items,
1249
+ parallel: 4,
1250
+ hooks: {
1251
+ onUploadProgress: ({ path: p, loaded, total }) => {
1252
+ if (!total) return;
1253
+ setUploadItems(
1254
+ (prev) => prev.map(
1255
+ (item) => item.path === p ? {
1256
+ ...item,
1257
+ loaded,
1258
+ total: total ?? item.total,
1259
+ status: "uploading",
1260
+ error: void 0
1261
+ } : item
1262
+ )
1263
+ );
1264
+ },
1265
+ onUploadComplete: ({ path: p }) => setUploadItems(
1266
+ (prev) => prev.map(
1267
+ (item) => item.path === p ? {
1268
+ ...item,
1269
+ loaded: item.total,
1270
+ status: "done",
1271
+ error: void 0
1272
+ } : item
1273
+ )
1274
+ ),
1275
+ onUploadError: ({ path: p, error }) => setUploadItems(
1276
+ (prev) => prev.map((item) => {
1277
+ if (item.path !== p) return item;
1278
+ const message = error instanceof Error ? error.message : error ? String(error) : "Upload failed";
1279
+ return { ...item, status: "error", error: message };
1280
+ })
1281
+ )
1282
+ }
1283
+ });
1284
+ refresh();
1285
+ }
1286
+ async function retryUpload(item) {
1287
+ if (!can.upload || view !== "files") return;
1288
+ if (!item.file) {
1289
+ window.alert("Original file not available. Please re-upload.");
1290
+ return;
1291
+ }
1292
+ const file = item.file;
1293
+ setUploadCardOpen(true);
1294
+ setUploadItems(
1295
+ (prev) => prev.map(
1296
+ (entry) => entry.path === item.path ? {
1297
+ ...entry,
1298
+ loaded: 0,
1299
+ total: file.size ?? entry.total,
1300
+ status: "uploading",
1301
+ error: void 0
1302
+ } : entry
1303
+ )
1304
+ );
1305
+ await client.uploadFiles({
1306
+ files: [{ file, path: item.path }],
1307
+ parallel: 1,
1308
+ hooks: {
1309
+ onUploadProgress: ({ path: p, loaded, total }) => {
1310
+ if (!total) return;
1311
+ setUploadItems(
1312
+ (prev) => prev.map(
1313
+ (entry) => entry.path === p ? {
1314
+ ...entry,
1315
+ loaded,
1316
+ total: total ?? entry.total,
1317
+ status: "uploading",
1318
+ error: void 0
1319
+ } : entry
1320
+ )
1321
+ );
1322
+ },
1323
+ onUploadComplete: ({ path: p }) => setUploadItems(
1324
+ (prev) => prev.map(
1325
+ (entry) => entry.path === p ? {
1326
+ ...entry,
1327
+ loaded: entry.total,
1328
+ status: "done",
1329
+ error: void 0
1330
+ } : entry
1331
+ )
1332
+ ),
1333
+ onUploadError: ({ path: p, error }) => setUploadItems(
1334
+ (prev) => prev.map((entry) => {
1335
+ if (entry.path !== p) return entry;
1336
+ const message = error instanceof Error ? error.message : error ? String(error) : "Upload failed";
1337
+ return { ...entry, status: "error", error: message };
1338
+ })
1339
+ )
1340
+ }
1341
+ });
1342
+ refresh();
1343
+ }
1344
+ function openEntry(entry) {
1345
+ if (entry.type === "folder") {
1346
+ let nextPath = entry.path;
1347
+ if (view === "trash") {
1348
+ nextPath = entry.path.slice(TRASH_PATH.length + 1);
1349
+ setPath(nextPath);
1350
+ return;
1351
+ }
1352
+ if (entry.path.startsWith(TRASH_PATH)) {
1353
+ setView("trash");
1354
+ const relPath = entry.path.slice(TRASH_PATH.length + 1);
1355
+ setPath(relPath);
1356
+ return;
1357
+ }
1358
+ setPath(nextPath);
1359
+ return;
1360
+ }
1361
+ setSelected(/* @__PURE__ */ new Set([entry.path]));
1362
+ selectionAnchorRef.current = entry.path;
1363
+ setIsSelectionMode(true);
1364
+ setLastSelected(entry);
1365
+ onFileSelect?.(entry);
1366
+ }
1367
+ function selectRange(entries2, startPath, endPath) {
1368
+ const startIndex = entries2.findIndex((entry) => entry.path === startPath);
1369
+ const endIndex = entries2.findIndex((entry) => entry.path === endPath);
1370
+ if (startIndex === -1 || endIndex === -1) {
1371
+ return /* @__PURE__ */ new Set([endPath]);
1372
+ }
1373
+ const [from, to] = startIndex < endIndex ? [startIndex, endIndex] : [endIndex, startIndex];
1374
+ const next = /* @__PURE__ */ new Set();
1375
+ for (let i = from; i <= to; i += 1) {
1376
+ next.add(entries2[i].path);
1377
+ }
1378
+ return next;
1379
+ }
1380
+ function handleEntryClickWithSelection(entry, index, entries2, e) {
1381
+ const isTrashFolder = view === "files" && entry.type === "folder" && entry.path === `${TRASH_PATH}/`;
1382
+ if (isTrashFolder) {
1383
+ openEntry(entry);
1384
+ return;
1385
+ }
1386
+ if (suppressClickRef.current) {
1387
+ suppressClickRef.current = false;
1388
+ return;
1389
+ }
1390
+ if (e.shiftKey && selectionAnchorRef.current) {
1391
+ if (selection === "single") {
1392
+ setSelected(/* @__PURE__ */ new Set([entry.path]));
1393
+ setLastSelected(null);
1394
+ setIsSelectionMode(true);
1395
+ return;
1396
+ }
1397
+ const next = selectRange(entries2, selectionAnchorRef.current, entry.path);
1398
+ setSelected(next);
1399
+ setLastSelected(null);
1400
+ setIsSelectionMode(true);
1401
+ return;
1402
+ }
1403
+ if (isSelectionMode || e.metaKey || e.ctrlKey) {
1404
+ setSelected((prev) => {
1405
+ const next = new Set(prev);
1406
+ if (next.has(entry.path)) {
1407
+ next.delete(entry.path);
1408
+ } else {
1409
+ if (selection === "single") {
1410
+ next.clear();
1411
+ }
1412
+ next.add(entry.path);
1413
+ }
1414
+ setIsSelectionMode(next.size > 0);
1415
+ return next;
1416
+ });
1417
+ selectionAnchorRef.current = entry.path;
1418
+ setLastSelected(null);
1419
+ return;
1420
+ }
1421
+ setSelected((prev) => {
1422
+ const next = new Set(prev);
1423
+ const alreadySelected = next.has(entry.path);
1424
+ if (selection === "single") {
1425
+ next.clear();
1426
+ if (!alreadySelected) {
1427
+ next.add(entry.path);
1428
+ }
1429
+ } else if (alreadySelected) {
1430
+ next.delete(entry.path);
1431
+ } else {
1432
+ next.add(entry.path);
1433
+ }
1434
+ setIsSelectionMode(next.size > 0);
1435
+ return next;
1436
+ });
1437
+ selectionAnchorRef.current = entry.path;
1438
+ setLastSelected(null);
1439
+ }
1440
+ function handleLongPressSelect(entry) {
1441
+ if (view === "files" && entry.type === "folder" && entry.path === `${TRASH_PATH}/`) {
1442
+ return;
1443
+ }
1444
+ setIsSelectionMode(true);
1445
+ selectionAnchorRef.current = entry.path;
1446
+ setSelected((prev) => {
1447
+ const next = new Set(prev);
1448
+ if (selection === "single") {
1449
+ next.clear();
1450
+ }
1451
+ next.add(entry.path);
1452
+ return next;
1453
+ });
1454
+ setLastSelected(null);
1455
+ suppressClickRef.current = true;
1456
+ }
1457
+ async function bulkDownload(entriesToDownload) {
1458
+ const files = entriesToDownload.filter((entry) => entry.type === "file");
1459
+ if (files.length === 0) return;
1460
+ const cachedUrls = files.map((f) => downloadUrlCacheRef.current.get(f.path)).filter((u) => typeof u === "string");
1461
+ if (cachedUrls.length !== files.length) {
1462
+ if (files.length === 1) {
1463
+ try {
1464
+ const out = await client.getPreviewUrl({
1465
+ path: files[0].path,
1466
+ inline: false
1467
+ });
1468
+ downloadUrlCacheRef.current.set(files[0].path, out.url);
1469
+ const a = document.createElement("a");
1470
+ a.href = out.url;
1471
+ a.target = "_blank";
1472
+ a.rel = "noopener noreferrer";
1473
+ document.body.appendChild(a);
1474
+ a.click();
1475
+ document.body.removeChild(a);
1476
+ } catch (e) {
1477
+ console.error(e);
1478
+ }
1479
+ return;
1480
+ }
1481
+ window.alert("Preparing download links\u2026 please click Download again in a moment.");
1482
+ return;
1483
+ }
1484
+ if (cachedUrls.length > 20) {
1485
+ const ok = window.confirm(
1486
+ `Download ${cachedUrls.length} files? Your browser may ask to allow multiple downloads.`
1487
+ );
1488
+ if (!ok) return;
1489
+ }
1490
+ for (const url of cachedUrls) {
1491
+ const a = document.createElement("a");
1492
+ a.href = url;
1493
+ a.target = "_blank";
1494
+ a.rel = "noopener noreferrer";
1495
+ document.body.appendChild(a);
1496
+ a.click();
1497
+ document.body.removeChild(a);
1498
+ }
1499
+ }
1500
+ async function bulkCopy(entriesToCopy) {
1501
+ if (!can.copy) return;
1502
+ const dest = window.prompt("Copy to folder path", path || "");
1503
+ if (!dest) return;
1504
+ const baseDest = dest.replace(/\/+$/, "");
1505
+ for (const entry of entriesToCopy) {
1506
+ if (entry.path.startsWith(TRASH_PATH)) continue;
1507
+ const targetName = entry.name || entry.path.split("/").pop() || entry.path;
1508
+ const toPath = entry.type === "folder" ? `${joinPath(baseDest, targetName)}/` : joinPath(baseDest, targetName);
1509
+ await client.copy({ fromPath: entry.path, toPath });
1510
+ }
1511
+ refresh();
1512
+ }
1513
+ async function bulkMove(entriesToMove) {
1514
+ if (!can.move) return;
1515
+ const dest = window.prompt("Move to folder path", path || "");
1516
+ if (!dest) return;
1517
+ const baseDest = dest.replace(/\/+$/, "");
1518
+ for (const entry of entriesToMove) {
1519
+ if (entry.path.startsWith(TRASH_PATH)) continue;
1520
+ const targetName = entry.name || entry.path.split("/").pop() || entry.path;
1521
+ const toPath = entry.type === "folder" ? `${joinPath(baseDest, targetName)}/` : joinPath(baseDest, targetName);
1522
+ await client.move({ fromPath: entry.path, toPath });
1523
+ }
1524
+ refresh();
1525
+ }
1526
+ const breadcrumbs = useMemo(() => {
1527
+ const parts = path.split("/").filter(Boolean);
1528
+ const crumbs = [{ name: view === "trash" ? "Trash" : "Home", path: "" }];
1529
+ let cur = "";
1530
+ for (const part of parts) {
1531
+ cur = cur ? `${cur}/${part}` : part;
1532
+ crumbs.push({ name: part, path: cur });
1533
+ }
1534
+ return crumbs;
1535
+ }, [path, view]);
1536
+ const handleScroll = useCallback(
1537
+ (e) => {
1538
+ const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
1539
+ const isNearBottom = scrollHeight - scrollTop <= clientHeight + 100;
1540
+ if (isNearBottom && !loadingMore && !loading && !searching) {
1541
+ const isSearching = searchQuery.trim();
1542
+ const hasMoreItems = isSearching ? searchHasMore : hasMore;
1543
+ if (hasMoreItems) {
1544
+ if (isSearching) {
1545
+ performSearch(searchQuery, true);
1546
+ } else {
1547
+ refresh(true);
1548
+ }
1549
+ }
1550
+ }
1551
+ },
1552
+ [loadingMore, loading, searching, searchQuery, searchHasMore, hasMore, performSearch, refresh]
1553
+ );
1554
+ const currentEntries = useMemo(() => {
1555
+ return searchQuery.trim() ? searchResults : entries;
1556
+ }, [searchQuery, searchResults, entries]);
1557
+ useEffect2(() => {
1558
+ let cancelled = false;
1559
+ const toFetch = currentEntries.filter((entry) => entry.type === "file" && isImageFileName(entry.name)).filter((entry) => !inlinePreviews[entry.path]).slice(0, 50);
1560
+ if (toFetch.length === 0) return;
1561
+ const run = async () => {
1562
+ for (const entry of toFetch) {
1563
+ try {
1564
+ const out = await client.getPreviewUrl({
1565
+ path: entry.path,
1566
+ inline: true
1567
+ });
1568
+ if (cancelled) return;
1569
+ setInlinePreviews(
1570
+ (prev) => prev[entry.path] ? prev : { ...prev, [entry.path]: out.url }
1571
+ );
1572
+ } catch {
1573
+ }
1574
+ }
1575
+ };
1576
+ void run();
1577
+ return () => {
1578
+ cancelled = true;
1579
+ };
1580
+ }, [currentEntries, inlinePreviews, client, allowedExtensions]);
1581
+ const showSidebar = previewDisplay !== null;
1582
+ const showSidebarOnLayout = !isMobile && mode !== "picker";
1583
+ const isSidebarVisible = showSidebarOnLayout && showSidebar;
1584
+ async function getDroppedItems(items) {
1585
+ const entries2 = [];
1586
+ for (const item of Array.from(items)) {
1587
+ const entry = item.webkitGetAsEntry?.();
1588
+ if (entry) entries2.push(entry);
1589
+ }
1590
+ if (entries2.length === 0) return [];
1591
+ const results = [];
1592
+ const readEntries = (reader) => new Promise((resolve) => {
1593
+ reader.readEntries((batch) => resolve(batch));
1594
+ });
1595
+ const traverse = async (entry, prefix) => {
1596
+ if (entry.isFile) {
1597
+ const file = await new Promise((resolve) => entry.file(resolve));
1598
+ const rel = normalizeRelativePath(prefix ? `${prefix}/${file.name}` : file.name);
1599
+ results.push({ file, path: joinPath(path, rel) });
1600
+ return;
1601
+ }
1602
+ if (entry.isDirectory) {
1603
+ const reader = entry.createReader();
1604
+ let batch = await readEntries(reader);
1605
+ while (batch.length > 0) {
1606
+ for (const child of batch) {
1607
+ await traverse(child, prefix ? `${prefix}/${entry.name}` : entry.name);
1608
+ }
1609
+ batch = await readEntries(reader);
1610
+ }
1611
+ }
1612
+ };
1613
+ for (const entry of entries2) {
1614
+ await traverse(entry, "");
1615
+ }
1616
+ return results;
1617
+ }
1618
+ return /* @__PURE__ */ jsxs2(
1619
+ "div",
1620
+ {
1621
+ style: {
1622
+ minHeight: isMobile ? 0 : 500,
1623
+ maxHeight: isMobile ? "100%" : "100vh",
1624
+ ...style,
1625
+ ...{
1626
+ "--s3kit-bg": theme.bg,
1627
+ "--s3kit-bg-secondary": theme.bgSecondary,
1628
+ "--s3kit-border": theme.border,
1629
+ "--s3kit-text": theme.text,
1630
+ "--s3kit-text-secondary": theme.textSecondary,
1631
+ "--s3kit-icon-bg": theme.bgSecondary,
1632
+ "--s3kit-icon-border": theme.border,
1633
+ "--s3kit-icon-radius": "8px"
1634
+ }
1635
+ },
1636
+ className: `${FileManager_default.root}${className ? ` ${className}` : ""}`,
1637
+ ref: rootRef,
1638
+ onDragEnter: handleDragEnter,
1639
+ onDragLeave: handleDragLeave,
1640
+ onDragOver: handleDragOver,
1641
+ onDrop: handleDrop,
1642
+ onKeyDown: handleKeyDown,
1643
+ tabIndex: 0,
1644
+ children: [
1645
+ /* @__PURE__ */ jsxs2(
1646
+ "div",
1647
+ {
1648
+ className: FileManager_default.main,
1649
+ style: {
1650
+ ...isDragOver ? {
1651
+ backgroundColor: theme.bg === "#ffffff" ? "#fafafa" : "#1a1a1a"
1652
+ } : void 0
1653
+ },
1654
+ children: [
1655
+ /* @__PURE__ */ jsxs2(
1656
+ "div",
1657
+ {
1658
+ className: FileManager_default.header,
1659
+ style: {
1660
+ padding: isMobile ? "12px 16px" : "16px 24px",
1661
+ ...isMobile ? { gap: 8 } : {}
1662
+ },
1663
+ children: [
1664
+ /* @__PURE__ */ jsx5(
1665
+ "div",
1666
+ {
1667
+ style: {
1668
+ display: "flex",
1669
+ alignItems: "center",
1670
+ flex: 1,
1671
+ minWidth: 0
1672
+ },
1673
+ children: /* @__PURE__ */ jsx5(
1674
+ "div",
1675
+ {
1676
+ className: FileManager_default.navTabs,
1677
+ style: { flexWrap: isMobile ? "wrap" : "nowrap" },
1678
+ children: /* @__PURE__ */ jsxs2(
1679
+ "button",
1680
+ {
1681
+ type: "button",
1682
+ className: FileManager_default.navTab,
1683
+ style: {
1684
+ backgroundColor: view === "files" ? theme.selected : "transparent",
1685
+ color: view === "files" ? theme.text : theme.textSecondary,
1686
+ ...isMobile ? { flex: "1 1 auto", justifyContent: "center" } : {}
1687
+ },
1688
+ onClick: () => {
1689
+ setView("files");
1690
+ setPath("");
1691
+ setSelected(/* @__PURE__ */ new Set());
1692
+ setLastSelected(null);
1693
+ },
1694
+ children: [
1695
+ /* @__PURE__ */ jsx5(UiIcon, { icon: House, size: 16 }),
1696
+ "Home"
1697
+ ]
1698
+ }
1699
+ )
1700
+ }
1701
+ )
1702
+ }
1703
+ ),
1704
+ /* @__PURE__ */ jsxs2(
1705
+ "div",
1706
+ {
1707
+ style: {
1708
+ display: "flex",
1709
+ gap: 8,
1710
+ flexWrap: "nowrap",
1711
+ flexShrink: 0
1712
+ },
1713
+ children: [
1714
+ view === "trash" && selected.size === 0 && !hideTrash && can.restore && /* @__PURE__ */ jsx5(Button, { variant: "danger", onClick: () => setEmptyTrashOpen(true), theme, children: labelText.emptyTrash }),
1715
+ view === "trash" && selected.size > 0 && !hideTrash && can.restore && /* @__PURE__ */ jsxs2(Button, { onClick: onRestore, theme, children: [
1716
+ /* @__PURE__ */ jsx5(UiIcon, { icon: ArrowCounterClockwise, size: 16 }),
1717
+ " ",
1718
+ labelText.restore
1719
+ ] }),
1720
+ selected.size > 0 ? can.delete && /* @__PURE__ */ jsxs2(Button, { variant: "danger", onClick: () => setDeleteOpen(true), theme, children: [
1721
+ /* @__PURE__ */ jsx5(UiIcon, { icon: Trash, size: 16 }),
1722
+ " ",
1723
+ view === "trash" ? labelText.deleteForever : labelText.delete
1724
+ ] }) : view === "files" ? /* @__PURE__ */ jsxs2(Fragment, { children: [
1725
+ can.createFolder && /* @__PURE__ */ jsxs2(Button, { onClick: () => setCreateFolderOpen(true), theme, children: [
1726
+ /* @__PURE__ */ jsx5(UiIcon, { icon: FolderPlus, size: 16 }),
1727
+ " ",
1728
+ labelText.newFolder
1729
+ ] }),
1730
+ can.upload && /* @__PURE__ */ jsxs2(
1731
+ Button,
1732
+ {
1733
+ variant: "primary",
1734
+ onClick: () => fileInputRef.current?.click(),
1735
+ theme,
1736
+ children: [
1737
+ /* @__PURE__ */ jsx5(UiIcon, { icon: UploadSimple, size: 16 }),
1738
+ " ",
1739
+ labelText.upload
1740
+ ]
1741
+ }
1742
+ )
1743
+ ] }) : null,
1744
+ mode === "picker" && /* @__PURE__ */ jsx5(
1745
+ Button,
1746
+ {
1747
+ variant: "primary",
1748
+ onClick: () => {
1749
+ const source = searchQuery.trim() ? searchResults : entries;
1750
+ const selectedEntries = source.filter((entry) => selected.has(entry.path));
1751
+ onConfirm?.(selectedEntries);
1752
+ },
1753
+ disabled: selected.size === 0,
1754
+ theme,
1755
+ children: labelText.confirm
1756
+ }
1757
+ ),
1758
+ /* @__PURE__ */ jsx5(
1759
+ "input",
1760
+ {
1761
+ type: "file",
1762
+ multiple: true,
1763
+ ref: fileInputRef,
1764
+ style: { display: "none" },
1765
+ disabled: !can.upload || view !== "files",
1766
+ onChange: (e) => onUpload(e.target.files)
1767
+ }
1768
+ )
1769
+ ]
1770
+ }
1771
+ )
1772
+ ]
1773
+ }
1774
+ ),
1775
+ showBreadcrumbs && (path || view === "trash") && /* @__PURE__ */ jsx5(
1776
+ "div",
1777
+ {
1778
+ style: {
1779
+ display: "flex",
1780
+ alignItems: "center",
1781
+ gap: 6,
1782
+ fontSize: 13,
1783
+ padding: isMobile ? "8px 16px" : "10px 24px",
1784
+ borderBottom: `1px solid ${theme.border}`,
1785
+ overflowX: isMobile ? "auto" : "visible",
1786
+ whiteSpace: isMobile ? "nowrap" : "normal"
1787
+ },
1788
+ children: breadcrumbs.map((crumb, idx) => /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
1789
+ idx > 0 && /* @__PURE__ */ jsx5(UiIcon, { icon: CaretRight, size: 12, color: theme.textSecondary }),
1790
+ /* @__PURE__ */ jsx5(
1791
+ "button",
1792
+ {
1793
+ type: "button",
1794
+ onClick: () => handleNavigate(crumb.path),
1795
+ style: {
1796
+ background: "none",
1797
+ border: "none",
1798
+ padding: 0,
1799
+ cursor: "pointer",
1800
+ fontSize: 13,
1801
+ fontWeight: idx === breadcrumbs.length - 1 ? 600 : 400,
1802
+ color: idx === breadcrumbs.length - 1 ? theme.text : theme.textSecondary
1803
+ },
1804
+ children: crumb.name
1805
+ }
1806
+ )
1807
+ ] }, idx))
1808
+ }
1809
+ ),
1810
+ (showSearch || showSort || mode !== "viewer" && showViewSwitcher) && /* @__PURE__ */ jsxs2(
1811
+ "div",
1812
+ {
1813
+ style: {
1814
+ display: "flex",
1815
+ alignItems: "center",
1816
+ justifyContent: showSearch ? "flex-start" : "flex-end",
1817
+ gap: isMobile ? 8 : 12,
1818
+ padding: isMobile ? "10px 16px" : "12px 24px",
1819
+ borderBottom: `1px solid ${theme.border}`,
1820
+ flexWrap: "nowrap"
1821
+ },
1822
+ children: [
1823
+ showSearch && /* @__PURE__ */ jsxs2(
1824
+ "div",
1825
+ {
1826
+ style: {
1827
+ display: "flex",
1828
+ alignItems: "center",
1829
+ gap: 8,
1830
+ flex: 1,
1831
+ minWidth: 0
1832
+ },
1833
+ children: [
1834
+ /* @__PURE__ */ jsxs2(
1835
+ "div",
1836
+ {
1837
+ style: {
1838
+ display: "flex",
1839
+ alignItems: "center",
1840
+ gap: 8,
1841
+ flex: 1,
1842
+ minWidth: 0,
1843
+ height: toolbarControlHeight,
1844
+ padding: "0 10px",
1845
+ border: `1px solid ${theme.border}`,
1846
+ backgroundColor: theme.bg,
1847
+ color: theme.text,
1848
+ boxSizing: "border-box"
1849
+ },
1850
+ children: [
1851
+ /* @__PURE__ */ jsx5(UiIcon, { icon: MagnifyingGlass, size: 16, color: theme.textSecondary }),
1852
+ /* @__PURE__ */ jsx5(
1853
+ "input",
1854
+ {
1855
+ type: "text",
1856
+ placeholder: labelText.searchPlaceholder,
1857
+ value: searchQuery,
1858
+ onChange: (e) => setSearchQuery(e.target.value),
1859
+ style: {
1860
+ flex: 1,
1861
+ minWidth: 0,
1862
+ height: "100%",
1863
+ padding: 0,
1864
+ fontSize: 13,
1865
+ border: "none",
1866
+ backgroundColor: "transparent",
1867
+ color: theme.text,
1868
+ outline: "none"
1869
+ }
1870
+ }
1871
+ )
1872
+ ]
1873
+ }
1874
+ ),
1875
+ (searchQuery || searching) && /* @__PURE__ */ jsx5(
1876
+ "button",
1877
+ {
1878
+ type: "button",
1879
+ onClick: () => {
1880
+ setSearchQuery("");
1881
+ setSearchResults([]);
1882
+ },
1883
+ disabled: searching,
1884
+ style: {
1885
+ background: "none",
1886
+ border: "none",
1887
+ cursor: searching ? "default" : "pointer",
1888
+ width: toolbarControlHeight,
1889
+ height: toolbarControlHeight,
1890
+ padding: 0,
1891
+ display: "flex",
1892
+ alignItems: "center",
1893
+ justifyContent: "center",
1894
+ color: theme.textSecondary,
1895
+ opacity: searching ? 0.5 : 1
1896
+ },
1897
+ children: /* @__PURE__ */ jsx5(UiIcon, { icon: X2, size: 14 })
1898
+ }
1899
+ )
1900
+ ]
1901
+ }
1902
+ ),
1903
+ (showSort || mode !== "viewer" && showViewSwitcher) && /* @__PURE__ */ jsxs2(
1904
+ "div",
1905
+ {
1906
+ style: {
1907
+ display: "flex",
1908
+ alignItems: "center",
1909
+ gap: 8,
1910
+ flexShrink: 0
1911
+ },
1912
+ children: [
1913
+ mode !== "viewer" && showViewSwitcher && /* @__PURE__ */ jsxs2(
1914
+ "div",
1915
+ {
1916
+ style: {
1917
+ display: "flex",
1918
+ border: `1px solid ${theme.border}`,
1919
+ flexShrink: 0,
1920
+ height: toolbarControlHeight
1921
+ },
1922
+ children: [
1923
+ /* @__PURE__ */ jsx5(
1924
+ "button",
1925
+ {
1926
+ type: "button",
1927
+ onClick: () => setViewMode("list"),
1928
+ style: {
1929
+ background: viewMode === "list" ? theme.selected : "none",
1930
+ border: "none",
1931
+ cursor: "pointer",
1932
+ padding: 0,
1933
+ display: "flex",
1934
+ alignItems: "center",
1935
+ justifyContent: "center",
1936
+ color: theme.text,
1937
+ width: toolbarControlHeight,
1938
+ height: toolbarControlHeight
1939
+ },
1940
+ title: "List View",
1941
+ children: /* @__PURE__ */ jsx5(UiIcon, { icon: List, size: 16 })
1942
+ }
1943
+ ),
1944
+ /* @__PURE__ */ jsx5(
1945
+ "button",
1946
+ {
1947
+ type: "button",
1948
+ onClick: () => setViewMode("grid"),
1949
+ style: {
1950
+ background: viewMode === "grid" ? theme.selected : "none",
1951
+ border: "none",
1952
+ cursor: "pointer",
1953
+ padding: 0,
1954
+ display: "flex",
1955
+ alignItems: "center",
1956
+ justifyContent: "center",
1957
+ color: theme.text,
1958
+ width: toolbarControlHeight,
1959
+ height: toolbarControlHeight
1960
+ },
1961
+ title: "Grid View",
1962
+ children: /* @__PURE__ */ jsx5(UiIcon, { icon: SquaresFour, size: 16 })
1963
+ }
1964
+ )
1965
+ ]
1966
+ }
1967
+ ),
1968
+ showSort && viewMode === "grid" && /* @__PURE__ */ jsx5(
1969
+ "button",
1970
+ {
1971
+ type: "button",
1972
+ onClick: () => setSortOrder(sortOrder === "asc" ? "desc" : "asc"),
1973
+ style: {
1974
+ background: theme.bg,
1975
+ border: `1px solid ${theme.border}`,
1976
+ cursor: "pointer",
1977
+ padding: 0,
1978
+ display: "flex",
1979
+ alignItems: "center",
1980
+ justifyContent: "center",
1981
+ color: theme.text,
1982
+ width: toolbarControlHeight,
1983
+ height: toolbarControlHeight
1984
+ },
1985
+ title: `Sort ${sortOrder === "asc" ? "Descending" : "Ascending"}`,
1986
+ children: sortOrder === "asc" ? /* @__PURE__ */ jsx5(UiIcon, { icon: ArrowUp, size: 16 }) : /* @__PURE__ */ jsx5(UiIcon, { icon: ArrowDown, size: 16 })
1987
+ }
1988
+ )
1989
+ ]
1990
+ }
1991
+ )
1992
+ ]
1993
+ }
1994
+ ),
1995
+ showSearch && searchQuery.trim() && /* @__PURE__ */ jsxs2(
1996
+ "div",
1997
+ {
1998
+ style: {
1999
+ padding: "8px 24px",
2000
+ backgroundColor: theme.bgSecondary,
2001
+ borderBottom: `1px solid ${theme.border}`,
2002
+ fontSize: 12,
2003
+ color: theme.textSecondary,
2004
+ display: "flex",
2005
+ alignItems: "center",
2006
+ justifyContent: "space-between"
2007
+ },
2008
+ children: [
2009
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
2010
+ /* @__PURE__ */ jsx5(UiIcon, { icon: MagnifyingGlass, size: 14 }),
2011
+ searching ? `Searching for "${searchQuery}"...` : `Found ${currentEntries.length} result${currentEntries.length !== 1 ? "s" : ""} for "${searchQuery}"`
2012
+ ] }),
2013
+ searchHasMore && /* @__PURE__ */ jsx5("div", { style: { fontSize: 11, opacity: 0.7 }, children: "More results available" })
2014
+ ]
2015
+ }
2016
+ ),
2017
+ /* @__PURE__ */ jsxs2(
2018
+ "div",
2019
+ {
2020
+ style: {
2021
+ flex: 1,
2022
+ overflow: "auto",
2023
+ minHeight: 0,
2024
+ // This is important for flex children to shrink properly
2025
+ WebkitOverflowScrolling: "touch",
2026
+ position: "relative"
2027
+ },
2028
+ ref: listRef,
2029
+ onScroll: handleScroll,
2030
+ onMouseDown: (e) => {
2031
+ if (isMobile) return;
2032
+ if (e.button !== 0) return;
2033
+ const target = e.target;
2034
+ if (target.closest("[data-entry-path]")) return;
2035
+ const container = listRef.current;
2036
+ if (!container) return;
2037
+ e.preventDefault();
2038
+ const rect = container.getBoundingClientRect();
2039
+ const startX = e.clientX - rect.left + container.scrollLeft;
2040
+ const startY = e.clientY - rect.top + container.scrollTop;
2041
+ dragSelectionBaseRef.current = new Set(selected);
2042
+ setDragSelect({
2043
+ active: true,
2044
+ startX,
2045
+ startY,
2046
+ x: startX,
2047
+ y: startY,
2048
+ additive: e.metaKey || e.ctrlKey
2049
+ });
2050
+ },
2051
+ children: [
2052
+ dragSelect?.active && /* @__PURE__ */ jsx5(
2053
+ "div",
2054
+ {
2055
+ style: {
2056
+ position: "absolute",
2057
+ left: Math.min(dragSelect.startX, dragSelect.x),
2058
+ top: Math.min(dragSelect.startY, dragSelect.y),
2059
+ width: Math.abs(dragSelect.x - dragSelect.startX),
2060
+ height: Math.abs(dragSelect.y - dragSelect.startY),
2061
+ border: `1px solid ${theme.accent}`,
2062
+ backgroundColor: theme.bg === "#ffffff" ? "rgba(0,0,0,0.04)" : "rgba(255,255,255,0.08)",
2063
+ pointerEvents: "none",
2064
+ zIndex: 3
2065
+ }
2066
+ }
2067
+ ),
2068
+ viewMode === "list" && /* @__PURE__ */ jsxs2(
2069
+ "div",
2070
+ {
2071
+ className: FileManager_default.tableHeader,
2072
+ style: {
2073
+ gridTemplateColumns: isMobile ? "1fr 48px" : "1fr 120px 100px 48px",
2074
+ padding: isMobile ? "10px 16px" : "10px 24px"
2075
+ },
2076
+ children: [
2077
+ showSort ? /* @__PURE__ */ jsxs2(
2078
+ "button",
2079
+ {
2080
+ type: "button",
2081
+ onClick: () => {
2082
+ if (sortBy === "name") {
2083
+ setSortOrder(sortOrder === "asc" ? "desc" : "asc");
2084
+ } else {
2085
+ setSortBy("name");
2086
+ setSortOrder("asc");
2087
+ }
2088
+ },
2089
+ style: {
2090
+ background: "none",
2091
+ border: "none",
2092
+ cursor: "pointer",
2093
+ display: "flex",
2094
+ alignItems: "center",
2095
+ gap: 4,
2096
+ fontSize: 12,
2097
+ fontWeight: 600,
2098
+ color: sortBy === "name" ? theme.text : theme.textSecondary,
2099
+ textTransform: "uppercase",
2100
+ letterSpacing: "0.05em",
2101
+ padding: 0
2102
+ },
2103
+ children: [
2104
+ "Name",
2105
+ sortBy === "name" && (sortOrder === "asc" ? /* @__PURE__ */ jsx5(UiIcon, { icon: CaretUp, size: 12 }) : /* @__PURE__ */ jsx5(UiIcon, { icon: CaretDown, size: 12 }))
2106
+ ]
2107
+ }
2108
+ ) : /* @__PURE__ */ jsx5("div", { style: headerLabelStyle, children: "Name" }),
2109
+ !isMobile && (showSort ? /* @__PURE__ */ jsxs2(
2110
+ "button",
2111
+ {
2112
+ type: "button",
2113
+ onClick: () => {
2114
+ if (sortBy === "date") {
2115
+ setSortOrder(sortOrder === "asc" ? "desc" : "asc");
2116
+ } else {
2117
+ setSortBy("date");
2118
+ setSortOrder("desc");
2119
+ }
2120
+ },
2121
+ style: {
2122
+ background: "none",
2123
+ border: "none",
2124
+ cursor: "pointer",
2125
+ display: "flex",
2126
+ alignItems: "center",
2127
+ gap: 4,
2128
+ fontSize: 12,
2129
+ fontWeight: 600,
2130
+ color: sortBy === "date" ? theme.text : theme.textSecondary,
2131
+ textTransform: "uppercase",
2132
+ letterSpacing: "0.05em",
2133
+ padding: 0
2134
+ },
2135
+ children: [
2136
+ "Date",
2137
+ sortBy === "date" && (sortOrder === "asc" ? /* @__PURE__ */ jsx5(UiIcon, { icon: CaretUp, size: 12 }) : /* @__PURE__ */ jsx5(UiIcon, { icon: CaretDown, size: 12 }))
2138
+ ]
2139
+ }
2140
+ ) : /* @__PURE__ */ jsx5("div", { style: headerLabelStyle, children: "Date" })),
2141
+ !isMobile && (showSort ? /* @__PURE__ */ jsxs2(
2142
+ "button",
2143
+ {
2144
+ type: "button",
2145
+ onClick: () => {
2146
+ if (sortBy === "size") {
2147
+ setSortOrder(sortOrder === "asc" ? "desc" : "asc");
2148
+ } else {
2149
+ setSortBy("size");
2150
+ setSortOrder("desc");
2151
+ }
2152
+ },
2153
+ style: {
2154
+ background: "none",
2155
+ border: "none",
2156
+ cursor: "pointer",
2157
+ display: "flex",
2158
+ alignItems: "center",
2159
+ gap: 4,
2160
+ fontSize: 12,
2161
+ fontWeight: 600,
2162
+ color: sortBy === "size" ? theme.text : theme.textSecondary,
2163
+ textTransform: "uppercase",
2164
+ letterSpacing: "0.05em",
2165
+ padding: 0
2166
+ },
2167
+ children: [
2168
+ "Size",
2169
+ sortBy === "size" && (sortOrder === "asc" ? /* @__PURE__ */ jsx5(UiIcon, { icon: CaretUp, size: 12 }) : /* @__PURE__ */ jsx5(UiIcon, { icon: CaretDown, size: 12 }))
2170
+ ]
2171
+ }
2172
+ ) : /* @__PURE__ */ jsx5("div", { style: headerLabelStyle, children: "Size" })),
2173
+ /* @__PURE__ */ jsx5("div", {})
2174
+ ]
2175
+ }
2176
+ ),
2177
+ loading || searching ? /* @__PURE__ */ jsx5(
2178
+ "div",
2179
+ {
2180
+ style: {
2181
+ padding: 40,
2182
+ textAlign: "center",
2183
+ color: theme.textSecondary
2184
+ },
2185
+ children: searching ? "Searching..." : "Loading..."
2186
+ }
2187
+ ) : viewMode === "grid" ? (
2188
+ // Grid View
2189
+ /* @__PURE__ */ jsx5(Fragment, { children: (() => {
2190
+ const gridTileGap = 10;
2191
+ const gridTileMinHeight = isMobile ? 120 : 140;
2192
+ const gridThumbSize = 64;
2193
+ const gridIconSize = 48;
2194
+ const gridThumbStyle = {
2195
+ width: gridThumbSize,
2196
+ height: gridThumbSize,
2197
+ display: "flex",
2198
+ alignItems: "center",
2199
+ justifyContent: "center",
2200
+ flexShrink: 0
2201
+ };
2202
+ const renderGridThumb = (entry) => {
2203
+ if (entry.type === "folder") {
2204
+ return /* @__PURE__ */ jsx5(
2205
+ UiIcon,
2206
+ {
2207
+ icon: entry.path === `${TRASH_PATH}/` ? Trash : Folder,
2208
+ size: gridIconSize,
2209
+ weight: "fill",
2210
+ color: theme.text,
2211
+ boxed: true
2212
+ }
2213
+ );
2214
+ }
2215
+ const previewUrl = inlinePreviews[entry.path];
2216
+ if (previewUrl && isImageFileName(entry.name)) {
2217
+ return /* @__PURE__ */ jsx5(
2218
+ "img",
2219
+ {
2220
+ src: previewUrl,
2221
+ alt: entry.name,
2222
+ loading: "lazy",
2223
+ decoding: "async",
2224
+ style: {
2225
+ width: gridThumbSize,
2226
+ height: gridThumbSize,
2227
+ objectFit: "cover",
2228
+ borderRadius: 8,
2229
+ border: `1px solid ${theme.border}`
2230
+ }
2231
+ }
2232
+ );
2233
+ }
2234
+ return getFileIcon(entry.name, gridIconSize);
2235
+ };
2236
+ return /* @__PURE__ */ jsx5(
2237
+ "div",
2238
+ {
2239
+ style: {
2240
+ display: "grid",
2241
+ gridTemplateColumns: isMobile ? "repeat(auto-fill, minmax(120px, 1fr))" : "repeat(auto-fill, minmax(140px, 1fr))",
2242
+ gap: 16,
2243
+ padding: isMobile ? 16 : 24
2244
+ },
2245
+ children: (() => {
2246
+ const processEntries = (entries2) => {
2247
+ let filtered = entries2;
2248
+ filtered = [...filtered].sort((a, b) => {
2249
+ let comparison = 0;
2250
+ const aIsTrash = view === "files" && a.type === "folder" && a.path === `${TRASH_PATH}/`;
2251
+ const bIsTrash = view === "files" && b.type === "folder" && b.path === `${TRASH_PATH}/`;
2252
+ if (aIsTrash !== bIsTrash) return aIsTrash ? 1 : -1;
2253
+ if (a.type !== b.type) {
2254
+ return a.type === "folder" ? -1 : 1;
2255
+ }
2256
+ switch (sortBy) {
2257
+ case "name":
2258
+ comparison = a.name.localeCompare(b.name);
2259
+ break;
2260
+ case "date":
2261
+ const aDate = a.type === "file" && a.lastModified ? new Date(a.lastModified).getTime() : 0;
2262
+ const bDate = b.type === "file" && b.lastModified ? new Date(b.lastModified).getTime() : 0;
2263
+ comparison = aDate - bDate;
2264
+ break;
2265
+ case "size":
2266
+ comparison = (a.type === "file" ? a.size || 0 : 0) - (b.type === "file" ? b.size || 0 : 0);
2267
+ break;
2268
+ case "type":
2269
+ const aExt = a.name.split(".").pop()?.toLowerCase() || "";
2270
+ const bExt = b.name.split(".").pop()?.toLowerCase() || "";
2271
+ comparison = aExt.localeCompare(bExt);
2272
+ break;
2273
+ }
2274
+ return sortOrder === "asc" ? comparison : -comparison;
2275
+ });
2276
+ return filtered;
2277
+ };
2278
+ const filteredEntries = processEntries(currentEntries);
2279
+ const selectedEntries = filteredEntries.filter(
2280
+ (item) => selected.has(item.path)
2281
+ );
2282
+ return filteredEntries.map((entry, index) => {
2283
+ const isSelected = selected.has(entry.path);
2284
+ const isTrashFolder = view === "files" && entry.type === "folder" && entry.path === `${TRASH_PATH}/`;
2285
+ const entryLabel = isTrashFolder ? "Trash" : entry.name;
2286
+ const isMultiSelected = selectedEntries.length > 1 && isSelected;
2287
+ const actionEntries = isMultiSelected ? selectedEntries : [entry];
2288
+ const hasContextMenuItems = !isMultiSelected && can.rename || actionEntries.some((item) => item.type === "file") || view === "files" && can.copy || isMultiSelected && view === "files" && can.move || isMultiSelected && view === "trash" && can.restore || can.delete;
2289
+ if (isTrashFolder) {
2290
+ return /* @__PURE__ */ jsxs2(
2291
+ "div",
2292
+ {
2293
+ "data-entry-path": entry.path,
2294
+ "data-entry-selectable": "false",
2295
+ style: {
2296
+ display: "flex",
2297
+ flexDirection: "column",
2298
+ alignItems: "center",
2299
+ justifyContent: "center",
2300
+ textAlign: "center",
2301
+ gap: gridTileGap,
2302
+ padding: 16,
2303
+ minHeight: gridTileMinHeight,
2304
+ backgroundColor: isSelected ? theme.selected : hoverRow === entry.path ? theme.hover : theme.bg,
2305
+ border: `1px solid ${isSelected ? theme.accent : theme.border}`,
2306
+ cursor: "pointer",
2307
+ outline: "none",
2308
+ transition: "all 0.15s",
2309
+ position: "relative"
2310
+ },
2311
+ onClick: (e) => handleEntryClickWithSelection(entry, index, filteredEntries, e),
2312
+ onDoubleClick: () => openEntry(entry),
2313
+ onMouseEnter: () => setHoverRow(entry.path),
2314
+ onMouseLeave: () => setHoverRow(null),
2315
+ onTouchStart: () => {
2316
+ if (!isMobile) return;
2317
+ if (longPressTimerRef.current)
2318
+ window.clearTimeout(longPressTimerRef.current);
2319
+ longPressTimerRef.current = window.setTimeout(() => {
2320
+ handleLongPressSelect(entry);
2321
+ }, 350);
2322
+ },
2323
+ onTouchMove: () => {
2324
+ if (longPressTimerRef.current) {
2325
+ window.clearTimeout(longPressTimerRef.current);
2326
+ longPressTimerRef.current = null;
2327
+ }
2328
+ },
2329
+ onTouchEnd: () => {
2330
+ if (longPressTimerRef.current) {
2331
+ window.clearTimeout(longPressTimerRef.current);
2332
+ longPressTimerRef.current = null;
2333
+ }
2334
+ },
2335
+ children: [
2336
+ /* @__PURE__ */ jsx5("div", { style: gridThumbStyle, children: renderGridThumb(entry) }),
2337
+ /* @__PURE__ */ jsx5(
2338
+ "div",
2339
+ {
2340
+ style: {
2341
+ fontSize: 12,
2342
+ fontWeight: 500,
2343
+ wordBreak: "break-word",
2344
+ color: theme.text,
2345
+ width: "100%"
2346
+ },
2347
+ children: entryLabel
2348
+ }
2349
+ )
2350
+ ]
2351
+ },
2352
+ entry.path
2353
+ );
2354
+ }
2355
+ if (!hasContextMenuItems) {
2356
+ return /* @__PURE__ */ jsxs2(
2357
+ "div",
2358
+ {
2359
+ "data-entry-path": entry.path,
2360
+ "data-entry-selectable": isTrashFolder ? "false" : "true",
2361
+ style: {
2362
+ display: "flex",
2363
+ flexDirection: "column",
2364
+ alignItems: "center",
2365
+ justifyContent: "center",
2366
+ textAlign: "center",
2367
+ gap: gridTileGap,
2368
+ padding: 16,
2369
+ minHeight: gridTileMinHeight,
2370
+ backgroundColor: isSelected ? theme.selected : hoverRow === entry.path ? theme.hover : theme.bg,
2371
+ border: `1px solid ${isSelected ? theme.accent : theme.border}`,
2372
+ cursor: "pointer",
2373
+ outline: "none",
2374
+ transition: "all 0.15s",
2375
+ position: "relative"
2376
+ },
2377
+ onClick: (e) => handleEntryClickWithSelection(entry, index, filteredEntries, e),
2378
+ onDoubleClick: () => openEntry(entry),
2379
+ onMouseEnter: () => setHoverRow(entry.path),
2380
+ onMouseLeave: () => setHoverRow(null),
2381
+ onTouchStart: () => {
2382
+ if (!isMobile) return;
2383
+ if (longPressTimerRef.current)
2384
+ window.clearTimeout(longPressTimerRef.current);
2385
+ longPressTimerRef.current = window.setTimeout(() => {
2386
+ handleLongPressSelect(entry);
2387
+ }, 350);
2388
+ },
2389
+ onTouchMove: () => {
2390
+ if (longPressTimerRef.current) {
2391
+ window.clearTimeout(longPressTimerRef.current);
2392
+ longPressTimerRef.current = null;
2393
+ }
2394
+ },
2395
+ onTouchEnd: () => {
2396
+ if (longPressTimerRef.current) {
2397
+ window.clearTimeout(longPressTimerRef.current);
2398
+ longPressTimerRef.current = null;
2399
+ }
2400
+ },
2401
+ children: [
2402
+ /* @__PURE__ */ jsx5("div", { style: gridThumbStyle, children: renderGridThumb(entry) }),
2403
+ /* @__PURE__ */ jsx5(
2404
+ "div",
2405
+ {
2406
+ style: {
2407
+ fontSize: 12,
2408
+ fontWeight: 500,
2409
+ wordBreak: "break-word",
2410
+ color: theme.text,
2411
+ width: "100%"
2412
+ },
2413
+ children: entryLabel
2414
+ }
2415
+ )
2416
+ ]
2417
+ },
2418
+ entry.path
2419
+ );
2420
+ }
2421
+ return /* @__PURE__ */ jsxs2(
2422
+ ContextMenu.Root,
2423
+ {
2424
+ onOpenChange: (open) => {
2425
+ if (open) {
2426
+ if (isTrashFolder) return;
2427
+ if (!selected.has(entry.path)) {
2428
+ setSelected(/* @__PURE__ */ new Set([entry.path]));
2429
+ selectionAnchorRef.current = entry.path;
2430
+ setLastSelected(null);
2431
+ setIsSelectionMode(true);
2432
+ }
2433
+ }
2434
+ },
2435
+ children: [
2436
+ /* @__PURE__ */ jsxs2(
2437
+ ContextMenu.Trigger,
2438
+ {
2439
+ "data-entry-path": entry.path,
2440
+ "data-entry-selectable": isTrashFolder ? "false" : "true",
2441
+ style: {
2442
+ display: "flex",
2443
+ flexDirection: "column",
2444
+ alignItems: "center",
2445
+ justifyContent: "center",
2446
+ textAlign: "center",
2447
+ gap: gridTileGap,
2448
+ padding: 16,
2449
+ minHeight: gridTileMinHeight,
2450
+ backgroundColor: isSelected ? theme.selected : hoverRow === entry.path ? theme.hover : theme.bg,
2451
+ border: `1px solid ${isSelected ? theme.accent : theme.border}`,
2452
+ cursor: "pointer",
2453
+ outline: "none",
2454
+ transition: "all 0.15s",
2455
+ position: "relative"
2456
+ },
2457
+ onClick: (e) => handleEntryClickWithSelection(entry, index, filteredEntries, e),
2458
+ onDoubleClick: () => openEntry(entry),
2459
+ onContextMenu: (e) => {
2460
+ if (isTrashFolder) {
2461
+ e.preventDefault();
2462
+ e.stopPropagation();
2463
+ }
2464
+ },
2465
+ onMouseEnter: () => setHoverRow(entry.path),
2466
+ onMouseLeave: () => setHoverRow(null),
2467
+ onTouchStart: () => {
2468
+ if (!isMobile) return;
2469
+ if (longPressTimerRef.current)
2470
+ window.clearTimeout(longPressTimerRef.current);
2471
+ longPressTimerRef.current = window.setTimeout(() => {
2472
+ handleLongPressSelect(entry);
2473
+ }, 350);
2474
+ },
2475
+ onTouchMove: () => {
2476
+ if (longPressTimerRef.current) {
2477
+ window.clearTimeout(longPressTimerRef.current);
2478
+ longPressTimerRef.current = null;
2479
+ }
2480
+ },
2481
+ onTouchEnd: () => {
2482
+ if (longPressTimerRef.current) {
2483
+ window.clearTimeout(longPressTimerRef.current);
2484
+ longPressTimerRef.current = null;
2485
+ }
2486
+ },
2487
+ children: [
2488
+ /* @__PURE__ */ jsx5("div", { style: gridThumbStyle, children: renderGridThumb(entry) }),
2489
+ /* @__PURE__ */ jsx5(
2490
+ "div",
2491
+ {
2492
+ style: {
2493
+ fontSize: 12,
2494
+ fontWeight: 500,
2495
+ wordBreak: "break-word",
2496
+ color: theme.text,
2497
+ width: "100%"
2498
+ },
2499
+ children: entryLabel
2500
+ }
2501
+ )
2502
+ ]
2503
+ }
2504
+ ),
2505
+ /* @__PURE__ */ jsx5(ContextMenu.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx5(ContextMenu.Positioner, { style: { zIndex: 1e4 }, children: /* @__PURE__ */ jsxs2(
2506
+ ContextMenu.Popup,
2507
+ {
2508
+ style: {
2509
+ zIndex: 1e4,
2510
+ backgroundColor: theme.bg,
2511
+ border: `1px solid ${theme.border}`,
2512
+ boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
2513
+ padding: 4,
2514
+ display: "flex",
2515
+ flexDirection: "column",
2516
+ minWidth: 160,
2517
+ outline: "none"
2518
+ },
2519
+ children: [
2520
+ !isMultiSelected && can.rename && /* @__PURE__ */ jsxs2(
2521
+ ContextMenu.Item,
2522
+ {
2523
+ style: {
2524
+ display: "flex",
2525
+ alignItems: "center",
2526
+ gap: 10,
2527
+ padding: "8px 12px",
2528
+ border: "none",
2529
+ background: "none",
2530
+ cursor: "pointer",
2531
+ fontSize: 13,
2532
+ textAlign: "left",
2533
+ color: theme.text,
2534
+ outline: "none"
2535
+ },
2536
+ onClick: () => {
2537
+ setRenameName(entry.name || "");
2538
+ setRenameTarget(entry);
2539
+ setRenameOpen(true);
2540
+ },
2541
+ children: [
2542
+ /* @__PURE__ */ jsx5(UiIcon, { icon: PencilSimple, size: 16 }),
2543
+ " Rename"
2544
+ ]
2545
+ }
2546
+ ),
2547
+ actionEntries.some((item) => item.type === "file") && /* @__PURE__ */ jsxs2(
2548
+ ContextMenu.Item,
2549
+ {
2550
+ style: {
2551
+ display: "flex",
2552
+ alignItems: "center",
2553
+ gap: 10,
2554
+ padding: "8px 12px",
2555
+ border: "none",
2556
+ background: "none",
2557
+ cursor: "pointer",
2558
+ fontSize: 13,
2559
+ textAlign: "left",
2560
+ color: theme.text,
2561
+ outline: "none"
2562
+ },
2563
+ onClick: () => bulkDownload(actionEntries),
2564
+ children: [
2565
+ /* @__PURE__ */ jsx5(UiIcon, { icon: DownloadSimple, size: 16 }),
2566
+ " Download"
2567
+ ]
2568
+ }
2569
+ ),
2570
+ isMultiSelected && view === "files" && can.move && /* @__PURE__ */ jsxs2(
2571
+ ContextMenu.Item,
2572
+ {
2573
+ style: {
2574
+ display: "flex",
2575
+ alignItems: "center",
2576
+ gap: 10,
2577
+ padding: "8px 12px",
2578
+ border: "none",
2579
+ background: "none",
2580
+ cursor: "pointer",
2581
+ fontSize: 13,
2582
+ textAlign: "left",
2583
+ color: theme.text,
2584
+ outline: "none"
2585
+ },
2586
+ onClick: () => bulkMove(actionEntries),
2587
+ children: [
2588
+ /* @__PURE__ */ jsx5(UiIcon, { icon: ArrowRight, size: 16 }),
2589
+ " Move"
2590
+ ]
2591
+ }
2592
+ ),
2593
+ view === "files" && can.copy && /* @__PURE__ */ jsxs2(
2594
+ ContextMenu.Item,
2595
+ {
2596
+ style: {
2597
+ display: "flex",
2598
+ alignItems: "center",
2599
+ gap: 10,
2600
+ padding: "8px 12px",
2601
+ border: "none",
2602
+ background: "none",
2603
+ cursor: "pointer",
2604
+ fontSize: 13,
2605
+ textAlign: "left",
2606
+ color: theme.text,
2607
+ outline: "none"
2608
+ },
2609
+ onClick: () => bulkCopy(actionEntries),
2610
+ children: [
2611
+ /* @__PURE__ */ jsx5(UiIcon, { icon: Copy, size: 16 }),
2612
+ " Copy"
2613
+ ]
2614
+ }
2615
+ ),
2616
+ isMultiSelected && view === "trash" && can.restore && /* @__PURE__ */ jsxs2(
2617
+ ContextMenu.Item,
2618
+ {
2619
+ style: {
2620
+ display: "flex",
2621
+ alignItems: "center",
2622
+ gap: 10,
2623
+ padding: "8px 12px",
2624
+ border: "none",
2625
+ background: "none",
2626
+ cursor: "pointer",
2627
+ fontSize: 13,
2628
+ textAlign: "left",
2629
+ color: theme.text,
2630
+ outline: "none"
2631
+ },
2632
+ onClick: () => restoreEntries(actionEntries),
2633
+ children: [
2634
+ /* @__PURE__ */ jsx5(UiIcon, { icon: ArrowCounterClockwise, size: 16 }),
2635
+ " Restore"
2636
+ ]
2637
+ }
2638
+ ),
2639
+ can.delete && /* @__PURE__ */ jsxs2(
2640
+ ContextMenu.Item,
2641
+ {
2642
+ style: {
2643
+ display: "flex",
2644
+ alignItems: "center",
2645
+ gap: 10,
2646
+ padding: "8px 12px",
2647
+ border: "none",
2648
+ background: "none",
2649
+ cursor: "pointer",
2650
+ fontSize: 13,
2651
+ textAlign: "left",
2652
+ color: theme.danger,
2653
+ outline: "none"
2654
+ },
2655
+ onClick: () => {
2656
+ setSelected(new Set(actionEntries.map((item) => item.path)));
2657
+ setLastSelected(null);
2658
+ setIsSelectionMode(true);
2659
+ setDeleteOpen(true);
2660
+ },
2661
+ children: [
2662
+ /* @__PURE__ */ jsx5(UiIcon, { icon: Trash, size: 16 }),
2663
+ " Delete"
2664
+ ]
2665
+ }
2666
+ )
2667
+ ]
2668
+ }
2669
+ ) }) })
2670
+ ]
2671
+ },
2672
+ entry.path
2673
+ );
2674
+ });
2675
+ })()
2676
+ }
2677
+ );
2678
+ })() })
2679
+ ) : (
2680
+ // List View
2681
+ (() => {
2682
+ const processEntries = (entries2) => {
2683
+ let filtered = entries2;
2684
+ filtered = [...filtered].sort((a, b) => {
2685
+ let comparison = 0;
2686
+ const aIsTrash = view === "files" && a.type === "folder" && a.path === `${TRASH_PATH}/`;
2687
+ const bIsTrash = view === "files" && b.type === "folder" && b.path === `${TRASH_PATH}/`;
2688
+ if (aIsTrash !== bIsTrash) return aIsTrash ? 1 : -1;
2689
+ if (a.type !== b.type) {
2690
+ return a.type === "folder" ? -1 : 1;
2691
+ }
2692
+ switch (sortBy) {
2693
+ case "name":
2694
+ comparison = a.name.localeCompare(b.name);
2695
+ break;
2696
+ case "date":
2697
+ const aDate = a.type === "file" && a.lastModified ? new Date(a.lastModified).getTime() : 0;
2698
+ const bDate = b.type === "file" && b.lastModified ? new Date(b.lastModified).getTime() : 0;
2699
+ comparison = aDate - bDate;
2700
+ break;
2701
+ case "size":
2702
+ comparison = (a.type === "file" ? a.size || 0 : 0) - (b.type === "file" ? b.size || 0 : 0);
2703
+ break;
2704
+ case "type":
2705
+ const aExt = a.name.split(".").pop()?.toLowerCase() || "";
2706
+ const bExt = b.name.split(".").pop()?.toLowerCase() || "";
2707
+ comparison = aExt.localeCompare(bExt);
2708
+ break;
2709
+ }
2710
+ return sortOrder === "asc" ? comparison : -comparison;
2711
+ });
2712
+ return filtered;
2713
+ };
2714
+ const filteredEntries = processEntries(currentEntries);
2715
+ const selectedEntries = filteredEntries.filter((item) => selected.has(item.path));
2716
+ return filteredEntries.map((entry, index) => {
2717
+ const isSelected = selected.has(entry.path);
2718
+ const isHovered = hoverRow === entry.path;
2719
+ const isTrashFolder = view === "files" && entry.type === "folder" && entry.path === `${TRASH_PATH}/`;
2720
+ const entryLabel = isTrashFolder ? "Trash" : entry.name;
2721
+ const isMultiSelected = selectedEntries.length > 1 && isSelected;
2722
+ const actionEntries = isMultiSelected ? selectedEntries : [entry];
2723
+ const hasContextMenuItems = !isMultiSelected && can.rename || actionEntries.some((item) => item.type === "file") || view === "files" && can.copy || isMultiSelected && view === "files" && can.move || isMultiSelected && view === "trash" && can.restore || can.delete;
2724
+ if (isTrashFolder) {
2725
+ return /* @__PURE__ */ jsxs2(
2726
+ "div",
2727
+ {
2728
+ "data-entry-path": entry.path,
2729
+ "data-entry-selectable": "false",
2730
+ className: FileManager_default.row,
2731
+ style: {
2732
+ gridTemplateColumns: isMobile ? "1fr 48px" : "1fr 120px 100px 48px",
2733
+ padding: isMobile ? "12px 16px" : "12px 24px",
2734
+ backgroundColor: isSelected ? theme.selected : isHovered ? theme.hover : theme.bg,
2735
+ outline: "none"
2736
+ },
2737
+ onClick: (e) => {
2738
+ if (e.button !== 0) return;
2739
+ handleEntryClickWithSelection(entry, index, filteredEntries, e);
2740
+ },
2741
+ onDoubleClick: () => openEntry(entry),
2742
+ onMouseEnter: () => setHoverRow(entry.path),
2743
+ onMouseLeave: () => setHoverRow(null),
2744
+ onTouchStart: () => {
2745
+ if (!isMobile) return;
2746
+ if (longPressTimerRef.current)
2747
+ window.clearTimeout(longPressTimerRef.current);
2748
+ longPressTimerRef.current = window.setTimeout(() => {
2749
+ handleLongPressSelect(entry);
2750
+ }, 350);
2751
+ },
2752
+ onTouchMove: () => {
2753
+ if (longPressTimerRef.current) {
2754
+ window.clearTimeout(longPressTimerRef.current);
2755
+ longPressTimerRef.current = null;
2756
+ }
2757
+ },
2758
+ onTouchEnd: () => {
2759
+ if (longPressTimerRef.current) {
2760
+ window.clearTimeout(longPressTimerRef.current);
2761
+ longPressTimerRef.current = null;
2762
+ }
2763
+ },
2764
+ children: [
2765
+ /* @__PURE__ */ jsxs2(
2766
+ "div",
2767
+ {
2768
+ style: {
2769
+ display: "flex",
2770
+ alignItems: isMobile ? "flex-start" : "center",
2771
+ gap: 12,
2772
+ fontWeight: 500,
2773
+ flexDirection: isMobile ? "column" : "row"
2774
+ },
2775
+ children: [
2776
+ /* @__PURE__ */ jsxs2(
2777
+ "div",
2778
+ {
2779
+ style: {
2780
+ display: "flex",
2781
+ alignItems: "center",
2782
+ gap: 12
2783
+ },
2784
+ children: [
2785
+ /* @__PURE__ */ jsx5(UiIcon, { icon: Trash, size: 20, weight: "fill", color: theme.text, boxed: true }),
2786
+ entryLabel
2787
+ ]
2788
+ }
2789
+ ),
2790
+ isMobile && /* @__PURE__ */ jsx5(
2791
+ "div",
2792
+ {
2793
+ style: {
2794
+ fontSize: 11,
2795
+ color: theme.textSecondary,
2796
+ fontWeight: 400
2797
+ },
2798
+ children: "Folder"
2799
+ }
2800
+ )
2801
+ ]
2802
+ }
2803
+ ),
2804
+ !isMobile && /* @__PURE__ */ jsx5("div", { style: { color: theme.textSecondary }, children: "--" }),
2805
+ !isMobile && /* @__PURE__ */ jsx5(
2806
+ "div",
2807
+ {
2808
+ style: {
2809
+ color: theme.textSecondary,
2810
+ fontFamily: "monospace"
2811
+ },
2812
+ children: "--"
2813
+ }
2814
+ ),
2815
+ /* @__PURE__ */ jsx5("div", {})
2816
+ ]
2817
+ },
2818
+ entry.path
2819
+ );
2820
+ }
2821
+ if (!hasContextMenuItems) {
2822
+ return /* @__PURE__ */ jsxs2(
2823
+ "div",
2824
+ {
2825
+ "data-entry-path": entry.path,
2826
+ "data-entry-selectable": isTrashFolder ? "false" : "true",
2827
+ className: FileManager_default.row,
2828
+ style: {
2829
+ gridTemplateColumns: isMobile ? "1fr 48px" : "1fr 120px 100px 48px",
2830
+ padding: isMobile ? "12px 16px" : "12px 24px",
2831
+ backgroundColor: isSelected ? theme.selected : isHovered ? theme.hover : theme.bg,
2832
+ outline: "none"
2833
+ },
2834
+ onClick: (e) => {
2835
+ if (e.button !== 0) return;
2836
+ handleEntryClickWithSelection(entry, index, filteredEntries, e);
2837
+ },
2838
+ onDoubleClick: () => openEntry(entry),
2839
+ onMouseEnter: () => setHoverRow(entry.path),
2840
+ onMouseLeave: () => setHoverRow(null),
2841
+ onTouchStart: () => {
2842
+ if (!isMobile) return;
2843
+ if (longPressTimerRef.current)
2844
+ window.clearTimeout(longPressTimerRef.current);
2845
+ longPressTimerRef.current = window.setTimeout(() => {
2846
+ handleLongPressSelect(entry);
2847
+ }, 350);
2848
+ },
2849
+ onTouchMove: () => {
2850
+ if (longPressTimerRef.current) {
2851
+ window.clearTimeout(longPressTimerRef.current);
2852
+ longPressTimerRef.current = null;
2853
+ }
2854
+ },
2855
+ onTouchEnd: () => {
2856
+ if (longPressTimerRef.current) {
2857
+ window.clearTimeout(longPressTimerRef.current);
2858
+ longPressTimerRef.current = null;
2859
+ }
2860
+ },
2861
+ children: [
2862
+ /* @__PURE__ */ jsxs2(
2863
+ "div",
2864
+ {
2865
+ style: {
2866
+ display: "flex",
2867
+ alignItems: isMobile ? "flex-start" : "center",
2868
+ gap: 12,
2869
+ fontWeight: 500,
2870
+ flexDirection: isMobile ? "column" : "row"
2871
+ },
2872
+ children: [
2873
+ /* @__PURE__ */ jsxs2(
2874
+ "div",
2875
+ {
2876
+ style: {
2877
+ display: "flex",
2878
+ alignItems: "center",
2879
+ gap: 12
2880
+ },
2881
+ children: [
2882
+ entry.type === "folder" ? isTrashFolder ? /* @__PURE__ */ jsx5(
2883
+ UiIcon,
2884
+ {
2885
+ icon: Trash,
2886
+ size: 20,
2887
+ weight: "fill",
2888
+ color: theme.text,
2889
+ boxed: true
2890
+ }
2891
+ ) : /* @__PURE__ */ jsx5(
2892
+ UiIcon,
2893
+ {
2894
+ icon: Folder,
2895
+ size: 20,
2896
+ weight: "fill",
2897
+ color: theme.text,
2898
+ boxed: true
2899
+ }
2900
+ ) : (() => {
2901
+ const previewUrl = inlinePreviews[entry.path];
2902
+ if (previewUrl && isImageFileName(entry.name)) {
2903
+ return /* @__PURE__ */ jsx5(
2904
+ "img",
2905
+ {
2906
+ src: previewUrl,
2907
+ alt: entry.name,
2908
+ loading: "lazy",
2909
+ decoding: "async",
2910
+ style: {
2911
+ width: 20,
2912
+ height: 20,
2913
+ objectFit: "cover",
2914
+ borderRadius: 4,
2915
+ border: `1px solid ${theme.border}`
2916
+ }
2917
+ }
2918
+ );
2919
+ }
2920
+ return getFileIcon(entry.name);
2921
+ })(),
2922
+ entryLabel
2923
+ ]
2924
+ }
2925
+ ),
2926
+ isMobile && /* @__PURE__ */ jsx5(
2927
+ "div",
2928
+ {
2929
+ style: {
2930
+ fontSize: 11,
2931
+ color: theme.textSecondary,
2932
+ fontWeight: 400
2933
+ },
2934
+ children: entry.type === "file" ? `${entry.lastModified ? formatDate(entry.lastModified) : "--"} | ${formatBytes(entry.size || 0)}` : "Folder"
2935
+ }
2936
+ )
2937
+ ]
2938
+ }
2939
+ ),
2940
+ !isMobile && /* @__PURE__ */ jsx5("div", { style: { color: theme.textSecondary }, children: entry.type === "file" && entry.lastModified ? formatDate(entry.lastModified) : "--" }),
2941
+ !isMobile && /* @__PURE__ */ jsx5(
2942
+ "div",
2943
+ {
2944
+ style: {
2945
+ color: theme.textSecondary,
2946
+ fontFamily: "monospace"
2947
+ },
2948
+ children: entry.type === "file" ? formatBytes(entry.size || 0) : "--"
2949
+ }
2950
+ ),
2951
+ /* @__PURE__ */ jsx5("div", {})
2952
+ ]
2953
+ },
2954
+ entry.path
2955
+ );
2956
+ }
2957
+ return /* @__PURE__ */ jsxs2(
2958
+ ContextMenu.Root,
2959
+ {
2960
+ onOpenChange: (open) => {
2961
+ if (open) {
2962
+ if (isTrashFolder) return;
2963
+ if (!selected.has(entry.path)) {
2964
+ setSelected(/* @__PURE__ */ new Set([entry.path]));
2965
+ selectionAnchorRef.current = entry.path;
2966
+ setLastSelected(null);
2967
+ setIsSelectionMode(true);
2968
+ }
2969
+ }
2970
+ },
2971
+ children: [
2972
+ /* @__PURE__ */ jsxs2(
2973
+ ContextMenu.Trigger,
2974
+ {
2975
+ "data-entry-path": entry.path,
2976
+ "data-entry-selectable": isTrashFolder ? "false" : "true",
2977
+ ...FileManager_default.row ? { className: FileManager_default.row } : {},
2978
+ style: {
2979
+ gridTemplateColumns: isMobile ? "1fr 48px" : "1fr 120px 100px 48px",
2980
+ padding: isMobile ? "12px 16px" : "12px 24px",
2981
+ backgroundColor: isSelected ? theme.selected : isHovered ? theme.hover : theme.bg,
2982
+ outline: "none"
2983
+ },
2984
+ onClick: (e) => {
2985
+ if (e.button !== 0) return;
2986
+ handleEntryClickWithSelection(entry, index, filteredEntries, e);
2987
+ },
2988
+ onDoubleClick: () => openEntry(entry),
2989
+ onContextMenu: (e) => {
2990
+ if (isTrashFolder) {
2991
+ e.preventDefault();
2992
+ e.stopPropagation();
2993
+ return;
2994
+ }
2995
+ e.stopPropagation();
2996
+ },
2997
+ onMouseEnter: () => setHoverRow(entry.path),
2998
+ onMouseLeave: () => setHoverRow(null),
2999
+ onTouchStart: () => {
3000
+ if (!isMobile) return;
3001
+ if (longPressTimerRef.current)
3002
+ window.clearTimeout(longPressTimerRef.current);
3003
+ longPressTimerRef.current = window.setTimeout(() => {
3004
+ handleLongPressSelect(entry);
3005
+ }, 350);
3006
+ },
3007
+ onTouchMove: () => {
3008
+ if (longPressTimerRef.current) {
3009
+ window.clearTimeout(longPressTimerRef.current);
3010
+ longPressTimerRef.current = null;
3011
+ }
3012
+ },
3013
+ onTouchEnd: () => {
3014
+ if (longPressTimerRef.current) {
3015
+ window.clearTimeout(longPressTimerRef.current);
3016
+ longPressTimerRef.current = null;
3017
+ }
3018
+ },
3019
+ children: [
3020
+ /* @__PURE__ */ jsxs2(
3021
+ "div",
3022
+ {
3023
+ style: {
3024
+ display: "flex",
3025
+ alignItems: isMobile ? "flex-start" : "center",
3026
+ gap: 12,
3027
+ fontWeight: 500,
3028
+ flexDirection: isMobile ? "column" : "row"
3029
+ },
3030
+ children: [
3031
+ /* @__PURE__ */ jsxs2(
3032
+ "div",
3033
+ {
3034
+ style: {
3035
+ display: "flex",
3036
+ alignItems: "center",
3037
+ gap: 12
3038
+ },
3039
+ children: [
3040
+ entry.type === "folder" ? isTrashFolder ? /* @__PURE__ */ jsx5(
3041
+ UiIcon,
3042
+ {
3043
+ icon: Trash,
3044
+ size: 20,
3045
+ weight: "fill",
3046
+ color: theme.text,
3047
+ boxed: true
3048
+ }
3049
+ ) : /* @__PURE__ */ jsx5(
3050
+ UiIcon,
3051
+ {
3052
+ icon: Folder,
3053
+ size: 20,
3054
+ weight: "fill",
3055
+ color: theme.text,
3056
+ boxed: true
3057
+ }
3058
+ ) : (() => {
3059
+ const previewUrl = inlinePreviews[entry.path];
3060
+ if (previewUrl && isImageFileName(entry.name)) {
3061
+ return /* @__PURE__ */ jsx5(
3062
+ "img",
3063
+ {
3064
+ src: previewUrl,
3065
+ alt: entry.name,
3066
+ loading: "lazy",
3067
+ decoding: "async",
3068
+ style: {
3069
+ width: 20,
3070
+ height: 20,
3071
+ objectFit: "cover",
3072
+ borderRadius: 4,
3073
+ border: `1px solid ${theme.border}`
3074
+ }
3075
+ }
3076
+ );
3077
+ }
3078
+ return getFileIcon(entry.name);
3079
+ })(),
3080
+ entryLabel
3081
+ ]
3082
+ }
3083
+ ),
3084
+ isMobile && /* @__PURE__ */ jsx5(
3085
+ "div",
3086
+ {
3087
+ style: {
3088
+ fontSize: 11,
3089
+ color: theme.textSecondary,
3090
+ fontWeight: 400
3091
+ },
3092
+ children: entry.type === "file" ? `${entry.lastModified ? formatDate(entry.lastModified) : "--"} | ${formatBytes(entry.size || 0)}` : "Folder"
3093
+ }
3094
+ )
3095
+ ]
3096
+ }
3097
+ ),
3098
+ !isMobile && /* @__PURE__ */ jsx5("div", { style: { color: theme.textSecondary }, children: entry.type === "file" && entry.lastModified ? formatDate(entry.lastModified) : "--" }),
3099
+ !isMobile && /* @__PURE__ */ jsx5(
3100
+ "div",
3101
+ {
3102
+ style: {
3103
+ color: theme.textSecondary,
3104
+ fontFamily: "monospace"
3105
+ },
3106
+ children: entry.type === "file" ? formatBytes(entry.size || 0) : "--"
3107
+ }
3108
+ ),
3109
+ /* @__PURE__ */ jsx5(
3110
+ "div",
3111
+ {
3112
+ onClick: (e) => {
3113
+ e.stopPropagation();
3114
+ e.preventDefault();
3115
+ },
3116
+ children: /* @__PURE__ */ jsxs2(Menu.Root, { children: [
3117
+ /* @__PURE__ */ jsx5(
3118
+ Menu.Trigger,
3119
+ {
3120
+ style: {
3121
+ background: "none",
3122
+ border: "none",
3123
+ cursor: "pointer",
3124
+ padding: 4,
3125
+ display: "flex",
3126
+ opacity: isMobile ? 1 : isHovered ? 1 : 0,
3127
+ transition: "opacity 0.2s",
3128
+ outline: "none"
3129
+ },
3130
+ children: /* @__PURE__ */ jsx5(UiIcon, { icon: DotsThree, size: 24, color: theme.textSecondary })
3131
+ }
3132
+ ),
3133
+ /* @__PURE__ */ jsx5(Menu.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx5(
3134
+ Menu.Positioner,
3135
+ {
3136
+ side: "bottom",
3137
+ align: "end",
3138
+ sideOffset: 5,
3139
+ style: { zIndex: 1e4 },
3140
+ children: /* @__PURE__ */ jsxs2(
3141
+ Menu.Popup,
3142
+ {
3143
+ style: {
3144
+ zIndex: 1e4,
3145
+ backgroundColor: theme.bg,
3146
+ border: `1px solid ${theme.border}`,
3147
+ boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
3148
+ padding: 4,
3149
+ display: "flex",
3150
+ flexDirection: "column",
3151
+ minWidth: 160,
3152
+ outline: "none"
3153
+ },
3154
+ children: [
3155
+ !isMultiSelected && can.rename && /* @__PURE__ */ jsxs2(
3156
+ Menu.Item,
3157
+ {
3158
+ style: {
3159
+ display: "flex",
3160
+ alignItems: "center",
3161
+ gap: 10,
3162
+ padding: "8px 12px",
3163
+ border: "none",
3164
+ background: "none",
3165
+ cursor: "pointer",
3166
+ fontSize: 13,
3167
+ textAlign: "left",
3168
+ color: theme.text,
3169
+ outline: "none"
3170
+ },
3171
+ onClick: () => {
3172
+ setRenameName(entry.name || "");
3173
+ setRenameTarget(entry);
3174
+ setRenameOpen(true);
3175
+ },
3176
+ children: [
3177
+ /* @__PURE__ */ jsx5(UiIcon, { icon: PencilSimple, size: 16 }),
3178
+ " Rename"
3179
+ ]
3180
+ }
3181
+ ),
3182
+ actionEntries.some((item) => item.type === "file") && /* @__PURE__ */ jsxs2(
3183
+ Menu.Item,
3184
+ {
3185
+ style: {
3186
+ display: "flex",
3187
+ alignItems: "center",
3188
+ gap: 10,
3189
+ padding: "8px 12px",
3190
+ border: "none",
3191
+ background: "none",
3192
+ cursor: "pointer",
3193
+ fontSize: 13,
3194
+ textAlign: "left",
3195
+ color: theme.text,
3196
+ outline: "none"
3197
+ },
3198
+ onClick: () => bulkDownload(actionEntries),
3199
+ children: [
3200
+ /* @__PURE__ */ jsx5(UiIcon, { icon: DownloadSimple, size: 16 }),
3201
+ " Download"
3202
+ ]
3203
+ }
3204
+ ),
3205
+ isMultiSelected && view === "files" && can.move && /* @__PURE__ */ jsxs2(
3206
+ Menu.Item,
3207
+ {
3208
+ style: {
3209
+ display: "flex",
3210
+ alignItems: "center",
3211
+ gap: 10,
3212
+ padding: "8px 12px",
3213
+ border: "none",
3214
+ background: "none",
3215
+ cursor: "pointer",
3216
+ fontSize: 13,
3217
+ textAlign: "left",
3218
+ color: theme.text,
3219
+ outline: "none"
3220
+ },
3221
+ onClick: () => bulkMove(actionEntries),
3222
+ children: [
3223
+ /* @__PURE__ */ jsx5(UiIcon, { icon: ArrowRight, size: 16 }),
3224
+ " Move"
3225
+ ]
3226
+ }
3227
+ ),
3228
+ view === "files" && can.copy && /* @__PURE__ */ jsxs2(
3229
+ Menu.Item,
3230
+ {
3231
+ style: {
3232
+ display: "flex",
3233
+ alignItems: "center",
3234
+ gap: 10,
3235
+ padding: "8px 12px",
3236
+ border: "none",
3237
+ background: "none",
3238
+ cursor: "pointer",
3239
+ fontSize: 13,
3240
+ textAlign: "left",
3241
+ color: theme.text,
3242
+ outline: "none"
3243
+ },
3244
+ onClick: () => bulkCopy(actionEntries),
3245
+ children: [
3246
+ /* @__PURE__ */ jsx5(UiIcon, { icon: Copy, size: 16 }),
3247
+ " Copy"
3248
+ ]
3249
+ }
3250
+ ),
3251
+ isMultiSelected && view === "trash" && can.restore && /* @__PURE__ */ jsxs2(
3252
+ Menu.Item,
3253
+ {
3254
+ style: {
3255
+ display: "flex",
3256
+ alignItems: "center",
3257
+ gap: 10,
3258
+ padding: "8px 12px",
3259
+ border: "none",
3260
+ background: "none",
3261
+ cursor: "pointer",
3262
+ fontSize: 13,
3263
+ textAlign: "left",
3264
+ color: theme.text,
3265
+ outline: "none"
3266
+ },
3267
+ onClick: () => restoreEntries(actionEntries),
3268
+ children: [
3269
+ /* @__PURE__ */ jsx5(UiIcon, { icon: ArrowCounterClockwise, size: 16 }),
3270
+ " Restore"
3271
+ ]
3272
+ }
3273
+ ),
3274
+ can.delete && /* @__PURE__ */ jsxs2(
3275
+ Menu.Item,
3276
+ {
3277
+ style: {
3278
+ display: "flex",
3279
+ alignItems: "center",
3280
+ gap: 10,
3281
+ padding: "8px 12px",
3282
+ border: "none",
3283
+ background: "none",
3284
+ cursor: "pointer",
3285
+ fontSize: 13,
3286
+ textAlign: "left",
3287
+ color: theme.danger,
3288
+ outline: "none"
3289
+ },
3290
+ onClick: () => {
3291
+ setSelected(new Set(actionEntries.map((item) => item.path)));
3292
+ setLastSelected(null);
3293
+ setIsSelectionMode(true);
3294
+ setDeleteOpen(true);
3295
+ },
3296
+ children: [
3297
+ /* @__PURE__ */ jsx5(UiIcon, { icon: Trash, size: 16 }),
3298
+ " Delete"
3299
+ ]
3300
+ }
3301
+ )
3302
+ ]
3303
+ }
3304
+ )
3305
+ }
3306
+ ) })
3307
+ ] })
3308
+ }
3309
+ )
3310
+ ]
3311
+ }
3312
+ ),
3313
+ /* @__PURE__ */ jsx5(ContextMenu.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx5(ContextMenu.Positioner, { style: { zIndex: 1e4 }, children: /* @__PURE__ */ jsxs2(
3314
+ ContextMenu.Popup,
3315
+ {
3316
+ style: {
3317
+ zIndex: 1e4,
3318
+ backgroundColor: theme.bg,
3319
+ border: `1px solid ${theme.border}`,
3320
+ boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
3321
+ padding: 4,
3322
+ display: "flex",
3323
+ flexDirection: "column",
3324
+ minWidth: 160,
3325
+ outline: "none"
3326
+ },
3327
+ children: [
3328
+ !isMultiSelected && can.rename && /* @__PURE__ */ jsxs2(
3329
+ ContextMenu.Item,
3330
+ {
3331
+ style: {
3332
+ display: "flex",
3333
+ alignItems: "center",
3334
+ gap: 10,
3335
+ padding: "8px 12px",
3336
+ border: "none",
3337
+ background: "none",
3338
+ cursor: "pointer",
3339
+ fontSize: 13,
3340
+ textAlign: "left",
3341
+ color: theme.text,
3342
+ outline: "none"
3343
+ },
3344
+ onClick: () => {
3345
+ setRenameName(entry.name || "");
3346
+ setRenameTarget(entry);
3347
+ setRenameOpen(true);
3348
+ },
3349
+ children: [
3350
+ /* @__PURE__ */ jsx5(UiIcon, { icon: PencilSimple, size: 16 }),
3351
+ " Rename"
3352
+ ]
3353
+ }
3354
+ ),
3355
+ actionEntries.some((item) => item.type === "file") && /* @__PURE__ */ jsxs2(
3356
+ ContextMenu.Item,
3357
+ {
3358
+ style: {
3359
+ display: "flex",
3360
+ alignItems: "center",
3361
+ gap: 10,
3362
+ padding: "8px 12px",
3363
+ border: "none",
3364
+ background: "none",
3365
+ cursor: "pointer",
3366
+ fontSize: 13,
3367
+ textAlign: "left",
3368
+ color: theme.text,
3369
+ outline: "none"
3370
+ },
3371
+ onClick: () => bulkDownload(actionEntries),
3372
+ children: [
3373
+ /* @__PURE__ */ jsx5(UiIcon, { icon: DownloadSimple, size: 16 }),
3374
+ " Download"
3375
+ ]
3376
+ }
3377
+ ),
3378
+ isMultiSelected && view === "files" && can.move && /* @__PURE__ */ jsxs2(
3379
+ ContextMenu.Item,
3380
+ {
3381
+ style: {
3382
+ display: "flex",
3383
+ alignItems: "center",
3384
+ gap: 10,
3385
+ padding: "8px 12px",
3386
+ border: "none",
3387
+ background: "none",
3388
+ cursor: "pointer",
3389
+ fontSize: 13,
3390
+ textAlign: "left",
3391
+ color: theme.text,
3392
+ outline: "none"
3393
+ },
3394
+ onClick: () => bulkMove(actionEntries),
3395
+ children: [
3396
+ /* @__PURE__ */ jsx5(UiIcon, { icon: ArrowRight, size: 16 }),
3397
+ " Move"
3398
+ ]
3399
+ }
3400
+ ),
3401
+ view === "files" && can.copy && /* @__PURE__ */ jsxs2(
3402
+ ContextMenu.Item,
3403
+ {
3404
+ style: {
3405
+ display: "flex",
3406
+ alignItems: "center",
3407
+ gap: 10,
3408
+ padding: "8px 12px",
3409
+ border: "none",
3410
+ background: "none",
3411
+ cursor: "pointer",
3412
+ fontSize: 13,
3413
+ textAlign: "left",
3414
+ color: theme.text,
3415
+ outline: "none"
3416
+ },
3417
+ onClick: () => bulkCopy(actionEntries),
3418
+ children: [
3419
+ /* @__PURE__ */ jsx5(UiIcon, { icon: Copy, size: 16 }),
3420
+ " Copy"
3421
+ ]
3422
+ }
3423
+ ),
3424
+ isMultiSelected && view === "trash" && can.restore && /* @__PURE__ */ jsxs2(
3425
+ ContextMenu.Item,
3426
+ {
3427
+ style: {
3428
+ display: "flex",
3429
+ alignItems: "center",
3430
+ gap: 10,
3431
+ padding: "8px 12px",
3432
+ border: "none",
3433
+ background: "none",
3434
+ cursor: "pointer",
3435
+ fontSize: 13,
3436
+ textAlign: "left",
3437
+ color: theme.text,
3438
+ outline: "none"
3439
+ },
3440
+ onClick: () => restoreEntries(actionEntries),
3441
+ children: [
3442
+ /* @__PURE__ */ jsx5(UiIcon, { icon: ArrowCounterClockwise, size: 16 }),
3443
+ " Restore"
3444
+ ]
3445
+ }
3446
+ ),
3447
+ can.delete && /* @__PURE__ */ jsxs2(
3448
+ ContextMenu.Item,
3449
+ {
3450
+ style: {
3451
+ display: "flex",
3452
+ alignItems: "center",
3453
+ gap: 10,
3454
+ padding: "8px 12px",
3455
+ border: "none",
3456
+ background: "none",
3457
+ cursor: "pointer",
3458
+ fontSize: 13,
3459
+ textAlign: "left",
3460
+ color: theme.danger,
3461
+ outline: "none"
3462
+ },
3463
+ onClick: () => {
3464
+ setSelected(new Set(actionEntries.map((item) => item.path)));
3465
+ setLastSelected(null);
3466
+ setIsSelectionMode(true);
3467
+ setDeleteOpen(true);
3468
+ },
3469
+ children: [
3470
+ /* @__PURE__ */ jsx5(UiIcon, { icon: Trash, size: 16 }),
3471
+ " Delete"
3472
+ ]
3473
+ }
3474
+ )
3475
+ ]
3476
+ }
3477
+ ) }) })
3478
+ ]
3479
+ },
3480
+ entry.path
3481
+ );
3482
+ });
3483
+ })()
3484
+ ),
3485
+ (() => {
3486
+ const isSearching = searchQuery.trim();
3487
+ const hasMoreItems = isSearching ? searchHasMore : hasMore;
3488
+ if (loadingMore) {
3489
+ return /* @__PURE__ */ jsx5(
3490
+ "div",
3491
+ {
3492
+ style: {
3493
+ padding: "20px 24px",
3494
+ textAlign: "center",
3495
+ borderTop: `1px solid ${theme.border}`,
3496
+ color: theme.textSecondary,
3497
+ fontSize: 13
3498
+ },
3499
+ children: "Loading more..."
3500
+ }
3501
+ );
3502
+ }
3503
+ if (hasMoreItems && currentEntries.length > 0) {
3504
+ return /* @__PURE__ */ jsx5(
3505
+ "div",
3506
+ {
3507
+ style: {
3508
+ padding: "20px 24px",
3509
+ textAlign: "center",
3510
+ borderTop: `1px solid ${theme.border}`
3511
+ },
3512
+ children: /* @__PURE__ */ jsx5(
3513
+ Button,
3514
+ {
3515
+ onClick: () => {
3516
+ if (isSearching) {
3517
+ performSearch(searchQuery, true);
3518
+ } else {
3519
+ refresh(true);
3520
+ }
3521
+ },
3522
+ disabled: loadingMore,
3523
+ style: {
3524
+ minWidth: 120,
3525
+ opacity: loadingMore ? 0.6 : 1
3526
+ },
3527
+ theme,
3528
+ children: "Load More"
3529
+ }
3530
+ )
3531
+ }
3532
+ );
3533
+ }
3534
+ return null;
3535
+ })(),
3536
+ (() => {
3537
+ if (currentEntries.length === 0 && !loading && !searching) {
3538
+ return /* @__PURE__ */ jsx5(
3539
+ "div",
3540
+ {
3541
+ style: {
3542
+ padding: 40,
3543
+ textAlign: "center",
3544
+ color: theme.textSecondary
3545
+ },
3546
+ children: searchQuery.trim() ? `No results found for "${searchQuery}"` : view === "trash" ? "Trash is empty" : /* @__PURE__ */ jsxs2("div", { children: [
3547
+ /* @__PURE__ */ jsx5("div", { style: { marginBottom: 16 }, children: "No files found" }),
3548
+ can.upload && view === "files" && /* @__PURE__ */ jsxs2(
3549
+ "div",
3550
+ {
3551
+ style: {
3552
+ fontSize: 12,
3553
+ opacity: 0.7,
3554
+ display: "flex",
3555
+ alignItems: "center",
3556
+ justifyContent: "center",
3557
+ gap: 8
3558
+ },
3559
+ children: [
3560
+ /* @__PURE__ */ jsx5(UiIcon, { icon: UploadSimple, size: 16 }),
3561
+ "Drag and drop files here or click Upload"
3562
+ ]
3563
+ }
3564
+ )
3565
+ ] })
3566
+ }
3567
+ );
3568
+ }
3569
+ return null;
3570
+ })()
3571
+ ]
3572
+ }
3573
+ )
3574
+ ]
3575
+ }
3576
+ ),
3577
+ showSidebarOnLayout && previewDisplay && /* @__PURE__ */ jsx5(
3578
+ Dialog2.Root,
3579
+ {
3580
+ open: !!previewDisplay,
3581
+ onOpenChange: (open) => {
3582
+ if (!open) {
3583
+ setSelected(/* @__PURE__ */ new Set());
3584
+ setLastSelected(null);
3585
+ }
3586
+ },
3587
+ children: /* @__PURE__ */ jsxs2(Dialog2.Portal, { container: previewPortalContainer, children: [
3588
+ /* @__PURE__ */ jsx5(
3589
+ Dialog2.Backdrop,
3590
+ {
3591
+ style: {
3592
+ position: "absolute",
3593
+ inset: 0,
3594
+ backgroundColor: "rgba(0,0,0,0.04)",
3595
+ zIndex: 9,
3596
+ pointerEvents: "auto"
3597
+ }
3598
+ }
3599
+ ),
3600
+ /* @__PURE__ */ jsxs2(
3601
+ Dialog2.Popup,
3602
+ {
3603
+ ...FileManager_default.sidebarRight ? { className: FileManager_default.sidebarRight } : {},
3604
+ style: {
3605
+ backgroundColor: theme.bg,
3606
+ borderLeft: isSidebarVisible ? `1px solid ${theme.border}` : "none",
3607
+ animation: isPreviewClosing ? "tokoPreviewSlideOut 200ms ease-in" : "tokoPreviewSlideIn 200ms ease-out",
3608
+ opacity: isSidebarVisible ? 1 : 0,
3609
+ pointerEvents: isPreviewClosing ? "none" : isSidebarVisible ? "auto" : "none",
3610
+ width: sidebarWidth,
3611
+ ...isSidebarVisible ? {
3612
+ boxShadow: "0 0 0 1px rgba(0,0,0,0.03), -8px 0 24px rgba(0,0,0,0.08)"
3613
+ } : {}
3614
+ },
3615
+ children: [
3616
+ isSidebarVisible && /* @__PURE__ */ jsx5(
3617
+ "div",
3618
+ {
3619
+ onMouseDown: (e) => {
3620
+ e.preventDefault();
3621
+ setIsResizing(true);
3622
+ updateSidebarWidth(e.clientX);
3623
+ },
3624
+ onTouchStart: (e) => {
3625
+ const touch = e.touches[0];
3626
+ if (!touch) return;
3627
+ setIsResizing(true);
3628
+ updateSidebarWidth(touch.clientX);
3629
+ },
3630
+ style: {
3631
+ position: "absolute",
3632
+ left: 0,
3633
+ top: 0,
3634
+ bottom: 0,
3635
+ width: 8,
3636
+ cursor: "col-resize",
3637
+ zIndex: 2,
3638
+ background: isResizing ? "rgba(0,0,0,0.06)" : "transparent"
3639
+ },
3640
+ "aria-hidden": true,
3641
+ children: /* @__PURE__ */ jsx5(
3642
+ "div",
3643
+ {
3644
+ style: {
3645
+ position: "absolute",
3646
+ left: 3,
3647
+ top: 0,
3648
+ bottom: 0,
3649
+ width: 2,
3650
+ backgroundColor: isResizing ? theme.textSecondary : theme.border,
3651
+ opacity: 0.4
3652
+ }
3653
+ }
3654
+ )
3655
+ }
3656
+ ),
3657
+ /* @__PURE__ */ jsx5(
3658
+ Dialog2.Close,
3659
+ {
3660
+ style: {
3661
+ position: "absolute",
3662
+ top: 8,
3663
+ right: 8,
3664
+ zIndex: 3,
3665
+ background: theme.bg,
3666
+ border: `1px solid ${theme.border}`,
3667
+ borderRadius: 4,
3668
+ padding: 4,
3669
+ cursor: "pointer",
3670
+ display: "flex",
3671
+ alignItems: "center",
3672
+ color: theme.textSecondary
3673
+ },
3674
+ "aria-label": "Close preview",
3675
+ title: "Close preview",
3676
+ children: /* @__PURE__ */ jsx5(UiIcon, { icon: X2, size: 16 })
3677
+ }
3678
+ ),
3679
+ /* @__PURE__ */ jsx5("div", { className: FileManager_default.previewBox, children: ["jpg", "png", "gif", "jpeg", "webp"].some(
3680
+ (ext) => previewDisplay.entry.path.toLowerCase().endsWith(ext)
3681
+ ) ? /* @__PURE__ */ jsx5(
3682
+ "img",
3683
+ {
3684
+ src: previewDisplay.url,
3685
+ alt: "preview",
3686
+ style: {
3687
+ maxWidth: "100%",
3688
+ maxHeight: "100%",
3689
+ objectFit: "contain"
3690
+ }
3691
+ }
3692
+ ) : /* @__PURE__ */ jsx5(UiIcon, { icon: FileText2, size: 64, weight: "thin", color: theme.textSecondary }) }),
3693
+ /* @__PURE__ */ jsxs2("div", { className: FileManager_default.metadata, style: { color: theme.text }, children: [
3694
+ /* @__PURE__ */ jsx5(
3695
+ "h3",
3696
+ {
3697
+ style: {
3698
+ margin: "0 0 20px",
3699
+ fontSize: 16,
3700
+ fontWeight: 600,
3701
+ color: theme.text
3702
+ },
3703
+ children: previewDisplay.entry.name
3704
+ }
3705
+ ),
3706
+ /* @__PURE__ */ jsxs2(
3707
+ "div",
3708
+ {
3709
+ style: {
3710
+ display: "grid",
3711
+ gridTemplateColumns: "1fr 1fr",
3712
+ gap: 20
3713
+ },
3714
+ children: [
3715
+ /* @__PURE__ */ jsxs2("div", { className: FileManager_default.metaItem, children: [
3716
+ /* @__PURE__ */ jsx5("div", { className: FileManager_default.metaLabel, children: "Type" }),
3717
+ /* @__PURE__ */ jsx5("div", { className: FileManager_default.metaValue, children: previewDisplay.entry.name.split(".").pop()?.toUpperCase() || "FILE" })
3718
+ ] }),
3719
+ /* @__PURE__ */ jsxs2("div", { className: FileManager_default.metaItem, children: [
3720
+ /* @__PURE__ */ jsx5("div", { className: FileManager_default.metaLabel, children: "Size" }),
3721
+ /* @__PURE__ */ jsx5("div", { className: FileManager_default.metaValue, children: previewDisplay.entry.type === "file" ? formatBytes(previewDisplay.entry.size || 0) : "0 B" })
3722
+ ] })
3723
+ ]
3724
+ }
3725
+ ),
3726
+ /* @__PURE__ */ jsxs2("div", { className: FileManager_default.metaItem, children: [
3727
+ /* @__PURE__ */ jsx5("div", { className: FileManager_default.metaLabel, children: "Location" }),
3728
+ /* @__PURE__ */ jsx5("div", { className: FileManager_default.metaValue, children: getParentPath(previewDisplay.entry.path) || "/" })
3729
+ ] }),
3730
+ /* @__PURE__ */ jsxs2("div", { className: FileManager_default.metaItem, children: [
3731
+ /* @__PURE__ */ jsx5("div", { className: FileManager_default.metaLabel, children: "Modified" }),
3732
+ /* @__PURE__ */ jsx5("div", { className: FileManager_default.metaValue, children: previewDisplay.entry.type === "file" && previewDisplay.entry.lastModified ? new Date(previewDisplay.entry.lastModified).toLocaleString() : "--" })
3733
+ ] }),
3734
+ /* @__PURE__ */ jsx5(
3735
+ "div",
3736
+ {
3737
+ style: {
3738
+ marginTop: 20,
3739
+ display: "flex",
3740
+ flexDirection: "column",
3741
+ gap: 8
3742
+ },
3743
+ children: view === "files" ? /* @__PURE__ */ jsxs2(Fragment, { children: [
3744
+ /* @__PURE__ */ jsxs2(
3745
+ Button,
3746
+ {
3747
+ style: { width: "100%", justifyContent: "center" },
3748
+ theme,
3749
+ onClick: async () => {
3750
+ try {
3751
+ const out = await client.getPreviewUrl({
3752
+ path: previewDisplay.entry.path,
3753
+ inline: false
3754
+ });
3755
+ const link = document.createElement("a");
3756
+ link.href = out.url;
3757
+ link.download = previewDisplay.entry.name;
3758
+ document.body.appendChild(link);
3759
+ link.click();
3760
+ document.body.removeChild(link);
3761
+ } catch (e) {
3762
+ console.error(e);
3763
+ }
3764
+ },
3765
+ children: [
3766
+ /* @__PURE__ */ jsx5(UiIcon, { icon: DownloadSimple, size: 16 }),
3767
+ " Download"
3768
+ ]
3769
+ }
3770
+ ),
3771
+ can.delete && /* @__PURE__ */ jsxs2(
3772
+ Button,
3773
+ {
3774
+ variant: "danger",
3775
+ onClick: () => {
3776
+ setSelected(/* @__PURE__ */ new Set([previewDisplay.entry.path]));
3777
+ setLastSelected(previewDisplay.entry);
3778
+ setDeleteOpen(true);
3779
+ },
3780
+ style: { width: "100%", justifyContent: "center" },
3781
+ theme,
3782
+ children: [
3783
+ /* @__PURE__ */ jsx5(UiIcon, { icon: Trash, size: 16 }),
3784
+ " Delete"
3785
+ ]
3786
+ }
3787
+ )
3788
+ ] }) : /* @__PURE__ */ jsxs2(
3789
+ Button,
3790
+ {
3791
+ onClick: onRestore,
3792
+ style: { width: "100%", justifyContent: "center" },
3793
+ theme,
3794
+ children: [
3795
+ /* @__PURE__ */ jsx5(UiIcon, { icon: ArrowCounterClockwise, size: 16 }),
3796
+ " Restore"
3797
+ ]
3798
+ }
3799
+ )
3800
+ }
3801
+ )
3802
+ ] })
3803
+ ]
3804
+ }
3805
+ )
3806
+ ] })
3807
+ }
3808
+ ),
3809
+ /* @__PURE__ */ jsxs2(
3810
+ Modal,
3811
+ {
3812
+ open: createFolderOpen,
3813
+ onClose: () => setCreateFolderOpen(false),
3814
+ title: "New Folder",
3815
+ theme,
3816
+ ...portalContainer ? { portalContainer } : {},
3817
+ children: [
3818
+ /* @__PURE__ */ jsx5(
3819
+ "input",
3820
+ {
3821
+ type: "text",
3822
+ placeholder: "Name",
3823
+ value: newFolderName,
3824
+ onChange: (e) => setNewFolderName(e.target.value),
3825
+ onKeyDown: (e) => e.key === "Enter" && onCreateFolder(),
3826
+ style: {
3827
+ width: "100%",
3828
+ padding: "10px",
3829
+ fontSize: 14,
3830
+ border: `1px solid ${theme.border}`,
3831
+ outline: "none",
3832
+ backgroundColor: theme.bg,
3833
+ color: theme.text
3834
+ },
3835
+ autoFocus: true
3836
+ }
3837
+ ),
3838
+ /* @__PURE__ */ jsxs2(
3839
+ "div",
3840
+ {
3841
+ style: {
3842
+ marginTop: 20,
3843
+ display: "flex",
3844
+ justifyContent: "flex-end",
3845
+ gap: 10
3846
+ },
3847
+ children: [
3848
+ /* @__PURE__ */ jsx5(Button, { onClick: () => setCreateFolderOpen(false), theme, children: "Cancel" }),
3849
+ /* @__PURE__ */ jsx5(Button, { variant: "primary", onClick: onCreateFolder, theme, children: "Create Folder" })
3850
+ ]
3851
+ }
3852
+ )
3853
+ ]
3854
+ }
3855
+ ),
3856
+ /* @__PURE__ */ jsxs2(
3857
+ Modal,
3858
+ {
3859
+ open: renameOpen,
3860
+ onClose: () => {
3861
+ if (isRenaming) return;
3862
+ setRenameOpen(false);
3863
+ setRenameTarget(null);
3864
+ },
3865
+ title: "Rename",
3866
+ theme,
3867
+ ...portalContainer ? { portalContainer } : {},
3868
+ closeDisabled: isRenaming,
3869
+ children: [
3870
+ /* @__PURE__ */ jsx5(
3871
+ "input",
3872
+ {
3873
+ type: "text",
3874
+ placeholder: "Name",
3875
+ value: renameName,
3876
+ onChange: (e) => setRenameName(e.target.value),
3877
+ onKeyDown: (e) => e.key === "Enter" && onRename(),
3878
+ style: {
3879
+ width: "100%",
3880
+ padding: "10px",
3881
+ fontSize: 14,
3882
+ border: `1px solid ${theme.border}`,
3883
+ outline: "none",
3884
+ backgroundColor: theme.bg,
3885
+ color: theme.text
3886
+ },
3887
+ disabled: isRenaming,
3888
+ autoFocus: true
3889
+ }
3890
+ ),
3891
+ /* @__PURE__ */ jsxs2(
3892
+ "div",
3893
+ {
3894
+ style: {
3895
+ marginTop: 20,
3896
+ display: "flex",
3897
+ justifyContent: "flex-end",
3898
+ gap: 10
3899
+ },
3900
+ children: [
3901
+ /* @__PURE__ */ jsx5(Button, { onClick: () => setRenameOpen(false), theme, disabled: isRenaming, children: "Cancel" }),
3902
+ /* @__PURE__ */ jsx5(Button, { variant: "primary", onClick: onRename, theme, disabled: isRenaming, children: isRenaming ? /* @__PURE__ */ jsxs2(Fragment, { children: [
3903
+ /* @__PURE__ */ jsx5("span", { className: FileManager_default.spinner }),
3904
+ "Renaming..."
3905
+ ] }) : "Rename" })
3906
+ ]
3907
+ }
3908
+ )
3909
+ ]
3910
+ }
3911
+ ),
3912
+ /* @__PURE__ */ jsxs2(
3913
+ Modal,
3914
+ {
3915
+ open: deleteOpen,
3916
+ onClose: () => setDeleteOpen(false),
3917
+ title: view === "trash" ? "Delete Forever" : "Delete items",
3918
+ theme,
3919
+ ...portalContainer ? { portalContainer } : {},
3920
+ closeDisabled: isDeleting,
3921
+ children: [
3922
+ /* @__PURE__ */ jsxs2("p", { style: { margin: "0 0 20px", color: theme.textSecondary }, children: [
3923
+ "Are you sure you want to ",
3924
+ view === "trash" ? "permanently" : "",
3925
+ " delete ",
3926
+ selected.size,
3927
+ " ",
3928
+ "item",
3929
+ selected.size > 1 ? "s" : "",
3930
+ "?",
3931
+ view === "files" && " (Items will be moved to Trash)"
3932
+ ] }),
3933
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "flex-end", gap: 10 }, children: [
3934
+ /* @__PURE__ */ jsx5(Button, { onClick: () => setDeleteOpen(false), theme, disabled: isDeleting, children: "Cancel" }),
3935
+ /* @__PURE__ */ jsx5(Button, { variant: "danger", onClick: onDelete, theme, disabled: isDeleting, children: isDeleting ? /* @__PURE__ */ jsxs2(Fragment, { children: [
3936
+ /* @__PURE__ */ jsx5("span", { className: FileManager_default.spinner }),
3937
+ "Deleting..."
3938
+ ] }) : "Delete" })
3939
+ ] })
3940
+ ]
3941
+ }
3942
+ ),
3943
+ /* @__PURE__ */ jsxs2(
3944
+ Modal,
3945
+ {
3946
+ open: emptyTrashOpen,
3947
+ onClose: () => setEmptyTrashOpen(false),
3948
+ title: "Empty Trash",
3949
+ theme,
3950
+ ...portalContainer ? { portalContainer } : {},
3951
+ closeDisabled: isEmptyingTrash,
3952
+ children: [
3953
+ /* @__PURE__ */ jsx5("p", { style: { margin: "0 0 20px", color: theme.textSecondary }, children: "Are you sure you want to empty the trash? All items will be permanently deleted." }),
3954
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "flex-end", gap: 10 }, children: [
3955
+ /* @__PURE__ */ jsx5(Button, { onClick: () => setEmptyTrashOpen(false), theme, disabled: isEmptyingTrash, children: "Cancel" }),
3956
+ /* @__PURE__ */ jsx5(Button, { variant: "danger", onClick: onEmptyTrash, theme, disabled: isEmptyingTrash, children: isEmptyingTrash ? /* @__PURE__ */ jsxs2(Fragment, { children: [
3957
+ /* @__PURE__ */ jsx5("span", { className: FileManager_default.spinner }),
3958
+ "Emptying..."
3959
+ ] }) : "Empty Trash" })
3960
+ ] })
3961
+ ]
3962
+ }
3963
+ ),
3964
+ isDragOver && view === "files" && /* @__PURE__ */ jsx5(
3965
+ "div",
3966
+ {
3967
+ style: {
3968
+ position: "absolute",
3969
+ top: 0,
3970
+ left: 0,
3971
+ right: 0,
3972
+ bottom: 0,
3973
+ backgroundColor: theme.bg === "#ffffff" ? "rgba(0, 0, 0, 0.05)" : "rgba(255, 255, 255, 0.05)",
3974
+ backdropFilter: "blur(2px)",
3975
+ zIndex: 1e3,
3976
+ display: "flex",
3977
+ alignItems: "center",
3978
+ justifyContent: "center",
3979
+ border: `2px dashed ${theme.accent}`,
3980
+ margin: 4,
3981
+ animation: "fadeIn 0.2s ease-in-out"
3982
+ },
3983
+ children: /* @__PURE__ */ jsxs2(
3984
+ "div",
3985
+ {
3986
+ style: {
3987
+ textAlign: "center",
3988
+ padding: 40,
3989
+ backgroundColor: theme.bg,
3990
+ border: `1px solid ${theme.border}`,
3991
+ color: theme.text,
3992
+ fontSize: 16,
3993
+ fontWeight: 500,
3994
+ transform: "scale(1.02)",
3995
+ transition: "transform 0.2s ease"
3996
+ },
3997
+ children: [
3998
+ /* @__PURE__ */ jsx5(UiIcon, { icon: UploadSimple, size: 48, boxStyle: { marginBottom: 16, opacity: 0.7 } }),
3999
+ /* @__PURE__ */ jsx5("div", { children: "Drop files here to upload" }),
4000
+ /* @__PURE__ */ jsxs2("div", { style: { fontSize: 13, color: theme.textSecondary, marginTop: 8 }, children: [
4001
+ "Files will be uploaded to ",
4002
+ path || "root directory"
4003
+ ] })
4004
+ ]
4005
+ }
4006
+ )
4007
+ }
4008
+ ),
4009
+ uploadCardOpen && (() => {
4010
+ const totalItems = uploadItems.length;
4011
+ const activeItems = uploadItems.filter((item) => item.status === "uploading");
4012
+ const completedItems = uploadItems.filter((item) => item.status === "done");
4013
+ const failedItems = uploadItems.filter((item) => item.status === "error");
4014
+ const totalBytes = uploadItems.reduce((acc, item) => acc + (item.total || 0), 0);
4015
+ const loadedBytes = uploadItems.reduce((acc, item) => acc + (item.loaded || 0), 0);
4016
+ const totalPct = totalBytes > 0 ? Math.round(loadedBytes / totalBytes * 100) : 0;
4017
+ const hasActiveUploads = activeItems.length > 0;
4018
+ return /* @__PURE__ */ jsxs2(
4019
+ "div",
4020
+ {
4021
+ style: {
4022
+ position: "absolute",
4023
+ right: 16,
4024
+ bottom: 16,
4025
+ width: 340,
4026
+ maxWidth: "calc(100% - 32px)",
4027
+ backgroundColor: theme.bg,
4028
+ border: `1px solid ${theme.border}`,
4029
+ boxShadow: "0 8px 24px rgba(0,0,0,0.12)",
4030
+ padding: 14,
4031
+ zIndex: 1200
4032
+ },
4033
+ children: [
4034
+ /* @__PURE__ */ jsxs2(
4035
+ "div",
4036
+ {
4037
+ style: {
4038
+ display: "flex",
4039
+ alignItems: "center",
4040
+ justifyContent: "space-between",
4041
+ marginBottom: 10
4042
+ },
4043
+ children: [
4044
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: 12, fontWeight: 600 }, children: hasActiveUploads ? `Uploading ${completedItems.length}/${totalItems}` : "Uploads complete" }),
4045
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4046
+ /* @__PURE__ */ jsxs2("div", { style: { fontSize: 12, color: theme.textSecondary }, children: [
4047
+ totalItems,
4048
+ " file",
4049
+ totalItems !== 1 ? "s" : "",
4050
+ " \u2022 ",
4051
+ totalPct,
4052
+ "%"
4053
+ ] }),
4054
+ /* @__PURE__ */ jsx5(
4055
+ "button",
4056
+ {
4057
+ type: "button",
4058
+ onClick: () => setUploadCardOpen(false),
4059
+ disabled: hasActiveUploads,
4060
+ style: {
4061
+ background: "none",
4062
+ border: "none",
4063
+ cursor: hasActiveUploads ? "not-allowed" : "pointer",
4064
+ padding: 2,
4065
+ display: "flex",
4066
+ alignItems: "center",
4067
+ color: theme.textSecondary,
4068
+ opacity: hasActiveUploads ? 0.5 : 1
4069
+ },
4070
+ title: hasActiveUploads ? "Uploads in progress" : "Close",
4071
+ children: /* @__PURE__ */ jsx5(UiIcon, { icon: X2, size: 14 })
4072
+ }
4073
+ )
4074
+ ] })
4075
+ ]
4076
+ }
4077
+ ),
4078
+ totalItems > 1 && /* @__PURE__ */ jsx5(
4079
+ "div",
4080
+ {
4081
+ style: {
4082
+ height: 6,
4083
+ backgroundColor: theme.bgSecondary,
4084
+ borderRadius: 999,
4085
+ overflow: "hidden",
4086
+ marginBottom: 10
4087
+ },
4088
+ children: /* @__PURE__ */ jsx5(
4089
+ "div",
4090
+ {
4091
+ style: {
4092
+ height: "100%",
4093
+ width: `${totalPct}%`,
4094
+ backgroundColor: theme.accent,
4095
+ transition: "width 120ms linear"
4096
+ }
4097
+ }
4098
+ )
4099
+ }
4100
+ ),
4101
+ /* @__PURE__ */ jsxs2(
4102
+ "div",
4103
+ {
4104
+ style: {
4105
+ display: "flex",
4106
+ flexDirection: "column",
4107
+ gap: 8,
4108
+ maxHeight: 220,
4109
+ overflow: "auto"
4110
+ },
4111
+ children: [
4112
+ uploadItems.map((item) => {
4113
+ const pct = item.total ? Math.round(item.loaded / item.total * 100) : 0;
4114
+ const statusColor = item.status === "error" ? theme.danger : item.status === "done" ? theme.text : theme.textSecondary;
4115
+ return /* @__PURE__ */ jsxs2(
4116
+ "div",
4117
+ {
4118
+ style: {
4119
+ display: "flex",
4120
+ flexDirection: "column",
4121
+ gap: 6
4122
+ },
4123
+ children: [
4124
+ /* @__PURE__ */ jsxs2(
4125
+ "div",
4126
+ {
4127
+ style: {
4128
+ display: "flex",
4129
+ alignItems: "baseline",
4130
+ gap: 10
4131
+ },
4132
+ children: [
4133
+ /* @__PURE__ */ jsx5(
4134
+ "div",
4135
+ {
4136
+ style: {
4137
+ flex: 1,
4138
+ fontSize: 12,
4139
+ color: theme.textSecondary,
4140
+ overflow: "hidden",
4141
+ textOverflow: "ellipsis",
4142
+ whiteSpace: "nowrap"
4143
+ },
4144
+ children: item.name
4145
+ }
4146
+ ),
4147
+ /* @__PURE__ */ jsxs2("div", { style: { fontSize: 11, color: theme.textSecondary }, children: [
4148
+ formatBytes(item.loaded),
4149
+ item.total ? ` / ${formatBytes(item.total)}` : ""
4150
+ ] }),
4151
+ /* @__PURE__ */ jsxs2(
4152
+ "div",
4153
+ {
4154
+ style: {
4155
+ display: "flex",
4156
+ alignItems: "center",
4157
+ gap: 8,
4158
+ justifyContent: "flex-end",
4159
+ minWidth: 90
4160
+ },
4161
+ children: [
4162
+ /* @__PURE__ */ jsx5(
4163
+ "div",
4164
+ {
4165
+ style: {
4166
+ fontSize: 12,
4167
+ color: statusColor,
4168
+ minWidth: 42,
4169
+ textAlign: "right"
4170
+ },
4171
+ children: item.status === "error" ? "Error" : `${pct}%`
4172
+ }
4173
+ ),
4174
+ item.status === "error" && item.file && /* @__PURE__ */ jsx5(
4175
+ "button",
4176
+ {
4177
+ type: "button",
4178
+ onClick: () => retryUpload(item),
4179
+ style: {
4180
+ background: theme.bg,
4181
+ border: `1px solid ${theme.border}`,
4182
+ color: theme.text,
4183
+ fontSize: 11,
4184
+ padding: "4px 8px",
4185
+ cursor: "pointer"
4186
+ },
4187
+ children: "Retry"
4188
+ }
4189
+ )
4190
+ ]
4191
+ }
4192
+ )
4193
+ ]
4194
+ }
4195
+ ),
4196
+ /* @__PURE__ */ jsx5(
4197
+ "div",
4198
+ {
4199
+ style: {
4200
+ width: "100%",
4201
+ height: 6,
4202
+ backgroundColor: theme.bgSecondary,
4203
+ borderRadius: 999,
4204
+ overflow: "hidden"
4205
+ },
4206
+ children: /* @__PURE__ */ jsx5(
4207
+ "div",
4208
+ {
4209
+ style: {
4210
+ height: "100%",
4211
+ width: `${item.status === "error" ? 100 : pct}%`,
4212
+ backgroundColor: item.status === "error" ? theme.danger : theme.accent,
4213
+ transition: "width 120ms linear"
4214
+ }
4215
+ }
4216
+ )
4217
+ }
4218
+ ),
4219
+ item.status === "error" && item.error && /* @__PURE__ */ jsx5("div", { style: { fontSize: 11, color: theme.danger }, children: item.error })
4220
+ ]
4221
+ },
4222
+ item.path
4223
+ );
4224
+ }),
4225
+ failedItems.length > 0 && /* @__PURE__ */ jsxs2("div", { style: { fontSize: 11, color: theme.danger }, children: [
4226
+ failedItems.length,
4227
+ " upload",
4228
+ failedItems.length !== 1 ? "s" : "",
4229
+ " failed"
4230
+ ] })
4231
+ ]
4232
+ }
4233
+ )
4234
+ ]
4235
+ }
4236
+ );
4237
+ })(),
4238
+ isDragOver && view === "trash" && /* @__PURE__ */ jsx5(
4239
+ "div",
4240
+ {
4241
+ style: {
4242
+ position: "absolute",
4243
+ top: 0,
4244
+ left: 0,
4245
+ right: 0,
4246
+ bottom: 0,
4247
+ backgroundColor: theme.bg === "#ffffff" ? "rgba(220, 38, 38, 0.1)" : "rgba(239, 68, 68, 0.1)",
4248
+ backdropFilter: "blur(2px)",
4249
+ zIndex: 1e3,
4250
+ display: "flex",
4251
+ alignItems: "center",
4252
+ justifyContent: "center",
4253
+ border: `2px dashed ${theme.danger}`,
4254
+ margin: 4
4255
+ },
4256
+ children: /* @__PURE__ */ jsxs2(
4257
+ "div",
4258
+ {
4259
+ style: {
4260
+ textAlign: "center",
4261
+ padding: 40,
4262
+ backgroundColor: theme.bg,
4263
+ border: `1px solid ${theme.border}`,
4264
+ color: theme.danger,
4265
+ fontSize: 16,
4266
+ fontWeight: 500
4267
+ },
4268
+ children: [
4269
+ /* @__PURE__ */ jsx5(UiIcon, { icon: Warning, size: 48, boxStyle: { marginBottom: 16, opacity: 0.7 } }),
4270
+ /* @__PURE__ */ jsx5("div", { children: "Cannot upload to trash" }),
4271
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: 13, color: theme.textSecondary, marginTop: 8 }, children: "Switch to files view to upload" })
4272
+ ]
4273
+ }
4274
+ )
4275
+ }
4276
+ ),
4277
+ /* @__PURE__ */ jsx5("div", { ref: previewPortalRef, className: FileManager_default.previewPortal })
4278
+ ]
4279
+ }
4280
+ );
4281
+ }
4282
+
4283
+ // src/react/FilePicker.tsx
4284
+ import { jsx as jsx6 } from "react/jsx-runtime";
4285
+ function FilePicker({
4286
+ selection = "single",
4287
+ allowActions,
4288
+ confirmLabel = "Select",
4289
+ ...props
4290
+ }) {
4291
+ return /* @__PURE__ */ jsx6(
4292
+ FileManager,
4293
+ {
4294
+ ...props,
4295
+ mode: "picker",
4296
+ selection,
4297
+ confirmLabel,
4298
+ allowActions: {
4299
+ upload: true,
4300
+ createFolder: true,
4301
+ delete: false,
4302
+ rename: false,
4303
+ move: false,
4304
+ copy: false,
4305
+ restore: false,
4306
+ ...allowActions
4307
+ }
4308
+ }
4309
+ );
4310
+ }
4311
+ export {
4312
+ FileManager,
4313
+ FilePicker
4314
+ };
4315
+ //# sourceMappingURL=index.js.map