Commit Graph

15 Commits

Author SHA1 Message Date
Ty 25c1f79fc1 fix(fe): downscale mask flood-fill to a working resolution
The border flood-fill on a full-res 2256x4000 (9M px) photo was too slow in
JS and the async mask pass timed out into the catch branch (overlay stayed
null, 'Mask settled' never rendered). Downscale to <=1200px for the fill,
then nearest-neighbor upsample the slab bitset back to full size so
minAreaRect/slabBounds stay in source-pixel coordinates.
2026-09-25 09:09:30 -07:00
Ty 1c58f12c5d feat(fe): replace threshold mask with client-side sheet BG removal (flood-fill)
The green/black-sheet luminance threshold could not separate the slab from
the 'black' sheet, which in real photos reads at lum ~50 and overlaps the
slab's bark/shadow tones (measured on the user's samples: 0% green, 60-80%
dark, no brightness gap) - it removed 100% of the image. Replace the mask
pass with connectivity-based background removal: sample the sheet tone from
the border, flood-fill from the border marking sheet-connected pixels as
background, keep the connected remainder as the slab. Robust to tonal
overlap. Adds bgRemoval.ts (removeBackground + sampleSheetTone) with unit
tests proving a sheet-toned slab pixel survives via enclosure.
2026-09-25 09:07:39 -07:00
Ty 49c22c9385 feat(greenfield): rebuild SlabUploader M1-M3 on clean foundation, all gates green
Clean re-implementation of the full product (not a port of the old M3 tree):
- P0 foundation: 13-table schema + per-call sqlite factory (thread-safe),
  FastAPI app (health, JSON logging + secret redaction, CORS, startup guard).
- P1 M1 Capture: mask/sheetKeep, geometry/rect (min-area rect), measure,
  png crop/inventory/overlay, EXIF decodeOrientedBitmap.
- P2 M2 Photos: stateful capture session (batch, pick-cal, apply mask/crop
  to batch) + photos-ready lineup.
- P3 M3 Create: real backend (crypto, woo client, taxonomy sync, pricing,
  inference Call1/Call2, review, publish, settings, slabs, photos) + real FE
  review/publish/settings screens consuming the API (no hardcoded Woo ids).
- P4 gate exit: check_requirements + check_coverage both exit 0
  (137/155 IDs, 0 gaps); backend 130/130, frontend 150/150, build green.

