Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
cd76744
perf(react): skip unchanged style and custom prop updates
rubnogueira Oct 10, 2026
b5789d9
perf(react): diff event handlers over the props an element has
rubnogueira Oct 10, 2026
d47fb1d
feat(react): createPortal mounts children into an element
rubnogueira Oct 10, 2026
782607b
perf(native): repaint only when a batch changed something
rubnogueira Oct 10, 2026
d8cf51a
perf(native): track host bounds from paint, not an extra child node
rubnogueira Oct 10, 2026
ef14339
test: benchmark a list that re-renders with nothing visibly changed
rubnogueira Oct 10, 2026
ab37d93
build(react): move to react-reconciler 0.33 and wait on AnimatePresen…
rubnogueira Oct 10, 2026
a650681
feat(native): aspectRatio, dashed borders, shadow lists, inset shadow…
rubnogueira Oct 10, 2026
f10a6c1
test(examples): run perf budgets against a production React and add a…
rubnogueira Oct 10, 2026
f3eede6
feat(native): registerFont loads a font that fontFamily can name
rubnogueira Oct 10, 2026
c7dd862
feat(native): onLayout reports an element's size when it changes
rubnogueira Oct 10, 2026
5ab017a
feat(native): onScrollChange reports every scroll of a scroll container
rubnogueira Oct 10, 2026
d8fd814
fix(native): hover ends when the pointer leaves the window
rubnogueira Oct 10, 2026
720e975
perf(native): focus sync visits only the focusable elements
rubnogueira Oct 10, 2026
82390c4
test(examples): profile-chat takes a QUERY override
rubnogueira Oct 10, 2026
5d86e4f
feat(native): getElementsAt lists the elements under a point
rubnogueira Oct 10, 2026
4f62248
feat(native): collectGarbageWhenIdle collects once the app has stoppe…
rubnogueira Oct 10, 2026
d789ba4
feat(native): scrollbarColor paints a native scrollbar thumb
rubnogueira Oct 10, 2026
be82b36
perf(native): compile the highlight query once per keystroke
rubnogueira Oct 10, 2026
a064295
feat(native): scrollbarColor draws a thumb on a virtual list
rubnogueira Oct 10, 2026
8a75170
feat(native): a box around a virtual list draws the list's thumb at i…
rubnogueira Oct 10, 2026
90c6ad0
feat(native): a list's thumb sizes unmeasured rows from the measured …
rubnogueira Oct 10, 2026
239f16d
perf(react): root.render() keeps one context value so useGpuix consum…
rubnogueira Oct 10, 2026
eb4ef4a
docs: describe the new features without reference to any one app
rubnogueira Oct 11, 2026
f15c044
test(react): read scrollbar pixels in logical units so they pass on a…
rubnogueira Oct 11, 2026
3c991f2
revert(native): drop the box-around-virtual-list thumb
rubnogueira Oct 11, 2026
447ade9
feat(native): padding on a virtual list insets its rows on all four s…
rubnogueira Oct 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/collect-garbage-when-idle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@gpuix/native': patch
'@gpuix/react': patch
---

`collectGarbageWhenIdle()` runs one full garbage collection once the app has stopped committing for a while, so the heap of a long session comes back down after a burst of work. It watches the new `appliedBatchCount()` and the process's CPU, and returns a function that stops it.
5 changes: 5 additions & 0 deletions .changeset/create-portal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gpuix/react': minor
---

Added `createPortal(children, target)`, which mounts children into an element that is already mounted. Take `target` from a `ref`. The portal's children are appended to the target and kept in key order, so a toast layer, a drag preview or an overlay root can live in another part of the tree than the component that renders it. See "Portals" in the README.
5 changes: 5 additions & 0 deletions .changeset/elements-at.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gpuix/native': patch
---

`getElementsAt(x, y)` lists the elements under a window point, topmost first, like the DOM's `elementsFromPoint`. It clips at `overflow: hidden | scroll`, skips `pointer-events: none`, and puts a deferred `<anchored>` layer above the rest. The test renderer has it too.
5 changes: 5 additions & 0 deletions .changeset/focusable-ids.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gpuix/native': patch
---

Every frame no longer walks every element to find the ones that take focus. The tree now keeps that set up to date as props and listeners change, so an idle frame of a tree with thousands of elements costs about a third less CPU. Focus, tab order, `autoFocus`, and `onKeyDown`/`onFocus`/`onBlur` behave as before.
5 changes: 5 additions & 0 deletions .changeset/highlight-search-needle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gpuix/native': patch
---

Typing in a find-in-page query no longer rebuilds the search for every block of text. The query is lowercased and compiled once per keystroke and shared by all the text it is matched against, so each keystroke over a long document costs a few percent less CPU. Matches are the same as before: leftmost, non-overlapping, case-insensitive by default.
5 changes: 5 additions & 0 deletions .changeset/hover-ends-at-window-edge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gpuix/native': patch
---

Leaving the window ends hover. The `hover` style of the element the pointer last touched used to stay painted, and the cursor and tooltips stayed with it, until the pointer came back. `onMouseLeave` already fired. The test renderer gains `simulateMouseExit` and `nativeSimulateMouseExit`.
5 changes: 5 additions & 0 deletions .changeset/invalidate-only-on-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gpuix/native': patch
---

`applyBatch` asks the window to repaint only when the batch changed something a frame would draw. A batch that only resends the style, text, listeners or custom props an element already has no longer rebuilds and re-lays-out the whole element tree for a frame that comes out identical.
13 changes: 13 additions & 0 deletions .changeset/more-css-layout-styles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@gpuix/native': minor
'@gpuix/react': minor
'@gpuix/solid': minor
---

