@gallop.software/studio 0.1.80 → 0.1.82
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-VPNL5NMI.mjs → StudioUI-VJVOSOPD.mjs} +485 -402
- package/dist/StudioUI-VJVOSOPD.mjs.map +1 -0
- package/dist/{StudioUI-OAZ7CTB4.js → StudioUI-YFDO5MGG.js} +420 -337
- package/dist/StudioUI-YFDO5MGG.js.map +1 -0
- package/dist/{handlers.d.ts → handlers/index.d.mts} +1 -1
- package/dist/{handlers.d.mts → handlers/index.d.ts} +1 -1
- package/dist/{handlers.js → handlers/index.js} +611 -579
- package/dist/handlers/index.js.map +1 -0
- package/dist/{handlers.mjs → handlers/index.mjs} +620 -588
- package/dist/handlers/index.mjs.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +4 -4
- package/dist/StudioUI-OAZ7CTB4.js.map +0 -1
- package/dist/StudioUI-VPNL5NMI.mjs.map +0 -1
- package/dist/handlers.js.map +0 -1
- package/dist/handlers.mjs.map +0 -1
|
@@ -52,6 +52,11 @@ var defaultState = {
|
|
|
52
52
|
},
|
|
53
53
|
searchQuery: "",
|
|
54
54
|
setSearchQuery: () => {
|
|
55
|
+
},
|
|
56
|
+
error: null,
|
|
57
|
+
showError: () => {
|
|
58
|
+
},
|
|
59
|
+
clearError: () => {
|
|
55
60
|
}
|
|
56
61
|
};
|
|
57
62
|
var StudioContext = _react.createContext.call(void 0, defaultState);
|
|
@@ -891,6 +896,7 @@ function StudioToolbar() {
|
|
|
891
896
|
const [imagesToProcess, setImagesToProcess] = _react.useState.call(void 0, []);
|
|
892
897
|
const [alertMessage, setAlertMessage] = _react.useState.call(void 0, null);
|
|
893
898
|
const [showNewFolderModal, setShowNewFolderModal] = _react.useState.call(void 0, false);
|
|
899
|
+
const [showRenameFolderModal, setShowRenameFolderModal] = _react.useState.call(void 0, false);
|
|
894
900
|
const [showMoveModal, setShowMoveModal] = _react.useState.call(void 0, false);
|
|
895
901
|
const isInImagesFolder = currentPath === "public/images" || currentPath.startsWith("public/images/");
|
|
896
902
|
const handleUpload = _react.useCallback.call(void 0, () => {
|
|
@@ -947,13 +953,13 @@ function StudioToolbar() {
|
|
|
947
953
|
const handleProcessImages = _react.useCallback.call(void 0, async () => {
|
|
948
954
|
const hasSelection2 = selectedItems.size > 0;
|
|
949
955
|
if (hasSelection2) {
|
|
950
|
-
const
|
|
956
|
+
const selectedPaths2 = Array.from(selectedItems);
|
|
951
957
|
const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"];
|
|
952
|
-
const selectedImagePaths =
|
|
958
|
+
const selectedImagePaths = selectedPaths2.filter((p) => {
|
|
953
959
|
const ext = _optionalChain([p, 'access', _5 => _5.split, 'call', _6 => _6("."), 'access', _7 => _7.pop, 'call', _8 => _8(), 'optionalAccess', _9 => _9.toLowerCase, 'call', _10 => _10()]) || "";
|
|
954
960
|
return imageExtensions.includes(ext);
|
|
955
961
|
});
|
|
956
|
-
const selectedFolders =
|
|
962
|
+
const selectedFolders = selectedPaths2.filter((p) => !p.includes(".") || p.endsWith("/"));
|
|
957
963
|
if (selectedFolders.length > 0) {
|
|
958
964
|
try {
|
|
959
965
|
const response = await fetch(`/api/studio/folder-images?folders=${encodeURIComponent(selectedFolders.join(","))}`);
|
|
@@ -1263,6 +1269,27 @@ function StudioToolbar() {
|
|
|
1263
1269
|
}
|
|
1264
1270
|
}, [setSearchQuery]);
|
|
1265
1271
|
const hasSelection = selectedItems.size > 0;
|
|
1272
|
+
const selectedPaths = Array.from(selectedItems);
|
|
1273
|
+
const singleFolderSelected = selectedPaths.length === 1 && !selectedPaths[0].includes(".");
|
|
1274
|
+
const selectedFolderPath = singleFolderSelected ? selectedPaths[0] : null;
|
|
1275
|
+
const selectedFolderName = selectedFolderPath ? selectedFolderPath.split("/").pop() || "" : "";
|
|
1276
|
+
const handleRenameFolder = _react.useCallback.call(void 0, async (newName) => {
|
|
1277
|
+
if (!selectedFolderPath) return;
|
|
1278
|
+
setShowRenameFolderModal(false);
|
|
1279
|
+
try {
|
|
1280
|
+
const response = await fetch("/api/studio/rename", {
|
|
1281
|
+
method: "POST",
|
|
1282
|
+
headers: { "Content-Type": "application/json" },
|
|
1283
|
+
body: JSON.stringify({ oldPath: selectedFolderPath, newName })
|
|
1284
|
+
});
|
|
1285
|
+
if (response.ok) {
|
|
1286
|
+
clearSelection();
|
|
1287
|
+
triggerRefresh();
|
|
1288
|
+
}
|
|
1289
|
+
} catch (error) {
|
|
1290
|
+
console.error("Failed to rename folder:", error);
|
|
1291
|
+
}
|
|
1292
|
+
}, [selectedFolderPath, clearSelection, triggerRefresh]);
|
|
1266
1293
|
if (focusedItem) {
|
|
1267
1294
|
return null;
|
|
1268
1295
|
}
|
|
@@ -1328,6 +1355,18 @@ function StudioToolbar() {
|
|
|
1328
1355
|
onCancel: () => setShowMoveModal(false)
|
|
1329
1356
|
}
|
|
1330
1357
|
),
|
|
1358
|
+
showRenameFolderModal && selectedFolderPath && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1359
|
+
InputModal,
|
|
1360
|
+
{
|
|
1361
|
+
title: "Rename Folder",
|
|
1362
|
+
message: "Enter a new name for the folder:",
|
|
1363
|
+
placeholder: selectedFolderName,
|
|
1364
|
+
defaultValue: selectedFolderName,
|
|
1365
|
+
confirmLabel: "Rename",
|
|
1366
|
+
onConfirm: handleRenameFolder,
|
|
1367
|
+
onCancel: () => setShowRenameFolderModal(false)
|
|
1368
|
+
}
|
|
1369
|
+
),
|
|
1331
1370
|
alertMessage && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1332
1371
|
AlertModal,
|
|
1333
1372
|
{
|
|
@@ -1365,12 +1404,12 @@ function StudioToolbar() {
|
|
|
1365
1404
|
"button",
|
|
1366
1405
|
{
|
|
1367
1406
|
css: styles3.btn,
|
|
1368
|
-
onClick: () => setShowNewFolderModal(true),
|
|
1369
|
-
disabled: isInImagesFolder,
|
|
1370
|
-
title: isInImagesFolder ? "Cannot create folders in protected images folder" : void 0,
|
|
1407
|
+
onClick: () => singleFolderSelected ? setShowRenameFolderModal(true) : setShowNewFolderModal(true),
|
|
1408
|
+
disabled: isInImagesFolder && !singleFolderSelected,
|
|
1409
|
+
title: isInImagesFolder && !singleFolderSelected ? "Cannot create folders in protected images folder" : void 0,
|
|
1371
1410
|
children: [
|
|
1372
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, FolderPlusIcon, {}),
|
|
1373
|
-
"New Folder"
|
|
1411
|
+
singleFolderSelected ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, RenameIcon, {}) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FolderPlusIcon, {}),
|
|
1412
|
+
singleFolderSelected ? "Rename Folder" : "New Folder"
|
|
1374
1413
|
]
|
|
1375
1414
|
}
|
|
1376
1415
|
),
|
|
@@ -1497,6 +1536,9 @@ function TrashIcon() {
|
|
|
1497
1536
|
function FolderPlusIcon() {
|
|
1498
1537
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles3.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: "M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z" }) });
|
|
1499
1538
|
}
|
|
1539
|
+
function RenameIcon() {
|
|
1540
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles3.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: "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" }) });
|
|
1541
|
+
}
|
|
1500
1542
|
function MoveIcon() {
|
|
1501
1543
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles3.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: "M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" }) });
|
|
1502
1544
|
}
|
|
@@ -1517,6 +1559,194 @@ function ImageStackIcon() {
|
|
|
1517
1559
|
|
|
1518
1560
|
|
|
1519
1561
|
|
|
1562
|
+
// src/hooks/useFileList.ts
|
|
1563
|
+
|
|
1564
|
+
|
|
1565
|
+
// src/lib/api.ts
|
|
1566
|
+
var StudioApiClient = class {
|
|
1567
|
+
async get(url) {
|
|
1568
|
+
const response = await fetch(url);
|
|
1569
|
+
if (!response.ok) {
|
|
1570
|
+
const data = await response.json().catch(() => ({}));
|
|
1571
|
+
throw new Error(data.error || `Request failed: ${response.status}`);
|
|
1572
|
+
}
|
|
1573
|
+
return response.json();
|
|
1574
|
+
}
|
|
1575
|
+
async post(url, body) {
|
|
1576
|
+
const response = await fetch(url, {
|
|
1577
|
+
method: "POST",
|
|
1578
|
+
headers: body ? { "Content-Type": "application/json" } : void 0,
|
|
1579
|
+
body: body ? JSON.stringify(body) : void 0
|
|
1580
|
+
});
|
|
1581
|
+
if (!response.ok) {
|
|
1582
|
+
const data = await response.json().catch(() => ({}));
|
|
1583
|
+
throw new Error(data.error || `Request failed: ${response.status}`);
|
|
1584
|
+
}
|
|
1585
|
+
return response.json();
|
|
1586
|
+
}
|
|
1587
|
+
// List handlers
|
|
1588
|
+
async list(path = "public") {
|
|
1589
|
+
return this.get(`/api/studio/list?path=${encodeURIComponent(path)}`);
|
|
1590
|
+
}
|
|
1591
|
+
async search(query) {
|
|
1592
|
+
return this.get(`/api/studio/search?q=${encodeURIComponent(query)}`);
|
|
1593
|
+
}
|
|
1594
|
+
async listFolders() {
|
|
1595
|
+
return this.get("/api/studio/list-folders");
|
|
1596
|
+
}
|
|
1597
|
+
async countImages() {
|
|
1598
|
+
return this.get("/api/studio/count-images");
|
|
1599
|
+
}
|
|
1600
|
+
async folderImages(folders) {
|
|
1601
|
+
return this.get(`/api/studio/folder-images?folders=${encodeURIComponent(folders.join(","))}`);
|
|
1602
|
+
}
|
|
1603
|
+
// File handlers
|
|
1604
|
+
async upload(file, targetPath = "public") {
|
|
1605
|
+
const formData = new FormData();
|
|
1606
|
+
formData.append("file", file);
|
|
1607
|
+
formData.append("path", targetPath);
|
|
1608
|
+
const response = await fetch("/api/studio/upload", {
|
|
1609
|
+
method: "POST",
|
|
1610
|
+
body: formData
|
|
1611
|
+
});
|
|
1612
|
+
if (!response.ok) {
|
|
1613
|
+
const data = await response.json().catch(() => ({}));
|
|
1614
|
+
throw new Error(data.error || `Upload failed: ${response.status}`);
|
|
1615
|
+
}
|
|
1616
|
+
return response.json();
|
|
1617
|
+
}
|
|
1618
|
+
async delete(paths) {
|
|
1619
|
+
return this.post("/api/studio/delete", { paths });
|
|
1620
|
+
}
|
|
1621
|
+
async createFolder(parentPath, name) {
|
|
1622
|
+
return this.post("/api/studio/create-folder", { parentPath, name });
|
|
1623
|
+
}
|
|
1624
|
+
async rename(oldPath, newName) {
|
|
1625
|
+
return this.post("/api/studio/rename", { oldPath, newName });
|
|
1626
|
+
}
|
|
1627
|
+
async move(paths, destination) {
|
|
1628
|
+
return this.post("/api/studio/move", { paths, destination });
|
|
1629
|
+
}
|
|
1630
|
+
// Image handlers
|
|
1631
|
+
async sync(imageKeys) {
|
|
1632
|
+
return this.post("/api/studio/sync", { imageKeys });
|
|
1633
|
+
}
|
|
1634
|
+
async reprocess(imageKeys) {
|
|
1635
|
+
return this.post("/api/studio/reprocess", { imageKeys });
|
|
1636
|
+
}
|
|
1637
|
+
// Process all returns a stream, handle separately
|
|
1638
|
+
processAllStream() {
|
|
1639
|
+
return new EventSource("/api/studio/process-all");
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
var studioApi = new StudioApiClient();
|
|
1643
|
+
|
|
1644
|
+
// src/hooks/useFileList.ts
|
|
1645
|
+
function useFileList() {
|
|
1646
|
+
const {
|
|
1647
|
+
currentPath,
|
|
1648
|
+
setCurrentPath,
|
|
1649
|
+
navigateUp,
|
|
1650
|
+
selectedItems,
|
|
1651
|
+
toggleSelection,
|
|
1652
|
+
selectRange,
|
|
1653
|
+
lastSelectedPath,
|
|
1654
|
+
selectAll,
|
|
1655
|
+
clearSelection,
|
|
1656
|
+
refreshKey,
|
|
1657
|
+
setFocusedItem,
|
|
1658
|
+
triggerRefresh,
|
|
1659
|
+
searchQuery,
|
|
1660
|
+
showError
|
|
1661
|
+
} = useStudio();
|
|
1662
|
+
const [items, setItems] = _react.useState.call(void 0, []);
|
|
1663
|
+
const [loading, setLoading] = _react.useState.call(void 0, true);
|
|
1664
|
+
const isInitialLoad = _react.useRef.call(void 0, true);
|
|
1665
|
+
const lastPath = _react.useRef.call(void 0, currentPath);
|
|
1666
|
+
_react.useEffect.call(void 0, () => {
|
|
1667
|
+
async function loadItems() {
|
|
1668
|
+
const isPathChange = lastPath.current !== currentPath;
|
|
1669
|
+
if (isInitialLoad.current || isPathChange) {
|
|
1670
|
+
setLoading(true);
|
|
1671
|
+
}
|
|
1672
|
+
lastPath.current = currentPath;
|
|
1673
|
+
try {
|
|
1674
|
+
const data = searchQuery && searchQuery.length >= 2 ? await studioApi.search(searchQuery) : await studioApi.list(currentPath);
|
|
1675
|
+
setItems(data.items || []);
|
|
1676
|
+
} catch (error) {
|
|
1677
|
+
const message = error instanceof Error ? error.message : "Failed to load items";
|
|
1678
|
+
showError("Load Error", message);
|
|
1679
|
+
setItems([]);
|
|
1680
|
+
}
|
|
1681
|
+
setLoading(false);
|
|
1682
|
+
isInitialLoad.current = false;
|
|
1683
|
+
}
|
|
1684
|
+
loadItems();
|
|
1685
|
+
}, [currentPath, refreshKey, searchQuery, showError]);
|
|
1686
|
+
const isAtRoot = currentPath === "public";
|
|
1687
|
+
const isSearching = searchQuery && searchQuery.length >= 2;
|
|
1688
|
+
const sortedItems = [...items].sort((a, b) => {
|
|
1689
|
+
if (a.type === "folder" && b.type !== "folder") return -1;
|
|
1690
|
+
if (a.type !== "folder" && b.type === "folder") return 1;
|
|
1691
|
+
return a.name.localeCompare(b.name);
|
|
1692
|
+
});
|
|
1693
|
+
const allItemsSelected = sortedItems.length > 0 && sortedItems.every((item) => selectedItems.has(item.path));
|
|
1694
|
+
const someItemsSelected = sortedItems.some((item) => selectedItems.has(item.path));
|
|
1695
|
+
const handleItemClick = _react.useCallback.call(void 0, (item, e) => {
|
|
1696
|
+
if (e.shiftKey && lastSelectedPath) {
|
|
1697
|
+
selectRange(lastSelectedPath, item.path, sortedItems);
|
|
1698
|
+
} else {
|
|
1699
|
+
toggleSelection(item.path);
|
|
1700
|
+
}
|
|
1701
|
+
}, [lastSelectedPath, selectRange, sortedItems, toggleSelection]);
|
|
1702
|
+
const handleOpen = _react.useCallback.call(void 0, (item) => {
|
|
1703
|
+
if (item.type === "folder") {
|
|
1704
|
+
setCurrentPath(item.path);
|
|
1705
|
+
} else {
|
|
1706
|
+
setFocusedItem(item);
|
|
1707
|
+
}
|
|
1708
|
+
}, [setCurrentPath, setFocusedItem]);
|
|
1709
|
+
const handleGenerateThumbnail = _react.useCallback.call(void 0, async (item) => {
|
|
1710
|
+
try {
|
|
1711
|
+
const imageKey = "/" + item.path.replace(/^public\//, "");
|
|
1712
|
+
await studioApi.reprocess([imageKey]);
|
|
1713
|
+
triggerRefresh();
|
|
1714
|
+
} catch (error) {
|
|
1715
|
+
const message = error instanceof Error ? error.message : "Failed to generate thumbnail";
|
|
1716
|
+
showError("Processing Error", message);
|
|
1717
|
+
}
|
|
1718
|
+
}, [triggerRefresh, showError]);
|
|
1719
|
+
const handleSelectAll = _react.useCallback.call(void 0, () => {
|
|
1720
|
+
if (allItemsSelected) {
|
|
1721
|
+
clearSelection();
|
|
1722
|
+
} else {
|
|
1723
|
+
selectAll(sortedItems);
|
|
1724
|
+
}
|
|
1725
|
+
}, [allItemsSelected, clearSelection, selectAll, sortedItems]);
|
|
1726
|
+
return {
|
|
1727
|
+
// State
|
|
1728
|
+
items,
|
|
1729
|
+
loading,
|
|
1730
|
+
sortedItems,
|
|
1731
|
+
// Computed
|
|
1732
|
+
isAtRoot,
|
|
1733
|
+
isSearching,
|
|
1734
|
+
allItemsSelected,
|
|
1735
|
+
someItemsSelected,
|
|
1736
|
+
// Context values
|
|
1737
|
+
currentPath,
|
|
1738
|
+
selectedItems,
|
|
1739
|
+
navigateUp,
|
|
1740
|
+
// Handlers
|
|
1741
|
+
handleItemClick,
|
|
1742
|
+
handleOpen,
|
|
1743
|
+
handleGenerateThumbnail,
|
|
1744
|
+
handleSelectAll
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
// src/components/StudioFileGrid.tsx
|
|
1749
|
+
|
|
1520
1750
|
var spin2 = _react3.keyframes`
|
|
1521
1751
|
to { transform: rotate(360deg); }
|
|
1522
1752
|
`;
|
|
@@ -1582,10 +1812,6 @@ var styles4 = {
|
|
|
1582
1812
|
&:hover {
|
|
1583
1813
|
border-color: #d0d5dd;
|
|
1584
1814
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.06);
|
|
1585
|
-
|
|
1586
|
-
button[title="Rename"] {
|
|
1587
|
-
opacity: 1;
|
|
1588
|
-
}
|
|
1589
1815
|
}
|
|
1590
1816
|
`,
|
|
1591
1817
|
itemSelected: _react3.css`
|
|
@@ -1745,35 +1971,6 @@ var styles4 = {
|
|
|
1745
1971
|
color: ${_chunkUFCWGUAGjs.colors.text};
|
|
1746
1972
|
}
|
|
1747
1973
|
`,
|
|
1748
|
-
nameRow: _react3.css`
|
|
1749
|
-
display: flex;
|
|
1750
|
-
align-items: center;
|
|
1751
|
-
gap: 4px;
|
|
1752
|
-
`,
|
|
1753
|
-
renameBtn: _react3.css`
|
|
1754
|
-
flex-shrink: 0;
|
|
1755
|
-
height: 20px;
|
|
1756
|
-
width: 20px;
|
|
1757
|
-
color: ${_chunkUFCWGUAGjs.colors.textMuted};
|
|
1758
|
-
background: transparent;
|
|
1759
|
-
border: none;
|
|
1760
|
-
padding: 0;
|
|
1761
|
-
cursor: pointer;
|
|
1762
|
-
border-radius: 4px;
|
|
1763
|
-
transition: all 0.15s ease;
|
|
1764
|
-
display: flex;
|
|
1765
|
-
align-items: center;
|
|
1766
|
-
justify-content: center;
|
|
1767
|
-
opacity: 0;
|
|
1768
|
-
|
|
1769
|
-
&:hover {
|
|
1770
|
-
color: ${_chunkUFCWGUAGjs.colors.text};
|
|
1771
|
-
}
|
|
1772
|
-
`,
|
|
1773
|
-
renameIcon: _react3.css`
|
|
1774
|
-
width: 14px;
|
|
1775
|
-
height: 14px;
|
|
1776
|
-
`,
|
|
1777
1974
|
copyIcon: _react3.css`
|
|
1778
1975
|
width: 18px;
|
|
1779
1976
|
height: 18px;
|
|
@@ -1871,116 +2068,31 @@ var styles4 = {
|
|
|
1871
2068
|
`
|
|
1872
2069
|
};
|
|
1873
2070
|
function StudioFileGrid() {
|
|
1874
|
-
const {
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
const url = searchQuery && searchQuery.length >= 2 ? `/api/studio/search?q=${encodeURIComponent(searchQuery)}` : `/api/studio/list?path=${encodeURIComponent(currentPath)}`;
|
|
1889
|
-
const response = await fetch(url);
|
|
1890
|
-
if (response.ok) {
|
|
1891
|
-
const data = await response.json();
|
|
1892
|
-
setItems(data.items || []);
|
|
1893
|
-
}
|
|
1894
|
-
} catch (error) {
|
|
1895
|
-
console.error("Failed to load items:", error);
|
|
1896
|
-
}
|
|
1897
|
-
setLoading(false);
|
|
1898
|
-
isInitialLoad.current = false;
|
|
1899
|
-
}
|
|
1900
|
-
loadItems();
|
|
1901
|
-
}, [currentPath, refreshKey, searchQuery]);
|
|
2071
|
+
const {
|
|
2072
|
+
loading,
|
|
2073
|
+
sortedItems,
|
|
2074
|
+
isAtRoot,
|
|
2075
|
+
isSearching,
|
|
2076
|
+
allItemsSelected,
|
|
2077
|
+
someItemsSelected,
|
|
2078
|
+
selectedItems,
|
|
2079
|
+
navigateUp,
|
|
2080
|
+
handleItemClick,
|
|
2081
|
+
handleOpen,
|
|
2082
|
+
handleGenerateThumbnail,
|
|
2083
|
+
handleSelectAll
|
|
2084
|
+
} = useFileList();
|
|
1902
2085
|
if (loading) {
|
|
1903
2086
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles4.loading, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles4.spinner }) });
|
|
1904
2087
|
}
|
|
1905
|
-
|
|
1906
|
-
if (items.length === 0 && isAtRoot) {
|
|
2088
|
+
if (sortedItems.length === 0 && isAtRoot) {
|
|
1907
2089
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles4.empty, children: [
|
|
1908
2090
|
/* @__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" }) }),
|
|
1909
2091
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles4.emptyText, children: "No files in this folder" }),
|
|
1910
2092
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles4.emptyText, children: "Upload images to get started" })
|
|
1911
2093
|
] });
|
|
1912
2094
|
}
|
|
1913
|
-
const isSearching = searchQuery && searchQuery.length >= 2;
|
|
1914
|
-
const sortedItems = [...items].sort((a, b) => {
|
|
1915
|
-
if (a.type === "folder" && b.type !== "folder") return -1;
|
|
1916
|
-
if (a.type !== "folder" && b.type === "folder") return 1;
|
|
1917
|
-
return a.name.localeCompare(b.name);
|
|
1918
|
-
});
|
|
1919
|
-
const handleItemClick = (item, e) => {
|
|
1920
|
-
if (e.shiftKey && lastSelectedPath) {
|
|
1921
|
-
selectRange(lastSelectedPath, item.path, sortedItems);
|
|
1922
|
-
} else {
|
|
1923
|
-
toggleSelection(item.path);
|
|
1924
|
-
}
|
|
1925
|
-
};
|
|
1926
|
-
const handleOpen = (item) => {
|
|
1927
|
-
if (item.type === "folder") {
|
|
1928
|
-
setCurrentPath(item.path);
|
|
1929
|
-
} else {
|
|
1930
|
-
setFocusedItem(item);
|
|
1931
|
-
}
|
|
1932
|
-
};
|
|
1933
|
-
const handleGenerateThumbnail = async (item) => {
|
|
1934
|
-
try {
|
|
1935
|
-
const imageKey = item.path.replace(/^public\//, "");
|
|
1936
|
-
await fetch("/api/studio/reprocess", {
|
|
1937
|
-
method: "POST",
|
|
1938
|
-
headers: { "Content-Type": "application/json" },
|
|
1939
|
-
body: JSON.stringify({ imageKeys: [imageKey] })
|
|
1940
|
-
});
|
|
1941
|
-
triggerRefresh();
|
|
1942
|
-
} catch (error) {
|
|
1943
|
-
console.error("Failed to generate thumbnail:", error);
|
|
1944
|
-
}
|
|
1945
|
-
};
|
|
1946
|
-
const handleRename = async (newName) => {
|
|
1947
|
-
if (!renameItem) return;
|
|
1948
|
-
setRenameItem(null);
|
|
1949
|
-
try {
|
|
1950
|
-
const response = await fetch("/api/studio/rename", {
|
|
1951
|
-
method: "POST",
|
|
1952
|
-
headers: { "Content-Type": "application/json" },
|
|
1953
|
-
body: JSON.stringify({ oldPath: renameItem.path, newName })
|
|
1954
|
-
});
|
|
1955
|
-
if (response.ok) {
|
|
1956
|
-
triggerRefresh();
|
|
1957
|
-
}
|
|
1958
|
-
} catch (error) {
|
|
1959
|
-
console.error("Failed to rename:", error);
|
|
1960
|
-
}
|
|
1961
|
-
};
|
|
1962
|
-
const allItemsSelected = sortedItems.length > 0 && sortedItems.every((item) => selectedItems.has(item.path));
|
|
1963
|
-
const someItemsSelected = sortedItems.some((item) => selectedItems.has(item.path));
|
|
1964
|
-
const handleSelectAll = () => {
|
|
1965
|
-
if (allItemsSelected) {
|
|
1966
|
-
clearSelection();
|
|
1967
|
-
} else {
|
|
1968
|
-
selectAll(sortedItems);
|
|
1969
|
-
}
|
|
1970
|
-
};
|
|
1971
2095
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { children: [
|
|
1972
|
-
renameItem && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1973
|
-
InputModal,
|
|
1974
|
-
{
|
|
1975
|
-
title: renameItem.type === "folder" ? "Rename Folder" : "Rename File",
|
|
1976
|
-
message: "Enter a new name:",
|
|
1977
|
-
placeholder: renameItem.name,
|
|
1978
|
-
defaultValue: renameItem.name,
|
|
1979
|
-
confirmLabel: "Rename",
|
|
1980
|
-
onConfirm: handleRename,
|
|
1981
|
-
onCancel: () => setRenameItem(null)
|
|
1982
|
-
}
|
|
1983
|
-
),
|
|
1984
2096
|
sortedItems.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles4.selectAllRow, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "label", { css: styles4.selectAllLabel, children: [
|
|
1985
2097
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1986
2098
|
"input",
|
|
@@ -2020,15 +2132,14 @@ function StudioFileGrid() {
|
|
|
2020
2132
|
isSelected: selectedItems.has(item.path),
|
|
2021
2133
|
onClick: (e) => handleItemClick(item, e),
|
|
2022
2134
|
onOpen: () => handleOpen(item),
|
|
2023
|
-
onGenerateThumbnail: () => handleGenerateThumbnail(item)
|
|
2024
|
-
onRename: () => setRenameItem(item)
|
|
2135
|
+
onGenerateThumbnail: () => handleGenerateThumbnail(item)
|
|
2025
2136
|
},
|
|
2026
2137
|
item.path
|
|
2027
2138
|
))
|
|
2028
2139
|
] })
|
|
2029
2140
|
] });
|
|
2030
2141
|
}
|
|
2031
|
-
function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail
|
|
2142
|
+
function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
|
|
2032
2143
|
const [showCopied, setShowCopied] = _react.useState.call(void 0, false);
|
|
2033
2144
|
const isFolder = item.type === "folder";
|
|
2034
2145
|
const isImage = !isFolder && item.thumbnail !== void 0;
|
|
@@ -2115,21 +2226,7 @@ function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail, onRe
|
|
|
2115
2226
|
) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles4.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: "M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" }) })
|
|
2116
2227
|
] }),
|
|
2117
2228
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles4.label, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles4.labelRow, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles4.labelText, children: [
|
|
2118
|
-
/* @__PURE__ */ _jsxruntime.
|
|
2119
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles4.name, title: item.name, children: truncateMiddle(item.name) }),
|
|
2120
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2121
|
-
"button",
|
|
2122
|
-
{
|
|
2123
|
-
css: styles4.renameBtn,
|
|
2124
|
-
onClick: (e) => {
|
|
2125
|
-
e.stopPropagation();
|
|
2126
|
-
onRename();
|
|
2127
|
-
},
|
|
2128
|
-
title: "Rename",
|
|
2129
|
-
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles4.renameIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" }) })
|
|
2130
|
-
}
|
|
2131
|
-
)
|
|
2132
|
-
] }),
|
|
2229
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles4.name, title: item.name, children: truncateMiddle(item.name) }),
|
|
2133
2230
|
isFolder ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { css: styles4.size, children: [
|
|
2134
2231
|
item.fileCount !== void 0 ? `${item.fileCount} files` : "",
|
|
2135
2232
|
item.fileCount !== void 0 && item.totalSize !== void 0 ? " \xB7 " : "",
|
|
@@ -2472,159 +2569,71 @@ var styles5 = {
|
|
|
2472
2569
|
`
|
|
2473
2570
|
};
|
|
2474
2571
|
function StudioFileList() {
|
|
2475
|
-
const {
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
const url = searchQuery && searchQuery.length >= 2 ? `/api/studio/search?q=${encodeURIComponent(searchQuery)}` : `/api/studio/list?path=${encodeURIComponent(currentPath)}`;
|
|
2490
|
-
const response = await fetch(url);
|
|
2491
|
-
if (response.ok) {
|
|
2492
|
-
const data = await response.json();
|
|
2493
|
-
setItems(data.items || []);
|
|
2494
|
-
}
|
|
2495
|
-
} catch (error) {
|
|
2496
|
-
console.error("Failed to load items:", error);
|
|
2497
|
-
}
|
|
2498
|
-
setLoading(false);
|
|
2499
|
-
isInitialLoad.current = false;
|
|
2500
|
-
}
|
|
2501
|
-
loadItems();
|
|
2502
|
-
}, [currentPath, refreshKey, searchQuery]);
|
|
2572
|
+
const {
|
|
2573
|
+
loading,
|
|
2574
|
+
sortedItems,
|
|
2575
|
+
isAtRoot,
|
|
2576
|
+
isSearching,
|
|
2577
|
+
allItemsSelected,
|
|
2578
|
+
someItemsSelected,
|
|
2579
|
+
selectedItems,
|
|
2580
|
+
navigateUp,
|
|
2581
|
+
handleItemClick,
|
|
2582
|
+
handleOpen,
|
|
2583
|
+
handleGenerateThumbnail,
|
|
2584
|
+
handleSelectAll
|
|
2585
|
+
} = useFileList();
|
|
2503
2586
|
if (loading) {
|
|
2504
2587
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles5.loading, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles5.spinner }) });
|
|
2505
2588
|
}
|
|
2506
|
-
|
|
2507
|
-
if (items.length === 0 && isAtRoot) {
|
|
2589
|
+
if (sortedItems.length === 0 && isAtRoot) {
|
|
2508
2590
|
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" }) });
|
|
2509
2591
|
}
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
toggleSelection(item.path);
|
|
2521
|
-
}
|
|
2522
|
-
};
|
|
2523
|
-
const handleOpen = (item) => {
|
|
2524
|
-
if (item.type === "folder") {
|
|
2525
|
-
setCurrentPath(item.path);
|
|
2526
|
-
} else {
|
|
2527
|
-
setFocusedItem(item);
|
|
2528
|
-
}
|
|
2529
|
-
};
|
|
2530
|
-
const handleGenerateThumbnail = async (item) => {
|
|
2531
|
-
try {
|
|
2532
|
-
const imageKey = item.path.replace(/^public\//, "");
|
|
2533
|
-
await fetch("/api/studio/reprocess", {
|
|
2534
|
-
method: "POST",
|
|
2535
|
-
headers: { "Content-Type": "application/json" },
|
|
2536
|
-
body: JSON.stringify({ imageKeys: [imageKey] })
|
|
2537
|
-
});
|
|
2538
|
-
triggerRefresh();
|
|
2539
|
-
} catch (error) {
|
|
2540
|
-
console.error("Failed to generate thumbnail:", error);
|
|
2541
|
-
}
|
|
2542
|
-
};
|
|
2543
|
-
const handleRename = async (newName) => {
|
|
2544
|
-
if (!renameItem) return;
|
|
2545
|
-
setRenameItem(null);
|
|
2546
|
-
try {
|
|
2547
|
-
const response = await fetch("/api/studio/rename", {
|
|
2548
|
-
method: "POST",
|
|
2549
|
-
headers: { "Content-Type": "application/json" },
|
|
2550
|
-
body: JSON.stringify({ oldPath: renameItem.path, newName })
|
|
2551
|
-
});
|
|
2552
|
-
if (response.ok) {
|
|
2553
|
-
triggerRefresh();
|
|
2554
|
-
}
|
|
2555
|
-
} catch (error) {
|
|
2556
|
-
console.error("Failed to rename:", error);
|
|
2557
|
-
}
|
|
2558
|
-
};
|
|
2559
|
-
const allItemsSelected = sortedItems.length > 0 && sortedItems.every((item) => selectedItems.has(item.path));
|
|
2560
|
-
const someItemsSelected = sortedItems.some((item) => selectedItems.has(item.path));
|
|
2561
|
-
const handleSelectAll = () => {
|
|
2562
|
-
if (allItemsSelected) {
|
|
2563
|
-
clearSelection();
|
|
2564
|
-
} else {
|
|
2565
|
-
selectAll(sortedItems);
|
|
2566
|
-
}
|
|
2567
|
-
};
|
|
2568
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.tableWrapper, children: [
|
|
2569
|
-
renameItem && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2570
|
-
InputModal,
|
|
2571
|
-
{
|
|
2572
|
-
title: renameItem.type === "folder" ? "Rename Folder" : "Rename File",
|
|
2573
|
-
message: "Enter a new name:",
|
|
2574
|
-
placeholder: renameItem.name,
|
|
2575
|
-
defaultValue: renameItem.name,
|
|
2576
|
-
confirmLabel: "Rename",
|
|
2577
|
-
onConfirm: handleRename,
|
|
2578
|
-
onCancel: () => setRenameItem(null)
|
|
2579
|
-
}
|
|
2580
|
-
),
|
|
2581
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "table", { css: styles5.table, children: [
|
|
2582
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "thead", { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "tr", { children: [
|
|
2583
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [styles5.th, styles5.thCheckbox], children: sortedItems.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2584
|
-
"input",
|
|
2585
|
-
{
|
|
2586
|
-
type: "checkbox",
|
|
2587
|
-
css: styles5.checkbox,
|
|
2588
|
-
checked: allItemsSelected,
|
|
2589
|
-
ref: (el) => {
|
|
2590
|
-
if (el) el.indeterminate = someItemsSelected && !allItemsSelected;
|
|
2591
|
-
},
|
|
2592
|
-
onChange: handleSelectAll
|
|
2593
|
-
}
|
|
2594
|
-
) }),
|
|
2595
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: styles5.th, children: "Name" }),
|
|
2596
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [styles5.th, styles5.thSize], children: "Size" }),
|
|
2597
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [styles5.th, styles5.thDimensions], children: "Dimensions" }),
|
|
2598
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [styles5.th, styles5.thCdn], children: "CDN" })
|
|
2599
|
-
] }) }),
|
|
2600
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "tbody", { css: styles5.tbody, children: [
|
|
2601
|
-
!isAtRoot && !isSearching && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "tr", { css: styles5.parentRow, onClick: navigateUp, children: [
|
|
2602
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles5.td }),
|
|
2603
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles5.td, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.nameCell, children: [
|
|
2604
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles5.parentIcon, 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: "M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" }) }),
|
|
2605
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles5.name, children: ".." })
|
|
2606
|
-
] }) }),
|
|
2607
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: [styles5.td, styles5.meta], children: "--" }),
|
|
2608
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: [styles5.td, styles5.meta], children: "Parent folder" }),
|
|
2609
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles5.td, children: "--" })
|
|
2610
|
-
] }),
|
|
2611
|
-
sortedItems.map((item) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2612
|
-
ListRow,
|
|
2613
|
-
{
|
|
2614
|
-
item,
|
|
2615
|
-
isSelected: selectedItems.has(item.path),
|
|
2616
|
-
onClick: (e) => handleItemClick(item, e),
|
|
2617
|
-
onOpen: () => handleOpen(item),
|
|
2618
|
-
onGenerateThumbnail: () => handleGenerateThumbnail(item),
|
|
2619
|
-
onRename: () => setRenameItem(item)
|
|
2592
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles5.tableWrapper, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "table", { css: styles5.table, children: [
|
|
2593
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "thead", { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "tr", { children: [
|
|
2594
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [styles5.th, styles5.thCheckbox], children: sortedItems.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2595
|
+
"input",
|
|
2596
|
+
{
|
|
2597
|
+
type: "checkbox",
|
|
2598
|
+
css: styles5.checkbox,
|
|
2599
|
+
checked: allItemsSelected,
|
|
2600
|
+
ref: (el) => {
|
|
2601
|
+
if (el) el.indeterminate = someItemsSelected && !allItemsSelected;
|
|
2620
2602
|
},
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2603
|
+
onChange: handleSelectAll
|
|
2604
|
+
}
|
|
2605
|
+
) }),
|
|
2606
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: styles5.th, children: "Name" }),
|
|
2607
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [styles5.th, styles5.thSize], children: "Size" }),
|
|
2608
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [styles5.th, styles5.thDimensions], children: "Dimensions" }),
|
|
2609
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "th", { css: [styles5.th, styles5.thCdn], children: "CDN" })
|
|
2610
|
+
] }) }),
|
|
2611
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "tbody", { css: styles5.tbody, children: [
|
|
2612
|
+
!isAtRoot && !isSearching && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "tr", { css: styles5.parentRow, onClick: navigateUp, children: [
|
|
2613
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles5.td }),
|
|
2614
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles5.td, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.nameCell, children: [
|
|
2615
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles5.parentIcon, 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: "M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" }) }),
|
|
2616
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles5.name, children: ".." })
|
|
2617
|
+
] }) }),
|
|
2618
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: [styles5.td, styles5.meta], children: "--" }),
|
|
2619
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: [styles5.td, styles5.meta], children: "Parent folder" }),
|
|
2620
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles5.td, children: "--" })
|
|
2621
|
+
] }),
|
|
2622
|
+
sortedItems.map((item) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2623
|
+
ListRow,
|
|
2624
|
+
{
|
|
2625
|
+
item,
|
|
2626
|
+
isSelected: selectedItems.has(item.path),
|
|
2627
|
+
onClick: (e) => handleItemClick(item, e),
|
|
2628
|
+
onOpen: () => handleOpen(item),
|
|
2629
|
+
onGenerateThumbnail: () => handleGenerateThumbnail(item)
|
|
2630
|
+
},
|
|
2631
|
+
item.path
|
|
2632
|
+
))
|
|
2624
2633
|
] })
|
|
2625
|
-
] });
|
|
2634
|
+
] }) });
|
|
2626
2635
|
}
|
|
2627
|
-
function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail
|
|
2636
|
+
function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
|
|
2628
2637
|
const [showCopied, setShowCopied] = _react.useState.call(void 0, false);
|
|
2629
2638
|
const isFolder = item.type === "folder";
|
|
2630
2639
|
const isImage = !isFolder && item.thumbnail !== void 0;
|
|
@@ -2688,18 +2697,6 @@ function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail, onRen
|
|
|
2688
2697
|
]
|
|
2689
2698
|
}
|
|
2690
2699
|
),
|
|
2691
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2692
|
-
"button",
|
|
2693
|
-
{
|
|
2694
|
-
css: styles5.copyBtn,
|
|
2695
|
-
onClick: (e) => {
|
|
2696
|
-
e.stopPropagation();
|
|
2697
|
-
onRename();
|
|
2698
|
-
},
|
|
2699
|
-
title: "Rename",
|
|
2700
|
-
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles5.copyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" }) })
|
|
2701
|
-
}
|
|
2702
|
-
),
|
|
2703
2700
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2704
2701
|
"button",
|
|
2705
2702
|
{
|
|
@@ -3592,10 +3589,85 @@ function SettingsPanel({ onClose }) {
|
|
|
3592
3589
|
] }) });
|
|
3593
3590
|
}
|
|
3594
3591
|
|
|
3592
|
+
// src/components/ErrorModal.tsx
|
|
3593
|
+
|
|
3594
|
+
|
|
3595
|
+
var styles8 = {
|
|
3596
|
+
overlay: _react3.css`
|
|
3597
|
+
position: fixed;
|
|
3598
|
+
inset: 0;
|
|
3599
|
+
background: rgba(0, 0, 0, 0.5);
|
|
3600
|
+
display: flex;
|
|
3601
|
+
align-items: center;
|
|
3602
|
+
justify-content: center;
|
|
3603
|
+
z-index: 1100;
|
|
3604
|
+
`,
|
|
3605
|
+
modal: _react3.css`
|
|
3606
|
+
background: ${_chunkUFCWGUAGjs.colors.surface};
|
|
3607
|
+
border-radius: 12px;
|
|
3608
|
+
padding: 24px;
|
|
3609
|
+
max-width: 400px;
|
|
3610
|
+
width: 90%;
|
|
3611
|
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
|
|
3612
|
+
`,
|
|
3613
|
+
header: _react3.css`
|
|
3614
|
+
display: flex;
|
|
3615
|
+
align-items: center;
|
|
3616
|
+
gap: 12px;
|
|
3617
|
+
margin-bottom: 12px;
|
|
3618
|
+
`,
|
|
3619
|
+
icon: _react3.css`
|
|
3620
|
+
width: 24px;
|
|
3621
|
+
height: 24px;
|
|
3622
|
+
color: ${_chunkUFCWGUAGjs.colors.danger};
|
|
3623
|
+
flex-shrink: 0;
|
|
3624
|
+
`,
|
|
3625
|
+
title: _react3.css`
|
|
3626
|
+
font-size: ${_chunkUFCWGUAGjs.fontSize.lg};
|
|
3627
|
+
font-weight: 600;
|
|
3628
|
+
color: ${_chunkUFCWGUAGjs.colors.text};
|
|
3629
|
+
margin: 0;
|
|
3630
|
+
`,
|
|
3631
|
+
message: _react3.css`
|
|
3632
|
+
font-size: ${_chunkUFCWGUAGjs.fontSize.base};
|
|
3633
|
+
color: ${_chunkUFCWGUAGjs.colors.textSecondary};
|
|
3634
|
+
margin: 0 0 20px 0;
|
|
3635
|
+
line-height: 1.5;
|
|
3636
|
+
`,
|
|
3637
|
+
button: _react3.css`
|
|
3638
|
+
width: 100%;
|
|
3639
|
+
padding: 10px 16px;
|
|
3640
|
+
border-radius: 6px;
|
|
3641
|
+
font-size: ${_chunkUFCWGUAGjs.fontSize.base};
|
|
3642
|
+
font-weight: 500;
|
|
3643
|
+
border: none;
|
|
3644
|
+
background: ${_chunkUFCWGUAGjs.colors.primary};
|
|
3645
|
+
color: white;
|
|
3646
|
+
cursor: pointer;
|
|
3647
|
+
transition: background 0.15s ease;
|
|
3648
|
+
|
|
3649
|
+
&:hover {
|
|
3650
|
+
background: ${_chunkUFCWGUAGjs.colors.primaryHover};
|
|
3651
|
+
}
|
|
3652
|
+
`
|
|
3653
|
+
};
|
|
3654
|
+
function ErrorModal() {
|
|
3655
|
+
const { error, clearError } = useStudio();
|
|
3656
|
+
if (!error) return null;
|
|
3657
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles8.overlay, onClick: clearError, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles8.modal, onClick: (e) => e.stopPropagation(), children: [
|
|
3658
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles8.header, children: [
|
|
3659
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles8.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: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }),
|
|
3660
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { css: styles8.title, children: error.title })
|
|
3661
|
+
] }),
|
|
3662
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles8.message, children: error.message }),
|
|
3663
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: styles8.button, onClick: clearError, children: "OK" })
|
|
3664
|
+
] }) });
|
|
3665
|
+
}
|
|
3666
|
+
|
|
3595
3667
|
// src/components/StudioUI.tsx
|
|
3596
3668
|
|
|
3597
3669
|
var btnHeight3 = "36px";
|
|
3598
|
-
var
|
|
3670
|
+
var styles9 = {
|
|
3599
3671
|
container: _react3.css`
|
|
3600
3672
|
${_chunkUFCWGUAGjs.baseReset}
|
|
3601
3673
|
display: flex;
|
|
@@ -3742,10 +3814,17 @@ function StudioUI({ onClose, isVisible = true }) {
|
|
|
3742
3814
|
const [isLoading, setIsLoading] = _react.useState.call(void 0, false);
|
|
3743
3815
|
const [refreshKey, setRefreshKey] = _react.useState.call(void 0, 0);
|
|
3744
3816
|
const [searchQuery, setSearchQuery] = _react.useState.call(void 0, "");
|
|
3817
|
+
const [error, setError] = _react.useState.call(void 0, null);
|
|
3745
3818
|
const [isDragging, setIsDragging] = _react.useState.call(void 0, false);
|
|
3746
3819
|
const triggerRefresh = _react.useCallback.call(void 0, () => {
|
|
3747
3820
|
setRefreshKey((k) => k + 1);
|
|
3748
3821
|
}, []);
|
|
3822
|
+
const showError = _react.useCallback.call(void 0, (title, message) => {
|
|
3823
|
+
setError({ title, message });
|
|
3824
|
+
}, []);
|
|
3825
|
+
const clearError = _react.useCallback.call(void 0, () => {
|
|
3826
|
+
setError(null);
|
|
3827
|
+
}, []);
|
|
3749
3828
|
const handleDragOver = _react.useCallback.call(void 0, (e) => {
|
|
3750
3829
|
e.preventDefault();
|
|
3751
3830
|
e.stopPropagation();
|
|
@@ -3774,8 +3853,8 @@ function StudioUI({ onClose, isVisible = true }) {
|
|
|
3774
3853
|
method: "POST",
|
|
3775
3854
|
body: formData
|
|
3776
3855
|
});
|
|
3777
|
-
} catch (
|
|
3778
|
-
console.error("Upload error:",
|
|
3856
|
+
} catch (error2) {
|
|
3857
|
+
console.error("Upload error:", error2);
|
|
3779
3858
|
}
|
|
3780
3859
|
}
|
|
3781
3860
|
triggerRefresh();
|
|
@@ -3877,18 +3956,21 @@ function StudioUI({ onClose, isVisible = true }) {
|
|
|
3877
3956
|
refreshKey,
|
|
3878
3957
|
triggerRefresh,
|
|
3879
3958
|
searchQuery,
|
|
3880
|
-
setSearchQuery
|
|
3959
|
+
setSearchQuery,
|
|
3960
|
+
error,
|
|
3961
|
+
showError,
|
|
3962
|
+
clearError
|
|
3881
3963
|
};
|
|
3882
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioContext.Provider, { value: contextValue, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
3883
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
3884
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css:
|
|
3885
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css:
|
|
3886
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css:
|
|
3964
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioContext.Provider, { value: contextValue, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles9.container, children: [
|
|
3965
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles9.header, children: [
|
|
3966
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles9.headerLeft, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h1", { css: styles9.title, children: "Studio" }) }),
|
|
3967
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles9.headerCenter, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Breadcrumbs, { currentPath, onNavigate: setCurrentPath }) }),
|
|
3968
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles9.headerActions, children: [
|
|
3887
3969
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioSettings, {}),
|
|
3888
3970
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
3889
3971
|
"button",
|
|
3890
3972
|
{
|
|
3891
|
-
css:
|
|
3973
|
+
css: styles9.headerBtn,
|
|
3892
3974
|
onClick: onClose,
|
|
3893
3975
|
"aria-label": "Close Studio",
|
|
3894
3976
|
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, CloseIcon, {})
|
|
@@ -3900,20 +3982,21 @@ function StudioUI({ onClose, isVisible = true }) {
|
|
|
3900
3982
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
3901
3983
|
"div",
|
|
3902
3984
|
{
|
|
3903
|
-
css:
|
|
3985
|
+
css: styles9.content,
|
|
3904
3986
|
onDragOver: handleDragOver,
|
|
3905
3987
|
onDragLeave: handleDragLeave,
|
|
3906
3988
|
onDrop: handleDrop,
|
|
3907
3989
|
children: [
|
|
3908
|
-
isDragging && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css:
|
|
3909
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css:
|
|
3990
|
+
isDragging && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles9.dropOverlay, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles9.dropMessage, children: [
|
|
3991
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles9.dropIcon, 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" }) }),
|
|
3910
3992
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: "Drop files to upload" })
|
|
3911
3993
|
] }) }),
|
|
3912
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css:
|
|
3994
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles9.fileBrowser, children: viewMode === "grid" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioFileGrid, {}) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioFileList, {}) })
|
|
3913
3995
|
]
|
|
3914
3996
|
}
|
|
3915
3997
|
),
|
|
3916
|
-
focusedItem && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioDetailView, {})
|
|
3998
|
+
focusedItem && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioDetailView, {}),
|
|
3999
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, ErrorModal, {})
|
|
3917
4000
|
] }) });
|
|
3918
4001
|
}
|
|
3919
4002
|
function Breadcrumbs({ currentPath, onNavigate }) {
|
|
@@ -3922,12 +4005,12 @@ function Breadcrumbs({ currentPath, onNavigate }) {
|
|
|
3922
4005
|
name: part,
|
|
3923
4006
|
path: parts.slice(0, index + 1).join("/")
|
|
3924
4007
|
}));
|
|
3925
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css:
|
|
3926
|
-
index > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css:
|
|
3927
|
-
index === breadcrumbs.length - 1 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css:
|
|
4008
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles9.breadcrumbs, children: breadcrumbs.map((crumb, index) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
4009
|
+
index > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles9.breadcrumbSeparator, children: "/" }),
|
|
4010
|
+
index === breadcrumbs.length - 1 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles9.breadcrumbCurrent, children: crumb.name }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
3928
4011
|
"span",
|
|
3929
4012
|
{
|
|
3930
|
-
css:
|
|
4013
|
+
css: styles9.breadcrumbItem,
|
|
3931
4014
|
onClick: () => onNavigate(crumb.path),
|
|
3932
4015
|
children: crumb.name
|
|
3933
4016
|
}
|
|
@@ -3938,7 +4021,7 @@ function CloseIcon() {
|
|
|
3938
4021
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
3939
4022
|
"svg",
|
|
3940
4023
|
{
|
|
3941
|
-
css:
|
|
4024
|
+
css: styles9.headerIcon,
|
|
3942
4025
|
xmlns: "http://www.w3.org/2000/svg",
|
|
3943
4026
|
viewBox: "0 0 24 24",
|
|
3944
4027
|
fill: "none",
|
|
@@ -3958,4 +4041,4 @@ var StudioUI_default = StudioUI;
|
|
|
3958
4041
|
|
|
3959
4042
|
|
|
3960
4043
|
exports.StudioUI = StudioUI; exports.default = StudioUI_default;
|
|
3961
|
-
//# sourceMappingURL=StudioUI-
|
|
4044
|
+
//# sourceMappingURL=StudioUI-YFDO5MGG.js.map
|