hazo_files 1.0.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.
@@ -0,0 +1,3982 @@
1
+ // src/ui/components/FileBrowser.tsx
2
+ import { useState as useState6, useCallback as useCallback7, useEffect as useEffect4 } from "react";
3
+
4
+ // src/ui/components/PathBreadcrumb.tsx
5
+ import React from "react";
6
+
7
+ // src/common/path-utils.ts
8
+ function normalizePath(inputPath) {
9
+ if (!inputPath) return "/";
10
+ let normalized = inputPath.replace(/\\/g, "/");
11
+ normalized = normalized.replace(/\/+/g, "/");
12
+ const parts = normalized.split("/");
13
+ const result = [];
14
+ for (const part of parts) {
15
+ if (part === "." || part === "") continue;
16
+ if (part === "..") {
17
+ result.pop();
18
+ } else {
19
+ result.push(part);
20
+ }
21
+ }
22
+ normalized = "/" + result.join("/");
23
+ return normalized;
24
+ }
25
+ function getPathSegments(inputPath) {
26
+ const normalized = normalizePath(inputPath);
27
+ if (normalized === "/") return [];
28
+ return normalized.slice(1).split("/");
29
+ }
30
+ function getExtension(filename) {
31
+ const lastDot = filename.lastIndexOf(".");
32
+ if (lastDot === -1 || lastDot === 0) return "";
33
+ return filename.slice(lastDot);
34
+ }
35
+ function getNameWithoutExtension(filename) {
36
+ const lastDot = filename.lastIndexOf(".");
37
+ if (lastDot === -1 || lastDot === 0) return filename;
38
+ return filename.slice(0, lastDot);
39
+ }
40
+ function getBreadcrumbs(inputPath) {
41
+ const segments = getPathSegments(inputPath);
42
+ const breadcrumbs = [
43
+ { name: "Root", path: "/" }
44
+ ];
45
+ let currentPath = "";
46
+ for (const segment of segments) {
47
+ currentPath += "/" + segment;
48
+ breadcrumbs.push({
49
+ name: segment,
50
+ path: currentPath
51
+ });
52
+ }
53
+ return breadcrumbs;
54
+ }
55
+
56
+ // src/ui/icons/FileIcons.tsx
57
+ import { jsx, jsxs } from "react/jsx-runtime";
58
+ var defaultSize = 24;
59
+ function FolderIcon({ className, size = defaultSize }) {
60
+ return /* @__PURE__ */ jsx(
61
+ "svg",
62
+ {
63
+ xmlns: "http://www.w3.org/2000/svg",
64
+ width: size,
65
+ height: size,
66
+ viewBox: "0 0 24 24",
67
+ fill: "none",
68
+ stroke: "currentColor",
69
+ strokeWidth: "2",
70
+ strokeLinecap: "round",
71
+ strokeLinejoin: "round",
72
+ className,
73
+ children: /* @__PURE__ */ jsx("path", { d: "M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" })
74
+ }
75
+ );
76
+ }
77
+ function FolderOpenIcon({ className, size = defaultSize }) {
78
+ return /* @__PURE__ */ jsx(
79
+ "svg",
80
+ {
81
+ xmlns: "http://www.w3.org/2000/svg",
82
+ width: size,
83
+ height: size,
84
+ viewBox: "0 0 24 24",
85
+ fill: "none",
86
+ stroke: "currentColor",
87
+ strokeWidth: "2",
88
+ strokeLinecap: "round",
89
+ strokeLinejoin: "round",
90
+ className,
91
+ children: /* @__PURE__ */ jsx("path", { d: "m6 14 1.45-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.55 6a2 2 0 0 1-1.94 1.5H4a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2h3.93a2 2 0 0 1 1.66.9l.82 1.2a2 2 0 0 0 1.66.9H18a2 2 0 0 1 2 2v2" })
92
+ }
93
+ );
94
+ }
95
+ function FileIcon({ className, size = defaultSize }) {
96
+ return /* @__PURE__ */ jsxs(
97
+ "svg",
98
+ {
99
+ xmlns: "http://www.w3.org/2000/svg",
100
+ width: size,
101
+ height: size,
102
+ viewBox: "0 0 24 24",
103
+ fill: "none",
104
+ stroke: "currentColor",
105
+ strokeWidth: "2",
106
+ strokeLinecap: "round",
107
+ strokeLinejoin: "round",
108
+ className,
109
+ children: [
110
+ /* @__PURE__ */ jsx("path", { d: "M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z" }),
111
+ /* @__PURE__ */ jsx("polyline", { points: "14 2 14 8 20 8" })
112
+ ]
113
+ }
114
+ );
115
+ }
116
+ function FileTextIcon({ className, size = defaultSize }) {
117
+ return /* @__PURE__ */ jsxs(
118
+ "svg",
119
+ {
120
+ xmlns: "http://www.w3.org/2000/svg",
121
+ width: size,
122
+ height: size,
123
+ viewBox: "0 0 24 24",
124
+ fill: "none",
125
+ stroke: "currentColor",
126
+ strokeWidth: "2",
127
+ strokeLinecap: "round",
128
+ strokeLinejoin: "round",
129
+ className,
130
+ children: [
131
+ /* @__PURE__ */ jsx("path", { d: "M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z" }),
132
+ /* @__PURE__ */ jsx("polyline", { points: "14 2 14 8 20 8" }),
133
+ /* @__PURE__ */ jsx("line", { x1: "16", x2: "8", y1: "13", y2: "13" }),
134
+ /* @__PURE__ */ jsx("line", { x1: "16", x2: "8", y1: "17", y2: "17" }),
135
+ /* @__PURE__ */ jsx("line", { x1: "10", x2: "8", y1: "9", y2: "9" })
136
+ ]
137
+ }
138
+ );
139
+ }
140
+ function ImageIcon({ className, size = defaultSize }) {
141
+ return /* @__PURE__ */ jsxs(
142
+ "svg",
143
+ {
144
+ xmlns: "http://www.w3.org/2000/svg",
145
+ width: size,
146
+ height: size,
147
+ viewBox: "0 0 24 24",
148
+ fill: "none",
149
+ stroke: "currentColor",
150
+ strokeWidth: "2",
151
+ strokeLinecap: "round",
152
+ strokeLinejoin: "round",
153
+ className,
154
+ children: [
155
+ /* @__PURE__ */ jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
156
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "2" }),
157
+ /* @__PURE__ */ jsx("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
158
+ ]
159
+ }
160
+ );
161
+ }
162
+ function VideoIcon({ className, size = defaultSize }) {
163
+ return /* @__PURE__ */ jsxs(
164
+ "svg",
165
+ {
166
+ xmlns: "http://www.w3.org/2000/svg",
167
+ width: size,
168
+ height: size,
169
+ viewBox: "0 0 24 24",
170
+ fill: "none",
171
+ stroke: "currentColor",
172
+ strokeWidth: "2",
173
+ strokeLinecap: "round",
174
+ strokeLinejoin: "round",
175
+ className,
176
+ children: [
177
+ /* @__PURE__ */ jsx("path", { d: "m22 8-6 4 6 4V8Z" }),
178
+ /* @__PURE__ */ jsx("rect", { width: "14", height: "12", x: "2", y: "6", rx: "2", ry: "2" })
179
+ ]
180
+ }
181
+ );
182
+ }
183
+ function AudioIcon({ className, size = defaultSize }) {
184
+ return /* @__PURE__ */ jsxs(
185
+ "svg",
186
+ {
187
+ xmlns: "http://www.w3.org/2000/svg",
188
+ width: size,
189
+ height: size,
190
+ viewBox: "0 0 24 24",
191
+ fill: "none",
192
+ stroke: "currentColor",
193
+ strokeWidth: "2",
194
+ strokeLinecap: "round",
195
+ strokeLinejoin: "round",
196
+ className,
197
+ children: [
198
+ /* @__PURE__ */ jsx("path", { d: "M9 18V5l12-2v13" }),
199
+ /* @__PURE__ */ jsx("circle", { cx: "6", cy: "18", r: "3" }),
200
+ /* @__PURE__ */ jsx("circle", { cx: "18", cy: "16", r: "3" })
201
+ ]
202
+ }
203
+ );
204
+ }
205
+ function ArchiveIcon({ className, size = defaultSize }) {
206
+ return /* @__PURE__ */ jsxs(
207
+ "svg",
208
+ {
209
+ xmlns: "http://www.w3.org/2000/svg",
210
+ width: size,
211
+ height: size,
212
+ viewBox: "0 0 24 24",
213
+ fill: "none",
214
+ stroke: "currentColor",
215
+ strokeWidth: "2",
216
+ strokeLinecap: "round",
217
+ strokeLinejoin: "round",
218
+ className,
219
+ children: [
220
+ /* @__PURE__ */ jsx("rect", { width: "20", height: "5", x: "2", y: "3", rx: "1" }),
221
+ /* @__PURE__ */ jsx("path", { d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8" }),
222
+ /* @__PURE__ */ jsx("path", { d: "M10 12h4" })
223
+ ]
224
+ }
225
+ );
226
+ }
227
+ function PdfIcon({ className, size = defaultSize }) {
228
+ return /* @__PURE__ */ jsxs(
229
+ "svg",
230
+ {
231
+ xmlns: "http://www.w3.org/2000/svg",
232
+ width: size,
233
+ height: size,
234
+ viewBox: "0 0 24 24",
235
+ fill: "none",
236
+ stroke: "currentColor",
237
+ strokeWidth: "2",
238
+ strokeLinecap: "round",
239
+ strokeLinejoin: "round",
240
+ className,
241
+ children: [
242
+ /* @__PURE__ */ jsx("path", { d: "M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z" }),
243
+ /* @__PURE__ */ jsx("polyline", { points: "14 2 14 8 20 8" }),
244
+ /* @__PURE__ */ jsx("path", { d: "M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1" }),
245
+ /* @__PURE__ */ jsx("path", { d: "M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1" })
246
+ ]
247
+ }
248
+ );
249
+ }
250
+ function CodeIcon({ className, size = defaultSize }) {
251
+ return /* @__PURE__ */ jsxs(
252
+ "svg",
253
+ {
254
+ xmlns: "http://www.w3.org/2000/svg",
255
+ width: size,
256
+ height: size,
257
+ viewBox: "0 0 24 24",
258
+ fill: "none",
259
+ stroke: "currentColor",
260
+ strokeWidth: "2",
261
+ strokeLinecap: "round",
262
+ strokeLinejoin: "round",
263
+ className,
264
+ children: [
265
+ /* @__PURE__ */ jsx("polyline", { points: "16 18 22 12 16 6" }),
266
+ /* @__PURE__ */ jsx("polyline", { points: "8 6 2 12 8 18" })
267
+ ]
268
+ }
269
+ );
270
+ }
271
+ function UploadIcon({ className, size = defaultSize }) {
272
+ return /* @__PURE__ */ jsxs(
273
+ "svg",
274
+ {
275
+ xmlns: "http://www.w3.org/2000/svg",
276
+ width: size,
277
+ height: size,
278
+ viewBox: "0 0 24 24",
279
+ fill: "none",
280
+ stroke: "currentColor",
281
+ strokeWidth: "2",
282
+ strokeLinecap: "round",
283
+ strokeLinejoin: "round",
284
+ className,
285
+ children: [
286
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
287
+ /* @__PURE__ */ jsx("polyline", { points: "17 8 12 3 7 8" }),
288
+ /* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "3", y2: "15" })
289
+ ]
290
+ }
291
+ );
292
+ }
293
+ function DownloadIcon({ className, size = defaultSize }) {
294
+ return /* @__PURE__ */ jsxs(
295
+ "svg",
296
+ {
297
+ xmlns: "http://www.w3.org/2000/svg",
298
+ width: size,
299
+ height: size,
300
+ viewBox: "0 0 24 24",
301
+ fill: "none",
302
+ stroke: "currentColor",
303
+ strokeWidth: "2",
304
+ strokeLinecap: "round",
305
+ strokeLinejoin: "round",
306
+ className,
307
+ children: [
308
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
309
+ /* @__PURE__ */ jsx("polyline", { points: "7 10 12 15 17 10" }),
310
+ /* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "15", y2: "3" })
311
+ ]
312
+ }
313
+ );
314
+ }
315
+ function TrashIcon({ className, size = defaultSize }) {
316
+ return /* @__PURE__ */ jsxs(
317
+ "svg",
318
+ {
319
+ xmlns: "http://www.w3.org/2000/svg",
320
+ width: size,
321
+ height: size,
322
+ viewBox: "0 0 24 24",
323
+ fill: "none",
324
+ stroke: "currentColor",
325
+ strokeWidth: "2",
326
+ strokeLinecap: "round",
327
+ strokeLinejoin: "round",
328
+ className,
329
+ children: [
330
+ /* @__PURE__ */ jsx("path", { d: "M3 6h18" }),
331
+ /* @__PURE__ */ jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
332
+ /* @__PURE__ */ jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
333
+ ]
334
+ }
335
+ );
336
+ }
337
+ function PencilIcon({ className, size = defaultSize }) {
338
+ return /* @__PURE__ */ jsxs(
339
+ "svg",
340
+ {
341
+ xmlns: "http://www.w3.org/2000/svg",
342
+ width: size,
343
+ height: size,
344
+ viewBox: "0 0 24 24",
345
+ fill: "none",
346
+ stroke: "currentColor",
347
+ strokeWidth: "2",
348
+ strokeLinecap: "round",
349
+ strokeLinejoin: "round",
350
+ className,
351
+ children: [
352
+ /* @__PURE__ */ jsx("path", { d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" }),
353
+ /* @__PURE__ */ jsx("path", { d: "m15 5 4 4" })
354
+ ]
355
+ }
356
+ );
357
+ }
358
+ function PlusIcon({ className, size = defaultSize }) {
359
+ return /* @__PURE__ */ jsxs(
360
+ "svg",
361
+ {
362
+ xmlns: "http://www.w3.org/2000/svg",
363
+ width: size,
364
+ height: size,
365
+ viewBox: "0 0 24 24",
366
+ fill: "none",
367
+ stroke: "currentColor",
368
+ strokeWidth: "2",
369
+ strokeLinecap: "round",
370
+ strokeLinejoin: "round",
371
+ className,
372
+ children: [
373
+ /* @__PURE__ */ jsx("path", { d: "M5 12h14" }),
374
+ /* @__PURE__ */ jsx("path", { d: "M12 5v14" })
375
+ ]
376
+ }
377
+ );
378
+ }
379
+ function RefreshIcon({ className, size = defaultSize }) {
380
+ return /* @__PURE__ */ jsxs(
381
+ "svg",
382
+ {
383
+ xmlns: "http://www.w3.org/2000/svg",
384
+ width: size,
385
+ height: size,
386
+ viewBox: "0 0 24 24",
387
+ fill: "none",
388
+ stroke: "currentColor",
389
+ strokeWidth: "2",
390
+ strokeLinecap: "round",
391
+ strokeLinejoin: "round",
392
+ className,
393
+ children: [
394
+ /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }),
395
+ /* @__PURE__ */ jsx("path", { d: "M21 3v5h-5" }),
396
+ /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }),
397
+ /* @__PURE__ */ jsx("path", { d: "M8 16H3v5" })
398
+ ]
399
+ }
400
+ );
401
+ }
402
+ function ChevronRightIcon({ className, size = defaultSize }) {
403
+ return /* @__PURE__ */ jsx(
404
+ "svg",
405
+ {
406
+ xmlns: "http://www.w3.org/2000/svg",
407
+ width: size,
408
+ height: size,
409
+ viewBox: "0 0 24 24",
410
+ fill: "none",
411
+ stroke: "currentColor",
412
+ strokeWidth: "2",
413
+ strokeLinecap: "round",
414
+ strokeLinejoin: "round",
415
+ className,
416
+ children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
417
+ }
418
+ );
419
+ }
420
+ function ChevronDownIcon({ className, size = defaultSize }) {
421
+ return /* @__PURE__ */ jsx(
422
+ "svg",
423
+ {
424
+ xmlns: "http://www.w3.org/2000/svg",
425
+ width: size,
426
+ height: size,
427
+ viewBox: "0 0 24 24",
428
+ fill: "none",
429
+ stroke: "currentColor",
430
+ strokeWidth: "2",
431
+ strokeLinecap: "round",
432
+ strokeLinejoin: "round",
433
+ className,
434
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
435
+ }
436
+ );
437
+ }
438
+ function HomeIcon({ className, size = defaultSize }) {
439
+ return /* @__PURE__ */ jsxs(
440
+ "svg",
441
+ {
442
+ xmlns: "http://www.w3.org/2000/svg",
443
+ width: size,
444
+ height: size,
445
+ viewBox: "0 0 24 24",
446
+ fill: "none",
447
+ stroke: "currentColor",
448
+ strokeWidth: "2",
449
+ strokeLinecap: "round",
450
+ strokeLinejoin: "round",
451
+ className,
452
+ children: [
453
+ /* @__PURE__ */ jsx("path", { d: "m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }),
454
+ /* @__PURE__ */ jsx("polyline", { points: "9 22 9 12 15 12 15 22" })
455
+ ]
456
+ }
457
+ );
458
+ }
459
+ function MoreVerticalIcon({ className, size = defaultSize }) {
460
+ return /* @__PURE__ */ jsxs(
461
+ "svg",
462
+ {
463
+ xmlns: "http://www.w3.org/2000/svg",
464
+ width: size,
465
+ height: size,
466
+ viewBox: "0 0 24 24",
467
+ fill: "none",
468
+ stroke: "currentColor",
469
+ strokeWidth: "2",
470
+ strokeLinecap: "round",
471
+ strokeLinejoin: "round",
472
+ className,
473
+ children: [
474
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1" }),
475
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1" }),
476
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1" })
477
+ ]
478
+ }
479
+ );
480
+ }
481
+ function XIcon({ className, size = defaultSize }) {
482
+ return /* @__PURE__ */ jsxs(
483
+ "svg",
484
+ {
485
+ xmlns: "http://www.w3.org/2000/svg",
486
+ width: size,
487
+ height: size,
488
+ viewBox: "0 0 24 24",
489
+ fill: "none",
490
+ stroke: "currentColor",
491
+ strokeWidth: "2",
492
+ strokeLinecap: "round",
493
+ strokeLinejoin: "round",
494
+ className,
495
+ children: [
496
+ /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
497
+ /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
498
+ ]
499
+ }
500
+ );
501
+ }
502
+ function LoaderIcon({ className, size = defaultSize }) {
503
+ return /* @__PURE__ */ jsx(
504
+ "svg",
505
+ {
506
+ xmlns: "http://www.w3.org/2000/svg",
507
+ width: size,
508
+ height: size,
509
+ viewBox: "0 0 24 24",
510
+ fill: "none",
511
+ stroke: "currentColor",
512
+ strokeWidth: "2",
513
+ strokeLinecap: "round",
514
+ strokeLinejoin: "round",
515
+ className: `animate-spin ${className || ""}`,
516
+ children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
517
+ }
518
+ );
519
+ }
520
+ function getFileIcon(mimeType, isDirectory) {
521
+ if (isDirectory) {
522
+ return FolderIcon;
523
+ }
524
+ if (mimeType.startsWith("image/")) {
525
+ return ImageIcon;
526
+ }
527
+ if (mimeType.startsWith("video/")) {
528
+ return VideoIcon;
529
+ }
530
+ if (mimeType.startsWith("audio/")) {
531
+ return AudioIcon;
532
+ }
533
+ if (mimeType === "application/pdf") {
534
+ return PdfIcon;
535
+ }
536
+ if (mimeType.startsWith("text/") || mimeType === "application/json" || mimeType === "application/javascript" || mimeType === "application/typescript") {
537
+ return FileTextIcon;
538
+ }
539
+ if (mimeType === "application/zip" || mimeType === "application/x-rar-compressed" || mimeType === "application/x-7z-compressed" || mimeType === "application/gzip") {
540
+ return ArchiveIcon;
541
+ }
542
+ return FileIcon;
543
+ }
544
+
545
+ // src/ui/components/PathBreadcrumb.tsx
546
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
547
+ function PathBreadcrumb({ currentPath, onNavigate, className = "" }) {
548
+ const breadcrumbs = getBreadcrumbs(currentPath);
549
+ return /* @__PURE__ */ jsx2("nav", { className: `flex items-center space-x-1 text-sm ${className}`, children: breadcrumbs.map((crumb, index) => /* @__PURE__ */ jsxs2(React.Fragment, { children: [
550
+ index > 0 && /* @__PURE__ */ jsx2(ChevronRightIcon, { size: 16, className: "text-gray-400 flex-shrink-0" }),
551
+ /* @__PURE__ */ jsxs2(
552
+ "button",
553
+ {
554
+ onClick: () => onNavigate(crumb.path),
555
+ className: `
556
+ flex items-center px-2 py-1 rounded hover:bg-gray-100 transition-colors
557
+ ${index === breadcrumbs.length - 1 ? "font-medium text-gray-900" : "text-gray-600"}
558
+ `,
559
+ children: [
560
+ index === 0 ? /* @__PURE__ */ jsx2(HomeIcon, { size: 16, className: "mr-1" }) : null,
561
+ /* @__PURE__ */ jsx2("span", { className: "truncate max-w-[150px]", children: crumb.name })
562
+ ]
563
+ }
564
+ )
565
+ ] }, crumb.path)) });
566
+ }
567
+
568
+ // src/ui/components/FolderTree.tsx
569
+ import { useCallback } from "react";
570
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
571
+ function TreeNodeItem({
572
+ node,
573
+ level,
574
+ currentPath,
575
+ onSelect,
576
+ onExpand,
577
+ onToggle
578
+ }) {
579
+ const isSelected = currentPath === node.path;
580
+ const hasChildren = node.children && node.children.length > 0;
581
+ const isExpanded = node.isExpanded;
582
+ const isLoading = node.isLoading;
583
+ const handleToggle = useCallback((e) => {
584
+ e.stopPropagation();
585
+ if (isExpanded) {
586
+ onToggle(node.path);
587
+ } else {
588
+ onExpand(node.path);
589
+ }
590
+ }, [isExpanded, node.path, onExpand, onToggle]);
591
+ const handleSelect = useCallback(() => {
592
+ onSelect(node.path);
593
+ }, [node.path, onSelect]);
594
+ return /* @__PURE__ */ jsxs3("div", { children: [
595
+ /* @__PURE__ */ jsxs3(
596
+ "div",
597
+ {
598
+ className: `
599
+ flex items-center py-1 px-2 cursor-pointer rounded
600
+ hover:bg-gray-100 transition-colors
601
+ ${isSelected ? "bg-blue-100 text-blue-700" : "text-gray-700"}
602
+ `,
603
+ style: { paddingLeft: `${level * 16 + 8}px` },
604
+ onClick: handleSelect,
605
+ onDoubleClick: handleToggle,
606
+ children: [
607
+ /* @__PURE__ */ jsx3(
608
+ "button",
609
+ {
610
+ onClick: handleToggle,
611
+ className: "p-0.5 mr-1 hover:bg-gray-200 rounded flex-shrink-0",
612
+ children: isLoading ? /* @__PURE__ */ jsx3(LoaderIcon, { size: 14 }) : hasChildren || !isExpanded ? isExpanded ? /* @__PURE__ */ jsx3(ChevronDownIcon, { size: 14 }) : /* @__PURE__ */ jsx3(ChevronRightIcon, { size: 14 }) : /* @__PURE__ */ jsx3("span", { className: "w-3.5" })
613
+ }
614
+ ),
615
+ isExpanded ? /* @__PURE__ */ jsx3(FolderOpenIcon, { size: 16, className: "mr-2 text-yellow-500 flex-shrink-0" }) : /* @__PURE__ */ jsx3(FolderIcon, { size: 16, className: "mr-2 text-yellow-500 flex-shrink-0" }),
616
+ /* @__PURE__ */ jsx3("span", { className: "truncate text-sm", children: node.name })
617
+ ]
618
+ }
619
+ ),
620
+ isExpanded && hasChildren && /* @__PURE__ */ jsx3("div", { children: node.children.map((child) => /* @__PURE__ */ jsx3(
621
+ TreeNodeItem,
622
+ {
623
+ node: child,
624
+ level: level + 1,
625
+ currentPath,
626
+ onSelect,
627
+ onExpand,
628
+ onToggle
629
+ },
630
+ child.id
631
+ )) })
632
+ ] });
633
+ }
634
+ function FolderTree({
635
+ tree,
636
+ currentPath,
637
+ onSelect,
638
+ onExpand,
639
+ onToggle,
640
+ className = ""
641
+ }) {
642
+ const rootNode = {
643
+ id: "root",
644
+ name: "Root",
645
+ path: "/",
646
+ children: tree,
647
+ isExpanded: true
648
+ };
649
+ return /* @__PURE__ */ jsx3("div", { className: `overflow-auto ${className}`, children: /* @__PURE__ */ jsx3(
650
+ TreeNodeItem,
651
+ {
652
+ node: rootNode,
653
+ level: 0,
654
+ currentPath,
655
+ onSelect,
656
+ onExpand,
657
+ onToggle
658
+ }
659
+ ) });
660
+ }
661
+
662
+ // src/ui/components/FileList.tsx
663
+ import { useCallback as useCallback2 } from "react";
664
+
665
+ // src/common/utils.ts
666
+ function formatBytes(bytes, decimals = 2) {
667
+ if (bytes === 0) return "0 Bytes";
668
+ const k = 1024;
669
+ const dm = decimals < 0 ? 0 : decimals;
670
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB"];
671
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
672
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
673
+ }
674
+
675
+ // src/ui/components/FileList.tsx
676
+ import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
677
+ function FileItemCard({
678
+ item,
679
+ isSelected,
680
+ onSelect,
681
+ onOpen,
682
+ onContextMenu,
683
+ viewMode
684
+ }) {
685
+ const Icon = getFileIcon(
686
+ item.isDirectory ? "folder" : item.mimeType,
687
+ item.isDirectory
688
+ );
689
+ const handleClick = useCallback2((e) => {
690
+ e.stopPropagation();
691
+ onSelect();
692
+ }, [onSelect]);
693
+ const handleDoubleClick = useCallback2((e) => {
694
+ e.stopPropagation();
695
+ onOpen();
696
+ }, [onOpen]);
697
+ const handleContextMenu = useCallback2((e) => {
698
+ e.preventDefault();
699
+ onSelect();
700
+ onContextMenu?.(e);
701
+ }, [onSelect, onContextMenu]);
702
+ if (viewMode === "grid") {
703
+ return /* @__PURE__ */ jsx4(
704
+ "div",
705
+ {
706
+ className: `
707
+ p-3 rounded-lg cursor-pointer transition-all
708
+ hover:bg-gray-100
709
+ ${isSelected ? "bg-blue-100 ring-2 ring-blue-500" : "bg-white"}
710
+ `,
711
+ onClick: handleClick,
712
+ onDoubleClick: handleDoubleClick,
713
+ onContextMenu: handleContextMenu,
714
+ children: /* @__PURE__ */ jsxs4("div", { className: "flex flex-col items-center text-center", children: [
715
+ /* @__PURE__ */ jsx4(
716
+ Icon,
717
+ {
718
+ size: 40,
719
+ className: item.isDirectory ? "text-yellow-500" : "text-gray-500"
720
+ }
721
+ ),
722
+ /* @__PURE__ */ jsx4("span", { className: "mt-2 text-sm truncate w-full", title: item.name, children: item.name }),
723
+ !item.isDirectory && /* @__PURE__ */ jsx4("span", { className: "text-xs text-gray-400 mt-1", children: formatBytes(item.size) })
724
+ ] })
725
+ }
726
+ );
727
+ }
728
+ return /* @__PURE__ */ jsxs4(
729
+ "div",
730
+ {
731
+ className: `
732
+ flex items-center p-2 rounded cursor-pointer transition-all
733
+ hover:bg-gray-100
734
+ ${isSelected ? "bg-blue-100" : ""}
735
+ `,
736
+ onClick: handleClick,
737
+ onDoubleClick: handleDoubleClick,
738
+ onContextMenu: handleContextMenu,
739
+ children: [
740
+ /* @__PURE__ */ jsx4(
741
+ Icon,
742
+ {
743
+ size: 20,
744
+ className: `flex-shrink-0 ${item.isDirectory ? "text-yellow-500" : "text-gray-500"}`
745
+ }
746
+ ),
747
+ /* @__PURE__ */ jsx4("span", { className: "ml-3 flex-1 truncate text-sm", title: item.name, children: item.name }),
748
+ !item.isDirectory && /* @__PURE__ */ jsxs4(Fragment, { children: [
749
+ /* @__PURE__ */ jsx4("span", { className: "text-xs text-gray-400 ml-4", children: formatBytes(item.size) }),
750
+ /* @__PURE__ */ jsx4("span", { className: "text-xs text-gray-400 ml-4 w-32 truncate", children: new Date(item.modifiedAt).toLocaleDateString() })
751
+ ] })
752
+ ]
753
+ }
754
+ );
755
+ }
756
+ function FileList({
757
+ files,
758
+ selectedItem,
759
+ isLoading,
760
+ onSelect,
761
+ onOpen,
762
+ onContextMenu,
763
+ viewMode = "grid",
764
+ className = ""
765
+ }) {
766
+ const handleBackgroundClick = useCallback2(() => {
767
+ onSelect(null);
768
+ }, [onSelect]);
769
+ if (isLoading) {
770
+ return /* @__PURE__ */ jsx4("div", { className: `flex items-center justify-center h-full ${className}`, children: /* @__PURE__ */ jsx4(LoaderIcon, { size: 32, className: "text-gray-400" }) });
771
+ }
772
+ if (files.length === 0) {
773
+ return /* @__PURE__ */ jsxs4(
774
+ "div",
775
+ {
776
+ className: `flex flex-col items-center justify-center h-full text-gray-400 ${className}`,
777
+ onClick: handleBackgroundClick,
778
+ children: [
779
+ /* @__PURE__ */ jsx4(
780
+ "svg",
781
+ {
782
+ className: "w-16 h-16 mb-4",
783
+ fill: "none",
784
+ stroke: "currentColor",
785
+ viewBox: "0 0 24 24",
786
+ children: /* @__PURE__ */ jsx4(
787
+ "path",
788
+ {
789
+ strokeLinecap: "round",
790
+ strokeLinejoin: "round",
791
+ strokeWidth: 1.5,
792
+ d: "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"
793
+ }
794
+ )
795
+ }
796
+ ),
797
+ /* @__PURE__ */ jsx4("p", { children: "This folder is empty" })
798
+ ]
799
+ }
800
+ );
801
+ }
802
+ if (viewMode === "grid") {
803
+ return /* @__PURE__ */ jsx4(
804
+ "div",
805
+ {
806
+ className: `grid grid-cols-[repeat(auto-fill,minmax(120px,1fr))] gap-2 p-2 ${className}`,
807
+ onClick: handleBackgroundClick,
808
+ children: files.map((file) => /* @__PURE__ */ jsx4(
809
+ FileItemCard,
810
+ {
811
+ item: file,
812
+ isSelected: selectedItem?.id === file.id,
813
+ onSelect: () => onSelect(file),
814
+ onOpen: () => onOpen(file),
815
+ onContextMenu: onContextMenu ? (e) => onContextMenu(file, e) : void 0,
816
+ viewMode: "grid"
817
+ },
818
+ file.id
819
+ ))
820
+ }
821
+ );
822
+ }
823
+ return /* @__PURE__ */ jsxs4(
824
+ "div",
825
+ {
826
+ className: `flex flex-col ${className}`,
827
+ onClick: handleBackgroundClick,
828
+ children: [
829
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center p-2 border-b text-xs text-gray-500 font-medium", children: [
830
+ /* @__PURE__ */ jsx4("span", { className: "flex-1 ml-8", children: "Name" }),
831
+ /* @__PURE__ */ jsx4("span", { className: "w-20 ml-4", children: "Size" }),
832
+ /* @__PURE__ */ jsx4("span", { className: "w-32 ml-4", children: "Modified" })
833
+ ] }),
834
+ /* @__PURE__ */ jsx4("div", { className: "flex-1 overflow-auto", children: files.map((file) => /* @__PURE__ */ jsx4(
835
+ FileItemCard,
836
+ {
837
+ item: file,
838
+ isSelected: selectedItem?.id === file.id,
839
+ onSelect: () => onSelect(file),
840
+ onOpen: () => onOpen(file),
841
+ onContextMenu: onContextMenu ? (e) => onContextMenu(file, e) : void 0,
842
+ viewMode: "list"
843
+ },
844
+ file.id
845
+ )) })
846
+ ]
847
+ }
848
+ );
849
+ }
850
+
851
+ // src/ui/components/FilePreview.tsx
852
+ import { useEffect, useState } from "react";
853
+
854
+ // src/common/mime-types.ts
855
+ var MIME_TYPES = {
856
+ // Text
857
+ ".txt": "text/plain",
858
+ ".html": "text/html",
859
+ ".htm": "text/html",
860
+ ".css": "text/css",
861
+ ".csv": "text/csv",
862
+ ".xml": "text/xml",
863
+ ".json": "application/json",
864
+ ".js": "application/javascript",
865
+ ".ts": "application/typescript",
866
+ ".jsx": "text/jsx",
867
+ ".tsx": "text/tsx",
868
+ ".md": "text/markdown",
869
+ ".yaml": "text/yaml",
870
+ ".yml": "text/yaml",
871
+ // Images
872
+ ".png": "image/png",
873
+ ".jpg": "image/jpeg",
874
+ ".jpeg": "image/jpeg",
875
+ ".gif": "image/gif",
876
+ ".bmp": "image/bmp",
877
+ ".webp": "image/webp",
878
+ ".svg": "image/svg+xml",
879
+ ".ico": "image/x-icon",
880
+ // Documents
881
+ ".pdf": "application/pdf",
882
+ ".doc": "application/msword",
883
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
884
+ ".xls": "application/vnd.ms-excel",
885
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
886
+ ".ppt": "application/vnd.ms-powerpoint",
887
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
888
+ // Archives
889
+ ".zip": "application/zip",
890
+ ".rar": "application/x-rar-compressed",
891
+ ".7z": "application/x-7z-compressed",
892
+ ".tar": "application/x-tar",
893
+ ".gz": "application/gzip",
894
+ // Audio
895
+ ".mp3": "audio/mpeg",
896
+ ".wav": "audio/wav",
897
+ ".ogg": "audio/ogg",
898
+ ".m4a": "audio/mp4",
899
+ ".flac": "audio/flac",
900
+ // Video
901
+ ".mp4": "video/mp4",
902
+ ".webm": "video/webm",
903
+ ".avi": "video/x-msvideo",
904
+ ".mov": "video/quicktime",
905
+ ".wmv": "video/x-ms-wmv",
906
+ ".mkv": "video/x-matroska",
907
+ // Fonts
908
+ ".ttf": "font/ttf",
909
+ ".otf": "font/otf",
910
+ ".woff": "font/woff",
911
+ ".woff2": "font/woff2",
912
+ // Other
913
+ ".exe": "application/x-msdownload",
914
+ ".dmg": "application/x-apple-diskimage",
915
+ ".bin": "application/octet-stream"
916
+ };
917
+ var EXTENSION_BY_MIME = Object.entries(MIME_TYPES).reduce(
918
+ (acc, [ext, mime]) => {
919
+ if (!acc[mime]) {
920
+ acc[mime] = ext;
921
+ }
922
+ return acc;
923
+ },
924
+ {}
925
+ );
926
+ function getMimeType(filename) {
927
+ const ext = getExtension(filename).toLowerCase();
928
+ return MIME_TYPES[ext] || "application/octet-stream";
929
+ }
930
+ function isImage(filenameOrMime) {
931
+ const mime = filenameOrMime.includes("/") ? filenameOrMime : getMimeType(filenameOrMime);
932
+ return mime.startsWith("image/");
933
+ }
934
+ function isVideo(filenameOrMime) {
935
+ const mime = filenameOrMime.includes("/") ? filenameOrMime : getMimeType(filenameOrMime);
936
+ return mime.startsWith("video/");
937
+ }
938
+ function isAudio(filenameOrMime) {
939
+ const mime = filenameOrMime.includes("/") ? filenameOrMime : getMimeType(filenameOrMime);
940
+ return mime.startsWith("audio/");
941
+ }
942
+ function isText(filenameOrMime) {
943
+ const mime = filenameOrMime.includes("/") ? filenameOrMime : getMimeType(filenameOrMime);
944
+ return mime.startsWith("text/") || mime === "application/json" || mime === "application/javascript";
945
+ }
946
+ function isPreviewable(filenameOrMime) {
947
+ return isImage(filenameOrMime) || isText(filenameOrMime) || isVideo(filenameOrMime) || isAudio(filenameOrMime);
948
+ }
949
+
950
+ // src/ui/components/FilePreview.tsx
951
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
952
+ function FilePreview({
953
+ item,
954
+ getPreviewUrl,
955
+ getFileContent,
956
+ className = ""
957
+ }) {
958
+ const [previewUrl, setPreviewUrl] = useState(null);
959
+ const [textContent, setTextContent] = useState(null);
960
+ const [isLoading, setIsLoading] = useState(false);
961
+ const [error, setError] = useState(null);
962
+ useEffect(() => {
963
+ setPreviewUrl(null);
964
+ setTextContent(null);
965
+ setError(null);
966
+ if (!item || item.isDirectory) {
967
+ return;
968
+ }
969
+ const fileItem2 = item;
970
+ const mimeType2 = fileItem2.mimeType;
971
+ const loadPreview = async () => {
972
+ setIsLoading(true);
973
+ try {
974
+ if ((isImage(mimeType2) || isVideo(mimeType2) || isAudio(mimeType2)) && getPreviewUrl) {
975
+ const url = await getPreviewUrl(item.path);
976
+ setPreviewUrl(url);
977
+ } else if (isText(mimeType2) && getFileContent) {
978
+ const content = await getFileContent(item.path);
979
+ setTextContent(content);
980
+ }
981
+ } catch (err) {
982
+ setError(err.message);
983
+ } finally {
984
+ setIsLoading(false);
985
+ }
986
+ };
987
+ if (isPreviewable(mimeType2) && (getPreviewUrl || getFileContent)) {
988
+ loadPreview();
989
+ }
990
+ }, [item, getPreviewUrl, getFileContent]);
991
+ if (!item) {
992
+ return /* @__PURE__ */ jsx5("div", { className: `flex items-center justify-center h-full text-gray-400 ${className}`, children: /* @__PURE__ */ jsx5("p", { children: "Select a file to preview" }) });
993
+ }
994
+ if (item.isDirectory) {
995
+ const Icon2 = getFileIcon("folder", true);
996
+ return /* @__PURE__ */ jsxs5("div", { className: `flex flex-col items-center justify-center h-full p-4 ${className}`, children: [
997
+ /* @__PURE__ */ jsx5(Icon2, { size: 64, className: "text-yellow-500 mb-4" }),
998
+ /* @__PURE__ */ jsx5("h3", { className: "text-lg font-medium", children: item.name }),
999
+ /* @__PURE__ */ jsx5("p", { className: "text-sm text-gray-500 mt-2", children: "Folder" }),
1000
+ /* @__PURE__ */ jsxs5("div", { className: "mt-4 text-xs text-gray-400 space-y-1", children: [
1001
+ /* @__PURE__ */ jsxs5("p", { children: [
1002
+ "Created: ",
1003
+ new Date(item.createdAt).toLocaleString()
1004
+ ] }),
1005
+ /* @__PURE__ */ jsxs5("p", { children: [
1006
+ "Modified: ",
1007
+ new Date(item.modifiedAt).toLocaleString()
1008
+ ] })
1009
+ ] })
1010
+ ] });
1011
+ }
1012
+ const fileItem = item;
1013
+ const mimeType = fileItem.mimeType;
1014
+ const Icon = getFileIcon(mimeType, false);
1015
+ if (isLoading) {
1016
+ return /* @__PURE__ */ jsx5("div", { className: `flex items-center justify-center h-full ${className}`, children: /* @__PURE__ */ jsx5(LoaderIcon, { size: 32, className: "text-gray-400" }) });
1017
+ }
1018
+ if (error) {
1019
+ return /* @__PURE__ */ jsxs5("div", { className: `flex flex-col items-center justify-center h-full p-4 text-red-500 ${className}`, children: [
1020
+ /* @__PURE__ */ jsx5("p", { children: "Error loading preview" }),
1021
+ /* @__PURE__ */ jsx5("p", { className: "text-sm mt-2", children: error })
1022
+ ] });
1023
+ }
1024
+ return /* @__PURE__ */ jsxs5("div", { className: `flex flex-col h-full ${className}`, children: [
1025
+ /* @__PURE__ */ jsxs5("div", { className: "flex-1 overflow-auto p-4 flex items-center justify-center bg-gray-50", children: [
1026
+ isImage(mimeType) && previewUrl && /* @__PURE__ */ jsx5(
1027
+ "img",
1028
+ {
1029
+ src: previewUrl,
1030
+ alt: item.name,
1031
+ className: "max-w-full max-h-full object-contain"
1032
+ }
1033
+ ),
1034
+ isVideo(mimeType) && previewUrl && /* @__PURE__ */ jsx5(
1035
+ "video",
1036
+ {
1037
+ src: previewUrl,
1038
+ controls: true,
1039
+ className: "max-w-full max-h-full",
1040
+ children: "Your browser does not support video playback."
1041
+ }
1042
+ ),
1043
+ isAudio(mimeType) && previewUrl && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col items-center", children: [
1044
+ /* @__PURE__ */ jsx5(Icon, { size: 64, className: "text-gray-400 mb-4" }),
1045
+ /* @__PURE__ */ jsx5("audio", { src: previewUrl, controls: true, className: "w-full max-w-md", children: "Your browser does not support audio playback." })
1046
+ ] }),
1047
+ isText(mimeType) && textContent !== null && /* @__PURE__ */ jsx5("pre", { className: "w-full h-full overflow-auto text-sm bg-white p-4 rounded border font-mono whitespace-pre-wrap", children: textContent }),
1048
+ !previewUrl && textContent === null && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col items-center text-gray-400", children: [
1049
+ /* @__PURE__ */ jsx5(Icon, { size: 64, className: "mb-4" }),
1050
+ /* @__PURE__ */ jsx5("p", { children: "No preview available" })
1051
+ ] })
1052
+ ] }),
1053
+ /* @__PURE__ */ jsxs5("div", { className: "border-t bg-white p-4", children: [
1054
+ /* @__PURE__ */ jsx5("h3", { className: "font-medium truncate", title: item.name, children: item.name }),
1055
+ /* @__PURE__ */ jsxs5("div", { className: "mt-2 text-sm text-gray-500 grid grid-cols-2 gap-2", children: [
1056
+ /* @__PURE__ */ jsxs5("div", { children: [
1057
+ /* @__PURE__ */ jsx5("span", { className: "text-gray-400", children: "Size:" }),
1058
+ " ",
1059
+ formatBytes(fileItem.size)
1060
+ ] }),
1061
+ /* @__PURE__ */ jsxs5("div", { children: [
1062
+ /* @__PURE__ */ jsx5("span", { className: "text-gray-400", children: "Type:" }),
1063
+ " ",
1064
+ mimeType
1065
+ ] }),
1066
+ /* @__PURE__ */ jsxs5("div", { children: [
1067
+ /* @__PURE__ */ jsx5("span", { className: "text-gray-400", children: "Created:" }),
1068
+ " ",
1069
+ new Date(item.createdAt).toLocaleDateString()
1070
+ ] }),
1071
+ /* @__PURE__ */ jsxs5("div", { children: [
1072
+ /* @__PURE__ */ jsx5("span", { className: "text-gray-400", children: "Modified:" }),
1073
+ " ",
1074
+ new Date(item.modifiedAt).toLocaleDateString()
1075
+ ] })
1076
+ ] })
1077
+ ] })
1078
+ ] });
1079
+ }
1080
+
1081
+ // src/ui/components/FileActions.tsx
1082
+ import { useRef } from "react";
1083
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1084
+ function ActionButton({ icon, label, onClick, disabled, variant = "default" }) {
1085
+ return /* @__PURE__ */ jsxs6(
1086
+ "button",
1087
+ {
1088
+ onClick,
1089
+ disabled,
1090
+ title: label,
1091
+ className: `
1092
+ flex items-center gap-2 px-3 py-2 rounded text-sm transition-colors
1093
+ ${disabled ? "text-gray-300 cursor-not-allowed" : variant === "danger" ? "text-red-600 hover:bg-red-50" : "text-gray-700 hover:bg-gray-100"}
1094
+ `,
1095
+ children: [
1096
+ icon,
1097
+ /* @__PURE__ */ jsx6("span", { className: "hidden sm:inline", children: label })
1098
+ ]
1099
+ }
1100
+ );
1101
+ }
1102
+ function FileActions({
1103
+ selectedItem,
1104
+ onCreateFolder,
1105
+ onUpload,
1106
+ onDownload,
1107
+ onDelete,
1108
+ onRename,
1109
+ onRefresh,
1110
+ isLoading,
1111
+ className = ""
1112
+ }) {
1113
+ const fileInputRef = useRef(null);
1114
+ const handleUploadClick = () => {
1115
+ fileInputRef.current?.click();
1116
+ };
1117
+ const handleFileChange = (e) => {
1118
+ if (e.target.files && e.target.files.length > 0) {
1119
+ onUpload(e.target.files);
1120
+ e.target.value = "";
1121
+ }
1122
+ };
1123
+ const hasSelection = selectedItem !== null;
1124
+ const isFile = hasSelection && !selectedItem.isDirectory;
1125
+ return /* @__PURE__ */ jsxs6("div", { className: `flex items-center gap-1 ${className}`, children: [
1126
+ /* @__PURE__ */ jsx6(
1127
+ "input",
1128
+ {
1129
+ ref: fileInputRef,
1130
+ type: "file",
1131
+ multiple: true,
1132
+ onChange: handleFileChange,
1133
+ className: "hidden"
1134
+ }
1135
+ ),
1136
+ /* @__PURE__ */ jsx6(
1137
+ ActionButton,
1138
+ {
1139
+ icon: /* @__PURE__ */ jsx6(PlusIcon, { size: 18 }),
1140
+ label: "New Folder",
1141
+ onClick: onCreateFolder,
1142
+ disabled: isLoading
1143
+ }
1144
+ ),
1145
+ /* @__PURE__ */ jsx6(
1146
+ ActionButton,
1147
+ {
1148
+ icon: /* @__PURE__ */ jsx6(UploadIcon, { size: 18 }),
1149
+ label: "Upload",
1150
+ onClick: handleUploadClick,
1151
+ disabled: isLoading
1152
+ }
1153
+ ),
1154
+ /* @__PURE__ */ jsx6("div", { className: "w-px h-6 bg-gray-200 mx-1" }),
1155
+ /* @__PURE__ */ jsx6(
1156
+ ActionButton,
1157
+ {
1158
+ icon: /* @__PURE__ */ jsx6(DownloadIcon, { size: 18 }),
1159
+ label: "Download",
1160
+ onClick: onDownload,
1161
+ disabled: !isFile || isLoading
1162
+ }
1163
+ ),
1164
+ /* @__PURE__ */ jsx6(
1165
+ ActionButton,
1166
+ {
1167
+ icon: /* @__PURE__ */ jsx6(PencilIcon, { size: 18 }),
1168
+ label: "Rename",
1169
+ onClick: onRename,
1170
+ disabled: !hasSelection || isLoading
1171
+ }
1172
+ ),
1173
+ /* @__PURE__ */ jsx6(
1174
+ ActionButton,
1175
+ {
1176
+ icon: /* @__PURE__ */ jsx6(TrashIcon, { size: 18 }),
1177
+ label: "Delete",
1178
+ onClick: onDelete,
1179
+ disabled: !hasSelection || isLoading,
1180
+ variant: "danger"
1181
+ }
1182
+ ),
1183
+ /* @__PURE__ */ jsx6("div", { className: "w-px h-6 bg-gray-200 mx-1" }),
1184
+ /* @__PURE__ */ jsx6(
1185
+ ActionButton,
1186
+ {
1187
+ icon: /* @__PURE__ */ jsx6(RefreshIcon, { size: 18, className: isLoading ? "animate-spin" : "" }),
1188
+ label: "Refresh",
1189
+ onClick: onRefresh,
1190
+ disabled: isLoading
1191
+ }
1192
+ )
1193
+ ] });
1194
+ }
1195
+
1196
+ // src/ui/components/dialogs/CreateFolderDialog.tsx
1197
+ import { useState as useState2, useCallback as useCallback3, useEffect as useEffect2 } from "react";
1198
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1199
+ function CreateFolderDialog({
1200
+ isOpen,
1201
+ onClose,
1202
+ onSubmit,
1203
+ currentPath
1204
+ }) {
1205
+ const [name, setName] = useState2("");
1206
+ const [isLoading, setIsLoading] = useState2(false);
1207
+ const [error, setError] = useState2(null);
1208
+ useEffect2(() => {
1209
+ if (isOpen) {
1210
+ setName("");
1211
+ setError(null);
1212
+ }
1213
+ }, [isOpen]);
1214
+ const handleSubmit = useCallback3(async (e) => {
1215
+ e.preventDefault();
1216
+ if (!name.trim()) {
1217
+ setError("Please enter a folder name");
1218
+ return;
1219
+ }
1220
+ if (/[<>:"/\\|?*]/.test(name)) {
1221
+ setError("Folder name contains invalid characters");
1222
+ return;
1223
+ }
1224
+ setIsLoading(true);
1225
+ setError(null);
1226
+ try {
1227
+ await onSubmit(name.trim());
1228
+ onClose();
1229
+ } catch (err) {
1230
+ setError(err.message);
1231
+ } finally {
1232
+ setIsLoading(false);
1233
+ }
1234
+ }, [name, onSubmit, onClose]);
1235
+ if (!isOpen) return null;
1236
+ return /* @__PURE__ */ jsxs7("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
1237
+ /* @__PURE__ */ jsx7(
1238
+ "div",
1239
+ {
1240
+ className: "absolute inset-0 bg-black/50",
1241
+ onClick: onClose
1242
+ }
1243
+ ),
1244
+ /* @__PURE__ */ jsxs7("div", { className: "relative bg-white rounded-lg shadow-xl w-full max-w-md mx-4", children: [
1245
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between p-4 border-b", children: [
1246
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
1247
+ /* @__PURE__ */ jsx7(FolderIcon, { size: 20, className: "text-yellow-500" }),
1248
+ /* @__PURE__ */ jsx7("h2", { className: "text-lg font-medium", children: "Create New Folder" })
1249
+ ] }),
1250
+ /* @__PURE__ */ jsx7(
1251
+ "button",
1252
+ {
1253
+ onClick: onClose,
1254
+ className: "p-1 hover:bg-gray-100 rounded transition-colors",
1255
+ children: /* @__PURE__ */ jsx7(XIcon, { size: 20 })
1256
+ }
1257
+ )
1258
+ ] }),
1259
+ /* @__PURE__ */ jsxs7("form", { onSubmit: handleSubmit, children: [
1260
+ /* @__PURE__ */ jsxs7("div", { className: "p-4", children: [
1261
+ /* @__PURE__ */ jsxs7("div", { className: "text-sm text-gray-500 mb-4", children: [
1262
+ "Creating folder in: ",
1263
+ /* @__PURE__ */ jsx7("span", { className: "font-medium", children: currentPath })
1264
+ ] }),
1265
+ /* @__PURE__ */ jsxs7("label", { className: "block", children: [
1266
+ /* @__PURE__ */ jsx7("span", { className: "text-sm font-medium text-gray-700", children: "Folder Name" }),
1267
+ /* @__PURE__ */ jsx7(
1268
+ "input",
1269
+ {
1270
+ type: "text",
1271
+ value: name,
1272
+ onChange: (e) => setName(e.target.value),
1273
+ placeholder: "Enter folder name",
1274
+ className: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
1275
+ autoFocus: true,
1276
+ disabled: isLoading
1277
+ }
1278
+ )
1279
+ ] }),
1280
+ error && /* @__PURE__ */ jsx7("p", { className: "mt-2 text-sm text-red-600", children: error })
1281
+ ] }),
1282
+ /* @__PURE__ */ jsxs7("div", { className: "flex justify-end gap-2 p-4 border-t bg-gray-50 rounded-b-lg", children: [
1283
+ /* @__PURE__ */ jsx7(
1284
+ "button",
1285
+ {
1286
+ type: "button",
1287
+ onClick: onClose,
1288
+ className: "px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 rounded-md transition-colors",
1289
+ disabled: isLoading,
1290
+ children: "Cancel"
1291
+ }
1292
+ ),
1293
+ /* @__PURE__ */ jsxs7(
1294
+ "button",
1295
+ {
1296
+ type: "submit",
1297
+ className: "px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2",
1298
+ disabled: isLoading || !name.trim(),
1299
+ children: [
1300
+ isLoading && /* @__PURE__ */ jsx7(LoaderIcon, { size: 16 }),
1301
+ "Create Folder"
1302
+ ]
1303
+ }
1304
+ )
1305
+ ] })
1306
+ ] })
1307
+ ] })
1308
+ ] });
1309
+ }
1310
+
1311
+ // src/ui/components/dialogs/RenameDialog.tsx
1312
+ import { useState as useState3, useCallback as useCallback4, useEffect as useEffect3 } from "react";
1313
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1314
+ function RenameDialog({
1315
+ isOpen,
1316
+ item,
1317
+ onClose,
1318
+ onSubmit
1319
+ }) {
1320
+ const [name, setName] = useState3("");
1321
+ const [isLoading, setIsLoading] = useState3(false);
1322
+ const [error, setError] = useState3(null);
1323
+ useEffect3(() => {
1324
+ if (isOpen && item) {
1325
+ setName(item.name);
1326
+ setError(null);
1327
+ }
1328
+ }, [isOpen, item]);
1329
+ const handleSubmit = useCallback4(async (e) => {
1330
+ e.preventDefault();
1331
+ if (!name.trim()) {
1332
+ setError("Please enter a name");
1333
+ return;
1334
+ }
1335
+ if (/[<>:"/\\|?*]/.test(name)) {
1336
+ setError("Name contains invalid characters");
1337
+ return;
1338
+ }
1339
+ if (name.trim() === item?.name) {
1340
+ onClose();
1341
+ return;
1342
+ }
1343
+ setIsLoading(true);
1344
+ setError(null);
1345
+ try {
1346
+ await onSubmit(name.trim());
1347
+ onClose();
1348
+ } catch (err) {
1349
+ setError(err.message);
1350
+ } finally {
1351
+ setIsLoading(false);
1352
+ }
1353
+ }, [name, item, onSubmit, onClose]);
1354
+ if (!isOpen || !item) return null;
1355
+ return /* @__PURE__ */ jsxs8("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
1356
+ /* @__PURE__ */ jsx8(
1357
+ "div",
1358
+ {
1359
+ className: "absolute inset-0 bg-black/50",
1360
+ onClick: onClose
1361
+ }
1362
+ ),
1363
+ /* @__PURE__ */ jsxs8("div", { className: "relative bg-white rounded-lg shadow-xl w-full max-w-md mx-4", children: [
1364
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between p-4 border-b", children: [
1365
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
1366
+ /* @__PURE__ */ jsx8(PencilIcon, { size: 20, className: "text-gray-500" }),
1367
+ /* @__PURE__ */ jsxs8("h2", { className: "text-lg font-medium", children: [
1368
+ "Rename ",
1369
+ item.isDirectory ? "Folder" : "File"
1370
+ ] })
1371
+ ] }),
1372
+ /* @__PURE__ */ jsx8(
1373
+ "button",
1374
+ {
1375
+ onClick: onClose,
1376
+ className: "p-1 hover:bg-gray-100 rounded transition-colors",
1377
+ children: /* @__PURE__ */ jsx8(XIcon, { size: 20 })
1378
+ }
1379
+ )
1380
+ ] }),
1381
+ /* @__PURE__ */ jsxs8("form", { onSubmit: handleSubmit, children: [
1382
+ /* @__PURE__ */ jsxs8("div", { className: "p-4", children: [
1383
+ /* @__PURE__ */ jsxs8("label", { className: "block", children: [
1384
+ /* @__PURE__ */ jsx8("span", { className: "text-sm font-medium text-gray-700", children: "New Name" }),
1385
+ /* @__PURE__ */ jsx8(
1386
+ "input",
1387
+ {
1388
+ type: "text",
1389
+ value: name,
1390
+ onChange: (e) => setName(e.target.value),
1391
+ placeholder: "Enter new name",
1392
+ className: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
1393
+ autoFocus: true,
1394
+ disabled: isLoading
1395
+ }
1396
+ )
1397
+ ] }),
1398
+ error && /* @__PURE__ */ jsx8("p", { className: "mt-2 text-sm text-red-600", children: error })
1399
+ ] }),
1400
+ /* @__PURE__ */ jsxs8("div", { className: "flex justify-end gap-2 p-4 border-t bg-gray-50 rounded-b-lg", children: [
1401
+ /* @__PURE__ */ jsx8(
1402
+ "button",
1403
+ {
1404
+ type: "button",
1405
+ onClick: onClose,
1406
+ className: "px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 rounded-md transition-colors",
1407
+ disabled: isLoading,
1408
+ children: "Cancel"
1409
+ }
1410
+ ),
1411
+ /* @__PURE__ */ jsxs8(
1412
+ "button",
1413
+ {
1414
+ type: "submit",
1415
+ className: "px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2",
1416
+ disabled: isLoading || !name.trim(),
1417
+ children: [
1418
+ isLoading && /* @__PURE__ */ jsx8(LoaderIcon, { size: 16 }),
1419
+ "Rename"
1420
+ ]
1421
+ }
1422
+ )
1423
+ ] })
1424
+ ] })
1425
+ ] })
1426
+ ] });
1427
+ }
1428
+
1429
+ // src/ui/components/dialogs/DeleteConfirmDialog.tsx
1430
+ import { useState as useState4, useCallback as useCallback5 } from "react";
1431
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1432
+ function DeleteConfirmDialog({
1433
+ isOpen,
1434
+ item,
1435
+ onClose,
1436
+ onConfirm
1437
+ }) {
1438
+ const [isLoading, setIsLoading] = useState4(false);
1439
+ const [error, setError] = useState4(null);
1440
+ const handleConfirm = useCallback5(async () => {
1441
+ setIsLoading(true);
1442
+ setError(null);
1443
+ try {
1444
+ await onConfirm();
1445
+ onClose();
1446
+ } catch (err) {
1447
+ setError(err.message);
1448
+ } finally {
1449
+ setIsLoading(false);
1450
+ }
1451
+ }, [onConfirm, onClose]);
1452
+ if (!isOpen || !item) return null;
1453
+ return /* @__PURE__ */ jsxs9("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
1454
+ /* @__PURE__ */ jsx9(
1455
+ "div",
1456
+ {
1457
+ className: "absolute inset-0 bg-black/50",
1458
+ onClick: onClose
1459
+ }
1460
+ ),
1461
+ /* @__PURE__ */ jsxs9("div", { className: "relative bg-white rounded-lg shadow-xl w-full max-w-md mx-4", children: [
1462
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between p-4 border-b", children: [
1463
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
1464
+ /* @__PURE__ */ jsx9(TrashIcon, { size: 20, className: "text-red-500" }),
1465
+ /* @__PURE__ */ jsxs9("h2", { className: "text-lg font-medium", children: [
1466
+ "Delete ",
1467
+ item.isDirectory ? "Folder" : "File"
1468
+ ] })
1469
+ ] }),
1470
+ /* @__PURE__ */ jsx9(
1471
+ "button",
1472
+ {
1473
+ onClick: onClose,
1474
+ className: "p-1 hover:bg-gray-100 rounded transition-colors",
1475
+ children: /* @__PURE__ */ jsx9(XIcon, { size: 20 })
1476
+ }
1477
+ )
1478
+ ] }),
1479
+ /* @__PURE__ */ jsxs9("div", { className: "p-4", children: [
1480
+ /* @__PURE__ */ jsxs9("p", { className: "text-gray-700", children: [
1481
+ "Are you sure you want to delete",
1482
+ " ",
1483
+ /* @__PURE__ */ jsx9("span", { className: "font-medium", children: item.name }),
1484
+ "?"
1485
+ ] }),
1486
+ item.isDirectory && /* @__PURE__ */ jsx9("p", { className: "mt-2 text-sm text-amber-600 bg-amber-50 p-3 rounded", children: "Warning: This folder and all its contents will be permanently deleted." }),
1487
+ error && /* @__PURE__ */ jsx9("p", { className: "mt-2 text-sm text-red-600", children: error })
1488
+ ] }),
1489
+ /* @__PURE__ */ jsxs9("div", { className: "flex justify-end gap-2 p-4 border-t bg-gray-50 rounded-b-lg", children: [
1490
+ /* @__PURE__ */ jsx9(
1491
+ "button",
1492
+ {
1493
+ type: "button",
1494
+ onClick: onClose,
1495
+ className: "px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 rounded-md transition-colors",
1496
+ disabled: isLoading,
1497
+ children: "Cancel"
1498
+ }
1499
+ ),
1500
+ /* @__PURE__ */ jsxs9(
1501
+ "button",
1502
+ {
1503
+ type: "button",
1504
+ onClick: handleConfirm,
1505
+ className: "px-4 py-2 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2",
1506
+ disabled: isLoading,
1507
+ children: [
1508
+ isLoading && /* @__PURE__ */ jsx9(LoaderIcon, { size: 16 }),
1509
+ "Delete"
1510
+ ]
1511
+ }
1512
+ )
1513
+ ] })
1514
+ ] })
1515
+ ] });
1516
+ }
1517
+
1518
+ // src/ui/components/dialogs/UploadDialog.tsx
1519
+ import { useState as useState5, useCallback as useCallback6, useRef as useRef2 } from "react";
1520
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1521
+ function UploadDialog({
1522
+ isOpen,
1523
+ currentPath,
1524
+ onClose,
1525
+ onUpload
1526
+ }) {
1527
+ const [selectedFiles, setSelectedFiles] = useState5([]);
1528
+ const [isDragOver, setIsDragOver] = useState5(false);
1529
+ const [isLoading, setIsLoading] = useState5(false);
1530
+ const [error, setError] = useState5(null);
1531
+ const fileInputRef = useRef2(null);
1532
+ const handleFileSelect = useCallback6((files) => {
1533
+ if (files) {
1534
+ setSelectedFiles((prev) => [...prev, ...Array.from(files)]);
1535
+ setError(null);
1536
+ }
1537
+ }, []);
1538
+ const handleDragOver = useCallback6((e) => {
1539
+ e.preventDefault();
1540
+ setIsDragOver(true);
1541
+ }, []);
1542
+ const handleDragLeave = useCallback6((e) => {
1543
+ e.preventDefault();
1544
+ setIsDragOver(false);
1545
+ }, []);
1546
+ const handleDrop = useCallback6((e) => {
1547
+ e.preventDefault();
1548
+ setIsDragOver(false);
1549
+ handleFileSelect(e.dataTransfer.files);
1550
+ }, [handleFileSelect]);
1551
+ const handleRemoveFile = useCallback6((index) => {
1552
+ setSelectedFiles((prev) => prev.filter((_, i) => i !== index));
1553
+ }, []);
1554
+ const handleUpload = useCallback6(async () => {
1555
+ if (selectedFiles.length === 0) {
1556
+ setError("Please select files to upload");
1557
+ return;
1558
+ }
1559
+ setIsLoading(true);
1560
+ setError(null);
1561
+ try {
1562
+ const dt = new DataTransfer();
1563
+ selectedFiles.forEach((file) => dt.items.add(file));
1564
+ await onUpload(dt.files);
1565
+ setSelectedFiles([]);
1566
+ onClose();
1567
+ } catch (err) {
1568
+ setError(err.message);
1569
+ } finally {
1570
+ setIsLoading(false);
1571
+ }
1572
+ }, [selectedFiles, onUpload, onClose]);
1573
+ const handleClose = useCallback6(() => {
1574
+ setSelectedFiles([]);
1575
+ setError(null);
1576
+ onClose();
1577
+ }, [onClose]);
1578
+ if (!isOpen) return null;
1579
+ const totalSize = selectedFiles.reduce((sum, file) => sum + file.size, 0);
1580
+ return /* @__PURE__ */ jsxs10("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
1581
+ /* @__PURE__ */ jsx10(
1582
+ "div",
1583
+ {
1584
+ className: "absolute inset-0 bg-black/50",
1585
+ onClick: handleClose
1586
+ }
1587
+ ),
1588
+ /* @__PURE__ */ jsxs10("div", { className: "relative bg-white rounded-lg shadow-xl w-full max-w-lg mx-4", children: [
1589
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between p-4 border-b", children: [
1590
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
1591
+ /* @__PURE__ */ jsx10(UploadIcon, { size: 20, className: "text-blue-500" }),
1592
+ /* @__PURE__ */ jsx10("h2", { className: "text-lg font-medium", children: "Upload Files" })
1593
+ ] }),
1594
+ /* @__PURE__ */ jsx10(
1595
+ "button",
1596
+ {
1597
+ onClick: handleClose,
1598
+ className: "p-1 hover:bg-gray-100 rounded transition-colors",
1599
+ children: /* @__PURE__ */ jsx10(XIcon, { size: 20 })
1600
+ }
1601
+ )
1602
+ ] }),
1603
+ /* @__PURE__ */ jsxs10("div", { className: "p-4", children: [
1604
+ /* @__PURE__ */ jsxs10("div", { className: "text-sm text-gray-500 mb-4", children: [
1605
+ "Uploading to: ",
1606
+ /* @__PURE__ */ jsx10("span", { className: "font-medium", children: currentPath })
1607
+ ] }),
1608
+ /* @__PURE__ */ jsxs10(
1609
+ "div",
1610
+ {
1611
+ className: `
1612
+ border-2 border-dashed rounded-lg p-8 text-center transition-colors
1613
+ ${isDragOver ? "border-blue-500 bg-blue-50" : "border-gray-300"}
1614
+ `,
1615
+ onDragOver: handleDragOver,
1616
+ onDragLeave: handleDragLeave,
1617
+ onDrop: handleDrop,
1618
+ onClick: () => fileInputRef.current?.click(),
1619
+ children: [
1620
+ /* @__PURE__ */ jsx10(
1621
+ "input",
1622
+ {
1623
+ ref: fileInputRef,
1624
+ type: "file",
1625
+ multiple: true,
1626
+ onChange: (e) => handleFileSelect(e.target.files),
1627
+ className: "hidden"
1628
+ }
1629
+ ),
1630
+ /* @__PURE__ */ jsx10(UploadIcon, { size: 40, className: "mx-auto text-gray-400 mb-4" }),
1631
+ /* @__PURE__ */ jsxs10("p", { className: "text-gray-600", children: [
1632
+ "Drag and drop files here, or",
1633
+ " ",
1634
+ /* @__PURE__ */ jsx10("span", { className: "text-blue-500 cursor-pointer", children: "browse" })
1635
+ ] })
1636
+ ]
1637
+ }
1638
+ ),
1639
+ selectedFiles.length > 0 && /* @__PURE__ */ jsxs10("div", { className: "mt-4", children: [
1640
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between mb-2", children: [
1641
+ /* @__PURE__ */ jsxs10("span", { className: "text-sm font-medium text-gray-700", children: [
1642
+ selectedFiles.length,
1643
+ " file(s) selected"
1644
+ ] }),
1645
+ /* @__PURE__ */ jsxs10("span", { className: "text-sm text-gray-500", children: [
1646
+ "Total: ",
1647
+ formatBytes(totalSize)
1648
+ ] })
1649
+ ] }),
1650
+ /* @__PURE__ */ jsx10("div", { className: "max-h-40 overflow-auto space-y-2", children: selectedFiles.map((file, index) => /* @__PURE__ */ jsxs10(
1651
+ "div",
1652
+ {
1653
+ className: "flex items-center justify-between p-2 bg-gray-50 rounded",
1654
+ children: [
1655
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 min-w-0", children: [
1656
+ /* @__PURE__ */ jsx10(FileIcon, { size: 16, className: "text-gray-400 flex-shrink-0" }),
1657
+ /* @__PURE__ */ jsx10("span", { className: "text-sm truncate", children: file.name }),
1658
+ /* @__PURE__ */ jsx10("span", { className: "text-xs text-gray-400 flex-shrink-0", children: formatBytes(file.size) })
1659
+ ] }),
1660
+ /* @__PURE__ */ jsx10(
1661
+ "button",
1662
+ {
1663
+ onClick: () => handleRemoveFile(index),
1664
+ className: "p-1 hover:bg-gray-200 rounded",
1665
+ children: /* @__PURE__ */ jsx10(XIcon, { size: 14 })
1666
+ }
1667
+ )
1668
+ ]
1669
+ },
1670
+ index
1671
+ )) })
1672
+ ] }),
1673
+ error && /* @__PURE__ */ jsx10("p", { className: "mt-2 text-sm text-red-600", children: error })
1674
+ ] }),
1675
+ /* @__PURE__ */ jsxs10("div", { className: "flex justify-end gap-2 p-4 border-t bg-gray-50 rounded-b-lg", children: [
1676
+ /* @__PURE__ */ jsx10(
1677
+ "button",
1678
+ {
1679
+ type: "button",
1680
+ onClick: handleClose,
1681
+ className: "px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 rounded-md transition-colors",
1682
+ disabled: isLoading,
1683
+ children: "Cancel"
1684
+ }
1685
+ ),
1686
+ /* @__PURE__ */ jsxs10(
1687
+ "button",
1688
+ {
1689
+ type: "button",
1690
+ onClick: handleUpload,
1691
+ className: "px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2",
1692
+ disabled: isLoading || selectedFiles.length === 0,
1693
+ children: [
1694
+ isLoading && /* @__PURE__ */ jsx10(LoaderIcon, { size: 16 }),
1695
+ "Upload ",
1696
+ selectedFiles.length > 0 && `(${selectedFiles.length})`
1697
+ ]
1698
+ }
1699
+ )
1700
+ ] })
1701
+ ] })
1702
+ ] });
1703
+ }
1704
+
1705
+ // src/ui/components/FileBrowser.tsx
1706
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1707
+ function FileBrowser({
1708
+ api,
1709
+ initialPath = "/",
1710
+ showPreview = true,
1711
+ showTree = true,
1712
+ viewMode = "grid",
1713
+ className = "",
1714
+ treeWidth = 250,
1715
+ previewHeight = 300,
1716
+ onError,
1717
+ onNavigate,
1718
+ onSelect
1719
+ }) {
1720
+ const [currentPath, setCurrentPath] = useState6(initialPath);
1721
+ const [files, setFiles] = useState6([]);
1722
+ const [tree, setTree] = useState6([]);
1723
+ const [selectedItem, setSelectedItem] = useState6(null);
1724
+ const [isLoading, setIsLoading] = useState6(false);
1725
+ const [createFolderOpen, setCreateFolderOpen] = useState6(false);
1726
+ const [renameOpen, setRenameOpen] = useState6(false);
1727
+ const [deleteOpen, setDeleteOpen] = useState6(false);
1728
+ const [uploadOpen, setUploadOpen] = useState6(false);
1729
+ const loadDirectory = useCallback7(async (path) => {
1730
+ setIsLoading(true);
1731
+ try {
1732
+ const result = await api.listDirectory(path);
1733
+ if (result.success && result.data) {
1734
+ setFiles(result.data);
1735
+ setCurrentPath(path);
1736
+ setSelectedItem(null);
1737
+ onNavigate?.(path);
1738
+ } else {
1739
+ onError?.(result.error || "Failed to load directory");
1740
+ }
1741
+ } finally {
1742
+ setIsLoading(false);
1743
+ }
1744
+ }, [api, onError, onNavigate]);
1745
+ const loadTree = useCallback7(async () => {
1746
+ const result = await api.getFolderTree("/", 3);
1747
+ if (result.success && result.data) {
1748
+ setTree(result.data);
1749
+ }
1750
+ }, [api]);
1751
+ useEffect4(() => {
1752
+ loadDirectory(initialPath);
1753
+ if (showTree) {
1754
+ loadTree();
1755
+ }
1756
+ }, [initialPath, loadDirectory, loadTree, showTree]);
1757
+ const handleNavigate = useCallback7((path) => {
1758
+ loadDirectory(path);
1759
+ }, [loadDirectory]);
1760
+ const handleRefresh = useCallback7(async () => {
1761
+ await loadDirectory(currentPath);
1762
+ if (showTree) {
1763
+ await loadTree();
1764
+ }
1765
+ }, [currentPath, loadDirectory, loadTree, showTree]);
1766
+ const handleSelect = useCallback7((item) => {
1767
+ setSelectedItem(item);
1768
+ onSelect?.(item);
1769
+ }, [onSelect]);
1770
+ const handleOpen = useCallback7((item) => {
1771
+ if (item.isDirectory) {
1772
+ loadDirectory(item.path);
1773
+ } else {
1774
+ handleDownload();
1775
+ }
1776
+ }, [loadDirectory]);
1777
+ const handleTreeToggle = useCallback7((path) => {
1778
+ setTree((prev) => toggleTreeNode(prev, path));
1779
+ }, []);
1780
+ const handleTreeExpand = useCallback7(async (path) => {
1781
+ const result = await api.listDirectory(path);
1782
+ if (result.success && result.data) {
1783
+ const folders = result.data.filter((item) => item.isDirectory);
1784
+ const children = folders.map((folder) => ({
1785
+ id: folder.id,
1786
+ name: folder.name,
1787
+ path: folder.path,
1788
+ children: []
1789
+ }));
1790
+ setTree((prev) => updateTreeNode(prev, path, children, true));
1791
+ }
1792
+ }, [api]);
1793
+ const handleCreateFolder = useCallback7(async (name) => {
1794
+ const newPath = currentPath === "/" ? `/${name}` : `${currentPath}/${name}`;
1795
+ const result = await api.createDirectory(newPath);
1796
+ if (result.success && result.data) {
1797
+ setFiles((prev) => sortItems([...prev, result.data]));
1798
+ await loadTree();
1799
+ } else {
1800
+ throw new Error(result.error || "Failed to create folder");
1801
+ }
1802
+ }, [api, currentPath, loadTree]);
1803
+ const handleUpload = useCallback7(async (fileList) => {
1804
+ for (let i = 0; i < fileList.length; i++) {
1805
+ const file = fileList[i];
1806
+ const remotePath = currentPath === "/" ? `/${file.name}` : `${currentPath}/${file.name}`;
1807
+ const result = await api.uploadFile(file, remotePath);
1808
+ if (result.success && result.data) {
1809
+ setFiles((prev) => sortItems([...prev, result.data]));
1810
+ } else {
1811
+ onError?.(result.error || `Failed to upload ${file.name}`);
1812
+ }
1813
+ }
1814
+ }, [api, currentPath, onError]);
1815
+ const handleDownload = useCallback7(async () => {
1816
+ if (!selectedItem || selectedItem.isDirectory) return;
1817
+ const result = await api.downloadFile(selectedItem.path);
1818
+ if (result.success && result.data) {
1819
+ const blob = result.data;
1820
+ const url = URL.createObjectURL(blob);
1821
+ const a = document.createElement("a");
1822
+ a.href = url;
1823
+ a.download = selectedItem.name;
1824
+ document.body.appendChild(a);
1825
+ a.click();
1826
+ document.body.removeChild(a);
1827
+ URL.revokeObjectURL(url);
1828
+ } else {
1829
+ onError?.(result.error || "Failed to download file");
1830
+ }
1831
+ }, [api, selectedItem, onError]);
1832
+ const handleDelete = useCallback7(async () => {
1833
+ if (!selectedItem) return;
1834
+ let result;
1835
+ if (selectedItem.isDirectory) {
1836
+ result = await api.removeDirectory(selectedItem.path, true);
1837
+ } else {
1838
+ result = await api.deleteFile(selectedItem.path);
1839
+ }
1840
+ if (result.success) {
1841
+ setFiles((prev) => prev.filter((f) => f.path !== selectedItem.path));
1842
+ setSelectedItem(null);
1843
+ if (selectedItem.isDirectory) {
1844
+ await loadTree();
1845
+ }
1846
+ } else {
1847
+ throw new Error(result.error || "Failed to delete");
1848
+ }
1849
+ }, [api, selectedItem, loadTree]);
1850
+ const handleRename = useCallback7(async (newName) => {
1851
+ if (!selectedItem) return;
1852
+ let result;
1853
+ if (selectedItem.isDirectory) {
1854
+ result = await api.renameFolder(selectedItem.path, newName);
1855
+ } else {
1856
+ result = await api.renameFile(selectedItem.path, newName);
1857
+ }
1858
+ if (result.success && result.data) {
1859
+ setFiles((prev) => sortItems(prev.filter((f) => f.path !== selectedItem.path).concat(result.data)));
1860
+ setSelectedItem(result.data);
1861
+ if (selectedItem.isDirectory) {
1862
+ await loadTree();
1863
+ }
1864
+ } else {
1865
+ throw new Error(result.error || "Failed to rename");
1866
+ }
1867
+ }, [api, selectedItem, loadTree]);
1868
+ return /* @__PURE__ */ jsxs11("div", { className: `flex flex-col h-full bg-white border rounded-lg overflow-hidden ${className}`, children: [
1869
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between p-3 border-b bg-gray-50", children: [
1870
+ /* @__PURE__ */ jsx11(
1871
+ PathBreadcrumb,
1872
+ {
1873
+ currentPath,
1874
+ onNavigate: handleNavigate
1875
+ }
1876
+ ),
1877
+ /* @__PURE__ */ jsx11(
1878
+ FileActions,
1879
+ {
1880
+ selectedItem,
1881
+ onCreateFolder: () => setCreateFolderOpen(true),
1882
+ onUpload: (files2) => handleUpload(files2),
1883
+ onDownload: handleDownload,
1884
+ onDelete: () => setDeleteOpen(true),
1885
+ onRename: () => setRenameOpen(true),
1886
+ onRefresh: handleRefresh,
1887
+ isLoading
1888
+ }
1889
+ )
1890
+ ] }),
1891
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-1 min-h-0", children: [
1892
+ showTree && /* @__PURE__ */ jsx11(
1893
+ "div",
1894
+ {
1895
+ className: "border-r overflow-auto",
1896
+ style: { width: treeWidth, minWidth: treeWidth },
1897
+ children: /* @__PURE__ */ jsx11(
1898
+ FolderTree,
1899
+ {
1900
+ tree,
1901
+ currentPath,
1902
+ onSelect: handleNavigate,
1903
+ onExpand: handleTreeExpand,
1904
+ onToggle: handleTreeToggle,
1905
+ className: "p-2"
1906
+ }
1907
+ )
1908
+ }
1909
+ ),
1910
+ /* @__PURE__ */ jsx11("div", { className: "flex-1 min-w-0 overflow-auto", children: /* @__PURE__ */ jsx11(
1911
+ FileList,
1912
+ {
1913
+ files,
1914
+ selectedItem,
1915
+ isLoading,
1916
+ onSelect: handleSelect,
1917
+ onOpen: handleOpen,
1918
+ viewMode,
1919
+ className: "h-full"
1920
+ }
1921
+ ) })
1922
+ ] }),
1923
+ showPreview && /* @__PURE__ */ jsx11(
1924
+ "div",
1925
+ {
1926
+ className: "border-t",
1927
+ style: { height: previewHeight, minHeight: previewHeight },
1928
+ children: /* @__PURE__ */ jsx11(
1929
+ FilePreview,
1930
+ {
1931
+ item: selectedItem,
1932
+ getPreviewUrl: api.getPreviewUrl,
1933
+ getFileContent: api.getFileContent,
1934
+ className: "h-full"
1935
+ }
1936
+ )
1937
+ }
1938
+ ),
1939
+ /* @__PURE__ */ jsx11(
1940
+ CreateFolderDialog,
1941
+ {
1942
+ isOpen: createFolderOpen,
1943
+ currentPath,
1944
+ onClose: () => setCreateFolderOpen(false),
1945
+ onSubmit: handleCreateFolder
1946
+ }
1947
+ ),
1948
+ /* @__PURE__ */ jsx11(
1949
+ RenameDialog,
1950
+ {
1951
+ isOpen: renameOpen,
1952
+ item: selectedItem,
1953
+ onClose: () => setRenameOpen(false),
1954
+ onSubmit: handleRename
1955
+ }
1956
+ ),
1957
+ /* @__PURE__ */ jsx11(
1958
+ DeleteConfirmDialog,
1959
+ {
1960
+ isOpen: deleteOpen,
1961
+ item: selectedItem,
1962
+ onClose: () => setDeleteOpen(false),
1963
+ onConfirm: handleDelete
1964
+ }
1965
+ ),
1966
+ /* @__PURE__ */ jsx11(
1967
+ UploadDialog,
1968
+ {
1969
+ isOpen: uploadOpen,
1970
+ currentPath,
1971
+ onClose: () => setUploadOpen(false),
1972
+ onUpload: handleUpload
1973
+ }
1974
+ )
1975
+ ] });
1976
+ }
1977
+ function toggleTreeNode(nodes, path) {
1978
+ return nodes.map((node) => {
1979
+ if (node.path === path) {
1980
+ return { ...node, isExpanded: !node.isExpanded };
1981
+ }
1982
+ if (node.children.length > 0) {
1983
+ return { ...node, children: toggleTreeNode(node.children, path) };
1984
+ }
1985
+ return node;
1986
+ });
1987
+ }
1988
+ function updateTreeNode(nodes, path, children, isExpanded) {
1989
+ return nodes.map((node) => {
1990
+ if (node.path === path) {
1991
+ return { ...node, children, isExpanded };
1992
+ }
1993
+ if (node.children.length > 0) {
1994
+ return { ...node, children: updateTreeNode(node.children, path, children, isExpanded) };
1995
+ }
1996
+ return node;
1997
+ });
1998
+ }
1999
+ function sortItems(items) {
2000
+ return [...items].sort((a, b) => {
2001
+ if (a.isDirectory && !b.isDirectory) return -1;
2002
+ if (!a.isDirectory && b.isDirectory) return 1;
2003
+ return a.name.localeCompare(b.name);
2004
+ });
2005
+ }
2006
+
2007
+ // src/ui/hooks/useFileBrowser.ts
2008
+ import { useCallback as useCallback8, useEffect as useEffect5, useState as useState7 } from "react";
2009
+ function useFileBrowser(options) {
2010
+ const { api, initialPath = "/", autoLoad = true } = options;
2011
+ const [state, setState] = useState7({
2012
+ currentPath: initialPath,
2013
+ files: [],
2014
+ tree: [],
2015
+ selectedItem: null,
2016
+ isLoading: false,
2017
+ error: null
2018
+ });
2019
+ const loadDirectory = useCallback8(async (path) => {
2020
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
2021
+ const result = await api.listDirectory(path);
2022
+ if (result.success && result.data) {
2023
+ setState((prev) => ({
2024
+ ...prev,
2025
+ files: result.data,
2026
+ currentPath: path,
2027
+ isLoading: false
2028
+ }));
2029
+ } else {
2030
+ setState((prev) => ({
2031
+ ...prev,
2032
+ error: result.error || "Failed to load directory",
2033
+ isLoading: false
2034
+ }));
2035
+ }
2036
+ }, [api]);
2037
+ const loadTree = useCallback8(async (depth = 2) => {
2038
+ const result = await api.getFolderTree("/", depth);
2039
+ if (result.success && result.data) {
2040
+ setState((prev) => ({ ...prev, tree: result.data }));
2041
+ }
2042
+ }, [api]);
2043
+ const navigateTo = useCallback8(async (path) => {
2044
+ setState((prev) => ({ ...prev, selectedItem: null }));
2045
+ await loadDirectory(path);
2046
+ }, [loadDirectory]);
2047
+ const navigateUp = useCallback8(async () => {
2048
+ if (state.currentPath === "/") return;
2049
+ const parentPath = state.currentPath.split("/").slice(0, -1).join("/") || "/";
2050
+ await navigateTo(parentPath);
2051
+ }, [state.currentPath, navigateTo]);
2052
+ const refresh = useCallback8(async () => {
2053
+ await loadDirectory(state.currentPath);
2054
+ await loadTree();
2055
+ }, [loadDirectory, loadTree, state.currentPath]);
2056
+ const selectItem = useCallback8((item) => {
2057
+ setState((prev) => ({ ...prev, selectedItem: item }));
2058
+ }, []);
2059
+ const toggleTreeNode2 = useCallback8((path) => {
2060
+ setState((prev) => ({
2061
+ ...prev,
2062
+ tree: toggleNode(prev.tree, path)
2063
+ }));
2064
+ }, []);
2065
+ const expandTreeNode = useCallback8(async (path) => {
2066
+ const result = await api.listDirectory(path);
2067
+ if (result.success && result.data) {
2068
+ const folders = result.data.filter((item) => item.isDirectory);
2069
+ const children = folders.map((folder) => ({
2070
+ id: folder.id,
2071
+ name: folder.name,
2072
+ path: folder.path,
2073
+ children: []
2074
+ }));
2075
+ setState((prev) => ({
2076
+ ...prev,
2077
+ tree: updateTreeNode2(prev.tree, path, children, true)
2078
+ }));
2079
+ }
2080
+ }, [api]);
2081
+ const createFolder = useCallback8(async (name) => {
2082
+ const newPath = state.currentPath === "/" ? `/${name}` : `${state.currentPath}/${name}`;
2083
+ const result = await api.createDirectory(newPath);
2084
+ if (result.success && result.data) {
2085
+ setState((prev) => ({
2086
+ ...prev,
2087
+ files: sortItems2([...prev.files, result.data])
2088
+ }));
2089
+ }
2090
+ return result;
2091
+ }, [api, state.currentPath]);
2092
+ const deleteItem = useCallback8(async (path) => {
2093
+ const item = state.files.find((f) => f.path === path);
2094
+ if (!item) {
2095
+ return { success: false, error: "Item not found" };
2096
+ }
2097
+ let result;
2098
+ if (item.isDirectory) {
2099
+ result = await api.removeDirectory(path, true);
2100
+ } else {
2101
+ result = await api.deleteFile(path);
2102
+ }
2103
+ if (result.success) {
2104
+ setState((prev) => ({
2105
+ ...prev,
2106
+ files: prev.files.filter((f) => f.path !== path),
2107
+ selectedItem: prev.selectedItem?.path === path ? null : prev.selectedItem
2108
+ }));
2109
+ }
2110
+ return result;
2111
+ }, [api, state.files]);
2112
+ const deleteSelected = useCallback8(async () => {
2113
+ if (!state.selectedItem) {
2114
+ return { success: false, error: "No item selected" };
2115
+ }
2116
+ return deleteItem(state.selectedItem.path);
2117
+ }, [deleteItem, state.selectedItem]);
2118
+ const renameItem = useCallback8(async (path, newName, isDirectory) => {
2119
+ let result;
2120
+ if (isDirectory) {
2121
+ result = await api.renameFolder(path, newName);
2122
+ } else {
2123
+ result = await api.renameFile(path, newName);
2124
+ }
2125
+ if (result.success && result.data) {
2126
+ setState((prev) => ({
2127
+ ...prev,
2128
+ files: sortItems2(prev.files.filter((f) => f.path !== path).concat(result.data)),
2129
+ selectedItem: prev.selectedItem?.path === path ? result.data : prev.selectedItem
2130
+ }));
2131
+ }
2132
+ return result;
2133
+ }, [api]);
2134
+ const renameSelected = useCallback8(async (newName) => {
2135
+ if (!state.selectedItem) {
2136
+ return { success: false, error: "No item selected" };
2137
+ }
2138
+ return renameItem(state.selectedItem.path, newName, state.selectedItem.isDirectory);
2139
+ }, [renameItem, state.selectedItem]);
2140
+ const uploadFiles = useCallback8(async (files) => {
2141
+ const results = [];
2142
+ for (let i = 0; i < files.length; i++) {
2143
+ const file = files[i];
2144
+ const remotePath = state.currentPath === "/" ? `/${file.name}` : `${state.currentPath}/${file.name}`;
2145
+ const result = await api.uploadFile(file, remotePath);
2146
+ results.push(result);
2147
+ if (result.success && result.data) {
2148
+ setState((prev) => ({
2149
+ ...prev,
2150
+ files: sortItems2([...prev.files, result.data])
2151
+ }));
2152
+ }
2153
+ }
2154
+ return results;
2155
+ }, [api, state.currentPath]);
2156
+ const downloadFile = useCallback8(async (path) => {
2157
+ const item = state.files.find((f) => f.path === path);
2158
+ if (!item || item.isDirectory) return;
2159
+ const result = await api.downloadFile(path);
2160
+ if (result.success && result.data) {
2161
+ const blob = result.data;
2162
+ const url = URL.createObjectURL(blob);
2163
+ const a = document.createElement("a");
2164
+ a.href = url;
2165
+ a.download = item.name;
2166
+ document.body.appendChild(a);
2167
+ a.click();
2168
+ document.body.removeChild(a);
2169
+ URL.revokeObjectURL(url);
2170
+ }
2171
+ }, [api, state.files]);
2172
+ const downloadSelected = useCallback8(async () => {
2173
+ if (!state.selectedItem || state.selectedItem.isDirectory) return;
2174
+ await downloadFile(state.selectedItem.path);
2175
+ }, [downloadFile, state.selectedItem]);
2176
+ const moveItem = useCallback8(async (sourcePath, destinationPath) => {
2177
+ const result = await api.moveItem(sourcePath, destinationPath);
2178
+ if (result.success) {
2179
+ setState((prev) => ({
2180
+ ...prev,
2181
+ files: prev.files.filter((f) => f.path !== sourcePath),
2182
+ selectedItem: prev.selectedItem?.path === sourcePath ? null : prev.selectedItem
2183
+ }));
2184
+ }
2185
+ return result;
2186
+ }, [api]);
2187
+ useEffect5(() => {
2188
+ if (autoLoad) {
2189
+ loadDirectory(initialPath);
2190
+ loadTree();
2191
+ }
2192
+ }, [autoLoad, initialPath, loadDirectory, loadTree]);
2193
+ return {
2194
+ // State
2195
+ currentPath: state.currentPath,
2196
+ files: state.files,
2197
+ tree: state.tree,
2198
+ selectedItem: state.selectedItem,
2199
+ isLoading: state.isLoading,
2200
+ error: state.error,
2201
+ // Navigation
2202
+ navigateTo,
2203
+ navigateUp,
2204
+ refresh,
2205
+ // Selection
2206
+ selectItem,
2207
+ // Tree
2208
+ toggleTreeNode: toggleTreeNode2,
2209
+ expandTreeNode,
2210
+ loadTree,
2211
+ // Operations
2212
+ createFolder,
2213
+ deleteItem,
2214
+ deleteSelected,
2215
+ renameItem,
2216
+ renameSelected,
2217
+ uploadFiles,
2218
+ downloadFile,
2219
+ downloadSelected,
2220
+ moveItem
2221
+ };
2222
+ }
2223
+ function toggleNode(nodes, path) {
2224
+ return nodes.map((node) => {
2225
+ if (node.path === path) {
2226
+ return { ...node, isExpanded: !node.isExpanded };
2227
+ }
2228
+ if (node.children.length > 0) {
2229
+ return { ...node, children: toggleNode(node.children, path) };
2230
+ }
2231
+ return node;
2232
+ });
2233
+ }
2234
+ function updateTreeNode2(nodes, path, children, isExpanded) {
2235
+ return nodes.map((node) => {
2236
+ if (node.path === path) {
2237
+ return { ...node, children, isExpanded };
2238
+ }
2239
+ if (node.children.length > 0) {
2240
+ return { ...node, children: updateTreeNode2(node.children, path, children, isExpanded) };
2241
+ }
2242
+ return node;
2243
+ });
2244
+ }
2245
+ function sortItems2(items) {
2246
+ return [...items].sort((a, b) => {
2247
+ if (a.isDirectory && !b.isDirectory) return -1;
2248
+ if (!a.isDirectory && b.isDirectory) return 1;
2249
+ return a.name.localeCompare(b.name);
2250
+ });
2251
+ }
2252
+
2253
+ // src/ui/hooks/useFileOperations.ts
2254
+ import { useState as useState8, useCallback as useCallback9 } from "react";
2255
+ function useFileOperations() {
2256
+ const [state, setState] = useState8({
2257
+ isLoading: false,
2258
+ error: null,
2259
+ lastResult: null
2260
+ });
2261
+ const execute = useCallback9(async (operation) => {
2262
+ setState({ isLoading: true, error: null, lastResult: null });
2263
+ try {
2264
+ const result = await operation();
2265
+ setState({
2266
+ isLoading: false,
2267
+ error: result.success ? null : result.error || "Operation failed",
2268
+ lastResult: result
2269
+ });
2270
+ return result;
2271
+ } catch (error) {
2272
+ const errorMessage = error.message || "Unknown error";
2273
+ const result = { success: false, error: errorMessage };
2274
+ setState({
2275
+ isLoading: false,
2276
+ error: errorMessage,
2277
+ lastResult: result
2278
+ });
2279
+ return result;
2280
+ }
2281
+ }, []);
2282
+ const reset = useCallback9(() => {
2283
+ setState({ isLoading: false, error: null, lastResult: null });
2284
+ }, []);
2285
+ return { state, execute, reset };
2286
+ }
2287
+ function useMultiFileOperations() {
2288
+ const [state, setState] = useState8({
2289
+ activeOperations: /* @__PURE__ */ new Set(),
2290
+ errors: /* @__PURE__ */ new Map()
2291
+ });
2292
+ const isOperating = useCallback9((id) => {
2293
+ return state.activeOperations.has(id);
2294
+ }, [state.activeOperations]);
2295
+ const getError = useCallback9((id) => {
2296
+ return state.errors.get(id);
2297
+ }, [state.errors]);
2298
+ const execute = useCallback9(async (id, operation) => {
2299
+ setState((prev) => {
2300
+ const newActive = new Set(prev.activeOperations);
2301
+ newActive.add(id);
2302
+ const newErrors = new Map(prev.errors);
2303
+ newErrors.delete(id);
2304
+ return { activeOperations: newActive, errors: newErrors };
2305
+ });
2306
+ try {
2307
+ const result = await operation();
2308
+ setState((prev) => {
2309
+ const newActive = new Set(prev.activeOperations);
2310
+ newActive.delete(id);
2311
+ const newErrors = new Map(prev.errors);
2312
+ if (!result.success && result.error) {
2313
+ newErrors.set(id, result.error);
2314
+ }
2315
+ return { activeOperations: newActive, errors: newErrors };
2316
+ });
2317
+ return result;
2318
+ } catch (error) {
2319
+ const errorMessage = error.message || "Unknown error";
2320
+ setState((prev) => {
2321
+ const newActive = new Set(prev.activeOperations);
2322
+ newActive.delete(id);
2323
+ const newErrors = new Map(prev.errors);
2324
+ newErrors.set(id, errorMessage);
2325
+ return { activeOperations: newActive, errors: newErrors };
2326
+ });
2327
+ return { success: false, error: errorMessage };
2328
+ }
2329
+ }, []);
2330
+ const clear = useCallback9((id) => {
2331
+ setState((prev) => {
2332
+ const newErrors = new Map(prev.errors);
2333
+ newErrors.delete(id);
2334
+ return { ...prev, errors: newErrors };
2335
+ });
2336
+ }, []);
2337
+ const clearAll = useCallback9(() => {
2338
+ setState({ activeOperations: /* @__PURE__ */ new Set(), errors: /* @__PURE__ */ new Map() });
2339
+ }, []);
2340
+ return { state, isOperating, getError, execute, clear, clearAll };
2341
+ }
2342
+
2343
+ // src/ui/context/FileBrowserContext.tsx
2344
+ import { createContext, useContext, useReducer, useCallback as useCallback10 } from "react";
2345
+ import { jsx as jsx12 } from "react/jsx-runtime";
2346
+ var initialState = {
2347
+ currentPath: "/",
2348
+ selectedItem: null,
2349
+ tree: [],
2350
+ files: [],
2351
+ isLoading: false,
2352
+ error: null
2353
+ };
2354
+ function fileBrowserReducer(state, action) {
2355
+ switch (action.type) {
2356
+ case "SET_LOADING":
2357
+ return { ...state, isLoading: action.payload };
2358
+ case "SET_ERROR":
2359
+ return { ...state, error: action.payload, isLoading: false };
2360
+ case "SET_CURRENT_PATH":
2361
+ return { ...state, currentPath: action.payload };
2362
+ case "SET_FILES":
2363
+ return { ...state, files: action.payload, isLoading: false };
2364
+ case "SET_TREE":
2365
+ return { ...state, tree: action.payload };
2366
+ case "SET_SELECTED_ITEM":
2367
+ return { ...state, selectedItem: action.payload };
2368
+ case "UPDATE_TREE_NODE": {
2369
+ const updateNode = (nodes, path, children, isExpanded) => {
2370
+ return nodes.map((node) => {
2371
+ if (node.path === path) {
2372
+ return { ...node, children, isExpanded };
2373
+ }
2374
+ if (node.children.length > 0) {
2375
+ return { ...node, children: updateNode(node.children, path, children, isExpanded) };
2376
+ }
2377
+ return node;
2378
+ });
2379
+ };
2380
+ return {
2381
+ ...state,
2382
+ tree: updateNode(state.tree, action.payload.path, action.payload.children, action.payload.isExpanded)
2383
+ };
2384
+ }
2385
+ case "TOGGLE_TREE_NODE": {
2386
+ const toggleNode2 = (nodes, path) => {
2387
+ return nodes.map((node) => {
2388
+ if (node.path === path) {
2389
+ return { ...node, isExpanded: !node.isExpanded };
2390
+ }
2391
+ if (node.children.length > 0) {
2392
+ return { ...node, children: toggleNode2(node.children, path) };
2393
+ }
2394
+ return node;
2395
+ });
2396
+ };
2397
+ return { ...state, tree: toggleNode2(state.tree, action.payload) };
2398
+ }
2399
+ case "ADD_ITEM":
2400
+ return {
2401
+ ...state,
2402
+ files: [...state.files, action.payload].sort((a, b) => {
2403
+ if (a.isDirectory && !b.isDirectory) return -1;
2404
+ if (!a.isDirectory && b.isDirectory) return 1;
2405
+ return a.name.localeCompare(b.name);
2406
+ })
2407
+ };
2408
+ case "REMOVE_ITEM":
2409
+ return {
2410
+ ...state,
2411
+ files: state.files.filter((f) => f.path !== action.payload),
2412
+ selectedItem: state.selectedItem?.path === action.payload ? null : state.selectedItem
2413
+ };
2414
+ case "UPDATE_ITEM":
2415
+ return {
2416
+ ...state,
2417
+ files: state.files.map((f) => f.path === action.payload.path ? action.payload : f),
2418
+ selectedItem: state.selectedItem?.path === action.payload.path ? action.payload : state.selectedItem
2419
+ };
2420
+ case "REFRESH":
2421
+ return { ...state, isLoading: true };
2422
+ default:
2423
+ return state;
2424
+ }
2425
+ }
2426
+ var FileBrowserContext = createContext(null);
2427
+ function FileBrowserProvider({ children, api, initialPath = "/" }) {
2428
+ const [state, dispatch] = useReducer(fileBrowserReducer, {
2429
+ ...initialState,
2430
+ currentPath: initialPath
2431
+ });
2432
+ const navigateTo = useCallback10(async (path) => {
2433
+ dispatch({ type: "SET_LOADING", payload: true });
2434
+ dispatch({ type: "SET_CURRENT_PATH", payload: path });
2435
+ dispatch({ type: "SET_SELECTED_ITEM", payload: null });
2436
+ const result = await api.listDirectory(path);
2437
+ if (result.success && result.data) {
2438
+ dispatch({ type: "SET_FILES", payload: result.data });
2439
+ } else {
2440
+ dispatch({ type: "SET_ERROR", payload: result.error || "Failed to load directory" });
2441
+ }
2442
+ }, [api]);
2443
+ const refresh = useCallback10(async () => {
2444
+ await navigateTo(state.currentPath);
2445
+ const treeResult = await api.getFolderTree("/", 2);
2446
+ if (treeResult.success && treeResult.data) {
2447
+ dispatch({ type: "SET_TREE", payload: treeResult.data });
2448
+ }
2449
+ }, [api, navigateTo, state.currentPath]);
2450
+ const selectItem = useCallback10((item) => {
2451
+ dispatch({ type: "SET_SELECTED_ITEM", payload: item });
2452
+ }, []);
2453
+ const toggleTreeNode2 = useCallback10((path) => {
2454
+ dispatch({ type: "TOGGLE_TREE_NODE", payload: path });
2455
+ }, []);
2456
+ const expandTreeNode = useCallback10(async (path) => {
2457
+ const result = await api.listDirectory(path);
2458
+ if (result.success && result.data) {
2459
+ const folders = result.data.filter((item) => item.isDirectory);
2460
+ const children2 = folders.map((folder) => ({
2461
+ id: folder.id,
2462
+ name: folder.name,
2463
+ path: folder.path,
2464
+ children: []
2465
+ }));
2466
+ dispatch({ type: "UPDATE_TREE_NODE", payload: { path, children: children2, isExpanded: true } });
2467
+ }
2468
+ }, [api]);
2469
+ const createFolder = useCallback10(async (name) => {
2470
+ const newPath = state.currentPath === "/" ? `/${name}` : `${state.currentPath}/${name}`;
2471
+ const result = await api.createDirectory(newPath);
2472
+ if (result.success && result.data) {
2473
+ dispatch({ type: "ADD_ITEM", payload: result.data });
2474
+ }
2475
+ return result;
2476
+ }, [api, state.currentPath]);
2477
+ const deleteSelected = useCallback10(async () => {
2478
+ if (!state.selectedItem) {
2479
+ return { success: false, error: "No item selected" };
2480
+ }
2481
+ let result;
2482
+ if (state.selectedItem.isDirectory) {
2483
+ result = await api.removeDirectory(state.selectedItem.path, true);
2484
+ } else {
2485
+ result = await api.deleteFile(state.selectedItem.path);
2486
+ }
2487
+ if (result.success) {
2488
+ dispatch({ type: "REMOVE_ITEM", payload: state.selectedItem.path });
2489
+ }
2490
+ return result;
2491
+ }, [api, state.selectedItem]);
2492
+ const renameSelected = useCallback10(async (newName) => {
2493
+ if (!state.selectedItem) {
2494
+ return { success: false, error: "No item selected" };
2495
+ }
2496
+ let result;
2497
+ if (state.selectedItem.isDirectory) {
2498
+ result = await api.renameFolder(state.selectedItem.path, newName);
2499
+ } else {
2500
+ result = await api.renameFile(state.selectedItem.path, newName);
2501
+ }
2502
+ if (result.success && result.data) {
2503
+ dispatch({ type: "REMOVE_ITEM", payload: state.selectedItem.path });
2504
+ dispatch({ type: "ADD_ITEM", payload: result.data });
2505
+ dispatch({ type: "SET_SELECTED_ITEM", payload: result.data });
2506
+ }
2507
+ return result;
2508
+ }, [api, state.selectedItem]);
2509
+ const uploadFiles = useCallback10(async (files) => {
2510
+ const results = [];
2511
+ for (let i = 0; i < files.length; i++) {
2512
+ const file = files[i];
2513
+ const remotePath = state.currentPath === "/" ? `/${file.name}` : `${state.currentPath}/${file.name}`;
2514
+ const result = await api.uploadFile(file, remotePath);
2515
+ results.push(result);
2516
+ if (result.success && result.data) {
2517
+ dispatch({ type: "ADD_ITEM", payload: result.data });
2518
+ }
2519
+ }
2520
+ return results;
2521
+ }, [api, state.currentPath]);
2522
+ const downloadSelected = useCallback10(async () => {
2523
+ if (!state.selectedItem || state.selectedItem.isDirectory) {
2524
+ return;
2525
+ }
2526
+ const result = await api.downloadFile(state.selectedItem.path);
2527
+ if (result.success && result.data) {
2528
+ const blob = result.data;
2529
+ const url = URL.createObjectURL(blob);
2530
+ const a = document.createElement("a");
2531
+ a.href = url;
2532
+ a.download = state.selectedItem.name;
2533
+ document.body.appendChild(a);
2534
+ a.click();
2535
+ document.body.removeChild(a);
2536
+ URL.revokeObjectURL(url);
2537
+ }
2538
+ }, [api, state.selectedItem]);
2539
+ const moveSelected = useCallback10(async (destinationPath) => {
2540
+ if (!state.selectedItem) {
2541
+ return { success: false, error: "No item selected" };
2542
+ }
2543
+ const result = await api.moveItem(state.selectedItem.path, destinationPath);
2544
+ if (result.success) {
2545
+ dispatch({ type: "REMOVE_ITEM", payload: state.selectedItem.path });
2546
+ }
2547
+ return result;
2548
+ }, [api, state.selectedItem]);
2549
+ const value = {
2550
+ state,
2551
+ dispatch,
2552
+ api,
2553
+ navigateTo,
2554
+ refresh,
2555
+ selectItem,
2556
+ toggleTreeNode: toggleTreeNode2,
2557
+ expandTreeNode,
2558
+ createFolder,
2559
+ deleteSelected,
2560
+ renameSelected,
2561
+ uploadFiles,
2562
+ downloadSelected,
2563
+ moveSelected
2564
+ };
2565
+ return /* @__PURE__ */ jsx12(FileBrowserContext.Provider, { value, children });
2566
+ }
2567
+ function useFileBrowserContext() {
2568
+ const context = useContext(FileBrowserContext);
2569
+ if (!context) {
2570
+ throw new Error("useFileBrowserContext must be used within a FileBrowserProvider");
2571
+ }
2572
+ return context;
2573
+ }
2574
+
2575
+ // src/ui/components/naming/NamingRuleConfigurator.tsx
2576
+ import { useCallback as useCallback13, useRef as useRef5, useState as useState12 } from "react";
2577
+ import {
2578
+ DndContext,
2579
+ DragOverlay,
2580
+ PointerSensor,
2581
+ useSensor,
2582
+ useSensors,
2583
+ closestCenter
2584
+ } from "@dnd-kit/core";
2585
+
2586
+ // src/ui/hooks/useNamingRule.ts
2587
+ import { useState as useState9, useCallback as useCallback11, useEffect as useEffect6, useRef as useRef3 } from "react";
2588
+
2589
+ // src/common/naming-utils.ts
2590
+ var DEFAULT_DATE_FORMATS = [
2591
+ "YYYY",
2592
+ "YY",
2593
+ "MM",
2594
+ "M",
2595
+ "DD",
2596
+ "D",
2597
+ "MMM",
2598
+ "MMMM",
2599
+ "YYYY-MM-DD",
2600
+ "YYYY-MMM-DD",
2601
+ "DD-MM-YYYY",
2602
+ "MM-DD-YYYY"
2603
+ ];
2604
+ var MONTH_NAMES_SHORT = [
2605
+ "Jan",
2606
+ "Feb",
2607
+ "Mar",
2608
+ "Apr",
2609
+ "May",
2610
+ "Jun",
2611
+ "Jul",
2612
+ "Aug",
2613
+ "Sep",
2614
+ "Oct",
2615
+ "Nov",
2616
+ "Dec"
2617
+ ];
2618
+ var MONTH_NAMES_FULL = [
2619
+ "January",
2620
+ "February",
2621
+ "March",
2622
+ "April",
2623
+ "May",
2624
+ "June",
2625
+ "July",
2626
+ "August",
2627
+ "September",
2628
+ "October",
2629
+ "November",
2630
+ "December"
2631
+ ];
2632
+ var SYSTEM_DATE_VARIABLES = [
2633
+ { variable_name: "YYYY", description: "Full year (4 digits)", example_value: "2024", category: "date" },
2634
+ { variable_name: "YY", description: "Year (2 digits)", example_value: "24", category: "date" },
2635
+ { variable_name: "MM", description: "Month (2 digits, 01-12)", example_value: "01", category: "date" },
2636
+ { variable_name: "M", description: "Month (1-12)", example_value: "1", category: "date" },
2637
+ { variable_name: "DD", description: "Day (2 digits, 01-31)", example_value: "15", category: "date" },
2638
+ { variable_name: "D", description: "Day (1-31)", example_value: "15", category: "date" },
2639
+ { variable_name: "MMM", description: "Short month name", example_value: "Jan", category: "date" },
2640
+ { variable_name: "MMMM", description: "Full month name", example_value: "January", category: "date" },
2641
+ { variable_name: "YYYY-MM-DD", description: "ISO date format", example_value: "2024-01-15", category: "date" },
2642
+ { variable_name: "YYYY-MMM-DD", description: "Date with month name", example_value: "2024-Jan-15", category: "date" },
2643
+ { variable_name: "DD-MM-YYYY", description: "Day-Month-Year", example_value: "15-01-2024", category: "date" },
2644
+ { variable_name: "MM-DD-YYYY", description: "Month-Day-Year", example_value: "01-15-2024", category: "date" }
2645
+ ];
2646
+ var SYSTEM_FILE_VARIABLES = [
2647
+ { variable_name: "original_name", description: "Original filename without extension", example_value: "document", category: "file" },
2648
+ { variable_name: "extension", description: "Original file extension with dot", example_value: ".pdf", category: "file" },
2649
+ { variable_name: "ext", description: "Extension without dot", example_value: "pdf", category: "file" }
2650
+ ];
2651
+ var SYSTEM_COUNTER_VARIABLES = [
2652
+ { variable_name: "counter", description: "Auto-incrementing number (3 digits)", example_value: "001", category: "counter" }
2653
+ ];
2654
+ var ALL_SYSTEM_VARIABLES = [
2655
+ ...SYSTEM_DATE_VARIABLES,
2656
+ ...SYSTEM_FILE_VARIABLES,
2657
+ ...SYSTEM_COUNTER_VARIABLES
2658
+ ];
2659
+ function formatDateToken(date, format) {
2660
+ const year = date.getFullYear();
2661
+ const month = date.getMonth();
2662
+ const day = date.getDate();
2663
+ switch (format) {
2664
+ case "YYYY":
2665
+ return String(year);
2666
+ case "YY":
2667
+ return String(year).slice(-2);
2668
+ case "MM":
2669
+ return String(month + 1).padStart(2, "0");
2670
+ case "M":
2671
+ return String(month + 1);
2672
+ case "DD":
2673
+ return String(day).padStart(2, "0");
2674
+ case "D":
2675
+ return String(day);
2676
+ case "MMM":
2677
+ return MONTH_NAMES_SHORT[month];
2678
+ case "MMMM":
2679
+ return MONTH_NAMES_FULL[month];
2680
+ case "YYYY-MM-DD":
2681
+ return `${year}-${String(month + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
2682
+ case "YYYY-MMM-DD":
2683
+ return `${year}-${MONTH_NAMES_SHORT[month]}-${String(day).padStart(2, "0")}`;
2684
+ case "DD-MM-YYYY":
2685
+ return `${String(day).padStart(2, "0")}-${String(month + 1).padStart(2, "0")}-${year}`;
2686
+ case "MM-DD-YYYY":
2687
+ return `${String(month + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}-${year}`;
2688
+ default:
2689
+ return format;
2690
+ }
2691
+ }
2692
+ function formatCounter(value, digits = 3) {
2693
+ return String(value).padStart(digits, "0");
2694
+ }
2695
+ function getFileMetadataValues(filename) {
2696
+ if (!filename) {
2697
+ return {
2698
+ original_name: "",
2699
+ extension: "",
2700
+ ext: ""
2701
+ };
2702
+ }
2703
+ const extension = getExtension(filename);
2704
+ const originalName = getNameWithoutExtension(filename);
2705
+ return {
2706
+ original_name: originalName,
2707
+ extension,
2708
+ ext: extension.startsWith(".") ? extension.slice(1) : extension
2709
+ };
2710
+ }
2711
+ function validateNamingRuleSchema(schema) {
2712
+ if (!schema || typeof schema !== "object") return false;
2713
+ const s = schema;
2714
+ if (typeof s.version !== "number") return false;
2715
+ if (!Array.isArray(s.filePattern) || !Array.isArray(s.folderPattern)) return false;
2716
+ const isValidSegment = (seg) => {
2717
+ if (!seg || typeof seg !== "object") return false;
2718
+ const segment = seg;
2719
+ return typeof segment.id === "string" && (segment.type === "variable" || segment.type === "literal") && typeof segment.value === "string";
2720
+ };
2721
+ return s.filePattern.every(isValidSegment) && s.folderPattern.every(isValidSegment);
2722
+ }
2723
+ function generateSegmentId() {
2724
+ return `seg_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
2725
+ }
2726
+ function createVariableSegment(variableName) {
2727
+ return {
2728
+ id: generateSegmentId(),
2729
+ type: "variable",
2730
+ value: variableName
2731
+ };
2732
+ }
2733
+ function createLiteralSegment(text) {
2734
+ return {
2735
+ id: generateSegmentId(),
2736
+ type: "literal",
2737
+ value: text
2738
+ };
2739
+ }
2740
+ function clonePattern(pattern) {
2741
+ return pattern.map((seg) => ({
2742
+ ...seg,
2743
+ id: generateSegmentId()
2744
+ }));
2745
+ }
2746
+ function getSystemVariablePreviewValues(date = /* @__PURE__ */ new Date(), options = {}) {
2747
+ const { counterValue = 1, counterDigits = 3, originalFileName } = options;
2748
+ const fileMetadata = getFileMetadataValues(originalFileName);
2749
+ const values = {};
2750
+ for (const format of DEFAULT_DATE_FORMATS) {
2751
+ values[format] = formatDateToken(date, format);
2752
+ }
2753
+ values.original_name = fileMetadata.original_name || "document";
2754
+ values.extension = fileMetadata.extension || ".pdf";
2755
+ values.ext = fileMetadata.ext || "pdf";
2756
+ values.counter = formatCounter(counterValue, counterDigits);
2757
+ return values;
2758
+ }
2759
+ function generatePreviewName(pattern, userVariables, options = {}) {
2760
+ if (pattern.length === 0) {
2761
+ return "(empty pattern)";
2762
+ }
2763
+ const systemValues = getSystemVariablePreviewValues(
2764
+ options.date,
2765
+ options
2766
+ );
2767
+ const userValues = {};
2768
+ for (const variable of userVariables) {
2769
+ userValues[variable.variable_name] = variable.example_value;
2770
+ }
2771
+ const allValues = { ...userValues, ...systemValues };
2772
+ const parts = [];
2773
+ for (const segment of pattern) {
2774
+ if (segment.type === "literal") {
2775
+ parts.push(segment.value);
2776
+ } else if (segment.type === "variable") {
2777
+ const value = allValues[segment.value];
2778
+ if (value !== void 0) {
2779
+ parts.push(value);
2780
+ } else {
2781
+ parts.push(`{${segment.value}}`);
2782
+ }
2783
+ }
2784
+ }
2785
+ return parts.join("") || "(empty)";
2786
+ }
2787
+
2788
+ // src/ui/hooks/useNamingRule.ts
2789
+ var MAX_HISTORY_SIZE = 50;
2790
+ function useNamingRule(options = {}) {
2791
+ const { initialSchema, onChange } = options;
2792
+ const [filePattern, setFilePattern] = useState9(
2793
+ initialSchema?.filePattern ? clonePattern(initialSchema.filePattern) : []
2794
+ );
2795
+ const [folderPattern, setFolderPattern] = useState9(
2796
+ initialSchema?.folderPattern ? clonePattern(initialSchema.folderPattern) : []
2797
+ );
2798
+ const [history, setHistory] = useState9([]);
2799
+ const [historyIndex, setHistoryIndex] = useState9(-1);
2800
+ const initialStateRef = useRef3({
2801
+ filePattern: initialSchema?.filePattern ? clonePattern(initialSchema.filePattern) : [],
2802
+ folderPattern: initialSchema?.folderPattern ? clonePattern(initialSchema.folderPattern) : []
2803
+ });
2804
+ const isUndoRedoRef = useRef3(false);
2805
+ const recordHistory = useCallback11(() => {
2806
+ if (isUndoRedoRef.current) return;
2807
+ const entry = {
2808
+ filePattern: clonePattern(filePattern),
2809
+ folderPattern: clonePattern(folderPattern),
2810
+ timestamp: Date.now()
2811
+ };
2812
+ setHistory((prev) => {
2813
+ const newHistory = prev.slice(0, historyIndex + 1);
2814
+ newHistory.push(entry);
2815
+ if (newHistory.length > MAX_HISTORY_SIZE) {
2816
+ return newHistory.slice(-MAX_HISTORY_SIZE);
2817
+ }
2818
+ return newHistory;
2819
+ });
2820
+ setHistoryIndex((prev) => Math.min(prev + 1, MAX_HISTORY_SIZE - 1));
2821
+ }, [filePattern, folderPattern, historyIndex]);
2822
+ useEffect6(() => {
2823
+ if (onChange) {
2824
+ onChange(getSchema());
2825
+ }
2826
+ }, [filePattern, folderPattern]);
2827
+ const getSchema = useCallback11(() => {
2828
+ return {
2829
+ version: 1,
2830
+ filePattern: clonePattern(filePattern),
2831
+ folderPattern: clonePattern(folderPattern),
2832
+ metadata: {
2833
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2834
+ }
2835
+ };
2836
+ }, [filePattern, folderPattern]);
2837
+ const loadSchema = useCallback11((schema) => {
2838
+ const newFilePattern = schema.filePattern ? clonePattern(schema.filePattern) : [];
2839
+ const newFolderPattern = schema.folderPattern ? clonePattern(schema.folderPattern) : [];
2840
+ setFilePattern(newFilePattern);
2841
+ setFolderPattern(newFolderPattern);
2842
+ setHistory([]);
2843
+ setHistoryIndex(-1);
2844
+ initialStateRef.current = {
2845
+ filePattern: clonePattern(newFilePattern),
2846
+ folderPattern: clonePattern(newFolderPattern)
2847
+ };
2848
+ }, []);
2849
+ const reset = useCallback11(() => {
2850
+ setFilePattern(clonePattern(initialStateRef.current.filePattern));
2851
+ setFolderPattern(clonePattern(initialStateRef.current.folderPattern));
2852
+ setHistory([]);
2853
+ setHistoryIndex(-1);
2854
+ }, []);
2855
+ const undo = useCallback11(() => {
2856
+ if (historyIndex < 0) return;
2857
+ isUndoRedoRef.current = true;
2858
+ const entry = history[historyIndex];
2859
+ if (entry) {
2860
+ setFilePattern(clonePattern(entry.filePattern));
2861
+ setFolderPattern(clonePattern(entry.folderPattern));
2862
+ }
2863
+ setHistoryIndex((prev) => prev - 1);
2864
+ setTimeout(() => {
2865
+ isUndoRedoRef.current = false;
2866
+ }, 0);
2867
+ }, [history, historyIndex]);
2868
+ const redo = useCallback11(() => {
2869
+ if (historyIndex >= history.length - 1) return;
2870
+ isUndoRedoRef.current = true;
2871
+ const entry = history[historyIndex + 1];
2872
+ if (entry) {
2873
+ setFilePattern(clonePattern(entry.filePattern));
2874
+ setFolderPattern(clonePattern(entry.folderPattern));
2875
+ }
2876
+ setHistoryIndex((prev) => prev + 1);
2877
+ setTimeout(() => {
2878
+ isUndoRedoRef.current = false;
2879
+ }, 0);
2880
+ }, [history, historyIndex]);
2881
+ const addToFilePattern = useCallback11(
2882
+ (segment, index) => {
2883
+ recordHistory();
2884
+ setFilePattern((prev) => {
2885
+ const newSegment = { ...segment, id: generateSegmentId() };
2886
+ if (index !== void 0 && index >= 0 && index <= prev.length) {
2887
+ const newPattern = [...prev];
2888
+ newPattern.splice(index, 0, newSegment);
2889
+ return newPattern;
2890
+ }
2891
+ return [...prev, newSegment];
2892
+ });
2893
+ },
2894
+ [recordHistory]
2895
+ );
2896
+ const removeFromFilePattern = useCallback11(
2897
+ (id) => {
2898
+ recordHistory();
2899
+ setFilePattern((prev) => prev.filter((seg) => seg.id !== id));
2900
+ },
2901
+ [recordHistory]
2902
+ );
2903
+ const updateFilePatternSegment = useCallback11(
2904
+ (id, value) => {
2905
+ recordHistory();
2906
+ setFilePattern(
2907
+ (prev) => prev.map((seg) => seg.id === id ? { ...seg, value } : seg)
2908
+ );
2909
+ },
2910
+ [recordHistory]
2911
+ );
2912
+ const reorderFilePattern = useCallback11(
2913
+ (fromIndex, toIndex) => {
2914
+ if (fromIndex === toIndex) return;
2915
+ recordHistory();
2916
+ setFilePattern((prev) => {
2917
+ const newPattern = [...prev];
2918
+ const [removed] = newPattern.splice(fromIndex, 1);
2919
+ newPattern.splice(toIndex, 0, removed);
2920
+ return newPattern;
2921
+ });
2922
+ },
2923
+ [recordHistory]
2924
+ );
2925
+ const clearFilePattern = useCallback11(() => {
2926
+ recordHistory();
2927
+ setFilePattern([]);
2928
+ }, [recordHistory]);
2929
+ const addToFolderPattern = useCallback11(
2930
+ (segment, index) => {
2931
+ recordHistory();
2932
+ setFolderPattern((prev) => {
2933
+ const newSegment = { ...segment, id: generateSegmentId() };
2934
+ if (index !== void 0 && index >= 0 && index <= prev.length) {
2935
+ const newPattern = [...prev];
2936
+ newPattern.splice(index, 0, newSegment);
2937
+ return newPattern;
2938
+ }
2939
+ return [...prev, newSegment];
2940
+ });
2941
+ },
2942
+ [recordHistory]
2943
+ );
2944
+ const removeFromFolderPattern = useCallback11(
2945
+ (id) => {
2946
+ recordHistory();
2947
+ setFolderPattern((prev) => prev.filter((seg) => seg.id !== id));
2948
+ },
2949
+ [recordHistory]
2950
+ );
2951
+ const updateFolderPatternSegment = useCallback11(
2952
+ (id, value) => {
2953
+ recordHistory();
2954
+ setFolderPattern(
2955
+ (prev) => prev.map((seg) => seg.id === id ? { ...seg, value } : seg)
2956
+ );
2957
+ },
2958
+ [recordHistory]
2959
+ );
2960
+ const reorderFolderPattern = useCallback11(
2961
+ (fromIndex, toIndex) => {
2962
+ if (fromIndex === toIndex) return;
2963
+ recordHistory();
2964
+ setFolderPattern((prev) => {
2965
+ const newPattern = [...prev];
2966
+ const [removed] = newPattern.splice(fromIndex, 1);
2967
+ newPattern.splice(toIndex, 0, removed);
2968
+ return newPattern;
2969
+ });
2970
+ },
2971
+ [recordHistory]
2972
+ );
2973
+ const clearFolderPattern = useCallback11(() => {
2974
+ recordHistory();
2975
+ setFolderPattern([]);
2976
+ }, [recordHistory]);
2977
+ useEffect6(() => {
2978
+ const handleKeyDown = (e) => {
2979
+ if ((e.ctrlKey || e.metaKey) && e.key === "z") {
2980
+ if (e.shiftKey) {
2981
+ e.preventDefault();
2982
+ redo();
2983
+ } else {
2984
+ e.preventDefault();
2985
+ undo();
2986
+ }
2987
+ }
2988
+ if ((e.ctrlKey || e.metaKey) && e.key === "y") {
2989
+ e.preventDefault();
2990
+ redo();
2991
+ }
2992
+ };
2993
+ window.addEventListener("keydown", handleKeyDown);
2994
+ return () => window.removeEventListener("keydown", handleKeyDown);
2995
+ }, [undo, redo]);
2996
+ const isDirty = JSON.stringify(filePattern) !== JSON.stringify(initialStateRef.current.filePattern) || JSON.stringify(folderPattern) !== JSON.stringify(initialStateRef.current.folderPattern);
2997
+ return {
2998
+ // State
2999
+ filePattern,
3000
+ folderPattern,
3001
+ canUndo: historyIndex >= 0,
3002
+ canRedo: historyIndex < history.length - 1,
3003
+ isDirty,
3004
+ // File pattern operations
3005
+ addToFilePattern,
3006
+ removeFromFilePattern,
3007
+ updateFilePatternSegment,
3008
+ reorderFilePattern,
3009
+ clearFilePattern,
3010
+ // Folder pattern operations
3011
+ addToFolderPattern,
3012
+ removeFromFolderPattern,
3013
+ updateFolderPatternSegment,
3014
+ reorderFolderPattern,
3015
+ clearFolderPattern,
3016
+ // History
3017
+ undo,
3018
+ redo,
3019
+ // Schema
3020
+ getSchema,
3021
+ loadSchema,
3022
+ reset
3023
+ };
3024
+ }
3025
+
3026
+ // src/ui/components/naming/VariableList.tsx
3027
+ import { useState as useState10 } from "react";
3028
+
3029
+ // src/ui/components/naming/DraggableVariable.tsx
3030
+ import { useDraggable } from "@dnd-kit/core";
3031
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
3032
+ function getCategoryColor(category) {
3033
+ switch (category) {
3034
+ case "date":
3035
+ return "bg-emerald-100 hover:bg-emerald-200 border-emerald-300 text-emerald-800";
3036
+ case "file":
3037
+ return "bg-purple-100 hover:bg-purple-200 border-purple-300 text-purple-800";
3038
+ case "counter":
3039
+ return "bg-amber-100 hover:bg-amber-200 border-amber-300 text-amber-800";
3040
+ case "user":
3041
+ default:
3042
+ return "bg-blue-100 hover:bg-blue-200 border-blue-300 text-blue-800";
3043
+ }
3044
+ }
3045
+ function DraggableVariable({
3046
+ variable,
3047
+ onClick,
3048
+ className = "",
3049
+ disabled = false
3050
+ }) {
3051
+ const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
3052
+ id: `draggable-${variable.variable_name}`,
3053
+ data: {
3054
+ type: "variable",
3055
+ variable
3056
+ },
3057
+ disabled
3058
+ });
3059
+ const colorClass = getCategoryColor(variable.category);
3060
+ return /* @__PURE__ */ jsxs12(
3061
+ "div",
3062
+ {
3063
+ ref: setNodeRef,
3064
+ ...listeners,
3065
+ ...attributes,
3066
+ onClick: () => !disabled && onClick?.(variable),
3067
+ className: `
3068
+ inline-flex items-center gap-1 px-2.5 py-1 rounded-full border text-sm font-medium
3069
+ cursor-grab active:cursor-grabbing select-none transition-all
3070
+ ${colorClass}
3071
+ ${isDragging ? "opacity-50 scale-95 shadow-lg" : "shadow-sm"}
3072
+ ${disabled ? "opacity-50 cursor-not-allowed" : ""}
3073
+ ${className}
3074
+ `,
3075
+ title: `${variable.description}
3076
+ Example: ${variable.example_value}`,
3077
+ children: [
3078
+ /* @__PURE__ */ jsx13("span", { className: "text-xs opacity-60", children: "{" }),
3079
+ /* @__PURE__ */ jsx13("span", { children: variable.variable_name }),
3080
+ /* @__PURE__ */ jsx13("span", { className: "text-xs opacity-60", children: "}" })
3081
+ ]
3082
+ }
3083
+ );
3084
+ }
3085
+
3086
+ // src/ui/components/naming/VariableList.tsx
3087
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
3088
+ function VariableList({
3089
+ userVariables,
3090
+ customDateFormats,
3091
+ onVariableClick,
3092
+ disabled = false,
3093
+ className = ""
3094
+ }) {
3095
+ const [activeCategory, setActiveCategory] = useState10("user");
3096
+ const userVars = userVariables.map((v) => ({
3097
+ ...v,
3098
+ category: "user"
3099
+ }));
3100
+ const dateVars = customDateFormats ? SYSTEM_DATE_VARIABLES.filter(
3101
+ (v) => customDateFormats.includes(v.variable_name)
3102
+ ) : SYSTEM_DATE_VARIABLES;
3103
+ const fileVars = SYSTEM_FILE_VARIABLES;
3104
+ const counterVars = SYSTEM_COUNTER_VARIABLES;
3105
+ const allTabs = [
3106
+ { id: "user", label: "User", count: userVars.length },
3107
+ { id: "date", label: "Dates", count: dateVars.length },
3108
+ { id: "file", label: "File", count: fileVars.length },
3109
+ { id: "counter", label: "Counter", count: counterVars.length }
3110
+ ];
3111
+ const tabs = allTabs.filter((tab) => tab.count > 0);
3112
+ const getVariablesForCategory = (category) => {
3113
+ switch (category) {
3114
+ case "user":
3115
+ return userVars;
3116
+ case "date":
3117
+ return dateVars;
3118
+ case "file":
3119
+ return fileVars;
3120
+ case "counter":
3121
+ return counterVars;
3122
+ default:
3123
+ return [];
3124
+ }
3125
+ };
3126
+ const activeVariables = getVariablesForCategory(activeCategory);
3127
+ if (activeVariables.length === 0 && tabs.length > 0) {
3128
+ const firstTab = tabs[0];
3129
+ if (firstTab.id !== activeCategory) {
3130
+ setActiveCategory(firstTab.id);
3131
+ }
3132
+ }
3133
+ return /* @__PURE__ */ jsxs13("div", { className: `bg-gray-50 rounded-lg border border-gray-200 ${className}`, children: [
3134
+ /* @__PURE__ */ jsx14("div", { className: "flex border-b border-gray-200", children: tabs.map((tab) => /* @__PURE__ */ jsxs13(
3135
+ "button",
3136
+ {
3137
+ type: "button",
3138
+ onClick: () => setActiveCategory(tab.id),
3139
+ className: `
3140
+ flex-1 px-3 py-2 text-sm font-medium transition-colors
3141
+ ${activeCategory === tab.id ? "bg-white border-b-2 border-blue-500 text-blue-600" : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"}
3142
+ `,
3143
+ children: [
3144
+ tab.label,
3145
+ /* @__PURE__ */ jsxs13("span", { className: "ml-1 text-xs text-gray-400", children: [
3146
+ "(",
3147
+ tab.count,
3148
+ ")"
3149
+ ] })
3150
+ ]
3151
+ },
3152
+ tab.id
3153
+ )) }),
3154
+ /* @__PURE__ */ jsxs13("div", { className: "p-3", children: [
3155
+ activeVariables.length > 0 ? /* @__PURE__ */ jsx14("div", { className: "flex flex-wrap gap-2", children: activeVariables.map((variable) => /* @__PURE__ */ jsx14(
3156
+ DraggableVariable,
3157
+ {
3158
+ variable,
3159
+ onClick: onVariableClick,
3160
+ disabled
3161
+ },
3162
+ variable.variable_name
3163
+ )) }) : /* @__PURE__ */ jsx14("p", { className: "text-sm text-gray-500 text-center py-4", children: "No variables available in this category" }),
3164
+ /* @__PURE__ */ jsx14("p", { className: "text-xs text-gray-400 mt-3 text-center", children: "Drag variables to the pattern below, or click to add" })
3165
+ ] })
3166
+ ] });
3167
+ }
3168
+
3169
+ // src/ui/components/naming/PatternBuilder.tsx
3170
+ import { useCallback as useCallback12 } from "react";
3171
+ import { useDroppable } from "@dnd-kit/core";
3172
+ import {
3173
+ SortableContext,
3174
+ horizontalListSortingStrategy
3175
+ } from "@dnd-kit/sortable";
3176
+
3177
+ // src/ui/components/naming/PatternSegmentItem.tsx
3178
+ import { useState as useState11, useRef as useRef4, useEffect as useEffect7 } from "react";
3179
+ import { useSortable } from "@dnd-kit/sortable";
3180
+ import { CSS } from "@dnd-kit/utilities";
3181
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
3182
+ function getSegmentStyle(type) {
3183
+ if (type === "variable") {
3184
+ return "bg-blue-100 border-blue-300 text-blue-800";
3185
+ }
3186
+ return "bg-gray-100 border-gray-300 text-gray-700";
3187
+ }
3188
+ function PatternSegmentItem({
3189
+ segment,
3190
+ onUpdate,
3191
+ onDelete,
3192
+ readOnly = false,
3193
+ className = ""
3194
+ }) {
3195
+ const [isEditing, setIsEditing] = useState11(false);
3196
+ const [editValue, setEditValue] = useState11(segment.value);
3197
+ const inputRef = useRef4(null);
3198
+ const {
3199
+ attributes,
3200
+ listeners,
3201
+ setNodeRef,
3202
+ transform,
3203
+ transition,
3204
+ isDragging
3205
+ } = useSortable({
3206
+ id: segment.id,
3207
+ disabled: readOnly
3208
+ });
3209
+ const style = {
3210
+ transform: CSS.Transform.toString(transform),
3211
+ transition
3212
+ };
3213
+ useEffect7(() => {
3214
+ if (isEditing && inputRef.current) {
3215
+ inputRef.current.focus();
3216
+ inputRef.current.select();
3217
+ }
3218
+ }, [isEditing]);
3219
+ const handleSubmit = () => {
3220
+ if (segment.type === "literal" && editValue !== segment.value) {
3221
+ onUpdate?.(segment.id, editValue);
3222
+ }
3223
+ setIsEditing(false);
3224
+ };
3225
+ const handleKeyDown = (e) => {
3226
+ if (e.key === "Enter") {
3227
+ handleSubmit();
3228
+ } else if (e.key === "Escape") {
3229
+ setEditValue(segment.value);
3230
+ setIsEditing(false);
3231
+ } else if (e.key === "Delete" || e.key === "Backspace") {
3232
+ if (!isEditing) {
3233
+ e.preventDefault();
3234
+ onDelete?.(segment.id);
3235
+ }
3236
+ }
3237
+ };
3238
+ const segmentStyle = getSegmentStyle(segment.type);
3239
+ return /* @__PURE__ */ jsxs14(
3240
+ "div",
3241
+ {
3242
+ ref: setNodeRef,
3243
+ style,
3244
+ className: `
3245
+ inline-flex items-center gap-1 rounded border text-sm
3246
+ ${segmentStyle}
3247
+ ${isDragging ? "opacity-50 shadow-lg z-50" : "shadow-sm"}
3248
+ ${readOnly ? "" : "cursor-grab active:cursor-grabbing"}
3249
+ ${className}
3250
+ `,
3251
+ ...attributes,
3252
+ ...listeners,
3253
+ onKeyDown: handleKeyDown,
3254
+ tabIndex: 0,
3255
+ children: [
3256
+ segment.type === "variable" ? (
3257
+ // Variable segment - not editable, just display
3258
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center px-2 py-1", children: [
3259
+ /* @__PURE__ */ jsx15("span", { className: "text-xs opacity-60", children: "{" }),
3260
+ /* @__PURE__ */ jsx15("span", { className: "font-medium", children: segment.value }),
3261
+ /* @__PURE__ */ jsx15("span", { className: "text-xs opacity-60", children: "}" })
3262
+ ] })
3263
+ ) : isEditing && !readOnly ? (
3264
+ // Literal segment in edit mode
3265
+ /* @__PURE__ */ jsx15(
3266
+ "input",
3267
+ {
3268
+ ref: inputRef,
3269
+ type: "text",
3270
+ value: editValue,
3271
+ onChange: (e) => setEditValue(e.target.value),
3272
+ onBlur: handleSubmit,
3273
+ onKeyDown: handleKeyDown,
3274
+ className: "px-2 py-1 bg-transparent border-none outline-none min-w-[40px] text-sm",
3275
+ style: { width: `${Math.max(40, editValue.length * 8)}px` }
3276
+ }
3277
+ )
3278
+ ) : (
3279
+ // Literal segment in display mode
3280
+ /* @__PURE__ */ jsx15(
3281
+ "div",
3282
+ {
3283
+ className: "px-2 py-1 cursor-text",
3284
+ onDoubleClick: () => !readOnly && setIsEditing(true),
3285
+ children: segment.value || /* @__PURE__ */ jsx15("span", { className: "opacity-50 italic", children: "empty" })
3286
+ }
3287
+ )
3288
+ ),
3289
+ !readOnly && /* @__PURE__ */ jsx15(
3290
+ "button",
3291
+ {
3292
+ type: "button",
3293
+ onClick: (e) => {
3294
+ e.stopPropagation();
3295
+ onDelete?.(segment.id);
3296
+ },
3297
+ className: "p-1 hover:bg-black/10 rounded transition-colors mr-1",
3298
+ title: "Remove segment",
3299
+ children: /* @__PURE__ */ jsxs14(
3300
+ "svg",
3301
+ {
3302
+ xmlns: "http://www.w3.org/2000/svg",
3303
+ width: "12",
3304
+ height: "12",
3305
+ viewBox: "0 0 24 24",
3306
+ fill: "none",
3307
+ stroke: "currentColor",
3308
+ strokeWidth: "2",
3309
+ strokeLinecap: "round",
3310
+ strokeLinejoin: "round",
3311
+ children: [
3312
+ /* @__PURE__ */ jsx15("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
3313
+ /* @__PURE__ */ jsx15("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
3314
+ ]
3315
+ }
3316
+ )
3317
+ }
3318
+ )
3319
+ ]
3320
+ }
3321
+ );
3322
+ }
3323
+
3324
+ // src/ui/components/naming/SeparatorPicker.tsx
3325
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
3326
+ var COMMON_SEPARATORS = [
3327
+ { value: "-", label: "-", title: "Hyphen" },
3328
+ { value: "_", label: "_", title: "Underscore" },
3329
+ { value: "/", label: "/", title: "Slash (for folder paths)" },
3330
+ { value: " ", label: "\u2423", title: "Space" },
3331
+ { value: ".", label: ".", title: "Period" }
3332
+ ];
3333
+ function SeparatorPicker({
3334
+ onSelect,
3335
+ disabled = false,
3336
+ className = ""
3337
+ }) {
3338
+ return /* @__PURE__ */ jsxs15("div", { className: `flex items-center gap-1 ${className}`, children: [
3339
+ /* @__PURE__ */ jsx16("span", { className: "text-xs text-gray-500 mr-1", children: "Add:" }),
3340
+ COMMON_SEPARATORS.map(({ value, label, title }) => /* @__PURE__ */ jsx16(
3341
+ "button",
3342
+ {
3343
+ type: "button",
3344
+ onClick: () => onSelect(value),
3345
+ disabled,
3346
+ title,
3347
+ className: `
3348
+ w-7 h-7 flex items-center justify-center rounded border text-sm font-mono
3349
+ bg-gray-50 border-gray-200 text-gray-600
3350
+ hover:bg-gray-100 hover:border-gray-300
3351
+ disabled:opacity-50 disabled:cursor-not-allowed
3352
+ transition-colors
3353
+ `,
3354
+ children: label
3355
+ },
3356
+ value
3357
+ ))
3358
+ ] });
3359
+ }
3360
+
3361
+ // src/ui/components/naming/PatternBuilder.tsx
3362
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
3363
+ function DroppableZone({
3364
+ id,
3365
+ children,
3366
+ isEmpty,
3367
+ placeholder
3368
+ }) {
3369
+ const { setNodeRef, isOver } = useDroppable({ id });
3370
+ return /* @__PURE__ */ jsx17(
3371
+ "div",
3372
+ {
3373
+ ref: setNodeRef,
3374
+ className: `
3375
+ min-h-[48px] p-2 rounded-lg border-2 border-dashed transition-colors
3376
+ ${isOver ? "border-blue-400 bg-blue-50" : isEmpty ? "border-gray-300 bg-gray-50" : "border-gray-200 bg-white"}
3377
+ `,
3378
+ children: isEmpty ? /* @__PURE__ */ jsx17("div", { className: "flex items-center justify-center h-8 text-gray-400 text-sm", children: placeholder }) : /* @__PURE__ */ jsx17("div", { className: "flex flex-wrap gap-1.5 items-center", children })
3379
+ }
3380
+ );
3381
+ }
3382
+ function PatternBuilder({
3383
+ pattern,
3384
+ label,
3385
+ droppableId,
3386
+ onAddSegment,
3387
+ onRemoveSegment,
3388
+ onUpdateSegment,
3389
+ onClear,
3390
+ readOnly = false,
3391
+ placeholder = "Drop variables here or type text...",
3392
+ className = ""
3393
+ }) {
3394
+ const handleAddSeparator = useCallback12(
3395
+ (separator) => {
3396
+ onAddSegment(createLiteralSegment(separator));
3397
+ },
3398
+ [onAddSegment]
3399
+ );
3400
+ const handleAddText = useCallback12(() => {
3401
+ onAddSegment(createLiteralSegment("text"));
3402
+ }, [onAddSegment]);
3403
+ return /* @__PURE__ */ jsxs16("div", { className: `space-y-2 ${className}`, children: [
3404
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center justify-between", children: [
3405
+ /* @__PURE__ */ jsx17("label", { className: "text-sm font-medium text-gray-700", children: label }),
3406
+ !readOnly && pattern.length > 0 && /* @__PURE__ */ jsx17(
3407
+ "button",
3408
+ {
3409
+ type: "button",
3410
+ onClick: onClear,
3411
+ className: "text-xs text-red-500 hover:text-red-700 transition-colors",
3412
+ children: "Clear"
3413
+ }
3414
+ )
3415
+ ] }),
3416
+ /* @__PURE__ */ jsx17(
3417
+ SortableContext,
3418
+ {
3419
+ items: pattern.map((seg) => seg.id),
3420
+ strategy: horizontalListSortingStrategy,
3421
+ children: /* @__PURE__ */ jsx17(
3422
+ DroppableZone,
3423
+ {
3424
+ id: droppableId,
3425
+ isEmpty: pattern.length === 0,
3426
+ placeholder,
3427
+ children: pattern.map((segment) => /* @__PURE__ */ jsx17(
3428
+ PatternSegmentItem,
3429
+ {
3430
+ segment,
3431
+ onUpdate: onUpdateSegment,
3432
+ onDelete: onRemoveSegment,
3433
+ readOnly
3434
+ },
3435
+ segment.id
3436
+ ))
3437
+ }
3438
+ )
3439
+ }
3440
+ ),
3441
+ !readOnly && /* @__PURE__ */ jsxs16("div", { className: "flex items-center justify-between", children: [
3442
+ /* @__PURE__ */ jsx17(SeparatorPicker, { onSelect: handleAddSeparator, disabled: readOnly }),
3443
+ /* @__PURE__ */ jsx17(
3444
+ "button",
3445
+ {
3446
+ type: "button",
3447
+ onClick: handleAddText,
3448
+ className: "text-xs text-blue-600 hover:text-blue-800 transition-colors",
3449
+ children: "+ Add text"
3450
+ }
3451
+ )
3452
+ ] })
3453
+ ] });
3454
+ }
3455
+
3456
+ // src/ui/components/naming/PatternPreview.tsx
3457
+ import { useMemo } from "react";
3458
+ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
3459
+ function PatternPreview({
3460
+ filePattern,
3461
+ folderPattern,
3462
+ userVariables,
3463
+ sampleFileName = "document.pdf",
3464
+ previewDate,
3465
+ counterValue = 1,
3466
+ className = ""
3467
+ }) {
3468
+ const date = previewDate || /* @__PURE__ */ new Date();
3469
+ const filePreview = useMemo(() => {
3470
+ if (filePattern.length === 0) {
3471
+ return { name: "(no pattern defined)", isEmpty: true };
3472
+ }
3473
+ const baseName = generatePreviewName(filePattern, userVariables, {
3474
+ date,
3475
+ counterValue,
3476
+ originalFileName: sampleFileName
3477
+ });
3478
+ const ext = sampleFileName.includes(".") ? sampleFileName.substring(sampleFileName.lastIndexOf(".")) : "";
3479
+ const hasExtension = baseName.includes(".");
3480
+ return {
3481
+ name: hasExtension ? baseName : baseName + ext,
3482
+ isEmpty: false
3483
+ };
3484
+ }, [filePattern, userVariables, date, counterValue, sampleFileName]);
3485
+ const folderPreview = useMemo(() => {
3486
+ if (folderPattern.length === 0) {
3487
+ return { name: "(no pattern defined)", isEmpty: true };
3488
+ }
3489
+ return {
3490
+ name: generatePreviewName(folderPattern, userVariables, {
3491
+ date,
3492
+ counterValue,
3493
+ originalFileName: sampleFileName
3494
+ }),
3495
+ isEmpty: false
3496
+ };
3497
+ }, [folderPattern, userVariables, date, counterValue, sampleFileName]);
3498
+ const dateDisplay = date.toLocaleDateString("en-US", {
3499
+ weekday: "short",
3500
+ year: "numeric",
3501
+ month: "short",
3502
+ day: "numeric"
3503
+ });
3504
+ return /* @__PURE__ */ jsxs17(
3505
+ "div",
3506
+ {
3507
+ className: `bg-gradient-to-br from-gray-50 to-gray-100 rounded-lg border border-gray-200 p-4 ${className}`,
3508
+ children: [
3509
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-center justify-between mb-3", children: [
3510
+ /* @__PURE__ */ jsx18("h3", { className: "text-sm font-medium text-gray-700", children: "Preview" }),
3511
+ /* @__PURE__ */ jsx18("span", { className: "text-xs text-gray-400", children: dateDisplay })
3512
+ ] }),
3513
+ /* @__PURE__ */ jsxs17("div", { className: "space-y-3", children: [
3514
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-start gap-3", children: [
3515
+ /* @__PURE__ */ jsx18("div", { className: "flex-shrink-0 w-16 text-xs text-gray-500 pt-0.5", children: "File:" }),
3516
+ /* @__PURE__ */ jsx18(
3517
+ "div",
3518
+ {
3519
+ className: `
3520
+ flex-1 font-mono text-sm px-3 py-2 rounded bg-white border
3521
+ ${filePreview.isEmpty ? "text-gray-400 border-gray-200" : "text-gray-800 border-gray-300"}
3522
+ `,
3523
+ children: /* @__PURE__ */ jsxs17("span", { className: "flex items-center gap-2", children: [
3524
+ /* @__PURE__ */ jsxs17(
3525
+ "svg",
3526
+ {
3527
+ xmlns: "http://www.w3.org/2000/svg",
3528
+ width: "14",
3529
+ height: "14",
3530
+ viewBox: "0 0 24 24",
3531
+ fill: "none",
3532
+ stroke: "currentColor",
3533
+ strokeWidth: "2",
3534
+ strokeLinecap: "round",
3535
+ strokeLinejoin: "round",
3536
+ className: "text-gray-400",
3537
+ children: [
3538
+ /* @__PURE__ */ jsx18("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
3539
+ /* @__PURE__ */ jsx18("polyline", { points: "14,2 14,8 20,8" })
3540
+ ]
3541
+ }
3542
+ ),
3543
+ filePreview.name
3544
+ ] })
3545
+ }
3546
+ )
3547
+ ] }),
3548
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-start gap-3", children: [
3549
+ /* @__PURE__ */ jsx18("div", { className: "flex-shrink-0 w-16 text-xs text-gray-500 pt-0.5", children: "Folder:" }),
3550
+ /* @__PURE__ */ jsx18(
3551
+ "div",
3552
+ {
3553
+ className: `
3554
+ flex-1 font-mono text-sm px-3 py-2 rounded bg-white border
3555
+ ${folderPreview.isEmpty ? "text-gray-400 border-gray-200" : "text-gray-800 border-gray-300"}
3556
+ `,
3557
+ children: /* @__PURE__ */ jsxs17("span", { className: "flex items-center gap-2", children: [
3558
+ /* @__PURE__ */ jsx18(
3559
+ "svg",
3560
+ {
3561
+ xmlns: "http://www.w3.org/2000/svg",
3562
+ width: "14",
3563
+ height: "14",
3564
+ viewBox: "0 0 24 24",
3565
+ fill: "none",
3566
+ stroke: "currentColor",
3567
+ strokeWidth: "2",
3568
+ strokeLinecap: "round",
3569
+ strokeLinejoin: "round",
3570
+ className: "text-yellow-500",
3571
+ children: /* @__PURE__ */ jsx18("path", { d: "M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" })
3572
+ }
3573
+ ),
3574
+ folderPreview.name
3575
+ ] })
3576
+ }
3577
+ )
3578
+ ] }),
3579
+ !filePreview.isEmpty && !folderPreview.isEmpty && /* @__PURE__ */ jsxs17("div", { className: "flex items-start gap-3 pt-2 border-t border-gray-200", children: [
3580
+ /* @__PURE__ */ jsx18("div", { className: "flex-shrink-0 w-16 text-xs text-gray-500 pt-0.5", children: "Full path:" }),
3581
+ /* @__PURE__ */ jsxs17("div", { className: "flex-1 font-mono text-xs text-gray-600 px-3 py-2 rounded bg-gray-100 border border-gray-200 overflow-x-auto", children: [
3582
+ "/",
3583
+ folderPreview.name,
3584
+ "/",
3585
+ filePreview.name
3586
+ ] })
3587
+ ] })
3588
+ ] }),
3589
+ /* @__PURE__ */ jsx18("p", { className: "text-xs text-gray-400 mt-3 text-center", children: "Preview uses example values from your variables" })
3590
+ ]
3591
+ }
3592
+ );
3593
+ }
3594
+
3595
+ // src/ui/components/naming/NamingRuleConfigurator.tsx
3596
+ import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
3597
+ function NamingRuleConfigurator({
3598
+ variables,
3599
+ initialSchema,
3600
+ onChange,
3601
+ onExport,
3602
+ onImport,
3603
+ className = "",
3604
+ customDateFormats,
3605
+ readOnly = false,
3606
+ sampleFileName = "document.pdf"
3607
+ }) {
3608
+ const fileInputRef = useRef5(null);
3609
+ const [activeVariable, setActiveVariable] = useState12(null);
3610
+ const sensors = useSensors(
3611
+ useSensor(PointerSensor, {
3612
+ activationConstraint: {
3613
+ distance: 8
3614
+ }
3615
+ })
3616
+ );
3617
+ const {
3618
+ filePattern,
3619
+ folderPattern,
3620
+ canUndo,
3621
+ canRedo,
3622
+ isDirty,
3623
+ addToFilePattern,
3624
+ removeFromFilePattern,
3625
+ updateFilePatternSegment,
3626
+ reorderFilePattern,
3627
+ clearFilePattern,
3628
+ addToFolderPattern,
3629
+ removeFromFolderPattern,
3630
+ updateFolderPatternSegment,
3631
+ reorderFolderPattern,
3632
+ clearFolderPattern,
3633
+ undo,
3634
+ redo,
3635
+ getSchema,
3636
+ loadSchema
3637
+ } = useNamingRule({
3638
+ initialSchema,
3639
+ onChange
3640
+ });
3641
+ const handleVariableClick = useCallback13(
3642
+ (variable) => {
3643
+ addToFilePattern(createVariableSegment(variable.variable_name));
3644
+ },
3645
+ [addToFilePattern]
3646
+ );
3647
+ const handleDragStart = useCallback13((event) => {
3648
+ const { active } = event;
3649
+ if (String(active.id).startsWith("draggable-")) {
3650
+ const data = active.data.current;
3651
+ if (data?.type === "variable" && data.variable) {
3652
+ setActiveVariable(data.variable);
3653
+ }
3654
+ }
3655
+ }, []);
3656
+ const handleDragEnd = useCallback13(
3657
+ (event) => {
3658
+ const { active, over } = event;
3659
+ setActiveVariable(null);
3660
+ if (!over) return;
3661
+ const activeId = String(active.id);
3662
+ const overId = String(over.id);
3663
+ if (activeId.startsWith("draggable-")) {
3664
+ const data = active.data.current;
3665
+ if (data?.type === "variable" && data.variable) {
3666
+ const segment = createVariableSegment(data.variable.variable_name);
3667
+ if (overId === "file-pattern-drop") {
3668
+ addToFilePattern(segment);
3669
+ } else if (overId === "folder-pattern-drop") {
3670
+ addToFolderPattern(segment);
3671
+ } else {
3672
+ const fileIndex = filePattern.findIndex((seg) => seg.id === overId);
3673
+ const folderIndex = folderPattern.findIndex((seg) => seg.id === overId);
3674
+ if (fileIndex >= 0) {
3675
+ addToFilePattern(segment, fileIndex + 1);
3676
+ } else if (folderIndex >= 0) {
3677
+ addToFolderPattern(segment, folderIndex + 1);
3678
+ }
3679
+ }
3680
+ }
3681
+ return;
3682
+ }
3683
+ if (activeId !== overId) {
3684
+ const activeFileIndex = filePattern.findIndex((seg) => seg.id === activeId);
3685
+ const overFileIndex = filePattern.findIndex((seg) => seg.id === overId);
3686
+ if (activeFileIndex >= 0 && overFileIndex >= 0) {
3687
+ reorderFilePattern(activeFileIndex, overFileIndex);
3688
+ return;
3689
+ }
3690
+ const activeFolderIndex = folderPattern.findIndex((seg) => seg.id === activeId);
3691
+ const overFolderIndex = folderPattern.findIndex((seg) => seg.id === overId);
3692
+ if (activeFolderIndex >= 0 && overFolderIndex >= 0) {
3693
+ reorderFolderPattern(activeFolderIndex, overFolderIndex);
3694
+ return;
3695
+ }
3696
+ }
3697
+ },
3698
+ [filePattern, folderPattern, addToFilePattern, addToFolderPattern, reorderFilePattern, reorderFolderPattern]
3699
+ );
3700
+ const handleExport = useCallback13(() => {
3701
+ const schema = getSchema();
3702
+ schema.metadata = {
3703
+ ...schema.metadata,
3704
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3705
+ };
3706
+ if (onExport) {
3707
+ onExport(schema);
3708
+ } else {
3709
+ const blob = new Blob([JSON.stringify(schema, null, 2)], {
3710
+ type: "application/json"
3711
+ });
3712
+ const url = URL.createObjectURL(blob);
3713
+ const a = document.createElement("a");
3714
+ a.href = url;
3715
+ a.download = "naming-rule.json";
3716
+ document.body.appendChild(a);
3717
+ a.click();
3718
+ document.body.removeChild(a);
3719
+ URL.revokeObjectURL(url);
3720
+ }
3721
+ }, [getSchema, onExport]);
3722
+ const handleImportClick = useCallback13(() => {
3723
+ fileInputRef.current?.click();
3724
+ }, []);
3725
+ const handleFileChange = useCallback13(
3726
+ async (e) => {
3727
+ const file = e.target.files?.[0];
3728
+ if (!file) return;
3729
+ try {
3730
+ const text = await file.text();
3731
+ const schema = JSON.parse(text);
3732
+ if (validateNamingRuleSchema(schema)) {
3733
+ loadSchema(schema);
3734
+ onImport?.(schema);
3735
+ } else {
3736
+ console.error("Invalid naming rule schema");
3737
+ alert("Invalid naming rule schema. Please check the file format.");
3738
+ }
3739
+ } catch (err) {
3740
+ console.error("Failed to parse JSON:", err);
3741
+ alert("Failed to parse JSON file. Please ensure it is valid JSON.");
3742
+ }
3743
+ e.target.value = "";
3744
+ },
3745
+ [loadSchema, onImport]
3746
+ );
3747
+ return /* @__PURE__ */ jsxs18(
3748
+ DndContext,
3749
+ {
3750
+ sensors,
3751
+ collisionDetection: closestCenter,
3752
+ onDragStart: handleDragStart,
3753
+ onDragEnd: handleDragEnd,
3754
+ children: [
3755
+ /* @__PURE__ */ jsxs18(
3756
+ "div",
3757
+ {
3758
+ className: `flex flex-col bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden h-full ${className}`,
3759
+ children: [
3760
+ /* @__PURE__ */ jsxs18("div", { className: "flex-1 overflow-y-auto min-h-0", children: [
3761
+ /* @__PURE__ */ jsxs18("div", { className: "p-4 bg-gray-50 border-b border-gray-200", children: [
3762
+ /* @__PURE__ */ jsx19("h3", { className: "text-sm font-semibold text-gray-800 mb-3", children: "Available Variables" }),
3763
+ /* @__PURE__ */ jsx19(
3764
+ VariableList,
3765
+ {
3766
+ userVariables: variables,
3767
+ customDateFormats,
3768
+ onVariableClick: handleVariableClick,
3769
+ disabled: readOnly
3770
+ }
3771
+ )
3772
+ ] }),
3773
+ /* @__PURE__ */ jsxs18("div", { className: "p-4 space-y-6", children: [
3774
+ /* @__PURE__ */ jsx19(
3775
+ PatternBuilder,
3776
+ {
3777
+ pattern: filePattern,
3778
+ label: "File Name Pattern",
3779
+ droppableId: "file-pattern-drop",
3780
+ onPatternChange: () => {
3781
+ },
3782
+ onAddSegment: addToFilePattern,
3783
+ onRemoveSegment: removeFromFilePattern,
3784
+ onUpdateSegment: updateFilePatternSegment,
3785
+ onReorderSegments: reorderFilePattern,
3786
+ onClear: clearFilePattern,
3787
+ readOnly,
3788
+ placeholder: "Drop variables here or click to add..."
3789
+ }
3790
+ ),
3791
+ /* @__PURE__ */ jsx19(
3792
+ PatternBuilder,
3793
+ {
3794
+ pattern: folderPattern,
3795
+ label: "Folder Name Pattern",
3796
+ droppableId: "folder-pattern-drop",
3797
+ onPatternChange: () => {
3798
+ },
3799
+ onAddSegment: addToFolderPattern,
3800
+ onRemoveSegment: removeFromFolderPattern,
3801
+ onUpdateSegment: updateFolderPatternSegment,
3802
+ onReorderSegments: reorderFolderPattern,
3803
+ onClear: clearFolderPattern,
3804
+ readOnly,
3805
+ placeholder: "Drop variables here for folder structure..."
3806
+ }
3807
+ )
3808
+ ] }),
3809
+ /* @__PURE__ */ jsx19("div", { className: "p-4 border-t border-gray-200", children: /* @__PURE__ */ jsx19(
3810
+ PatternPreview,
3811
+ {
3812
+ filePattern,
3813
+ folderPattern,
3814
+ userVariables: variables,
3815
+ sampleFileName
3816
+ }
3817
+ ) })
3818
+ ] }),
3819
+ /* @__PURE__ */ jsxs18("div", { className: "flex items-center justify-between px-4 py-3 bg-gray-50 border-t border-gray-200", children: [
3820
+ /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", children: [
3821
+ /* @__PURE__ */ jsx19(
3822
+ "button",
3823
+ {
3824
+ type: "button",
3825
+ onClick: undo,
3826
+ disabled: !canUndo || readOnly,
3827
+ className: `
3828
+ p-2 rounded hover:bg-gray-200 transition-colors
3829
+ ${canUndo && !readOnly ? "text-gray-600" : "text-gray-300 cursor-not-allowed"}
3830
+ `,
3831
+ title: "Undo (Ctrl+Z)",
3832
+ children: /* @__PURE__ */ jsxs18(
3833
+ "svg",
3834
+ {
3835
+ xmlns: "http://www.w3.org/2000/svg",
3836
+ width: "16",
3837
+ height: "16",
3838
+ viewBox: "0 0 24 24",
3839
+ fill: "none",
3840
+ stroke: "currentColor",
3841
+ strokeWidth: "2",
3842
+ strokeLinecap: "round",
3843
+ strokeLinejoin: "round",
3844
+ children: [
3845
+ /* @__PURE__ */ jsx19("path", { d: "M3 7v6h6" }),
3846
+ /* @__PURE__ */ jsx19("path", { d: "M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13" })
3847
+ ]
3848
+ }
3849
+ )
3850
+ }
3851
+ ),
3852
+ /* @__PURE__ */ jsx19(
3853
+ "button",
3854
+ {
3855
+ type: "button",
3856
+ onClick: redo,
3857
+ disabled: !canRedo || readOnly,
3858
+ className: `
3859
+ p-2 rounded hover:bg-gray-200 transition-colors
3860
+ ${canRedo && !readOnly ? "text-gray-600" : "text-gray-300 cursor-not-allowed"}
3861
+ `,
3862
+ title: "Redo (Ctrl+Y)",
3863
+ children: /* @__PURE__ */ jsxs18(
3864
+ "svg",
3865
+ {
3866
+ xmlns: "http://www.w3.org/2000/svg",
3867
+ width: "16",
3868
+ height: "16",
3869
+ viewBox: "0 0 24 24",
3870
+ fill: "none",
3871
+ stroke: "currentColor",
3872
+ strokeWidth: "2",
3873
+ strokeLinecap: "round",
3874
+ strokeLinejoin: "round",
3875
+ children: [
3876
+ /* @__PURE__ */ jsx19("path", { d: "M21 7v6h-6" }),
3877
+ /* @__PURE__ */ jsx19("path", { d: "M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7" })
3878
+ ]
3879
+ }
3880
+ )
3881
+ }
3882
+ ),
3883
+ isDirty && /* @__PURE__ */ jsx19("span", { className: "text-xs text-amber-600 ml-2", children: "* Unsaved changes" })
3884
+ ] }),
3885
+ /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", children: [
3886
+ /* @__PURE__ */ jsx19(
3887
+ "input",
3888
+ {
3889
+ ref: fileInputRef,
3890
+ type: "file",
3891
+ accept: ".json",
3892
+ onChange: handleFileChange,
3893
+ className: "hidden"
3894
+ }
3895
+ ),
3896
+ /* @__PURE__ */ jsx19(
3897
+ "button",
3898
+ {
3899
+ type: "button",
3900
+ onClick: handleImportClick,
3901
+ disabled: readOnly,
3902
+ className: `
3903
+ px-3 py-1.5 text-sm rounded border transition-colors
3904
+ ${readOnly ? "bg-gray-100 text-gray-400 border-gray-200 cursor-not-allowed" : "bg-white text-gray-600 border-gray-300 hover:bg-gray-50"}
3905
+ `,
3906
+ children: "Import"
3907
+ }
3908
+ ),
3909
+ /* @__PURE__ */ jsx19(
3910
+ "button",
3911
+ {
3912
+ type: "button",
3913
+ onClick: handleExport,
3914
+ className: "px-3 py-1.5 text-sm rounded bg-blue-600 text-white hover:bg-blue-700 transition-colors",
3915
+ children: "Export"
3916
+ }
3917
+ )
3918
+ ] })
3919
+ ] })
3920
+ ]
3921
+ }
3922
+ ),
3923
+ /* @__PURE__ */ jsx19(DragOverlay, { children: activeVariable ? /* @__PURE__ */ jsx19(
3924
+ DraggableVariable,
3925
+ {
3926
+ variable: activeVariable,
3927
+ className: "shadow-xl opacity-90"
3928
+ }
3929
+ ) : null })
3930
+ ]
3931
+ }
3932
+ );
3933
+ }
3934
+ export {
3935
+ ArchiveIcon,
3936
+ AudioIcon,
3937
+ ChevronDownIcon,
3938
+ ChevronRightIcon,
3939
+ CodeIcon,
3940
+ CreateFolderDialog,
3941
+ DeleteConfirmDialog,
3942
+ DownloadIcon,
3943
+ DraggableVariable,
3944
+ FileActions,
3945
+ FileBrowser,
3946
+ FileBrowserProvider,
3947
+ FileIcon,
3948
+ FileList,
3949
+ FilePreview,
3950
+ FileTextIcon,
3951
+ FolderIcon,
3952
+ FolderOpenIcon,
3953
+ FolderTree,
3954
+ HomeIcon,
3955
+ ImageIcon,
3956
+ LoaderIcon,
3957
+ MoreVerticalIcon,
3958
+ NamingRuleConfigurator,
3959
+ PathBreadcrumb,
3960
+ PatternBuilder,
3961
+ PatternPreview,
3962
+ PatternSegmentItem,
3963
+ PdfIcon,
3964
+ PencilIcon,
3965
+ PlusIcon,
3966
+ RefreshIcon,
3967
+ RenameDialog,
3968
+ SeparatorPicker,
3969
+ TrashIcon,
3970
+ UploadDialog,
3971
+ UploadIcon,
3972
+ VariableList,
3973
+ VideoIcon,
3974
+ XIcon,
3975
+ getFileIcon,
3976
+ useFileBrowser,
3977
+ useFileBrowserContext,
3978
+ useFileOperations,
3979
+ useMultiFileOperations,
3980
+ useNamingRule
3981
+ };
3982
+ //# sourceMappingURL=index.mjs.map