@gallop.software/studio 2.3.89 → 2.3.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.
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-Bgp5iXtu.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,
|
|
@@ -4456,6 +4456,137 @@ 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
|
+
const exifCorrectedBuffer = await sharp7(imageBuffer).rotate().toBuffer();
|
|
4484
|
+
const exifMeta = await sharp7(exifCorrectedBuffer).metadata();
|
|
4485
|
+
const exifWidth = exifMeta.width || 0;
|
|
4486
|
+
const exifHeight = exifMeta.height || 0;
|
|
4487
|
+
let rotatedBuffer;
|
|
4488
|
+
let rotatedWidth;
|
|
4489
|
+
let rotatedHeight;
|
|
4490
|
+
if (rotation !== 0) {
|
|
4491
|
+
rotatedBuffer = await sharp7(exifCorrectedBuffer).rotate(rotation).toBuffer();
|
|
4492
|
+
if (rotation === 90 || rotation === 270) {
|
|
4493
|
+
rotatedWidth = exifHeight;
|
|
4494
|
+
rotatedHeight = exifWidth;
|
|
4495
|
+
} else {
|
|
4496
|
+
rotatedWidth = exifWidth;
|
|
4497
|
+
rotatedHeight = exifHeight;
|
|
4498
|
+
}
|
|
4499
|
+
} else {
|
|
4500
|
+
rotatedBuffer = exifCorrectedBuffer;
|
|
4501
|
+
rotatedWidth = exifWidth;
|
|
4502
|
+
rotatedHeight = exifHeight;
|
|
4503
|
+
}
|
|
4504
|
+
const cropX = Math.max(0, Math.min(crop.x, rotatedWidth - 1));
|
|
4505
|
+
const cropY = Math.max(0, Math.min(crop.y, rotatedHeight - 1));
|
|
4506
|
+
const cropWidth = Math.min(crop.width, rotatedWidth - cropX);
|
|
4507
|
+
const cropHeight = Math.min(crop.height, rotatedHeight - cropY);
|
|
4508
|
+
let croppedPipeline = sharp7(rotatedBuffer);
|
|
4509
|
+
if (cropX > 0 || cropY > 0 || cropWidth < rotatedWidth || cropHeight < rotatedHeight) {
|
|
4510
|
+
croppedPipeline = croppedPipeline.extract({
|
|
4511
|
+
left: Math.round(cropX),
|
|
4512
|
+
top: Math.round(cropY),
|
|
4513
|
+
width: Math.round(cropWidth),
|
|
4514
|
+
height: Math.round(cropHeight)
|
|
4515
|
+
});
|
|
4516
|
+
}
|
|
4517
|
+
if (resize.width !== cropWidth || resize.height !== cropHeight) {
|
|
4518
|
+
croppedPipeline = croppedPipeline.resize(resize.width, resize.height);
|
|
4519
|
+
}
|
|
4520
|
+
const ext = path10.extname(imagePath).toLowerCase();
|
|
4521
|
+
let finalBuffer;
|
|
4522
|
+
if (ext === ".png") {
|
|
4523
|
+
finalBuffer = await croppedPipeline.png({ quality: 85 }).toBuffer();
|
|
4524
|
+
} else if (ext === ".webp") {
|
|
4525
|
+
finalBuffer = await croppedPipeline.webp({ quality: 85 }).toBuffer();
|
|
4526
|
+
} else if (ext === ".gif") {
|
|
4527
|
+
finalBuffer = await croppedPipeline.gif().toBuffer();
|
|
4528
|
+
} else {
|
|
4529
|
+
finalBuffer = await croppedPipeline.jpeg({ quality: 85 }).toBuffer();
|
|
4530
|
+
}
|
|
4531
|
+
const finalMeta = await sharp7(finalBuffer).metadata();
|
|
4532
|
+
const finalWidth = finalMeta.width || resize.width;
|
|
4533
|
+
const finalHeight = finalMeta.height || resize.height;
|
|
4534
|
+
await fs11.writeFile(absolutePath, finalBuffer);
|
|
4535
|
+
const meta = await loadMeta();
|
|
4536
|
+
const imageKey = "/" + imagePath.replace(/^public\//, "");
|
|
4537
|
+
const entry = meta[imageKey];
|
|
4538
|
+
const updatedEntry = {
|
|
4539
|
+
...entry,
|
|
4540
|
+
o: { w: finalWidth, h: finalHeight }
|
|
4541
|
+
};
|
|
4542
|
+
delete updatedEntry.sm;
|
|
4543
|
+
delete updatedEntry.md;
|
|
4544
|
+
delete updatedEntry.lg;
|
|
4545
|
+
delete updatedEntry.f;
|
|
4546
|
+
meta[imageKey] = updatedEntry;
|
|
4547
|
+
await saveMeta(meta);
|
|
4548
|
+
const thumbnailPaths = getAllThumbnailPaths(imageKey);
|
|
4549
|
+
for (const thumbPath of thumbnailPaths) {
|
|
4550
|
+
const absoluteThumbPath = getPublicPath(thumbPath);
|
|
4551
|
+
try {
|
|
4552
|
+
await fs11.unlink(absoluteThumbPath);
|
|
4553
|
+
} catch {
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
const stats = await fs11.stat(absolutePath);
|
|
4557
|
+
const updatedItem = {
|
|
4558
|
+
name: path10.basename(imagePath),
|
|
4559
|
+
path: imagePath,
|
|
4560
|
+
type: "file",
|
|
4561
|
+
size: stats.size,
|
|
4562
|
+
dimensions: { width: finalWidth, height: finalHeight },
|
|
4563
|
+
hasSm: false,
|
|
4564
|
+
hasMd: false,
|
|
4565
|
+
hasLg: false,
|
|
4566
|
+
hasFull: false,
|
|
4567
|
+
hasThumbnail: false,
|
|
4568
|
+
// Preserve CDN status from original entry
|
|
4569
|
+
cdnPushed: entry?.c !== void 0,
|
|
4570
|
+
isRemote: false,
|
|
4571
|
+
// If we're editing, it's local
|
|
4572
|
+
hasUpdate: entry?.c !== void 0
|
|
4573
|
+
// If was on CDN, now has local update
|
|
4574
|
+
};
|
|
4575
|
+
return jsonResponse({
|
|
4576
|
+
success: true,
|
|
4577
|
+
updatedItem,
|
|
4578
|
+
dimensions: { width: finalWidth, height: finalHeight }
|
|
4579
|
+
});
|
|
4580
|
+
} catch (error) {
|
|
4581
|
+
console.error("Edit image error:", error);
|
|
4582
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
4583
|
+
return jsonResponse(
|
|
4584
|
+
{ error: `Failed to edit image: ${message}` },
|
|
4585
|
+
{ status: 500 }
|
|
4586
|
+
);
|
|
4587
|
+
}
|
|
4588
|
+
}
|
|
4589
|
+
|
|
4459
4590
|
// src/server/index.ts
|
|
4460
4591
|
var __filename = fileURLToPath(import.meta.url);
|
|
4461
4592
|
var __dirname = dirname(__filename);
|
|
@@ -4534,6 +4665,7 @@ async function startServer(options) {
|
|
|
4534
4665
|
app.post("/api/studio/rename", wrapHandler(handleRename));
|
|
4535
4666
|
app.post("/api/studio/rename-stream", wrapHandler(handleRenameStream, true));
|
|
4536
4667
|
app.post("/api/studio/move", wrapHandler(handleMoveStream, true));
|
|
4668
|
+
app.post("/api/studio/edit-image", wrapHandler(handleEditImage));
|
|
4537
4669
|
app.post("/api/studio/sync", wrapHandler(handleSync, true));
|
|
4538
4670
|
app.post("/api/studio/sync-stream", wrapHandler(handleSyncStream, true));
|
|
4539
4671
|
app.post(
|