Tokens (light + dark, neumorphism, 54/54 WCAG AA contrast pairs), the self-hosted JetBrains Mono fonts, and interactive mockups of every screen across three iterations. Iteration 3 is final: phone, tablet and desktop tiers, device preview, and "empty spool weight" terminology. The build scripts regenerate the screens byte-identically from source. Implementation is tracked in Maestro feature UI Refresh - V3.0 (224b72a1-8d84-4970-97ea-5dafa4d8ea3b). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
11 KiB
Iteration 3: the remaining screens, plus tablet and phone
2026-09-18. Built on iteration 2 (left unchanged in ../iteration-2/). Same v3
tokens, same shared panel/palette/toast system. Start at
screens/index.html (flow map, 14 guided flows) or screens/devices.html
(every screen in live phone, tablet-portrait and tablet-landscape frames).
1. Your answers, applied
| Question | Your answer | What changed |
|---|---|---|
| One-click suggestion | Fine if accurate; fixable by hand | Kept one click. Every attributed print now has Change spool in its panel, and it states the real effect: "46 g goes back to Elegoo Black #42 and comes off the spool you pick; cost is re-rated." (That's what SetPrintSpool does on dev.) Rows attributed before deductions were tracked get the real refusal: reconcile to the same spool first (see print Test_cube). |
| Weigh-session order | By weight, with location visible | The session always runs lightest first, whatever the table sort. Each step opens with a "Find it: Shelf C" strip and "spool 2 of 4 · lightest first". |
| Landing page | Dashboard | Unchanged. |
| Filament badge | Yes: it flags an assumed weight that could be off | Filament [7] badge = empty spool weight unverified: 3 unknown + 4 estimated (Generic 180 g, Refill 35 g). Its tooltip splits the two. The saved view is renamed Empty spool weight unverified, and the Dashboard item reads "7 spools have an unverified empty spool weight: 3 can't be weighed yet, 4 could be off." |
| Palette commands | Makes sense | load ‹spool› into ‹holder› and attribute ‹print› to ‹spool›. They're typed in stages: the list narrows to spools or prints, then to holders or spools. Full holders show "full · 4/4" and refuse. Two new actions in the empty palette prefill them. |
Check my reading of 4a. I took "identifies an assumed weight" to include estimated empty spool weights, not just unknown ones. If you meant unknown only, it's a one-line change and the badge drops to 3.
1b. Terminology: "empty spool weight", not "tare" (changed 2026-09-18)
"Tare" isn't how the 3D printing community talks, so every user-facing string now says empty spool weight. The short form empty spool is used only where space is tight: column headers, and the scale equation "262 g empty spool
- 812 g filament = 1,074 g". Examples:
| Was | Now |
|---|---|
| Tare unverified (view) | Empty spool weight unverified |
| Empty spool (tare) | Empty spool / Empty spool weight |
| tare unknown | empty spool weight unknown |
| Set a tare · Save tare | Set empty spool weight · Save empty spool weight |
| Remaining = reading − 140 g tare | Remaining = reading − 140 g empty spool |
| Brand tare | Brand empty spool weight |
Not changed: code and API names (tare_grams, tare_source,
effective_tare_grams, tare_origin). Renaming those would be an API break for
no user benefit. CTXD's GLOSSARY.MD still defines "Tare"; I haven't edited CTXD
(read-only for this task). You may want to add "empty spool weight" there as
the user-facing term.
Iterations 1 and 2 still say "tare"; they're kept as snapshots.
2. New screens
| Screen | Who | What it's for | Built from on dev |
|---|---|---|---|
| 05 Lists | operator+ | The 5 name-part lists + spool types. A name bar shows which part you're editing ("Sunlu Rapid PETG CF Silk"). "Used by N spools" links to Filament filtered. Remove is disabled with the reason when in use. Spool types show empty spool weight + trust level; adding one enforces "weight and source together, never 0 g". Demos: rename conflict (409), add error. | ManageLookups.tsx, lookup store rules |
| 06 Labels | operator+ | Queue of spools without a label (preselected) → one PDF. Previews at the real 51 × 21 mm layout (19 mm QR · Brand / Color · taxonomy · #id). Tabs: all spools (reprint), holders (per-printer holder labels that start scan sessions). A format reference is included. | labels.go, /api/spool-labels/unprinted, /api/filament-containers/{id}/label |
| 07 Access | admin | Authentik owns people and roles; Extrudex shows its local view. Role cards (reader / operator / admin, from the route policy). People with sessions (idle and hard expiry) and Revoke… with an inline confirm. Machines & API tokens: new token shown once (exd_…), per-token revoke. |
AccessAdministration.tsx; machines/tokens have API but no screen on dev |
| 08 System | admin | Low-filament report: schedule, threshold, weekly delivery history (retries, errors), Run this week's now. Audit log: who/what/when with links to the thing changed, filters, paging. This device: install, update-ready, offline readiness. | webhook.go, audit.go, PWAControls.tsx; webhook and audit have API but no screen on dev |
09 Scan: holder (/c/:id) |
phone-first | Scan a holder label → a session for this tab. Each spool scanned loads (from storage), returns (already here) or moves here (from another holder). No mode picker. Full → refused, nothing changes. Result card + haptic, a session log, "Done scanning". Camera sheet (demo) + type-a-number fallback. Reader view shows contents only. | ScanSession.tsx, ScannedSpoolRoute |
10 Scan: spool (/s/:id) |
phone-first | A spool label scanned with no holder session active: big remaining, Lives in, one-handed weigh-in (68 px input, before → after, the same two refusals), then open its holder or full details. Empty spool weight unknown → pick a spool type first. | ScanWeighSpool.tsx |
| Printer panels (on Printers) | admin | Add / edit printer (brand filters models from printerCatalog.ts; access code write-only; Elegoo has none), Manage holders (add AMS/AMS Lite, the external holder can't be removed, loaded holders can't be removed, print holder label), Remove printer (type the exact name). |
PrinterForm.tsx, ManagePrinterContainers.tsx, ConfirmRemovePrinter.tsx |
| Devices | — | Every screen in 3 live frames, dark/light. | — |
The nav now reaches everything: Manage (Lists, Labels, Access, System) on the rail, and a More tab on phones.
3. Things I checked against the code (and corrected in the design)
| Claim I almost made | What dev actually does |
Design now says |
|---|---|---|
| "Run the report again resends it" | A delivered week returns its existing result; only a retrying week is retried (runPeriod) |
"Already delivered Monday 08:00; nothing was sent again. Running it never sends a duplicate." |
| "Remove printer, and history is kept" | Also refused while any holder still has spools, and an open print is settled as unknown | Both shown in the remove panel; confirm disabled while loaded |
| Re-attributing a charged print is blocked | Allowed: grams are returned to the old spool and taken from the new one, except rows from before tracking (ErrUnreconciledAttribution) |
"Change spool" with the exact effect, and the real refusal |
| Scanning a spool label always opens it | Only with no holder session active; otherwise it's loaded/moved into the active holder | Both routes designed; each page explains the other |
Sample token ctxd_… |
Extrudex tokens are exd_… |
Fixed |
Two findings for you (not design changes):
- List names are case-sensitive in the schema (
UNIQUE, noCOLLATE NOCASE), so "petg" can be added beside "PETG", which is the very problem the lists exist to prevent. The Lists screen shows a proposed near-duplicate warning, labeled as a proposal. - Audit log, low-filament report and machine tokens have APIs but no
screens on
dev. They're designed here and tagged "API exists · no screen on dev yet".
4. Responsive specification
| Phone < 700 px | Tablet 700–1180 px | Desktop > 1180 px | |
|---|---|---|---|
| Navigation | Bottom bar: Dashboard · Filament · Prints · Printers · More (sheet with Manage + theme), badges on icons | Compact rail (88 px): icon + label, badges top-right, Manage group | Full rail with labels, Manage group, theme switch |
| Search | "Search…" pill, no shortcut hint | Full placeholder; shortcut hint hidden on touch | Full placeholder + ⌘K |
| Primary action | Floating + (56 px) above the bar | Header button | Header button |
| Panel | Full-screen sheet | 480 px side sheet over the page | 520 px side sheet |
| Tables | Low-priority columns dropped (hide-m); Lists become stacked rows |
Tablet-only drops (hide-t): Value, Cost, Use; Weigh becomes icon-only |
All columns |
| Touch | 44 px+ targets; menu items 12 px padding; pointer: coarse rules |
Same pointer: coarse rules |
Mouse sizes, keyboard hints |
| Scan pages | Designed for this: 64 px scan button, 68 px scale input, haptic on result | Centered 560 px column | Centered 560 px column |
| Dashboard | One column; attention buttons full-width | One column below 980 px, two above | Two columns |
| Printers | One card per row | 2 per row (portrait 1, landscape 2) | 3 per row |
5. New open questions
- Empty spool weight badge scope. Unknown + estimated (as built), or unknown only? See §1.
- Near-duplicate list names. Add a case-insensitive uniqueness check (a schema change plus migration), or only warn in the UI?
- Holder scan session end.
devkeeps the active holder for the browser session. Is an explicit Done scanning (as designed) wanted, and should it also time out after a few minutes idle? - Machine tokens, audit log, low-filament report. Worth building screens for all three now, or keep them API-only until something needs them?
- Label printing. Always PDF download, or is there a specific label printer you'd want a direct-print path for?
6. More implementation tasks (titles only, smallest first)
- Rename the "Tare unknown" view to "Empty spool weight unverified" and include estimated weights
- Add a Filament nav badge for unverified empty spool weights
- Always run weigh sessions lightest first
- Show the spool's location at the top of the weigh step
- Add "Change spool" to attributed prints, stating the returned grams
- Show the unreconciled-attribution refusal with its fix
- Add a case-insensitive near-duplicate warning to Lists
- Show "Used by N spools" with a link on every list value
- Add label previews at 51 × 21 mm proportions
- Add holder labels to the Labels screen
- Add the "load … into …" palette command
- Add the "attribute … to …" palette command
- Add a More tab and sheet to the phone navigation
- Add the compact tablet rail (700–1180 px)
- Add a Manage holders panel with the removal rules
- Add "Remove printer" with name confirmation and the loaded-holders refusal
- Add a "Done scanning" control to holder scan sessions
- Add the phone weigh-from-label layout
- Add an audit log screen over
GET /api/admin/audit - Add a low-filament report screen over the webhook endpoints
- Add a machine and API token screen with show-once tokens