@gallop.software/studio 0.1.38 → 0.1.39

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-DIJR5Y6S.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);
@@ -433,6 +436,27 @@ var styles2 = {
433
436
  border-radius: 6px;
434
437
  overflow: hidden;
435
438
  `,
439
+ searchInput: css2`
440
+ height: ${btnHeight};
441
+ padding: 0 12px;
442
+ border: 1px solid ${colors.border};
443
+ border-radius: 6px;
444
+ font-size: ${fontSize.base};
445
+ background: ${colors.surface};
446
+ color: ${colors.text};
447
+ width: 180px;
448
+ transition: all 0.15s ease;
449
+
450
+ &:focus {
451
+ outline: none;
452
+ border-color: ${colors.primary};
453
+ box-shadow: 0 0 0 2px ${colors.primaryLight};
454
+ }
455
+
456
+ &::placeholder {
457
+ color: ${colors.textMuted};
458
+ }
459
+ `,
436
460
  viewBtn: css2`
437
461
  height: 100%;
438
462
  padding: 0 10px;
@@ -774,9 +798,10 @@ function StudioToolbar() {
774
798
  const handleSyncCdn = useCallback(() => {
775
799
  console.log("Sync CDN clicked", selectedItems);
776
800
  }, [selectedItems]);
777
- const handleScan = useCallback(() => {
778
- console.log("Scan clicked");
779
- }, []);
801
+ const { searchQuery, setSearchQuery } = useStudio();
802
+ const handleSearch = useCallback((e) => {
803
+ setSearchQuery(e.target.value);
804
+ }, [setSearchQuery]);
780
805
  const hasSelection = selectedItems.size > 0;
781
806
  if (focusedItem) {
782
807
  return null;
@@ -891,10 +916,16 @@ function StudioToolbar() {
891
916
  ]
892
917
  }
893
918
  ),
894
- /* @__PURE__ */ jsxs2("button", { css: styles2.btn, onClick: handleScan, children: [
895
- /* @__PURE__ */ jsx2(ScanIcon, {}),
896
- "Scan"
897
- ] })
919
+ /* @__PURE__ */ jsx2(
920
+ "input",
921
+ {
922
+ css: styles2.searchInput,
923
+ type: "text",
924
+ placeholder: "Search images...",
925
+ value: searchQuery,
926
+ onChange: handleSearch
927
+ }
928
+ )
898
929
  ] }),
