Closes https://github.com/zed-industries/zed/issues/9318
Most of the diff is tests.
Notable behavior changes during this mode, compared to current mode:
* unsaved changes in multi buffer won't spawn any "save or not" pop-ups
during typing — submitting the query still does it, as before.
Unsaved files will stay so after being sorted out on typing: we cannot
save them by default as file-less buffers need a save modal; Zed should
already be able to persist and restore such changes on restart.
* gutter may jump during search, if new excerpts with large line numbers
appear during query input — yet it would not shrink back to avoid
excessive flickering and rewraps
Release Notes:
- Enabled project search on type by default (use `{ "search": {
"search_on_type": false } }` to restore the previous behavior)
GPUI's `bench-support` feature provides the benchmark context and
headless-renderer interface without enabling `test-support`, but the
platform implementation remained available only through
`gpui_platform/test-support`. As a result, consumers could not use
`#[gpui::bench]` with a fully isolated feature graph because the
generated benchmark harness calls
`gpui_platform::current_headless_renderer()`.
This threads `bench-support` through `gpui_platform`, `gpui_macos`, and
`gpui_apple`, exposing the existing Metal headless renderer without
widening unrelated test window or prompt APIs. The existing benchmark
package now requests the platform's canonical `bench-support` feature.
Its other test-backed fixtures are intentionally unchanged and remain
outside this focused dependency fix.
Validation:
- Confirmed `gpui_apple/bench-support`, `gpui_macos/bench-support`, and
`gpui_platform/bench-support` resolve no `test-support` feature with
`cargo tree --locked --no-default-features --features bench-support -e
no-dev,features`.
- Ran `cargo check --locked -p benchmarks --benches` to compile every
existing `#[gpui::bench]` target against the real platform headless
renderer.
- Ran `cargo nextest run --locked -p gpui_apple -p gpui_macos -p
gpui_platform`.
- Ran `./script/clippy` for each changed platform package with
`--no-default-features --features bench-support`.
- Ran `cargo fmt --check`.
Release Notes:
- N/A
`gpui::bench` previously required GPUI's full `test-support` feature.
Any benchmark using the supported GPUI harness therefore compiled
test-only APIs and fakes, even when the benchmark otherwise followed
production dependency paths.
This change introduces `bench-support` as GPUI's canonical benchmark
feature and keeps `bench` as a compatibility alias for existing
consumers. The benchmark feature now exposes only the existing GPUI
internals that `BenchAppContext` needs: the real threaded dispatcher,
profiler integration, and a headless platform surface. Benchmark HTTP
requests use the production `BlockedHttpClient`, so accidental network
access fails instead of silently entering a fake response path.
The current headless implementation reuses the cfg-stripped core of
`TestPlatform`. Test-only prompt state, simulation APIs, and test
executors remain unavailable under `bench-support`. Cargo feature-tree
checks confirm that both `bench-support` and the compatibility alias
remain independent of `test-support`.
This also adds a `gpui-bench` agent skill covering production-shaped
fixtures, frame and foreground responsiveness, macOS headless Metal
rendering, deterministic correctness guards, Criterion measurement,
Instruments symbolication, and the requirement that a benchmark's
complete dependency graph contain no `test-support` feature.
Two follow-ups are intentionally deferred to keep this feature-isolation
change reviewable:
- Extract the shared headless implementation into
`HeadlessPlatformCore`, with separate `BenchPlatform` and `TestPlatform`
wrappers. This will remove benchmark-related cfg branches from the
test-facing type and make their supported surfaces explicit.
- Add a shared scripted HTTP client that compiles independently under
either `http_client/test-support` or `http_client/bench-support`. Tests
and benchmarks could then serve predefined responses through the same
deterministic transport boundary without either support feature enabling
the other; the default benchmark context should remain network-blocked.
This PR only isolates GPUI itself. Migrating downstream benchmark
fixtures that explicitly request other crates' `test-support` features
remains separate work.
Testing performed:
- `cargo tree --offline --package gpui --no-default-features --features
bench-support --edges features --invert gpui`
- `cargo tree --offline --package gpui --no-default-features --features
bench --edges features --invert gpui`
- `cargo check -p gpui --no-default-features --features bench-support`
- `cargo check -p gpui --no-default-features --features bench`
- `git diff --check`
Release Notes:
- N/A
Prevent mobile browsers from dismissing the software keyboard during
touch handling, and only activate the keyboard when a touch targets an
editable text input. On iOS, the hidden IME element is made editable
before it receives focus so the keyboard can reopen after interacting
with non-editable controls or dismissing it manually.
Release Notes:
- N/A
OpenAI can return streamed overload errors with the codes
`server_is_overloaded` and `slow_down`. Zed previously only recognized
`overloaded_error`, so these responses fell through to
`ProviderErrorCategory::Other`. Downstream consumers could then treat a
routine provider-capacity failure as an unexpected error.
Classify all three codes as `ProviderErrorCategory::Overloaded`. This
lets existing retry and error-reporting behavior handle them
consistently without matching provider-specific messages outside the
OpenAI response parser.
This matches Codex’s handling. Codex classifies both
[`server_is_overloaded` and `slow_down` in streamed
responses](https://github.com/openai/codex/blob/a26f1806a4f4b8cfec2ea1be129963815a61e58c/codex-rs/codex-api/src/sse/responses.rs#L696-L699),
and performs the [same classification for HTTP 503 response
bodies](https://github.com/openai/codex/blob/a26f1806a4f4b8cfec2ea1be129963815a61e58c/codex-rs/codex-api/src/api_bridge.rs#L63-L74).
Release Notes:
- Improved OpenAI error handling
# Objective
Fixes#48927. On Windows, when monitors have different DPI scaling
factors, a new window opened on a secondary monitor can be positioned
partially off-screen, with the title bar unreachable.
## Solution
`WindowsWindow::new` calls `CreateWindowExW` with `CW_USEDEFAULT` for
the window's initial position, so Windows picks a starting spot
(typically based on where the previous window was, which is often the
primary monitor) before the window is explicitly moved to its intended
monitor via `SetWindowPlacement`.
`WindowsWindowState::new` immediately reads the window's scale factor
via `GetDpiForWindow(hwnd)` at that point — i.e. the DPI of wherever
Windows initially placed it, not necessarily the target monitor.
`retrieve_window_placement` then used that scale factor to convert the
target monitor's logical bounds into physical pixels. When the two
monitors have different scaling (e.g. 150% vs 100%, as in the linked
issue), this produces the wrong physical rect, leaving the window
off-screen.
The target `WindowsDisplay` already computes its own correct
`scale_factor` from the actual monitor (`get_scale_factor_for_monitor`)
— this is also what `check_given_bounds` already uses. This PR exposes
that value via `WindowsDisplay::scale_factor()` and uses it in
`retrieve_window_placement` instead of the transient window's scale
factor, since the bounds being converted are expressed in logical pixels
for that target display.
## Testing
- Verified the logic by tracing through `WindowsWindowState::new` →
`retrieve_window_placement` → `calculate_window_rect` and confirming the
target monitor's own DPI (not the temporarily-assigned window DPI) is
now used to compute the physical placement rect, matching the approach
`WindowsDisplay::check_given_bounds` already uses for the same
target/temporary-monitor mismatch.
- I wasn't able to build/run Zed locally in this environment (missing
Windows SDK components in the local toolchain, unrelated to this change)
to reproduce the original multi-monitor/mixed-DPI repro steps
first-hand. I'd appreciate a maintainer or anyone who can reproduce
#48927 double-checking on real mixed-DPI hardware.
- Platforms: Windows only (`gpui_windows` crate); no other platform code
touched.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments (no new unsafe
blocks added)
- [ ] The content adheres to Zed's UI standards (N/A, no UI change)
- [ ] Tests cover the new/changed behavior (no existing test harness for
Windows-specific placement logic; happy to add one if pointed at the
right pattern)
- [x] Performance impact has been considered and is acceptable (no
measurable impact; same number of DPI queries, just reading from the
already-computed `WindowsDisplay` instead of the window)
Release Notes:
- Fixed: new windows on Windows could open partially off-screen when
placed on a secondary monitor with a different DPI scaling factor than
the monitor Windows initially placed them on.
---------
Co-authored-by: Lukas Wirth <lukas@zed.dev>
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
# Objective
`LineWrapper` treats every punctuation character as a break opportunity,
so a wrap can land right before `!`, `?`, `/`, `)`, `]`, `}`, a closing
quote or an ellipsis. Text such as `please fix this plz!`, `8.0/8.0`,
`cli/install`, `(see)` or `“quoted”` then wraps with the closing mark
orphaned at the start of the next line.
## Solution
Add the "UAX 14 LB13 rule" to `LineWrapper::is_word_char`, which is to
not break before `! ? / ) ] } " ” » …`
## Testing
Added unit tests and manually tested myself with a long paragraph in a
gpui app.
## 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
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable (one
extra `matches!` per character in the wrap loop)
---
Release Notes:
- Fixed text wrapping so closing punctuation and slashes never begin a
wrapped line.
Co-authored-by: Lukas Wirth <lukas@zed.dev>
# Objective
`gpui_macos` implements `PlatformWindow::render_to_image` — the
MetalRenderer samples an offscreen target, so a **hidden** window can be
captured. `gpui_windows` does not implement it, so the method falls
through to the trait default and returns `Err("render_to_image not
implemented for this platform")`.
That leaves Windows consumers (tests, headless screenshot harnesses) on
`PrintWindow`-style workarounds, which require the window to actually be
on screen: a DirectComposition swap chain that never presents has no
frame to read, so you get a visible flash — or an off-screen parking
trick — for every capture.
This closes the macOS/Windows gap. Linux (`gpui_linux`) still returns
the trait default; a Blade/wgpu readback is the obvious follow-up but is
not in this PR.
## Solution
The DirectX analogue of the Metal path:
- `DirectXRenderer::draw`'s clear, scene upload and batch encoding are
factored into a shared `render(scene, background_appearance)`. `draw` is
now `render` + `present`; `render_to_image` is `render` + readback.
Nothing about how a frame is produced is duplicated between them, so the
two cannot drift.
- `render_to_image` renders into the **existing** render target —
created at window construction, so no window need ever be shown — then
copies it into a `D3D11_USAGE_STAGING` texture, `Map`s it, and converts
BGRA → RGBA. `RowPitch` padding is honoured per row; the copy loop
carries a safety comment.
- It refuses to run while `skip_draws` is set. That flag marks a pending
device-lost recovery, where the atlas still holds tile references from
the previous device — drawing before the forced re-render rebuilds them
panics in `DirectXAtlasState::texture` (the case
`WindowState::force_render_pending` documents). Returning an error beats
panicking in a capture harness.
- Gated on `cfg(any(test, feature = "test-support"))` to match the trait
method. `gpui_platform`'s `test-support` now forwards to
`gpui_windows/test-support`, the way it already does for `gpui_macos`.
No behaviour change outside `test-support`/`cfg(test)` builds beyond the
`render` factoring, which is a pure code move.
## Testing
- **Windows, functional:** an earlier revision of this patch has been in
production use since June in the `--screenshot` harness of a GPUI app I
ship ([Ferail](https://github.com/jonx/Ferail)), capturing a hidden
window (`show: false`) with no flash. BGRA→RGBA output was verified
against the on-screen rendering.
- **This exact revision** has been cross-checked from macOS only: `cargo
clippy -p gpui_windows --no-default-features --features test-support
--target x86_64-pc-windows-msvc -- -D warnings` is clean, both with and
without `test-support` (clang-cl + `xwin`; `--no-default-features` skips
only the `windows-manifest` embed-resource step, which is irrelevant
here). I'm relying on CI for a native Windows build of this revision —
happy to report back from a Windows host if that's a blocker for review.
- **Not covered by an automated test.** The behaviour needs a live D3D11
device, so it can only be exercised by a Windows-hosted test. Happy to
add one under `gpui_windows` if you'd like it — say the word and I'll
push it rather than guess at the shape you want.
- **Reviewers:** the interesting part is the staging-texture readback in
`directx_renderer.rs`. Worth a second pair of eyes on the `skip_draws`
guard and on premultiplied alpha (the swap chain is
`DXGI_ALPHA_MODE_PREMULTIPLIED`, so non-opaque captures come back
premultiplied — same as the macOS path, but it does mean
semi-transparent regions are not straight-alpha).
## 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) — n/a, no UI surface
- [ ] Tests cover the new/changed behavior — see Testing; needs a
Windows host, happy to add
- [x] Performance impact has been considered and is acceptable — the
readback path is `test-support`-only; `draw` is unchanged apart from the
`render` factoring
## Showcase
<!-- TODO: drop in the headless Windows capture (and the macOS one
beside it) -->
---
Release Notes:
- N/A
Co-authored-by: Lukas Wirth <lukas@zed.dev>
## Objective
GPUI's X11 `WM_CLASS` property omits its final NUL terminator, so any
window
manager that relies on the terminator reads the class one byte short.
`WM_CLASS` is specified by [ICCCM
§4.1.2.5](https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.2.5)
as two consecutive NUL-**terminated** strings, `instance\0class\0`.
`set_app_id` writes the separator but not the final terminator:
```rust
let mut data = Vec::with_capacity(app_id.len() * 2 + 1);
data.extend(app_id.bytes()); // instance
data.push(b'\0');
data.extend(app_id.bytes()); // class <- no trailing NUL
```
This affects Zed itself. `ReleaseChannel::app_id()` returns
`dev.zed.Zed` on
stable, so under X11 and XWayland the class is read as `dev.zed.Ze`. The
shipped
desktop file is `dev.zed.Zed.desktop`, so the association it depends on
is
exactly the string being truncated, and window-manager rules or taskbar
grouping
matching `dev.zed.Zed` silently fail to apply.
Wayland is unaffected, since the app id is set through `xdg_toplevel`
rather
than an X11 property.
## Solution
Append the missing terminator after the class string, and size the `Vec`
capacity to match (`* 2 + 2` rather than `* 2 + 1`).
## Testing
Tested manually on Arch Linux with Hyprland v0.56.2 (wlroots) via
XWayland.
**Reproducing the bug** — Zed 1.14.2, before the fix:
```
$ env -u WAYLAND_DISPLAY zeditor some-dir
$ hyprctl clients -j | jq '.[] | select(.xwayland) | .class'
"dev.zed.Zed\u0000dev.zed.Ze"
```
The same window read with `xprop`, which does not require the
terminator, shows
the intended value — confirming the property itself is malformed rather
than the
compositor misreading it:
```
$ xprop -id 0x800001 WM_CLASS
WM_CLASS(STRING) = "dev.zed.Zed", "dev.zed.Zed"
```
**Control** — on the same compositor and the same XWayland path,
Alacritty
(winit, which writes the trailing NUL) reports its class intact:
```
$ hyprctl clients -j | jq '.[] | select(.xwayland) | .class'
"Alacritty"
```
The same truncation was reproduced independently with a third-party GPUI
application using `app_id: Some("sprite")`, which reports
`sprite\u0000sprit`.
**Platforms:** verified on Linux/XWayland only. Wayland does not use
this code
path. macOS and Windows have their own `set_app_id` implementations and
are
unaffected by this change.
**Needs more testing:** confirmation on a bare X11 session (not
XWayland) and on
a non-wlroots window manager such as i3, KWin, or Mutter would be
welcome — the
malformed property is compositor-independent, but which readers visibly
truncate
it is not.
## Self-Review Checklist
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments — *none added*
- [x] The content adheres to Zed's UI standards — *N/A, no UI change*
- [ ] Tests cover the new/changed behavior — *see note below*
- [x] Performance impact has been considered and is acceptable — *one
additional
byte in a property set once per window*
"No automated test: asserting the property bytes requires a live X
server. Happy to add one if there's an existing pattern for X11 window
tests I've missed."
Co-authored-by: Lukas Wirth <lukas@zed.dev>
… layers
# Objective
olor emoji (COLR glyphs) are rasterized on the GPU by compositing each
glyph layer into a D3D11 render target that is created with
`pInitialData: None` and, until now, never cleared. The texture contents
are undefined: texels not covered by any layer quad retained garbage
(typically leftovers from previous rasterizations via the DXGI
allocation pool).
## Solution
- Clear the render target with `[0, 0, 0, 0]` (premultiplied
transparent) immediately after binding it and before drawing the layers.
## Testing
add test to test this specifically
## Showcase
before :
<img width="1634" height="754" alt="ezgif-1a339c7dea79f905"
src="https://github.com/user-attachments/assets/6e282c46-d2a4-4043-9c1c-7f4ee9232fcc"
/>
after :
<img width="1677" height="690" alt="Screenshot 2026-08-26 015248"
src="https://github.com/user-attachments/assets/c922909e-c70b-49f0-88fd-5a0c7cbe574b"
/>
---
Release Notes:
- Windows: Fixed artifacts around color emoji glyphs
# Objective
I’m building a component library with GPUI and ran into a panic when I
enabled the profiler feature for a `wasm32-unknown-unknown` build.
Dispatching an action calls std::time::Instant::now(), which has no
clock implementation on this target, and the browser reports time not
implemented on this platform. The panic then triggers RefCell already
borrowed errors and leaves the GPUI window unresponsive.
The current `main` fails to compile this feature combination because the
action profiler’s std::time::Instant does not match the scheduler-based
timestamps in the profiler journal. I want GPUI’s action profiler to use
the same cross-platform clock as the rest of the profiler and keep this
configuration covered by CI.
## Solution
Use `scheduler::Instant`, the scheduler crate's cross-platform instant
type, for action timings so they match the rest of the GPUI profiler.
Keep the existing downstream wasm check and add a separate
profiler-enabled GPUI wasm check so CI covers both feature
configurations.
## Testing
- `cargo check --target wasm32-unknown-unknown -p gpui
--no-default-features --features profiler`
- `cargo -Zbuild-std=std,panic_abort check --target
wasm32-unknown-unknown -p gpui_platform -p cloud_api_client --features
gpui/profiler`
- `cargo test -p gpui --no-default-features --features profiler
profiler::` (48 tests passed)
- `./script/clippy -p gpui`
I ran a wasm GPUI app in Edge with WebGPU. The app opens a window and
dispatches an action with `profiler` enabled. Before this change, the
action produced the clock panic and follow-on borrow errors. After the
fix change, the action completed and the window remained active.
I did not run the browser harness in Firefox or Safari, though the wasm
compile check covers the changed profiler configuration without
depending on a browser.
## 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); this PR has no UI changes
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed a crash when dispatching profiled GPUI actions in web
applications.
---------
Co-authored-by: Anthony Eid <anthony@zed.dev>
# Objective
The settings panel rendered as a persistent bar above the table, always
taking up vertical space even though most of its controls (text
alignment, filter sort order, multiline rows, dev-only toggles) are
rarely touched. @Anthony-Eid suggested reworking it into popover
## Solution
Replaced the always-visible settings panel with a single settings icon
button (next to the row-identifier toggle) that opens a popover menu
with the same controls, grouped under headers:
- Text Alignment, Filter Sort, Display
- and (behind `dev-tools`) Rendering Mode / debug toggles.
Frees up the row of vertical space previously occupied by the panel.
Similarly how it's done in git pannel
## Testing
`cargo check -p tabular_data_preview` and `--features dev-tools` both
build clean; `./script/clippy` clean.
Manually opened the popover and toggled each setting (alignment, filter
sort, multiline rows) to confirm they still apply and persist while the
popover stays open.
## Demo
| Before | After |
| --- | --- |
| <img width="720" height="427" alt="image"
src="https://github.com/user-attachments/assets/0af2e7a5-ab70-4cb8-b106-6cecd5a5752d"
/> | <img width="721" height="384" alt="image"
src="https://github.com/user-attachments/assets/c2938156-938e-4f05-b842-08e599e4bcdd"
/> |
Line numbers vs Row number
<img width="572" height="253" alt="image"
src="https://github.com/user-attachments/assets/1ccc5e8b-ffa3-4a8f-9dcb-07cba340306c"
/>
<img width="571" height="412" alt="image"
src="https://github.com/user-attachments/assets/ecb3c686-cb4c-41e7-918c-b8a5e123a452"
/>
## 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
---
~~# NOTE TO REVIEWER (release notes related)~~
~~This feature is still under feature flag, but I've head from
@Anthony-Eid few weeks ago that it's already good enough to go public.
Do we make it live and I keep release notes or should I remove release
notes due to some blockers?~~
Disabled feature flag in
https://github.com/zed-industries/zed/pull/62773
---
Release Notes:
- Refine tabular data preview setting UI
---------
Co-authored-by: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
Extends the existing GPUI frame-duration telemetry with the average time
from a window's first invalidation through presentation.
- Record dirty-to-present durations in each window profiler
- Include the declared root entity type name on window handles
- Add the average and root type to the existing five-minute `Frame
Duration Report` event
Testing:
- `cargo fmt --all -- --check`
- `cargo test -p gpui --features profiler
records_dirty_to_present_durations`
- `cargo check -p gpui --no-default-features`
- `cargo check -p input_latency_ui -p zed`
Release Notes:
- N/A
OpenAI can return structured provider failures either as Responses
stream events or as non-success HTTP responses. The streaming path
classified the provider's error code, while the HTTP path treated the
body as opaque and classified only its status. As a result, exhausted
credit could look like a retryable rate limit, and prompt policy
rejections looked like generic provider failures.
This change gives billing failures their existing payment-required
category and adds a content-policy category for `invalid_prompt` and
`cyber_policy`. It also parses OpenAI's standard HTTP error envelope and
sends both HTTP and stream failures through the same classifier while
preserving the original status, code, and message. Responses with
another body format retain the previous status-based fallback.
Release Notes:
- N/A
# Objective
Get some speedups by using unstable sortts.
Supersedes #58761 after rebasing onto current `main`.
## Solution
replace `.sort()` by `.unstable_sort()`.
## Testing
All tests pass in affected crates:
```
cargo test -p edit_prediction_context -p editor -p git -p language -p language_tools -p migrator
```
## 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
GPUI's keyboard handling on the web was previously managed with a hidden
`<input>`, and raw key events from this element were forwarded to GPUI's
key handling code.
However, this approach had some issues:
- it's not sufficient for key handling on android - gboard (the default
keyboard) does not produce raw key events, and instead presents as an
IME. The existing code did handle IMEs, but the implementation was
minimal, and didn't support gboard
- there was a bug relating to how state mapped between GPUI elements and
the underlying `<input>` that allowed for edits to be applied to stale
element text
- gboard seems to be picky about exactly how events are reported back,
and would lose state if we naively pass events back as soon as they are
ready. Now, we coalesce edits to the underlying `<textinput>` to at most
1 per JS event loop cycle
---
Release Notes:
- N/A or Added/Fixed/Improved ...
Zed saves the selected external agent per workspace and as an app-wide
last-used agent. Uninstalling the agent leaves those saved values in
place, so the Agent Panel could restore an agent that no longer exists
and fail to start a thread.
For local projects, this changes restoration behavior in these cases:
- A new workspace ignores an uninstalled global last-used agent.
- A reopened workspace ignores its uninstalled agent. It uses an
installed global agent if one exists, otherwise it uses the Zed Agent.
- A project opened in the same window does not copy an uninstalled agent
from the previous project.
- An unsent draft keeps its text and switches to the selected fallback
agent if its original agent was uninstalled.
Two boundaries remain unchanged:
- A thread with a saved session stays bound to its original agent so it
can resume after the agent is reinstalled.
- Remote projects keep their saved external-agent selection because this
change only detects local uninstalls.
Release Notes:
- Fixed the Agent Panel selecting an uninstalled external agent when
opening a project or restoring an unsent draft, which prevented the
thread from starting.
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
## Problem
When the agent overwrites an existing file (for example via `write_file`
in write mode, or "rewrite this file"), the edits panel reported the
change as purely added lines — e.g. `+237 -0` — and the review diff did
not show any of the removed lines.
The root cause is in `action_log::track_buffer_internal`: a created
buffer always used an empty rope as its diff baseline, even when it was
overwriting a file that already had content. Diffing the new content
against an empty base makes every line look like an addition and hides
all deletions.
This only affects the edits-panel stats and the Review diff (the
`action_log` path). The inline per-tool-call diff card already captures
its own pre-edit baseline and was correct, which is likely why this went
unnoticed.
## Solution
When tracking a created buffer that has captured existing file content
(`TrackedBufferStatus::Created { existing_file_content: Some(_) }`), use
that content as the diff baseline and seed `unreviewed_edits` from a
line diff against it, so removed lines are counted and displayed.
Genuinely new files (`existing_file_content: None`) keep the previous
empty-baseline behavior. Reject/undo are unaffected because they rely on
`existing_file_content` and the captured agent content rather than the
diff baseline.
## Testing
Added `test_overwriting_file_counts_removed_lines`, asserting
`diff_stats` reports both added and removed lines for an overwrite.
Updated the display assertions in `test_overwriting_files`,
`test_overwriting_previously_edited_files`, and `test_deleting_files` —
these previously asserted the buggy all-added/empty-base output and now
assert the correct `Modified` hunks with the original content. Their
reject assertions are unchanged and still pass.
`cargo test -p action_log` (30 passed), `cargo test -p agent write_file`
(19 passed), and `./script/clippy -p action_log` are all clean.
---
Release Notes:
- Fixed the agent edits panel and review diff showing whole-file
overwrites as entirely added lines with no removals.
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
# Objective
- Streaming completions fail for any model served through Mistral that
omits `object` in its stream chunks. Zed's `StreamResponse` requires the
field, so the response is rejected before any text is rendered and the
agent panel shows `missing field object at line 1 column 167`.
- This now matters because Mistral serves Z.ai's GLM 5.2 (`glm-5-2` /
`zai-glm-5-2`), which is usable in Zed via a custom model entry, but is
unreachable today because of this deserialization failure. Its first
stream chunk omits `object`, while every subsequent chunk includes it.
- No linked issue.
## Solution
- Mark `StreamResponse::object` as `#[serde(default)]` in
`crates/mistral/src/mistral.rs`.
- The field is never read anywhere in the codebase, so defaulting it to
an empty string when absent loses nothing and keeps the struct's shape
unchanged for serialization.
## Testing
- `cargo check -p mistral` passes.
- Verified end to end in Zed: with the settings below, selecting "GLM
5.2" in the agent panel and sending a message previously errored
immediately; after this change the response streams normally.
```json
"language_models": {
"mistral": {
"available_models": [
{
"name": "zai-glm-5-2",
"display_name": "GLM 5.2",
"max_tokens": 1048576,
"supports_tools": true,
"supports_images": false,
"supports_thinking": true
}
]
}
}
```
- Tested on macOS only. The change is platform-independent, so no
platform-specific risk 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 (none added)
- [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) (no UI change)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
(deserialization only, no measurable cost)
---
Release Notes:
- Fixed streaming failing for Mistral-hosted models that omit the
`object` field in stream chunks, such as GLM 5.2.
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
## Summary
Follow-up to KevinLaveto's #58213 (closed due to no response). Adds two
ways to restart a broken external ACP agent connection from the Agent
Panel, without restarting Zed:
1. A retry button in the toolbar, shown when the active thread's agent
is in `LoadError` state.
2. A "Reload Agent" entry in the panel options menu, available whenever
an agent thread is active.
Both call `ConversationView::retry_connection`, which drops the cached
connection entry (via `AgentConnectionStore::restart_connection`) so the
next request spawns a fresh server process, then rebuilds the thread
state. This covers both launch failures and agents that died after
connecting.
Closes the scenarios in #52102, #55501, #60213, #61386 and
zed-industries/zed#62668.
## Showcase
https://github.com/user-attachments/assets/51b5b588-187d-45fb-a35c-b7d625644b30
Release Notes:
- Added the ability to reload a broken external agent connection from
the Agent Panel without restarting Zed.
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
# Objective
Some OpenAI-compatible models end a stream with a usage-only chunk
(`"choices": []`) where the cache token fields are `null`. Such as, the
`deepseek-v4-flash` model in `OpenCode Go` provider.
Zed chokes on it and kills the whole turn:
```text
ERROR [open_ai] Failed to parse OpenAI response into ResponseStreamResult: `data did not match any variant of untagged enum ResponseStreamResult`
Response: `{"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1787286372,"model":"deepseek-v4-flash","choices":[],"usage":{"prompt_tokens":12191,"completion_tokens":95,"total_tokens":12286,"prompt_tokens_details":{"cached_tokens":0,"cache_write_tokens":null}}}`
ERROR [agent::thread] Turn execution failed: data did not match any variant of untagged enum ResponseStreamResult
```
## Solution
`PromptTokensDetails` stored these as `u64` with `#[serde(default)]`,
which handles a missing field but not `null`. Switched them to
`Option<u64>` so `null` parses to `None`, and updated the one consumer
(`OpenAiEventMapper::map_event`) to `.and_then(...).unwrap_or(0)`.
Behavior is unchanged.
## Testing
- Added a regression test using the failing `null` chunk.
- `cargo test -p open_ai` — 78 tests + doc-test pass.
- `./script/clippy -p open_ai` — clean.
## 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 and icon
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed a crash when an OpenAI-compatible model sends a usage-only
stream chunk with null cache token fields.
# Objective
`test_select_agent_action_updates_visible_draft` (added in #60452) is
order-dependent: it passes at seed 0, which CI runs, but fails on others
(e.g. 7, 11). Any unrelated change that schedules one extra task shifts
the deterministic test scheduler enough to flip it at seed 0 too — which
is how it surfaced, while working on #61009. The bug it exposes is real
and pre-existing (same class as #61142):
`set_selected_agent_and_persist` fires a detached write of the global
last-used agent each time it's called. Activating the draft persists the
default agent while selecting one persists the choice, and with several
writes in flight the one that finishes last wins — so the default can
clobber the user's selection, which then loses it on the next launch.
## Solution
Chain the writes so they land in call order and the most recent
selection is the one left persisted.
## Testing
- `test_select_agent_action_updates_visible_draft` now runs 30 seeds to
cover both orderings.
## 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 the agent panel occasionally forgetting the selected agent
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
# Objective
The Markdown preview has been collecting complaints about how it reads.
Body text is dimmed and shrunk, headings carry no weight, and paragraphs
sit at a 1.3 line height, so a long document turns into a wall. This
came up in the feedback on #58465, in #58364, and in the discussion at
#43384. This PR changes the defaults so the preview reads like GitHub's
rendering. It adds no settings. The feedback on #60402 and #59544 was
that a good default beats more knobs, and I agree.
## Solution
What changed in the preview:
- Body text renders at the configured `markdown_preview_font_size`.
Before, it rendered at 92% of that value in a blended muted color. It is
now full contrast at 1:1.
- Line height is 1.5 across body text, lists, and blockquotes.
Paragraphs used to override the base line height with `rems(1.3)`.
- Paragraphs, lists, blockquotes, rules, and code blocks are spaced 16px
apart.
- Headings are semibold, from h1 at 1.75rem down to h6 at 0.85rem. Only
h1 and h2 keep a bottom border, same as GitHub. Dropping the h3 border
addresses the comment on #58465 that a line under every heading is too
much.
- Inline code backgrounds are rounded chips instead of square text-run
backgrounds. The chips are tracked in rendered indices so they cover the
code glyphs exactly, and they extend only 1px past the glyphs so the gap
between words stays at full width.
- Code blocks get 12px padding and 6px corners. Links lose their
background tint. Blockquote text is muted. Table cells get 10px
horizontal and 4px vertical padding.
The first commit makes block spacing, table cell padding, and inline
code backgrounds style-driven, with defaults that keep the agent panel,
tooltips, and editor rendering exactly as they are today. The second
commit sets the preview's values. I split it this way so the mechanism
can be reviewed on its own, and so the chip painting can move to its own
PR if you would rather take it separately.
## Testing
- Added a test in the `markdown` crate that checks the inline code chips
cover exactly the code span glyphs, and that no chips are produced when
the corner radius is zero.
- Ran `cargo test -p markdown` and `cargo test -p markdown_preview`
locally, plus `./script/clippy`.
- Smoke-tested the preview on Linux (Arch) against a long document with
headings, lists, blockquotes, tables, and inline code. The screenshots
below are from that run.
- To try it, open any Markdown file and run `markdown: open preview`. It
is also worth glancing at the agent panel and a hover tooltip to confirm
those still look the same, since they share the renderer but keep the
old defaults.
- I could not test on macOS or Windows. Nothing here is
platform-specific, but the chip inset is a fraction of the line height,
so it would be good to have a second pair of eyes on a different font
stack.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments (there are none)
- [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 | After |
|--------|-------|
| <img width="1420" height="1782" alt="zed_before"
src="https://github.com/user-attachments/assets/84382a52-99d1-43ba-9ee6-d6c3c6cf0cfa"
/> | <img width="1346" height="1800" alt="zed_afterv2"
src="https://github.com/user-attachments/assets/c73c076f-9ba4-4b7d-92dc-20855af595f5"
/> |
Release Notes:
- Improved Markdown preview styling with better typography, spacing, and
rounded inline code backgrounds.
---------
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
## Summary
- Select the first Git panel entry from the visible row indices instead
of treating an entry index as a visible-row position
- Avoid selecting files hidden by collapsed sections in both tree and
flat views
- Add a regression test for collapsing the only populated section in
tree view with no active selection
Fixes ZED-AZ3
Fixes ZED-B0Z
Release Notes:
- Fixed a crash in the Git panel when collapsed sections were used in
tree view.
# Objective
Prevent extension feature tags from overlapping card actions when
horizontal space is constrained.
## Solution
Let the metadata row shrink and clip while reserving space and a gap for
the action buttons.
## Testing
- `cargo fmt --check`
- `cargo test -p extensions_ui`
- `./script/clippy -p extensions_ui`
- `git diff --check`
## 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
- [ ] Tests cover the new/changed behavior (layout-only change;
before/after videos to follow)
- [x] Performance impact has been considered and is acceptable
## Showcase
Before
https://github.com/user-attachments/assets/4268ff59-d1ec-4081-b977-c748e2dd8351
After
https://github.com/user-attachments/assets/d8617b90-45e2-41b4-9504-7eb8a808e5e2
---
Release Notes:
- Fixed extension tags obscuring action buttons on extension cards.
Update the Stable, Preview, Dev, and Nightly Developer ID provisioning
profiles to authorize the renewed Developer ID Application certificate.
The Dev and Nightly channels share the same App ID and regenerated
profile.
Verification:
- Confirmed all profiles authorize certificate serial
`0F5EFD4C811CF974BA2849312971F5CC`.
- Built the Stable Intel artifact through the signed and notarized CI
workflow.
- Confirmed the generated app signer matches the certificate authorized
by its embedded profile.
- Passed strict deep code-signature verification.
Release Notes:
- N/A
# Objective
- Clean up a stray debug `println!("{:?}", std::any::type_name::<R>());`
statement in `Statement::single` that was left in production code.
- Prevent unintended output to standard output whenever single-row
database queries are executed.
## Solution
- Removed the `println!` call from `crates/sqlez/src/statement.rs`
inside `Statement::single`.
## Testing
- Existing unit and integration tests in `crates/sqlez` cover
`Statement::single` query execution.
## 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
Closes#61005
The bookmark store notified on every change, but nothing was listening.
Editors only repainted their bookmark icons because the toggle actions
happened to notify the editor directly - so workspace::ClearBookmarks,
which never touches an editor, left stale icons in the gutter until
something else caused a repaint.
Now editors observe the bookmark store, so any change repaints them.
This also fixes toggling a bookmark in one split not updating another
split showing the same file.
Release Notes:
- Fixed bookmarks not disappearing from the gutter immediately when
running `workspace::ClearBookmarks`.
Language model providers can reject a request inside an otherwise
successful response stream. Those failures were represented by
overlapping completion-error variants and provider-specific code enums,
and OpenAI Responses errors such as `cyber_policy` fell through to
`Other`. Agent sessions consequently retried permanent provider
rejections and user interfaces lost the provider's actionable message.
This gives every provider-originated failure one shared representation.
`ProviderRejection` preserves the provider, exact message, retry delay,
raw provider code, and an HTTP status only when one was actually
reported. `ProviderErrorCategory` supplies the normalized meaning used
by shared UI and policy code. Provider adapters map their native wire
errors into that category explicitly, so typed SDK exceptions and errors
embedded in successful streams no longer fabricate HTTP statuses.
Retry selection is independent from error construction.
`LanguageModelCompletionError::retry_delay` owns transient
classification and a bounded schedule of 5, 10, 20, then 40 seconds. A
provider-supplied `retry_after` overrides that schedule. The scheduling
layer adds positive jitter of up to 10%, while interactive requests and
evaluation runs retain their own attempt budgets. Permanent status-less
errors such as `cyber_policy` do not retry. OpenRouter also keeps its
numeric body code distinct from an optional HTTP status, including for
errors embedded in a stream.
Testing performed:
- `corgi test -p language_model_core`
- `corgi test -p anthropic` (33 tests)
- `corgi test -p open_ai` (77 tests)
- `cargo nextest run -p open_router -p copilot_chat -p
language_models_cloud -p language_models` (125 tests)
- `cargo nextest run -p agent -p agent_ui` (1,153 tests; 11 skipped)
- `cargo check -p open_router -p copilot_chat -p language_models_cloud
-p language_models -p agent -p agent_ui`
- `./script/clippy -p language_model_core -p anthropic -p open_ai -p
open_router -p copilot_chat -p language_models_cloud -p language_models
-p agent -p agent_ui` completed the release Clippy phase; Cargo Shear
then reported the repository's existing warnings
- `cargo fmt --all --check`
Suggested .rules additions
None.
Release Notes:
- Fixed permanent language model provider rejections being retried and
displayed without their actionable provider message.
Prewarming cosmic-text font cache completely eliminated expensive
`get_font_matches` calls and thus reducing the time it takes to call
`cosmic_text::shape::ShapeLine::new`.
Release Notes:
- Improved rendering performance on Linux by prewarming font match
caches.
This matches `GoLocator` and `NodeLocator`
# Objective
The problem: `env` variables defined in `tasks.json` aren't used when
the task runs in debug mode.
Example:
```json
[
{
"label": "pytest (custom)",
"env": {
"DATABASE_URL": "...",
},
"command": "$ZED_CUSTOM_PYTHON_ACTIVE_ZED_TOOLCHAIN",
"args": [
"-m",
"pytest",
"$ZED_CUSTOM_PYTHON_TEST_TARGET",
],
"tags": ["python-pytest-class", "python-pytest-method"],
"cwd": "$ZED_WORKTREE_ROOT",
},
]
```
When a test runs normally, the `DATABASE_URL` variable is set correctly.
When the test runs in debug mode, the variable is not set.
## Solution
`PythonLocator` copies the `env` value from the `TaskTemplate`. This
matches other locators, such as `GoLocator` and `NodeLocator`
## Testing
- Did you test these changes? If so, how?
- The existing integration test is updated to verify the change
- Are there any parts that need more testing?
- No
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- Run `cargo test -p project --test integration
debugger::python_locator::test_python_locator`
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
- Linux (NixOS), the change is platform-agnostic.
## 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
N/A
---
Release Notes:
- Fix `PythonLocator` to pass through `env` variables from the task
template
# Objective
Fixes#58104.
When using the `fold_at_level` action (default keybind: Ctrl-K
Ctrl-(1-9) on Windows) on a function with its arguments split across
multiple lines (see the example in #58104), only the arguments would be
folded, while the function body remained expanded and only blocks at the
next level inside the function were folded.
To reproduce:
1. Open a `.rs` file (or any language with similar folding behavior).
2. Add a function with multiline arguments:
```rust
fn test(
a: i32,
) {
let b = a;
}
```
3. Run `fold_at_level` with level `1`.
4. Observe that the argument list is folded, but the function body
remains expanded.
## Solution
When iterating through foldable ranges, `fold_at_level` advanced the
search position to the line after the folded crease. This caused it to
skip any crease starting on the same line as the end of the previous
one. The fix keeps the search position on the last line of the folded
crease instead, allowing consecutive creases to be processed.
## Testing
Automated: Added 1 test covering chained foldable blocks, including
functions with multiline arguments and if/else blocks.
Manual: Verified that invoking `fold_at_level(1)` on a function with
multiline arguments folds both the argument list and the function body.
## 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="211" height="107" alt="image"
src="https://github.com/user-attachments/assets/dbfbbdfa-faee-42f3-ac25-0581a796e124"
/>
**After:**
<img width="221" height="41" alt="image"
src="https://github.com/user-attachments/assets/6746b341-0587-48a4-908a-e743c974cae9"
/>
## Release Notes:
- Fixed: `fold_at_level` not folding function bodies when arguments span
multiple lines
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
`wasmtime-wasi` was locked to 36.0.12, which resolved the vulnerable
`cap-primitives` 3.4.4 release. That version is affected by a
trailing-slash symlink handling issue that can bypass capability
filesystem confinement.
Raise the workspace's minimum `wasmtime-wasi` version to 36.0.14 and
refresh the Wasmtime dependency family. This resolves `cap-primitives`
to the patched 3.4.6 release while remaining on Wasmtime's existing 36.x
line.
Verified with `cargo check --locked -p extension_host` and by confirming
the reverse dependency tree resolves `cap-primitives` 3.4.6 through
`wasmtime-wasi` 36.0.14.
See
https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-vqjp-4c8c-hfgg
Release Notes:
- Fixed a potential filesystem sandbox escape when running extensions.
GitHub Enterprise Cloud with Data Residency requires API requests to use
the tenant-specific `api.<subdomain>.ghe.com` hostname. Copilot Chat
previously treated configured enterprise hosts like GHES, which produced
an unsupported `/api/graphql` URL, and stored all OAuth credentials
under the same GitHub.com key.
Use the GHE.com API hostname shape for configured enterprise tenants and
scope stored credentials by GitHub hostname. Changing the configured
host now clears active authentication state and loads credentials for
the new tenant, with guards against stale asynchronous loads.
Enterprise endpoint discovery failures are now surfaced instead of
silently falling back to the public Copilot endpoint. GitHub.com retains
its existing endpoint and fallback behavior.
Release Notes:
- Fixed GitHub Copilot Chat authentication and API routing for GitHub
Enterprise Cloud
Closes https://github.com/zed-industries/zed/issues/48468
## Solution
This change addresses a macOS shutdown ordering issue where closing the
last window with the native close button could clear session state
before persistence tasks complete, causing next launch to open no
project/untitled.
-
[crates/workspace/src/workspace.rs](/Users/caleul/Documents/Work/Projects/zed/crates/workspace/src/workspace.rs)
- Make `save_last_workspace` dynamic on macOS based on whether closing
the last window will quit the app (`on_last_window_closed == quit_app`).
- Keeps `save_last_workspace` true only when the window close path is
equivalent to quit, so last-session data remains valid only in the
intended scenario.
-
[crates/workspace/src/multi_workspace.rs](/Users/caleul/Documents/Work/Projects/zed/crates/workspace/src/multi_workspace.rs)
- In `close_window`, before removing the window, explicitly flush:
- workspace serialization tasks
- pending workspace removal tasks
- multi-workspace serialization task
- This prevents losing in-memory state on fast close/quit paths.
-
[crates/session/src/session.rs](/Users/caleul/Documents/Work/Projects/zed/crates/session/src/session.rs)
- Guard `app_will_quit` to only persist window stack when non-empty,
preventing empty-stack state from being written during close path that
destroys the window stack first.
## Testing
- Did you test these changes?
- Manual repro/fix confirmed in local testing flow by the previous
author.
- Please confirm after merge with:
1) open 2–3 projects in workspace sidebar
2) set `on_last_window_closed: "quit_app"` and `restore_on_startup:
"last_session"`
3) close with red X
4) relaunch app and verify sessions restore all active workspaces
- Are there any parts that need more testing?
- Multi-window mixed close scenarios and one-window with dirty
non-serializable items.
- Regression for non-quit close mode (`on_last_window_closed: "close")
to ensure behavior remains unchanged.
- How can others test this?
- Follow the 4-step repro path above on macOS.
- Check logs for `timed out waiting on app_will_quit` in
`~/Library/Logs/Zed/Zed.log` and confirm no restore regression.
- Platforms tested:
- Verified for macOS (target path of bug).
- Not validated on Linux/Windows in this patch path.
## 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/UX diff here)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes
- Closing last macOS window via the red close button now correctly
preserves and restores last-session workspace state when configured to
quit on last-window close.
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
# Objective
The Elixir extension has been shipping ElixirLS's debug adapter for a
while now. This finally documents it, providing examples on how to use
it with Zed too.
This also includes information about how the extension activates the
language servers/debug adapter, and an assortment of other minor changes
## 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
- [ ] Performance impact has been considered and is acceptable
---
Release Notes:
- N/A
---------
Co-authored-by: Finn Evers <finn.evers@outlook.de>
Google has announced shutdown dates for the Gemini 2.5 models and Gemini
3.1 Flash-Lite, but they remained available in the Google AI model
picker. Deprecated aliases could also still be deserialized as supported
models.
Remove those deprecated models and aliases from the built-in model list.
Gemini 3.5 Flash-Lite is now the default and fast model, matching
Google's recommended replacement for Gemini 3.1 Flash-Lite.
Release Notes:
- agent: Remove deprecated Gemini models
Follow-up to #62670, which added Gemini 3.7 Flash.
Adds Gemini 3.5 Flash-Lite (`gemini-3.5-flash-lite`), Google's cheapest
3.5 model and the one they currently offer for free on the API free
tier.
It keeps the 1 million token context window and the 64k output limit.
Like Gemini 3.1 Flash-Lite it supports Minimal, Low, Medium and High
thinking, and defaults to Minimal. `disabled_thinking_level` in
`completion.rs` already returns Minimal for it through the generic
`gemini-3` case, so no change was needed there.
The provider builds its model list from `google_ai::Model::iter()`, so
adding the enum variant is enough for it to show up in the model
dropdown.
Release Notes:
- Added Gemini 3.5 Flash-Lite to the Google AI models
# Objective
Pressing the recent projects shortcut right after launching Zed opens
the picker, which then dismisses itself a moment later. The terminal
panel takes focus after the keystroke, and the picker cancels on blur.
## Solution
Two paths focused the panel unconditionally at the end of async startup
work: the tail of `TerminalPanel::restore_serialized_state`, and
`RevealStrategy::Always` in `add_terminal_shell` (which
`finish_restoration` uses to spawn the default shell). Both now check
`has_active_modal`, as `add_center_terminal` already did. For the shell
path the check is at reveal time, because the spawn is itself async.
## Testing
Three tests in `terminal_panel.rs`. Two of them fail with the source
change reverted. Manually tested on Linux.
## Before:
https://github.com/user-attachments/assets/66ab9860-b381-46da-8f85-f7a58c5157e6
## After:
https://github.com/user-attachments/assets/57583fe2-3c8a-40e0-9130-bbb612c1e158
Release Notes:
- Fixed the terminal panel dismissing an open modal when terminals
finish restoring during startup