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
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 ...
The markdown preview used `vertical_scrollbar_for()` which always sets
the scrollbar to auto-hide mode, ignoring the user's `scrollbar.show`
setting.
This fix uses
`Scrollbars::for_settings::<EditorSettingsScrollbarProxy>()` so the
markdown preview respects the same editor scrollbar visibility setting
as regular editor panes (always, auto, system, never).
Changes:
- Replaced `vertical_scrollbar_for()` with `custom_scrollbars()` +
`Scrollbars::for_settings::<EditorSettingsScrollbarProxy>()` in the
markdown preview render method
Fixes#60380
Release Notes:
- Fixed `scrollbar.show` setting not being respected in Markdown
previews
---------
Co-authored-by: Akshit Jain <akshitj11@users.noreply.github.com>
Co-authored-by: MrSubidubi <finn@zed.dev>
# 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>
Label | Description
-- | --
area:integrations/git/panel | Feedback for the Git panel UI and
behavior.
area:gpui/graphics/wgpu | Feedback for GPUI's WGPU graphics backend,
including initialization and rendering.
area:breadcrumbs | Feedback for editor path and symbol breadcrumbs.
area:title bar | Issues suitable for title bar.
area:ai/agent thread/checkpoints | Feedback for Agent checkpoint
creation, comparison, restoration, and related Git behavior.
area:ai/agent thread/tools | Feedback for built-in Agent tools,
including execution, permissions, inputs, and outputs.
Release Notes:
- N/A
While building Zed with nightly rustc I've noticed it doesn't compile
because of good old pathfinder_simd. It also emits a bunch of warnings
about use of f64 literals where f32 is expected, so I've fixed them - it
should make future upgrades more straightforward.
# Objective
Prevent the thread from automatically scrolling to the end when
expanding a "Context Compacted" message at the end of the thread, as we
suspect the user would likely be trying to read it from top to bottom.
## Solution
Update how the `ThreadView` behaves when expanding a "Context Compacted"
message, namely by updating `ThreadView::toggle_compaction_expansion` in
order to anchor the scroll position, which disables the tail following
behavior, ensuring that the scroll offset is kept.
In order to better support this, a new method is introduced to
`gpui::elements::list::ListState`, `anchor_scroll_position`, which takes
the current scroll offset and scrolls to it which, in turns, disables
the following behavior.
Another option would be to simply introduce
`gpui::elements::list::ListState::stop_following` but I'm not sure
whether we'd want to expose that implementation detail, even though we
already have `gpui::elements::list::ListState::is_following_tail` .
## Testing
Tested both manually as well as introduced a new test for
`ListState::anchor_scroll_position`.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
<details>
<summary>Before</summary>
https://github.com/user-attachments/assets/cf8ee10f-9c74-429d-afed-7a39ccd71c67
</details>
<details>
<summary>After</summary>
https://github.com/user-attachments/assets/78edfca9-099a-4fdb-9c4e-75f790096957
</details>
---
Release Notes:
- Improved expanding "Context Compacted" messages in the Agent Panel to
ensure that the scroll position is preserved, instead of automatically
scrolling to the end of the thread.
---------
Co-authored-by: Joseph T. Lyons <JosephTLyons@gmail.com>
Auto-hiding scrollbars currently reveal whenever their prepaint geometry
changes. That matches browser behavior, but it causes continuously
updating views to restart the visibility timer on every content change
even when the user has not scrolled.
Add a per-instance `ScrollbarRevealPolicy`. The default
`ScrollOrContentChange` policy preserves the existing behavior for all
current callers. Views with continuously growing content can opt into
`ScrollOnly`, which reveals only when the offset changes independently
of content size, including when a view remains anchored to the bottom.
Tests pin the default behavior, the opt-in behavior, ordinary scrolling,
stationary content growth, bottom-anchored growth, and scrolling while
content is growing.
Release Notes:
- N/A
# Objective
Fixes FR-149
- Make word movement and selection predictable around punctuation.
- Prior art: [#58882](https://github.com/zed-industries/zed/pull/58882),
[#61916](https://github.com/zed-industries/zed/pull/61916), and their
revert [#62213](https://github.com/zed-industries/zed/pull/62213).
## Solution
- Treat words and punctuation runs as separate movement units in both
directions.
- This adds a stop between leading punctuation and its following word,
such as `.▏foo`.
## Testing
- Added table-driven coverage for reported examples, delimiters,
operators, punctuation runs, and Unicode punctuation.
- Tested editor movement, selection, and deletion behavior.
## 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 word movement and selection stopping on the wrong side of
punctuation.
In the current Python Tree-sitter `highlights.scm`, dunder variables
(e.g., `__name__`, `__file__`) are not highlighted.
This PR adds a regex `#match?` so all dunder variables are highlighted
under `@attribute.special`.
An alternative solution would be to use `#any-of?` and list all dunder
variables.
| Before | After |
|
:---------------------------------------------------------------------------------------------------------------------------------:
|
:--------------------------------------------------------------------------------------------------------------------------------:
|
|<img width="623" height="895" alt="image"
src="https://github.com/user-attachments/assets/ad7b67ba-541c-40ac-a38a-cc863e417fcc"
/> | <img width="612" height="903" alt="image"
src="https://github.com/user-attachments/assets/a91f5efb-230a-4214-b16b-04278b3ef9ee"
/>|
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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Added Python dunder variables highlighting via `attribute.special` in
themes.
## Objective
Add functional horizontal scrollbars to wide Markdown tables, matching
the existing code-block scrollbar pattern.
Fixes#61437
PR was talked about in https://github.com/zed-industries/zed/pull/61698
## Implementation:
- Added a `BTreeMap<usize, ScrollHandle>` to `Markdown` for table scroll
handles, keyed by source-range start.
- During `MarkdownElement` layout, each table container is connected to
a `Scrollbars` widget configured for the horizontal axis, using a stable
ID derived from `("markdown-table-scrollbar", range.start)`.
- The inner table div uses `overflow_x_scroll()` + `track_scroll()` with
`restrict_scroll_to_axis`.
- Handles for removed tables are discarded after re-rendering.
- No public API, setting, schema, or migration changes required.
## Recording
https://github.com/user-attachments/assets/cd0f635b-a485-4de8-9059-40a05f256fb1
## Release Notes:
- Improved navigation of wide Markdown tables with horizontal
scrollbars.
---------
Co-authored-by: MrSubidubi <finn@zed.dev>
# Objective
Zed's `wgpu` backend currently allocates memory for fonts, even if they
are statically available in memory. Zed embeds 8 fonts, which accounts
for about 1.6 megabytes of memory. This change should reduce memory
usage by that amount on the `wgpu` backend. (Linux and Web)
For more context, this is the function signature for `load_font_data`:
```rust
/// Loads a font data into the `Database`.
///
/// Will load all font faces in case of a font collection.
pub fn load_font_data(&mut self, data: Vec<u8>) {
self.load_font_source(Source::Binary(alloc::sync::Arc::new(data)));
}
```
## Solution
Instead of calling `fontdb::Database::load_font_data`, which needs an
owned `Vec`, use `load_font_source`, which allows us to create an `Arc`
ourselves, which doesn't require ownership of the underlying data.
## Testing
All tests pass
## 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:
- Slightly improved memory usage on Linux
# Objective
This PR aims to fix these related deficiencies with the terminal tool's
working directory path resolution:
- Fix#60014
- Fix#60040
- Fix#60043
## Solution
- Use `project.path_style(cx).is_absolute(dir)` instead of
`Path::is_absolute`. The latter follows the path semantics of the host
which fails to recognize `/home/...` etc as an absolute path on Windows,
we want to follow the path semantics of the *project*.
- Splits out path resolution into a separate pure function
`resolve_cd_in_worktrees` to enable comprehensive cross-PathStyle
testing
- The function unifies path prefix checking across both absolute and
relative modes, using the `path_style.strip_prefix` (which uses the
RelPath util internally) which fails when subdirs try to escape with
`..`.
- Add comprehensive tests for absolute/relative modes, path styles, and
potential `..` escapes
- Enables targeting subdirectories with both absolute and relative path
modes (separate commit)
- Also makes both path modes use `project.worktrees`. Before, the
relative path mode used `project.visible_worktrees`.
## Testing
Here's how I tested these changes:
- Added a new test: `test_resolve_cd_uses_project_path_style` with a
comprehensive collection of assertions for `..` edge cases
- Run `cargo test -p agent` and `./script/clippy -p agent` on:
- Linux
- Windows in Ubuntu WSL
- Windows native
- Build and run Zed on:
- Linux
- Windows
- Verify that the three issues are fixed:
- `cd` param path traversal escapes are blocked
- Linux local project
- Windows local project
- Windows remote WSL project
- Allows absolute paths when running on Windows in a WSL project
- `cd` param may target subdirectories in both absolute and relative
path modes
- Linux local project
- Windows local project
- Windows WSL project
- Check that different models find the updated tool descriptions to be
understandable and usable the first time. Used the prompt below to get
the following models to test path resolution on each of: Windows local,
Windows WSL, Linux. All models except GPT-5-nano one-shot the below
prompt.
- Claude Sonnet 4.6
- Claude Opus 4.8
- Claude Haiku 4.5
- GPT-5.5 pro
- GPT-5-nano - got there eventually but is confused by the design of the
cd param, it expects that if you set cd to `my-project` that it will
target the directory `my-project/my-project`.
- Gemini 3.1 Pro
- Gemini 3.5 Flash
- Grok 4.3
- Grok 0.1 build
<details>
<summary>Agent test prompt</summary>
<blockquote>
List the current directory contents and pick one subdir.
Then use that information and the project path details you were provided
to execute `pwd` with the terminal tool's cd parameter set to the
following values:
- current project absolute path
- current project name
- current project absolute path + an existing subdir
- current project name + an existing subdir
- absolute path to user's .ssh dir
- current project absolute path + whatever necessary path traversal .. +
.ssh segments to target the user's .ssh dir
Then tell me what happened.
Then stop.
</blockquote>
</details>
## 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 terminal tool targeting absolute directories on Windows host
connected to a remote SSH / WSL project
- Fixed terminal tool not blocking path traversal escapes
- Improved terminal tool targeting project subdirectories
---------
Co-authored-by: Richard Feldman <richard@zed.dev>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
# Objective
Fix several Git panel focus issues:
- Directional navigation could not move focus into the History tab.
- History focus state was not updated when the panel received focus.
- Switching back to Changes could leave focus on the panel instead of
the commit editor.
Fixes#62211
## Solution
- Use the Git panel root as the activation focus target when History is
active.
- Reuse the panel's focus handle for focus events.
- Use the appropriate activation target when switching tabs.
## Testing
- Add a regression test
`test_history_tab_pane_navigation_focuses_rendered_panel` for navigating
between the editor and History.
## 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
after fix:
[2026-08-05
22-45-38.webm](https://github.com/user-attachments/assets/fdb3f87a-f1af-4ae1-95f7-0d61acfb3095)
---
Release Notes:
- Fixed directional focus navigation into the Git panel's History tab.
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
# Objective
Document how to use Poolside in Zed through the ACP Registry, Poolside
Agent CLI, manual Custom Agent configuration, and Terminal Threads.
## Solution
- Add Poolside paths to the AI by Company guide.
- Document ACP Registry installation and in-thread login.
- Document CLI-assisted and manual Custom Agent configuration.
- Explain the settings-path and PATH requirements.
- Link to the public Poolside Agent CLI repository and Poolside’s Zed
documentation.
## Testing
- Ran `./script/prettier`.
- Ran `git diff --check`.
- Verified Poolside’s live ACP Registry entry.
- Verified the commands, configuration, and authentication behavior
against the Poolside and Zed implementations.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks are not applicable
- [x] The content follows Zed’s documentation and UI conventions
- [x] Code tests are not applicable to this documentation-only change
- [x] Performance impact is not applicable
---
Release Notes:
- N/A
# Objective
Prevent auto-indent from using stale syntax when text changes during a
background parse.
## Solution
Keep auto-indent pending and parsing active until the current parse
completes.
## 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:
- Editor: Fixed auto-indent occasionally using stale syntax after rapid
edits.
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
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.
`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
Removes an unnecessary dependency on settings as
`settings::OpenAiReasoningEffort` just points to
`language_model::ReasoningEffort`
Release Notes:
- N/A
# Objective
Closes#41744
When a multi-key binding switches from Insert mode to Helix normal mode,
subsequent editing actions can place the cursor incorrectly.
While a printable multi-key binding is pending, Zed temporarily inserts
the pending keys into the buffer. Once the binding is matched, Zed
dispatches the associated action and then deletes the pending text.
Because the buffer uses a CRDT, the deleted text remains as tombstoned
fragments. The cursor can still resolve to the correct visible offset
while its selection anchor remains associated with one of those
fragments, affecting the ordering of subsequent edits.
Among the explicit Vim mode-switch actions, `SwitchToHelixNormalMode` is
the only one that preserves the existing selections:
https://github.com/zed-industries/zed/blob/377382e1966caee4bd829dea2b106c4ab2897045/crates/vim/src/vim.rs#L713-L719
As a result, the tombstone-associated anchor is carried into Helix
normal mode, where later actions such as `o` can place the cursor on the
wrong side of the inserted newline.
The affected path is narrow: a multi-key binding must invoke
`SwitchToHelixNormalMode` from Insert mode. This creates pending text
and then preserves the resulting selection anchors when entering Helix
normal mode.
There are two possible layers at which to address this. At the Editor
layer, selections could be refreshed whenever pending text is removed so
that they no longer reference deleted fragments. At the Vim layer, the
Helix mode transition can explicitly guard against preserving those
anchors.
PR #50918 attempted the broader Editor-layer solution by refreshing
selections after resolving a multi-key binding. However, as discussed in
this review comment:
https://github.com/zed-industries/zed/pull/50918#issuecomment-4411319469,
the more appropriate scope for this reported issue is the Vim
mode-switching layer.
## Solution
Based on PR #50918 and its review feedback, this PR limits the fix to
`SwitchToHelixNormalMode`.
When that action is triggered by pending Insert-mode keystrokes, Vim
refreshes the preserved selection anchors after the pending text is
removed. This keeps the fix scoped to the affected Helix transition
without changing other multi-key bindings.
## Testing
Added a GPUI regression test.
## 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 incorrect cursor placement after using a multi-key binding to
leave insert mode in Vim or Helix mode.
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
# Objective
Fixes#61767.
Double-clicking a service name after a `systemctl` tree branch also
selects the `└─` prefix.
## Solution
Add `─` to Alacritty's semantic escape characters for both PTY and
display-only terminals. The regression test checks that selecting
`└─zms-demo.target` returns only `zms-demo.target`.
## Testing
- `cargo fmt --all -- --check`
- `git diff --check`
- Added `semantic_selection_stops_at_tree_branch`
The regression test could not run locally because this Windows
environment does not have MSVC's `link.exe`. It can be run with:
`cargo test -p terminal semantic_selection_stops_at_tree_branch`
## 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
---
Release Notes:
- Fixed terminal word selection around tree branch glyphs.
# Objective
This is a follow-up to my own PR #59270 which fixed the hang it targeted
(#55042) only partially and broke `ShellBuilder::build` for fish.
**The hang is only half fixed.** The agent's terminal tool wraps
commands as `sh -i -c 'exec </dev/null; <command>'`. The redirect sits
on the same line as the command, but POSIX shells parse an entire line
before executing any of it. So when `<command>` has a syntax error, the
whole line is discarded — including the `exec` that was supposed to
close stdin. Interactive `dash` then recovers from the error, finds
stdin still attached to the PTY, and sits at a `$` prompt until the user
presses Ctrl+D.
That means #59270 only helped when the redirect had already run, i.e.
multi-line commands whose error is on a later line. A single-line
command still hangs, including `cat <(echo hi)`, the example from that
PR's own description. Probably prompts or tool definitions changed since
that PR landed which now make it more likely again to run into this
issue, at least with Anthropic models.
**fish runs no command at all.** #59270 also applied the `exec
</dev/null; …` form to fish, replacing its `begin; …; end </dev/null`
wrapper. But fish's `exec` requires a command to replace the shell with,
so a bare `exec </dev/null` is a usage error: fish prints `exec`'s help
text, skips the rest of the command, and exits 0.
## Solution
For POSIX shells, put the redirect on its own line:
```sh
sh -i -c 'exec </dev/null
<command>'
```
That makes it a separate parse-and-execute unit, so it runs before the
command line is parsed. Any syntax error is then reported by a shell
that already has `/dev/null` on stdin, and it exits instead of
prompting.
For fish, go back to `begin; <command>; end </dev/null`. fish exits on a
parse error even when interactive, so it never needed the `exec` form to
begin with.
Only the `Posix` and `Fish` arms change; the other shells' redirects are
untouched.
Note this is still a targeted fix for `dash`. The underlying reason a
syntax error becomes an interactive prompt is that agent terminals are
built with `-i` at all — non-interactive `dash` exits cleanly even with
no redirect. Dropping `-i` would remove the whole class of hang, but it
would also stop sourcing users' interactive shell config in agent
terminals.
## Testing
`cargo test -p util --lib shell_builder` covers the changed command
assembly.
Reviewers can check the shell behavior directly on a PTY, without
building Zed:
```sh
# hangs at a `$` prompt until Ctrl+D
script -qec "sh -i -c 'exec </dev/null; echo hi ;;'" /dev/null
# exits immediately with just the syntax error
script -qec "sh -i -c 'exec </dev/null
echo hi ;;'" /dev/null
```
```sh
fish -i -c 'exec </dev/null; echo test' # prints exec's help, no "test", exit 0
fish -i -c 'begin; echo test; end </dev/null' # prints "test"
fish -i -c 'if true' # exit 127, no hang
```
macOS is unaffected in practice because `/bin/sh` there is bash, which
exits on a syntax error even with `-i`; Windows goes through the
`Cmd`/`PowerShell` arms, which this doesn't touch. A `zsh`-as-`/bin/sh`
setup is likewise unaffected.
## 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 agent terminal commands hanging at a shell prompt after a syntax
error
# Objective
Follow-up to #62028. When archiving removed a thread's workspace, the
replacement
was derived from whichever sidebar row sat adjacent to the archived one.
Rows are
a flat list with project headers as separators, so archiving the oldest
thread in
a project selected the next project's newest thread and moved the user
out of the
project they were working in — and when that neighbor was remote,
connected to
its host just to pick a fallback.
# Solution
- `MultiWorkspace::remove` now chooses the replacement itself from a
`RemovalIntent` (keep the project vs close it): a live workspace in the
same
project, the project's own roots when kept, the nearest retained
neighbor,
an adjacent local project, then an empty workspace. The workspaces being
removed are excluded from that search in one place instead of five.
- Sidebar neighbor selection stays within the entry's project section.
- The three archive paths now share one removal orchestration, and the
three
"open the closed workspace first" helpers collapse into one.
# Testing
New tests cover: the keep-project fallback, neighbor selection staying
inside the
project section, and archiving with a mock-remote neighboring project
never
connecting to it. Each was verified to fail against the previous
behavior.
## 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 archiving a thread switching the window to a different project.
- Fixed a possible crash when archiving a thread while its window was
closing.
### 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
# Objective
- Prevent changed main/folder associations from being skipped during
thread metadata updates.
## Solution
- Compare WorktreePaths as ordered main/folder pairs.
- Keep PathList equality order-insensitive.
## Testing
- cargo test -p project test_worktree_paths_equality_compares_pairings
## Self-Review Checklist:
- [x] I have reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed UI standards
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- N/A
Currently gpui only supports webgpu as a backend when compiling for the
web. However, webgpu support on Linux browsers is spotty (without
setting experimental flags):
- chromium has is enabled for nvidia and recent intel chips
- firefox does not enable it
This PR adds a WebGL backend, which is much more widely supported
---
Release Notes:
- N/A or Added/Fixed/Improved ...
---------
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments (N/A)
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
I was unable to find an issue or PR which mentions this.
Extensions with non-GitHub repository links always use the GitHub icon,
which is inconsistent with the changes merged in #44738 and #57500. This
change makes `extension_ui.rs` use the Git hosting provider registry to
determine these icons. Below is a preview of the change:
<div align="center">
<img
width="450"
alt="Preview"
src="https://github.com/user-attachments/assets/e35f0eed-de54-48f9-824d-8a5b9bfc596f"
/>
</div>
Release Notes:
- Improved extension repository links to show provider-specific icons.
`RawSemanticTokens` says tokens later in the list override earlier ones
on overlap, but there's no list to be later in — the tokens are stored
in a `HashMap<LanguageServerId, _>` and the sort that flattens them keys
only on `range.start`:
https://github.com/zed-industries/zed/blob/1c2a643cde2345ff7beca0178aee617b1651e561/crates/project/src/lsp_store/semantic_tokens.rs#L702-L709https://github.com/zed-industries/zed/blob/1c2a643cde2345ff7beca0178aee617b1651e561/crates/editor/src/semantic_tokens.rs#L312-L314
`sort_by` is stable, so two servers' tokens at the same position keep
whatever order the map iteration gave them, and which one wins changes
between sessions.
You hit this any time two servers tokenize the same range. In my case a
server that highlights an embedded query inside a string literal renders
fine, then loses the range to svelte-language-server once that
refreshes. Setting `languages.Svelte.language_servers` doesn't help,
since that isn't what decides it.
Breaking ties by `server_id` makes it deterministic.
`SemanticTokenHighlight` already carries the id and `LanguageServerId`
is `Ord`, so it's just the tiebreak.
Worth noting the servers do arrive as an ordered `Vec` from
`language_server_ids_for_buffer` and that order is dropped when they go
into the `HashMap`. Sorting by id gets determinism but not that original
order; if preserving it is what was intended I'm happy to submit a PR
for that functionality instead.
Release Notes:
- Fixed semantic token highlighting being applied unpredictably when
more than one language server provides tokens for the same range.
Fixes InsertLineAbove (Shift + O) auto-indent handling by deciding
whether to trim the first character or the last character based on
direction. Previously, inserting a line above would auto format the line
the cursor was originally on and not the new line (instead of the
correct behavior which is to do the opposite).
No tests because I couldn't find any Vim-specific auto-indent tests and
this only affects Vim mode. If I'm missing something there LMK and I'll
take a closer look.
Closes#52588.
Release Notes:
- Vim: Fixed auto-indentation for insert above action.
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
- **project_search: Add failing test for regex searches with
assertions**
- **project_search: Improve handling of regex assertions**
# Objective
Fixes#55995 by removing a faulty optimization path for regex searches.
## Solution
## Testing
We've added 3 extra tests for the changed behavior.
## 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 section is optional. If this PR does not include a visual change
or does not add a new user-facing feature, you can delete this section.
- Help others understand the result of this PR by showcasing your
awesome work!
- If this PR includes a visual change, consider adding a screenshot,
GIF, or video
- A before/after comparison is very useful for changes to existing
features!
While a showcase should aim to be brief and digestible, you can use a
toggleable section to save space on longer showcases:
<details>
<summary>Click to view showcase</summary>
My super cool demos here
</details>
---
Release Notes:
- Improved handling of regexes in project/buffer searches
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
## Context
When cmd/ctrl-clicking a relative path in the terminal, Zed was
resolving it against the *current* working directory at click time, not
the directory that was active when the line was printed. So if you run
command `cd folder1` after running command `ls`, clicking `test.txt` in
the old `ls` output would open `ROOT/test.txt` instead of
`ROOT/folder1/test.txt`.
The approach is similar to the VSCode approach for hyperlinks in the
terminal
**Video below of the test on VSCode:**
[Screencast from 2026-03-25
23-28-26.webm](https://github.com/user-attachments/assets/d1c3b163-7b18-43db-9981-71c1cb09a801)
**Video below of the test on Zed after the fix:**
[Screencast from 2026-03-25
23-31-49.webm](https://github.com/user-attachments/assets/fc23da34-dd16-4cce-afed-53f681762152)
Closes#52325
## How to Review
Two files changed — read in this order:
1. **`crates/terminal/src/pty_info.rs`** Bug fix:
`emit_title_changed_if_changed` was comparing cwd with itself because
`load()` writes to `self.current` *before* the comparison. Fixed by
capturing `prev_cwd`/`prev_name` before calling `load()`. Also extended
the background task to return the new cwd when it changes, so the
foreground task can call `terminal.record_cwd_change(cwd)`.
2. **`crates/terminal/src/terminal.rs`**
- `cwd_history: Vec<(i32, PathBuf)>` field on `Terminal`, seeded with
`(i32::MIN, initial_cwd)` for PTY terminals so all pre-history lines
resolve correctly.
- `record_cwd_change` called when the PTY cwd changes; records the
current scrollback position alongside the new path.
- `cwd_at_line` given a scrollback line, binary-searches the history to
find the most recent cwd active at or before that line.
- `process_hyperlink` updated to use `cwd_at_line` for both plain paths
and `file://` URLs.
## 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](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Fixed terminal path hyperlinks resolving against the current working
directory instead of the one active when the line was printed
---------
Co-authored-by: Lukas Wirth <lukas@zed.dev>
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes#47375
The current `crates/remote/src/transport/wsl.rs` remote transport code
tries to copy the remote-server from /mnt/c/* from within WSL. This
fails if you have automount disabled, or otherwise have your drive
unmounted.
How to reproduce this error:
Prerequisites: Windows with WSL2 and a WSL distro installed.
1. Inside the WSL distro, disable Windows automount — edit
`/etc/wsl.conf`:
```ini
[automount]
enabled = false
```
2. From a Windows shell: `wsl --shutdown`
3. Open the distro shell again and confirm `mount | grep /mnt/c` returns
nothing.
4. From Zed (any version before this PR), open a remote WSL project on
that distro for the first time (no cached
`~/.zed_server/zed-remote-server-*` inside the distro — `rm` it if
present).
The solution was to pipe the server in over stdin if the copy fails.
This is a fallback path similar to how ssh.rs has a fallback path if the
download fails there.
With this PR the logs look like this:
```
2026-05-24T18:51:37-07:00 INFO [remote::transport::wsl] uploading remote server to WSL ".zed_server/download-72808-remote_server" (441187kb)
2026-05-24T18:51:37-07:00 WARN [remote::transport::wsl] failed to upload remote server via /mnt, falling back to wsl.exe stdin: Command 'Command("wsl.exe" "--distribution" "Ubuntu" "--cd" "~" "--exec" "wslpath" "-u" "C:/Users/<user>/Documents/GitHub/zed/target/remote_server/x86_64-unknown-linux-musl/debug/remote_server")' failed: wslpath: C:/Users/<user>/Documents/GitHub/zed/target/remote_server/x86_64-unknown-linux-musl/debug/remote_server: Command 'Command("wsl.exe" "--distribution" "Ubuntu" "--cd" "~" "wslpath" "-u" "C:/Users/<user>/Documents/GitHub/zed/target/remote_server/x86_64-unknown-linux-musl/debug/remote_server")' failed: wslpath: C:/Users/<user>/Documents/GitHub/zed/target/remote_server/x86_64-unknown-linux-musl/debug/remote_server
2026-05-24T18:51:38-07:00 INFO [remote::transport::wsl] uploaded remote server in 945.899ms
```
I considered making wsl just use curl/wget to download as a fallback, as
docker/ssh do, but that would lose the 'cache' effect of downloading
once on windows and sending to multiple WSL distros. Which seems to have
been the intent of the copy method in the first place.
I also considered using the network-drive style UNC mount to copy from
Windows to the WSL distro, but that could run into similar issues where
the user may have that disabled. It also seems to copy as WSL's default
user, rather than the --user specified to Zed for the distro. Currently
--user is not passed by default to wsl.exe, so that ends up being the
default user. However, it is settable through the settings.json, so some
rare users may still have an issue with ownership of the copied files.
I chose to make the new stdin option the fallback instead of the default
because the current code is working for most people, and it seemed like
a much larger change to replace the default.
For the tests there don't seem to be any tests for wsl.rs currently that
I can find, so it seemed like a lot of extra code needed to satisfy that
PR checkbox. wsl.rs and docker.rs have zero inline tests, and the ones
that are in ssh.rs don't cover the ensure/upload/download portion. The
integration tests seem to use RemoteClient::fake_server and bypass the
real transports.
For manual testing I tested these scenarios:
1. WSL distro with automount **on** AND existing binary: **still works**
2. WSL distro with automount **on** AND **no** existing binary: **still
works**
3. WSL distro with automount **off** AND existing binary: **still
works**
4. WSL distro with automount **off** AND **no** existing binary: **now
works**
I'm not all that proficient with rust so I used a coding agent to help,
but I reviewed every line and it all makes sense to me. Please let me
know if something looks off, I'm trying to learn.
Release Notes:
- Fixed remote server installation failing in WSL when automount is
disabled.
---------
Co-authored-by: Lukas Wirth <lukas@zed.dev>
# Objective
Fix the `fallback workspace must not be one of the workspaces being
removed` panic.
Fixes FR-148
Fixes ZED-AKR
Fixes ZED-97M
# Solution
The remote path of `find_or_create_workspace` opened a project, then
returned the
window's *active* workspace instead of the one it created. The open
awaits toolchain
loading and item restoration, and a save prompt from a concurrent
workspace removal
can re-activate a workspace being removed during that wait; the stale
return value
then trips the assert in `MultiWorkspace::remove`.
Return the created workspace from `open_remote_project_inner` and use it
at the
call sites instead of re-reading the active workspace after the await.
# Testing
Added
`test_find_or_create_workspace_returns_the_created_remote_workspace`:
opens a
mock remote project and re-activates the previous workspace mid-open
(standing in
for the save prompt), then asserts the open returns the workspace it
created.
Verified it fails against the pre-fix behavior.
## 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 a crash when a workspace was removed while a remote project was
opening in the same window.
# Objective
Prevent MCP servers from remaining unavailable when a project is created
before its first worktree.
## Solution
Refresh effective MCP settings before maintaining servers after worktree
additions or removals.
## 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:
- Agent: Fixed MCP servers missing from new project threads after a
worktree opens.
# Objective
Prevent branch diffs from failing when a server does not recognize
worktree diff requests.
## Solution
Send `HEAD` as a valid committed-only fallback for servers that ignore
`includes_worktree`.
## 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
# Objective
Fix the Agent sidebar header being two pixels lower than the title bar
on Linux.
This only affects Linux with client-side decorations: the main title bar
uses a negative one-pixel margin to overlap the client-drawn window
border, while the sidebar previously used a positive one-pixel margin on
every platform.
Fixes#53739
## Solution
Match the sidebar header spacing to the window decoration mode:
- Use `-1px` top margin for client-side decorations.
- Preserve the existing spacing for server-side decorations.
- Apply the fix to both the thread list and archive headers.
## Testing
- Manually tested on Linux. This only affects Linux because its
client-side window decorations require the adjusted title bar offset.
## 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)](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)](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
After this change:
[2026-08-02
18-20-19.webm](https://github.com/user-attachments/assets/1ef13ff2-2036-442b-8bcc-74ebeb8436c7)
---
Release Notes:
- Fixed misaligned Agent sidebar headers on Linux.
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.