From 5a9b811d2ae60eaa4aad2b5e8614e7990df14783 Mon Sep 17 00:00:00 2001 From: yuanzoo Date: Sat, 16 May 2026 22:50:20 +0800 Subject: [PATCH] Skip remote URLs in thumbnail generation --- src/utils/__tests__/imageThumbnail.test.ts | 14 +++++++ src/utils/imageThumbnail.ts | 43 ++++++++++++---------- 2 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 src/utils/__tests__/imageThumbnail.test.ts diff --git a/src/utils/__tests__/imageThumbnail.test.ts b/src/utils/__tests__/imageThumbnail.test.ts new file mode 100644 index 00000000..8fd9fd36 --- /dev/null +++ b/src/utils/__tests__/imageThumbnail.test.ts @@ -0,0 +1,14 @@ +import { describe, expect, it } from "vitest"; +import { generateThumbnail } from "../imageThumbnail"; + +describe("generateThumbnail", () => { + it("returns empty sources unchanged", async () => { + await expect(generateThumbnail("")).resolves.toBe(""); + }); + + it("does not export remote image URLs through canvas", async () => { + const remoteUrl = "https://r2cdn.copilotbase.com/r2cdn2/generated.png"; + + await expect(generateThumbnail(remoteUrl)).resolves.toBe(remoteUrl); + }); +}); diff --git a/src/utils/imageThumbnail.ts b/src/utils/imageThumbnail.ts index ba258776..05a755ee 100644 --- a/src/utils/imageThumbnail.ts +++ b/src/utils/imageThumbnail.ts @@ -9,35 +9,40 @@ export async function generateThumbnail( quality: number = 0.6 ): Promise { if (!base64DataUrl) return base64DataUrl; + if (!base64DataUrl.startsWith("data:image/")) return base64DataUrl; return new Promise((resolve) => { const img = new Image(); img.onload = () => { - const { naturalWidth: w, naturalHeight: h } = img; + try { + const { naturalWidth: w, naturalHeight: h } = img; - // Skip if already small enough - if (w <= maxDim && h <= maxDim) { - resolve(base64DataUrl); - return; - } + // Skip if already small enough + if (w <= maxDim && h <= maxDim) { + resolve(base64DataUrl); + return; + } - // Calculate scaled dimensions preserving aspect ratio - const scale = Math.min(maxDim / w, maxDim / h); - const newW = Math.round(w * scale); - const newH = Math.round(h * scale); + // Calculate scaled dimensions preserving aspect ratio + const scale = Math.min(maxDim / w, maxDim / h); + const newW = Math.round(w * scale); + const newH = Math.round(h * scale); - const canvas = document.createElement("canvas"); - canvas.width = newW; - canvas.height = newH; + const canvas = document.createElement("canvas"); + canvas.width = newW; + canvas.height = newH; - const ctx = canvas.getContext("2d"); - if (!ctx) { + const ctx = canvas.getContext("2d"); + if (!ctx) { + resolve(base64DataUrl); + return; + } + + ctx.drawImage(img, 0, 0, newW, newH); + resolve(canvas.toDataURL("image/jpeg", quality)); + } catch { resolve(base64DataUrl); - return; } - - ctx.drawImage(img, 0, 0, newW, newH); - resolve(canvas.toDataURL("image/jpeg", quality)); }; img.onerror = () => resolve(base64DataUrl); img.src = base64DataUrl;