@gallop.software/studio 1.4.1 → 1.4.3

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.
@@ -1741,15 +1741,13 @@ var styles5 = {
1741
1741
  `
1742
1742
  };
1743
1743
  function StudioToolbar() {
1744
- const { selectedItems, viewMode, setViewMode, clearSelection, currentPath, triggerRefresh, focusedItem, scanRequested, clearScanRequest, fileItems } = useStudio();
1744
+ const { selectedItems, viewMode, setViewMode, clearSelection, currentPath, triggerRefresh, focusedItem, scanRequested, clearScanRequest, fileItems, requestProcess, actionState } = useStudio();
1745
1745
  const fileInputRef = _react.useRef.call(void 0, null);
1746
1746
  const abortControllerRef = _react.useRef.call(void 0, null);
1747
1747
  const [showAddNewModal, setShowAddNewModal] = _react.useState.call(void 0, false);
1748
1748
  const [uploading, setUploading] = _react.useState.call(void 0, false);
1749
1749
  const [scanning, setScanning] = _react.useState.call(void 0, false);
1750
- const [processing, setProcessing] = _react.useState.call(void 0, false);
1751
1750
  const [showDeleteConfirm, setShowDeleteConfirm] = _react.useState.call(void 0, false);
1752
- const [showProcessConfirm, setShowProcessConfirm] = _react.useState.call(void 0, false);
1753
1751
  const [showSyncConfirm, setShowSyncConfirm] = _react.useState.call(void 0, false);
1754
1752
  const [syncImageCount, setSyncImageCount] = _react.useState.call(void 0, 0);
1755
1753
  const [syncHasRemote, setSyncHasRemote] = _react.useState.call(void 0, false);
@@ -1762,9 +1760,6 @@ function StudioToolbar() {
1762
1760
  percent: 0,
1763
1761
  status: "processing"
1764
1762
  });
1765
- const [processCount, setProcessCount] = _react.useState.call(void 0, 0);
1766
- const [processMode, setProcessMode] = _react.useState.call(void 0, "all");
1767
- const [imagesToProcess, setImagesToProcess] = _react.useState.call(void 0, []);
1768
1763
  const [alertMessage, setAlertMessage] = _react.useState.call(void 0, null);
1769
1764
  const [showNewFolderModal, setShowNewFolderModal] = _react.useState.call(void 0, false);
1770
1765
  const [showRenameFolderModal, setShowRenameFolderModal] = _react.useState.call(void 0, false);
@@ -1999,234 +1994,29 @@ function StudioToolbar() {
1999
1994
  });
2000
1995
  return;
2001
1996
  }
2002
- setProcessCount(selectedImagePaths.length);
2003
- setImagesToProcess(selectedImagePaths);
2004
- setProcessMode("selected");
2005
- setShowProcessConfirm(true);
1997
+ requestProcess(selectedImagePaths);
2006
1998
  } else {
2007
1999
  try {
2008
2000
  const response = await fetch("/api/studio/count-images");
2009
2001
  const data = await response.json();
2010
- if (data.count === 0) {
2002
+ if (!data.images || data.images.length === 0) {
2011
2003
  setAlertMessage({
2012
2004
  title: "No Images Found",
2013
2005
  message: "No images found in the public folder to process."
2014
2006
  });
2015
2007
  return;
2016
2008
  }
2017
- setProcessCount(data.count);
2018
- setProcessMode("all");
2019
- setShowProcessConfirm(true);
2009
+ const allImagePaths = data.images.map((img) => `public/${img}`);
2010
+ requestProcess(allImagePaths);
2020
2011
  } catch (error) {
2021
- console.error("Failed to count images:", error);
2012
+ console.error("Failed to get images:", error);
2022
2013
  setAlertMessage({
2023
2014
  title: "Error",
2024
- message: "Failed to count images."
2015
+ message: "Failed to get images."
2025
2016
  });
2026
2017
  }
2027
2018
  }
2028
- }, [selectedItems]);
2029
- const handleProcessConfirm = _react.useCallback.call(void 0, async () => {
2030
- setShowProcessConfirm(false);
2031
- setProcessing(true);
2032
- abortControllerRef.current = new AbortController();
2033
- const signal = abortControllerRef.current.signal;
2034
- try {
2035
- if (processMode === "all") {
2036
- setProgressTitle("Processing Images");
2037
- setShowProgress(true);
2038
- setProgressState({
2039
- current: 0,
2040
- total: processCount,
2041
- percent: 0,
2042
- status: "processing"
2043
- });
2044
- const response = await fetch("/api/studio/process-all", {
2045
- method: "POST",
2046
- signal
2047
- });
2048
- if (!response.body) {
2049
- throw new Error("No response body");
2050
- }
2051
- const reader = response.body.getReader();
2052
- const decoder = new TextDecoder();
2053
- try {
2054
- while (true) {
2055
- const { done, value } = await reader.read();
2056
- if (done) break;
2057
- if (signal.aborted) {
2058
- reader.cancel();
2059
- break;
2060
- }
2061
- const text = decoder.decode(value);
2062
- const lines = text.split("\n\n").filter((line) => line.startsWith("data: "));
2063
- for (const line of lines) {
2064
- try {
2065
- const data = JSON.parse(line.replace("data: ", ""));
2066
- if (data.type === "start") {
2067
- setProgressState((prev) => ({
2068
- ...prev,
2069
- total: data.total
2070
- }));
2071
- } else if (data.type === "progress") {
2072
- setProgressState({
2073
- current: data.current,
2074
- total: data.total,
2075
- percent: data.percent,
2076
- currentFile: data.currentFile,
2077
- status: "processing"
2078
- });
2079
- } else if (data.type === "cleanup") {
2080
- setProgressState((prev) => ({
2081
- ...prev,
2082
- status: "cleanup",
2083
- currentFile: void 0
2084
- }));
2085
- } else if (data.type === "complete") {
2086
- setProgressState({
2087
- current: data.processed,
2088
- total: data.processed,
2089
- percent: 100,
2090
- status: "complete",
2091
- processed: data.processed,
2092
- alreadyProcessed: data.alreadyProcessed,
2093
- orphansRemoved: data.orphansRemoved,
2094
- errors: data.errors
2095
- });
2096
- triggerRefresh();
2097
- } else if (data.type === "error") {
2098
- setProgressState((prev) => ({
2099
- ...prev,
2100
- status: "error",
2101
- message: data.message
2102
- }));
2103
- }
2104
- } catch (e3) {
2105
- }
2106
- }
2107
- }
2108
- } catch (err) {
2109
- if (signal.aborted) {
2110
- setProgressState((prev) => ({
2111
- ...prev,
2112
- status: "stopped",
2113
- processed: prev.current
2114
- }));
2115
- triggerRefresh();
2116
- } else {
2117
- throw err;
2118
- }
2119
- }
2120
- } else {
2121
- setShowProgress(true);
2122
- setProgressState({
2123
- current: 0,
2124
- total: processCount,
2125
- percent: 0,
2126
- status: "processing"
2127
- });
2128
- const selectedImageKeys = imagesToProcess.map((p) => {
2129
- const key = p.replace(/^public\//, "");
2130
- return key.startsWith("/") ? key : `/${key}`;
2131
- });
2132
- const response = await fetch("/api/studio/reprocess-stream", {
2133
- method: "POST",
2134
- headers: { "Content-Type": "application/json" },
2135
- body: JSON.stringify({ imageKeys: selectedImageKeys }),
2136
- signal
2137
- });
2138
- if (!response.ok) {
2139
- const error = await response.json();
2140
- setProgressState({
2141
- current: 0,
2142
- total: 0,
2143
- percent: 0,
2144
- status: "error",
2145
- message: error.error || "Unknown error"
2146
- });
2147
- } else {
2148
- const reader = _optionalChain([response, 'access', _20 => _20.body, 'optionalAccess', _21 => _21.getReader, 'call', _22 => _22()]);
2149
- const decoder = new TextDecoder();
2150
- if (reader) {
2151
- let buffer = "";
2152
- while (true) {
2153
- const { done, value } = await reader.read();
2154
- if (done) break;
2155
- buffer += decoder.decode(value, { stream: true });
2156
- const lines = buffer.split("\n");
2157
- buffer = lines.pop() || "";
2158
- for (const line of lines) {
2159
- if (line.startsWith("data: ")) {
2160
- try {
2161
- const data = JSON.parse(line.slice(6));
2162
- if (data.type === "start") {
2163
- setProgressState((prev) => ({
2164
- ...prev,
2165
- total: data.total
2166
- }));
2167
- } else if (data.type === "progress") {
2168
- setProgressState({
2169
- current: data.current,
2170
- total: data.total,
2171
- percent: data.percent,
2172
- status: "processing",
2173
- message: data.message
2174
- });
2175
- } else if (data.type === "cleanup") {
2176
- setProgressState((prev) => ({
2177
- ...prev,
2178
- status: "cleanup",
2179
- message: data.message
2180
- }));
2181
- } else if (data.type === "complete") {
2182
- setProgressState({
2183
- current: data.processed,
2184
- total: data.processed,
2185
- percent: 100,
2186
- status: data.errors > 0 ? "error" : "complete",
2187
- processed: data.processed,
2188
- message: data.message
2189
- });
2190
- clearSelection();
2191
- triggerRefresh();
2192
- } else if (data.type === "error") {
2193
- setProgressState((prev) => ({
2194
- ...prev,
2195
- status: "error",
2196
- message: data.message
2197
- }));
2198
- }
2199
- } catch (e4) {
2200
- }
2201
- }
2202
- }
2203
- }
2204
- }
2205
- }
2206
- }
2207
- } catch (error) {
2208
- if (signal.aborted) {
2209
- setProgressState((prev) => ({
2210
- ...prev,
2211
- status: "stopped",
2212
- processed: prev.current
2213
- }));
2214
- triggerRefresh();
2215
- } else {
2216
- console.error("Processing error:", error);
2217
- setProgressState({
2218
- current: 0,
2219
- total: 0,
2220
- percent: 0,
2221
- status: "error",
2222
- message: "Processing failed. Check console for details."
2223
- });
2224
- }
2225
- } finally {
2226
- setProcessing(false);
2227
- abortControllerRef.current = null;
2228
- }
2229
- }, [processMode, processCount, imagesToProcess, clearSelection, triggerRefresh]);
2019
+ }, [selectedItems, requestProcess]);
2230
2020
  const handleStopProcessing = _react.useCallback.call(void 0, () => {
2231
2021
  if (abortControllerRef.current) {
2232
2022
  abortControllerRef.current.abort();
@@ -2299,7 +2089,7 @@ function StudioToolbar() {
2299
2089
  const selectedPaths2 = Array.from(selectedItems);
2300
2090
  const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"];
2301
2091
  const selectedImagePaths = selectedPaths2.filter((p) => {
2302
- const ext = _optionalChain([p, 'access', _23 => _23.split, 'call', _24 => _24("."), 'access', _25 => _25.pop, 'call', _26 => _26(), 'optionalAccess', _27 => _27.toLowerCase, 'call', _28 => _28()]) || "";
2092
+ const ext = _optionalChain([p, 'access', _20 => _20.split, 'call', _21 => _21("."), 'access', _22 => _22.pop, 'call', _23 => _23(), 'optionalAccess', _24 => _24.toLowerCase, 'call', _25 => _25()]) || "";
2303
2093
  return imageExtensions.includes(ext);
2304
2094
  });
2305
2095
  const selectedFolders = selectedPaths2.filter((p) => !p.includes(".") || p.endsWith("/"));
@@ -2348,7 +2138,7 @@ function StudioToolbar() {
2348
2138
  const selectedPaths2 = Array.from(selectedItems);
2349
2139
  const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"];
2350
2140
  const selectedImagePaths = selectedPaths2.filter((p) => {
2351
- const ext = _optionalChain([p, 'access', _29 => _29.split, 'call', _30 => _30("."), 'access', _31 => _31.pop, 'call', _32 => _32(), 'optionalAccess', _33 => _33.toLowerCase, 'call', _34 => _34()]) || "";
2141
+ const ext = _optionalChain([p, 'access', _26 => _26.split, 'call', _27 => _27("."), 'access', _28 => _28.pop, 'call', _29 => _29(), 'optionalAccess', _30 => _30.toLowerCase, 'call', _31 => _31()]) || "";
2352
2142
  return imageExtensions.includes(ext);
2353
2143
  });
2354
2144
  const selectedFolders = selectedPaths2.filter((p) => !p.includes(".") || p.endsWith("/"));
@@ -2399,16 +2189,16 @@ function StudioToolbar() {
2399
2189
  });
2400
2190
  const data = await response.json();
2401
2191
  if (!response.ok) {
2402
- if (_optionalChain([data, 'access', _35 => _35.error, 'optionalAccess', _36 => _36.includes, 'call', _37 => _37("R2 not configured")]) || _optionalChain([data, 'access', _38 => _38.error, 'optionalAccess', _39 => _39.includes, 'call', _40 => _40("CLOUDFLARE_R2")])) {
2192
+ if (_optionalChain([data, 'access', _32 => _32.error, 'optionalAccess', _33 => _33.includes, 'call', _34 => _34("R2 not configured")]) || _optionalChain([data, 'access', _35 => _35.error, 'optionalAccess', _36 => _36.includes, 'call', _37 => _37("CLOUDFLARE_R2")])) {
2403
2193
  setShowProgress(false);
2404
2194
  setShowR2SetupModal(true);
2405
2195
  return;
2406
2196
  }
2407
2197
  errors++;
2408
2198
  errorMessages.push(data.error || `Failed: ${imageKey}`);
2409
- } else if (_optionalChain([data, 'access', _41 => _41.pushed, 'optionalAccess', _42 => _42.length]) > 0) {
2199
+ } else if (_optionalChain([data, 'access', _38 => _38.pushed, 'optionalAccess', _39 => _39.length]) > 0) {
2410
2200
  pushed++;
2411
- } else if (_optionalChain([data, 'access', _43 => _43.errors, 'optionalAccess', _44 => _44.length]) > 0) {
2201
+ } else if (_optionalChain([data, 'access', _40 => _40.errors, 'optionalAccess', _41 => _41.length]) > 0) {
2412
2202
  errors++;
2413
2203
  for (const errMsg of data.errors) {
2414
2204
  errorMessages.push(errMsg);
@@ -2530,7 +2320,7 @@ function StudioToolbar() {
2530
2320
  errorMessage: data.message
2531
2321
  }));
2532
2322
  }
2533
- } catch (e5) {
2323
+ } catch (e3) {
2534
2324
  }
2535
2325
  }
2536
2326
  }
@@ -2605,16 +2395,6 @@ function StudioToolbar() {
2605
2395
  onCancel: () => setShowSyncConfirm(false)
2606
2396
  }
2607
2397
  ),
2608
- showProcessConfirm && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2609
- ConfirmModal,
2610
- {
2611
- title: "Process Images",
2612
- 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.`,
2613
- confirmLabel: processing ? "Processing..." : "Process",
2614
- onConfirm: handleProcessConfirm,
2615
- onCancel: () => setShowProcessConfirm(false)
2616
- }
2617
- ),
2618
2398
  showProgress && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2619
