fix(m4): keep tape calibration on one page (#99)

This commit is contained in:
Ty
2026-09-24 22:33:23 -07:00
parent 6000ac2b0f
commit 7908a99ec3
10 changed files with 335 additions and 247 deletions
+4 -106
View File
@@ -1,9 +1,7 @@
<script lang="ts">
import { onDestroy } from 'svelte';
import type { Capture } from './capture.types';
import { inToEighths, eighthsToIn } from './capture.types';
import { fmtEighths } from './session';
import PhotoStage from './PhotoStage.svelte';
import {
buildPhotosReadyLineup,
photosReadyPngAlt,
@@ -11,22 +9,13 @@
} from './photosReadyLineup';
export let state: Extract<Capture, { k: 'measure' }>;
export let setLength: (e: number) => void;
export let setThickness: (e: number) => void;
export let calculate: () => Promise<void>;
export let back: () => void;
export let pngUrl: string | null;
/** C2: hand off the calibrated capture to Review (App builds the slab + uploads). */
export let onContinue: (() => void) | undefined = undefined;
let calcBusy = false;
let lengthIn = state.lengthEighths === 0 ? '' : String(eighthsToIn(state.lengthEighths));
let thicknessIn = state.thicknessEighths === 0 ? '' : String(eighthsToIn(state.thicknessEighths));
let lineupUrls: string[] = [];
let lineupItems: PhotosReadyLineupItem[] = [];
$: photosReady = !!state.results;
$: {
for (const u of lineupUrls) URL.revokeObjectURL(u);
lineupUrls = [];
@@ -48,92 +37,17 @@
onDestroy(() => {
for (const u of lineupUrls) URL.revokeObjectURL(u);
});
const positiveInches = (raw: string): number | null => {
const inches = Number.parseFloat(raw);
return Number.isFinite(inches) && inches > 0 ? inches : null;
};
const commitLength = () => {
const inches = positiveInches(String(lengthIn));
const eighths = inches === null ? 0 : inToEighths(inches);
setLength(eighths);
lengthIn = eighths === 0 ? '' : String(eighthsToIn(eighths));
};
const commitThickness = () => {
const inches = positiveInches(String(thicknessIn));
const eighths = inches === null ? 0 : inToEighths(inches);
setThickness(eighths);
thicknessIn = eighths === 0 ? '' : String(eighthsToIn(eighths));
};
$: canCalc = positiveInches(String(lengthIn)) !== null && positiveInches(String(thicknessIn)) !== null;
const doCalc = async () => {
calcBusy = true;
try {
commitLength();
commitThickness();
await calculate();
} finally { calcBusy = false; }
};
</script>
<section class="panel">
<h1>{photosReady ? 'Photos ready' : 'Measure'}</h1>
{#if !photosReady}
<PhotoStage
blob={state.source.blob}
bbox={state.axis.bbox}
maskBitset={state.approvedMask.bitset}
maskW={state.source.widthPx > 0 ? state.source.widthPx : 0}
maskH={state.source.widthPx > 0 ? state.approvedMask.bitset.length / state.source.widthPx : 0}
/>
<div class="fields">
<div class="field">
<span class="label">Length L (tape reading)</span>
<input
type="number"
aria-label="Length L"
inputmode="decimal"
step="0.125"
min="0"
placeholder="0"
bind:value={lengthIn}
on:blur={commitLength}
on:change={commitLength}
/>
</div>
<div class="field">
<span class="label">Thickness</span>
<input
type="number"
aria-label="Thickness"
inputmode="decimal"
step="0.125"
min="0"
placeholder="0"
bind:value={thicknessIn}
on:blur={commitThickness}
on:change={commitThickness}
/>
</div>
</div>
<h1>Photos ready</h1>
{#if !state.results}
<p class="hint">Calculating…</p>
<div class="row">
<button type="button" class="ghost" on:click={back}>Back</button>
<button
type="button"
class="primary"
disabled={calcBusy || !canCalc}
on:click={doCalc}
>
{calcBusy ? 'Calculating…' : 'Calculate'}
</button>
</div>
{:else if state.results}
{:else}
<div class="results" aria-label="Photos ready">
<div class="nums">
<div><span>Widths</span><span>{fmtEighths(state.results.widths.min)} – {fmtEighths(state.results.widths.max)} (avg {fmtEighths(state.results.widths.avg)})</span></div>
@@ -169,12 +83,6 @@
{/each}
</ol>
<!-- Legacy single-PNG fallback only when lineup helper yielded nothing but pngUrl exists -->
{#if lineupItems.length === 0 && pngUrl && !state.results.sourceBelowFloor}
<div class="png-wrap">
<img src={pngUrl} alt="Inventory PNG" />
</div>
{/if}
</div>
<div class="row">
@@ -191,14 +99,6 @@
<style>
.panel { padding: 16px; color: #eee; }
h1 { font-size: 1.3rem; margin: 0 0 12px; }
.fields { display: flex; gap: 16px; margin-top: 16px; }
.field { flex: 1; }
.label { display: block; font-size: 0.9rem; color: #aaa; margin-bottom: 8px; }
.field input {
box-sizing: border-box; width: 100%; min-height: 52px; padding: 12px; font-size: 1rem;
border-radius: 10px; border: 1px solid #444; background: #1d1d1d; color: #eee;
font-variant-numeric: tabular-nums;
}
.row { display: flex; gap: 10px; margin-top: 20px; }
.primary {
flex: 2; padding: 14px; font-size: 1.05rem; border-radius: 10px; border: 0;
@@ -229,6 +129,4 @@
}
.err-title { color: #ffb4a2; font-weight: 700; font-size: 0.95rem; }
.err-msg { color: #e8cfc8; font-size: 0.9rem; }
.png-wrap { margin-top: 16px; text-align: center; background-image: linear-gradient(45deg,#ccc 25%,transparent 25%),linear-gradient(-45deg,#ccc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#ccc 75%),linear-gradient(-45deg,transparent 75%,#ccc 75%); background-size: 16px 16px; background-position: 0 0,0 8px,8px -8px,-8px 0; background-color: #eee; border-radius: 8px; padding: 8px; }
.png-wrap img { max-width: 100%; border-radius: 4px; }
</style>