Files
SlabUploader/frontend/src/App.svelte
T
grokbot_ty be2ea6c4ea feat(capture): tape-line overlay UI replaces axis rectangle (#99)
Pointer-Events pan/zoom/tilt under a fixed black ~2pt line with
rifle-scope end ticks (≥44×44 hit). AxisPanel becomes Tape line step;
session persists tape transform; scale = tick distance / L.
sveltedrag unavailable for Svelte 4 — Pointer Events path used.
2026-09-25 17:41:29 -07:00

106 lines
3.1 KiB
Svelte

<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { supported } from './browser';
import { createCaptureSession, type CaptureSession } from './capture/session';
import type { Capture } from './capture/capture.types';
import Resume from './capture/Resume.svelte';
import BatchPhotos from './capture/BatchPhotos.svelte';
import PickCalibration from './capture/PickCalibration.svelte';
import MaskPanel from './capture/MaskPanel.svelte';
import AxisPanel from './capture/AxisPanel.svelte';
import MeasurePanel from './capture/MeasurePanel.svelte';
const gate = typeof window !== 'undefined' ? supported() : 'window unavailable';
const session: CaptureSession = createCaptureSession();
let cap: Capture = { k: 'batch', photos: [] };
let busy = false;
let pngUrl: string | null = null;
const unsubCap = session.capture.subscribe((c) => {
cap = c;
pngUrl = c.k === 'measure' ? session.getInventoryUrl() : null;
});
const unsubBusy = session.busy.subscribe((b) => {
busy = b;
});
onMount(() => {
void session.resumeProbe();
});
onDestroy(() => {
unsubCap();
unsubBusy();
session.dispose();
});
</script>
{#if gate}
<main class="hardstop">
<h1>Unsupported browser</h1>
<p>
SlabUploader needs a modern browser with canvas, workers, and IndexedDB support:
iOS Safari, Android Chrome, or desktop Chrome, Edge, or Firefox.
</p>
<p class="detail">Missing: {gate}</p>
</main>
{:else}
<main class="app">
{#if cap.k === 'resume'}
<Resume onContinue={() => session.continueDraft()} onFresh={() => void session.startFresh()} />
{:else if cap.k === 'batch'}
<BatchPhotos
photos={cap.photos}
onAdd={(f) => void session.addToBatch(f)}
onRemove={session.removeFromBatch}
onContinue={session.continueBatch}
/>
{:else if cap.k === 'pickCalibration'}
<PickCalibration
photos={cap.photos}
onPick={session.pickCalibration}
onBack={session.back}
/>
{:else if cap.k === 'mask'}
<MaskPanel
state={cap}
{busy}
setSensitivity={session.setSensitivity}
setSheet={session.setSheet}
approveMask={session.approveMask}
retakes={session.retakes}
back={session.back}
/>
{:else if cap.k === 'axis'}
<AxisPanel
state={cap}
setAxisAngle={session.setAxisAngle}
setTape={session.setTape}
approveAxis={session.approveAxis}
back={session.back}
/>
{:else}
<MeasurePanel
state={cap}
setLength={session.setLength}
setThickness={session.setThickness}
calculate={session.calculate}
back={session.back}
{pngUrl}
/>
{/if}
</main>
{/if}
<style>
:global(body) {
margin: 0;
background: #121212;
font-family: system-ui, -apple-system, Segoe UI, sans-serif;
}
.app, .hardstop { max-width: 720px; margin: 0 auto; min-height: 100vh; }
.hardstop { padding: 48px 24px; color: #eee; text-align: center; }
.hardstop h1 { font-size: 1.6rem; }
.detail { color: #888; font-size: 0.85rem; }
</style>