Files
buzz-sheet/lib/image-export/client.ts
T

53 lines
1.6 KiB
TypeScript

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<Blob> {
if (!worker) {
worker = new Worker(new URL("./export.worker.ts", import.meta.url), {
type: "module",
});
worker.onmessage = ({ data }: MessageEvent<WorkerResult>) => {
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);
}