UAT is POC, so #10 is in DOC COMPLETE. Capture knobs live on-screen, review confidence presentation, 409/422 field recovery, taxonomy cache, portable Call 1→Call 2, shared validation. U2Net Try harder deferred, not deleted.
7.6 KiB
SlabUploader UX — POC interaction spec
Status: SPEC · 2026-09-01 · issue #10 (presentation) · functional rules in #9 / AGENTS
This is the UAT-facing interface contract. Wireframes are described as screens and states, not pixels. Implementers and testers use this file plus AGENTS.md and docs/TECH-SPEC-PIPELINE.md.
Functional numbers (0.7 threshold, empty vs prefill) live in AGENTS §6. This file only says how those rules look and feel.
0. Principles
- One-handed phone, outdoor or mill-floor lighting, thumb reach.
- The user always sees what is missing on the field, not only after Submit.
- Manual values always beat inference. Empty is honest; fake prefill is not.
- No raw HTTP codes or stack traces. Recovery stays on the same screen as the field.
Mandatory before ready (continue to publishable):
- exactly one species
- at least one wood category
- at least one figure (
fig-*or the Figure attribute — same locked set as DATA-MODEL)
Issue #9 named species + ≥1 figure as the confidence-gate floor. Wood category is also required to list (original product). Presentation treats all three as mandatory with inline nudges (#10).
1. First-run (calibration trick)
Not OpenCV ruler detection (that path is deferred). First launch of capture shows a short overlay, dismissible, remembered in localStorage:
- Lay the slab on a green or black sheet.
- Shoot top-down, lens parallel to the broad face.
- Put a tape or known length along the prevailing grain so you can type length in inches. The app does not read the tape.
- Take 1–5 photos. Confirm the mask before trusting numbers.
Skip control: “Don’t show again.” Reset lives in Settings, not on the capture chrome.
2. Capture — mask is the load-bearing step
Background removal defines the pixel footprint. Length, widths, sqft, and bdft all sit on that mask. A clipped or fat mask silently moves the bounding rectangle and the 6" width samples.
Layout (one screen)
- Full-bleed photo with mask overlay on the original (included vs excluded), not only the cropped PNG.
- Four knobs on this screen, next to the preview — never a trip to Settings:
- Sheet: auto / green / black
- Sensitivity
- Edge offset
- Feather
- Confirm edge. Then length-axis overlay (rotate / confirm). Then length, thickness, SKU.
- Retake photo. POC does not show “Try harder” (server U2Net). That control is deferred, not deleted — do not remove it from architecture docs.
Live re-run
Changing any knob re-runs client background removal on the current photo and refreshes the overlay immediately. No save button for knobs. No server round trip for mask tuning in POC.
Empty / error states (capture)
| Situation | What the user sees |
|---|---|
| Sheet color not auto-detected | Prompt: pick green or black. Stay on this screen. |
| Mask not one contiguous slab | Banner on the photo: “Cut is broken — retake or tighten the sliders.” Confirm stays disabled. |
| Axis looks wrong | Rotate control; if still wrong, retake. |
| After crop, shorter side would be < 1600px | Warn: this photo cannot list until retake. No fake upscale. |
| User tries to continue without confirming mask | Confirm is the gate; Continue is disabled until confirm. |
Knobs persist in localStorage (reset-to-default in Settings). They are not server secrets.
3. Review screen
After calibrated upload (and Call 1 if inference is on).
Field rendering
| Call 1 confidence | Species, wood categories, edge, figure, grade, feat-* |
|---|---|
| ≥ 0.7 (default; configurable in server settings later, not a POC slider) | Prefill from Call 1. Every value is editable. Show a quiet “suggested” affordance, not a lock. |
| < 0.7 | Leave empty. Do not stash the low-confidence guess in a hidden field. |
| Inference OFF | Empty; user fills by hand. Same mandatory set. |
Pickers for species, wood category, and figure list only Woo-synced leaves. No “other”, no hardcoded mill list. A species that appeared in Woo after last sync is invisible until the next successful sync.
Price: recommendation from species $/bdft × bdft when a rate exists; otherwise empty. Override always allowed.
Title / description: empty until the user taps Generate text (Call 2) or types. Call 2 never auto-runs.
Per-field nudges (not submit-only)
While a mandatory field is empty, that control shows an inline state (mark + short line under the field):
- Species: “Pick the species from the store list.”
- Wood category: “Pick at least one wood category.”
- Figure: “Pick at least one figure.”
Nudges appear as soon as the screen loads with empties, and clear when the field is valid. Submit/Continue to ready stays disabled while any of the three is empty — and the user can still see which one without scrolling to a toast after tap.
Do not use a single post-submit modal as the only signal.
Overrides
Changing a prefilled species/figure is a normal edit. No confirm dialog. Manual value is what publishes.
4. Settings (relevant bits)
- Store URL: read-only text from
WOO_BASE_URL(compose). Not an input. - Woo username + application password: inputs. Password never round-trips on GET.
- Inference endpoint + key + model; enable flags.
- Species $/bdft table (seeded from synced species only).
woo_create_statusdraft | publish.- Reset capture knobs to factory.
- Manual “Refresh taxonomy” is optional for POC; test-connection always syncs.
5. Validation (field exit and submit)
Shared module on FastAPI (docs/OPENAPI.md). Client caches it; hash checked on submit, not on every blur.
- Blur: local check with cached rules. Required fields block; optional empty is valid; optional with content must pass.
- Length: hard cap at the Woo limit (cannot type past it).
- Character set: invalid characters rejected on exit.
- Submit: full pass + hash check. Mismatch → “Updating rules” → re-validate → retry. No data loss.
Presentation: errors sit under the field. No top-of-screen dump for field problems.
6. Publish errors (after commit)
Draft stays on the phone.
409 duplicate SKU — inline on SKU:
- “This SKU already exists in the store (any status).”
- Actions: Edit SKU (focus the field) · Open existing listing (admin URL if the server returned it; otherwise copy SKU and a one-line “look this SKU up in Woo admin”).
- Never show
409as the message.
422 stale value — inline on the field Woo rejected (category, attribute, price, …):
- “This value is no longer valid. Pick from the current list.”
- Options from the synced cache after the submission-time refresh.
- Never show
422as the message.
Server 5xx / timeout / auth: one recoverable banner (“Couldn’t reach the store — try again”), draft intact. Not a field nudge.
7. UAT can write cases against
- First-run overlay appears once; skip persists.
- Knobs on capture; each slider live-reruns mask overlay on the source photo.
- Confirm disabled until mask is contiguous.
- Review with inference ON and confidence ≥ 0.7: fields filled, editable.
- Review with confidence < 0.7: species/wood category/figure empty; inline nudges; cannot mark ready until all three satisfied.
- Species picker contains only synced names; typing a non-synced name is impossible.
- Duplicate SKU: inline SKU recovery, two actions, no raw code.
- Stale category after sync: 422 copy on that field with current options.
8. Non-goals (this spec)
- Visual branding, type scale, color tokens.
- Server U2Net “Try harder” control (deferred).
- Configurable 0.7 slider in the UI.
- Offline / airplane-mode chrome.