Files
Ely-GPUI-Components/frontend/src/docs/webassembly.md
T
ZacharyZhang-NY 49ed1f8405
CI / crate (push) Failing after 2s
CI / site (push) Successful in 30s
CI / platforms (ubuntu-24.04) (push) Failing after 1m48s
CI / platforms (macos-15-intel) (push) Canceled after 0s
CI / platforms (ubuntu-24.04-arm) (push) Canceled after 0s
CI / platforms (windows-11-arm) (push) Canceled after 0s
CI / platforms (windows-latest) (push) Failing after 15s
Site and gallery: the repo and the showcase catalog live on Gitea at git.zacharyzhang.com
Links, the header's Gitea mark, the catalog's archive download and the showcase pin
workflow (Gitea's API and commit status) move off GitHub; pinned at 6973834, which
restores TokenLedger.
2026-10-09 16:20:32 -04:00

3.1 KiB

WebAssembly

Ely builds for wasm32-unknown-unknown through gpui_web, Zed's browser platform for GPUI. The components page runs Ely's own gallery that way, in a canvas inside the page.

Check the build

rustup target add wasm32-unknown-unknown
RUSTC_BOOTSTRAP=1 cargo check --lib --target wasm32-unknown-unknown

gpui_web uses an unstable feature for its threads, as Zed's own web examples do, so the check needs RUSTC_BOOTSTRAP=1.

Start an app in the browser

The gallery's web entry shows the steps:

Step Why
#[wasm_bindgen(start)] on the entry The browser runs it when the module loads.
gpui_platform::web_init() Sends panics and logs to the browser console.
WebPlatform::new_with_backend_and_font_fallback(false, WebBackendPreference::Auto, CanvasFontFallback::EmojiAndCjk) One thread, since a static site gets no cross-origin isolation; WebGPU where the browser has it, WebGL2 where it does not; the browser lends fonts for emoji and CJK alone.
Application::with_platform(platform) Then with_http_client(Arc::new(platform.fetch_http_client())), which fetches pictures by address, and with_assets(Assets) and run, as on the desktop.

ELY_GALLERY_ASSETS=<address>/ scripts/web.sh <out> builds the gallery with the web profile, runs wasm-bindgen and wasm-opt -Oz, and copies its page beside it and its pictures into <out>/assets/. It needs the wasm-bindgen CLI at the version in Cargo.lock and binaryen's wasm-opt, and it fails when the wasm passes Cloudflare's 25 MiB. ELY_GALLERY_ASSETS is the address <out>/assets/ is served from, ending in /: GPUI reads no files in a browser.

The gallery opens at ?page=<slug>&story=<title or slug>&theme=light|dark. A story needs its page.

Embed it in a page

Message Direction
gallery.setTheme(mode), or postMessage({ ely: "theme", theme }) From the host, before the wasm runs or after.
{ ely: "ready" } To the parent, once the gallery runs.
{ ely: "failed", error } To the parent, after a start that failed three times.

Until a press or a key reaches it, the embedded gallery keeps GPUI's hidden text field from taking focus, so a starting frame leaves the host page's focus alone. Tab inside GPUI cycles within its window; a host gives a way past the frame, as the components page does.

What differs on the web

Area On the web
Threads One in the gallery: more need a page served with cross-origin isolation.
Pictures Fetched by address, never read from files.
Time web_time::Instant; std's panics in a browser.
Paste Arrives as the browser's paste event. Code that reads the clipboard itself awaits read_from_clipboard_async.
Accessibility GPUI builds no accessibility tree in the browser.
Native only The live terminal and the gallery's capture tool do not build for WebAssembly.
macOS only The tray icon, the Dock badge and the web view return an error or say so, as on Windows and Linux.