import type { ExportFormat } from "../types"; export interface LoadedImage { file: File; /** Object URL for display in the cropper. Revoke when replaced. */ url: string; width: number; height: number; } const ACCEPTED = /^image\/(jpeg|png|webp|avif|gif|bmp|x-icon|svg\+xml)$/; export function isImageFile(file: File): boolean { return file.type ? ACCEPTED.test(file.type) : /\.(jpe?g|png|webp|avif|gif|bmp|ico|svg)$/i.test(file.name); } function readSize(url: string): Promise<{ width: number; height: number }> { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight }); img.onerror = () => reject(new Error("Could not decode this image.")); img.src = url; }); } export async function loadImage(file: File): Promise { const url = URL.createObjectURL(file); try { const { width, height } = await readSize(url); return { file, url, width, height }; } catch (err) { URL.revokeObjectURL(url); throw err; } } const EXT: Record = { jpeg: "jpg", webp: "webp", png: "png", avif: "avif", }; export function buildFilename(sourceName: string, templateId: string, format: ExportFormat): string { const base = sourceName.replace(/\.[^./\\]+$/, "") || "image"; const suffix = templateId === "original" ? "edited" : templateId; return `${base}-${suffix}.${EXT[format]}`; } export function triggerDownload(blob: Blob, filename: string): void { const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); a.remove(); // Defer revocation so the download has time to start. setTimeout(() => URL.revokeObjectURL(url), 10_000); } export function formatBytes(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(2)} MB`; } export function megapixels(width: number, height: number): string { const mp = (width * height) / 1_000_000; return mp >= 1 ? `${mp.toFixed(1)} MP` : `${(mp * 1000).toFixed(0)} KP`; }