@gallop.software/studio 0.1.24 → 0.1.25
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-YO6WPG5E.js → StudioUI-4TDLHJCA.js} +208 -33
- package/dist/StudioUI-4TDLHJCA.js.map +1 -0
- package/dist/{StudioUI-F2C4N66F.mjs → StudioUI-BPOKRRW7.mjs} +214 -39
- package/dist/StudioUI-BPOKRRW7.mjs.map +1 -0
- package/dist/handlers.d.mts +2 -24
- package/dist/handlers.d.ts +2 -24
- package/dist/handlers.js +175 -149
- package/dist/handlers.js.map +1 -1
- package/dist/handlers.mjs +175 -149
- package/dist/handlers.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{types-DIXDq6Cy.d.mts → types-lg2VkHIb.d.mts} +1 -1
- package/dist/{types-DIXDq6Cy.d.ts → types-lg2VkHIb.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/StudioUI-F2C4N66F.mjs.map +0 -1
- package/dist/StudioUI-YO6WPG5E.js.map +0 -1
|
@@ -62,7 +62,7 @@ import { css as css2, keyframes as keyframes2 } from "@emotion/react";
|
|
|
62
62
|
|
|
63
63
|
// src/components/StudioModal.tsx
|
|
64
64
|
import { css, keyframes } from "@emotion/react";
|
|
65
|
-
import { jsx, jsxs } from "@emotion/react/jsx-runtime";
|
|
65
|
+
import { Fragment, jsx, jsxs } from "@emotion/react/jsx-runtime";
|
|
66
66
|
var fadeIn = keyframes`
|
|
67
67
|
from { opacity: 0; }
|
|
68
68
|
to { opacity: 1; }
|
|
@@ -204,9 +204,101 @@ function AlertModal({
|
|
|
204
204
|
/* @__PURE__ */ jsx("div", { css: styles.footer, children: /* @__PURE__ */ jsx("button", { css: [styles.btn, styles.btnConfirm], onClick: onClose, children: buttonLabel }) })
|
|
205
205
|
] }) });
|
|
206
206
|
}
|
|
207
|
+
var progressStyles = {
|
|
208
|
+
progressContainer: css`
|
|
209
|
+
margin-top: 16px;
|
|
210
|
+
`,
|
|
211
|
+
progressBar: css`
|
|
212
|
+
width: 100%;
|
|
213
|
+
height: 8px;
|
|
214
|
+
background-color: ${colors.background};
|
|
215
|
+
border-radius: 4px;
|
|
216
|
+
overflow: hidden;
|
|
217
|
+
margin-bottom: 12px;
|
|
218
|
+
`,
|
|
219
|
+
progressFill: css`
|
|
220
|
+
height: 100%;
|
|
221
|
+
background: linear-gradient(90deg, ${colors.primary}, ${colors.primaryHover});
|
|
222
|
+
border-radius: 4px;
|
|
223
|
+
transition: width 0.3s ease;
|
|
224
|
+
`,
|
|
225
|
+
progressText: css`
|
|
226
|
+
font-size: ${fontSize.sm};
|
|
227
|
+
color: ${colors.textSecondary};
|
|
228
|
+
margin: 0;
|
|
229
|
+
display: flex;
|
|
230
|
+
justify-content: space-between;
|
|
231
|
+
align-items: center;
|
|
232
|
+
`,
|
|
233
|
+
currentFile: css`
|
|
234
|
+
font-size: ${fontSize.xs};
|
|
235
|
+
color: ${colors.textMuted};
|
|
236
|
+
margin: 8px 0 0;
|
|
237
|
+
white-space: nowrap;
|
|
238
|
+
overflow: hidden;
|
|
239
|
+
text-overflow: ellipsis;
|
|
240
|
+
`
|
|
241
|
+
};
|
|
242
|
+
function ProgressModal({
|
|
243
|
+
title,
|
|
244
|
+
progress,
|
|
245
|
+
onClose
|
|
246
|
+
}) {
|
|
247
|
+
const isComplete = progress.status === "complete";
|
|
248
|
+
const isError = progress.status === "error";
|
|
249
|
+
const canClose = isComplete || isError;
|
|
250
|
+
return /* @__PURE__ */ jsx("div", { css: styles.overlay, onClick: canClose ? onClose : void 0, children: /* @__PURE__ */ jsxs("div", { css: styles.modal, onClick: (e) => e.stopPropagation(), children: [
|
|
251
|
+
/* @__PURE__ */ jsx("div", { css: styles.header, children: /* @__PURE__ */ jsx("h3", { css: styles.title, children: title }) }),
|
|
252
|
+
/* @__PURE__ */ jsx("div", { css: styles.body, children: isError ? /* @__PURE__ */ jsx("p", { css: styles.message, children: progress.message || "An error occurred" }) : isComplete ? /* @__PURE__ */ jsxs("p", { css: styles.message, children: [
|
|
253
|
+
"Processed ",
|
|
254
|
+
progress.processed,
|
|
255
|
+
" image",
|
|
256
|
+
progress.processed !== 1 ? "s" : "",
|
|
257
|
+
".",
|
|
258
|
+
progress.orphansRemoved && progress.orphansRemoved > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
259
|
+
" Removed ",
|
|
260
|
+
progress.orphansRemoved,
|
|
261
|
+
" orphaned thumbnail",
|
|
262
|
+
progress.orphansRemoved !== 1 ? "s" : "",
|
|
263
|
+
"."
|
|
264
|
+
] }),
|
|
265
|
+
progress.errors && progress.errors > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
266
|
+
" ",
|
|
267
|
+
progress.errors,
|
|
268
|
+
" error",
|
|
269
|
+
progress.errors !== 1 ? "s" : "",
|
|
270
|
+
" occurred."
|
|
271
|
+
] })
|
|
272
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
273
|
+
/* @__PURE__ */ jsx("p", { css: styles.message, children: progress.status === "cleanup" ? "Cleaning up orphaned files..." : `Processing images...` }),
|
|
274
|
+
/* @__PURE__ */ jsxs("div", { css: progressStyles.progressContainer, children: [
|
|
275
|
+
/* @__PURE__ */ jsx("div", { css: progressStyles.progressBar, children: /* @__PURE__ */ jsx(
|
|
276
|
+
"div",
|
|
277
|
+
{
|
|
278
|
+
css: progressStyles.progressFill,
|
|
279
|
+
style: { width: `${progress.percent}%` }
|
|
280
|
+
}
|
|
281
|
+
) }),
|
|
282
|
+
/* @__PURE__ */ jsxs("div", { css: progressStyles.progressText, children: [
|
|
283
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
284
|
+
progress.current,
|
|
285
|
+
" of ",
|
|
286
|
+
progress.total
|
|
287
|
+
] }),
|
|
288
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
289
|
+
progress.percent,
|
|
290
|
+
"%"
|
|
291
|
+
] })
|
|
292
|
+
] }),
|
|
293
|
+
progress.currentFile && /* @__PURE__ */ jsx("p", { css: progressStyles.currentFile, title: progress.currentFile, children: progress.currentFile })
|
|
294
|
+
] })
|
|
295
|
+
] }) }),
|
|
296
|
+
canClose && /* @__PURE__ */ jsx("div", { css: styles.footer, children: /* @__PURE__ */ jsx("button", { css: [styles.btn, styles.btnConfirm], onClick: onClose, children: "Done" }) })
|
|
297
|
+
] }) });
|
|
298
|
+
}
|
|
207
299
|
|
|
208
300
|
// src/components/StudioToolbar.tsx
|
|
209
|
-
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "@emotion/react/jsx-runtime";
|
|
301
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "@emotion/react/jsx-runtime";
|
|
210
302
|
var btnHeight = "36px";
|
|
211
303
|
var spin = keyframes2`
|
|
212
304
|
to { transform: rotate(360deg); }
|
|
@@ -351,6 +443,13 @@ function StudioToolbar() {
|
|
|
351
443
|
const [processing, setProcessing] = useState(false);
|
|
352
444
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
|
353
445
|
const [showProcessConfirm, setShowProcessConfirm] = useState(false);
|
|
446
|
+
const [showProgress, setShowProgress] = useState(false);
|
|
447
|
+
const [progressState, setProgressState] = useState({
|
|
448
|
+
current: 0,
|
|
449
|
+
total: 0,
|
|
450
|
+
percent: 0,
|
|
451
|
+
status: "processing"
|
|
452
|
+
});
|
|
354
453
|
const [processCount, setProcessCount] = useState(0);
|
|
355
454
|
const [processMode, setProcessMode] = useState("all");
|
|
356
455
|
const [alertMessage, setAlertMessage] = useState(null);
|
|
@@ -425,12 +524,12 @@ function StudioToolbar() {
|
|
|
425
524
|
setShowProcessConfirm(true);
|
|
426
525
|
} else {
|
|
427
526
|
try {
|
|
428
|
-
const response = await fetch("/api/studio/count-
|
|
527
|
+
const response = await fetch("/api/studio/count-images");
|
|
429
528
|
const data = await response.json();
|
|
430
529
|
if (data.count === 0) {
|
|
431
530
|
setAlertMessage({
|
|
432
|
-
title: "
|
|
433
|
-
message: "
|
|
531
|
+
title: "No Images Found",
|
|
532
|
+
message: "No images found in the public folder to process."
|
|
434
533
|
});
|
|
435
534
|
return;
|
|
436
535
|
}
|
|
@@ -438,10 +537,10 @@ function StudioToolbar() {
|
|
|
438
537
|
setProcessMode("all");
|
|
439
538
|
setShowProcessConfirm(true);
|
|
440
539
|
} catch (error) {
|
|
441
|
-
console.error("Failed to count
|
|
540
|
+
console.error("Failed to count images:", error);
|
|
442
541
|
setAlertMessage({
|
|
443
542
|
title: "Error",
|
|
444
|
-
message: "Failed to count
|
|
543
|
+
message: "Failed to count images."
|
|
445
544
|
});
|
|
446
545
|
}
|
|
447
546
|
}
|
|
@@ -451,28 +550,78 @@ function StudioToolbar() {
|
|
|
451
550
|
setProcessing(true);
|
|
452
551
|
try {
|
|
453
552
|
if (processMode === "all") {
|
|
553
|
+
setShowProgress(true);
|
|
554
|
+
setProgressState({
|
|
555
|
+
current: 0,
|
|
556
|
+
total: processCount,
|
|
557
|
+
percent: 0,
|
|
558
|
+
status: "processing"
|
|
559
|
+
});
|
|
454
560
|
const response = await fetch("/api/studio/process-all", {
|
|
455
561
|
method: "POST"
|
|
456
562
|
});
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
563
|
+
if (!response.body) {
|
|
564
|
+
throw new Error("No response body");
|
|
565
|
+
}
|
|
566
|
+
const reader = response.body.getReader();
|
|
567
|
+
const decoder = new TextDecoder();
|
|
568
|
+
while (true) {
|
|
569
|
+
const { done, value } = await reader.read();
|
|
570
|
+
if (done) break;
|
|
571
|
+
const text = decoder.decode(value);
|
|
572
|
+
const lines = text.split("\n\n").filter((line) => line.startsWith("data: "));
|
|
573
|
+
for (const line of lines) {
|
|
574
|
+
try {
|
|
575
|
+
const data = JSON.parse(line.replace("data: ", ""));
|
|
576
|
+
if (data.type === "start") {
|
|
577
|
+
setProgressState((prev) => ({
|
|
578
|
+
...prev,
|
|
579
|
+
total: data.total
|
|
580
|
+
}));
|
|
581
|
+
} else if (data.type === "progress") {
|
|
582
|
+
setProgressState({
|
|
583
|
+
current: data.current,
|
|
584
|
+
total: data.total,
|
|
585
|
+
percent: data.percent,
|
|
586
|
+
currentFile: data.currentFile,
|
|
587
|
+
status: "processing"
|
|
588
|
+
});
|
|
589
|
+
} else if (data.type === "cleanup") {
|
|
590
|
+
setProgressState((prev) => ({
|
|
591
|
+
...prev,
|
|
592
|
+
status: "cleanup",
|
|
593
|
+
currentFile: void 0
|
|
594
|
+
}));
|
|
595
|
+
} else if (data.type === "complete") {
|
|
596
|
+
setProgressState({
|
|
597
|
+
current: data.processed,
|
|
598
|
+
total: data.processed,
|
|
599
|
+
percent: 100,
|
|
600
|
+
status: "complete",
|
|
601
|
+
processed: data.processed,
|
|
602
|
+
orphansRemoved: data.orphansRemoved,
|
|
603
|
+
errors: data.errors
|
|
604
|
+
});
|
|
605
|
+
triggerRefresh();
|
|
606
|
+
} else if (data.type === "error") {
|
|
607
|
+
setProgressState((prev) => ({
|
|
608
|
+
...prev,
|
|
609
|
+
status: "error",
|
|
610
|
+
message: data.message
|
|
611
|
+
}));
|
|
612
|
+
}
|
|
613
|
+
} catch {
|
|
614
|
+
}
|
|
615
|
+
}
|
|
474
616
|
}
|
|
475
617
|
} else {
|
|
618
|
+
setShowProgress(true);
|
|
619
|
+
setProgressState({
|
|
620
|
+
current: 0,
|
|
621
|
+
total: processCount,
|
|
622
|
+
percent: 0,
|
|
623
|
+
status: "processing"
|
|
624
|
+
});
|
|
476
625
|
const selectedImageKeys = Array.from(selectedItems).filter((p) => {
|
|
477
626
|
const ext = p.split(".").pop()?.toLowerCase() || "";
|
|
478
627
|
return ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"].includes(ext);
|
|
@@ -484,29 +633,39 @@ function StudioToolbar() {
|
|
|
484
633
|
});
|
|
485
634
|
const data = await response.json();
|
|
486
635
|
if (response.ok) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
636
|
+
setProgressState({
|
|
637
|
+
current: data.processed?.length || 0,
|
|
638
|
+
total: data.processed?.length || 0,
|
|
639
|
+
percent: 100,
|
|
640
|
+
status: "complete",
|
|
641
|
+
processed: data.processed?.length || 0,
|
|
642
|
+
errors: data.errors?.length || 0
|
|
490
643
|
});
|
|
491
644
|
clearSelection();
|
|
492
645
|
triggerRefresh();
|
|
493
646
|
} else {
|
|
494
|
-
|
|
495
|
-
|
|
647
|
+
setProgressState({
|
|
648
|
+
current: 0,
|
|
649
|
+
total: 0,
|
|
650
|
+
percent: 0,
|
|
651
|
+
status: "error",
|
|
496
652
|
message: data.error || "Unknown error"
|
|
497
653
|
});
|
|
498
654
|
}
|
|
499
655
|
}
|
|
500
656
|
} catch (error) {
|
|
501
657
|
console.error("Processing error:", error);
|
|
502
|
-
|
|
503
|
-
|
|
658
|
+
setProgressState({
|
|
659
|
+
current: 0,
|
|
660
|
+
total: 0,
|
|
661
|
+
percent: 0,
|
|
662
|
+
status: "error",
|
|
504
663
|
message: "Processing failed. Check console for details."
|
|
505
664
|
});
|
|
506
665
|
} finally {
|
|
507
666
|
setProcessing(false);
|
|
508
667
|
}
|
|
509
|
-
}, [processMode, selectedItems, clearSelection, triggerRefresh]);
|
|
668
|
+
}, [processMode, processCount, selectedItems, clearSelection, triggerRefresh]);
|
|
510
669
|
const handleDeleteClick = useCallback(() => {
|
|
511
670
|
if (selectedItems.size === 0) return;
|
|
512
671
|
setShowDeleteConfirm(true);
|
|
@@ -547,7 +706,7 @@ function StudioToolbar() {
|
|
|
547
706
|
if (focusedItem) {
|
|
548
707
|
return null;
|
|
549
708
|
}
|
|
550
|
-
return /* @__PURE__ */ jsxs2(
|
|
709
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
551
710
|
showDeleteConfirm && /* @__PURE__ */ jsx2(
|
|
552
711
|
ConfirmModal,
|
|
553
712
|
{
|
|
@@ -563,12 +722,28 @@ function StudioToolbar() {
|
|
|
563
722
|
ConfirmModal,
|
|
564
723
|
{
|
|
565
724
|
title: "Process Images",
|
|
566
|
-
message: processMode === "all" ? `Found ${processCount}
|
|
725
|
+
message: processMode === "all" ? `Found ${processCount} image${processCount !== 1 ? "s" : ""} in the public folder. This will regenerate all thumbnails and remove any orphaned files from the images folder.` : `Process ${processCount} selected image${processCount !== 1 ? "s" : ""}? This will regenerate thumbnails for these files.`,
|
|
567
726
|
confirmLabel: processing ? "Processing..." : "Process",
|
|
568
727
|
onConfirm: handleProcessConfirm,
|
|
569
728
|
onCancel: () => setShowProcessConfirm(false)
|
|
570
729
|
}
|
|
571
730
|
),
|
|
731
|
+
showProgress && /* @__PURE__ */ jsx2(
|
|
732
|
+
ProgressModal,
|
|
733
|
+
{
|
|
734
|
+
title: "Processing Images",
|
|
735
|
+
progress: progressState,
|
|
736
|
+
onClose: () => {
|
|
737
|
+
setShowProgress(false);
|
|
738
|
+
setProgressState({
|
|
739
|
+
current: 0,
|
|
740
|
+
total: 0,
|
|
741
|
+
percent: 0,
|
|
742
|
+
status: "processing"
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
),
|
|
572
747
|
alertMessage && /* @__PURE__ */ jsx2(
|
|
573
748
|
AlertModal,
|
|
574
749
|
{
|
|
@@ -1434,7 +1609,7 @@ function formatFileSize2(bytes) {
|
|
|
1434
1609
|
// src/components/StudioDetailView.tsx
|
|
1435
1610
|
import { useState as useState4 } from "react";
|
|
1436
1611
|
import { css as css5 } from "@emotion/react";
|
|
1437
|
-
import { Fragment as
|
|
1612
|
+
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "@emotion/react/jsx-runtime";
|
|
1438
1613
|
var IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".ico", ".bmp", ".tiff", ".tif"];
|
|
1439
1614
|
var VIDEO_EXTENSIONS = [".mp4", ".webm", ".mov", ".avi", ".mkv", ".m4v"];
|
|
1440
1615
|
function isImageFile(filename) {
|
|
@@ -1675,7 +1850,7 @@ function StudioDetailView() {
|
|
|
1675
1850
|
/* @__PURE__ */ jsx5("p", { css: styles5.fileName, children: focusedItem.name })
|
|
1676
1851
|
] });
|
|
1677
1852
|
};
|
|
1678
|
-
return /* @__PURE__ */ jsxs5(
|
|
1853
|
+
return /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
1679
1854
|
showDeleteConfirm && /* @__PURE__ */ jsx5(
|
|
1680
1855
|
ConfirmModal,
|
|
1681
1856
|
{
|
|
@@ -1757,7 +1932,7 @@ function formatFileSize3(bytes) {
|
|
|
1757
1932
|
// src/components/StudioSettings.tsx
|
|
1758
1933
|
import { useState as useState5 } from "react";
|
|
1759
1934
|
import { css as css6 } from "@emotion/react";
|
|
1760
|
-
import { Fragment as
|
|
1935
|
+
import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "@emotion/react/jsx-runtime";
|
|
1761
1936
|
var btnHeight2 = "36px";
|
|
1762
1937
|
var styles6 = {
|
|
1763
1938
|
btn: css6`
|
|
@@ -1941,7 +2116,7 @@ var styles6 = {
|
|
|
1941
2116
|
};
|
|
1942
2117
|
function StudioSettings() {
|
|
1943
2118
|
const [isOpen, setIsOpen] = useState5(false);
|
|
1944
|
-
return /* @__PURE__ */ jsxs6(
|
|
2119
|
+
return /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
1945
2120
|
/* @__PURE__ */ jsx6("button", { css: styles6.btn, onClick: () => setIsOpen(true), "aria-label": "Settings", children: /* @__PURE__ */ jsxs6(
|
|
1946
2121
|
"svg",
|
|
1947
2122
|
{
|
|
@@ -2222,4 +2397,4 @@ export {
|
|
|
2222
2397
|
StudioUI,
|
|
2223
2398
|
StudioUI_default as default
|
|
2224
2399
|
};
|
|
2225
|
-
//# sourceMappingURL=StudioUI-
|
|
2400
|
+
//# sourceMappingURL=StudioUI-BPOKRRW7.mjs.map
|