feat(m4): crop_fill setting + tape-line prototype (#99)

Add global crop_fill percent (default 90, 1-100) as a settings key with
FastAPI 4xx validation. Wires the value through to the inventory PNG
encode (non-retro: stored PNGs untouched). Adds a throwaway
TapePrototype.svelte behind ?tape-prototype for the #99 design sandbox.

Gates: check_requirements PASS, check_coverage PASS, settings tests 8/8.
This commit is contained in:
Ty
2026-09-24 12:01:18 -07:00
parent 0c00d25cc7
commit 818874e65c
13 changed files with 831 additions and 23 deletions
+12 -2
View File
@@ -10,6 +10,7 @@ import { measure } from '../measure/measure';
import { orientedRect } from '../geometry/rect';
import { encodeInventoryPng, type InventoryInput } from '../png/inventory';
import { encodeOverlayPng, type EncodeOverlayInput } from '../png/overlay';
import { FILL_DEFAULT, fillFromSetting } from '../png/crop';
import { decodeOrientedBitmap } from '../decodeOrientedBitmap';
const DEBOUNCE_MS = 2000;
@@ -41,6 +42,8 @@ export interface CaptureSession {
setSheet(s: Sheet): void;
approveMask(): void;
setAxisAngle(deg: number): void;
/** #99: global crop fill from settings (percent or fraction); drives the PNG crop. */
setCropFill(pctOrFraction: number): void;
approveAxis(): void;
setLength(e: Eighths): void;
setThickness(e: Eighths): void;
@@ -83,6 +86,8 @@ export function createCaptureSession(opts: SessionOpts = {}): CaptureSession {
let busyTimer: ReturnType<typeof setTimeout> | null = null;
let pngUrl: string | null = null;
let resumed = false;
// #99: global crop fill (fraction). Set from settings; FILL_DEFAULT (90%) until told otherwise.
let cropFill = FILL_DEFAULT;
const set = (next: Capture) => {
state = next;
@@ -487,6 +492,11 @@ export function createCaptureSession(opts: SessionOpts = {}): CaptureSession {
void persist();
},
setCropFill(pctOrFraction: number) {
// #99: non-retro — only future encodes use this; stored PNGs are untouched.
cropFill = fillFromSetting(pctOrFraction);
},
approveAxis() {
if (state.k !== 'axis') return;
set({
@@ -523,7 +533,7 @@ export function createCaptureSession(opts: SessionOpts = {}): CaptureSession {
let pngBlob: Blob;
let belowFloor = false;
try {
const r = await encodeInventory({ bitmap, mask: approvedMask, source });
const r = await encodeInventory({ bitmap, mask: approvedMask, source, fill: cropFill });
pngBlob = r.blob;
belowFloor = r.sourceBelowFloor;
} catch {
@@ -593,7 +603,7 @@ export function createCaptureSession(opts: SessionOpts = {}): CaptureSession {
const siblingBitmap = await decodeOrientedBitmap(photo.blob);
let encoded: { blob: Blob; sourceBelowFloor: boolean };
try {
encoded = await encodeInventory({ bitmap: siblingBitmap, mask: derived.mask, source: siblingSource });
encoded = await encodeInventory({ bitmap: siblingBitmap, mask: derived.mask, source: siblingSource, fill: cropFill });
} finally {
siblingBitmap.close();
}