@gallop.software/studio 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{StudioUI-YYWQ6CCE.mjs → StudioUI-QWSXK4R6.mjs} +40 -17
- package/dist/StudioUI-QWSXK4R6.mjs.map +1 -0
- package/dist/{StudioUI-KVKV7KNF.js → StudioUI-U6GFAPCY.js} +37 -14
- package/dist/StudioUI-U6GFAPCY.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/StudioUI-KVKV7KNF.js.map +0 -1
- package/dist/StudioUI-YYWQ6CCE.mjs.map +0 -1
|
@@ -49,7 +49,7 @@ function useStudio() {
|
|
|
49
49
|
|
|
50
50
|
// src/components/StudioToolbar.tsx
|
|
51
51
|
import { useCallback, useRef, useState } from "react";
|
|
52
|
-
import { css as css2 } from "@emotion/react";
|
|
52
|
+
import { css as css2, keyframes as keyframes2 } from "@emotion/react";
|
|
53
53
|
|
|
54
54
|
// src/components/StudioModal.tsx
|
|
55
55
|
import { css, keyframes } from "@emotion/react";
|
|
@@ -190,6 +190,9 @@ function AlertModal({
|
|
|
190
190
|
|
|
191
191
|
// src/components/StudioToolbar.tsx
|
|
192
192
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "@emotion/react/jsx-runtime";
|
|
193
|
+
var spin = keyframes2`
|
|
194
|
+
to { transform: rotate(360deg); }
|
|
195
|
+
`;
|
|
193
196
|
var styles2 = {
|
|
194
197
|
toolbar: css2`
|
|
195
198
|
display: flex;
|
|
@@ -245,6 +248,9 @@ var styles2 = {
|
|
|
245
248
|
width: 16px;
|
|
246
249
|
height: 16px;
|
|
247
250
|
`,
|
|
251
|
+
iconSpin: css2`
|
|
252
|
+
animation: ${spin} 1s linear infinite;
|
|
253
|
+
`,
|
|
248
254
|
selectionCount: css2`
|
|
249
255
|
font-size: 14px;
|
|
250
256
|
color: #4b5563;
|
|
@@ -290,11 +296,18 @@ function StudioToolbar() {
|
|
|
290
296
|
const { selectedItems, viewMode, setViewMode, clearSelection, currentPath, triggerRefresh } = useStudio();
|
|
291
297
|
const fileInputRef = useRef(null);
|
|
292
298
|
const [uploading, setUploading] = useState(false);
|
|
299
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
293
300
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
|
294
301
|
const [alertMessage, setAlertMessage] = useState(null);
|
|
302
|
+
const isInImagesFolder = currentPath === "public/images" || currentPath.startsWith("public/images/");
|
|
295
303
|
const handleUpload = useCallback(() => {
|
|
296
304
|
fileInputRef.current?.click();
|
|
297
305
|
}, []);
|
|
306
|
+
const handleRefresh = useCallback(() => {
|
|
307
|
+
setRefreshing(true);
|
|
308
|
+
triggerRefresh();
|
|
309
|
+
setTimeout(() => setRefreshing(false), 600);
|
|
310
|
+
}, [triggerRefresh]);
|
|
298
311
|
const handleFileChange = useCallback(async (e) => {
|
|
299
312
|
const files = e.target.files;
|
|
300
313
|
if (!files || files.length === 0) return;
|
|
@@ -417,7 +430,7 @@ function StudioToolbar() {
|
|
|
417
430
|
onClick: handleUpload,
|
|
418
431
|
icon: "upload",
|
|
419
432
|
label: uploading ? "Uploading..." : "Upload",
|
|
420
|
-
disabled: uploading
|
|
433
|
+
disabled: uploading || isInImagesFolder
|
|
421
434
|
}
|
|
422
435
|
),
|
|
423
436
|
/* @__PURE__ */ jsx2(
|
|
@@ -459,9 +472,10 @@ function StudioToolbar() {
|
|
|
459
472
|
/* @__PURE__ */ jsx2(
|
|
460
473
|
ToolbarButton,
|
|
461
474
|
{
|
|
462
|
-
onClick:
|
|
475
|
+
onClick: handleRefresh,
|
|
463
476
|
icon: "reload",
|
|
464
|
-
label: "Refresh"
|
|
477
|
+
label: "Refresh",
|
|
478
|
+
spinning: refreshing
|
|
465
479
|
}
|
|
466
480
|
),
|
|
467
481
|
/* @__PURE__ */ jsxs2("div", { css: styles2.viewToggle, children: [
|
|
@@ -493,7 +507,8 @@ function ToolbarButton({
|
|
|
493
507
|
icon,
|
|
494
508
|
label,
|
|
495
509
|
disabled,
|
|
496
|
-
variant = "default"
|
|
510
|
+
variant = "default",
|
|
511
|
+
spinning
|
|
497
512
|
}) {
|
|
498
513
|
return /* @__PURE__ */ jsxs2(
|
|
499
514
|
"button",
|
|
@@ -502,13 +517,13 @@ function ToolbarButton({
|
|
|
502
517
|
onClick,
|
|
503
518
|
disabled,
|
|
504
519
|
children: [
|
|
505
|
-
/* @__PURE__ */ jsx2(IconComponent, { icon }),
|
|
520
|
+
/* @__PURE__ */ jsx2(IconComponent, { icon, spinning }),
|
|
506
521
|
label
|
|
507
522
|
]
|
|
508
523
|
}
|
|
509
524
|
);
|
|
510
525
|
}
|
|
511
|
-
function IconComponent({ icon }) {
|
|
526
|
+
function IconComponent({ icon, spinning }) {
|
|
512
527
|
switch (icon) {
|
|
513
528
|
case "upload":
|
|
514
529
|
return /* @__PURE__ */ jsx2("svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" }) });
|
|
@@ -521,7 +536,7 @@ function IconComponent({ icon }) {
|
|
|
521
536
|
case "scan":
|
|
522
537
|
return /* @__PURE__ */ jsx2("svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) });
|
|
523
538
|
case "reload":
|
|
524
|
-
return /* @__PURE__ */ jsx2("svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 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" }) });
|
|
539
|
+
return /* @__PURE__ */ jsx2("svg", { css: [styles2.icon, spinning && styles2.iconSpin], fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("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" }) });
|
|
525
540
|
default:
|
|
526
541
|
return null;
|
|
527
542
|
}
|
|
@@ -625,9 +640,9 @@ function StudioBreadcrumb() {
|
|
|
625
640
|
|
|
626
641
|
// src/components/StudioFileGrid.tsx
|
|
627
642
|
import { useEffect, useState as useState2 } from "react";
|
|
628
|
-
import { css as css4, keyframes as
|
|
643
|
+
import { css as css4, keyframes as keyframes3 } from "@emotion/react";
|
|
629
644
|
import { jsx as jsx4, jsxs as jsxs4 } from "@emotion/react/jsx-runtime";
|
|
630
|
-
var
|
|
645
|
+
var spin2 = keyframes3`
|
|
631
646
|
to { transform: rotate(360deg); }
|
|
632
647
|
`;
|
|
633
648
|
var styles4 = {
|
|
@@ -643,7 +658,7 @@ var styles4 = {
|
|
|
643
658
|
border-radius: 50%;
|
|
644
659
|
border: 2px solid transparent;
|
|
645
660
|
border-bottom-color: #9333ea;
|
|
646
|
-
animation: ${
|
|
661
|
+
animation: ${spin2} 1s linear infinite;
|
|
647
662
|
`,
|
|
648
663
|
empty: css4`
|
|
649
664
|
display: flex;
|
|
@@ -680,6 +695,7 @@ var styles4 = {
|
|
|
680
695
|
cursor: pointer;
|
|
681
696
|
transition: all 0.15s;
|
|
682
697
|
background-color: #f9fafb;
|
|
698
|
+
user-select: none;
|
|
683
699
|
|
|
684
700
|
&:hover {
|
|
685
701
|
border-color: #e5e7eb;
|
|
@@ -870,7 +886,10 @@ function GridItem({ item, isSelected, onClick }) {
|
|
|
870
886
|
checked: isSelected,
|
|
871
887
|
onChange: () => {
|
|
872
888
|
},
|
|
873
|
-
onClick: (e) =>
|
|
889
|
+
onClick: (e) => {
|
|
890
|
+
e.stopPropagation();
|
|
891
|
+
onClick(e);
|
|
892
|
+
}
|
|
874
893
|
}
|
|
875
894
|
),
|
|
876
895
|
item.cdnSynced && /* @__PURE__ */ jsx4("span", { css: styles4.cdnBadge, children: "CDN" }),
|
|
@@ -897,9 +916,9 @@ function formatFileSize(bytes) {
|
|
|
897
916
|
|
|
898
917
|
// src/components/StudioFileList.tsx
|
|
899
918
|
import { useEffect as useEffect2, useState as useState3 } from "react";
|
|
900
|
-
import { css as css5, keyframes as
|
|
919
|
+
import { css as css5, keyframes as keyframes4 } from "@emotion/react";
|
|
901
920
|
import { jsx as jsx5, jsxs as jsxs5 } from "@emotion/react/jsx-runtime";
|
|
902
|
-
var
|
|
921
|
+
var spin3 = keyframes4`
|
|
903
922
|
to { transform: rotate(360deg); }
|
|
904
923
|
`;
|
|
905
924
|
var styles5 = {
|
|
@@ -915,7 +934,7 @@ var styles5 = {
|
|
|
915
934
|
border-radius: 50%;
|
|
916
935
|
border: 2px solid transparent;
|
|
917
936
|
border-bottom-color: #9333ea;
|
|
918
|
-
animation: ${
|
|
937
|
+
animation: ${spin3} 1s linear infinite;
|
|
919
938
|
`,
|
|
920
939
|
empty: css5`
|
|
921
940
|
display: flex;
|
|
@@ -956,6 +975,7 @@ var styles5 = {
|
|
|
956
975
|
row: css5`
|
|
957
976
|
cursor: pointer;
|
|
958
977
|
transition: background-color 0.15s;
|
|
978
|
+
user-select: none;
|
|
959
979
|
|
|
960
980
|
&:hover {
|
|
961
981
|
background-color: #f9fafb;
|
|
@@ -1109,7 +1129,10 @@ function ListRow({ item, isSelected, onClick }) {
|
|
|
1109
1129
|
checked: isSelected,
|
|
1110
1130
|
onChange: () => {
|
|
1111
1131
|
},
|
|
1112
|
-
onClick: (e) =>
|
|
1132
|
+
onClick: (e) => {
|
|
1133
|
+
e.stopPropagation();
|
|
1134
|
+
onClick(e);
|
|
1135
|
+
}
|
|
1113
1136
|
}
|
|
1114
1137
|
) }),
|
|
1115
1138
|
/* @__PURE__ */ jsx5("td", { css: styles5.td, children: /* @__PURE__ */ jsxs5("div", { css: styles5.nameCell, children: [
|
|
@@ -1861,4 +1884,4 @@ export {
|
|
|
1861
1884
|
StudioUI,
|
|
1862
1885
|
StudioUI_default as default
|
|
1863
1886
|
};
|
|
1864
|
-
//# sourceMappingURL=StudioUI-
|
|
1887
|
+
//# sourceMappingURL=StudioUI-QWSXK4R6.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/StudioUI.tsx","../src/components/StudioContext.tsx","../src/components/StudioToolbar.tsx","../src/components/StudioModal.tsx","../src/components/StudioBreadcrumb.tsx","../src/components/StudioFileGrid.tsx","../src/components/StudioFileList.tsx","../src/components/StudioPreview.tsx","../src/components/StudioSettings.tsx"],"sourcesContent":["/** @jsxImportSource @emotion/react */\n'use client'\n\nimport { useEffect, useCallback, useState } from 'react'\nimport { css } from '@emotion/react'\nimport { StudioContext } from './StudioContext'\nimport { StudioToolbar } from './StudioToolbar'\nimport { StudioBreadcrumb } from './StudioBreadcrumb'\nimport { StudioFileGrid } from './StudioFileGrid'\nimport { StudioFileList } from './StudioFileList'\nimport { StudioPreview } from './StudioPreview'\nimport { StudioSettings } from './StudioSettings'\nimport type { FileItem, StudioMeta } from '../types'\n\ninterface StudioUIProps {\n onClose: () => void\n}\n\nconst styles = {\n container: css`\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n `,\n header: css`\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 24px;\n border-bottom: 1px solid #e5e7eb;\n `,\n title: css`\n font-size: 20px;\n font-weight: 600;\n color: #111827;\n margin: 0;\n `,\n headerActions: css`\n display: flex;\n align-items: center;\n gap: 8px;\n `,\n closeBtn: css`\n padding: 8px;\n background: none;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: background-color 0.15s;\n \n &:hover {\n background-color: #f3f4f6;\n }\n `,\n closeIcon: css`\n width: 20px;\n height: 20px;\n color: #6b7280;\n `,\n content: css`\n flex: 1;\n display: flex;\n overflow: hidden;\n `,\n fileBrowser: css`\n flex: 1;\n min-width: 0;\n overflow: auto;\n padding: 16px;\n `,\n}\n\n/**\n * Main Studio UI - contains all panels and manages internal state\n * Rendered inside the modal via lazy loading\n */\nexport function StudioUI({ onClose }: StudioUIProps) {\n const [currentPath, setCurrentPathInternal] = useState('public')\n const [selectedItems, setSelectedItems] = useState<Set<string>>(new Set())\n const [lastSelectedPath, setLastSelectedPath] = useState<string | null>(null)\n const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')\n const [meta, setMeta] = useState<StudioMeta | null>(null)\n const [isLoading, setIsLoading] = useState(false)\n const [refreshKey, setRefreshKey] = useState(0)\n\n const triggerRefresh = useCallback(() => {\n setRefreshKey((k) => k + 1)\n }, [])\n\n const navigateUp = useCallback(() => {\n if (currentPath === 'public') return\n const parts = currentPath.split('/')\n parts.pop()\n setCurrentPathInternal(parts.join('/') || 'public')\n setSelectedItems(new Set())\n }, [currentPath])\n\n const setCurrentPath = useCallback((path: string) => {\n setCurrentPathInternal(path)\n setSelectedItems(new Set())\n }, [])\n\n const toggleSelection = useCallback((path: string) => {\n setSelectedItems((prev) => {\n const next = new Set(prev)\n if (next.has(path)) {\n next.delete(path)\n } else {\n next.add(path)\n }\n return next\n })\n setLastSelectedPath(path)\n }, [])\n\n const selectRange = useCallback((fromPath: string, toPath: string, allItems: FileItem[]) => {\n // Get only files (not folders)\n const files = allItems.filter(item => item.type !== 'folder')\n const fromIndex = files.findIndex(item => item.path === fromPath)\n const toIndex = files.findIndex(item => item.path === toPath)\n \n if (fromIndex === -1 || toIndex === -1) return\n \n const start = Math.min(fromIndex, toIndex)\n const end = Math.max(fromIndex, toIndex)\n \n setSelectedItems((prev) => {\n const next = new Set(prev)\n for (let i = start; i <= end; i++) {\n next.add(files[i].path)\n }\n return next\n })\n setLastSelectedPath(toPath)\n }, [])\n\n const selectAll = useCallback((items: FileItem[]) => {\n setSelectedItems(new Set(items.map((item) => item.path)))\n }, [])\n\n const clearSelection = useCallback(() => {\n setSelectedItems(new Set())\n }, [])\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose()\n }\n },\n [onClose]\n )\n\n useEffect(() => {\n document.addEventListener('keydown', handleKeyDown)\n document.body.style.overflow = 'hidden'\n return () => {\n document.removeEventListener('keydown', handleKeyDown)\n document.body.style.overflow = ''\n }\n }, [handleKeyDown])\n\n const contextValue = {\n isOpen: true,\n openStudio: () => {},\n closeStudio: onClose,\n toggleStudio: onClose,\n currentPath,\n setCurrentPath,\n navigateUp,\n selectedItems,\n toggleSelection,\n selectRange,\n selectAll,\n clearSelection,\n lastSelectedPath,\n viewMode,\n setViewMode,\n meta,\n setMeta,\n isLoading,\n setIsLoading,\n refreshKey,\n triggerRefresh,\n }\n\n return (\n <StudioContext.Provider value={contextValue}>\n <div css={styles.container}>\n <div css={styles.header}>\n <h1 css={styles.title}>Studio</h1>\n <div css={styles.headerActions}>\n <StudioSettings />\n <button\n css={styles.closeBtn}\n onClick={onClose}\n aria-label=\"Close Studio\"\n >\n <CloseIcon />\n </button>\n </div>\n </div>\n\n <StudioToolbar />\n <StudioBreadcrumb />\n\n <div css={styles.content}>\n <div css={styles.fileBrowser}>\n {viewMode === 'grid' ? <StudioFileGrid /> : <StudioFileList />}\n </div>\n <StudioPreview />\n </div>\n </div>\n </StudioContext.Provider>\n )\n}\n\nfunction CloseIcon() {\n return (\n <svg\n css={styles.closeIcon}\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n )\n}\n\nexport default StudioUI\n","'use client'\n\nimport { createContext, useContext } from 'react'\nimport type { FileItem, StudioMeta } from '../types'\n\n/**\n * Studio state interface\n * State is managed by StudioUI and provided to all child components\n */\nexport interface StudioState {\n isOpen: boolean\n openStudio: () => void\n closeStudio: () => void\n toggleStudio: () => void\n\n // Navigation\n currentPath: string\n setCurrentPath: (path: string) => void\n navigateUp: () => void\n\n // Selection\n selectedItems: Set<string>\n toggleSelection: (path: string) => void\n selectRange: (fromPath: string, toPath: string, allItems: FileItem[]) => void\n selectAll: (items: FileItem[]) => void\n clearSelection: () => void\n lastSelectedPath: string | null\n\n // View\n viewMode: 'grid' | 'list'\n setViewMode: (mode: 'grid' | 'list') => void\n\n // Meta\n meta: StudioMeta | null\n setMeta: (meta: StudioMeta) => void\n\n // Loading\n isLoading: boolean\n setIsLoading: (loading: boolean) => void\n\n // Refresh trigger\n refreshKey: number\n triggerRefresh: () => void\n}\n\nconst defaultState: StudioState = {\n isOpen: false,\n openStudio: () => {},\n closeStudio: () => {},\n toggleStudio: () => {},\n currentPath: 'public',\n setCurrentPath: () => {},\n navigateUp: () => {},\n selectedItems: new Set(),\n toggleSelection: () => {},\n selectRange: () => {},\n selectAll: () => {},\n clearSelection: () => {},\n lastSelectedPath: null,\n viewMode: 'grid',\n setViewMode: () => {},\n meta: null,\n setMeta: () => {},\n isLoading: false,\n setIsLoading: () => {},\n refreshKey: 0,\n triggerRefresh: () => {},\n}\n\nexport const StudioContext = createContext<StudioState>(defaultState)\n\n/**\n * Hook to access Studio state from child components\n */\nexport function useStudio() {\n return useContext(StudioContext)\n}\n","/** @jsxImportSource @emotion/react */\n'use client'\n\nimport { useCallback, useRef, useState } from 'react'\nimport { css, keyframes } from '@emotion/react'\nimport { useStudio } from './StudioContext'\nimport { ConfirmModal, AlertModal } from './StudioModal'\n\nconst spin = keyframes`\n to { transform: rotate(360deg); }\n`\n\nconst styles = {\n toolbar: css`\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 24px;\n background-color: #f9fafb;\n border-bottom: 1px solid #e5e7eb;\n `,\n left: css`\n display: flex;\n align-items: center;\n gap: 8px;\n `,\n right: css`\n display: flex;\n align-items: center;\n gap: 16px;\n `,\n btn: css`\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n background: none;\n border: none;\n cursor: pointer;\n transition: background-color 0.15s;\n \n &:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n }\n `,\n btnDefault: css`\n color: #374151;\n \n &:hover:not(:disabled) {\n background-color: white;\n }\n `,\n btnDanger: css`\n color: #dc2626;\n \n &:hover:not(:disabled) {\n background-color: #fef2f2;\n }\n `,\n icon: css`\n width: 16px;\n height: 16px;\n `,\n iconSpin: css`\n animation: ${spin} 1s linear infinite;\n `,\n selectionCount: css`\n font-size: 14px;\n color: #4b5563;\n `,\n clearBtn: css`\n margin-left: 8px;\n color: #9333ea;\n background: none;\n border: none;\n cursor: pointer;\n font-size: 14px;\n \n &:hover {\n text-decoration: underline;\n }\n `,\n viewToggle: css`\n display: flex;\n align-items: center;\n background-color: white;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n overflow: hidden;\n `,\n viewBtn: css`\n padding: 8px;\n background: none;\n border: none;\n cursor: pointer;\n color: #6b7280;\n transition: all 0.15s;\n \n &:hover {\n background-color: #f9fafb;\n }\n `,\n viewBtnActive: css`\n background-color: #f3e8ff;\n color: #7c3aed;\n `,\n}\n\nexport function StudioToolbar() {\n const { selectedItems, viewMode, setViewMode, clearSelection, currentPath, triggerRefresh } = useStudio()\n const fileInputRef = useRef<HTMLInputElement>(null)\n const [uploading, setUploading] = useState(false)\n const [refreshing, setRefreshing] = useState(false)\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)\n const [alertMessage, setAlertMessage] = useState<{ title: string; message: string } | null>(null)\n\n // Check if we're in the images folder (uploads not allowed there)\n const isInImagesFolder = currentPath === 'public/images' || currentPath.startsWith('public/images/')\n\n const handleUpload = useCallback(() => {\n fileInputRef.current?.click()\n }, [])\n\n const handleRefresh = useCallback(() => {\n setRefreshing(true)\n triggerRefresh()\n // Stop spinning after a short delay (the actual refresh is instant)\n setTimeout(() => setRefreshing(false), 600)\n }, [triggerRefresh])\n\n const handleFileChange = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = e.target.files\n if (!files || files.length === 0) return\n\n setUploading(true)\n try {\n for (const file of Array.from(files)) {\n const formData = new FormData()\n formData.append('file', file)\n formData.append('path', currentPath)\n\n const response = await fetch('/api/studio/upload', {\n method: 'POST',\n body: formData,\n })\n\n if (!response.ok) {\n const error = await response.json()\n // Only log server errors (500s), not validation messages (400s)\n if (response.status >= 500) {\n console.error('Upload error:', error)\n setAlertMessage({\n title: 'Upload Failed',\n message: `Failed to upload ${file.name}: ${error.error || 'Unknown error'}`,\n })\n } else {\n // Validation message - not an error, just guidance\n setAlertMessage({\n title: 'Cannot Upload Here',\n message: error.error || 'Upload not allowed in this location.',\n })\n }\n }\n }\n triggerRefresh()\n } catch (error) {\n console.error('Upload error:', error)\n setAlertMessage({\n title: 'Upload Failed',\n message: 'Upload failed. Check console for details.',\n })\n } finally {\n setUploading(false)\n if (fileInputRef.current) {\n fileInputRef.current.value = ''\n }\n }\n }, [currentPath, triggerRefresh])\n\n const handleReprocess = useCallback(() => {\n console.log('Reprocess clicked', selectedItems)\n }, [selectedItems])\n\n const handleDeleteClick = useCallback(() => {\n if (selectedItems.size === 0) return\n setShowDeleteConfirm(true)\n }, [selectedItems])\n\n const handleDeleteConfirm = useCallback(async () => {\n setShowDeleteConfirm(false)\n \n try {\n const response = await fetch('/api/studio/delete', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ paths: Array.from(selectedItems) }),\n })\n\n if (response.ok) {\n clearSelection()\n triggerRefresh()\n } else {\n const error = await response.json()\n setAlertMessage({\n title: 'Delete Failed',\n message: error.error || 'Unknown error',\n })\n }\n } catch (error) {\n console.error('Delete error:', error)\n setAlertMessage({\n title: 'Delete Failed',\n message: 'Delete failed. Check console for details.',\n })\n }\n }, [selectedItems, clearSelection, triggerRefresh])\n\n const handleSyncCdn = useCallback(() => {\n console.log('Sync CDN clicked', selectedItems)\n }, [selectedItems])\n\n const handleScan = useCallback(() => {\n console.log('Scan clicked')\n }, [])\n\n const hasSelection = selectedItems.size > 0\n\n return (\n <>\n {showDeleteConfirm && (\n <ConfirmModal\n title=\"Delete Items\"\n message={`Are you sure you want to delete ${selectedItems.size} item(s)? This action cannot be undone.`}\n confirmLabel=\"Delete\"\n variant=\"danger\"\n onConfirm={handleDeleteConfirm}\n onCancel={() => setShowDeleteConfirm(false)}\n />\n )}\n\n {alertMessage && (\n <AlertModal\n title={alertMessage.title}\n message={alertMessage.message}\n onClose={() => setAlertMessage(null)}\n />\n )}\n\n <div css={styles.toolbar}>\n {/* Hidden file input for upload */}\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n accept=\"image/*\"\n onChange={handleFileChange}\n style={{ display: 'none' }}\n />\n \n <div css={styles.left}>\n <ToolbarButton \n onClick={handleUpload} \n icon=\"upload\" \n label={uploading ? 'Uploading...' : 'Upload'} \n disabled={uploading || isInImagesFolder}\n />\n <ToolbarButton\n onClick={handleReprocess}\n icon=\"refresh\"\n label=\"Reprocess\"\n disabled={!hasSelection}\n />\n <ToolbarButton\n onClick={handleDeleteClick}\n icon=\"trash\"\n label=\"Delete\"\n disabled={!hasSelection}\n variant=\"danger\"\n />\n <ToolbarButton\n onClick={handleSyncCdn}\n icon=\"cloud\"\n label=\"Sync CDN\"\n disabled={!hasSelection}\n />\n <ToolbarButton onClick={handleScan} icon=\"scan\" label=\"Scan\" />\n </div>\n\n <div css={styles.right}>\n {hasSelection && (\n <span css={styles.selectionCount}>\n {selectedItems.size} selected\n <button css={styles.clearBtn} onClick={clearSelection}>\n Clear\n </button>\n </span>\n )}\n\n <ToolbarButton\n onClick={handleRefresh}\n icon=\"reload\"\n label=\"Refresh\"\n spinning={refreshing}\n />\n\n <div css={styles.viewToggle}>\n <button\n css={[styles.viewBtn, viewMode === 'grid' && styles.viewBtnActive]}\n onClick={() => setViewMode('grid')}\n aria-label=\"Grid view\"\n >\n <GridIcon />\n </button>\n <button\n css={[styles.viewBtn, viewMode === 'list' && styles.viewBtnActive]}\n onClick={() => setViewMode('list')}\n aria-label=\"List view\"\n >\n <ListIcon />\n </button>\n </div>\n </div>\n </div>\n </>\n )\n}\n\ninterface ToolbarButtonProps {\n onClick: () => void\n icon: 'upload' | 'refresh' | 'trash' | 'cloud' | 'scan' | 'reload'\n label: string\n disabled?: boolean\n variant?: 'default' | 'danger'\n spinning?: boolean\n}\n\nfunction ToolbarButton({\n onClick,\n icon,\n label,\n disabled,\n variant = 'default',\n spinning,\n}: ToolbarButtonProps) {\n return (\n <button\n css={[styles.btn, variant === 'danger' ? styles.btnDanger : styles.btnDefault]}\n onClick={onClick}\n disabled={disabled}\n >\n <IconComponent icon={icon} spinning={spinning} />\n {label}\n </button>\n )\n}\n\nfunction IconComponent({ icon, spinning }: { icon: string; spinning?: boolean }) {\n switch (icon) {\n case 'upload':\n return (\n <svg css={styles.icon} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n )\n case 'refresh':\n return (\n <svg css={styles.icon} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n )\n case 'trash':\n return (\n <svg css={styles.icon} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n )\n case 'cloud':\n return (\n <svg css={styles.icon} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n )\n case 'scan':\n return (\n <svg css={styles.icon} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" />\n </svg>\n )\n case 'reload':\n return (\n <svg css={[styles.icon, spinning && styles.iconSpin]} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n )\n default:\n return null\n }\n}\n\nfunction GridIcon() {\n return (\n <svg css={styles.icon} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n )\n}\n\nfunction ListIcon() {\n return (\n <svg css={styles.icon} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 6h16M4 10h16M4 14h16M4 18h16\" />\n </svg>\n )\n}\n","/** @jsxImportSource @emotion/react */\n'use client'\n\nimport { css, keyframes } from '@emotion/react'\n\nconst fadeIn = keyframes`\n from { opacity: 0; }\n to { opacity: 1; }\n`\n\nconst slideIn = keyframes`\n from { \n opacity: 0;\n transform: scale(0.95);\n }\n to { \n opacity: 1;\n transform: scale(1);\n }\n`\n\nconst styles = {\n overlay: css`\n position: fixed;\n inset: 0;\n background-color: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n animation: ${fadeIn} 0.15s ease-out;\n `,\n modal: css`\n background-color: white;\n border-radius: 12px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n max-width: 400px;\n width: 90%;\n animation: ${slideIn} 0.15s ease-out;\n `,\n header: css`\n padding: 20px 24px 0;\n `,\n title: css`\n font-size: 18px;\n font-weight: 600;\n color: #111827;\n margin: 0;\n `,\n body: css`\n padding: 12px 24px 24px;\n `,\n message: css`\n font-size: 14px;\n color: #6b7280;\n margin: 0;\n line-height: 1.5;\n `,\n footer: css`\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 24px;\n border-top: 1px solid #e5e7eb;\n background-color: #f9fafb;\n border-radius: 0 0 12px 12px;\n `,\n btn: css`\n padding: 8px 16px;\n font-size: 14px;\n font-weight: 500;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.15s;\n `,\n btnCancel: css`\n background-color: white;\n border: 1px solid #d1d5db;\n color: #374151;\n \n &:hover {\n background-color: #f9fafb;\n }\n `,\n btnConfirm: css`\n background-color: #9333ea;\n border: 1px solid #9333ea;\n color: white;\n \n &:hover {\n background-color: #7c3aed;\n }\n `,\n btnDanger: css`\n background-color: #dc2626;\n border: 1px solid #dc2626;\n color: white;\n \n &:hover {\n background-color: #b91c1c;\n }\n `,\n}\n\ninterface ConfirmModalProps {\n title: string\n message: string\n confirmLabel?: string\n cancelLabel?: string\n variant?: 'default' | 'danger'\n onConfirm: () => void\n onCancel: () => void\n}\n\nexport function ConfirmModal({\n title,\n message,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n variant = 'default',\n onConfirm,\n onCancel,\n}: ConfirmModalProps) {\n return (\n <div css={styles.overlay} onClick={onCancel}>\n <div css={styles.modal} onClick={(e) => e.stopPropagation()}>\n <div css={styles.header}>\n <h3 css={styles.title}>{title}</h3>\n </div>\n <div css={styles.body}>\n <p css={styles.message}>{message}</p>\n </div>\n <div css={styles.footer}>\n <button css={[styles.btn, styles.btnCancel]} onClick={onCancel}>\n {cancelLabel}\n </button>\n <button\n css={[styles.btn, variant === 'danger' ? styles.btnDanger : styles.btnConfirm]}\n onClick={onConfirm}\n >\n {confirmLabel}\n </button>\n </div>\n </div>\n </div>\n )\n}\n\ninterface AlertModalProps {\n title: string\n message: string\n buttonLabel?: string\n onClose: () => void\n}\n\nexport function AlertModal({\n title,\n message,\n buttonLabel = 'OK',\n onClose,\n}: AlertModalProps) {\n return (\n <div css={styles.overlay} onClick={onClose}>\n <div css={styles.modal} onClick={(e) => e.stopPropagation()}>\n <div css={styles.header}>\n <h3 css={styles.title}>{title}</h3>\n </div>\n <div css={styles.body}>\n <p css={styles.message}>{message}</p>\n </div>\n <div css={styles.footer}>\n <button css={[styles.btn, styles.btnConfirm]} onClick={onClose}>\n {buttonLabel}\n </button>\n </div>\n </div>\n </div>\n )\n}\n","/** @jsxImportSource @emotion/react */\n'use client'\n\nimport { css } from '@emotion/react'\nimport { useStudio } from './StudioContext'\n\nconst styles = {\n container: css`\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 24px;\n background-color: white;\n border-bottom: 1px solid #f3f4f6;\n `,\n backBtn: css`\n padding: 4px;\n background: none;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n transition: background-color 0.15s;\n \n &:hover {\n background-color: #f3f4f6;\n }\n `,\n backIcon: css`\n width: 16px;\n height: 16px;\n color: #6b7280;\n `,\n nav: css`\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 14px;\n `,\n item: css`\n display: flex;\n align-items: center;\n gap: 4px;\n `,\n separator: css`\n color: #d1d5db;\n `,\n btn: css`\n padding: 2px 4px;\n background: none;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.15s;\n \n &:hover {\n background-color: #f3f4f6;\n }\n `,\n btnActive: css`\n color: #111827;\n font-weight: 500;\n `,\n btnInactive: css`\n color: #6b7280;\n \n &:hover {\n color: #374151;\n }\n `,\n}\n\nexport function StudioBreadcrumb() {\n const { currentPath, setCurrentPath, navigateUp } = useStudio()\n\n const parts = currentPath.split('/').filter(Boolean)\n\n const handleClick = (index: number) => {\n const newPath = parts.slice(0, index + 1).join('/')\n setCurrentPath(newPath)\n }\n\n return (\n <div css={styles.container}>\n {currentPath !== 'public' && (\n <button css={styles.backBtn} onClick={navigateUp} aria-label=\"Go back\">\n <svg css={styles.backIcon} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n )}\n\n <nav css={styles.nav}>\n {parts.map((part, index) => (\n <span key={index} css={styles.item}>\n {index > 0 && <span css={styles.separator}>/</span>}\n <button\n css={[styles.btn, index === parts.length - 1 ? styles.btnActive : styles.btnInactive]}\n onClick={() => handleClick(index)}\n >\n {part}\n </button>\n </span>\n ))}\n </nav>\n </div>\n )\n}\n","/** @jsxImportSource @emotion/react */\n'use client'\n\nimport { useEffect, useState } from 'react'\nimport { css, keyframes } from '@emotion/react'\nimport { useStudio } from './StudioContext'\nimport type { FileItem } from '../types'\n\nconst spin = keyframes`\n to { transform: rotate(360deg); }\n`\n\nconst styles = {\n loading: css`\n display: flex;\n align-items: center;\n justify-content: center;\n height: 256px;\n `,\n spinner: css`\n width: 32px;\n height: 32px;\n border-radius: 50%;\n border: 2px solid transparent;\n border-bottom-color: #9333ea;\n animation: ${spin} 1s linear infinite;\n `,\n empty: css`\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 256px;\n color: #6b7280;\n `,\n emptyIcon: css`\n width: 48px;\n height: 48px;\n margin-bottom: 16px;\n `,\n emptyText: css`\n font-size: 14px;\n margin: 0;\n `,\n grid: css`\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 16px;\n \n @media (min-width: 640px) { grid-template-columns: repeat(3, 1fr); }\n @media (min-width: 768px) { grid-template-columns: repeat(4, 1fr); }\n @media (min-width: 1024px) { grid-template-columns: repeat(5, 1fr); }\n @media (min-width: 1280px) { grid-template-columns: repeat(6, 1fr); }\n `,\n item: css`\n position: relative;\n border-radius: 8px;\n border: 2px solid transparent;\n overflow: hidden;\n cursor: pointer;\n transition: all 0.15s;\n background-color: #f9fafb;\n user-select: none;\n \n &:hover {\n border-color: #e5e7eb;\n }\n `,\n itemSelected: css`\n border-color: #a855f7;\n background-color: #faf5ff;\n \n &:hover {\n border-color: #a855f7;\n }\n `,\n checkbox: css`\n position: absolute;\n top: 8px;\n left: 8px;\n z-index: 10;\n width: 16px;\n height: 16px;\n accent-color: #9333ea;\n `,\n cdnBadge: css`\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 10;\n background-color: #dcfce7;\n color: #15803d;\n font-size: 12px;\n padding: 2px 6px;\n border-radius: 9999px;\n `,\n content: css`\n aspect-ratio: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n `,\n folderIcon: css`\n width: 64px;\n height: 64px;\n color: #facc15;\n `,\n image: css`\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n border-radius: 4px;\n `,\n label: css`\n padding: 6px 8px;\n background-color: white;\n border-top: 1px solid #e5e7eb;\n `,\n name: css`\n font-size: 12px;\n color: #374151;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n margin: 0;\n `,\n size: css`\n font-size: 12px;\n color: #9ca3af;\n margin: 0;\n `,\n selectAllRow: css`\n display: flex;\n align-items: center;\n margin-bottom: 12px;\n padding-bottom: 12px;\n border-bottom: 1px solid #e5e7eb;\n `,\n selectAllLabel: css`\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n color: #6b7280;\n cursor: pointer;\n \n &:hover {\n color: #374151;\n }\n `,\n selectAllCheckbox: css`\n width: 16px;\n height: 16px;\n accent-color: #9333ea;\n `,\n}\n\nexport function StudioFileGrid() {\n const { currentPath, setCurrentPath, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey } = useStudio()\n const [items, setItems] = useState<FileItem[]>([])\n const [loading, setLoading] = useState(true)\n\n useEffect(() => {\n async function loadItems() {\n setLoading(true)\n try {\n const response = await fetch(`/api/studio/list?path=${encodeURIComponent(currentPath)}`)\n if (response.ok) {\n const data = await response.json()\n setItems(data.items || [])\n }\n } catch (error) {\n console.error('Failed to load items:', error)\n }\n setLoading(false)\n }\n loadItems()\n }, [currentPath, refreshKey])\n\n if (loading) {\n return (\n <div css={styles.loading}>\n <div css={styles.spinner} />\n </div>\n )\n }\n\n if (items.length === 0) {\n return (\n <div css={styles.empty}>\n <svg css={styles.emptyIcon} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n <p css={styles.emptyText}>No files in this folder</p>\n <p css={styles.emptyText}>Upload images to get started</p>\n </div>\n )\n }\n\n const sortedItems = [...items].sort((a, b) => {\n if (a.type === 'folder' && b.type !== 'folder') return -1\n if (a.type !== 'folder' && b.type === 'folder') return 1\n return a.name.localeCompare(b.name)\n })\n\n const files = sortedItems.filter(item => item.type !== 'folder')\n const allFilesSelected = files.length > 0 && files.every(item => selectedItems.has(item.path))\n const someFilesSelected = files.some(item => selectedItems.has(item.path))\n\n const handleSelectAll = () => {\n if (allFilesSelected) {\n clearSelection()\n } else {\n selectAll(files)\n }\n }\n\n const handleItemClick = (item: FileItem, e: React.MouseEvent) => {\n if (item.type === 'folder') {\n setCurrentPath(item.path)\n return\n }\n\n if (e.shiftKey && lastSelectedPath) {\n selectRange(lastSelectedPath, item.path, sortedItems)\n } else {\n toggleSelection(item.path)\n }\n }\n\n return (\n <div>\n {files.length > 0 && (\n <div css={styles.selectAllRow}>\n <label css={styles.selectAllLabel}>\n <input\n type=\"checkbox\"\n css={styles.selectAllCheckbox}\n checked={allFilesSelected}\n ref={(el) => {\n if (el) el.indeterminate = someFilesSelected && !allFilesSelected\n }}\n onChange={handleSelectAll}\n />\n Select all ({files.length})\n </label>\n </div>\n )}\n <div css={styles.grid}>\n {sortedItems.map((item) => (\n <GridItem\n key={item.path}\n item={item}\n isSelected={selectedItems.has(item.path)}\n onClick={(e) => handleItemClick(item, e)}\n />\n ))}\n </div>\n </div>\n )\n}\n\ninterface GridItemProps {\n item: FileItem\n isSelected: boolean\n onClick: (e: React.MouseEvent) => void\n}\n\nfunction GridItem({ item, isSelected, onClick }: GridItemProps) {\n const isFolder = item.type === 'folder'\n\n return (\n <div css={[styles.item, isSelected && styles.itemSelected]} onClick={onClick}>\n {/* Only show checkbox for files, not folders */}\n {!isFolder && (\n <input\n type=\"checkbox\"\n css={styles.checkbox}\n checked={isSelected}\n onChange={() => {}}\n onClick={(e) => {\n e.stopPropagation()\n // Trigger the same click handler as the container\n onClick(e as unknown as React.MouseEvent)\n }}\n />\n )}\n\n {item.cdnSynced && <span css={styles.cdnBadge}>CDN</span>}\n\n <div css={styles.content}>\n {isFolder ? (\n <svg css={styles.folderIcon} fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n ) : (\n <img\n css={styles.image}\n src={item.path.replace('public', '')}\n alt={item.name}\n loading=\"lazy\"\n />\n )}\n </div>\n\n <div css={styles.label}>\n <p css={styles.name} title={item.name}>{item.name}</p>\n {item.size && <p css={styles.size}>{formatFileSize(item.size)}</p>}\n </div>\n </div>\n )\n}\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n","/** @jsxImportSource @emotion/react */\n'use client'\n\nimport { useEffect, useState } from 'react'\nimport { css, keyframes } from '@emotion/react'\nimport { useStudio } from './StudioContext'\nimport type { FileItem } from '../types'\n\nconst spin = keyframes`\n to { transform: rotate(360deg); }\n`\n\nconst styles = {\n loading: css`\n display: flex;\n align-items: center;\n justify-content: center;\n height: 256px;\n `,\n spinner: css`\n width: 32px;\n height: 32px;\n border-radius: 50%;\n border: 2px solid transparent;\n border-bottom-color: #9333ea;\n animation: ${spin} 1s linear infinite;\n `,\n empty: css`\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 256px;\n color: #6b7280;\n `,\n table: css`\n width: 100%;\n border-collapse: collapse;\n `,\n th: css`\n text-align: left;\n font-size: 12px;\n color: #6b7280;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n padding-bottom: 8px;\n font-weight: normal;\n `,\n thCheckbox: css`\n width: 32px;\n `,\n thSize: css`\n width: 96px;\n `,\n thDimensions: css`\n width: 128px;\n `,\n thCdn: css`\n width: 96px;\n `,\n tbody: css`\n border-top: 1px solid #f3f4f6;\n `,\n row: css`\n cursor: pointer;\n transition: background-color 0.15s;\n user-select: none;\n \n &:hover {\n background-color: #f9fafb;\n }\n `,\n rowSelected: css`\n background-color: #faf5ff;\n \n &:hover {\n background-color: #faf5ff;\n }\n `,\n td: css`\n padding: 8px 0;\n border-bottom: 1px solid #f3f4f6;\n `,\n checkbox: css`\n width: 16px;\n height: 16px;\n accent-color: #9333ea;\n `,\n nameCell: css`\n display: flex;\n align-items: center;\n gap: 8px;\n `,\n folderIcon: css`\n width: 20px;\n height: 20px;\n color: #facc15;\n `,\n fileIcon: css`\n width: 20px;\n height: 20px;\n color: #9ca3af;\n `,\n name: css`\n font-size: 14px;\n color: #111827;\n `,\n meta: css`\n font-size: 14px;\n color: #6b7280;\n `,\n cdnBadge: css`\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n color: #15803d;\n `,\n cdnIcon: css`\n width: 12px;\n height: 12px;\n `,\n cdnEmpty: css`\n font-size: 12px;\n color: #9ca3af;\n `,\n}\n\nexport function StudioFileList() {\n const { currentPath, setCurrentPath, selectedItems, toggleSelection, selectRange, lastSelectedPath, selectAll, clearSelection, refreshKey } = useStudio()\n const [items, setItems] = useState<FileItem[]>([])\n const [loading, setLoading] = useState(true)\n\n useEffect(() => {\n async function loadItems() {\n setLoading(true)\n try {\n const response = await fetch(`/api/studio/list?path=${encodeURIComponent(currentPath)}`)\n if (response.ok) {\n const data = await response.json()\n setItems(data.items || [])\n }\n } catch (error) {\n console.error('Failed to load items:', error)\n }\n setLoading(false)\n }\n loadItems()\n }, [currentPath, refreshKey])\n\n if (loading) {\n return (\n <div css={styles.loading}>\n <div css={styles.spinner} />\n </div>\n )\n }\n\n if (items.length === 0) {\n return (\n <div css={styles.empty}>\n <p>No files in this folder</p>\n </div>\n )\n }\n\n const sortedItems = [...items].sort((a, b) => {\n if (a.type === 'folder' && b.type !== 'folder') return -1\n if (a.type !== 'folder' && b.type === 'folder') return 1\n return a.name.localeCompare(b.name)\n })\n\n const files = sortedItems.filter(item => item.type !== 'folder')\n const allFilesSelected = files.length > 0 && files.every(item => selectedItems.has(item.path))\n const someFilesSelected = files.some(item => selectedItems.has(item.path))\n\n const handleSelectAll = () => {\n if (allFilesSelected) {\n clearSelection()\n } else {\n selectAll(files)\n }\n }\n\n const handleItemClick = (item: FileItem, e: React.MouseEvent) => {\n if (item.type === 'folder') {\n setCurrentPath(item.path)\n return\n }\n\n if (e.shiftKey && lastSelectedPath) {\n selectRange(lastSelectedPath, item.path, sortedItems)\n } else {\n toggleSelection(item.path)\n }\n }\n\n return (\n <table css={styles.table}>\n <thead>\n <tr>\n <th css={[styles.th, styles.thCheckbox]}>\n {files.length > 0 && (\n <input\n type=\"checkbox\"\n css={styles.checkbox}\n checked={allFilesSelected}\n ref={(el) => {\n if (el) el.indeterminate = someFilesSelected && !allFilesSelected\n }}\n onChange={handleSelectAll}\n />\n )}\n </th>\n <th css={styles.th}>Name</th>\n <th css={[styles.th, styles.thSize]}>Size</th>\n <th css={[styles.th, styles.thDimensions]}>Dimensions</th>\n <th css={[styles.th, styles.thCdn]}>CDN</th>\n </tr>\n </thead>\n <tbody css={styles.tbody}>\n {sortedItems.map((item) => (\n <ListRow\n key={item.path}\n item={item}\n isSelected={selectedItems.has(item.path)}\n onClick={(e) => handleItemClick(item, e)}\n />\n ))}\n </tbody>\n </table>\n )\n}\n\ninterface ListRowProps {\n item: FileItem\n isSelected: boolean\n onClick: (e: React.MouseEvent) => void\n}\n\nfunction ListRow({ item, isSelected, onClick }: ListRowProps) {\n const isFolder = item.type === 'folder'\n\n return (\n <tr css={[styles.row, isSelected && styles.rowSelected]} onClick={onClick}>\n <td css={styles.td}>\n {/* Only show checkbox for files, not folders */}\n {!isFolder && (\n <input\n type=\"checkbox\"\n css={styles.checkbox}\n checked={isSelected}\n onChange={() => {}}\n onClick={(e) => {\n e.stopPropagation()\n // Trigger the same click handler as the row\n onClick(e as unknown as React.MouseEvent)\n }}\n />\n )}\n </td>\n <td css={styles.td}>\n <div css={styles.nameCell}>\n {isFolder ? (\n <svg css={styles.folderIcon} fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n ) : (\n <svg css={styles.fileIcon} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n )}\n <span css={styles.name}>{item.name}</span>\n </div>\n </td>\n <td css={[styles.td, styles.meta]}>\n {item.size ? formatFileSize(item.size) : '--'}\n </td>\n <td css={[styles.td, styles.meta]}>\n {item.dimensions ? `${item.dimensions.width}x${item.dimensions.height}` : '--'}\n </td>\n <td css={styles.td}>\n {item.cdnSynced ? (\n <span css={styles.cdnBadge}>\n <svg css={styles.cdnIcon} fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <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\" />\n </svg>\n Synced\n </span>\n ) : (\n <span css={styles.cdnEmpty}>--</span>\n )}\n </td>\n </tr>\n )\n}\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n","/** @jsxImportSource @emotion/react */\n'use client'\n\nimport { useState } from 'react'\nimport { css } from '@emotion/react'\nimport { useStudio } from './StudioContext'\nimport { ConfirmModal, AlertModal } from './StudioModal'\n\nconst styles = {\n panel: css`\n width: 320px;\n border-left: 1px solid #e5e7eb;\n background-color: #f9fafb;\n padding: 16px;\n overflow: auto;\n `,\n title: css`\n font-size: 14px;\n font-weight: 500;\n color: #111827;\n margin: 0 0 16px 0;\n `,\n imageContainer: css`\n background-color: white;\n border-radius: 8px;\n border: 1px solid #e5e7eb;\n padding: 8px;\n margin-bottom: 16px;\n `,\n image: css`\n width: 100%;\n height: auto;\n border-radius: 4px;\n `,\n info: css`\n display: flex;\n flex-direction: column;\n gap: 12px;\n `,\n row: css`\n display: flex;\n justify-content: space-between;\n font-size: 12px;\n `,\n label: css`\n color: #6b7280;\n `,\n value: css`\n color: #111827;\n `,\n valueTruncate: css`\n max-width: 128px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n `,\n section: css`\n padding-top: 8px;\n border-top: 1px solid #e5e7eb;\n `,\n sectionTitle: css`\n font-size: 12px;\n font-weight: 500;\n color: #6b7280;\n margin: 0 0 8px 0;\n `,\n cdnStatus: css`\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: #16a34a;\n `,\n cdnIcon: css`\n width: 16px;\n height: 16px;\n `,\n copyBtn: css`\n margin-top: 8px;\n font-size: 12px;\n color: #9333ea;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n \n &:hover {\n text-decoration: underline;\n }\n `,\n colorSwatch: css`\n margin-top: 8px;\n height: 32px;\n border-radius: 4px;\n `,\n emptyState: css`\n display: flex;\n align-items: center;\n justify-content: center;\n height: 200px;\n `,\n emptyText: css`\n font-size: 14px;\n color: #9ca3af;\n margin: 0;\n `,\n actions: css`\n margin-top: 16px;\n padding-top: 16px;\n border-top: 1px solid #e5e7eb;\n display: flex;\n flex-direction: column;\n gap: 8px;\n `,\n actionBtn: css`\n width: 100%;\n padding: 8px 12px;\n font-size: 14px;\n background-color: white;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n cursor: pointer;\n transition: background-color 0.15s;\n color: #374151;\n \n &:hover {\n background-color: #f9fafb;\n }\n `,\n actionBtnDanger: css`\n color: #dc2626;\n \n &:hover {\n background-color: #fef2f2;\n }\n `,\n}\n\nexport function StudioPreview() {\n const { selectedItems, meta, triggerRefresh, clearSelection } = useStudio()\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)\n const [alertMessage, setAlertMessage] = useState<{ title: string; message: string } | null>(null)\n\n const handleDeleteClick = () => {\n if (selectedItems.size === 0) return\n setShowDeleteConfirm(true)\n }\n\n const handleDeleteConfirm = async () => {\n setShowDeleteConfirm(false)\n\n try {\n const response = await fetch('/api/studio/delete', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ paths: Array.from(selectedItems) }),\n })\n\n if (response.ok) {\n clearSelection()\n triggerRefresh()\n } else {\n const error = await response.json()\n setAlertMessage({\n title: 'Delete Failed',\n message: error.error || 'Unknown error',\n })\n }\n } catch (error) {\n console.error('Delete error:', error)\n setAlertMessage({\n title: 'Delete Failed',\n message: 'Delete failed. Check console for details.',\n })\n }\n }\n\n const modals = (\n <>\n {showDeleteConfirm && (\n <ConfirmModal\n title=\"Delete Items\"\n message={`Are you sure you want to delete ${selectedItems.size} item(s)? This action cannot be undone.`}\n confirmLabel=\"Delete\"\n variant=\"danger\"\n onConfirm={handleDeleteConfirm}\n onCancel={() => setShowDeleteConfirm(false)}\n />\n )}\n\n {alertMessage && (\n <AlertModal\n title={alertMessage.title}\n message={alertMessage.message}\n onClose={() => setAlertMessage(null)}\n />\n )}\n </>\n )\n\n // Always show the sidebar\n if (selectedItems.size === 0) {\n return (\n <>\n {modals}\n <div css={styles.panel}>\n <h3 css={styles.title}>Preview</h3>\n <div css={styles.emptyState}>\n <p css={styles.emptyText}>Select an image to preview</p>\n </div>\n </div>\n </>\n )\n }\n\n if (selectedItems.size > 1) {\n return (\n <>\n {modals}\n <div css={styles.panel}>\n <h3 css={styles.title}>{selectedItems.size} items selected</h3>\n <div css={styles.actions}>\n <button css={[styles.actionBtn, styles.actionBtnDanger]} onClick={handleDeleteClick}>\n Delete {selectedItems.size} items\n </button>\n </div>\n </div>\n </>\n )\n }\n\n const selectedPath = Array.from(selectedItems)[0]\n const imageKey = selectedPath\n .replace(/^public\\/images\\//, '')\n .replace(/^public\\/originals\\//, '')\n\n const imageData = meta?.images?.[imageKey]\n\n return (\n <>\n {modals}\n <div css={styles.panel}>\n <h3 css={styles.title}>Preview</h3>\n\n <div css={styles.imageContainer}>\n <img\n css={styles.image}\n src={selectedPath.replace('public', '')}\n alt=\"Preview\"\n />\n </div>\n\n <div css={styles.info}>\n <InfoRow label=\"Filename\" value={selectedPath.split('/').pop() || ''} />\n\n {imageData && (\n <>\n <InfoRow\n label=\"Original\"\n value={`${imageData.original.width}x${imageData.original.height}`}\n />\n <InfoRow\n label=\"File size\"\n value={formatFileSize(imageData.original.fileSize)}\n />\n\n <div css={styles.section}>\n <p css={styles.sectionTitle}>Generated sizes</p>\n {Object.entries(imageData.sizes).map(([size, data]) => (\n <InfoRow key={size} label={size} value={`${data.width}x${data.height}`} />\n ))}\n </div>\n\n {imageData.cdn?.synced && (\n <div css={styles.section}>\n <p css={styles.sectionTitle}>CDN</p>\n <div css={styles.cdnStatus}>\n <svg css={styles.cdnIcon} fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <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\" />\n </svg>\n Synced to CDN\n </div>\n <button\n css={styles.copyBtn}\n onClick={() => {\n navigator.clipboard.writeText(`${imageData.cdn?.baseUrl}${imageData.sizes.full.path}`)\n }}\n >\n Copy CDN URL\n </button>\n </div>\n )}\n\n {imageData.blurhash && (\n <div css={styles.section}>\n <InfoRow label=\"Blurhash\" value={imageData.blurhash} truncate />\n <div\n css={styles.colorSwatch}\n style={{ backgroundColor: imageData.dominantColor }}\n title={`Dominant color: ${imageData.dominantColor}`}\n />\n </div>\n )}\n </>\n )}\n </div>\n\n <div css={styles.actions}>\n <button css={styles.actionBtn}>Rename</button>\n <button css={[styles.actionBtn, styles.actionBtnDanger]} onClick={handleDeleteClick}>Delete</button>\n </div>\n </div>\n </>\n )\n}\n\nfunction InfoRow({ label, value, truncate }: { label: string; value: string; truncate?: boolean }) {\n return (\n <div css={styles.row}>\n <span css={styles.label}>{label}</span>\n <span css={[styles.value, truncate && styles.valueTruncate]} title={truncate ? value : undefined}>\n {value}\n </span>\n </div>\n )\n}\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n","/** @jsxImportSource @emotion/react */\n'use client'\n\nimport { useState } from 'react'\nimport { css } from '@emotion/react'\n\nconst styles = {\n btn: css`\n padding: 8px;\n background: none;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: background-color 0.15s;\n \n &:hover {\n background-color: #f3f4f6;\n }\n `,\n icon: css`\n width: 20px;\n height: 20px;\n color: #6b7280;\n `,\n overlay: css`\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 10000;\n display: flex;\n align-items: center;\n justify-content: center;\n `,\n backdrop: css`\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n background-color: rgba(0, 0, 0, 0.3);\n `,\n panel: css`\n position: relative;\n background-color: white;\n border-radius: 12px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n width: 100%;\n max-width: 512px;\n padding: 24px;\n `,\n header: css`\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 24px;\n `,\n title: css`\n font-size: 18px;\n font-weight: 600;\n margin: 0;\n `,\n closeBtn: css`\n padding: 4px;\n background: none;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n \n &:hover {\n background-color: #f3f4f6;\n }\n `,\n sections: css`\n display: flex;\n flex-direction: column;\n gap: 24px;\n `,\n sectionTitle: css`\n font-size: 14px;\n font-weight: 500;\n color: #111827;\n margin: 0 0 12px 0;\n `,\n description: css`\n font-size: 12px;\n color: #6b7280;\n margin: 0 0 12px 0;\n `,\n code: css`\n background-color: #f9fafb;\n border-radius: 8px;\n padding: 12px;\n font-family: monospace;\n font-size: 12px;\n color: #4b5563;\n `,\n codeLine: css`\n margin: 0 0 4px 0;\n \n &:last-child {\n margin: 0;\n }\n `,\n input: css`\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n font-size: 14px;\n \n &:focus {\n outline: none;\n box-shadow: 0 0 0 2px #a855f7;\n }\n `,\n grid: css`\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 12px;\n `,\n label: css`\n font-size: 12px;\n color: #6b7280;\n display: block;\n margin-bottom: 4px;\n `,\n footer: css`\n margin-top: 24px;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n `,\n cancelBtn: css`\n padding: 8px 16px;\n font-size: 14px;\n color: #4b5563;\n background: none;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n \n &:hover {\n background-color: #f3f4f6;\n }\n `,\n saveBtn: css`\n padding: 8px 16px;\n font-size: 14px;\n color: white;\n background-color: #9333ea;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n \n &:hover {\n background-color: #7c3aed;\n }\n `,\n}\n\nexport function StudioSettings() {\n const [isOpen, setIsOpen] = useState(false)\n\n return (\n <>\n <button css={styles.btn} onClick={() => setIsOpen(true)} aria-label=\"Settings\">\n <svg\n css={styles.icon}\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <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\" />\n </svg>\n </button>\n\n {isOpen && <SettingsPanel onClose={() => setIsOpen(false)} />}\n </>\n )\n}\n\nfunction SettingsPanel({ onClose }: { onClose: () => void }) {\n return (\n <div css={styles.overlay}>\n <div css={styles.backdrop} onClick={onClose} />\n\n <div css={styles.panel}>\n <div css={styles.header}>\n <h2 css={styles.title}>Settings</h2>\n <button css={styles.closeBtn} onClick={onClose}>\n <svg css={styles.icon} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div css={styles.sections}>\n <section>\n <h3 css={styles.sectionTitle}>Cloudflare R2</h3>\n <p css={styles.description}>Configure in .env.local file:</p>\n <div css={styles.code}>\n <p css={styles.codeLine}>CLOUDFLARE_R2_ACCOUNT_ID</p>\n <p css={styles.codeLine}>CLOUDFLARE_R2_ACCESS_KEY_ID</p>\n <p css={styles.codeLine}>CLOUDFLARE_R2_SECRET_ACCESS_KEY</p>\n <p css={styles.codeLine}>CLOUDFLARE_R2_BUCKET_NAME</p>\n <p css={styles.codeLine}>CLOUDFLARE_R2_PUBLIC_URL</p>\n </div>\n </section>\n\n <section>\n <h3 css={styles.sectionTitle}>Custom CDN URL</h3>\n <p css={styles.description}>Override the default R2 URL with a custom domain:</p>\n <input css={styles.input} type=\"text\" placeholder=\"https://cdn.yourdomain.com\" />\n </section>\n\n <section>\n <h3 css={styles.sectionTitle}>Thumbnail Sizes</h3>\n <div css={styles.grid}>\n <div>\n <label css={styles.label}>Small</label>\n <input css={styles.input} type=\"number\" defaultValue={300} />\n </div>\n <div>\n <label css={styles.label}>Medium</label>\n <input css={styles.input} type=\"number\" defaultValue={700} />\n </div>\n <div>\n <label css={styles.label}>Large</label>\n <input css={styles.input} type=\"number\" defaultValue={1400} />\n </div>\n </div>\n </section>\n </div>\n\n <div css={styles.footer}>\n <button css={styles.cancelBtn} onClick={onClose}>Cancel</button>\n <button css={styles.saveBtn}>Save Changes</button>\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["useEffect","useCallback","useState","css","css","keyframes","jsx","jsxs","keyframes","styles","css","css","jsx","jsxs","styles","css","useState","css","keyframes","jsx","jsxs","spin","keyframes","styles","css","useState","useEffect","useState","css","keyframes","jsx","jsxs","spin","keyframes","styles","css","useState","useEffect","formatFileSize","useState","css","Fragment","jsx","jsxs","styles","css","useState","formatFileSize","useState","css","Fragment","jsx","jsxs","styles","jsx","jsxs","styles","css","useState","useCallback","useEffect"]}
|
|
@@ -190,6 +190,9 @@ function AlertModal({
|
|
|
190
190
|
|
|
191
191
|
// src/components/StudioToolbar.tsx
|
|
192
192
|
|
|
193
|
+
var spin = _react3.keyframes`
|
|
194
|
+
to { transform: rotate(360deg); }
|
|
195
|
+
`;
|
|
193
196
|
var styles2 = {
|
|
194
197
|
toolbar: _react3.css`
|
|
195
198
|
display: flex;
|
|
@@ -245,6 +248,9 @@ var styles2 = {
|
|
|
245
248
|
width: 16px;
|
|
246
249
|
height: 16px;
|
|
247
250
|
`,
|
|
251
|
+
iconSpin: _react3.css`
|
|
252
|
+
animation: ${spin} 1s linear infinite;
|
|
253
|
+
`,
|
|
248
254
|
selectionCount: _react3.css`
|
|
249
255
|
font-size: 14px;
|
|
250
256
|
color: #4b5563;
|
|
@@ -290,11 +296,18 @@ function StudioToolbar() {
|
|
|
290
296
|
const { selectedItems, viewMode, setViewMode, clearSelection, currentPath, triggerRefresh } = useStudio();
|
|
291
297
|
const fileInputRef = _react.useRef.call(void 0, null);
|
|
292
298
|
const [uploading, setUploading] = _react.useState.call(void 0, false);
|
|
299
|
+
const [refreshing, setRefreshing] = _react.useState.call(void 0, false);
|
|
293
300
|
const [showDeleteConfirm, setShowDeleteConfirm] = _react.useState.call(void 0, false);
|
|
294
301
|
const [alertMessage, setAlertMessage] = _react.useState.call(void 0, null);
|
|
302
|
+
const isInImagesFolder = currentPath === "public/images" || currentPath.startsWith("public/images/");
|
|
295
303
|
const handleUpload = _react.useCallback.call(void 0, () => {
|
|
296
304
|
_optionalChain([fileInputRef, 'access', _ => _.current, 'optionalAccess', _2 => _2.click, 'call', _3 => _3()]);
|
|
297
305
|
}, []);
|
|
306
|
+
const handleRefresh = _react.useCallback.call(void 0, () => {
|
|
307
|
+
setRefreshing(true);
|
|
308
|
+
triggerRefresh();
|
|
309
|
+
setTimeout(() => setRefreshing(false), 600);
|
|
310
|
+
}, [triggerRefresh]);
|
|
298
311
|
const handleFileChange = _react.useCallback.call(void 0, async (e) => {
|
|
299
312
|
const files = e.target.files;
|
|
300
313
|
if (!files || files.length === 0) return;
|
|
@@ -417,7 +430,7 @@ function StudioToolbar() {
|
|
|
417
430
|
onClick: handleUpload,
|
|
418
431
|
icon: "upload",
|
|
419
432
|
label: uploading ? "Uploading..." : "Upload",
|
|
420
|
-
disabled: uploading
|
|
433
|
+
disabled: uploading || isInImagesFolder
|
|
421
434
|
}
|
|
422
435
|
),
|
|
423
436
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
@@ -459,9 +472,10 @@ function StudioToolbar() {
|
|
|
459
472
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
460
473
|
ToolbarButton,
|
|
461
474
|
{
|
|
462
|
-
onClick:
|
|
475
|
+
onClick: handleRefresh,
|
|
463
476
|
icon: "reload",
|
|
464
|
-
label: "Refresh"
|
|
477
|
+
label: "Refresh",
|
|
478
|
+
spinning: refreshing
|
|
465
479
|
}
|
|
466
480
|
),
|
|
467
481
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles2.viewToggle, children: [
|
|
@@ -493,7 +507,8 @@ function ToolbarButton({
|
|
|
493
507
|
icon,
|
|
494
508
|
label,
|
|
495
509
|
disabled,
|
|
496
|
-
variant = "default"
|
|
510
|
+
variant = "default",
|
|
511
|
+
spinning
|
|
497
512
|
}) {
|
|
498
513
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
499
514
|
"button",
|
|
@@ -502,13 +517,13 @@ function ToolbarButton({
|
|
|
502
517
|
onClick,
|
|
503
518
|
disabled,
|
|
504
519
|
children: [
|
|
505
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, IconComponent, { icon }),
|
|
520
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, IconComponent, { icon, spinning }),
|
|
506
521
|
label
|
|
507
522
|
]
|
|
508
523
|
}
|
|
509
524
|
);
|
|
510
525
|
}
|
|
511
|
-
function IconComponent({ icon }) {
|
|
526
|
+
function IconComponent({ icon, spinning }) {
|
|
512
527
|
switch (icon) {
|
|
513
528
|
case "upload":
|
|
514
529
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" }) });
|
|
@@ -521,7 +536,7 @@ function IconComponent({ icon }) {
|
|
|
521
536
|
case "scan":
|
|
522
537
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) });
|
|
523
538
|
case "reload":
|
|
524
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles2.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "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" }) });
|
|
539
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: [styles2.icon, spinning && styles2.iconSpin], fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 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" }) });
|
|
525
540
|
default:
|
|
526
541
|
return null;
|
|
527
542
|
}
|
|
@@ -627,7 +642,7 @@ function StudioBreadcrumb() {
|
|
|
627
642
|
|
|
628
643
|
|
|
629
644
|
|
|
630
|
-
var
|
|
645
|
+
var spin2 = _react3.keyframes`
|
|
631
646
|
to { transform: rotate(360deg); }
|
|
632
647
|
`;
|
|
633
648
|
var styles4 = {
|
|
@@ -643,7 +658,7 @@ var styles4 = {
|
|
|
643
658
|
border-radius: 50%;
|
|
644
659
|
border: 2px solid transparent;
|
|
645
660
|
border-bottom-color: #9333ea;
|
|
646
|
-
animation: ${
|
|
661
|
+
animation: ${spin2} 1s linear infinite;
|
|
647
662
|
`,
|
|
648
663
|
empty: _react3.css`
|
|
649
664
|
display: flex;
|
|
@@ -680,6 +695,7 @@ var styles4 = {
|
|
|
680
695
|
cursor: pointer;
|
|
681
696
|
transition: all 0.15s;
|
|
682
697
|
background-color: #f9fafb;
|
|
698
|
+
user-select: none;
|
|
683
699
|
|
|
684
700
|
&:hover {
|
|
685
701
|
border-color: #e5e7eb;
|
|
@@ -870,7 +886,10 @@ function GridItem({ item, isSelected, onClick }) {
|
|
|
870
886
|
checked: isSelected,
|
|
871
887
|
onChange: () => {
|
|
872
888
|
},
|
|
873
|
-
onClick: (e) =>
|
|
889
|
+
onClick: (e) => {
|
|
890
|
+
e.stopPropagation();
|
|
891
|
+
onClick(e);
|
|
892
|
+
}
|
|
874
893
|
}
|
|
875
894
|
),
|
|
876
895
|
item.cdnSynced && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles4.cdnBadge, children: "CDN" }),
|
|
@@ -899,7 +918,7 @@ function formatFileSize(bytes) {
|
|
|
899
918
|
|
|
900
919
|
|
|
901
920
|
|
|
902
|
-
var
|
|
921
|
+
var spin3 = _react3.keyframes`
|
|
903
922
|
to { transform: rotate(360deg); }
|
|
904
923
|
`;
|
|
905
924
|
var styles5 = {
|
|
@@ -915,7 +934,7 @@ var styles5 = {
|
|
|
915
934
|
border-radius: 50%;
|
|
916
935
|
border: 2px solid transparent;
|
|
917
936
|
border-bottom-color: #9333ea;
|
|
918
|
-
animation: ${
|
|
937
|
+
animation: ${spin3} 1s linear infinite;
|
|
919
938
|
`,
|
|
920
939
|
empty: _react3.css`
|
|
921
940
|
display: flex;
|
|
@@ -956,6 +975,7 @@ var styles5 = {
|
|
|
956
975
|
row: _react3.css`
|
|
957
976
|
cursor: pointer;
|
|
958
977
|
transition: background-color 0.15s;
|
|
978
|
+
user-select: none;
|
|
959
979
|
|
|
960
980
|
&:hover {
|
|
961
981
|
background-color: #f9fafb;
|
|
@@ -1109,7 +1129,10 @@ function ListRow({ item, isSelected, onClick }) {
|
|
|
1109
1129
|
checked: isSelected,
|
|
1110
1130
|
onChange: () => {
|
|
1111
1131
|
},
|
|
1112
|
-
onClick: (e) =>
|
|
1132
|
+
onClick: (e) => {
|
|
1133
|
+
e.stopPropagation();
|
|
1134
|
+
onClick(e);
|
|
1135
|
+
}
|
|
1113
1136
|
}
|
|
1114
1137
|
) }),
|
|
1115
1138
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles5.td, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.nameCell, children: [
|
|
@@ -1861,4 +1884,4 @@ var StudioUI_default = StudioUI;
|
|
|
1861
1884
|
|
|
1862
1885
|
|
|
1863
1886
|
exports.StudioUI = StudioUI; exports.default = StudioUI_default;
|
|
1864
|
-
//# sourceMappingURL=StudioUI-
|
|
1887
|
+
//# sourceMappingURL=StudioUI-U6GFAPCY.js.map
|