Files
SlabUploader/docs/UX.md
T

13 KiB
Raw Blame History

Mill-owner UX — phone and PC browsers

Status: SIGNED · 2026-09-06 PT · Ty approved with PC browsers in the compatibility matrix · SlabUploader

This is the presentation spec for the mill-owner flow. It says what a mill owner sees and does, in the order they do it. The rules behind each screen live elsewhere: AGENTS.md locks the product decisions, docs/TECH-SPEC-PIPELINE.md owns the math and image prep, docs/DATA-MODEL.md owns the store row and its statuses, docs/CONTENT-WOO.md owns listing copy and the Woo payload, and docs/OPENAPI.md owns the wire. This file names what shows up on screen; it does not restate those rules.

The reader is a mill owner photographing one slab at a time, online, from a supported browser on a phone or a PC. The app is a single column of screens, top to bottom: capture, then review, then publish. There is no dashboard and no batch view in this version. Capture often starts on a phone camera; the same flow must also run on a PC browser.

Jobs

Five jobs cover the whole flow. Each section below names what the owner does, what they see, and the lock that governs it.

  1. Phone photo to a store listing. Photograph the slab, confirm the cut and the numbers on the browser, review the listing, create the WooCommerce product. The happy path stays in the browser until the owner confirms the mask and the numbers.
  2. Practice drafts customers never see. A practice run creates a WooCommerce draft with an SKU that starts SLAB-UAT-. Customers cannot open it. It is always a draft, even when Settings say publish.
  3. Retake when the overlay cut looks wrong. The mask overlay on the source photo is the only quality check. If the edge is off after tuning the four knobs, the owner retakes the photo.
  4. Fill or override store fields. On review, the owner fills every required field by hand, or keeps what vision filled when it was confident enough. Typed values always win.
  5. Publish. The owner taps publish. The server creates the product in the store and reports the result back on the same screen.

Capture: photo to numbers

The capture flow is one continuous screen with a few confirmations, not separate pages. The owner stays on it from first photo through confirmed mask and axis.

Photos

The owner takes 1 to 5 inventory photos of the slab. The first should be top-down, lens parallel to the broad face; a tilted shot makes square feet and board feet wrong, and retake is the fix. There are no calibration photos in this design. Only inventory photos exist.

Mask overlay and four knobs

The app removes the background sheet and draws the mask as an overlay on the source photo, showing which pixels count as slab and which do not. The owner reviews that edge against the real wood. This is the only quality check in the pipeline. There is no confidence score, no auto-gate, and no photo-quality checker. A clipped or over-inclusive mask shifts every downstream number with nothing later to catch it, so the eye on the overlay is what stands behind the measurement.

Four knobs sit on this same screen, next to the overlay:

  • Sheet — auto, green, or black. Auto samples the borders and picks; the owner overrides when the sheet is an odd color.
  • Sensitivity — one slider from tighter to looser detection.
  • Edge offset — pulls the cut in or out a few pixels so it sits on the wood, not in the sheet or into the grain.
  • Feather — hard edge versus a couple of pixels of blend.

Changing any knob re-runs background removal immediately and refreshes the overlay. There is no apply step and no server round trip for mask tuning in this version. The four values persist on the browser until reset to default; they are not Settings fields.

If the edge still looks wrong after tuning, the owner retakes the photo. That is the resolution in this version. A server-side matting path exists in the architecture for awkward backgrounds later, but it is deferred and has no control here.

Length axis

After the mask is confirmed, the app draws the bounding rectangle and the length axis over the slab, with width sample lines perpendicular to that axis. The owner confirms the axis follows the slab's prevailing length. If it is skewed, they rotate it; samples and square feet update live. On a nearly square slab the owner says which way is length.

Numbers

The owner enters length (in 1/8" steps), thickness (a 1/8" pick), and the SKU by hand. The browser then computes square feet, board feet, and width samples from the confirmed mask and axis:

  • Board feet = square feet × thickness in inches. One square foot at one inch thick is one board foot. There is no divide-by-12.
  • Widths are sampled every 6" along the length axis; the listing carries min, max, and average.
  • Square feet and board feet show to two decimals.

These numbers are computed in the browser and stored as sent. The server does not recompute them.

Photo prep

Each inventory photo is cropped to a 3:4 portrait frame with the slab centered at 80% fill, saved as a transparent PNG. If the source has enough pixels, the shorter side lands at 1600 or more after crop. If it cannot reach that, the app warns and asks for a retake; it does not invent pixels. The owner sees the processed result alongside the original while the draft is retained.

Continue

When the mask, axis, length, thickness, and SKU are set, the owner continues. That uploads the originals plus the processed PNGs plus the numbers to the server as a calibrated draft, and moves to review. The slab status becomes calibrated.

Review: fill or override store fields

Review is where the listing row gets filled. Every field below maps to a column in the slabs table in docs/DATA-MODEL.md. The owner can type any of them; vision may have pre-filled some, but typed values always win and every value stays editable.

What vision fills, and when

When inference is on, Call 1 runs after the calibrated draft uploads. It sends all inventory photos downscaled to 1024 plus the store's synced species and attribute lists, and returns a guess with a confidence per field. The threshold is 0.7 by default:

  • A field at or above 0.7 pre-fills on review. It stays editable; the owner can change or clear it.
  • A field below 0.7 starts empty. The app does not show the model's low-confidence guess as a default, and an empty start is not the same as a finished listing.

