Files
lasercut-scrap-modeller/src/lib/opencv-loader.ts
T
immortaly007andClaude Opus 5.5 bec28fd0c1 feat: add zoomable image canvas with detection overlays
Load OpenCV in the browser via a script tag: the bundler's CJS interop
copies the module's then() onto the import namespace, which never settles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 14:55:55 +02:00

38 lines
1.5 KiB
TypeScript

export type CV = typeof import('@techstark/opencv-js');
// Everything returns { cv }: the cv module is a thenable, so resolving with it directly never settles.
let loading: Promise<{ cv: CV }> | null = null;
export function loadOpenCV(): Promise<{ cv: CV }> {
loading ??= (async () => {
const { cv } = typeof document === 'undefined' ? await importModule() : await loadScript();
if (!cv.Mat) {
await new Promise<void>((resolve) => {
(cv as unknown as { onRuntimeInitialized: () => void }).onRuntimeInitialized = () => resolve();
});
}
return { cv };
})();
return loading;
}
/** Node (tests). The variable specifier keeps the bundler from emitting a second OpenCV chunk. */
async function importModule(): Promise<{ cv: CV }> {
const specifier = '@techstark/opencv-js';
const mod = (await import(/* @vite-ignore */ specifier)) as { default: CV };
return { cv: mod.default };
}
// A script tag, not import(): bundler CJS interop copies `then` and the import never settles.
async function loadScript(): Promise<{ cv: CV }> {
const { default: url } = await import('@techstark/opencv-js/dist/opencv.js?url');
await new Promise<void>((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.onload = () => resolve();
script.onerror = () => reject(new Error('Failed to load OpenCV'));
document.head.append(script);
});
return { cv: (window as unknown as { cv: CV }).cv };
}