@gallop.software/studio 0.1.38 → 0.1.40

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.
@@ -4,7 +4,7 @@ import {
4
4
  colors,
5
5
  fontSize,
6
6
  fontStack
7
- } from "./chunk-R5WKNVEV.mjs";
7
+ } from "./chunk-HXE6XCG2.mjs";
8
8
 
9
9
  // src/components/StudioUI.tsx
10
10
  import { useEffect as useEffect3, useCallback as useCallback2, useState as useState6 } from "react";
@@ -49,6 +49,9 @@ var defaultState = {
49
49
  },
50
50
  refreshKey: 0,
51
51
  triggerRefresh: () => {
52
+ },
53
+ searchQuery: "",
54
+ setSearchQuery: () => {
52
55
  }
53
56
  };
54
57
  var StudioContext = createContext(defaultState);
@@ -318,13 +321,15 @@ var spin = keyframes2`
318
321
  var styles2 = {
319
322
  toolbar: css2`
320
323
  display: flex;
321
- flex-wrap: wrap;
324
+ flex-wrap: nowrap;
322
325
  align-items: center;
323
326
  justify-content: space-between;
324
327
  gap: 8px;
325
328
  padding: 12px 16px;
326
329
  background-color: ${colors.surface};
327
330
  border-bottom: 1px solid ${colors.border};
331
+ overflow-x: auto;
332
+ min-width: 0;
328
333
 
