# Retiring the egui shell

Branch `retire-egui`, cut from `30a3661`.

The Dioxus web shell has been the real front end since S1 (#55). The egui
shell has been kept at rough parity behind it, and every nit now costs twice:
two shells to fix, two sets of goldens to bless, two parity items on every
todo entry. This branch ends that — the web shell becomes the only front end.

`cargo xtask ci` green after every phase, as `exit-egui` was run.

## What this is not

Not a port and not a rewrite. `exit-egui` and `shell-on-kernel` already did
the hard part: the app is a `kernel(&mut Session, events, &impl TextLayout)
-> View` and two consumers of `View`. Deleting one consumer takes nothing
with it that the other needs.

## The finding

Deleting `src/` and `crates/egui/` is the small half. The weight is in three
places the grep does not show:

### 1. egui is a dev-dependency of editor, tools and export

Not of their libraries — of their **tests**. `blockworx-egui` is pulled in
with `features = ["test-support"]` by `crates/editor`, `crates/tools` and
`crates/export`, because the suites measure real text: *"a fake font would
change which tests pass"* (`xtask`'s own words, in the headless gate).

This is the piece that could have made the branch large, and does not, because
of how the harness is built:

```rust
// crates/egui/src/measure.rs
pub type Headless = Recording<ContextLayout>;
```

`Recording<L: TextLayout>` lives in **`blockworx-paint`** and implements
`Renderer`. Only the type parameter is egui's. `blockworx_text::Shaper` is
already a `TextLayout` — it is what the kernel's own tests are called with,
and what the web shell shapes through. So the harness swap is
`Recording<ContextLayout>` → `Recording<Shaper>`: a constructor, not a
rewrite.

Where the replacement lives: `blockworx-text` (it depends on `paint`, and
`paint` cannot depend on it), behind `test-support`, as
`blockworx-doc` does with `fixtures`.

What the egui constructor takes from its `Ui` and where each comes from
instead: the palette and typeface are already arguments; `ContextLayout::new`
becomes `Shaper::new`; `ui.clip_rect()` becomes a stated viewport; the tick
and pointer kind become stated values, as `Scripted` already states the
pointer.

### 2. `src/` is the CLI as well as the shell

`blockworx log`, `blockworx verify` and `blockworx migrate` read containers
and never touch a toolkit. They are how a container is inspected from outside
the app, and the log dump deliberately shares its reader with the in-editor
panel *"so the console and the panel cannot disagree about what a record
says."*

They stay. The binary keeps its name and loses its GUI: `blockworx` becomes
a CLI over the store, and opening a path stops meaning "open a window".

### 3. The gates are written around there being two backends

Four of `cargo xtask ci`'s steps name egui, and two exist only because it is
there: **headless** proves the ten core crates carry no egui-family crate,
and **backend** proves each backend knows nothing above `paint`. With one
backend left they do not disappear — they get narrower and keep their point.

## What goes

| | |
|---|---|
| `src/` — 53 files, ~21.7k lines | minus the CLI, which stays |
| `crates/egui/` — ~2.5k lines | the backend |
| `eframe`, `egui`, `egui_extras`, `egui_kittest`, `rfd` | and the `kittest` and `ui_debug` features |
| `tests/snapshots/` — 17 PNGs | the egui shell's pictures |
| The desktop file dialogs and the recent list | shell-only; the web shell answers each its own way |
| The theme and font editors, `render_bench`, `tessellation_snapshots` | dropped by decision, below |
| `--trace` and its `SpanTiming` switch | every span it could close was on the frame path |

Two todo items are **deleted rather than scheduled**: desktop parity for
drop-import, and desktop parity for the tag field's suggestions. So is the
`wasm32` cfg sweep in `src/` — 158 `target_arch = "wasm32"` occurrences left
from when the egui shell was also the web build.

## What this costs, stated plainly

**There is no desktop app after this.** The web shell runs in a browser, and
a container lives in origin storage, not on the filesystem — `Native` storage
keeps only the CLI as a consumer. A desktop blockworx later means wrapping
the web shell (Dioxus desktop, Tauri) rather than reviving this one.

That is the decision this branch makes, and it should be made deliberately
rather than discovered at the end.

## Phases

- [x] **P0** Branch and this playbook.
- [x] **P1** The test harness moves off egui. `blockworx-text` grows a
      `test-support` headless `Recording<Shaper>`; the twenty uses in
      `editor`, `tools` and `export` move to it; the three dev-dependencies
      on `blockworx-egui` go. **This phase carries the risk**: text metrics
      decide which hit tests pass, so a suite that goes green on a different
      shaper has to be read, not just re-run. Do it first, and alone, while
      the egui shell still builds and can be diffed against.
- [x] **P2** The CLI leaves the shell. `blockworx log|verify|migrate`, the
      path argument and `--trace`'s subscriber are `src/cli.rs`, which names
      no toolkit; the GUI entry point is what is left of `main.rs`.
- [x] **P3** Delete `src/`'s shell and its entry point, `crates/egui/`,
      `tests/snapshots/`, the egui dependencies and the `kittest` and
      `ui_debug` features. The workspace loses a member; the root package
      stops being a shell.
- [x] **P4** The gates narrow: **headless** and **backend** to one backend,
      **shell** to the browser alone. `docs/` follows — CLAUDE.md's crate
      table and frame note, and the playbooks that describe two front ends.
      **And one tightens.** `headless` walks `editor`, `tools` and `export`
      as `(normal,build)` while every other crate gets `(normal,build,dev)`
      — dev was excluded for exactly the three crates that had the egui
      dev-dependency P1 removed. All ten can now be walked with `dev`, which
      is a stronger claim than the gate has ever made.
      *Done 2026-09-18: walking all ten over `normal,build,dev` caught
      nothing — the three suites' only egui edge was the one P1 removed, and
      no core crate carries a host on any edge. `headless` now forbids
      `dioxus` beside the egui family, `tokio`, `wgpu` and `winit`; `shell`
      is down to the three crates that may reach the browser (the root
      package no longer among them); `palette` lost its two dead file
      exemptions and the egui color spellings.*
- [x] **P5** `cargo xtask web walk` in Chrome and Firefox, and a read of what
      the desktop did that the web does not: the font and theme editors, the
      render bench, `--trace`.

## Order, and why

P1 first because it is the only phase that can be wrong quietly. Everything
after it is deletion, which fails loudly or not at all.

P1 also has to happen whatever else does: those three suites cannot keep a
dev-dependency on a crate that is being deleted. So if this branch stalls,
P1 is still worth having landed.

## Decisions (user, 2026-09-18)

- **`--trace` goes** (reversed 2026-09-18, after P2 found what it measures).
  Every instrumented span in the tree is on the frame path — the app's
  `frame`, the kernel's `tool_widget`, the editor's render passes, the router
  — and `log`/`verify`/`migrate` open none of them. Over the CLI the flag was
  only a filter switch, and after P3 it would have had nothing left to time.
  `RUST_LOG` already does the filtering. Timing the web is tracing-web spans
  in a CPU profile, which is what `TUNING.md`'s later findings used; TUNING's
  `--trace` recipes describe a build that no longer exists and are marked as
  history at P4.
- **The font and theme editors go.** Dev tooling with no web equivalent, and
  the project's own practice is *"for author/dev tooling, prefer the console
  and an edit-and-re-run loop over in-app UI"* — so a theme is a file and a
  re-run, which is what `theme.json` and `font_sizes.json` already are.
- **`render_bench` and `tessellation_snapshots` go.** Both measure egui's own
  tessellation, which stops existing. Whether the web wants an equivalent is
  a separate question and not this branch's.

## State at the end of the branch

The web shell is the only front end. `cargo xtask ci` runs in ~22 s rather
than ~450: the egui doc build and the GPU snapshot harness were most of it.
`Cargo.lock` holds 587 packages, down from 844.

`blockworx` is a command line over a container — `log`, `verify`, `migrate`
— depending on `clap`, `tracing-subscriber` and `blockworx-store`, and
nothing else. `fixtures/demo.bwx{,.zip}` is a container in the current format
to run it against, in both the directory and archive forms.

P5: `cargo xtask web walk` — Chrome 63 checks over four walks, Firefox 9,
none failed.

What the desktop did that the web does not, for the record: the theme and
font editors (a theme is `theme.json` and a re-run), `render_bench` and the
tessellation snapshots (they measured egui's tessellator), and `--trace`
(every span it could close was on the frame path; timing is now a browser CPU
profile). Each was a decision, not an omission.