2399
  ProgressModal,
2620
2400
  {
@@ -2738,11 +2518,11 @@ function StudioToolbar() {
2738
2518
  {
2739
2519
  css: styles5.btn,
2740
2520
  onClick: handleProcessImages,
2741
- disabled: processing || isInImagesFolder,
2521
+ disabled: actionState.showProgress || isInImagesFolder,
2742
2522
  title: isInImagesFolder ? "Cannot process images folder" : void 0,
2743
2523
  children: [
2744
2524
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ImageStackIcon, {}),
2745
- processing ? "Processing..." : "Process Images"
2525
+ "Process Images"
2746
2526
  ]
2747
2527
  }
2748
2528
  ),
@@ -5434,7 +5214,7 @@ function useStudioActions({
5434
5214
  });
5435
5215
  return;
5436
5216
  }
5437
- const reader = _optionalChain([response, 'access', _45 => _45.body, 'optionalAccess', _46 => _46.getReader, 'call', _47 => _47()]);
5217
+ const reader = _optionalChain([response, 'access', _42 => _42.body, 'optionalAccess', _43 => _43.getReader, 'call', _44 => _44()]);
5438
5218
  const decoder = new TextDecoder();
5439
5219
  if (reader) {
5440
5220
  let buffer = "";
@@ -5464,7 +5244,7 @@ function useStudioActions({
5464
5244
  status: "complete",
5465
5245
  message: `Moved ${data.moved} file${data.moved !== 1 ? "s" : ""}${data.errors > 0 ? `, ${data.errors} error${data.errors !== 1 ? "s" : ""}` : ""}`
5466
5246
  }));
