← Design · c130

research/gpui-kit.md

GPUI Kit

Use this file to choose the kit layer for each piece of the app, read the theme model before c132 builds the token bridge, see how a kit app starts and how its headless tests look, and know what breaks between the weekly releases. Facts come from gpui-kit.com, the repo and crates.io; every source was read on 2026-11-09. Companion: gpui.md for the engine, platform.md for what the kit leaves to us.

Identity

The layers

The site says three Kit layers; its own docs page lists five, counting the engine and the facade. — https://gpui-kit.com/docs, https://gpui-kit.com/base

CrateWhat it isUse it when
gpui (via gpui-pre)The engine (gpui.md)Always, through the facade
gpui-kitThe facade: re-exports GPUI and "brings together Base, Component, and default assets through one dependency". Paths gpui_kit::*, ::base, ::component, ::assets, ::platform.Always; it is the one dependency an app declares
gpui-base"Unstyled behavior, controlled state, focus, overlays, virtual lists, dock infrastructure, and semantic design tokens." Button::new("save") "has no default height, padding, background, border, or radius"."You are building a design system and want to own every visual choice." Not V1 (D5)
gpui-componentThe styled library: 80 component pages, themes, data tables, dock, editor. Builds on gpui-base; "Applications can use either layer directly.""You want a complete set of styled, ready-to-use desktop components." Our layer (D2).
gpui-shellJavaScript extensions on QuickJS, "one granted capability at a time". "It is not an Electron or a Tauri. There is no WebView, no DOM, no HTML or CSS." At "Milestone M0… not a stable interface".Later, if notices or trainings should arrive as scripts without a new binary (c133, c137)

Rule from the architecture doc: gpui-base "must not import gpui-component themes, assets, or façade types"; the arrow runs application → component → base → GPUI. — https://raw.githubusercontent.com/longbridge/gpui-kit/main/docs/ARCHITECTURE.md

Published crates, all 0.9.1: gpui-kit, gpui-base, gpui-component, gpui-component-macros, gpui-kit-assets (Lucide icons), gpui-fps, gpui-shell, gpui-webview. Features on gpui-kit: default = ["component", "assets"], gpui-fast, test-support, inspector, profiler, speech, syntect, tree-sitter and one tree-sitter-<lang> per grammar. — https://raw.githubusercontent.com/longbridge/gpui-kit/main/crates/kit/Cargo.toml

Components our pages need

Need (page)Kit pieceNotes
Inventory lists, Events logDataTable: "Virtual scrolling, fixed and resizable columns, sorting, and cell selection across hundreds of thousands of rows"; a TableDelegate supplies columns, counts and cells; columns pin left or right; rows and columns virtualise; sorting the data is the delegate's jobThe old stateful Table became DataTable in 0.6.0; Table is now a declarative element. — https://gpui-kit.com/component/data-table
Long listsVirtualList: "Render only the visible range, including differently sized items."
Status cards, Config panesSidebar, Tabs, GroupBox, DescriptionList, Form, Input, Select, Switch, Badge, Tag, Alert, Empty, Skeleton, ProgressEmpty and Skeleton cover R5's honest states
Status resourcesCharts: Line, Bar, Area, Pie, Radar, Candlestick, Sankey; appear animation on by default since 0.7.1 (.appear(false))— https://gpui-kit.com/component/chart
Events detailTextView (selectable Markdown and HTML, no CSS layout), Tree (for a JSON tree), Editor (rope, Tree-sitter or syntect)The Editor is heavy; a read-only TextView may do
Dialogs, toastsRoot "always mounts the dialog, sheet and notification layers above application content"; window.open_dialog, window.open_sheet, window.push_notification; Notification::info(...).system() routes to the OS— https://gpui-kit.com/component/root, https://gpui-kit.com/docs/system-notification
Window chromeTitleBar with a menu bar; TitleBar::window_options() as the base WindowOptions; native traffic lights on macOS, drawn controls on Linux— https://gpui-kit.com/component/title-bar
Workspace layoutsDock: tab groups, nested splits, collapsible docks; DockLayout "is a value… serialize it, compare it"; "the layout foundation used by Longbridge in production"Probably not V1
qmap embed (D8)gpui-webview, experimental, X11 only on LinuxCosted in comparison.md

Chat surfaces (Message, Bubble, Attachment, Speech) and a Command palette exist too; the full list is 80 component pages and 40 primitive pages. — https://gpui-kit.com/llms-full.txt

Theming

