@gallop.software/studio 0.1.88 → 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-Y35A2T7S.js → StudioUI-JQHRTF45.js} +211 -25
- package/dist/StudioUI-JQHRTF45.js.map +1 -0
- package/dist/{StudioUI-6CQ7MX7R.mjs → StudioUI-T7FA7S7Z.mjs} +199 -13
- 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-6CQ7MX7R.mjs.map +0 -1
- package/dist/StudioUI-Y35A2T7S.js.map +0 -1
- package/dist/chunk-3RI33B7A.mjs.map +0 -1
- package/dist/chunk-CN5NRNWB.js.map +0 -1
|
@@ -1289,7 +1289,7 @@ function StudioToolbar() {
|
|
|
1289
1289
|
const fileInputRef = _react.useRef.call(void 0, null);
|
|
1290
1290
|
const abortControllerRef = _react.useRef.call(void 0, null);
|
|
1291
1291
|
const [uploading, setUploading] = _react.useState.call(void 0, false);
|
|
1292
|
-
const [
|
|
1292
|
+
const [scanning, setScanning] = _react.useState.call(void 0, false);
|
|
1293
1293
|
const [processing, setProcessing] = _react.useState.call(void 0, false);
|
|
1294
1294
|
const [showDeleteConfirm, setShowDeleteConfirm] = _react.useState.call(void 0, false);
|
|
1295
1295
|
const [showProcessConfirm, setShowProcessConfirm] = _react.useState.call(void 0, false);
|
|
@@ -1315,10 +1315,81 @@ function StudioToolbar() {
|
|
|
1315
1315
|
const handleUpload = _react.useCallback.call(void 0, () => {
|
|
1316
1316
|
_optionalChain([fileInputRef, 'access', _2 => _2.current, 'optionalAccess', _3 => _3.click, 'call', _4 => _4()]);
|
|
1317
1317
|
}, []);
|
|
1318
|
-
const
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
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
|
+
}
|
|
1322
1393
|
}, [triggerRefresh]);
|
|
1323
1394
|
const handleFileChange = _react.useCallback.call(void 0, async (e) => {
|
|
1324
1395
|
const files = e.target.files;
|
|
@@ -1422,7 +1493,7 @@ function StudioToolbar() {
|
|
|
1422
1493
|
const selectedPaths2 = Array.from(selectedItems);
|
|
1423
1494
|
const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"];
|
|
1424
1495
|
const selectedImagePaths = selectedPaths2.filter((p) => {
|
|
1425
|
-
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()]) || "";
|
|
1426
1497
|
return imageExtensions.includes(ext);
|
|
1427
1498
|
});
|
|
1428
1499
|
const selectedFolders = selectedPaths2.filter((p) => !p.includes(".") || p.endsWith("/"));
|
|
@@ -1583,12 +1654,12 @@ function StudioToolbar() {
|
|
|
1583
1654
|
const data = await response.json();
|
|
1584
1655
|
if (response.ok) {
|
|
1585
1656
|
setProgressState({
|
|
1586
|
-
current: _optionalChain([data, 'access',
|
|
1587
|
-
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,
|
|
1588
1659
|
percent: 100,
|
|
1589
1660
|
status: "complete",
|
|
1590
|
-
processed: _optionalChain([data, 'access',
|
|
1591
|
-
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
|
|
1592
1663
|
});
|
|
1593
1664
|
clearSelection();
|
|
1594
1665
|
triggerRefresh();
|
|
@@ -1665,7 +1736,7 @@ function StudioToolbar() {
|
|
|
1665
1736
|
const selectedPaths2 = Array.from(selectedItems);
|
|
1666
1737
|
const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"];
|
|
1667
1738
|
const selectedImagePaths = selectedPaths2.filter((p) => {
|
|
1668
|
-
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()]) || "";
|
|
1669
1740
|
return imageExtensions.includes(ext);
|
|
1670
1741
|
});
|
|
1671
1742
|
const selectedFolders = selectedPaths2.filter((p) => !p.includes(".") || p.endsWith("/"));
|
|
@@ -1700,7 +1771,7 @@ function StudioToolbar() {
|
|
|
1700
1771
|
const selectedPaths2 = Array.from(selectedItems);
|
|
1701
1772
|
const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"];
|
|
1702
1773
|
const selectedImagePaths = selectedPaths2.filter((p) => {
|
|
1703
|
-
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()]) || "";
|
|
1704
1775
|
return imageExtensions.includes(ext);
|
|
1705
1776
|
});
|
|
1706
1777
|
const selectedFolders = selectedPaths2.filter((p) => !p.includes(".") || p.endsWith("/"));
|
|
@@ -1750,16 +1821,16 @@ function StudioToolbar() {
|
|
|
1750
1821
|
});
|
|
1751
1822
|
const data = await response.json();
|
|
1752
1823
|
if (!response.ok) {
|
|
1753
|
-
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")])) {
|
|
1754
1825
|
setShowProgress(false);
|
|
1755
1826
|
setShowR2SetupModal(true);
|
|
1756
1827
|
return;
|
|
1757
1828
|
}
|
|
1758
1829
|
errors++;
|
|
1759
1830
|
errorMessages.push(data.error || `Failed: ${imageKey}`);
|
|
1760
|
-
} else if (_optionalChain([data, 'access',
|
|
1831
|
+
} else if (_optionalChain([data, 'access', _40 => _40.synced, 'optionalAccess', _41 => _41.length]) > 0) {
|
|
1761
1832
|
synced++;
|
|
1762
|
-
} else if (_optionalChain([data, 'access',
|
|
1833
|
+
} else if (_optionalChain([data, 'access', _42 => _42.errors, 'optionalAccess', _43 => _43.length]) > 0) {
|
|
1763
1834
|
errors++;
|
|
1764
1835
|
for (const errMsg of data.errors) {
|
|
1765
1836
|
errorMessages.push(errMsg);
|
|
@@ -2104,12 +2175,16 @@ function StudioToolbar() {
|
|
|
2104
2175
|
" selected",
|
|
2105
2176
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: styles4.clearBtn, onClick: clearSelection, children: "Clear" })
|
|
2106
2177
|
] }),
|
|
2107
|
-
/* @__PURE__ */ _jsxruntime.
|
|
2178
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2108
2179
|
"button",
|
|
2109
2180
|
{
|
|
2110
|
-
css:
|
|
2111
|
-
onClick:
|
|
2112
|
-
|
|
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
|
+
]
|
|
2113
2188
|
}
|
|
2114
2189
|
),
|
|
2115
2190
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles4.viewToggle, children: [
|
|
@@ -2139,7 +2214,7 @@ function StudioToolbar() {
|
|
|
2139
2214
|
function UploadIcon() {
|
|
2140
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" }) });
|
|
2141
2216
|
}
|
|
2142
|
-
function
|
|
2217
|
+
function ScanIcon({ spinning }) {
|
|
2143
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" }) });
|
|
2144
2219
|
}
|
|
2145
2220
|
function TrashIcon() {
|
|
@@ -2273,6 +2348,7 @@ function useFileList() {
|
|
|
2273
2348
|
} = useStudio();
|
|
2274
2349
|
const [items, setItems] = _react.useState.call(void 0, []);
|
|
2275
2350
|
const [loading, setLoading] = _react.useState.call(void 0, true);
|
|
2351
|
+
const [metaEmpty, setMetaEmpty] = _react.useState.call(void 0, false);
|
|
2276
2352
|
const isInitialLoad = _react.useRef.call(void 0, true);
|
|
2277
2353
|
const lastPath = _react.useRef.call(void 0, currentPath);
|
|
2278
2354
|
_react.useEffect.call(void 0, () => {
|
|
@@ -2285,10 +2361,12 @@ function useFileList() {
|
|
|
2285
2361
|
try {
|
|
2286
2362
|
const data = searchQuery && searchQuery.length >= 2 ? await studioApi.search(searchQuery) : await studioApi.list(currentPath);
|
|
2287
2363
|
setItems(data.items || []);
|
|
2364
|
+
setMetaEmpty(data.isEmpty === true);
|
|
2288
2365
|
} catch (error) {
|
|
2289
2366
|
const message = error instanceof Error ? error.message : "Failed to load items";
|
|
2290
2367
|
showError("Load Error", message);
|
|
2291
2368
|
setItems([]);
|
|
2369
|
+
setMetaEmpty(false);
|
|
2292
2370
|
}
|
|
2293
2371
|
setLoading(false);
|
|
2294
2372
|
isInitialLoad.current = false;
|
|
@@ -2340,6 +2418,7 @@ function useFileList() {
|
|
|
2340
2418
|
items,
|
|
2341
2419
|
loading,
|
|
2342
2420
|
sortedItems,
|
|
2421
|
+
metaEmpty,
|
|
2343
2422
|
// Computed
|
|
2344
2423
|
isAtRoot,
|
|
2345
2424
|
isSearching,
|
|
@@ -2400,6 +2479,27 @@ var styles5 = {
|
|
|
2400
2479
|
font-size: ${_chunkUFCWGUAGjs.fontSize.sm};
|
|
2401
2480
|
}
|
|
2402
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
|
+
`,
|
|
2403
2503
|
grid: _react3.css`
|
|
2404
2504
|
display: grid;
|
|
2405
2505
|
grid-template-columns: 1fr;
|
|
@@ -2683,6 +2783,7 @@ function StudioFileGrid() {
|
|
|
2683
2783
|
const {
|
|
2684
2784
|
loading,
|
|
2685
2785
|
sortedItems,
|
|
2786
|
+
metaEmpty,
|
|
2686
2787
|
isAtRoot,
|
|
2687
2788
|
isSearching,
|
|
2688
2789
|
allItemsSelected,
|
|
@@ -2694,14 +2795,42 @@ function StudioFileGrid() {
|
|
|
2694
2795
|
handleGenerateThumbnail,
|
|
2695
2796
|
handleSelectAll
|
|
2696
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
|
+
};
|
|
2697
2810
|
if (loading) {
|
|
2698
2811
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles5.loading, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles5.spinner }) });
|
|
2699
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
|
+
}
|
|
2700
2829
|
if (sortedItems.length === 0 && isAtRoot) {
|
|
2701
2830
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.empty, children: [
|
|
2702
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" }) }),
|
|
2703
2832
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles5.emptyText, children: "No files in this folder" }),
|
|
2704
|
-
/* @__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" })
|
|
2705
2834
|
] });
|
|
2706
2835
|
}
|
|
2707
2836
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { children: [
|
|
@@ -2898,6 +3027,32 @@ var styles6 = {
|
|
|
2898
3027
|
height: 256px;
|
|
2899
3028
|
color: ${_chunkUFCWGUAGjs.colors.textSecondary};
|
|
2900
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
|
+
`,
|
|
2901
3056
|
tableWrapper: _react3.css`
|
|
2902
3057
|
background: ${_chunkUFCWGUAGjs.colors.surface};
|
|
2903
3058
|
border-radius: 8px;
|
|
@@ -3184,6 +3339,7 @@ function StudioFileList() {
|
|
|
3184
3339
|
const {
|
|
3185
3340
|
loading,
|
|
3186
3341
|
sortedItems,
|
|
3342
|
+
metaEmpty,
|
|
3187
3343
|
isAtRoot,
|
|
3188
3344
|
isSearching,
|
|
3189
3345
|
allItemsSelected,
|
|
@@ -3195,11 +3351,41 @@ function StudioFileList() {
|
|
|
3195
3351
|
handleGenerateThumbnail,
|
|
3196
3352
|
handleSelectAll
|
|
3197
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
|
+
};
|
|
3198
3366
|
if (loading) {
|
|
3199
3367
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles6.loading, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles6.spinner }) });
|
|
3200
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
|
+
}
|
|
3201
3384
|
if (sortedItems.length === 0 && isAtRoot) {
|
|
3202
|
-
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
|
+
] });
|
|
3203
3389
|
}
|
|
3204
3390
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles6.tableWrapper, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "table", { css: styles6.table, children: [
|
|
3205
3391
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "thead", { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "tr", { children: [
|
|
@@ -3709,7 +3895,7 @@ function StudioDetailView() {
|
|
|
3709
3895
|
});
|
|
3710
3896
|
triggerRefresh();
|
|
3711
3897
|
} else {
|
|
3712
|
-
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")])) {
|
|
3713
3899
|
setShowR2SetupModal(true);
|
|
3714
3900
|
} else {
|
|
3715
3901
|
setAlertMessage({
|
|
@@ -3748,7 +3934,7 @@ function StudioDetailView() {
|
|
|
3748
3934
|
if (!response.ok) {
|
|
3749
3935
|
throw new Error("Processing failed");
|
|
3750
3936
|
}
|
|
3751
|
-
const reader = _optionalChain([response, 'access',
|
|
3937
|
+
const reader = _optionalChain([response, 'access', _50 => _50.body, 'optionalAccess', _51 => _51.getReader, 'call', _52 => _52()]);
|
|
3752
3938
|
if (!reader) {
|
|
3753
3939
|
throw new Error("No response body");
|
|
3754
3940
|
}
|
|
@@ -4695,4 +4881,4 @@ var StudioUI_default = StudioUI;
|
|
|
4695
4881
|
|
|
4696
4882
|
|
|
4697
4883
|
exports.StudioUI = StudioUI; exports.default = StudioUI_default;
|
|
4698
|
-
//# sourceMappingURL=StudioUI-
|
|
4884
|
+
//# sourceMappingURL=StudioUI-JQHRTF45.js.map
|