@gallop.software/studio 0.1.116 → 1.0.1

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.
@@ -341,6 +341,7 @@ function ProgressModal({
341
341
  title,
342
342
  progress,
343
343
  onClose,
344
+ onDeleteOrphans,
344
345
  onStop
345
346
  }) {
346
347
  const isComplete = progress.status === "complete";
@@ -437,6 +438,12 @@ function ProgressModal({
437
438
  ] }) }),
438
439
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles.footer, children: [
439
440
  isRunning && onStop && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: [styles.btn, styles.btnDanger], onClick: onStop, children: "Stop" }),
441
+ canClose && progress.orphanedFiles && progress.orphanedFiles.length > 0 && onDeleteOrphans && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { css: [styles.btn, styles.btnDanger], onClick: onDeleteOrphans, children: [
442
+ "Delete ",
443
+ progress.orphanedFiles.length,
444
+ " Orphan",
445
+ progress.orphanedFiles.length !== 1 ? "s" : ""
446
+ ] }),
440
447
  canClose && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { css: [styles.btn, styles.btnConfirm], onClick: onClose, children: "Done" })
441
448
  ] })
442
449
  ] }) });
@@ -1766,7 +1773,16 @@ function StudioToolbar() {
1766
1773
  status: "processing",
1767
1774
  currentFile: data.currentFile
1768
1775
  });
1776
+ } else if (data.type === "cleanup") {
1777
+ setProgressState((prev) => ({
1778
+ ...prev,
1779
+ message: data.message
1780
+ }));
1769
1781
  } else if (data.type === "complete") {
1782
+ let message = data.renamed > 0 ? `${data.renamed} file(s) renamed due to conflicts. ` : "";
1783
+ if (data.orphanedFiles && data.orphanedFiles.length > 0) {
1784
+ message += `Found ${data.orphanedFiles.length} orphaned thumbnail(s) in images folder.`;
1785
+ }
1770
1786
  setProgressState({
1771
1787
  current: data.total || 0,
1772
1788
  total: data.total || 0,
@@ -1775,7 +1791,8 @@ function StudioToolbar() {
1775
1791
  processed: data.added,
1776
1792
  alreadyProcessed: data.existingCount,
1777
1793
  errors: data.errors,
1778
- message: data.renamed > 0 ? `${data.renamed} file(s) renamed due to conflicts` : void 0,
1794
+ orphanedFiles: data.orphanedFiles,
1795
+ message: message || void 0,
1779
1796
  isScan: true
1780
1797
  });
1781
1798
  triggerRefresh();
@@ -2121,6 +2138,38 @@ function StudioToolbar() {
2121
2138
  abortControllerRef.current.abort();
2122
2139
  }
2123
2140
  }, []);
