T44d Web · What a browser cannot do says why; fonts resolve; the bundle builds

Demos that open a second window, take files, move the window, read the
clipboard or set native menus show a note on the web in the blocked style.
IBM Plex Sans Regular joins the assets, unmodified, where gpui's svg
renderer loads it: gpui_web answers .SystemUIFont with it. The gallery's
web entry registers a Hebrew cut of Noto Sans Hebrew. scripts/web.sh builds
the bundle with wasm-bindgen and wasm-opt -Oz, 20.0 MB, and fails past 25 MiB.
This commit is contained in:
ZacharyZhang-NY committed 2026-09-29 19:16:41 -04:00
1 parent 48c7b09d8d
commit 2cd2ca8bc1
26 files changed
+381 -76

No files matched your search

+13 -6
View File
@@ -6,7 +6,7 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
- Rust 1.95, edition 2024. One crate: `ely-gpui-component`.
- gpui and gpui_platform from Zed's repo by git at 1a28cff, the code gpui-pre 0.3.7 republishes. gpui takes `stacker`, which keeps deep layouts off the stack's end; gpui_platform takes `font-kit`, without which macOS draws no text. Feature `runtime_shaders`, on by default, turns on gpui_platform's: machines without Xcode lack the Metal compiler.
- Assets, embedded with `rust-embed`: Lucide 1.48.0 icons (ISC), Inter 4.1 (regular, medium, semibold, italic) and JetBrains Mono 2.304 (OFL).
- Assets, embedded with `rust-embed`: Lucide 1.48.0 icons (ISC), Inter 4.1 (regular, medium, semibold, italic), JetBrains Mono 2.304 and IBM Plex Sans Regular (OFL). Plex sits unmodified, its name reserved, where gpui's svg renderer loads it: gpui_web answers `.SystemUIFont` with it, and svg text falls back to it on the web.
- Catalog data: `emojis` (Unicode emoji), `isolang` (ISO 639, native names), `isocountry` (ISO 3166), `iso_currency` (ISO 4217, also minor units for money).
- Codes: `qrcode` (QR) and `barcoders` (Code 128, EAN-13), both MIT OR Apache-2.0, default features off. `rqrr` 0.11 ((MIT OR Apache-2.0) AND ISC, default features off) reads QR codes from the host's frames.
- macOS extras (tray icon, Dock badge) call AppKit through `cocoa` 0.26 and `objc` 0.2, the crates gpui already links.
@@ -16,15 +16,17 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
- Markdown: `pulldown-cmark` 0.13 (MIT, default features off) for CommonMark with tables, tasks, strikethrough, footnotes and math.
- Pictures: `image` 0.25 (MIT OR Apache-2.0), the crate gpui decodes with, default features off; it turns decoded frames and rims map tiles.
- Maps: Natural Earth's 110m countries (public domain). `assets/maps/countries.geojson`, trimmed by `scripts/countries.py` to an ISO code, a name and positions to 0.01°, backs `maps::WorldMap`; the gallery's tiles are cut from the same file, z0 to z3, by `scripts/tiles.py` with Pillow.
- Web: on wasm32, `web-time` 1.1 (MIT OR Apache-2.0) gives the `Instant` gpui's executor returns, std's own natively; jiff takes `js`, rust-embed `debug-embed`, similar `wasm32_web_time`.
- Web: on wasm32, `web-time` 1.1 (MIT OR Apache-2.0) gives the `Instant` gpui's executor returns, std's own natively; jiff takes `js`, rust-embed `debug-embed`, similar `wasm32_web_time`. The gallery's web entry takes `gpui_web`, `wasm-bindgen`, `js-sys` and `web-sys` as wasm-only dev-dependencies, and a Hebrew cut of Noto Sans Hebrew 2.003 (OFL, `examples/gallery/fonts`): gpui_web asks the browser for emoji and CJK alone. The `wasm-bindgen` CLI at Cargo.lock's version and binaryen's `wasm-opt` finish the bundle.
- Gallery: `examples/gallery`. Website: `frontend/` (Vite 8, pnpm), built after the components: a home page in motion with three.js and smooth scroll, and a components page with categories on the left and the chosen component on the right, after astryx.atmeta.com and gpui-kit.com. Each component runs live from the gallery built for wasm32 through gpui_web, in an iframe, never as a screenshot. It deploys to Cloudflare through `npx wrangler`.
## Commands
- `cargo run --example gallery` opens the gallery. `-- --page <slug>` starts on a page.
- `cargo run --example gallery` opens the gallery. `-- --page <slug>` starts on a page; `--story <title or slug>` with it draws that section alone.
- `cargo run --example gallery -- --capture <dir>` writes PNGs of every page, light and dark, top to bottom, then each page's scripted states, including windows the demos open. macOS only.
- `cargo run --example gallery -- --narrow 280 --capture <dir>` lays each page, header and body, at that width in a padded card and shoots it without its script, whose aims are measured at full width. Demos that set their own width run past the card.
- `RUSTC_BOOTSTRAP=1 cargo check --lib --target wasm32-unknown-unknown` checks the web build; gpui_web's `wasm_thread` needs the bootstrap, as Zed's web examples do.
- `ELY_GALLERY_ASSETS=<address of out/assets/> scripts/web.sh <out>` builds the gallery for the browser, profile `web`, and fails past Cloudflare's 25 MiB. It opens at `?page=<slug>&story=<title or slug>&theme=light|dark`; the host page calls `gallery.setTheme(mode)` or posts `{ ely: "theme", theme }` later.
- `python3 scripts/stories.py` writes `examples/gallery/stories.json`, every page's sections in drawing order with their components, for the site. `scripts/check.sh` refuses a stale one.
- `cargo test --lib --features test-support`, `cargo clippy --all-targets --features test-support -- -D warnings`, `cargo fmt --check`. `scripts/check.sh` runs them with the house rules.
- `rm -rf target/debug/incremental` and `cargo clean -p ely-gpui-component` after each task item keep the disk lean. Cargo keeps every old build of the crate and its tests under a new hash; by T25 they had filled the disk.
- `scripts/icons.sh <lucide-name>...` adds icons. Then add a line to `IconName` in `src/primitives/icon.rs`.
@@ -142,7 +144,7 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
- Badges, tags and avatars live in `data_display`; forms and shell draw theirs from there, and `data_display` imports neither. A hue worn in a row, a label's or an event's, is `data_display::color_mark`, and `Palette::hue` gives the chart color by index. A tag is never wider than its container; a long label ends in an ellipsis beside its remove mark.
- A picture drawn in code reaches gpui as a `RenderImage`. A host that replaces one frees the old through `App::drop_image`, or every frame stays in the sprite atlas (the gallery's model).
- `img()` keeps loading state, and moves an animated picture's frames, only with an id; the frames hold still while its window is inactive or gpui's own `reduce_motion` is on, which the theme's reduced motion does not set. Content masks are rectangles, but `img()` draws only the part of its picture inside its box and rounds that part with its own radius, so a `Cover` crop keeps its corners.
- gpui reads a `&str` picture source with a scheme as a web address, and any other as an embedded asset. A file goes through `Path` (`documents::source` in the crate, `Path::new` in the gallery).
- gpui reads a `&str` picture source with a scheme as a web address, and any other as an embedded asset. A file goes through `Path` (`documents::source` in the crate, `ui::picture` in the gallery).
- Taffy sizes a flex column by the height a ratio box has at its set width, before its cap, so chat's `shaped` pictures sit in a block inside such a column (`chat::ImageMessage`, `VideoMessage`, `ImageGrid`).
- Taffy 0.13 collapses a child's vertical margin through a block parent with no padding or border on that side, and its size cache keeps only that block's size, so a box that measures it again leaves the margin out. A component moves a box with a relative offset, or with padding where what follows moves too, not with such a margin: `layout::Drawer` pulls and snaps its panel, and a fullscreen `overlays::Dialog` slides its card, with `top`; `motion::Stagger`, `Transition`, a centered Dialog and the palette's card rise with padding, so the box a scrim centers grows with them; the gallery's inspector sample is its probe's own child. A margin that stops at its parent is safe: a flex or grid item's own, or a child's of a flex item or of a box with padding or a border on that side. A flex wrapper stops the collapse but gives its items a definite height, so percent heights inside start to resolve (the Drawer's `max_h(relative(0.85))`).
- Tiles in a picture's shape sit in flex rows of `flex_1` cells (`generative::GenerationGrid`). A cell's padding counts in its flex base, so a row of fewer cells gives each more width; cells that match across rows keep their inset on a box inside (`messaging::VideoCallGrid`).
@@ -175,8 +177,12 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
- A component built on `lists::Tree` fills its box; the host gives it a height. A fixed-width column beside a `flex_1` one takes `flex_none`, or it shrinks.
- Editor rows read their text through `CodeEditor::row_text`: a multi-line ghost cuts its line, and the rest follows the ghost's last line.
- gpui clears `debug_bounds` each frame: a test sees an element leave as well as appear.
- Time reads `web_time::Instant`: std's compiles for wasm32 and panics in the browser. `scripts/check.sh` refuses std's in `src`.
- What cannot build for wasm32 is absent there through `cfg(not(target_family = "wasm"))`, never faked: the live terminal, `Terminal` and `Launch` with their grid, keys and search.
- Time reads `web_time::Instant`: std's compiles for wasm32 and panics in the browser. `scripts/check.sh` refuses std's in `src` and the gallery.
- What cannot build for wasm32 is absent there through `cfg(not(target_family = "wasm"))`, never faked: the live terminal, `Terminal` and `Launch` with their grid, keys and search, and the gallery's capture harness.
- A gallery demo a browser cannot run says why on the web through `ui::web_note`, in the blocked style: a second window (`own_window`), files from the dialog or a drop (`NO_FILES`), the menu bar, window moves, a clipboard read.
- A story is a gallery section: its title is a literal, one per page. Under `ui::Story` the one asked for draws without its title and the rest hide, which skips their prepaint and paint; a story the page lacks fails in the page.
- The gallery's pictures go through `asset!` and `ui::picture` or `ui::resource`: a file natively, on the web an address under `ELY_GALLERY_ASSETS`, since gpui reads no files there.
- The first frame wears the host's mode through `Theme::set_mode_now`; `set_mode` fades from the colors on screen.
- The system time zone comes from `typography::format::system_zone`, which fails loud; jiff's `TimeZone::system()` falls back to UTC in silence.
- A Select with nothing to pick stays shut: disabled, it shows its placeholder; open, it fails (`media::DeviceSelector` says no device was found).
- A list opens with its cursor on the current choice: Select on the chosen row, Combobox on its value, MultiSelect on the first one ticked, SelectableList on its first selected row. A selection the owner makes, not the echo of the list's own pick, moves SelectableList's cursor there.
@@ -241,3 +247,4 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
- 2026-09-28: gpui builds an AccessKit tree on macOS, Windows and Linux, not the web, from elements with an id and a `role`, so the accessibility entries turn from `blocked` to `prove`. It still has no tray or badge API, so TrayIcon and DockBadge call AppKit.
- 2026-09-28: SystemNotification is `prove`: gpui's `show_system_notification` posts only from an app bundle; unbundled, as `cargo run` starts the gallery, it logs and drops the call, and the web posts nothing.
- 2026-09-29: the ported debug gallery draws its heaviest page about 1.6 times slower than on gpui 0.2.2 (tooling: 9 fps, worst frame 117 ms, against 14 fps and 72 ms), with gpui's accessibility tree on or off (`Application::new_inaccessible` measured the same). Ely's code did not change there; the frame cost is gpui's.
- 2026-09-29: the web gallery runs on one thread, since a static site sends no cross-origin isolation, and draws through WebGL2 where WebGPU is missing. Its pictures are served beside the wasm, not embedded: the wasm stays under Cloudflare's 25 MiB, and a story fetches only its own. `wasm-opt -Oz` takes the wasm from 23.0 to 20.0 MB; gzip and brotli stay near 7.9 and 5.5 MB.