feat(capture): ship Milestone 1 browser capture app #57

Closed
grokbot_ty wants to merge 0 commits from doc-simplification into main
Owner

Why

Phone and PC need one capture flow that ends at the inventory PNG and board-foot numbers. Milestone 1 stays in the browser with no backend, inference, or Woo create.

Scope

  • frontend/ Vite+Svelte capture UI: Resume, Add photo, Mask, Length axis, Measure
  • Mask worker with Auto/Green/Black and 2.0s sensitivity debounce
  • Measure: scale, widths, sqft, bdft, 3:4 alpha inventory PNG
  • IndexedDB device draft
  • frontend/Dockerfile serves static build via nginx (deploy/nginx-m1.conf)
  • No FastAPI, no inference URL, no Woo create

Tradeoffs

Vite+Svelte SPA instead of SvelteKit. Same static deploy shape, smaller scaffold for M1.

Blast Radius

New frontend/ tree and nginx fragment only. Old deploy/docker-compose.yml FastAPI stack is untouched and remains non-M1.

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 and bdft
  • UAT: http://100.64.0.3:18080
## Why Phone and PC need one capture flow that ends at the inventory PNG and board-foot numbers. Milestone 1 stays in the browser with no backend, inference, or Woo create. ## Scope - `frontend/` Vite+Svelte capture UI: Resume, Add photo, Mask, Length axis, Measure - Mask worker with Auto/Green/Black and 2.0s sensitivity debounce - Measure: scale, widths, sqft, bdft, 3:4 alpha inventory PNG - IndexedDB device draft - `frontend/Dockerfile` serves static build via nginx (`deploy/nginx-m1.conf`) - No FastAPI, no inference URL, no Woo create ## Tradeoffs Vite+Svelte SPA instead of SvelteKit. Same static deploy shape, smaller scaffold for M1. ## Blast Radius New `frontend/` tree and nginx fragment only. Old `deploy/docker-compose.yml` FastAPI stack is untouched and remains non-M1. ## 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 and bdft - UAT: http://100.64.0.3:18080
grokbot_ty added 52 commits 2026-09-14 07:54:17 +00:00
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
Ty added 1 commit 2026-09-14 14:16:50 +00:00
grokbot_ty added 1 commit 2026-09-14 15:03:50 +00:00
grokbot_ty added 1 commit 2026-09-14 15:10:08 +00:00
ubudual :10880 is Ty local Node/SvelteKit — do not overwrite.
grokbot_ty added 1 commit 2026-09-14 15:12:35 +00:00
grokbot_ty added 1 commit 2026-09-14 15:23:12 +00:00
Why
On phone, the single file control opened the camera because it set
capture=environment. CAPTURE.md requires camera or picker.

Scope
AddPhoto.svelte now has Take photo (capture=environment) and Choose
from gallery (no capture). Both call the same onPicked path.

Verification
Playwright against overnight after redeploy asserts two inputs and
gallery has no capture attribute.
grokbot_ty added 2 commits 2026-09-14 16:37:07 +00:00
Contain-fit waits for a real parent width, uses ResizeObserver, and shrinks
width when maxH would otherwise stretch the CSS box. Pure fitDisplay is
covered by node tests.
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.
grokbot_ty added 2 commits 2026-09-14 18:26:40 +00:00
Ty UAT rejects ± steppers as the only Length L / Thickness control.
Assert MeasurePanel exposes type=number inputs and that inches snap via inToEighths.
Replace MeasurePanel steppers with number inputs. Commit on blur, change,
and Calculate snaps inches to nearest 1/8 via inToEighths. Calculate
enables when both drafts parse to > 0. Axis, mask, and scale math unchanged.
grokbot_ty added 2 commits 2026-09-14 19:12:09 +00:00
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.
Why
CAPTURE.md Crop keeps slab pixels and makes removed pixels transparent.
encodeInventoryPng only drawImage-cropped the AABB from the raw photo.

Scope
composeInventoryFrame samples the source through approvedMask.bitset.
Non-slab pixels stay alpha 0. Drop the 8% MARGIN; fill 80% of mask
extents in a 3:4 frame. Floor rules unchanged.

Verification
cd frontend && npm test
12 pass, including alpha contract (no sheet green in opaque pixels).
Author
Owner

Inventory PNG mask + crop fix (Ty UAT 2026-09-14)

SHA: fbcd05169f64bf50581aa430eaa17367bcfee2e5 (on doc-simplification)

Why

After Calculate, measurements were accurate but the inventory PNG kept the sheet background. encodeInventoryPng cropped the AABB from the raw photo and never applied approvedMask.bitset.

Scope

  • composeInventoryFrame applies bitset → alpha 0 for non-slab pixels
  • 3:4 portrait, 80% fill of mask extents, floor 1600 unchanged; removed 8% MARGIN
  • calculate() passes { bitmap, mask: approvedMask, source }
  • inventory.test.ts alpha / crop / floor proofs

