From 98285d0539cdaee274c13c40cd7df29a11a6e5ad Mon Sep 17 00:00:00 2001 From: Bas Dado Date: Thu, 15 Oct 2026 15:43:06 +0200 Subject: [PATCH] feat: style the UI with Oat and make the empty canvas a click-to-load drop zone Co-Authored-By: Claude Opus 5.5 --- package-lock.json | 6 +++ package.json | 1 + src/App.svelte | 38 +++++++++++++---- src/components/Sidebar.svelte | 78 +++++++++++++++++++++-------------- src/main.ts | 1 + 5 files changed, 86 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index 89cf334..39b9512 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "scrap-tracer", "version": "0.1.0", "dependencies": { + "@knadh/oat": "0.8.3", "@techstark/opencv-js": "4.12.0-release.1" }, "devDependencies": { @@ -67,6 +68,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@knadh/oat": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@knadh/oat/-/oat-0.8.3.tgz", + "integrity": "sha512-eE07LJ6RpC21PfEbu3U6dFbTO0V6m1OcXocrYr7dwN+Ffr8sx+SqVTPwHO5/+Azv0NWEU8V7frwzBQXdx7CzLA==" + }, "node_modules/@oxc-project/types": { "version": "0.154.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.154.0.tgz", diff --git a/package.json b/package.json index 69a32f2..c59b714 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "test": "vitest run" }, "dependencies": { + "@knadh/oat": "0.8.3", "@techstark/opencv-js": "4.12.0-release.1" }, "devDependencies": { diff --git a/src/App.svelte b/src/App.svelte index 18374ff..7696a3d 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -19,6 +19,11 @@ const file = e.dataTransfer?.files[0]; if (file) app.loadFile(file); } + + function onFile(e: Event & { currentTarget: HTMLInputElement }) { + const file = e.currentTarget.files?.[0]; + if (file) app.loadFile(file); + }
@@ -29,7 +34,11 @@ {#if app.image} {:else} -

Drop a photo of a scrap piece here, or use “Load photo”.

+ {/if}
@@ -37,8 +46,6 @@ diff --git a/src/components/Sidebar.svelte b/src/components/Sidebar.svelte index 14117be..570e3e9 100644 --- a/src/components/Sidebar.svelte +++ b/src/components/Sidebar.svelte @@ -6,6 +6,7 @@ import { scrapFilename, shapeSize } from '../lib/geometry'; let { app }: { app: AppState } = $props(); + let fileInput: HTMLInputElement; const size = $derived(app.exportShape ? shapeSize(app.exportShape) : null); const nothingDetected = $derived(app.cv !== null && app.image !== null && app.shapePx === null); @@ -30,24 +31,27 @@

Scrap Tracer

{#if app.cvError} -

{app.cvError}

+

{app.cvError}

{:else if !app.cv}

Loading image processing…

{/if}

1. Photo

- + + {#if app.loadError}

{app.loadError}

{/if}

2. Scale

Click both ends of something whose length you know.

- - +
+ + +
{#if app.scaleInvalid}

Enter a length above 0 and pick two different points.

{:else if app.pxPerMm} @@ -58,19 +62,26 @@

3. Outline

{#if app.mode === 'auto'} - - - - {/if} - - {#if app.drawing.length > 0} - - {/if} - {#if app.mode === 'manual' || app.drawing.length > 0} - + +
+ + +
{/if} +
+ + {#if app.drawing.length > 0} + + {/if} + {#if app.mode === 'manual' || app.drawing.length > 0} + + {/if} +
{#if nothingDetected}

Nothing detected, try adjusting the threshold, clicking the piece, or draw manually.

{/if} @@ -79,8 +90,8 @@

4. Safety margin

{#if !app.pxPerMm}

Set the scale first.

{/if}
@@ -88,8 +99,10 @@

5. Export

{#if size}

Piece: {Math.round(size.width)} × {Math.round(size.height)} mm

{/if} - - +
+ + +
{#if !app.exportShape}

{app.pxPerMm ? 'No outline to export yet.' : 'Set the scale first.'}

{/if} @@ -97,15 +110,18 @@ diff --git a/src/main.ts b/src/main.ts index 3fcb539..c9db2ce 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,4 @@ +import '@knadh/oat/oat.min.css'; import { mount } from 'svelte'; import App from './App.svelte';