899
930
  /* @__PURE__ */ jsxs2("div", { css: styles2.right, children: [
900
931
  hasSelection && /* @__PURE__ */ jsxs2("span", { css: styles2.selectionCount, children: [
@@ -946,9 +977,6 @@ function TrashIcon() {
946
977
  function CloudIcon() {
947
978
  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
979
  }
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
980
  function GridIcon() {
953
981
  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
982
  }
@@ -1084,6 +1112,25 @@ var styles3 = {
1084
1112
  height: 56px;
1085
1113
  color: #f5a623;
1086
1114
  `,
1115
+ imagesFolderIcon: css3`
1116
+ width: 56px;
1117
+ height: 56px;
1118
+ color: ${colors.imagesFolder};
1119
+ `,
1120
+ imagesFolderWrapper: css3`
1121
+ position: relative;
1122
+ `,
1123
+ lockIcon: css3`
1124
+ position: absolute;
1125
+ bottom: 4px;
1126
+ right: 4px;
1127
+ width: 16px;
1128
+ height: 16px;
1129
+ color: ${colors.imagesFolder};
1130
+ background: white;
1131
+ border-radius: 50%;
1132
+ padding: 2px;
1133
+ `,
1087
1134
  parentIcon: css3`
1088
1135
  width: 56px;
1089
1136
  height: 56px;
@@ -1209,7 +1256,7 @@ var styles3 = {
1209
1256
  `
1210
1257
  };
1211
1258
  function StudioFileGrid() {
1212
- const { currentPath, setCurrentPath, navigateUp, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey, setFocusedItem, triggerRefresh } = useStudio();
1259
+ const { currentPath, setCurrentPath, navigateUp, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey, setFocusedItem, triggerRefresh, searchQuery } = useStudio();
1213
1260
  const [items, setItems] = useState2([]);
1214
1261
  const [loading, setLoading] = useState2(true);
1215
1262
  const isInitialLoad = useRef2(true);
@@ -1246,7 +1293,12 @@ function StudioFileGrid() {
1246
1293
  /* @__PURE__ */ jsx3("p", { css: styles3.emptyText, children: "Upload images to get started" })
1247
1294
  ] });
1248
1295
  }
1249
- const sortedItems = [...items].sort((a, b) => {
1296
+ const filteredItems = searchQuery ? items.filter((item) => {
1297
+ if (item.type === "folder") return true;
1298
+ const query = searchQuery.toLowerCase();
1299
+ return item.name.toLowerCase().includes(query);
1300
+ }) : items;
1301
+ const sortedItems = [...filteredItems].sort((a, b) => {
1250
1302
  if (a.type === "folder" && b.type !== "folder") return -1;
1251
1303
  if (a.type !== "folder" && b.type === "folder") return 1;
1252
1304
  return a.name.localeCompare(b.name);
@@ -1337,6 +1389,7 @@ function StudioFileGrid() {
1337
1389
  function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
1338
1390
  const isFolder = item.type === "folder";
1339
1391
  const isImage = !isFolder && item.thumbnail !== void 0;
1392
+ const isImagesFolder = isFolder && (item.name === "images" || item.path.includes("/images/"));
1340
1393
  return /* @__PURE__ */ jsxs3(
1341
1394
  "div",
1342
1395
  {
@@ -1360,7 +1413,10 @@ function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
1360
1413
  }
1361
1414
  ),
1362
1415
  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(
1416
+ /* @__PURE__ */ jsx3("div", { css: styles3.content, children: isFolder ? isImagesFolder ? /* @__PURE__ */ jsxs3("div", { css: styles3.imagesFolderWrapper, children: [
1417
+ /* @__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" }) }),
1418
+ /* @__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" }) })
1419
+ ] }) : /* @__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
1420
  "img",
1365
1421
  {
1366
1422
  css: styles3.image,
@@ -1513,6 +1569,7 @@ var styles4 = {
1513
1569
  `,
1514
1570
  parentRow: css4`
1515
1571
  cursor: pointer;
1572
+ border-bottom: 1px solid ${colors.border};
1516
1573
 
1517
1574
  &:hover {
1518
1575
  background-color: ${colors.surfaceHover};
@@ -1542,6 +1599,24 @@ var styles4 = {
1542
1599
  color: #f5a623;
1543
1600
  flex-shrink: 0;
1544
1601
  `,
1602
+ imagesFolderWrapper: css4`
1603
+ position: relative;
1604
+ display: flex;
1605
+ align-items: center;
1606
+ `,
1607
+ imagesFolderIcon: css4`
1608
+ width: 20px;
1609
+ height: 20px;
1610
+ color: ${colors.imagesFolder};
1611
+ flex-shrink: 0;
1612
+ `,
1613
+ lockIcon: css4`
1614
+ width: 10px;
1615
+ height: 10px;
1616
+ color: ${colors.imagesFolder};
1617
+ margin-left: -6px;
1618
+ margin-top: 8px;
1619
+ `,
1545
1620
  parentIcon: css4`
1546
1621
  width: 20px;
1547
1622
  height: 20px;
@@ -1635,7 +1710,7 @@ var styles4 = {
1635
1710
  `
1636
1711
  };
1637
1712
  function StudioFileList() {
1638
- const { currentPath, setCurrentPath, navigateUp, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey, setFocusedItem, triggerRefresh } = useStudio();
1713
+ const { currentPath, setCurrentPath, navigateUp, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey, setFocusedItem, triggerRefresh, searchQuery } = useStudio();
1639
1714
  const [items, setItems] = useState3([]);
1640
1715
  const [loading, setLoading] = useState3(true);
1641
1716
  const isInitialLoad = useRef3(true);
@@ -1668,7 +1743,12 @@ function StudioFileList() {
1668
1743
  if (items.length === 0 && isAtRoot) {
1669
1744
  return /* @__PURE__ */ jsx4("div", { css: styles4.empty, children: /* @__PURE__ */ jsx4("p", { children: "No files in this folder" }) });
1670
1745
  }
1671
- const sortedItems = [...items].sort((a, b) => {
1746
+ const filteredItems = searchQuery ? items.filter((item) => {
1747
+ if (item.type === "folder") return true;
1748
+ const query = searchQuery.toLowerCase();
1749
+ return item.name.toLowerCase().includes(query);
1750
+ }) : items;
1751
+ const sortedItems = [...filteredItems].sort((a, b) => {
1672
1752
  if (a.type === "folder" && b.type !== "folder") return -1;
1673
1753
  if (a.type !== "folder" && b.type === "folder") return 1;
1674
1754
  return a.name.localeCompare(b.name);
@@ -1756,6 +1836,7 @@ function StudioFileList() {
1756
1836
  function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
1757
1837
  const isFolder = item.type === "folder";
1758
1838
  const isImage = !isFolder && item.thumbnail !== void 0;
1839
+ const isImagesFolder = isFolder && (item.name === "images" || item.path.includes("/images/"));
1759
1840
  return /* @__PURE__ */ jsxs4(
1760
1841
  "tr",
1761
1842
  {
@@ -1779,7 +1860,10 @@ function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
1779
1860
  }
1780
1861
  ),
1781
1862
  /* @__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(
1863
+ isFolder ? isImagesFolder ? /* @__PURE__ */ jsxs4("div", { css: styles4.imagesFolderWrapper, children: [
1864
+ /* @__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" }) }),
1865
+ /* @__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" }) })
1866
+ ] }) : /* @__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
1867
  "button",
1784
1868
  {
1785
1869
  css: styles4.noThumbnail,
@@ -1863,6 +1947,7 @@ var styles5 = {
1863
1947
  flex: 1;
1864
1948
  margin: 24px;
1865
1949
  background: ${colors.surface};
1950
+ border: 1px solid ${colors.border};
1866
1951
  border-radius: 12px;
1867
1952
  overflow: hidden;
1868
1953
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
@@ -2281,15 +2366,43 @@ var styles6 = {
2281
2366
  color: ${colors.textSecondary};
2282
2367
  margin: 0 0 12px 0;
2283
2368
  `,
2369
+ codeWrapper: css6`
2370
+ position: relative;
2371
+ `,
2284
2372
  code: css6`
2285
2373
  background-color: ${colors.background};
2286
2374
  border-radius: 8px;
2287
2375
  padding: 12px;
2376
+ padding-right: 40px;
2288
2377
  font-family: 'SF Mono', Monaco, Consolas, monospace;
2289
2378
  font-size: ${fontSize.xs};
2290
2379
  color: ${colors.textSecondary};
2291
2380
  border: 1px solid ${colors.border};
2292
2381
  `,
2382
+ copyBtn: css6`
2383
+ position: absolute;
2384
+ top: 8px;
2385
+ right: 8px;
2386
+ padding: 4px;
2387
+ background: ${colors.surface};
2388
+ border: 1px solid ${colors.border};
2389
+ border-radius: 4px;
2390
+ cursor: pointer;
2391
+ transition: all 0.15s ease;
2392
+ display: flex;
2393
+ align-items: center;
2394
+ justify-content: center;
2395
+
2396
+ &:hover {
2397
+ background-color: ${colors.surfaceHover};
2398
+ border-color: ${colors.borderHover};
2399
+ }
2400
+ `,
2401
+ copyIcon: css6`
2402
+ width: 14px;
2403
+ height: 14px;
2404
+ color: ${colors.textSecondary};
2405
+ `,
2293
2406
  codeLine: css6`
2294
2407
  margin: 0 0 4px 0;
2295
2408
 
@@ -2393,7 +2506,18 @@ function StudioSettings() {
2393
2506
  isOpen && /* @__PURE__ */ jsx6(SettingsPanel, { onClose: () => setIsOpen(false) })
2394
2507
  ] });
2395
2508
  }
2509
+ var envTemplate = `CLOUDFLARE_R2_ACCOUNT_ID=abc123def456ghi789
2510
+ CLOUDFLARE_R2_ACCESS_KEY_ID=your_access_key_id_here
2511
+ CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_secret_access_key_here
2512
+ CLOUDFLARE_R2_BUCKET_NAME=my-images-bucket
2513
+ CLOUDFLARE_R2_PUBLIC_URL=https://cdn.yourdomain.com`;
2396
2514
  function SettingsPanel({ onClose }) {
2515
+ const [copied, setCopied] = useState5(false);
2516
+ const handleCopy = () => {
2517
+ navigator.clipboard.writeText(envTemplate);
2518
+ setCopied(true);
2519
+ setTimeout(() => setCopied(false), 2e3);
2520
+ };
2397
2521
  return /* @__PURE__ */ jsx6("div", { css: styles6.overlay, onClick: onClose, children: /* @__PURE__ */ jsxs6("div", { css: styles6.panel, onClick: (e) => e.stopPropagation(), children: [
2398
2522
  /* @__PURE__ */ jsxs6("div", { css: styles6.header, children: [
2399
2523
  /* @__PURE__ */ jsx6("h2", { css: styles6.title, children: "Settings" }),
@@ -2403,12 +2527,15 @@ function SettingsPanel({ onClose }) {
2403
2527
  /* @__PURE__ */ jsxs6("section", { children: [
2404
2528
  /* @__PURE__ */ jsx6("h3", { css: styles6.sectionTitle, children: "Cloudflare R2" }),
2405
2529
  /* @__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" })
2530
+ /* @__PURE__ */ jsxs6("div", { css: styles6.codeWrapper, children: [
2531
+ /* @__PURE__ */ jsx6("button", { css: styles6.copyBtn, onClick: handleCopy, title: copied ? "Copied!" : "Copy to clipboard", children: 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" }) }) }),
2532
+ /* @__PURE__ */ jsxs6("div", { css: styles6.code, children: [
2533
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_ACCOUNT_ID=abc123def456ghi789" }),
2534
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_ACCESS_KEY_ID=your_access_key_id_here" }),
2535
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_secret_access_key_here" }),
2536
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_BUCKET_NAME=my-images-bucket" }),
2537
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_PUBLIC_URL=https://cdn.yourdomain.com" })
2538
+ ] })
2412
2539
  ] })
2413
2540
  ] }),
2414
2541
  /* @__PURE__ */ jsxs6("section", { children: [
@@ -2504,6 +2631,34 @@ var styles7 = {
2504
2631
  min-width: 0;
2505
2632
  overflow: auto;
2506
2633
  padding: 20px 24px;
2634
+ `,
2635
+ dropOverlay: css7`
2636
+ position: absolute;
2637
+ top: 0;
2638
+ left: 0;
2639
+ right: 0;
2640
+ bottom: 0;
2641
+ background: rgba(99, 91, 255, 0.1);
2642
+ border: 3px dashed ${colors.primary};
2643
+ border-radius: 8px;
2644
+ display: flex;
2645
+ align-items: center;
2646
+ justify-content: center;
2647
+ z-index: 50;
2648
+ pointer-events: none;
2649
+ `,
2650
+ dropMessage: css7`
2651
+ display: flex;
2652
+ flex-direction: column;
2653
+ align-items: center;
2654
+ gap: 12px;
2655
+ color: ${colors.primary};
2656
+ font-size: ${fontSize.lg};
2657
+ font-weight: 600;
2658
+ `,
2659
+ dropIcon: css7`
2660
+ width: 48px;
2661
+ height: 48px;
2507
2662
  `
2508
2663
  };
2509
2664
  function StudioUI({ onClose, isVisible = true }) {
@@ -2515,9 +2670,45 @@ function StudioUI({ onClose, isVisible = true }) {
2515
2670
  const [meta, setMeta] = useState6(null);
2516
2671
  const [isLoading, setIsLoading] = useState6(false);
2517
2672
  const [refreshKey, setRefreshKey] = useState6(0);
2673
+ const [searchQuery, setSearchQuery] = useState6("");
2674
+ const [isDragging, setIsDragging] = useState6(false);
2518
2675
  const triggerRefresh = useCallback2(() => {
2519
2676
  setRefreshKey((k) => k + 1);
2520
2677
  }, []);
2678
+ const handleDragOver = useCallback2((e) => {
2679
+ e.preventDefault();
2680
+ e.stopPropagation();
2681
+ setIsDragging(true);
2682
+ }, []);
2683
+ const handleDragLeave = useCallback2((e) => {
2684
+ e.preventDefault();
2685
+ e.stopPropagation();
2686
+ setIsDragging(false);
2687
+ }, []);
2688
+ const handleDrop = useCallback2(async (e) => {
2689
+ e.preventDefault();
2690
+ e.stopPropagation();
2691
+ setIsDragging(false);
2692
+ const files = Array.from(e.dataTransfer.files);
2693
+ if (files.length === 0) return;
2694
+ if (currentPath === "public/images" || currentPath.startsWith("public/images/")) {
2695
+ return;
2696
+ }
2697
+ for (const file of files) {
2698
+ const formData = new FormData();
2699
+ formData.append("file", file);
2700
+ formData.append("path", currentPath);
2701
+ try {
2702
+ await fetch("/api/studio/upload", {
2703
+ method: "POST",
2704
+ body: formData
2705
+ });
2706
+ } catch (error) {
2707
+ console.error("Upload error:", error);
2708
+ }
2709
+ }
2710
+ triggerRefresh();
2711
+ }, [currentPath, triggerRefresh]);
2521
2712
  const navigateUp = useCallback2(() => {
2522
2713
  if (currentPath === "public") return;
2523
2714
  const parts = currentPath.split("/");
@@ -2609,7 +2800,9 @@ function StudioUI({ onClose, isVisible = true }) {
2609
2800
  isLoading,
2610
2801
  setIsLoading,
2611
2802
  refreshKey,
2612
- triggerRefresh
2803
+ triggerRefresh,
2804
+ searchQuery,
2805
+ setSearchQuery
2613
2806
  };
2614
2807
  return /* @__PURE__ */ jsx7(StudioContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs7("div", { css: styles7.container, children: [
2615
2808
  /* @__PURE__ */ jsxs7("div", { css: styles7.header, children: [
@@ -2628,7 +2821,22 @@ function StudioUI({ onClose, isVisible = true }) {
2628
2821
  ] })
2629
2822
  ] }),
2630
2823
  /* @__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, {}) }) }),
2824
+ /* @__PURE__ */ jsxs7(
2825
+ "div",
2826
+ {
2827
+ css: styles7.content,
2828
+ onDragOver: handleDragOver,
2829
+ onDragLeave: handleDragLeave,
2830
+ onDrop: handleDrop,
2831
+ children: [
2832
+ isDragging && /* @__PURE__ */ jsx7("div", { css: styles7.dropOverlay, children: /* @__PURE__ */ jsxs7("div", { css: styles7.dropMessage, children: [
2833
+ /* @__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" }) }),
2834
+ /* @__PURE__ */ jsx7("span", { children: "Drop files to upload" })
2835
+ ] }) }),
2836
+ /* @__PURE__ */ jsx7("div", { css: styles7.fileBrowser, children: viewMode === "grid" ? /* @__PURE__ */ jsx7(StudioFileGrid, {}) : /* @__PURE__ */ jsx7(StudioFileList, {}) })
2837
+ ]
2838
+ }
2839
+ ),
2632
2840
  focusedItem && /* @__PURE__ */ jsx7(StudioDetailView, {})
2633
2841
  ] }) });
2634
2842
  }
@@ -2656,4 +2864,4 @@ export {
2656
2864
  StudioUI,
2657
2865
  StudioUI_default as default
2658
2866
  };
2659
- //# sourceMappingURL=StudioUI-DADFQY6I.mjs.map
2867
+ //# sourceMappingURL=StudioUI-RHBJ6LVY.mjs.map