Verification

  • cd frontend && npm test → 12 pass
  • Overnight redeployed only: slabuploader-m1-overnight on http://100.64.0.9:10881 (bundle index-BLPFL1gM.js includes bitset gate). Did not touch :10880 or :18080.

UAT

Approve mask → axis → L/thickness → Calculate → confirm transparent background + 3:4/80% crop of the masked silhouette.

## Inventory PNG mask + crop fix (Ty UAT 2026-09-14) **SHA:** `fbcd05169f64bf50581aa430eaa17367bcfee2e5` (on `doc-simplification`) ### Why After Calculate, measurements were accurate but the inventory PNG kept the sheet background. `encodeInventoryPng` cropped the AABB from the raw photo and never applied `approvedMask.bitset`. ### Scope - `composeInventoryFrame` applies bitset → alpha 0 for non-slab pixels - 3:4 portrait, 80% fill of mask extents, floor 1600 unchanged; removed 8% `MARGIN` - `calculate()` passes `{ bitmap, mask: approvedMask, source }` - `inventory.test.ts` alpha / crop / floor proofs ### Verification - `cd frontend && npm test` → 12 pass - Overnight redeployed only: `slabuploader-m1-overnight` on http://100.64.0.9:10881 (bundle `index-BLPFL1gM.js` includes bitset gate). Did not touch :10880 or :18080. ### UAT Approve mask → axis → L/thickness → Calculate → confirm transparent background + 3:4/80% crop of the masked silhouette.
grokbot_ty added 2 commits 2026-09-14 23:40:13 +00:00
Ty UAT rejected phone photos under the 1600px bar.
Lock the mid-band case (shorter side 1000) and floor
geometry at 800×1067 / 0.8×800=640 before the code change.
Product call for the WooCommerce redo. Calculate accepts
sources whose post-crop shorter side is at least 800px.
Warn copy and CAPTURE.md match. No upscale still holds.
Author
Owner

Landed on this branch: inventory PNG shorter-side floor 1600→800 (Ty UAT / Woo redo).

  • Commits: f20d84c (tests), 44d428b (FLOOR + warn + CAPTURE.md)
  • Overnight redeployed: http://100.64.0.9:10881 (slabuploader-m1-overnight only; :10880 untouched)
  • Live bundle check: Be=800, warn says 800px floor
  • UAT: same photo that warned at 1600 should Calculate cleanly if post-crop shorter side ≥ 800
Landed on this branch: inventory PNG shorter-side floor 1600→800 (Ty UAT / Woo redo). - Commits: `f20d84c` (tests), `44d428b` (FLOOR + warn + CAPTURE.md) - Overnight redeployed: http://100.64.0.9:10881 (`slabuploader-m1-overnight` only; :10880 untouched) - Live bundle check: `Be=800`, warn says `800px floor` - UAT: same photo that warned at 1600 should Calculate cleanly if post-crop shorter side ≥ 800
grokbot_ty added 2 commits 2026-09-15 03:20:41 +00:00
Ty UAT: width min/max must describe bulk, not tips. Pin L=48 tapered
case (7 interior stations, no tip endpoints), uniform mid-board count,
and L≤12 empty/zero fallback before the sampleWidths change lands.
sampleWidths used floor(L/6)+1 stations from t=0, so tapered tips
dominated min/max. Sample only interior stations at 6, 12, … ≤ L−6.
When L≤12 there is no interior span: every6In stays empty and
min/max/avg stay 0. CAPTURE.md Widths bullet matches.

sqft, bdft, scale, and inventory PNG are unchanged.
grokbot_ty reviewed 2026-09-15 03:23:02 +00:00
grokbot_ty left a comment
Author
Owner

Tip-trim width sampling (eaedcb5)

Why. Ty UAT: width min/max described tips, not bulk.

Scope. sampleWidths now keeps stations at 6, 12, … ≤ L−6 only. L≤12 → empty/zero. Tests in measure.test.ts. CAPTURE.md Widths bullet updated.

Verification. npm test 17/17. Overnight rebuilt only at http://100.64.0.9:10881 from eaedcb5. Tapered 48in repro: before min=2in (tips), after min=10in (bulk).

UAT. Calculate on a tapered-tip slab; min/max/avg should ignore the first and last 6 in.

## Tip-trim width sampling (eaedcb5) **Why.** Ty UAT: width min/max described tips, not bulk. **Scope.** `sampleWidths` now keeps stations at 6, 12, … ≤ L−6 only. L≤12 → empty/zero. Tests in `measure.test.ts`. CAPTURE.md Widths bullet updated. **Verification.** npm test 17/17. Overnight rebuilt only at http://100.64.0.9:10881 from eaedcb5. Tapered 48in repro: before min=2in (tips), after min=10in (bulk). **UAT.** Calculate on a tapered-tip slab; min/max/avg should ignore the first and last 6 in.
HA_Researcher closed this pull request 2026-09-19 04:11:04 +00:00
HA_Researcher deleted branch doc-simplification 2026-09-19 04:11:04 +00:00

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Ty_Tech/SlabUploader#57