Commit Graph
3364 Commits
Author SHA1 Message Date
Anthony Eid 0307288d90 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 dd04a229dd 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
ff9f114cd3 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 cdf33ac25e 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 9e236090b9 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 59b2ebf103 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 38ca9106c5 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 4ed3738c40 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 417d03305a 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 cc053a4a6f 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 82878540b5 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 00cba838ad 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 a8cae3bd77 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 38df25d54c 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 300972bea3 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 be8c6f9fb3 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 8886dcb0d4 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 e99616cdd4 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 ae99a867d7 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 79cc17c216 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 a8491e63b5 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 a473ea63a8 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 36911f8cab 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 f52fd9ac44 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 a11083f9a7 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é dc1e815e47 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 5ccbbbd88f 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 82aef44308 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 8e4e5a39ee 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 63b52a7ee3 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 a50a292fd1 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 e191dd00c7 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
58df5a14ce gpui: Fix images not being drawn with rounded corners when using ObjectFit::Cover (#61383)
# Objective

Fixes #44339

## Solution

The issue was taking place as image bounds where larger than the view
bounds. Hence, the rounding of the image happened outside the view
bounds, hence the corners where not rounded.
This solution changed the paint_image function in window.rs. Now, it
takes in 2 parameters, image bounds and bounds of the area to be draw.
Then, the function creates the tile field of PolychromeSprite by
cropping the image to fit the area to be drawn. This ensures that the
rounding happens correctly.

## Testing
Testing was done as per the issue and results where verified visually.

## 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

## Showcase

<img width="1409" height="1030" alt="image"
src="https://github.com/user-attachments/assets/3645b36d-f182-4853-bdfe-d66a4132d273"
/>


## Release Notes:

Fixed rounded corners not rendering on images when using
ObjectFit::Cover

---------

Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
Co-authored-by: Nathan Sobo <nathan@zed.dev>
2026-07-25 00:03:32 +00:00
Anthony Eid c16d19c94d gpui: Fix deadlock in performance profiler and reenable it (#61584)
### Summary

PR #58942 disabled the performance profiler within Zed because there was
a race condition that caused Zed to hang forever due to a deadlock
involving the foreground thread. This PR fixes the deadlock and
re-enables the performance profiler.

The deadlock happened because `ThreadTimings::drop()` locks
`GLOBAL_THREAD_TIMINGS`, and that drop could run in places where
`GLOBAL_THREAD_TIMINGS` was already locked: the collection paths
(`get_all_timings`, `take_all_stats`, `set_trace_enabled(false)`) held
the global lock while upgrading and then dropping per-thread
`Arc<GuardedTaskTimings>` handles. If a worker thread exited in that
window (e.g. GCD reclaiming an idle thread), the collector inherited the
last strong reference, and dropping it ran `ThreadTimings::drop` ->
`GLOBAL_THREAD_TIMINGS.lock()` reentrantly on the same thread. The
spinlock is not reentrant, so the thread spun forever while holding the
lock, hanging every other thread that touched the profiler.

The fix: hold `GLOBAL_THREAD_TIMINGS` only long enough to upgrade the
`Weak` handles (`upgraded_thread_timings()`), and release it before any
per-thread buffer is locked, copied, or dropped. A last-reference drop
now always runs with the global lock free. As a side benefit, the
up-to-16MiB per-thread buffer copies no longer happen under the global
lock.

### Diagram

```mermaid
sequenceDiagram
    participant C as Collector thread (get_all_timings)
    participant G as GLOBAL_THREAD_TIMINGS (spin::Mutex)
    participant T as Worker thread (exiting)

    Note over T: holds the only strong Arc<br/>in its THREAD_TIMINGS TLS
    C->>G: lock() — guard held for entire collection
    C->>C: Weak::upgrade() (strong: 1 → 2)
    T->>T: thread exits, TLS destructor drops its Arc (strong: 2 → 1)
    C->>C: temp Arc dropped at end of iteration (strong: 1 → 0)
    C->>C: ThreadTimings::drop() runs on collector thread
    C->>G: lock() again — already held by this thread
    Note over C,G: spin lock is not reentrant → spins forever,<br/>every other profiler user spins behind it

    Note over C,T: Fix: upgrade all Weaks under the lock, release it,<br/>then lock/copy/drop per-thread handles — the reentrant<br/>drop can now only ever run with the global lock free
```

Release Notes:

- Fixed a deadlock in the performance profiler and re-enabled it (`zed:
open performance profiler`)
2026-07-24 15:34:17 +00:00
Kirill Bulatov 8c7811ea72 Split VSCode and Zed keymap files (#61532)
Currently, Zed's "VSCode" keymap has certain discrepancies that are not
changed for compatibility reasons, e.g. opening inline assistant is
different in each editor.

To simplify the transition, extract VSCode bindings its own keymap so in
the future it's simpler to accept changes to each keymap separately.

Caveat: people who had ever changed the keymap, will have VSCode keymap
in their settings which will change their bindings slightly after this
commit is released.

I had to introduce more logic to `null` handling as had to somehow
disable `ctrl-enter` on mac from spawning the inline assistant block for
VSCode keymap (it's cmd-i there).

Release Notes:

- Split VSCode and Zed keymap files
2026-07-24 07:54:03 +00:00
Earthgames dc682ef120 Print Asset Error with the whole context (#61482)
# Objective

So while using gpui I didn't know that you needed to register a
http_client if you wanted to use images with a link as a source.
So I enabled logs, and I did get errors.
```
20:48:11 [ERROR] Failed to load asset2: error: loading image asset from "https://im.fumo.ing/tLNyIY4n7PEG.jpg"
```
But they didn't tell me what was wrong.

## Solution

I change 2 lines to print the whole error with all the context.

```
20:49:09 [ERROR] Failed to load asset2: Other(loading image asset from "https://im.fumo.ing/tLNyIY4n7PEG.jpg"

Caused by:
    No HttpClient available)
```
And now it does tell you why it doesn't work. ✨Amazing✨.

## Testing

Because I did change the traits needed for the AssetLogger I build zed
to check if it didn't break anything. And it build, so it fine I think.

A quick way to test the change in logging is the gpui example
`image_gallery.rs` and commenting the lines 259 & 260
```rust
        #[cfg(not(target_family = "wasm"))]
        {
            let http_client = ReqwestClient::user_agent("gpui example").unwrap();
            cx.set_http_client(Arc::new(http_client));
        }
```
Or change the URI on line 42 to something non existing.

## 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:

- Improved Error logging for Asset loading
2026-07-23 06:26:38 +00:00
ᴀᴍᴛᴏᴀᴇʀ 547a6cd599 editor: Fix right-click context menus failing to appear at pixel-snapped scroll positions (#61348)
# Objective

Fixes #56136.

The issue was initially reported while an agent was generating code.
Later reports reproduced it during manual editing and with AI disabled,
ruling out the agent as the cause.

At fractional display scales, the editor's right-click context menu
could fail to appear at specific discrete scroll positions. Scrolling
one line at a time could make the menu alternate between hidden and
visible, even when its anchor remained within the viewport.



## Solution

The editor-specific changes in #54728 started pixel-snapping the
vertical scroll position used by `EditorElement` to derive and render
visible rows. However, `Editor::display_to_pixel_point` continued using
the raw scroll position for its visibility check and vertical
projection.

When a downward pixel snap crossed an integer display-row boundary,
`EditorElement` treated the preceding row as the visible range start.
`display_to_pixel_point` then rejected that row as being above the raw
viewport. Mouse context menu layout returned early before inspecting the
actual clicked anchor, leaving the menu state present without rendering
its element.

This change applies the same line-height and display-scale pixel
snapping in `display_to_pixel_point`. Its visibility check and
coordinate projection now use the coordinate space actually rendered by
the editor.

A test-support-only scale-factor setter was also added so GPUI tests can
exercise fractional display scaling without depending on the host
display.

## Testing

Added a GPUI regression test using 100 plain-text lines so the editor
can scroll. The test uses a `1.25` scale factor and a `14px` font with
`1.3` relative line height. The resulting `18.2px` line height is
rounded to `18px`, producing exactly `22.5` device pixels per row.

At a raw scroll position of one row, midpoint-toward-zero snapping maps
`22.5` device pixels to `22`, or approximately `0.978` rows. This
reproduces the old boundary mismatch where the snapped visible range
starts at row zero while the raw visibility check starts at row one.

The test pins these values with precondition assertions. Row five and an
in-bounds click keep the actual source visible, while checking the
rendered bounds of the `Copy` item ensures the test does not pass merely
because menu state was created.

Manually verified that scrolling from the start of a document no longer
makes the context menu alternate between hidden and visible.

## 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:


https://github.com/user-attachments/assets/e14c030f-1587-4172-aed2-fecfbfcb65ed


After:



https://github.com/user-attachments/assets/3080db1e-46eb-4e20-a619-b1608f233668



---

Release Notes:

- Fixed editor right-click context menus intermittently failing to
appear at certain scroll positions.
2026-07-22 19:17:25 +00:00
Richard Feldman ec3d887507 gpui: Defer element arena clears while an enclosing draw is in progress (#60295)
GPUI allocates every element for a draw in a single per-App bump arena,
and `Arena::clear()` assumed no draw was in progress. But draws can
nest: on Windows, the window procedure re-enters whenever the main
thread pumps messages mid-draw (cross-thread `SendMessage` dispatch,
modal message loops entered by COM/OLE calls such as clipboard reads),
and on any platform a draw can be triggered from within another draw
(e.g. `open_window`). When a nested draw finished, its arena clear freed
and rewound memory the outer draw was still using. The `ArenaBox`
validity flag only guards new derefs, so `&mut` references already held
by the outer draw silently pointed into reused memory. That
use-after-free corrupted element-state keys
(`GlobalElementId`/`SharedString` Arcs) and heap metadata, crashing
later in innocent-looking frames — seen in the wild as
`EXCEPTION_ACCESS_VIOLATION_READ / 0xffffffffffffffff` in
`Frame::finish` and element allocation
([ZED-9QN](https://zed-dev.sentry.io/issues/7577818040/),
[ZED-7JC](https://sentry.io/organizations/zed-dev/issues/7465778926/),
[ZED-7C6](https://zed-dev.sentry.io/issues/7460258444/)), plus
occasional "attempted to dereference an ArenaRef after its Arena was
cleared" panics (ZED-96P, ZED-8XN), Windows-dominant and spanning
versions 1.1.7–1.8.2.

The fix makes the arena sound under nesting: `ElementArenaScope` tracks
a scope depth on the arena, and `Arena::clear()` is deferred while any
scope is active — the outermost draw's clear drops both draws'
allocations. Since arena chunks are stable heap blocks and allocation
only appends, nested allocation was already safe; the mid-draw clear was
the only destructive operation. Scopes are ended via a consuming
`exit(arena)` call that asserts arena identity (by pointer comparison,
never dereferencing), and `ArenaClearNeeded::clear(cx)` reaches the
arena through the App, so this bookkeeping contains no unsafe code
beyond what existed before. A panic that unwinds a draw balances the
scope depth in the guard's `Drop`, so later clears still run; only the
unwound draw's own clear token is never produced, leaving the arena
populated until the next draw's clear — a one-frame leak at worst, never
a use-after-free.

Additionally, draws are no longer run re-entrantly: GPUI's
`on_request_frame` callback skips requests that arrive while a draw is
already on the thread's stack (remembering `force_render` for the next
frame), and on Windows a `DrawCoordinator` owned by the platform and
shared with every window additionally guards the wider `draw_window`
span (presentation, IME updates). Deferred windows validate their update
region (so nested message pumps don't busy-loop on WM_PAINT) and are
repainted at most one vsync later by the vsync thread's existing
per-tick invalidation.

An integration test opens a window from within an element's paint;
without the deferred clear it reproduces the exact "ArenaRef after its
Arena was cleared" panic seen in the wild. Deferrals are logged so the
diagnosis can be confirmed from user logs.

Closes FR-110

Closes FR-114

Release Notes:

- Fixed a crash on Windows caused by re-entrant window drawing
corrupting UI element memory.
2026-07-22 15:25:22 +00:00
John Tur 0c51c7fd24 Reduce overdraw for border-only quads (#61274)
Restore an optimization that I mistakenly removed in
https://github.com/zed-industries/zed/pull/54728.

Drawing a quad with a border but no fill color will run the quad
fragment shader for every transparent interior pixel, which is
especially costly when the quad is large. Instead, split these quads
into four non-overlapping strips that cover the regions where borders
are painted. The side strips own the straight left and right edges,
while the top and bottom strips own the horizontal edges and the rounded
corners.

Previously, this optimization only applied to borders drawn by the Taffy
layout. I've decided to reinstate the logic directly in `paint_quad` so
that the optimization can be applied more generally... though if this
feels like too much policy, we can move it back.

This reduces the GPU time spent painting a representative Zed scene by
about 20% or so.

Release Notes:

- N/A
2026-07-19 06:52:53 +00:00
Lukas WirthandJohn Tur de827bce2f gpui: Add system notification platform APIs (#61189)
Release Notes:

- N/A or Added/Fixed/Improved ...

---------

Co-authored-by: John Tur <john-tur@outlook.com>
2026-07-17 18:52:23 +00:00
saberoueslati 9552acc2bc gpui: Apply EXIF orientation when decoding static images (#61017)
## Context

Static images decoded by GPUI ignored their EXIF `Orientation` tag, so
JPEGs that rely on it (photos from phones and cameras) rendered rotated
or flipped in image preview, Markdown preview, and other `ImageSource`
consumers. The `image` crate only exposes orientation through
`ImageDecoder::orientation()`, which must be read before the decoder is
consumed; `DynamicImage::from_decoder` does not apply it. The old path
went straight through `image::load_from_memory_with_format`, so the
metadata was never read.

Closes #60844.

Video of manual test below :

[Screencast from 2026-07-15
02-14-41.webm](https://github.com/user-attachments/assets/002b0324-44f8-4ddb-acbd-8c3e070d5248)


## How to Review

**Cargo.toml**
Raises the workspace `image` dependency minimum from `0.25.1` to
`0.25.4`, the first release exposing `orientation()` and
`apply_orientation()`. The lockfile already resolves to `0.25.10`, so no
lock change is required.

**crates/gpui/src/platform.rs**
Adds two crate-private helpers. `decode_static_image_from_decoder` reads
`orientation()`, decodes to a `DynamicImage`, applies the orientation,
then converts RGBA to GPUI's BGRA. `decode_static_image` builds a format
decoder from bytes and delegates to it. `Image::to_image_data` now
routes its static formats through these helpers instead of
`image::load_from_memory_with_format`. Adds
`test_image_to_image_data_applies_exif_orientation`, which decodes a
16x32 fixture stored upside down with EXIF Orientation 3 and asserts the
corner pixels were flipped upright; the test fails if orientation is
dropped.

**crates/gpui/src/elements/img.rs**
Routes `ImageAssetLoader` (Markdown paths, URLs, embedded resources)
through the same helpers, including the static WebP branch. GIF and
animated-WebP frame handling are left unchanged, since animated
orientation is out of scope.

## 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 is consistent with the UI/UX checklist
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Fixed static images ignoring their EXIF orientation, which caused some
JPEGs to render rotated or flipped in image and Markdown preview.
2026-07-17 08:29:17 +00:00
Smit Barmase 058f01fa93 gpui: Fix clicks on window prompts dismissing popovers behind them (#61136)
I noticed that when Zed renders prompts in-window (Linux, or
`use_system_prompts: false`), clicking a prompt button also dismisses
the popover behind it. Native macOS dialogs don't have this problem
since the click goes to the OS dialog and the window never sees it.

For example, in the worktree picker:
1. Delete a dirty worktree.
2. Confirm "Force Delete".
3. Mouse down dismissed the picker, which aborted the confirmed
deletion.

This PR makes GPUI-rendered prompts behave the same way as native. If
someone actually wants to observe mouse downs during a prompt, the raw
`window.on_mouse_event` API still sees every event.

macOS:


https://github.com/user-attachments/assets/9f85622f-c0d1-44f2-83f9-8378d9d7d13b


Before GPUI prompt:


https://github.com/user-attachments/assets/d1845ba1-9886-4733-8cf6-ab32424416e1

After GPUI prompt:


https://github.com/user-attachments/assets/27936aca-8de5-4da8-88be-d88dbc1259d8

Release Notes:

- Fixed confirmation dialog buttons dismissing the popover underneath
them on click when using Zed-rendered prompts.
2026-07-16 19:45:21 +00:00
Daan De Meyer ae3bbdeb2d gpui: Avoid redundant group hover redraws (#61118)
Anonymous elements with group hover styles have no persistent hover
state. The
prepaint mouse handler treated missing state as unhovered and notified
the
current view for every pointer movement within the group. This made
controls
such as switches redraw continuously while the pointer moved over them.

Let anonymous elements use the existing paint-time hover transition
handler.
Only elements with persistent hover state now update that state and
notify from
prepaint. Add regression coverage for entering, moving within, and
leaving an
anonymous hover group.

Signed-off-by: Daan De Meyer <daan@amutable.com>

Release Notes:

- N/A

Signed-off-by: Daan De Meyer <daan@amutable.com>
2026-07-16 19:26:49 +00:00
Kirill Bulatov b05f40c554 Fix post-taffy-bump layouting issues (#61107)
Follow-up to https://github.com/zed-industries/zed/pull/60721

Before:
<img width="564" height="470" alt="before"
src="https://github.com/user-attachments/assets/d825464d-f8d9-4fa1-bee3-42b5f615d4ba"
/>

<img width="745" height="221" alt="image"
src="https://github.com/user-attachments/assets/18139bec-6778-491a-9aa5-0112bd121723"
/>


After:
<img width="576" height="472" alt="after"
src="https://github.com/user-attachments/assets/4c3c37cf-12c4-421e-98e9-cc6e40aa099d"
/>

<img width="812" height="275" alt="image"
src="https://github.com/user-attachments/assets/407e06b4-101f-4984-8d5b-4e33895b5ae9"
/>


### Bug 1 — constraint-blind text layout cache (the collapse)

`TextLayout`'s measure closure is stateful (each call overwrites the
shared element state used for painting), and its cache check served
cached sizes to unconstrained probes regardless of the constraints they
were computed under:

1. `uniform_list::measure_item` lays an item out at `(MinContent,
MinContent)`; the row's `min_w_0` lets it shrink to ~0, so labels get
truncated at a tiny width — and that tiny size lands in the element
state.
2. The real layout probes the label's flex base size with
`available_space.width = MaxContent` (taffy maps Definite → MaxContent).
Truncated labels are `whitespace_nowrap`, so both `wrap_width` and
`truncate_width` were `None` → the cache check passed → the poisoned
tiny size became the flex basis → labels rendered at ~5 chars.

**Fix**: record `truncate_width` in `TextLayoutInner` and never serve a
cached layout that was computed *with* truncation to an unconstrained
probe; the honest intrinsic size is recomputed instead, and the final
`PerformLayout` call re-truncates against the real resolved width.

### Bug 2 — truncation width math disagrees with shaping (the ~2-char
loss)

After fix 1, rows got their full width, but exact-fit strings like
`Cargo.toml` still lost ~2 chars:

- Sizing measures via `shape_text` (real shaping with kerning); the
element gets exactly `ceil(shaped_width)`.
- The truncation decision (`LineWrapper::should_truncate_line_middle`
etc.) sums **per-character advances** — no kerning — with zero
tolerance, overestimating width for some glyph sequences. Text sitting
in a box of exactly its own measured width got truncated, losing ~2
chars to fit the `…`. String-dependent (kerning pairs), hence
`Cargo.toml` broke while `Cargo.lock` didn't.
- Previously masked because taffy 0.10 usually skipped the final measure
invocation, so the truncation path rarely ran with an exact-fit width.

**Fix**: before truncating (nowrap case, `wrap_width.is_none()`), shape
the untruncated text and skip truncation if the *shaped* width fits —
matching CSS `text-overflow`, which only activates on actual overflow.
Cheap: the shaping result is a `line_layout_cache` hit from the earlier
untruncated sizing probe.

### Bug 3 — `MinContent` used instead of `MaxContent` that caused over
truncation of certain labels

Release Notes:

- N/A
2026-07-16 17:23:49 +00:00
Mikayla Maki 91fdd55889 gpui: Bump taffy from 0.10.1 to 0.12.1 (#60721)
Migrates the alignment conversions to taffy's new safe-alignment style
types: `AlignItems`/`AlignContent` and friends are now structs pairing a
keyword with a safety modifier, constructed via associated constants
(e.g. `AlignItems::START`) instead of enum variants.

The motivating change in this release is [taffy
#911](https://github.com/DioxusLabs/taffy/commit/df2663aad35801f7522f6878a0300646dfe624f7)
("More correct caching logic"), which keys taffy's per-node measure
cache on axis, parent size, and available space. Today GPUI is largely
immune to the old cache-key conflation because text answers every sizing
probe with the same width; the follow-up PR in this stack (honest
min-content text measurement) returns different sizes for different
constraints, which makes cache-key correctness a prerequisite. 0.12.1
additionally hotfixes two block layout/caching regressions in 0.12.0,
and 0.11.0's grid fix (resolving item percentages against the grid area
rather than the container) comes along as well.

Stack:
1. **This PR** — taffy 0.12.1
2. #60722 — honest min-content text measurement
3. #60723 — CSS `auto` grid tracks for table-like column sizing

Release Notes:

- N/A
2026-07-15 19:47:15 +00:00
Philipp Schaffrath 166f044fd0 gpui: Linux wayland set exclusive zone and edge (#60163)
# Objective

The exclusive zone and exclusive edge of a wlr-layer-shell surface could
only be set once, at creation, through `LayerShellOptions`. This adds
runtime setters so a live layer-shell window such as a panel can update
them without being recreated.

## Solution

Add two methods on `Window`:

- `set_exclusive_zone` updates how much screen space the surface
reserves. A positive value reserves that distance from the anchored
edge, 0 lets the surface be moved out of others' exclusive zones, and -1
ignores reserved space and may extend under other surfaces.
- `set_exclusive_edge` chooses which anchored edge the exclusive zone
applies to, which is needed to disambiguate a corner-anchored surface.

Setting an exclusive edge the surface is not anchored to is a fatal
protocol error, so the edge is validated (it must be a single edge that
the surface anchor contains) and an invalid edge is logged and ignored.
The same validation now also guards the creation path. Both setters only
commit the surface when a change actually applies, and are no-ops on
non-layer-shell windows and on other platforms.

This is where these 2 are documented:

https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:set_exclusive_zone

https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:set_exclusive_edge

## Testing

- Verified with a small local layer-shell window (a top bar anchored
TOP, LEFT, RIGHT) with buttons that call the setters at runtime
- No automated test was added, since this calls through to the
compositor.
- Reviewers without a layer-shell capable compositor (for example
GNOME/Mutter) cannot exercise this, as `zwlr_layer_shell_v1` is
unavailable there, I tested this on the
[Smithay](https://github.com/Smithay/smithay/) based compositor
[niri](https://github.com/niri-wm/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
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- N/A
2026-07-15 08:18:46 +00:00
Kirill Bulatov af7de9a03c Fix project panel follow focus issues (#60974)
Closes https://github.com/zed-industries/zed/issues/60939
Closes https://github.com/zed-industries/zed/issues/60940

Release Notes:

- Fixed focus follows mouse in project panel's blank space
2026-07-14 15:10:32 +00:00
Kirill Bulatov d06ebb7c03 Disable all spinners in Zed (#60614)
Closes https://github.com/zed-industries/zed/pull/48577
Part of https://github.com/zed-industries/zed/issues/8043
See
https://zed-industries.slack.com/archives/C07NUKHLVUZ/p1783335475957139
for context.

Release Notes:

- Allow to reduce animations with `"reduce_motion": "on"` settings
2026-07-14 14:49:06 +00:00
Danilo Leal 5e982c6bdc gpui: Fix crash when opening a popover from inside another popover (#60926)
Opening a popover from inside another popover (e.g. the branch picker's
new filter menu, when the picker is hosted in the git panel or title
bar) corrupted the frame-caching bookkeeping for whichever cached view
contained them. The corruption was invisible until a later, unrelated
redraw reused that view's cached subtree, at which point the window
crashed (debug) or could misroute input events (release). Fixed by
processing deferred draws in place so their recorded positions remain
valid across frames; added a regression test reproducing the crash via a
cached panel hosting nested popovers.

Release Notes:

- Fixed the branch picker menu in the Git Panel, which was previously
broken due to popovers-within-popovers not being possible in GPUI.
2026-07-14 11:11:27 +00:00
Danilo Leal a496bf0ff6 Add a bunch of stray design refinement throughout (#60937)
This PR is a collection of grab bag UI design refinements across the
entire app. Most of them are minor icon fixes/standardizations, spacing,
and sizing tweaks.

The one change that ended up getting a bit bigger than I initially
anticipated is the debugger panel tabs improvements; added a bunch of
tweaks there to make it look slicker. A relevant change is a small
one-line change to the GPUI div element where it wouldn't add drag-over
styles to elements that aren't initially a hitbox target. Given I wanted
to pull off the drop indicator you see on the video below, this turned
out to be needed:


https://github.com/user-attachments/assets/964b456a-eef0-42bb-a433-7dac54ab8ec8

---

Release Notes:

- N/A
2026-07-14 10:07:58 +00:00