@gallop.software/studio 0.1.81 → 0.1.83

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.
@@ -7,8 +7,8 @@ import {
7
7
  } from "./chunk-HXE6XCG2.mjs";
8
8
 
9
9
  // src/components/StudioUI.tsx
10
- import { useEffect as useEffect4, useCallback as useCallback2, useState as useState7 } from "react";
11
- import { css as css8 } from "@emotion/react";
10
+ import { useEffect as useEffect3, useCallback as useCallback3, useState as useState8 } from "react";
11
+ import { css as css10 } from "@emotion/react";
12
12
 
13
13
  // src/components/StudioContext.tsx
14
14
  import { createContext, useContext } from "react";
@@ -52,6 +52,11 @@ var defaultState = {
52
52
  },
53
53
  searchQuery: "",
54
54
  setSearchQuery: () => {
55
+ },
56
+ error: null,
57
+ showError: () => {
58
+ },
59
+ clearError: () => {
55
60
  }
56
61
  };
57
62
  var StudioContext = createContext(defaultState);
@@ -61,7 +66,7 @@ function useStudio() {
61
66
 
62
67
  // src/components/StudioToolbar.tsx
63
68
  import { useCallback, useRef, useState as useState2 } from "react";
64
- import { css as css3, keyframes as keyframes3 } from "@emotion/react";
69
+ import { css as css4, keyframes as keyframes3 } from "@emotion/react";
65
70
 
66
71
  // src/components/StudioModal.tsx
67
72
  import React from "react";
@@ -669,14 +674,313 @@ function StudioFolderPicker({ selectedItems, currentPath, onMove, onCancel }) {
669
674
  ] }) });
670
675
  }
671
676
 