329
334
  @media (min-width: 768px) {
330
335
  padding: 12px 24px;
@@ -332,13 +337,15 @@ var styles2 = {
332
337
  `,
333
338
  left: css2`
334
339
  display: flex;
335
- flex-wrap: wrap;
340
+ flex-wrap: nowrap;
341
+ flex-shrink: 0;
336
342
  align-items: center;
337
343
  gap: 8px;
338
344
  `,
339
345
  right: css2`
340
346
  display: flex;
341
- flex-wrap: wrap;
347
+ flex-wrap: nowrap;
348
+ flex-shrink: 0;
342
349
  align-items: center;
343
350
  gap: 8px;
344
351
  `,
@@ -433,6 +440,27 @@ var styles2 = {
433
440
  border-radius: 6px;
434
441
  overflow: hidden;
435
442
  `,
443
+ searchInput: css2`
444
+ height: ${btnHeight};
445
+ padding: 0 12px;
446
+ border: 1px solid ${colors.border};
447
+ border-radius: 6px;
448
+ font-size: ${fontSize.base};
449
+ background: ${colors.surface};
450
+ color: ${colors.text};
451
+ width: 180px;
452
+ transition: all 0.15s ease;
453
+
454
+ &:focus {
455
+ outline: none;
456
+ border-color: ${colors.primary};
457
+ box-shadow: 0 0 0 2px ${colors.primaryLight};
458
+ }
459
+
460
+ &::placeholder {
461
+ color: ${colors.textMuted};
462
+ }
463
+ `,
436
464
  viewBtn: css2`
437
465
  height: 100%;
438
466
  padding: 0 10px;
@@ -451,8 +479,13 @@ var styles2 = {
451
479
  }
452
480
  `,
453
481
  viewBtnActive: css2`
454
- background-color: ${colors.background};
455
- color: ${colors.text};
482
+ background-color: ${colors.primaryLight};
483
+ color: ${colors.primary};
484
+
485
+ &:hover {
486
+ background-color: ${colors.primaryLight};
487
+ color: ${colors.primary};
488
+ }
456
489
  `
457
490
  };
458
491
  function StudioToolbar() {
@@ -774,10 +807,14 @@ function StudioToolbar() {
774
807
  const handleSyncCdn = useCallback(() => {
775
808
  console.log("Sync CDN clicked", selectedItems);
776
809
  }, [selectedItems]);
777
- const handleScan = useCallback(() => {
778
- console.log("Scan clicked");
779
- }, []);
810
+ const { searchQuery, setSearchQuery } = useStudio();
811
+ const handleSearch = useCallback((e) => {
812
+ setSearchQuery(e.target.value);
813
+ }, [setSearchQuery]);
780
814
  const hasSelection = selectedItems.size > 0;
815
+ const hasImagesSelected = Array.from(selectedItems).some(
816
+ (path) => path === "public/images" || path.startsWith("public/images/")
817
+ );
781
818
  if (focusedItem) {
782
819
  return null;
783
820
  }
@@ -859,8 +896,8 @@ function StudioToolbar() {
859
896
  {
860
897
  css: styles2.btn,
861
898
  onClick: handleProcessImages,
862
- disabled: processing || isInImagesFolder,
863
- title: isInImagesFolder ? "Cannot process images from within the images folder" : void 0,
899
+ disabled: processing || isInImagesFolder || hasImagesSelected,
900
+ title: isInImagesFolder || hasImagesSelected ? "Cannot process protected images folder" : void 0,
864
901
  children: [
865
902
  /* @__PURE__ */ jsx2(ImageStackIcon, {}),
866
903
  processing ? "Processing..." : "Process Images"
@@ -872,7 +909,8 @@ function StudioToolbar() {
872
909
  {
873
910
  css: [styles2.btn, styles2.btnDanger],
874
911
  onClick: handleDeleteClick,
875
- disabled: !hasSelection,
912
+ disabled: !hasSelection || hasImagesSelected,
913
+ title: hasImagesSelected ? "Cannot delete protected images folder items" : void 0,
876
914
  children: [
877
915
  /* @__PURE__ */ jsx2(TrashIcon, {}),
878
916
  "Delete"
@@ -891,10 +929,16 @@ function StudioToolbar() {
891
929
  ]
892
930
  }
893
931
  ),
894
- /* @__PURE__ */ jsxs2("button", { css: styles2.btn, onClick: handleScan, children: [
895
- /* @__PURE__ */ jsx2(ScanIcon, {}),
896
- "Scan"
897
- ] })
932
+ /* @__PURE__ */ jsx2(
933
+ "input",
934
+ {
935
+ css: styles2.searchInput,
936
+ type: "text",
937
+ placeholder: "Search images...",
938
+ value: searchQuery,
939
+ onChange: handleSearch
940
+ }
941
+ )
898
942
  ] }),
899
943
  /* @__PURE__ */ jsxs2("div", { css: styles2.right, children: [
900
944
  hasSelection && /* @__PURE__ */ jsxs2("span", { css: styles2.selectionCount, children: [
@@ -946,9 +990,6 @@ function TrashIcon() {
946
990
  function CloudIcon() {
947
991
  return /* @__PURE__ */ jsx2("svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" }) });
948
992
  }
949
- function ScanIcon() {
950
- return /* @__PURE__ */ jsx2("svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) });
951
- }
952
993
  function GridIcon() {
953
994
  return /* @__PURE__ */ jsx2("svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" }) });
954
995
  }
@@ -1006,13 +1047,13 @@ var styles3 = {
1006
1047
  `,
1007
1048
  grid: css3`
1008
1049
  display: grid;
1009
- grid-template-columns: repeat(2, 1fr);
1050
+ grid-template-columns: 1fr;
1010
1051
  gap: 12px;
1011
1052
 
1012
- @media (min-width: 640px) { grid-template-columns: repeat(3, 1fr); }
1013
- @media (min-width: 768px) { grid-template-columns: repeat(4, 1fr); }
1014
- @media (min-width: 1024px) { grid-template-columns: repeat(5, 1fr); }
1015
- @media (min-width: 1280px) { grid-template-columns: repeat(6, 1fr); }
1053
+ @media (min-width: 480px) { grid-template-columns: repeat(2, 1fr); }
1054
+ @media (min-width: 768px) { grid-template-columns: repeat(3, 1fr); }
1055
+ @media (min-width: 1024px) { grid-template-columns: repeat(4, 1fr); }
1056
+ @media (min-width: 1280px) { grid-template-columns: repeat(5, 1fr); }
1016
1057
  `,
1017
1058
  item: css3`
1018
1059
  position: relative;
@@ -1084,6 +1125,25 @@ var styles3 = {
1084
1125
  height: 56px;
1085
1126
  color: #f5a623;
1086
1127
  `,
1128
+ imagesFolderIcon: css3`
1129
+ width: 56px;
1130
+ height: 56px;
1131
+ color: ${colors.imagesFolder};
1132
+ `,
1133
+ imagesFolderWrapper: css3`
1134
+ position: relative;
1135
+ `,
1136
+ lockIcon: css3`
1137
+ position: absolute;
1138
+ bottom: 4px;
1139
+ right: 4px;
1140
+ width: 16px;
1141
+ height: 16px;
1142
+ color: ${colors.imagesFolder};
1143
+ background: white;
1144
+ border-radius: 50%;
1145
+ padding: 2px;
1146
+ `,
1087
1147
  parentIcon: css3`
1088
1148
  width: 56px;
1089
1149
  height: 56px;
@@ -1137,14 +1197,69 @@ var styles3 = {
1137
1197
  `,
1138
1198
  labelRow: css3`
1139
1199
  display: flex;
1140
- align-items: center;
1141
- justify-content: space-between;
1142
- gap: 8px;
1200
+ flex-direction: column;
1201
+ gap: 6px;
1143
1202
  `,
1144
1203
  labelText: css3`
1145
1204
  flex: 1;
1146
1205
  min-width: 0;
1147
1206
  `,
1207
+ buttonRow: css3`
1208
+ display: flex;
1209
+ gap: 6px;
1210
+ `,
1211
+ copyBtn: css3`
1212
+ position: relative;
1213
+ flex-shrink: 0;
1214
+ height: 28px;
1215
+ width: 28px;
1216
+ font-size: ${fontSize.xs};
1217
+ color: ${colors.textSecondary};
1218
+ background: ${colors.surface};
1219
+ border: 1px solid ${colors.border};
1220
+ padding: 0;
1221
+ cursor: pointer;
1222
+ border-radius: 4px;
1223
+ transition: all 0.15s ease;
1224
+ display: inline-flex;
1225
+ align-items: center;
1226
+ justify-content: center;
1227
+
1228
+ &:hover {
1229
+ background: ${colors.surfaceHover};
1230
+ border-color: ${colors.borderHover};
1231
+ color: ${colors.text};
1232
+ }
1233
+ `,
1234
+ copyIcon: css3`
1235
+ width: 14px;
1236
+ height: 14px;
1237
+ `,
1238
+ tooltip: css3`
1239
+ position: absolute;
1240
+ bottom: 100%;
1241
+ left: 50%;
1242
+ transform: translateX(-50%);
1243
+ background: #1a1f36;
1244
+ color: white;
1245
+ padding: 4px 8px;
1246
+ border-radius: 4px;
1247
+ font-size: 12px;
1248
+ white-space: nowrap;
1249
+ margin-bottom: 6px;
1250
+ pointer-events: none;
1251
+ z-index: 100;
1252
+
1253
+ &::after {
1254
+ content: '';
1255
+ position: absolute;
1256
+ top: 100%;
1257
+ left: 50%;
1258
+ transform: translateX(-50%);
1259
+ border: 4px solid transparent;
1260
+ border-top-color: #1a1f36;
1261
+ }
1262
+ `,
1148
1263
  name: css3`
1149
1264
  font-size: ${fontSize.sm};
1150
1265
  font-weight: 500;
@@ -1209,7 +1324,7 @@ var styles3 = {
1209
1324
  `
1210
1325
  };
1211
1326
  function StudioFileGrid() {
1212
- const { currentPath, setCurrentPath, navigateUp, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey, setFocusedItem, triggerRefresh } = useStudio();
1327
+ const { currentPath, setCurrentPath, navigateUp, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey, setFocusedItem, triggerRefresh, searchQuery } = useStudio();
1213
1328
  const [items, setItems] = useState2([]);
1214
1329
  const [loading, setLoading] = useState2(true);
1215
1330
  const isInitialLoad = useRef2(true);
@@ -1246,7 +1361,12 @@ function StudioFileGrid() {
1246
1361
  /* @__PURE__ */ jsx3("p", { css: styles3.emptyText, children: "Upload images to get started" })
1247
1362
  ] });
1248
1363
  }
1249
- const sortedItems = [...items].sort((a, b) => {
1364
+ const filteredItems = searchQuery ? items.filter((item) => {
1365
+ if (item.type === "folder") return true;
1366
+ const query = searchQuery.toLowerCase();
1367
+ return item.name.toLowerCase().includes(query);
1368
+ }) : items;
1369
+ const sortedItems = [...filteredItems].sort((a, b) => {
1250
1370
  if (a.type === "folder" && b.type !== "folder") return -1;
1251
1371
  if (a.type !== "folder" && b.type === "folder") return 1;
1252
1372
  return a.name.localeCompare(b.name);
@@ -1335,8 +1455,17 @@ function StudioFileGrid() {
1335
1455
  ] });
1336
1456
  }
1337
1457
  function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
1458
+ const [showCopied, setShowCopied] = useState2(false);
1338
1459
  const isFolder = item.type === "folder";
1339
1460
  const isImage = !isFolder && item.thumbnail !== void 0;
1461
+ const isImagesFolder = isFolder && (item.name === "images" || item.path.includes("/images/"));
1462
+ const handleCopyPath = (e) => {
1463
+ e.stopPropagation();
1464
+ const pathToCopy = "/" + item.path;
1465
+ navigator.clipboard.writeText(pathToCopy);
1466
+ setShowCopied(true);
1467
+ setTimeout(() => setShowCopied(false), 1500);
1468
+ };
1340
1469
  return /* @__PURE__ */ jsxs3(
1341
1470
  "div",
1342
1471
  {
@@ -1360,7 +1489,10 @@ function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
1360
1489
  }
1361
1490
  ),
1362
1491
  item.cdnSynced && /* @__PURE__ */ jsx3("span", { css: styles3.cdnBadge, children: "CDN" }),
1363
- /* @__PURE__ */ jsx3("div", { css: styles3.content, children: isFolder ? /* @__PURE__ */ jsx3("svg", { css: styles3.folderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { d: "M10 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" }) }) : isImage && item.hasThumbnail ? /* @__PURE__ */ jsx3(
1492
+ /* @__PURE__ */ jsx3("div", { css: styles3.content, children: isFolder ? isImagesFolder ? /* @__PURE__ */ jsxs3("div", { css: styles3.imagesFolderWrapper, children: [
1493
+ /* @__PURE__ */ jsx3("svg", { css: styles3.imagesFolderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { d: "M10 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" }) }),
1494
+ /* @__PURE__ */ jsx3("svg", { css: styles3.lockIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx3("path", { fillRule: "evenodd", d: "M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z", clipRule: "evenodd" }) })
1495
+ ] }) : /* @__PURE__ */ jsx3("svg", { css: styles3.folderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { d: "M10 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" }) }) : isImage && item.hasThumbnail ? /* @__PURE__ */ jsx3(
1364
1496
  "img",
1365
1497
  {
1366
1498
  css: styles3.image,
@@ -1392,17 +1524,31 @@ function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
1392
1524
  item.totalSize !== void 0 ? formatFileSize(item.totalSize) : ""
1393
1525
  ] }) : item.size !== void 0 && /* @__PURE__ */ jsx3("p", { css: styles3.size, children: formatFileSize(item.size) })
1394
1526
  ] }),
1395
- /* @__PURE__ */ jsx3(
1396
- "button",
1397
- {
1398
- css: styles3.openBtn,
1399
- onClick: (e) => {
1400
- e.stopPropagation();
1401
- onOpen();
1402
- },
1403
- children: "Open"
1404
- }
1405
- )
1527
+ /* @__PURE__ */ jsxs3("div", { css: styles3.buttonRow, children: [
1528
+ /* @__PURE__ */ jsxs3(
1529
+ "button",
1530
+ {
1531
+ css: styles3.copyBtn,
1532
+ onClick: handleCopyPath,
1533
+ title: "Copy file path",
1534
+ children: [
1535
+ showCopied && /* @__PURE__ */ jsx3("span", { css: styles3.tooltip, children: "Copied!" }),
1536
+ /* @__PURE__ */ jsx3("svg", { css: styles3.copyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" }) })
1537
+ ]
1538
+ }
1539
+ ),
1540
+ /* @__PURE__ */ jsx3(
1541
+ "button",
1542
+ {
1543
+ css: styles3.openBtn,
1544
+ onClick: (e) => {
1545
+ e.stopPropagation();
1546
+ onOpen();
1547
+ },
1548
+ children: "Open"
1549
+ }
1550
+ )
1551
+ ] })
1406
1552
  ] }) })
1407
1553
  ]
1408
1554
  }
@@ -1513,6 +1659,7 @@ var styles4 = {
1513
1659
  `,
1514
1660
  parentRow: css4`
1515
1661
  cursor: pointer;
1662
+ border-bottom: 1px solid ${colors.border};
1516
1663
 
1517
1664
  &:hover {
1518
1665
  background-color: ${colors.surfaceHover};
@@ -1524,12 +1671,66 @@ var styles4 = {
1524
1671
  checkboxCell: css4`
1525
1672
  padding: 12px 16px;
1526
1673
  cursor: pointer;
1674
+ vertical-align: middle;
1527
1675
  `,
1528
1676
  checkbox: css4`
1529
1677
  width: 16px;
1530
1678
  height: 16px;
1531
1679
  accent-color: ${colors.primary};
1532
1680
  cursor: pointer;
1681
+ display: block;
1682
+ `,
1683
+ copyBtn: css4`
1684
+ position: relative;
1685
+ flex-shrink: 0;
1686
+ height: 28px;
1687
+ width: 28px;
1688
+ font-size: ${fontSize.xs};
1689
+ color: ${colors.textSecondary};
1690
+ background: ${colors.surface};
1691
+ border: 1px solid ${colors.border};
1692
+ padding: 0;
1693
+ cursor: pointer;
1694
+ border-radius: 4px;
1695
+ transition: all 0.15s ease;
1696
+ display: inline-flex;
1697
+ align-items: center;
1698
+ justify-content: center;
1699
+
1700
+ &:hover {
1701
+ background: ${colors.surfaceHover};
1702
+ border-color: ${colors.borderHover};
1703
+ color: ${colors.text};
1704
+ }
1705
+ `,
1706
+ copyIcon: css4`
1707
+ width: 14px;
1708
+ height: 14px;
1709
+ `,
1710
+ tooltip: css4`
1711
+ position: absolute;
1712
+ bottom: 100%;
1713
+ left: 50%;
1714
+ transform: translateX(-50%);
1715
+ background: #1a1f36;
1716
+ color: white;
1717
+ padding: 4px 8px;
1718
+ border-radius: 4px;
1719
+ font-size: 12px;
1720
+ white-space: nowrap;
1721
+ margin-bottom: 6px;
1722
+ pointer-events: none;
1723
+ z-index: 100;
1724
+
1725
+ &::after {
1726
+ content: '';
1727
+ position: absolute;
1728
+ top: 100%;
1729
+ left: 50%;
1730
+ transform: translateX(-50%);
1731
+ border: 4px solid transparent;
1732
+ border-top-color: #1a1f36;
1733
+ }
1533
1734
  `,
1534
1735
  nameCell: css4`
1535
1736
  display: flex;
@@ -1542,6 +1743,24 @@ var styles4 = {
1542
1743
  color: #f5a623;
1543
1744
  flex-shrink: 0;
1544
1745
  `,
1746
+ imagesFolderWrapper: css4`
1747
+ position: relative;
1748
+ display: flex;
1749
+ align-items: center;
1750
+ `,
1751
+ imagesFolderIcon: css4`
1752
+ width: 20px;
1753
+ height: 20px;
1754
+ color: ${colors.imagesFolder};
1755
+ flex-shrink: 0;
1756
+ `,
1757
+ lockIcon: css4`
1758
+ width: 10px;
1759
+ height: 10px;
1760
+ color: ${colors.imagesFolder};
1761
+ margin-left: -6px;
1762
+ margin-top: 8px;
1763
+ `,
1545
1764
  parentIcon: css4`
1546
1765
  width: 20px;
1547
1766
  height: 20px;
@@ -1635,7 +1854,7 @@ var styles4 = {
1635
1854
  `
1636
1855
  };
1637
1856
  function StudioFileList() {
1638
- const { currentPath, setCurrentPath, navigateUp, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey, setFocusedItem, triggerRefresh } = useStudio();
1857
+ const { currentPath, setCurrentPath, navigateUp, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey, setFocusedItem, triggerRefresh, searchQuery } = useStudio();
1639
1858
  const [items, setItems] = useState3([]);
1640
1859
  const [loading, setLoading] = useState3(true);
1641
1860
  const isInitialLoad = useRef3(true);
@@ -1668,7 +1887,12 @@ function StudioFileList() {
1668
1887
  if (items.length === 0 && isAtRoot) {
1669
1888
  return /* @__PURE__ */ jsx4("div", { css: styles4.empty, children: /* @__PURE__ */ jsx4("p", { children: "No files in this folder" }) });
1670
1889
  }
1671
- const sortedItems = [...items].sort((a, b) => {
1890
+ const filteredItems = searchQuery ? items.filter((item) => {
1891
+ if (item.type === "folder") return true;
1892
+ const query = searchQuery.toLowerCase();
1893
+ return item.name.toLowerCase().includes(query);
1894
+ }) : items;
1895
+ const sortedItems = [...filteredItems].sort((a, b) => {
1672
1896
  if (a.type === "folder" && b.type !== "folder") return -1;
1673
1897
  if (a.type !== "folder" && b.type === "folder") return 1;
1674
1898
  return a.name.localeCompare(b.name);
@@ -1754,8 +1978,17 @@ function StudioFileList() {
1754
1978
  ] }) });
1755
1979
  }
1756
1980
  function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
1981
+ const [showCopied, setShowCopied] = useState3(false);
1757
1982
  const isFolder = item.type === "folder";
1758
1983
  const isImage = !isFolder && item.thumbnail !== void 0;
1984
+ const isImagesFolder = isFolder && (item.name === "images" || item.path.includes("/images/"));
1985
+ const handleCopyPath = (e) => {
1986
+ e.stopPropagation();
1987
+ const pathToCopy = "/" + item.path;
1988
+ navigator.clipboard.writeText(pathToCopy);
1989
+ setShowCopied(true);
1990
+ setTimeout(() => setShowCopied(false), 1500);
1991
+ };
1759
1992
  return /* @__PURE__ */ jsxs4(
1760
1993
  "tr",
1761
1994
  {
@@ -1779,7 +2012,10 @@ function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
1779
2012
  }
1780
2013
  ),
1781
2014
  /* @__PURE__ */ jsx4("td", { css: styles4.td, children: /* @__PURE__ */ jsxs4("div", { css: styles4.nameCell, children: [
1782
- isFolder ? /* @__PURE__ */ jsx4("svg", { css: styles4.folderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { d: "M10 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" }) }) : isImage && item.hasThumbnail ? /* @__PURE__ */ jsx4("img", { css: styles4.thumbnail, src: item.thumbnail, alt: item.name, loading: "lazy" }) : isImage && !item.hasThumbnail ? /* @__PURE__ */ jsx4(
2015
+ isFolder ? isImagesFolder ? /* @__PURE__ */ jsxs4("div", { css: styles4.imagesFolderWrapper, children: [
2016
+ /* @__PURE__ */ jsx4("svg", { css: styles4.imagesFolderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { d: "M10 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" }) }),
2017
+ /* @__PURE__ */ jsx4("svg", { css: styles4.lockIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx4("path", { fillRule: "evenodd", d: "M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z", clipRule: "evenodd" }) })
2018
+ ] }) : /* @__PURE__ */ jsx4("svg", { css: styles4.folderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { d: "M10 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" }) }) : isImage && item.hasThumbnail ? /* @__PURE__ */ jsx4("img", { css: styles4.thumbnail, src: item.thumbnail, alt: item.name, loading: "lazy" }) : isImage && !item.hasThumbnail ? /* @__PURE__ */ jsx4(
1783
2019
  "button",
1784
2020
  {
1785
2021
  css: styles4.noThumbnail,
@@ -1792,6 +2028,18 @@ function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
1792
2028
  }
1793
2029
  ) : /* @__PURE__ */ jsx4("svg", { css: styles4.fileIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("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" }) }),
1794
2030
  /* @__PURE__ */ jsx4("span", { css: styles4.name, title: item.name, children: truncateMiddle2(item.name) }),
2031
+ /* @__PURE__ */ jsxs4(
2032
+ "button",
2033
+ {
2034
+ css: styles4.copyBtn,
2035
+ onClick: handleCopyPath,
2036
+ title: "Copy file path",
2037
+ children: [
2038
+ showCopied && /* @__PURE__ */ jsx4("span", { css: styles4.tooltip, children: "Copied!" }),
2039
+ /* @__PURE__ */ jsx4("svg", { css: styles4.copyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" }) })
2040
+ ]
2041
+ }
2042
+ ),
1795
2043
  /* @__PURE__ */ jsx4(
1796
2044
  "button",
1797
2045
  {
@@ -1863,6 +2111,7 @@ var styles5 = {
1863
2111
  flex: 1;
1864
2112
  margin: 24px;
1865
2113
  background: ${colors.surface};
2114
+ border: 1px solid ${colors.border};
1866
2115
  border-radius: 12px;
1867
2116
  overflow: hidden;
1868
2117
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
@@ -1878,10 +2127,16 @@ var styles5 = {
1878
2127
  background: ${colors.background};
1879
2128
  overflow: auto;
1880
2129
  `,
1881
- mainCloseBtn: css5`
2130
+ headerButtons: css5`
1882
2131
  position: absolute;
1883
2132
  top: 16px;
1884
2133
  right: 16px;
2134
+ display: flex;
2135
+ gap: 8px;
2136
+ z-index: 10;
2137
+ `,
2138
+ copyBtn: css5`
2139
+ position: relative;
1885
2140
  padding: 8px;
1886
2141
  background: ${colors.surface};
1887
2142
  border: 1px solid ${colors.border};
@@ -1891,7 +2146,6 @@ var styles5 = {
1891
2146
  display: flex;
1892
2147
  align-items: center;
1893
2148
  justify-content: center;
1894
- z-index: 10;
1895
2149
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
1896
2150
 
1897
2151
  &:hover {
@@ -1899,11 +2153,58 @@ var styles5 = {
1899
2153
  border-color: ${colors.borderHover};
1900
2154
  }
1901
2155
  `,
1902
- mainCloseIcon: css5`
2156
+ copyIcon: css5`
1903
2157
  width: 20px;
1904
2158
  height: 20px;
1905
2159
  color: ${colors.textSecondary};
1906
2160
  `,
2161
+ tooltip: css5`
2162
+ position: absolute;
2163
+ bottom: 100%;
2164
+ left: 50%;
2165
+ transform: translateX(-50%);
2166
+ background: #1a1f36;
2167
+ color: white;
2168
+ padding: 4px 8px;
2169
+ border-radius: 4px;
2170
+ font-size: 12px;
2171
+ white-space: nowrap;
2172
+ margin-bottom: 6px;
2173
+ pointer-events: none;
2174
+ z-index: 100;
2175
+
2176
+ &::after {
2177
+ content: '';
2178
+ position: absolute;
2179
+ top: 100%;
2180
+ left: 50%;
2181
+ transform: translateX(-50%);
2182
+ border: 4px solid transparent;
2183
+ border-top-color: #1a1f36;
2184
+ }
2185
+ `,
2186
+ mainCloseBtn: css5`
2187
+ padding: 8px;
2188
+ background: ${colors.danger};
2189
+ border: 1px solid ${colors.danger};
2190
+ border-radius: 8px;
2191
+ cursor: pointer;
2192
+ transition: all 0.15s ease;
2193
+ display: flex;
2194
+ align-items: center;
2195
+ justify-content: center;
2196
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
2197
+
2198
+ &:hover {
2199
+ background-color: ${colors.dangerHover};
2200
+ border-color: ${colors.dangerHover};
2201
+ }
2202
+ `,
2203
+ mainCloseIcon: css5`
2204
+ width: 20px;
2205
+ height: 20px;
2206
+ color: white;
2207
+ `,
1907
2208
  mediaWrapper: css5`
1908
2209
  max-width: 100%;
1909
2210
  max-height: 100%;
@@ -2044,6 +2345,7 @@ function StudioDetailView() {
2044
2345
  const { focusedItem, setFocusedItem, triggerRefresh, clearSelection } = useStudio();
2045
2346
  const [showDeleteConfirm, setShowDeleteConfirm] = useState4(false);
2046
2347
  const [alertMessage, setAlertMessage] = useState4(null);
2348
+ const [showCopied, setShowCopied] = useState4(false);
2047
2349
  if (!focusedItem) return null;
2048
2350
  const isImage = isImageFile(focusedItem.name);
2049
2351
  const isVideo = isVideoFile(focusedItem.name);
@@ -2051,6 +2353,12 @@ function StudioDetailView() {
2051
2353
  const handleClose = () => {
2052
2354
  setFocusedItem(null);
2053
2355
  };
2356
+ const handleCopyPath = () => {
2357
+ const pathToCopy = "/" + focusedItem.path;
2358
+ navigator.clipboard.writeText(pathToCopy);
2359
+ setShowCopied(true);
2360
+ setTimeout(() => setShowCopied(false), 1500);
2361
+ };
2054
2362
  const handleRename = () => {
2055
2363
  const newName = prompt("Enter new name:", focusedItem.name);
2056
2364
  if (newName && newName !== focusedItem.name) {
@@ -2124,7 +2432,13 @@ function StudioDetailView() {
2124
2432
  ),
2125
2433
  /* @__PURE__ */ jsx5("div", { css: styles5.overlay, onClick: handleClose, children: /* @__PURE__ */ jsxs5("div", { css: styles5.container, onClick: (e) => e.stopPropagation(), children: [
2126
2434
  /* @__PURE__ */ jsxs5("div", { css: styles5.main, children: [
2127
- /* @__PURE__ */ jsx5("button", { css: styles5.mainCloseBtn, onClick: handleClose, "aria-label": "Close", children: /* @__PURE__ */ jsx5("svg", { css: styles5.mainCloseIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }),
2435
+ /* @__PURE__ */ jsxs5("div", { css: styles5.headerButtons, children: [
2436
+ /* @__PURE__ */ jsxs5("button", { css: styles5.copyBtn, onClick: handleCopyPath, title: "Copy file path", children: [
2437
+ showCopied && /* @__PURE__ */ jsx5("span", { css: styles5.tooltip, children: "Copied!" }),
2438
+ /* @__PURE__ */ jsx5("svg", { css: styles5.copyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" }) })
2439
+ ] }),
2440
+ /* @__PURE__ */ jsx5("button", { css: styles5.mainCloseBtn, onClick: handleClose, "aria-label": "Close", children: /* @__PURE__ */ jsx5("svg", { css: styles5.mainCloseIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })
2441
+ ] }),
2128
2442
  /* @__PURE__ */ jsx5("div", { css: styles5.mediaWrapper, children: renderMedia() })
2129
2443
  ] }),
2130
2444
  /* @__PURE__ */ jsxs5("div", { css: styles5.sidebar, children: [
@@ -2281,14 +2595,69 @@ var styles6 = {
2281
2595
  color: ${colors.textSecondary};
2282
2596
  margin: 0 0 12px 0;
2283
2597
  `,
2598
+ codeWrapper: css6`
2599
+ position: relative;
2600
+ `,
2284
2601
  code: css6`
2285
2602
  background-color: ${colors.background};
2286
2603
  border-radius: 8px;
2287
2604
  padding: 12px;
2605
+ padding-right: 40px;
2288
2606
  font-family: 'SF Mono', Monaco, Consolas, monospace;
2289
2607
  font-size: ${fontSize.xs};
2290
2608
  color: ${colors.textSecondary};
2291
2609
  border: 1px solid ${colors.border};
2610
+ overflow-x: auto;
2611
+ white-space: nowrap;
2612
+ `,
2613
+ copyBtn: css6`
2614
+ position: absolute;
2615
+ top: 8px;
2616
+ right: 8px;
2617
+ padding: 4px;
2618
+ background: ${colors.surface};
2619
+ border: 1px solid ${colors.border};
2620
+ border-radius: 4px;
2621
+ cursor: pointer;
2622
+ transition: all 0.15s ease;
2623
+ display: flex;
2624
+ align-items: center;
2625
+ justify-content: center;
2626
+
2627
+ &:hover {
2628
+ background-color: ${colors.surfaceHover};
2629
+ border-color: ${colors.borderHover};
2630
+ }
2631
+ `,
2632
+ tooltip: css6`
2633
+ position: absolute;
2634
+ bottom: 100%;
2635
+ left: 50%;
2636
+ transform: translateX(-50%);
2637
+ background: #1a1f36;
2638
+ color: white;
2639
+ padding: 4px 8px;
2640
+ border-radius: 4px;
2641
+ font-size: 12px;
2642
+ white-space: nowrap;
2643
+ margin-bottom: 6px;
2644
+ pointer-events: none;
2645
+ z-index: 100;
2646
+
2647
+ &::after {
2648
+ content: '';
2649
+ position: absolute;
2650
+ top: 100%;
2651
+ left: 50%;
2652
+ transform: translateX(-50%);
2653
+ border: 4px solid transparent;
2654
+ border-top-color: #1a1f36;
2655
+ }
2656
+ `,
2657
+ copyIcon: css6`
2658
+ width: 14px;
2659
+ height: 14px;
2660
+ color: ${colors.textSecondary};
2292
2661
  `,
2293
2662
  codeLine: css6`
2294
2663
  margin: 0 0 4px 0;
@@ -2393,7 +2762,18 @@ function StudioSettings() {
2393
2762
  isOpen && /* @__PURE__ */ jsx6(SettingsPanel, { onClose: () => setIsOpen(false) })
2394
2763
  ] });
2395
2764
  }
2765
+ var envTemplate = `CLOUDFLARE_R2_ACCOUNT_ID=abc123def456ghi789
2766
+ CLOUDFLARE_R2_ACCESS_KEY_ID=your_access_key_id_here
2767
+ CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_secret_access_key_here
2768
+ CLOUDFLARE_R2_BUCKET_NAME=my-images-bucket
2769
+ CLOUDFLARE_R2_PUBLIC_URL=https://cdn.yourdomain.com`;
2396
2770
  function SettingsPanel({ onClose }) {
2771
+ const [copied, setCopied] = useState5(false);
2772
+ const handleCopy = () => {
2773
+ navigator.clipboard.writeText(envTemplate);
2774
+ setCopied(true);
2775
+ setTimeout(() => setCopied(false), 2e3);
2776
+ };
2397
2777
  return /* @__PURE__ */ jsx6("div", { css: styles6.overlay, onClick: onClose, children: /* @__PURE__ */ jsxs6("div", { css: styles6.panel, onClick: (e) => e.stopPropagation(), children: [
2398
2778
  /* @__PURE__ */ jsxs6("div", { css: styles6.header, children: [
2399
2779
  /* @__PURE__ */ jsx6("h2", { css: styles6.title, children: "Settings" }),
@@ -2403,12 +2783,18 @@ function SettingsPanel({ onClose }) {
2403
2783
  /* @__PURE__ */ jsxs6("section", { children: [
2404
2784
  /* @__PURE__ */ jsx6("h3", { css: styles6.sectionTitle, children: "Cloudflare R2" }),
2405
2785
  /* @__PURE__ */ jsx6("p", { css: styles6.description, children: "Configure in .env.local file:" }),
2406
- /* @__PURE__ */ jsxs6("div", { css: styles6.code, children: [
2407
- /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_ACCOUNT_ID" }),
2408
- /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_ACCESS_KEY_ID" }),
2409
- /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_SECRET_ACCESS_KEY" }),
2410
- /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_BUCKET_NAME" }),
2411
- /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_PUBLIC_URL" })
2786
+ /* @__PURE__ */ jsxs6("div", { css: styles6.codeWrapper, children: [
2787
+ /* @__PURE__ */ jsxs6("button", { css: styles6.copyBtn, onClick: handleCopy, title: "Copy to clipboard", children: [
2788
+ copied && /* @__PURE__ */ jsx6("span", { css: styles6.tooltip, children: "Copied!" }),
2789
+ copied ? /* @__PURE__ */ jsx6("svg", { css: styles6.copyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) }) : /* @__PURE__ */ jsx6("svg", { css: styles6.copyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" }) })
2790
+ ] }),
2791
+ /* @__PURE__ */ jsxs6("div", { css: styles6.code, children: [
2792
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_ACCOUNT_ID=abc123def456ghi789" }),
2793
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_ACCESS_KEY_ID=your_access_key_id_here" }),
2794
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_secret_access_key_here" }),
2795
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_BUCKET_NAME=my-images-bucket" }),
2796
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_PUBLIC_URL=https://cdn.yourdomain.com" })
2797
+ ] })
2412
2798
  ] })
2413
2799
  ] }),
2414
2800
  /* @__PURE__ */ jsxs6("section", { children: [
@@ -2504,6 +2890,34 @@ var styles7 = {
2504
2890
  min-width: 0;
2505
2891
  overflow: auto;
2506
2892
  padding: 20px 24px;
2893
+ `,
2894
+ dropOverlay: css7`
2895
+ position: absolute;
2896
+ top: 0;
2897
+ left: 0;
2898
+ right: 0;
2899
+ bottom: 0;
2900
+ background: rgba(99, 91, 255, 0.1);
2901
+ border: 3px dashed ${colors.primary};
2902
+ border-radius: 8px;
2903
+ display: flex;
2904
+ align-items: center;
2905
+ justify-content: center;
2906
+ z-index: 50;
2907
+ pointer-events: none;
2908
+ `,
2909
+ dropMessage: css7`
2910
+ display: flex;
2911
+ flex-direction: column;
2912
+ align-items: center;
2913
+ gap: 12px;
2914
+ color: ${colors.primary};
2915
+ font-size: ${fontSize.lg};
2916
+ font-weight: 600;
2917
+ `,
2918
+ dropIcon: css7`
2919
+ width: 48px;
2920
+ height: 48px;
2507
2921
  `
2508
2922
  };
2509
2923
  function StudioUI({ onClose, isVisible = true }) {
@@ -2515,9 +2929,45 @@ function StudioUI({ onClose, isVisible = true }) {
2515
2929
  const [meta, setMeta] = useState6(null);
2516
2930
  const [isLoading, setIsLoading] = useState6(false);
2517
2931
  const [refreshKey, setRefreshKey] = useState6(0);
2932
+ const [searchQuery, setSearchQuery] = useState6("");
2933
+ const [isDragging, setIsDragging] = useState6(false);
2518
2934
  const triggerRefresh = useCallback2(() => {
2519
2935
  setRefreshKey((k) => k + 1);
2520
2936
  }, []);
2937
+ const handleDragOver = useCallback2((e) => {
2938
+ e.preventDefault();
2939
+ e.stopPropagation();
2940
+ setIsDragging(true);
2941
+ }, []);
2942
+ const handleDragLeave = useCallback2((e) => {
2943
+ e.preventDefault();
2944
+ e.stopPropagation();
2945
+ setIsDragging(false);
2946
+ }, []);
2947
+ const handleDrop = useCallback2(async (e) => {
2948
+ e.preventDefault();
2949
+ e.stopPropagation();
2950
+ setIsDragging(false);
2951
+ const files = Array.from(e.dataTransfer.files);
2952
+ if (files.length === 0) return;
2953
+ if (currentPath === "public/images" || currentPath.startsWith("public/images/")) {
2954
+ return;
2955
+ }
2956
+ for (const file of files) {
2957
+ const formData = new FormData();
2958
+ formData.append("file", file);
2959
+ formData.append("path", currentPath);
2960
+ try {
2961
+ await fetch("/api/studio/upload", {
2962
+ method: "POST",
2963
+ body: formData
2964
+ });
2965
+ } catch (error) {
2966
+ console.error("Upload error:", error);
2967
+ }
2968
+ }
2969
+ triggerRefresh();
2970
+ }, [currentPath, triggerRefresh]);
2521
2971
  const navigateUp = useCallback2(() => {
2522
2972
  if (currentPath === "public") return;
2523
2973
  const parts = currentPath.split("/");
@@ -2609,7 +3059,9 @@ function StudioUI({ onClose, isVisible = true }) {
2609
3059
  isLoading,
2610
3060
  setIsLoading,
2611
3061
  refreshKey,
2612
- triggerRefresh
3062
+ triggerRefresh,
3063
+ searchQuery,
3064
+ setSearchQuery
2613
3065
  };
2614
3066
  return /* @__PURE__ */ jsx7(StudioContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs7("div", { css: styles7.container, children: [
2615
3067
  /* @__PURE__ */ jsxs7("div", { css: styles7.header, children: [
@@ -2628,7 +3080,22 @@ function StudioUI({ onClose, isVisible = true }) {
2628
3080
  ] })
2629
3081
  ] }),
2630
3082
  /* @__PURE__ */ jsx7(StudioToolbar, {}),
2631
- /* @__PURE__ */ jsx7("div", { css: styles7.content, children: /* @__PURE__ */ jsx7("div", { css: styles7.fileBrowser, children: viewMode === "grid" ? /* @__PURE__ */ jsx7(StudioFileGrid, {}) : /* @__PURE__ */ jsx7(StudioFileList, {}) }) }),
3083
+ /* @__PURE__ */ jsxs7(
3084
+ "div",
3085
+ {
3086
+ css: styles7.content,
3087
+ onDragOver: handleDragOver,
3088
+ onDragLeave: handleDragLeave,
3089
+ onDrop: handleDrop,
3090
+ children: [
3091
+ isDragging && /* @__PURE__ */ jsx7("div", { css: styles7.dropOverlay, children: /* @__PURE__ */ jsxs7("div", { css: styles7.dropMessage, children: [
3092
+ /* @__PURE__ */ jsx7("svg", { css: styles7.dropIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx7("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" }) }),
3093
+ /* @__PURE__ */ jsx7("span", { children: "Drop files to upload" })
3094
+ ] }) }),
3095
+ /* @__PURE__ */ jsx7("div", { css: styles7.fileBrowser, children: viewMode === "grid" ? /* @__PURE__ */ jsx7(StudioFileGrid, {}) : /* @__PURE__ */ jsx7(StudioFileList, {}) })
3096
+ ]
3097
+ }
3098
+ ),
2632
3099
  focusedItem && /* @__PURE__ */ jsx7(StudioDetailView, {})
2633
3100
  ] }) });
2634
3101
  }
@@ -2656,4 +3123,4 @@ export {
2656
3123
  StudioUI,
2657
3124
  StudioUI_default as default
2658
3125
  };
2659
- //# sourceMappingURL=StudioUI-DADFQY6I.mjs.map
3126
+ //# sourceMappingURL=StudioUI-4RENMCDG.mjs.map