Cite: docs/REQUIREMENTS.md (155 rows), docs/MILESTONES.md M1-M3.
2026-09-25 06:57:29 -07:00
Ty 4ca2ef57a7 feat(capture): ship tape-line calibration (#99) 2026-09-24 14:47:13 -07:00
Ty 818874e65c 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.
2026-09-24 12:01:18 -07:00
Ty 1133201f30 M3 Review→Publish UI (#77): one-column flow, searchable pickers, fig/feat tags, e2e
- Review screen: slug-resolved species/wcat groupers, case-insensitive attrs,
  fig-*/feat-* tag pickers (METADATA-30/40), AI-assist labels + AI bubbles.
- App orchestrator: single writable flow store, capture→review→publish, Resume boot.
- API client (slabs.ts) mirroring live backend routes; Blob name fix.
- Playwright e2e (7 tests, hermetic mock): canvas PNG seed, client-UUID mock,
  SKU blur commit, ready-state reactive fix; expect timeout 4s, 2 workers.
- e2e green 7/7 in 6s; check_requirements + check_coverage PASS.
2026-09-20 14:34:24 -07:00
grokbot_ty 037a4b870a fix(photos): shared EXIF-oriented decode for axis/overlay space (#72)
PhotoStage used new Image() while mask worker and calculate used bare
createImageBitmap; Orientation-8 matched-set JPEGs can disagree across
those paths so the approved pink axis is not the overlay (ux,uy).

One decodeOrientedBitmap helper always requests imageOrientation
from-image (with bare fallback), wired into PhotoStage, mask.worker,
and session calculate/siblings.
2026-09-16 05:08:13 +00:00
grokbot_ty a22525023a feat(photos): burn axis-oriented dimension overlay PNG (#63)
Add pure station/axis math and canvas burn-in for a second calibration
inventory PNG (results.overlayPng). Width stations start 6 in from the
EXIF-oriented photo bottom along the approved length axis (6, 18, … ≤
L−6); length graphic sits on axis-right outside the slab. Clean
inventoryPng and batchApply[cal]=null stay unchanged.
2026-09-15 21:31:22 +00:00
grokbot_ty 3936c575b2 feat(photos): apply frozen mask to batch siblings after Calculate (#62)
After calibration Calculate, reuse approved sheet/sensitivity on non-calibration
photos via the mask worker + inventory compose. Persist per-photo ok/error on
measure.batchApply and the device draft for #64; leave Mask UI unopened and keep
scale/widths/sqft/bdft on the calibration results only.
2026-09-15 21:24:10 +00:00
grokbot_ty 8ff008f85c feat(photos): keep M1 mask/axis/measure on calibration pick (#61)
Document single-source M1 path after pickCalibration: Mask/Axis/Measure/
Calculate use the chosen Source only; batch siblings stay draft-untouched
until #62 apply. Tag encode-fallback inventory Blob as image/png (CAPTURE.md).
2026-09-15 21:14:16 +00:00
grokbot_ty d18b96c10b feat(photos): pick calibration photo before Mask (#60)
Add pickCalibration state/UI over batch thumbs. Continue from batch opens
pick (including single-photo confirm). User pick enters M1 Mask on that
Source only; draft stores calibrationIndex. Removes first-photo proxy.
2026-09-15 21:11:07 +00:00
grokbot_ty ff4bb0f31e feat(photos): multi-photo batch + device draft (#59)
Replace single Add photo with Batch: accumulate 1–N photos, Continue
when ≥1 (M1 bridge: mask on first), persist batch blobs in the device
draft, Resume Continue restores. Keeps Mask/Axis/Measure intact.
2026-09-15 21:08:44 +00:00
grokbot_ty b3a9fdf330 test(capture): require mask alpha in inventory PNG compose
Why
Calculate showed accurate numbers but the inventory PNG kept the sheet
background. Measure already uses approvedMask.bitset; encode did not.

Scope
Add composeInventoryFrame and inventory.test.ts alpha/crop proofs.
Wire encodeInventoryPng({ bitmap, mask, source }) at calculate().
This commit still paints the slabBounds AABB without the bitset gate so
the alpha contract fails until the follow-up fix.

Verification
node --import tsx --test src/png/inventory.test.ts
alpha contract fails (sheet green leaks); 3:4 / 80% / floor pass.
2026-09-14 19:11:52 +00:00
grokbot_ty b328871549 fix(capture): rotate length-axis rectangle and scale from mask span
Length axis keeps the approved mask, replaces Flip 180 with a ± angle
slider, and sets scale from wood span along the approved direction rather
than bbox.longSidePx. Widths still sample every 6 in perpendicular to that
axis.
2026-09-14 16:37:00 +00:00
grokbot_ty d86fc93ed6 feat(capture): ship Milestone 1 browser capture app
Why
Phone and PC need a single capture flow that ends at inventory PNG and
board-foot numbers with no backend, inference, or Woo create.

Scope
Add frontend/ Vite+Svelte capture UI with Resume, Add photo, Mask,
Length axis, and Measure. Mask runs in a worker with Auto/Green/Black
and a 2.0s sensitivity debounce. Measure computes scale, widths, sqft,
bdft, and a 3:4 alpha inventory PNG. Persist one device draft in
IndexedDB. Serve the static build from frontend/Dockerfile via nginx
(deploy/nginx-m1.conf). No FastAPI.

Verification
sudo docker build -f frontend/Dockerfile -t slabuploader-m1 .
sudo docker run -d --name slabuploader-m1 -p 0.0.0.0:18080:80 slabuploader-m1
curl http://127.0.0.1:18080/ returns 200
Playwright: synthetic green-sheet photo through Calculate shows sqft/bdft
2026-09-14 07:53:39 +00:00