# 0024: Lumbridge builds its own UI toolkit, and GPUI becomes the interim backend Status: accepted. Supersedes the framework selection in decisions 0017 and 0023. Those records' accounts of *why* the published GPUI crate is unusable, and the pinned revision Lumbridge runs on today, both stand — this record changes where the project is going, not where it currently is. ## The decision Lumbridge will build and open-source `lumbridge-toolkit`, a native Rust UI toolkit, and Lumbridge will ship on it. GPUI remains the backend until the toolkit reaches parity, and is then removed. This is the same shape as Zed's own arrangement — an application and the toolkit it needed, developed together — and it is chosen for the same reason: no existing toolkit is built for what this product is. It is not a general-purpose GUI framework, and competing with GPUI on breadth is not the goal. ## Why now, and what changed The case against building a toolkit was that it means writing a GPU renderer, a text shaping and layout engine, and an accessibility layer from scratch, and that owning menus, IME, clipboard, drag-and-drop, multi-window and packaging on three platforms is where such projects die. Most of that is still true. What has changed is that the hardest primitives are now solved by permissively licensed crates that did not exist, or were not ready, when GPUI was started: - `vello` for GPU 2D rendering, `wgpu` underneath; - `parley` with `swash`/`skrifa` for shaping, layout and font fallback; - `kurbo` and `peniko` for geometry, paths and brushes; - `accesskit` for the platform accessibility tree; - `winit` for windowing, input and IME; - `taffy` for flex layout, which GPUI itself uses. Measured at revision `ce48461e`, the GPUI family is 129,110 lines across nine crates — `gpui` alone is 75,995, with 15,391 for the Linux backend, 12,282 for Windows and 10,503 for macOS — carrying 108 non-Zed dependencies. The large majority of that is the primitives above. What remains, and what this project actually has to write, is the element model, layout composition, the paint pipeline, input routing, and the terminal grid. ## What the toolkit is for, and what it is not Two claims are worth making because nothing else in the Rust ecosystem makes them, and they are the reasons this is worth building rather than adopting: **Accessibility is structural.** `lt-a11y` is a crate the element model is built on, not an adapter added later. Both candidates Lumbridge evaluated failed its accessibility hard gate — Floem has no AccessKit integration at its pinned revision, and published GPUI has no AccessKit dependency at all — and decision 0017 knowingly shipped with that gate unmet. A toolkit that cannot be retrofitted into that state is the point. **The terminal grid is a first-class widget**, not an embedding problem. Every Rust application that puts a terminal on screen rebuilds cell measurement, styled run coalescing and viewport arithmetic, and `docs/ARCHITECTURE.md` records two ways this project already got it wrong: a cell advance hardcoded 13% wider than the face actually painted, and `ws_xpixel`/`ws_ypixel` reported as zero, so every program doing pixel arithmetic was told the window had no size. It is explicitly **not** a general-purpose application framework, not a web target, and not a competitor for editor authors. Its first client is Lumbridge, and features arrive because Lumbridge needs them. ## Repository and licence A separate repository, consumed by Lumbridge as a revision-pinned Git dependency through the same guard that pins GPUI today. Separate, rather than a directory in this tree, because this repository has twice shipped code that no build ever looked at: the GPUI shell in `spikes/`, which `Cargo.toml` excluded so that every test written into it silently never ran (decision 0017), and `crates/lumbridge-devices`, which was in neither `members` nor `exclude` and did not compile at all. A toolkit developed in an unlinted corner of an application's tree is the third instance waiting to happen. It also needs its own CI, its own issue tracker, and a release cadence that is not Lumbridge's. Licensed **MIT OR Apache-2.0**, the Rust ecosystem's dual-licence norm. This is not only about adoption: an Apache-2.0 crate that unconditionally pulls GPL-3.0-or-later is precisely the trap that ended Lumbridge's use of upstream GPUI as a long-term dependency, and a permissive dual licence with a `cargo deny` source and licence gate from the first commit is how this project avoids setting the same trap for somebody else. ## Shape ```text lt-core ids, geometry, colour, units (kurbo, peniko) lt-text shaping, font fallback, cell metrics (parley, swash) lt-layout flex composition and measurement (taffy) lt-render scene building, paint runs, damage (vello) lt-input key encoding, IME, mouse, focus (winit) lt-a11y the AccessKit tree lt-platform window, menus, clipboard, drag-and-drop lt-element the builder API applications write against lt-terminal the styled grid ``` ## M0 is a gate, not a formality Before any port begins, one spike answers the three questions that would end this. Its thresholds are written here, in advance, so the result cannot be rationalised afterwards. The baseline is the current GPUI shell on the same machine and the same fixture. 1. **Density.** Five panels of 200x60 styled cells consuming live PTY output. Frame p95 no worse than the GPUI baseline, and key-to-present no worse. 2. **Input.** Composed accents and multi-codepoint graphemes survive an IME round trip without splitting UTF-8 — the same assertions `spikes/gpui-accessibility-probe` already makes. 3. **Accessibility.** An AccessKit tree naming each pane, its selected state, its execution target and its waiting state, **driven by Orca on Linux and VoiceOver on macOS**. A passing unit test over a `TreeUpdate` is not an assistive-technology claim and does not satisfy this. Failing M0 means Lumbridge stays on pinned GPUI and this record is superseded. Three weeks is the budget. ## Migration, and what is not allowed to happen Lumbridge ships on GPUI throughout. The port is a strangler, and the seam already exists: no crate outside `apps/lumbridge` imports a GPUI type, `lumbridge-terminal` already produces framework-neutral immutable snapshots and coalesced paint runs, `lumbridge-theme` derives colours with no framework types, and the `a11y` trait already abstracts the semantics away from the framework that renders them. - **M1** — the `lt-*` crates, with `lt-terminal` rendering the grid. - **M2** — `lt-element`, and the framework-neutral `lumbridge-ui` boundary that `ARCHITECTURE.md` has named since the beginning. One panel ports. GPUI renders the rest. - **M3** — the shell builds on either backend behind a Cargo feature, both in CI, with the deterministic fixture replaying identically through each. That identical replay is the parity evidence; a screenshot is not. - **M4** — GPUI is removed, and `lumbridge-toolkit` 0.1 is published. The failure mode to guard against is a year spent with neither a finished toolkit nor a finished product. Two rules follow. Phase 1 of `ROADMAP.md` — "Lumbridge is worth using as a terminal multiplexer without AI features" — is not deferred for toolkit work. And no GPUI capability is removed before its `lt-` replacement passes the same gate, so `main` is never the worse option. ## Known risks `vello` is pre-1.0, and this trades one pre-1.0 dependency for several. macOS notarization and VoiceOver are where toolkit projects die, and there is currently no Mac in the development loop at all — every macOS row of `UI_SPIKE_SCORECARD.md` is still `pending`. Text is a tarpit; the mitigation is that a terminal grid needs a fraction of what a word processor does, and that scoping must be defended rather than allowed to erode.