This is the part a designer needs, and the ground for c132's token bridge.

  • Access. cx.theme() through the ActiveTheme trait: cx.theme().primary, .background, .foreground. The coding guide forbids raw colours in app code: "read a semantic color from cx.theme() or add the missing role to the product theme". — https://gpui-kit.com/component/theme, https://gpui-kit.com/docs/coding-guides
  • Two vocabularies. The component ThemeColor has 134 roles: background, foreground, border, input, ring, selection, muted(+foreground), accent(+foreground), primary(+hover/active/foreground), secondary(+…), danger(+…), success(+…), warning(+…), info(+…), link(+hover/active), popover, sidebar(+foreground/border/accent/primary), title_bar, status_bar, scrollbar, list(+active/even/head/hover), table(+active/even/head/foot/hover/row_border), tab(+active/bar/foreground), chart_bullish, chart_bearish, chart_grid, button(+primary/secondary/danger/info/success/warning, each with hover/active/foreground), plus a base palette red, green, blue, yellow, magenta, cyan with _light variants. The word is danger, not destructive. Beneath it, gpui-base carries an 18-colour shadcn-shaped ColorTokens set (background, foreground, surface, primary, secondary, muted, accent, destructive, border, input, ring, selection, each with a foreground) and scales for radius {none sm md lg xl full}, spacing {xxs…xxl}, typography {sans, mono, xs…xl}, shadow {sm md lg}. "Prefer Theme::semantic_tokens() for new application-owned presentation". — https://raw.githubusercontent.com/longbridge/gpui-kit/main/crates/component/src/theme/theme_color.rs, https://raw.githubusercontent.com/longbridge/gpui-kit/main/crates/base/src/theme_tokens.rs
  • A theme is a JSON file, validated by .theme-schema.json: a ThemeSet (name, author, url, themes[]), each ThemeConfig with name, mode, is_default, font.size, font.family, mono_font.family, radius, radius.lg, window.radius, shadow, colors, highlight. Colour keys are dotted roles ("primary.background", "primary.hover.background", "sidebar.accent.foreground", "table.row.border", "chart.1"); values take hex, Tailwind palette names ("neutral-200/60") and gradients on tokens that opt in. The default theme is shadcn's neutral palette with 99 keys. — https://raw.githubusercontent.com/longbridge/gpui-kit/main/.theme-schema.json, https://raw.githubusercontent.com/longbridge/gpui-kit/main/crates/component/src/theme/default-theme.json
  • Built in: 21 theme files (ayu, catppuccin, gruvbox, solarized, tokyonight, macos-classic…), "38 (36 variants + Light/Dark)" presets. — https://api.github.com/repos/longbridge/gpui-kit/contents/themes
  • Loading and switching: ThemeRegistry::watch_dir("./themes", cx, …), then Theme::update(cx, |t| t.apply_config(&config)); Theme::change(ThemeMode::Dark, None, cx) "refreshes every window"; Theme::sync_system_appearance(window, cx) follows the OS when the app wires observe_window_appearance to it. gpui_component::init starts in Light. Since 0.9.0 the theme structs are #[non_exhaustive]: start from Default::default(). — https://gpui-kit.com/component/theme, https://gpui-kit.com/releases
  • Zoom is the base font. Root calls window.set_rem_size(cx.theme().font_size), so theme.font_size = px(18.) scales everything built with rem helpers. Radius: a finite scale, inner radius ≈ outer minus the gap; density tiers with Medium as default. — https://gpui-kit.com/docs/design-guides
  • Fonts: theme font_family / mono_font_family; "Use the platform UI font for interface text and monospace only for code"; bundle a font with include_bytes! + add_fonts, then name it in the theme. Licensing in platform.md. — https://gpui-kit.com/docs/fonts

A first sketch of the bridge, for c132 to test (not a ruling):

Qpoint token (brand/design_tokens.md)Kit role
Grape 500 #895AE8, 600 hover, 700 activeprimary, primary.hover, primary.active; ring; link
Leaf 300 #58E392success (never danger or warning)
Grey 900 / 600 text, Grey 50/100 surfaces, Grey 200/300 bordersforeground / muted.foreground, background / sidebar / list.even, border / table.row.border
error #EF4444, warning #F59E0B, info #D7CAFEdanger, warning, info
Inter / Geist Monofont.family / mono_font.family, if shipped (c131 Q5)

The app skeleton

