Skip to content

gpui-rhai 0.4.1

Choose a tag to compare

@eddix eddix released this 06 Nov 03:45
· 53 commits to main since this release

gpui-rhai 0.4.1

Release date: 2026-11-05. A patch release, compatible with 0.4.0 in Rhai and
Rust; read Compatibility for the one behavior fix that can
change what a callback sees. Runtime API stays 3, gpui-pre/core/platform
stay 0.3.9 and MSRV stays 1.95. Upgrade core, registry and CLI together.

This release closes the five issues open after 0.4.0: frames replay in panes a
Host resizes (#215), collections separate behavior from the rows an
application draws (#217), AppShell's side regions dock, hide or slide in as a
drawer (#218), every theme reads at a reading scale (#219), and auto grids take
their tracks from tokens (#220). The full list is in the
CHANGELOG.

Frames replay in resized panes (#215)

A view mounted with host_sets_width(true) anchored its cache islands on the
window's width, so a Host that resized a pane without resizing the window
replayed heights measured at the old width. The root now anchors on the width
it last laid out at, and a frame that lays it out at a new width builds the
content again at that width before painting, so no frame shows the old
heights. Window-owned views keep the window anchor.

Islands also reach content they gave up before: a cold wrapper that sets its
own height (a flex_grow pane, a clipped body) no longer takes its content out
of the islands, and both panes of a horizontal SplitPane hold islands. An auto
grid inside an island lays out at an estimated width on its first frame and at
its own the next; the island now waits for the second (the drift was present
in 0.4.0).

SelectableList and Tabs heads (#217)

components/selectable_list exports SelectableList: List's selection, one
tab stop and its keys, cursor and focus frame, row and context requests,
listbox / option roles and virtualization, with rows the application draws
through a row Node factory:

fn track_row(ctx, row) {
    row([cover(row.item), column([text(row.item.title), text(row.item.artist)])])
}

selectable_list::SelectableList(#{ key: "tracks", label: "Tracks", items: tracks,
    selection_mode: "single", selected_keys: ctx.get_state("selected"),
    row: ctx.node_factory(Fn("track_row")), on_selection_change: Fn("selected") })

The factory gets #{ key, item, index, selected, current }; a control in a row
calls back into the application and keeps its own tab stop. By default a row is
an undivided List row (selection tint, hover fill, radius.md, metrics.inset);
row_chrome: false leaves it bare. List and Table now call the same module's
behavior functions, so the three agree on clicks, keys and context requests.

A Tabs item takes an optional head node drawn in place of its icon, label and
count, such as a title over a muted count; the selection marks, close button,
dirty mark, reordering, keys and overflow menu stay the tab's, and label still
names it. Decision D82.

AppShell side regions (#218)

sidebar_mode and inspector_mode place each side region: dock (default,
as before), hidden (out of layout and F6, content still mounted) or
overlay, a modal drawer from the region's window edge over a scrim. Escape or
a press on the scrim emits sidebar_dismiss / inspector_dismiss, and focus
returns where it was. The application measures the room it has and chooses;
AppShell holds no breakpoints. The Gallery's AppShell page shows a shell that
docks its inspector from 800px and its sidebar from 600px. Decision D81.

Reading scale (#219)

Every theme has the environment axis scale: 100 (default), 110, 125, 150,
175 and 200. It multiplies pixel and rem length tokens and typography sizes and
line heights, so controls keep their proportions around larger text and layouts
reflow; literal lengths, borders and focus frames stay as written. A Host sets
it through ThemeTokenOverrides::environment_defaults, a script with
.env(#{ scale: "125" }). Components neither declare nor read it. The Gallery
offers the scale in its command palette, and every page passes the composition
audit and a reach check at 100, 125 and 150. Decision D80.

Auto grid tracks from tokens (#220)

auto_grid_columns(min, ideal, max) takes Lengths as well as numbers: pixels,
rems or a theme token, resolved natively in the node's environment on every
frame, so a theme, density or scale change moves the tracks without Rhai.
Bounds that resolve out of order lay out with the minimum kept and the maximum
raised to it, and the Host receives a presentation diagnostic. Rust gains
AutoGridColumns::from_lengths and Style::auto_grid_column_lengths.

Compatibility

  • Node factory callbacks. The callbacks in the nodes a Node factory
    returns, and the callback props of the components it renders directly, now
    belong to the scope that bound the factory. Before, they ran with the
    receiving component's ctx and failed on the application's state fields. A
    factory that relied on reaching the receiving component's state from a
    handler must curry what it needs instead.
  • List and Table depend on SelectableList. gpui-rhai update installs
    components/selectable_list with them. A Host that lists the modules it
    loads by hand (ScriptSources, Mount::module) adds it next to
    components/list or components/table.
  • A scale axis of the application's own. A token base that already
    declares an environment axis named scale keeps its declaration; numeric
    values are now read as a reading-scale percentage.
  • Component widths in literal pixels do not follow the reading scale; use
    theme_length tokens or rems where a width holds text.
  • Rust API. Additive only: AutoGridColumns::from_lengths,
    Style::auto_grid_column_lengths, SCALE_ENVIRONMENT, SCALE_STEPS and
    ThemeTokens::reading_scale.

Upgrading from 0.4.0

Upgrade the CLI first, then run gpui-rhai update. It merges the four modules
this release changed, which take 0.4.1 (List, Table, Tabs and AppShell), and
adds SelectableList (new at 0.4.1) where List or Table is installed. Merges keep local edits and
write conflicts to .gpui-rhai/conflicts/.

Verification

Before the release, on macOS: format and strict Clippy for the workspace,
tests/native-keyboard and the apps; cargo doc with warnings denied; the
workspace tests (879), the default-feature library tests (617), the native
suite (690, also with every subtree an island), all tests/native-keyboard
tests (691), the token reload tests (5) and the apps' tests (51). Each feature
commit of the release passes the workspace tests, the app component check, the
native suite in both island modes and strict Clippy on its own. A new
acceptance test runs every Gallery page at 100, 125 and 150 percent through the
composition audit and a reach check (no control cut off where nothing scrolls,
none laid over another).

gallery_profile: Window::draw p95 at most 3.76 ms over every interaction,
within the 8.3 ms budget. Its page sweep samples every seventh page, so the new
page changes which pages it visits (it now reaches Table, Calendar and the data
scene); over 0.4.0's sequence the sweep's p95 is 2.10 to 2.22 ms against 0.4.0's
1.95 to 1.97 ms on the same machine.

Visuals: the 38 example baselines render identical in RGB; 15 of the 19 Gallery
baselines were recaptured, their only change the sidebar's scrollbar thumb
after the Lists group gained a page. Every Gallery page in both densities was
compared with 0.4.0: the title bars are identical, page content differs only on
AppShell, Density and Tabs, the pages with new demos, and the rest of the
differences are the sidebar's new entry. The theme appearance probes (light,
dark) reported no error and left OS preferences unchanged, and the appearance
lifecycle passed. The release smoke launched its 25 cases and the artifact
audit passed for its 11 binaries. disktree: key_harness passes, and its
screenshots in dark and light show the treemap filling the stage beside the
SplitPane panel. cargo package packaged and built gpui-rhai and
gpui-rhai-registry.

Upgrade: a project the 0.4.0 CLI created with List, Table, Tabs and AppShell,
and a local edit in List, took gpui-rhai update from this release: it added
SelectableList, merged the four modules to 0.4.1 with the edit kept and no
conflict, gpui-rhai check passed for its 15 modules, and a second update
changed nothing. oh-my-byted, built through a local path patch against the
branch once the #215 fix landed, with its panes mounted host_sets_width(true),
passes its render replay tests (one of which drifts on 0.4.0) and its 212 GUI
tests.