Experimental. GPUI running inside a Wasm component, embedded back into a
native GPUI application. A plugin is an ordinary GPUI program — entities,
elements, flexbox layout, input handlers, async tasks — compiled to
wasm32-wasip2 and rendered by a host app that treats it as just another
element in its tree.
This is a spike exploring a possible UI-extension model for Zed. Expect sharp edges and breaking changes; nothing here is a supported API yet.
- A guest-side GPUI platform: windows, retained display lists,
mouse and keyboard input, timers/async, SVG and image rendering, text via
host-side shaping — all over a WIT protocol (
wit/plugin.wit). - A host runtime: loads a component with wasmtime, replays its display lists as native GPUI primitives (text hits the host's real rasterizer), and never calls into wasm from the frame path.
- Shared objects: an entity lives on one end (its "home"); the other end
holds a
Remotethat behaves as much like anEntity<T>as a sandbox wall allows — typed method calls with responses,observefor the home'scx.notify,subscribefor itscx.emitevents, refcounted auto-release on drop, and capability references you can embed in payloads (Ref). The object model is symmetric and stringless: each end installs one root object at the reserved address 0 (share_root), attaches to the other end's root withroot(), and discovers everything else through method calls — ref-returning methods resolve directly to connectedRemotes, and authority is reachability from your root. Object ids are random u64s, so a ref can be known but never guessed. - Typed interfaces: one attribute on a trait (
#[interface]) makes one name the whole interface —Remote<CounterApi>for callers,#[shared] impl CounterApi for MyEntityfor the implementation, both compile-time checked against the same schema. - OCAP utilities (
embedded_gpui_util):Revocable(caretaker/membrane),Attenuated(allowlist),Audited(call ledger), andMirror(a local, observable cache of remote state — snapshots as a library, not a protocol).
// Compile + instantiate on a background thread; the store never blocks your UI.
let plugin = PluginHost::load(path, PluginOptions::new(text_system), cx);
// The bootstrap: install your root, take theirs. Every capability either way
// is a method call from here on.
host.share_root(&my_root_entity, cx);
let plugin = host.root::<DemoPlugin>(cx);
let palette = plugin.palette(cx).await?; // a connected Remote<PaletteApi>
// Views by name (for now), placed like any other element. Creation is lazy (the
// guest sees the measured slot size), and layout changes become window resizes.
div()
.w(px(480.))
.h(px(320.))
.child(host.view("panel", cx))The WASI sandbox grants nothing but stdout/stderr by default; every additional
authority is an explicit PluginOptions::with_wasi choice — and everything the
plugin can do to your app is reachable from the one root object you install
with share_root, so tailoring (or faking) a plugin's world is constructing a
different root.
Requires the wasm32-wasip2 target (rustup target add wasm32-wasip2; the
pinned toolchain in rust-toolchain.toml installs it automatically).
cargo run -p example_host(the demo builds its wasm plugin automatically on first run)
The demo window shows two embedded plugin views (a counter button and a panel with text input, SVG, image, and an animated path) plus a native button — all three mutate the same shared counter entity.
cargo test -p tests -- --test-threads 1 # protocol testsembedded_gpui/— the one crate both sides use: schema layer (always), host runtime (native), guest platform (wasm32). The WIT protocol lives inembedded_gpui/wit/.embedded_gpui_macros/— the#[interface]/#[shared]/#[data]proc macros.embedded_gpui_util/— object-capability patterns (Revocable,Attenuated,Audited,Mirror).example/— the demo:host/(native window) andplugin/(the wasm component).tests/— protocol integration tests plus theirtest_plugin/fixture.
DESIGN.md— architecture and invariants.embedded_gpui/wit/plugin.wit— the wire protocol, heavily commented.example/plugin/— what plugin code looks like.
GPUI is consumed as a git dependency on the zed repository (the
gpui-embedded-in-gpui branch until the small upstream hook it needs merges).
Apache 2.0, like GPUI itself — see LICENSE-APACHE.