Commit Graph
3382 Commits
Author SHA1 Message Date
bf560a4572 gpui: Make the Wayland render loop demand-driven (#60690)
Closes #55345.

Wayland is the only platform whose frame ticks are conditional: a
wl_surface frame callback only arrives after a commit the compositor
goes on to repaint.

GPUI assumed unconditional ticks (any previously our Wayland backend
faked them), so an idle fullscreen window
stopped receiving callbacks and froze until external damage arrived.

This PR makes the Wayland render loop demand-driven, instead parking
when there's nothing to draw, and stops Zed committing empty frames on
every tick as the artificial heartbeat from the compositor.

---

Release Notes:

- Fixed the UI freezing in fullscreen on some Wayland compositors
- Fixed idle windows waking at the display's refresh rate on Wayland

---------

Co-authored-by: Philipp Schaffrath <philipp.schaffrath@gmail.com>
Co-authored-by: Daan De Meyer <daan@amutable.com>
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
2026-08-21 09:43:16 +00:00
Anthony Eid b559ad4f26 gpui: Journal foreground work between frames and report hang incidents (#62779)
Zed's hang telemetry could say a hang happened, but not what the app was
doing. This PR adds a foreground journal to gpui (behind the `profiler`
feature): the main thread records task polls, action handlers, input
dispatches, window draws, and frame presentations into a fixed-size
ring, and boundary entries — a newly drawn frame's presentation, or the
foreground going idle — partition that stream into activity intervals.
Recording is designed to be cheap enough to ship enabled: sub-threshold
task polls fold into a counter instead of individual entries, and the
ring is drained in bounded chunks off the main thread.

A `HangDetector` drains the journal and reports an incident for any
interval containing a single event at or above the hang threshold (100ms
in release), or whose cumulative foreground spend reaches a frame budget
(8ms in release) — catching both one long stall and many small pieces of
work that together drop a frame. Incidents serialize with phase
(startup/steady), stall and active durations, busy fraction,
dirty-to-present time, and up to 8 contributors in start order with
nesting depth: task polls carry their spawn location, actions their
names, inputs their kind. Zed batches the top 10 incidents by stall per
30 minutes plus a total incident count into a "Hang Incidents" telemetry
event, flushes the remainder on quit, and drops the old per-location
"Hang Report" histograms this replaces.

Release Notes:

- N/A
2026-08-19 18:17:06 +00:00
Agus Zubiaga 7c46498fe8 gpui: Add split and paint APIs on LineLayout (#60831)
`ShapedLine` is nearly 3KB by value because it inlines a
`SmallVec<[DecorationRun; 32]>`. A consumer that builds wrapped lines
out of many small uniformly styled fragments only needs the shaped
glyphs plus a single decoration run it can track itself, but it ends up
moving and reallocating that mostly dead capacity constantly because
splitting and painting are only reachable through `ShapedLine`.

This moves the glyph-partitioning half of `ShapedLine::split_at` down to
a new `LineLayout::split_at`, and adds
`LineLayout::paint`/`paint_background` methods that take explicit
decoration runs. Together with the already-public `layout_line`, callers
can now hold a bare `Arc<LineLayout>` and their own decorations.
`ShapedLine::split_at` delegates to the new method and behaves the same.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- N/A
2026-08-19 17:40:37 +00:00
Kevin BravoandTom Houlé d7f85eed4e Preserve --user-data-dir across restarts (#62022)
# Objective

Fixes #57701.

When Zed is launched with `--user-data-dir`, a Zed-initiated
restart—such as `workspace: clear trusted worktrees` or Restart to
Update—can relaunch Zed without that argument. The restarted process
then uses the default data directory instead of the directory selected
by the user.

## Solution

- Store the resolved custom data directory as GPUI restart arguments
alongside the existing optional restart executable path.
- Forward those arguments when relaunching on Linux, macOS, and Windows.
- Preserve argument boundaries and native `OsString` values, including
paths containing spaces.
- On Windows updates, carry the arguments through
`auto_update_helper.exe` when it launches the updated `Zed.exe`.

Normal launches without `--user-data-dir` continue to restart without
additional arguments. This does not attempt to preserve unrelated CLI
arguments or general launch state.

## Testing

- `cargo fmt --all -- --check`
- `cargo test -p gpui`
- `cargo test -p auto_update --features workspace/test-support`
- `cargo check -p auto_update_helper --tests --target
x86_64-pc-windows-msvc`
- `./script/clippy -p gpui -p gpui_macos -p auto_update -p zed`
- `./script/clippy -p auto_update_helper --target
x86_64-pc-windows-msvc`

The regression tests cover simultaneous restart executable and argument
forwarding, Windows paths containing spaces and trailing backslashes,
updater-helper argument parsing, and the final `Zed.exe` command.

Compilation and unit tests were run on macOS. Linux and Windows runtime
behavior was reviewed but not run locally. A full `gpui_windows`
cross-target check was attempted, but its `psm` dependency requires the
MSVC `lib.exe` tool, which is not available on this macOS host; the
standalone Windows updater helper and its tests compile successfully for
the Windows target.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards (no UI changes)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- Fixed Zed forgetting an explicitly passed `--user-data-dir` argument
after restarting (e.g. clicking "Restart to Update")

---------

Co-authored-by: Tom Houlé <tom@tomhoule.com>
2026-08-19 14:32:19 +00:00
Gaauwe Rombouts 702a310ea4 gpui: Support image paste and async clipboard reads on web (#62871)
Deliver the DOM paste event as a full ClipboardItem (text and image
files) through a new InputHandler::paste hook instead of inserting only
plain text, and add Platform::read_from_clipboard_async so app-initiated
paste (e.g. a custom context menu) can use the browser's asynchronous,
permission-gated clipboard API. Desktop platforms are unaffected: the
async read defaults to a ready task wrapping the synchronous read, and
the paste hook defaults to plain-text insertion.

Release Notes:
- N/A
2026-08-19 11:56:03 +00:00
Anthony Eid b9e156ba46 Restart interrupted update downloads after system wake (#60366)
Follow-up to #60301, which was reverted in #60321.

I removed the reqwest part of the fix because it crashed. reqwest's
`read_timeout` relies on a tokio timer, but Zed polls HTTP responses on
gpui's executor instead of a tokio runtime, so it panicked when the
response body was awaited. I'll follow up later by patching our reqwest
fork to support the poll behavior we wanted.

This fixes the download being stalled since we restart it when the
system wakes.

I also changed `App::on_system_wake` to return a `gpui::Subscription` to
be consistent with gpui's other on-system-event handlers like
`on_keyboard_layout_change` and `on_thermal_state_change`.

Release Notes:

- Fixed Zed update downloads stalling after the system wakes from sleep
2026-08-19 01:38:41 +00:00
John YaniandKirill Bulatov 9fa09ccc24 Remove deprecated std::usize import from uniform_list.rs (#62841)
<img width="993" height="370" alt="image"
src="https://github.com/user-attachments/assets/15e3a185-3328-4069-ad86-21d29f647f83"
/>

# Objective

- using `std::usize` currently is a warning, but it's a hard fairule in
rust nightly
- gpui-web requires rust nightly
- https://doc.rust-lang.org/std/usize/index.html
 
## Solution

- remove std::usize import, it would be used a primitive type

## Testing

-
https://github.com/iamnbutler/gpui-unofficial/actions/runs/32189970651/job/95882109895#step:8:753

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-18 22:41:54 +00:00
Cameron Mcloughlin be5535ef7b treesitter: Worker-pinned treesitter parsing (#62784)
Bumps the treesitter version to include
https://github.com/tree-sitter/tree-sitter/pull/5851

Also makes some changes to `cx.spawn_dedicated` to make it work on web:
- remove the blocking `recv` on the main thread
- adds a new variant `TaskState::Rendezvous` to allow this code to
synchronously return a task

`TaskState::Rendezvous` is needed because of how
`spawn_dedicated_thread` works:
- the caller provides a callback that produces a non-`Send` `Future` on
the dedicated thread
- the callback is sent to the dedicated thread, executed, and the
resulting `Task` is sent back to the main thread
- this all happens synchronously, so that `spawn_dedicated_thread`
returns a synchronous `Task`

However, the blocking `recv` on the main thread traps on the main worker
in the browser.

This PR replaces that with a call to `recv_async`, but this requires a
new `Rendezvous` variant on `TaskState` which represents "a task that it
still waiting to receive the underlying handle from the dedicated
thread".

It also enables the `flume/spin` feature on the web, which is needed to
avoid a synchronous lock acquisition in `send` (it's replaced with a
spinlock). Note that `send_async` wouldn't work here because it acquires
the same lock, and it's unnecessary because `send` only blocks when the
channel is full, but it's an unbounded channel.

---

Release Notes:

- N/A or Added/Fixed/Improved ...
2026-08-18 16:55:09 +00:00
Ben Kunkle 9d8cbc9980 gpui: Rasterize parsed SVGs at exact sizes (#62770)
# Objective

Allow callers to rasterize parsed SVGs at exact device-pixel dimensions.

## Solution

Add `SvgSize::ExactSize` and accept `SvgSize` in `render_parsed`.
Existing scale-factor calls retain their 2× rasterization behavior.
`SvgSize::Size` continues to preserve the SVG aspect ratio.

Release Notes:

- N/A
2026-08-18 14:13:27 +00:00
Remco Smits e8fcbd476d gpui_macos: Add simple fullscreen mode that covers the notch (#60020)
Closes #60013

# Objective

Right now Zed can go full screen but it does not allow you to fix the
hole screen,
by that I mean that Zed can go behind the notch so you don't have extra
useless room left.

## Solution

You can now use the `fullscreen_mode` = `simple` setting to use the new
simple full screen feature, that lives besides the normal full screen
feature. But allows you to have an option to go 100% full screen without
losing any useless space on your macbook screen.
**Note** this is mostly usefull when you have a macbook that has a notch
whitch is kinda in the way of your work flow.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

## Showcase

**Before**
<img width="5712" height="4284" alt="IMG_0339"
src="https://github.com/user-attachments/assets/9f908ffd-7cef-4999-a454-c80f72c40dc8"
/>

**After** (Note now Zed is behind your notch when using the simple full
screen feature)
<img width="5712" height="4284" alt="IMG_0360"
src="https://github.com/user-attachments/assets/5917ed4d-2a64-4464-a794-bc46fd034521"
/>


---

Release Notes:

- Added support for simple fullscreen mode using the `fullscreen_mode`
setting, set it to `simple` to try it out.
2026-08-18 07:00:34 +00:00
Cameron Mcloughlin 29202c1ab5 gpui: Frame time debug overlay (#62749)
<img width="372" height="207" alt="image"
src="https://github.com/user-attachments/assets/5e44a121-b6f3-4bb8-9f94-5bb7da1c04d2"
/>

New keybinds:
- `ctrl-alt-shift-p` - cycle debug overlay between "off", "minimal",
"full"
- `ctrl-alt-shift-o` - reset stats

---

Release Notes:

- Added: Frame time debug overlay
2026-08-17 22:17:24 +00:00
goring 484d75f0ab gpui: Fix TestWindow panicking on window_handle (#62775)
> **AI disclosure:** I used Claude (Anthropic) to diagnose this, write
the fix and its test, and draft this PR description, after every
headless test in my own app started panicking during a gpui upgrade. I
reviewed the diff and the reasoning below, ran the tests myself, and
understand and take responsibility for the change, per Zed's [AI
Policy](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#ai-policy).

# Objective

- `TestWindow` panics instead of returning an error when asked for its
raw window handle. `HasWindowHandle` and `HasDisplayHandle` both return
`Result<_, HandleError>`, so a window with no C-typed handle can report
that — but `TestWindow` calls `unimplemented!()`. Callers who correctly
write `window_handle().ok()?` still crash, and any headless test whose
window reaches code that asks for the platform handle dies on boot.
- This has two halves, and only the second made it reachable:
- `d4781bc29` ("Add raw window handle implementations to GPUI", #7101,
2024-01-30) added both impls for `TestWindow` as `unimplemented!()`
placeholders while the real platforms got real ones. `TestWindow` was
`pub(crate)` then, so nothing outside gpui could hit it.
- `386174cca` ("gpui: Implement `HasWindowHandle` on `Window`", #24327,
2025-02-06) made `Window` implement both traits by forwarding to
`platform_window`. From then on, any consumer asking a `Window` for its
raw handle reaches `TestWindow` under `TestAppContext`.
- I hit this through
[gpui-component](https://github.com/longbridge/gpui-component), whose
macOS accessibility code calls
`HasWindowHandle::window_handle(window).ok()?` on every window it roots
— correct defensive code that still panics, taking every one of my
headless `#[gpui::test]` tests down at boot.


## Solution

- `TestWindow::window_handle` and `TestWindow::display_handle` now
return `Err(HandleError::NotSupported)` instead of calling
`unimplemented!()`. That variant's documentation describes exactly this
case — "the underlying handle cannot be represented using the types in
this crate".
- Real platform windows are untouched: this only changes what the test
platform reports, so `Ok(...)` on macOS, Linux and Windows is
unaffected. Callers that already handle the error path now get the error
they were written for.

## Testing

- Added `test_window_reports_no_raw_handle_instead_of_panicking` in
`crates/gpui/src/window.rs`, asserting both calls return
`Err(HandleError::NotSupported)`.
- Reverting the fix while keeping the test reproduces the original
panic, so the test pins the behaviour rather than passing vacuously.
- `cargo test -p gpui --lib` from a clean target directory — 220 passed,
0 failed:

```
   Compiling gpui v0.2.2 (/…/zed/crates/gpui)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 5m 25s
     Running unittests src/gpui.rs (target/debug/deps/gpui-463768d254ca0c08)

running 220 tests
...
test window::tests::test_window_reports_no_raw_handle_instead_of_panicking ... ok
...
test result: ok. 220 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.49s
```
- Tested on macOS. The change is confined to
`crates/gpui/src/platform/test/`, which is platform independent, but I
have not built the Linux or Windows platform crates.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

## Showcase

Before — the new test against the current `unimplemented!()`:

```
thread 'window::tests::test_window_reports_no_raw_handle_instead_of_panicking' panicked at
crates/gpui/src/platform/test/window.rs:57:9:
not implemented: Test Windows are not backed by a real platform window

test result: FAILED. 0 passed; 1 failed
```

After — see the full run above.

Release Notes:

- N/A
2026-08-17 21:50:09 +00:00
David Barsky 611adabc57 gpui: Add spring animations; examples (#62778)
# Objective

I wanted spring animations in GPUI for an application built atop of
GPUI.

## Solution

I texted @mikayla-maki about wanting spring animations; we collaborated
in Delta; this change is the co-authored result. We built atop of GPUI's
existing APIs, but this *is* new API surface. The verbiage is borrowed
from SwiftUI's approach.

First, what _are_ spring animations? If you're unfamiliar, they're the
bouncy, physical animations that you'd often see in mobile applications.
As Claude put it to me, "keyframe animation is a recording; a spring is
a simulation". There's a few consequences:
1. The animation API needs to support interrupts/cancelation, or the
application using spring animations will feel broken. You need to cancel
the momentum in a physically realistic way!
2. The API is more complex! GPUI's existing animations are, roughly,
`Fn(progress: f32) -> eased_progress: f32`, but a spring animation is
closer to `Fn(state: SpringState, target: f32, delta_time: f32) ->
SpringState`. Concretely, GPUI landed on the following:

```
pub fn step(
    &self,
    state: SpringState,
    target: f32,
    delta_time: f32,
) -> SpringState;
```

...where `&self` is:
```
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SpringConfig {
    /// The spring stiffness, conventionally written as $k$.
    pub stiffness: f32,
    /// The viscous damping coefficient, conventionally written as $c$.
    pub damping: f32,
    /// The moving mass, conventionally written as $m$.
    pub mass: f32,
}
```

Conceptually, at each frame, GPUI does `let state = spring.step(state,
target, delta_time);`, or as an other LLM summarized:

```
// Ordinary animation:
progress: f32 -> phase: f32

// GPUI spring:
(state: { position, velocity }, target, dt)
    -> new { position, velocity }
    -> typed presentation value
```

---

Release Notes:

- gpui: Added spring animations
2026-08-17 21:43:46 +00:00
Kirill BulatovandMrSubidubi aad8ae4b34 Fix overflowing highlights in the markdown blocks (#62714)
Before:
<img width="1728" height="1084" alt="before"
src="https://github.com/user-attachments/assets/68a33e27-7ee7-42cc-9f1b-53e541ef8d2f"
/>


After:
<img width="1728" height="1084" alt="after"
src="https://github.com/user-attachments/assets/b51df462-6457-44fd-ba83-f13c6129125d"
/>


Release Notes:

- Fixed overflowing highlights in the markdown blocks

---------

Co-authored-by: MrSubidubi <finn@zed.dev>
2026-08-17 19:07:31 +00:00
Pavel Olizko 5b85565d93 gpui: Make inactive frame throttling configurable (#62628)
# Objective

I am building an [app](https://github.com/nolight132/sonora) with GPUI,
and the current animation throttling implementation does not quite meet
its requirements. I needed the window to stay at a smooth framerate when
unfocused to display lyrics, which was not possible at the time without
modifying GPUI’s frame scheduling behavior. I understand that this is
not a problem for Zed, but I believe making this behavior a field in
`WindowOptions` could be beneficial for other programs going forward.

## Solution

Added a field to `WindowOptions` struct which controls the minimum
interval between animation frames while the window is inactive
(documented). Kept the default value GPUI currently uses.

## Testing

- Ran `gpui/src/examples/animation.rs` with different presets - works as
expected. Zed compiles. One thing worth noting: this setting does not
guarantee the window refreshes at this exact interval, as frames are
still paced by the compositor. For granular FPS control, a helper
function is needed. I have decided to keep it simple and use the
existing logic.
- Tested on NixOS x86_64, Niri.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior (the default didn't change,
tests still pass)
- [x] Performance impact has been considered and is acceptable (no
performance impact unless explicitly set)

## Showcase

Before (unfocused):
<img width="1280" height="802" alt="image"
src="https://github.com/user-attachments/assets/e3302c1d-4f4a-4503-96a2-0bf24e28096c"
/>

After (unfocused with `inactive_frame_interval` set to 16ms):
<img width="1450" height="972" alt="image"
src="https://github.com/user-attachments/assets/1183adf1-2da1-42d5-8e97-f4ad7a31b783"
/>

---

Release Notes:

- Added `WindowOptions::inactive_frame_interval` for configuring
animation frame throttling on inactive GPUI windows.
2026-08-17 07:20:20 +00:00
Kirill Bulatov 3a21e04379 Fixed the zoomed agent panel closing unexpectedly on rewind (#62711)
`ThreadView::regenerate` does `thread.rewind()` which de-focused the
message editor mid-flight causing the zoomed panel to disappear.

* workspace: Keep zoomed panels open when window focus is lost

Commit adds a test + tweaks a default, generic, fallback to be more
lenient: instead of focusing the workspace (and hiding all panels right
away), try to fall back to whatever panel open

* gpui: Restore focus to nearest surviving ancestor when focus is lost

Commit tries to push it down to a library level and add a way to fall
back to closest ancestor that's still visible with the focus instead.


Release Notes:

- Fixed the zoomed agent panel closing unexpectedly on rewind
2026-08-16 14:42:24 +00:00
Kunall Banerjee 4155155572 acp: Poll the client connection future on a dedicated thread (#62259)
Some context behind this change: I’ve been meaning to look into this
because I kept running into this crash when working on
[other](https://github.com/zed-industries/zed/issues/61040) fixes.
Everything worked fine in dev builds, right up until you tried to create
a new Agent Thread using an ACP adapter. The crash reports in this
instance were throwing me off: I was seeing different things each time.

So, I had Fable look into this. It did so by binary-searching the
minimum thread stack on which a probe replicating Zed’s exact handler
chain can dispatch one message. Full methodology, probe source, and raw
numbers can be found in [this
gist](https://gist.github.com/yeskunall/2ac2b00f51389d9388d607974f6f8a04).
The results of the probe are as follows:

| SDK | Minimum stack (dev profile) | vs. 512 KiB GCD budget |
|---|---|---|
| 1.3.0 | 409,600–413,696 B | fit, ~100 KiB headroom |
| 2.0.0 | 507,904–512,000 B | entire budget before runtime overhead |

The oversized frames are monomorphized into `agent_servers`, not the SDK
crate -- a `[profile.dev.package]` opt-level override on
`agent-client-protocol` does **not** fix this (see gist), and optimized
builds collapse the frames entirely, which is why only dev builds
crashed. It found that the real signature is `fault_address ==
stack_pointer` on a `com.apple.root.default-qos` thread inside the ACP
dispatch specialization, which I then had it verify across six local
`.ips` reports. It seems in #61570, we pushed the dispatch chain past
the GCD budget, explained further below:

`AcpConnection::stdio` polled the ACP client connection future via
`background_spawn`, which on macOS executes runnables on
[GCD’s](https://developer.apple.com/documentation/DISPATCH) global-queue
workers. Those threads have kernel-fixed, unconfigurable [512 KiB
stacks](https://github.com/apple-oss-distributions/libpthread/blob/42d026df5b07825070f60134b980a1ec2552dfee/kern/kern_internal.h#L154).
In unoptimized builds, the SDK’s chained-handler dispatch needs **~0.5
MiB of stack per inbound message** (again, see linked gist), so the
first message overflows the guard page and takes the process down.

Therefore, this 512 KiB constraint is **macOS-only**. Linux doesn’t use
GCD -- GPUI [spawns its own `std::thread`
workers](https://github.com/zed-industries/zed/blob/2ade98f6a9320b9c7d6e65398fc4c364c367c1bd/crates/gpui_linux/src/linux/dispatcher.rs#L39)
([2 MiB Rust
default](https://github.com/rust-lang/rust/blob/59807616e1fa2540724bfbac14d7976d7e4a3860/library/std/src/sys/thread/unix.rs#L26)).
Windows uses [the OS thread
pool](https://github.com/zed-industries/zed/blob/2ade98f6a9320b9c7d6e65398fc4c364c367c1bd/crates/gpui_windows/src/dispatcher.rs#L68),
which [inherits the executable’s stack
reserve](https://github.com/MicrosoftDocs/sdk-api/blob/4502fff176b3b56beddb6a63c9f980377b11ba9b/sdk-api-src/content/threadpoolapiset/nf-threadpoolapiset-setthreadpoolstackinformation.md?plain=1#L58)
-- [1 MB linker
default](https://github.com/MicrosoftDocs/win32/blob/2eb6588c6703d31599285bbb06563c3d41b57590/desktop-src/ProcThread/thread-stack-size.md?plain=1#L17),
but Zed already bumps it to 8 MiB in
[crates/zed/build.rs:88](https://github.com/zed-industries/zed/blob/2ade98f6a9320b9c7d6e65398fc4c364c367c1bd/crates/zed/build.rs#L88)
(see [TODO
comment](https://github.com/zed-industries/zed/blob/2ade98f6a9320b9c7d6e65398fc4c364c367c1bd/crates/zed/build.rs#L87)).

---

Release Notes:

- N/A
2026-08-15 00:30:00 +00:00
Anthony Eidandzed-zippy[bot] a55d40ac62 gpui: Unify performance tracking under the profiler feature (#62496)
GPUI currently splits performance instrumentation across several Cargo
features and runtime controls. Task profiling, frame-duration
histograms, input-latency histograms, and benchmark frame timing
therefore follow separate code paths despite measuring related parts of
the same UI work.

This PR consolidates those systems under the `profiler` Cargo feature.
The `bench` feature now enables `profiler`, aggregate frame and
input-latency histograms remain active whenever profiling is compiled
in, and `set_trace_enabled` controls whether individual task timings and
per-frame draw and presentation records are retained.

The change also introduces a single per-window profiler that owns the
begin and end state for input dispatch, drawing, and presentation, and
routes window action-handler timing through the existing aggregate
action tracker. Draw and presentation records reuse the same timestamps
and computed intervals as the aggregate histograms, avoiding duplicate
clock reads. Benchmark trace scopes are reference-counted so overlapping
measurements cannot disable tracing while another measurement still
needs it.

This does not change hang detection directly. It establishes the common
profiling foundation needed to correlate slow or delayed frames with
actions and foreground or background tasks. Better attribution should
make it easier to find and prevent responsiveness regressions.

Release Notes:

- N/A

---------

Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-08-14 16:33:25 +00:00
Anthony Eid 03453dae3e gpui: Settle benchmark app state between task iterations (#62587)
Dropped entities are released only inside an update's effect flush, and
releases cascade: one flush drops the entities whose handles are gone,
their drops release further handles and can queue foreground work, and a
later flush collects those. `BenchAppContext` callers that only pump the
executor between iterations therefore saw torn-down state linger in the
entity map until some woken task happened to run an update — in a
downstream benchmark this looked like a per-iteration leak of the whole
app graph (~35 MB per iteration), releasing on an apparently timer-bound
schedule.

This adds `BenchAppContext::settle`, which alternates draining queued
work with GPUI update cycles until the dispatcher reports idle,
mirroring the update cadence production gets for free from frames and
input events. `bench_batched_task` now settles before each iteration's
setup (outside the timed interval), so the previous iteration's state is
fully released and cannot accumulate across a measurement. A new
`ThreadedDispatcher::is_idle` predicate backs the loop's termination and
is covered by a unit test.

Release Notes:

- N/A
2026-08-13 19:14:49 +00:00
Cameron Mcloughlin 4476f5c7f6 gpui: Allow setting max FPS for Animation (#62579)
title

---

Release Notes:

- N/A or Added/Fixed/Improved ...
2026-08-13 13:16:33 +00:00
8a52ec4b66 gpui: Add binary data support to Svg element (#52319)
## Context

Add support for binary SVG data in the `Svg` component in the `gpui`
crate. The component now accepts raw SVG bytes via a new `.data()`
method, in addition to the existing `.path()` and `.external_path()`
methods.

**Example usage:**
```rust
svg().data(svg_bytes).color(colors::WHITE)
```

This solves the problem of unnecessary asset bundling and enables Rust's
dead code elimination to optimize binary size.

**Related issue:** Closes #52315

## How to Review

- Review `crates/gpui/src/elements/svg.rs` - the changes are localized
to this file
- Focus on: API design, caching strategy (hash-based virtual path),
priority ordering in paint()
- Verify the implementation properly integrates with existing
`SvgRenderer` and `Window::paint_svg`

## Self-Review Checklist

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (none in this change)
- [x] The content is consistent with the UI/UX checklist (trivial UI
change)
- [x] Tests cover the new/changed behavior (manual testing may be needed
for SVG rendering)
- [x] Performance impact has been considered (hash calculation is
minimal; caching prevents redundant work)

## Technical Details

The implementation:
- Adds `data: Option<Arc<[u8]>>` and `data_path: Option<SharedString>`
fields to `Svg`
- Adds `pub fn data(mut self, data: &[u8]>) -> Self` builder method
- Generates deterministic virtual path from SHA256-like hash:
`__binary_svg__{hash}`
- `paint()` checks sources in priority order: binary data >
external_path > path
- Uses existing `Window::paint_svg(..., data: Some(&bytes))` path

This is a minimal, non-breaking change that leverages existing
infrastructure.

Release Notes:

- Added `.data()` method to `Svg` element to accept raw SVG bytes

Co-authored-by: Lukas Wirth <lukas@zed.dev>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-12 18:55:51 +00:00
Tim Vermeulen 63d4e29a12 markdown: Auto-size table columns to content width (#61773)
Closes https://github.com/zed-industries/zed/issues/62498
Fixes #50044:

| before | after |
| --- | --- |
| <img width="1260" height="1449" alt="before"
src="https://github.com/user-attachments/assets/84dfca5a-66da-4179-90f3-8408255346d4"
/> | <img width="1260" height="1449" alt="after"
src="https://github.com/user-attachments/assets/d63f9128-1397-4f1a-87a5-4e681e9b65f0"
/> |

~Submitted as a draft PR because it relies on
https://github.com/DioxusLabs/taffy/pull/1001. I temporarily pinned our
`taffy` dependency to that particular branch to make the new tests
pass.~ That fix got released!

This fix is similar to the original fix in #50839 which got partially
reverted by #52864 due to a regression, and
`test_table_never_renders_past_its_available_width` ensures this doesn't
regress again.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- Markdown table columns are now sized based on their content.
2026-08-12 17:33:28 +00:00
Richard Feldman bf59de563e gpui: Add per-window frame-duration and present-interval histograms (#61871)
GPUI's input-latency histograms only sample frames that were preceded by
input, so a window that janks while animating or while streaming content
(agent panel output, terminal scrollback) never shows up in the fleet's
latency reports. Hang detection catches outright stalls, but frames that
are merely late — stutters in the 30–100ms range during animation —
currently aren't visible anywhere.

This adds a `frame-duration-histogram` feature to GPUI with a per-window
tracker recording two histograms: the duration of every `Window::draw`,
and the interval between consecutively presented frames while the window
is animating (a next-frame callback was already scheduled at the
previous present, so frames are being produced back-to-back and a
stretched interval means frames were missed). Intervals are only
recorded for active windows, since inactive windows are deliberately
throttled to a lower frame rate, and re-presents of unchanged frames
(e.g. sustaining the display's refresh rate during high-rate input) are
excluded. Zed enables the feature and reports both histograms every five
minutes as a "Frame Duration Report" telemetry event alongside the
existing "Latency Report", bucketed at roughly the 120Hz/60Hz/30Hz frame
budgets so dropped-frame rates can be aggregated across the fleet.


Release Notes:

- Added frame rendering performance to the diagnostics Zed collects when
telemetry is enabled, to help find and fix stutters and dropped frames.
2026-08-10 17:37:27 +00:00
Zak Nesler 7d29f184f0 gpui: Fix img aspect ratio overriding existing value (#62367)
# Objective

The `gpui::img` element always overrides the `aspect_ratio` field, so if
you have an image element that applies its own `.aspect_ratio()` it just
gets wiped out.

## Solution

Only apply the aspect ratio default if one is not already set.

## Testing

It's a very minor change but I did add a small test to ensure it
actually gets overridden.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

## Showcase

This came from an issue where vertical images inside an element (square
in this case, as you'd see in an image gallery) do not behave correctly
with `object_fit` values of `ObjectFit::Contain` or
`ObjectFit::ScaleDown`.

This was simply because despite the image element having a fixed square
size (`img().size(px(200.))`), the aspect ratio would be forced to the
ratio of the image itself, so vertical images weren't being properly
fitted into their containers.

Minimal repro for that issue:
https://github.com/zaknesler/gpui-object-fit

So with this change, you can set `.aspect_square()` and the object fit
will behave as you'd expect:

<img width="1237" height="986" alt="image"
src="https://github.com/user-attachments/assets/5b1045a8-bd71-4b77-8bbd-c3b12112bcb0"
/>

---

Release Notes:

- gpui: Fix image element's aspect ratio overriding existing value
2026-08-09 11:31:23 +00:00
Conrad Irwin 383ea8b546 gpui: Reconcile hover listeners after layout changes (#62335)
GPUI's declarative hover styles are recomputed from the current frame's
hit test, but `on_hover` listeners previously updated only in response
to mouse movement or the pointer leaving the window. When layout moved a
different element beneath a stationary pointer, the new element looked
hovered without receiving its hover-start callback.

This change reconciles each hover listener against the current hit test
during painting and defers transitions until after the paint cycle, when
application callbacks can safely update state. Reconciliation pauses
while a mouse press is pending so hover-only controls remain mounted
between mouse-down and mouse-up.

The regression coverage moves an element beneath and away from a
stationary pointer and verifies both transitions. It also verifies that
repainting during a stationary mouse press does not generate a spurious
hover exit.

Testing:

- `cargo test -p gpui --lib`
- `cargo fmt --check -p gpui`
- `cargo check -p delta --bin delta` with Delta temporarily patched to
the local GPUI checkout

Release Notes:

- Fixed hover interactions not updating when interface elements move
beneath a stationary pointer.
2026-08-07 22:17:41 +00:00
Agus Zubiaga c5f035e1bd gpui: Add synced repeating animations and phase-lock spinners (#62332)
Spinners rendered near each other (like a sidebar full of running
threads) each start rotating from the moment their element first
renders, so they spin out of phase and the group looks jittery.

This adds `Animation::repeat_synced()`, which derives the phase from a
clock shared by the whole app instead of the element's mount time, and
switches `with_rotate_animation` over to it, so every spinner with the
same period now renders the same frame at the same time. The epoch is
read off the scheduler clock at app creation, so the phase stays
deterministic under the test scheduler.

Release Notes:

- Improved loading spinners to rotate in phase with one another
2026-08-07 21:14:36 +00:00
Cameron Mcloughlin 86a7452424 Gpui web dont repaint (#62327)
Currently, GPUI on web calls `requestAnimationFrame` every frame, which
causes it to do meaningful GPU work every frame, even if it was
identical.

This PR makes GPUI only call `requestAnimationFrame` when it has new
content to display.

---

Release Notes:

- N/A or Added/Fixed/Improved ...
2026-08-07 18:51:36 +00:00
Chris HynesandFriday a1b5c49390 gpui: Expose accessibility identifiers to platform clients (#61926)
# Objective

- Fixes #61925.
- GPUI already carries AccessKit author IDs through its platform
adapters, but applications cannot set one on an ordinary element.

## Solution

- Add `accessibility_id(...)` beside the other semantic builders.
- Store the value separately from GPUI's element ID and write it to
`Node::set_author_id`.
- Document that applications should keep the value stable and unique
within the accessibility tree.
- Document the platform mapping accurately: UIA `AutomationId` on
Windows, `AXIdentifier` on macOS, and AT-SPI `AccessibleId` on Linux
stacks whose deployed adapter exposes it.

## Testing

Exact candidate `d099c529`:

- `cargo fmt --all -- --check`
- `git diff --check`
- `cargo test -p gpui test_accessibility_id_builder_writes_author_id
--offline`: 1 passed, 218 filtered out

The test exercises the public builder and verifies that the author ID
reaches the AccessKit node. Validation is unit-level; this branch has
not run live platform UIA, AX, or AT-SPI end-to-end tests.

## Self-Review Checklist:

- [ ] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments ; no unsafe block
added
- [x] The content adheres to Zed's UI standards ; no visual change
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- N/A

---------

Co-authored-by: Friday <260232009+buildfriday@users.noreply.github.com>
2026-08-07 11:55:17 +00:00
Anthony Eid 2ade98f6a9 gpui: Bound benchmark frame servicing to the tasks queued at frame start (#62235)
The renderer benchmarks drain the main queue to quiescence before each
measured frame, so a task that yields and re-queues itself (an idle
sweep, a poller) runs to completion inside a single measured frame — or
keeps the benchmark spinning forever. Real platforms don't work that
way: macOS's main-queue drain only runs the items present when the drain
starts, Linux runs one runnable per calloop idle callback, and Windows
drains under a 10ms budget before servicing paint messages — frames
always get a chance to preempt. So `run_ready_main_tasks` now only runs
the tasks that were queued when servicing began, letting self-re-queuing
work advance one batch per frame like it would under a real run loop,
and `BenchAppContext` grows a `run_until` (wrapping the dispatcher
primitive task benchmarks already use) so setup code can pump the app
until a condition holds without settling to idle first.

Release Notes:

- N/A
2026-08-05 20:24:19 +00:00
Cameron Mcloughlin 2c60a17676 markdown: Mermaid zoom (#62115)
Enables zooming of mermaid diagrams by supporting horizontal scroll

---

Release Notes:

- improved: Mermaid diagrams can now be zoomed and horizontally
scrolled, in both the markdown preview and the agent panel
2026-08-05 16:54:27 +00:00
liam 9b11e3b64d gpui: Prevent multi-modifier gestures from dispatching standalone modifiers (#62036)
Resolves #61888.

Multi-modifier gestures could leave GPUI’s standalone-modifier state
armed after one modifier was released. For example, releasing `Alt`
before `Shift` during an `Alt+Shift` gesture caused the final `Shift`
release to be synthesized as a standalone shift keystroke.

This diff invalidates standalone-modifier synthesis whenever multiple
modifiers are active, while preserving genuine modifier-only key
bindings.

Release Notes:
- Fixed Alt+Shift gestures potentially triggering standalone Shift key
bindings.
2026-08-05 16:31:15 +00:00
Anthony Eid 336fa8b38e gpui: Check run_until readiness between main-thread runnables (#62220)
`ThreadedDispatcher::run_until` - the completion mechanism under
`BenchAppContext` async task benchmarks (`bench_task` /
`bench_batched_task`, added in #62180) - drained the entire main queue
before checking its readiness predicate. Main-thread work that re-queues
itself (an idle-time sweep, a polling loop) keeps the queue non-empty
until it finishes every iteration, so a task benchmark's measured
interval silently extended past the awaited task's completion until all
such deferred work settled. Benchmarks of UI workloads that schedule
follow-up idle work could report several times their true completion
latency.

This changes `run_until` to step main-thread runnables one at a time and
check readiness before each, so it returns at the completion it awaits
rather than at queue quiescence, making async task benchmarks more
accurate. The regression test spawns a task that re-queues itself 10,000
times plus a one-shot completion task, and asserts `run_until` returns
without draining the re-queued work; it fails against the previous
drain-first implementation.

Release Notes:

- N/A
2026-08-05 16:23:02 +00:00
Anthony Eid e88195fd74 gpui: Add async task benchmark support (#62180)
### Summary

This PR improves GPUI's Criterion benchmark integration by adding
task-based measurement APIs to `BenchAppContext`. Benchmarks can now
await asynchronous GPUI operations whose completion depends on
foreground work, background work, timers, or external wakes, while
keeping setup and destruction outside the measured interval.

#### Added `BenchAppContext` APIs

- Add `bench_task` to measure a GPUI `Task` through completion in
Criterion's iteration loop.
- Add `bench_batched_task` to perform per-iteration setup outside the
timed interval before measuring a task.
- Preserve support for non-`Send` foreground task outputs and destroy
outputs after timing stops.
- Collect frame timings produced during asynchronous benchmark
iterations without including collection in the measurement.
- Drive task completion across temporary dispatcher quiescence and
externally delivered wakes.

Release Notes:

- N/A
2026-08-04 23:38:36 +00:00
John Tur ec5ddcf55e Tweak renderer resource management (#62069)
Factoring some changes out of
https://github.com/zed-industries/zed/pull/61461:
- On Metal and wgpu, bulk-upload instance buffers at the start of the
frame rather than uploading chunks as we draw them. The depth buffer
patch needs this architecture since all opaque quads in the scene are
painted before anything else. This also lets us get rid of the awkward
retry loop when the GPU buffer is too small for the scene.
- On Windows, use a constant buffer for the instance offset rather than
creating SRVs with different `FirstElement`s. `FirstElement` is buggy on
some vendors' drivers, and reusing the SRV object should reduce D3D
runtime overhead.
- Misc. refactoring: remove redundant parameters, deduplicate code,
create fewer bind groups on wgpu, etc.

Fixes https://github.com/zed-industries/zed/issues/49588
Fixes https://github.com/zed-industries/zed/issues/59192

Release Notes:

- Fixed an issue where UI elements would flicker on some Windows
devices.
2026-08-03 14:37:21 +00:00
Nathan Sobo 077311366d gpui: Expose threaded dispatcher for tests (#62096)
Some tests need real OS threading and timers rather than the
deterministic, single-threaded behavior of `TestDispatcher`. The
existing multithreaded dispatcher provides those production-like
scheduling semantics, but it is named and gated as a benchmark-only
facility, which unnecessarily pulls in Criterion and histogram
dependencies.

This renames `BenchDispatcher` to `ThreadedDispatcher` and exposes it
through GPUI's `test-support` feature. Benchmark contexts continue using
the same dispatcher, while Criterion and benchmark reporting remain
isolated behind the `bench` feature.

### Testing

- `cargo check -p gpui --features test-support`
- `cargo check -p gpui --features bench`
- `cargo nextest run -p gpui 'threaded_dispatcher::tests'`
- `cargo fmt --all --check`

Release Notes:

- N/A
2026-08-02 22:21:34 +00:00
Mayank Verma 938f8d7454 gpui: Add resizable and minimizable state to Window (#61473)
### Objective

PR #36859 made native macOS and Windows controls respect `is_resizable`
and `is_minimizable`, but custom window decorations cannot access those
values from `Window`. Linux custom controls and client-side resize
interactions remain active, macOS custom titlebar double-click does not
check either option and custom Windows caption buttons still appear
enabled even though the backend blocks their actions.

### Solution

Expose `is_resizable` and `is_minimizable` on `Window`. Use them to
disable custom window controls and titlebar actions and omit client-side
resize cursors and hitboxes for non-resizable windows.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- N/A
2026-07-31 06:38:08 +00:00
mTvare 922d2dd05c x11: Request repaint after exposure instead of within blocked loop (#61162)
# Objective

Closes #61054

This PR fixes Zed window not repainting after they are uncovered on X11.
When an X11 window becomes visible after being covered, X11 sends an
`Expose` event to tell the application that the window contents need to
be presented again. Previously, Zed stored this event and waited for the
periodic refresh loop to request a presentation but, the refresh loop is
stopped while a window is fully hidden to avoid unnecessary rendering.
This meant there might be no refresh tick to handle the pending
exposure.

window is fully hidden -> periodic refresh loop stops

window is uncovered -> expose event -> waits for refresh loop -> window
may remain blank

This change requests one presentation directly after processing the
pending X11 events. Multiple `Expose` events for the same window are
coalesced, and unmapped windows are ignored.

## Solution

window is uncovered -> expose event -> presentation is requested ->
window contents are displayed

Required presentations are also excluded from inactive-window frame
throttling, so the request is not dropped when Zed is unfocused. Regular
rendering for unfocused or fully obscured windows remains throttled.

## Testing
Check the issue being closed on steps to reproduction.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable


Release Notes:

- Fixed Zed windows sometimes not repainting after being uncovered on
X11
2026-07-30 16:52:19 +00:00
Finn Eitreim d73ad3de55 gpui: Give divs (the option for) a better scrolling experience (#61797)
# Objective

Closes #56127

Right now there is an issue where in certain contexts, specifically GPUI
div elements, horizontal scrolling w/ a trackpad works very poorly, even
when trying to scroll sideways, vertical scrolling will happen.

This happens with both a trackpad and scrollwheel. The showcase video
also shows the broken behavior.

## Solution

The way that scrolling works in the editor is really nice, scrolls are
locked to the axis they started on but with enough force can change
axis. so by taking the ongoing scroll handler from the editor and moving
it up into GPUI, we can get that same functionality for GPUI.

I somewhat "take over" the functionality of the existing
`restrict_scroll_to_axis` property, because it seems like its purpose
was basically trying to fix this problem already, just unsuccessfully.
The alternative, and would make things overall nicer to look at, would
be to tie this to `allow_concurrent_scroll`, but the effect radius tied
to that option is way bigger, so im holding off on that atleast right
now.

## Testing

Theres a good number of scroll related tests generally, so I don't think
i've perturbed any common existing behaviors. attach the functionality
to `restrict_scroll_to_axis` really limits the surface. There are also
some new tests for the ongoing_scroll behaviors.
Tested on my mac w/ both trackpad and mouse. 

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

## Showcase

Video show casing all the behaviors:
broken w/ trackpad
working w/ trackpad
broken w/ scroll
working w/ scroll



https://github.com/user-attachments/assets/1935cd2d-a255-4a77-81d5-daa9f72470a6


---

Release Notes:

- markdown_preview: Fix horizontal scrolling inside certain elements
2026-07-30 16:30:13 +00:00
Smit Barmase a8b25c3131 gpui: Restore file drags when re-entering the source window on macOS (#61940)
Follow-up to #58161, which handed file drags to AppKit once the pointer
left the window but left the handoff one-way. Re-entering the source
window and dropping was not possible.

GPUI now suspends the original drag when handing it to the platform and
restores it when the drag re-enters the source window, so drop targets
receive the original typed payload instead of the synthetic
external-paths payload.

No release notes this hasn't shipped yet.

Release Notes:

- N/A
2026-07-30 16:11:24 +00:00
Ron HarelandSmit Barmase ca474e2252 image_viewer: Fix texture leak when reloading images (#58803)
When the image viewer reloads an image after the underlying file
changes, it now tracks the `RenderImage` currently displayed by the view
and drops that image’s atlas entry before replacing it with a newly
decoded render image. This prevents rapidly updated images from
accumulating atlas textures per reload while keeping cleanup scoped to
the window that displayed the image.

Closes #35894

Release Notes:

- Fixed a memory leak when previewing images that are repeatedly updated
on disk.

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2026-07-30 12:35:59 +00:00
Kunall Banerjee 721c5bf42f docs: Clarify window_decorations currently only works on Linux (#61883)
Also add the setting to our docs at https://zed.dev/docs -- it’s
currently missing. Updated a stale comment as well -- window decorations
were [always supported on
X11](https://github.com/zed-industries/zed/pull/13611)?

Closes #61788.

---

Release Notes:

- N/A
2026-07-29 21:06:03 +00:00
Lee ByeongJunandSmit Barmase 9a6ed13221 Add macOS file drag-out from project panel (#58161)
## Description

Adds native macOS file drag out support for Project Panel entries. Local
file paths are resolved only when a drag gesture starts, remote or
missing paths are skipped, and unsupported platforms keep the default
no-op behavior.

The macOS implementation starts an AppKit drag session with file URL
pasteboard items, allows Finder and other external apps to choose copy
or move, keeps in-app GPUI drag/drop behavior in control for internal
drags, and clears the active drag state when the native session ends.


https://github.com/user-attachments/assets/42909389-1a32-4574-a200-9a14b913f55f

Self-Review Checklist:

- [X] I've reviewed my own diff for quality, security, and reliability
- [X] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [X] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable

Closes #55370

Release Notes:

- Added support for dragging files from the Project Panel to external
macOS apps.

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2026-07-29 18:55:25 +00:00
Mayank Verma 5aca79139a gpui: Defer appearance change callback to avoid reentrant borrow (#61789)
### Objective

PR #58902 made native macOS window chrome follow the selected theme, but
AppKit can synchronously invoke `viewDidChangeEffectiveAppearance` while
the settings observer holds an `App` borrow. `handle.update` then fails
with `RefCell already borrowed`, leaving `Window::appearance()` and its
observers stale.

### Steps to reproduce

1. Set Zed's theme mode to Dark.
2. Set macOS Appearance to Light.
3. Change Zed's theme mode to System.
4. Notice that the window chrome switches to light, but Zed's UI remains
dark.


https://github.com/user-attachments/assets/f91962cc-76ba-4d8e-b587-2a2343f365ba

### Expectation

Zed should switch to its configured light theme when returning to System
mode.

### Solution

Defer `Window::appearance_changed` to the foreground executor so the
current `App` borrow finishes before refreshing the cached appearance
and notifying observers.

### Testing

- Added a regression test.

### Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- N/A
2026-07-29 13:35:25 +00:00
Mikhail PertsevandTom Houlé f89b733a07 gpui: Complete pending key bindings before IME input (#61270)
# Objective

- Fix multi-stroke keybindings such as `ctrl-x k` being intercepted by
the macOS Japanese IME before GPUI can complete the binding.
- Preserve IME-first handling for ordinary Japanese text input and
active marked-text composition.
- Fixes #56043.

## Solution

- Stop preferring IME-first printable input while a valid multi-stroke
keybinding is pending. This allows the next printable keystroke to reach
GPUI and complete the binding.
- Consider pending input active only when it belongs to the window's
current focus. This prevents stale pending state after a focus change or
blur from bypassing normal IME handling.
- Preserve the existing macOS active-composition path, so marked text,
candidate selection, and composition commands continue to reach
`NSTextInputContext` first.

## Testing

Manually tested on macOS using Apple’s built-in Japanese–Romaji/Hiragana
input source with a `ctrl-x k` keybinding. Also added regression test
for pending keybinding routing.


## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- Fixed multi-stroke keybindings not completing when using a Japanese
IME on macOS.

---------

Co-authored-by: Tom Houlé <tom@tomhoule.com>
2026-07-29 13:31:58 +00:00
Bowen Xu 5aff80f553 gpui: Add grid_rows_min_content and grid_rows_max_content methods (#61837)
# Objective

- GPUI provides `grid_cols_min_content()` and `grid_cols_max_content()`,
but the `grid_rows_*_content()` methods are missing.

## Solution

- This PR adds the `grid_rows_min_content()` and
`grid_rows_max_content()` methods, and renames the enum
`TemplateColumnMinSize` to `GridTemplateMinSize`.

## Testing

- I have tested locally.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable


Release Notes:

- N/A
2026-07-29 10:24:22 +00:00
Cole Miller 6ac1191f6a gpui: Add an abstraction for scheduling foreground work for idle periods, and implement it for the web (#61827)
- Add `ForegroundExecutor::spawn_when_idle(timeout, future)`, which
schedules `future` to be polled on the main thread when the main thread
is idle, but tries to do so no later than `timeout` after the current
instant (with the same `timeout` being applied to subsequent polls of
the same future)
- Add `Platform::dispatch_on_main_thread_when_idle(runnable, timeout)`
as the basis for `spawn_when_idle`, with a default implementation that
ignores the timeout and delegates to `dispatch_on_main_thread(runnable,
Priority::Low)`
- Implement `dispatch_on_main_thread_when_idle` for the web platform
using
[`requestIdleCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)

Release Notes:
- N/A
2026-07-29 06:41:29 +00:00
Jason LeeandClaude Opus 4.8 ff57679788 Match the macOS app appearance to the selected theme (#58902)
Closes #5049.

When the system is in light mode but Zed uses a dark theme, the macOS
window chrome — the window border and the titlebar — is rendered light,
leaving a pale, low-contrast edge that makes the window look washed out.
Zed's own content renders correctly; the affected chrome is drawn by
AppKit, not Zed.

Why it happens: `NSWindow` conforms to `NSAppearanceCustomization`, so a
window's appearance is inherited app → window → view and resolved
through `effectiveAppearance` unless an explicit appearance is set. Zed
never set one, so every window inherited the *system* appearance and
AppKit drew the chrome for light mode regardless of the dark theme.

The fix uses the override AppKit provides for exactly this: set
`NSApplication.appearance` app-wide, so every window inherits it. VSCode
(`window.systemColorTheme`) and JetBrains IDEs do the same.

<img width="2600" height="1346" alt="after-light"
src="https://github.com/user-attachments/assets/c31376af-8f51-4c36-a62f-55714f63b59c"
/>

## GPUI

- Add `App::set_window_appearance(Option<WindowAppearance>)`, the setter
paired with the existing `App::window_appearance()` getter. `Some(_)`
forces a light/dark appearance; `None` clears the override and follows
the system again.
- macOS sets `NSApplication.appearance` (`None` → `nil`); no-op on other
platforms.
- Demo: `cargo run -p gpui --example window_appearance`.

## Zed

- An explicit light/dark theme (or a static theme) forces the matching
chrome; `System` follows the OS.
- Wired once at startup via a global settings observer, so every window
— including the settings window — stays in sync.

| Before | After |
| --- | --- |
| <img width="3630" height="1878" alt="before-light"
src="https://github.com/user-attachments/assets/44c9ab66-c0cf-433a-93b0-17446b34a284"
/> | <img width="3686" height="1832" alt="SCR-20260609-nzjk"
src="https://github.com/user-attachments/assets/32b0412b-0913-4e68-acba-bf39dcb6aa6a"
/> |


References:
[`NSAppearanceCustomization`](https://developer.apple.com/documentation/appkit/nsappearancecustomization)
·
[`NSApplication.appearance`](https://developer.apple.com/documentation/appkit/nsapplication/appearance)

Release Notes:

- Fixed the macOS window border and titlebar not matching the selected
theme when the system appearance differed (for example, a dark theme
under a light system theme).
([#5049](https://github.com/zed-industries/zed/issues/5049))

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-28 08:45:03 +00:00
shenghaiyang d9e2550e28 gpui: Make HSLA functions const (#61682)
# Objective

Make hsla functions `const`.

## Solution

Made Hsla-related functions `const fn`.

## Testing

> Did you test these changes? If so, how?

Ran `cargo test -p gpui color` to verify the color-related tests pass.

> Are there any parts that need more testing?

No additional testing is required since this change only changes
existing color functions to `const fn`.

> How can other people (reviewers) test your changes? Is there anything
specific they need to know?
 
Reviewers can run `cargo test -p gpui color` to verify the changes.

> If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?

Tested on macOS. No platform-specific behavior is expected.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- N/A
2026-07-27 09:23:49 +00:00
Agus Zubiaga 0fd17feb17 gpui: Fix dropped frames when scrolling an unfocused window (#61600)
Scroll events are delivered to the window under the pointer even when it
isn't focused, but we cap inactive windows at ~30fps to save energy, so
scrolling an unfocused window visibly drops frames. We now exempt
high-rate input from the cap using the existing `InputRateTracker`: a
trackpad scroll stream lifts it while events are arriving and for one
second after the last burst, then the cap resumes on its own.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- Fixed dropped frames when scrolling unfocused windows.
2026-07-26 05:33:42 +00:00
Eric Holk 5d2df6acd5 Fix crash when dumping debug buffers in a collab session (#61616)
The `dev: dump input latency histogram` and `dev: dump accessibility
tree` actions called `Project::create_local_buffer`, which panics on
remote projects — so running either one as a guest in a collab session
aborted the whole app. Both now use the async `Project::create_buffer` +
`set_text` instead (the same pattern `open_log_file` uses), which routes
through the collab protocol on remote projects.

Since the collab-created buffer is visible to other participants, the
latency report now includes a "Reported from <username>'s machine" line
when the project is shared, using the collab username so we don't expose
anything that isn't already shared with the session.

While restructuring the handler I also split snapshotting from
formatting: the histogram snapshot (and the delta baseline) is captured
synchronously when the action fires, and the report string is built on a
background task. The accessibility tree dump stays synchronous because
gpui doesn't currently expose the captured tree data outside the window
borrow, and adding public API for a debug action's JSON pretty-print
didn't seem worth it.

Release Notes:

- Fixed a crash when running the `dev: dump input latency histogram` or
`dev: dump accessibility tree` actions in a shared project.
2026-07-26 01:19:29 +00:00