diff --git a/src/App.svelte b/src/App.svelte
index 5fede75..da42345 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -1,3 +1,64 @@
-
+
+
+
+
+
e.preventDefault()} {ondrop}>
+ {#if app.image}
+
+ {:else}
+ Drop a photo of a scrap piece here, or use “Load photo”.
+ {/if}
+
+
+
+
diff --git a/src/components/ImageCanvas.svelte b/src/components/ImageCanvas.svelte
new file mode 100644
index 0000000..e5e18d1
--- /dev/null
+++ b/src/components/ImageCanvas.svelte
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
diff --git a/src/lib/opencv-loader.ts b/src/lib/opencv-loader.ts
index 1059268..9fcbaed 100644
--- a/src/lib/opencv-loader.ts
+++ b/src/lib/opencv-loader.ts
@@ -1,12 +1,11 @@
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;
-// Wrapped in an object: the cv module is a thenable, so resolving with it directly never settles.
export function loadOpenCV(): Promise<{ cv: CV }> {
loading ??= (async () => {
- const mod = (await import('@techstark/opencv-js')) as unknown as { default: CV };
- const cv = mod.default;
+ const { cv } = typeof document === 'undefined' ? await importModule() : await loadScript();
if (!cv.Mat) {
await new Promise((resolve) => {
(cv as unknown as { onRuntimeInitialized: () => void }).onRuntimeInitialized = () => resolve();
@@ -16,3 +15,23 @@ export function loadOpenCV(): Promise<{ cv: 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((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 };
+}
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/src/vite-env.d.ts
@@ -0,0 +1 @@
+///