@gallop.software/studio 0.1.53 → 0.1.55
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-IULAQEIK.mjs → StudioUI-5QGFJQXF.mjs} +238 -21
- package/dist/StudioUI-5QGFJQXF.mjs.map +1 -0
- package/dist/{StudioUI-T5BF5W4H.js → StudioUI-RIV7ERQD.js} +240 -23
- package/dist/StudioUI-RIV7ERQD.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/StudioUI-IULAQEIK.mjs.map +0 -1
- package/dist/StudioUI-T5BF5W4H.js.map +0 -1
|
@@ -64,6 +64,7 @@ import { useCallback, useRef, useState } from "react";
|
|
|
64
64
|
import { css as css2, keyframes as keyframes2 } from "@emotion/react";
|
|
65
65
|
|
|
66
66
|
// src/components/StudioModal.tsx
|
|
67
|
+
import React from "react";
|
|
67
68
|
import { css, keyframes } from "@emotion/react";
|
|
68
69
|
import { Fragment, jsx, jsxs } from "@emotion/react/jsx-runtime";
|
|
69
70
|
var fadeIn = keyframes`
|
|
@@ -195,6 +196,70 @@ function ConfirmModal({
|
|
|
195
196
|
] })
|
|
196
197
|
] }) });
|
|
197
198
|
}
|
|
199
|
+
var inputStyles = {
|
|
200
|
+
input: css`
|
|
201
|
+
width: 100%;
|
|
202
|
+
padding: 10px 12px;
|
|
203
|
+
font-size: ${fontSize.base};
|
|
204
|
+
border: 1px solid ${colors.border};
|
|
205
|
+
border-radius: 6px;
|
|
206
|
+
background: ${colors.surface};
|
|
207
|
+
color: ${colors.text};
|
|
208
|
+
margin-top: 12px;
|
|
209
|
+
transition: all 0.15s ease;
|
|
210
|
+
|
|
211
|
+
&:focus {
|
|
212
|
+
outline: none;
|
|
213
|
+
border-color: ${colors.primary};
|
|
214
|
+
box-shadow: 0 0 0 2px ${colors.primaryLight};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
&::placeholder {
|
|
218
|
+
color: ${colors.textMuted};
|
|
219
|
+
}
|
|
220
|
+
`
|
|
221
|
+
};
|
|
222
|
+
function InputModal({
|
|
223
|
+
title,
|
|
224
|
+
message,
|
|
225
|
+
inputLabel,
|
|
226
|
+
defaultValue = "",
|
|
227
|
+
placeholder,
|
|
228
|
+
confirmLabel = "Confirm",
|
|
229
|
+
cancelLabel = "Cancel",
|
|
230
|
+
onConfirm,
|
|
231
|
+
onCancel
|
|
232
|
+
}) {
|
|
233
|
+
const [value, setValue] = React.useState(defaultValue);
|
|
234
|
+
const handleSubmit = (e) => {
|
|
235
|
+
e.preventDefault();
|
|
236
|
+
if (value.trim()) {
|
|
237
|
+
onConfirm(value.trim());
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
return /* @__PURE__ */ jsx("div", { css: styles.overlay, onClick: onCancel, children: /* @__PURE__ */ jsx("div", { css: styles.modal, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, children: [
|
|
241
|
+
/* @__PURE__ */ jsx("div", { css: styles.header, children: /* @__PURE__ */ jsx("h3", { css: styles.title, children: title }) }),
|
|
242
|
+
/* @__PURE__ */ jsxs("div", { css: styles.body, children: [
|
|
243
|
+
message && /* @__PURE__ */ jsx("p", { css: styles.message, children: message }),
|
|
244
|
+
inputLabel && /* @__PURE__ */ jsx("label", { css: styles.message, children: inputLabel }),
|
|
245
|
+
/* @__PURE__ */ jsx(
|
|
246
|
+
"input",
|
|
247
|
+
{
|
|
248
|
+
css: inputStyles.input,
|
|
249
|
+
type: "text",
|
|
250
|
+
value,
|
|
251
|
+
onChange: (e) => setValue(e.target.value),
|
|
252
|
+
placeholder,
|
|
253
|
+
autoFocus: true
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
] }),
|
|
257
|
+
/* @__PURE__ */ jsxs("div", { css: styles.footer, children: [
|
|
258
|
+
/* @__PURE__ */ jsx("button", { type: "button", css: [styles.btn, styles.btnCancel], onClick: onCancel, children: cancelLabel }),
|
|
259
|
+
/* @__PURE__ */ jsx("button", { type: "submit", css: [styles.btn, styles.btnConfirm], disabled: !value.trim(), children: confirmLabel })
|
|
260
|
+
] })
|
|
261
|
+
] }) }) });
|
|
262
|
+
}
|
|
198
263
|
function AlertModal({
|
|
199
264
|
title,
|
|
200
265
|
message,
|
|
@@ -440,9 +505,14 @@ var styles2 = {
|
|
|
440
505
|
border-radius: 6px;
|
|
441
506
|
overflow: hidden;
|
|
442
507
|
`,
|
|
508
|
+
searchWrapper: css2`
|
|
509
|
+
position: relative;
|
|
510
|
+
display: flex;
|
|
511
|
+
align-items: center;
|
|
512
|
+
`,
|
|
443
513
|
searchInput: css2`
|
|
444
514
|
height: ${btnHeight};
|
|
445
|
-
padding: 0 12px;
|
|
515
|
+
padding: 0 32px 0 12px;
|
|
446
516
|
border: 1px solid ${colors.border};
|
|
447
517
|
border-radius: 6px;
|
|
448
518
|
font-size: ${fontSize.base};
|
|
@@ -461,6 +531,26 @@ var styles2 = {
|
|
|
461
531
|
color: ${colors.textMuted};
|
|
462
532
|
}
|
|
463
533
|
`,
|
|
534
|
+
searchClearBtn: css2`
|
|
535
|
+
position: absolute;
|
|
536
|
+
right: 6px;
|
|
537
|
+
top: 50%;
|
|
538
|
+
transform: translateY(-50%);
|
|
539
|
+
background: ${colors.textMuted};
|
|
540
|
+
border: none;
|
|
541
|
+
padding: 3px;
|
|
542
|
+
cursor: pointer;
|
|
543
|
+
color: white;
|
|
544
|
+
display: flex;
|
|
545
|
+
align-items: center;
|
|
546
|
+
justify-content: center;
|
|
547
|
+
border-radius: 50%;
|
|
548
|
+
transition: all 0.15s ease;
|
|
549
|
+
|
|
550
|
+
&:hover {
|
|
551
|
+
background: ${colors.text};
|
|
552
|
+
}
|
|
553
|
+
`,
|
|
464
554
|
viewBtn: css2`
|
|
465
555
|
height: 100%;
|
|
466
556
|
padding: 0 10px;
|
|
@@ -811,6 +901,12 @@ function StudioToolbar() {
|
|
|
811
901
|
const handleSearch = useCallback((e) => {
|
|
812
902
|
setSearchQuery(e.target.value);
|
|
813
903
|
}, [setSearchQuery]);
|
|
904
|
+
const handleSearchKeyDown = useCallback((e) => {
|
|
905
|
+
if (e.key === "Escape") {
|
|
906
|
+
setSearchQuery("");
|
|
907
|
+
e.target.blur();
|
|
908
|
+
}
|
|
909
|
+
}, [setSearchQuery]);
|
|
814
910
|
const hasSelection = selectedItems.size > 0;
|
|
815
911
|
const hasImagesSelected = Array.from(selectedItems).some(
|
|
816
912
|
(path) => path === "public/images" || path.startsWith("public/images/")
|
|
@@ -929,16 +1025,28 @@ function StudioToolbar() {
|
|
|
929
1025
|
]
|
|
930
1026
|
}
|
|
931
1027
|
),
|
|
932
|
-
/* @__PURE__ */
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1028
|
+
/* @__PURE__ */ jsxs2("div", { css: styles2.searchWrapper, children: [
|
|
1029
|
+
/* @__PURE__ */ jsx2(
|
|
1030
|
+
"input",
|
|
1031
|
+
{
|
|
1032
|
+
css: styles2.searchInput,
|
|
1033
|
+
type: "text",
|
|
1034
|
+
placeholder: "Search images...",
|
|
1035
|
+
value: searchQuery,
|
|
1036
|
+
onChange: handleSearch,
|
|
1037
|
+
onKeyDown: handleSearchKeyDown
|
|
1038
|
+
}
|
|
1039
|
+
),
|
|
1040
|
+
searchQuery && /* @__PURE__ */ jsx2(
|
|
1041
|
+
"button",
|
|
1042
|
+
{
|
|
1043
|
+
css: styles2.searchClearBtn,
|
|
1044
|
+
onClick: () => setSearchQuery(""),
|
|
1045
|
+
title: "Clear search",
|
|
1046
|
+
children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
1047
|
+
}
|
|
1048
|
+
)
|
|
1049
|
+
] })
|
|
942
1050
|
] }),
|
|
943
1051
|
/* @__PURE__ */ jsxs2("div", { css: styles2.right, children: [
|
|
944
1052
|
hasSelection && /* @__PURE__ */ jsxs2("span", { css: styles2.selectionCount, children: [
|
|
@@ -2365,6 +2473,9 @@ var styles5 = {
|
|
|
2365
2473
|
function StudioDetailView() {
|
|
2366
2474
|
const { focusedItem, setFocusedItem, triggerRefresh, clearSelection } = useStudio();
|
|
2367
2475
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState4(false);
|
|
2476
|
+
const [showRenameModal, setShowRenameModal] = useState4(false);
|
|
2477
|
+
const [showProcessConfirm, setShowProcessConfirm] = useState4(false);
|
|
2478
|
+
const [processProgress, setProcessProgress] = useState4(null);
|
|
2368
2479
|
const [alertMessage, setAlertMessage] = useState4(null);
|
|
2369
2480
|
const [showCopied, setShowCopied] = useState4(false);
|
|
2370
2481
|
if (!focusedItem) return null;
|
|
@@ -2380,10 +2491,35 @@ function StudioDetailView() {
|
|
|
2380
2491
|
setShowCopied(true);
|
|
2381
2492
|
setTimeout(() => setShowCopied(false), 1500);
|
|
2382
2493
|
};
|
|
2383
|
-
const handleRename = () => {
|
|
2384
|
-
|
|
2494
|
+
const handleRename = async (newName) => {
|
|
2495
|
+
setShowRenameModal(false);
|
|
2385
2496
|
if (newName && newName !== focusedItem.name) {
|
|
2386
|
-
|
|
2497
|
+
try {
|
|
2498
|
+
const response = await fetch("/api/studio/rename", {
|
|
2499
|
+
method: "POST",
|
|
2500
|
+
headers: { "Content-Type": "application/json" },
|
|
2501
|
+
body: JSON.stringify({
|
|
2502
|
+
oldPath: focusedItem.path,
|
|
2503
|
+
newName
|
|
2504
|
+
})
|
|
2505
|
+
});
|
|
2506
|
+
if (response.ok) {
|
|
2507
|
+
triggerRefresh();
|
|
2508
|
+
setFocusedItem(null);
|
|
2509
|
+
} else {
|
|
2510
|
+
const data = await response.json();
|
|
2511
|
+
setAlertMessage({
|
|
2512
|
+
title: "Rename Failed",
|
|
2513
|
+
message: data.error || "Failed to rename file"
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2516
|
+
} catch (error) {
|
|
2517
|
+
console.error("Rename error:", error);
|
|
2518
|
+
setAlertMessage({
|
|
2519
|
+
title: "Rename Failed",
|
|
2520
|
+
message: "An error occurred while renaming the file"
|
|
2521
|
+
});
|
|
2522
|
+
}
|
|
2387
2523
|
}
|
|
2388
2524
|
};
|
|
2389
2525
|
const handleDelete = async () => {
|
|
@@ -2416,8 +2552,59 @@ function StudioDetailView() {
|
|
|
2416
2552
|
const handleSync = () => {
|
|
2417
2553
|
console.log("Sync to CDN:", focusedItem.path);
|
|
2418
2554
|
};
|
|
2419
|
-
const
|
|
2420
|
-
|
|
2555
|
+
const handleProcessImage = async () => {
|
|
2556
|
+
setShowProcessConfirm(false);
|
|
2557
|
+
setProcessProgress({
|
|
2558
|
+
current: 0,
|
|
2559
|
+
total: 1,
|
|
2560
|
+
percent: 0,
|
|
2561
|
+
status: "processing",
|
|
2562
|
+
currentFile: focusedItem.name
|
|
2563
|
+
});
|
|
2564
|
+
try {
|
|
2565
|
+
const response = await fetch("/api/studio/reprocess", {
|
|
2566
|
+
method: "POST",
|
|
2567
|
+
headers: { "Content-Type": "application/json" },
|
|
2568
|
+
body: JSON.stringify({
|
|
2569
|
+
paths: [focusedItem.path]
|
|
2570
|
+
})
|
|
2571
|
+
});
|
|
2572
|
+
if (!response.ok) {
|
|
2573
|
+
throw new Error("Processing failed");
|
|
2574
|
+
}
|
|
2575
|
+
const reader = response.body?.getReader();
|
|
2576
|
+
if (!reader) {
|
|
2577
|
+
throw new Error("No response body");
|
|
2578
|
+
}
|
|
2579
|
+
const decoder = new TextDecoder();
|
|
2580
|
+
let buffer = "";
|
|
2581
|
+
while (true) {
|
|
2582
|
+
const { done, value } = await reader.read();
|
|
2583
|
+
if (done) break;
|
|
2584
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2585
|
+
const lines = buffer.split("\n");
|
|
2586
|
+
buffer = lines.pop() || "";
|
|
2587
|
+
for (const line of lines) {
|
|
2588
|
+
if (line.startsWith("data: ")) {
|
|
2589
|
+
try {
|
|
2590
|
+
const data = JSON.parse(line.slice(6));
|
|
2591
|
+
setProcessProgress(data);
|
|
2592
|
+
} catch {
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
triggerRefresh();
|
|
2598
|
+
} catch (error) {
|
|
2599
|
+
console.error("Process error:", error);
|
|
2600
|
+
setProcessProgress({
|
|
2601
|
+
current: 0,
|
|
2602
|
+
total: 1,
|
|
2603
|
+
percent: 0,
|
|
2604
|
+
status: "error",
|
|
2605
|
+
message: "Failed to process image"
|
|
2606
|
+
});
|
|
2607
|
+
}
|
|
2421
2608
|
};
|
|
2422
2609
|
const renderMedia = () => {
|
|
2423
2610
|
if (isImage) {
|
|
@@ -2451,6 +2638,36 @@ function StudioDetailView() {
|
|
|
2451
2638
|
onClose: () => setAlertMessage(null)
|
|
2452
2639
|
}
|
|
2453
2640
|
),
|
|
2641
|
+
showRenameModal && /* @__PURE__ */ jsx5(
|
|
2642
|
+
InputModal,
|
|
2643
|
+
{
|
|
2644
|
+
title: "Rename File",
|
|
2645
|
+
message: "Enter a new name for the file:",
|
|
2646
|
+
defaultValue: focusedItem.name,
|
|
2647
|
+
placeholder: "Enter new filename",
|
|
2648
|
+
confirmLabel: "Rename",
|
|
2649
|
+
onConfirm: handleRename,
|
|
2650
|
+
onCancel: () => setShowRenameModal(false)
|
|
2651
|
+
}
|
|
2652
|
+
),
|
|
2653
|
+
showProcessConfirm && /* @__PURE__ */ jsx5(
|
|
2654
|
+
ConfirmModal,
|
|
2655
|
+
{
|
|
2656
|
+
title: "Process Image",
|
|
2657
|
+
message: `Generate thumbnails for "${focusedItem.name}"?`,
|
|
2658
|
+
confirmLabel: "Process",
|
|
2659
|
+
onConfirm: handleProcessImage,
|
|
2660
|
+
onCancel: () => setShowProcessConfirm(false)
|
|
2661
|
+
}
|
|
2662
|
+
),
|
|
2663
|
+
processProgress && /* @__PURE__ */ jsx5(
|
|
2664
|
+
ProgressModal,
|
|
2665
|
+
{
|
|
2666
|
+
title: "Processing Image",
|
|
2667
|
+
progress: processProgress,
|
|
2668
|
+
onClose: () => setProcessProgress(null)
|
|
2669
|
+
}
|
|
2670
|
+
),
|
|
2454
2671
|
/* @__PURE__ */ jsx5("div", { css: styles5.overlay, onClick: handleClose, children: /* @__PURE__ */ jsxs5("div", { css: styles5.container, onClick: (e) => e.stopPropagation(), children: [
|
|
2455
2672
|
/* @__PURE__ */ jsxs5("div", { css: styles5.main, children: [
|
|
2456
2673
|
/* @__PURE__ */ jsxs5("div", { css: styles5.headerButtons, children: [
|
|
@@ -2492,7 +2709,7 @@ function StudioDetailView() {
|
|
|
2492
2709
|
] })
|
|
2493
2710
|
] }),
|
|
2494
2711
|
/* @__PURE__ */ jsxs5("div", { css: styles5.actions, children: [
|
|
2495
|
-
/* @__PURE__ */ jsxs5("button", { css: styles5.actionBtn, onClick:
|
|
2712
|
+
/* @__PURE__ */ jsxs5("button", { css: styles5.actionBtn, onClick: () => setShowRenameModal(true), children: [
|
|
2496
2713
|
/* @__PURE__ */ jsx5("svg", { css: styles5.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" }) }),
|
|
2497
2714
|
"Rename"
|
|
2498
2715
|
] }),
|
|
@@ -2500,9 +2717,9 @@ function StudioDetailView() {
|
|
|
2500
2717
|
/* @__PURE__ */ jsx5("svg", { css: styles5.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" }) }),
|
|
2501
2718
|
"Sync to CDN"
|
|
2502
2719
|
] }),
|
|
2503
|
-
/* @__PURE__ */ jsxs5("button", { css: styles5.actionBtn, onClick:
|
|
2504
|
-
/* @__PURE__ */ jsx5("svg", { css: styles5.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4
|
|
2505
|
-
"
|
|
2720
|
+
/* @__PURE__ */ jsxs5("button", { css: styles5.actionBtn, onClick: () => setShowProcessConfirm(true), children: [
|
|
2721
|
+
/* @__PURE__ */ jsx5("svg", { css: styles5.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 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" }) }),
|
|
2722
|
+
"Process Image"
|
|
2506
2723
|
] }),
|
|
2507
2724
|
/* @__PURE__ */ jsxs5("button", { css: [styles5.actionBtn, styles5.actionBtnDanger], onClick: () => setShowDeleteConfirm(true), children: [
|
|
2508
2725
|
/* @__PURE__ */ jsx5("svg", { css: styles5.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) }),
|
|
@@ -3139,4 +3356,4 @@ export {
|
|
|
3139
3356
|
StudioUI,
|
|
3140
3357
|
StudioUI_default as default
|
|
3141
3358
|
};
|
|
3142
|
-
//# sourceMappingURL=StudioUI-
|
|
3359
|
+
//# sourceMappingURL=StudioUI-5QGFJQXF.mjs.map
|