@gallop.software/studio 0.1.22 → 0.1.24

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.
@@ -8,7 +8,7 @@ import {
8
8
 
9
9
  // src/components/StudioUI.tsx
10
10
  import { useEffect as useEffect3, useCallback as useCallback2, useState as useState6 } from "react";
11
- import { css as css8 } from "@emotion/react";
11
+ import { css as css7 } from "@emotion/react";
12
12
 
13
13
  // src/components/StudioContext.tsx
14
14
  import { createContext, useContext } from "react";
@@ -38,6 +38,9 @@ var defaultState = {
38
38
  viewMode: "grid",
39
39
  setViewMode: () => {
40
40
  },
41
+ focusedItem: null,
42
+ setFocusedItem: () => {
43
+ },
41
44
  meta: null,
42
45
  setMeta: () => {
43
46
  },
@@ -204,6 +207,7 @@ function AlertModal({
204
207
 
205
208
  // src/components/StudioToolbar.tsx
206
209
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "@emotion/react/jsx-runtime";
210
+ var btnHeight = "36px";
207
211
  var spin = keyframes2`
208
212
  to { transform: rotate(360deg); }
209
213
  `;
@@ -224,13 +228,15 @@ var styles2 = {
224
228
  right: css2`
225
229
  display: flex;
226
230
  align-items: center;
227
- gap: 12px;
231
+ gap: 8px;
228
232
  `,
229
233
  btn: css2`
230
234
  display: inline-flex;
231
235
  align-items: center;
236
+ justify-content: center;
232
237
  gap: 6px;
233
- padding: 8px 14px;
238
+ height: ${btnHeight};
239
+ padding: 0 14px;
234
240
  border-radius: 6px;
235
241
  font-size: ${fontSize.base};
236
242
  font-weight: 500;
@@ -251,6 +257,9 @@ var styles2 = {
251
257
  opacity: 0.5;
252
258
  }
253
259
  `,
260
+ btnIconOnly: css2`
261
+ padding: 0 10px;
262
+ `,
254
263
  btnPrimary: css2`
255
264
  background: ${colors.primary};
256
265
  border-color: ${colors.primary};
@@ -270,8 +279,8 @@ var styles2 = {
270
279
  }
271
280
  `,
272
281
  icon: css2`
273
- width: 15px;
274
- height: 15px;
282
+ width: 16px;
283
+ height: 16px;
275
284
  `,
276
285
  iconSpin: css2`
277
286
  animation: ${spin} 1s linear infinite;
@@ -282,6 +291,7 @@ var styles2 = {
282
291
  display: flex;
283
292
  align-items: center;
284
293
  gap: 8px;
294
+ margin-right: 8px;
285
295
  `,
286
296
  clearBtn: css2`
287
297
  color: ${colors.primary};
@@ -305,16 +315,17 @@ var styles2 = {
305
315
  viewToggle: css2`
306
316
  display: flex;
307
317
  align-items: center;
308
- background-color: ${colors.surfaceHover};
318
+ height: ${btnHeight};
319
+ background-color: ${colors.surface};
309
320
  border: 1px solid ${colors.border};
310
321
  border-radius: 6px;
311
- padding: 2px;
322
+ overflow: hidden;
312
323
  `,
313
324
  viewBtn: css2`
314
- padding: 6px 8px;
325
+ height: 100%;
326
+ padding: 0 10px;
315
327
  background: transparent;
316
328
  border: none;
317
- border-radius: 4px;
318
329
  cursor: pointer;
319
330
  color: ${colors.textSecondary};
320
331
  transition: all 0.15s ease;
@@ -324,20 +335,24 @@ var styles2 = {
324
335
 
325
336
  &:hover {
326
337
  color: ${colors.text};
338
+ background-color: ${colors.surfaceHover};
327
339
  }
328
340
  `,
329
341
  viewBtnActive: css2`
330
- background-color: ${colors.surface};
342
+ background-color: ${colors.background};
331
343
  color: ${colors.text};
332
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
333
344
  `
334
345
  };
335
346
  function StudioToolbar() {
336
- const { selectedItems, viewMode, setViewMode, clearSelection, currentPath, triggerRefresh } = useStudio();
347
+ const { selectedItems, viewMode, setViewMode, clearSelection, currentPath, triggerRefresh, focusedItem } = useStudio();
337
348
  const fileInputRef = useRef(null);
338
349
  const [uploading, setUploading] = useState(false);
339
350
  const [refreshing, setRefreshing] = useState(false);
351
+ const [processing, setProcessing] = useState(false);
340
352
  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
353
+ const [showProcessConfirm, setShowProcessConfirm] = useState(false);
354
+ const [processCount, setProcessCount] = useState(0);
355
+ const [processMode, setProcessMode] = useState("all");
341
356
  const [alertMessage, setAlertMessage] = useState(null);
342
357
  const isInImagesFolder = currentPath === "public/images" || currentPath.startsWith("public/images/");
343
358
  const handleUpload = useCallback(() => {
@@ -391,9 +406,107 @@ function StudioToolbar() {
391
406
  }
392
407
  }
393
408
  }, [currentPath, triggerRefresh]);
394
- const handleReprocess = useCallback(() => {
395
- console.log("Reprocess clicked", selectedItems);
409
+ const handleProcessImages = useCallback(async () => {
410
+ const hasSelection2 = selectedItems.size > 0;
411
+ if (hasSelection2) {
412
+ const selectedImagePaths = Array.from(selectedItems).filter((p) => {
413
+ const ext = p.split(".").pop()?.toLowerCase() || "";
414
+ return ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"].includes(ext);
415
+ });
416
+ if (selectedImagePaths.length === 0) {
417
+ setAlertMessage({
418
+ title: "No Images Selected",
419
+ message: "Please select image files to process."
420
+ });
421
+ return;
422
+ }
423
+ setProcessCount(selectedImagePaths.length);
424
+ setProcessMode("selected");
425
+ setShowProcessConfirm(true);
426
+ } else {
427
+ try {
428
+ const response = await fetch("/api/studio/count-unprocessed");
429
+ const data = await response.json();
430
+ if (data.count === 0) {
431
+ setAlertMessage({
432
+ title: "All Images Processed",
433
+ message: "All images in the public folder have already been processed."
434
+ });
435
+ return;
436
+ }
437
+ setProcessCount(data.count);
438
+ setProcessMode("all");
439
+ setShowProcessConfirm(true);
440
+ } catch (error) {
441
+ console.error("Failed to count unprocessed images:", error);
442
+ setAlertMessage({
443
+ title: "Error",
444
+ message: "Failed to count unprocessed images."
445
+ });
446
+ }
447
+ }
396
448
  }, [selectedItems]);
449
+ const handleProcessConfirm = useCallback(async () => {
450
+ setShowProcessConfirm(false);
451
+ setProcessing(true);
452
+ try {
453
+ if (processMode === "all") {
454
+ const response = await fetch("/api/studio/process-all", {
455
+ method: "POST"
456
+ });
457
+ const data = await response.json();
458
+ if (response.ok) {
459
+ const message = [
460
+ `Processed ${data.processed?.length || 0} images.`,
461
+ data.orphansRemoved?.length > 0 ? `Removed ${data.orphansRemoved.length} orphaned thumbnails.` : "",
462
+ data.errors?.length > 0 ? `${data.errors.length} errors occurred.` : ""
463
+ ].filter(Boolean).join(" ");
464
+ setAlertMessage({
465
+ title: "Processing Complete",
466
+ message
467
+ });
468
+ triggerRefresh();
469
+ } else {
470
+ setAlertMessage({
471
+ title: "Processing Failed",
472
+ message: data.error || "Unknown error"
473
+ });
474
+ }
475
+ } else {
476
+ const selectedImageKeys = Array.from(selectedItems).filter((p) => {
477
+ const ext = p.split(".").pop()?.toLowerCase() || "";
478
+ return ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"].includes(ext);
479
+ }).map((p) => p.replace(/^public\//, ""));
480
+ const response = await fetch("/api/studio/reprocess", {
481
+ method: "POST",
482
+ headers: { "Content-Type": "application/json" },
483
+ body: JSON.stringify({ imageKeys: selectedImageKeys })
484
+ });
485
+ const data = await response.json();
486
+ if (response.ok) {
487
+ setAlertMessage({
488
+ title: "Processing Complete",
489
+ message: `Processed ${data.processed?.length || 0} images.${data.errors?.length > 0 ? ` ${data.errors.length} errors occurred.` : ""}`
490
+ });
491
+ clearSelection();
492
+ triggerRefresh();
493
+ } else {
494
+ setAlertMessage({
495
+ title: "Processing Failed",
496
+ message: data.error || "Unknown error"
497
+ });
498
+ }
499
+ }
500
+ } catch (error) {
501
+ console.error("Processing error:", error);
502
+ setAlertMessage({
503
+ title: "Processing Failed",
504
+ message: "Processing failed. Check console for details."
505
+ });
506
+ } finally {
507
+ setProcessing(false);
508
+ }
509
+ }, [processMode, selectedItems, clearSelection, triggerRefresh]);
397
510
  const handleDeleteClick = useCallback(() => {
398
511
  if (selectedItems.size === 0) return;
399
512
  setShowDeleteConfirm(true);
@@ -431,6 +544,9 @@ function StudioToolbar() {
431
544
  console.log("Scan clicked");
432
545
  }, []);
433
546
  const hasSelection = selectedItems.size > 0;
547
+ if (focusedItem) {
548
+ return null;
549
+ }
434
550
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
435
551
  showDeleteConfirm && /* @__PURE__ */ jsx2(
436
552
  ConfirmModal,
@@ -443,6 +559,16 @@ function StudioToolbar() {
443
559
  onCancel: () => setShowDeleteConfirm(false)
444
560
  }
445
561
  ),
562
+ showProcessConfirm && /* @__PURE__ */ jsx2(
563
+ ConfirmModal,
564
+ {
565
+ title: "Process Images",
566
+ message: processMode === "all" ? `Found ${processCount} unprocessed image${processCount !== 1 ? "s" : ""} in the public folder. This will generate thumbnails and remove any orphaned files from the images folder.` : `Process ${processCount} selected image${processCount !== 1 ? "s" : ""}? This will regenerate thumbnails for these files.`,
567
+ confirmLabel: processing ? "Processing..." : "Process",
568
+ onConfirm: handleProcessConfirm,
569
+ onCancel: () => setShowProcessConfirm(false)
570
+ }
571
+ ),
446
572
  alertMessage && /* @__PURE__ */ jsx2(
447
573
  AlertModal,
448
574
  {
@@ -481,11 +607,11 @@ function StudioToolbar() {
481
607
  "button",
482
608
  {
483
609
  css: styles2.btn,
484
- onClick: handleReprocess,
485
- disabled: !hasSelection,
610
+ onClick: handleProcessImages,
611
+ disabled: processing,
486
612
  children: [
487
- /* @__PURE__ */ jsx2(RefreshIcon, {}),
488
- "Reprocess"
613
+ /* @__PURE__ */ jsx2(ImageStackIcon, {}),
614
+ processing ? "Processing..." : "Process Images"
489
615
  ]
490
616
  }
491
617
  ),
@@ -527,7 +653,7 @@ function StudioToolbar() {
527
653
  /* @__PURE__ */ jsx2(
528
654
  "button",
529
655
  {
530
- css: styles2.btn,
656
+ css: [styles2.btn, styles2.btnIconOnly],
531
657
  onClick: handleRefresh,
532
658
  children: /* @__PURE__ */ jsx2(RefreshIcon, { spinning: refreshing })
533
659
  }
@@ -577,119 +703,25 @@ function GridIcon() {
577
703
  function ListIcon() {
578
704
  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 6h16M4 10h16M4 14h16M4 18h16" }) });
579
705
  }
580
-
581
- // src/components/StudioBreadcrumb.tsx
582
- import { css as css3 } from "@emotion/react";
583
- import { jsx as jsx3, jsxs as jsxs3 } from "@emotion/react/jsx-runtime";
584
- var styles3 = {
585
- container: css3`
586
- display: flex;
587
- align-items: center;
588
- gap: 8px;
589
- padding: 10px 24px;
590
- background-color: ${colors.surface};
591
- border-bottom: 1px solid ${colors.borderLight};
592
- `,
593
- backBtn: css3`
594
- padding: 6px;
595
- background: ${colors.surface};
596
- border: 1px solid ${colors.border};
597
- border-radius: 6px;
598
- cursor: pointer;
599
- transition: all 0.15s ease;
600
- display: flex;
601
- align-items: center;
602
- justify-content: center;
603
-
604
- &:hover {
605
- background-color: ${colors.surfaceHover};
606
- border-color: ${colors.borderHover};
607
- }
608
- `,
609
- backIcon: css3`
610
- width: 16px;
611
- height: 16px;
612
- color: ${colors.textSecondary};
613
- `,
614
- nav: css3`
615
- display: flex;
616
- align-items: center;
617
- gap: 2px;
618
- font-size: ${fontSize.base};
619
- `,
620
- item: css3`
621
- display: flex;
622
- align-items: center;
623
- gap: 2px;
624
- `,
625
- separator: css3`
626
- color: ${colors.textMuted};
627
- margin: 0 2px;
628
- `,
629
- btn: css3`
630
- padding: 4px 8px;
631
- background: none;
632
- border: none;
633
- border-radius: 4px;
634
- cursor: pointer;
635
- transition: all 0.15s ease;
636
- font-size: ${fontSize.base};
637
- letter-spacing: -0.01em;
638
-
639
- &:hover {
640
- background-color: ${colors.surfaceHover};
641
- }
642
- `,
643
- btnActive: css3`
644
- color: ${colors.text};
645
- font-weight: 600;
646
- `,
647
- btnInactive: css3`
648
- color: ${colors.textSecondary};
649
-
650
- &:hover {
651
- color: ${colors.text};
652
- }
653
- `
654
- };
655
- function StudioBreadcrumb() {
656
- const { currentPath, setCurrentPath, navigateUp } = useStudio();
657
- const parts = currentPath.split("/").filter(Boolean);
658
- const handleClick = (index) => {
659
- const newPath = parts.slice(0, index + 1).join("/");
660
- setCurrentPath(newPath);
661
- };
662
- return /* @__PURE__ */ jsxs3("div", { css: styles3.container, children: [
663
- currentPath !== "public" && /* @__PURE__ */ jsx3("button", { css: styles3.backBtn, onClick: navigateUp, "aria-label": "Go back", children: /* @__PURE__ */ jsx3("svg", { css: styles3.backIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }),
664
- /* @__PURE__ */ jsx3("nav", { css: styles3.nav, children: parts.map((part, index) => /* @__PURE__ */ jsxs3("span", { css: styles3.item, children: [
665
- index > 0 && /* @__PURE__ */ jsx3("span", { css: styles3.separator, children: "/" }),
666
- /* @__PURE__ */ jsx3(
667
- "button",
668
- {
669
- css: [styles3.btn, index === parts.length - 1 ? styles3.btnActive : styles3.btnInactive],
670
- onClick: () => handleClick(index),
671
- children: part
672
- }
673
- )
674
- ] }, index)) })
675
- ] });
706
+ function ImageStackIcon() {
707
+ 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 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" }) });
676
708
  }
677
709
 
678
710
  // src/components/StudioFileGrid.tsx
679
711
  import { useEffect, useState as useState2 } from "react";
680
- import { css as css4, keyframes as keyframes3 } from "@emotion/react";
681
- import { jsx as jsx4, jsxs as jsxs4 } from "@emotion/react/jsx-runtime";
712
+ import { css as css3, keyframes as keyframes3 } from "@emotion/react";
713
+ import { jsx as jsx3, jsxs as jsxs3 } from "@emotion/react/jsx-runtime";
682
714
  var spin2 = keyframes3`
683
715
  to { transform: rotate(360deg); }
684
716
  `;
685
- var styles4 = {
686
- loading: css4`
717
+ var styles3 = {
718
+ loading: css3`
687
719
  display: flex;
688
720
  align-items: center;
689
721
  justify-content: center;
690
722
  height: 256px;
691
723
  `,
692
- spinner: css4`
724
+ spinner: css3`
693
725
  width: 32px;
694
726
  height: 32px;
695
727
  border-radius: 50%;
@@ -697,7 +729,7 @@ var styles4 = {
697
729
  border-top-color: ${colors.primary};
698
730
  animation: ${spin2} 0.8s linear infinite;
699
731
  `,
700
- empty: css4`
732
+ empty: css3`
701
733
  display: flex;
702
734
  flex-direction: column;
703
735
  align-items: center;
@@ -705,13 +737,13 @@ var styles4 = {
705
737
  height: 256px;
706
738
  color: ${colors.textSecondary};
707
739
  `,
708
- emptyIcon: css4`
740
+ emptyIcon: css3`
709
741
  width: 48px;
710
742
  height: 48px;
711
743
  margin-bottom: 16px;
712
744
  opacity: 0.5;
713
745
  `,
714
- emptyText: css4`
746
+ emptyText: css3`
715
747
  font-size: ${fontSize.base};
716
748
  margin: 0 0 4px 0;
717
749
 
@@ -720,7 +752,7 @@ var styles4 = {
720
752
  font-size: ${fontSize.sm};
721
753
  }
722
754
  `,
723
- grid: css4`
755
+ grid: css3`
724
756
  display: grid;
725
757
  grid-template-columns: repeat(2, 1fr);
726
758
  gap: 12px;
@@ -730,7 +762,7 @@ var styles4 = {
730
762
  @media (min-width: 1024px) { grid-template-columns: repeat(5, 1fr); }
731
763
  @media (min-width: 1280px) { grid-template-columns: repeat(6, 1fr); }
732
764
  `,
733
- item: css4`
765
+ item: css3`
734
766
  position: relative;
735
767
  border-radius: 8px;
736
768
  border: 1px solid ${colors.border};
@@ -746,7 +778,7 @@ var styles4 = {
746
778
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.06);
747
779
  }
748
780
  `,
749
- itemSelected: css4`
781
+ itemSelected: css3`
750
782
  border-color: ${colors.primary};
751
783
  box-shadow: 0 0 0 1px ${colors.primary};
752
784
 
@@ -754,7 +786,14 @@ var styles4 = {
754
786
  border-color: ${colors.primary};
755
787
  }
756
788
  `,
757
- checkboxWrapper: css4`
789
+ parentItem: css3`
790
+ cursor: pointer;
791
+
792
+ &:hover {
793
+ border-color: ${colors.primary};
794
+ }
795
+ `,
796
+ checkboxWrapper: css3`
758
797
  position: absolute;
759
798
  top: 0;
760
799
  left: 0;
@@ -762,13 +801,13 @@ var styles4 = {
762
801
  padding: 8px;
763
802
  cursor: pointer;
764
803
  `,
765
- checkbox: css4`
804
+ checkbox: css3`
766
805
  width: 16px;
767
806
  height: 16px;
768
807
  accent-color: ${colors.primary};
769
808
  cursor: pointer;
770
809
  `,
771
- cdnBadge: css4`
810
+ cdnBadge: css3`
772
811
  position: absolute;
773
812
  top: 8px;
774
813
  right: 8px;
@@ -780,7 +819,7 @@ var styles4 = {
780
819
  padding: 2px 8px;
781
820
  border-radius: 4px;
782
821
  `,
783
- content: css4`
822
+ content: css3`
784
823
  aspect-ratio: 1;
785
824
  display: flex;
786
825
  align-items: center;
@@ -788,38 +827,43 @@ var styles4 = {
788
827
  padding: 16px;
789
828
  background: ${colors.background};
790
829
  `,
791
- folderIcon: css4`
830
+ folderIcon: css3`
792
831
  width: 56px;
793
832
  height: 56px;
794
833
  color: #f5a623;
795
834
  `,
796
- fileIcon: css4`
835
+ parentIcon: css3`
836
+ width: 56px;
837
+ height: 56px;
838
+ color: ${colors.textMuted};
839
+ `,
840
+ fileIcon: css3`
797
841
  width: 40px;
798
842
  height: 40px;
799
843
  color: ${colors.textMuted};
800
844
  `,
801
- image: css4`
845
+ image: css3`
802
846
  max-width: 100%;
803
847
  max-height: 100%;
804
848
  object-fit: contain;
805
849
  border-radius: 4px;
806
850
  `,
807
- label: css4`
851
+ label: css3`
808
852
  padding: 10px 12px;
809
853
  background-color: ${colors.surface};
810
854
  border-top: 1px solid ${colors.borderLight};
811
855
  `,
812
- labelRow: css4`
856
+ labelRow: css3`
813
857
  display: flex;
814
858
  align-items: center;
815
859
  justify-content: space-between;
816
860
  gap: 8px;
817
861
  `,
818
- labelText: css4`
862
+ labelText: css3`
819
863
  flex: 1;
820
864
  min-width: 0;
821
865
  `,
822
- name: css4`
866
+ name: css3`
823
867
  font-size: ${fontSize.sm};
824
868
  font-weight: 500;
825
869
  color: ${colors.text};
@@ -829,29 +873,32 @@ var styles4 = {
829
873
  margin: 0;
830
874
  letter-spacing: -0.01em;
831
875
  `,
832
- size: css4`
876
+ size: css3`
833
877
  font-size: ${fontSize.xs};
834
878
  color: ${colors.textMuted};
835
879
  margin: 2px 0 0 0;
836
880
  `,
837
- openBtn: css4`
881
+ openBtn: css3`
838
882
  flex-shrink: 0;
883
+ height: 28px;
839
884
  font-size: ${fontSize.xs};
840
885
  font-weight: 500;
841
886
  color: ${colors.primary};
842
887
  background: ${colors.surface};
843
888
  border: 1px solid ${colors.border};
844
- padding: 4px 10px;
889
+ padding: 0 10px;
845
890
  cursor: pointer;
846
891
  border-radius: 4px;
847
892
  transition: all 0.15s ease;
893
+ display: inline-flex;
894
+ align-items: center;
848
895
 
849
896
  &:hover {
850
897
  background-color: ${colors.primaryLight};
851
898
  border-color: ${colors.primary};
852
899
  }
853
900
  `,
854
- selectAllRow: css4`
901
+ selectAllRow: css3`
855
902
  display: flex;
856
903
  align-items: center;
857
904
  margin-bottom: 16px;
@@ -860,7 +907,7 @@ var styles4 = {
860
907
  border-radius: 8px;
861
908
  border: 1px solid ${colors.border};
862
909
  `,
863
- selectAllLabel: css4`
910
+ selectAllLabel: css3`
864
911
  display: flex;
865
912
  align-items: center;
866
913
  gap: 10px;
@@ -873,14 +920,14 @@ var styles4 = {
873
920
  color: ${colors.text};
874
921
  }
875
922
  `,
876
- selectAllCheckbox: css4`
923
+ selectAllCheckbox: css3`
877
924
  width: 16px;
878
925
  height: 16px;
879
926
  accent-color: ${colors.primary};
880
927
  `
881
928
  };
882
929
  function StudioFileGrid() {
883
- const { currentPath, setCurrentPath, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey } = useStudio();
930
+ const { currentPath, setCurrentPath, navigateUp, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey, setFocusedItem } = useStudio();
884
931
  const [items, setItems] = useState2([]);
885
932
  const [loading, setLoading] = useState2(true);
886
933
  useEffect(() => {
@@ -900,13 +947,14 @@ function StudioFileGrid() {
900
947
  loadItems();
901
948
  }, [currentPath, refreshKey]);
902
949
  if (loading) {
903
- return /* @__PURE__ */ jsx4("div", { css: styles4.loading, children: /* @__PURE__ */ jsx4("div", { css: styles4.spinner }) });
950
+ return /* @__PURE__ */ jsx3("div", { css: styles3.loading, children: /* @__PURE__ */ jsx3("div", { css: styles3.spinner }) });
904
951
  }
905
- if (items.length === 0) {
906
- return /* @__PURE__ */ jsxs4("div", { css: styles4.empty, children: [
907
- /* @__PURE__ */ jsx4("svg", { css: styles4.emptyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("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" }) }),
908
- /* @__PURE__ */ jsx4("p", { css: styles4.emptyText, children: "No files in this folder" }),
909
- /* @__PURE__ */ jsx4("p", { css: styles4.emptyText, children: "Upload images to get started" })
952
+ const isAtRoot = currentPath === "public";
953
+ if (items.length === 0 && isAtRoot) {
954
+ return /* @__PURE__ */ jsxs3("div", { css: styles3.empty, children: [
955
+ /* @__PURE__ */ jsx3("svg", { css: styles3.emptyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("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" }) }),
956
+ /* @__PURE__ */ jsx3("p", { css: styles3.emptyText, children: "No files in this folder" }),
957
+ /* @__PURE__ */ jsx3("p", { css: styles3.emptyText, children: "Upload images to get started" })
910
958
  ] });
911
959
  }
912
960
  const sortedItems = [...items].sort((a, b) => {
@@ -921,9 +969,11 @@ function StudioFileGrid() {
921
969
  toggleSelection(item.path);
922
970
  }
923
971
  };
924
- const handleOpenFolder = (item) => {
972
+ const handleOpen = (item) => {
925
973
  if (item.type === "folder") {
926
974
  setCurrentPath(item.path);
975
+ } else {
976
+ setFocusedItem(item);
927
977
  }
928
978
  };
929
979
  const allItemsSelected = sortedItems.length > 0 && sortedItems.every((item) => selectedItems.has(item.path));
@@ -935,13 +985,13 @@ function StudioFileGrid() {
935
985
  selectAll(sortedItems);
936
986
  }
937
987
  };
938
- return /* @__PURE__ */ jsxs4("div", { children: [
939
- sortedItems.length > 0 && /* @__PURE__ */ jsx4("div", { css: styles4.selectAllRow, children: /* @__PURE__ */ jsxs4("label", { css: styles4.selectAllLabel, children: [
940
- /* @__PURE__ */ jsx4(
988
+ return /* @__PURE__ */ jsxs3("div", { children: [
989
+ sortedItems.length > 0 && /* @__PURE__ */ jsx3("div", { css: styles3.selectAllRow, children: /* @__PURE__ */ jsxs3("label", { css: styles3.selectAllLabel, children: [
990
+ /* @__PURE__ */ jsx3(
941
991
  "input",
942
992
  {
943
993
  type: "checkbox",
944
- css: styles4.selectAllCheckbox,
994
+ css: styles3.selectAllCheckbox,
945
995
  checked: allItemsSelected,
946
996
  ref: (el) => {
947
997
  if (el) el.indeterminate = someItemsSelected && !allItemsSelected;
@@ -953,65 +1003,81 @@ function StudioFileGrid() {
953
1003
  sortedItems.length,
954
1004
  ")"
955
1005
  ] }) }),
956
- /* @__PURE__ */ jsx4("div", { css: styles4.grid, children: sortedItems.map((item) => /* @__PURE__ */ jsx4(
957
- GridItem,
958
- {
959
- item,
960
- isSelected: selectedItems.has(item.path),
961
- onClick: (e) => handleItemClick(item, e),
962
- onOpen: () => handleOpenFolder(item)
963
- },
964
- item.path
965
- )) })
1006
+ /* @__PURE__ */ jsxs3("div", { css: styles3.grid, children: [
1007
+ !isAtRoot && /* @__PURE__ */ jsxs3(
1008
+ "div",
1009
+ {
1010
+ css: [styles3.item, styles3.parentItem],
1011
+ onClick: navigateUp,
1012
+ children: [
1013
+ /* @__PURE__ */ jsx3("div", { css: styles3.content, children: /* @__PURE__ */ jsx3("svg", { css: styles3.parentIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" }) }) }),
1014
+ /* @__PURE__ */ jsxs3("div", { css: styles3.label, children: [
1015
+ /* @__PURE__ */ jsx3("p", { css: styles3.name, children: ".." }),
1016
+ /* @__PURE__ */ jsx3("p", { css: styles3.size, children: "Parent folder" })
1017
+ ] })
1018
+ ]
1019
+ }
1020
+ ),
1021
+ sortedItems.map((item) => /* @__PURE__ */ jsx3(
1022
+ GridItem,
1023
+ {
1024
+ item,
1025
+ isSelected: selectedItems.has(item.path),
1026
+ onClick: (e) => handleItemClick(item, e),
1027
+ onOpen: () => handleOpen(item)
1028
+ },
1029
+ item.path
1030
+ ))
1031
+ ] })
966
1032
  ] });
967
1033
  }
968
1034
  function GridItem({ item, isSelected, onClick, onOpen }) {
969
1035
  const isFolder = item.type === "folder";
970
- return /* @__PURE__ */ jsxs4(
1036
+ return /* @__PURE__ */ jsxs3(
971
1037
  "div",
972
1038
  {
973
- css: [styles4.item, isSelected && styles4.itemSelected],
1039
+ css: [styles3.item, isSelected && styles3.itemSelected],
974
1040
  onClick,
975
1041
  children: [
976
- /* @__PURE__ */ jsx4(
1042
+ /* @__PURE__ */ jsx3(
977
1043
  "div",
978
1044
  {
979
- css: styles4.checkboxWrapper,
1045
+ css: styles3.checkboxWrapper,
980
1046
  onClick: (e) => e.stopPropagation(),
981
- children: /* @__PURE__ */ jsx4(
1047
+ children: /* @__PURE__ */ jsx3(
982
1048
  "input",
983
1049
  {
984
1050
  type: "checkbox",
985
- css: styles4.checkbox,
1051
+ css: styles3.checkbox,
986
1052
  checked: isSelected,
987
1053
  onChange: () => onClick({})
988
1054
  }
989
1055
  )
990
1056
  }
991
1057
  ),
992
- item.cdnSynced && /* @__PURE__ */ jsx4("span", { css: styles4.cdnBadge, children: "CDN" }),
993
- /* @__PURE__ */ jsx4("div", { css: styles4.content, children: 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" }) }) : item.thumbnail ? /* @__PURE__ */ jsx4(
1058
+ item.cdnSynced && /* @__PURE__ */ jsx3("span", { css: styles3.cdnBadge, children: "CDN" }),
1059
+ /* @__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" }) }) : item.thumbnail ? /* @__PURE__ */ jsx3(
994
1060
  "img",
995
1061
  {
996
- css: styles4.image,
1062
+ css: styles3.image,
997
1063
  src: item.thumbnail,
998
1064
  alt: item.name,
999
1065
  loading: "lazy"
1000
1066
  }
1001
- ) : /* @__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" }) }) }),
1002
- /* @__PURE__ */ jsx4("div", { css: styles4.label, children: /* @__PURE__ */ jsxs4("div", { css: styles4.labelRow, children: [
1003
- /* @__PURE__ */ jsxs4("div", { css: styles4.labelText, children: [
1004
- /* @__PURE__ */ jsx4("p", { css: styles4.name, title: item.name, children: item.name }),
1005
- isFolder ? /* @__PURE__ */ jsxs4("p", { css: styles4.size, children: [
1067
+ ) : /* @__PURE__ */ jsx3("svg", { css: styles3.fileIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("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" }) }) }),
1068
+ /* @__PURE__ */ jsx3("div", { css: styles3.label, children: /* @__PURE__ */ jsxs3("div", { css: styles3.labelRow, children: [
1069
+ /* @__PURE__ */ jsxs3("div", { css: styles3.labelText, children: [
1070
+ /* @__PURE__ */ jsx3("p", { css: styles3.name, title: item.name, children: item.name }),
1071
+ isFolder ? /* @__PURE__ */ jsxs3("p", { css: styles3.size, children: [
1006
1072
  item.fileCount !== void 0 ? `${item.fileCount} files` : "",
1007
1073
  item.fileCount !== void 0 && item.totalSize !== void 0 ? " \xB7 " : "",
1008
1074
  item.totalSize !== void 0 ? formatFileSize(item.totalSize) : ""
1009
- ] }) : item.size !== void 0 && /* @__PURE__ */ jsx4("p", { css: styles4.size, children: formatFileSize(item.size) })
1075
+ ] }) : item.size !== void 0 && /* @__PURE__ */ jsx3("p", { css: styles3.size, children: formatFileSize(item.size) })
1010
1076
  ] }),
1011
- isFolder && /* @__PURE__ */ jsx4(
1077
+ /* @__PURE__ */ jsx3(
1012
1078
  "button",
1013
1079
  {
1014
- css: styles4.openBtn,
1080
+ css: styles3.openBtn,
1015
1081
  onClick: (e) => {
1016
1082
  e.stopPropagation();
1017
1083
  onOpen();
@@ -1032,19 +1098,19 @@ function formatFileSize(bytes) {
1032
1098
 
1033
1099
  // src/components/StudioFileList.tsx
1034
1100
  import { useEffect as useEffect2, useState as useState3 } from "react";
1035
- import { css as css5, keyframes as keyframes4 } from "@emotion/react";
1036
- import { jsx as jsx5, jsxs as jsxs5 } from "@emotion/react/jsx-runtime";
1101
+ import { css as css4, keyframes as keyframes4 } from "@emotion/react";
1102
+ import { jsx as jsx4, jsxs as jsxs4 } from "@emotion/react/jsx-runtime";
1037
1103
  var spin3 = keyframes4`
1038
1104
  to { transform: rotate(360deg); }
1039
1105
  `;
1040
- var styles5 = {
1041
- loading: css5`
1106
+ var styles4 = {
1107
+ loading: css4`
1042
1108
  display: flex;
1043
1109
  align-items: center;
1044
1110
  justify-content: center;
1045
1111
  height: 256px;
1046
1112
  `,
1047
- spinner: css5`
1113
+ spinner: css4`
1048
1114
  width: 32px;
1049
1115
  height: 32px;
1050
1116
  border-radius: 50%;
@@ -1052,7 +1118,7 @@ var styles5 = {
1052
1118
  border-top-color: ${colors.primary};
1053
1119
  animation: ${spin3} 0.8s linear infinite;
1054
1120
  `,
1055
- empty: css5`
1121
+ empty: css4`
1056
1122
  display: flex;
1057
1123
  flex-direction: column;
1058
1124
  align-items: center;
@@ -1060,17 +1126,17 @@ var styles5 = {
1060
1126
  height: 256px;
1061
1127
  color: ${colors.textSecondary};
1062
1128
  `,
1063
- tableWrapper: css5`
1129
+ tableWrapper: css4`
1064
1130
  background: ${colors.surface};
1065
1131
  border-radius: 8px;
1066
1132
  border: 1px solid ${colors.border};
1067
1133
  overflow: hidden;
1068
1134
  `,
1069
- table: css5`
1135
+ table: css4`
1070
1136
  width: 100%;
1071
1137
  border-collapse: collapse;
1072
1138
  `,
1073
- th: css5`
1139
+ th: css4`
1074
1140
  text-align: left;
1075
1141
  font-size: 11px;
1076
1142
  color: ${colors.textMuted};
@@ -1081,20 +1147,20 @@ var styles5 = {
1081
1147
  background: ${colors.background};
1082
1148
  border-bottom: 1px solid ${colors.border};
1083
1149
  `,
1084
- thCheckbox: css5`
1150
+ thCheckbox: css4`
1085
1151
  width: 48px;
1086
1152
  `,
1087
- thSize: css5`
1153
+ thSize: css4`
1088
1154
  width: 96px;
1089
1155
  `,
1090
- thDimensions: css5`
1156
+ thDimensions: css4`
1091
1157
  width: 128px;
1092
1158
  `,
1093
- thCdn: css5`
1159
+ thCdn: css4`
1094
1160
  width: 96px;
1095
1161
  `,
1096
- tbody: css5``,
1097
- row: css5`
1162
+ tbody: css4``,
1163
+ row: css4`
1098
1164
  cursor: pointer;
1099
1165
  transition: background-color 0.15s ease;
1100
1166
  user-select: none;
@@ -1107,44 +1173,57 @@ var styles5 = {
1107
1173
  border-bottom: 1px solid ${colors.borderLight};
1108
1174
  }
1109
1175
  `,
1110
- rowSelected: css5`
1176
+ rowSelected: css4`
1111
1177
  background-color: ${colors.primaryLight};
1112
1178
 
1113
1179
  &:hover {
1114
1180
  background-color: ${colors.primaryLight};
1115
1181
  }
1116
1182
  `,
1117
- td: css5`
1183
+ parentRow: css4`
1184
+ cursor: pointer;
1185
+
1186
+ &:hover {
1187
+ background-color: ${colors.surfaceHover};
1188
+ }
1189
+ `,
1190
+ td: css4`
1118
1191
  padding: 12px 16px;
1119
1192
  `,
1120
- checkboxCell: css5`
1193
+ checkboxCell: css4`
1121
1194
  padding: 12px 16px;
1122
1195
  cursor: pointer;
1123
1196
  `,
1124
- checkbox: css5`
1197
+ checkbox: css4`
1125
1198
  width: 16px;
1126
1199
  height: 16px;
1127
1200
  accent-color: ${colors.primary};
1128
1201
  cursor: pointer;
1129
1202
  `,
1130
- nameCell: css5`
1203
+ nameCell: css4`
1131
1204
  display: flex;
1132
1205
  align-items: center;
1133
1206
  gap: 12px;
1134
1207
  `,
1135
- folderIcon: css5`
1208
+ folderIcon: css4`
1136
1209
  width: 20px;
1137
1210
  height: 20px;
1138
1211
  color: #f5a623;
1139
1212
  flex-shrink: 0;
1140
1213
  `,
1141
- fileIcon: css5`
1214
+ parentIcon: css4`
1215
+ width: 20px;
1216
+ height: 20px;
1217
+ color: ${colors.textMuted};
1218
+ flex-shrink: 0;
1219
+ `,
1220
+ fileIcon: css4`
1142
1221
  width: 20px;
1143
1222
  height: 20px;
1144
1223
  color: ${colors.textMuted};
1145
1224
  flex-shrink: 0;
1146
1225
  `,
1147
- thumbnail: css5`
1226
+ thumbnail: css4`
1148
1227
  width: 36px;
1149
1228
  height: 36px;
1150
1229
  object-fit: cover;
@@ -1152,17 +1231,17 @@ var styles5 = {
1152
1231
  flex-shrink: 0;
1153
1232
  border: 1px solid ${colors.borderLight};
1154
1233
  `,
1155
- name: css5`
1234
+ name: css4`
1156
1235
  font-size: ${fontSize.base};
1157
1236
  font-weight: 500;
1158
1237
  color: ${colors.text};
1159
1238
  letter-spacing: -0.01em;
1160
1239
  `,
1161
- meta: css5`
1240
+ meta: css4`
1162
1241
  font-size: ${fontSize.sm};
1163
1242
  color: ${colors.textSecondary};
1164
1243
  `,
1165
- cdnBadge: css5`
1244
+ cdnBadge: css4`
1166
1245
  display: inline-flex;
1167
1246
  align-items: center;
1168
1247
  gap: 4px;
@@ -1170,24 +1249,28 @@ var styles5 = {
1170
1249
  font-weight: 500;
1171
1250
  color: ${colors.success};
1172
1251
  `,
1173
- cdnIcon: css5`
1252
+ cdnIcon: css4`
1174
1253
  width: 12px;
1175
1254
  height: 12px;
1176
1255
  `,
1177
- cdnEmpty: css5`
1256
+ cdnEmpty: css4`
1178
1257
  font-size: ${fontSize.sm};
1179
1258
  color: ${colors.textMuted};
1180
1259
  `,
1181
- openBtn: css5`
1260
+ openBtn: css4`
1261
+ height: 28px;
1182
1262
  font-size: ${fontSize.xs};
1183
1263
  font-weight: 500;
1184
1264
  color: ${colors.primary};
1185
1265
  background: ${colors.surface};
1186
1266
  border: 1px solid ${colors.border};
1187
- padding: 4px 12px;
1267
+ padding: 0 12px;
1188
1268
  cursor: pointer;
1189
1269
  border-radius: 4px;
1190
1270
  transition: all 0.15s ease;
1271
+ display: inline-flex;
1272
+ align-items: center;
1273
+ margin-left: auto;
1191
1274
 
1192
1275
  &:hover {
1193
1276
  background-color: ${colors.primaryLight};
@@ -1196,7 +1279,7 @@ var styles5 = {
1196
1279
  `
1197
1280
  };
1198
1281
  function StudioFileList() {
1199
- const { currentPath, setCurrentPath, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey } = useStudio();
1282
+ const { currentPath, setCurrentPath, navigateUp, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey, setFocusedItem } = useStudio();
1200
1283
  const [items, setItems] = useState3([]);
1201
1284
  const [loading, setLoading] = useState3(true);
1202
1285
  useEffect2(() => {
@@ -1216,10 +1299,11 @@ function StudioFileList() {
1216
1299
  loadItems();
1217
1300
  }, [currentPath, refreshKey]);
1218
1301
  if (loading) {
1219
- return /* @__PURE__ */ jsx5("div", { css: styles5.loading, children: /* @__PURE__ */ jsx5("div", { css: styles5.spinner }) });
1302
+ return /* @__PURE__ */ jsx4("div", { css: styles4.loading, children: /* @__PURE__ */ jsx4("div", { css: styles4.spinner }) });
1220
1303
  }
1221
- if (items.length === 0) {
1222
- return /* @__PURE__ */ jsx5("div", { css: styles5.empty, children: /* @__PURE__ */ jsx5("p", { children: "No files in this folder" }) });
1304
+ const isAtRoot = currentPath === "public";
1305
+ if (items.length === 0 && isAtRoot) {
1306
+ return /* @__PURE__ */ jsx4("div", { css: styles4.empty, children: /* @__PURE__ */ jsx4("p", { children: "No files in this folder" }) });
1223
1307
  }
1224
1308
  const sortedItems = [...items].sort((a, b) => {
1225
1309
  if (a.type === "folder" && b.type !== "folder") return -1;
@@ -1233,9 +1317,11 @@ function StudioFileList() {
1233
1317
  toggleSelection(item.path);
1234
1318
  }
1235
1319
  };
1236
- const handleOpenFolder = (item) => {
1320
+ const handleOpen = (item) => {
1237
1321
  if (item.type === "folder") {
1238
1322
  setCurrentPath(item.path);
1323
+ } else {
1324
+ setFocusedItem(item);
1239
1325
  }
1240
1326
  };
1241
1327
  const allItemsSelected = sortedItems.length > 0 && sortedItems.every((item) => selectedItems.has(item.path));
@@ -1247,13 +1333,13 @@ function StudioFileList() {
1247
1333
  selectAll(sortedItems);
1248
1334
  }
1249
1335
  };
1250
- return /* @__PURE__ */ jsxs5("table", { css: styles5.table, children: [
1251
- /* @__PURE__ */ jsx5("thead", { children: /* @__PURE__ */ jsxs5("tr", { children: [
1252
- /* @__PURE__ */ jsx5("th", { css: [styles5.th, styles5.thCheckbox], children: sortedItems.length > 0 && /* @__PURE__ */ jsx5(
1336
+ return /* @__PURE__ */ jsx4("div", { css: styles4.tableWrapper, children: /* @__PURE__ */ jsxs4("table", { css: styles4.table, children: [
1337
+ /* @__PURE__ */ jsx4("thead", { children: /* @__PURE__ */ jsxs4("tr", { children: [
1338
+ /* @__PURE__ */ jsx4("th", { css: [styles4.th, styles4.thCheckbox], children: sortedItems.length > 0 && /* @__PURE__ */ jsx4(
1253
1339
  "input",
1254
1340
  {
1255
1341
  type: "checkbox",
1256
- css: styles5.checkbox,
1342
+ css: styles4.checkbox,
1257
1343
  checked: allItemsSelected,
1258
1344
  ref: (el) => {
1259
1345
  if (el) el.indeterminate = someItemsSelected && !allItemsSelected;
@@ -1261,54 +1347,66 @@ function StudioFileList() {
1261
1347
  onChange: handleSelectAll
1262
1348
  }
1263
1349
  ) }),
1264
- /* @__PURE__ */ jsx5("th", { css: styles5.th, children: "Name" }),
1265
- /* @__PURE__ */ jsx5("th", { css: [styles5.th, styles5.thSize], children: "Size" }),
1266
- /* @__PURE__ */ jsx5("th", { css: [styles5.th, styles5.thDimensions], children: "Dimensions" }),
1267
- /* @__PURE__ */ jsx5("th", { css: [styles5.th, styles5.thCdn], children: "CDN" })
1350
+ /* @__PURE__ */ jsx4("th", { css: styles4.th, children: "Name" }),
1351
+ /* @__PURE__ */ jsx4("th", { css: [styles4.th, styles4.thSize], children: "Size" }),
1352
+ /* @__PURE__ */ jsx4("th", { css: [styles4.th, styles4.thDimensions], children: "Dimensions" }),
1353
+ /* @__PURE__ */ jsx4("th", { css: [styles4.th, styles4.thCdn], children: "CDN" })
1268
1354
  ] }) }),
1269
- /* @__PURE__ */ jsx5("tbody", { css: styles5.tbody, children: sortedItems.map((item) => /* @__PURE__ */ jsx5(
1270
- ListRow,
1271
- {
1272
- item,
1273
- isSelected: selectedItems.has(item.path),
1274
- onClick: (e) => handleItemClick(item, e),
1275
- onOpen: () => handleOpenFolder(item)
1276
- },
1277
- item.path
1278
- )) })
1279
- ] });
1355
+ /* @__PURE__ */ jsxs4("tbody", { css: styles4.tbody, children: [
1356
+ !isAtRoot && /* @__PURE__ */ jsxs4("tr", { css: styles4.parentRow, onClick: navigateUp, children: [
1357
+ /* @__PURE__ */ jsx4("td", { css: styles4.td }),
1358
+ /* @__PURE__ */ jsx4("td", { css: styles4.td, children: /* @__PURE__ */ jsxs4("div", { css: styles4.nameCell, children: [
1359
+ /* @__PURE__ */ jsx4("svg", { css: styles4.parentIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" }) }),
1360
+ /* @__PURE__ */ jsx4("span", { css: styles4.name, children: ".." })
1361
+ ] }) }),
1362
+ /* @__PURE__ */ jsx4("td", { css: [styles4.td, styles4.meta], children: "--" }),
1363
+ /* @__PURE__ */ jsx4("td", { css: [styles4.td, styles4.meta], children: "Parent folder" }),
1364
+ /* @__PURE__ */ jsx4("td", { css: styles4.td, children: "--" })
1365
+ ] }),
1366
+ sortedItems.map((item) => /* @__PURE__ */ jsx4(
1367
+ ListRow,
1368
+ {
1369
+ item,
1370
+ isSelected: selectedItems.has(item.path),
1371
+ onClick: (e) => handleItemClick(item, e),
1372
+ onOpen: () => handleOpen(item)
1373
+ },
1374
+ item.path
1375
+ ))
1376
+ ] })
1377
+ ] }) });
1280
1378
  }
1281
1379
  function ListRow({ item, isSelected, onClick, onOpen }) {
1282
1380
  const isFolder = item.type === "folder";
1283
- return /* @__PURE__ */ jsxs5(
1381
+ return /* @__PURE__ */ jsxs4(
1284
1382
  "tr",
1285
1383
  {
1286
- css: [styles5.row, isSelected && styles5.rowSelected],
1384
+ css: [styles4.row, isSelected && styles4.rowSelected],
1287
1385
  onClick,
1288
1386
  children: [
1289
- /* @__PURE__ */ jsx5(
1387
+ /* @__PURE__ */ jsx4(
1290
1388
  "td",
1291
1389
  {
1292
- css: [styles5.td, styles5.checkboxCell],
1390
+ css: [styles4.td, styles4.checkboxCell],
1293
1391
  onClick: (e) => e.stopPropagation(),
1294
- children: /* @__PURE__ */ jsx5(
1392
+ children: /* @__PURE__ */ jsx4(
1295
1393
  "input",
1296
1394
  {
1297
1395
  type: "checkbox",
1298
- css: styles5.checkbox,
1396
+ css: styles4.checkbox,
1299
1397
  checked: isSelected,
1300
1398
  onChange: () => onClick({})
1301
1399
  }
1302
1400
  )
1303
1401
  }
1304
1402
  ),
1305
- /* @__PURE__ */ jsx5("td", { css: styles5.td, children: /* @__PURE__ */ jsxs5("div", { css: styles5.nameCell, children: [
1306
- isFolder ? /* @__PURE__ */ jsx5("svg", { css: styles5.folderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("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" }) }) : item.thumbnail ? /* @__PURE__ */ jsx5("img", { css: styles5.thumbnail, src: item.thumbnail, alt: item.name, loading: "lazy" }) : /* @__PURE__ */ jsx5("svg", { css: styles5.fileIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("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" }) }),
1307
- /* @__PURE__ */ jsx5("span", { css: styles5.name, children: item.name }),
1308
- isFolder && /* @__PURE__ */ jsx5(
1403
+ /* @__PURE__ */ jsx4("td", { css: styles4.td, children: /* @__PURE__ */ jsxs4("div", { css: styles4.nameCell, children: [
1404
+ 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" }) }) : item.thumbnail ? /* @__PURE__ */ jsx4("img", { css: styles4.thumbnail, src: item.thumbnail, alt: item.name, loading: "lazy" }) : /* @__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" }) }),
1405
+ /* @__PURE__ */ jsx4("span", { css: styles4.name, children: item.name }),
1406
+ /* @__PURE__ */ jsx4(
1309
1407
  "button",
1310
1408
  {
1311
- css: styles5.openBtn,
1409
+ css: styles4.openBtn,
1312
1410
  onClick: (e) => {
1313
1411
  e.stopPropagation();
1314
1412
  onOpen();
@@ -1317,12 +1415,12 @@ function ListRow({ item, isSelected, onClick, onOpen }) {
1317
1415
  }
1318
1416
  )
1319
1417
  ] }) }),
1320
- /* @__PURE__ */ jsx5("td", { css: [styles5.td, styles5.meta], children: isFolder ? item.fileCount !== void 0 ? `${item.fileCount} files` : "--" : item.size !== void 0 ? formatFileSize2(item.size) : "--" }),
1321
- /* @__PURE__ */ jsx5("td", { css: [styles5.td, styles5.meta], children: isFolder ? item.totalSize !== void 0 ? formatFileSize2(item.totalSize) : "--" : item.dimensions ? `${item.dimensions.width}x${item.dimensions.height}` : "--" }),
1322
- /* @__PURE__ */ jsx5("td", { css: styles5.td, children: item.cdnSynced ? /* @__PURE__ */ jsxs5("span", { css: styles5.cdnBadge, children: [
1323
- /* @__PURE__ */ jsx5("svg", { css: styles5.cdnIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx5("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }),
1418
+ /* @__PURE__ */ jsx4("td", { css: [styles4.td, styles4.meta], children: isFolder ? item.fileCount !== void 0 ? `${item.fileCount} files` : "--" : item.size !== void 0 ? formatFileSize2(item.size) : "--" }),
1419
+ /* @__PURE__ */ jsx4("td", { css: [styles4.td, styles4.meta], children: isFolder ? item.totalSize !== void 0 ? formatFileSize2(item.totalSize) : "--" : item.dimensions ? `${item.dimensions.width}x${item.dimensions.height}` : "--" }),
1420
+ /* @__PURE__ */ jsx4("td", { css: styles4.td, children: item.cdnSynced ? /* @__PURE__ */ jsxs4("span", { css: styles4.cdnBadge, children: [
1421
+ /* @__PURE__ */ jsx4("svg", { css: styles4.cdnIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx4("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }),
1324
1422
  "Synced"
1325
- ] }) : /* @__PURE__ */ jsx5("span", { css: styles5.cdnEmpty, children: "--" }) })
1423
+ ] }) : /* @__PURE__ */ jsx4("span", { css: styles4.cdnEmpty, children: "--" }) })
1326
1424
  ]
1327
1425
  }
1328
1426
  );
@@ -1333,10 +1431,10 @@ function formatFileSize2(bytes) {
1333
1431
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1334
1432
  }
1335
1433
 
1336
- // src/components/StudioPreview.tsx
1434
+ // src/components/StudioDetailView.tsx
1337
1435
  import { useState as useState4 } from "react";
1338
- import { css as css6 } from "@emotion/react";
1339
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "@emotion/react/jsx-runtime";
1436
+ import { css as css5 } from "@emotion/react";
1437
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "@emotion/react/jsx-runtime";
1340
1438
  var IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".ico", ".bmp", ".tiff", ".tif"];
1341
1439
  var VIDEO_EXTENSIONS = [".mp4", ".webm", ".mov", ".avi", ".mkv", ".m4v"];
1342
1440
  function isImageFile(filename) {
@@ -1347,191 +1445,203 @@ function isVideoFile(filename) {
1347
1445
  const ext = filename.toLowerCase().substring(filename.lastIndexOf("."));
1348
1446
  return VIDEO_EXTENSIONS.includes(ext);
1349
1447
  }
1350
- var styles6 = {
1351
- panel: css6`
1352
- width: 320px;
1353
- border-left: 1px solid ${colors.border};
1354
- background-color: ${colors.surface};
1355
- padding: 20px;
1448
+ var styles5 = {
1449
+ container: css5`
1450
+ display: flex;
1451
+ flex: 1;
1452
+ overflow: hidden;
1453
+ `,
1454
+ main: css5`
1455
+ flex: 1;
1456
+ display: flex;
1457
+ flex-direction: column;
1458
+ align-items: center;
1459
+ justify-content: center;
1460
+ padding: 24px;
1461
+ background: ${colors.background};
1356
1462
  overflow: auto;
1357
1463
  `,
1358
- title: css6`
1359
- font-size: ${fontSize.sm};
1360
- font-weight: 600;
1361
- color: ${colors.textSecondary};
1362
- text-transform: uppercase;
1363
- letter-spacing: 0.05em;
1364
- margin: 0 0 16px 0;
1464
+ mediaWrapper: css5`
1465
+ max-width: 100%;
1466
+ max-height: 100%;
1467
+ display: flex;
1468
+ align-items: center;
1469
+ justify-content: center;
1365
1470
  `,
1366
- imageContainer: css6`
1367
- background-color: ${colors.background};
1471
+ image: css5`
1472
+ max-width: 100%;
1473
+ max-height: calc(100vh - 200px);
1474
+ object-fit: contain;
1368
1475
  border-radius: 8px;
1369
- border: 1px solid ${colors.border};
1370
- padding: 12px;
1371
- margin-bottom: 20px;
1476
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
1372
1477
  `,
1373
- image: css6`
1374
- width: 100%;
1375
- height: auto;
1376
- border-radius: 6px;
1478
+ video: css5`
1479
+ max-width: 100%;
1480
+ max-height: calc(100vh - 200px);
1481
+ border-radius: 8px;
1482
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
1377
1483
  `,
1378
- info: css6`
1484
+ filePlaceholder: css5`
1379
1485
  display: flex;
1380
1486
  flex-direction: column;
1381
- gap: 10px;
1382
- `,
1383
- row: css6`
1384
- display: flex;
1385
- justify-content: space-between;
1386
- font-size: ${fontSize.sm};
1487
+ align-items: center;
1488
+ justify-content: center;
1489
+ padding: 48px;
1490
+ background: ${colors.surface};
1491
+ border-radius: 12px;
1492
+ border: 1px solid ${colors.border};
1387
1493
  `,
1388
- label: css6`
1389
- color: ${colors.textSecondary};
1494
+ fileIcon: css5`
1495
+ width: 80px;
1496
+ height: 80px;
1497
+ color: ${colors.textMuted};
1498
+ margin-bottom: 16px;
1390
1499
  `,
1391
- value: css6`
1500
+ fileName: css5`
1501
+ font-size: ${fontSize.lg};
1502
+ font-weight: 600;
1392
1503
  color: ${colors.text};
1393
- font-weight: 500;
1504
+ margin: 0;
1394
1505
  `,
1395
- valueTruncate: css6`
1396
- max-width: 140px;
1397
- white-space: nowrap;
1506
+ sidebar: css5`
1507
+ width: 280px;
1508
+ background: ${colors.surface};
1509
+ border-left: 1px solid ${colors.border};
1510
+ display: flex;
1511
+ flex-direction: column;
1398
1512
  overflow: hidden;
1399
- text-overflow: ellipsis;
1400
- `,
1401
- section: css6`
1402
- padding-top: 12px;
1403
- margin-top: 4px;
1404
- border-top: 1px solid ${colors.borderLight};
1405
1513
  `,
1406
- sectionTitle: css6`
1407
- font-size: ${fontSize.xs};
1408
- font-weight: 600;
1409
- color: ${colors.textMuted};
1410
- text-transform: uppercase;
1411
- letter-spacing: 0.05em;
1412
- margin: 0 0 10px 0;
1413
- `,
1414
- cdnStatus: css6`
1514
+ sidebarHeader: css5`
1515
+ padding: 16px 20px;
1516
+ border-bottom: 1px solid ${colors.border};
1415
1517
  display: flex;
1416
1518
  align-items: center;
1417
- gap: 8px;
1418
- font-size: ${fontSize.sm};
1419
- color: ${colors.success};
1420
- font-weight: 500;
1519
+ justify-content: space-between;
1421
1520
  `,
1422
- cdnIcon: css6`
1423
- width: 16px;
1424
- height: 16px;
1521
+ sidebarTitle: css5`
1522
+ font-size: ${fontSize.base};
1523
+ font-weight: 600;
1524
+ color: ${colors.text};
1525
+ margin: 0;
1425
1526
  `,
1426
- copyBtn: css6`
1427
- margin-top: 8px;
1428
- font-size: ${fontSize.sm};
1429
- font-weight: 500;
1430
- color: ${colors.primary};
1431
- background: none;
1432
- border: none;
1527
+ closeBtn: css5`
1528
+ padding: 6px;
1529
+ background: ${colors.surface};
1530
+ border: 1px solid ${colors.border};
1531
+ border-radius: 6px;
1433
1532
  cursor: pointer;
1434
- padding: 0;
1533
+ transition: all 0.15s ease;
1534
+ display: flex;
1535
+ align-items: center;
1536
+ justify-content: center;
1435
1537
 
1436
1538
  &:hover {
1437
- text-decoration: underline;
1539
+ background-color: ${colors.surfaceHover};
1540
+ border-color: ${colors.borderHover};
1438
1541
  }
1439
1542
  `,
1440
- colorSwatch: css6`
1441
- margin-top: 8px;
1442
- height: 32px;
1443
- border-radius: 6px;
1444
- border: 1px solid ${colors.border};
1543
+ closeIcon: css5`
1544
+ width: 16px;
1545
+ height: 16px;
1546
+ color: ${colors.textSecondary};
1445
1547
  `,
1446
- emptyState: css6`
1548
+ sidebarContent: css5`
1549
+ flex: 1;
1550
+ padding: 20px;
1551
+ overflow: auto;
1552
+ `,
1553
+ info: css5`
1447
1554
  display: flex;
1448
1555
  flex-direction: column;
1449
- align-items: center;
1450
- justify-content: center;
1451
- height: 200px;
1452
- text-align: center;
1453
- `,
1454
- emptyText: css6`
1455
- font-size: ${fontSize.sm};
1456
- color: ${colors.textMuted};
1457
- margin: 0;
1556
+ gap: 12px;
1557
+ margin-bottom: 24px;
1458
1558
  `,
1459
- filePlaceholder: css6`
1559
+ infoRow: css5`
1460
1560
  display: flex;
1461
- align-items: center;
1462
- justify-content: center;
1463
- height: 120px;
1464
- background: ${colors.background};
1465
- border-radius: 6px;
1466
- `,
1467
- fileIcon: css6`
1468
- width: 56px;
1469
- height: 56px;
1470
- color: ${colors.textMuted};
1561
+ justify-content: space-between;
1562
+ font-size: ${fontSize.sm};
1471
1563
  `,
1472
- folderIcon: css6`
1473
- width: 56px;
1474
- height: 56px;
1475
- color: #f5a623;
1564
+ infoLabel: css5`
1565
+ color: ${colors.textSecondary};
1476
1566
  `,
1477
- video: css6`
1478
- width: 100%;
1479
- height: auto;
1480
- border-radius: 6px;
1567
+ infoValue: css5`
1568
+ color: ${colors.text};
1569
+ font-weight: 500;
1570
+ text-align: right;
1571
+ max-width: 160px;
1572
+ overflow: hidden;
1573
+ text-overflow: ellipsis;
1574
+ white-space: nowrap;
1481
1575
  `,
1482
- actions: css6`
1483
- margin-top: 20px;
1484
- padding-top: 20px;
1485
- border-top: 1px solid ${colors.border};
1576
+ actions: css5`
1486
1577
  display: flex;
1487
1578
  flex-direction: column;
1488
1579
  gap: 8px;
1489
1580
  `,
1490
- actionBtn: css6`
1581
+ actionBtn: css5`
1582
+ display: flex;
1583
+ align-items: center;
1584
+ gap: 10px;
1491
1585
  width: 100%;
1492
- padding: 10px 14px;
1586
+ padding: 12px 14px;
1493
1587
  font-size: ${fontSize.base};
1494
1588
  font-weight: 500;
1495
- background-color: ${colors.surface};
1589
+ background: ${colors.surface};
1496
1590
  border: 1px solid ${colors.border};
1497
1591
  border-radius: 6px;
1498
1592
  cursor: pointer;
1499
1593
  transition: all 0.15s ease;
1500
1594
  color: ${colors.text};
1595
+ text-align: left;
1501
1596
 
1502
1597
  &:hover {
1503
1598
  background-color: ${colors.surfaceHover};
1504
- border-color: #d0d5dd;
1599
+ border-color: ${colors.borderHover};
1505
1600
  }
1506
1601
  `,
1507
- actionBtnDanger: css6`
1602
+ actionBtnDanger: css5`
1508
1603
  color: ${colors.danger};
1509
1604
 
1510
1605
  &:hover {
1511
1606
  background-color: ${colors.dangerLight};
1512
1607
  border-color: ${colors.danger};
1513
1608
  }
1609
+ `,
1610
+ actionIcon: css5`
1611
+ width: 16px;
1612
+ height: 16px;
1613
+ flex-shrink: 0;
1514
1614
  `
1515
1615
  };
1516
- function StudioPreview() {
1517
- const { selectedItems, meta, triggerRefresh, clearSelection } = useStudio();
1616
+ function StudioDetailView() {
1617
+ const { focusedItem, setFocusedItem, triggerRefresh, clearSelection } = useStudio();
1518
1618
  const [showDeleteConfirm, setShowDeleteConfirm] = useState4(false);
1519
1619
  const [alertMessage, setAlertMessage] = useState4(null);
1520
- const handleDeleteClick = () => {
1521
- if (selectedItems.size === 0) return;
1522
- setShowDeleteConfirm(true);
1620
+ if (!focusedItem) return null;
1621
+ const isImage = isImageFile(focusedItem.name);
1622
+ const isVideo = isVideoFile(focusedItem.name);
1623
+ const imageSrc = focusedItem.path.replace("public", "");
1624
+ const handleClose = () => {
1625
+ setFocusedItem(null);
1523
1626
  };
1524
- const handleDeleteConfirm = async () => {
1627
+ const handleRename = () => {
1628
+ const newName = prompt("Enter new name:", focusedItem.name);
1629
+ if (newName && newName !== focusedItem.name) {
1630
+ console.log("Rename to:", newName);
1631
+ }
1632
+ };
1633
+ const handleDelete = async () => {
1525
1634
  setShowDeleteConfirm(false);
1526
1635
  try {
1527
1636
  const response = await fetch("/api/studio/delete", {
1528
1637
  method: "POST",
1529
1638
  headers: { "Content-Type": "application/json" },
1530
- body: JSON.stringify({ paths: Array.from(selectedItems) })
1639
+ body: JSON.stringify({ paths: [focusedItem.path] })
1531
1640
  });
1532
1641
  if (response.ok) {
1533
1642
  clearSelection();
1534
1643
  triggerRefresh();
1644
+ setFocusedItem(null);
1535
1645
  } else {
1536
1646
  const error = await response.json();
1537
1647
  setAlertMessage({
@@ -1547,155 +1657,97 @@ function StudioPreview() {
1547
1657
  });
1548
1658
  }
1549
1659
  };
1550
- const modals = /* @__PURE__ */ jsxs6(Fragment2, { children: [
1551
- showDeleteConfirm && /* @__PURE__ */ jsx6(
1660
+ const handleSync = () => {
1661
+ console.log("Sync to CDN:", focusedItem.path);
1662
+ };
1663
+ const handleRegenerate = () => {
1664
+ console.log("Regenerate:", focusedItem.path);
1665
+ };
1666
+ const renderMedia = () => {
1667
+ if (isImage) {
1668
+ return /* @__PURE__ */ jsx5("img", { css: styles5.image, src: imageSrc, alt: focusedItem.name });
1669
+ }
1670
+ if (isVideo) {
1671
+ return /* @__PURE__ */ jsx5("video", { css: styles5.video, src: imageSrc, controls: true });
1672
+ }
1673
+ return /* @__PURE__ */ jsxs5("div", { css: styles5.filePlaceholder, children: [
1674
+ /* @__PURE__ */ jsx5("svg", { css: styles5.fileIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("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" }) }),
1675
+ /* @__PURE__ */ jsx5("p", { css: styles5.fileName, children: focusedItem.name })
1676
+ ] });
1677
+ };
1678
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
1679
+ showDeleteConfirm && /* @__PURE__ */ jsx5(
1552
1680
  ConfirmModal,
1553
1681
  {
1554
- title: "Delete Items",
1555
- message: `Are you sure you want to delete ${selectedItems.size} item(s)? This action cannot be undone.`,
1682
+ title: "Delete File",
1683
+ message: `Are you sure you want to delete "${focusedItem.name}"? This action cannot be undone.`,
1556
1684
  confirmLabel: "Delete",
1557
1685
  variant: "danger",
1558
- onConfirm: handleDeleteConfirm,
1686
+ onConfirm: handleDelete,
1559
1687
  onCancel: () => setShowDeleteConfirm(false)
1560
1688
  }
1561
1689
  ),
1562
- alertMessage && /* @__PURE__ */ jsx6(
1690
+ alertMessage && /* @__PURE__ */ jsx5(
1563
1691
  AlertModal,
1564
1692
  {
1565
1693
  title: alertMessage.title,
1566
1694
  message: alertMessage.message,
1567
1695
  onClose: () => setAlertMessage(null)
1568
1696
  }
1569
- )
1570
- ] });
1571
- if (selectedItems.size === 0) {
1572
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1573
- modals,
1574
- /* @__PURE__ */ jsxs6("div", { css: styles6.panel, children: [
1575
- /* @__PURE__ */ jsx6("h3", { css: styles6.title, children: "Preview" }),
1576
- /* @__PURE__ */ jsx6("div", { css: styles6.emptyState, children: /* @__PURE__ */ jsx6("p", { css: styles6.emptyText, children: "Select an image to preview" }) })
1577
- ] })
1578
- ] });
1579
- }
1580
- if (selectedItems.size > 1) {
1581
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1582
- modals,
1583
- /* @__PURE__ */ jsxs6("div", { css: styles6.panel, children: [
1584
- /* @__PURE__ */ jsxs6("h3", { css: styles6.title, children: [
1585
- selectedItems.size,
1586
- " items selected"
1697
+ ),
1698
+ /* @__PURE__ */ jsxs5("div", { css: styles5.container, children: [
1699
+ /* @__PURE__ */ jsx5("div", { css: styles5.main, children: /* @__PURE__ */ jsx5("div", { css: styles5.mediaWrapper, children: renderMedia() }) }),
1700
+ /* @__PURE__ */ jsxs5("div", { css: styles5.sidebar, children: [
1701
+ /* @__PURE__ */ jsxs5("div", { css: styles5.sidebarHeader, children: [
1702
+ /* @__PURE__ */ jsx5("h3", { css: styles5.sidebarTitle, children: "Details" }),
1703
+ /* @__PURE__ */ jsx5("button", { css: styles5.closeBtn, onClick: handleClose, "aria-label": "Close", children: /* @__PURE__ */ jsx5("svg", { css: styles5.closeIcon, 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" }) }) })
1587
1704
  ] }),
1588
- /* @__PURE__ */ jsx6("div", { css: styles6.actions, children: /* @__PURE__ */ jsxs6("button", { css: [styles6.actionBtn, styles6.actionBtnDanger], onClick: handleDeleteClick, children: [
1589
- "Delete ",
1590
- selectedItems.size,
1591
- " items"
1592
- ] }) })
1593
- ] })
1594
- ] });
1595
- }
1596
- const selectedPath = Array.from(selectedItems)[0];
1597
- const isFolder = !selectedPath.includes(".") || selectedPath.endsWith("/");
1598
- const filename = selectedPath.split("/").pop() || "";
1599
- const isImage = isImageFile(filename);
1600
- const isVideo = isVideoFile(filename);
1601
- const imageKey = selectedPath.replace(/^public\/images\//, "").replace(/^public\/originals\//, "").replace(/^public\//, "");
1602
- const imageData = meta?.images?.[imageKey];
1603
- const renderPreview = () => {
1604
- if (isFolder) {
1605
- return /* @__PURE__ */ jsx6("div", { css: styles6.filePlaceholder, children: /* @__PURE__ */ jsx6("svg", { css: styles6.folderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("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" }) }) });
1606
- }
1607
- if (isImage) {
1608
- return /* @__PURE__ */ jsx6(
1609
- "img",
1610
- {
1611
- css: styles6.image,
1612
- src: selectedPath.replace("public", ""),
1613
- alt: "Preview"
1614
- }
1615
- );
1616
- }
1617
- if (isVideo) {
1618
- return /* @__PURE__ */ jsx6(
1619
- "video",
1620
- {
1621
- css: styles6.video,
1622
- src: selectedPath.replace("public", ""),
1623
- controls: true,
1624
- muted: true
1625
- }
1626
- );
1627
- }
1628
- return /* @__PURE__ */ jsx6("div", { css: styles6.filePlaceholder, children: /* @__PURE__ */ jsx6("svg", { css: styles6.fileIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("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" }) }) });
1629
- };
1630
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1631
- modals,
1632
- /* @__PURE__ */ jsxs6("div", { css: styles6.panel, children: [
1633
- /* @__PURE__ */ jsx6("h3", { css: styles6.title, children: "Preview" }),
1634
- /* @__PURE__ */ jsx6("div", { css: styles6.imageContainer, children: renderPreview() }),
1635
- /* @__PURE__ */ jsxs6("div", { css: styles6.info, children: [
1636
- /* @__PURE__ */ jsx6(InfoRow, { label: "Filename", value: selectedPath.split("/").pop() || "" }),
1637
- imageData && /* @__PURE__ */ jsxs6(Fragment2, { children: [
1638
- /* @__PURE__ */ jsx6(
1639
- InfoRow,
1640
- {
1641
- label: "Original",
1642
- value: `${imageData.original.width}x${imageData.original.height}`
1643
- }
1644
- ),
1645
- /* @__PURE__ */ jsx6(
1646
- InfoRow,
1647
- {
1648
- label: "File size",
1649
- value: formatFileSize3(imageData.original.fileSize)
1650
- }
1651
- ),
1652
- /* @__PURE__ */ jsxs6("div", { css: styles6.section, children: [
1653
- /* @__PURE__ */ jsx6("p", { css: styles6.sectionTitle, children: "Generated sizes" }),
1654
- Object.entries(imageData.sizes).map(([size, data]) => /* @__PURE__ */ jsx6(InfoRow, { label: size, value: `${data.width}x${data.height}` }, size))
1655
- ] }),
1656
- imageData.cdn?.synced && /* @__PURE__ */ jsxs6("div", { css: styles6.section, children: [
1657
- /* @__PURE__ */ jsx6("p", { css: styles6.sectionTitle, children: "CDN" }),
1658
- /* @__PURE__ */ jsxs6("div", { css: styles6.cdnStatus, children: [
1659
- /* @__PURE__ */ jsx6("svg", { css: styles6.cdnIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx6("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }),
1660
- "Synced to CDN"
1705
+ /* @__PURE__ */ jsxs5("div", { css: styles5.sidebarContent, children: [
1706
+ /* @__PURE__ */ jsxs5("div", { css: styles5.info, children: [
1707
+ /* @__PURE__ */ jsxs5("div", { css: styles5.infoRow, children: [
1708
+ /* @__PURE__ */ jsx5("span", { css: styles5.infoLabel, children: "Name" }),
1709
+ /* @__PURE__ */ jsx5("span", { css: styles5.infoValue, title: focusedItem.name, children: focusedItem.name })
1661
1710
  ] }),
1662
- /* @__PURE__ */ jsx6(
1663
- "button",
1664
- {
1665
- css: styles6.copyBtn,
1666
- onClick: () => {
1667
- navigator.clipboard.writeText(`${imageData.cdn?.baseUrl}${imageData.sizes.full.path}`);
1668
- },
1669
- children: "Copy CDN URL"
1670
- }
1671
- )
1711
+ focusedItem.size !== void 0 && /* @__PURE__ */ jsxs5("div", { css: styles5.infoRow, children: [
1712
+ /* @__PURE__ */ jsx5("span", { css: styles5.infoLabel, children: "Size" }),
1713
+ /* @__PURE__ */ jsx5("span", { css: styles5.infoValue, children: formatFileSize3(focusedItem.size) })
1714
+ ] }),
1715
+ focusedItem.dimensions && /* @__PURE__ */ jsxs5("div", { css: styles5.infoRow, children: [
1716
+ /* @__PURE__ */ jsx5("span", { css: styles5.infoLabel, children: "Dimensions" }),
1717
+ /* @__PURE__ */ jsxs5("span", { css: styles5.infoValue, children: [
1718
+ focusedItem.dimensions.width,
1719
+ " \xD7 ",
1720
+ focusedItem.dimensions.height
1721
+ ] })
1722
+ ] }),
1723
+ /* @__PURE__ */ jsxs5("div", { css: styles5.infoRow, children: [
1724
+ /* @__PURE__ */ jsx5("span", { css: styles5.infoLabel, children: "CDN Status" }),
1725
+ /* @__PURE__ */ jsx5("span", { css: styles5.infoValue, children: focusedItem.cdnSynced ? "Synced" : "Not synced" })
1726
+ ] })
1672
1727
  ] }),
1673
- imageData.blurhash && /* @__PURE__ */ jsxs6("div", { css: styles6.section, children: [
1674
- /* @__PURE__ */ jsx6(InfoRow, { label: "Blurhash", value: imageData.blurhash, truncate: true }),
1675
- /* @__PURE__ */ jsx6(
1676
- "div",
1677
- {
1678
- css: styles6.colorSwatch,
1679
- style: { backgroundColor: imageData.dominantColor },
1680
- title: `Dominant color: ${imageData.dominantColor}`
1681
- }
1682
- )
1728
+ /* @__PURE__ */ jsxs5("div", { css: styles5.actions, children: [
1729
+ /* @__PURE__ */ jsxs5("button", { css: styles5.actionBtn, onClick: handleRename, children: [
1730
+ /* @__PURE__ */ jsx5("svg", { css: styles5.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("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" }) }),
1731
+ "Rename"
1732
+ ] }),
1733
+ /* @__PURE__ */ jsxs5("button", { css: styles5.actionBtn, onClick: handleSync, children: [
1734
+ /* @__PURE__ */ jsx5("svg", { css: styles5.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("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" }) }),
1735
+ "Sync to CDN"
1736
+ ] }),
1737
+ /* @__PURE__ */ jsxs5("button", { css: styles5.actionBtn, onClick: handleRegenerate, children: [
1738
+ /* @__PURE__ */ jsx5("svg", { css: styles5.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" }) }),
1739
+ "Regenerate"
1740
+ ] }),
1741
+ /* @__PURE__ */ jsxs5("button", { css: [styles5.actionBtn, styles5.actionBtnDanger], onClick: () => setShowDeleteConfirm(true), children: [
1742
+ /* @__PURE__ */ jsx5("svg", { css: styles5.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) }),
1743
+ "Delete"
1744
+ ] })
1683
1745
  ] })
1684
1746
  ] })
1685
- ] }),
1686
- /* @__PURE__ */ jsxs6("div", { css: styles6.actions, children: [
1687
- /* @__PURE__ */ jsx6("button", { css: styles6.actionBtn, children: "Rename" }),
1688
- /* @__PURE__ */ jsx6("button", { css: [styles6.actionBtn, styles6.actionBtnDanger], onClick: handleDeleteClick, children: "Delete" })
1689
1747
  ] })
1690
1748
  ] })
1691
1749
  ] });
1692
1750
  }
1693
- function InfoRow({ label, value, truncate }) {
1694
- return /* @__PURE__ */ jsxs6("div", { css: styles6.row, children: [
1695
- /* @__PURE__ */ jsx6("span", { css: styles6.label, children: label }),
1696
- /* @__PURE__ */ jsx6("span", { css: [styles6.value, truncate && styles6.valueTruncate], title: truncate ? value : void 0, children: value })
1697
- ] });
1698
- }
1699
1751
  function formatFileSize3(bytes) {
1700
1752
  if (bytes < 1024) return `${bytes} B`;
1701
1753
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -1704,11 +1756,13 @@ function formatFileSize3(bytes) {
1704
1756
 
1705
1757
  // src/components/StudioSettings.tsx
1706
1758
  import { useState as useState5 } from "react";
1707
- import { css as css7 } from "@emotion/react";
1708
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "@emotion/react/jsx-runtime";
1709
- var styles7 = {
1710
- btn: css7`
1711
- padding: 8px;
1759
+ import { css as css6 } from "@emotion/react";
1760
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "@emotion/react/jsx-runtime";
1761
+ var btnHeight2 = "36px";
1762
+ var styles6 = {
1763
+ btn: css6`
1764
+ height: ${btnHeight2};
1765
+ padding: 0 12px;
1712
1766
  background: ${colors.surface};
1713
1767
  border: 1px solid ${colors.border};
1714
1768
  border-radius: 6px;
@@ -1723,12 +1777,12 @@ var styles7 = {
1723
1777
  border-color: ${colors.borderHover};
1724
1778
  }
1725
1779
  `,
1726
- icon: css7`
1727
- width: 18px;
1728
- height: 18px;
1780
+ icon: css6`
1781
+ width: 16px;
1782
+ height: 16px;
1729
1783
  color: ${colors.textSecondary};
1730
1784
  `,
1731
- overlay: css7`
1785
+ overlay: css6`
1732
1786
  position: fixed;
1733
1787
  top: 0;
1734
1788
  right: 0;
@@ -1741,7 +1795,7 @@ var styles7 = {
1741
1795
  background-color: rgba(26, 31, 54, 0.4);
1742
1796
  backdrop-filter: blur(4px);
1743
1797
  `,
1744
- panel: css7`
1798
+ panel: css6`
1745
1799
  ${baseReset}
1746
1800
  position: relative;
1747
1801
  background-color: ${colors.surface};
@@ -1751,20 +1805,20 @@ var styles7 = {
1751
1805
  max-width: 512px;
1752
1806
  padding: 24px;
1753
1807
  `,
1754
- header: css7`
1808
+ header: css6`
1755
1809
  display: flex;
1756
1810
  align-items: center;
1757
1811
  justify-content: space-between;
1758
1812
  margin-bottom: 24px;
1759
1813
  `,
1760
- title: css7`
1814
+ title: css6`
1761
1815
  font-size: ${fontSize.xl};
1762
1816
  font-weight: 600;
1763
1817
  color: ${colors.text};
1764
1818
  margin: 0;
1765
1819
  letter-spacing: -0.02em;
1766
1820
  `,
1767
- closeBtn: css7`
1821
+ closeBtn: css6`
1768
1822
  padding: 6px;
1769
1823
  background: ${colors.surface};
1770
1824
  border: 1px solid ${colors.border};
@@ -1780,23 +1834,23 @@ var styles7 = {
1780
1834
  border-color: ${colors.borderHover};
1781
1835
  }
1782
1836
  `,
1783
- sections: css7`
1837
+ sections: css6`
1784
1838
  display: flex;
1785
1839
  flex-direction: column;
1786
1840
  gap: 24px;
1787
1841
  `,
1788
- sectionTitle: css7`
1842
+ sectionTitle: css6`
1789
1843
  font-size: ${fontSize.base};
1790
1844
  font-weight: 600;
1791
1845
  color: ${colors.text};
1792
1846
  margin: 0 0 12px 0;
1793
1847
  `,
1794
- description: css7`
1848
+ description: css6`
1795
1849
  font-size: ${fontSize.sm};
1796
1850
  color: ${colors.textSecondary};
1797
1851
  margin: 0 0 12px 0;
1798
1852
  `,
1799
- code: css7`
1853
+ code: css6`
1800
1854
  background-color: ${colors.background};
1801
1855
  border-radius: 8px;
1802
1856
  padding: 12px;
@@ -1805,14 +1859,14 @@ var styles7 = {
1805
1859
  color: ${colors.textSecondary};
1806
1860
  border: 1px solid ${colors.border};
1807
1861
  `,
1808
- codeLine: css7`
1862
+ codeLine: css6`
1809
1863
  margin: 0 0 4px 0;
1810
1864
 
1811
1865
  &:last-child {
1812
1866
  margin: 0;
1813
1867
  }
1814
1868
  `,
1815
- input: css7`
1869
+ input: css6`
1816
1870
  width: 100%;
1817
1871
  padding: 10px 14px;
1818
1872
  border: 1px solid ${colors.border};
@@ -1832,19 +1886,19 @@ var styles7 = {
1832
1886
  color: ${colors.textMuted};
1833
1887
  }
1834
1888
  `,
1835
- grid: css7`
1889
+ grid: css6`
1836
1890
  display: grid;
1837
1891
  grid-template-columns: repeat(3, 1fr);
1838
1892
  gap: 12px;
1839
1893
  `,
1840
- label: css7`
1894
+ label: css6`
1841
1895
  font-size: ${fontSize.xs};
1842
1896
  font-weight: 500;
1843
1897
  color: ${colors.textSecondary};
1844
1898
  display: block;
1845
1899
  margin-bottom: 6px;
1846
1900
  `,
1847
- footer: css7`
1901
+ footer: css6`
1848
1902
  margin-top: 24px;
1849
1903
  padding-top: 20px;
1850
1904
  border-top: 1px solid ${colors.border};
@@ -1852,7 +1906,7 @@ var styles7 = {
1852
1906
  justify-content: flex-end;
1853
1907
  gap: 12px;
1854
1908
  `,
1855
- cancelBtn: css7`
1909
+ cancelBtn: css6`
1856
1910
  padding: 10px 18px;
1857
1911
  font-size: ${fontSize.base};
1858
1912
  font-weight: 500;
@@ -1868,7 +1922,7 @@ var styles7 = {
1868
1922
  border-color: ${colors.borderHover};
1869
1923
  }
1870
1924
  `,
1871
- saveBtn: css7`
1925
+ saveBtn: css6`
1872
1926
  padding: 10px 18px;
1873
1927
  font-size: ${fontSize.base};
1874
1928
  font-weight: 500;
@@ -1887,11 +1941,11 @@ var styles7 = {
1887
1941
  };
1888
1942
  function StudioSettings() {
1889
1943
  const [isOpen, setIsOpen] = useState5(false);
1890
- return /* @__PURE__ */ jsxs7(Fragment3, { children: [
1891
- /* @__PURE__ */ jsx7("button", { css: styles7.btn, onClick: () => setIsOpen(true), "aria-label": "Settings", children: /* @__PURE__ */ jsxs7(
1944
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
1945
+ /* @__PURE__ */ jsx6("button", { css: styles6.btn, onClick: () => setIsOpen(true), "aria-label": "Settings", children: /* @__PURE__ */ jsxs6(
1892
1946
  "svg",
1893
1947
  {
1894
- css: styles7.icon,
1948
+ css: styles6.icon,
1895
1949
  xmlns: "http://www.w3.org/2000/svg",
1896
1950
  viewBox: "0 0 24 24",
1897
1951
  fill: "none",
@@ -1900,94 +1954,96 @@ function StudioSettings() {
1900
1954
  strokeLinecap: "round",
1901
1955
  strokeLinejoin: "round",
1902
1956
  children: [
1903
- /* @__PURE__ */ jsx7("circle", { cx: "12", cy: "12", r: "3" }),
1904
- /* @__PURE__ */ jsx7("path", { d: "M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z" })
1957
+ /* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "3" }),
1958
+ /* @__PURE__ */ jsx6("path", { d: "M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z" })
1905
1959
  ]
1906
1960
  }
1907
1961
  ) }),
1908
- isOpen && /* @__PURE__ */ jsx7(SettingsPanel, { onClose: () => setIsOpen(false) })
1962
+ isOpen && /* @__PURE__ */ jsx6(SettingsPanel, { onClose: () => setIsOpen(false) })
1909
1963
  ] });
1910
1964
  }
1911
1965
  function SettingsPanel({ onClose }) {
1912
- return /* @__PURE__ */ jsx7("div", { css: styles7.overlay, onClick: onClose, children: /* @__PURE__ */ jsxs7("div", { css: styles7.panel, onClick: (e) => e.stopPropagation(), children: [
1913
- /* @__PURE__ */ jsxs7("div", { css: styles7.header, children: [
1914
- /* @__PURE__ */ jsx7("h2", { css: styles7.title, children: "Settings" }),
1915
- /* @__PURE__ */ jsx7("button", { css: styles7.closeBtn, onClick: onClose, children: /* @__PURE__ */ jsx7("svg", { css: styles7.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx7("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })
1966
+ return /* @__PURE__ */ jsx6("div", { css: styles6.overlay, onClick: onClose, children: /* @__PURE__ */ jsxs6("div", { css: styles6.panel, onClick: (e) => e.stopPropagation(), children: [
1967
+ /* @__PURE__ */ jsxs6("div", { css: styles6.header, children: [
1968
+ /* @__PURE__ */ jsx6("h2", { css: styles6.title, children: "Settings" }),
1969
+ /* @__PURE__ */ jsx6("button", { css: styles6.closeBtn, onClick: onClose, children: /* @__PURE__ */ jsx6("svg", { css: styles6.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })
1916
1970
  ] }),
1917
- /* @__PURE__ */ jsxs7("div", { css: styles7.sections, children: [
1918
- /* @__PURE__ */ jsxs7("section", { children: [
1919
- /* @__PURE__ */ jsx7("h3", { css: styles7.sectionTitle, children: "Cloudflare R2" }),
1920
- /* @__PURE__ */ jsx7("p", { css: styles7.description, children: "Configure in .env.local file:" }),
1921
- /* @__PURE__ */ jsxs7("div", { css: styles7.code, children: [
1922
- /* @__PURE__ */ jsx7("p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_ACCOUNT_ID" }),
1923
- /* @__PURE__ */ jsx7("p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_ACCESS_KEY_ID" }),
1924
- /* @__PURE__ */ jsx7("p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_SECRET_ACCESS_KEY" }),
1925
- /* @__PURE__ */ jsx7("p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_BUCKET_NAME" }),
1926
- /* @__PURE__ */ jsx7("p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_PUBLIC_URL" })
1971
+ /* @__PURE__ */ jsxs6("div", { css: styles6.sections, children: [
1972
+ /* @__PURE__ */ jsxs6("section", { children: [
1973
+ /* @__PURE__ */ jsx6("h3", { css: styles6.sectionTitle, children: "Cloudflare R2" }),
1974
+ /* @__PURE__ */ jsx6("p", { css: styles6.description, children: "Configure in .env.local file:" }),
1975
+ /* @__PURE__ */ jsxs6("div", { css: styles6.code, children: [
1976
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_ACCOUNT_ID" }),
1977
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_ACCESS_KEY_ID" }),
1978
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_SECRET_ACCESS_KEY" }),
1979
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_BUCKET_NAME" }),
1980
+ /* @__PURE__ */ jsx6("p", { css: styles6.codeLine, children: "CLOUDFLARE_R2_PUBLIC_URL" })
1927
1981
  ] })
1928
1982
  ] }),
1929
- /* @__PURE__ */ jsxs7("section", { children: [
1930
- /* @__PURE__ */ jsx7("h3", { css: styles7.sectionTitle, children: "Custom CDN URL" }),
1931
- /* @__PURE__ */ jsx7("p", { css: styles7.description, children: "Override the default R2 URL with a custom domain:" }),
1932
- /* @__PURE__ */ jsx7("input", { css: styles7.input, type: "text", placeholder: "https://cdn.yourdomain.com" })
1983
+ /* @__PURE__ */ jsxs6("section", { children: [
1984
+ /* @__PURE__ */ jsx6("h3", { css: styles6.sectionTitle, children: "Custom CDN URL" }),
1985
+ /* @__PURE__ */ jsx6("p", { css: styles6.description, children: "Override the default R2 URL with a custom domain:" }),
1986
+ /* @__PURE__ */ jsx6("input", { css: styles6.input, type: "text", placeholder: "https://cdn.yourdomain.com" })
1933
1987
  ] }),
1934
- /* @__PURE__ */ jsxs7("section", { children: [
1935
- /* @__PURE__ */ jsx7("h3", { css: styles7.sectionTitle, children: "Thumbnail Sizes" }),
1936
- /* @__PURE__ */ jsxs7("div", { css: styles7.grid, children: [
1937
- /* @__PURE__ */ jsxs7("div", { children: [
1938
- /* @__PURE__ */ jsx7("label", { css: styles7.label, children: "Small" }),
1939
- /* @__PURE__ */ jsx7("input", { css: styles7.input, type: "number", defaultValue: 300 })
1988
+ /* @__PURE__ */ jsxs6("section", { children: [
1989
+ /* @__PURE__ */ jsx6("h3", { css: styles6.sectionTitle, children: "Thumbnail Sizes" }),
1990
+ /* @__PURE__ */ jsxs6("div", { css: styles6.grid, children: [
1991
+ /* @__PURE__ */ jsxs6("div", { children: [
1992
+ /* @__PURE__ */ jsx6("label", { css: styles6.label, children: "Small" }),
1993
+ /* @__PURE__ */ jsx6("input", { css: styles6.input, type: "number", defaultValue: 300 })
1940
1994
  ] }),
1941
- /* @__PURE__ */ jsxs7("div", { children: [
1942
- /* @__PURE__ */ jsx7("label", { css: styles7.label, children: "Medium" }),
1943
- /* @__PURE__ */ jsx7("input", { css: styles7.input, type: "number", defaultValue: 700 })
1995
+ /* @__PURE__ */ jsxs6("div", { children: [
1996
+ /* @__PURE__ */ jsx6("label", { css: styles6.label, children: "Medium" }),
1997
+ /* @__PURE__ */ jsx6("input", { css: styles6.input, type: "number", defaultValue: 700 })
1944
1998
  ] }),
1945
- /* @__PURE__ */ jsxs7("div", { children: [
1946
- /* @__PURE__ */ jsx7("label", { css: styles7.label, children: "Large" }),
1947
- /* @__PURE__ */ jsx7("input", { css: styles7.input, type: "number", defaultValue: 1400 })
1999
+ /* @__PURE__ */ jsxs6("div", { children: [
2000
+ /* @__PURE__ */ jsx6("label", { css: styles6.label, children: "Large" }),
2001
+ /* @__PURE__ */ jsx6("input", { css: styles6.input, type: "number", defaultValue: 1400 })
1948
2002
  ] })
1949
2003
  ] })
1950
2004
  ] })
1951
2005
  ] }),
1952
- /* @__PURE__ */ jsxs7("div", { css: styles7.footer, children: [
1953
- /* @__PURE__ */ jsx7("button", { css: styles7.cancelBtn, onClick: onClose, children: "Cancel" }),
1954
- /* @__PURE__ */ jsx7("button", { css: styles7.saveBtn, children: "Save Changes" })
2006
+ /* @__PURE__ */ jsxs6("div", { css: styles6.footer, children: [
2007
+ /* @__PURE__ */ jsx6("button", { css: styles6.cancelBtn, onClick: onClose, children: "Cancel" }),
2008
+ /* @__PURE__ */ jsx6("button", { css: styles6.saveBtn, children: "Save Changes" })
1955
2009
  ] })
1956
2010
  ] }) });
1957
2011
  }
1958
2012
 
1959
2013
  // src/components/StudioUI.tsx
1960
- import { jsx as jsx8, jsxs as jsxs8 } from "@emotion/react/jsx-runtime";
1961
- var styles8 = {
1962
- container: css8`
2014
+ import { jsx as jsx7, jsxs as jsxs7 } from "@emotion/react/jsx-runtime";
2015
+ var btnHeight3 = "36px";
2016
+ var styles7 = {
2017
+ container: css7`
1963
2018
  ${baseReset}
1964
2019
  display: flex;
1965
2020
  flex-direction: column;
1966
2021
  height: 100%;
1967
2022
  background: ${colors.background};
1968
2023
  `,
1969
- header: css8`
2024
+ header: css7`
1970
2025
  display: flex;
1971
2026
  align-items: center;
1972
2027
  justify-content: space-between;
1973
- padding: 16px 24px;
2028
+ padding: 12px 24px;
1974
2029
  background: ${colors.surface};
1975
2030
  border-bottom: 1px solid ${colors.border};
1976
2031
  `,
1977
- title: css8`
1978
- font-size: ${fontSize.xl};
2032
+ title: css7`
2033
+ font-size: ${fontSize.lg};
1979
2034
  font-weight: 600;
1980
2035
  color: ${colors.text};
1981
2036
  margin: 0;
1982
2037
  letter-spacing: -0.02em;
1983
2038
  `,
1984
- headerActions: css8`
2039
+ headerActions: css7`
1985
2040
  display: flex;
1986
2041
  align-items: center;
1987
2042
  gap: 8px;
1988
2043
  `,
1989
- closeBtn: css8`
1990
- padding: 8px;
2044
+ headerBtn: css7`
2045
+ height: ${btnHeight3};
2046
+ padding: 0 12px;
1991
2047
  background: ${colors.surface};
1992
2048
  border: 1px solid ${colors.border};
1993
2049
  border-radius: 6px;
@@ -2002,17 +2058,17 @@ var styles8 = {
2002
2058
  border-color: ${colors.borderHover};
2003
2059
  }
2004
2060
  `,
2005
- closeIcon: css8`
2006
- width: 18px;
2007
- height: 18px;
2061
+ headerIcon: css7`
2062
+ width: 16px;
2063
+ height: 16px;
2008
2064
  color: ${colors.textSecondary};
2009
2065
  `,
2010
- content: css8`
2066
+ content: css7`
2011
2067
  flex: 1;
2012
2068
  display: flex;
2013
2069
  overflow: hidden;
2014
2070
  `,
2015
- fileBrowser: css8`
2071
+ fileBrowser: css7`
2016
2072
  flex: 1;
2017
2073
  min-width: 0;
2018
2074
  overflow: auto;
@@ -2024,6 +2080,7 @@ function StudioUI({ onClose }) {
2024
2080
  const [selectedItems, setSelectedItems] = useState6(/* @__PURE__ */ new Set());
2025
2081
  const [lastSelectedPath, setLastSelectedPath] = useState6(null);
2026
2082
  const [viewMode, setViewMode] = useState6("grid");
2083
+ const [focusedItem, setFocusedItem] = useState6(null);
2027
2084
  const [meta, setMeta] = useState6(null);
2028
2085
  const [isLoading, setIsLoading] = useState6(false);
2029
2086
  const [refreshKey, setRefreshKey] = useState6(0);
@@ -2040,6 +2097,7 @@ function StudioUI({ onClose }) {
2040
2097
  const setCurrentPath = useCallback2((path) => {
2041
2098
  setCurrentPathInternal(path);
2042
2099
  setSelectedItems(/* @__PURE__ */ new Set());
2100
+ setFocusedItem(null);
2043
2101
  }, []);
2044
2102
  const toggleSelection = useCallback2((path) => {
2045
2103
  setSelectedItems((prev) => {
@@ -2077,10 +2135,14 @@ function StudioUI({ onClose }) {
2077
2135
  const handleKeyDown = useCallback2(
2078
2136
  (e) => {
2079
2137
  if (e.key === "Escape") {
2080
- onClose();
2138
+ if (focusedItem) {
2139
+ setFocusedItem(null);
2140
+ } else {
2141
+ onClose();
2142
+ }
2081
2143
  }
2082
2144
  },
2083
- [onClose]
2145
+ [onClose, focusedItem]
2084
2146
  );
2085
2147
  useEffect3(() => {
2086
2148
  document.addEventListener("keydown", handleKeyDown);
@@ -2107,6 +2169,8 @@ function StudioUI({ onClose }) {
2107
2169
  lastSelectedPath,
2108
2170
  viewMode,
2109
2171
  setViewMode,
2172
+ focusedItem,
2173
+ setFocusedItem,
2110
2174
  meta,
2111
2175
  setMeta,
2112
2176
  isLoading,
@@ -2114,35 +2178,31 @@ function StudioUI({ onClose }) {
2114
2178
  refreshKey,
2115
2179
  triggerRefresh
2116
2180
  };
2117
- return /* @__PURE__ */ jsx8(StudioContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs8("div", { css: styles8.container, children: [
2118
- /* @__PURE__ */ jsxs8("div", { css: styles8.header, children: [
2119
- /* @__PURE__ */ jsx8("h1", { css: styles8.title, children: "Studio" }),
2120
- /* @__PURE__ */ jsxs8("div", { css: styles8.headerActions, children: [
2121
- /* @__PURE__ */ jsx8(StudioSettings, {}),
2122
- /* @__PURE__ */ jsx8(
2181
+ return /* @__PURE__ */ jsx7(StudioContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs7("div", { css: styles7.container, children: [
2182
+ /* @__PURE__ */ jsxs7("div", { css: styles7.header, children: [
2183
+ /* @__PURE__ */ jsx7("h1", { css: styles7.title, children: "Studio" }),
2184
+ /* @__PURE__ */ jsxs7("div", { css: styles7.headerActions, children: [
2185
+ /* @__PURE__ */ jsx7(StudioSettings, {}),
2186
+ /* @__PURE__ */ jsx7(
2123
2187
  "button",
2124
2188
  {
2125
- css: styles8.closeBtn,
2189
+ css: styles7.headerBtn,
2126
2190
  onClick: onClose,
2127
2191
  "aria-label": "Close Studio",
2128
- children: /* @__PURE__ */ jsx8(CloseIcon, {})
2192
+ children: /* @__PURE__ */ jsx7(CloseIcon, {})
2129
2193
  }
2130
2194
  )
2131
2195
  ] })
2132
2196
  ] }),
2133
- /* @__PURE__ */ jsx8(StudioToolbar, {}),
2134
- /* @__PURE__ */ jsx8(StudioBreadcrumb, {}),
2135
- /* @__PURE__ */ jsxs8("div", { css: styles8.content, children: [
2136
- /* @__PURE__ */ jsx8("div", { css: styles8.fileBrowser, children: viewMode === "grid" ? /* @__PURE__ */ jsx8(StudioFileGrid, {}) : /* @__PURE__ */ jsx8(StudioFileList, {}) }),
2137
- /* @__PURE__ */ jsx8(StudioPreview, {})
2138
- ] })
2197
+ /* @__PURE__ */ jsx7(StudioToolbar, {}),
2198
+ /* @__PURE__ */ jsx7("div", { css: styles7.content, children: focusedItem ? /* @__PURE__ */ jsx7(StudioDetailView, {}) : /* @__PURE__ */ jsx7("div", { css: styles7.fileBrowser, children: viewMode === "grid" ? /* @__PURE__ */ jsx7(StudioFileGrid, {}) : /* @__PURE__ */ jsx7(StudioFileList, {}) }) })
2139
2199
  ] }) });
2140
2200
  }
2141
2201
  function CloseIcon() {
2142
- return /* @__PURE__ */ jsxs8(
2202
+ return /* @__PURE__ */ jsxs7(
2143
2203
  "svg",
2144
2204
  {
2145
- css: styles8.closeIcon,
2205
+ css: styles7.headerIcon,
2146
2206
  xmlns: "http://www.w3.org/2000/svg",
2147
2207
  viewBox: "0 0 24 24",
2148
2208
  fill: "none",
@@ -2151,8 +2211,8 @@ function CloseIcon() {
2151
2211
  strokeLinecap: "round",
2152
2212
  strokeLinejoin: "round",
2153
2213
  children: [
2154
- /* @__PURE__ */ jsx8("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2155
- /* @__PURE__ */ jsx8("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2214
+ /* @__PURE__ */ jsx7("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2215
+ /* @__PURE__ */ jsx7("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2156
2216
  ]
2157
2217
  }
2158
2218
  );
@@ -2162,4 +2222,4 @@ export {
2162
2222
  StudioUI,
2163
2223
  StudioUI_default as default
2164
2224
  };
2165
- //# sourceMappingURL=StudioUI-3VFEM3VE.mjs.map
2225
+ //# sourceMappingURL=StudioUI-F2C4N66F.mjs.map