diff --git a/src/components/PromptBox.tsx b/src/components/PromptBox.tsx index d08bb79..31785a9 100644 --- a/src/components/PromptBox.tsx +++ b/src/components/PromptBox.tsx @@ -54,6 +54,67 @@ interface PromptBoxProps contextPercent?: number | null; } +const TARGET_IMAGE_SIZE = 4.5 * 1024 * 1024; // Target slightly under 5 MB +const MAX_DIMENSION = 4096; // Max width/height + +async function compressImage(file: File): Promise { + const dataUrl = await readFileAsDataURL(file); + if (file.size <= TARGET_IMAGE_SIZE) { + return dataUrl; + } + + return new Promise((resolve, reject) => { + const img = new Image(); + img.onload = () => { + const canvas = document.createElement("canvas"); + let width = img.width; + let height = img.height; + + if (width > MAX_DIMENSION || height > MAX_DIMENSION) { + const ratio = Math.min(MAX_DIMENSION / width, MAX_DIMENSION / height); + width = Math.round(width * ratio); + height = Math.round(height * ratio); + } + + canvas.width = width; + canvas.height = height; + const ctx = canvas.getContext("2d"); + if (!ctx) { + reject(new Error("Failed to get canvas context")); + return; + } + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, width, height); + ctx.drawImage(img, 0, 0, width, height); + + let quality = 0.9; + const tryCompress = () => { + const result = canvas.toDataURL("image/jpeg", quality); + const base64Length = result.length - "data:image/jpeg;base64,".length; + const byteSize = Math.round((base64Length * 3) / 4); + + if (byteSize <= TARGET_IMAGE_SIZE) { + resolve(result); + } else if (quality > 0.1) { + quality -= 0.1; + tryCompress(); + } else { + console.warn( + `Image still exceeds target size (${byteSize} bytes) even at minimum quality. Returning low-quality result.`, + ); + resolve(result); + } + }; + tryCompress(); + img.src = ""; + }; + img.onerror = () => { + reject(new Error("Failed to load image. The file may be corrupt.")); + }; + img.src = dataUrl; + }); +} + function readFileAsDataURL(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); @@ -254,7 +315,7 @@ export const PromptBox = React.forwardRef( f.type.startsWith("image/"), ); if (imageFiles.length === 0) return; - const results = await Promise.all(imageFiles.map(readFileAsDataURL)); + const results = await Promise.all(imageFiles.map(compressImage)); setImagePreviews((prev) => [...prev, ...results]); }, [isDisabled],