5467
- if (data.errors > 0 && _optionalChain([data, 'access', _48 => _48.errorMessages, 'optionalAccess', _49 => _49.length]) > 0) {
5247
+ if (data.errors > 0 && _optionalChain([data, 'access', _45 => _45.errorMessages, 'optionalAccess', _46 => _46.length]) > 0) {
5468
5248
  showError("Move Failed", data.errorMessages.join("\n"));
5469
5249
  }
5470
5250
  clearSelection();
@@ -5477,7 +5257,7 @@ function useStudioActions({
5477
5257
  message: data.message || "Unknown error"
5478
5258
  }));
5479
5259
  }
5480
- } catch (e6) {
5260
+ } catch (e4) {
5481
5261
  }
5482
5262
  }
5483
5263
  }
@@ -5557,8 +5337,8 @@ function useStudioActions({
5557
5337
  });
5558
5338
  const isRemove = mode === "remove";
5559
5339
  const endpoint = isRemove ? "/api/studio/unprocess-stream" : "/api/studio/reprocess-stream";
5560
- const progressTitle = isRemove ? "Removing Thumbnails" : "Processing Images";
5561
- const progressMessage = isRemove ? "Removing thumbnails..." : "Processing images...";
5340
+ const progressTitle = isRemove ? "Removing Thumbnails" : "Generating Thumbnails";
5341
+ const progressMessage = isRemove ? "Removing thumbnails..." : "Generating thumbnails...";
5562
5342
  setActionState((prev) => ({
5563
5343
  ...prev,
5564
5344
  showProcessConfirm: false,
@@ -5592,7 +5372,7 @@ function useStudioActions({
5592
5372
  });
5593
5373
  return;
5594
5374
  }
5595
- const reader = _optionalChain([response, 'access', _50 => _50.body, 'optionalAccess', _51 => _51.getReader, 'call', _52 => _52()]);
5375
+ const reader = _optionalChain([response, 'access', _47 => _47.body, 'optionalAccess', _48 => _48.getReader, 'call', _49 => _49()]);
5596
5376
  const decoder = new TextDecoder();
5597
5377
  if (reader) {
5598
5378
  let buffer = "";
@@ -5641,7 +5421,7 @@ function useStudioActions({
5641
5421
  message: data.message
5642
5422
  }));
5643
5423
  }
5644
- } catch (e7) {
5424
+ } catch (e5) {
5645
5425
  }
5646
5426
  }
5647
5427
  }
@@ -5681,7 +5461,7 @@ function useStudioActions({
5681
5461
  setProgressState((prev) => ({
5682
5462
  ...prev,
5683
5463
  orphanedFiles: void 0,
5684
- message: _optionalChain([prev, 'access', _53 => _53.message, 'optionalAccess', _54 => _54.replace, 'call', _55 => _55(/Found \d+ orphaned thumbnail\(s\).*/, "Orphaned thumbnails deleted.")])
5464
+ message: _optionalChain([prev, 'access', _50 => _50.message, 'optionalAccess', _51 => _51.replace, 'call', _52 => _52(/Found \d+ orphaned thumbnail\(s\).*/, "Orphaned thumbnails deleted.")])
5685
5465
  }));
5686
5466
  triggerRefresh();
5687
5467
  } else {
@@ -6324,4 +6104,4 @@ var StudioUI_default = StudioUI;
6324
6104
 
6325
6105
 
6326
6106
  exports.StudioUI = StudioUI; exports.default = StudioUI_default;
6327
- //# sourceMappingURL=StudioUI-DBZLGGPL.js.map
6107
+ //# sourceMappingURL=StudioUI-2IMM2BEH.js.map