The U1 printer panel: three cards, an ACE mode row and a toolhead gri… - #39
Draft
physicsG wants to merge 5 commits into
Draft
The U1 printer panel: three cards, an ACE mode row and a toolhead gri…#39physicsG wants to merge 5 commits into
physicsG wants to merge 5 commits into
Conversation
#38) The Printer section is one widget seen in both **Prepare** and **Preview**. On a U1 it drew four tabs named *Nozzle 1..4* that each held the same `Diameter` combo — changing any one writes all four and switches the whole preset, because the machine refuses a mixed set — and it hid its sync behind an unlabelled 16px glyph in the title bar. Measured by running the app, not by reading it. It now follows `docs/ace-mmu/15-printer-panel.md` and its mockup. ## What is here | Commit | | |---|---| | `docs(ace-mmu)` | Corrects the plates paired with the wrong `BedType` enum | | `feat(config)` | `ace_mode` + per-head `ace_head_unit` / `ace_head_capacity` | | `feat(sidebar)` | The panel rebuild and seven plate assets | The two design commits underneath are the docs branch this stacks on, so the design and the code arrive together. **The panel** — three cards at equal height (printer with its thumbnail over the preset combo, plate, and *Sync info*); an ACE mode row mirroring the printer's own three-way switch; a bordered box per toolhead wrapped 2×2 with an `ACE` row and a `Diameter` row. The plate card absorbs what was a full-width `Bed type` row, which is where the height for the head boxes comes from, and the whole card opens the list so the picture is the click target. Heads are named **Toolhead N**, which is what the machine's own interface calls them; the sidebar was the last place saying *Nozzle N*. **Every other printer keeps the names it had** — an IDEX machine's *Left / Right Nozzle* tabs are untouched. `update_nozzle_settings` builds those boxes instead of notebook pages, which leaves `CustomNotebook` with no callers; it goes, 269 lines and the stray `#pragma once` that warned on every build. The 135-line sync handler is lifted verbatim into `Sidebar::sync_printer_info`. **Config** — `ace_head_unit` and `ace_head_capacity` are taken verbatim from `feat/ace-mmu-slicing` so that branch merges onto this rather than colliding. `ace_mode` is new: that branch reads the mode from the printer and never stores it, and a panel has to draw something with the printer switched off.
…eset on Linux (#41) One process profile is on disk as 0.10mm Color Mixing @Snapmaker U1 (0.4 Nozzle).json while Snapmaker.json lists it, and the preset inside it names itself, with a lowercase "nozzle". It is the only one of 198 profile filenames spelled that way; every other file, every manifest entry and every compatible_printers string uses "nozzle". Windows and macOS have case-insensitive filesystems and never notice. On Linux the file is not found, load_vendor_configs_from_json fails on it, and one missing file of 570 takes the whole vendor bundle down with it. No Snapmaker machine preset loads at all: load_from_json: parse .../0.10mm Color Mixing @Snapmaker U1 (0.4 nozzle).json got a parse_error load_vendor_configs_from_json, got error when parse process setting from ... can not find parent for config .../user/default/machine/<any user preset>.json! The printer combo comes up empty, the sidebar draws NO IMAGE with a blank nozzle diameter, and any user preset inheriting a Snapmaker machine is orphaned. A clean install on Linux cannot select a printer. The fix is the filename rather than the manifest: the manifest entry, the preset's own "name" field and its compatible_printers all already agree on lowercase, so the file is the outlier. The vendor version goes to 02.02.55.03 as well. Without it the corrected file only reaches new installs - check_installed_vendor_profiles() reinstalls from resources only when the resource bundle is newer than the one in the datadir, so anyone already carrying 02.02.55.02 would keep the broken set. Verified on Linux against a datadir in the failing state: zero parse errors, zero orphaned parents, all 570 manifest entries resolving, and the printer selectable again.
Reconstructs the U1 **Device page** — the webview Orca shows for a connected printer — as plain HTML/JS, alongside the shipped Flutter bundle. `PrinterWebView` has an **Original / Rebuilt** switcher and the choice persists in `u1_reconstructed_ui`, so nothing is replaced: both are there and either can be shown. Cut from `develop/add-multiace-support`, 0 behind, so it merges clean. ## What is in it | | | |---|---| | `resources/web/device_page/` | The reconstructed Device page: two destinations (Device control, Storage), five panels, one directory per panel | | `resources/web/shared/` | Bridge client, protocol constants, state store, a simulated printer, the fault catalogue | | `resources/web/print_processing/` | The print-processing popup, same treatment | | `docs/u1-webui/` | The reverse engineering it was built from, and 19 extractors that regenerate every data file | | `src/slic3r/` | The host side: answer every page request, survive a printer that is not there | `js/registry.js` is the table of contents — destinations and their panels — and everything inside `.content` is built from it, so `index.html` is 42 lines of shell. A panel is handed its own commands and nothing else, which makes reaching for another panel's command a `TypeError` rather than a quiet dependency. ## Where to start reading [`docs/u1-webui/STATUS.md`](docs/u1-webui/STATUS.md) — what is proven against hardware, what is not, and what to pick up next. It is deliberately explicit about the second: the **Not verified** section names what was built against the simulator only, because that proves the client and the simulation agree and not that the printer does.
Nothing loads `flutter_web?path=2` any more and the Original / Rebuilt switcher is gone. Getting there was not a matter of deleting a button: the shipped page was doing one job for Orca that nothing else does. `sw_UpdateMachineFilamentInfo` reads like "set the filament on the printer" and never leaves the host. It writes `machine_filaments` and `m_connect_machine_info_list` — the only source the Prepare sidebar's filament combos have, one writer, three clearers. The reconstruction was not calling it, so retiring the shipped page silently deleted the sidebar's filament sync. `core/orcasync.js` does it now, on every state change, guarded by a comparison and re-armed on reconnect. Two controls WERE calling it, for something else: `setFilament` and Print Preferences sent a flat `print_task_config` patch, which fails the host's first `if` and never reaches the printer. Neither awaits its own request, so both did nothing, silently, on a real machine. What writes the printer is `SET_PRINT_FILAMENT_CONFIG` and `SET_PRINT_PREFERENCES`, recovered verbatim from the shipped bundle — including that the first is single-quoted, the second is not, and that Auto Leveling goes out as BED_LEVEL though the machine reports `auto_bed_leveling`. The print popup had the same two calls. `nozzle_diameters` is not in `print_task_config` at all; it comes from `machine.system_info`. Three bugs that only running the app could show: - A side effect must not ride on the repaint. The sync was inside render()'s requestAnimationFrame; WebKit fires none into a view that is not composited, which the Device tab is not at startup. It ran in every suite and never once in Orca. - A push-only channel needs an initial value. sw_SubscribeUserLoginState registered a subscriber and replied with nothing, so it reported only changes — and the account is restored ~4 s before the Flutter bundle finishes parsing and subscribes. The Home tab showed signed-out over a live session that was in the config the whole time. - Finishing the path lit up code that had never run. With `m_connect_machine_info_list` non-empty for the first time, `refresh_filament_sync_marks()` segfaulted at address 0: SyncMarkOverlay is a child of the filament combo, both removal sites Destroy() the combo without dropping the pointer, and SetSynced() reaches the virtual IsShown() through a freed vtable. The invariant was a comment on the member and nothing else. Same switch-on is why the sidebar's "Machine Filament" section had never been drawn: it is filtered on a nozzle diameter the page never used to send. Also: `load_current_presets()` no longer runs on the stack of a webview message handler, and `handle_web_message` catches rather than letting a malformed command unwind through wxWidgets. The Snapmaker login is untouched — sw_UserLogin still opens Orca's own SMUserLogin on id.snapmaker.com, and no reconstructed page has a login form. The rail can now ask for that dialog rather than offering one. Verified in Orca against 811002511261022618B3. Suites: unit 208/208, conformance 165/165 (7 new), coverage clean, WebKit 61/61 and 54/54, drive/orca-sync.js 23/23 (new).
…n hardware (#44) Rebuilds the print-processing popup — the dialog Orca opens between "Send to printer" and the print starting — from the shipped Flutter bundle, the same way the Device page was done, **and then drives it against a real U1 end to end**: upload, start, print, cancel. ## Why it needed rebuilding rather than finishing Reading the host's own handlers turned up more than "unfinished": - **The filament card read a shape the host has never returned.** It read `mapping.filaments[i]`; `sw_GetFileFilamentMapping` replies with **parallel arrays**. Only the popup's own mock invented an array of objects, so it drew four rows against the simulator and *"No filament requirements read from this file yet."* against Orca. - **The send failed on its first command.** `sw_StartLocalPrint` rejects `{}`. - **The file came through the wrong door.** `sw_GetPrintZip` serialises a `std::vector<char>`, so a 12 MB zip crosses the bridge as ~40 MB of JSON integers. - **The progress bar was a `setTimeout`** counting to 100 whether or not anything uploaded. - **Five fields arrived and went undrawn**, chiefly `thumbnails[]` — the rendered plate. Written up with line numbers in [`04-requirements.md`](docs/u1-webui/03-print-processing/04-requirements.md). ## What the shipped dialog actually is The widget tree is all in `main.dart.js` and the constants resolve, so [`original-dialog-mockup.html`](docs/u1-webui/03-print-processing/original-dialog-mockup.html) is measurement rather than inference — every number cites its source, held by `check_mockup.py` (73 checks). **Three things neither shipped screenshot could show**, both having been captured with the filament section empty: - **Edit Filament is a Wrap of 80 × 100 cards**, a colour block over a toolhead picker. - **There is a fifth section** — a nozzle-mismatch banner (`A.R5`). - **A toolhead whose type _or_ nozzle does not match is passed `enabled: false` and cannot be picked at all.** That is behaviour; the drive script checks it by clicking a refused item and asserting nothing moved. ## The rebuild Follows the Device page's contract — a registry, a folder per panel, its own commands module each. Six primitives moved to `shared/` to be used by both surfaces: `dom`, `render`, `pending`, the modal sheet, the trace pane, and `connection.js`. `cssColor` is **not** reimplemented; it stays where the alpha-zero rule already lives. **One mock for both surfaces.** The U1 was already shared; Orca's half of this dialog now is too, on the same simulated printer. Not tidiness — this dialog matches the *file's* filaments against the *machine's*, and a mock that invented both lists could never report a mismatch. ## Driven against a real U1 `811002511261022618B3`, Orca closed, a person watching. The full account with the wire is [`05-hardware-e2e.md`](docs/u1-webui/03-print-processing/05-hardware-e2e.md). **The send contract, which Orca's source does not contain** — `sw_StartLocalPrint` passes params straight through, so only the firmware could say: - the upload is the page's and works cross-origin: `POST :7125/server/files/upload` → `HTTP 201` with the item it created - **`path` carries no root.** `item.path` and `item.root` are separate fields; `gcodes/x.zip` → `File not found`, `x.zip` → `Print started`. The page reads the stored path back off the upload's own reply instead of constructing one. - **the firmware unpacks the zip** — the start reply names the inner `.gcode`, and the archive does not survive. **A bug the simulator could not find.** `initialAssignment` fell back to *identity* when Orca had no `filament_extruder_map` — and outside Orca there never is one. On this machine that put filament 3 on the empty head and filament 4 on PETG: **two toolheads the picker itself refuses**, with nothing on screen saying so, because the warning mark means "nothing chosen" and something had been. Unassigned is the default now, which is the bundle's own answer. **A cancel that reported failure while it worked** — and the reason was not what I first said. Klipper runs G-code sequentially, so the cancel was queued behind the homing move. Measured with `G4` blocking the queue, moving nothing: | queue | `sw_MachinePrintCancel` | |---|---| | empty | 197 ms | | behind `G4 P3000` | 3323 ms | | behind `G4 P6000` | 6213 ms | The round trip is the queue plus ~250 ms every time. `u1_bridge.py` had already learned this (80 s, after a 31 s toolchange came back as "the printer refused the command"); the page's own client was still at 15 s for everything. `sswcp.js` now waits on two clocks, split by `PRINTER_BACKED`. ## Testing it, and a skill for next time ```bash R=resources/web/shared/tests python3 $R/run_webkit.py --size 714x750 --page 'web/print_processing/index.html?mock=1' \ --drive $R/drive/print-dialog.js # the simulator python3 $R/run_webkit.py --real --sn <SN> --size 714x750 --settle 25 \ --gcode ~/models/plate.gcode --page web/print_processing/index.html \ --drive $R/drive/print-dialog-machine.js # a real U1, read-only ``` `u1_bridge.py` answers Orca's half out of the `.gcode` passed to `--gcode`, parsing the metadata Orca's own slicer wrote, so the dialog sees a real plate rather than a fixture. `sw_StartLocalPrint` is gated behind `--allow-print`: a suite that can start a print is a suite that will. **`.claude/skills/u1-hardware-test`** holds the procedure — preconditions, the ladder of read-only rungs before anything that moves the machine, the rails on the one script that starts a print, the cleanup (the firmware unpacks the zip, so the *unpacked* name is what to delete), and how to read a slow reply. ## Three bugs found in existing code - **`sw_GetFileStream` was misclassified as printer-backed, and the check agreed.** The conformance suite took each handler's span up to the next `sw_*` signature; anything not named `sw_*` in between landed in the earlier one's span. List and check came from the same derivation, so they agreed with each other and not with the C++. - **`bridge-methods.json` had `sw_StartLocalPrint → method: null`** with an honest reason and a wrong conclusion — the method was three lines above the call, behind a named constant the extractor only looked for as a literal. - **The mock returned a `file_url` on port zero**, so the send path was *unexercisable* in the simulator. Nothing had noticed because no check had ever pressed Send there; the first thing to try it was a script being smoke-tested before it ran against hardware. ## One deliberate departure from the shipped dialog **Printer rows carry an address**, and the serial when that is what tells two rows apart. Orca's config routinely holds a stale record beside a live one — this machine has two identically-named entries on the same IP. Noted in both directions: the README says what and why, and the specification mockup says the reconstruction differs here. ## Checks | | | |---|---| | print dialog — simulator, `?path=4` / `?path=5` | 52/52, 5/5 | | print dialog — real bridge, real plate, no printer | 21/21 | | print dialog — **connected U1**, read-only | 15/15 | | cancel route, idle machine | 4/4 | | cancel latency under a blocked queue | 5/5 | | **send → print → cancel, real machine** | 6/6 | | specification mockup | 73/73 | | Device page | 61/61 | | conformance · unit_jsc · coverage | 165/165 · 228/228 · clean | ## Honest limits - **No plate has been left to run.** Everything is cancelled within seconds. - **That homing is what filled the queue is inference** from the timing; the `G4` measurement is direct, that observation is not. - **The cloud path is untouched** — `sw_StartCloudPrint`, `server.files.pull`, S3. - **Whether a started job honours the mapping and preferences** is unchecked; the macros are acknowledged, which is not the same thing. - **The multi-plate Model Information layout is not built** — unreachable through this host, recovered and drawn in the mockup. - **A file filament's nozzle is inferred**, the only inferred thing here. ## Also in this branch The first commit predates the brief being clarified: three interactive **design options** under `resources/web/print_processing/mockups/`. Kept deliberately — the reconstruction was asked for *first*, and these are where a design conversation starts once it ships. They are not wired into anything.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request introduces comprehensive design documentation for the new U1 printer panel, ACE visual standards, and U1 plate template in the documentation directory. These documents lay out the intended user interface, visual language, and data handling for upcoming features, providing detailed references for future implementation. No code changes are included—these are design-only documents meant to guide development and ensure consistency across the user interface.
Printer Panel Design and Workflow
15-printer-panel.md, detailing the planned redesign of the printer panel for the U1, including a unified sidebar for Prepare/Preview, improved topology and filament information, and a new sync workflow. The document specifies current defects, the new UI structure, ACE mode handling, assignment popovers, and outlines the implementation/shipping order.ACE Visual Standards
16-ace-visuals.md, establishing a standardized visual language for ACE units, including badge and glyph specifications, color schemes, and integration with existing AMS widgets. It covers visual treatments, moisture/temperature display, and how filament levels are determined and rendered.Plate Template and Visual Consistency
17-plate-template.md, defining the canonical silhouette and fill strategy for U1 build plate thumbnails. The document covers precise measurements, drawing conventions, photo usage for official plates, and SVG requirements for generics, ensuring visual consistency and correct identification across the UI.…d (The U1 printer panel: three cards, an ACE mode row and a toolhead grid #38)The Printer section is one widget seen in both Prepare and Preview. On a U1 it drew four tabs named Nozzle 1..4 that each held the same
Diametercombo — changing any one writes all four and switches the whole preset, because the machine refuses a mixed set — and it hid its sync behind an unlabelled 16px glyph in the title bar. Measured by running the app, not by reading it.It now follows
docs/ace-mmu/15-printer-panel.mdand its mockup.What is here
docs(ace-mmu)BedTypeenumThe two design commits underneath are the docs branch this stacks on, so the design and the code arrive together.
The panel — three cards at equal height (printer with its thumbnail over the preset combo, plate, and Sync info); an ACE mode row mirroring the printer's own three-way switch; a bordered box per toolhead wrapped 2×2 with an
ACErow and aDiameterrow. The plate card absorbs what was a full-widthBed typerow, which is where the height for the head boxes comes from, and the whole card opens the list so the picture is the click target.Heads are named Toolhead N, which is what the machine's own interface calls them; the sidebar was the last place saying Nozzle N. Every other printer keeps the names it had — an IDEX machine's Left / Right Nozzle tabs are untouched.
update_nozzle_settingsbuilds those boxes instead of notebook pages, which leavesCustomNotebookwith no callers; it goes, 269 lines and the stray#pragma oncethat warned on every build. The 135-line sync handler is lifted verbatim intoSidebar::sync_printer_info.Config —
ace_head_unitandace_head_capacityare taken verbatim fromfeat/ace-mmu-slicingso that branch merges onto this rather than colliding.ace_modeis new: that branch reads the mode from the printer and never stores it, and a panel has to draw something with the printer switched off.Description
Screenshots/Recordings/Graphs
Tests