@gallop.software/studio 1.5.5 → 1.5.7
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-VIYONKHA.mjs → StudioUI-6Q7GX6IY.mjs} +165 -24
- package/dist/StudioUI-6Q7GX6IY.mjs.map +1 -0
- package/dist/{StudioUI-RRWDEHCI.js → StudioUI-O53YFD6Q.js} +174 -33
- package/dist/StudioUI-O53YFD6Q.js.map +1 -0
- package/dist/handlers/index.js +99 -0
- package/dist/handlers/index.js.map +1 -1
- package/dist/handlers/index.mjs +99 -0
- package/dist/handlers/index.mjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/StudioUI-RRWDEHCI.js.map +0 -1
- package/dist/StudioUI-VIYONKHA.mjs.map +0 -1
|
@@ -1752,6 +1752,8 @@ function StudioToolbar() {
|
|
|
1752
1752
|
const [syncImageCount, setSyncImageCount] = useState4(0);
|
|
1753
1753
|
const [syncHasRemote, setSyncHasRemote] = useState4(false);
|
|
1754
1754
|
const [syncHasLocal, setSyncHasLocal] = useState4(false);
|
|
1755
|
+
const [showDownloadConfirm, setShowDownloadConfirm] = useState4(false);
|
|
1756
|
+
const [downloadImageCount, setDownloadImageCount] = useState4(0);
|
|
1755
1757
|
const [showProgress, setShowProgress] = useState4(false);
|
|
1756
1758
|
const [progressTitle, setProgressTitle] = useState4("Processing Images");
|
|
1757
1759
|
const [progressState, setProgressState] = useState4({
|
|
@@ -2181,32 +2183,43 @@ function StudioToolbar() {
|
|
|
2181
2183
|
status: "processing",
|
|
2182
2184
|
currentFile: imageKey.replace(/^\//, "")
|
|
2183
2185
|
});
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2186
|
+
let success = false;
|
|
2187
|
+
let lastError;
|
|
2188
|
+
for (let attempt = 0; attempt < 3 && !success; attempt++) {
|
|
2189
|
+
try {
|
|
2190
|
+
const response = await fetch("/api/studio/sync", {
|
|
2191
|
+
method: "POST",
|
|
2192
|
+
headers: { "Content-Type": "application/json" },
|
|
2193
|
+
body: JSON.stringify({ imageKeys: [imageKey] })
|
|
2194
|
+
});
|
|
2195
|
+
const data = await response.json();
|
|
2196
|
+
if (!response.ok) {
|
|
2197
|
+
if (data.error?.includes("R2 not configured") || data.error?.includes("CLOUDFLARE_R2")) {
|
|
2198
|
+
setShowProgress(false);
|
|
2199
|
+
setShowR2SetupModal(true);
|
|
2200
|
+
return;
|
|
2201
|
+
}
|
|
2202
|
+
lastError = data.error || `Failed: ${imageKey}`;
|
|
2203
|
+
} else if (data.pushed?.length > 0) {
|
|
2204
|
+
pushed++;
|
|
2205
|
+
success = true;
|
|
2206
|
+
} else if (data.errors?.length > 0) {
|
|
2207
|
+
for (const errMsg of data.errors) {
|
|
2208
|
+
lastError = errMsg;
|
|
2209
|
+
}
|
|
2210
|
+
} else {
|
|
2211
|
+
success = true;
|
|
2196
2212
|
}
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
} else if (data.errors?.length > 0) {
|
|
2202
|
-
errors++;
|
|
2203
|
-
for (const errMsg of data.errors) {
|
|
2204
|
-
errorMessages.push(errMsg);
|
|
2213
|
+
} catch (err) {
|
|
2214
|
+
lastError = `Network error: ${imageKey}`;
|
|
2215
|
+
if (attempt < 2) {
|
|
2216
|
+
await new Promise((resolve) => setTimeout(resolve, 500 * (attempt + 1)));
|
|
2205
2217
|
}
|
|
2206
2218
|
}
|
|
2207
|
-
}
|
|
2219
|
+
}
|
|
2220
|
+
if (!success && lastError) {
|
|
2208
2221
|
errors++;
|
|
2209
|
-
errorMessages.push(
|
|
2222
|
+
errorMessages.push(lastError);
|
|
2210
2223
|
}
|
|
2211
2224
|
}
|
|
2212
2225
|
setProgressState({
|
|
@@ -2231,6 +2244,106 @@ function StudioToolbar() {
|
|
|
2231
2244
|
});
|
|
2232
2245
|
}
|
|
2233
2246
|
}, [selectedItems, clearSelection, triggerRefresh]);
|
|
2247
|
+
const handleDownloadClick = useCallback2(async () => {
|
|
2248
|
+
if (selectedItems.size === 0) return;
|
|
2249
|
+
const selectedPaths2 = Array.from(selectedItems);
|
|
2250
|
+
const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"];
|
|
2251
|
+
const selectedImagePaths = selectedPaths2.filter((p) => {
|
|
2252
|
+
const ext = p.split(".").pop()?.toLowerCase() || "";
|
|
2253
|
+
return imageExtensions.includes(ext);
|
|
2254
|
+
});
|
|
2255
|
+
if (selectedImagePaths.length === 0) {
|
|
2256
|
+
setAlertMessage({
|
|
2257
|
+
title: "No Images Found",
|
|
2258
|
+
message: "No images found in the selected items."
|
|
2259
|
+
});
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
2262
|
+
setDownloadImageCount(selectedImagePaths.length);
|
|
2263
|
+
setShowDownloadConfirm(true);
|
|
2264
|
+
}, [selectedItems]);
|
|
2265
|
+
const handleDownloadConfirm = useCallback2(async () => {
|
|
2266
|
+
setShowDownloadConfirm(false);
|
|
2267
|
+
const selectedPaths2 = Array.from(selectedItems);
|
|
2268
|
+
const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "bmp", "tiff", "tif"];
|
|
2269
|
+
const selectedImagePaths = selectedPaths2.filter((p) => {
|
|
2270
|
+
const ext = p.split(".").pop()?.toLowerCase() || "";
|
|
2271
|
+
return imageExtensions.includes(ext);
|
|
2272
|
+
});
|
|
2273
|
+
const imageKeys = selectedImagePaths.map((p) => "/" + p.replace(/^public\//, ""));
|
|
2274
|
+
setProgressTitle("Downloading from CDN");
|
|
2275
|
+
setShowProgress(true);
|
|
2276
|
+
setProgressState({
|
|
2277
|
+
current: 0,
|
|
2278
|
+
total: imageKeys.length,
|
|
2279
|
+
percent: 0,
|
|
2280
|
+
status: "processing"
|
|
2281
|
+
});
|
|
2282
|
+
try {
|
|
2283
|
+
const response = await fetch("/api/studio/download-stream", {
|
|
2284
|
+
method: "POST",
|
|
2285
|
+
headers: { "Content-Type": "application/json" },
|
|
2286
|
+
body: JSON.stringify({ imageKeys })
|
|
2287
|
+
});
|
|
2288
|
+
if (!response.ok || !response.body) {
|
|
2289
|
+
throw new Error("Download request failed");
|
|
2290
|
+
}
|
|
2291
|
+
const reader = response.body.getReader();
|
|
2292
|
+
const decoder = new TextDecoder();
|
|
2293
|
+
let buffer = "";
|
|
2294
|
+
while (true) {
|
|
2295
|
+
const { done, value } = await reader.read();
|
|
2296
|
+
if (done) break;
|
|
2297
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2298
|
+
const lines = buffer.split("\n");
|
|
2299
|
+
buffer = lines.pop() || "";
|
|
2300
|
+
for (const line of lines) {
|
|
2301
|
+
if (line.startsWith("data: ")) {
|
|
2302
|
+
try {
|
|
2303
|
+
const data = JSON.parse(line.slice(6));
|
|
2304
|
+
if (data.type === "progress") {
|
|
2305
|
+
setProgressState({
|
|
2306
|
+
current: data.current,
|
|
2307
|
+
total: data.total,
|
|
2308
|
+
percent: Math.round(data.current / data.total * 100),
|
|
2309
|
+
status: "processing",
|
|
2310
|
+
message: data.message
|
|
2311
|
+
});
|
|
2312
|
+
} else if (data.type === "complete") {
|
|
2313
|
+
setProgressState({
|
|
2314
|
+
current: data.total || imageKeys.length,
|
|
2315
|
+
total: data.total || imageKeys.length,
|
|
2316
|
+
percent: 100,
|
|
2317
|
+
status: "complete",
|
|
2318
|
+
message: data.message
|
|
2319
|
+
});
|
|
2320
|
+
} else if (data.type === "error") {
|
|
2321
|
+
setProgressState({
|
|
2322
|
+
current: 0,
|
|
2323
|
+
total: 0,
|
|
2324
|
+
percent: 0,
|
|
2325
|
+
status: "error",
|
|
2326
|
+
message: data.message
|
|
2327
|
+
});
|
|
2328
|
+
}
|
|
2329
|
+
} catch {
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
clearSelection();
|
|
2335
|
+
triggerRefresh();
|
|
2336
|
+
} catch (error) {
|
|
2337
|
+
console.error("Download error:", error);
|
|
2338
|
+
setProgressState({
|
|
2339
|
+
current: 0,
|
|
2340
|
+
total: 0,
|
|
2341
|
+
percent: 0,
|
|
2342
|
+
status: "error",
|
|
2343
|
+
message: "Failed to download from CDN."
|
|
2344
|
+
});
|
|
2345
|
+
}
|
|
2346
|
+
}, [selectedItems, clearSelection, triggerRefresh]);
|
|
2234
2347
|
const handleCreateFolder = useCallback2(async (folderName) => {
|
|
2235
2348
|
setShowNewFolderModal(false);
|
|
2236
2349
|
try {
|
|
@@ -2349,6 +2462,10 @@ function StudioToolbar() {
|
|
|
2349
2462
|
const item = fileItems.find((f) => f.path === path);
|
|
2350
2463
|
return item && item.cdnPushed && !item.isRemote;
|
|
2351
2464
|
});
|
|
2465
|
+
const allR2Selection = hasSelection && Array.from(selectedItems).every((path) => {
|
|
2466
|
+
const item = fileItems.find((f) => f.path === path);
|
|
2467
|
+
return item && item.type === "file" && item.cdnPushed && !item.isRemote;
|
|
2468
|
+
});
|
|
2352
2469
|
const selectedPaths = Array.from(selectedItems);
|
|
2353
2470
|
const singleFolderSelected = selectedPaths.length === 1 && !selectedPaths[0].includes(".");
|
|
2354
2471
|
const selectedFolderPath = singleFolderSelected ? selectedPaths[0] : null;
|
|
@@ -2395,6 +2512,16 @@ function StudioToolbar() {
|
|
|
2395
2512
|
onCancel: () => setShowSyncConfirm(false)
|
|
2396
2513
|
}
|
|
2397
2514
|
),
|
|
2515
|
+
showDownloadConfirm && /* @__PURE__ */ jsx5(
|
|
2516
|
+
ConfirmModal,
|
|
2517
|
+
{
|
|
2518
|
+
title: "Download from CDN",
|
|
2519
|
+
message: `Download ${downloadImageCount} image${downloadImageCount !== 1 ? "s" : ""} from Cloudflare R2 to local storage? Images will be removed from the CDN.`,
|
|
2520
|
+
confirmLabel: "Download",
|
|
2521
|
+
onConfirm: handleDownloadConfirm,
|
|
2522
|
+
onCancel: () => setShowDownloadConfirm(false)
|
|
2523
|
+
}
|
|
2524
|
+
),
|
|
2398
2525
|
showProgress && /* @__PURE__ */ jsx5(
|
|
2399
2526
|
ProgressModal,
|
|
2400
2527
|
{
|
|
@@ -2550,7 +2677,18 @@ function StudioToolbar() {
|
|
|
2550
2677
|
]
|
|
2551
2678
|
}
|
|
2552
2679
|
),
|
|
2553
|
-
/* @__PURE__ */ jsxs5(
|
|
2680
|
+
allR2Selection ? /* @__PURE__ */ jsxs5(
|
|
2681
|
+
"button",
|
|
2682
|
+
{
|
|
2683
|
+
css: styles5.btn,
|
|
2684
|
+
onClick: handleDownloadClick,
|
|
2685
|
+
disabled: !hasSelection,
|
|
2686
|
+
children: [
|
|
2687
|
+
/* @__PURE__ */ jsx5(CloudDownloadIcon, {}),
|
|
2688
|
+
"Download"
|
|
2689
|
+
]
|
|
2690
|
+
}
|
|
2691
|
+
) : /* @__PURE__ */ jsxs5(
|
|
2554
2692
|
"button",
|
|
2555
2693
|
{
|
|
2556
2694
|
css: styles5.btn,
|
|
@@ -2649,6 +2787,9 @@ function MoveIcon() {
|
|
|
2649
2787
|
function CloudIcon() {
|
|
2650
2788
|
return /* @__PURE__ */ jsx5("svg", { css: styles5.icon, 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" }) });
|
|
2651
2789
|
}
|
|
2790
|
+
function CloudDownloadIcon() {
|
|
2791
|
+
return /* @__PURE__ */ jsx5("svg", { css: styles5.icon, 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.9M9 19l3 3m0 0l3-3m-3 3V10" }) });
|
|
2792
|
+
}
|
|
2652
2793
|
function GridIcon() {
|
|
2653
2794
|
return /* @__PURE__ */ jsx5("svg", { css: styles5.icon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" }) });
|
|
2654
2795
|
}
|
|
@@ -6352,4 +6493,4 @@ export {
|
|
|
6352
6493
|
StudioUI,
|
|
6353
6494
|
StudioUI_default as default
|
|
6354
6495
|
};
|
|
6355
|
-
//# sourceMappingURL=StudioUI-
|
|
6496
|
+
//# sourceMappingURL=StudioUI-6Q7GX6IY.mjs.map
|