Repository navigation
Releases
gpui-rhai 0.4.2
gpui-rhai 0.4.2
Release date: 2026-11-06. A release that is not compatible with 0.4.1 in
places, numbered 0.4.2 by the maintainer's choice rather than 0.5.0: read
Not compatible with 0.4.1 before upgrading.
Runtime API stays 3 and gpui-pre/core/platform stay 0.3.9; gpui-fast 0.1.9
is a second backend; MSRV moves to 1.99. Upgrade core, registry and CLI
together.
This release works through the feedback from oh-my-traex, an agent workbench
that runs a gpui-fast UI (nsh) and a gpui-rhai UI side by side. gpui-rhai now
builds on gpui-fast, so both fit in one binary; editors let the script above
them take keys first; overlays leave their trigger's layout alone; scripts read
any theme's colors and reach notifications and the Dock badge; and a dozen
smaller items, from interval to error messages that say where the mistake is.
Item numbers below are that feedback's. The full list is in the
CHANGELOG.
One binary with a gpui-fast UI
Two GPUI forks register the same Objective-C classes at load time, so a binary
that links both aborts before main, and an application with a gpui-fast UI
had to ship a second binary for gpui-rhai. gpui-rhai now builds on either fork:
gpui-rhai = { version = "0.4", default-features = false, features = ["gpui-fast"] }gpui-pre stays the default; the two features exclude each other, and
gpui_rhai::gpui and gpui_rhai::gpui_platform name the selected fork's
crates. gpui-fast is pinned at 0.1.9, the version nsh-platform-gpui 0.4 builds
on. On gpui-fast, cache islands are off and a Host starts the process with
GPUI_SCROLL_LAYERS=0 (embedding).
gpui-rhai check reads the backend from the dependency, rejects a direct
dependency on the other fork, and warns when the lockfile resolves both;
gpui-rhai init --backend fast writes the dependency. A test binary linking
nsh's window and the Gallery on gpui-fast opened both in one application.
Field text sat 4pt high on gpui-fast: its Taffy resolves a percentage height
under a min-height as CSS does, and the centering on gpui-pre came from the
older resolution. Aligned primitive nodes now lay out as flex boxes, so fields
center on both backends.
Keys above editors (#27)
Input and Textarea bind their keys as GPUI actions, which run before any raw
key listener, so an on_capture("key:…") above an editor never saw Up, Down or
Enter. An editor now runs its ancestors' capture handlers for a key before it
acts; one that returns event_response().prevent_default() or .stop() keeps
the editor from acting. A completion list above a Composer takes Up, Down and
Enter while it is open and returns a plain event_response() when closed.
Overlays leave the trigger's layout alone (#26)
The box between an overlay and its trigger was a flex row that took the
trigger's content width, so a ContextMenu around a sidebar row shrank the row
and pushed a long title past the sidebar. The box is now a column that may
shrink: the trigger lays out as it would unwrapped, for every overlay.
Theme colors, a third surface, and radius names (#1, #16, #17, #35)
ctx.theme_colors(family, name[, keys]) returns any variant's resolved colors,
so a theme picker paints each variant's swatch while the window shows another.
ctx.theme_variants() already reported each variant's mode.
The token base adds surface.chrome, halfway from surface to
surface_raised, for an application's sidebars and title bars between the
window canvas and raised cards.
The markers' capsule was named radius.sm, so a box meant to be slightly
rounded came out a pill. The capsule is now radius.pill, and radius.sm is
a fixed 4px rounded rectangle for an application's own shapes (thumbnails,
swatches), which no built-in component takes. D83.
Notifications, the Dock badge and attention (#29)
gpui_rhai::system gives a Host notifications_available() (macOS posts
notifications only for a bundle with an identifier, and an unbundled binary
shows none), set_badge_label for the Dock badge, and
ScriptApplication::app_identity for Linux and Windows. The SystemServices
extension gives scripts system.notification@1 (show, dismiss, available, and
a responses subscription for clicks), system.badge@1 and
system.attention@1 (capabilities).
Timers (#2, #19)
A mounted view without a Host clock now follows GPUI's executor clock: the
system clock in an application, the scheduler's in a GPUI test, so
advance_clock and run_until_parked fire due timers and move animation in
tests and offscreen captures. Before, a one-second counter in an offscreen test
moved only when something else woke the view.
interval(key, period_ms, paused, Fn("callback"), payload) calls back every
period, 16ms at least, in the timeout key space, so ctx.pause_timeout,
resume_timeout and cancel_timeout apply. A late tick is one call; missed
ticks are not made up.
Names from content (#20)
A button, tab, option, menu item, checkbox, radio, switch, tree item, row,
cell, header, heading or tooltip without a label takes its accessible name
from the text its subtree shows, as ARIA's name from content does. A row drawn
with accessibility_role("button") reads as its text and automation finds it
by name, without a separate accessibility_label.
Errors that say where (#15, #24, #25, #32, #36)
- A duplicate key names the keys from the root to the parent and where the
script created both children, instead of a runtime node number:
duplicate keygoalin child groupchildrenof work > completion-panel, created at ui/main.rhai:42:9 and ui/main.rhai:43:9. Inside an
error_boundary's child it no longer fails the view: the boundary shows its
fallback, which carries the error asboundary_error. - A function allows an expression depth of 64, as the top level does; a node
with a style chain built in a closure failed at 32. - Files beside the images in
ui/assets(a license,.DS_Store) no longer
fail check; non-image files are skipped with a warning and left out of embed. - A Rhai keyword where a name belongs fails with an error that names it; the
generated Switch reference imported the moduleas switch, which does not
parse, and now usesswitch_component. import "themes/…"explains that a theme is not a module and points to
ctx.theme_colors.
Collapsible, Composer and viewers (#5, #8, #9)
Collapsible's content is optional, for a disclosure row whose content the
caller lays out elsewhere, such as rows of a virtual collection. Composer's
editor shows the Composer's surface, so a root background covers the whole
field. The document viewers take part_styles like every component;
document viewers shows restyling one on a raised
card.
Not compatible with 0.4.1
- Rust 1.99. The toolchain and MSRV move from 1.95.
--all-featuresno longer buildsgpui-rhai:gpui-preand
gpui-fastexclude each other. List features explicitly.radius.smmeans 4px. A script or theme override that usedsmfor a
capsule usespill. A shape whose shorter side is 8px or less still reads as
a capsule at 4px, since radii clamp to half the shorter side.- Editors and capture handlers. An ancestor's
on_capture("key:…")that
returnsprevent_default()orstop()now keeps an editor from acting on
that key; before, the editor acted and the handler never ran. - Overlay triggers stretch across a column parent as they would unwrapped,
and may shrink. Wrap a trigger that should keep its content width, for
example in a start-aligned Stack. - Timers in tests. Without a Host clock, a GPUI test's timers and motion
follow the scheduler's clock: advance it rather than sleeping. - Accessible names. Nodes in the roles above that had no name now have
their text; automation that matched them by an empty name matches the text. - Rust API.
ReconcileError::DuplicateKeygainspathandsources;
KnownCallDiagnosticgainsmodule;TimerErrorgainsIntervalTooShort.
An exhaustivematchor a struct literal needs the new names. New, so
breaking nothing: thegpui-fastfeature,gpui_rhai::system,
SystemServices,ScriptViewExtension::poll(empty by default),
ScriptApplication::app_identity,UiContext::theme_colors,
RetainedUiTree::reconcile_contained,TimerDescriptor::repeatingand
repeats, andSourceLocation'sDisplay.
Upgrading from 0.4.1
Upgrade the CLI first, then run gpui-rhai update. It merges the token base
and the nine modules this release changed, which take 0.4.2: Badge, Carousel,
Collapsible, SplitPane, Switch, Tabs, Tag, Toast and Composer. Merges keep
local edits and write conflicts to .gpui-rhai/conflicts/. Then replace
theme_radius("sm") with theme_radius("pill") where a script meant a
capsule.
To move a Host to gpui-fast, depend on gpui-rhai with
default-features = false, features = ["gpui-fast"], reach GPUI through
gpui_rhai::gpui, drop direct gpui-pre dependencies, and start the process
with GPUI_SCROLL_LAYERS=0.
Verification
Before the release, on macOS: format and strict Clippy for the workspace,
tests/native-keyboard and the apps on both backends; cargo doc with
warnings denied on both backends; the workspace tests (892 on each backend),
the default-feature library tests (627), the native suite (701 on gpui-pre,
also with every subtree an island, and 689 on gpui-fast with scroll layers
off), all tests/native-keyboard tests (702), the token reload tests (5) and
the apps' tests (51). Each code commit of the release passes the workspace
tests, the app component check...
gpui-rhai 0.4.1
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'sctxand 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 updateinstalls
components/selectable_listwith them. A Host that lists the modules it
loads by hand (ScriptSources,Mount::module) adds it next to
components/listorcomponents/table. - A
scaleaxis of the application's own. A token base that already
declares an environment axis namedscalekeeps 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_lengthtokens or rems where a width holds text. - Rust API. Additive only:
AutoGridColumns::from_lengths,
Style::auto_grid_column_lengths,SCALE_ENVIRONMENT,SCALE_STEPSand
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.
gpui-rhai 0.4.0
gpui-rhai 0.4.0
Release date: 2026-10-31. A minor release that is not compatible with 0.3.3;
read Not compatible with 0.3.3 before upgrading.
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 is about design. Corner radius now tells kinds of things apart
instead of being a corner style an application picks; fields pad by the gap
around their text again; pressable rows are rounded blocks without the accent
bar; Toolbar sets its fields first; focus frames show after navigation keys
rather than after every click; and key legends sit on their label's baseline.
The full list is in the CHANGELOG.
Radius by kind
The token base no longer has a corners environment axis (square, subtle,
round). Each component takes the radius of its kind, so a tag never reads as a
button beside one:
| Kind | Role | Value |
|---|---|---|
Layout: Region, divider-built bars, rows between dividers (Table, List dividers: true) |
none | 0 |
| Controls: Button, IconButton, fields, Textarea, triggers, groups, the Tabs track; rows you press: Menu items, Select and Combobox options, Command rows, Tree, Timeline, undivided List rows | radius.md |
4 up to 24px tall, 6 above |
| Boxes in a control: Checkbox, Kbd, the Tabs thumb, clear and close targets | radius.xs |
half of md |
| Markers: Tag, Badge, chips, Switch, counts, the SplitPane grip | radius.sm |
capsule |
| Surfaces: Card, Alert, selectable cards, Menu, Popover, Dialog, Toast, field panels | radius.lg |
8 |
A one-line Tooltip takes 4px. Selection in a pressable row is a fill; the
start-edge accent bar is gone. Rows that float sit 4px in from a bare container
or inside a panel's padding. The SplitPane grip darkens on hover and extends only
while dragged. See design/atoms.md and
decisions D74 and D76.
Field padding
metrics.field_pad returns to about 1.4 times the gap between a field's frame
and its text (comfortable 5 / 7 / 10 / 12, compact 3 / 4 / 6 / 10), so md field
text starts on metrics.inset with row text again. Textarea pads evenly:
metrics.multiline_pad is field_pad less the half-leading of its text
(comfortable 2 / 3 / 6 / 8, compact 0 / 0 / 2 / 6). Decision D75.
Toolbar
Toolbar sets fill first, then filters, then context (tags, a breadcrumb),
with the actions at the end, so control-height items hold both edges and
shorter markers sit between them. DataView's toolbar follows. Decision D77.
Focus indicators
ScriptViewConfig::focus_indicators(FocusIndicators) decides when focus styles
paint. By default (Navigation) a focus frame shows after a navigation key
(Tab, Shift+Tab, an arrow, Home, End, Page Up, Page Down, F6) and hides at the
next pointer press; a key with Command, Control or Alt, a shortcut, does not
show it. Always keeps the 0.3 behavior and Never suits an application driven
by shortcuts. A focused text field shows its frame in every mode. The policy
covers focus, group_focus and focus_within styles, overlay triggers and
the native SplitPane and Resizable handles and sliders; a custom primitive reads
PrimitiveContext::focus_indicators. Script windows follow the opening view.
Decision D78.
Baseline alignment
style().items_baseline() sets a row's text children on the baseline of the
row's own text style, measured with the font metrics GPUI paints with (GPUI
gives Taffy no text baselines). Button, Menu and Command set their key legends
on the label's baseline: the ⌘K of Search ⌘K sat 1.5pt high and now ends on
the label's pixel row. An inline Kbd renders one run of text. Decision D79.
Not compatible with 0.3.3
- The
cornersaxis is removed. A Host that setscornersthrough
ThemeTokenOverrides::environment_defaultsfails theme preparation with
UndeclaredEnvironment; drop the entry. A script's.env(#{ corners: ... })
has no effect and can be removed. - Token base and components move together. 0.4.0 components no longer
declarecorners. A project with its own copy ofui/tokens.rhaikeeps
working until it updates the copy; update the token base and the components
in onegpui-rhai update. - The
indicator_barpart is removed from Menu, Combobox (and Select),
Command, Tree, List, Table and Timeline. A stylesheet that names it fails to
load withunknown part. - Behavior. Focus frames no longer follow a click (
focus_indicators);
Toolbar's order changed; cards, rows, fields and the grip changed shape and
padding. A Textarea of a given row count is a few pixels taller. - Structure. An inline Kbd is one text node; its
keypart styles that
text. Button, Menu and Command put the label and the legend in one row. - Rust API.
Aligngains aBaselinevariant: an exhaustivematchneeds
an arm.FocusIndicators,ScriptViewConfig::focus_indicatorsand
PrimitiveContext::focus_indicatorsare new, so they break nothing.
Upgrading from 0.3.3
Upgrade the CLI first, then run gpui-rhai update. It merges the token base
and the 37 modules this release changed, which take 0.4.0: 33 components plus
Toolbar, Composer, DataView and NavigationStack. Merges keep local edits and
write conflicts to .gpui-rhai/conflicts/. Then:
- remove
cornersfrom the Host's environment defaults and from scripts; - remove
indicator_barfromui/styles.rhai; - to keep frames after a click, mount with
.focus_indicators(FocusIndicators::Always); tests that focus a node in code
and expect its frame press Tab instead, or useAlways.
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 (876), the default-feature library tests (614), the native suite
(665, with new tests for focus indicators), the token reload tests (5) and the
apps' tests (51). Each of the release's feature commits passes the registry
lint, the official registry tests, the CLI tests, the app component check, the
native suite and gpui-rhai Clippy on its own.
gallery_profile: Window::draw p95 at most 2.72 ms over every interaction,
within the 8.3 ms budget. The 57 visual baselines (19 Gallery, 38 examples) were
recaptured for the new shapes, and the second pass's 21 changed files are
attributed to D76, D77 and D79; the theme appearance probes (light, dark)
reported no error and closed their windows, 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. CI ran
the stable (latest stable and the Grain backend), portable, native, release
(Linux X11 and Wayland smoke, artifact audit) and apps jobs on the pull request
(#216).
gpui-rhai 0.3.3
gpui-rhai 0.3.3
Release date: 2026-10-29. A patch release, compatible with 0.3.2 in Rhai and
in Rust. Runtime API stays 3 and MSRV stays 1.95. gpui-pre/core/platform
move to 0.3.9: a Host that depends on gpui/gpui_platform directly pins
=0.3.9. Upgrade core, registry and CLI together.
This release is about frame and commit cost. A frame replays the parts of a
view that did not change instead of building, laying out and painting them
again, and a commit does work in proportion to what changed. No Rhai source
changes; no registry module changes. The full list is in the
CHANGELOG.
Frame replay
A subtree of 16 nodes or more with nothing that changes from frame to frame is
embedded as a cached GPUI view (a cache island) and replayed while its nodes
and what it inherits stay the same. Motion, signal styles, scroll containers,
virtual collections, overlays and layers, primitives, canvases, images,
element refs, raw pointer handlers and window drag areas stay outside islands.
An island's parent is a flex column, its height follows its content and its
width must not depend on its content: the width of a window
(ScriptApplication, script windows) is known, a Host says so for a view it
mounts with ScriptViewConfig::host_sets_width(true), and a row's one
growing child, as an AppShell's main area, takes the room its fixed siblings
leave. A render of the whole view, as a state change in the view itself makes,
still replays the subtrees it renders the same.
Replay is on by default. ScriptViewConfig::render_islands(false) or
GPUI_RHAI_RENDER_ISLANDS=off renders every frame in full;
GPUI_RHAI_RENDER_ISLANDS=verify panics when a replayed subtree would lay out
at another height. See Frame replay.
A wheel over a scrollbar and a drag that autoscrolls a collection now notify
the scrolling view instead of refreshing the window, so the window's islands
beside them replay.
Commit cost
A subtree a commit shares with the last one keeps its retained nodes, which
are now shared, and the node table is copied rather than rebuilt. Motion, exit
ghosts, signal validation, resource budgets, collection bindings, page
visibility, semantics and the walks a view makes after a commit read counts the
retained tree keeps per subtree, or carry what they inherit down the tree,
instead of walking all of it or each node's ancestors. A geometry state a
transaction shares is copied only when a change needs it. Rendering a node
also does less: building a view of 4,100 nodes in full took 3.8 ms instead of
6.3 ms.
Measured
Release build on an Apple Silicon Mac, p50 per step, against 0.3.2 with the
benchmark added (benchmarks/baselines/):
| Scenario | 0.3.2 | 0.3.3 |
|---|---|---|
| Scroll a 400-row Table above 1,000 plain rows, within rendered rows | 18.2 ms | 0.74 ms |
| The same, a step that renders new rows | 43.4 ms | 4.0 ms |
| Above 2,500 rows, within rendered rows | 49.5 ms | 1.2 ms |
| Above 2,500 rows, a step that renders new rows | 120.7 ms | 7.4 ms |
Typing into a field of an AppShell with a sidebar and three cards took
0.41 ms a keystroke with islands on and 1.8 ms with them off when the draft is
in a component around the field, and 1.1 against 2.5 ms when it is in the
view, which renders the view whole.
GPUI itself renders every cached view again on some frames: a pointer click
(its active state), a focus change and an inspector pick.
Fixes
motion_hoverandmotion_pressrun. On a node with no other pointer
handling they never received the pointer events, andmotion_hoveron a
node with its own handlers, such ason_click, made the render fail.- Realizing items of a virtual collection inside a
motion_groupno longer
copies every item it keeps and adds the group to component items again on
every scroll; new items take the group, once. - In an application with several script windows, a commit in one window no
longer drops the virtual collection state of the others. - A commit commits its runtime transaction before it publishes the new tree,
and rolls the runtime and the engine back if that fails.
Upgrading from 0.3.2
Pin a direct GPUI dependency to the new family; gpui-rhai check reports a
mismatch:
gpui = { package = "gpui-pre", version = "=0.3.9", default-features = false, features = ["font-kit"] }
gpui_platform = { package = "gpui-pre-platform", version = "=0.3.9", default-features = false, features = ["font-kit", "runtime_shaders", "wayland", "x11"] }The APIs gpui-rhai uses did not change between 0.3.7 and 0.3.9; a Host's own
GPUI code may meet Keystroke::parse taking &'static str, KeyBinding::load
taking &App, and ScrollWheelEvent gaining source. In 0.3.9
debug_selector takes effect only in debug builds.
A Host that mounts a view filling its window or a pane it sizes adds
.host_sets_width(true) to the view's ScriptViewConfig. Tests that count
renders exactly may want render_islands(false).
Not in this release
An optional GPUI Fast backend was evaluated and is not shipped: gpui-rhai's
per-frame registries (overlays, interactions, presented geometry, virtual list
frames) are refilled by each view's prepaint, which GPUI Fast skips for the
views and list rows it retains.
Verification
Before the release, on macOS: format and strict Clippy for the workspace,
tests/native-keyboard, the apps and the grain backend; cargo +stable check;
a release check; cargo doc with warnings denied; the workspace tests (875),
the default-feature library tests (614), the native suite (663) and the native
suite again with every subtree an island (GPUI_RHAI_RENDER_ISLANDS=aggressive,
662), the token reload tests (5) and the apps' tests (51). New native tests
compare the quads a frame paints with islands and without them over a scrolled
Table, controls, hovered and focused rows, a container scrolled beside cached
rows and an AppShell, and check that a quiet scroll frame replays the cold
rows.
gallery_profile: Window::draw p95 at most 3.43 ms over every interaction,
within the 8.3 ms budget. The 57 visual captures (19 Gallery, 38 examples) are
identical in RGB to 0.3.2's taken on the same Mac; the committed baseline PNGs,
last refreshed on 2026-10-11, differ from both. The theme appearance probes
(light, dark) and the appearance lifecycle passed and closed their windows. The
release smoke launched its 25 cases with GPUI_RHAI_RENDER_ISLANDS=verify, and
the artifact audit passed for its 11 binaries. disktree: key_harness passes,
and its screenshots in dark and light are byte-identical with islands on and
off. cargo package --workspace --locked packaged and built the three crates.
CI ran the stable, portable, native (with the native suite over cache
islands), release and apps jobs on the pull request (#214).
gpui-rhai 0.3.2
gpui-rhai 0.3.2
Release date: 2026-10-29. A patch release, compatible with 0.3.1. Runtime API
stays 3, gpui-pre/core/platform stay 0.3.7 and MSRV stays 1.95. Upgrade
core, registry and CLI together.
Virtual collections, such as a Table's body, now render ahead in batches and
keep the items a scroll leaves, so scrolling runs the renderer and commits the
view far less often. The values are the application's: per collection, per
Table, or as a Host default. The full list is in the
CHANGELOG.
New
-
Rendering ahead and keeping items (decision D73).
virtual_collection
takes three settings:overscan_items: items rendered beyond each edge of the viewport. When
set, it replacesoverdraw_pixels, which keeps working.batch_items(16 unless set): a scroll that needs items also renders the
ones after them in the direction it is going.retain_items(200 unless set): items a scroll left come back without
running the renderer until the collection renders again. An item whose
component owns an effect, a timer or a signal is released as before.
A frame asks for a render only when it needs an item, so scrolling inside a
rendered batch commits nothing. Scrolling a Table 64 rows one row per step
rendered on 4 steps instead of 63. See
Rendering ahead and keeping items. -
Host defaults.
RuntimeEngine::set_virtual_collection_policy, called in
ScriptViewExtension::configure_engine, sets the values for every
collection in the view; a collection's own value wins. The policy type is
VirtualCollectionPolicy. -
Table 0.3.2 takes
overscan_rows,batch_rowsandretain_rowsand
passes them to its body. See
Rendering ahead and keeping rows.
An item rendered ahead that fails does not fail the items the frame needs: the
realization is retried without the batch. A kept item keeps its data reads, so
a change to data it read renders the collection again rather than bringing back
a stale row.
Upgrading from 0.3.1
The new defaults apply to every virtual collection with the 0.3.2 runtime,
without a source update. To return to rendering one item at a time and
keeping none, set batch_items: 1, retain_items: 0 on a collection, the
batch_rows/retain_rows props on a Table, or a Host policy with
batch_items: Some(1) and retain_items: Some(0). Tests that count
realizations exactly may want those values.
Upgrade the CLI first, then run gpui-rhai update to bring Table 0.3.2.
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, the apps and the grain backend; cargo +stable check;
cargo doc with warnings denied; the workspace tests (867), the default-feature
library tests (606) and the native suite (653), including four new Table tests
for batches, kept rows, a new render dropping kept rows and the Host policy;
generated references current. CI ran the stable, portable, native, release and
apps jobs on the merged pull request (#212).
gpui-rhai 0.3.1
gpui-rhai 0.3.1
Release date: 2026-10-28. A patch release, compatible with 0.3.0. Runtime API
stays 3, gpui-pre/core/platform stay 0.3.7 and MSRV stays 1.95. Upgrade
core, registry and CLI together.
It adds Carousel, a controlled pager for compact places, and SlidingPanel,
which brings new content in from the side it comes from; Tabs can slide its
panels the same way. It also fixes disabled icons that kept their enabled
color and scroll containers that scrolled a viewport past their end. The full
list is in the CHANGELOG.
New
- Carousel (
components/carousel) shows one item at a time and never
turns by itself.variant: "header"puts the label and the controls above
the item;"overlay"lays arrows over its sides, shown on hover or focus.
The position is a count (2 / 5), dots or nothing, andwrapsteps past the
ends. Its controls are one tab stop beside the item, so keys typed in a field
or list inside an item stay the item's. See decision D72. - SlidingPanel (
components/sliding_panel) slides new content in 12px
with a fade from the side it comes from, mirrored in right-to-left text and
settled by the Host's motion policy. Only the incoming content moves, so any
content can slide. - Tabs takes
panel_motion: "slide"(withkey) to slide a newly
selected panel in from the side of the tab it replaces; AnimatedTabs
forwards it.
Fixes
- A disabled IconButton, and the
prefix/suffixicons of a disabled Button,
now take thedisabledink like the label. - A scroll container whose box is not a flex container, such as ScrollArea's
viewport, no longer scrolls a viewport's height past its last row. The
runtime's overlay scrollbar is pinned to the container's origin instead of
sitting after the content.
Upgrading from 0.3.0
Upgrade the CLI first, then run gpui-rhai update:
- Tabs 0.3.1 imports
components/sliding_panel, whichupdateinstalls. - Button and IconButton 0.3.1 carry the disabled-icon fix;
AnimatedTabs 0.3.1 forwardspanel_motion. - Carousel is new:
gpui-rhai add components/carouselinstalls it with
SlidingPanel and the chevron icons.
The scroll fix is in the runtime crate, so it reaches every application with
the 0.3.1 dependency, without a source update. 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; the workspace tests and the native suite,
including the Gallery audit of every page in both densities, the new Carousel
tests and the overscroll regression; generated references current; offscreen
Gallery captures of the Carousel and SlidingPanel pages. CI ran the stable,
portable, native, release and apps jobs on the merged pull request (#210).
gpui-rhai 0.3.0
gpui-rhai 0.3.0
Release date: 2026-10-27. A minor release that is not compatible with 0.2.3;
read Not compatible with 0.2.3 below before upgrading.
Runtime API stays 3, gpui-pre/core/platform stay 0.3.7 and MSRV stays 1.95.
Upgrade core, registry and CLI together.
It folds TabBar into Tabs, which now has three looks (track, underline,
bar) that share counts, closing, adding, reordering, slots and an all-tabs
menu; adds the Dracula, Molokai and Absolutely themes; groups the Gallery by the
chapters of the design contracts; keeps named callbacks in their module's
environment; reports recoverable image failures and every view failure to the
Host; and adds responsive grid tracks, aspect ratios and CSS cubic-Bézier
easings. The full list is in the CHANGELOG.
Not compatible with 0.2.3
- TabBar is removed. Use
Tabs(#{ variant: "bar", panel: false, ... }):
the tab fields (closable,dirty,icon,disabled),start/end,
overflow_menu,reorderableand thechange,reorderand
context_requestevents carry over.on_closenow receives
#{ value, next }, wherenextis the tab to select instead. The arrows
select directly: there is no separate cursor and no Enter step.keyis
required only withreorderableoroverflow_menu. See decision D71. - Tabs behavior. Tabs reveals the selected tab through an effect, so it
renders inside a mounted view (every application does); a test that renders
a view once without aScriptLifecyclemust mount it. Content-width tabs keep
their width, up to two label columns, and the strip scrolls instead of
squeezing them. The tablist now sits inside astrippart, so code that
walks the node tree finds it one level down;part_styleskeep working. - Token base. Tabs reads
metrics.counter, thecountertypography role,
tabs.counterandcontrol.hover.gpui-rhai updatebrings them into an
unmodified or baselinedui/tokens.rhai; a hand-edited copy without a
baseline must take the new lines fromregistry/tokens.rhai, or the theme
fails to load withlacks tokens required by components/tabs. - Rust API.
MotionEasinggains aCubicBeziervariant: an exhaustive
matchneeds an arm (#186).ScriptViewConfig::on_errorand its
ScriptErrorReportare new (#185, #205), so they break nothing.
Upgrading from 0.2.3
Upgrade the CLI first, then run gpui-rhai update:
- Tabs and its new dependencies.
updatemergescomponents/tabs0.3.0
and installs what it now imports:components/tab_overflow_menu(new) and
components/icon_button, with thecloseandplusicons. - A copied TabBar stays yours.
updateleavesui/components/tab_bar.rhai
and its baseline alone with anote:line, and updates everything else; the
copy keeps running until you migrate its callers and remove it. - New themes. Dracula, Molokai, and Absolutely Light and Dark join
ui/themes/; existing palettes are untouched. - Versions. Modules changed in this release take 0.3.0 (Tabs,
TabOverflowMenu, AnimatedTabs); the authoring guide now states the rule.
Merges keep local edits and write conflicts to .gpui-rhai/conflicts/.
Verification
Before the release, on macOS: format and strict Clippy for the workspace and
tests/native-keyboard; the workspace tests and the native suite, including
the Gallery audit of every page in both densities; generated references
current; Gallery screenshots of the Tabs page under every bundled theme. CI ran
the stable, portable, native, release and apps jobs on the merged pull request
(#208).
gpui-rhai 0.2.3
gpui-rhai 0.2.3
Release date: 2026-10-22. A patch release of 0.2 that is not fully
compatible with 0.2.2; read Not compatible with 0.2.2 below
before upgrading. Runtime API stays 3, gpui-pre/core/platform stay 0.3.7
and MSRV stays 1.95. Upgrade core, registry and CLI together.
It adds retained pages and navigation stacks, shared scrollbar sources and
tail-following collections, ColumnBrowser, Composer and selectable cards,
calendars, time inputs, time ranges and timelines, a native Markdown viewer,
recalibrated palettes, and CLI fixes that keep copied sources current; the full
list is in the CHANGELOG.
Not compatible with 0.2.2
- Rust Hosts:
PendingEventandActionInvocationgain anownerfield.
Addowner: Noneto literals you build by hand, keep the owner on messages
the runtime produced, and deliver them through the lifecycle's transactional
event/action entry points. - Tables: an Array-backed Table with an explicit
sortnow sorts in Rust,
as NativeCollection Tables do, instead of keeping the caller's order.
sort.keynames the declared display column and an optional column
sort_keynames its scalar rank field. Remove a caller-side sort that
duplicates it, or omitsortto keep an explicitly ordered Array. - Token base and palettes: official components now use
control.border
andcontrol.danger_hover/control.warning_hover/
control.success_hover, which the token base derives from the palette's
on_danger,on_warningandon_success.gpui-rhai updatebrings them
into an unmodified or baselinedui/tokens.rhai(below). A hand-edited copy
without a baseline must take the new lines fromregistry/tokens.rhai;
otherwise the theme fails to load withlacks tokens required by components/button. A custom palette must define the threeon_*colors;
every bundled palette does.
Upgrading from 0.2.2
Copied official components and theme sources
Upgrade the CLI first: the 0.2.3 gpui-rhai update does what the 0.2.2 one
could not.
- Same-version changes: 19 official modules changed without a version
change:components/button,checkbox,code_viewer,combobox,
command,date_picker,diff_viewer,icon_button,input,
input_group,list,menu,popover,radio,split_pane,table,
tabs,textareaandtoggle_group. The 0.2.2 CLI skipped such changes
(this is how a pre-#140 SplitPane survived updates, #156); 0.2.3 compares the
installed baseline with the bundled source and merges them. - Token base and palettes (#183):
updatenow mergesui/tokens.rhaiand
the bundled palettes inui/themes/like components. A project from 0.2.0 to
0.2.2 has no baselines for them: a copy equal to the registry gets one, a copy
still exactly as one of those releases installed it is replaced, and any
other is left alone with anote:line to merge by hand.ui/theme.rhai
stays yours. - New modules:
components/calendar,column_browser,markdown_viewer,
selectable_card_group,time_input,time_range_picker,timelineand
patterns/composer,navigation_stack; install them withgpui-rhai add.
Merges keep local edits and write conflicts to .gpui-rhai/conflicts/.
Behaviour to check
- Bundled palettes are recalibrated for reading contrast, and editable fields,
choice marks and outline buttons draw the strongercontrol.border. Input
placeholders usetext_muted(an overridableplaceholderpart) instead of a
fixed 55% opacity. - SplitPane's default grip is an animated bar that grows from 24px to 40px. A
drag is cancelled only when a committed change alters its interaction
contract, not by unrelated rerenders. gpui-rhai checknow resolves calls into the application's own modules
(#184). It used to fail on any application with modules of its own, so
running it may now report real findings it never reached before.gpui-rhai embedincludes the application's own modules; regenerate the
embedded module if the entry imports any.
Hosts (Rust)
Additions, besides the owner field above:
ScrollbarSource,ScrollbarContext,ScrollbarSourceHandleand source
adapters for Host scroll surfaces;VirtualCollectionScrollHandleshares the
tail-following state.NavigateBackandScriptViewHost::navigate_back.WallClock/WallClockSource, time zones andTimeRangevalues, without
the charts feature.NativeTextDocumentregistration on mounted views, the native Markdown
viewer and a Host-ownedMarkdownPreparationCache
(document_runtime_config().set_markdown_cache(...),
markdown_preparation_stats()).
Scripts
- Tabs
keep_mounted,patterns/navigation_stackandctx.is_visible(). ColumnBrowser,patterns/composer,SelectableCardGroup, Textarea
submit_key/on_submitandctx.copy_text.Calendar,TimeInput,TimeRangePickerandTimeline.MarkdownViewer.- Collections'
follow_tailandon_follow_change. - SplitPane
handle,grip,handle_renderer,ctx.element_size,
ctx.node_factoryandrender_node.
Known gaps
gpui-rhai 0.2.2
gpui-rhai 0.2.2
Release date: 2026-10-13. A compatible patch release of 0.2: Runtime API stays
3, gpui-pre/core/platform stay 0.3.7 and MSRV stays 1.95. Upgrade core,
registry and CLI together.
It adds Toast's stacked layout, a Host hook for the Rhai engine and a focus
event for scripts, and fixes what applications met on 0.2.1; the full list is
in the CHANGELOG.
Upgrading from 0.2.1
Copied official components
gpui-rhai update brings components/toast, now at 0.2.2. It merges it with
local edits as usual and writes conflicts to .gpui-rhai/conflicts/.
Behaviour to check
style().hidden()on aroworcolumnnow hides it. Before, the node kept
its room and paint; check any row or column that was hidden and still meant
to show.- Toast close buttons carry a 1px transparent border for the focus frame and
round their hover fill withradius.sm. - Messages of a
one_ofvalidation failure name the path inside each variant,
and capability errors print the capability id plainly; code that matched the
old text needs the new one.
Hosts (Rust)
All additions; nothing existing changes signature:
ScriptViewExtension::configure_rhai_engine(&self, &mut rhai::Engine), with
a default that does nothing, and thegpui_rhai::rhaire-export (#151); see
the user guide
for disablingevaland replacing every overload ofsleepandexit.impl Display for CapabilityId.
Scripts
node.on_focus_within_change(Fn)andnode.on_focus_within_value(Fn, value);
see the component authoring guide.- Toast props
layout,heading,clear_labelandon_activate, the
activateevent and thestack_*parts; see
Toast.
Known gaps
- #156 (absolute content in a SplitPane stage) does not reproduce on 0.2.1 or
0.2.2; the issue stays open for the reporter to recheck.
gpui-rhai 0.2.1
gpui-rhai 0.2.1
Release date: 2026-10-12. A compatible patch release of 0.2: Runtime API stays 3,
gpui-pre/core/platform stay 0.3.7 and MSRV stays 1.95. Upgrade core, registry
and CLI together.
It fixes what applications met after moving to 0.2.0 and adds small APIs; the
full list is in the CHANGELOG.
Upgrading from 0.2.0
Copied official components
gpui-rhai update brings the eleven modules that changed, now at 0.2.1:
components/code_viewer, components/combobox, components/command,
components/command_dialog, components/list, components/menu,
components/resizable, components/tab_bar, components/table,
layouts/region and patterns/data_view. It merges them with local edits as
usual and writes conflicts to .gpui-rhai/conflicts/.
The token base
ui/tokens.rhai belongs to the application, and update does not overwrite an
existing one. To keep text at 4.5:1 on the facet of a Tag and on selected Table
and List rows (#134), change two derived colors in it:
// before
"tag.facet": mix(theme_color("surface_hover"), theme_color("text_primary"), 0.14),
"table.selection": mix(theme_color("surface"), theme_color("accent"), 0.28),
// after
"tag.facet": readable(mix(theme_color("surface_hover"), theme_color("text_primary"), 0.14),
theme_color("surface_hover"), theme_color("text_primary"), 4.5),
"table.selection": readable(mix(theme_color("surface"), theme_color("accent"), 0.28),
theme_color("surface"), theme_color("text_primary"), 4.5),Hosts that load gpui_rhai_registry::TOKEN_BASE_SOURCE get it with the crate.
Among the bundled themes only Everforest and Gruvbox change.
Behaviour to check
- Table group headers show the group value in its own case (#146). Styles do
not change case; a caller that wants uppercase headers uppercases the values
it groups by. - Selected List rows draw secondary and meta text in
text_primary. - The panels of Menu, ContextMenu, Select, Combobox, Command and CommandDialog
pad every side by half ofradius.lg(D69). In the square style they lose
the 4px above and below their rows, so an open list is 8px shorter (Command
4px); in the subtle and round styles every side is inset by 2px or 4px. - Region's footer wraps and clips at the region's edge (#137); a footer that was
laid out to run past the region now takes a second line. - Line breaks in text typed or composed into a single-line field become
spaces, and input that is only line breaks is ignored. On macOS Return never
reached the field as text, so Enter behaves as before. - Combobox emits
submitonly when the caller passeson_submit; without it
Enter behaves as before.
Hosts (Rust)
All additions; nothing existing changes signature:
ScriptViewHost::open_overlays()andScriptViewHost::dismiss_escape(window, cx),
with the newOpenOverlay(#147); see embedding.AsyncCapabilityHandler::start_with,SubscriptionCapabilityHandler::subscribe_with,
CapabilityRegistry::start_task_withandstart_subscription_with(#133);
see capabilities.HostFunction,HostFunctionErrorandRuntimeEngine::lint_known_calls_with_host
(#141).gpui_rhai_cli::CheckReportgainshost_extensions: code that builds or fully
destructures aCheckReportwith a struct literal needs the field.
Projects with Host extensions
gpui-rhai check reports calls to functions and primitives the Host registers.
Declare them in ui/app.toml so check treats them as known (#141):
[host]
functions = ["open_file/1", "omb::status/0"]
primitives = ["OmbChart"]gpui_rhai::HostSlot needs no declaration.
Known gaps
- Focus visibility: a focused List with a selection mode and nothing selected
shows no frame; a ContextMenu trigger area without its own control is a tab
stop with no frame; an AppShell region is a tab stop before its controls. - Resizable's corner grips show the default cursor: styles have no diagonal
resize cursor yet. - Toast's stacked presentation (#148) is planned for 0.3.
- An Input keeps painting its selection after focus moves to another field.
- Window drag areas do nothing on Windows: GPUI's Windows platform implements
neither the window move nor the title-bar double press they call, so a Rhai
title bar there neither moves nor zooms the window. - On Linux a span's typography role uses the role's first font family only;
whether a missingcodefamily falls back to DejaVu Sans Mono there is
unverified. - IME preedit and VoiceOver are checked by hand.