@gallop.software/studio 0.1.89 → 0.1.91

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.
@@ -50,6 +50,11 @@ var defaultState = {
50
50
  refreshKey: 0,
51
51
  triggerRefresh: () => {
52
52
  },
53
+ scanRequested: false,
54
+ triggerScan: () => {
55
+ },
56
+ clearScanRequest: () => {
57
+ },
53
58
  searchQuery: "",
54
59
  setSearchQuery: () => {
55
60
  },
@@ -352,9 +357,19 @@ function ProgressModal({
352
357
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { css: styles.message, children: [
353
358
  "Processed ",
354
359
  progress.processed,
355
- " image",
360
+ " new image",
356
361
  progress.processed !== 1 ? "s" : "",
357
362
  ".",
363
+ progress.alreadyProcessed !== void 0 && progress.alreadyProcessed > 0 ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
364
+ " ",
365
+ progress.alreadyProcessed,
366
+ " already processed."
367
+ ] }) : null,
368
+ progress.pushedToCloud !== void 0 && progress.pushedToCloud > 0 ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
369
+ " ",
370
+ progress.pushedToCloud,
371
+ " pushed to CDN."
372
+ ] }) : null,
358
373
  progress.orphansRemoved !== void 0 && progress.orphansRemoved > 0 ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
359
374
  " Removed ",
360
375
  progress.orphansRemoved,
