const MAX_DIMENSION = 384; /** * Resize an image to fit within MAX_DIMENSION while preserving aspect ratio. * Returns a base64 data URL. */ export function resizeImage(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { const img = new Image(); img.onload = () => { let { width, height } = img; if (width > MAX_DIMENSION || height > MAX_DIMENSION) { const scale = MAX_DIMENSION / Math.max(width, height); width = Math.round(width * scale); height = Math.round(height * scale); } const canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; const ctx = canvas.getContext("2d")!; ctx.drawImage(img, 0, 0, width, height); resolve(canvas.toDataURL("image/jpeg", 0.9)); }; img.onerror = reject; img.src = reader.result as string; }; reader.onerror = reject; reader.readAsDataURL(file); }); } /** * Load an image URL, resize to MAX_DIMENSION, return as base64 data URL. */ export async function urlToDataUrl(url: string): Promise { const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status} for ${url}`); const blob = await response.blob(); const file = new File([blob], "image.jpg", { type: blob.type }); return resizeImage(file); } /** * Get image dimensions from a data URL. */ export function getImageDimensions( dataUrl: string, ): Promise<{ width: number; height: number }> { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve({ width: img.width, height: img.height }); img.onerror = reject; img.src = dataUrl; }); }