use gpui_kit::*;
fn main() {
    application().with_assets(assets::Assets).run(|cx| {
        init(cx);                                   // themes and the enabled layers; before any window
        open_window(WindowOptions::default(), cx,   // cx.open_window plus the Root wrapper
            |_, cx| cx.new(|_| HelloWorld)).expect("Failed to open window");
    });
}

"Root owns the window's overlay layers, including dialogs, sheets and notifications. Return your content view from the closure, not another Root." Quit and close actions are the app's to define. On Linux with a WebView, start with gpui_kit::platform::linux(WindowingModes::X11). — https://gpui-kit.com/docs/getting-started, https://gpui-kit.com/component/root

The repo's crates/story is the gallery (cargo run opens it; cargo run -p gpui-component-story -- NotificationStory opens one story), with 86 stories; examples/ holds 30 runnable apps (hello_world, dock, editor, markdown, webview, system_monitor…). Our stories crate (c131 R6) follows this shape. — https://api.github.com/repos/longbridge/gpui-kit/contents/crates/story/src/stories

Headless UI tests

"A UI integration test renders real components or an application view in a headless window, simulates clicks, keyboard input and scrolling, then verifies state, focus, layout and application callbacks." Enabled by the test-support feature; "no additional testing crate, GPUI fork or Cargo patch". — https://gpui-kit.com/docs/test

#[gpui_kit::test]
fn saves_a_profile_through_the_ui(cx: &mut TestAppContext) {
    // … open the window …
    window.render_frame(cx);
    window.click("name", cx);
    window.input("Ada", cx);
    window.click("save", cx);
    assert_eq!(window.find("status").label(), Some("Saved: Ada"));
    assert_eq!(profile.read(cx).submitted.as_deref(), Some("Ada"));
}
  • API: find(id), find_all, within(id), click, right_click, double_click, hover, scroll, drag, press("backspace"), input, render_frame; snapshots give role(), bounds(), visible(), focused(), disabled(), label() (the accessibility name, not visible text), value(), checked().
  • Limits: no general rendered-text assertion ("The pinned GPUI version does not expose an enumeration of anonymous rendered text"); no positive enabled assertion.
  • CI: the kit runs its suite on ubuntu-latest, macos-latest and windows-latest with no Xvfb or DISPLAY; only the opt-in pixel suite needs a Vulkan driver. — https://raw.githubusercontent.com/longbridge/gpui-kit/main/.github/workflows/ci.yml

For us: this is "the demo is the test" in Rust. One story per page state, asserted by role and label, is both the design site and the regression suite (c131 R6, c132 medium B).

AccessKit

  • Claim: "AccessKit roles, names, states, relationships, and actions are built into the interaction layer and covered by tests." Per component: Button and Link (role, name, activation), Checkbox / Switch / Toggle / Radio (checked state), Input (role by content type, value, SetValue; masked values withheld), Select (ComboBox), Tabs, Slider and Progress (numeric range), Table ("row, header and cell roles with indices"), DataTable grid role since 0.9.0. Icon-only buttons need .accessibility_label("…"). — https://gpui-kit.com/docs/accessibility
  • Limits the kit states: "A passing headless tree assertion cannot prove what a screen reader announces or whether a focus ring is visible"; "Check assistive technology separately on each target platform." It rates itself Partial. No screen-reader run is reported anywhere; four GPUI accessibility bugs were filed on 2026-11-08 and 09 by a library user. — https://gpui-kit.com/docs/comparison, https://github.com/zed-industries/zed/issues/66691

wasm builds

  • "WebAssembly support is used primarily to showcase and try components in a browser… shipping an application in a browser is not a supported path yet." Label: Showcase only. — https://gpui-kit.com/docs/webassembly
  • Toolchain: wasm32-unknown-unknown + wasm-bindgen; the gallery pins nightly (CI: nightly-2026-10-31). gpui_web builds on stable only with its multithreaded feature off; whether a kit app does is unverified. Backend: WebGPU, then WebGL2. One canvas, one window; dialogs render inside it; fonts must be embedded (no font database on the web); accessibility is painted into a canvas.
  • The gallery at https://gpui-kit.com/gallery/ takes ?story= and a theme JSON; every Base primitive page embeds a live wasm example "backed by the same example crate that can run natively". Build: cd crates/story-web && make dev.
  • For c132 medium C: embedding a mock screen in a lab page is exactly the kit's own showcase use, and the theme JSON can be passed in. Fidelity of text input and tables in wasm, and the nightly toolchain, are the costs to measure.

Version churn

