@gallop.software/studio 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/StudioUI-5I7VRE4F.mjs +1839 -0
- package/dist/StudioUI-5I7VRE4F.mjs.map +1 -0
- package/dist/{StudioUI-P5VY2DPS.js → StudioUI-ELH3QOUT.js} +592 -276
- package/dist/StudioUI-ELH3QOUT.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/StudioUI-P5VY2DPS.js.map +0 -1
- package/dist/StudioUI-PW3ZGLXL.mjs +0 -1523
- package/dist/StudioUI-PW3ZGLXL.mjs.map +0 -1
|
@@ -22,10 +22,13 @@ var defaultState = {
|
|
|
22
22
|
selectedItems: /* @__PURE__ */ new Set(),
|
|
23
23
|
toggleSelection: () => {
|
|
24
24
|
},
|
|
25
|
+
selectRange: () => {
|
|
26
|
+
},
|
|
25
27
|
selectAll: () => {
|
|
26
28
|
},
|
|
27
29
|
clearSelection: () => {
|
|
28
30
|
},
|
|
31
|
+
lastSelectedPath: null,
|
|
29
32
|
viewMode: "grid",
|
|
30
33
|
setViewMode: () => {
|
|
31
34
|
},
|
|
@@ -47,8 +50,147 @@ function useStudio() {
|
|
|
47
50
|
// src/components/StudioToolbar.tsx
|
|
48
51
|
|
|
49
52
|
|
|
53
|
+
|
|
54
|
+
// src/components/StudioModal.tsx
|
|
55
|
+
|
|
50
56
|
var _jsxruntime = require('@emotion/react/jsx-runtime');
|
|
57
|
+
var fadeIn = _react3.keyframes`
|
|
58
|
+
from { opacity: 0; }
|
|
59
|
+
to { opacity: 1; }
|
|
60
|
+
`;
|
|
61
|
+
var slideIn = _react3.keyframes`
|
|
62
|
+
from {
|
|
63
|
+
opacity: 0;
|
|
64
|
+
transform: scale(0.95);
|
|
65
|
+
}
|
|
66
|
+
to {
|
|
67
|
+
opacity: 1;
|
|
68
|
+
transform: scale(1);
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
51
71
|
var styles = {
|
|
72
|
+
overlay: _react3.css`
|
|
73
|
+
position: fixed;
|
|
74
|
+
inset: 0;
|
|
75
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
justify-content: center;
|
|
79
|
+
z-index: 10000;
|
|
80
|
+
animation: ${fadeIn} 0.15s ease-out;
|
|
81
|
+
`,
|
|
82
|
+
modal: _react3.css`
|
|
83
|
+
background-color: white;
|
|
84
|
+
border-radius: 12px;
|
|
85
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
86
|
+
max-width: 400px;
|
|
87
|
+
width: 90%;
|
|
88
|
+
animation: ${slideIn} 0.15s ease-out;
|
|
89
|
+
`,
|
|
90
|
+
header: _react3.css`
|
|
91
|
+
padding: 20px 24px 0;
|
|
92
|
+
`,
|
|
93
|
+
title: _react3.css`
|
|
94
|
+
font-size: 18px;
|
|
95
|
+
font-weight: 600;
|
|
96
|
+
color: #111827;
|
|
97
|
+
margin: 0;
|
|
98
|
+
`,
|
|
99
|
+
body: _react3.css`
|
|
100
|
+
padding: 12px 24px 24px;
|
|
101
|
+
`,
|
|
102
|
+
message: _react3.css`
|
|
103
|
+
font-size: 14px;
|
|
104
|
+
color: #6b7280;
|
|
105
|
+
margin: 0;
|
|
106
|
+
line-height: 1.5;
|
|
107
|
+
`,
|
|
108
|
+
footer: _react3.css`
|
|
109
|
+
display: flex;
|
|
110
|
+
justify-content: flex-end;
|
|
111
|
+
gap: 12px;
|
|
112
|
+
padding: 16px 24px;
|
|
113
|
+
border-top: 1px solid #e5e7eb;
|
|
114
|
+
background-color: #f9fafb;
|
|
115
|
+
border-radius: 0 0 12px 12px;
|
|
116
|
+
`,
|
|
117
|
+
btn: _react3.css`
|
|
118
|
+
padding: 8px 16px;
|
|
119
|
+
font-size: 14px;
|
|
120
|
+
font-weight: 500;
|
|
121
|
+
border-radius: 8px;
|
|
122
|
+
cursor: pointer;
|
|
123
|
+
transition: all 0.15s;
|
|
124
|
+
`,
|
|
125
|
+
btnCancel: _react3.css`
|
|
126
|
+
background-color: white;
|
|
127
|
+
border: 1px solid #d1d5db;
|
|
128
|
+
color: #374151;
|
|
129
|
+
|
|
130
|
+
&:hover {
|
|
131
|
+
background-color: #f9fafb;
|
|
132
|
+
}
|
|
133
|
+
`,
|
|
134
|
+
btnConfirm: _react3.css`
|
|
135
|
+
background-color: #9333ea;
|
|
136
|
+
border: 1px solid #9333ea;
|
|
137
|
+
color: white;
|
|
138
|
+
|
|
139
|
+
&:hover {
|
|
140
|
+
background-color: #7c3aed;
|
|
141
|
+
}
|
|
142
|
+
`,
|
|
143
|
+
btnDanger: _react3.css`
|
|
144
|
+
background-color: #dc2626;
|
|
145
|
+
border: 1px solid #dc2626;
|
|
146
|
+
color: white;
|
|
147
|
+
|
|
148
|
+
&:hover {
|
|
149
|
+
background-color: #b91c1c;
|
|
150
|
+
}
|
|
151
|
+
`
|
|
152
|
+
};
|
|
153
|
+
function ConfirmModal({
|
|
154
|
+
title,
|
|
155
|
+
message,
|
|
156
|
+
confirmLabel = "Confirm",
|
|
157
|
+
cancelLabel = "Cancel",
|
|
158
|
+
variant = "default",
|
|
159
|
+
onConfirm,
|
|
160
|
+
onCancel
|
|
161
|
+
}) {
|
|
162
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.overlay, onClick: onCancel, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles.modal, onClick: (e) => e.stopPropagation(), children: [
|
|
163
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.header, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { css: styles.title, children: title }) }),
|
|
164
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.body, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles.message, children: message }) }),
|
|
165
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles.footer, children: [
|
|
166
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: [styles.btn, styles.btnCancel], onClick: onCancel, children: cancelLabel }),
|
|
167
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
168
|
+
"button",
|
|
169
|
+
{
|
|
170
|
+
css: [styles.btn, variant === "danger" ? styles.btnDanger : styles.btnConfirm],
|
|
171
|
+
onClick: onConfirm,
|
|
172
|
+
children: confirmLabel
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
] })
|
|
176
|
+
] }) });
|
|
177
|
+
}
|
|
178
|
+
function AlertModal({
|
|
179
|
+
title,
|
|
180
|
+
message,
|
|
181
|
+
buttonLabel = "OK",
|
|
182
|
+
onClose
|
|
183
|
+
}) {
|
|
184
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.overlay, onClick: onClose, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles.modal, onClick: (e) => e.stopPropagation(), children: [
|
|
185
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.header, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { css: styles.title, children: title }) }),
|
|
186
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.body, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles.message, children: message }) }),
|
|
187
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.footer, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: [styles.btn, styles.btnConfirm], onClick: onClose, children: buttonLabel }) })
|
|
188
|
+
] }) });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/components/StudioToolbar.tsx
|
|
192
|
+
|
|
193
|
+
var styles2 = {
|
|
52
194
|
toolbar: _react3.css`
|
|
53
195
|
display: flex;
|
|
54
196
|
align-items: center;
|
|
@@ -148,6 +290,8 @@ function StudioToolbar() {
|
|
|
148
290
|
const { selectedItems, viewMode, setViewMode, clearSelection, currentPath, triggerRefresh } = useStudio();
|
|
149
291
|
const fileInputRef = _react.useRef.call(void 0, null);
|
|
150
292
|
const [uploading, setUploading] = _react.useState.call(void 0, false);
|
|
293
|
+
const [showDeleteConfirm, setShowDeleteConfirm] = _react.useState.call(void 0, false);
|
|
294
|
+
const [alertMessage, setAlertMessage] = _react.useState.call(void 0, null);
|
|
151
295
|
const handleUpload = _react.useCallback.call(void 0, () => {
|
|
152
296
|
_optionalChain([fileInputRef, 'access', _ => _.current, 'optionalAccess', _2 => _2.click, 'call', _3 => _3()]);
|
|
153
297
|
}, []);
|
|
@@ -167,13 +311,19 @@ function StudioToolbar() {
|
|
|
167
311
|
if (!response.ok) {
|
|
168
312
|
const error = await response.json();
|
|
169
313
|
console.error("Upload failed:", error);
|
|
170
|
-
|
|
314
|
+
setAlertMessage({
|
|
315
|
+
title: "Upload Failed",
|
|
316
|
+
message: `Failed to upload ${file.name}: ${error.error || "Unknown error"}`
|
|
317
|
+
});
|
|
171
318
|
}
|
|
172
319
|
}
|
|
173
320
|
triggerRefresh();
|
|
174
321
|
} catch (error) {
|
|
175
322
|
console.error("Upload error:", error);
|
|
176
|
-
|
|
323
|
+
setAlertMessage({
|
|
324
|
+
title: "Upload Failed",
|
|
325
|
+
message: "Upload failed. Check console for details."
|
|
326
|
+
});
|
|
177
327
|
} finally {
|
|
178
328
|
setUploading(false);
|
|
179
329
|
if (fileInputRef.current) {
|
|
@@ -184,9 +334,12 @@ function StudioToolbar() {
|
|
|
184
334
|
const handleReprocess = _react.useCallback.call(void 0, () => {
|
|
185
335
|
console.log("Reprocess clicked", selectedItems);
|
|
186
336
|
}, [selectedItems]);
|
|
187
|
-
const
|
|
337
|
+
const handleDeleteClick = _react.useCallback.call(void 0, () => {
|
|
188
338
|
if (selectedItems.size === 0) return;
|
|
189
|
-
|
|
339
|
+
setShowDeleteConfirm(true);
|
|
340
|
+
}, [selectedItems]);
|
|
341
|
+
const handleDeleteConfirm = _react.useCallback.call(void 0, async () => {
|
|
342
|
+
setShowDeleteConfirm(false);
|
|
190
343
|
try {
|
|
191
344
|
const response = await fetch("/api/studio/delete", {
|
|
192
345
|
method: "POST",
|
|
@@ -198,11 +351,17 @@ function StudioToolbar() {
|
|
|
198
351
|
triggerRefresh();
|
|
199
352
|
} else {
|
|
200
353
|
const error = await response.json();
|
|
201
|
-
|
|
354
|
+
setAlertMessage({
|
|
355
|
+
title: "Delete Failed",
|
|
356
|
+
message: error.error || "Unknown error"
|
|
357
|
+
});
|
|
202
358
|
}
|
|
203
359
|
} catch (error) {
|
|
204
360
|
console.error("Delete error:", error);
|
|
205
|
-
|
|
361
|
+
setAlertMessage({
|
|
362
|
+
title: "Delete Failed",
|
|
363
|
+
message: "Delete failed. Check console for details."
|
|
364
|
+
});
|
|
206
365
|
}
|
|
207
366
|
}, [selectedItems, clearSelection, triggerRefresh]);
|
|
208
367
|
const handleSyncCdn = _react.useCallback.call(void 0, () => {
|
|
@@ -212,83 +371,104 @@ function StudioToolbar() {
|
|
|
212
371
|
console.log("Scan clicked");
|
|
213
372
|
}, []);
|
|
214
373
|
const hasSelection = selectedItems.size > 0;
|
|
215
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
216
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
217
|
-
|
|
374
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
375
|
+
showDeleteConfirm && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
376
|
+
ConfirmModal,
|
|
218
377
|
{
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
378
|
+
title: "Delete Items",
|
|
379
|
+
message: `Are you sure you want to delete ${selectedItems.size} item(s)? This action cannot be undone.`,
|
|
380
|
+
confirmLabel: "Delete",
|
|
381
|
+
variant: "danger",
|
|
382
|
+
onConfirm: handleDeleteConfirm,
|
|
383
|
+
onCancel: () => setShowDeleteConfirm(false)
|
|
225
384
|
}
|
|
226
385
|
),
|
|
227
|
-
/* @__PURE__ */ _jsxruntime.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
),
|
|
237
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
238
|
-
ToolbarButton,
|
|
239
|
-
{
|
|
240
|
-
onClick: handleReprocess,
|
|
241
|
-
icon: "refresh",
|
|
242
|
-
label: "Reprocess",
|
|
243
|
-
disabled: !hasSelection
|
|
244
|
-
}
|
|
245
|
-
),
|
|
246
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
247
|
-
ToolbarButton,
|
|
248
|
-
{
|
|
249
|
-
onClick: handleDelete,
|
|
250
|
-
icon: "trash",
|
|
251
|
-
label: "Delete",
|
|
252
|
-
disabled: !hasSelection,
|
|
253
|
-
variant: "danger"
|
|
254
|
-
}
|
|
255
|
-
),
|
|
386
|
+
alertMessage && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
387
|
+
AlertModal,
|
|
388
|
+
{
|
|
389
|
+
title: alertMessage.title,
|
|
390
|
+
message: alertMessage.message,
|
|
391
|
+
onClose: () => setAlertMessage(null)
|
|
392
|
+
}
|
|
393
|
+
),
|
|
394
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles2.toolbar, children: [
|
|
256
395
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
257
|
-
|
|
396
|
+
"input",
|
|
258
397
|
{
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
398
|
+
ref: fileInputRef,
|
|
399
|
+
type: "file",
|
|
400
|
+
multiple: true,
|
|
401
|
+
accept: "image/*",
|
|
402
|
+
onChange: handleFileChange,
|
|
403
|
+
style: { display: "none" }
|
|
263
404
|
}
|
|
264
405
|
),
|
|
265
|
-
/* @__PURE__ */ _jsxruntime.
|
|
266
|
-
] }),
|
|
267
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles.right, children: [
|
|
268
|
-
hasSelection && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { css: styles.selectionCount, children: [
|
|
269
|
-
selectedItems.size,
|
|
270
|
-
" selected",
|
|
271
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: styles.clearBtn, onClick: clearSelection, children: "Clear" })
|
|
272
|
-
] }),
|
|
273
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles.viewToggle, children: [
|
|
406
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles2.left, children: [
|
|
274
407
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
275
|
-
|
|
408
|
+
ToolbarButton,
|
|
276
409
|
{
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
410
|
+
onClick: handleUpload,
|
|
411
|
+
icon: "upload",
|
|
412
|
+
label: uploading ? "Uploading..." : "Upload",
|
|
413
|
+
disabled: uploading
|
|
281
414
|
}
|
|
282
415
|
),
|
|
283
416
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
284
|
-
|
|
417
|
+
ToolbarButton,
|
|
285
418
|
{
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
419
|
+
onClick: handleReprocess,
|
|
420
|
+
icon: "refresh",
|
|
421
|
+
label: "Reprocess",
|
|
422
|
+
disabled: !hasSelection
|
|
290
423
|
}
|
|
291
|
-
)
|
|
424
|
+
),
|
|
425
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
426
|
+
ToolbarButton,
|
|
427
|
+
{
|
|
428
|
+
onClick: handleDeleteClick,
|
|
429
|
+
icon: "trash",
|
|
430
|
+
label: "Delete",
|
|
431
|
+
disabled: !hasSelection,
|
|
432
|
+
variant: "danger"
|
|
433
|
+
}
|
|
434
|
+
),
|
|
435
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
436
|
+
ToolbarButton,
|
|
437
|
+
{
|
|
438
|
+
onClick: handleSyncCdn,
|
|
439
|
+
icon: "cloud",
|
|
440
|
+
label: "Sync CDN",
|
|
441
|
+
disabled: !hasSelection
|
|
442
|
+
}
|
|
443
|
+
),
|
|
444
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, ToolbarButton, { onClick: handleScan, icon: "scan", label: "Scan" })
|
|
445
|
+
] }),
|
|
446
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles2.right, children: [
|
|
447
|
+
hasSelection && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { css: styles2.selectionCount, children: [
|
|
448
|
+
selectedItems.size,
|
|
449
|
+
" selected",
|
|
450
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: styles2.clearBtn, onClick: clearSelection, children: "Clear" })
|
|
451
|
+
] }),
|
|
452
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles2.viewToggle, children: [
|
|
453
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
454
|
+
"button",
|
|
455
|
+
{
|
|
456
|
+
css: [styles2.viewBtn, viewMode === "grid" && styles2.viewBtnActive],
|
|
457
|
+
onClick: () => setViewMode("grid"),
|
|
458
|
+
"aria-label": "Grid view",
|
|
459
|
+
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, GridIcon, {})
|
|
460
|
+
}
|
|
461
|
+
),
|
|
462
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
463
|
+
"button",
|
|
464
|
+
{
|
|
465
|
+
css: [styles2.viewBtn, viewMode === "list" && styles2.viewBtnActive],
|
|
466
|
+
onClick: () => setViewMode("list"),
|
|
467
|
+
"aria-label": "List view",
|
|
468
|
+
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ListIcon, {})
|
|
469
|
+
}
|
|
470
|
+
)
|
|
471
|
+
] })
|
|
292
472
|
] })
|
|
293
473
|
] })
|
|
294
474
|
] });
|
|
@@ -303,7 +483,7 @@ function ToolbarButton({
|
|
|
303
483
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
304
484
|
"button",
|
|
305
485
|
{
|
|
306
|
-
css: [
|
|
486
|
+
css: [styles2.btn, variant === "danger" ? styles2.btnDanger : styles2.btnDefault],
|
|
307
487
|
onClick,
|
|
308
488
|
disabled,
|
|
309
489
|
children: [
|
|
@@ -316,30 +496,30 @@ function ToolbarButton({
|
|
|
316
496
|
function IconComponent({ icon }) {
|
|
317
497
|
switch (icon) {
|
|
318
498
|
case "upload":
|
|
319
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css:
|
|
499
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" }) });
|
|
320
500
|
case "refresh":
|
|
321
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css:
|
|
501
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" }) });
|
|
322
502
|
case "trash":
|
|
323
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css:
|
|
503
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) });
|
|
324
504
|
case "cloud":
|
|
325
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css:
|
|
505
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" }) });
|
|
326
506
|
case "scan":
|
|
327
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css:
|
|
507
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) });
|
|
328
508
|
default:
|
|
329
509
|
return null;
|
|
330
510
|
}
|
|
331
511
|
}
|
|
332
512
|
function GridIcon() {
|
|
333
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css:
|
|
513
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" }) });
|
|
334
514
|
}
|
|
335
515
|
function ListIcon() {
|
|
336
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css:
|
|
516
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 10h16M4 14h16M4 18h16" }) });
|
|
337
517
|
}
|
|
338
518
|
|
|
339
519
|
// src/components/StudioBreadcrumb.tsx
|
|
340
520
|
|
|
341
521
|
|
|
342
|
-
var
|
|
522
|
+
var styles3 = {
|
|
343
523
|
container: _react3.css`
|
|
344
524
|
display: flex;
|
|
345
525
|
align-items: center;
|
|
@@ -410,14 +590,14 @@ function StudioBreadcrumb() {
|
|
|
410
590
|
const newPath = parts.slice(0, index + 1).join("/");
|
|
411
591
|
setCurrentPath(newPath);
|
|
412
592
|
};
|
|
413
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
414
|
-
currentPath !== "public" && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css:
|
|
415
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "nav", { css:
|
|
416
|
-
index > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css:
|
|
593
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles3.container, children: [
|
|
594
|
+
currentPath !== "public" && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: styles3.backBtn, onClick: navigateUp, "aria-label": "Go back", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles3.backIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }),
|
|
595
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "nav", { css: styles3.nav, children: parts.map((part, index) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { css: styles3.item, children: [
|
|
596
|
+
index > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles3.separator, children: "/" }),
|
|
417
597
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
418
598
|
"button",
|
|
419
599
|
{
|
|
420
|
-
css: [
|
|
600
|
+
css: [styles3.btn, index === parts.length - 1 ? styles3.btnActive : styles3.btnInactive],
|
|
421
601
|
onClick: () => handleClick(index),
|
|
422
602
|
children: part
|
|
423
603
|
}
|
|
@@ -433,7 +613,7 @@ function StudioBreadcrumb() {
|
|
|
433
613
|
var spin = _react3.keyframes`
|
|
434
614
|
to { transform: rotate(360deg); }
|
|
435
615
|
`;
|
|
436
|
-
var
|
|
616
|
+
var styles4 = {
|
|
437
617
|
loading: _react3.css`
|
|
438
618
|
display: flex;
|
|
439
619
|
align-items: center;
|
|
@@ -547,10 +727,34 @@ var styles3 = {
|
|
|
547
727
|
font-size: 12px;
|
|
548
728
|
color: #9ca3af;
|
|
549
729
|
margin: 0;
|
|
730
|
+
`,
|
|
731
|
+
selectAllRow: _react3.css`
|
|
732
|
+
display: flex;
|
|
733
|
+
align-items: center;
|
|
734
|
+
margin-bottom: 12px;
|
|
735
|
+
padding-bottom: 12px;
|
|
736
|
+
border-bottom: 1px solid #e5e7eb;
|
|
737
|
+
`,
|
|
738
|
+
selectAllLabel: _react3.css`
|
|
739
|
+
display: flex;
|
|
740
|
+
align-items: center;
|
|
741
|
+
gap: 8px;
|
|
742
|
+
font-size: 14px;
|
|
743
|
+
color: #6b7280;
|
|
744
|
+
cursor: pointer;
|
|
745
|
+
|
|
746
|
+
&:hover {
|
|
747
|
+
color: #374151;
|
|
748
|
+
}
|
|
749
|
+
`,
|
|
750
|
+
selectAllCheckbox: _react3.css`
|
|
751
|
+
width: 16px;
|
|
752
|
+
height: 16px;
|
|
753
|
+
accent-color: #9333ea;
|
|
550
754
|
`
|
|
551
755
|
};
|
|
552
756
|
function StudioFileGrid() {
|
|
553
|
-
const { currentPath, setCurrentPath, selectedItems, toggleSelection, refreshKey } = useStudio();
|
|
757
|
+
const { currentPath, setCurrentPath, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey } = useStudio();
|
|
554
758
|
const [items, setItems] = _react.useState.call(void 0, []);
|
|
555
759
|
const [loading, setLoading] = _react.useState.call(void 0, true);
|
|
556
760
|
_react.useEffect.call(void 0, () => {
|
|
@@ -570,13 +774,13 @@ function StudioFileGrid() {
|
|
|
570
774
|
loadItems();
|
|
571
775
|
}, [currentPath, refreshKey]);
|
|
572
776
|
if (loading) {
|
|
573
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css:
|
|
777
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles4.loading, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles4.spinner }) });
|
|
574
778
|
}
|
|
575
779
|
if (items.length === 0) {
|
|
576
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
577
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css:
|
|
578
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css:
|
|
579
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css:
|
|
780
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles4.empty, children: [
|
|
781
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles4.emptyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
|
|
782
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles4.emptyText, children: "No files in this folder" }),
|
|
783
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles4.emptyText, children: "Upload images to get started" })
|
|
580
784
|
] });
|
|
581
785
|
}
|
|
582
786
|
const sortedItems = [...items].sort((a, b) => {
|
|
@@ -584,54 +788,83 @@ function StudioFileGrid() {
|
|
|
584
788
|
if (a.type !== "folder" && b.type === "folder") return 1;
|
|
585
789
|
return a.name.localeCompare(b.name);
|
|
586
790
|
});
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
const handleClick = () => {
|
|
605
|
-
if (isFolder) {
|
|
606
|
-
onOpen();
|
|
791
|
+
const files = sortedItems.filter((item) => item.type !== "folder");
|
|
792
|
+
const allFilesSelected = files.length > 0 && files.every((item) => selectedItems.has(item.path));
|
|
793
|
+
const someFilesSelected = files.some((item) => selectedItems.has(item.path));
|
|
794
|
+
const handleSelectAll = () => {
|
|
795
|
+
if (allFilesSelected) {
|
|
796
|
+
clearSelection();
|
|
797
|
+
} else {
|
|
798
|
+
selectAll(files);
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
const handleItemClick = (item, e) => {
|
|
802
|
+
if (item.type === "folder") {
|
|
803
|
+
setCurrentPath(item.path);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
if (e.shiftKey && lastSelectedPath) {
|
|
807
|
+
selectRange(lastSelectedPath, item.path, sortedItems);
|
|
607
808
|
} else {
|
|
608
|
-
|
|
809
|
+
toggleSelection(item.path);
|
|
609
810
|
}
|
|
610
811
|
};
|
|
611
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", {
|
|
812
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { children: [
|
|
813
|
+
files.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles4.selectAllRow, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "label", { css: styles4.selectAllLabel, children: [
|
|
814
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
815
|
+
"input",
|
|
816
|
+
{
|
|
817
|
+
type: "checkbox",
|
|
818
|
+
css: styles4.selectAllCheckbox,
|
|
819
|
+
checked: allFilesSelected,
|
|
820
|
+
ref: (el) => {
|
|
821
|
+
if (el) el.indeterminate = someFilesSelected && !allFilesSelected;
|
|
822
|
+
},
|
|
823
|
+
onChange: handleSelectAll
|
|
824
|
+
}
|
|
825
|
+
),
|
|
826
|
+
"Select all (",
|
|
827
|
+
files.length,
|
|
828
|
+
")"
|
|
829
|
+
] }) }),
|
|
830
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles4.grid, children: sortedItems.map((item) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
831
|
+
GridItem,
|
|
832
|
+
{
|
|
833
|
+
item,
|
|
834
|
+
isSelected: selectedItems.has(item.path),
|
|
835
|
+
onClick: (e) => handleItemClick(item, e)
|
|
836
|
+
},
|
|
837
|
+
item.path
|
|
838
|
+
)) })
|
|
839
|
+
] });
|
|
840
|
+
}
|
|
841
|
+
function GridItem({ item, isSelected, onClick }) {
|
|
842
|
+
const isFolder = item.type === "folder";
|
|
843
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: [styles4.item, isSelected && styles4.itemSelected], onClick, children: [
|
|
612
844
|
!isFolder && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
613
845
|
"input",
|
|
614
846
|
{
|
|
615
847
|
type: "checkbox",
|
|
616
|
-
css:
|
|
848
|
+
css: styles4.checkbox,
|
|
617
849
|
checked: isSelected,
|
|
618
|
-
onChange:
|
|
850
|
+
onChange: () => {
|
|
851
|
+
},
|
|
619
852
|
onClick: (e) => e.stopPropagation()
|
|
620
853
|
}
|
|
621
854
|
),
|
|
622
|
-
item.cdnSynced && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css:
|
|
623
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css:
|
|
855
|
+
item.cdnSynced && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles4.cdnBadge, children: "CDN" }),
|
|
856
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles4.content, children: isFolder ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles4.folderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M10 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" }) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
624
857
|
"img",
|
|
625
858
|
{
|
|
626
|
-
css:
|
|
859
|
+
css: styles4.image,
|
|
627
860
|
src: item.path.replace("public", ""),
|
|
628
861
|
alt: item.name,
|
|
629
862
|
loading: "lazy"
|
|
630
863
|
}
|
|
631
864
|
) }),
|
|
632
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
633
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css:
|
|
634
|
-
item.size && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css:
|
|
865
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles4.label, children: [
|
|
866
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles4.name, title: item.name, children: item.name }),
|
|
867
|
+
item.size && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles4.size, children: formatFileSize(item.size) })
|
|
635
868
|
] })
|
|
636
869
|
] });
|
|
637
870
|
}
|
|
@@ -648,7 +881,7 @@ function formatFileSize(bytes) {
|
|
|
648
881
|
var spin2 = _react3.keyframes`
|
|
649
882
|
to { transform: rotate(360deg); }
|
|
650
883
|
`;
|
|
651
|
-
var
|
|
884
|
+
var styles5 = {
|
|
652
885
|
loading: _react3.css`
|
|
653
886
|
display: flex;
|
|
654
887
|
align-items: center;
|
|
@@ -759,7 +992,7 @@ var styles4 = {
|
|
|
759
992
|
`
|
|
760
993
|
};
|
|
761
994
|
function StudioFileList() {
|
|
762
|
-
const { currentPath, setCurrentPath, selectedItems, toggleSelection, refreshKey } = useStudio();
|
|
995
|
+
const { currentPath, setCurrentPath, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey } = useStudio();
|
|
763
996
|
const [items, setItems] = _react.useState.call(void 0, []);
|
|
764
997
|
const [loading, setLoading] = _react.useState.call(void 0, true);
|
|
765
998
|
_react.useEffect.call(void 0, () => {
|
|
@@ -779,70 +1012,91 @@ function StudioFileList() {
|
|
|
779
1012
|
loadItems();
|
|
780
1013
|
}, [currentPath, refreshKey]);
|
|
781
1014
|
if (loading) {
|
|
782
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css:
|
|
1015
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles5.loading, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles5.spinner }) });
|
|
783
1016
|
}
|
|
784
1017
|
if (items.length === 0) {
|
|
785
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css:
|
|
1018
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles5.empty, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { children: "No files in this folder" }) });
|
|
786
1019
|
}
|
|
787
1020
|
const sortedItems = [...items].sort((a, b) => {
|
|
788
1021
|
if (a.type === "folder" && b.type !== "folder") return -1;
|
|
789
1022
|
if (a.type !== "folder" && b.type === "folder") return 1;
|
|
790
1023
|
return a.name.localeCompare(b.name);
|
|
791
1024
|
});
|
|
792
|
-
|
|
1025
|
+
const files = sortedItems.filter((item) => item.type !== "folder");
|
|
1026
|
+
const allFilesSelected = files.length > 0 && files.every((item) => selectedItems.has(item.path));
|
|
1027
|
+
const someFilesSelected = files.some((item) => selectedItems.has(item.path));
|
|
1028
|
+
const handleSelectAll = () => {
|
|
1029
|
+
if (allFilesSelected) {
|
|
1030
|
+
clearSelection();
|
|
1031
|
+
} else {
|
|
1032
|
+
selectAll(files);
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
const handleItemClick = (item, e) => {
|
|
1036
|
+
if (item.type === "folder") {
|
|
1037
|
+
setCurrentPath(item.path);
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
if (e.shiftKey && lastSelectedPath) {
|
|
1041
|
+
selectRange(lastSelectedPath, item.path, sortedItems);
|
|
1042
|
+
} else {
|
|
1043
|
+
toggleSelection(item.path);
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "table", { css: styles5.table, children: [
|
|
793
1047
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "thead", { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "tr", { children: [
|
|
794
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
1048
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [styles5.th, styles5.thCheckbox], children: files.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1049
|
+
"input",
|
|
1050
|
+
{
|
|
1051
|
+
type: "checkbox",
|
|
1052
|
+
css: styles5.checkbox,
|
|
1053
|
+
checked: allFilesSelected,
|
|
1054
|
+
ref: (el) => {
|
|
1055
|
+
if (el) el.indeterminate = someFilesSelected && !allFilesSelected;
|
|
1056
|
+
},
|
|
1057
|
+
onChange: handleSelectAll
|
|
1058
|
+
}
|
|
1059
|
+
) }),
|
|
1060
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: styles5.th, children: "Name" }),
|
|
1061
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [styles5.th, styles5.thSize], children: "Size" }),
|
|
1062
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [styles5.th, styles5.thDimensions], children: "Dimensions" }),
|
|
1063
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [styles5.th, styles5.thCdn], children: "CDN" })
|
|
799
1064
|
] }) }),
|
|
800
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "tbody", { css:
|
|
1065
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "tbody", { css: styles5.tbody, children: sortedItems.map((item) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
801
1066
|
ListRow,
|
|
802
1067
|
{
|
|
803
1068
|
item,
|
|
804
1069
|
isSelected: selectedItems.has(item.path),
|
|
805
|
-
|
|
806
|
-
onOpen: () => {
|
|
807
|
-
if (item.type === "folder") {
|
|
808
|
-
setCurrentPath(item.path);
|
|
809
|
-
}
|
|
810
|
-
}
|
|
1070
|
+
onClick: (e) => handleItemClick(item, e)
|
|
811
1071
|
},
|
|
812
1072
|
item.path
|
|
813
1073
|
)) })
|
|
814
1074
|
] });
|
|
815
1075
|
}
|
|
816
|
-
function ListRow({ item, isSelected,
|
|
1076
|
+
function ListRow({ item, isSelected, onClick }) {
|
|
817
1077
|
const isFolder = item.type === "folder";
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
onOpen();
|
|
821
|
-
} else {
|
|
822
|
-
onSelect();
|
|
823
|
-
}
|
|
824
|
-
};
|
|
825
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "tr", { css: [styles4.row, isSelected && styles4.rowSelected], onClick: handleClick, children: [
|
|
826
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles4.td, children: !isFolder && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1078
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "tr", { css: [styles5.row, isSelected && styles5.rowSelected], onClick, children: [
|
|
1079
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles5.td, children: !isFolder && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
827
1080
|
"input",
|
|
828
1081
|
{
|
|
829
1082
|
type: "checkbox",
|
|
830
|
-
css:
|
|
1083
|
+
css: styles5.checkbox,
|
|
831
1084
|
checked: isSelected,
|
|
832
|
-
onChange:
|
|
1085
|
+
onChange: () => {
|
|
1086
|
+
},
|
|
833
1087
|
onClick: (e) => e.stopPropagation()
|
|
834
1088
|
}
|
|
835
1089
|
) }),
|
|
836
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css:
|
|
837
|
-
isFolder ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css:
|
|
838
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css:
|
|
1090
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles5.td, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.nameCell, children: [
|
|
1091
|
+
isFolder ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles5.folderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M10 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" }) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles5.fileIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
|
|
1092
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles5.name, children: item.name })
|
|
839
1093
|
] }) }),
|
|
840
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: [
|
|
841
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: [
|
|
842
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css:
|
|
843
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css:
|
|
1094
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: [styles5.td, styles5.meta], children: item.size ? formatFileSize2(item.size) : "--" }),
|
|
1095
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: [styles5.td, styles5.meta], children: item.dimensions ? `${item.dimensions.width}x${item.dimensions.height}` : "--" }),
|
|
1096
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles5.td, children: item.cdnSynced ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { css: styles5.cdnBadge, children: [
|
|
1097
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles5.cdnIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }),
|
|
844
1098
|
"Synced"
|
|
845
|
-
] }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css:
|
|
1099
|
+
] }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles5.cdnEmpty, children: "--" }) })
|
|
846
1100
|
] });
|
|
847
1101
|
}
|
|
848
1102
|
function formatFileSize2(bytes) {
|
|
@@ -854,7 +1108,8 @@ function formatFileSize2(bytes) {
|
|
|
854
1108
|
// src/components/StudioPreview.tsx
|
|
855
1109
|
|
|
856
1110
|
|
|
857
|
-
|
|
1111
|
+
|
|
1112
|
+
var styles6 = {
|
|
858
1113
|
panel: _react3.css`
|
|
859
1114
|
width: 320px;
|
|
860
1115
|
border-left: 1px solid #e5e7eb;
|
|
@@ -985,9 +1240,14 @@ var styles5 = {
|
|
|
985
1240
|
};
|
|
986
1241
|
function StudioPreview() {
|
|
987
1242
|
const { selectedItems, meta, triggerRefresh, clearSelection } = useStudio();
|
|
988
|
-
const
|
|
1243
|
+
const [showDeleteConfirm, setShowDeleteConfirm] = _react.useState.call(void 0, false);
|
|
1244
|
+
const [alertMessage, setAlertMessage] = _react.useState.call(void 0, null);
|
|
1245
|
+
const handleDeleteClick = () => {
|
|
989
1246
|
if (selectedItems.size === 0) return;
|
|
990
|
-
|
|
1247
|
+
setShowDeleteConfirm(true);
|
|
1248
|
+
};
|
|
1249
|
+
const handleDeleteConfirm = async () => {
|
|
1250
|
+
setShowDeleteConfirm(false);
|
|
991
1251
|
try {
|
|
992
1252
|
const response = await fetch("/api/studio/delete", {
|
|
993
1253
|
method: "POST",
|
|
@@ -999,106 +1259,142 @@ function StudioPreview() {
|
|
|
999
1259
|
triggerRefresh();
|
|
1000
1260
|
} else {
|
|
1001
1261
|
const error = await response.json();
|
|
1002
|
-
|
|
1262
|
+
setAlertMessage({
|
|
1263
|
+
title: "Delete Failed",
|
|
1264
|
+
message: error.error || "Unknown error"
|
|
1265
|
+
});
|
|
1003
1266
|
}
|
|
1004
1267
|
} catch (error) {
|
|
1005
1268
|
console.error("Delete error:", error);
|
|
1006
|
-
|
|
1269
|
+
setAlertMessage({
|
|
1270
|
+
title: "Delete Failed",
|
|
1271
|
+
message: "Delete failed. Check console for details."
|
|
1272
|
+
});
|
|
1007
1273
|
}
|
|
1008
1274
|
};
|
|
1275
|
+
const modals = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1276
|
+
showDeleteConfirm && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1277
|
+
ConfirmModal,
|
|
1278
|
+
{
|
|
1279
|
+
title: "Delete Items",
|
|
1280
|
+
message: `Are you sure you want to delete ${selectedItems.size} item(s)? This action cannot be undone.`,
|
|
1281
|
+
confirmLabel: "Delete",
|
|
1282
|
+
variant: "danger",
|
|
1283
|
+
onConfirm: handleDeleteConfirm,
|
|
1284
|
+
onCancel: () => setShowDeleteConfirm(false)
|
|
1285
|
+
}
|
|
1286
|
+
),
|
|
1287
|
+
alertMessage && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1288
|
+
AlertModal,
|
|
1289
|
+
{
|
|
1290
|
+
title: alertMessage.title,
|
|
1291
|
+
message: alertMessage.message,
|
|
1292
|
+
onClose: () => setAlertMessage(null)
|
|
1293
|
+
}
|
|
1294
|
+
)
|
|
1295
|
+
] });
|
|
1009
1296
|
if (selectedItems.size === 0) {
|
|
1010
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1011
|
-
|
|
1012
|
-
/* @__PURE__ */ _jsxruntime.
|
|
1297
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1298
|
+
modals,
|
|
1299
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.panel, children: [
|
|
1300
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { css: styles6.title, children: "Preview" }),
|
|
1301
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles6.emptyState, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles6.emptyText, children: "Select an image to preview" }) })
|
|
1302
|
+
] })
|
|
1013
1303
|
] });
|
|
1014
1304
|
}
|
|
1015
1305
|
if (selectedItems.size > 1) {
|
|
1016
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
"
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1306
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1307
|
+
modals,
|
|
1308
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.panel, children: [
|
|
1309
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "h3", { css: styles6.title, children: [
|
|
1310
|
+
selectedItems.size,
|
|
1311
|
+
" items selected"
|
|
1312
|
+
] }),
|
|
1313
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles6.actions, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { css: [styles6.actionBtn, styles6.actionBtnDanger], onClick: handleDeleteClick, children: [
|
|
1314
|
+
"Delete ",
|
|
1315
|
+
selectedItems.size,
|
|
1316
|
+
" items"
|
|
1317
|
+
] }) })
|
|
1318
|
+
] })
|
|
1026
1319
|
] });
|
|
1027
1320
|
}
|
|
1028
1321
|
const selectedPath = Array.from(selectedItems)[0];
|
|
1029
1322
|
const imageKey = selectedPath.replace(/^public\/images\//, "").replace(/^public\/originals\//, "");
|
|
1030
1323
|
const imageData = _optionalChain([meta, 'optionalAccess', _4 => _4.images, 'optionalAccess', _5 => _5[imageKey]]);
|
|
1031
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1032
|
-
|
|
1033
|
-
/* @__PURE__ */ _jsxruntime.
|
|
1034
|
-
"
|
|
1035
|
-
{
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1045
|
-
|
|
1046
|
-
{
|
|
1047
|
-
label: "Original",
|
|
1048
|
-
value: `${imageData.original.width}x${imageData.original.height}`
|
|
1049
|
-
}
|
|
1050
|
-
),
|
|
1051
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1052
|
-
InfoRow,
|
|
1053
|
-
{
|
|
1054
|
-
label: "File size",
|
|
1055
|
-
value: formatFileSize3(imageData.original.fileSize)
|
|
1056
|
-
}
|
|
1057
|
-
),
|
|
1058
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.section, children: [
|
|
1059
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles5.sectionTitle, children: "Generated sizes" }),
|
|
1060
|
-
Object.entries(imageData.sizes).map(([size, data]) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, InfoRow, { label: size, value: `${data.width}x${data.height}` }, size))
|
|
1061
|
-
] }),
|
|
1062
|
-
_optionalChain([imageData, 'access', _6 => _6.cdn, 'optionalAccess', _7 => _7.synced]) && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.section, children: [
|
|
1063
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles5.sectionTitle, children: "CDN" }),
|
|
1064
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.cdnStatus, children: [
|
|
1065
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles5.cdnIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }),
|
|
1066
|
-
"Synced to CDN"
|
|
1067
|
-
] }),
|
|
1324
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1325
|
+
modals,
|
|
1326
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.panel, children: [
|
|
1327
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { css: styles6.title, children: "Preview" }),
|
|
1328
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles6.imageContainer, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1329
|
+
"img",
|
|
1330
|
+
{
|
|
1331
|
+
css: styles6.image,
|
|
1332
|
+
src: selectedPath.replace("public", ""),
|
|
1333
|
+
alt: "Preview"
|
|
1334
|
+
}
|
|
1335
|
+
) }),
|
|
1336
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.info, children: [
|
|
1337
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, InfoRow, { label: "Filename", value: selectedPath.split("/").pop() || "" }),
|
|
1338
|
+
imageData && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1068
1339
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1069
|
-
|
|
1340
|
+
InfoRow,
|
|
1070
1341
|
{
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
navigator.clipboard.writeText(`${_optionalChain([imageData, 'access', _8 => _8.cdn, 'optionalAccess', _9 => _9.baseUrl])}${imageData.sizes.full.path}`);
|
|
1074
|
-
},
|
|
1075
|
-
children: "Copy CDN URL"
|
|
1342
|
+
label: "Original",
|
|
1343
|
+
value: `${imageData.original.width}x${imageData.original.height}`
|
|
1076
1344
|
}
|
|
1077
|
-
)
|
|
1078
|
-
] }),
|
|
1079
|
-
imageData.blurhash && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.section, children: [
|
|
1080
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, InfoRow, { label: "Blurhash", value: imageData.blurhash, truncate: true }),
|
|
1345
|
+
),
|
|
1081
1346
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1082
|
-
|
|
1347
|
+
InfoRow,
|
|
1083
1348
|
{
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
title: `Dominant color: ${imageData.dominantColor}`
|
|
1349
|
+
label: "File size",
|
|
1350
|
+
value: formatFileSize3(imageData.original.fileSize)
|
|
1087
1351
|
}
|
|
1088
|
-
)
|
|
1352
|
+
),
|
|
1353
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.section, children: [
|
|
1354
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles6.sectionTitle, children: "Generated sizes" }),
|
|
1355
|
+
Object.entries(imageData.sizes).map(([size, data]) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, InfoRow, { label: size, value: `${data.width}x${data.height}` }, size))
|
|
1356
|
+
] }),
|
|
1357
|
+
_optionalChain([imageData, 'access', _6 => _6.cdn, 'optionalAccess', _7 => _7.synced]) && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.section, children: [
|
|
1358
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles6.sectionTitle, children: "CDN" }),
|
|
1359
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.cdnStatus, children: [
|
|
1360
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles6.cdnIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }),
|
|
1361
|
+
"Synced to CDN"
|
|
1362
|
+
] }),
|
|
1363
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1364
|
+
"button",
|
|
1365
|
+
{
|
|
1366
|
+
css: styles6.copyBtn,
|
|
1367
|
+
onClick: () => {
|
|
1368
|
+
navigator.clipboard.writeText(`${_optionalChain([imageData, 'access', _8 => _8.cdn, 'optionalAccess', _9 => _9.baseUrl])}${imageData.sizes.full.path}`);
|
|
1369
|
+
},
|
|
1370
|
+
children: "Copy CDN URL"
|
|
1371
|
+
}
|
|
1372
|
+
)
|
|
1373
|
+
] }),
|
|
1374
|
+
imageData.blurhash && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.section, children: [
|
|
1375
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, InfoRow, { label: "Blurhash", value: imageData.blurhash, truncate: true }),
|
|
1376
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1377
|
+
"div",
|
|
1378
|
+
{
|
|
1379
|
+
css: styles6.colorSwatch,
|
|
1380
|
+
style: { backgroundColor: imageData.dominantColor },
|
|
1381
|
+
title: `Dominant color: ${imageData.dominantColor}`
|
|
1382
|
+
}
|
|
1383
|
+
)
|
|
1384
|
+
] })
|
|
1089
1385
|
] })
|
|
1386
|
+
] }),
|
|
1387
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.actions, children: [
|
|
1388
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: styles6.actionBtn, children: "Rename" }),
|
|
1389
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: [styles6.actionBtn, styles6.actionBtnDanger], onClick: handleDeleteClick, children: "Delete" })
|
|
1090
1390
|
] })
|
|
1091
|
-
] }),
|
|
1092
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.actions, children: [
|
|
1093
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: styles5.actionBtn, children: "Rename" }),
|
|
1094
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: [styles5.actionBtn, styles5.actionBtnDanger], onClick: handleDelete, children: "Delete" })
|
|
1095
1391
|
] })
|
|
1096
1392
|
] });
|
|
1097
1393
|
}
|
|
1098
1394
|
function InfoRow({ label, value, truncate }) {
|
|
1099
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
1100
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css:
|
|
1101
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: [
|
|
1395
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.row, children: [
|
|
1396
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles6.label, children: label }),
|
|
1397
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: [styles6.value, truncate && styles6.valueTruncate], title: truncate ? value : void 0, children: value })
|
|
1102
1398
|
] });
|
|
1103
1399
|
}
|
|
1104
1400
|
function formatFileSize3(bytes) {
|
|
@@ -1111,7 +1407,7 @@ function formatFileSize3(bytes) {
|
|
|
1111
1407
|
|
|
1112
1408
|
|
|
1113
1409
|
|
|
1114
|
-
var
|
|
1410
|
+
var styles7 = {
|
|
1115
1411
|
btn: _react3.css`
|
|
1116
1412
|
padding: 8px;
|
|
1117
1413
|
background: none;
|
|
@@ -1269,10 +1565,10 @@ var styles6 = {
|
|
|
1269
1565
|
function StudioSettings() {
|
|
1270
1566
|
const [isOpen, setIsOpen] = _react.useState.call(void 0, false);
|
|
1271
1567
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1272
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css:
|
|
1568
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: styles7.btn, onClick: () => setIsOpen(true), "aria-label": "Settings", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1273
1569
|
"svg",
|
|
1274
1570
|
{
|
|
1275
|
-
css:
|
|
1571
|
+
css: styles7.icon,
|
|
1276
1572
|
xmlns: "http://www.w3.org/2000/svg",
|
|
1277
1573
|
viewBox: "0 0 24 24",
|
|
1278
1574
|
fill: "none",
|
|
@@ -1290,51 +1586,51 @@ function StudioSettings() {
|
|
|
1290
1586
|
] });
|
|
1291
1587
|
}
|
|
1292
1588
|
function SettingsPanel({ onClose }) {
|
|
1293
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
1294
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css:
|
|
1295
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
1296
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
1297
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { css:
|
|
1298
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css:
|
|
1589
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.overlay, children: [
|
|
1590
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles7.backdrop, onClick: onClose }),
|
|
1591
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.panel, children: [
|
|
1592
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.header, children: [
|
|
1593
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { css: styles7.title, children: "Settings" }),
|
|
1594
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: styles7.closeBtn, onClick: onClose, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles7.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })
|
|
1299
1595
|
] }),
|
|
1300
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
1596
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.sections, children: [
|
|
1301
1597
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "section", { children: [
|
|
1302
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { css:
|
|
1303
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css:
|
|
1304
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
1305
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css:
|
|
1306
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css:
|
|
1307
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css:
|
|
1308
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css:
|
|
1309
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css:
|
|
1598
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { css: styles7.sectionTitle, children: "Cloudflare R2" }),
|
|
1599
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles7.description, children: "Configure in .env.local file:" }),
|
|
1600
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.code, children: [
|
|
1601
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_ACCOUNT_ID" }),
|
|
1602
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_ACCESS_KEY_ID" }),
|
|
1603
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_SECRET_ACCESS_KEY" }),
|
|
1604
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_BUCKET_NAME" }),
|
|
1605
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_PUBLIC_URL" })
|
|
1310
1606
|
] })
|
|
1311
1607
|
] }),
|
|
1312
1608
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "section", { children: [
|
|
1313
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { css:
|
|
1314
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css:
|
|
1315
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { css:
|
|
1609
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { css: styles7.sectionTitle, children: "Custom CDN URL" }),
|
|
1610
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles7.description, children: "Override the default R2 URL with a custom domain:" }),
|
|
1611
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { css: styles7.input, type: "text", placeholder: "https://cdn.yourdomain.com" })
|
|
1316
1612
|
] }),
|
|
1317
1613
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "section", { children: [
|
|
1318
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { css:
|
|
1319
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
1614
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { css: styles7.sectionTitle, children: "Thumbnail Sizes" }),
|
|
1615
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.grid, children: [
|
|
1320
1616
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { children: [
|
|
1321
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { css:
|
|
1322
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { css:
|
|
1617
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { css: styles7.label, children: "Small" }),
|
|
1618
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { css: styles7.input, type: "number", defaultValue: 300 })
|
|
1323
1619
|
] }),
|
|
1324
1620
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { children: [
|
|
1325
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { css:
|
|
1326
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { css:
|
|
1621
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { css: styles7.label, children: "Medium" }),
|
|
1622
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { css: styles7.input, type: "number", defaultValue: 700 })
|
|
1327
1623
|
] }),
|
|
1328
1624
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { children: [
|
|
1329
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { css:
|
|
1330
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { css:
|
|
1625
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { css: styles7.label, children: "Large" }),
|
|
1626
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { css: styles7.input, type: "number", defaultValue: 1400 })
|
|
1331
1627
|
] })
|
|
1332
1628
|
] })
|
|
1333
1629
|
] })
|
|
1334
1630
|
] }),
|
|
1335
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
1336
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css:
|
|
1337
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css:
|
|
1631
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.footer, children: [
|
|
1632
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: styles7.cancelBtn, onClick: onClose, children: "Cancel" }),
|
|
1633
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: styles7.saveBtn, children: "Save Changes" })
|
|
1338
1634
|
] })
|
|
1339
1635
|
] })
|
|
1340
1636
|
] });
|
|
@@ -1342,7 +1638,7 @@ function SettingsPanel({ onClose }) {
|
|
|
1342
1638
|
|
|
1343
1639
|
// src/components/StudioUI.tsx
|
|
1344
1640
|
|
|
1345
|
-
var
|
|
1641
|
+
var styles8 = {
|
|
1346
1642
|
container: _react3.css`
|
|
1347
1643
|
display: flex;
|
|
1348
1644
|
flex-direction: column;
|
|
@@ -1399,6 +1695,7 @@ var styles7 = {
|
|
|
1399
1695
|
function StudioUI({ onClose }) {
|
|
1400
1696
|
const [currentPath, setCurrentPathInternal] = _react.useState.call(void 0, "public");
|
|
1401
1697
|
const [selectedItems, setSelectedItems] = _react.useState.call(void 0, /* @__PURE__ */ new Set());
|
|
1698
|
+
const [lastSelectedPath, setLastSelectedPath] = _react.useState.call(void 0, null);
|
|
1402
1699
|
const [viewMode, setViewMode] = _react.useState.call(void 0, "grid");
|
|
1403
1700
|
const [meta, setMeta] = _react.useState.call(void 0, null);
|
|
1404
1701
|
const [isLoading, setIsLoading] = _react.useState.call(void 0, false);
|
|
@@ -1427,6 +1724,23 @@ function StudioUI({ onClose }) {
|
|
|
1427
1724
|
}
|
|
1428
1725
|
return next;
|
|
1429
1726
|
});
|
|
1727
|
+
setLastSelectedPath(path);
|
|
1728
|
+
}, []);
|
|
1729
|
+
const selectRange = _react.useCallback.call(void 0, (fromPath, toPath, allItems) => {
|
|
1730
|
+
const files = allItems.filter((item) => item.type !== "folder");
|
|
1731
|
+
const fromIndex = files.findIndex((item) => item.path === fromPath);
|
|
1732
|
+
const toIndex = files.findIndex((item) => item.path === toPath);
|
|
1733
|
+
if (fromIndex === -1 || toIndex === -1) return;
|
|
1734
|
+
const start = Math.min(fromIndex, toIndex);
|
|
1735
|
+
const end = Math.max(fromIndex, toIndex);
|
|
1736
|
+
setSelectedItems((prev) => {
|
|
1737
|
+
const next = new Set(prev);
|
|
1738
|
+
for (let i = start; i <= end; i++) {
|
|
1739
|
+
next.add(files[i].path);
|
|
1740
|
+
}
|
|
1741
|
+
return next;
|
|
1742
|
+
});
|
|
1743
|
+
setLastSelectedPath(toPath);
|
|
1430
1744
|
}, []);
|
|
1431
1745
|
const selectAll = _react.useCallback.call(void 0, (items) => {
|
|
1432
1746
|
setSelectedItems(new Set(items.map((item) => item.path)));
|
|
@@ -1461,8 +1775,10 @@ function StudioUI({ onClose }) {
|
|
|
1461
1775
|
navigateUp,
|
|
1462
1776
|
selectedItems,
|
|
1463
1777
|
toggleSelection,
|
|
1778
|
+
selectRange,
|
|
1464
1779
|
selectAll,
|
|
1465
1780
|
clearSelection,
|
|
1781
|
+
lastSelectedPath,
|
|
1466
1782
|
viewMode,
|
|
1467
1783
|
setViewMode,
|
|
1468
1784
|
meta,
|
|
@@ -1472,15 +1788,15 @@ function StudioUI({ onClose }) {
|
|
|
1472
1788
|
refreshKey,
|
|
1473
1789
|
triggerRefresh
|
|
1474
1790
|
};
|
|
1475
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioContext.Provider, { value: contextValue, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
1476
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
1477
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h1", { css:
|
|
1478
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
1791
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioContext.Provider, { value: contextValue, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles8.container, children: [
|
|
1792
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles8.header, children: [
|
|
1793
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h1", { css: styles8.title, children: "Studio" }),
|
|
1794
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles8.headerActions, children: [
|
|
1479
1795
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioSettings, {}),
|
|
1480
1796
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1481
1797
|
"button",
|
|
1482
1798
|
{
|
|
1483
|
-
css:
|
|
1799
|
+
css: styles8.closeBtn,
|
|
1484
1800
|
onClick: onClose,
|
|
1485
1801
|
"aria-label": "Close Studio",
|
|
1486
1802
|
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, CloseIcon, {})
|
|
@@ -1490,8 +1806,8 @@ function StudioUI({ onClose }) {
|
|
|
1490
1806
|
] }),
|
|
1491
1807
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioToolbar, {}),
|
|
1492
1808
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioBreadcrumb, {}),
|
|
1493
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
1494
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css:
|
|
1809
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles8.content, children: [
|
|
1810
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles8.fileBrowser, children: viewMode === "grid" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioFileGrid, {}) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioFileList, {}) }),
|
|
1495
1811
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioPreview, {})
|
|
1496
1812
|
] })
|
|
1497
1813
|
] }) });
|
|
@@ -1500,7 +1816,7 @@ function CloseIcon() {
|
|
|
1500
1816
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1501
1817
|
"svg",
|
|
1502
1818
|
{
|
|
1503
|
-
css:
|
|
1819
|
+
css: styles8.closeIcon,
|
|
1504
1820
|
xmlns: "http://www.w3.org/2000/svg",
|
|
1505
1821
|
viewBox: "0 0 24 24",
|
|
1506
1822
|
fill: "none",
|
|
@@ -1520,4 +1836,4 @@ var StudioUI_default = StudioUI;
|
|
|
1520
1836
|
|
|
1521
1837
|
|
|
1522
1838
|
exports.StudioUI = StudioUI; exports.default = StudioUI_default;
|
|
1523
|
-
//# sourceMappingURL=StudioUI-
|
|
1839
|
+
//# sourceMappingURL=StudioUI-ELH3QOUT.js.map
|