The app tour¶
guide/app.mdwas a "work in progress" stub listing what the page would eventually cover. It now covers all of it, panel by panel, in the order a first-time user meets them. Every screenshot is a real frame fromhenad-app, captured through the egui MCP server at 2x and cropped with ImageMagick using widget bounds read back from the accessibility tree. The.uichip that has been sitting unused inhenad.csssince session 15 is now the page's main device: every button, tab and menu entry appears inline as the widget itself, icon included. Two states could not be photographed on this machine and are described in prose instead.
State before¶
docs/guide/app.md was 17 lines: frontmatter, an !!! failure "Work in progress" block naming the seven things a tour should cover, and two links out.
docs/stylesheets/henad.css carried a .md-typeset .ui rule with :hover and :active states and a .twemoji size override, written for inline UI chips. Nothing in docs/ used it.
docs/assets/ held favicon.ico, logo.png, logo.svg and the two IBM Plex woff2 files. No screenshots anywhere in the site.
What was done¶
Capturing the app¶
The browser build was tried first, since it is the least trouble to reach. WebGPU initialises fine in the in-app browser, and the canvas reports its real size, but every CDP screenshot came back as flat page background. A GPU-composited canvas is not in the capture path. Dead end, and worth remembering.
The native app through the egui MCP server worked on the first try, and better than the browser would have:
screenshottakespixels_per_pointandsave_path, so a 1440x900 window writes a 2880x1800 PNG straight todocs/assets/app/.query_treereturns every widget's bounds in logical points. Screenshot pixel = 2 x logical point, so a panel crop is arithmetic rather than guesswork. Reading coordinates off the downscaled image the tool returns for viewing does not work, and the first four crops had to be redone because of it.dragmoves dock splitters, which is how the left column was widened for the parameter shots (two labels clip at the default width) and how the System panel was given the height to show all three of its sections.
press_key with Escape closed the app. Menus were dismissed by clicking elsewhere after that.
egui_dock's tab bar is still absent from the accessibility tree, so switching to the System tab needed a raw position click, as session 15's note and the memory both say.
The screenshots¶
Fifteen files under docs/assets/app/, 1.3 MB in total.
| File | Model | Note |
|---|---|---|
overview.png |
Ant Foraging (GPU) | Full window at ~30k TPS, the page's hero |
view-menu.png |
The View menu open | |
model-select.png |
The dropdown, all eight entries | |
params.png |
Ant Foraging (GPU) | Every label carrying the reload marker |
params-reload.png |
Ant Foraging (GPU) | Amber label and the "Reload needed" banner |
playback.png |
Running, so the pause icon | |
pacing-cpu.png, pacing-gpu.png |
Boids, gpu ants | The two halves of the swap |
viewport-toolbar.png |
Boids | Rendering checkbox and the mode selector |
viewport-sprites.png, viewport-density.png |
Ant Foraging | 800,000 ants, same run |
statistics.png, charts.png |
Boids | Chosen for the Vector2D stat and its arrow plot |
performance.png |
Ant Foraging (GPU) | |
system.png |
Host, Graphics and Device limits, plus the "uncertain" banner |
The sprites/density pair took three attempts. 50k boids bins to well under one agent per heatmap cell, and 280k boids at ~1 TPS never formed flocks inside a useful wait. 800k CPU ants at 225 TPS uncapped converge in about a minute and pack a trail hard enough that the two modes actually differ.
system.png shows "GPU performance uncertain" on an M4 Pro, which is the SoC arm of classify_adapter doing exactly what it should.
The page¶
Fourteen sections: the workspace, one per panel in dock order, then failure states, browser differences and a "Where next" card grid.
Zensical features used, all verified in the built output:
| Feature | Where |
|---|---|
<figure markdown="span"> with captions |
11 figures |
| Content tabs | CPU/GPU pacing, sprites/density |
| Definition lists | Playback buttons, Performance rows, the two modals |
| Admonitions | 5, including !!! danger on quoting FPS as a benchmark |
??? details |
The display-texture cap |
| Mermaid | The build/play/offload state machine |
Grid cards, .md-button |
Where next, and 3 inline calls to action |
| Abbreviations | TPS, FPS, CPU, GPU, SoC, UI |
Icons with title= |
The stat-kind table |
The .ui chip renders as <span class="ui"> with the icon inlined as SVG, so <span class="ui" markdown>:material-restart: Build</span> comes out looking like the button it names. The markdown attribute survives into the output as an unknown attribute and is ignored by the browser. Icons work inside the chip either way, since pymdownx.emoji is an inline processor.
One trap: md_in_html does not descend into content tabs, so a <figure markdown="span"> indented under === "..." renders as a literal <figure> inside a <p>. Plain images with attr_list are used inside tabs instead.
docs/
├── guide/app.md # 17 lines -> 361
└── assets/app/ # new, 15 PNGs, 1.3 MB
├── overview.png
├── view-menu.png
├── model-select.png
├── params.png, params-reload.png
├── playback.png
├── pacing-cpu.png, pacing-gpu.png
├── viewport-toolbar.png
├── viewport-sprites.png, viewport-density.png
├── statistics.png, charts.png
├── performance.png
└── system.png
State after¶
uv run zensical build reports no issues. The page was opened in a browser and read top to bottom at the widths the pane would render.
Every claim on the page was checked against the source rather than the running app: parameter ranges against params.rs and each model's declarations, the four banner states against params.rs::notice, the batching controls against gpu/timing.rs, the display cap against display_scale.rs, and the adapter verdicts against runtime_info.rs::classify_adapter.
No Rust changed. The only files touched are docs/guide/app.md and the new docs/assets/app/.
Issues found & future directions¶
Two states have no screenshot. "Too large for this device" needs a model over a device limit, and an M4 Pro grants 4 GB per storage binding, so even GPU SIR at 16384 by 16384 (1.07 GB per buffer, four of them) builds. The fault modal needs a real device error or a panicking kernel, and no shipped model provides either. Both are described in prose. A weaker machine or the browser build, where limits are the WebGPU baseline, would produce both.
AGENTS.md's ui/ file list is stale. It names toolbar.rs and sidebar.rs. The directory holds dock.rs, menu_bar.rs, model.rs, pacing.rs, params.rs, performance.rs, playback.rs, stats.rs, charts.rs, system.rs, fault.rs, viewport.rs and agent_layer.rs.
Screenshots pin the UI. Fifteen images now go stale the moment a panel is rearranged. Nothing checks them. The capture procedure is reproducible from the egui MCP server, and could be written down the way the consistency-fixture procedures are.
Density mode has a fixed scale and a fixed grid. density_max is hard-coded at 4.0 and the heatmap is always 512 by 512, so a small world aliases into a visible dotted pattern and a sparse population reads as uniform noise. Both are visible in the shipped screenshot. A scale derived from the population, or a heatmap sized to the world, would make the mode useful below a million agents.
The browser build cannot be screenshotted. Its canvas is invisible to CDP capture. Anything the web build shows differently has to be photographed by hand.
Manual notes (human)¶
See Record #20 for manual notes for records #15-#20.