@gallop.software/studio 2.3.88 → 2.3.90
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/client/index.html
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
12
12
|
}
|
|
13
13
|
</style>
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-D7CzOTkD.js"></script>
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
package/dist/server/index.js
CHANGED
|
@@ -3967,8 +3967,8 @@ import sharp4 from "sharp";
|
|
|
3967
3967
|
function parseImageUrl(url) {
|
|
3968
3968
|
const parsed = new URL(url);
|
|
3969
3969
|
const base = `${parsed.protocol}//${parsed.host}`;
|
|
3970
|
-
const
|
|
3971
|
-
return { base, path:
|
|
3970
|
+
const path11 = parsed.pathname;
|
|
3971
|
+
return { base, path: path11 };
|
|
3972
3972
|
}
|
|
3973
3973
|
async function processRemoteImage(url) {
|
|
3974
3974
|
const response = await fetch(url);
|
|
@@ -4029,10 +4029,10 @@ async function handleImportUrls(request) {
|
|
|
4029
4029
|
const url = urls[i].trim();
|
|
4030
4030
|
if (!url) continue;
|
|
4031
4031
|
try {
|
|
4032
|
-
const { base, path:
|
|
4033
|
-
const existingEntry = getMetaEntry(meta,
|
|
4032
|
+
const { base, path: path11 } = parseImageUrl(url);
|
|
4033
|
+
const existingEntry = getMetaEntry(meta, path11);
|
|
4034
4034
|
if (existingEntry) {
|
|
4035
|
-
skipped.push(
|
|
4035
|
+
skipped.push(path11);
|
|
4036
4036
|
sendEvent({
|
|
4037
4037
|
type: "progress",
|
|
4038
4038
|
current: i + 1,
|
|
@@ -4045,13 +4045,13 @@ async function handleImportUrls(request) {
|
|
|
4045
4045
|
}
|
|
4046
4046
|
const cdnIndex = getOrAddCdnIndex(meta, base);
|
|
4047
4047
|
const imageData = await processRemoteImage(url);
|
|
4048
|
-
setMetaEntry(meta,
|
|
4048
|
+
setMetaEntry(meta, path11, {
|
|
4049
4049
|
o: imageData.o,
|
|
4050
4050
|
b: imageData.b,
|
|
4051
4051
|
c: cdnIndex
|
|
4052
4052
|
});
|
|
4053
4053
|
await saveMeta(meta);
|
|
4054
|
-
added.push(
|
|
4054
|
+
added.push(path11);
|
|
4055
4055
|
sendEvent({
|
|
4056
4056
|
type: "progress",
|
|
4057
4057
|
current: i + 1,
|
|
@@ -4300,8 +4300,8 @@ async function handleGenerateFeaturedImage(request) {
|
|
|
4300
4300
|
}
|
|
4301
4301
|
} catch {
|
|
4302
4302
|
}
|
|
4303
|
-
const outputPath = getPublicPath(`
|
|
4304
|
-
const relativePath = `public/
|
|
4303
|
+
const outputPath = getPublicPath(`screenshot.jpg`);
|
|
4304
|
+
const relativePath = `public/screenshot.jpg`;
|
|
4305
4305
|
sendEvent({
|
|
4306
4306
|
type: "start",
|
|
4307
4307
|
total: 4,
|
|
@@ -4363,7 +4363,7 @@ async function handleGenerateFeaturedImage(request) {
|
|
|
4363
4363
|
const width = metadata.width || 0;
|
|
4364
4364
|
const height = metadata.height || 0;
|
|
4365
4365
|
const meta = await loadMeta();
|
|
4366
|
-
const metaKey = `/
|
|
4366
|
+
const metaKey = `/screenshot.jpg`;
|
|
4367
4367
|
setMetaEntry(meta, metaKey, {
|
|
4368
4368
|
o: { w: width, h: height }
|
|
4369
4369
|
});
|
|
@@ -4373,7 +4373,7 @@ async function handleGenerateFeaturedImage(request) {
|
|
|
4373
4373
|
processed: 1,
|
|
4374
4374
|
errors: 0,
|
|
4375
4375
|
outputPath: relativePath,
|
|
4376
|
-
message: `
|
|
4376
|
+
message: `Screenshot saved to ${relativePath}`
|
|
4377
4377
|
});
|
|
4378
4378
|
} finally {
|
|
4379
4379
|
await browser.close();
|
|
@@ -4384,7 +4384,7 @@ async function handleGenerateFeaturedImage(request) {
|
|
|
4384
4384
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
4385
4385
|
sendEvent({
|
|
4386
4386
|
type: "error",
|
|
4387
|
-
message: `Failed to generate
|
|
4387
|
+
message: `Failed to generate screenshot: ${errorMessage}`
|
|
4388
4388
|
});
|
|
4389
4389
|
controller.close();
|
|
4390
4390
|
}
|
|
@@ -4439,8 +4439,8 @@ async function handleGetFeaturedImageOptions() {
|
|
|
4439
4439
|
}
|
|
4440
4440
|
async function handleCheckFeaturedImage() {
|
|
4441
4441
|
try {
|
|
4442
|
-
const expectedFilename = `
|
|
4443
|
-
const metaKey = `/
|
|
4442
|
+
const expectedFilename = `screenshot.jpg`;
|
|
4443
|
+
const metaKey = `/screenshot.jpg`;
|
|
4444
4444
|
const meta = await loadMeta();
|
|
4445
4445
|
const exists = metaKey in meta && !Array.isArray(meta[metaKey]);
|
|
4446
4446
|
return jsonResponse({
|
|
@@ -4456,6 +4456,124 @@ async function handleCheckFeaturedImage() {
|
|
|
4456
4456
|
}
|
|
4457
4457
|
}
|
|
4458
4458
|
|
|
4459
|
+
// src/handlers/edit-image.ts
|
|
4460
|
+
import { promises as fs11 } from "fs";
|
|
4461
|
+
import path10 from "path";
|
|
4462
|
+
import sharp7 from "sharp";
|
|
4463
|
+
async function handleEditImage(request) {
|
|
4464
|
+
try {
|
|
4465
|
+
const body = await request.json();
|
|
4466
|
+
const { imagePath, crop, rotation, resize } = body;
|
|
4467
|
+
if (!imagePath || !imagePath.startsWith("public/")) {
|
|
4468
|
+
return jsonResponse({ error: "Invalid image path" }, { status: 400 });
|
|
4469
|
+
}
|
|
4470
|
+
if (!isImageFile(path10.basename(imagePath))) {
|
|
4471
|
+
return jsonResponse({ error: "Not an image file" }, { status: 400 });
|
|
4472
|
+
}
|
|
4473
|
+
const absolutePath = getWorkspacePath(imagePath);
|
|
4474
|
+
try {
|
|
4475
|
+
await fs11.access(absolutePath);
|
|
4476
|
+
} catch {
|
|
4477
|
+
return jsonResponse(
|
|
4478
|
+
{ error: "Image file not found locally. Download it first." },
|
|
4479
|
+
{ status: 404 }
|
|
4480
|
+
);
|
|
4481
|
+
}
|
|
4482
|
+
const imageBuffer = await fs11.readFile(absolutePath);
|
|
4483
|
+
let pipeline = sharp7(imageBuffer).rotate();
|
|
4484
|
+
if (rotation !== 0) {
|
|
4485
|
+
pipeline = pipeline.rotate(rotation);
|
|
4486
|
+
}
|
|
4487
|
+
const rotatedBuffer = await pipeline.toBuffer();
|
|
4488
|
+
const rotatedMeta = await sharp7(rotatedBuffer).metadata();
|
|
4489
|
+
const rotatedWidth = rotatedMeta.width || 0;
|
|
4490
|
+
const rotatedHeight = rotatedMeta.height || 0;
|
|
4491
|
+
const cropX = Math.max(0, Math.min(crop.x, rotatedWidth - 1));
|
|
4492
|
+
const cropY = Math.max(0, Math.min(crop.y, rotatedHeight - 1));
|
|
4493
|
+
const cropWidth = Math.min(crop.width, rotatedWidth - cropX);
|
|
4494
|
+
const cropHeight = Math.min(crop.height, rotatedHeight - cropY);
|
|
4495
|
+
let croppedPipeline = sharp7(rotatedBuffer);
|
|
4496
|
+
if (cropX > 0 || cropY > 0 || cropWidth < rotatedWidth || cropHeight < rotatedHeight) {
|
|
4497
|
+
croppedPipeline = croppedPipeline.extract({
|
|
4498
|
+
left: Math.round(cropX),
|
|
4499
|
+
top: Math.round(cropY),
|
|
4500
|
+
width: Math.round(cropWidth),
|
|
4501
|
+
height: Math.round(cropHeight)
|
|
4502
|
+
});
|
|
4503
|
+
}
|
|
4504
|
+
if (resize.width !== cropWidth || resize.height !== cropHeight) {
|
|
4505
|
+
croppedPipeline = croppedPipeline.resize(resize.width, resize.height);
|
|
4506
|
+
}
|
|
4507
|
+
const ext = path10.extname(imagePath).toLowerCase();
|
|
4508
|
+
let finalBuffer;
|
|
4509
|
+
if (ext === ".png") {
|
|
4510
|
+
finalBuffer = await croppedPipeline.png({ quality: 85 }).toBuffer();
|
|
4511
|
+
} else if (ext === ".webp") {
|
|
4512
|
+
finalBuffer = await croppedPipeline.webp({ quality: 85 }).toBuffer();
|
|
4513
|
+
} else if (ext === ".gif") {
|
|
4514
|
+
finalBuffer = await croppedPipeline.gif().toBuffer();
|
|
4515
|
+
} else {
|
|
4516
|
+
finalBuffer = await croppedPipeline.jpeg({ quality: 85 }).toBuffer();
|
|
4517
|
+
}
|
|
4518
|
+
const finalMeta = await sharp7(finalBuffer).metadata();
|
|
4519
|
+
const finalWidth = finalMeta.width || resize.width;
|
|
4520
|
+
const finalHeight = finalMeta.height || resize.height;
|
|
4521
|
+
await fs11.writeFile(absolutePath, finalBuffer);
|
|
4522
|
+
const meta = await loadMeta();
|
|
4523
|
+
const imageKey = "/" + imagePath.replace(/^public\//, "");
|
|
4524
|
+
const entry = meta[imageKey];
|
|
4525
|
+
const updatedEntry = {
|
|
4526
|
+
...entry,
|
|
4527
|
+
o: { w: finalWidth, h: finalHeight }
|
|
4528
|
+
};
|
|
4529
|
+
delete updatedEntry.sm;
|
|
4530
|
+
delete updatedEntry.md;
|
|
4531
|
+
delete updatedEntry.lg;
|
|
4532
|
+
delete updatedEntry.f;
|
|
4533
|
+
meta[imageKey] = updatedEntry;
|
|
4534
|
+
await saveMeta(meta);
|
|
4535
|
+
const thumbnailPaths = getAllThumbnailPaths(imageKey);
|
|
4536
|
+
for (const thumbPath of thumbnailPaths) {
|
|
4537
|
+
const absoluteThumbPath = getPublicPath(thumbPath);
|
|
4538
|
+
try {
|
|
4539
|
+
await fs11.unlink(absoluteThumbPath);
|
|
4540
|
+
} catch {
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
4543
|
+
const stats = await fs11.stat(absolutePath);
|
|
4544
|
+
const updatedItem = {
|
|
4545
|
+
name: path10.basename(imagePath),
|
|
4546
|
+
path: imagePath,
|
|
4547
|
+
type: "file",
|
|
4548
|
+
size: stats.size,
|
|
4549
|
+
dimensions: { width: finalWidth, height: finalHeight },
|
|
4550
|
+
hasSm: false,
|
|
4551
|
+
hasMd: false,
|
|
4552
|
+
hasLg: false,
|
|
4553
|
+
hasFull: false,
|
|
4554
|
+
hasThumbnail: false,
|
|
4555
|
+
// Preserve CDN status from original entry
|
|
4556
|
+
cdnPushed: entry?.c !== void 0,
|
|
4557
|
+
isRemote: false,
|
|
4558
|
+
// If we're editing, it's local
|
|
4559
|
+
hasUpdate: entry?.c !== void 0
|
|
4560
|
+
// If was on CDN, now has local update
|
|
4561
|
+
};
|
|
4562
|
+
return jsonResponse({
|
|
4563
|
+
success: true,
|
|
4564
|
+
updatedItem,
|
|
4565
|
+
dimensions: { width: finalWidth, height: finalHeight }
|
|
4566
|
+
});
|
|
4567
|
+
} catch (error) {
|
|
4568
|
+
console.error("Edit image error:", error);
|
|
4569
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
4570
|
+
return jsonResponse(
|
|
4571
|
+
{ error: `Failed to edit image: ${message}` },
|
|
4572
|
+
{ status: 500 }
|
|
4573
|
+
);
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
|
|
4459
4577
|
// src/server/index.ts
|
|
4460
4578
|
var __filename = fileURLToPath(import.meta.url);
|
|
4461
4579
|
var __dirname = dirname(__filename);
|
|
@@ -4534,6 +4652,7 @@ async function startServer(options) {
|
|
|
4534
4652
|
app.post("/api/studio/rename", wrapHandler(handleRename));
|
|
4535
4653
|
app.post("/api/studio/rename-stream", wrapHandler(handleRenameStream, true));
|
|
4536
4654
|
app.post("/api/studio/move", wrapHandler(handleMoveStream, true));
|
|
4655
|
+
app.post("/api/studio/edit-image", wrapHandler(handleEditImage));
|
|
4537
4656
|
app.post("/api/studio/sync", wrapHandler(handleSync, true));
|
|
4538
4657
|
app.post("/api/studio/sync-stream", wrapHandler(handleSyncStream, true));
|
|
4539
4658
|
app.post(
|