import type { ExportRequest, WorkerResult } from "./types"; let worker: Worker | undefined; let nextId = 0; const pending = new Map< number, { resolve: (blob: Blob) => void; reject: (error: Error) => void; format: string; } >(); export function renderImage(request: ExportRequest): Promise { if (!worker) { worker = new Worker(new URL("./export.worker.ts", import.meta.url), { type: "module", }); worker.onmessage = ({ data }: MessageEvent) => { const job = pending.get(data.id); if (!job) return; pending.delete(data.id); if (data.error !== undefined) job.reject(new Error(data.error)); else job.resolve(new Blob([data.bytes], { type: `image/${job.format}` })); }; worker.onerror = () => { worker?.terminate(); worker = undefined; for (const job of pending.values()) job.reject(new Error("Image renderer failed")); pending.clear(); }; } return new Promise((resolve, reject) => { const id = ++nextId; pending.set(id, { resolve, reject, format: request.format }); try { worker!.postMessage({ id, request, origin: window.location.origin }); } catch (error) { pending.delete(id); reject(error); } }); } export function downloadImage(blob: Blob, fileName: string) { const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.download = fileName.replace(/[\\/:*?"<>|]/gu, "-"); link.href = url; document.body.append(link); link.click(); link.remove(); window.setTimeout(() => URL.revokeObjectURL(url), 1000); }