@gallop.software/studio 0.1.87 → 0.1.89
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-ZBSTYTUV.js → StudioUI-JQHRTF45.js} +268 -48
- package/dist/StudioUI-JQHRTF45.js.map +1 -0
- package/dist/{StudioUI-6HTM3QHM.mjs → StudioUI-T7FA7S7Z.mjs} +255 -35
- package/dist/StudioUI-T7FA7S7Z.mjs.map +1 -0
- package/dist/{chunk-CN5NRNWB.js → chunk-HE2DOD2K.js} +1 -1
- package/dist/chunk-HE2DOD2K.js.map +1 -0
- package/dist/{chunk-3RI33B7A.mjs → chunk-QR2FRA4L.mjs} +1 -1
- package/dist/chunk-QR2FRA4L.mjs.map +1 -0
- package/dist/handlers/index.d.mts +1 -1
- package/dist/handlers/index.d.ts +1 -1
- package/dist/handlers/index.js +442 -324
- package/dist/handlers/index.js.map +1 -1
- package/dist/handlers/index.mjs +452 -334
- package/dist/handlers/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/index.mjs +2 -2
- package/dist/{types-C9CMIJLW.d.mts → types-Cxqb0WUK.d.mts} +10 -7
- package/dist/{types-C9CMIJLW.d.ts → types-Cxqb0WUK.d.ts} +10 -7
- package/package.json +1 -1
- package/dist/StudioUI-6HTM3QHM.mjs.map +0 -1
- package/dist/StudioUI-ZBSTYTUV.js.map +0 -1
- package/dist/chunk-3RI33B7A.mjs.map +0 -1
- package/dist/chunk-CN5NRNWB.js.map +0 -1
|
@@ -310,6 +310,23 @@ var progressStyles = {
|
|
|
310
310
|
white-space: nowrap;
|
|
311
311
|
overflow: hidden;
|
|
312
312
|
text-overflow: ellipsis;
|
|
313
|
+
`,
|
|
314
|
+
errorList: _react3.css`
|
|
315
|
+
margin-top: 12px;
|
|
316
|
+
padding: 12px;
|
|
317
|
+
background: #fef2f2;
|
|
318
|
+
border: 1px solid #fecaca;
|
|
319
|
+
border-radius: 6px;
|
|
320
|
+
max-height: 200px;
|
|
321
|
+
overflow-y: auto;
|
|
322
|
+
`,
|
|
323
|
+
errorItem: _react3.css`
|
|
324
|
+
font-size: ${_chunkUFCWGUAGjs.fontSize.xs};
|
|
325
|
+
color: #991b1b;
|
|
326
|
+
margin: 0 0 4px;
|
|
327
|
+
&:last-child {
|
|
328
|
+
margin-bottom: 0;
|
|
329
|
+
}
|
|
313
330
|
`
|
|
314
331
|
};
|
|
315
332
|
function ProgressModal({
|
|
@@ -331,26 +348,36 @@ function ProgressModal({
|
|
|
331
348
|
" image",
|
|
332
349
|
(_nullishCoalesce(progress.processed, () => ( progress.current))) !== 1 ? "s" : "",
|
|
333
350
|
" before stopping."
|
|
334
|
-
] }) : isComplete ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
351
|
+
] }) : isComplete ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
352
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { css: styles.message, children: [
|
|
353
|
+
"Processed ",
|
|
354
|
+
progress.processed,
|
|
355
|
+
" image",
|
|
356
|
+
progress.processed !== 1 ? "s" : "",
|
|
357
|
+
".",
|
|
358
|
+
progress.orphansRemoved !== void 0 && progress.orphansRemoved > 0 ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
359
|
+
" Removed ",
|
|
360
|
+
progress.orphansRemoved,
|
|
361
|
+
" orphaned thumbnail",
|
|
362
|
+
progress.orphansRemoved !== 1 ? "s" : "",
|
|
363
|
+
"."
|
|
364
|
+
] }) : null,
|
|
365
|
+
progress.errors !== void 0 && progress.errors > 0 ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
366
|
+
" ",
|
|
367
|
+
progress.errors,
|
|
368
|
+
" error",
|
|
369
|
+
progress.errors !== 1 ? "s" : "",
|
|
370
|
+
" occurred."
|
|
371
|
+
] }) : null
|
|
372
|
+
] }),
|
|
373
|
+
progress.errorMessages && progress.errorMessages.length > 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: progressStyles.errorList, children: [
|
|
374
|
+
progress.errorMessages.slice(0, 10).map((msg, i) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: progressStyles.errorItem, children: msg }, i)),
|
|
375
|
+
progress.errorMessages.length > 10 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { css: progressStyles.errorItem, children: [
|
|
376
|
+
"...and ",
|
|
377
|
+
progress.errorMessages.length - 10,
|
|
378
|
+
" more"
|
|
379
|
+
] })
|
|
380
|
+
] })
|
|
354
381
|
] }) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
355
382
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles.message, children: progress.status === "cleanup" ? "Cleaning up orphaned files..." : `Processing images...` }),
|
|
356
383
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: progressStyles.progressContainer, children: [
|
|
@@ -1262,7 +1289,7 @@ function StudioToolbar() {
|
|
|
1262
1289
|
const fileInputRef = _react.useRef.call(void 0, null);
|
|
1263
1290
|
const abortControllerRef = _react.useRef.call(void 0, null);
|
|
1264
1291
|
const [uploading, setUploading] = _react.useState.call(void 0, false);
|
|
1265
|
-
const [
|
|
1292
|
+
const [scanning, setScanning] = _react.useState.call(void 0, false);
|
|
1266
1293
|
const [processing, setProcessing] = _react.useState.call(void 0, false);
|
|
1267
1294
|
const [showDeleteConfirm, setShowDeleteConfirm] = _react.useState.call(void 0, false);
|
|
1268
1295
|
const [showProcessConfirm, setShowProcessConfirm] = _react.useState.call(void 0, false);
|
|
@@ -1288,10 +1315,81 @@ function StudioToolbar() {
|
|
|
1288
1315
|
const handleUpload = _react.useCallback.call(void 0, () => {
|
|
1289
1316
|
_optionalChain([fileInputRef, 'access', _2 => _2.current, 'optionalAccess', _3 => _3.click, 'call', _4 => _4()]);
|
|
1290
1317
|
}, []);
|
|
1291
|
-
const
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1318
|
+
const handleScan = _react.useCallback.call(void 0, async () => {
|
|
1319
|
+
setScanning(true);
|
|
1320
|
+
setShowProgress(true);
|
|
1321
|
+
setProgressState({
|
|
1322
|
+
current: 0,
|
|
1323
|
+
total: 0,
|
|
1324
|
+
percent: 0,
|
|
1325
|
+
status: "processing",
|
|
1326
|
+
message: "Scanning for files..."
|
|
1327
|
+
});
|
|
1328
|
+
try {
|
|
1329
|
+
const response = await fetch("/api/studio/scan", { method: "POST" });
|
|
1330
|
+
const reader = _optionalChain([response, 'access', _5 => _5.body, 'optionalAccess', _6 => _6.getReader, 'call', _7 => _7()]);
|
|
1331
|
+
if (!reader) throw new Error("No reader");
|
|
1332
|
+
const decoder = new TextDecoder();
|
|
1333
|
+
let buffer = "";
|
|
1334
|
+
while (true) {
|
|
1335
|
+
const { done, value } = await reader.read();
|
|
1336
|
+
if (done) break;
|
|
1337
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1338
|
+
const lines = buffer.split("\n\n");
|
|
1339
|
+
buffer = lines.pop() || "";
|
|
1340
|
+
for (const line of lines) {
|
|
1341
|
+
if (!line.startsWith("data: ")) continue;
|
|
1342
|
+
const data = JSON.parse(line.slice(6));
|
|
1343
|
+
if (data.type === "start") {
|
|
1344
|
+
setProgressState({
|
|
1345
|
+
current: 0,
|
|
1346
|
+
total: data.total,
|
|
1347
|
+
percent: 0,
|
|
1348
|
+
status: "processing",
|
|
1349
|
+
message: `Scanning ${data.total} files...`
|
|
1350
|
+
});
|
|
1351
|
+
} else if (data.type === "progress") {
|
|
1352
|
+
setProgressState({
|
|
1353
|
+
current: data.current,
|
|
1354
|
+
total: data.total,
|
|
1355
|
+
percent: data.percent,
|
|
1356
|
+
status: "processing",
|
|
1357
|
+
currentFile: data.currentFile
|
|
1358
|
+
});
|
|
1359
|
+
} else if (data.type === "complete") {
|
|
1360
|
+
setProgressState({
|
|
1361
|
+
current: data.total || 0,
|
|
1362
|
+
total: data.total || 0,
|
|
1363
|
+
percent: 100,
|
|
1364
|
+
status: "complete",
|
|
1365
|
+
processed: data.added,
|
|
1366
|
+
errors: data.errors,
|
|
1367
|
+
message: data.renamed > 0 ? `${data.renamed} file(s) renamed due to conflicts` : void 0
|
|
1368
|
+
});
|
|
1369
|
+
triggerRefresh();
|
|
1370
|
+
} else if (data.type === "error") {
|
|
1371
|
+
setProgressState({
|
|
1372
|
+
current: 0,
|
|
1373
|
+
total: 0,
|
|
1374
|
+
percent: 0,
|
|
1375
|
+
status: "error",
|
|
1376
|
+
message: data.message || "Scan failed"
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
} catch (error) {
|
|
1382
|
+
console.error("Scan error:", error);
|
|
1383
|
+
setProgressState({
|
|
1384
|
+
current: 0,
|
|
1385
|
+
total: 0,
|
|
1386
|
+
percent: 0,
|
|
1387
|
+
status: "error",
|
|
1388
|
+
message: "Scan failed"
|
|
1389
|
+
});
|
|
1390
|
+
} finally {
|
|
1391
|
+
setScanning(false);
|
|
1392
|
+
}
|
|
1295
1393
|
}, [triggerRefresh]);
|
|
1296
1394
|
const handleFileChange = _react.useCallback.call(void 0, async (e) => {
|
|
1297
1395
|
const files = e.target.files;
|
|
@@ -1395,7 +1493,7 @@ function StudioToolbar() {
|
|
|
1395
1493
|
const selectedPaths2 = Array.from(selectedItems);
|
|
1396
1494
|
const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"];
|
|
1397
1495
|
const selectedImagePaths = selectedPaths2.filter((p) => {
|
|
1398
|
-
const ext = _optionalChain([p, 'access',
|
|
1496
|
+
const ext = _optionalChain([p, 'access', _8 => _8.split, 'call', _9 => _9("."), 'access', _10 => _10.pop, 'call', _11 => _11(), 'optionalAccess', _12 => _12.toLowerCase, 'call', _13 => _13()]) || "";
|
|
1399
1497
|
return imageExtensions.includes(ext);
|
|
1400
1498
|
});
|
|
1401
1499
|
const selectedFolders = selectedPaths2.filter((p) => !p.includes(".") || p.endsWith("/"));
|
|
@@ -1556,12 +1654,12 @@ function StudioToolbar() {
|
|
|
1556
1654
|
const data = await response.json();
|
|
1557
1655
|
if (response.ok) {
|
|
1558
1656
|
setProgressState({
|
|
1559
|
-
current: _optionalChain([data, 'access',
|
|
1560
|
-
total: _optionalChain([data, 'access',
|
|
1657
|
+
current: _optionalChain([data, 'access', _14 => _14.processed, 'optionalAccess', _15 => _15.length]) || 0,
|
|
1658
|
+
total: _optionalChain([data, 'access', _16 => _16.processed, 'optionalAccess', _17 => _17.length]) || 0,
|
|
1561
1659
|
percent: 100,
|
|
1562
1660
|
status: "complete",
|
|
1563
|
-
processed: _optionalChain([data, 'access',
|
|
1564
|
-
errors: _optionalChain([data, 'access',
|
|
1661
|
+
processed: _optionalChain([data, 'access', _18 => _18.processed, 'optionalAccess', _19 => _19.length]) || 0,
|
|
1662
|
+
errors: _optionalChain([data, 'access', _20 => _20.errors, 'optionalAccess', _21 => _21.length]) || 0
|
|
1565
1663
|
});
|
|
1566
1664
|
clearSelection();
|
|
1567
1665
|
triggerRefresh();
|
|
@@ -1638,7 +1736,7 @@ function StudioToolbar() {
|
|
|
1638
1736
|
const selectedPaths2 = Array.from(selectedItems);
|
|
1639
1737
|
const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"];
|
|
1640
1738
|
const selectedImagePaths = selectedPaths2.filter((p) => {
|
|
1641
|
-
const ext = _optionalChain([p, 'access',
|
|
1739
|
+
const ext = _optionalChain([p, 'access', _22 => _22.split, 'call', _23 => _23("."), 'access', _24 => _24.pop, 'call', _25 => _25(), 'optionalAccess', _26 => _26.toLowerCase, 'call', _27 => _27()]) || "";
|
|
1642
1740
|
return imageExtensions.includes(ext);
|
|
1643
1741
|
});
|
|
1644
1742
|
const selectedFolders = selectedPaths2.filter((p) => !p.includes(".") || p.endsWith("/"));
|
|
@@ -1673,7 +1771,7 @@ function StudioToolbar() {
|
|
|
1673
1771
|
const selectedPaths2 = Array.from(selectedItems);
|
|
1674
1772
|
const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"];
|
|
1675
1773
|
const selectedImagePaths = selectedPaths2.filter((p) => {
|
|
1676
|
-
const ext = _optionalChain([p, 'access',
|
|
1774
|
+
const ext = _optionalChain([p, 'access', _28 => _28.split, 'call', _29 => _29("."), 'access', _30 => _30.pop, 'call', _31 => _31(), 'optionalAccess', _32 => _32.toLowerCase, 'call', _33 => _33()]) || "";
|
|
1677
1775
|
return imageExtensions.includes(ext);
|
|
1678
1776
|
});
|
|
1679
1777
|
const selectedFolders = selectedPaths2.filter((p) => !p.includes(".") || p.endsWith("/"));
|
|
@@ -1704,6 +1802,7 @@ function StudioToolbar() {
|
|
|
1704
1802
|
setShowProgress(true);
|
|
1705
1803
|
let synced = 0;
|
|
1706
1804
|
let errors = 0;
|
|
1805
|
+
const errorMessages = [];
|
|
1707
1806
|
try {
|
|
1708
1807
|
for (let i = 0; i < imageKeys.length; i++) {
|
|
1709
1808
|
const imageKey = imageKeys[i];
|
|
@@ -1722,19 +1821,24 @@ function StudioToolbar() {
|
|
|
1722
1821
|
});
|
|
1723
1822
|
const data = await response.json();
|
|
1724
1823
|
if (!response.ok) {
|
|
1725
|
-
if (_optionalChain([data, 'access',
|
|
1824
|
+
if (_optionalChain([data, 'access', _34 => _34.error, 'optionalAccess', _35 => _35.includes, 'call', _36 => _36("R2 not configured")]) || _optionalChain([data, 'access', _37 => _37.error, 'optionalAccess', _38 => _38.includes, 'call', _39 => _39("CLOUDFLARE_R2")])) {
|
|
1726
1825
|
setShowProgress(false);
|
|
1727
1826
|
setShowR2SetupModal(true);
|
|
1728
1827
|
return;
|
|
1729
1828
|
}
|
|
1730
1829
|
errors++;
|
|
1731
|
-
|
|
1830
|
+
errorMessages.push(data.error || `Failed: ${imageKey}`);
|
|
1831
|
+
} else if (_optionalChain([data, 'access', _40 => _40.synced, 'optionalAccess', _41 => _41.length]) > 0) {
|
|
1732
1832
|
synced++;
|
|
1733
|
-
} else if (_optionalChain([data, 'access',
|
|
1833
|
+
} else if (_optionalChain([data, 'access', _42 => _42.errors, 'optionalAccess', _43 => _43.length]) > 0) {
|
|
1734
1834
|
errors++;
|
|
1835
|
+
for (const errMsg of data.errors) {
|
|
1836
|
+
errorMessages.push(errMsg);
|
|
1837
|
+
}
|
|
1735
1838
|
}
|
|
1736
|
-
} catch (
|
|
1839
|
+
} catch (err) {
|
|
1737
1840
|
errors++;
|
|
1841
|
+
errorMessages.push(`Network error: ${imageKey}`);
|
|
1738
1842
|
}
|
|
1739
1843
|
}
|
|
1740
1844
|
setProgressState({
|
|
@@ -1743,7 +1847,8 @@ function StudioToolbar() {
|
|
|
1743
1847
|
percent: 100,
|
|
1744
1848
|
status: "complete",
|
|
1745
1849
|
processed: synced,
|
|
1746
|
-
errors
|
|
1850
|
+
errors,
|
|
1851
|
+
errorMessages: errorMessages.length > 0 ? errorMessages : void 0
|
|
1747
1852
|
});
|
|
1748
1853
|
clearSelection();
|
|
1749
1854
|
triggerRefresh();
|
|
@@ -2070,12 +2175,16 @@ function StudioToolbar() {
|
|
|
2070
2175
|
" selected",
|
|
2071
2176
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: styles4.clearBtn, onClick: clearSelection, children: "Clear" })
|
|
2072
2177
|
] }),
|
|
2073
|
-
/* @__PURE__ */ _jsxruntime.
|
|
2178
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2074
2179
|
"button",
|
|
2075
2180
|
{
|
|
2076
|
-
css:
|
|
2077
|
-
onClick:
|
|
2078
|
-
|
|
2181
|
+
css: styles4.btn,
|
|
2182
|
+
onClick: handleScan,
|
|
2183
|
+
disabled: scanning,
|
|
2184
|
+
children: [
|
|
2185
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, ScanIcon, { spinning: scanning }),
|
|
2186
|
+
"Scan"
|
|
2187
|
+
]
|
|
2079
2188
|
}
|
|
2080
2189
|
),
|
|
2081
2190
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles4.viewToggle, children: [
|
|
@@ -2105,7 +2214,7 @@ function StudioToolbar() {
|
|
|
2105
2214
|
function UploadIcon() {
|
|
2106
2215
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles4.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" }) });
|
|
2107
2216
|
}
|
|
2108
|
-
function
|
|
2217
|
+
function ScanIcon({ spinning }) {
|
|
2109
2218
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: [styles4.icon, spinning && styles4.iconSpin], 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" }) });
|
|
2110
2219
|
}
|
|
2111
2220
|
function TrashIcon() {
|
|
@@ -2239,6 +2348,7 @@ function useFileList() {
|
|
|
2239
2348
|
} = useStudio();
|
|
2240
2349
|
const [items, setItems] = _react.useState.call(void 0, []);
|
|
2241
2350
|
const [loading, setLoading] = _react.useState.call(void 0, true);
|
|
2351
|
+
const [metaEmpty, setMetaEmpty] = _react.useState.call(void 0, false);
|
|
2242
2352
|
const isInitialLoad = _react.useRef.call(void 0, true);
|
|
2243
2353
|
const lastPath = _react.useRef.call(void 0, currentPath);
|
|
2244
2354
|
_react.useEffect.call(void 0, () => {
|
|
@@ -2251,10 +2361,12 @@ function useFileList() {
|
|
|
2251
2361
|
try {
|
|
2252
2362
|
const data = searchQuery && searchQuery.length >= 2 ? await studioApi.search(searchQuery) : await studioApi.list(currentPath);
|
|
2253
2363
|
setItems(data.items || []);
|
|
2364
|
+
setMetaEmpty(data.isEmpty === true);
|
|
2254
2365
|
} catch (error) {
|
|
2255
2366
|
const message = error instanceof Error ? error.message : "Failed to load items";
|
|
2256
2367
|
showError("Load Error", message);
|
|
2257
2368
|
setItems([]);
|
|
2369
|
+
setMetaEmpty(false);
|
|
2258
2370
|
}
|
|
2259
2371
|
setLoading(false);
|
|
2260
2372
|
isInitialLoad.current = false;
|
|
@@ -2306,6 +2418,7 @@ function useFileList() {
|
|
|
2306
2418
|
items,
|
|
2307
2419
|
loading,
|
|
2308
2420
|
sortedItems,
|
|
2421
|
+
metaEmpty,
|
|
2309
2422
|
// Computed
|
|
2310
2423
|
isAtRoot,
|
|
2311
2424
|
isSearching,
|
|
@@ -2366,6 +2479,27 @@ var styles5 = {
|
|
|
2366
2479
|
font-size: ${_chunkUFCWGUAGjs.fontSize.sm};
|
|
2367
2480
|
}
|
|
2368
2481
|
`,
|
|
2482
|
+
scanButton: _react3.css`
|
|
2483
|
+
margin-top: 16px;
|
|
2484
|
+
padding: 10px 24px;
|
|
2485
|
+
font-size: ${_chunkUFCWGUAGjs.fontSize.base};
|
|
2486
|
+
font-weight: 500;
|
|
2487
|
+
background: ${_chunkUFCWGUAGjs.colors.primary};
|
|
2488
|
+
color: white;
|
|
2489
|
+
border: none;
|
|
2490
|
+
border-radius: 8px;
|
|
2491
|
+
cursor: pointer;
|
|
2492
|
+
transition: background 0.15s ease;
|
|
2493
|
+
|
|
2494
|
+
&:hover:not(:disabled) {
|
|
2495
|
+
background: ${_chunkUFCWGUAGjs.colors.primaryHover};
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
&:disabled {
|
|
2499
|
+
opacity: 0.6;
|
|
2500
|
+
cursor: not-allowed;
|
|
2501
|
+
}
|
|
2502
|
+
`,
|
|
2369
2503
|
grid: _react3.css`
|
|
2370
2504
|
display: grid;
|
|
2371
2505
|
grid-template-columns: 1fr;
|
|
@@ -2649,6 +2783,7 @@ function StudioFileGrid() {
|
|
|
2649
2783
|
const {
|
|
2650
2784
|
loading,
|
|
2651
2785
|
sortedItems,
|
|
2786
|
+
metaEmpty,
|
|
2652
2787
|
isAtRoot,
|
|
2653
2788
|
isSearching,
|
|
2654
2789
|
allItemsSelected,
|
|
@@ -2660,14 +2795,42 @@ function StudioFileGrid() {
|
|
|
2660
2795
|
handleGenerateThumbnail,
|
|
2661
2796
|
handleSelectAll
|
|
2662
2797
|
} = useFileList();
|
|
2798
|
+
const [scanning, setScanning] = _react.useState.call(void 0, false);
|
|
2799
|
+
const handleScan = async () => {
|
|
2800
|
+
setScanning(true);
|
|
2801
|
+
try {
|
|
2802
|
+
await fetch("/api/studio/scan", { method: "POST" });
|
|
2803
|
+
window.location.reload();
|
|
2804
|
+
} catch (error) {
|
|
2805
|
+
console.error("Scan failed:", error);
|
|
2806
|
+
} finally {
|
|
2807
|
+
setScanning(false);
|
|
2808
|
+
}
|
|
2809
|
+
};
|
|
2663
2810
|
if (loading) {
|
|
2664
2811
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles5.loading, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles5.spinner }) });
|
|
2665
2812
|
}
|
|
2813
|
+
if (metaEmpty && isAtRoot) {
|
|
2814
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.empty, children: [
|
|
2815
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles5.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: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }),
|
|
2816
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles5.emptyText, children: "No files tracked yet" }),
|
|
2817
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles5.emptyText, children: "Click Scan to discover files in your public folder" }),
|
|
2818
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2819
|
+
"button",
|
|
2820
|
+
{
|
|
2821
|
+
css: styles5.scanButton,
|
|
2822
|
+
onClick: handleScan,
|
|
2823
|
+
disabled: scanning,
|
|
2824
|
+
children: scanning ? "Scanning..." : "Scan for Files"
|
|
2825
|
+
}
|
|
2826
|
+
)
|
|
2827
|
+
] });
|
|
2828
|
+
}
|
|
2666
2829
|
if (sortedItems.length === 0 && isAtRoot) {
|
|
2667
2830
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.empty, children: [
|
|
2668
2831
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles5.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" }) }),
|
|
2669
2832
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles5.emptyText, children: "No files in this folder" }),
|
|
2670
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles5.emptyText, children: "Upload images
|
|
2833
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles5.emptyText, children: "Upload images or click Scan in the toolbar" })
|
|
2671
2834
|
] });
|
|
2672
2835
|
}
|
|
2673
2836
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { children: [
|
|
@@ -2864,6 +3027,32 @@ var styles6 = {
|
|
|
2864
3027
|
height: 256px;
|
|
2865
3028
|
color: ${_chunkUFCWGUAGjs.colors.textSecondary};
|
|
2866
3029
|
`,
|
|
3030
|
+
emptyHint: _react3.css`
|
|
3031
|
+
font-size: ${_chunkUFCWGUAGjs.fontSize.sm};
|
|
3032
|
+
color: ${_chunkUFCWGUAGjs.colors.textMuted};
|
|
3033
|
+
margin-top: 4px;
|
|
3034
|
+
`,
|
|
3035
|
+
scanButton: _react3.css`
|
|
3036
|
+
margin-top: 16px;
|
|
3037
|
+
padding: 10px 24px;
|
|
3038
|
+
font-size: ${_chunkUFCWGUAGjs.fontSize.base};
|
|
3039
|
+
font-weight: 500;
|
|
3040
|
+
background: ${_chunkUFCWGUAGjs.colors.primary};
|
|
3041
|
+
color: white;
|
|
3042
|
+
border: none;
|
|
3043
|
+
border-radius: 8px;
|
|
3044
|
+
cursor: pointer;
|
|
3045
|
+
transition: background 0.15s ease;
|
|
3046
|
+
|
|
3047
|
+
&:hover:not(:disabled) {
|
|
3048
|
+
background: ${_chunkUFCWGUAGjs.colors.primaryHover};
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
&:disabled {
|
|
3052
|
+
opacity: 0.6;
|
|
3053
|
+
cursor: not-allowed;
|
|
3054
|
+
}
|
|
3055
|
+
`,
|
|
2867
3056
|
tableWrapper: _react3.css`
|
|
2868
3057
|
background: ${_chunkUFCWGUAGjs.colors.surface};
|
|
2869
3058
|
border-radius: 8px;
|
|
@@ -3150,6 +3339,7 @@ function StudioFileList() {
|
|
|
3150
3339
|
const {
|
|
3151
3340
|
loading,
|
|
3152
3341
|
sortedItems,
|
|
3342
|
+
metaEmpty,
|
|
3153
3343
|
isAtRoot,
|
|
3154
3344
|
isSearching,
|
|
3155
3345
|
allItemsSelected,
|
|
@@ -3161,11 +3351,41 @@ function StudioFileList() {
|
|
|
3161
3351
|
handleGenerateThumbnail,
|
|
3162
3352
|
handleSelectAll
|
|
3163
3353
|
} = useFileList();
|
|
3354
|
+
const [scanning, setScanning] = _react.useState.call(void 0, false);
|
|
3355
|
+
const handleScan = async () => {
|
|
3356
|
+
setScanning(true);
|
|
3357
|
+
try {
|
|
3358
|
+
await fetch("/api/studio/scan", { method: "POST" });
|
|
3359
|
+
window.location.reload();
|
|
3360
|
+
} catch (error) {
|
|
3361
|
+
console.error("Scan failed:", error);
|
|
3362
|
+
} finally {
|
|
3363
|
+
setScanning(false);
|
|
3364
|
+
}
|
|
3365
|
+
};
|
|
3164
3366
|
if (loading) {
|
|
3165
3367
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles6.loading, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles6.spinner }) });
|
|
3166
3368
|
}
|
|
3369
|
+
if (metaEmpty && isAtRoot) {
|
|
3370
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.empty, children: [
|
|
3371
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { children: "No files tracked yet" }),
|
|
3372
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles6.emptyHint, children: "Click Scan to discover files in your public folder" }),
|
|
3373
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
3374
|
+
"button",
|
|
3375
|
+
{
|
|
3376
|
+
css: styles6.scanButton,
|
|
3377
|
+
onClick: handleScan,
|
|
3378
|
+
disabled: scanning,
|
|
3379
|
+
children: scanning ? "Scanning..." : "Scan for Files"
|
|
3380
|
+
}
|
|
3381
|
+
)
|
|
3382
|
+
] });
|
|
3383
|
+
}
|
|
3167
3384
|
if (sortedItems.length === 0 && isAtRoot) {
|
|
3168
|
-
return /* @__PURE__ */ _jsxruntime.
|
|
3385
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.empty, children: [
|
|
3386
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { children: "No files in this folder" }),
|
|
3387
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles6.emptyHint, children: "Upload images or click Scan in the toolbar" })
|
|
3388
|
+
] });
|
|
3169
3389
|
}
|
|
3170
3390
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles6.tableWrapper, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "table", { css: styles6.table, children: [
|
|
3171
3391
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "thead", { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "tr", { children: [
|
|
@@ -3675,7 +3895,7 @@ function StudioDetailView() {
|
|
|
3675
3895
|
});
|
|
3676
3896
|
triggerRefresh();
|
|
3677
3897
|
} else {
|
|
3678
|
-
if (_optionalChain([data, 'access',
|
|
3898
|
+
if (_optionalChain([data, 'access', _44 => _44.error, 'optionalAccess', _45 => _45.includes, 'call', _46 => _46("R2 not configured")]) || _optionalChain([data, 'access', _47 => _47.error, 'optionalAccess', _48 => _48.includes, 'call', _49 => _49("CLOUDFLARE_R2")])) {
|
|
3679
3899
|
setShowR2SetupModal(true);
|
|
3680
3900
|
} else {
|
|
3681
3901
|
setAlertMessage({
|
|
@@ -3714,7 +3934,7 @@ function StudioDetailView() {
|
|
|
3714
3934
|
if (!response.ok) {
|
|
3715
3935
|
throw new Error("Processing failed");
|
|
3716
3936
|
}
|
|
3717
|
-
const reader = _optionalChain([response, 'access',
|
|
3937
|
+
const reader = _optionalChain([response, 'access', _50 => _50.body, 'optionalAccess', _51 => _51.getReader, 'call', _52 => _52()]);
|
|
3718
3938
|
if (!reader) {
|
|
3719
3939
|
throw new Error("No response body");
|
|
3720
3940
|
}
|
|
@@ -3731,7 +3951,7 @@ function StudioDetailView() {
|
|
|
3731
3951
|
try {
|
|
3732
3952
|
const data = JSON.parse(line.slice(6));
|
|
3733
3953
|
setProcessProgress(data);
|
|
3734
|
-
} catch (
|
|
3954
|
+
} catch (e4) {
|
|
3735
3955
|
}
|
|
3736
3956
|
}
|
|
3737
3957
|
}
|
|
@@ -4661,4 +4881,4 @@ var StudioUI_default = StudioUI;
|
|
|
4661
4881
|
|
|
4662
4882
|
|
|
4663
4883
|
exports.StudioUI = StudioUI; exports.default = StudioUI_default;
|
|
4664
|
-
//# sourceMappingURL=StudioUI-
|
|
4884
|
+
//# sourceMappingURL=StudioUI-JQHRTF45.js.map
|