The kit does not use crates.io gpui or a Zed git rev. It pins Longbridge's weekly gpui-pre snapshot exactly (=0.3.12 today; "a caret requirement let the weekly release move applications onto a newer snapshot that gpui-component did not compile with"), and CI fails on a non-exact pin. Never pin a gpui-pre-* crate yourself. gpui-fast is an optional feature that "switches GPUI, native platforms, the WebAssembly backend, and every enabled Kit layer together". — https://raw.githubusercontent.com/longbridge/gpui-kit/main/Cargo.toml, https://gpui-kit.com/docs/installation

ReleaseDateWhat broke
0.6.02026-09-03The rename; gpui-kit facade, gpui-base, gpui-shell; Table → DataTable; dock split into base and styled halves; Dialog::new(cx); many fields made private
0.6.109-09Headless UI tests arrive
0.7.009-28Root moved to Base; gpui_kit::open_window replaces manual Root; window.open_dialog; Plot renames; versioned docs begin
0.7.310-12Optional GPUI Fast backend; gpui-pre =0.3.9
0.8.010-19gpui-wry → gpui-webview; Linux WebView must start on X11; scroll behaviour changes; TestWindowExt gains required methods
0.8.110-26Upstream breaks passed through: KeyBinding::new needs static strings, Modifiers::secondary() needs &App, PlatformInput::Scroll
0.9.011-02Theme structs #[non_exhaustive]; Window::text_system() returns &Rc<…>; animations read the executor clock
0.9.111-09None; gpui-pre =0.3.12, gpui-fast 0.1.14, a rustls-webpki CVE fix

Every release note has a "Breaking changes" section with migration snippets; docs are versioned (/versions/v0.9.0, /versions/main) and pages carry maturity labels (Stable, Preview, Experimental, Showcase only). — https://gpui-kit.com/releases

For c131: pin gpui-kit = "=0.9.1", move on a schedule, version-stamp PATTERNS.md with the kit release, and expect a migration hour per minor.

Platform notes and gaps

  • Requirements: macOS 15 or later plus Xcode Command Line Tools; Windows 10 or later, MSVC toolchain, CMake; Linux verified on Ubuntu 24.04 with Vulkan drivers, Wayland or X11, fontconfig, xkbcommon, libwebkit2gtk-4.1; Rust 1.92 or later. — https://gpui-kit.com/docs/installation
  • Not in the kit: a tray or status item (no occurrence of "tray" in the docs; none in GPUI either); auto-update ("Your application and its distribution system own the update policy"; a guide shows self_update); installers ("cargo build --release produces an executable, not an installer"). Notifications exist through GPUI but on macOS need a real .app bundle and on Linux a D-Bus notification daemon. All of these are platform.md's subject. — https://gpui-kit.com/docs/auto-update, https://gpui-kit.com/docs/packaging, https://gpui-kit.com/docs/system-notification
  • Hello World release binary: "~12 MB" by the kit's own estimate. — https://gpui-kit.com/docs/comparison
  • Linux: client-side decorations, a GPUI-drawn menu fallback, WebView on X11 only.

Open questions

  1. Does any kit app follow the OS light/dark appearance by default, or must every app wire observe_window_appearance? The code suggests the latter.
  2. Does a kit app build for wasm on stable with single_threaded_web()?
  3. What does Longbridge Pro ship with: upstream GPUI or GPUI Fast?
  4. Is the "~12 MB" hello binary right for 0.9.1 on each OS? The spike measures.

Sources

https://gpui-kit.com · https://gpui-kit.com/docs (getting-started, installation, test, accessibility, webassembly, coding-guides, design-guides, fonts, comparison, webview, auto-update, packaging, system-notification, native-extension) · https://gpui-kit.com/component (root, theme, title-bar, data-table, dock, chart, editor) · https://gpui-kit.com/base · https://gpui-kit.com/shell · https://gpui-kit.com/releases · https://gpui-kit.com/apps · https://gpui-kit.com/gallery/ · https://gpui-kit.com/llms-full.txt · https://github.com/longbridge/gpui-kit (Cargo.toml, crates/kit/Cargo.toml, docs/ARCHITECTURE.md, .theme-schema.json, crates/component/src/theme/*, crates/base/src/theme_tokens.rs, .github/workflows/ci.yml, themes/) · https://api.github.com/repos/longbridge/gpui-kit/releases · https://crates.io/api/v1/crates/gpui-kit · https://crates.io/api/v1/crates/gpui-pre · https://zed.dev/blog/community-champion-jason-lee · https://github.com/zed-industries/zed/issues/66691

Qpoint Brand Style Guide