feat: scaffold Svelte app with shared types and scale calculation

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
immortaly007andClaude Opus 5.5 committed 2026-10-15 14:38:22 +02:00
1 parent 4284f2533b
commit a39122c504
12 files changed
+1491

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
node_modules
dist
+12
View File
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Scrap Tracer</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
+1369
View File
File diff suppressed because it is too large. Load diff
+27
View File
@@ -0,0 +1,27 @@
{
"name": "scrap-tracer",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-check --tsconfig ./tsconfig.json",
"test": "vitest run"
},
"dependencies": {
"@techstark/opencv-js": "4.12.0-release.1"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^7.3.1",
"@tsconfig/svelte": "^5.0.8",
"@types/node": "^22.20.5",
"jpeg-js": "^0.4.4",
"svelte": "^5.57.2",
"svelte-check": "^4.7.6",
"typescript": "^6.0.3",
"vite": "^8.3.4",
"vitest": "^5.0.4"
}
}
+3
View File
@@ -0,0 +1,3 @@
<script lang="ts"></script>
<h1>Scrap Tracer</h1>
+7
View File
@@ -0,0 +1,7 @@
import type { Point } from './types';
export function scaleFromLine(a: Point, b: Point, lengthMm: number): number | null {
const lengthPx = Math.hypot(b.x - a.x, b.y - a.y);
if (!(lengthMm > 0) || lengthPx < 1) return null;
return lengthPx / lengthMm;
}
+15
View File
@@ -0,0 +1,15 @@
export type Point = { x: number; y: number };
/** A piece outline: one outer ring plus hole rings. Units depend on the pipeline stage (px or mm). */
export type Shape = { outer: Point[]; holes: Point[][] };
/** RGBA pixels, structurally compatible with ImageData. */
export type RasterImage = { width: number; height: number; data: Uint8ClampedArray<ArrayBuffer> };
export type DetectParams = {
threshold: number;
invert: boolean;
seed: Point | null;
/** Negative shrinks the piece (and grows holes). */
marginPx: number;
};
+4
View File
@@ -0,0 +1,4 @@
import { mount } from 'svelte';
import App from './App.svelte';
mount(App, { target: document.getElementById('app')! });
+3
View File
@@ -0,0 +1,3 @@
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
export default { preprocess: vitePreprocess({ script: true }) };
+18
View File
@@ -0,0 +1,18 @@
import { describe, expect, it } from 'vitest';
import { scaleFromLine } from '../src/lib/scale';
describe('scaleFromLine', () => {
it('returns pixels per mm', () => {
expect(scaleFromLine({ x: 0, y: 0 }, { x: 300, y: 400 }, 100)).toBeCloseTo(5);
});
it('rejects a non-positive or missing length', () => {
expect(scaleFromLine({ x: 0, y: 0 }, { x: 100, y: 0 }, 0)).toBeNull();
expect(scaleFromLine({ x: 0, y: 0 }, { x: 100, y: 0 }, -5)).toBeNull();
expect(scaleFromLine({ x: 0, y: 0 }, { x: 100, y: 0 }, NaN)).toBeNull();
});
it('rejects a line shorter than one pixel', () => {
expect(scaleFromLine({ x: 10, y: 10 }, { x: 10.5, y: 10 }, 100)).toBeNull();
});
});
+16
View File
@@ -0,0 +1,16 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"types": ["node"],
"strict": true,
"noEmit": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts", "src/**/*.svelte", "tests/**/*.ts", "vite.config.ts"]
}
+15
View File
@@ -0,0 +1,15 @@
import { defineConfig } from 'vitest/config';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [svelte()],
// Rune modules must compile for the browser runtime under Vitest too.
resolve: process.env.VITEST ? { conditions: ['browser'] } : undefined,
build: { chunkSizeWarningLimit: 12000 },
test: {
environment: 'node',
include: ['tests/**/*.test.ts'],
testTimeout: 30000,
hookTimeout: 60000,
},
});