New styles for what gpui already draws and GPUIX did not expose:

- `aspectRatio` sizes the axis that has no definite size.
- `borderStyle: "dashed"` draws every border side dashed.
- `boxShadow` accepts an array of shadows, and each shadow takes `inset: true`.
- `alignItems` accepts `"stretch"` and `"baseline"`, which `alignSelf` already did.
- `justifyContent` accepts `"space-evenly"`, which `alignContent` already did.
5 changes: 5 additions & 0 deletions .changeset/native-scrollbars.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gpuix/native': patch
---

`scrollbarColor` and `scrollbarWidth` paint a native scrollbar thumb over a container with `overflow: "scroll"`, on each axis that has more to show. The thumb follows the scroll offset in the frame it paints, with no JavaScript in the loop and no extra frame, and it takes no pointer input.
9 changes: 9 additions & 0 deletions .changeset/on-layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@gpuix/native': minor
'@gpuix/react': minor
'@gpuix/solid': minor
---

`onLayout` on `<div>` and `<text>` reports the element's border box after its first paint and each time its size changes, with no polling of `getElementBounds`. The event payload gains `width` and `height`.

`visibility: "hidden"` elements have never reported bounds, because gpui paints nothing for them and nothing inside them. The canvas fallback that claimed otherwise could not run and is gone.
7 changes: 7 additions & 0 deletions .changeset/on-scroll-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@gpuix/native': minor
'@gpuix/react': minor
'@gpuix/solid': minor
---

`onScrollChange` reports the scroll offset of an `overflow: "scroll"` element after it moves, whatever moved it: the wheel, a scrollbar drag, `scrollTo` or `scrollIntoView`. `onScroll` still reports only wheel input. The event payload gains `scrollX` and `scrollY`.
5 changes: 5 additions & 0 deletions .changeset/react-reconciler-0-33.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gpuix/react': minor
---

`@gpuix/react` now uses `react-reconciler@^0.33.0` and peers on `react@^19.2.0`. React 19.2 pairs with reconciler 0.33, and the old `^0.31.0` pin forced every app on 19.2 to override it. The `^18.0.0` half of the old peer range was never true, since reconciler 0.31 already required React 19.
7 changes: 7 additions & 0 deletions .changeset/register-font.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@gpuix/native': minor
'@gpuix/react': minor
'@gpuix/solid': minor
---

`registerFont(bytes)` loads a TrueType or OpenType font so `fontFamily` can name it. Text that asked for the family before it was registered picks the font up on the next frame. Fonts GPUI would abort on, or cannot use, are rejected with an error instead.
5 changes: 5 additions & 0 deletions .changeset/root-context-stable-value.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gpuix/react': patch
---

`root.render()` no longer re-renders every `useGpuix()` consumer. The root's context value was a new object on each call, so calling `render()` again (a hot reload, a test, an app that renders the root with new props) counted as a context change and re-rendered each consumer and, through its new callbacks, every `memo` row below it. In the chat example this was 305 row renders and about 9 ms per keystroke of a find bar; it is now 0.2 ms.
11 changes: 11 additions & 0 deletions .changeset/skip-unchanged-style-updates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@gpuix/react': patch
'@gpuix/native': patch
'@gpuix/solid': patch
---

A re-render no longer resends styles and custom props that did not change. React calls `commitUpdate` for every element a parent re-rendered, and the reconciler used to serialize and send each one's `style` every time, which cost a JSON round trip and a style intern per element per commit. It now compares values, so `style={{ ... }}` rebuilt with the same contents sends nothing. The custom prop diff is linear instead of quadratic in the number of props, and the event handler diff walks the props an element has instead of every event GPUIX knows.

A style change on an element React has hidden (Suspense, Activity) no longer makes it visible again: it keeps `visibility: hidden` until React unhides it.

`@gpuix/native/host` exports `sameMutationValue(left, right)` and `EVENT_TYPE_BY_PROP` for adapters that diff the same payloads.
7 changes: 7 additions & 0 deletions .changeset/track-bounds-without-a-child.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@gpuix/native': patch
---

`<div>` and `<text>` no longer add a hidden child element to record their bounds. Each host element used to carry an extra `canvas` node, which doubled the nodes gpui laid out, prepainted and painted every frame. The bounds now come from the element's own paint, so a frame over the same tree is about 15% cheaper. An element with `visibility: hidden` keeps the child, because gpui does not paint a hidden element and its layout box must stay readable.

`getElementBounds()`, automation locators and text selection regions now use the element's border box. They used the padding box before, which was narrower by the border width on an element with a `borderWidth`.
5 changes: 5 additions & 0 deletions .changeset/virtual-list-horizontal-padding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gpuix/native': patch
---

`padding` on a `<virtual-list>` now insets its rows on the left and right as well as the top and bottom. Rows used to span the list's full width and ignore left and right padding. A `scrollbarColor` thumb stays at the list's own edge, past the padding.
5 changes: 5 additions & 0 deletions .changeset/virtual-list-scrollbar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gpuix/native': patch
---

`scrollbarColor` and `scrollbarWidth` now also draw a thumb on a `<virtual-list>`. It is painted over the list in the frame the list scrolls, from the heights the list has measured, so it never lags the scroll position and costs no frame of its own. The list's layout is unchanged.
5 changes: 5 additions & 0 deletions .changeset/virtual-list-thumb-averages-rows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gpuix/native': patch
---

The thumb of a `<virtual-list>` counts the rows the list has not measured yet as the average measured row, not as `estimatedItemHeight`. A list of tall rows with a short estimate no longer draws a thumb several times too long that shrinks as rows scroll into view; the thumb is right once a few rows have been measured.
Loading