Render A2UI in gpui
Install
Section titled “Install”Two crates: eui-core (the core) and eui-gpui (the painter and host runtime). The painter depends on gpui and gpui-component as git dependencies, which crates.io refuses, so an app takes both crates from the release tag. Your app links the same gpui as the painter: the same gpui-component rev, zed locked at the SDK’s rev and the ztracing patch, all copied from one tag (the gpui revision policy). gpui needs Rust 1.96: pin it in rust-toolchain.toml.
[dependencies]eui-core = { git = "https://github.com/Niach/exponential", tag = "eui-v0.12.0" }eui-gpui = { git = "https://github.com/Niach/exponential", tag = "eui-v0.12.0" }gpui = { version = "0.2.2", git = "https://github.com/zed-industries/zed" }gpui-component = { git = "https://github.com/longbridge/gpui-component", rev = "da4f93696dc2b2b4d91bcc42412b9053a3d24de8" }
[patch."https://github.com/zed-industries/zed"]ztracing = { git = "https://github.com/Niach/exponential", tag = "eui-v0.12.0" }ztracing_macro = { git = "https://github.com/Niach/exponential", tag = "eui-v0.12.0" }cargo update gpui --precise cc053a4a6fa2fd0e8793201ed9099466af1be0b1The guide’s manifest uses path dependencies in place of the git ones:
[package]name = "exponential-ui-guide-gpui"version = "0.1.0"edition = "2021"license = "Apache-2.0"publish = false
# A third-party app: its own workspace, outside apps/desktop's build graph.[workspace]
[dependencies]# The SDK. The painter is not on crates.io (gpui + gpui-component are git# dependencies) and wants the core at its own revision, so an app takes both# from the release tag on github.com/Niach/exponential (`tag = "eui-v0.12.0"`).# Path dependencies stand in for it here. The gpui pins below follow the# painter README's "gpui revisions" (gated by check-gpui-revs.ts).eui-core = { path = "../../../../apps/desktop/crates/eui-core" }eui-gpui = { path = "../../../../apps/desktop/crates/eui-gpui" }# gpui + gpui-component at the SDK's revisions (Cargo.lock pins zed's commit).gpui = { version = "0.2.2", git = "https://github.com/zed-industries/zed" }gpui_platform = { git = "https://github.com/zed-industries/zed", features = ["font-kit", "x11", "wayland", "runtime_shaders"] }gpui-component = { git = "https://github.com/longbridge/gpui-component", rev = "da4f93696dc2b2b4d91bcc42412b9053a3d24de8" }gpui-component-assets = { git = "https://github.com/longbridge/gpui-component", rev = "da4f93696dc2b2b4d91bcc42412b9053a3d24de8" }
# zed's ztracing is GPL-3.0-or-later; any app linking gpui at this revision# swaps in the SDK's no-op shims.[patch."https://github.com/zed-industries/zed"]ztracing = { path = "../../../../apps/desktop/crates/ztracing" }ztracing_macro = { path = "../../../../apps/desktop/crates/ztracing_macro" }
[profile.dev.package."*"]debug = falsePaint a surface
Section titled “Paint a surface”The host is a gpui entity. host.surface(id) returns the surface’s view once the agent has created it; it is full width and as tall as its content, so wrap it in your own scroller.
use eui_core::theme::Mode;use eui_gpui::runtime::{ExponentialHost, HostOptions};use eui_gpui::transport::{HttpTransportOptions, JsonlStreamTransport};use gpui::{div, prelude::*, px, size, App, Bounds, Context, Entity, Window, WindowBounds, WindowOptions};
struct Main { host: Entity<ExponentialHost>,}
impl Render for Main { fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { // The surface the agent creates, once it exists; you own the scroller. div().id("main").size_full().overflow_y_scroll().p(px(16.0)).children(self.host.read(cx).surface("main")) }}
fn main() { gpui_platform::application().with_assets(gpui_component_assets::Assets).run(|cx: &mut App| { gpui_component::init(cx); // One host per app: A2UI messages in over a transport, actions back out. let transport = JsonlStreamTransport::new(HttpTransportOptions::new("http://localhost:4300/a2ui.jsonl").post_url("http://localhost:4300/action")); let options = HostOptions { transport: Some(Box::new(transport)), theme: eui_core::themes::builtin_theme("exponential"), mode: Mode::Dark, ..Default::default() }; let host = cx.new(|cx| ExponentialHost::new(options, cx)); host.update(cx, |host, cx| host.connect(cx)); let window = WindowOptions { window_bounds: Some(WindowBounds::Windowed(Bounds::centered(None, size(px(540.0), px(640.0)), cx))), ..Default::default() }; cx.open_window(window, |_, cx| cx.new(|cx| { cx.observe(&host, |_, _, cx| cx.notify()).detach(); Main { host: host.clone() } })) .expect("a window"); });}Host plugin
Section titled “Host plugin”Underneath, a SurfaceView takes a HostPlugin implementation: catalog icon names to SVG asset paths, font families, actions and input. Register the theme’s fonts with the text system before the first frame, and extension natives with register_painter. Details in the gpui painter’s README.
Compiled in CI
Section titled “Compiled in CI”bun packages/exponential-ui/guides/check.ts gpui builds guides/gpui in an empty directory against the path crates.