2141
+ const handleDeleteOrphans = _react.useCallback.call(void 0, async () => {
2142
+ const orphanedFiles = progressState.orphanedFiles;
2143
+ if (!orphanedFiles || orphanedFiles.length === 0) return;
2144
+ try {
2145
+ const response = await fetch("/api/studio/delete-orphans", {
2146
+ method: "POST",
2147
+ headers: { "Content-Type": "application/json" },
2148
+ body: JSON.stringify({ paths: orphanedFiles })
2149
+ });
2150
+ const data = await response.json();
2151
+ if (response.ok) {
2152
+ setProgressState((prev) => ({
2153
+ ...prev,
2154
+ orphanedFiles: void 0,
2155
+ orphansRemoved: data.deleted,
2156
+ message: `Deleted ${data.deleted} orphaned thumbnail${data.deleted !== 1 ? "s" : ""}.`
2157
+ }));
2158
+ triggerRefresh();
2159
+ } else {
2160
+ setAlertMessage({
2161
+ title: "Delete Failed",
2162
+ message: data.error || "Failed to delete orphaned files."
2163
+ });
2164
+ }
2165
+ } catch (error) {
2166
+ console.error("Delete orphans error:", error);
2167
+ setAlertMessage({
2168
+ title: "Delete Failed",
2169
+ message: "Failed to delete orphaned files. Check console for details."
2170
+ });
2171
+ }
2172
+ }, [progressState.orphanedFiles, triggerRefresh]);
2124
2173
  const handleDeleteClick = _react.useCallback.call(void 0, () => {
2125
2174
  if (selectedItems.size === 0) return;
2126
2175
  setShowDeleteConfirm(true);
@@ -2478,6 +2527,7 @@ function StudioToolbar() {
2478
2527
  title: progressTitle,
2479
2528
  progress: progressState,
2480
2529
  onStop: handleStopProcessing,
2530
+ onDeleteOrphans: handleDeleteOrphans,
2481
2531
  onClose: () => {
2482
2532
  setShowProgress(false);
2483
2533
  setProgressState({
@@ -3387,7 +3437,7 @@ function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
3387
3437
  const [showCopied, setShowCopied] = _react.useState.call(void 0, false);
3388
3438
  const isFolder = item.type === "folder";
3389
3439
  const isImage = !isFolder && item.thumbnail !== void 0;
3390
- const isImagesFolder = isFolder && (item.name === "images" || item.path.includes("/images/"));
3440
+ const isProtected = item.isProtected || isFolder && item.name === "images" && item.path === "public/images";
3391
3441
  const handleCopyPath = (e) => {
3392
3442
  e.stopPropagation();
3393
3443
  const pathToCopy = "/" + item.path.replace(/^public\//, "");
@@ -3395,13 +3445,21 @@ function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
3395
3445
  setShowCopied(true);
3396
3446
  setTimeout(() => setShowCopied(false), 1500);
3397
3447
  };
3448
+ const handleClick = (e) => {
3449
+ if (isProtected) {
3450
+ e.stopPropagation();
3451
+ onOpen();
3452
+ return;
3453
+ }
3454
+ onClick(e);
3455
+ };
3398
3456
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
3399
3457
  "div",
3400
3458
  {
3401
- css: [styles6.item, isSelected && styles6.itemSelected],
3402
- onClick,
3459
+ css: [styles6.item, isSelected && !isProtected && styles6.itemSelected],
3460
+ onClick: handleClick,
3403
3461
  children: [
3404
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3462
+ !isProtected && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3405
3463
  "div",
3406
3464
  {
3407
3465
  css: styles6.checkboxWrapper,
@@ -3418,7 +3476,7 @@ function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
3418
3476
  }
3419
3477
  ),
3420
3478
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.content, children: [
3421
- item.cdnPushed && !item.isRemote && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles6.statusBtn, title: "Pushed to CDN", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles6.cloudIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z" }) }) }),
3479
+ item.cdnPushed && !item.isRemote && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles6.statusBtn, title: "Pushed to CDN", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles6.cloudIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z" }) }) }),
3422
3480
  item.isRemote && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles6.statusBtn, title: "Remote image", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles6.globeIcon, 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 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" }) }) }),
3423
3481
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
3424
3482
  "button",
@@ -3443,7 +3501,7 @@ function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
3443
3501
  children: "Open"
3444
3502
  }
3445
3503
  ),
3446
- isFolder ? isImagesFolder ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.imagesFolderWrapper, children: [
3504
+ isFolder ? isProtected ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles6.imagesFolderWrapper, children: [
3447
3505
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles6.imagesFolderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "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" }) }),
3448
3506
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles6.lockIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { fillRule: "evenodd", d: "M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z", clipRule: "evenodd" }) })
3449
3507
  ] }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles6.folderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M10 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" }) }) : isImage && item.thumbnail ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
@@ -3931,7 +3989,7 @@ function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
3931
3989
  const [showCopied, setShowCopied] = _react.useState.call(void 0, false);
3932
3990
  const isFolder = item.type === "folder";
3933
3991
  const isImage = !isFolder && item.thumbnail !== void 0;