677
+ // src/components/R2SetupModal.tsx
678
+ import { css as css3 } from "@emotion/react";
679
+ import { jsx as jsx3, jsxs as jsxs3 } from "@emotion/react/jsx-runtime";
680
+ var styles3 = {
681
+ overlay: css3`
682
+ position: fixed;
683
+ inset: 0;
684
+ background: rgba(0, 0, 0, 0.6);
685
+ display: flex;
686
+ align-items: center;
687
+ justify-content: center;
688
+ z-index: 1100;
689
+ padding: 20px;
690
+ `,
691
+ modal: css3`
692
+ background: ${colors.surface};
693
+ border-radius: 12px;
694
+ max-width: 560px;
695
+ width: 100%;
696
+ max-height: 90vh;
697
+ overflow-y: auto;
698
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
699
+ `,
700
+ header: css3`
701
+ display: flex;
702
+ align-items: center;
703
+ gap: 12px;
704
+ padding: 20px 24px;
705
+ border-bottom: 1px solid ${colors.border};
706
+ `,
707
+ icon: css3`
708
+ width: 32px;
709
+ height: 32px;
710
+ color: ${colors.primary};
711
+ flex-shrink: 0;
712
+ `,
713
+ title: css3`
714
+ font-size: ${fontSize.xl};
715
+ font-weight: 600;
716
+ color: ${colors.text};
717
+ margin: 0;
718
+ `,
719
+ closeBtn: css3`
720
+ margin-left: auto;
721
+ background: none;
722
+ border: none;
723
+ padding: 4px;
724
+ cursor: pointer;
725
+ color: ${colors.textMuted};
726
+ border-radius: 4px;
727
+
728
+ &:hover {
729
+ color: ${colors.text};
730
+ background: ${colors.surfaceHover};
731
+ }
732
+ `,
733
+ closeIcon: css3`
734
+ width: 20px;
735
+ height: 20px;
736
+ `,
737
+ content: css3`
738
+ padding: 24px;
739
+ `,
740
+ intro: css3`
741
+ font-size: ${fontSize.base};
742
+ color: ${colors.textSecondary};
743
+ margin: 0 0 20px 0;
744
+ line-height: 1.6;
745
+ `,
746
+ steps: css3`
747
+ list-style: none;
748
+ padding: 0;
749
+ margin: 0;
750
+ display: flex;
751
+ flex-direction: column;
752
+ gap: 16px;
753
+ `,
754
+ step: css3`
755
+ display: flex;
756
+ gap: 12px;
757
+ `,
758
+ stepNumber: css3`
759
+ width: 28px;
760
+ height: 28px;
761
+ border-radius: 50%;
762
+ background: ${colors.primaryLight};
763
+ color: ${colors.primary};
764
+ font-size: ${fontSize.sm};
765
+ font-weight: 600;
766
+ display: flex;
767
+ align-items: center;
768
+ justify-content: center;
769
+ flex-shrink: 0;
770
+ `,
771
+ stepContent: css3`
772
+ flex: 1;
773
+ padding-top: 3px;
774
+ `,
775
+ stepTitle: css3`
776
+ font-size: ${fontSize.base};
777
+ font-weight: 500;
778
+ color: ${colors.text};
779
+ margin: 0 0 4px 0;
780
+ `,
781
+ stepDesc: css3`
782
+ font-size: ${fontSize.sm};
783
+ color: ${colors.textSecondary};
784
+ margin: 0;
785
+ line-height: 1.5;
786
+ `,
787
+ link: css3`
788
+ color: ${colors.primary};
789
+ text-decoration: none;
790
+ font-weight: 500;
791
+
792
+ &:hover {
793
+ text-decoration: underline;
794
+ }
795
+ `,
796
+ envVars: css3`
797
+ background: ${colors.background};
798
+ border: 1px solid ${colors.border};
799
+ border-radius: 8px;
800
+ padding: 16px;
801
+ margin-top: 20px;
802
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
803
+ font-size: 13px;
804
+ line-height: 1.8;
805
+ color: ${colors.text};
806
+ overflow-x: auto;
807
+ `,
808
+ envVar: css3`
809
+ display: block;
810
+ `,
811
+ envKey: css3`
812
+ color: ${colors.primary};
813
+ `,
814
+ envValue: css3`
815
+ color: ${colors.textSecondary};
816
+ `,
817
+ footer: css3`
818
+ padding: 16px 24px;
819
+ border-top: 1px solid ${colors.border};
820
+ display: flex;
821
+ justify-content: flex-end;
822
+ gap: 12px;
823
+ `,
824
+ docsBtn: css3`
825
+ padding: 10px 16px;
826
+ border-radius: 6px;
827
+ font-size: ${fontSize.base};
828
+ font-weight: 500;
829
+ border: 1px solid ${colors.border};
830
+ background: ${colors.surface};
831
+ color: ${colors.text};
832
+ cursor: pointer;
833
+ text-decoration: none;
834
+ display: inline-flex;
835
+ align-items: center;
836
+ gap: 6px;
837
+ transition: all 0.15s ease;
838
+
839
+ &:hover {
840
+ background: ${colors.surfaceHover};
841
+ border-color: #d0d5dd;
842
+ }
843
+ `,
844
+ doneBtn: css3`
845
+ padding: 10px 20px;
846
+ border-radius: 6px;
847
+ font-size: ${fontSize.base};
848
+ font-weight: 500;
849
+ border: none;
850
+ background: ${colors.primary};
851
+ color: white;
852
+ cursor: pointer;
853
+ transition: background 0.15s ease;
854
+
855
+ &:hover {
856
+ background: ${colors.primaryHover};
857
+ }
858
+ `,
859
+ externalIcon: css3`
860
+ width: 14px;
861
+ height: 14px;
862
+ `
863
+ };
864
+ function R2SetupModal({ isOpen, onClose }) {
865
+ if (!isOpen) return null;
866
+ return /* @__PURE__ */ jsx3("div", { css: styles3.overlay, onClick: onClose, children: /* @__PURE__ */ jsxs3("div", { css: styles3.modal, onClick: (e) => e.stopPropagation(), children: [
867
+ /* @__PURE__ */ jsxs3("div", { css: styles3.header, children: [
868
+ /* @__PURE__ */ jsx3("svg", { css: styles3.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z" }) }),
869
+ /* @__PURE__ */ jsx3("h2", { css: styles3.title, children: "Set Up CDN Storage" }),
870
+ /* @__PURE__ */ jsx3("button", { css: styles3.closeBtn, onClick: onClose, children: /* @__PURE__ */ jsx3("svg", { css: styles3.closeIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })
871
+ ] }),
872
+ /* @__PURE__ */ jsxs3("div", { css: styles3.content, children: [
873
+ /* @__PURE__ */ jsx3("p", { css: styles3.intro, children: "Sync your images to Cloudflare R2 for faster global delivery. R2 offers generous free tier with no egress fees." }),
874
+ /* @__PURE__ */ jsxs3("ol", { css: styles3.steps, children: [
875
+ /* @__PURE__ */ jsxs3("li", { css: styles3.step, children: [
876
+ /* @__PURE__ */ jsx3("span", { css: styles3.stepNumber, children: "1" }),
877
+ /* @__PURE__ */ jsxs3("div", { css: styles3.stepContent, children: [
878
+ /* @__PURE__ */ jsx3("h4", { css: styles3.stepTitle, children: "Create a Cloudflare account" }),
879
+ /* @__PURE__ */ jsxs3("p", { css: styles3.stepDesc, children: [
880
+ "Sign up at",
881
+ " ",
882
+ /* @__PURE__ */ jsx3("a", { css: styles3.link, href: "https://dash.cloudflare.com/sign-up", target: "_blank", rel: "noopener noreferrer", children: "dash.cloudflare.com" }),
883
+ " ",
884
+ "if you don't have one already."
885
+ ] })
886
+ ] })
887
+ ] }),
888
+ /* @__PURE__ */ jsxs3("li", { css: styles3.step, children: [
889
+ /* @__PURE__ */ jsx3("span", { css: styles3.stepNumber, children: "2" }),
890
+ /* @__PURE__ */ jsxs3("div", { css: styles3.stepContent, children: [
891
+ /* @__PURE__ */ jsx3("h4", { css: styles3.stepTitle, children: "Create an R2 bucket" }),
892
+ /* @__PURE__ */ jsxs3("p", { css: styles3.stepDesc, children: [
893
+ "Go to R2 in your Cloudflare dashboard and create a new bucket. Choose a name like ",
894
+ /* @__PURE__ */ jsx3("code", { children: "my-images" }),
895
+ "."
896
+ ] })
897
+ ] })
898
+ ] }),
899
+ /* @__PURE__ */ jsxs3("li", { css: styles3.step, children: [
900
+ /* @__PURE__ */ jsx3("span", { css: styles3.stepNumber, children: "3" }),
901
+ /* @__PURE__ */ jsxs3("div", { css: styles3.stepContent, children: [
902
+ /* @__PURE__ */ jsx3("h4", { css: styles3.stepTitle, children: "Enable public access" }),
903
+ /* @__PURE__ */ jsxs3("p", { css: styles3.stepDesc, children: [
904
+ 'In bucket settings, enable "Public Access" and copy the public URL (e.g., ',
905
+ /* @__PURE__ */ jsx3("code", { children: "https://pub-xxx.r2.dev" }),
906
+ ")."
907
+ ] })
908
+ ] })
909
+ ] }),
910
+ /* @__PURE__ */ jsxs3("li", { css: styles3.step, children: [
911
+ /* @__PURE__ */ jsx3("span", { css: styles3.stepNumber, children: "4" }),
912
+ /* @__PURE__ */ jsxs3("div", { css: styles3.stepContent, children: [
913
+ /* @__PURE__ */ jsx3("h4", { css: styles3.stepTitle, children: "Create API token" }),
914
+ /* @__PURE__ */ jsx3("p", { css: styles3.stepDesc, children: 'Go to R2 \u2192 Manage R2 API Tokens \u2192 Create API Token. Select "Object Read & Write" permissions for your bucket.' })
915
+ ] })
916
+ ] }),
917
+ /* @__PURE__ */ jsxs3("li", { css: styles3.step, children: [
918
+ /* @__PURE__ */ jsx3("span", { css: styles3.stepNumber, children: "5" }),
919
+ /* @__PURE__ */ jsxs3("div", { css: styles3.stepContent, children: [
920
+ /* @__PURE__ */ jsx3("h4", { css: styles3.stepTitle, children: "Add environment variables" }),
921
+ /* @__PURE__ */ jsxs3("p", { css: styles3.stepDesc, children: [
922
+ "Add these to your ",
923
+ /* @__PURE__ */ jsx3("code", { children: ".env.local" }),
924
+ " file:"
925
+ ] })
926
+ ] })
927
+ ] })
928
+ ] }),
929
+ /* @__PURE__ */ jsxs3("div", { css: styles3.envVars, children: [
930
+ /* @__PURE__ */ jsxs3("span", { css: styles3.envVar, children: [
931
+ /* @__PURE__ */ jsx3("span", { css: styles3.envKey, children: "CLOUDFLARE_R2_ACCOUNT_ID" }),
932
+ "=",
933
+ /* @__PURE__ */ jsx3("span", { css: styles3.envValue, children: "your_account_id" })
934
+ ] }),
935
+ /* @__PURE__ */ jsxs3("span", { css: styles3.envVar, children: [
936
+ /* @__PURE__ */ jsx3("span", { css: styles3.envKey, children: "CLOUDFLARE_R2_ACCESS_KEY_ID" }),
937
+ "=",
938
+ /* @__PURE__ */ jsx3("span", { css: styles3.envValue, children: "your_access_key" })
939
+ ] }),
940
+ /* @__PURE__ */ jsxs3("span", { css: styles3.envVar, children: [
941
+ /* @__PURE__ */ jsx3("span", { css: styles3.envKey, children: "CLOUDFLARE_R2_SECRET_ACCESS_KEY" }),
942
+ "=",
943
+ /* @__PURE__ */ jsx3("span", { css: styles3.envValue, children: "your_secret_key" })
944
+ ] }),
945
+ /* @__PURE__ */ jsxs3("span", { css: styles3.envVar, children: [
946
+ /* @__PURE__ */ jsx3("span", { css: styles3.envKey, children: "CLOUDFLARE_R2_BUCKET_NAME" }),
947
+ "=",
948
+ /* @__PURE__ */ jsx3("span", { css: styles3.envValue, children: "your_bucket_name" })
949
+ ] }),
950
+ /* @__PURE__ */ jsxs3("span", { css: styles3.envVar, children: [
951
+ /* @__PURE__ */ jsx3("span", { css: styles3.envKey, children: "CLOUDFLARE_R2_PUBLIC_URL" }),
952
+ "=",
953
+ /* @__PURE__ */ jsx3("span", { css: styles3.envValue, children: "https://pub-xxx.r2.dev" })
954
+ ] })
955
+ ] })
956
+ ] }),
957
+ /* @__PURE__ */ jsxs3("div", { css: styles3.footer, children: [
958
+ /* @__PURE__ */ jsxs3(
959
+ "a",
960
+ {
961
+ css: styles3.docsBtn,
962
+ href: "https://developers.cloudflare.com/r2/get-started/",
963
+ target: "_blank",
964
+ rel: "noopener noreferrer",
965
+ children: [
966
+ "R2 Documentation",
967
+ /* @__PURE__ */ jsx3("svg", { css: styles3.externalIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" }) })
968
+ ]
969
+ }
970
+ ),
971
+ /* @__PURE__ */ jsx3("button", { css: styles3.doneBtn, onClick: onClose, children: "Got it" })
972
+ ] })
973
+ ] }) });
974
+ }
975
+
672
976
  // src/components/StudioToolbar.tsx
673
- import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "@emotion/react/jsx-runtime";
977
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "@emotion/react/jsx-runtime";
674
978
  var btnHeight = "36px";
675
979
  var spin = keyframes3`
676
980
  to { transform: rotate(360deg); }
677
981
  `;
678
- var styles3 = {
679
- toolbar: css3`
982
+ var styles4 = {
983
+ toolbar: css4`
680
984
  display: flex;
681
985
  flex-wrap: nowrap;
682
986
  align-items: center;
@@ -692,21 +996,21 @@ var styles3 = {
692
996
  padding: 12px 24px;
693
997
  }
694
998
  `,
695
- left: css3`
999
+ left: css4`
696
1000
  display: flex;
697
1001
  flex-wrap: nowrap;
698
1002
  flex-shrink: 0;
699
1003
  align-items: center;
700
1004
  gap: 8px;
701
1005
  `,
702
- right: css3`
1006
+ right: css4`
703
1007
  display: flex;
704
1008
  flex-wrap: nowrap;
705
1009
  flex-shrink: 0;
706
1010
  align-items: center;
707
1011
  gap: 8px;
708
1012
  `,
709
- btn: css3`
1013
+ btn: css4`
710
1014
  display: inline-flex;
711
1015
  align-items: center;
712
1016
  justify-content: center;
@@ -733,10 +1037,10 @@ var styles3 = {
733
1037
  opacity: 0.5;
734
1038
  }
735
1039
  `,
736
- btnIconOnly: css3`
1040
+ btnIconOnly: css4`
737
1041
  padding: 0 10px;
738
1042
  `,
739
- btnPrimary: css3`
1043
+ btnPrimary: css4`
740
1044
  background: ${colors.primary};
741
1045
  border-color: ${colors.primary};
742
1046
  color: white;
@@ -746,7 +1050,7 @@ var styles3 = {
746
1050
  border-color: ${colors.primaryHover};
747
1051
  }
748
1052
  `,
749
- btnDanger: css3`
1053
+ btnDanger: css4`
750
1054
  color: ${colors.danger};
751
1055
 
752
1056
  &:hover:not(:disabled) {
@@ -754,14 +1058,14 @@ var styles3 = {
754
1058
  border-color: ${colors.danger};
755
1059
  }
756
1060
  `,
757
- icon: css3`
1061
+ icon: css4`
758
1062
  width: 16px;
759
1063
  height: 16px;
760
1064
  `,
761
- iconSpin: css3`
1065
+ iconSpin: css4`
762
1066
  animation: ${spin} 1s linear infinite;
763
1067
  `,
764
- selectionCount: css3`
1068
+ selectionCount: css4`
765
1069
  font-size: ${fontSize.base};
766
1070
  color: ${colors.textSecondary};
767
1071
  display: flex;
@@ -769,7 +1073,7 @@ var styles3 = {
769
1073
  gap: 8px;
770
1074
  margin-right: 8px;
771
1075
  `,
772
- clearBtn: css3`
1076
+ clearBtn: css4`
773
1077
  color: ${colors.primary};
774
1078
  background: none;
775
1079
  border: none;
@@ -782,13 +1086,13 @@ var styles3 = {
782
1086
  text-decoration: underline;
783
1087
  }
784
1088
  `,
785
- divider: css3`
1089
+ divider: css4`
786
1090
  width: 1px;
787
1091
  height: 24px;
788
1092
  background: ${colors.border};
789
1093
  margin: 0 4px;
790
1094
  `,
791
- viewToggle: css3`
1095
+ viewToggle: css4`
792
1096
  display: flex;
793
1097
  align-items: center;
794
1098
  height: ${btnHeight};
@@ -797,12 +1101,12 @@ var styles3 = {
797
1101
  border-radius: 6px;
798
1102
  overflow: hidden;
799
1103
  `,
800
- searchWrapper: css3`
1104
+ searchWrapper: css4`
801
1105
  position: relative;
802
1106
  display: flex;
803
1107
  align-items: center;
804
1108
  `,
805
- searchInput: css3`
1109
+ searchInput: css4`
806
1110
  height: ${btnHeight};
807
1111
  padding: 0 32px 0 12px;
808
1112
  border: 1px solid ${colors.border};
@@ -823,7 +1127,7 @@ var styles3 = {
823
1127
  color: ${colors.textMuted};
824
1128
  }
825
1129
  `,
826
- searchClearBtn: css3`
1130
+ searchClearBtn: css4`
827
1131
  position: absolute;
828
1132
  right: 5px;
829
1133
  top: 5px;
@@ -843,7 +1147,7 @@ var styles3 = {
843
1147
  background: ${colors.primaryHover};
844
1148
  }
845
1149
  `,
846
- viewBtn: css3`
1150
+ viewBtn: css4`
847
1151
  height: 100%;
848
1152
  padding: 0 10px;
849
1153
  background: transparent;
@@ -860,7 +1164,7 @@ var styles3 = {
860
1164
  background-color: ${colors.surfaceHover};
861
1165
  }
862
1166
  `,
863
- viewBtnActive: css3`
1167
+ viewBtnActive: css4`
864
1168
  background-color: ${colors.primaryLight};
865
1169
  color: ${colors.primary};
866
1170
 
@@ -893,6 +1197,8 @@ function StudioToolbar() {
893
1197
  const [showNewFolderModal, setShowNewFolderModal] = useState2(false);
894
1198
  const [showRenameFolderModal, setShowRenameFolderModal] = useState2(false);
895
1199
  const [showMoveModal, setShowMoveModal] = useState2(false);
1200
+ const [showR2SetupModal, setShowR2SetupModal] = useState2(false);
1201
+ const [syncing, setSyncing] = useState2(false);
896
1202
  const isInImagesFolder = currentPath === "public/images" || currentPath.startsWith("public/images/");
897
1203
  const handleUpload = useCallback(() => {
898
1204
  fileInputRef.current?.click();
@@ -1189,9 +1495,60 @@ function StudioToolbar() {
1189
1495
  });
1190
1496
  }
1191
1497
  }, [selectedItems, clearSelection, triggerRefresh]);
1192
- const handleSyncCdn = useCallback(() => {
1193
- console.log("Sync CDN clicked", selectedItems);
1194
- }, [selectedItems]);
1498
+ const handleSyncCdn = useCallback(async () => {
1499
+ if (selectedItems.size === 0) return;
1500
+ const imageKeys = Array.from(selectedItems).filter((p) => !p.endsWith("/")).map((p) => "/" + p.replace(/^public\//, ""));
1501
+ if (imageKeys.length === 0) {
1502
+ setAlertMessage({
1503
+ title: "No Images Selected",
1504
+ message: "Please select image files to sync to CDN."
1505
+ });
1506
+ return;
1507
+ }
1508
+ setSyncing(true);
1509
+ try {
1510
+ const response = await fetch("/api/studio/sync", {
1511
+ method: "POST",
1512
+ headers: { "Content-Type": "application/json" },
1513
+ body: JSON.stringify({ imageKeys })
1514
+ });
1515
+ const data = await response.json();
1516
+ if (response.ok) {
1517
+ const syncedCount = data.synced?.length || 0;
1518
+ const errorCount = data.errors?.length || 0;
1519
+ if (errorCount > 0) {
1520
+ setAlertMessage({
1521
+ title: "Sync Partially Complete",
1522
+ message: `Synced ${syncedCount} images. ${errorCount} failed.`
1523
+ });
1524
+ } else {
1525
+ setAlertMessage({
1526
+ title: "Sync Complete",
1527
+ message: `Successfully synced ${syncedCount} images to CDN.`
1528
+ });
1529
+ }
1530
+ clearSelection();
1531
+ triggerRefresh();
1532
+ } else {
1533
+ if (data.error?.includes("R2 not configured") || data.error?.includes("CLOUDFLARE_R2")) {
1534
+ setShowR2SetupModal(true);
1535
+ } else {
1536
+ setAlertMessage({
1537
+ title: "Sync Failed",
1538
+ message: data.error || "Failed to sync to CDN."
1539
+ });
1540
+ }
1541
+ }
1542
+ } catch (error) {
1543
+ console.error("Sync error:", error);
1544
+ setAlertMessage({
1545
+ title: "Sync Failed",
1546
+ message: "Failed to sync to CDN. Check console for details."
1547
+ });
1548
+ } finally {
1549
+ setSyncing(false);
1550
+ }
1551
+ }, [selectedItems, clearSelection, triggerRefresh]);
1195
1552
  const handleCreateFolder = useCallback(async (folderName) => {
1196
1553
  setShowNewFolderModal(false);
1197
1554
  try {
@@ -1288,8 +1645,8 @@ function StudioToolbar() {
1288
1645
  if (focusedItem) {
1289
1646
  return null;
1290
1647
  }
1291
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
1292
- showDeleteConfirm && /* @__PURE__ */ jsx3(
1648
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1649
+ showDeleteConfirm && /* @__PURE__ */ jsx4(
1293
1650
  ConfirmModal,
1294
1651
  {
1295
1652
  title: "Delete Items",
@@ -1300,7 +1657,7 @@ function StudioToolbar() {
1300
1657
  onCancel: () => setShowDeleteConfirm(false)
1301
1658
  }
1302
1659
  ),
1303
- showProcessConfirm && /* @__PURE__ */ jsx3(
1660
+ showProcessConfirm && /* @__PURE__ */ jsx4(
1304
1661
  ConfirmModal,
1305
1662
  {
1306
1663
  title: "Process Images",
@@ -1310,7 +1667,7 @@ function StudioToolbar() {
1310
1667
  onCancel: () => setShowProcessConfirm(false)
1311
1668
  }
1312
1669
  ),
1313
- showProgress && /* @__PURE__ */ jsx3(
1670
+ showProgress && /* @__PURE__ */ jsx4(
1314
1671
  ProgressModal,
1315
1672
  {
1316
1673
  title: "Processing Images",
@@ -1327,7 +1684,7 @@ function StudioToolbar() {
1327
1684
  }
1328
1685
  }
1329
1686
  ),
1330
- showNewFolderModal && /* @__PURE__ */ jsx3(
1687
+ showNewFolderModal && /* @__PURE__ */ jsx4(
1331
1688
  InputModal,
1332
1689
  {
1333
1690
  title: "New Folder",
@@ -1338,7 +1695,7 @@ function StudioToolbar() {
1338
1695
  onCancel: () => setShowNewFolderModal(false)
1339
1696
  }
1340
1697
  ),
1341
- showMoveModal && /* @__PURE__ */ jsx3(
1698
+ showMoveModal && /* @__PURE__ */ jsx4(
1342
1699
  StudioFolderPicker,
1343
1700
  {
1344
1701
  selectedItems,
@@ -1350,7 +1707,7 @@ function StudioToolbar() {
1350
1707
  onCancel: () => setShowMoveModal(false)
1351
1708
  }
1352
1709
  ),
1353
- showRenameFolderModal && selectedFolderPath && /* @__PURE__ */ jsx3(
1710
+ showRenameFolderModal && selectedFolderPath && /* @__PURE__ */ jsx4(
1354
1711
  InputModal,
1355
1712
  {
1356
1713
  title: "Rename Folder",
@@ -1362,7 +1719,7 @@ function StudioToolbar() {
1362
1719
  onCancel: () => setShowRenameFolderModal(false)
1363
1720
  }
1364
1721
  ),
1365
- alertMessage && /* @__PURE__ */ jsx3(
1722
+ alertMessage && /* @__PURE__ */ jsx4(
1366
1723
  AlertModal,
1367
1724
  {
1368
1725
  title: alertMessage.title,
@@ -1370,8 +1727,15 @@ function StudioToolbar() {
1370
1727
  onClose: () => setAlertMessage(null)
1371
1728
  }
1372
1729
  ),
1373
- /* @__PURE__ */ jsxs3("div", { css: styles3.toolbar, children: [
1374
- /* @__PURE__ */ jsx3(
1730
+ /* @__PURE__ */ jsx4(
1731
+ R2SetupModal,
1732
+ {
1733
+ isOpen: showR2SetupModal,
1734
+ onClose: () => setShowR2SetupModal(false)
1735
+ }
1736
+ ),
1737
+ /* @__PURE__ */ jsxs4("div", { css: styles4.toolbar, children: [
1738
+ /* @__PURE__ */ jsx4(
1375
1739
  "input",
1376
1740
  {
1377
1741
  ref: fileInputRef,
@@ -1382,87 +1746,87 @@ function StudioToolbar() {
1382
1746
  style: { display: "none" }
1383
1747
  }
1384
1748
  ),
1385
- /* @__PURE__ */ jsxs3("div", { css: styles3.left, children: [
1386
- /* @__PURE__ */ jsxs3(
1749
+ /* @__PURE__ */ jsxs4("div", { css: styles4.left, children: [
1750
+ /* @__PURE__ */ jsxs4(
1387
1751
  "button",
1388
1752
  {
1389
- css: [styles3.btn, styles3.btnPrimary],
1753
+ css: [styles4.btn, styles4.btnPrimary],
1390
1754
  onClick: handleUpload,
1391
1755
  disabled: uploading || isInImagesFolder,
1392
1756
  children: [
1393
- /* @__PURE__ */ jsx3(UploadIcon, {}),
1757
+ /* @__PURE__ */ jsx4(UploadIcon, {}),
1394
1758
  uploading ? "Uploading..." : "Upload"
1395
1759
  ]
1396
1760
  }
1397
1761
  ),
1398
- /* @__PURE__ */ jsxs3(
1762
+ /* @__PURE__ */ jsxs4(
1399
1763
  "button",
1400
1764
  {
1401
- css: styles3.btn,
1765
+ css: styles4.btn,
1402
1766
  onClick: () => singleFolderSelected ? setShowRenameFolderModal(true) : setShowNewFolderModal(true),
1403
1767
  disabled: isInImagesFolder && !singleFolderSelected,
1404
1768
  title: isInImagesFolder && !singleFolderSelected ? "Cannot create folders in protected images folder" : void 0,
1405
1769
  children: [
1406
- singleFolderSelected ? /* @__PURE__ */ jsx3(RenameIcon, {}) : /* @__PURE__ */ jsx3(FolderPlusIcon, {}),
1770
+ singleFolderSelected ? /* @__PURE__ */ jsx4(RenameIcon, {}) : /* @__PURE__ */ jsx4(FolderPlusIcon, {}),
1407
1771
  singleFolderSelected ? "Rename Folder" : "New Folder"
1408
1772
  ]
1409
1773
  }
1410
1774
  ),
1411
- /* @__PURE__ */ jsx3("div", { css: styles3.divider }),
1412
- /* @__PURE__ */ jsxs3(
1775
+ /* @__PURE__ */ jsx4("div", { css: styles4.divider }),
1776
+ /* @__PURE__ */ jsxs4(
1413
1777
  "button",
1414
1778
  {
1415
- css: styles3.btn,
1779
+ css: styles4.btn,
1416
1780
  onClick: handleProcessImages,
1417
1781
  disabled: processing || isInImagesFolder,
1418
1782
  title: isInImagesFolder ? "Cannot process images folder" : void 0,
1419
1783
  children: [
1420
- /* @__PURE__ */ jsx3(ImageStackIcon, {}),
1784
+ /* @__PURE__ */ jsx4(ImageStackIcon, {}),
1421
1785
  processing ? "Processing..." : "Process Images"
1422
1786
  ]
1423
1787
  }
1424
1788
  ),
1425
- /* @__PURE__ */ jsxs3(
1789
+ /* @__PURE__ */ jsxs4(
1426
1790
  "button",
1427
1791
  {
1428
- css: [styles3.btn, styles3.btnDanger],
1792
+ css: [styles4.btn, styles4.btnDanger],
1429
1793
  onClick: handleDeleteClick,
1430
1794
  disabled: !hasSelection,
1431
1795
  children: [
1432
- /* @__PURE__ */ jsx3(TrashIcon, {}),
1796
+ /* @__PURE__ */ jsx4(TrashIcon, {}),
1433
1797
  "Delete"
1434
1798
  ]
1435
1799
  }
1436
1800
  ),
1437
- /* @__PURE__ */ jsxs3(
1801
+ /* @__PURE__ */ jsxs4(
1438
1802
  "button",
1439
1803
  {
1440
- css: styles3.btn,
1804
+ css: styles4.btn,
1441
1805
  onClick: handleMoveClick,
1442
1806
  disabled: !hasSelection,
1443
1807
  children: [
1444
- /* @__PURE__ */ jsx3(MoveIcon, {}),
1808
+ /* @__PURE__ */ jsx4(MoveIcon, {}),
1445
1809
  "Move"
1446
1810
  ]
1447
1811
  }
1448
1812
  ),
1449
- /* @__PURE__ */ jsxs3(
1813
+ /* @__PURE__ */ jsxs4(
1450
1814
  "button",
1451
1815
  {
1452
- css: styles3.btn,
1816
+ css: styles4.btn,
1453
1817
  onClick: handleSyncCdn,
1454
- disabled: !hasSelection,
1818
+ disabled: !hasSelection || syncing,
1455
1819
  children: [
1456
- /* @__PURE__ */ jsx3(CloudIcon, {}),
1457
- "Sync CDN"
1820
+ /* @__PURE__ */ jsx4(CloudIcon, {}),
1821
+ syncing ? "Syncing..." : "Sync CDN"
1458
1822
  ]
1459
1823
  }
1460
1824
  ),
1461
- /* @__PURE__ */ jsxs3("div", { css: styles3.searchWrapper, children: [
1462
- /* @__PURE__ */ jsx3(
1825
+ /* @__PURE__ */ jsxs4("div", { css: styles4.searchWrapper, children: [
1826
+ /* @__PURE__ */ jsx4(
1463
1827
  "input",
1464
1828
  {
1465
- css: styles3.searchInput,
1829
+ css: styles4.searchInput,
1466
1830
  type: "text",
1467
1831
  placeholder: "Search images...",
1468
1832
  value: searchQuery,
@@ -1470,48 +1834,48 @@ function StudioToolbar() {
1470
1834
  onKeyDown: handleSearchKeyDown
1471
1835
  }
1472
1836
  ),
1473
- searchQuery && /* @__PURE__ */ jsx3(
1837
+ searchQuery && /* @__PURE__ */ jsx4(
1474
1838
  "button",
1475
1839
  {
1476
- css: styles3.searchClearBtn,
1840
+ css: styles4.searchClearBtn,
1477
1841
  onClick: () => setSearchQuery(""),
1478
1842
  title: "Clear search",
1479
- children: /* @__PURE__ */ jsx3("svg", { width: "14", height: "14", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
1843
+ children: /* @__PURE__ */ jsx4("svg", { width: "14", height: "14", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
1480
1844
  }
1481
1845
  )
1482
1846
  ] })
1483
1847
  ] }),
1484
- /* @__PURE__ */ jsxs3("div", { css: styles3.right, children: [
1485
- hasSelection && /* @__PURE__ */ jsxs3("span", { css: styles3.selectionCount, children: [
1848
+ /* @__PURE__ */ jsxs4("div", { css: styles4.right, children: [
1849
+ hasSelection && /* @__PURE__ */ jsxs4("span", { css: styles4.selectionCount, children: [
1486
1850
  selectedItems.size,
1487
1851
  " selected",
1488
- /* @__PURE__ */ jsx3("button", { css: styles3.clearBtn, onClick: clearSelection, children: "Clear" })
1852
+ /* @__PURE__ */ jsx4("button", { css: styles4.clearBtn, onClick: clearSelection, children: "Clear" })
1489
1853
  ] }),
1490
- /* @__PURE__ */ jsx3(
1854
+ /* @__PURE__ */ jsx4(
1491
1855
  "button",
1492
1856
  {
1493
- css: [styles3.btn, styles3.btnIconOnly],
1857
+ css: [styles4.btn, styles4.btnIconOnly],
1494
1858
  onClick: handleRefresh,
1495
- children: /* @__PURE__ */ jsx3(RefreshIcon, { spinning: refreshing })
1859
+ children: /* @__PURE__ */ jsx4(RefreshIcon, { spinning: refreshing })
1496
1860
  }
1497
1861
  ),
1498
- /* @__PURE__ */ jsxs3("div", { css: styles3.viewToggle, children: [
1499
- /* @__PURE__ */ jsx3(
1862
+ /* @__PURE__ */ jsxs4("div", { css: styles4.viewToggle, children: [
1863
+ /* @__PURE__ */ jsx4(
1500
1864
  "button",
1501
1865
  {
1502
- css: [styles3.viewBtn, viewMode === "grid" && styles3.viewBtnActive],
1866
+ css: [styles4.viewBtn, viewMode === "grid" && styles4.viewBtnActive],
1503
1867
  onClick: () => setViewMode("grid"),
1504
1868
  "aria-label": "Grid view",
1505
- children: /* @__PURE__ */ jsx3(GridIcon, {})
1869
+ children: /* @__PURE__ */ jsx4(GridIcon, {})
1506
1870
  }
1507
1871
  ),
1508
- /* @__PURE__ */ jsx3(
1872
+ /* @__PURE__ */ jsx4(
1509
1873
  "button",
1510
1874
  {
1511
- css: [styles3.viewBtn, viewMode === "list" && styles3.viewBtnActive],
1875
+ css: [styles4.viewBtn, viewMode === "list" && styles4.viewBtnActive],
1512
1876
  onClick: () => setViewMode("list"),
1513
1877
  "aria-label": "List view",
1514
- children: /* @__PURE__ */ jsx3(ListIcon, {})
1878
+ children: /* @__PURE__ */ jsx4(ListIcon, {})
1515
1879
  }
1516
1880
  )
1517
1881
  ] })
@@ -1520,82 +1884,270 @@ function StudioToolbar() {
1520
1884
  ] });
1521
1885
  }
1522
1886
  function UploadIcon() {
1523
- return /* @__PURE__ */ jsx3("svg", { css: styles3.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("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" }) });
1887
+ return /* @__PURE__ */ jsx4("svg", { css: styles4.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("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" }) });
1524
1888
  }
1525
1889
  function RefreshIcon({ spinning }) {
1526
- return /* @__PURE__ */ jsx3("svg", { css: [styles3.icon, spinning && styles3.iconSpin], fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("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" }) });
1890
+ return /* @__PURE__ */ jsx4("svg", { css: [styles4.icon, spinning && styles4.iconSpin], fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("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" }) });
1527
1891
  }
1528
1892
  function TrashIcon() {
1529
- return /* @__PURE__ */ jsx3("svg", { css: styles3.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("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" }) });
1893
+ return /* @__PURE__ */ jsx4("svg", { css: styles4.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("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" }) });
1530
1894
  }
1531
1895
  function FolderPlusIcon() {
1532
- return /* @__PURE__ */ jsx3("svg", { css: styles3.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z" }) });
1896
+ return /* @__PURE__ */ jsx4("svg", { css: styles4.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z" }) });
1533
1897
  }
1534
1898
  function RenameIcon() {
1535
- return /* @__PURE__ */ jsx3("svg", { css: styles3.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("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" }) });
1899
+ return /* @__PURE__ */ jsx4("svg", { css: styles4.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("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" }) });
1536
1900
  }
1537
1901
  function MoveIcon() {
1538
- return /* @__PURE__ */ jsx3("svg", { css: styles3.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" }) });
1902
+ return /* @__PURE__ */ jsx4("svg", { css: styles4.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" }) });
1539
1903
  }
1540
1904
  function CloudIcon() {
1541
- return /* @__PURE__ */ jsx3("svg", { css: styles3.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("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" }) });
1905
+ return /* @__PURE__ */ jsx4("svg", { css: styles4.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("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" }) });
1542
1906
  }
1543
1907
  function GridIcon() {
1544
- return /* @__PURE__ */ jsx3("svg", { css: styles3.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("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" }) });
1908
+ return /* @__PURE__ */ jsx4("svg", { css: styles4.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("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" }) });
1545
1909
  }
1546
1910
  function ListIcon() {
1547
- return /* @__PURE__ */ jsx3("svg", { css: styles3.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 10h16M4 14h16M4 18h16" }) });
1911
+ return /* @__PURE__ */ jsx4("svg", { css: styles4.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 10h16M4 14h16M4 18h16" }) });
1548
1912
  }
1549
1913
  function ImageStackIcon() {
1550
- return /* @__PURE__ */ jsx3("svg", { css: styles3.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("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" }) });
1914
+ return /* @__PURE__ */ jsx4("svg", { css: styles4.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("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" }) });
1551
1915
  }
1552
1916
 
1553
1917
  // src/components/StudioFileGrid.tsx
1554
- import { useEffect as useEffect2, useState as useState3, useRef as useRef2 } from "react";
1555
- import { css as css4, keyframes as keyframes4 } from "@emotion/react";
1556
- import { jsx as jsx4, jsxs as jsxs4 } from "@emotion/react/jsx-runtime";
1557
- var spin2 = keyframes4`
1558
- to { transform: rotate(360deg); }
1559
- `;
1560
- var styles4 = {
1561
- loading: css4`
1562
- display: flex;
1563
- align-items: center;
1564
- justify-content: center;
1565
- height: 256px;
1566
- `,
1567
- spinner: css4`
1568
- width: 32px;
1569
- height: 32px;
1570
- border-radius: 50%;
1571
- border: 3px solid ${colors.border};
1572
- border-top-color: ${colors.primary};
1573
- animation: ${spin2} 0.8s linear infinite;
1574
- `,
1575
- empty: css4`
1576
- display: flex;
1577
- flex-direction: column;
1578
- align-items: center;
1579
- justify-content: center;
1580
- height: 256px;
1581
- color: ${colors.textSecondary};
1582
- `,
1583
- emptyIcon: css4`
1584
- width: 48px;
1585
- height: 48px;
1586
- margin-bottom: 16px;
1587
- opacity: 0.5;
1588
- `,
1589
- emptyText: css4`
1590
- font-size: ${fontSize.base};
1591
- margin: 0 0 4px 0;
1592
-
1593
- &:last-child {
1594
- color: ${colors.textMuted};
1595
- font-size: ${fontSize.sm};
1918
+ import { useState as useState4 } from "react";
1919
+ import { css as css5, keyframes as keyframes4 } from "@emotion/react";
1920
+
1921
+ // src/hooks/useFileList.ts
1922
+ import { useEffect as useEffect2, useState as useState3, useRef as useRef2, useCallback as useCallback2 } from "react";
1923
+
1924
+ // src/lib/api.ts
1925
+ var StudioApiClient = class {
1926
+ async get(url) {
1927
+ const response = await fetch(url);
1928
+ if (!response.ok) {
1929
+ const data = await response.json().catch(() => ({}));
1930
+ throw new Error(data.error || `Request failed: ${response.status}`);
1931
+ }
1932
+ return response.json();
1933
+ }
1934
+ async post(url, body) {
1935
+ const response = await fetch(url, {
1936
+ method: "POST",
1937
+ headers: body ? { "Content-Type": "application/json" } : void 0,
1938
+ body: body ? JSON.stringify(body) : void 0
1939
+ });
1940
+ if (!response.ok) {
1941
+ const data = await response.json().catch(() => ({}));
1942
+ throw new Error(data.error || `Request failed: ${response.status}`);
1943
+ }
1944
+ return response.json();
1945
+ }
1946
+ // List handlers
1947
+ async list(path = "public") {
1948
+ return this.get(`/api/studio/list?path=${encodeURIComponent(path)}`);
1949
+ }
1950
+ async search(query) {
1951
+ return this.get(`/api/studio/search?q=${encodeURIComponent(query)}`);
1952
+ }
1953
+ async listFolders() {
1954
+ return this.get("/api/studio/list-folders");
1955
+ }
1956
+ async countImages() {
1957
+ return this.get("/api/studio/count-images");
1958
+ }
1959
+ async folderImages(folders) {
1960
+ return this.get(`/api/studio/folder-images?folders=${encodeURIComponent(folders.join(","))}`);
1961
+ }
1962
+ // File handlers
1963
+ async upload(file, targetPath = "public") {
1964
+ const formData = new FormData();
1965
+ formData.append("file", file);
1966
+ formData.append("path", targetPath);
1967
+ const response = await fetch("/api/studio/upload", {
1968
+ method: "POST",
1969
+ body: formData
1970
+ });
1971
+ if (!response.ok) {
1972
+ const data = await response.json().catch(() => ({}));
1973
+ throw new Error(data.error || `Upload failed: ${response.status}`);
1974
+ }
1975
+ return response.json();
1976
+ }
1977
+ async delete(paths) {
1978
+ return this.post("/api/studio/delete", { paths });
1979
+ }
1980
+ async createFolder(parentPath, name) {
1981
+ return this.post("/api/studio/create-folder", { parentPath, name });
1982
+ }
1983
+ async rename(oldPath, newName) {
1984
+ return this.post("/api/studio/rename", { oldPath, newName });
1985
+ }
1986
+ async move(paths, destination) {
1987
+ return this.post("/api/studio/move", { paths, destination });
1988
+ }
1989
+ // Image handlers
1990
+ async sync(imageKeys) {
1991
+ return this.post("/api/studio/sync", { imageKeys });
1992
+ }
1993
+ async reprocess(imageKeys) {
1994
+ return this.post("/api/studio/reprocess", { imageKeys });
1995
+ }
1996
+ // Process all returns a stream, handle separately
1997
+ processAllStream() {
1998
+ return new EventSource("/api/studio/process-all");
1999
+ }
2000
+ };
2001
+ var studioApi = new StudioApiClient();
2002
+
2003
+ // src/hooks/useFileList.ts
2004
+ function useFileList() {
2005
+ const {
2006
+ currentPath,
2007
+ setCurrentPath,
2008
+ navigateUp,
2009
+ selectedItems,
2010
+ toggleSelection,
2011
+ selectRange,
2012
+ lastSelectedPath,
2013
+ selectAll,
2014
+ clearSelection,
2015
+ refreshKey,
2016
+ setFocusedItem,
2017
+ triggerRefresh,
2018
+ searchQuery,
2019
+ showError
2020
+ } = useStudio();
2021
+ const [items, setItems] = useState3([]);
2022
+ const [loading, setLoading] = useState3(true);
2023
+ const isInitialLoad = useRef2(true);
2024
+ const lastPath = useRef2(currentPath);
2025
+ useEffect2(() => {
2026
+ async function loadItems() {
2027
+ const isPathChange = lastPath.current !== currentPath;
2028
+ if (isInitialLoad.current || isPathChange) {
2029
+ setLoading(true);
2030
+ }
2031
+ lastPath.current = currentPath;
2032
+ try {
2033
+ const data = searchQuery && searchQuery.length >= 2 ? await studioApi.search(searchQuery) : await studioApi.list(currentPath);
2034
+ setItems(data.items || []);
2035
+ } catch (error) {
2036
+ const message = error instanceof Error ? error.message : "Failed to load items";
2037
+ showError("Load Error", message);
2038
+ setItems([]);
2039
+ }
2040
+ setLoading(false);
2041
+ isInitialLoad.current = false;
1596
2042
  }
2043
+ loadItems();
2044
+ }, [currentPath, refreshKey, searchQuery, showError]);
2045
+ const isAtRoot = currentPath === "public";
2046
+ const isSearching = searchQuery && searchQuery.length >= 2;
2047
+ const sortedItems = [...items].sort((a, b) => {
2048
+ if (a.type === "folder" && b.type !== "folder") return -1;
2049
+ if (a.type !== "folder" && b.type === "folder") return 1;
2050
+ return a.name.localeCompare(b.name);
2051
+ });
2052
+ const allItemsSelected = sortedItems.length > 0 && sortedItems.every((item) => selectedItems.has(item.path));
2053
+ const someItemsSelected = sortedItems.some((item) => selectedItems.has(item.path));
2054
+ const handleItemClick = useCallback2((item, e) => {
2055
+ if (e.shiftKey && lastSelectedPath) {
2056
+ selectRange(lastSelectedPath, item.path, sortedItems);
2057
+ } else {
2058
+ toggleSelection(item.path);
2059
+ }
2060
+ }, [lastSelectedPath, selectRange, sortedItems, toggleSelection]);
2061
+ const handleOpen = useCallback2((item) => {
2062
+ if (item.type === "folder") {
2063
+ setCurrentPath(item.path);
2064
+ } else {
2065
+ setFocusedItem(item);
2066
+ }
2067
+ }, [setCurrentPath, setFocusedItem]);
2068
+ const handleGenerateThumbnail = useCallback2(async (item) => {
2069
+ try {
2070
+ const imageKey = "/" + item.path.replace(/^public\//, "");
2071
+ await studioApi.reprocess([imageKey]);
2072
+ triggerRefresh();
2073
+ } catch (error) {
2074
+ const message = error instanceof Error ? error.message : "Failed to generate thumbnail";
2075
+ showError("Processing Error", message);
2076
+ }
2077
+ }, [triggerRefresh, showError]);
2078
+ const handleSelectAll = useCallback2(() => {
2079
+ if (allItemsSelected) {
2080
+ clearSelection();
2081
+ } else {
2082
+ selectAll(sortedItems);
2083
+ }
2084
+ }, [allItemsSelected, clearSelection, selectAll, sortedItems]);
2085
+ return {
2086
+ // State
2087
+ items,
2088
+ loading,
2089
+ sortedItems,
2090
+ // Computed
2091
+ isAtRoot,
2092
+ isSearching,
2093
+ allItemsSelected,
2094
+ someItemsSelected,
2095
+ // Context values
2096
+ currentPath,
2097
+ selectedItems,
2098
+ navigateUp,
2099
+ // Handlers
2100
+ handleItemClick,
2101
+ handleOpen,
2102
+ handleGenerateThumbnail,
2103
+ handleSelectAll
2104
+ };
2105
+ }
2106
+
2107
+ // src/components/StudioFileGrid.tsx
2108
+ import { jsx as jsx5, jsxs as jsxs5 } from "@emotion/react/jsx-runtime";
2109
+ var spin2 = keyframes4`
2110
+ to { transform: rotate(360deg); }
2111
+ `;
2112
+ var styles5 = {
2113
+ loading: css5`
2114
+ display: flex;
2115
+ align-items: center;
2116
+ justify-content: center;
2117
+ height: 256px;
1597
2118
  `,
1598
- grid: css4`
2119
+ spinner: css5`
2120
+ width: 32px;
2121
+ height: 32px;
2122
+ border-radius: 50%;
2123
+ border: 3px solid ${colors.border};
2124
+ border-top-color: ${colors.primary};
2125
+ animation: ${spin2} 0.8s linear infinite;
2126
+ `,
2127
+ empty: css5`
2128
+ display: flex;
2129
+ flex-direction: column;
2130
+ align-items: center;
2131
+ justify-content: center;
2132
+ height: 256px;
2133
+ color: ${colors.textSecondary};
2134
+ `,
2135
+ emptyIcon: css5`
2136
+ width: 48px;
2137
+ height: 48px;
2138
+ margin-bottom: 16px;
2139
+ opacity: 0.5;
2140
+ `,
2141
+ emptyText: css5`
2142
+ font-size: ${fontSize.base};
2143
+ margin: 0 0 4px 0;
2144
+
2145
+ &:last-child {
2146
+ color: ${colors.textMuted};
2147
+ font-size: ${fontSize.sm};
2148
+ }
2149
+ `,
2150
+ grid: css5`
1599
2151
  display: grid;
1600
2152
  grid-template-columns: 1fr;
1601
2153
  gap: 12px;
@@ -1605,7 +2157,7 @@ var styles4 = {
1605
2157
  @media (min-width: 1024px) { grid-template-columns: repeat(4, 1fr); }
1606
2158
  @media (min-width: 1280px) { grid-template-columns: repeat(5, 1fr); }
1607
2159
  `,
1608
- item: css4`
2160
+ item: css5`
1609
2161
  position: relative;
1610
2162
  border-radius: 8px;
1611
2163
  border: 1px solid ${colors.border};
@@ -1621,7 +2173,7 @@ var styles4 = {
1621
2173
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.06);
1622
2174
  }
1623
2175
  `,
1624
- itemSelected: css4`
2176
+ itemSelected: css5`
1625
2177
  border-color: ${colors.primary};
1626
2178
  box-shadow: 0 0 0 1px ${colors.primary};
1627
2179
 
@@ -1630,14 +2182,14 @@ var styles4 = {
1630
2182
  box-shadow: 0 0 0 1px ${colors.primary};
1631
2183
  }
1632
2184
  `,
1633
- parentItem: css4`
2185
+ parentItem: css5`
1634
2186
  cursor: pointer;
1635
2187
 
1636
2188
  &:hover {
1637
2189
  border-color: ${colors.primary};
1638
2190
  }
1639
2191
  `,
1640
- checkboxWrapper: css4`
2192
+ checkboxWrapper: css5`
1641
2193
  position: absolute;
1642
2194
  top: 0;
1643
2195
  left: 0;
@@ -1645,13 +2197,13 @@ var styles4 = {
1645
2197
  padding: 8px;
1646
2198
  cursor: pointer;
1647
2199
  `,
1648
- checkbox: css4`
2200
+ checkbox: css5`
1649
2201
  width: 18px;
1650
2202
  height: 18px;
1651
2203
  accent-color: ${colors.primary};
1652
2204
  cursor: pointer;
1653
2205
  `,
1654
- cdnBadge: css4`
2206
+ cdnBadge: css5`
1655
2207
  position: absolute;
1656
2208
  top: 8px;
1657
2209
  right: 8px;
@@ -1663,7 +2215,7 @@ var styles4 = {
1663
2215
  padding: 2px 8px;
1664
2216
  border-radius: 4px;
1665
2217
  `,
1666
- content: css4`
2218
+ content: css5`
1667
2219
  position: relative;
1668
2220
  aspect-ratio: 1;
1669
2221
  display: flex;
@@ -1672,20 +2224,20 @@ var styles4 = {
1672
2224
  padding: 16px;
1673
2225
  background: ${colors.background};
1674
2226
  `,
1675
- folderIcon: css4`
2227
+ folderIcon: css5`
1676
2228
  width: 56px;
1677
2229
  height: 56px;
1678
2230
  color: #f9935e;
1679
2231
  `,
1680
- imagesFolderIcon: css4`
2232
+ imagesFolderIcon: css5`
1681
2233
  width: 56px;
1682
2234
  height: 56px;
1683
2235
  color: ${colors.imagesFolder};
1684
2236
  `,
1685
- imagesFolderWrapper: css4`
2237
+ imagesFolderWrapper: css5`
1686
2238
  position: relative;
1687
2239
  `,
1688
- lockIcon: css4`
2240
+ lockIcon: css5`
1689
2241
  position: absolute;
1690
2242
  bottom: 4px;
1691
2243
  right: 4px;
@@ -1696,23 +2248,23 @@ var styles4 = {
1696
2248
  border-radius: 50%;
1697
2249
  padding: 2px;
1698
2250
  `,
1699
- parentIcon: css4`
2251
+ parentIcon: css5`
1700
2252
  width: 56px;
1701
2253
  height: 56px;
1702
2254
  color: ${colors.textMuted};
1703
2255
  `,
1704
- fileIcon: css4`
2256
+ fileIcon: css5`
1705
2257
  width: 40px;
1706
2258
  height: 40px;
1707
2259
  color: ${colors.textMuted};
1708
2260
  `,
1709
- image: css4`
2261
+ image: css5`
1710
2262
  max-width: 100%;
1711
2263
  max-height: 100%;
1712
2264
  object-fit: contain;
1713
2265
  border-radius: 4px;
1714
2266
  `,
1715
- noThumbnail: css4`
2267
+ noThumbnail: css5`
1716
2268
  display: flex;
1717
2269
  flex-direction: column;
1718
2270
  align-items: center;
@@ -1732,31 +2284,31 @@ var styles4 = {
1732
2284
  background: ${colors.surfaceHover};
1733
2285
  }
1734
2286
  `,
1735
- noThumbnailIcon: css4`
2287
+ noThumbnailIcon: css5`
1736
2288
  width: 32px;
1737
2289
  height: 32px;
1738
2290
  color: ${colors.textMuted};
1739
2291
  `,
1740
- noThumbnailText: css4`
2292
+ noThumbnailText: css5`
1741
2293
  font-size: ${fontSize.xs};
1742
2294
  color: ${colors.textMuted};
1743
2295
  text-align: center;
1744
2296
  `,
1745
- label: css4`
2297
+ label: css5`
1746
2298
  padding: 10px 12px;
1747
2299
  background-color: ${colors.surface};
1748
2300
  border-top: 1px solid ${colors.borderLight};
1749
2301
  `,
1750
- labelRow: css4`
2302
+ labelRow: css5`
1751
2303
  display: flex;
1752
2304
  flex-direction: column;
1753
2305
  gap: 2px;
1754
2306
  `,
1755
- labelText: css4`
2307
+ labelText: css5`
1756
2308
  flex: 1;
1757
2309
  min-width: 0;
1758
2310
  `,
1759
- copyBtn: css4`
2311
+ copyBtn: css5`
1760
2312
  position: absolute;
1761
2313
  top: 4px;
1762
2314
  right: 4px;
@@ -1778,11 +2330,11 @@ var styles4 = {
1778
2330
  color: ${colors.text};
1779
2331
  }
1780
2332
  `,
1781
- copyIcon: css4`
2333
+ copyIcon: css5`
1782
2334
  width: 18px;
1783
2335
  height: 18px;
1784
2336
  `,
1785
- tooltip: css4`
2337
+ tooltip: css5`
1786
2338
  position: absolute;
1787
2339
  top: 50%;
1788
2340
  right: 100%;
@@ -1807,7 +2359,7 @@ var styles4 = {
1807
2359
  border-left-color: #1a1f36;
1808
2360
  }
1809
2361
  `,
1810
- openBtn: css4`
2362
+ openBtn: css5`
1811
2363
  position: absolute;
1812
2364
  bottom: 8px;
1813
2365
  right: 8px;
@@ -1831,7 +2383,7 @@ var styles4 = {
1831
2383
  border-color: ${colors.primary};
1832
2384
  }
1833
2385
  `,
1834
- name: css4`
2386
+ name: css5`
1835
2387
  font-size: ${fontSize.sm};
1836
2388
  font-weight: 500;
1837
2389
  color: ${colors.text};
@@ -1841,12 +2393,12 @@ var styles4 = {
1841
2393
  margin: 0;
1842
2394
  letter-spacing: -0.01em;
1843
2395
  `,
1844
- size: css4`
2396
+ size: css5`
1845
2397
  font-size: ${fontSize.xs};
1846
2398
  color: ${colors.textMuted};
1847
2399
  margin: 2px 0 0 0;
1848
2400
  `,
1849
- selectAllRow: css4`
2401
+ selectAllRow: css5`
1850
2402
  display: flex;
1851
2403
  align-items: center;
1852
2404
  margin-bottom: 16px;
@@ -1855,7 +2407,7 @@ var styles4 = {
1855
2407
  border-radius: 8px;
1856
2408
  border: 1px solid ${colors.border};
1857
2409
  `,
1858
- selectAllLabel: css4`
2410
+ selectAllLabel: css5`
1859
2411
  display: flex;
1860
2412
  align-items: center;
1861
2413
  gap: 10px;
@@ -1868,100 +2420,44 @@ var styles4 = {
1868
2420
  color: ${colors.text};
1869
2421
  }
1870
2422
  `,
1871
- selectAllCheckbox: css4`
2423
+ selectAllCheckbox: css5`
1872
2424
  width: 16px;
1873
2425
  height: 16px;
1874
2426
  accent-color: ${colors.primary};
1875
2427
  `
1876
2428
  };
1877
2429
  function StudioFileGrid() {
1878
- const { currentPath, setCurrentPath, navigateUp, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey, setFocusedItem, triggerRefresh, searchQuery } = useStudio();
1879
- const [items, setItems] = useState3([]);
1880
- const [loading, setLoading] = useState3(true);
1881
- const isInitialLoad = useRef2(true);
1882
- const lastPath = useRef2(currentPath);
1883
- useEffect2(() => {
1884
- async function loadItems() {
1885
- const isPathChange = lastPath.current !== currentPath;
1886
- if (isInitialLoad.current || isPathChange) {
1887
- setLoading(true);
1888
- }
1889
- lastPath.current = currentPath;
1890
- try {
1891
- const url = searchQuery && searchQuery.length >= 2 ? `/api/studio/search?q=${encodeURIComponent(searchQuery)}` : `/api/studio/list?path=${encodeURIComponent(currentPath)}`;
1892
- const response = await fetch(url);
1893
- if (response.ok) {
1894
- const data = await response.json();
1895
- setItems(data.items || []);
1896
- }
1897
- } catch (error) {
1898
- console.error("Failed to load items:", error);
1899
- }
1900
- setLoading(false);
1901
- isInitialLoad.current = false;
1902
- }
1903
- loadItems();
1904
- }, [currentPath, refreshKey, searchQuery]);
2430
+ const {
2431
+ loading,
2432
+ sortedItems,
2433
+ isAtRoot,
2434
+ isSearching,
2435
+ allItemsSelected,
2436
+ someItemsSelected,
2437
+ selectedItems,
2438
+ navigateUp,
2439
+ handleItemClick,
2440
+ handleOpen,
2441
+ handleGenerateThumbnail,
2442
+ handleSelectAll
2443
+ } = useFileList();
1905
2444
  if (loading) {
1906
- return /* @__PURE__ */ jsx4("div", { css: styles4.loading, children: /* @__PURE__ */ jsx4("div", { css: styles4.spinner }) });
2445
+ return /* @__PURE__ */ jsx5("div", { css: styles5.loading, children: /* @__PURE__ */ jsx5("div", { css: styles5.spinner }) });
1907
2446
  }
1908
- const isAtRoot = currentPath === "public";
1909
- if (items.length === 0 && isAtRoot) {
1910
- return /* @__PURE__ */ jsxs4("div", { css: styles4.empty, children: [
1911
- /* @__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" }) }),
1912
- /* @__PURE__ */ jsx4("p", { css: styles4.emptyText, children: "No files in this folder" }),
1913
- /* @__PURE__ */ jsx4("p", { css: styles4.emptyText, children: "Upload images to get started" })
2447
+ if (sortedItems.length === 0 && isAtRoot) {
2448
+ return /* @__PURE__ */ jsxs5("div", { css: styles5.empty, children: [
2449
+ /* @__PURE__ */ jsx5("svg", { css: styles5.emptyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("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" }) }),
2450
+ /* @__PURE__ */ jsx5("p", { css: styles5.emptyText, children: "No files in this folder" }),
2451
+ /* @__PURE__ */ jsx5("p", { css: styles5.emptyText, children: "Upload images to get started" })
1914
2452
  ] });
1915
2453
  }
1916
- const isSearching = searchQuery && searchQuery.length >= 2;
1917
- const sortedItems = [...items].sort((a, b) => {
1918
- if (a.type === "folder" && b.type !== "folder") return -1;
1919
- if (a.type !== "folder" && b.type === "folder") return 1;
1920
- return a.name.localeCompare(b.name);
1921
- });
1922
- const handleItemClick = (item, e) => {
1923
- if (e.shiftKey && lastSelectedPath) {
1924
- selectRange(lastSelectedPath, item.path, sortedItems);
1925
- } else {
1926
- toggleSelection(item.path);
1927
- }
1928
- };
1929
- const handleOpen = (item) => {
1930
- if (item.type === "folder") {
1931
- setCurrentPath(item.path);
1932
- } else {
1933
- setFocusedItem(item);
1934
- }
1935
- };
1936
- const handleGenerateThumbnail = async (item) => {
1937
- try {
1938
- const imageKey = item.path.replace(/^public\//, "");
1939
- await fetch("/api/studio/reprocess", {
1940
- method: "POST",
1941
- headers: { "Content-Type": "application/json" },
1942
- body: JSON.stringify({ imageKeys: [imageKey] })
1943
- });
1944
- triggerRefresh();
1945
- } catch (error) {
1946
- console.error("Failed to generate thumbnail:", error);
1947
- }
1948
- };
1949
- const allItemsSelected = sortedItems.length > 0 && sortedItems.every((item) => selectedItems.has(item.path));
1950
- const someItemsSelected = sortedItems.some((item) => selectedItems.has(item.path));
1951
- const handleSelectAll = () => {
1952
- if (allItemsSelected) {
1953
- clearSelection();
1954
- } else {
1955
- selectAll(sortedItems);
1956
- }
1957
- };
1958
- return /* @__PURE__ */ jsxs4("div", { children: [
1959
- sortedItems.length > 0 && /* @__PURE__ */ jsx4("div", { css: styles4.selectAllRow, children: /* @__PURE__ */ jsxs4("label", { css: styles4.selectAllLabel, children: [
1960
- /* @__PURE__ */ jsx4(
2454
+ return /* @__PURE__ */ jsxs5("div", { children: [
2455
+ sortedItems.length > 0 && /* @__PURE__ */ jsx5("div", { css: styles5.selectAllRow, children: /* @__PURE__ */ jsxs5("label", { css: styles5.selectAllLabel, children: [
2456
+ /* @__PURE__ */ jsx5(
1961
2457
  "input",
1962
2458
  {
1963
2459
  type: "checkbox",
1964
- css: styles4.selectAllCheckbox,
2460
+ css: styles5.selectAllCheckbox,
1965
2461
  checked: allItemsSelected,
1966
2462
  ref: (el) => {
1967
2463
  if (el) el.indeterminate = someItemsSelected && !allItemsSelected;
@@ -1973,22 +2469,22 @@ function StudioFileGrid() {
1973
2469
  sortedItems.length,
1974
2470
  ")"
1975
2471
  ] }) }),
1976
- /* @__PURE__ */ jsxs4("div", { css: styles4.grid, children: [
1977
- !isAtRoot && !isSearching && /* @__PURE__ */ jsxs4(
2472
+ /* @__PURE__ */ jsxs5("div", { css: styles5.grid, children: [
2473
+ !isAtRoot && !isSearching && /* @__PURE__ */ jsxs5(
1978
2474
  "div",
1979
2475
  {
1980
- css: [styles4.item, styles4.parentItem],
2476
+ css: [styles5.item, styles5.parentItem],
1981
2477
  onClick: navigateUp,
1982
2478
  children: [
1983
- /* @__PURE__ */ jsx4("div", { css: styles4.content, children: /* @__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" }) }) }),
1984
- /* @__PURE__ */ jsxs4("div", { css: styles4.label, children: [
1985
- /* @__PURE__ */ jsx4("p", { css: styles4.name, children: ".." }),
1986
- /* @__PURE__ */ jsx4("p", { css: styles4.size, children: "Parent folder" })
2479
+ /* @__PURE__ */ jsx5("div", { css: styles5.content, children: /* @__PURE__ */ jsx5("svg", { css: styles5.parentIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" }) }) }),
2480
+ /* @__PURE__ */ jsxs5("div", { css: styles5.label, children: [
2481
+ /* @__PURE__ */ jsx5("p", { css: styles5.name, children: ".." }),
2482
+ /* @__PURE__ */ jsx5("p", { css: styles5.size, children: "Parent folder" })
1987
2483
  ] })
1988
2484
  ]
1989
2485
  }
1990
2486
  ),
1991
- sortedItems.map((item) => /* @__PURE__ */ jsx4(
2487
+ sortedItems.map((item) => /* @__PURE__ */ jsx5(
1992
2488
  GridItem,
1993
2489
  {
1994
2490
  item,
@@ -2003,7 +2499,7 @@ function StudioFileGrid() {
2003
2499
  ] });
2004
2500
  }
2005
2501
  function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
2006
- const [showCopied, setShowCopied] = useState3(false);
2502
+ const [showCopied, setShowCopied] = useState4(false);
2007
2503
  const isFolder = item.type === "folder";
2008
2504
  const isImage = !isFolder && item.thumbnail !== void 0;
2009
2505
  const isImagesFolder = isFolder && (item.name === "images" || item.path.includes("/images/"));
@@ -2014,46 +2510,46 @@ function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
2014
2510
  setShowCopied(true);
2015
2511
  setTimeout(() => setShowCopied(false), 1500);
2016
2512
  };
2017
- return /* @__PURE__ */ jsxs4(
2513
+ return /* @__PURE__ */ jsxs5(
2018
2514
  "div",
2019
2515
  {
2020
- css: [styles4.item, isSelected && styles4.itemSelected],
2516
+ css: [styles5.item, isSelected && styles5.itemSelected],
2021
2517
  onClick,
2022
2518
  children: [
2023
- /* @__PURE__ */ jsx4(
2519
+ /* @__PURE__ */ jsx5(
2024
2520
  "div",
2025
2521
  {
2026
- css: styles4.checkboxWrapper,
2522
+ css: styles5.checkboxWrapper,
2027
2523
  onClick: (e) => e.stopPropagation(),
2028
- children: /* @__PURE__ */ jsx4(
2524
+ children: /* @__PURE__ */ jsx5(
2029
2525
  "input",
2030
2526
  {
2031
2527
  type: "checkbox",
2032
- css: styles4.checkbox,
2528
+ css: styles5.checkbox,
2033
2529
  checked: isSelected,
2034
2530
  onChange: () => onClick({})
2035
2531
  }
2036
2532
  )
2037
2533
  }
2038
2534
  ),
2039
- item.cdnSynced && /* @__PURE__ */ jsx4("span", { css: styles4.cdnBadge, children: "CDN" }),
2040
- /* @__PURE__ */ jsxs4("div", { css: styles4.content, children: [
2041
- /* @__PURE__ */ jsxs4(
2535
+ item.cdnSynced && /* @__PURE__ */ jsx5("span", { css: styles5.cdnBadge, children: "CDN" }),
2536
+ /* @__PURE__ */ jsxs5("div", { css: styles5.content, children: [
2537
+ /* @__PURE__ */ jsxs5(
2042
2538
  "button",
2043
2539
  {
2044
- css: styles4.copyBtn,
2540
+ css: styles5.copyBtn,
2045
2541
  onClick: handleCopyPath,
2046
2542
  title: "Copy file path",
2047
2543
  children: [
2048
- showCopied && /* @__PURE__ */ jsx4("span", { css: styles4.tooltip, children: "Copied!" }),
2049
- /* @__PURE__ */ jsx4("svg", { css: styles4.copyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" }) })
2544
+ showCopied && /* @__PURE__ */ jsx5("span", { css: styles5.tooltip, children: "Copied!" }),
2545
+ /* @__PURE__ */ jsx5("svg", { css: styles5.copyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" }) })
2050
2546
  ]
2051
2547
  }
2052
2548
  ),
2053
- /* @__PURE__ */ jsx4(
2549
+ /* @__PURE__ */ jsx5(
2054
2550
  "button",
2055
2551
  {
2056
- css: styles4.openBtn,
2552
+ css: styles5.openBtn,
2057
2553
  onClick: (e) => {
2058
2554
  e.stopPropagation();
2059
2555
  onOpen();
@@ -2061,40 +2557,40 @@ function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
2061
2557
  children: "Open"
2062
2558
  }
2063
2559
  ),
2064
- isFolder ? isImagesFolder ? /* @__PURE__ */ jsxs4("div", { css: styles4.imagesFolderWrapper, children: [
2065
- /* @__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" }) }),
2066
- /* @__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" }) })
2067
- ] }) : /* @__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(
2560
+ isFolder ? isImagesFolder ? /* @__PURE__ */ jsxs5("div", { css: styles5.imagesFolderWrapper, children: [
2561
+ /* @__PURE__ */ jsx5("svg", { css: styles5.imagesFolderIcon, 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" }) }),
2562
+ /* @__PURE__ */ jsx5("svg", { css: styles5.lockIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx5("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" }) })
2563
+ ] }) : /* @__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" }) }) : isImage && item.hasThumbnail ? /* @__PURE__ */ jsx5(
2068
2564
  "img",
2069
2565
  {
2070
- css: styles4.image,
2566
+ css: styles5.image,
2071
2567
  src: item.thumbnail,
2072
2568
  alt: item.name,
2073
2569
  loading: "lazy"
2074
2570
  }
2075
- ) : isImage && !item.hasThumbnail ? /* @__PURE__ */ jsxs4(
2571
+ ) : isImage && !item.hasThumbnail ? /* @__PURE__ */ jsxs5(
2076
2572
  "button",
2077
2573
  {
2078
- css: styles4.noThumbnail,
2574
+ css: styles5.noThumbnail,
2079
2575
  onClick: (e) => {
2080
2576
  e.stopPropagation();
2081
2577
  onGenerateThumbnail();
2082
2578
  },
2083
2579
  title: "Generate thumbnail",
2084
2580
  children: [
2085
- /* @__PURE__ */ jsx4("svg", { css: styles4.noThumbnailIcon, 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" }) }),
2086
- /* @__PURE__ */ jsx4("span", { css: styles4.noThumbnailText, children: "Generate" })
2581
+ /* @__PURE__ */ jsx5("svg", { css: styles5.noThumbnailIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("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" }) }),
2582
+ /* @__PURE__ */ jsx5("span", { css: styles5.noThumbnailText, children: "Generate" })
2087
2583
  ]
2088
2584
  }
2089
- ) : /* @__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" }) })
2585
+ ) : /* @__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" }) })
2090
2586
  ] }),
2091
- /* @__PURE__ */ jsx4("div", { css: styles4.label, children: /* @__PURE__ */ jsx4("div", { css: styles4.labelRow, children: /* @__PURE__ */ jsxs4("div", { css: styles4.labelText, children: [
2092
- /* @__PURE__ */ jsx4("p", { css: styles4.name, title: item.name, children: truncateMiddle(item.name) }),
2093
- isFolder ? /* @__PURE__ */ jsxs4("p", { css: styles4.size, children: [
2587
+ /* @__PURE__ */ jsx5("div", { css: styles5.label, children: /* @__PURE__ */ jsx5("div", { css: styles5.labelRow, children: /* @__PURE__ */ jsxs5("div", { css: styles5.labelText, children: [
2588
+ /* @__PURE__ */ jsx5("p", { css: styles5.name, title: item.name, children: truncateMiddle(item.name) }),
2589
+ isFolder ? /* @__PURE__ */ jsxs5("p", { css: styles5.size, children: [
2094
2590
  item.fileCount !== void 0 ? `${item.fileCount} files` : "",
2095
2591
  item.fileCount !== void 0 && item.totalSize !== void 0 ? " \xB7 " : "",
2096
2592
  item.totalSize !== void 0 ? formatFileSize(item.totalSize) : ""
2097
- ] }) : item.size !== void 0 && /* @__PURE__ */ jsx4("p", { css: styles4.size, children: formatFileSize(item.size) })
2593
+ ] }) : item.size !== void 0 && /* @__PURE__ */ jsx5("p", { css: styles5.size, children: formatFileSize(item.size) })
2098
2594
  ] }) }) })
2099
2595
  ]
2100
2596
  }
@@ -2120,20 +2616,20 @@ function truncateMiddle(str, maxLength = 24) {
2120
2616
  }
2121
2617
 
2122
2618
  // src/components/StudioFileList.tsx
2123
- import { useEffect as useEffect3, useState as useState4, useRef as useRef3 } from "react";
2124
- import { css as css5, keyframes as keyframes5 } from "@emotion/react";
2125
- import { jsx as jsx5, jsxs as jsxs5 } from "@emotion/react/jsx-runtime";
2619
+ import { useState as useState5 } from "react";
2620
+ import { css as css6, keyframes as keyframes5 } from "@emotion/react";
2621
+ import { jsx as jsx6, jsxs as jsxs6 } from "@emotion/react/jsx-runtime";
2126
2622
  var spin3 = keyframes5`
2127
2623
  to { transform: rotate(360deg); }
2128
2624
  `;
2129
- var styles5 = {
2130
- loading: css5`
2625
+ var styles6 = {
2626
+ loading: css6`
2131
2627
  display: flex;
2132
2628
  align-items: center;
2133
2629
  justify-content: center;
2134
2630
  height: 256px;
2135
2631
  `,
2136
- spinner: css5`
2632
+ spinner: css6`
2137
2633
  width: 32px;
2138
2634
  height: 32px;
2139
2635
  border-radius: 50%;
@@ -2141,7 +2637,7 @@ var styles5 = {
2141
2637
  border-top-color: ${colors.primary};
2142
2638
  animation: ${spin3} 0.8s linear infinite;
2143
2639
  `,
2144
- empty: css5`
2640
+ empty: css6`
2145
2641
  display: flex;
2146
2642
  flex-direction: column;
2147
2643
  align-items: center;
@@ -2149,19 +2645,19 @@ var styles5 = {
2149
2645
  height: 256px;
2150
2646
  color: ${colors.textSecondary};
2151
2647
  `,
2152
- tableWrapper: css5`
2648
+ tableWrapper: css6`
2153
2649
  background: ${colors.surface};
2154
2650
  border-radius: 8px;
2155
2651
  border: 1px solid ${colors.border};
2156
2652
  overflow-x: auto;
2157
2653
  `,
2158
- table: css5`
2654
+ table: css6`
2159
2655
  width: 100%;
2160
2656
  min-width: 600px;
2161
2657
  border-collapse: collapse;
2162
2658
  white-space: nowrap;
2163
2659
  `,
2164
- th: css5`
2660
+ th: css6`
2165
2661
  text-align: left;
2166
2662
  font-size: 11px;
2167
2663
  color: ${colors.textMuted};
@@ -2172,20 +2668,20 @@ var styles5 = {
2172
2668
  background: ${colors.background};
2173
2669
  border-bottom: 1px solid ${colors.border};
2174
2670
  `,
2175
- thCheckbox: css5`
2671
+ thCheckbox: css6`
2176
2672
  width: 48px;
2177
2673
  `,
2178
- thSize: css5`
2674
+ thSize: css6`
2179
2675
  width: 96px;
2180
2676
  `,
2181
- thDimensions: css5`
2677
+ thDimensions: css6`
2182
2678
  width: 128px;
2183
2679
  `,
2184
- thCdn: css5`
2680
+ thCdn: css6`
2185
2681
  width: 96px;
2186
2682
  `,
2187
- tbody: css5``,
2188
- row: css5`
2683
+ tbody: css6``,
2684
+ row: css6`
2189
2685
  cursor: pointer;
2190
2686
  transition: background-color 0.15s ease;
2191
2687
  user-select: none;
@@ -2198,14 +2694,14 @@ var styles5 = {
2198
2694
  border-bottom: 1px solid ${colors.borderLight};
2199
2695
  }
2200
2696
  `,
2201
- rowSelected: css5`
2697
+ rowSelected: css6`
2202
2698
  background-color: ${colors.primaryLight};
2203
2699
 
2204
2700
  &:hover {
2205
2701
  background-color: ${colors.primaryLight};
2206
2702
  }
2207
2703
  `,
2208
- parentRow: css5`
2704
+ parentRow: css6`
2209
2705
  cursor: pointer;
2210
2706
  border-bottom: 1px solid ${colors.border};
2211
2707
 
@@ -2213,22 +2709,22 @@ var styles5 = {
2213
2709
  background-color: ${colors.surfaceHover};
2214
2710
  }
2215
2711
  `,
2216
- td: css5`
2712
+ td: css6`
2217
2713
  padding: 12px 16px;
2218
2714
  `,
2219
- checkboxCell: css5`
2715
+ checkboxCell: css6`
2220
2716
  padding: 12px 16px;
2221
2717
  cursor: pointer;
2222
2718
  vertical-align: middle;
2223
2719
  `,
2224
- checkbox: css5`
2720
+ checkbox: css6`
2225
2721
  width: 18px;
2226
2722
  height: 18px;
2227
2723
  accent-color: ${colors.primary};
2228
2724
  cursor: pointer;
2229
2725
  display: block;
2230
2726
  `,
2231
- actionsCell: css5`
2727
+ actionsCell: css6`
2232
2728
  display: flex;
2233
2729
  align-items: center;
2234
2730
  justify-content: flex-end;
@@ -2236,7 +2732,7 @@ var styles5 = {
2236
2732
  margin-left: auto;
2237
2733
  flex-shrink: 0;
2238
2734
  `,
2239
- copyBtn: css5`
2735
+ copyBtn: css6`
2240
2736
  position: relative;
2241
2737
  flex-shrink: 0;
2242
2738
  height: 32px;
@@ -2259,11 +2755,11 @@ var styles5 = {
2259
2755
  color: ${colors.text};
2260
2756
  }
2261
2757
  `,
2262
- copyIcon: css5`
2758
+ copyIcon: css6`
2263
2759
  width: 16px;
2264
2760
  height: 16px;
2265
2761
  `,
2266
- tooltip: css5`
2762
+ tooltip: css6`
2267
2763
  position: absolute;
2268
2764
  top: 50%;
2269
2765
  right: 100%;
@@ -2288,13 +2784,13 @@ var styles5 = {
2288
2784
  border-left-color: #1a1f36;
2289
2785
  }
2290
2786
  `,
2291
- nameCell: css5`
2787
+ nameCell: css6`
2292
2788
  display: flex;
2293
2789
  align-items: center;
2294
2790
  gap: 12px;
2295
2791
  flex: 1;
2296
2792
  `,
2297
- thumbnailWrapper: css5`
2793
+ thumbnailWrapper: css6`
2298
2794
  width: 48px;
2299
2795
  height: 36px;
2300
2796
  display: flex;
@@ -2302,7 +2798,7 @@ var styles5 = {
2302
2798
  justify-content: center;
2303
2799
  flex-shrink: 0;
2304
2800
  `,
2305
- folderIconWrapper: css5`
2801
+ folderIconWrapper: css6`
2306
2802
  width: 48px;
2307
2803
  height: 36px;
2308
2804
  display: flex;
@@ -2310,12 +2806,12 @@ var styles5 = {
2310
2806
  justify-content: center;
2311
2807
  flex-shrink: 0;
2312
2808
  `,
2313
- folderIcon: css5`
2809
+ folderIcon: css6`
2314
2810
  width: 24px;
2315
2811
  height: 24px;
2316
2812
  color: #f9935e;
2317
2813
  `,
2318
- imagesFolderWrapper: css5`
2814
+ imagesFolderWrapper: css6`
2319
2815
  width: 48px;
2320
2816
  height: 36px;
2321
2817
  display: flex;
@@ -2325,31 +2821,31 @@ var styles5 = {
2325
2821
  position: relative;
2326
2822
  align-items: center;
2327
2823
  `,
2328
- imagesFolderIcon: css5`
2824
+ imagesFolderIcon: css6`
2329
2825
  width: 24px;
2330
2826
  height: 24px;
2331
2827
  color: ${colors.imagesFolder};
2332
2828
  `,
2333
- lockIcon: css5`
2829
+ lockIcon: css6`
2334
2830
  width: 10px;
2335
2831
  height: 10px;
2336
2832
  color: ${colors.imagesFolder};
2337
2833
  margin-left: -6px;
2338
2834
  margin-top: 8px;
2339
2835
  `,
2340
- parentIcon: css5`
2836
+ parentIcon: css6`
2341
2837
  width: 20px;
2342
2838
  height: 20px;
2343
2839
  color: ${colors.textMuted};
2344
2840
  flex-shrink: 0;
2345
2841
  `,
2346
- fileIcon: css5`
2842
+ fileIcon: css6`
2347
2843
  width: 20px;
2348
2844
  height: 20px;
2349
2845
  color: ${colors.textMuted};
2350
2846
  flex-shrink: 0;
2351
2847
  `,
2352
- thumbnail: css5`
2848
+ thumbnail: css6`
2353
2849
  max-width: 100%;
2354
2850
  max-height: 100%;
2355
2851
  width: auto;
@@ -2358,7 +2854,7 @@ var styles5 = {
2358
2854
  border-radius: 4px;
2359
2855
  border: 1px solid ${colors.borderLight};
2360
2856
  `,
2361
- noThumbnail: css5`
2857
+ noThumbnail: css6`
2362
2858
  width: 36px;
2363
2859
  height: 36px;
2364
2860
  display: flex;
@@ -2376,12 +2872,12 @@ var styles5 = {
2376
2872
  background: ${colors.surfaceHover};
2377
2873
  }
2378
2874
  `,
2379
- noThumbnailIcon: css5`
2875
+ noThumbnailIcon: css6`
2380
2876
  width: 16px;
2381
2877
  height: 16px;
2382
2878
  color: ${colors.textMuted};
2383
2879
  `,
2384
- name: css5`
2880
+ name: css6`
2385
2881
  font-size: ${fontSize.base};
2386
2882
  font-weight: 500;
2387
2883
  color: ${colors.text};
@@ -2391,11 +2887,11 @@ var styles5 = {
2391
2887
  white-space: nowrap;
2392
2888
  max-width: 300px;
2393
2889
  `,
2394
- meta: css5`
2890
+ meta: css6`
2395
2891
  font-size: ${fontSize.sm};
2396
2892
  color: ${colors.textSecondary};
2397
2893
  `,
2398
- cdnBadge: css5`
2894
+ cdnBadge: css6`
2399
2895
  display: inline-flex;
2400
2896
  align-items: center;
2401
2897
  gap: 4px;
@@ -2403,15 +2899,15 @@ var styles5 = {
2403
2899
  font-weight: 500;
2404
2900
  color: ${colors.success};
2405
2901
  `,
2406
- cdnIcon: css5`
2902
+ cdnIcon: css6`
2407
2903
  width: 12px;
2408
2904
  height: 12px;
2409
2905
  `,
2410
- cdnEmpty: css5`
2906
+ cdnEmpty: css6`
2411
2907
  font-size: ${fontSize.sm};
2412
2908
  color: ${colors.textMuted};
2413
2909
  `,
2414
- openBtn: css5`
2910
+ openBtn: css6`
2415
2911
  height: 32px;
2416
2912
  font-size: ${fontSize.sm};
2417
2913
  font-weight: 500;
@@ -2432,89 +2928,33 @@ var styles5 = {
2432
2928
  `
2433
2929
  };
2434
2930
  function StudioFileList() {
2435
- const { currentPath, setCurrentPath, navigateUp, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey, setFocusedItem, triggerRefresh, searchQuery } = useStudio();
2436
- const [items, setItems] = useState4([]);
2437
- const [loading, setLoading] = useState4(true);
2438
- const isInitialLoad = useRef3(true);
2439
- const lastPath = useRef3(currentPath);
2440
- useEffect3(() => {
2441
- async function loadItems() {
2442
- const isPathChange = lastPath.current !== currentPath;
2443
- if (isInitialLoad.current || isPathChange) {
2444
- setLoading(true);
2445
- }
2446
- lastPath.current = currentPath;
2447
- try {
2448
- const url = searchQuery && searchQuery.length >= 2 ? `/api/studio/search?q=${encodeURIComponent(searchQuery)}` : `/api/studio/list?path=${encodeURIComponent(currentPath)}`;
2449
- const response = await fetch(url);
2450
- if (response.ok) {
2451
- const data = await response.json();
2452
- setItems(data.items || []);
2453
- }
2454
- } catch (error) {
2455
- console.error("Failed to load items:", error);
2456
- }
2457
- setLoading(false);
2458
- isInitialLoad.current = false;
2459
- }
2460
- loadItems();
2461
- }, [currentPath, refreshKey, searchQuery]);
2931
+ const {
2932
+ loading,
2933
+ sortedItems,
2934
+ isAtRoot,
2935
+ isSearching,
2936
+ allItemsSelected,
2937
+ someItemsSelected,
2938
+ selectedItems,
2939
+ navigateUp,
2940
+ handleItemClick,
2941
+ handleOpen,
2942
+ handleGenerateThumbnail,
2943
+ handleSelectAll
2944
+ } = useFileList();
2462
2945
  if (loading) {
2463
- return /* @__PURE__ */ jsx5("div", { css: styles5.loading, children: /* @__PURE__ */ jsx5("div", { css: styles5.spinner }) });
2946
+ return /* @__PURE__ */ jsx6("div", { css: styles6.loading, children: /* @__PURE__ */ jsx6("div", { css: styles6.spinner }) });
2464
2947
  }
2465
- const isAtRoot = currentPath === "public";
2466
- if (items.length === 0 && isAtRoot) {
2467
- return /* @__PURE__ */ jsx5("div", { css: styles5.empty, children: /* @__PURE__ */ jsx5("p", { children: "No files in this folder" }) });
2948
+ if (sortedItems.length === 0 && isAtRoot) {
2949
+ return /* @__PURE__ */ jsx6("div", { css: styles6.empty, children: /* @__PURE__ */ jsx6("p", { children: "No files in this folder" }) });
2468
2950
  }
2469
- const isSearching = searchQuery && searchQuery.length >= 2;
2470
- const sortedItems = [...items].sort((a, b) => {
2471
- if (a.type === "folder" && b.type !== "folder") return -1;
2472
- if (a.type !== "folder" && b.type === "folder") return 1;
2473
- return a.name.localeCompare(b.name);
2474
- });
2475
- const handleItemClick = (item, e) => {
2476
- if (e.shiftKey && lastSelectedPath) {
2477
- selectRange(lastSelectedPath, item.path, sortedItems);
2478
- } else {
2479
- toggleSelection(item.path);
2480
- }
2481
- };
2482
- const handleOpen = (item) => {
2483
- if (item.type === "folder") {
2484
- setCurrentPath(item.path);
2485
- } else {
2486
- setFocusedItem(item);
2487
- }
2488
- };
2489
- const handleGenerateThumbnail = async (item) => {
2490
- try {
2491
- const imageKey = item.path.replace(/^public\//, "");
2492
- await fetch("/api/studio/reprocess", {
2493
- method: "POST",
2494
- headers: { "Content-Type": "application/json" },
2495
- body: JSON.stringify({ imageKeys: [imageKey] })
2496
- });
2497
- triggerRefresh();
2498
- } catch (error) {
2499
- console.error("Failed to generate thumbnail:", error);
2500
- }
2501
- };
2502
- const allItemsSelected = sortedItems.length > 0 && sortedItems.every((item) => selectedItems.has(item.path));
2503
- const someItemsSelected = sortedItems.some((item) => selectedItems.has(item.path));
2504
- const handleSelectAll = () => {
2505
- if (allItemsSelected) {
2506
- clearSelection();
2507
- } else {
2508
- selectAll(sortedItems);
2509
- }
2510
- };
2511
- return /* @__PURE__ */ jsx5("div", { css: styles5.tableWrapper, children: /* @__PURE__ */ jsxs5("table", { css: styles5.table, children: [
2512
- /* @__PURE__ */ jsx5("thead", { children: /* @__PURE__ */ jsxs5("tr", { children: [
2513
- /* @__PURE__ */ jsx5("th", { css: [styles5.th, styles5.thCheckbox], children: sortedItems.length > 0 && /* @__PURE__ */ jsx5(
2951
+ return /* @__PURE__ */ jsx6("div", { css: styles6.tableWrapper, children: /* @__PURE__ */ jsxs6("table", { css: styles6.table, children: [
2952
+ /* @__PURE__ */ jsx6("thead", { children: /* @__PURE__ */ jsxs6("tr", { children: [
2953
+ /* @__PURE__ */ jsx6("th", { css: [styles6.th, styles6.thCheckbox], children: sortedItems.length > 0 && /* @__PURE__ */ jsx6(
2514
2954
  "input",
2515
2955
  {
2516
2956
  type: "checkbox",
2517
- css: styles5.checkbox,
2957
+ css: styles6.checkbox,
2518
2958
  checked: allItemsSelected,
2519
2959
  ref: (el) => {
2520
2960
  if (el) el.indeterminate = someItemsSelected && !allItemsSelected;
@@ -2522,23 +2962,23 @@ function StudioFileList() {
2522
2962
  onChange: handleSelectAll
2523
2963
  }
2524
2964
  ) }),
2525
- /* @__PURE__ */ jsx5("th", { css: styles5.th, children: "Name" }),
2526
- /* @__PURE__ */ jsx5("th", { css: [styles5.th, styles5.thSize], children: "Size" }),
2527
- /* @__PURE__ */ jsx5("th", { css: [styles5.th, styles5.thDimensions], children: "Dimensions" }),
2528
- /* @__PURE__ */ jsx5("th", { css: [styles5.th, styles5.thCdn], children: "CDN" })
2965
+ /* @__PURE__ */ jsx6("th", { css: styles6.th, children: "Name" }),
2966
+ /* @__PURE__ */ jsx6("th", { css: [styles6.th, styles6.thSize], children: "Size" }),
2967
+ /* @__PURE__ */ jsx6("th", { css: [styles6.th, styles6.thDimensions], children: "Dimensions" }),
2968
+ /* @__PURE__ */ jsx6("th", { css: [styles6.th, styles6.thCdn], children: "CDN" })
2529
2969
  ] }) }),
2530
- /* @__PURE__ */ jsxs5("tbody", { css: styles5.tbody, children: [
2531
- !isAtRoot && !isSearching && /* @__PURE__ */ jsxs5("tr", { css: styles5.parentRow, onClick: navigateUp, children: [
2532
- /* @__PURE__ */ jsx5("td", { css: styles5.td }),
2533
- /* @__PURE__ */ jsx5("td", { css: styles5.td, children: /* @__PURE__ */ jsxs5("div", { css: styles5.nameCell, children: [
2534
- /* @__PURE__ */ jsx5("svg", { css: styles5.parentIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" }) }),
2535
- /* @__PURE__ */ jsx5("span", { css: styles5.name, children: ".." })
2970
+ /* @__PURE__ */ jsxs6("tbody", { css: styles6.tbody, children: [
2971
+ !isAtRoot && !isSearching && /* @__PURE__ */ jsxs6("tr", { css: styles6.parentRow, onClick: navigateUp, children: [
2972
+ /* @__PURE__ */ jsx6("td", { css: styles6.td }),
2973
+ /* @__PURE__ */ jsx6("td", { css: styles6.td, children: /* @__PURE__ */ jsxs6("div", { css: styles6.nameCell, children: [
2974
+ /* @__PURE__ */ jsx6("svg", { css: styles6.parentIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" }) }),
2975
+ /* @__PURE__ */ jsx6("span", { css: styles6.name, children: ".." })
2536
2976
  ] }) }),
2537
- /* @__PURE__ */ jsx5("td", { css: [styles5.td, styles5.meta], children: "--" }),
2538
- /* @__PURE__ */ jsx5("td", { css: [styles5.td, styles5.meta], children: "Parent folder" }),
2539
- /* @__PURE__ */ jsx5("td", { css: styles5.td, children: "--" })
2977
+ /* @__PURE__ */ jsx6("td", { css: [styles6.td, styles6.meta], children: "--" }),
2978
+ /* @__PURE__ */ jsx6("td", { css: [styles6.td, styles6.meta], children: "Parent folder" }),
2979
+ /* @__PURE__ */ jsx6("td", { css: styles6.td, children: "--" })
2540
2980
  ] }),
2541
- sortedItems.map((item) => /* @__PURE__ */ jsx5(
2981
+ sortedItems.map((item) => /* @__PURE__ */ jsx6(
2542
2982
  ListRow,
2543
2983
  {
2544
2984
  item,
@@ -2553,7 +2993,7 @@ function StudioFileList() {
2553
2993
  ] }) });
2554
2994
  }
2555
2995
  function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
2556
- const [showCopied, setShowCopied] = useState4(false);
2996
+ const [showCopied, setShowCopied] = useState5(false);
2557
2997
  const isFolder = item.type === "folder";
2558
2998
  const isImage = !isFolder && item.thumbnail !== void 0;
2559
2999
  const isImagesFolder = isFolder && (item.name === "images" || item.path.includes("/images/"));
@@ -2564,62 +3004,62 @@ function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
2564
3004
  setShowCopied(true);
2565
3005
  setTimeout(() => setShowCopied(false), 1500);
2566
3006
  };
2567
- return /* @__PURE__ */ jsxs5(
3007
+ return /* @__PURE__ */ jsxs6(
2568
3008
  "tr",
2569
3009
  {
2570
- css: [styles5.row, isSelected && styles5.rowSelected],
3010
+ css: [styles6.row, isSelected && styles6.rowSelected],
2571
3011
  onClick,
2572
3012
  children: [
2573
- /* @__PURE__ */ jsx5(
3013
+ /* @__PURE__ */ jsx6(
2574
3014
  "td",
2575
3015
  {
2576
- css: [styles5.td, styles5.checkboxCell],
3016
+ css: [styles6.td, styles6.checkboxCell],
2577
3017
  onClick: (e) => e.stopPropagation(),
2578
- children: /* @__PURE__ */ jsx5(
3018
+ children: /* @__PURE__ */ jsx6(
2579
3019
  "input",
2580
3020
  {
2581
3021
  type: "checkbox",
2582
- css: styles5.checkbox,
3022
+ css: styles6.checkbox,
2583
3023
  checked: isSelected,
2584
3024
  onChange: () => onClick({})
2585
3025
  }
2586
3026
  )
2587
3027
  }
2588
3028
  ),
2589
- /* @__PURE__ */ jsx5("td", { css: styles5.td, children: /* @__PURE__ */ jsxs5("div", { css: styles5.nameCell, children: [
2590
- isFolder ? isImagesFolder ? /* @__PURE__ */ jsxs5("div", { css: styles5.imagesFolderWrapper, children: [
2591
- /* @__PURE__ */ jsx5("svg", { css: styles5.imagesFolderIcon, 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" }) }),
2592
- /* @__PURE__ */ jsx5("svg", { css: styles5.lockIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx5("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" }) })
2593
- ] }) : /* @__PURE__ */ jsx5("div", { css: styles5.folderIconWrapper, children: /* @__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" }) }) }) : isImage && item.hasThumbnail ? /* @__PURE__ */ jsx5("div", { css: styles5.thumbnailWrapper, children: /* @__PURE__ */ jsx5("img", { css: styles5.thumbnail, src: item.thumbnail, alt: item.name, loading: "lazy" }) }) : isImage && !item.hasThumbnail ? /* @__PURE__ */ jsx5("div", { css: styles5.thumbnailWrapper, children: /* @__PURE__ */ jsx5(
3029
+ /* @__PURE__ */ jsx6("td", { css: styles6.td, children: /* @__PURE__ */ jsxs6("div", { css: styles6.nameCell, children: [
3030
+ isFolder ? isImagesFolder ? /* @__PURE__ */ jsxs6("div", { css: styles6.imagesFolderWrapper, children: [
3031
+ /* @__PURE__ */ jsx6("svg", { css: styles6.imagesFolderIcon, 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" }) }),
3032
+ /* @__PURE__ */ jsx6("svg", { css: styles6.lockIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx6("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" }) })
3033
+ ] }) : /* @__PURE__ */ jsx6("div", { css: styles6.folderIconWrapper, 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" }) }) }) : isImage && item.hasThumbnail ? /* @__PURE__ */ jsx6("div", { css: styles6.thumbnailWrapper, children: /* @__PURE__ */ jsx6("img", { css: styles6.thumbnail, src: item.thumbnail, alt: item.name, loading: "lazy" }) }) : isImage && !item.hasThumbnail ? /* @__PURE__ */ jsx6("div", { css: styles6.thumbnailWrapper, children: /* @__PURE__ */ jsx6(
2594
3034
  "button",
2595
3035
  {
2596
- css: styles5.noThumbnail,
3036
+ css: styles6.noThumbnail,
2597
3037
  onClick: (e) => {
2598
3038
  e.stopPropagation();
2599
3039
  onGenerateThumbnail();
2600
3040
  },
2601
3041
  title: "Generate thumbnail",
2602
- children: /* @__PURE__ */ jsx5("svg", { css: styles5.noThumbnailIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("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" }) })
3042
+ children: /* @__PURE__ */ jsx6("svg", { css: styles6.noThumbnailIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("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" }) })
2603
3043
  }
2604
- ) }) : /* @__PURE__ */ jsx5("div", { css: styles5.thumbnailWrapper, children: /* @__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" }) }) }),
2605
- /* @__PURE__ */ jsx5("span", { css: styles5.name, title: item.name, children: truncateMiddle2(item.name) }),
2606
- /* @__PURE__ */ jsxs5("div", { css: styles5.actionsCell, children: [
2607
- /* @__PURE__ */ jsxs5(
3044
+ ) }) : /* @__PURE__ */ jsx6("div", { css: styles6.thumbnailWrapper, 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" }) }) }),
3045
+ /* @__PURE__ */ jsx6("span", { css: styles6.name, title: item.name, children: truncateMiddle2(item.name) }),
3046
+ /* @__PURE__ */ jsxs6("div", { css: styles6.actionsCell, children: [
3047
+ /* @__PURE__ */ jsxs6(
2608
3048
  "button",
2609
3049
  {
2610
- css: styles5.copyBtn,
3050
+ css: styles6.copyBtn,
2611
3051
  onClick: handleCopyPath,
2612
3052
  title: "Copy file path",
2613
3053
  children: [
2614
- showCopied && /* @__PURE__ */ jsx5("span", { css: styles5.tooltip, children: "Copied!" }),
2615
- /* @__PURE__ */ jsx5("svg", { css: styles5.copyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" }) })
3054
+ showCopied && /* @__PURE__ */ jsx6("span", { css: styles6.tooltip, children: "Copied!" }),
3055
+ /* @__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: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" }) })
2616
3056
  ]
2617
3057
  }
2618
3058
  ),
2619
- /* @__PURE__ */ jsx5(
3059
+ /* @__PURE__ */ jsx6(
2620
3060
  "button",
2621
3061
  {
2622
- css: styles5.openBtn,
3062
+ css: styles6.openBtn,
2623
3063
  onClick: (e) => {
2624
3064
  e.stopPropagation();
2625
3065
  onOpen();
@@ -2629,12 +3069,12 @@ function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
2629
3069
  )
2630
3070
  ] })
2631
3071
  ] }) }),
2632
- /* @__PURE__ */ jsx5("td", { css: [styles5.td, styles5.meta], children: isFolder ? item.fileCount !== void 0 ? `${item.fileCount} files` : "--" : item.size !== void 0 ? formatFileSize2(item.size) : "--" }),
2633
- /* @__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}` : "--" }),
2634
- /* @__PURE__ */ jsx5("td", { css: styles5.td, children: item.cdnSynced ? /* @__PURE__ */ jsxs5("span", { css: styles5.cdnBadge, children: [
2635
- /* @__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" }) }),
3072
+ /* @__PURE__ */ jsx6("td", { css: [styles6.td, styles6.meta], children: isFolder ? item.fileCount !== void 0 ? `${item.fileCount} files` : "--" : item.size !== void 0 ? formatFileSize2(item.size) : "--" }),
3073
+ /* @__PURE__ */ jsx6("td", { css: [styles6.td, styles6.meta], children: isFolder ? item.totalSize !== void 0 ? formatFileSize2(item.totalSize) : "--" : item.dimensions ? `${item.dimensions.width}x${item.dimensions.height}` : "--" }),
3074
+ /* @__PURE__ */ jsx6("td", { css: styles6.td, children: item.cdnSynced ? /* @__PURE__ */ jsxs6("span", { css: styles6.cdnBadge, children: [
3075
+ /* @__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" }) }),
2636
3076
  "Synced"
2637
- ] }) : /* @__PURE__ */ jsx5("span", { css: styles5.cdnEmpty, children: "--" }) })
3077
+ ] }) : /* @__PURE__ */ jsx6("span", { css: styles6.cdnEmpty, children: "--" }) })
2638
3078
  ]
2639
3079
  }
2640
3080
  );
@@ -2659,9 +3099,9 @@ function truncateMiddle2(str, maxLength = 32) {
2659
3099
  }
2660
3100
 
2661
3101
  // src/components/StudioDetailView.tsx
2662
- import { useState as useState5 } from "react";
2663
- import { css as css6 } from "@emotion/react";
2664
- import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "@emotion/react/jsx-runtime";
3102
+ import { useState as useState6 } from "react";
3103
+ import { css as css7 } from "@emotion/react";
3104
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "@emotion/react/jsx-runtime";
2665
3105
  var IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".ico", ".bmp", ".tiff", ".tif"];
2666
3106
  var VIDEO_EXTENSIONS = [".mp4", ".webm", ".mov", ".avi", ".mkv", ".m4v"];
2667
3107
  function isImageFile(filename) {
@@ -2672,8 +3112,8 @@ function isVideoFile(filename) {
2672
3112
  const ext = filename.toLowerCase().substring(filename.lastIndexOf("."));
2673
3113
  return VIDEO_EXTENSIONS.includes(ext);
2674
3114
  }
2675
- var styles6 = {
2676
- overlay: css6`
3115
+ var styles7 = {
3116
+ overlay: css7`
2677
3117
  position: absolute;
2678
3118
  top: 0;
2679
3119
  left: 0;
@@ -2683,7 +3123,7 @@ var styles6 = {
2683
3123
  display: flex;
2684
3124
  background: transparent;
2685
3125
  `,
2686
- container: css6`
3126
+ container: css7`
2687
3127
  display: flex;
2688
3128
  flex: 1;
2689
3129
  margin: 24px;
@@ -2693,7 +3133,7 @@ var styles6 = {
2693
3133
  overflow: hidden;
2694
3134
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
2695
3135
  `,
2696
- main: css6`
3136
+ main: css7`
2697
3137
  position: relative;
2698
3138
  flex: 1;
2699
3139
  display: flex;
@@ -2704,7 +3144,7 @@ var styles6 = {
2704
3144
  background: ${colors.background};
2705
3145
  overflow: auto;
2706
3146
  `,
2707
- headerButtons: css6`
3147
+ headerButtons: css7`
2708
3148
  position: absolute;
2709
3149
  top: 16px;
2710
3150
  right: 16px;
@@ -2712,7 +3152,7 @@ var styles6 = {
2712
3152
  gap: 8px;
2713
3153
  z-index: 10;
2714
3154
  `,
2715
- copyBtn: css6`
3155
+ copyBtn: css7`
2716
3156
  position: relative;
2717
3157
  padding: 8px;
2718
3158
  background: ${colors.surface};
@@ -2730,12 +3170,12 @@ var styles6 = {
2730
3170
  border-color: ${colors.borderHover};
2731
3171
  }
2732
3172
  `,
2733
- copyIcon: css6`
3173
+ copyIcon: css7`
2734
3174
  width: 20px;
2735
3175
  height: 20px;
2736
3176
  color: ${colors.textSecondary};
2737
3177
  `,
2738
- tooltip: css6`
3178
+ tooltip: css7`
2739
3179
  position: absolute;
2740
3180
  right: 100%;
2741
3181
  top: 50%;
@@ -2760,7 +3200,7 @@ var styles6 = {
2760
3200
  border-left-color: #1a1f36;
2761
3201
  }
2762
3202
  `,
2763
- mainCloseBtn: css6`
3203
+ mainCloseBtn: css7`
2764
3204
  padding: 8px;
2765
3205
  background: ${colors.surface};
2766
3206
  border: 1px solid ${colors.border};
@@ -2777,32 +3217,32 @@ var styles6 = {
2777
3217
  border-color: ${colors.borderHover};
2778
3218
  }
2779
3219
  `,
2780
- mainCloseIcon: css6`
3220
+ mainCloseIcon: css7`
2781
3221
  width: 20px;
2782
3222
  height: 20px;
2783
3223
  color: ${colors.textSecondary};
2784
3224
  `,
2785
- mediaWrapper: css6`
3225
+ mediaWrapper: css7`
2786
3226
  max-width: 100%;
2787
3227
  max-height: 100%;
2788
3228
  display: flex;
2789
3229
  align-items: center;
2790
3230
  justify-content: center;
2791
3231
  `,
2792
- image: css6`
3232
+ image: css7`
2793
3233
  max-width: 100%;
2794
3234
  max-height: calc(100vh - 200px);
2795
3235
  object-fit: contain;
2796
3236
  border-radius: 8px;
2797
3237
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
2798
3238
  `,
2799
- video: css6`
3239
+ video: css7`
2800
3240
  max-width: 100%;
2801
3241
  max-height: calc(100vh - 200px);
2802
3242
  border-radius: 8px;
2803
3243
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
2804
3244
  `,
2805
- filePlaceholder: css6`
3245
+ filePlaceholder: css7`
2806
3246
  display: flex;
2807
3247
  flex-direction: column;
2808
3248
  align-items: center;
@@ -2812,19 +3252,19 @@ var styles6 = {
2812
3252
  border-radius: 12px;
2813
3253
  border: 1px solid ${colors.border};
2814
3254
  `,
2815
- fileIcon: css6`
3255
+ fileIcon: css7`
2816
3256
  width: 80px;
2817
3257
  height: 80px;
2818
3258
  color: ${colors.textMuted};
2819
3259
  margin-bottom: 16px;
2820
3260
  `,
2821
- fileName: css6`
3261
+ fileName: css7`
2822
3262
  font-size: ${fontSize.lg};
2823
3263
  font-weight: 600;
2824
3264
  color: ${colors.text};
2825
3265
  margin: 0;
2826
3266
  `,
2827
- sidebar: css6`
3267
+ sidebar: css7`
2828
3268
  width: 280px;
2829
3269
  background: ${colors.surface};
2830
3270
  border-left: 1px solid ${colors.border};
@@ -2832,36 +3272,36 @@ var styles6 = {
2832
3272
  flex-direction: column;
2833
3273
  overflow: hidden;
2834
3274
  `,
2835
- sidebarHeader: css6`
3275
+ sidebarHeader: css7`
2836
3276
  padding: 16px 20px;
2837
3277
  border-bottom: 1px solid ${colors.border};
2838
3278
  `,
2839
- sidebarTitle: css6`
3279
+ sidebarTitle: css7`
2840
3280
  font-size: ${fontSize.base};
2841
3281
  font-weight: 600;
2842
3282
  color: ${colors.text};
2843
3283
  margin: 0;
2844
3284
  `,
2845
- sidebarContent: css6`
3285
+ sidebarContent: css7`
2846
3286
  flex: 1;
2847
3287
  padding: 20px;
2848
3288
  overflow: auto;
2849
3289
  `,
2850
- info: css6`
3290
+ info: css7`
2851
3291
  display: flex;
2852
3292
  flex-direction: column;
2853
3293
  gap: 12px;
2854
3294
  margin-bottom: 24px;
2855
3295
  `,
2856
- infoRow: css6`
3296
+ infoRow: css7`
2857
3297
  display: flex;
2858
3298
  justify-content: space-between;
2859
3299
  font-size: ${fontSize.sm};
2860
3300
  `,
2861
- infoLabel: css6`
3301
+ infoLabel: css7`
2862
3302
  color: ${colors.textSecondary};
2863
3303
  `,
2864
- infoValue: css6`
3304
+ infoValue: css7`
2865
3305
  color: ${colors.text};
2866
3306
  font-weight: 500;
2867
3307
  text-align: right;
@@ -2870,7 +3310,7 @@ var styles6 = {
2870
3310
  text-overflow: ellipsis;
2871
3311
  white-space: nowrap;
2872
3312
  `,
2873
- infoValueWrap: css6`
3313
+ infoValueWrap: css7`
2874
3314
  color: ${colors.text};
2875
3315
  font-weight: 500;
2876
3316
  text-align: right;
@@ -2878,12 +3318,12 @@ var styles6 = {
2878
3318
  word-break: break-all;
2879
3319
  white-space: normal;
2880
3320
  `,
2881
- actions: css6`
3321
+ actions: css7`
2882
3322
  display: flex;
2883
3323
  flex-direction: column;
2884
3324
  gap: 8px;
2885
3325
  `,
2886
- actionBtn: css6`
3326
+ actionBtn: css7`
2887
3327
  display: flex;
2888
3328
  align-items: center;
2889
3329
  gap: 10px;
@@ -2904,7 +3344,7 @@ var styles6 = {
2904
3344
  border-color: ${colors.borderHover};
2905
3345
  }
2906
3346
  `,
2907
- actionBtnDanger: css6`
3347
+ actionBtnDanger: css7`
2908
3348
  color: ${colors.danger};
2909
3349
 
2910
3350
  &:hover {
@@ -2912,7 +3352,7 @@ var styles6 = {
2912
3352
  border-color: ${colors.danger};
2913
3353
  }
2914
3354
  `,
2915
- actionIcon: css6`
3355
+ actionIcon: css7`
2916
3356
  width: 16px;
2917
3357
  height: 16px;
2918
3358
  flex-shrink: 0;
@@ -2920,12 +3360,14 @@ var styles6 = {
2920
3360
  };
2921
3361
  function StudioDetailView() {
2922
3362
  const { focusedItem, setFocusedItem, triggerRefresh, clearSelection } = useStudio();
2923
- const [showDeleteConfirm, setShowDeleteConfirm] = useState5(false);
2924
- const [showRenameModal, setShowRenameModal] = useState5(false);
2925
- const [showProcessConfirm, setShowProcessConfirm] = useState5(false);
2926
- const [processProgress, setProcessProgress] = useState5(null);
2927
- const [alertMessage, setAlertMessage] = useState5(null);
2928
- const [showCopied, setShowCopied] = useState5(false);
3363
+ const [showDeleteConfirm, setShowDeleteConfirm] = useState6(false);
3364
+ const [showRenameModal, setShowRenameModal] = useState6(false);
3365
+ const [showProcessConfirm, setShowProcessConfirm] = useState6(false);
3366
+ const [showR2SetupModal, setShowR2SetupModal] = useState6(false);
3367
+ const [processProgress, setProcessProgress] = useState6(null);
3368
+ const [alertMessage, setAlertMessage] = useState6(null);
3369
+ const [showCopied, setShowCopied] = useState6(false);
3370
+ const [syncing, setSyncing] = useState6(false);
2929
3371
  if (!focusedItem) return null;
2930
3372
  const isImage = isImageFile(focusedItem.name);
2931
3373
  const isVideo = isVideoFile(focusedItem.name);
@@ -2997,8 +3439,41 @@ function StudioDetailView() {
2997
3439
  });
2998
3440
  }
2999
3441
  };
3000
- const handleSync = () => {
3001
- console.log("Sync to CDN:", focusedItem.path);
3442
+ const handleSync = async () => {
3443
+ const imageKey = "/" + focusedItem.path.replace(/^public\//, "");
3444
+ setSyncing(true);
3445
+ try {
3446
+ const response = await fetch("/api/studio/sync", {
3447
+ method: "POST",
3448
+ headers: { "Content-Type": "application/json" },
3449
+ body: JSON.stringify({ imageKeys: [imageKey] })
3450
+ });
3451
+ const data = await response.json();
3452
+ if (response.ok) {
3453
+ setAlertMessage({
3454
+ title: "Sync Complete",
3455
+ message: "Successfully synced to CDN."
3456
+ });
3457
+ triggerRefresh();
3458
+ } else {
3459
+ if (data.error?.includes("R2 not configured") || data.error?.includes("CLOUDFLARE_R2")) {
3460
+ setShowR2SetupModal(true);
3461
+ } else {
3462
+ setAlertMessage({
3463
+ title: "Sync Failed",
3464
+ message: data.error || "Failed to sync to CDN."
3465
+ });
3466
+ }
3467
+ }
3468
+ } catch (error) {
3469
+ console.error("Sync error:", error);
3470
+ setAlertMessage({
3471
+ title: "Sync Failed",
3472
+ message: "Failed to sync to CDN. Check console for details."
3473
+ });
3474
+ } finally {
3475
+ setSyncing(false);
3476
+ }
3002
3477
  };
3003
3478
  const handleProcessImage = async () => {
3004
3479
  setShowProcessConfirm(false);
@@ -3056,18 +3531,18 @@ function StudioDetailView() {
3056
3531
  };
3057
3532
  const renderMedia = () => {
3058
3533
  if (isImage) {
3059
- return /* @__PURE__ */ jsx6("img", { css: styles6.image, src: imageSrc, alt: focusedItem.name });
3534
+ return /* @__PURE__ */ jsx7("img", { css: styles7.image, src: imageSrc, alt: focusedItem.name });
3060
3535
  }
3061
3536
  if (isVideo) {
3062
- return /* @__PURE__ */ jsx6("video", { css: styles6.video, src: imageSrc, controls: true });
3537
+ return /* @__PURE__ */ jsx7("video", { css: styles7.video, src: imageSrc, controls: true });
3063
3538
  }
3064
- return /* @__PURE__ */ jsxs6("div", { css: styles6.filePlaceholder, children: [
3065
- /* @__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" }) }),
3066
- /* @__PURE__ */ jsx6("p", { css: styles6.fileName, children: focusedItem.name })
3539
+ return /* @__PURE__ */ jsxs7("div", { css: styles7.filePlaceholder, children: [
3540
+ /* @__PURE__ */ jsx7("svg", { css: styles7.fileIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx7("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" }) }),
3541
+ /* @__PURE__ */ jsx7("p", { css: styles7.fileName, children: focusedItem.name })
3067
3542
  ] });
3068
3543
  };
3069
- return /* @__PURE__ */ jsxs6(Fragment3, { children: [
3070
- showDeleteConfirm && /* @__PURE__ */ jsx6(
3544
+ return /* @__PURE__ */ jsxs7(Fragment3, { children: [
3545
+ showDeleteConfirm && /* @__PURE__ */ jsx7(
3071
3546
  ConfirmModal,
3072
3547
  {
3073
3548
  title: "Delete File",
@@ -3078,7 +3553,7 @@ function StudioDetailView() {
3078
3553
  onCancel: () => setShowDeleteConfirm(false)
3079
3554
  }
3080
3555
  ),
3081
- alertMessage && /* @__PURE__ */ jsx6(
3556
+ alertMessage && /* @__PURE__ */ jsx7(
3082
3557
  AlertModal,
3083
3558
  {
3084
3559
  title: alertMessage.title,
@@ -3086,7 +3561,14 @@ function StudioDetailView() {
3086
3561
  onClose: () => setAlertMessage(null)
3087
3562
  }
3088
3563
  ),
3089
- showRenameModal && /* @__PURE__ */ jsx6(
3564
+ /* @__PURE__ */ jsx7(
3565
+ R2SetupModal,
3566
+ {
3567
+ isOpen: showR2SetupModal,
3568
+ onClose: () => setShowR2SetupModal(false)
3569
+ }
3570
+ ),
3571
+ showRenameModal && /* @__PURE__ */ jsx7(
3090
3572
  InputModal,
3091
3573
  {
3092
3574
  title: "Rename File",
@@ -3098,7 +3580,7 @@ function StudioDetailView() {
3098
3580
  onCancel: () => setShowRenameModal(false)
3099
3581
  }
3100
3582
  ),
3101
- showProcessConfirm && /* @__PURE__ */ jsx6(
3583
+ showProcessConfirm && /* @__PURE__ */ jsx7(
3102
3584
  ConfirmModal,
3103
3585
  {
3104
3586
  title: "Process Image",
@@ -3108,7 +3590,7 @@ function StudioDetailView() {
3108
3590
  onCancel: () => setShowProcessConfirm(false)
3109
3591
  }
3110
3592
  ),
3111
- processProgress && /* @__PURE__ */ jsx6(
3593
+ processProgress && /* @__PURE__ */ jsx7(
3112
3594
  ProgressModal,
3113
3595
  {
3114
3596
  title: "Processing Image",
@@ -3116,61 +3598,61 @@ function StudioDetailView() {
3116
3598
  onClose: () => setProcessProgress(null)
3117
3599
  }
3118
3600
  ),
3119
- /* @__PURE__ */ jsx6("div", { css: styles6.overlay, onClick: handleClose, children: /* @__PURE__ */ jsxs6("div", { css: styles6.container, onClick: (e) => e.stopPropagation(), children: [
3120
- /* @__PURE__ */ jsxs6("div", { css: styles6.main, children: [
3121
- /* @__PURE__ */ jsxs6("div", { css: styles6.headerButtons, children: [
3122
- /* @__PURE__ */ jsxs6("button", { css: styles6.copyBtn, onClick: handleCopyPath, title: "Copy file path", children: [
3123
- showCopied && /* @__PURE__ */ jsx6("span", { css: styles6.tooltip, children: "Copied!" }),
3124
- /* @__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: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" }) })
3601
+ /* @__PURE__ */ jsx7("div", { css: styles7.overlay, onClick: handleClose, children: /* @__PURE__ */ jsxs7("div", { css: styles7.container, onClick: (e) => e.stopPropagation(), children: [
3602
+ /* @__PURE__ */ jsxs7("div", { css: styles7.main, children: [
3603
+ /* @__PURE__ */ jsxs7("div", { css: styles7.headerButtons, children: [
3604
+ /* @__PURE__ */ jsxs7("button", { css: styles7.copyBtn, onClick: handleCopyPath, title: "Copy file path", children: [
3605
+ showCopied && /* @__PURE__ */ jsx7("span", { css: styles7.tooltip, children: "Copied!" }),
3606
+ /* @__PURE__ */ jsx7("svg", { css: styles7.copyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx7("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" }) })
3125
3607
  ] }),
3126
- /* @__PURE__ */ jsx6("button", { css: styles6.mainCloseBtn, onClick: handleClose, "aria-label": "Close", children: /* @__PURE__ */ jsx6("svg", { css: styles6.mainCloseIcon, 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" }) }) })
3608
+ /* @__PURE__ */ jsx7("button", { css: styles7.mainCloseBtn, onClick: handleClose, "aria-label": "Close", children: /* @__PURE__ */ jsx7("svg", { css: styles7.mainCloseIcon, 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" }) }) })
3127
3609
  ] }),
3128
- /* @__PURE__ */ jsx6("div", { css: styles6.mediaWrapper, children: renderMedia() })
3610
+ /* @__PURE__ */ jsx7("div", { css: styles7.mediaWrapper, children: renderMedia() })
3129
3611
  ] }),
3130
- /* @__PURE__ */ jsxs6("div", { css: styles6.sidebar, children: [
3131
- /* @__PURE__ */ jsx6("div", { css: styles6.sidebarHeader, children: /* @__PURE__ */ jsx6("h3", { css: styles6.sidebarTitle, children: "Details" }) }),
3132
- /* @__PURE__ */ jsxs6("div", { css: styles6.sidebarContent, children: [
3133
- /* @__PURE__ */ jsxs6("div", { css: styles6.info, children: [
3134
- /* @__PURE__ */ jsxs6("div", { css: styles6.infoRow, children: [
3135
- /* @__PURE__ */ jsx6("span", { css: styles6.infoLabel, children: "Name" }),
3136
- /* @__PURE__ */ jsx6("span", { css: styles6.infoValueWrap, children: focusedItem.name })
3612
+ /* @__PURE__ */ jsxs7("div", { css: styles7.sidebar, children: [
3613
+ /* @__PURE__ */ jsx7("div", { css: styles7.sidebarHeader, children: /* @__PURE__ */ jsx7("h3", { css: styles7.sidebarTitle, children: "Details" }) }),
3614
+ /* @__PURE__ */ jsxs7("div", { css: styles7.sidebarContent, children: [
3615
+ /* @__PURE__ */ jsxs7("div", { css: styles7.info, children: [
3616
+ /* @__PURE__ */ jsxs7("div", { css: styles7.infoRow, children: [
3617
+ /* @__PURE__ */ jsx7("span", { css: styles7.infoLabel, children: "Name" }),
3618
+ /* @__PURE__ */ jsx7("span", { css: styles7.infoValueWrap, children: focusedItem.name })
3137
3619
  ] }),
3138
- /* @__PURE__ */ jsxs6("div", { css: styles6.infoRow, children: [
3139
- /* @__PURE__ */ jsx6("span", { css: styles6.infoLabel, children: "Path" }),
3140
- /* @__PURE__ */ jsx6("span", { css: styles6.infoValueWrap, children: focusedItem.path.replace(/^public\//, "") })
3620
+ /* @__PURE__ */ jsxs7("div", { css: styles7.infoRow, children: [
3621
+ /* @__PURE__ */ jsx7("span", { css: styles7.infoLabel, children: "Path" }),
3622
+ /* @__PURE__ */ jsx7("span", { css: styles7.infoValueWrap, children: focusedItem.path.replace(/^public\//, "") })
3141
3623
  ] }),
3142
- focusedItem.size !== void 0 && /* @__PURE__ */ jsxs6("div", { css: styles6.infoRow, children: [
3143
- /* @__PURE__ */ jsx6("span", { css: styles6.infoLabel, children: "Size" }),
3144
- /* @__PURE__ */ jsx6("span", { css: styles6.infoValue, children: formatFileSize3(focusedItem.size) })
3624
+ focusedItem.size !== void 0 && /* @__PURE__ */ jsxs7("div", { css: styles7.infoRow, children: [
3625
+ /* @__PURE__ */ jsx7("span", { css: styles7.infoLabel, children: "Size" }),
3626
+ /* @__PURE__ */ jsx7("span", { css: styles7.infoValue, children: formatFileSize3(focusedItem.size) })
3145
3627
  ] }),
3146
- focusedItem.dimensions && /* @__PURE__ */ jsxs6("div", { css: styles6.infoRow, children: [
3147
- /* @__PURE__ */ jsx6("span", { css: styles6.infoLabel, children: "Dimensions" }),
3148
- /* @__PURE__ */ jsxs6("span", { css: styles6.infoValue, children: [
3628
+ focusedItem.dimensions && /* @__PURE__ */ jsxs7("div", { css: styles7.infoRow, children: [
3629
+ /* @__PURE__ */ jsx7("span", { css: styles7.infoLabel, children: "Dimensions" }),
3630
+ /* @__PURE__ */ jsxs7("span", { css: styles7.infoValue, children: [
3149
3631
  focusedItem.dimensions.width,
3150
3632
  " \xD7 ",
3151
3633
  focusedItem.dimensions.height
3152
3634
  ] })
3153
3635
  ] }),
3154
- /* @__PURE__ */ jsxs6("div", { css: styles6.infoRow, children: [
3155
- /* @__PURE__ */ jsx6("span", { css: styles6.infoLabel, children: "CDN Status" }),
3156
- /* @__PURE__ */ jsx6("span", { css: styles6.infoValue, children: focusedItem.cdnSynced ? "Synced" : "Not synced" })
3636
+ /* @__PURE__ */ jsxs7("div", { css: styles7.infoRow, children: [
3637
+ /* @__PURE__ */ jsx7("span", { css: styles7.infoLabel, children: "CDN Status" }),
3638
+ /* @__PURE__ */ jsx7("span", { css: styles7.infoValue, children: focusedItem.cdnSynced ? "Synced" : "Not synced" })
3157
3639
  ] })
3158
3640
  ] }),
3159
- /* @__PURE__ */ jsxs6("div", { css: styles6.actions, children: [
3160
- /* @__PURE__ */ jsxs6("button", { css: styles6.actionBtn, onClick: () => setShowRenameModal(true), children: [
3161
- /* @__PURE__ */ jsx6("svg", { css: styles6.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("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" }) }),
3641
+ /* @__PURE__ */ jsxs7("div", { css: styles7.actions, children: [
3642
+ /* @__PURE__ */ jsxs7("button", { css: styles7.actionBtn, onClick: () => setShowRenameModal(true), children: [
3643
+ /* @__PURE__ */ jsx7("svg", { css: styles7.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx7("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" }) }),
3162
3644
  "Rename"
3163
3645
  ] }),
3164
- /* @__PURE__ */ jsxs6("button", { css: styles6.actionBtn, onClick: handleSync, children: [
3165
- /* @__PURE__ */ jsx6("svg", { css: styles6.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("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" }) }),
3166
- "Sync to CDN"
3646
+ /* @__PURE__ */ jsxs7("button", { css: styles7.actionBtn, onClick: handleSync, disabled: syncing, children: [
3647
+ /* @__PURE__ */ jsx7("svg", { css: styles7.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx7("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" }) }),
3648
+ syncing ? "Syncing..." : "Sync to CDN"
3167
3649
  ] }),
3168
- /* @__PURE__ */ jsxs6("button", { css: styles6.actionBtn, onClick: () => setShowProcessConfirm(true), children: [
3169
- /* @__PURE__ */ jsx6("svg", { css: styles6.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("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" }) }),
3650
+ /* @__PURE__ */ jsxs7("button", { css: styles7.actionBtn, onClick: () => setShowProcessConfirm(true), children: [
3651
+ /* @__PURE__ */ jsx7("svg", { css: styles7.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx7("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" }) }),
3170
3652
  "Process Image"
3171
3653
  ] }),
3172
- /* @__PURE__ */ jsxs6("button", { css: [styles6.actionBtn, styles6.actionBtnDanger], onClick: () => setShowDeleteConfirm(true), children: [
3173
- /* @__PURE__ */ jsx6("svg", { css: styles6.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("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" }) }),
3654
+ /* @__PURE__ */ jsxs7("button", { css: [styles7.actionBtn, styles7.actionBtnDanger], onClick: () => setShowDeleteConfirm(true), children: [
3655
+ /* @__PURE__ */ jsx7("svg", { css: styles7.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx7("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" }) }),
3174
3656
  "Delete"
3175
3657
  ] })
3176
3658
  ] })
@@ -3186,12 +3668,12 @@ function formatFileSize3(bytes) {
3186
3668
  }
3187
3669
 
3188
3670
  // src/components/StudioSettings.tsx
3189
- import { useState as useState6 } from "react";
3190
- import { css as css7 } from "@emotion/react";
3191
- import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "@emotion/react/jsx-runtime";
3671
+ import { useState as useState7 } from "react";
3672
+ import { css as css8 } from "@emotion/react";
3673
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "@emotion/react/jsx-runtime";
3192
3674
  var btnHeight2 = "36px";
3193
- var styles7 = {
3194
- btn: css7`
3675
+ var styles8 = {
3676
+ btn: css8`
3195
3677
  height: ${btnHeight2};
3196
3678
  padding: 0 12px;
3197
3679
  background: ${colors.surface};
@@ -3208,12 +3690,12 @@ var styles7 = {
3208
3690
  border-color: ${colors.borderHover};
3209
3691
  }
3210
3692
  `,
3211
- icon: css7`
3693
+ icon: css8`
3212
3694
  width: 16px;
3213
3695
  height: 16px;
3214
3696
  color: ${colors.textSecondary};
3215
3697
  `,
3216
- overlay: css7`
3698
+ overlay: css8`
3217
3699
  position: fixed;
3218
3700
  top: 0;
3219
3701
  right: 0;
@@ -3226,7 +3708,7 @@ var styles7 = {
3226
3708
  background-color: rgba(26, 31, 54, 0.4);
3227
3709
  backdrop-filter: blur(4px);
3228
3710
  `,
3229
- panel: css7`
3711
+ panel: css8`
3230
3712
  ${baseReset}
3231
3713
  position: relative;
3232
3714
  background-color: ${colors.surface};
@@ -3236,20 +3718,20 @@ var styles7 = {
3236
3718
  max-width: 512px;
3237
3719
  padding: 24px;
3238
3720
  `,
3239
- header: css7`
3721
+ header: css8`
3240
3722
  display: flex;
3241
3723
  align-items: center;
3242
3724
  justify-content: space-between;
3243
3725
  margin-bottom: 24px;
3244
3726
  `,
3245
- title: css7`
3727
+ title: css8`
3246
3728
  font-size: ${fontSize.xl};
3247
3729
  font-weight: 600;
3248
3730
  color: ${colors.text};
3249
3731
  margin: 0;
3250
3732
  letter-spacing: -0.02em;
3251
3733
  `,
3252
- closeBtn: css7`
3734
+ closeBtn: css8`
3253
3735
  padding: 6px;
3254
3736
  background: ${colors.surface};
3255
3737
  border: 1px solid ${colors.border};
@@ -3265,26 +3747,26 @@ var styles7 = {
3265
3747
  border-color: ${colors.borderHover};
3266
3748
  }
3267
3749
  `,
3268
- sections: css7`
3750
+ sections: css8`
3269
3751
  display: flex;
3270
3752
  flex-direction: column;
3271
3753
  gap: 24px;
3272
3754
  `,
3273
- sectionTitle: css7`
3755
+ sectionTitle: css8`
3274
3756
  font-size: ${fontSize.base};
3275
3757
  font-weight: 600;
3276
3758
  color: ${colors.text};
3277
3759
  margin: 0 0 12px 0;
3278
3760
  `,
3279
- description: css7`
3761
+ description: css8`
3280
3762
  font-size: ${fontSize.sm};
3281
3763
  color: ${colors.textSecondary};
3282
3764
  margin: 0 0 12px 0;
3283
3765
  `,
3284
- codeWrapper: css7`
3766
+ codeWrapper: css8`
3285
3767
  position: relative;
3286
3768
  `,
3287
- code: css7`
3769
+ code: css8`
3288
3770
  background-color: ${colors.background};
3289
3771
  border-radius: 8px;
3290
3772
  padding: 12px;
@@ -3296,7 +3778,7 @@ var styles7 = {
3296
3778
  overflow-x: auto;
3297
3779
  white-space: nowrap;
3298
3780
  `,
3299
- copyBtn: css7`
3781
+ copyBtn: css8`
3300
3782
  position: absolute;
3301
3783
  top: 8px;
3302
3784
  right: 8px;
@@ -3315,7 +3797,7 @@ var styles7 = {
3315
3797
  border-color: ${colors.borderHover};
3316
3798
  }
3317
3799
  `,
3318
- tooltip: css7`
3800
+ tooltip: css8`
3319
3801
  position: absolute;
3320
3802
  bottom: 100%;
3321
3803
  left: 50%;
@@ -3340,19 +3822,19 @@ var styles7 = {
3340
3822
  border-top-color: #1a1f36;
3341
3823
  }
3342
3824
  `,
3343
- copyIcon: css7`
3825
+ copyIcon: css8`
3344
3826
  width: 14px;
3345
3827
  height: 14px;
3346
3828
  color: ${colors.textSecondary};
3347
3829
  `,
3348
- codeLine: css7`
3830
+ codeLine: css8`
3349
3831
  margin: 0 0 4px 0;
3350
3832
 
3351
3833
  &:last-child {
3352
3834
  margin: 0;
3353
3835
  }
3354
3836
  `,
3355
- input: css7`
3837
+ input: css8`
3356
3838
  width: 100%;
3357
3839
  padding: 10px 14px;
3358
3840
  border: 1px solid ${colors.border};
@@ -3372,19 +3854,19 @@ var styles7 = {
3372
3854
  color: ${colors.textMuted};
3373
3855
  }
3374
3856
  `,
3375
- grid: css7`
3857
+ grid: css8`
3376
3858
  display: grid;
3377
3859
  grid-template-columns: repeat(3, 1fr);
3378
3860
  gap: 12px;
3379
3861
  `,
3380
- label: css7`
3862
+ label: css8`
3381
3863
  font-size: ${fontSize.xs};
3382
3864
  font-weight: 500;
3383
3865
  color: ${colors.textSecondary};
3384
3866
  display: block;
3385
3867
  margin-bottom: 6px;
3386
3868
  `,
3387
- footer: css7`
3869
+ footer: css8`
3388
3870
  margin-top: 24px;
3389
3871
  padding-top: 20px;
3390
3872
  border-top: 1px solid ${colors.border};
@@ -3392,7 +3874,7 @@ var styles7 = {
3392
3874
  justify-content: flex-end;
3393
3875
  gap: 12px;
3394
3876
  `,
3395
- cancelBtn: css7`
3877
+ cancelBtn: css8`
3396
3878
  padding: 10px 18px;
3397
3879
  font-size: ${fontSize.base};
3398
3880
  font-weight: 500;
@@ -3408,7 +3890,7 @@ var styles7 = {
3408
3890
  border-color: ${colors.borderHover};
3409
3891
  }
3410
3892
  `,
3411
- saveBtn: css7`
3893
+ saveBtn: css8`
3412
3894
  padding: 10px 18px;
3413
3895
  font-size: ${fontSize.base};
3414
3896
  font-weight: 500;
@@ -3426,12 +3908,12 @@ var styles7 = {
3426
3908
  `
3427
3909
  };
3428
3910
  function StudioSettings() {
3429
- const [isOpen, setIsOpen] = useState6(false);
3430
- return /* @__PURE__ */ jsxs7(Fragment4, { children: [
3431
- /* @__PURE__ */ jsx7("button", { css: styles7.btn, onClick: () => setIsOpen(true), "aria-label": "Settings", children: /* @__PURE__ */ jsxs7(
3911
+ const [isOpen, setIsOpen] = useState7(false);
3912
+ return /* @__PURE__ */ jsxs8(Fragment4, { children: [
3913
+ /* @__PURE__ */ jsx8("button", { css: styles8.btn, onClick: () => setIsOpen(true), "aria-label": "Settings", children: /* @__PURE__ */ jsxs8(
3432
3914
  "svg",
3433
3915
  {
3434
- css: styles7.icon,
3916
+ css: styles8.icon,
3435
3917
  xmlns: "http://www.w3.org/2000/svg",
3436
3918
  viewBox: "0 0 24 24",
3437
3919
  fill: "none",
@@ -3440,12 +3922,12 @@ function StudioSettings() {
3440
3922
  strokeLinecap: "round",
3441
3923
  strokeLinejoin: "round",
3442
3924
  children: [
3443
- /* @__PURE__ */ jsx7("circle", { cx: "12", cy: "12", r: "3" }),
3444
- /* @__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" })
3925
+ /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r: "3" }),
3926
+ /* @__PURE__ */ jsx8("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" })
3445
3927
  ]
3446
3928
  }
3447
3929
  ) }),
3448
- isOpen && /* @__PURE__ */ jsx7(SettingsPanel, { onClose: () => setIsOpen(false) })
3930
+ isOpen && /* @__PURE__ */ jsx8(SettingsPanel, { onClose: () => setIsOpen(false) })
3449
3931
  ] });
3450
3932
  }
3451
3933
  var envTemplate = `CLOUDFLARE_R2_ACCOUNT_ID=abc123def456ghi789
@@ -3454,72 +3936,147 @@ CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_secret_access_key_here
3454
3936
  CLOUDFLARE_R2_BUCKET_NAME=my-images-bucket
3455
3937
  CLOUDFLARE_R2_PUBLIC_URL=https://cdn.yourdomain.com`;
3456
3938
  function SettingsPanel({ onClose }) {
3457
- const [copied, setCopied] = useState6(false);
3939
+ const [copied, setCopied] = useState7(false);
3458
3940
  const handleCopy = () => {
3459
3941
  navigator.clipboard.writeText(envTemplate);
3460
3942
  setCopied(true);
3461
3943
  setTimeout(() => setCopied(false), 2e3);
3462
3944
  };
3463
- return /* @__PURE__ */ jsx7("div", { css: styles7.overlay, onClick: onClose, children: /* @__PURE__ */ jsxs7("div", { css: styles7.panel, onClick: (e) => e.stopPropagation(), children: [
3464
- /* @__PURE__ */ jsxs7("div", { css: styles7.header, children: [
3465
- /* @__PURE__ */ jsx7("h2", { css: styles7.title, children: "Settings" }),
3466
- /* @__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" }) }) })
3945
+ return /* @__PURE__ */ jsx8("div", { css: styles8.overlay, onClick: onClose, children: /* @__PURE__ */ jsxs8("div", { css: styles8.panel, onClick: (e) => e.stopPropagation(), children: [
3946
+ /* @__PURE__ */ jsxs8("div", { css: styles8.header, children: [
3947
+ /* @__PURE__ */ jsx8("h2", { css: styles8.title, children: "Settings" }),
3948
+ /* @__PURE__ */ jsx8("button", { css: styles8.closeBtn, onClick: onClose, children: /* @__PURE__ */ jsx8("svg", { css: styles8.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx8("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })
3467
3949
  ] }),
3468
- /* @__PURE__ */ jsxs7("div", { css: styles7.sections, children: [
3469
- /* @__PURE__ */ jsxs7("section", { children: [
3470
- /* @__PURE__ */ jsx7("h3", { css: styles7.sectionTitle, children: "Cloudflare R2" }),
3471
- /* @__PURE__ */ jsx7("p", { css: styles7.description, children: "Configure in .env.local file:" }),
3472
- /* @__PURE__ */ jsxs7("div", { css: styles7.codeWrapper, children: [
3473
- /* @__PURE__ */ jsxs7("button", { css: styles7.copyBtn, onClick: handleCopy, title: "Copy to clipboard", children: [
3474
- copied && /* @__PURE__ */ jsx7("span", { css: styles7.tooltip, children: "Copied!" }),
3475
- /* @__PURE__ */ jsx7("svg", { css: styles7.copyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx7("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" }) })
3950
+ /* @__PURE__ */ jsxs8("div", { css: styles8.sections, children: [
3951
+ /* @__PURE__ */ jsxs8("section", { children: [
3952
+ /* @__PURE__ */ jsx8("h3", { css: styles8.sectionTitle, children: "Cloudflare R2" }),
3953
+ /* @__PURE__ */ jsx8("p", { css: styles8.description, children: "Configure in .env.local file:" }),
3954
+ /* @__PURE__ */ jsxs8("div", { css: styles8.codeWrapper, children: [
3955
+ /* @__PURE__ */ jsxs8("button", { css: styles8.copyBtn, onClick: handleCopy, title: "Copy to clipboard", children: [
3956
+ copied && /* @__PURE__ */ jsx8("span", { css: styles8.tooltip, children: "Copied!" }),
3957
+ /* @__PURE__ */ jsx8("svg", { css: styles8.copyIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx8("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" }) })
3476
3958
  ] }),
3477
- /* @__PURE__ */ jsxs7("div", { css: styles7.code, children: [
3478
- /* @__PURE__ */ jsx7("p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_ACCOUNT_ID=abc123def456ghi789" }),
3479
- /* @__PURE__ */ jsx7("p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_ACCESS_KEY_ID=your_access_key_id_here" }),
3480
- /* @__PURE__ */ jsx7("p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_secret_access_key_here" }),
3481
- /* @__PURE__ */ jsx7("p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_BUCKET_NAME=my-images-bucket" }),
3482
- /* @__PURE__ */ jsx7("p", { css: styles7.codeLine, children: "CLOUDFLARE_R2_PUBLIC_URL=https://cdn.yourdomain.com" })
3959
+ /* @__PURE__ */ jsxs8("div", { css: styles8.code, children: [
3960
+ /* @__PURE__ */ jsx8("p", { css: styles8.codeLine, children: "CLOUDFLARE_R2_ACCOUNT_ID=abc123def456ghi789" }),
3961
+ /* @__PURE__ */ jsx8("p", { css: styles8.codeLine, children: "CLOUDFLARE_R2_ACCESS_KEY_ID=your_access_key_id_here" }),
3962
+ /* @__PURE__ */ jsx8("p", { css: styles8.codeLine, children: "CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_secret_access_key_here" }),
3963
+ /* @__PURE__ */ jsx8("p", { css: styles8.codeLine, children: "CLOUDFLARE_R2_BUCKET_NAME=my-images-bucket" }),
3964
+ /* @__PURE__ */ jsx8("p", { css: styles8.codeLine, children: "CLOUDFLARE_R2_PUBLIC_URL=https://cdn.yourdomain.com" })
3483
3965
  ] })
3484
3966
  ] })
3485
3967
  ] }),
3486
- /* @__PURE__ */ jsxs7("section", { children: [
3487
- /* @__PURE__ */ jsx7("h3", { css: styles7.sectionTitle, children: "Thumbnail Sizes" }),
3488
- /* @__PURE__ */ jsxs7("div", { css: styles7.grid, children: [
3489
- /* @__PURE__ */ jsxs7("div", { children: [
3490
- /* @__PURE__ */ jsx7("label", { css: styles7.label, children: "Small" }),
3491
- /* @__PURE__ */ jsx7("input", { css: styles7.input, type: "number", defaultValue: 300 })
3968
+ /* @__PURE__ */ jsxs8("section", { children: [
3969
+ /* @__PURE__ */ jsx8("h3", { css: styles8.sectionTitle, children: "Thumbnail Sizes" }),
3970
+ /* @__PURE__ */ jsxs8("div", { css: styles8.grid, children: [
3971
+ /* @__PURE__ */ jsxs8("div", { children: [
3972
+ /* @__PURE__ */ jsx8("label", { css: styles8.label, children: "Small" }),
3973
+ /* @__PURE__ */ jsx8("input", { css: styles8.input, type: "number", defaultValue: 300 })
3492
3974
  ] }),
3493
- /* @__PURE__ */ jsxs7("div", { children: [
3494
- /* @__PURE__ */ jsx7("label", { css: styles7.label, children: "Medium" }),
3495
- /* @__PURE__ */ jsx7("input", { css: styles7.input, type: "number", defaultValue: 700 })
3975
+ /* @__PURE__ */ jsxs8("div", { children: [
3976
+ /* @__PURE__ */ jsx8("label", { css: styles8.label, children: "Medium" }),
3977
+ /* @__PURE__ */ jsx8("input", { css: styles8.input, type: "number", defaultValue: 700 })
3496
3978
  ] }),
3497
- /* @__PURE__ */ jsxs7("div", { children: [
3498
- /* @__PURE__ */ jsx7("label", { css: styles7.label, children: "Large" }),
3499
- /* @__PURE__ */ jsx7("input", { css: styles7.input, type: "number", defaultValue: 1400 })
3979
+ /* @__PURE__ */ jsxs8("div", { children: [
3980
+ /* @__PURE__ */ jsx8("label", { css: styles8.label, children: "Large" }),
3981
+ /* @__PURE__ */ jsx8("input", { css: styles8.input, type: "number", defaultValue: 1400 })
3500
3982
  ] })
3501
3983
  ] })
3502
3984
  ] })
3503
3985
  ] }),
3504
- /* @__PURE__ */ jsxs7("div", { css: styles7.footer, children: [
3505
- /* @__PURE__ */ jsx7("button", { css: styles7.cancelBtn, onClick: onClose, children: "Cancel" }),
3506
- /* @__PURE__ */ jsx7("button", { css: styles7.saveBtn, children: "Save Changes" })
3986
+ /* @__PURE__ */ jsxs8("div", { css: styles8.footer, children: [
3987
+ /* @__PURE__ */ jsx8("button", { css: styles8.cancelBtn, onClick: onClose, children: "Cancel" }),
3988
+ /* @__PURE__ */ jsx8("button", { css: styles8.saveBtn, children: "Save Changes" })
3507
3989
  ] })
3508
3990
  ] }) });
3509
3991
  }
3510
3992
 
3993
+ // src/components/ErrorModal.tsx
3994
+ import { css as css9 } from "@emotion/react";
3995
+ import { jsx as jsx9, jsxs as jsxs9 } from "@emotion/react/jsx-runtime";
3996
+ var styles9 = {
3997
+ overlay: css9`
3998
+ position: fixed;
3999
+ inset: 0;
4000
+ background: rgba(0, 0, 0, 0.5);
4001
+ display: flex;
4002
+ align-items: center;
4003
+ justify-content: center;
4004
+ z-index: 1100;
4005
+ `,
4006
+ modal: css9`
4007
+ background: ${colors.surface};
4008
+ border-radius: 12px;
4009
+ padding: 24px;
4010
+ max-width: 400px;
4011
+ width: 90%;
4012
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
4013
+ `,
4014
+ header: css9`
4015
+ display: flex;
4016
+ align-items: center;
4017
+ gap: 12px;
4018
+ margin-bottom: 12px;
4019
+ `,
4020
+ icon: css9`
4021
+ width: 24px;
4022
+ height: 24px;
4023
+ color: ${colors.danger};
4024
+ flex-shrink: 0;
4025
+ `,
4026
+ title: css9`
4027
+ font-size: ${fontSize.lg};
4028
+ font-weight: 600;
4029
+ color: ${colors.text};
4030
+ margin: 0;
4031
+ `,
4032
+ message: css9`
4033
+ font-size: ${fontSize.base};
4034
+ color: ${colors.textSecondary};
4035
+ margin: 0 0 20px 0;
4036
+ line-height: 1.5;
4037
+ `,
4038
+ button: css9`
4039
+ width: 100%;
4040
+ padding: 10px 16px;
4041
+ border-radius: 6px;
4042
+ font-size: ${fontSize.base};
4043
+ font-weight: 500;
4044
+ border: none;
4045
+ background: ${colors.primary};
4046
+ color: white;
4047
+ cursor: pointer;
4048
+ transition: background 0.15s ease;
4049
+
4050
+ &:hover {
4051
+ background: ${colors.primaryHover};
4052
+ }
4053
+ `
4054
+ };
4055
+ function ErrorModal() {
4056
+ const { error, clearError } = useStudio();
4057
+ if (!error) return null;
4058
+ return /* @__PURE__ */ jsx9("div", { css: styles9.overlay, onClick: clearError, children: /* @__PURE__ */ jsxs9("div", { css: styles9.modal, onClick: (e) => e.stopPropagation(), children: [
4059
+ /* @__PURE__ */ jsxs9("div", { css: styles9.header, children: [
4060
+ /* @__PURE__ */ jsx9("svg", { css: styles9.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }),
4061
+ /* @__PURE__ */ jsx9("h3", { css: styles9.title, children: error.title })
4062
+ ] }),
4063
+ /* @__PURE__ */ jsx9("p", { css: styles9.message, children: error.message }),
4064
+ /* @__PURE__ */ jsx9("button", { css: styles9.button, onClick: clearError, children: "OK" })
4065
+ ] }) });
4066
+ }
4067
+
3511
4068
  // src/components/StudioUI.tsx
3512
- import { jsx as jsx8, jsxs as jsxs8 } from "@emotion/react/jsx-runtime";
4069
+ import { jsx as jsx10, jsxs as jsxs10 } from "@emotion/react/jsx-runtime";
3513
4070
  var btnHeight3 = "36px";
3514
- var styles8 = {
3515
- container: css8`
4071
+ var styles10 = {
4072
+ container: css10`
3516
4073
  ${baseReset}
3517
4074
  display: flex;
3518
4075
  flex-direction: column;
3519
4076
  height: 100%;
3520
4077
  background: ${colors.background};
3521
4078
  `,
3522
- header: css8`
4079
+ header: css10`
3523
4080
  display: flex;
3524
4081
  align-items: center;
3525
4082
  justify-content: space-between;
@@ -3528,7 +4085,7 @@ var styles8 = {
3528
4085
  border-bottom: 1px solid ${colors.border};
3529
4086
  position: relative;
3530
4087
  `,
3531
- title: css8`
4088
+ title: css10`
3532
4089
  font-size: ${fontSize.lg};
3533
4090
  font-weight: 600;
3534
4091
  color: ${colors.text};
@@ -3536,14 +4093,14 @@ var styles8 = {
3536
4093
  letter-spacing: -0.02em;
3537
4094
  flex-shrink: 0;
3538
4095
  `,
3539
- headerLeft: css8`
4096
+ headerLeft: css10`
3540
4097
  display: flex;
3541
4098
  align-items: center;
3542
4099
  gap: 12px;
3543
4100
  flex: 1;
3544
4101
  min-width: 0;
3545
4102
  `,
3546
- headerCenter: css8`
4103
+ headerCenter: css10`
3547
4104
  position: absolute;
3548
4105
  left: 50%;
3549
4106
  transform: translateX(-50%);
@@ -3551,7 +4108,7 @@ var styles8 = {
3551
4108
  align-items: center;
3552
4109
  max-width: 50%;
3553
4110
  `,
3554
- breadcrumbs: css8`
4111
+ breadcrumbs: css10`
3555
4112
  display: flex;
3556
4113
  align-items: center;
3557
4114
  gap: 6px;
@@ -3559,11 +4116,11 @@ var styles8 = {
3559
4116
  color: ${colors.textSecondary};
3560
4117
  overflow: hidden;
3561
4118
  `,
3562
- breadcrumbSeparator: css8`
4119
+ breadcrumbSeparator: css10`
3563
4120
  color: ${colors.border};
3564
4121
  flex-shrink: 0;
3565
4122
  `,
3566
- breadcrumbItem: css8`
4123
+ breadcrumbItem: css10`
3567
4124
  color: ${colors.textSecondary};
3568
4125
  text-decoration: none;
3569
4126
  cursor: pointer;
@@ -3574,19 +4131,19 @@ var styles8 = {
3574
4131
  color: ${colors.primary};
3575
4132
  }
3576
4133
  `,
3577
- breadcrumbCurrent: css8`
4134
+ breadcrumbCurrent: css10`
3578
4135
  color: ${colors.text};
3579
4136
  font-weight: 500;
3580
4137
  white-space: nowrap;
3581
4138
  overflow: hidden;
3582
4139
  text-overflow: ellipsis;
3583
4140
  `,
3584
- headerActions: css8`
4141
+ headerActions: css10`
3585
4142
  display: flex;
3586
4143
  align-items: center;
3587
4144
  gap: 8px;
3588
4145
  `,
3589
- headerBtn: css8`
4146
+ headerBtn: css10`
3590
4147
  height: ${btnHeight3};
3591
4148
  padding: 0 12px;
3592
4149
  background: ${colors.surface};
@@ -3603,23 +4160,23 @@ var styles8 = {
3603
4160
  border-color: ${colors.borderHover};
3604
4161
  }
3605
4162
  `,
3606
- headerIcon: css8`
4163
+ headerIcon: css10`
3607
4164
  width: 16px;
3608
4165
  height: 16px;
3609
4166
  color: ${colors.textSecondary};
3610
4167
  `,
3611
- content: css8`
4168
+ content: css10`
3612
4169
  flex: 1;
3613
4170
  display: flex;
3614
4171
  overflow: hidden;
3615
4172
  `,
3616
- fileBrowser: css8`
4173
+ fileBrowser: css10`
3617
4174
  flex: 1;
3618
4175
  min-width: 0;
3619
4176
  overflow: auto;
3620
4177
  padding: 20px 24px;
3621
4178
  `,
3622
- dropOverlay: css8`
4179
+ dropOverlay: css10`
3623
4180
  position: absolute;
3624
4181
  top: 0;
3625
4182
  left: 0;
@@ -3634,7 +4191,7 @@ var styles8 = {
3634
4191
  z-index: 50;
3635
4192
  pointer-events: none;
3636
4193
  `,
3637
- dropMessage: css8`
4194
+ dropMessage: css10`
3638
4195
  display: flex;
3639
4196
  flex-direction: column;
3640
4197
  align-items: center;
@@ -3643,36 +4200,43 @@ var styles8 = {
3643
4200
  font-size: ${fontSize.lg};
3644
4201
  font-weight: 600;
3645
4202
  `,
3646
- dropIcon: css8`
4203
+ dropIcon: css10`
3647
4204
  width: 48px;
3648
4205
  height: 48px;
3649
4206
  `
3650
4207
  };
3651
4208
  function StudioUI({ onClose, isVisible = true }) {
3652
- const [currentPath, setCurrentPathInternal] = useState7("public");
3653
- const [selectedItems, setSelectedItems] = useState7(/* @__PURE__ */ new Set());
3654
- const [lastSelectedPath, setLastSelectedPath] = useState7(null);
3655
- const [viewMode, setViewMode] = useState7("grid");
3656
- const [focusedItem, setFocusedItem] = useState7(null);
3657
- const [meta, setMeta] = useState7(null);
3658
- const [isLoading, setIsLoading] = useState7(false);
3659
- const [refreshKey, setRefreshKey] = useState7(0);
3660
- const [searchQuery, setSearchQuery] = useState7("");
3661
- const [isDragging, setIsDragging] = useState7(false);
3662
- const triggerRefresh = useCallback2(() => {
4209
+ const [currentPath, setCurrentPathInternal] = useState8("public");
4210
+ const [selectedItems, setSelectedItems] = useState8(/* @__PURE__ */ new Set());
4211
+ const [lastSelectedPath, setLastSelectedPath] = useState8(null);
4212
+ const [viewMode, setViewMode] = useState8("grid");
4213
+ const [focusedItem, setFocusedItem] = useState8(null);
4214
+ const [meta, setMeta] = useState8(null);
4215
+ const [isLoading, setIsLoading] = useState8(false);
4216
+ const [refreshKey, setRefreshKey] = useState8(0);
4217
+ const [searchQuery, setSearchQuery] = useState8("");
4218
+ const [error, setError] = useState8(null);
4219
+ const [isDragging, setIsDragging] = useState8(false);
4220
+ const triggerRefresh = useCallback3(() => {
3663
4221
  setRefreshKey((k) => k + 1);
3664
4222
  }, []);
3665
- const handleDragOver = useCallback2((e) => {
4223
+ const showError = useCallback3((title, message) => {
4224
+ setError({ title, message });
4225
+ }, []);
4226
+ const clearError = useCallback3(() => {
4227
+ setError(null);
4228
+ }, []);
4229
+ const handleDragOver = useCallback3((e) => {
3666
4230
  e.preventDefault();
3667
4231
  e.stopPropagation();
3668
4232
  setIsDragging(true);
3669
4233
  }, []);
3670
- const handleDragLeave = useCallback2((e) => {
4234
+ const handleDragLeave = useCallback3((e) => {
3671
4235
  e.preventDefault();
3672
4236
  e.stopPropagation();
3673
4237
  setIsDragging(false);
3674
4238
  }, []);
3675
- const handleDrop = useCallback2(async (e) => {
4239
+ const handleDrop = useCallback3(async (e) => {
3676
4240
  e.preventDefault();
3677
4241
  e.stopPropagation();
3678
4242
  setIsDragging(false);
@@ -3690,25 +4254,25 @@ function StudioUI({ onClose, isVisible = true }) {
3690
4254
  method: "POST",
3691
4255
  body: formData
3692
4256
  });
3693
- } catch (error) {
3694
- console.error("Upload error:", error);
4257
+ } catch (error2) {
4258
+ console.error("Upload error:", error2);
3695
4259
  }
3696
4260
  }
3697
4261
  triggerRefresh();
3698
4262
  }, [currentPath, triggerRefresh]);
3699
- const navigateUp = useCallback2(() => {
4263
+ const navigateUp = useCallback3(() => {
3700
4264
  if (currentPath === "public") return;
3701
4265
  const parts = currentPath.split("/");
3702
4266
  parts.pop();
3703
4267
  setCurrentPathInternal(parts.join("/") || "public");
3704
4268
  setSelectedItems(/* @__PURE__ */ new Set());
3705
4269
  }, [currentPath]);
3706
- const setCurrentPath = useCallback2((path) => {
4270
+ const setCurrentPath = useCallback3((path) => {
3707
4271
  setCurrentPathInternal(path);
3708
4272
  setSelectedItems(/* @__PURE__ */ new Set());
3709
4273
  setFocusedItem(null);
3710
4274
  }, []);
3711
- const toggleSelection = useCallback2((path) => {
4275
+ const toggleSelection = useCallback3((path) => {
3712
4276
  setSelectedItems((prev) => {
3713
4277
  const next = new Set(prev);
3714
4278
  if (next.has(path)) {
@@ -3720,7 +4284,7 @@ function StudioUI({ onClose, isVisible = true }) {
3720
4284
  });
3721
4285
  setLastSelectedPath(path);
3722
4286
  }, []);
3723
- const selectRange = useCallback2((fromPath, toPath, allItems) => {
4287
+ const selectRange = useCallback3((fromPath, toPath, allItems) => {
3724
4288
  const fromIndex = allItems.findIndex((item) => item.path === fromPath);
3725
4289
  const toIndex = allItems.findIndex((item) => item.path === toPath);
3726
4290
  if (fromIndex === -1 || toIndex === -1) return;
@@ -3735,13 +4299,13 @@ function StudioUI({ onClose, isVisible = true }) {
3735
4299
  });
3736
4300
  setLastSelectedPath(toPath);
3737
4301
  }, []);
3738
- const selectAll = useCallback2((items) => {
4302
+ const selectAll = useCallback3((items) => {
3739
4303
  setSelectedItems(new Set(items.map((item) => item.path)));
3740
4304
  }, []);
3741
- const clearSelection = useCallback2(() => {
4305
+ const clearSelection = useCallback3(() => {
3742
4306
  setSelectedItems(/* @__PURE__ */ new Set());
3743
4307
  }, []);
3744
- const handleKeyDown = useCallback2(
4308
+ const handleKeyDown = useCallback3(
3745
4309
  (e) => {
3746
4310
  if (e.key === "Escape") {
3747
4311
  const target = e.target;
@@ -3757,7 +4321,7 @@ function StudioUI({ onClose, isVisible = true }) {
3757
4321
  },
3758
4322
  [onClose, focusedItem]
3759
4323
  );
3760
- useEffect4(() => {
4324
+ useEffect3(() => {
3761
4325
  if (isVisible) {
3762
4326
  document.addEventListener("keydown", handleKeyDown);
3763
4327
  document.body.style.overflow = "hidden";
@@ -3793,43 +4357,47 @@ function StudioUI({ onClose, isVisible = true }) {
3793
4357
  refreshKey,
3794
4358
  triggerRefresh,
3795
4359
  searchQuery,
3796
- setSearchQuery
4360
+ setSearchQuery,
4361
+ error,
4362
+ showError,
4363
+ clearError
3797
4364
  };
3798
- return /* @__PURE__ */ jsx8(StudioContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs8("div", { css: styles8.container, children: [
3799
- /* @__PURE__ */ jsxs8("div", { css: styles8.header, children: [
3800
- /* @__PURE__ */ jsx8("div", { css: styles8.headerLeft, children: /* @__PURE__ */ jsx8("h1", { css: styles8.title, children: "Studio" }) }),
3801
- /* @__PURE__ */ jsx8("div", { css: styles8.headerCenter, children: /* @__PURE__ */ jsx8(Breadcrumbs, { currentPath, onNavigate: setCurrentPath }) }),
3802
- /* @__PURE__ */ jsxs8("div", { css: styles8.headerActions, children: [
3803
- /* @__PURE__ */ jsx8(StudioSettings, {}),
3804
- /* @__PURE__ */ jsx8(
4365
+ return /* @__PURE__ */ jsx10(StudioContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs10("div", { css: styles10.container, children: [
4366
+ /* @__PURE__ */ jsxs10("div", { css: styles10.header, children: [
4367
+ /* @__PURE__ */ jsx10("div", { css: styles10.headerLeft, children: /* @__PURE__ */ jsx10("h1", { css: styles10.title, children: "Studio" }) }),
4368
+ /* @__PURE__ */ jsx10("div", { css: styles10.headerCenter, children: /* @__PURE__ */ jsx10(Breadcrumbs, { currentPath, onNavigate: setCurrentPath }) }),
4369
+ /* @__PURE__ */ jsxs10("div", { css: styles10.headerActions, children: [
4370
+ /* @__PURE__ */ jsx10(StudioSettings, {}),
4371
+ /* @__PURE__ */ jsx10(
3805
4372
  "button",
3806
4373
  {
3807
- css: styles8.headerBtn,
4374
+ css: styles10.headerBtn,
3808
4375
  onClick: onClose,
3809
4376
  "aria-label": "Close Studio",
3810
- children: /* @__PURE__ */ jsx8(CloseIcon, {})
4377
+ children: /* @__PURE__ */ jsx10(CloseIcon, {})
3811
4378
  }
3812
4379
  )
3813
4380
  ] })
3814
4381
  ] }),
3815
- /* @__PURE__ */ jsx8(StudioToolbar, {}),
3816
- /* @__PURE__ */ jsxs8(
4382
+ /* @__PURE__ */ jsx10(StudioToolbar, {}),
4383
+ /* @__PURE__ */ jsxs10(
3817
4384
  "div",
3818
4385
  {
3819
- css: styles8.content,
4386
+ css: styles10.content,
3820
4387
  onDragOver: handleDragOver,
3821
4388
  onDragLeave: handleDragLeave,
3822
4389
  onDrop: handleDrop,
3823
4390
  children: [
3824
- isDragging && /* @__PURE__ */ jsx8("div", { css: styles8.dropOverlay, children: /* @__PURE__ */ jsxs8("div", { css: styles8.dropMessage, children: [
3825
- /* @__PURE__ */ jsx8("svg", { css: styles8.dropIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx8("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" }) }),
3826
- /* @__PURE__ */ jsx8("span", { children: "Drop files to upload" })
4391
+ isDragging && /* @__PURE__ */ jsx10("div", { css: styles10.dropOverlay, children: /* @__PURE__ */ jsxs10("div", { css: styles10.dropMessage, children: [
4392
+ /* @__PURE__ */ jsx10("svg", { css: styles10.dropIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx10("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" }) }),
4393
+ /* @__PURE__ */ jsx10("span", { children: "Drop files to upload" })
3827
4394
  ] }) }),
3828
- /* @__PURE__ */ jsx8("div", { css: styles8.fileBrowser, children: viewMode === "grid" ? /* @__PURE__ */ jsx8(StudioFileGrid, {}) : /* @__PURE__ */ jsx8(StudioFileList, {}) })
4395
+ /* @__PURE__ */ jsx10("div", { css: styles10.fileBrowser, children: viewMode === "grid" ? /* @__PURE__ */ jsx10(StudioFileGrid, {}) : /* @__PURE__ */ jsx10(StudioFileList, {}) })
3829
4396
  ]
3830
4397
  }
3831
4398
  ),
3832
- focusedItem && /* @__PURE__ */ jsx8(StudioDetailView, {})
4399
+ focusedItem && /* @__PURE__ */ jsx10(StudioDetailView, {}),
4400
+ /* @__PURE__ */ jsx10(ErrorModal, {})
3833
4401
  ] }) });
3834
4402
  }
3835
4403
  function Breadcrumbs({ currentPath, onNavigate }) {
@@ -3838,12 +4406,12 @@ function Breadcrumbs({ currentPath, onNavigate }) {
3838
4406
  name: part,
3839
4407
  path: parts.slice(0, index + 1).join("/")
3840
4408
  }));
3841
- return /* @__PURE__ */ jsx8("div", { css: styles8.breadcrumbs, children: breadcrumbs.map((crumb, index) => /* @__PURE__ */ jsxs8("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
3842
- index > 0 && /* @__PURE__ */ jsx8("span", { css: styles8.breadcrumbSeparator, children: "/" }),
3843
- index === breadcrumbs.length - 1 ? /* @__PURE__ */ jsx8("span", { css: styles8.breadcrumbCurrent, children: crumb.name }) : /* @__PURE__ */ jsx8(
4409
+ return /* @__PURE__ */ jsx10("div", { css: styles10.breadcrumbs, children: breadcrumbs.map((crumb, index) => /* @__PURE__ */ jsxs10("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
4410
+ index > 0 && /* @__PURE__ */ jsx10("span", { css: styles10.breadcrumbSeparator, children: "/" }),
4411
+ index === breadcrumbs.length - 1 ? /* @__PURE__ */ jsx10("span", { css: styles10.breadcrumbCurrent, children: crumb.name }) : /* @__PURE__ */ jsx10(
3844
4412
  "span",
3845
4413
  {
3846
- css: styles8.breadcrumbItem,
4414
+ css: styles10.breadcrumbItem,
3847
4415
  onClick: () => onNavigate(crumb.path),
3848
4416
  children: crumb.name
3849
4417
  }
@@ -3851,10 +4419,10 @@ function Breadcrumbs({ currentPath, onNavigate }) {
3851
4419
  ] }, crumb.path)) });
3852
4420
  }
3853
4421
  function CloseIcon() {
3854
- return /* @__PURE__ */ jsxs8(
4422
+ return /* @__PURE__ */ jsxs10(
3855
4423
  "svg",
3856
4424
  {
3857
- css: styles8.headerIcon,
4425
+ css: styles10.headerIcon,
3858
4426
  xmlns: "http://www.w3.org/2000/svg",
3859
4427
  viewBox: "0 0 24 24",
3860
4428
  fill: "none",
@@ -3863,8 +4431,8 @@ function CloseIcon() {
3863
4431
  strokeLinecap: "round",
3864
4432
  strokeLinejoin: "round",
3865
4433
  children: [
3866
- /* @__PURE__ */ jsx8("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
3867
- /* @__PURE__ */ jsx8("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
4434
+ /* @__PURE__ */ jsx10("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
4435
+ /* @__PURE__ */ jsx10("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
3868
4436
  ]
3869
4437
  }
3870
4438
  );
@@ -3874,4 +4442,4 @@ export {
3874
4442
  StudioUI,
3875
4443
  StudioUI_default as default
3876
4444
  };
3877
- //# sourceMappingURL=StudioUI-SS3YMS53.mjs.map
4445
+ //# sourceMappingURL=StudioUI-XV7HCEAF.mjs.map