From 845e3df223430f6e0247ae6edb5dfdc4a3733f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Wed, 4 Mar 2026 21:22:30 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20image=20transforms=20=E2=80=94=20preserv?= =?UTF-8?q?e=20aspect=20ratio,=20increase=20quality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - preview/medium use fit:inside (no forced crop, preserves aspect ratio) - Only mini/thumbnail/banner force square/fixed crops - Increase WebP quality 85 → 92 - Increase preview width 480 → 800, medium 960 → 1400 Co-Authored-By: Claude Sonnet 4.6 --- packages/backend/src/index.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index c53f010..3770f1d 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -69,13 +69,13 @@ async function main() { yoga.handleNodeRequestAndResponse(req, reply, { req, reply, db, redis }), }); - // Transform presets: width x height (height optional = keep aspect ratio) - const TRANSFORMS: Record = { - mini: { width: 64, height: 64 }, - thumbnail: { width: 200, height: 200 }, - preview: { width: 480, height: 270 }, - medium: { width: 960 }, - banner: { width: 1280, height: 400 }, + // Transform presets — only banner/thumbnail force a crop; others preserve aspect ratio + const TRANSFORMS: Record = { + mini: { width: 80, height: 80, fit: "cover" }, + thumbnail: { width: 300, height: 300, fit: "cover" }, + preview: { width: 800, fit: "inside" }, + medium: { width: 1400, fit: "inside" }, + banner: { width: 1600, height: 480, fit: "cover" }, }; // Serve uploaded files: GET /assets/:id?transform= @@ -101,8 +101,8 @@ async function main() { if (!existsSync(cacheFile)) { const originalPath = path.join(UPLOAD_DIR, id, filename); await sharp(originalPath) - .resize({ width: preset.width, height: preset.height, fit: "cover", withoutEnlargement: true }) - .webp({ quality: 85 }) + .resize({ width: preset.width, height: preset.height, fit: preset.fit ?? "inside", withoutEnlargement: true }) + .webp({ quality: 92 }) .toFile(cacheFile); } reply.header("Content-Type", "image/webp");