3934
- const isImagesFolder = isFolder && (item.name === "images" || item.path.includes("/images/"));
3992
+ const isProtected = item.isProtected || isFolder && item.name === "images" && item.path === "public/images";
3935
3993
  const handleCopyPath = (e) => {
3936
3994
  e.stopPropagation();
3937
3995
  const pathToCopy = "/" + item.path.replace(/^public\//, "");
@@ -3939,18 +3997,26 @@ function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
3939
3997
  setShowCopied(true);
3940
3998
  setTimeout(() => setShowCopied(false), 1500);
3941
3999
  };
4000
+ const handleClick = (e) => {
4001
+ if (isProtected) {
4002
+ e.stopPropagation();
4003
+ onOpen();
4004
+ return;
4005
+ }
4006
+ onClick(e);
4007
+ };
3942
4008
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
3943
4009
  "tr",
3944
4010
  {
3945
- css: [styles7.row, isSelected && styles7.rowSelected],
3946
- onClick,
4011
+ css: [styles7.row, isSelected && !isProtected && styles7.rowSelected],
4012
+ onClick: handleClick,
3947
4013
  children: [
3948
4014
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3949
4015
  "td",
3950
4016
  {
3951
4017
  css: [styles7.td, styles7.checkboxCell],
3952
4018
  onClick: (e) => e.stopPropagation(),
3953
- children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
4019
+ children: !isProtected && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3954
4020
  "input",
3955
4021
  {
3956
4022
  type: "checkbox",
@@ -3962,13 +4028,13 @@ function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
3962
4028
  }
3963
4029
  ),
3964
4030
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles7.td, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.nameCell, children: [
3965
- isFolder ? isImagesFolder ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.imagesFolderWrapper, children: [
4031
+ isFolder ? isProtected ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.imagesFolderWrapper, children: [
3966
4032
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles7.imagesFolderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "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" }) }),
3967
4033
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles7.lockIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { fillRule: "evenodd", d: "M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z", clipRule: "evenodd" }) })
3968
4034
  ] }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles7.folderIconWrapper, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles7.folderIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M10 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" }) }) }) : isImage && item.thumbnail ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles7.thumbnailWrapper, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "img", { css: styles7.thumbnail, src: item.thumbnail, alt: item.name, loading: "lazy" }) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles7.thumbnailWrapper, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles7.fileIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" }) }) }),
3969
4035
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles7.name, title: item.name, children: truncateMiddle(item.name) }),
3970
4036
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.actionsCell, children: [
3971
- item.cdnPushed && !item.isRemote && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles7.statusBtn, title: "Pushed to CDN", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles7.cloudIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z" }) }) }),
4037
+ item.cdnPushed && !item.isRemote && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles7.statusBtn, title: "Pushed to CDN", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles7.cloudIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z" }) }) }),
3972
4038
  item.isRemote && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles7.statusBtn, title: "Remote image", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles7.globeIcon, 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 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" }) }) }),
3973
4039
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
3974
4040
  "button",
@@ -4629,7 +4695,7 @@ function StudioDetailView() {
4629
4695
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles8.overlay, onClick: handleClose, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles8.container, onClick: (e) => e.stopPropagation(), children: [
4630
4696
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles8.main, children: [
4631
4697
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles8.headerButtons, children: [
4632
- focusedItem.cdnPushed && !focusedItem.isRemote && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles8.statusIcon, title: "Pushed to CDN", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles8.cloudIcon, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z" }) }) }),
4698
+ focusedItem.cdnPushed && !focusedItem.isRemote && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles8.statusIcon, title: "Pushed to CDN", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles8.cloudIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z" }) }) }),
4633
4699
  focusedItem.isRemote && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles8.statusIcon, title: "Remote image", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles8.globeIcon, 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 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" }) }) }),
4634
4700
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { css: styles8.copyBtn, onClick: handleCopyPath, title: "Copy file path", children: [
4635
4701
  showCopied && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles8.tooltip, children: "Copied!" }),
@@ -5696,4 +5762,4 @@ var StudioUI_default = StudioUI;
5696
5762
 
5697
5763
 
5698
5764
  exports.StudioUI = StudioUI; exports.default = StudioUI_default;
5699
- //# sourceMappingURL=StudioUI-7LIOKKXE.js.map
5765
+ //# sourceMappingURL=StudioUI-GWMM47P7.js.map