With inference off, every field starts empty and the owner fills all of them by hand. Inference never blocks publish and never auto-publishes. Call 2 (Generate text) does not run on its own; it fires only when the owner taps it.

The fields

The required set for a list-ready slab is the full store row, not species plus one figure:

  • SKU — [A-Z0-9-]{3,24}. Practice runs use SLAB-UAT-*.
  • Length and thickness — from capture; still editable here.
  • Square feet, board feet, widths — shown as the client-computed values, with a manual override path. The owner's typed number wins over the computed one.
  • Species — exactly one leaf category from the synced store list. No hardcoded or invented species; a new store species appears only after the next sync.
  • Wood categories — at least one, from the synced list.
  • Edge type — one term from the synced attributes.
  • Figure — at least one term.
  • Grade — at least one term.
  • Thickness band — the Woo range that the measured thickness rounds up into (for example, a 1" pick lands in the 1"–1 1/2" band). The exact labels come from the synced store attribute.
  • Moisture — one term, defaulting to kiln-dried. It is never inferred; the owner sets or keeps it.
  • fig- tags* — at least one.
  • feat- tags* — zero or more (character, inclusions, voids, checks map to existing feat-* tags only).
  • Price — a recommendation pre-fills from the species $/bdft rule times board feet; the owner may override and that sticks. With no rule for the species, the price starts empty and the owner types it. Publish is still allowed with a manual price.

Title, short title, and description are not required to reach ready. They can be typed by hand, generated from templates at publish, or written by Generate text (Call 2) when enabled. Templates always run; they inject the deterministic numbers so the model never touches dimensions.

How missing fields show up

A field that is still empty and required shows its nudge inline, in place, the moment it is empty. The owner sees each gap where it sits rather than learning about a problem after scrolling past it. There is no single submit-time check that lists everything at once; the nudges are per-field as the owner works. The slab cannot reach ready until the full set above is filled, whether by hand or by a confident Call 1 the owner kept.

Generate text

When content generation is enabled, a Generate text button runs Call 2 and fills title, short title, and description with model prose plus template numbers. The owner can edit the result freely. If the endpoint is missing or down, the button is disabled; templates still work and publish is never blocked by a failed Generate text.

Publish: create the store listing

The owner taps Publish when review is complete. What happens next depends on the SKU and Settings.

Draft versus live

  • A normal SKU publishes according to the woo_create_status setting, which defaults to draft.
  • Any SKU matching SLAB-UAT-* always creates a WooCommerce draft, even if Settings say publish. That is how practice runs stay invisible to customers.

The app's "published" means the store create succeeded and the local row plus photos were cleaned up. That is not the same as the product being live in the store; a draft is created but hidden until Ty promotes it.

What the owner sees on each outcome

  • Success. The screen shows that the listing was created, with the store's product reference when available. The local draft and its photos are gone from the app.
  • SKU already exists. Under the SKU field: "This SKU already exists in the store (any status)." Two paths appear: edit the SKU to a new value, or open the existing listing. This version does not update the other product in place. The draft stays in the browser; nothing is lost.
  • A value no longer valid. If a category, attribute, or price was removed from the store between sync and publish, that field shows "This value is no longer valid. Pick from the current list," with the current options. The owner re-picks and retries.
  • Store unreachable or other failure. A plain message on the screen says the listing could not be created and to try again. No status number, no stack trace, no raw error code reaches the owner.

The server keeps a log of every attempt either way, so a failed publish can be retried from the same screen without re-photographing.

Browser compatibility

Ty signed this walk on 2026-09-06 PT with one amendment: the compatibility matrix includes PC browsers as well as phones. This app is not mobile-only.

Supported families (matrix):

  • Phone: iOS Safari, Android Chrome
  • PC: desktop Chrome, desktop Edge, desktop Firefox (Windows and macOS at minimum)

Exact minimum version pins are still tracked under issue #4. The matrix above is the product lock for which classes of client are in scope.

Unsupported browser behavior: hard stop. Show a clear message that this browser is not supported, with a link to update or switch to a supported browser. No polyfill strategy in this version.

Load-bearing client surfaces that must remain: canvas-based mask overlay and live knob re-run, image crop/prep to 3:4 transparent PNG, file/photo intake for inventory shots, and the online API calls for draft upload, review, and publish. Depth for APIs stays in docs/TECH-SPEC-PIPELINE.md and docs/OPENAPI.md.

Deferred features

Photo quality, OCR, ruler detection, server-side measuring, U2Net control, and offline capture are deferred post-POC. The overlay edge plus the owner's eye is the only quality gate. SKU is typed by hand. Length is entered by hand; the browser derives its own pixel-to-inch scale. The browser owns the happy path for measuring and cropping. The U2Net one-photo contract stays in the architecture for later. Capture is online only.

Where each job lives

Job Screen(s) Governing lock
Photo to listing Capture through review to publish AGENTS §1, TECH-SPEC, ARCHITECTURE swimlane
Practice drafts (SLAB-UAT-*) Review (SKU field), Publish outcome AGENTS §5, CONTENT-WOO payload status
Retake on bad cut Capture mask overlay TECH-SPEC §3.4–3.5, issue #15
Fill or override fields Review DATA-MODEL slabs, AGENTS §6/7, issue #9
Publish and recovery Publish outcome states CONTENT-WOO §2.5–2.6, OPENAPI 409/422, issue #18