@@ -1285,7 +1300,7 @@ var styles4 = {
1285
1300
  `
1286
1301
  };
1287
1302
  function StudioToolbar() {
1288
- const { selectedItems, viewMode, setViewMode, clearSelection, currentPath, triggerRefresh, focusedItem } = useStudio();
1303
+ const { selectedItems, viewMode, setViewMode, clearSelection, currentPath, triggerRefresh, focusedItem, scanRequested, clearScanRequest } = useStudio();
1289
1304
  const fileInputRef = _react.useRef.call(void 0, null);
1290
1305
  const abortControllerRef = _react.useRef.call(void 0, null);
1291
1306
  const [uploading, setUploading] = _react.useState.call(void 0, false);
@@ -1310,7 +1325,7 @@ function StudioToolbar() {
1310
1325
  const [showRenameFolderModal, setShowRenameFolderModal] = _react.useState.call(void 0, false);
1311
1326
  const [showMoveModal, setShowMoveModal] = _react.useState.call(void 0, false);
1312
1327
  const [showR2SetupModal, setShowR2SetupModal] = _react.useState.call(void 0, false);
1313
- const [syncing, setSyncing] = _react.useState.call(void 0, false);
1328
+ const [pushing, setPushing] = _react.useState.call(void 0, false);
1314
1329
  const isInImagesFolder = currentPath === "public/images" || currentPath.startsWith("public/images/");
1315
1330
  const handleUpload = _react.useCallback.call(void 0, () => {
1316
1331
  _optionalChain([fileInputRef, 'access', _2 => _2.current, 'optionalAccess', _3 => _3.click, 'call', _4 => _4()]);
@@ -1391,6 +1406,12 @@ function StudioToolbar() {
1391
1406
  setScanning(false);
1392
1407
  }
1393
1408
  }, [triggerRefresh]);
1409
+ _react.useEffect.call(void 0, () => {
1410
+ if (scanRequested && !scanning) {
1411
+ clearScanRequest();
1412
+ handleScan();
1413
+ }
1414
+ }, [scanRequested, scanning, clearScanRequest, handleScan]);
1394
1415
  const handleFileChange = _react.useCallback.call(void 0, async (e) => {
1395
1416
  const files = e.target.files;
1396
1417
  if (!files || files.length === 0) return;
@@ -1609,6 +1630,8 @@ function StudioToolbar() {
1609
1630
  percent: 100,
1610
1631
  status: "complete",
1611
1632
  processed: data.processed,
1633
+ alreadyProcessed: data.alreadyProcessed,
1634
+ pushedToCloud: data.pushedToCloud,
1612
1635
  orphansRemoved: data.orphansRemoved,
1613
1636
  errors: data.errors
1614
1637
  });
@@ -1797,10 +1820,10 @@ function StudioToolbar() {
1797
1820
  total: imageKeys.length,
1798
1821
  percent: 0,
1799
1822
  status: "processing",
1800
- message: "Syncing to CDN..."
1823
+ message: "Pushing to CDN..."
1801
1824
  });
1802
1825
  setShowProgress(true);
1803
- let synced = 0;
1826
+ let pushed = 0;
1804
1827
  let errors = 0;
1805
1828
  const errorMessages = [];
1806
1829
  try {
@@ -1828,8 +1851,8 @@ function StudioToolbar() {
1828
1851
  }
1829
1852
  errors++;
1830
1853
  errorMessages.push(data.error || `Failed: ${imageKey}`);
1831
- } else if (_optionalChain([data, 'access', _40 => _40.synced, 'optionalAccess', _41 => _41.length]) > 0) {
1832
- synced++;
1854
+ } else if (_optionalChain([data, 'access', _40 => _40.pushed, 'optionalAccess', _41 => _41.length]) > 0) {
1855
+ pushed++;
1833
1856
  } else if (_optionalChain([data, 'access', _42 => _42.errors, 'optionalAccess', _43 => _43.length]) > 0) {
1834
1857
  errors++;
1835
1858
  for (const errMsg of data.errors) {
@@ -1846,20 +1869,20 @@ function StudioToolbar() {
1846
1869
  total: imageKeys.length,
1847
1870
  percent: 100,
1848
1871
  status: "complete",
1849
- processed: synced,
1872
+ processed: pushed,
1850
1873
  errors,
1851
1874
  errorMessages: errorMessages.length > 0 ? errorMessages : void 0
1852
1875
  });
1853
1876
  clearSelection();
1854
1877
  triggerRefresh();
1855
1878
  } catch (error) {
1856
- console.error("Sync error:", error);
1879
+ console.error("Push error:", error);
1857
1880
  setProgressState({
1858
1881
  current: 0,
1859
1882
  total: 0,
1860
1883
  percent: 0,
1861
1884
  status: "error",
1862
- message: "Failed to sync to CDN."
1885
+ message: "Failed to push to CDN."
1863
1886
  });
1864
1887
  }
1865
1888
  }, [selectedItems, clearSelection, triggerRefresh]);
@@ -1974,8 +1997,8 @@ function StudioToolbar() {
1974
1997
  showSyncConfirm && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1975
1998
  ConfirmModal,
1976
1999
  {
1977
- title: "Sync to CDN",
1978
- message: `Sync ${syncImageCount} image${syncImageCount !== 1 ? "s" : ""} to Cloudflare R2? Images must be processed first. After syncing, local thumbnails will be deleted.`,
2000
+ title: "Push to CDN",
2001
+ message: `Push ${syncImageCount} image${syncImageCount !== 1 ? "s" : ""} to Cloudflare R2? Images must be processed first. After pushing, local files will be deleted.`,
1979
2002
  confirmLabel: "Sync",
1980
2003
  onConfirm: handleSyncConfirm,
1981
2004
  onCancel: () => setShowSyncConfirm(false)
@@ -2142,7 +2165,7 @@ function StudioToolbar() {
2142
2165
  disabled: !hasSelection,
2143
2166
  children: [
2144
2167
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, CloudIcon, {}),
2145
- "Sync CDN"
2168
+ "Push CDN"
2146
2169
  ]
2147
2170
  }
2148
2171
  ),
@@ -2315,7 +2338,7 @@ var StudioApiClient = class {
2315
2338
  return this.post("/api/studio/move", { paths, destination });
2316
2339
  }
2317
2340
  // Image handlers
2318
- async sync(imageKeys) {
2341
+ async push(imageKeys) {
2319
2342
  return this.post("/api/studio/sync", { imageKeys });
2320
2343
  }
2321
2344
  async reprocess(imageKeys) {
@@ -2343,6 +2366,7 @@ function useFileList() {
2343
2366
  refreshKey,
2344
2367
  setFocusedItem,
2345
2368
  triggerRefresh,
2369
+ triggerScan,
2346
2370
  searchQuery,
2347
2371
  showError
2348
2372
  } = useStudio();
@@ -2432,7 +2456,8 @@ function useFileList() {
2432
2456
  handleItemClick,
2433
2457
  handleOpen,
2434
2458
  handleGenerateThumbnail,
2435
- handleSelectAll
2459
+ handleSelectAll,
2460
+ triggerScan
2436
2461
  };
2437
2462
  }
2438
2463
 
@@ -2446,7 +2471,8 @@ var styles5 = {
2446
2471
  display: flex;
2447
2472
  align-items: center;
2448
2473
  justify-content: center;
2449
- height: 256px;
2474
+ flex: 1;
2475
+ min-height: 300px;
2450
2476
  `,
2451
2477
  spinner: _react3.css`
2452
2478
  width: 32px;
@@ -2461,7 +2487,8 @@ var styles5 = {
2461
2487
  flex-direction: column;
2462
2488
  align-items: center;
2463
2489
  justify-content: center;
2464
- height: 256px;
2490
+ flex: 1;
2491
+ min-height: 300px;
2465
2492
  color: ${_chunkUFCWGUAGjs.colors.textSecondary};
2466
2493
  `,
2467
2494
  emptyIcon: _react3.css`
@@ -2793,20 +2820,9 @@ function StudioFileGrid() {
2793
2820
  handleItemClick,
2794
2821
  handleOpen,
2795
2822
  handleGenerateThumbnail,
2796
- handleSelectAll
2823
+ handleSelectAll,
2824
+ triggerScan
2797
2825
  } = useFileList();
2798
- const [scanning, setScanning] = _react.useState.call(void 0, false);
2799
- const handleScan = async () => {
2800
- setScanning(true);
2801
- try {
2802
- await fetch("/api/studio/scan", { method: "POST" });
2803
- window.location.reload();
2804
- } catch (error) {
2805
- console.error("Scan failed:", error);
2806
- } finally {
2807
- setScanning(false);
2808
- }
2809
- };
2810
2826
  if (loading) {
2811
2827
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles5.loading, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles5.spinner }) });
2812
2828
  }
@@ -2819,9 +2835,8 @@ function StudioFileGrid() {
2819
2835
  "button",
2820
2836
  {
2821
2837
  css: styles5.scanButton,
2822
- onClick: handleScan,
2823
- disabled: scanning,
2824
- children: scanning ? "Scanning..." : "Scan for Files"
2838
+ onClick: triggerScan,
2839
+ children: "Scan for Files"
2825
2840
  }
2826
2841
  )
2827
2842
  ] });
@@ -2914,7 +2929,7 @@ function GridItem({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
2914
2929
  )
2915
2930
  }
2916
2931
  ),
2917
- item.cdnSynced && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles5.cdnBadge, children: "CDN" }),
2932
+ item.cdnPushed && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles5.cdnBadge, children: "CDN" }),
2918
2933
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles5.content, children: [
2919
2934
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
2920
2935
  "button",
@@ -3009,7 +3024,8 @@ var styles6 = {
3009
3024
  display: flex;
3010
3025
  align-items: center;
3011
3026
  justify-content: center;
3012
- height: 256px;
3027
+ flex: 1;
3028
+ min-height: 300px;
3013
3029
  `,
3014
3030
  spinner: _react3.css`
3015
3031
  width: 32px;
@@ -3024,7 +3040,8 @@ var styles6 = {
3024
3040
  flex-direction: column;
3025
3041
  align-items: center;
3026
3042
  justify-content: center;
3027
- height: 256px;
3043
+ flex: 1;
3044
+ min-height: 300px;
3028
3045
  color: ${_chunkUFCWGUAGjs.colors.textSecondary};
3029
3046
  `,
3030
3047
  emptyHint: _react3.css`
@@ -3349,20 +3366,9 @@ function StudioFileList() {
3349
3366
  handleItemClick,
3350
3367
  handleOpen,
3351
3368
  handleGenerateThumbnail,
3352
- handleSelectAll
3369
+ handleSelectAll,
3370
+ triggerScan
3353
3371
  } = useFileList();
3354
- const [scanning, setScanning] = _react.useState.call(void 0, false);
3355
- const handleScan = async () => {
3356
- setScanning(true);
3357
- try {
3358
- await fetch("/api/studio/scan", { method: "POST" });
3359
- window.location.reload();
3360
- } catch (error) {
3361
- console.error("Scan failed:", error);
3362
- } finally {
3363
- setScanning(false);
3364
- }
3365
- };
3366
3372
  if (loading) {
3367
3373
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles6.loading, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles6.spinner }) });
3368
3374
  }
@@ -3374,9 +3380,8 @@ function StudioFileList() {
3374
3380
  "button",
3375
3381
  {
3376
3382
  css: styles6.scanButton,
3377
- onClick: handleScan,
3378
- disabled: scanning,
3379
- children: scanning ? "Scanning..." : "Scan for Files"
3383
+ onClick: triggerScan,
3384
+ children: "Scan for Files"
3380
3385
  }
3381
3386
  )
3382
3387
  ] });
@@ -3510,7 +3515,7 @@ function ListRow({ item, isSelected, onClick, onOpen, onGenerateThumbnail }) {
3510
3515
  ] }) }),
3511
3516
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: [styles6.td, styles6.meta], children: isFolder ? item.fileCount !== void 0 ? `${item.fileCount} files` : "--" : item.size !== void 0 ? formatFileSize2(item.size) : "--" }),
3512
3517
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: [styles6.td, styles6.meta], children: isFolder ? item.totalSize !== void 0 ? formatFileSize2(item.totalSize) : "--" : item.dimensions ? `${item.dimensions.width}x${item.dimensions.height}` : "--" }),
3513
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles6.td, children: item.cdnSynced ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { css: styles6.cdnBadge, children: [
3518
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "td", { css: styles6.td, children: item.cdnPushed ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { css: styles6.cdnBadge, children: [
3514
3519
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles6.cdnIcon, fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "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" }) }),
3515
3520
  "Synced"
3516
3521
  ] }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles6.cdnEmpty, children: "--" }) })
@@ -3806,7 +3811,7 @@ function StudioDetailView() {
3806
3811
  const [processProgress, setProcessProgress] = _react.useState.call(void 0, null);
3807
3812
  const [alertMessage, setAlertMessage] = _react.useState.call(void 0, null);
3808
3813
  const [showCopied, setShowCopied] = _react.useState.call(void 0, false);
3809
- const [syncing, setSyncing] = _react.useState.call(void 0, false);
3814
+ const [pushing, setPushing] = _react.useState.call(void 0, false);
3810
3815
  if (!focusedItem) return null;
3811
3816
  const isImage = isImageFile(focusedItem.name);
3812
3817
  const isVideo = isVideoFile(focusedItem.name);
@@ -3880,7 +3885,7 @@ function StudioDetailView() {
3880
3885
  };
3881
3886
  const handleSync = async () => {
3882
3887
  const imageKey = "/" + focusedItem.path.replace(/^public\//, "");
3883
- setSyncing(true);
3888
+ setPushing(true);
3884
3889
  try {
3885
3890
  const response = await fetch("/api/studio/sync", {
3886
3891
  method: "POST",
@@ -3890,8 +3895,8 @@ function StudioDetailView() {
3890
3895
  const data = await response.json();
3891
3896
  if (response.ok) {
3892
3897
  setAlertMessage({
3893
- title: "Sync Complete",
3894
- message: "Successfully synced to CDN."
3898
+ title: "Push Complete",
3899
+ message: "Successfully pushed to CDN."
3895
3900
  });
3896
3901
  triggerRefresh();
3897
3902
  } else {
@@ -3911,7 +3916,7 @@ function StudioDetailView() {
3911
3916
  message: "Failed to sync to CDN. Check console for details."
3912
3917
  });
3913
3918
  } finally {
3914
- setSyncing(false);
3919
+ setPushing(false);
3915
3920
  }
3916
3921
  };
3917
3922
  const handleProcessImage = async () => {
@@ -4074,7 +4079,7 @@ function StudioDetailView() {
4074
4079
  ] }),
4075
4080
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.infoRow, children: [
4076
4081
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles7.infoLabel, children: "CDN Status" }),
4077
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles7.infoValue, children: focusedItem.cdnSynced ? "Synced" : "Not synced" })
4082
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { css: styles7.infoValue, children: focusedItem.cdnPushed ? "Pushed" : "Not pushed" })
4078
4083
  ] })
4079
4084
  ] }),
4080
4085
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles7.actions, children: [
@@ -4082,9 +4087,9 @@ function StudioDetailView() {
4082
4087
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles7.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "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" }) }),
4083
4088
  "Rename"
4084
4089
  ] }),
4085
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { css: styles7.actionBtn, onClick: handleSync, disabled: syncing, children: [
4090
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { css: styles7.actionBtn, onClick: handleSync, disabled: pushing, children: [
4086
4091
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles7.actionIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "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" }) }),
4087
- syncing ? "Syncing..." : "Sync to CDN"
4092
+ syncing ? "Pushing..." : "Push to CDN"
4088
4093
  ] }),
4089
4094
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { css: styles7.actionBtn, onClick: () => setShowProcessConfirm(true), children: [
4090
4095
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { css: styles7.actionIcon, 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 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" }) }),
@@ -4614,6 +4619,8 @@ var styles10 = {
4614
4619
  min-width: 0;
4615
4620
  overflow: auto;
4616
4621
  padding: 20px 24px;
4622
+ display: flex;
4623
+ flex-direction: column;
4617
4624
  `,
4618
4625
  dropOverlay: _react3.css`
4619
4626
  position: absolute;
@@ -4653,12 +4660,19 @@ function StudioUI({ onClose, isVisible = true }) {
4653
4660
  const [meta, setMeta] = _react.useState.call(void 0, null);
4654
4661
  const [isLoading, setIsLoading] = _react.useState.call(void 0, false);
4655
4662
  const [refreshKey, setRefreshKey] = _react.useState.call(void 0, 0);
4663
+ const [scanRequested, setScanRequested] = _react.useState.call(void 0, false);
4656
4664
  const [searchQuery, setSearchQuery] = _react.useState.call(void 0, "");
4657
4665
  const [error, setError] = _react.useState.call(void 0, null);
4658
4666
  const [isDragging, setIsDragging] = _react.useState.call(void 0, false);
4659
4667
  const triggerRefresh = _react.useCallback.call(void 0, () => {
4660
4668
  setRefreshKey((k) => k + 1);
4661
4669
  }, []);
4670
+ const triggerScan = _react.useCallback.call(void 0, () => {
4671
+ setScanRequested(true);
4672
+ }, []);
4673
+ const clearScanRequest = _react.useCallback.call(void 0, () => {
4674
+ setScanRequested(false);
4675
+ }, []);
4662
4676
  const showError = _react.useCallback.call(void 0, (title, message) => {
4663
4677
  setError({ title, message });
4664
4678
  }, []);
@@ -4795,6 +4809,9 @@ function StudioUI({ onClose, isVisible = true }) {
4795
4809
  setIsLoading,
4796
4810
  refreshKey,
4797
4811
  triggerRefresh,
4812
+ scanRequested,
4813
+ triggerScan,
4814
+ clearScanRequest,
4798
4815
  searchQuery,
4799
4816
  setSearchQuery,
4800
4817
  error,
@@ -4881,4 +4898,4 @@ var StudioUI_default = StudioUI;
4881
4898
 
4882
4899
 
4883
4900
  exports.StudioUI = StudioUI; exports.default = StudioUI_default;
4884
- //# sourceMappingURL=StudioUI-JQHRTF45.js.map
4901
+ //# sourceMappingURL=StudioUI-4376LEHU.js.map