Consume the pending changesets into a minor release. Users get the
terminal renderer, a headless Dialog with an Escape layer stack, default
Tab focus navigation, focus-visible styles and a headless Button,
createPortal, onLayout and onScrollChange, native scrollbars, new CSS
styles, registerFont, BGRA image uploads, getElementsAt, idle GC,
display-link frame pacing on macOS and Windows ARM64 builds.
Bump the native Cargo crate, napi loader version checks, native, React
and Solid packages to 0.11.0, @gpuix/cli to 0.1.2 and disktree to 0.2.1.
Fixes#73Fixes#74Fixes#75Fixes#76Fixes#77Fixes#78Fixes#84Fixes#91Fixes#93Fixes#95Fixes#96Fixes#97Fixes#98Fixes#99
Discord: https://discord.com/channels/1422625037164351591/1562091690376962111
Session: ses_ea68cd52affex3PxfjqF4PhYyY
Track visited element IDs during each locator collection and skip subtrees
already traversed. Preserve depth-first tree order and strict descendant
matching while preventing duplicate counts and false ambiguity errors.
Add a real native-tree regression covering count, all, element, waitFor,
multiple nested selector levels, sibling ordering, and genuine ambiguity.
Document the locator result contract and add the native patch changeset.
Validation: all seven automation tests pass against the release addon.
Fixes#99
Session: ses_ea76fa70affeF580gmCkDnrEtT
Discord: https://discord.com/channels/1422625037164351591/1562029343889428490
Install the selection listener before reading and delivering the initial
value so a callback can synchronously change the selection without losing
the notification. If initialization throws, unsubscribe before propagating
the original exception.
Exercise the public helper with a real EventEmitter-backed selection host.
Cover reentrant changes, clears to null, explicit unsubscribe, and cleanup
when the initial read or callback fails. Add API docs and a patch changeset.
Validation: shared runtime tests pass, including the new lifecycle flow.
Fixes#98
Session: ses_ea76fa70affeF580gmCkDnrEtT
Discord: https://discord.com/channels/1422625037164351591/1562029343889428490
Return a fresh fallback window size instead of exposing the shared default.
Copy safe-area, IME, and effective edges independently on every inset read,
including values supplied by custom hosts that reuse their own objects.
Add regressions for null and throwing hosts, mutation between reads, shared
nested edge inputs, renderer-owned values, and derived keyboard geometry.
Document snapshot ownership and add the native patch changeset.
Validation: shared host runtime tests and adapter builds pass.
Fixes#97
Session: ses_ea76fa70affeF580gmCkDnrEtT
Discord: https://discord.com/channels/1422625037164351591/1562029343889428490
Lowercase the complete text and query consistently so Greek final sigma
matches exact uppercase words. Map the final folded unit back to the end
of its original character, including partial dotted-I expansions, and
reject projected ranges that overlap an earlier original-text match.
Treat every Unicode mark category as part of a word in both JS and Rust.
Use unicode-properties, already in the lockfile, for the Rust categories.
Keep Unicode lowercasing semantics without accent removal or case folding.
Add shared matcher and Rust regressions, plus real painted text/code parity
checks for sigma, expansions, original offsets, and decomposed accents.
Update the README contract and the pending native search changeset.
Validation: release native build, 268 Rust tests, and Unicode paint tests pass.
The full React suite has one separate display-link wake timeout.
Fixes#95Fixes#96
Session: ses_ea76fa70affeF580gmCkDnrEtT
Discord: https://discord.com/channels/1422625037164351591/1562029343889428490
Bumps zed to 8152a6a875 (gpui_terminal: send frames inline when shared
memory is not reachable).
At window open gpui_terminal sends a one pixel `a=q,t=s` query and `CSI c`.
If the terminal cannot open our shared memory (SSH, a container), every
frame goes inline: zlib, base64, 4096 byte chunks, then an `a=q` fence. At
most two frames wait for their fence, so SSH buffers cannot queue seconds of
stale frames.
- README: "SSH works" replaces "Local only", with an `ssh -t` example
- changeset: terminal entry mentions the inline fallback
- docs/terminal-renderer.md: probe, inline format, fence gate, sizes, harness
`REMOTE=1` mode and the Docker cross-namespace test
- Cargo.lock: gpui_terminal depends on flate2
Discord: https://discord.com/channels/1422625037164351591/1562061946524532766
Session: ses_ea6f90cc0ffej0S2RFLCkQxS5W
`render(<App />, { terminal: true })` or `GPUIX_TERMINAL=1` runs the same
GpuixView on gpui_terminal, a GPUI platform in the Zed fork that paints with
the kitty graphics protocol. No native window opens.
```
macOS Linux
renderer.init ─► init_macos renderer.init ─► init_threaded
EmbeddedPlatform::Terminal gpuix-ui thread
TerminalPlatform::new_embedded() gpui_terminal::application().run()
tick() ─► pump_events()
```
- renderer.rs: `terminal_requested()` reads the option and the env var;
macOS swaps MacPlatform for TerminalPlatform inside the embedded path, so
tick() and the frame wake hook are unchanged. No per-method branches
- input.rs: `init_terminal` unbinds ctrl-c in text field contexts so it
reaches the platform and quits
- `isTerminal()` exported from @gpuix/native, @gpuix/react and @gpuix/solid
as a plain function (not a hook); browser.mjs returns false
- examples/chat.tsx, mail.tsx drop the traffic-light gap in a terminal
- AGENTS.md: terminal mode swaps the platform; no hooks for non-reactive values
- README: "Run in a terminal" section and the `terminal` option row. The same
README hunk also carries a note on `windowBackground: "blurred"` tinting
- docs/terminal-renderer.md: architecture, traps, research notes
- zed submodule: gpuix-terminal commits (gpui_terminal crate, embedded mode,
SIGWINCH-only forwarding)
- changeset (minor for native, react, solid)
Discord: https://discord.com/channels/1422625037164351591/1562055543466496043
Session: ses_ea7105a47ffeNLpB02sEyo5t9g
Linux apps silently opened no window under X11: the addon enabled the
gpui_platform backends but not gpui's own `wayland` and `x11` features, so
compositor selection was compiled out, also in release builds without
`test-support`.
- packages/native/Cargo.toml: gpui gets `wayland` and `x11`
- README: how GPUI picks Wayland or X11 (`WAYLAND_DISPLAY`, `DISPLAY`) and how
to force X11 under XWayland
- changeset (patch), Fixes#93
Upstream search: zed-industries/zed#56322 fixes the inverse mismatch in GPUI
examples, not this addon.
Session: ses_eb158e141ffeMMK5qeppygyhzs
Wake JavaScript through a weak, coalesced N-API callback after GPUI queues its display frame. Keep tick non-blocking and replace the independent 8 ms input timer with a 100 ms no-wake fallback. Explicit frameMs retains timer polling; Windows and Linux keep their existing loop.
Clear callbacks and timers on stop, make stop idempotent, and release the hook when the renderer drops. Exercise real display wakes through the shared runtime on Bun and Node, plus hidden-window fallback and replacement-loop cleanup. Document the latency and idle-CPU limits for both adapters.
Fixes#91
Session: ses_ec38cf159ffe0vKPJHE10v0gl6
Discord: https://discord.com/channels/1422625037164351591/1560050965288194129
Paint reports and element observers use Rc on Windows and Linux as well as macOS and wasm. Remove the obsolete platform gate that caused Windows ARM64, Windows x64, and Linux CI compilation to fail.
Session: ses_ed48f90d4ffeqym03XRjqsA5xw
Discord: https://discord.com/channels/1422625037164351591/1558853977012174980
Build aarch64-pc-windows-msvc on windows-11-arm with Visual Studio ARM64 Clang for ring. Compile the matching bun-windows-arm64 chat executable. Run the existing Windows binding, renderer, React, Solid, and example checks on both architectures with matching Node installations and artifacts. Publishing waits for both Windows test jobs.
Align napi.targets, README downloads, and the architecture guide with the release matrix. Replace the pending source-build-only changeset with the final ARM64 support entry for issues 73 and 78.
Validation: actionlint passes; build/package/test targets match; napi recognizes the ARM64 package; native TypeScript build and 15 package tests pass. Hosted ARM64 CI has not run. lintcn reports one pre-existing error in untouched js/idle-gc.ts.
Session: ses_ed48f90d4ffeqym03XRjqsA5xw
Discord: https://discord.com/channels/1422625037164351591/1558853977012174980
Document ARM64 MSVC, Windows SDK, Clang, CMake, and matching Node.js/Bun runtimes. Include PowerShell commands to locate Visual Studio LLVM and build the local native addon and adapters. State explicitly that published Windows packages and CI remain x64-only.
Use ring build documentation for its Windows ARM64 Clang requirement and validate the linked documentation. Add the patch changeset for issue 73. PowerShell commands were not executed on a Windows host.
Session: ses_ed48f90d4ffeqym03XRjqsA5xw
Discord: https://discord.com/channels/1422625037164351591/1558853977012174980
Reveal disktree selections through Finder, Explorer, or xdg-open instead of always invoking macOS open. Keep launcher errors in the selection panel and scope them to the selected path. Preserve Explorer selection arguments for paths with spaces. Normalize platform button labels in the existing scan/navigation snapshot.
Use the existing titlebarTransparent option to retain native Windows dragging and minimize/maximize/close controls in chat and disktree. Leave other platforms unchanged. Document both behaviors and add separate changesets for issues 75 and 77.
Validation: all package, Rust, and performance suites pass on macOS. A real missing-launcher probe leaves the selection panel responsive. Windows and Linux runtime behavior remains unverified locally.
Session: ses_ed48f90d4ffeqym03XRjqsA5xw
Discord: https://discord.com/channels/1422625037164351591/1558853977012174980
Replace the Unix-only path regex with the exact path formatted by the same displayPath helper as the selection panel. Keep the existing end-to-end scan and navigation snapshot unchanged. Add a patch changeset referencing issue 76.
Session: ses_ed48f90d4ffeqym03XRjqsA5xw
Discord: https://discord.com/channels/1422625037164351591/1558853977012174980
Use Node file permissions for the disktree and scaffolder executables on Unix. Skip permission changes on Windows, where a shell chmod command is not available. Document the portable build behavior and add a patch changeset for both packages.
Session: ses_ed48f90d4ffeqym03XRjqsA5xw
Discord: https://discord.com/channels/1422625037164351591/1558853977012174980
The list ignored left and right padding, so rows spanned its full width. Rows now sit inside all four sides, and a scrollbarColor thumb stays at the list's own edge, past the padding. Moves the zed submodule to the commit that lays rows out inside horizontal padding.
A padded box around a list is better served by padding on the list itself,
with the thumb at the list's own edge. The list now takes padding on all
four sides.
Each render() call wrapped the tree in a Provider whose value was a new
{ renderer } object, so every call was a context change. In the chat example
that re-rendered ChatApp, whose inline onRetry closure then busted the memo on
Transcript: 305 SafeMdxContent renders and about 9 ms per find-bar keystroke.
The example also keeps onRetry stable now, so typing in the composer no longer
re-renders every row.
Keep event and custom-prop maps in one optional box and release it when empty. On a 75k-element real ChatApp tree, live Rust heap falls from 44.5 MB to 33.6 MB while the 247 native tests and adapter suites stay green. Document the measured scope and the incomplete dense-tree prototype.
Session: ses_f1be8a987ffeslJ1dooQ4pgDnx
Allocate initial child lists only for parents and drop them after materialization. A real 5k-turn chat retains about 2 MiB less JavaScript heap. React lifecycle and integration suites pass.
Session: ses_f1be8a987ffeslJ1dooQ4pgDnx
Separate folder and file node shapes so retained file records carry no directory-only child arrays or metadata. The 350k-file scan kept about 8 MiB less Bun heap. Preserve navigation and scan totals through the existing app test.
Session: ses_f1be8a987ffeslJ1dooQ4pgDnx
Before, any `focusVisible` style (even `{}`) returned early from
apply_focus_visible, so one key controlled two states: the look of the
element while it has keyboard focus, and whether it dims while another
control has it. A custom focus ring therefore could not dim.
Now the two are separate:
- `focusVisible` only styles the element while it has keyboard focus
- every other focusable element off the focus path dims to 40%, with or
without a `focusVisible`
- new style key `keyboardFocusDim: false` keeps one element at full opacity
(StyleDesc.keyboard_focus_dim, not inherited, excluded from state styles)
- `Select.Content` and `Dialog.Popup` (React and Solid) switch from
`focusVisible: {}` to `keyboardFocusDim: false`
- the window-wide `render({ keyboardFocusDim: false })` option is unchanged
Test: the focus scene now checks that `keyboardFocusDim: false` opts an
element out and that an element with a focusVisible ring still dims.
README Focus styles and the pending focus changeset describe the new rule.
Session: ses_f231682b0ffe2fr5xaNLPPWQre
GPUI stores color images as BGRA (RenderImage, Metal and DirectX atlases).
setImagePixels only took RGBA, so every upload swapped bytes 0 and 2 of
every pixel on the UI thread. BGRA sources (ffmpeg -pix_fmt bgra,
VideoToolbox, node-canvas toBuffer('raw')) now skip that pass.
- native: PixelFormat enum in img.rs; napi, test renderer, UiCommand and
wasm bindings take an optional format. Unknown formats throw
- host types: ImagePixelFormat, ImagePixelsOptions
- react/solid: setImagePixels(w, h, pixels, { format })
- waveform example writes BGRA by hand
- README: format docs, node-canvas raw is BGRA premultiplied (opaque only)
- test: rgba and bgra of the same color paint byte-identical screenshots,
misread bytes swap red/blue, unknown format throws
Session: ses_f230bc6fdffe5CqhxBXqCeacz9
`render(<App />, { keyboardFocusDim: false })` (React and Solid) and
`createTestRoot({ keyboardFocusDim: false })` (React) stop GPUIX from dimming
every other focusable element while a control has keyboard focus. Apps then
draw their own focus look with `focusVisible`.
How it reaches native:
- new batch op `["setKeyboardFocusDim", bool]`, parsed in apply_batch_to_tree,
so macOS, Windows/Linux, web and the test renderer share one path with no
per-backend method and no pending state while the web window boots
- stored as RetainedTree.keyboard_focus_dim (default true)
- keyboard_focus_path() returns None when it is off, so apply_focus_visible
never dims
- each root enqueues the op on creation (React createRoot, Solid attach), so
a bun --hot remount without the option restores the default
Docs: README Focus styles section documents the option and the 8px pointer
slop from the GPUI bump; the pending focus changeset gets both bullets.
Test: the styles.test.tsx focus scene with keyboardFocusDim: false paints the
same frame as idle after Tab.
Session: ses_f231682b0ffe2fr5xaNLPPWQre
Instead of marking the focused control, dim the rest: while a control has
keyboard focus (last input keyboard, focused handle is not an input or
textarea), every other focusable element renders at 40% of its own
opacity. The focused one stands out, and every Tab target is visible.
- GpuixView::keyboard_focus_path computes the focused control and its
ancestors once per render; BuildCtx and CustomRenderContext carry it.
Ancestors stay undimmed because gpui opacity covers the subtree.
- A focused text field dims nothing: typing is keyboard input too.
- An element with its own focusVisible (even {}) is never dimmed.
- GPUI refreshes the window on focus and modality change, so the path is
recomputed without any extra wiring.
Session: ses_f2717eddcffeXQkI5JXN8eAnFD
Remove the default focusVisible ring and the inherited focusRingColor
style. Without its own focusVisible:
- a focusable control (Button, tabIndex div) renders at 70% of its own
opacity while keyboard-focused, which shows where Tab went without a
ring (FOCUS_VISIBLE_DIM, paint only)
- <input> / <textarea> draw nothing; the caret already shows focus
apply_focus_visible takes a FocusVisibleDefault (Dim | None) instead of
an inherited ring. outlineWidth/Color/Offset stay for apps that want a
ring via focusVisible.
Session: ses_f2717eddcffeXQkI5JXN8eAnFD
<input>/<textarea> track the same focus handle on the editor wrapper and
on the inner text element. GPUI's TabStopMap inserted a second order node
for the repeat, so prev() from the field returned the field itself and
Shift+Tab did nothing (Tab worked, next() skips both).
Fixed in the fork (upstream tab_stop.rs has the same code): a repeated
handle is recorded for replay but adds no second node, with a unit test.
- zed: c0d597f -> 672c259 (gpui: count a focus handle tracked twice as one tab stop)
- regression test: Shift+Tab / Tab around a textarea
Session: ses_f2717eddcffeXQkI5JXN8eAnFD