## Summary
- Document pricing and Zed-hosted context limits for Claude Fable 5,
Claude Sonnet 5, and GPT-5.6 Sol, Terra, and Luna.
- Record recent Gemini and xAI model retirements and replacement
guidance.
- Put the Fable safety-retention warning on the hosted-model reference
and align the privacy docs with the current Anthropic Covered Models
terminology and retention period.
## Sources
- zed-industries/cloud origin/main at
f5d109a868a303241e89ea30d6da8da19699ead4
- OpenAI GPT-5.6 model documentation
- Anthropic Covered Models retention policy
- xAI May 15, 2026 model retirement guide
## Testing
- pnpm dlx prettier@3.5.0 . --check
- mdbook build docs
Release Notes:
- N/A
Closes security loopholes and updates docs:
- installs seccomp filter for blocking naughty syscalls
- tightens macos seatbelt profile
- fetch tool responses that redirect are now constrained by allowed
domains list
Also adds a few "Learn More" buttons that link to the new docs.
Also fixes a bug where the agent would try to create a
`~/.config/zed/AGENTS.md` directory
Also adds unicode confusable detection to URL/path privilege escalation
prompts.
---
Release Notes:
- N/A or Added/Fixed/Improved ...
---------
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
The `edit_file` tool re-indents `new_text` by computing a single indent
delta from the first line of `old_text` versus the matched buffer line,
then applying that delta to every line of the replacement. When a model
omits the leading indentation on only the first line of
`old_text`/`new_text` (a common pattern when copying from mid-line
context), the delta computed from the first line was wrongly applied to
the remaining, already-correctly-indented lines, doubling their
indentation.
This PR:
- Tracks the `(query_row, buffer_row)` line pairs aligned by the
streaming fuzzy matcher so the indent of each `old_text` line can be
compared against the buffer line it actually matched.
- Computes a separate indent delta for the lines after the first: when
those lines agree on a consistent delta, it's used for the rest of the
replacement; otherwise the previous uniform behavior is preserved.
- Keeps `query_lines`/line pairs in sync when `finish()` extends a match
with a trailing incomplete line.
- Adds an end-to-end regression test reproducing the issue, plus unit
tests for the new re-indentation logic.
Closes https://github.com/zed-industries/zed/issues/60302
Release Notes:
- Fixed the agent's `edit_file` tool corrupting indentation when a
replacement omitted leading whitespace on only its first line.
On Linux and FreeBSD the native file watcher is non-recursive, so a
watch on the .git directory itself does not report changes to files
nested below it. Loose refs live in nested directories under refs, so
external git commit, fetch, branch, and update-ref operations that don't
also touch a direct child of .git (like the index) went entirely
unnoticed.
Watch every directory in the refs tree when a repository is inserted,
and watch directories subsequently created under refs (new remotes,
slash-named branches) as their creation events arrive. On platforms with
recursive watchers these registrations dedupe against the existing
recursive watch, making them free.
---
Release Notes:
- N/A or Added/Fixed/Improved ...
# Objective
Fixes#59820.
## Solution
At a high level, I approached this by conditionally rendering a `Below`
preview layout only when the preview was at or below the min width of
the `Right` when the layout is `Right`. Since there's one breakpoint at
`SizeBounds::min_width()` and hitting that breakpoint only changes
rendering (and doesn't change the state), there's no need to persist to
storage or memory that the view is forced to be Below.
I toggled between the two rendering modes by defining
`Picker::preview_layout_rendered()` that would return `Below` if the
window was equal to or smaller than its min width. Then I replaced all
calls of `preview_layout()` that used the response purely for rendering
purposes with `preview_layout_rendered()`. I avoided replacing
`preview_layout()` calls that were responsible for persisting the
current layout and window dimensions to disk to ensure that changes to
the window dimensions were properly synced to the correct layout. This
way changes to the window while in the `BelowAuto` rendering mode edit
the `Right` window dimensions on disk.
## Testing
I tested these changes manually with the following actions on my local
machine (an Apple Silicon Macbook Pro):
- resizing the popover
- resizing the window
- adjusting the split
- making sure the popover size and split percent remains the same after:
- closing and re-opening the popover
- closing and re-opening the application and making sure the window size
and split percent remains the same
Reviewers can test my changes by performing the same manual actions.
## Self-Review Checklist:
- [X] I've reviewed my own diff for quality, security, and reliability
- [X] Unsafe blocks (if any) have justifying comments
- [X] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [X] Tests cover the new/changed behavior
- [X] Performance impact has been considered and is acceptable
## Showcase
### Before
https://github.com/user-attachments/assets/a4524065-d1a3-4595-bc9e-50c9715b4e66
### After
https://github.com/user-attachments/assets/fede7eb0-5d73-423c-8df0-8192caee8d26
---
Release Notes:
- fix(picker): Automatically switch from Right split to Down split when
picker gets too narrow
LM Studio doesn't show the context token wheel (#53790) because token
usage is
never reported in streaming responses.
Causes:
1. `stream_options` was missing from the request. Without
`stream_options: { include_usage: true }`, the LM Studio API omits
`usage`
from every streaming chunk entirely.
2. The event mapper discarded usage data in the final chunk.
OpenAI-compatible
servers send the usage summary in a trailing chunk that has an empty
`choices`
array. The old guard treated that as an error, so even when usage was
present
it was thrown away before emitting a `UsageUpdate` event.
Fix:
- Add `StreamOptions { include_usage: bool }` and `stream_options` to
`ChatCompletionRequest`, and always set it to `true` for streaming
requests.
- Move usage handling in `LmStudioEventMapper::map_event` to run
*before* the
empty-choices guard, mirroring the OpenAI provider's approach.
- Add four unit tests for `map_event` covering the fixed behavior.
Release Notes:
- Fixed LM Studio not showing the context window usage wheel.
<img width="1184" height="1080" alt="Screenshot_20260527_130449"
src="https://github.com/user-attachments/assets/97eb8500-39dd-4824-aaf8-f0422b62119d"
/>
---------
Co-authored-by: Gabriele Ancillai <gabriele.ancillai@sofka.com.co>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Adds support for enabling Anthropic fast mode on configured models.
Previously, Anthropic models configured through
`language_models.anthropic.available_models` were always marked as not
supporting fast mode, so `speed: "fast"` would be stripped before
sending requests even when the configured model supported Anthropic fast
mode.
This adds an optional `supports_fast_mode` field to configured Anthropic
models. When enabled, the model is marked as supporting fast mode and
the required Anthropic beta header is added automatically. Built-in
fast-mode model detection remains the fallback when the setting is
omitted.
Testing:
- `cargo fmt --package anthropic --package language_models --package
settings_content`
- `cargo test -p language_models available_model --lib`
- `cargo test -p anthropic from_listed_enables_fast_mode --lib`
- `cargo check -p language_models`
- Manual: verified in a local build that a configured Anthropic model
can send fast mode requests correctly.
Release Notes:
- agent: Allow specifying if fast mode is supported for custom anthropic
models
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
This passes the `include_remote_name` flag through the remote
`GetDefaultBranch` RPC instead of dropping it at the client/host
boundary. That lets remote repositories resolve default branches as
`origin/main` when needed, so worktree creation uses a valid remote
branch name instead of falling back to `main`.
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
Closes#59121
Release Notes:
- Fixed remote worktree creation from the default branch when the
default branch requires its remote name.
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Closes#21822
The issue is closed already, but it was requested to provide the
`hard_tabs` boolean param.
Follow-up to #52175
Reviewed by @maxdeviant (it already bumped the settings path to v0.8.0,
so this `hard_tabs` change only needs 2 files, 2-3 lines of code
additions).
## Context
Exposes `hard_tabs` from `AllLanguageSettings` to the Extension
API's `LanguageSettings` struct. Currently, only `tab_size` and
`preferred_line_length` are available to extensions, which prevents
language extensions (e.g., Go, C++) from reading the user's hard tabs
preference and forwarding it to their language server or formatter
(e.g., as rustfmt.hard_tabs or clang-format.UseTab).
Related:
https://github.com/zed-industries/zed/issues/21822#issuecomment-4139809956
## How to Review
Small change — follow how `tab_size` is plumbed through:
1. WIT definition (`language-settings` record)
2. `extension_api` Rust struct
3. Host-side bridge conversion
The new field follows the exact same pattern.
## Self-Review Checklist
<!-- Check before requesting review: -->
- [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
- Compile-time verified via WIT bindings; no runtime behavior change
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
Extends the existing ECMAScript comment-label injection system to
support GLSL and WGSL shader languages inside JS/TS strings and template
literals.
The pattern `/* language */` before a string is already used for `html`,
`sql`, `graphql`, and `css`. This adds the same support for two shader
languages commonly embedded as strings in WebGL/WebGPU code:
before:
<img width="460" height="300" alt="Screenshot 2026-04-30 at 23 07 05"
src="https://github.com/user-attachments/assets/8952eddc-2f53-42ea-b086-7a0b63dceb10"
/>
after:
<img width="460" height="300" alt="Screenshot 2026-04-30 at 23 07 39"
src="https://github.com/user-attachments/assets/93bf536a-9df4-438b-af7b-382efda196ca"
/>
Syntax highlighting activates when the corresponding extension is
installed:
- GLSL: the built-in `glsl` extension
- WGSL: the community extension
[`wgsl-wesl-zed`](https://github.com/lucascompython/wgsl-wesl-zed)
(language name `WGSL/WESL`)
Both `/*glsl*/` and `/* glsl */` forms are accepted (same behavior as
the existing patterns).
Release Notes:
- Added `/*glsl*/` and `/*wgsl*/` comment-label syntax injection for
JavaScript and TypeScript template literals
Turns out I only tested it with all tabs closed, the keybinding would
only work when all tabs were closed and you focused the agent panel. If
a file was opened in the center pane, cmd-f would open a search in that
file instead of searching in the agent panel, even if the thread view as
focused.
Closes#60686
Release Notes:
- agent: Fixed an issue where cmd-f would not work if file is open in
center pane
## Context
On Linux, `ctrl-q` is globally bound to `zed::Quit`. When a
`TerminalView` is focused, pressing `ctrl-q` quit the application
instead of forwarding the keycode to the shell, breaking programs like
`ftp`, `tig`, and any app that uses XON/XOFF flow control.
Windows already had the fix: its `Terminal` keymap context overrides
`ctrl-q` with `["terminal::SendKeystroke", "ctrl-q"]`. The Linux keymap
was simply missing that override.
Closes#58809
Manual test after fix below :
[Screencast from 2026-06-09
00-46-37.webm](https://github.com/user-attachments/assets/3d103b2a-bff1-4559-af1d-2a52d57a6b18)
## How to Review
- **`assets/keymaps/default-linux.json`** : One-line addition in the
`Terminal` context under the "Overrides for conflicting keybindings"
comment, mirroring the existing Windows entry.
- **`crates/terminal_view/src/terminal_view.rs`** : Regression test
`ctrl_q_is_forwarded_to_terminal_not_quit` (Linux-only, `#[cfg(target_os
= "linux")]`): loads the default keymap, focuses a display-only
terminal, simulates `ctrl-q`, and asserts the PTY receives byte `0x11`
instead of the quit action firing.
## Self-Review Checklist
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the UI/UX checklist
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Fixed `ctrl-q` quitting Zed instead of being forwarded to the shell
when a terminal is focused on Linux
Removes the feature flag. The RFD is in Preview and I am confident it
will be stable without major changes by the time this hits Zed Stable.
Release Notes:
- acp: Allow ACP agents to use Elicitation capturing structure user
input.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes#57174
Release Notes:
- Open non-writeable files in Capability::Read mode
Co-authored-by: Lukas Wirth <lukas@zed.dev>
# Objective
- Fixes#58097.
- Opening a new project and clicking `+` in the agent panel to start a
terminal thread creates two terminals instead of one. I am able to
replicate the issue on version 1.8.0 on macOS 27
## Solution
The new-thread action creates the terminal and then focuses the agent
panel. Focusing re-activates the panel (`Panel::set_active` then
`ensure_thread_initialized`) before the terminal, which is spawned
asynchronously, has registered. The panel still looks uninitialized at
that moment, so it spawns its own "initial" terminal too, and that is
the duplicate.
`spawn_terminal` now marks the spawn as in-flight
(`pending_terminal_spawn`) the moment it starts, the same way the
restore and initial-terminal paths already do, so the existing guard in
`ensure_thread_initialized` skips the redundant terminal.
This only affected new (unrestored) projects, since existing ones
restore their previous entry instead of auto-creating one. The auto-init
behavior was introduced in #57150.
## Testing
- Verified in a local dev build on macOS: opening a fresh project and
clicking `+` now creates one terminal, and clicking `+` again creates a
second, as expected. Reopening an existing project still restores a
single terminal.
- Added `test_explicit_terminal_blocks_redundant_auto_init`, which fails
without the fix.
- The change is platform-agnostic (no platform-specific code); I wasn't
able to test on Linux/Windows.
## 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 duplicate terminal being created when starting an agent
terminal thread in a new project
### Closes#51951
## 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
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
#### Note : Reopens previous work from closed PR
https://github.com/zed-industries/zed/pull/52161 (fork was deleted)
## Video
[Screencast from 2026-03-22
23-26-06.webm](https://github.com/user-attachments/assets/ab68e47a-7e74-4f1e-991d-8ca4fed7952c)
## Release Notes:
- Fixed MCP servers from `.zed/settings.json` not being discovered when
multiple project folders are open in a workspace.
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
Release Notes:
- agent: Added GPT 5.6 Sol & Terra for ChatGPT subscription. Note: GPT
5.6 Luna is not available yet, since OpenAI has not unlocked access for
third-party clients
Dropping a scheduled runnable cancels its task and makes the next poll
of any awaiter panic with "Task polled after completion." The only paths
where we drop these runnables seem to be during shutdown or extreme
resource exhaustion, so, let's leak the runnables instead of crashing.
On Windows, we also moved to calling the Win32 thread pool API directly,
because 1) WinRT thread pool API is just a wrapper that adds overhead we
don't need, and 2) the closure we pass to the `WorkItemHandler` object
takes ownership of the runnable object, so if the WinRT thread pool
releases the delegate, it can free the runnable without our control.
Release Notes:
- N/A
This PR fixes a few History tab edge cases in the Git Panel.
For a fresh repo with no commits, the History tab now finishes loading
and shows No commits yet instead
of sitting on Loading… indefinitely or falling into a misleading
empty/error state.
It also fixes detached HEAD history loading. In that case, the Git Panel
asks the backend to load history
from the current commit SHA. The local git backend was accidentally
treating the raw object ID bytes as a
string instead of formatting them as a normal hex SHA, so git log could
fail before returning any
commits. The backend now passes the SHA in the format git expects.
**Repro for empty repo:**
mkdir /tmp/zed-empty-history
cd /tmp/zed-empty-history
git init
zed .
Open Git Panel → History.
Before: History could stay stuck on Loading….
After: History shows No commits yet.
**Repro for detached HEAD:**
mkdir /tmp/zed-detached-history
cd /tmp/zed-detached-history
git init
echo hi > file
git add file
git commit -m initial
git checkout --detach HEAD
zed .
Open Git Panel → History.
Before: History could fail to load commits.
After: History shows the commit history normally.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Fixed Git history tab states for empty repositories and detached HEAD
history.
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
Add new GPT 5.6 models
Release Notes:
- open_ai: Added support for GPT 5.6 Sol/Terra/Luna
---------
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
This makes them more consistent with regards to limiting the label size.
These two tabs are similar because they frequently house a label that's
pretty big, and the agent diff tab didn't truncate it in any way.
Release Notes:
- N/A
What looks like a single event of applying a few labels is actually
multiple events, and the raciness is racy as we've recently experienced.
One solution would've been adding a “first responders notified” label
for enforcing consistency but issues already have enough labels (and we
could take it off by mistake), so a bot reaction will instead serve as a
marker for any later runs.
So if we're applying multiple labels or changing our mind about, say, an
area label after the notification has already been sent, this should
work fine now, without duplicate notifications.
Release Notes:
- N/A
update_git_repositories mapped a changed .git path to a repository with
find_map, so when several repositories share a git directory - a main
checkout plus one of its linked worktrees in the same project worktree -
a ref update under the shared common dir only bumped git_dir_scan_id on
whichever repository iterated first, leaving the others stale until an
unrelated event happened to refresh them. Collect every matching
repository and bump each one.
---
Release Notes:
- N/A or Added/Fixed/Improved ...
GlobalWatcher::add returns Ok(None) while the native watch-limit
cooldown is active, and FsWatcher::add_existing_path treated that as
success: the path was never watched, with no retry and no error. A
long-lived watch (like a repository's git directory) that happened to
register during a cooldown window silently never received events.
Route the skipped registration through the existing pending-path
machinery, which already polls until registration succeeds and emits a
rescan event for the path so that changes missed in the interim are
picked up.
---
Release Notes:
- N/A or Added/Fixed/Improved ...
Reordering worktree roots by drag-and-drop had silently broken: a
worktree-root filter added to `disjoint_entries` for delete-safety
stripped roots before the drop handler could see them, so root-to-root
drops never reached the existing `move_worktree` reorder path.
This PR is the minimal regression fix:
- Move the worktree-root filter out of `disjoint_entries` and into
`disjoint_effective_entries` (used by cut/copy/delete), so drag-and-drop
keeps seeing roots and single-root reorder works again via the existing
`move_worktree` path.
- Filter worktree roots out of `drag_onto`'s copy branch, so holding the
copy modifier over a drag that contains a root no longer returns `None`
from `create_paste_path` and silently cancels the whole copy.
- Add `test_drag_worktree_root_reorders_worktrees` exercising the
drag-onto reorder flow end to end.
The larger feature work (multi-root group reordering, blank-area "send
to end", copy-mode drag feedback, and syncing worktree order to
collaborators) has been split into a separate follow-up PR so this fix
can land quickly. Note that worktree order was intentionally not synced
during collaboration, so that change is discussed separately.
Closes#46699
Release Notes:
- Fixed drag and drop to reorder worktrees
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
# Objective
Allow zed's language model stack to express OpenAI Responses API custom
tools (freeform text-input tools with an optional lark/regex grammar),
so downstream consumers can offer tools like a freeform `apply_patch` to
GPT models.
## Solution
- `LanguageModelRequestTool` now carries a Function-vs-Custom input
variant; `LanguageModelCustomToolFormat` models text/grammar formats.
- `LanguageModelToolUse.input` becomes a typed
`LanguageModelToolUseInput::{Json, Text}`. Serialization is tagged so
persisted Text inputs round-trip losslessly; legacy plain JSON values
still deserialize as `Json`.
- `open_ai` gains the custom tool wire types (tool definition,
`custom_tool_call`/`custom_tool_call_output` input items with
string-or-content-part outputs, output item, and
`custom_tool_call_input` delta/done stream events). The Responses event
mapper accumulates raw text deltas into `ToolUse` events, and history
replay derives custom-vs-function tool results from the matching
`ToolUse` by id.
- All non-OpenAI providers and the Chat Completions path error
explicitly when a request contains custom tools — no silent drops or
empty-schema coercions.
Release Notes:
- N/A
Closes https://github.com/zed-industries/zed/issues/59083
Closes https://github.com/zed-industries/zed/issues/42958
Closes https://github.com/zed-industries/zed/issues/59612
This PR improve selection copy of Agent Panel and Markdown Preview such
that copied text is always valid markdown, following how VSCode handles
it. The partial selection of styled text would not copy broken syntax
like `bold**` or ```inline code` ``` like before.
Now we follow simple rule:
> Selecting any part, no matter from where, copies it as its markdown.
Except when the selection sits entirely inside a single inline code
span, in which case we copy plain text, for terminal and code use cases.
Examples:
This is **bold** text, this is *italic* text, and this is `code` all `in
one` sentence.
- selecting only bold → `**bold**`
- selecting normal text and partial bold → `is is **bo**`
- selecting a single code span completely → `code`
- selecting partial code → `od`
- selecting partial text and code → `` his is `cod` ``
- selecting multiple code spans partially → `` `ode` all `in o` ``
- selecting multiple code spans end to end → `` `code` all `in one` ``
Nested spans, like **bold with `code` inside**:
- partial text in bold → `**ld wi**`
- partial code in bold → ``**`od`**``
- full code in bold → ``**`code`**``
- the whole sentence → ``**bold with `code` inside**``
Links, like [Visit Rust's website](https://rust.org):
- partial link text → `[bsite](https://rust.org)`
- full link text → `[Visit Rust's website](https://rust.org)`
How it works:
Selection boundaries that land inside delimiter syntax (`**`, backticks,
etc.) first snap out so no delimiter is left half-selected. Then any
spans the selection cuts through get their delimiters re-added,
outermost first, so nested styling stays balanced. Only the root blocks
containing the two selection boundaries are inspected, everything in
between is copied right as is, which also keeps this cheap on large
documents.
Release Notes:
- Improved copying selected text in Agent Panel and Markdown Preview.
Partial selections of styled text now copy as well-formed markdown, and
selections within a single inline code span copy as plain text.
BlockMap::sync unwrapped the transform cursor for every edit, assuming
each edit's old.start lands strictly inside the old transform tree. The
companion (split-diff) branch of sync can compose an edit anchored at
the trailing boundary of the old transforms, leaving the cursor past the
end of the tree and aborting the process on the None unwrap. Only bind
the transform when there are rows preceding the edit.
Fixes ZED-9V4.
Closes FR-113
Release Notes:
- N/A or Added/Fixed/Improved ...
Closes#60325, Follow up to #59415
Fixes an issue where launching Zed from the cli (just `zed`, no path)
would not restore the previous window when using
`cli_default_open_behavior: new_window`
Release Notes:
- cli: Fixed an issue where the previous workspace would not be restored
when using `cli_default_open_behavior: new_window` and no path was
provided
# Objective
Hi! This PR updates the Ruby doc to mention 2 language servers
`kanayago` and `fuzzy-ruby-server`.
## Testing
N/A
## 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:
- N/A
Jump the caret straight to the next/previous block of prose comments —
like paragraph motion, but for comments.
The motivating workflow: reflowing a file's comments with `editor:
rewrap`. Today that means reaching for the mouse to click into each
comment block scattered through the file, or using other cursor motions
and undershooting or overshooting the target. With these two actions you
can step from one comment paragraph to the next, `rewrap`, and repeat —
reflowing every comment in a file without ever touching the mouse. It's
also handy for skimming a heavily-documented file: hop from doc comment
to doc comment without manually scrolling past the code in between.
Adds two editor actions:
- `editor::MoveToNextCommentParagraph`
- `editor::MoveToPreviousCommentParagraph`
Both move the caret to the first non-whitespace character of the
next/previous *comment paragraph*. They have no default keybinding and
are available from the command palette ("editor: move to next/previous
comment paragraph").
### What counts as a comment paragraph
A comment paragraph is a run of consecutive comment lines. A line is a
comment line when its **first non-whitespace character is in a `comment`
syntax scope** and the line contains prose (at least one alphanumeric
character). This is determined from the syntax tree
(`language_scope_at(...).override_name()`), the same mechanism `rewrap`
and comment folding already use, so it behaves correctly without
per-language string matching:
- **End-of-line comments preceded by code are ignored** — on `let x = 1;
// note` the first non-whitespace character is code, not a comment, so
the line is not a paragraph line.
- **`//` inside a string literal is ignored** — its scope is `string`,
not `comment`.
- **Blank/divider comment lines separate paragraphs** — a bare `//` or
`// -----` (no prose) acts as a separator, so you can hop between
paragraphs *within* one comment block as well as across blocks.
Both directions always move to a paragraph *other* than the one the
caret is in: when the caret is inside a paragraph, the whole current
paragraph is skipped, so `Prev` lands on the previous paragraph's start
rather than the current paragraph's own start.
### On the autoscroll
These two actions scroll the destination near the top of the viewport
(`Autoscroll::top_relative`) rather than using the default `Fit`
strategy that sibling motions use. This is deliberate and specific to
the feature: you are jumping to the **start** of a comment paragraph
that extends *downward*, so biasing the destination toward the top keeps
the whole paragraph visible after the jump. This matters for the rewrap
workflow above — you want to see the full comment you are about to
reflow, and the reflow itself changes the paragraph's line count. With
the default `Fit`, repeated forward jumps creep the caret to the bottom
edge and leave long paragraphs cut off below the fold — the opposite of
what this motion is for. Happy to revisit the exact strategy/offset if
you'd prefer consistency with the other motions.
### Tests
Two tests in `editor_tests.rs` (using a real grammar + comment override
query):
- `test_move_to_next_and_previous_comment_paragraph` — full
forward/backward round trip, covering blank comment-line separators,
code separators, trailing comments, and the no-more-paragraphs stop.
- `test_move_to_previous_comment_paragraph_skips_current_paragraph` —
`Prev` from mid-paragraph skips to the previous paragraph, and stays put
when there is no previous paragraph.
---
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments — N/A, no unsafe
- [x] The content is consistent with the UI/UX checklist
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Added `editor::MoveToNextCommentParagraph` and
`editor::MoveToPreviousCommentParagraph` actions to move the caret
between comment paragraphs
`PolychromeSprite` in `crates/gpui/src/scene.rs` is `#[repr(C)]` and had
a `grayscale: bool` field followed by 3 compiler-inserted padding bytes
that were never written. The wgpu renderer's `instance_bytes`
reinterprets `&[PolychromeSprite]` as `&[u8]` via
`slice::from_raw_parts` and passes it to `queue.write_buffer`, so those
uninitialized padding bytes were exposed behind a shared `&[u8]` on
every frame that draws an image or emoji, which is undefined behavior.
Rather than widening the field to a raw `u32` (which would suggest
values other than 0 and 1 are meaningful), this introduces
`PaddedBool32`: a `#[repr(transparent)]` wrapper around `u32` whose only
public constructor is `From<bool>`, so the 0-or-1 invariant is enforced
by the type while the layout has no padding. `Underline.wavy`, which was
already a raw `u32` for the same reason, is converted too.
cbindgen emits the wrapper as `typedef uint32_t PaddedBool32;`, so the
generated Metal header and shaders are unchanged. The WGSL and HLSL
shaders already declared these fields as `u32`/`uint`; their `& 0xFFu`
masks, which existed to ignore the garbage padding bytes, are now
simplified to plain comparisons.
Release Notes:
- N/A
Context
This PR makes the Zed docs easier for AI tools, search crawlers, and
users to consume without changing the visible docs content. The current
production docs are primarily optimized for browser navigation. They do
not expose first-class Markdown URLs, an `llms.txt` index, page-level
copy affordances, or machine-readable freshness metadata that let users
and agents grab clean, current page content.
Changes
- Generate Markdown copies for docs pages during the mdBook postprocess
step, including `/docs/index.md` as an alias for Getting Started.
- Generate `/docs/llms.txt` from the mdBook chapter list, grouped by
`SUMMARY.md` sections and annotated with page frontmatter descriptions.
- Generate `/docs/sitemap.xml` with `<lastmod>` values for every docs
page.
- Emit machine-readable freshness metadata in HTML via `last-modified`
and `article:modified_time` meta tags.
- Generate Cloudflare Pages `_redirects` for `.html`, extensionless, and
`.md` redirect variants, with channel-aware docs destinations.
- Add discovery hints for agents and crawlers: `rel="llms.txt"`,
`rel="alternate" type="text/markdown"`, and a short generated `llms.txt`
directive in copied Markdown pages.
- Update the docs proxy so `Accept: text/markdown`, `/docs.md`, and
direct `.md` requests can resolve to the generated Markdown artifacts.
- Move primary docs content earlier in the HTML source while preserving
the visible layout, so crawlers and agent scorers encounter the article
before sidebar chrome.
- Move the existing copy-as-Markdown control from the top navigation
into the page-title row, using the generated Markdown alternate link as
the source of truth.
- Split AI-discovery artifact generation out of
`docs_preprocessor/src/main.rs` into a focused module.
Best Practices Adopted
- Use `llms.txt` as a concise navigation index, not a dump of full page
content.
- Link to absolute, canonical Markdown URLs from `llms.txt`.
- Preserve the docs hierarchy in `llms.txt` instead of emitting a flat
sitemap-like list.
- Include short per-link descriptions from existing metadata rather than
inventing summaries.
- Keep `llms.txt`, Markdown copies, sitemap data, redirects, and
freshness metadata generated from the same mdBook source to avoid drift.
- Advertise Markdown alternates with standard HTML metadata and
same-origin URLs.
- Support both explicit Markdown URLs and content negotiation for
clients that prefer Markdown.
- Keep browser copy behavior pointed at generated Markdown alternate
links instead of duplicating route inference in JavaScript.
- Keep the copy-as-Markdown affordance in the page title row without
duplicating header chrome controls.
Validation
- `cargo check -p docs_preprocessor`
- `cargo test -p docs_preprocessor`
- `./script/clippy -p docs_preprocessor`
- `mdbook build ./docs --dest-dir=../target/deploy/docs/`
- `node --check docs/theme/plugins.js`
- `pnpm dlx prettier@3.5.0 docs/theme/plugins.js --check`
- `git diff --check`
- Local artifact checks confirmed generated Markdown pages, `llms.txt`,
`sitemap.xml` lastmod values, HTML freshness metadata, Markdown
alternate links, redirect targets, and preprocessed action/keybinding
tags resolve as expected.
- Worker URL rewrite mock covered `/docs/`, `/docs.md`,
`/docs/index.md`, extensionless docs routes, `.html` routes,
`/docs/llms.txt`, and `/docs/sitemap.xml`.
- High-effort adversarial subagent review found blockers around
channel-aware redirects, shallow-checkout date fallback, file size,
duplicated Markdown path inference, and process spawning. Those were
addressed.
Remaining Notes
- Local `python -m http.server` does not emulate Cloudflare Pages pretty
URLs, `_redirects`, or the docs-proxy Worker, so full local `afdocs`
still cannot prove content negotiation end to end.
- Existing production remains unchanged until this PR is deployed
through the docs workflow.
- Production baseline `npx afdocs check https://zed.dev/docs/ --fixes
--verbose` still reports the original failures before this PR is
deployed: 12 passed, 8 failed, 3 skipped.
Release Notes:
- Improved docs AI-readiness by adding machine-readable discovery,
Markdown access, and freshness metadata.
---------
Co-authored-by: Katie Geer <katie@zed.dev>
Co-authored-by: Ben Kunkle <ben@zed.dev>
On Wayland, closing a window in the brief gap between requesting a
portal file dialog and ashpd exporting the window's surface (used to
parent the dialog) crashed Zed with `Unknown opcode 0 for object
<anonymous>@0`
([ZED-9KB](https://zed-dev.sentry.io/issues/7568720776/)). When the
export request fails because the surface is already dead,
wayland-scanner's generated code silently returns an inert proxy, and
ashpd's `Drop` impl later sends `destroy` on it — which wayland-backend
0.3.11 answers with a panic, because it looks up the request opcode
before checking whether the object is null. wayland-backend 0.3.15 fixes
this
([Smithay/wayland-rs#890](https://github.com/Smithay/wayland-rs/issues/890))
by returning an error instead, which the generated destructor discards,
so the drop becomes a harmless no-op. This bumps the lockfile to 0.3.15
and raises the version floor in `gpui_linux` so the fix can't silently
regress via a fresh lockfile.
Closes FR-100
Release Notes:
- Fixed a crash on Linux (Wayland) when a window was closed just as a
file dialog was being opened.
# Objective
Fix two gaps in element hover tracking at window boundaries. Hover was
only re-evaluated on `MouseMove`, so when the pointer left the window no
event fired `on_hover(false)` and the element stayed hovered.
Symmetrically on Wayland, no `Motion` follows `Enter` until the pointer
moves again, so hover was not established at the entry pixel. Both cases
are easy to miss since most hover-styled elements don't sit flush
against the window edge, but they surfaced while implementing
layer_shell popups with input_regions, which should close when stop
hovering.
## Solution
The hover compare-and-fire logic in `div` is refactored into a shared
`update_hover` closure, and a second listener on `MouseExitEvent` clears
hover when the pointer leaves the window. It clears unconditionally
because `MouseExited` doesn't update the tracked mouse position, so a
hit test during that dispatch would still report the element as hovered.
On Wayland, a `MouseMove` is synthesized at the entry position on
`wl_pointer.enter`, mirroring the `MouseExited` already dispatched on
`Leave`.
## Testing
Tested manually on Wayland/Linux: hover on a window-edge element clears
when the pointer leaves the window, and hover is established immediately
when the pointer enters a surface with an element under the entry pixel.
Not tested on other platforms. The `div` change relies on each
platform's existing `MouseExited` dispatch: macOS and X11 emit it, so
they get the exit fix too. Windows never dispatches `MouseExited`
(`WM_MOUSELEAVE` only flips the window-level hover flag), so the
stuck-hover case might remain there, unchanged from before.
Before:
https://github.com/user-attachments/assets/6af83bb3-de9d-40e2-a64c-bdefc98fc96d
After:
https://github.com/user-attachments/assets/721a9b5c-108a-499f-867e-da111836a34a
Here is an example application to test this:
```rs
#![cfg_attr(target_family = "wasm", no_main)]
use gpui::{
App, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px, rgb, size,
};
use gpui_platform::application;
struct HoverExit {
hovered: bool,
}
impl Render for HoverExit {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
// Fills the whole window so its edge is the window edge: moving the mouse
// out of the window is what exercises the MouseExited path.
div()
.id("hover-exit")
.size_full()
.flex()
.justify_center()
.items_center()
.text_xl()
.text_color(rgb(0xffffff))
.bg(if self.hovered {
rgb(0x585f58)
} else {
rgb(0x505050)
})
.child(if self.hovered { "HOVERED" } else { "not hovered" })
.on_hover(cx.listener(|this, hovered, _, cx| {
this.hovered = *hovered;
cx.notify();
}))
}
}
fn run_example() {
application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(240.), px(160.0)), cx);
cx.open_window(
WindowOptions {
window_bounds: Some(WindowBounds::Windowed(bounds)),
app_id: Some("gpui-hover-exit".to_string()),
..Default::default()
},
|_, cx| cx.new(|_| HoverExit { hovered: false }),
)
.unwrap();
cx.activate(true);
});
}
#[cfg(not(target_family = "wasm"))]
fn main() {
run_example();
}
#[cfg(target_family = "wasm")]
#[wasm_bindgen::prelude::wasm_bindgen(start)]
pub fn start() {
gpui_platform::web_init();
run_example();
}
```
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed element hover state not clearing when the mouse leaves the
window
Checks that SetWindowCompositionAttribute was found before transmuting
and calling the function pointer, making the composition setup a no-op
when the undocumented export is unavailable.
Release Notes:
- N/A
# Objective
gpui can't show UI that extends past the window it belongs to. Menus,
dropdowns and tooltips are drawn as elements inside the window, so they
clip at its edges. This PR adds a window kind for platform-native popups
anchored to a parent window, as groundwork for real native menus,
dropdowns and tooltips.
## Solution
`WindowKind::AnchoredPopup(PopupOptions)` opens a popup positioned
relative to a parent window. Instead of giving the popup an absolute
position, you describe where it should go and the platform figures out
the rest:
- `parent`: the window to anchor to
- `anchor_rect`: a rectangle in the parent, e.g. the button that opened
the menu
- `anchor` and `gravity`: which point of that rect to attach to, and
which direction to grow
- `constraint_adjustment`: what the platform may do if the popup would
leave the screen (slide, flip, resize)
- `grab`: menu behavior, the popup takes focus and is dismissed when
clicking outside the app
The popup's size comes from `WindowOptions::window_bounds`.
This model mirrors Wayland's `xdg_positioner`, where the compositor owns
positioning and the client can only describe intent. Since that's the
most restrictive case, the other platforms can implement the same
description later with simple math against screen bounds.
Only Wayland is implemented so far, via `xdg_popup` on top of the
existing surface implementation. Popups can be parented to toplevels,
layer-shell surfaces (a menu opened from a panel) and other popups
(nested menus). macOS, Windows, X11 and web reject the kind with
`PopupNotSupportedError`, so callers can detect that and fall back to
in-window popovers.
Some Wayland details that might help during review:
- Anchor rects are translated from gpui coordinates into the parent's
window geometry space and clamped to it. A rect outside the geometry, or
with zero size, is a fatal protocol error
- Resizing a mapped popup goes through `xdg_popup.reposition`
- Mouse press serials are now recorded on press only, not release.
Compositors decline grabs and interactive moves that reference a release
serial
## Testing
Tested manually on Wayland with an example app: the menu opens anchored
below its button, extends past the parent window, flips above the button
near the bottom of the screen, and a grabbing popup is dismissed when
clicking into another application.
Nested menus were tested in one of my projects (ignore that they are
ugly, that's just a prototype 😛):
https://github.com/user-attachments/assets/2cd3e2e9-87f7-4b02-986f-48e5633e205c
I also have a complete runnable example demonstrating it. I did not add
it to the PR, because this might give the impression that
`WindowKind::AnchoredPopup` are a complete implementation, despite only
working on wayland so far:
<details>
<summary>Click to view example</summary>
```rust
//! Example and manual test for platform-native popups (`WindowKind::AnchoredPopup`).
//!
//! A native popup is a real, parent-anchored window that can extend beyond its parent onto the
//! screen, unlike gpui's in-window popovers. Run it, open the menu, and confirm the points listed
//! in the window. On a platform without an implementation the button reports that popups are not
//! supported instead of opening anything.
//!
//! Run with: cargo run -p gpui --example popup
#![cfg_attr(target_family = "wasm", no_main)]
use gpui::{
AnyWindowHandle, App, Bounds, Context, MouseButton, SharedString, Window, WindowBounds,
WindowHandle, WindowKind, WindowOptions, div, point, popup::*, prelude::*, px, rgb, size,
};
use gpui_platform::application;
/// The trigger button, at a fixed position so the popup can anchor to a known rectangle. Real code
/// would anchor to the measured bounds of whatever element opens the popup.
const BUTTON_BOUNDS: Bounds<gpui::Pixels> = Bounds {
origin: point(px(24.), px(24.)),
size: size(px(200.), px(32.)),
};
const POPUP_SIZE: gpui::Size<gpui::Pixels> = size(px(260.), px(320.));
struct Menu;
impl Render for Menu {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
let item = |label: &str| {
div()
.id(label.to_string())
.px_3()
.py_1()
.rounded_sm()
.hover(|this| this.bg(rgb(0x3a3a3a)))
.cursor_pointer()
.child(label.to_string())
.on_click(|_, window, _| window.remove_window())
};
div()
.id("menu-root")
.size_full()
.p_1()
.flex()
.flex_col()
.gap_0p5()
.bg(rgb(0x2a2a2a))
.text_color(gpui::white())
.rounded_md()
.border_1()
.border_color(rgb(0x454545))
.child(item("Foo"))
.child(item("Bar"))
.child(item("Baz"))
.child(item("Qux"))
.child(item("Alice"))
.child(item("Bob"))
}
}
struct PopupExample {
menu: Option<WindowHandle<Menu>>,
status: SharedString,
}
impl Default for PopupExample {
fn default() -> Self {
Self {
menu: None,
status: "Click \"Open menu\" to open a native popup.".into(),
}
}
}
impl PopupExample {
/// Closes the menu if it is open. Returns true if a menu was actually open.
fn close_menu(&mut self, cx: &mut App) -> bool {
match self.menu.take() {
Some(menu) => menu
.update(cx, |_, window, _| window.remove_window())
.is_ok(),
None => false,
}
}
fn toggle_menu(&mut self, parent: AnyWindowHandle, cx: &mut App) {
if self.close_menu(cx) {
return;
}
match open_menu(parent, cx) {
Ok(menu) => {
self.menu = Some(menu);
self.status = "Menu open. Dismiss it by selecting an item, clicking elsewhere in \
this window, or clicking another application."
.into();
}
// A real application would fall back to an in-window popover here.
Err(error) => {
self.status = format!("Failed to open a native popup: {error}").into();
log::error!("failed to open popup: {error}");
}
}
}
}
impl Render for PopupExample {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let bullet = |text: &str| div().child(format!("• {text}"));
div()
.id("root")
.size_full()
.bg(rgb(0xf7f7f7))
.text_color(rgb(0x222222))
// Same-app clicks don't auto-dismiss a grabbing popup (see `PopupOptions::grab`).
.on_mouse_down(
MouseButton::Left,
cx.listener(|this, _, _window, cx| {
this.close_menu(cx);
}),
)
.child(
div()
.size_full()
.p_5()
.pt(px(76.))
.flex()
.flex_col()
.gap_3()
.child(div().text_xl().child("Native popup test"))
.child(div().text_sm().child(
"WindowKind::AnchoredPopup opens a real, parent-anchored window that can \
extend past this window onto the screen. Only some platforms implement \
it so far.",
))
.child(
div()
.flex()
.flex_col()
.gap_1()
.text_sm()
.text_color(rgb(0x555555))
.child(div().child("Verify:"))
.child(bullet("The menu opens anchored below the button."))
.child(bullet(
"The menu extends past the bottom edge of this window.",
))
.child(bullet(
"Near the bottom of the screen, the menu flips above the button.",
))
.child(bullet("Clicking another application dismisses the menu."))
.child(bullet(
"Selecting an item or clicking in this window dismisses it.",
)),
)
.child(
div()
.text_sm()
.text_color(rgb(0x333333))
.child(self.status.clone()),
),
)
.child(
div()
.absolute()
.left(BUTTON_BOUNDS.origin.x)
.top(BUTTON_BOUNDS.origin.y)
.w(BUTTON_BOUNDS.size.width)
.h(BUTTON_BOUNDS.size.height)
.flex()
.items_center()
.justify_center()
.bg(rgb(0xffffff))
.border_1()
.border_color(rgb(0xd0d0d0))
.rounded_md()
.cursor_pointer()
.id("open-menu")
.active(|this| this.bg(rgb(0xeeeeee)))
.child("Open menu ▾")
// Open on mouse-down, not on click, so the grab is taken while the button is still held.
.on_mouse_down(
MouseButton::Left,
cx.listener(|this, _, window, cx| {
// Don't let the window handler above close the menu we are opening.
cx.stop_propagation();
this.toggle_menu(window.window_handle(), cx);
}),
),
)
}
}
fn open_menu(parent: AnyWindowHandle, cx: &mut App) -> anyhow::Result<WindowHandle<Menu>> {
cx.open_window(
WindowOptions {
titlebar: None,
// Sizes the popup. The platform decides the position, so the origin is ignored.
window_bounds: Some(WindowBounds::Windowed(Bounds {
origin: point(px(0.), px(0.)),
size: POPUP_SIZE,
})),
kind: WindowKind::AnchoredPopup(PopupOptions {
parent,
anchor_rect: BUTTON_BOUNDS,
// Anchor to the button's bottom-left and grow down-right so the menu drops beneath it.
anchor: PopupAnchor::BottomLeft,
gravity: PopupGravity::BottomRight,
// Slide horizontally and flip vertically if the menu would leave the screen.
constraint_adjustment: PopupConstraintAdjustment::SLIDE_X
| PopupConstraintAdjustment::FLIP_Y,
offset: point(px(0.), px(4.)),
// Grab input so the compositor dismisses the popup on clicks into other applications.
grab: true,
}),
..Default::default()
},
|_, cx| cx.new(|_| Menu),
)
}
fn run_example() {
application().run(|cx: &mut App| {
cx.open_window(
WindowOptions {
window_bounds: Some(WindowBounds::Windowed(Bounds {
origin: point(px(100.), px(100.)),
size: size(px(420.), px(300.)),
})),
..Default::default()
},
|_, cx| cx.new(|_| PopupExample::default()),
)
.unwrap();
cx.activate(true);
});
}
#[cfg(not(target_family = "wasm"))]
fn main() {
run_example();
}
#[cfg(target_family = "wasm")]
#[wasm_bindgen::prelude::wasm_bindgen(start)]
pub fn start() {
gpui_platform::web_init();
run_example();
}
```
</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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- N/A
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes#43396
Release Notes:
- Project search now supports CRLF line endings correctly, as well as
other regex features like subroutine calls
## Summary
Introduces `format_on_save` variants that format only modified lines,
limiting formatting to lines changed since the last commit. This
prevents massive diffs when editing legacy codebases. Aligns with VS
Code's `editor.formatOnSaveMode` naming:
- `"modifications"` — formats only git-diffed lines. Requires source
control; skips formatting if no diff is available.
- `"modifications_if_available"` — formats only git-diffed lines,
falling back to full-file formatting for untracked files, when source
control is unavailable, or when the LSP does not support range
formatting.
Also supports importing equivalent VS Code settings
(`formatOnSaveMode`).
This PR uses the range-based whitespace/newline infrastructure from the
dependency PR above.
## Changes
<details>
<summary><b>New settings, modified ranges computation, VS Code
import</b></summary>
### New settings (`language.rs`, `default.json`, `all-settings.md`)
Two new `FormatOnSave` variants: `Modifications` and
`ModificationsIfAvailable`.
### Modified ranges computation (`items.rs`)
- `compute_format_decision()` — reads `format_on_save` setting across
all buffers, determines whether to use ranged formatting (`Ranges`),
full formatting (`Full`), or skip (`Skip`).
- `compute_modified_ranges()` — extracts modified line ranges from git
diff hunks via `BufferDiffSnapshot`.
- `is_empty_range()` — helper to detect deletion-only hunks that produce
no formatable content.
The `save()` method calls `compute_format_decision()` and dispatches
accordingly.
### Modifications mode in `lsp_store.rs`
- Adds `FormatOnSave::Modifications |
FormatOnSave::ModificationsIfAvailable` to the formatter selection match
arm.
- `ModificationsIfAvailable` falls back to full-file formatting when
ranged formatting produces no results.
### VS Code settings import (`vscode_import.rs`, `settings_store.rs`)
Imports `editor.formatOnSaveMode` mapping:
- `"modifications"` → `FormatOnSave::Modifications`
- `"modificationsIfAvailable"` →
`FormatOnSave::ModificationsIfAvailable`
- `"file"` → `FormatOnSave::On`
</details>
## Tests
- `test_modifications_format_on_save` — basic modifications mode
formatting with dirty buffer
- `test_modifications_format_no_changes` — clean buffer triggers no
formatting
- `test_modifications_format_lsp_no_range_support` — LSP without range
formatting skips entirely for `Modifications`
- `test_modifications_format_lsp_returns_empty_edits` — empty edits
handled gracefully
- `test_modifications_format_multiple_hunks` — two non-adjacent edits
produce two separate range formatting requests
---
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes#16509
Depends on #53942
Release Notes:
- Added `modifications` and `modifications_if_available` options to
`format_on_save`, which format only git-changed lines instead of the
entire file
- When using modifications mode, `remove_trailing_whitespace_on_save`
and `ensure_final_newline_on_save` are also scoped to changed lines,
preventing unwanted diff jitter in legacy codebases
- Added support for importing VS Code's `editor.formatOnSaveMode`
setting
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
## Context
This is an implementation of this feature:
https://github.com/zed-industries/zed/discussions/49821
Although, I'd argue it's a bug fix, not a feature.
Either way : This copies the window scroll logic from the `vim` mode
versions with out all the extra logic for visual mode.
Could refactor the common logic out of the vim code and make it common.
But that seems like a bigger PR. Happy to take a stab at it if that's
what you prefer.
Could also add new commands for the new behavior if you prefer. I didn't
do that, because it seems like more clutter in the commands, and my
belief that the existing behavior is a bug. But happy to do that if you
prefer.
## How to Review
creates new function `scroll_screen_with_cursor_margin` in `scroll.rs`
wires up editor::LineUp/LineDown to new function in `elements.rs`
adds a test in `editor_tests.rs`
## Self-Review Checklist
<!-- Check before requesting review: -->
- [X] I've reviewed my own diff for quality, security, and reliability
- [X] Unsafe blocks (if any) have justifying comments
- [X] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [X] Tests cover the new/changed behavior
- [X] Performance impact has been considered and is acceptable
Release Notes:
- editor::LineUp/LineDown commands now honor vertical_scroll_margin
(same as vim::LineUp/LineDown)
---------
Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes#55792
Release Notes:
- Fixed files in pnpm workspaces moving to symlinked `node_modules`
paths after saving.
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
# Objective
CSV feature needs row filtering feature by column values. This PR
provides base implementation of it with barebones ui.
> NOTE: Sleek UI with search & proper scrolling hanling is implemented
in next PR. It's stacked on top to reduce review scope
## Solution
- New `FilterEntry` / `FilterEntryState` model in
`table_data_engine/filtering_by_column.rs` tracking per-column
applied/candidate filter values
- Filtering runs in the background (`feat: Implement background
filtering`) so large CSVs don't block the UI thread while a filter is
applied
- Filter menu entries reflect live counts and support a configurable
sort order (`FilterSortOrder`, added in `renderer/settings.rs` /
`settings.rs`)
- Filter/sort trigger buttons on column headers are hidden until hover,
using `GradientFade` (new in `ui/src/components/gradient_fade.rs`) to
fade content behind them
## Testing
Filter chain tested on csv fixtures with multiple filters applied
sequentially columns.
## Self-Review Checklist: (todo)
- [ ] I've reviewed my own diff for quality, security, and reliability
- [x] 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) (out of scope of this pr)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
<img width="664" height="249" alt="image"
src="https://github.com/user-attachments/assets/0e9b0a91-1a27-4e0f-a8d4-fdce36735131"
/>
<img width="663" height="205" alt="image"
src="https://github.com/user-attachments/assets/0428f5c6-6aaa-4891-b010-ca79803f6613"
/>
---
Release Notes:
- Added initial row filtering UI & logic
This allows us to build powerful and flexible Input and TextArea
components
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments (Unsafe: none)
- [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
## Summary
When the agent is streaming output, expanding the message editor can
appear to work (the container grows) but the input editor itself remains
constrained to auto-height (only a few lines). Toggling minimize →
expand makes it render correctly.
This PR ensures the message editor stays in full mode while expanded,
preventing automatic editor mode syncing on thread updates from
overriding the user's explicit expand action during streaming.
## Steps to reproduce
1. Start an agent thread and send a message that causes streaming output
2. While streaming, click “Expand Message Editor”
3. Observe the input editor still shows only a few lines (auto-height)
4. Click “Minimize Message Editor” and then expand again; it becomes
fully expanded
## Test plan
- Start an agent thread and let it stream
- Click “Expand Message Editor” while streaming
- Verify the editor actually expands (not limited to the auto-height max
lines)
- Toggle minimize/expand multiple times during streaming; verify it
remains correct
## Additional context
Repro video:
https://github.com/user-attachments/assets/6e328fa8-d431-456a-b70a-864ae617ea88
Release Notes:
- Fixed message editor not fully expanding during agent generation
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
On ordinary platforms, `Platform::run` blocks for the lifetime of the
app, and `Application::run`'s stack frame keeps the app state alive.
Embedded platforms invert that: the run loop belongs to someone else.
`Application::run_embedded` supports that shape: it starts the app
exactly like `run()`, but returns an `ApplicationHandle` holding the
strong app handle.
Release Notes:
- N/A
`update_last_checkpoint` swallows `compare_checkpoints` errors with
`.unwrap_or(true)`. The "Restore checkpoint" button silently disappears
and nothing gets logged, which is what made the linked issue painful to
track down in the first place.
The sibling `update_last_checkpoint_if_changed` a few lines up already
handles the same call with `.context(...).log_err()` and an early
return, so I did the same here. On error the checkpoint's visibility is
left alone instead of being forced to hidden. I didn't propagate the
error because the task result gets `?`'d in `run_turn`'s cleanup, and
failing there would leave the panel stuck in its generating state.
Added a regression test that breaks the comparison mid-turn (recreating
`.git` makes the fake repo forget its checkpoints) and asserts an
already-visible checkpoint stays visible. It fails on main and passes
with this change. The new log line shows up when it runs: `failed to
compare checkpoints: invalid left checkpoint: ...`
Closes#59100
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Fixed checkpoint comparison errors silently hiding the "Restore
Checkpoint" button in the agent panel.
Co-authored-by: pstemporowski <110726755+pstemporowski@users.noreply.github.com>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
# Objective
Provide a means of quickly opening a permanent tab from project panel
instead of a preview tab.
Implements #51866 (_Middle click to open file in a new tab_ with 18×↑)
which is also part of #31822 (_Middle Click Improvments_) titled "middle
clicking on a file in the project panel should open the file in a
non-transient state".
With `preview_tabs.enable_preview_from_project_panel` enabled, a click
in the project panel opens a preview tab, and there's no easy gesture to
open a permanent one instead, simplest one currently being a double
click. VS Code and VSCode-based editors recognize middle mouse click as
a way to open a permanent tab since 2016 (microsoft/vscode#14453).
## Solution
Wired up an `on_aux_click` handler for project panel entries:
middle-clicking a file opens it in a permanent tab and focuses it,
regardless of the preview tabs setting.
Added a line to `docs/src/project-panel.md` to reflect the behavior.
## Testing
- Manually tested on Windows: middle click opens a permanent tab,
promotes an existing preview tab to a permanent one if already open,
does nothing for directories
- Haven't tested on macOS/Linux
## Self-Review Checklist:
- [ ] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
<details>
<summary>Click to view showcase</summary>
https://github.com/user-attachments/assets/2de5f23d-637f-4ffc-8d03-02dc11714af4
</details>
---
Release Notes:
- Added support for middle-clicking a file in the project panel to open
it in a permanent tab instead of a preview tab
This fixes#56956.
The terminal link-open gesture was split between `mouse_down` and
`mouse_up`, but both halves only ran in the non-mouse-reporting path.
When a foreground app enabled terminal mouse reporting, a secondary
click on a URL or file path was forwarded to the PTY instead of opening
the link.
This changes secondary-click link handling so it checks for a hyperlink
before mouse reporting on press, and completes the matching hyperlink
open before mouse reporting on release. The event is only consumed when
the click actually lands on the same link, so normal clicks in
mouse-reporting TUIs continue to be forwarded as before.
Validation:
- `CARGO_BUILD_JOBS=1 cargo test -j1 -p terminal
test_hyperlink_ctrl_click_same_position_in_mouse_mode`
- `CARGO_BUILD_JOBS=1 cargo check -j1 -p terminal`
Release Notes:
- Improved terminal links: Cmd/Ctrl-click now opens links even when the
application has mouse reporting enabled (e.g. vim, opencode, claude).
Disable `terminal.open_links_in_mouse_mode` to forward these clicks to
the application instead.
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
The Gentoo `ebuild` file format is a subset of a bash script, see
https://wiki.gentoo.org/wiki/Ebuild.
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
Closes #ISSUE
Release Notes:
- Changed `ebuild` files to be recognized as bash.
Signed-off-by: gcarq <egger.m@protonmail.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
# Objective
- Describe the objective or issue this PR addresses. -> ** Typo and
grammar fixes!**
## Solution
## Testing
## 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
---
Release Notes:
- N/A
Fixes#53527.
## Summary
- Suggest `untitled.<extension>` when saving an untitled editor buffer
with a selected non-Plain Text language.
- Preserve the existing title-based suggestion for existing files, Plain
Text buffers, and buffers without a language extension.
- Add a regression test for an untitled Rust buffer suggesting
`untitled.rs`.
## Testing
- `mise exec rust@1.95.0 -- cargo fmt --check -p editor`
- `mise exec rust@1.95.0 -- cargo test -p editor
test_suggested_filename_uses_language_extension_for_untitled_buffer
--lib`
## Suggested .rules additions
None.
Release Notes:
- Fixed Save As suggestions for untitled buffers with a selected
language.
---------
Co-authored-by: Kirill Bulatov <kirill@zed.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)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
This fixes Go outline extraction for methods whose receiver has no name,
such as `func (v2) Method()`. These methods are now included in outline
symbols, which also feed breadcrumbs and sticky
scroll.
Tested with:
- `cargo test -p languages`
- `cargo test -p grammars`
- `./script/clippy -p languages`
- `cargo fmt --check --package languages`
Before:
[before_cut.webm](https://github.com/user-attachments/assets/91eb5cb0-703a-4496-b0dd-5369c4c219fc)
After:
[after_cut.webm](https://github.com/user-attachments/assets/76d13d88-3671-4118-99fc-c073a6e64727)
Release Notes:
- Fixed Go methods with unnamed receivers not appearing in breadcrumbs
and sticky scroll.
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
A "rename symbol" whose workspace edit also renames the file (a
`TextDocumentEdit` followed by a `RenameFile` resource operation) only
applied the text edit to the in-memory buffer. The on-disk file still
held the pre-edit content, so the blind `fs.rename` moved stale bytes to
the new path while the edited buffer was stranded at the old path,
swapping the two files' contents.
This persists a dirty buffer for the rename source before renaming, so
the new file receives the edited content and the now-clean buffer can't
be saved back to the old 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
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes#59077
Release Notes:
- Fixed a symbol rename that also renames the file swapping the contents
of the old and new files
When you use `anyhow::anyhow!("{error}")` to convert a preexisting error
to an `anyhow::Error`, the error source can be lost (depending on the
`Display` impl of the error). Anyhow errors can display the whole source
chain when printed. This commit makes us consistently use `context()`
instead to preserve the underlying error's source.
Release Notes:
- N/A
Was trying to debug an r-a issue and ran into this.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliabilityx
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A or Added/Fixed/Improved ...
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Resolves https://github.com/zed-industries/zed/issues/55600
This diff fixes `pane::ReopenClosedItem` getting stuck when the
closed-item stack contains entries that cannot be reopened, such as
Project Search, untitled buffers, or Default Settings. Previously,
`Workspace::navigate_history_impl` would pop the newest closed entry and
stop if that item was no longer present in the pane and had no path
recorded for reopening. That made `cmd- shift-t` appear to do nothing
until enough attempts had consumed those unreopenable entries.
With this change, closed-item navigation keeps scanning when it
encounters an entry that cannot be activated or reopened by path. This
preserves the current path-based reopening behavior for normal files,
while avoiding no-op shortcuts caused by non-file items in the closed
stack.
This made me wonder whether or not we'd eventually want full reopen
support for non-traditional items like Project Search or bundled
settings editors. Supporting that properly would require storing
item-specific restoration state, such as search query/options for
Project Search or a bundled-file descriptor for Default Settings, and
teaching closed-item navigation how to recreate those items from that
state. Something definitely out of scope for this PR.
| Before | After |
| --- | --- |
| <video
src="https://github.com/user-attachments/assets/c7044423-4531-4857-84f2-4e9651826c6a"
controls width="500" title="Before"></video> | <video
src="https://github.com/user-attachments/assets/c89dcefb-1796-4cdf-bb21-f165145e678e"
controls width="500" title="After"></video> |
Release Notes:
- Fixed reopening closed tabs getting stuck on closed items that cannot
be reopened.
Decodes url escape sequences in hover preview `file:///` links like
escaped
spaces in the file path.
I'm working on an LSP and happened to be working with some files in a
directory with spaces. When adding Markdown links with `file:///` the
`%20` escape for spaces was being included verbatim in the path that Zed
tried to open.
I'm reusing the lines from `markdown_preview_view.rs` for decoding. In
the existing tests I don't see coverage for `file:///` links. If you'd
like some tests for this can you point me to any examples to start from?
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:
- Fixed decoding spaces and other escaped characters in `file://` links
used in hover popovers
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
This PR makes the `send_authenticated_json_request` method on the
`CloudApiClient` public.
This way we can use it to make requests by external callers.
The `build_request` method was also inlined into
`send_authenticated_request` to make the contract simpler.
Release Notes:
- N/A
Adds native support for AWS Bedrock's Mantle endpoint
(`bedrock-mantle`), which serves models with no `Converse`/`Invoke`
support on `bedrock-runtime`, such as GPT-5.5, GPT-5.4, and Grok 4.3 but
more importantly **open-weight** models
Closes#60471
## What's changed
- Renamed the existing `Model` enum in the `bedrock` crate to
`ConverseModel`, and added a new `MantleModel` enum for Mantle-only
models. Mantle models reuse the existing OpenAI-compatible Chat
Completions/Responses request and response plumbing
(`into_open_ai`/`into_open_ai_response`,
`OpenAiEventMapper`/`OpenAiResponseEventMapper`) already used by the
native OpenAI and OpenAI-compatible providers, rather than introducing
new marshalling code.
- Added a `BedrockMantleModel` language model that routes requests to
the `bedrock-mantle` endpoint, dispatching to Chat Completions or the
Responses API depending on the model. Mantle models appear in the model
picker alongside Converse models under the same Bedrock provider.
- Added region gating: `bedrock-mantle` is only available in a subset of
AWS Regions, so using a Mantle model outside of them surfaces a clear
error naming the current Region and the supported ones, instead of an
opaque HTTP failure.
- Implemented Bedrock bearer token authentication for Mantle requests: a
configured Bedrock API key is used as-is, and every other auth method
(IAM credentials, named profile, SSO, automatic) derives a short-term
token by locally SigV4-presigning a `CallWithBearerToken` request. This
requires no extra network round trip and no token caching, since
re-signing locally is cheap.
- Added a specific error for the 403 you get when your credentials have
`bedrock:CallWithBearerToken` but not the separate
`bedrock-mantle:CallWithBearerToken` permission Mantle models require,
since this is the most common misconfiguration.
- Added a `mantle_available_models` setting so custom models served
through `bedrock-mantle` can be configured, the same way other providers
support custom models via `available_models`.
- Documented Mantle models and the new setting in the Amazon Bedrock
section of [Use a
Gateway](https://zed.dev/docs/ai/use-a-gateway#amazon-bedrock).
## Testing
- Added unit tests covering: the local SigV4 bearer-token signing
(including a byte-for-byte cross-check against a reference
implementation), Mantle endpoint URL construction, the
Mantle-supported-regions list, thinking-effort normalization, and the
settings-to-model protocol mapping.
- `cargo test -p bedrock -p language_models -p settings_content -p
settings` passes.
- `./script/clippy` passes with no new warnings.
Release Notes:
- Added native support for AWS Bedrock's Mantle endpoint, enabling
GPT-5.5, GPT-5.4, and Grok 4.3 through the Amazon Bedrock provider.
# Objective
Fix a Windows CI flake in `extension_host
extension_store_test::test_extension_store_with_test_extension`, which
panicked in GPUI's leak detector with three leaked `LspStore` handles
([example
run](https://github.com/zed-industries/zed/actions/runs/28871529605/job/85635418351)).
## Solution
`LspAccess::ViaLspStore` held a strong `Entity<LspStore>`, cloned into
three `ExtensionHostProxy` registrations by `language_extension::init`.
The proxy sits inside an `Arc` cycle (proxy →
`LanguageServerRegistryProxy` → `LanguageRegistry` →
`ExtensionLspAdapter` → `WasmExtension` → `WasmHost` → proxy), so
whenever an extension LSP adapter was registered at app-drop time, the
cycle pinned the `LspStore` entity after its owning `Project` dropped.
The flake was purely timing: extension reload toggles the adapter
registration, and an unclean LSP pipe shutdown on Windows shifted
teardown into the pinned window.
`ViaLspStore` now holds a `WeakEntity<LspStore>`, upgraded at its sole
use site (`remove_language_server`), skipping the stop task when the
store is gone — matching the existing `ViaWorkspaces` semantics. A dead
store is the expected terminal state after the owning project drops, so
no error is logged or propagated. `Project`/`HeadlessProject` remain the
sole long-term owners of `LspStore`, which is already the convention
everywhere else (e.g. `json_schema_store`, the `lsp_store` message
handlers).
## Testing
- Ran `cargo nextest run -p extension_host
extension_store_test::test_extension_store_with_test_extension` locally:
passes.
- The flake is timing-dependent (reproduced on Windows CI), so a local
pass doesn't prove absence; the fix removes the only strong non-owner
handle, which the leak detector reported.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- N/A
---------
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
## Summary
Fixes#60288 and #60454 .
After a push, the push toast's "Create Pull Request" button fails with
`Unsupported remote URL` when the repository's remote is not a plain,
recognized host. This restores the pre-#53913 behavior as a fallback:
use the create-PR/MR link that `git push` itself prints, and only build
a URL from the provider registry when the push output had no link.
## Why this matters
#53913 made the button always appear and changed `create_pull_request`
to reconstruct the PR URL from the remote via
`git::parse_git_remote_url` against the `GitHostingProviderRegistry`.
When the remote is not recognized, parsing returns `None` and the action
errors. This affects self-hosted GitLab/GitHub, an SSH-config host alias
like `git@personal:owner/repo` (the duplicate #60076), and any
non-standard host. Before #53913, the flow used the link git prints in
the push output, which works regardless of host, so this is a regression
for anyone not pushing to a plainly-recognized GitHub URL.
## Solution
`git push` prints a `remote:` line with the hosting provider's
create-PR/MR URL (GitHub: "Create a pull request for '\<branch>' on
GitHub by visiting:", GitLab: "To create a merge request for \<branch>,
visit:", Bitbucket: "Create pull request for \<branch>:"), and we
already hold the push `RemoteCommandOutput`.
- `remote_output.rs`: add `extract_pull_request_url`, which scans the
push stderr for the first `http(s)` URL on a `remote:` line tied to a
create-PR/MR prompt. It ignores unrelated URLs (for example the OpenSSH
post-quantum warning line).
- `git_panel.rs`: capture that URL in `show_remote_output` into
`pending_pull_request_url`, and prefer it in `create_pull_request`,
falling back to the existing provider construction only when the push
output had no link. The cached URL is consumed once (`take()`) and
cleared when the active repo, the active branch/head, or the pending
remote operation changes, so a later `git: Create Pull Request` action
never opens a stale URL from an earlier push.
Recognized GitHub remotes are unaffected: they still get a link from the
push output, with the provider path as the fallback.
## Testing
Unit tests in `remote_output.rs` cover `extract_pull_request_url` for
the GitHub, GitLab, and Bitbucket prompt formats, the no-link case
(returns `None`, so the provider fallback runs), and an output
containing an unrelated URL that must not be mistaken for the PR link.
The existing remote-operation test also asserts the cached URL is
cleared when a new operation starts.
`cargo test -p git_ui remote_output` passes (5 tests). Tested on macOS.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed "Create Pull Request" button in the toast shown after `git:
push` failing for repositories on unrecognized Git hosts by using the
link printed in the push output.
- Fixed the button shown on the toast after `git: push` for GitLab
branches with an existing merge request. It now shows "View Merge
Request" and links to the existing merge request.
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: dino <dinojoaocosta@gmail.com>
# Objective
Opening and closing windows quickly on Linux could crash the renderer
with a `GPU resources not available` panic.
The `gpui_wgpu` renderer keeps its GPU resources in an `Option` that is
cleared when a window is torn down, and also while device-loss recovery
is pending. On Wayland the window's `Drop` releases those resources
synchronously but defers unregistering the surface to a later task, so a
compositor resize or transparency event can still reach the renderer in
that short gap. `update_drawable_size` and `update_transparency` assumed
the resources were always present and called an accessor that panics
when they are not.
I am not certain if there is a good case to reproduce this in zed, but I
had encountered it in my own GPUI app.
## Solution
Make `update_drawable_size` and `update_transparency` tolerate missing
GPU resources by skipping the surface reconfiguration instead of
panicking, matching how the rest of the renderer already guards this
state. The requested size and alpha mode are still recorded before the
guard, so they take effect if the renderer's resources are recreated,
for example after device-loss recovery.
## Testing
Tested on Linux with Wayland.
- Reproduced by opening and closing windows quickly, which previously
panicked with `GPU resources not available`. With this change the panic
no longer occurs. This was reproducible on a debug build or a weak/slow
device.
## 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 (not sure if worth mentioning):
- Fixed a crash that could occur when opening and closing windows
quickly on Linux
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
# Problem
Since the release of the new git UI, when `~/.gitconfig` on a remote
server is a symlink pointing to a file on a virtual filesystem (a common
setup when using [OrbStack](https://orbstack.dev/) on macOS), Zed fails
to connect with "Timed out pinging remote client".
# Cause
When setting up a file watcher for gitconfig, `fs::watch()` reads the
symlink target and adds its parent directory to the poll watcher.
`notify::PollWatcher::watch()` does a full synchronous recursive
directory scan at registration time to build an initial snapshot. If the
parent is something like a Mac home directory mounted via virtiofs, that
scan blocks the server's main thread long enough that it can't respond
to the initial ping within the 5 second timeout.
# Solution
The fix I implemented for this was to skip the parent directory watch
when using a poll watcher. As far as I can tell, it's redundant in the
poll case since the poll watcher detects changes by periodically reading
metadata at the registered path, so watching the parent doesn't add
anything for change detection. From my limited testing this seems to
work fine but if someone with more experience in this part of the
codebase would like to weigh in, that would be very much appreciated.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] 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
- [ ] Performance impact has been considered and is acceptable
Release Notes:
- Fixed remote SSH connections timing out when `~/.gitconfig` is a
symlink to a file on a virtual filesystem
# Objective
Ensure that all existing panels have corresponding menu items in the
"View" menu. I was onboarding a friend to Zed yesterday that was having
a hard time figuring out how to interact with the Agent. Although he did
open the "View" menu, I noticed that the Agent panel item was missing
from there, making it hard for new users to discover it exists.
## Solution
* Add both "Agent Panel" and "Git Panel" entries to the menu items for
the "View" app menu.
* Update the action used for the "Terminal Panel" menu item from
`terminal_panel::ToggleFocus` to `terminal_panel::Toggle` to ensure we
display a shortcut for this menu item.
* Another valid option would be to update the default keymap to use
`terminal_panel::ToggleFocus` instead but that would probably break
existing user's expectations that the default shortcut toggles the
terminal panel, instead of toggling its focus.
* Introduce `zed_actions::git_panel` to be able to extract its
`ToggleFocus` action, following the existing pattern.
### Next Steps
It's worth noting that, even though there's now an "Agent Panel" item
mapped to the `assistant::ToggleFocus` action, its default keybinding is
not displayed (at least on macOS), because of the way it's defined as
`cmd-?` . Using `cmd-shift-/` instead doesn't work, so we'll likely have
to update `MacKeyboardMapper` to allow mapping between shifted and
unshifted keys equivalent, that is, when `?` is detected, it is able to
determine that, in the user's layout that is the result of `shift-/`.
## Testing
Manually tested, screenshots can be seen in the "Showcase" section.
## 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
## Showcase
<details>
<summary>Before</summary>
<img width="488" height="946" alt="CleanShot 2026-07-03 at 14 24 35@2x"
src="https://github.com/user-attachments/assets/58b0497c-2929-4da3-86b6-a2e0ce0c5ca4"
/>
</details>
<details>
<summary>After</summary>
<img width="524" height="1116" alt="CleanShot 2026-07-03 at 14 24 59@2x"
src="https://github.com/user-attachments/assets/8e2c6320-97ad-4620-b595-182f6d0ded81"
/>
</details>
---
Release Notes:
- Added "Agent Panel" and "Git Panel" items to the "View" menu
# Objective
Whenever the git repository state is updated on disk (e.g., via staging,
committing, branch switching, or stashing), `reload_buffer_diff_bases`
is scheduled to reload the diff for all active buffers. This causes 2
git processes to be spawned for each open file which can become
noticeable when many files are open
5e32405669/crates/project/src/git_store.rs (L5179)
## Solution
This PR introduces `load_revisions` which uses a single `git cat-file
--batch` command to compute the diff for all files in the same git
process. This prevents the need to sequentially schedule 2 git
subprocesses per open buffer.
I also changed `load_index_text` and `load_commited_text` to rely on
`load_revisions` which simplifies the code.
## Testing
I added a unittest and manually verified that Zed now only runs a single
`git cat-file --batch` command instead of 2 `git show` processes per
open buffer.
On macOS I viewed the currently running git processes using:
```shell
sudo eslogger exec | jq --unbuffered -r '
select(.event.exec?.target?.executable?.path? | strings | contains("git")) |
(.event.exec?.args? // []) | join(" ")
'
```
## 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
/cc @Veykril
Release Notes:
- Reduced number of git processes for calculating diff of open buffers
when the repo state changes on disk
# Objective
- Make Git Panel History show commit tags so release/version markers are
visible without opening the full Git graph or commit details.
## Solution
- Store commit history entries with both the commit SHA and tag names
from existing git graph data.
- Render tag names as muted chips next to the commit subject.
- Limit visible tags to 3 per commit and show `+N` for additional tags.
## Testing
- Ran `cargo check -p git_ui`.
- Manually verified on Linux with `script/zed-local --stateful .`.
- Confirmed tags appear in Git Panel 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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
Git Panel History now shows commit tags inline as muted chips next to
the commit subject.
---
Release Notes:
- Added tag labels to the Git Panel commit history.
---------
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This PR adds adjustments to the tree view, making it more consistent
with all other tree view displays in the app (e.g., displaying indent
guides, removing chevron toggle, etc.), and also fixes an issue where
the commit message scrollbar was scrolling up with the message.
Release Notes:
- N/A
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)
(Arguably no, but it's an okay compromise methinks)
- [ ] Tests cover the new/changed behavior (I'm unsure how one would
properly test this, sorry!)
- [x] Performance impact has been considered and is acceptable
Closes#56466
To be completely honest I don't know if this is a good fix or not, it
does fix the problem I was running into where opening the large mermaid
diagram would blow up VRAM. It doesn't look amazing visually but I would
consider this behavior better, if it's not a good fix then that's okay:)
I chose 8192 because 8192 only brings VRAM usage up ~100MB in my testing
while 16384 brought my VRAM usage up to about 1GB from 150-200MB, for
lower end systems this seems unacceptable.
Before:
I can't take a screenshot of the before at this point because it eats my
system VRAM & Memory too fast. As a text description; It would show a
large empty rectangle where the mermaid diagram should be and blow up
Zed's VRAM usage from ~300MB to ~22GB (all of the available VRAM in my
system)
After:
<img width="1698" height="763" alt="image"
src="https://github.com/user-attachments/assets/62eb7c95-cca8-43f9-8257-c7e529f26e8d"
/>
<img width="1000" height="31" alt="image"
src="https://github.com/user-attachments/assets/4315c029-3cdd-44f6-ac78-971d125ab700"
/> (Up from ~150MB), the 257MiB figure is the GPU Memory.
Release Notes:
- N/A?
Co-authored-by: Lukas Wirth <lukas@zed.dev>
## Summary
- Refresh GPUI's cached mouse position when window bounds change so
hover hit-testing uses the current cursor position after live resize.
- Return X11 mouse positions in window-relative logical pixels to keep
`PlatformWindow::mouse_position()` consistent with other backends.
Fixes#57354
## Testing
- `cargo fmt -p gpui -p gpui_linux`
- `cargo check -p gpui_linux`
- `cargo check -p gpui`
## Suggested .rules additions
- In GPUI platform backends, `PlatformWindow::mouse_position()` should
return window-relative logical pixels; use separate APIs or fields for
global/device-pixel coordinates.
Release Notes:
- Fixed incorrect hover state while resizing GPUI windows.
Release Notes:
- Fixed clear drag overlay when external drag ends outside window
When dragging files from macOS Finder over the project panel and then
dragging back to Finder, the drag overlay remained visible because the
drag state was not properly cleaned up.
The root cause was that only `draggingExited:` was handled, but not
`draggingEnded:`. On macOS:
- `draggingExited:` is called when the drag leaves the window area
- `draggingEnded:` is called when the drag operation ends entirely
When a user drags a file back to Finder and drops it there,
`draggingEnded:` is called but was not being handled.
---------
Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
# Objective
The Copilot sign-in dialog was created without an `app_id` or window
title, resulting in an empty WM class/title on Linux. Tiling window
managers with class-based no-focus rules (like Hyprland's default
configuration in Omarchy) treat such windows as anonymous popups and
refuse to focus them, making the dialog impossible to interact with.
## Solution
Set both `app_id` and window title on the Copilot code verification
window, following the established pattern used in other UI components
like `agent_ui` and `settings_ui`.
Added `release_channel` as a dependency to
`crates/copilot_ui/Cargo.toml` and called
`app_id(ReleaseChannel::app_id(cx))` and `window_title("Use GitHub
Copilot in Zed")` in `open_copilot_code_verification_window`.
## Testing
Verified on Hyprland (Omarchy) by inspecting window properties with
`hyprctl clients`:
**Before (empty class/title):**
```
Window 5606ee1dd280 -> :
class:
title:
acceptsInput: 0
```
**After (with proper class/title):**
```
Window 5606ee22b660 -> Use GitHub Copilot in Zed:
class: dev.zed.Zed-Dev
title: Use GitHub Copilot in Zed
acceptsInput: 1
```
The dialog now receives keyboard focus and mouse input correctly on
Hyprland. I tested on Linux only; this fix lives in the window creation
call so it is a no-op on macOS and Windows where `app_id` is ignored.
## 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 Copilot sign-in window not being focusable on Hyprland and
similar tiling window managers
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
The unmount of the update disk image was made async-and-detached in
#38867, which introduced a race: the installer TempDir was dropped
(running remove_dir_all) while the DMG was still mounted inside it. The
removal failed silently, leaking a zed-auto-update* dir containing the
~140 MB DMG in /private/var/folders on every update.
Now the unmount is awaited before the temp dir is dropped (installation
already runs on the background executor since #58767, so this no longer
blocks the UI), with the Drop impl kept as a safety net for early exits
and cancellation. Additionally, stale installer dirs older than 24 hours
are swept from the temp dir when update polling starts, so existing
accumulated leaks get reclaimed.
Closes FR-104
Closes#58835
Release Notes:
- Fixed the macOS auto-updater leaking a copy of the downloaded update
in the system temp directory on every update, and added cleanup of
previously leaked files.
**TL;DR**: model updates + reasoning levels + fixes discovered when
working on https://github.com/zed-industries/zed/pull/60373
# Objective
Since the model auto-discovery PR was
[cancelled](https://github.com/zed-industries/zed/pull/60373#issuecomment-4886521448),
here is a manual model list update! I also copied the stand-alone
bugfixes/enhancements from that PR.
## Solution
A lot of manual work 😅
**OpenCode Zen**:
- added Fable 5 and Sonnet 5
- added models that were previously only available on OpenCode Go: GLM
5.2, Kimi K2.7 Code, and Minimax M3
- added reasoning levels for all models. I started from the data on
[`Models.dev`](https://models.dev) (the `/api.json` raw data), and then
I matched that with what is shown in the OpenCode CLI and what I know to
be true
**OpenCode Go**:
- added reasoning levels for GLM 5.2
**OpenCode in general**:
- added `protocol` validation for the settings, by moving from a random
`String` to an `enum`, for both nicer error messages (random strings or
typos will get an error instead of using `openai_chat` by default) and
to avoid issues like [folks saying non-existent protocols are a
thing](https://github.com/zed-industries/zed/issues/56869#issuecomment-4550154554)
- enabled parallel tool calls by default. As per [OpenCode developer on
Discord](https://discord.com/channels/1391832426048651334/1471233160993050918/1472020924881702912),
_"almost all models worth using support parallel tool calling
natively"_. Manual tests confirmed all OpenCode Go models support this
correctly (and was enabled by default on the OpenCode side for all but 1
model). I initially wanted to skip this from the release notes, but I
added it so folks are aware of it in case any issues are caused by this
being enabled for all models
- allegedly fixed Google thinking since reasoning levels / thinking
effort levels were added for Google models and an auto-checker LLM
highlighted that was not properly configured
- added support for the new-ish `supports_disabling_thinking` so
thinking-only models don't get a no-impact toggle to disable thinking
I have no idea if any of the Free models will disappear in 2 days or
not, so I did not update those 🤷 (as per decision in
https://github.com/zed-industries/zed/issues/56869#issuecomment-4466637648)
## Testing
The Zen and Google changes were not tested as I don't have a Zen
subscription and I stubbornly refuse to get one.
The Free&Go changes were tested by running a "_rename this variable for
me. add a function. delete the function_" test with a few different
models.
## 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:
- Agent: OpenCode settings now validate `protocol` values
- Agent: OpenCode only shows the "Disable thinking" toggle if thinking
can indeed be disabled/enabled
- Agent: OpenCode models now enable parallel tool calls by default
- Agent: Updated OpenCode Zen models (added Fable 5, Sonnet 5, GLM 5.2,
Kimi K2.7 Code, and Minimax M3)
- Agent: Added OpenCode Go GLM 5.2 reasoning effort levels
- Agent: Added reasoning effort levels for all OpenCode Zen models
- Agent: Fixed thinking for OpenCode Zen Google models
Fixed my PR #58753.
Change the url placeholder from http://localhost:11434 to
http://localhost:8080/v1/completions to match the URL endpoint in the
[docs](https://zed.dev/docs/ai/edit-prediction)
```json
{
"edit_predictions": {
"provider": "open_ai_compatible_api",
"open_ai_compatible_api": {
"api_url": "http://localhost:8080/v1/completions",
"model": "deepseek-coder-6.7b-base",
"prompt_format": "deepseek_coder",
"max_output_tokens": 512
}
}
}
```
Note: http://localhost:8080/v1/completions/ with an extra / does not
work.
Added the constants OPEN_AI_COMPATIBLE_API_URL_PLACEHOLDER and
OPEN_AI_COMPATIBLE_MODEL_PLACEHOLDER.
### Initial Issue
I noticed that using http://localhost:8080 doesn't work with llama.cpp.
Giving errors like this from (zed: open log):
```
2026-06-06T17:55:39+01:00 ERROR [crates/edit_prediction/src/edit_prediction.rs:2464] custom server error: 404 Not Found - {"error":{"message":"File Not Found","type":"not_found_error","code":404}}
```
After reading the docs above, I found out that I had to add
/v1/completions to the end.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
# Objective
Prevent a macOS crash in GPUI when AppKit temporarily reports that a
visible window has no associated `NSScreen` during display
reconfiguration.
The crash can happen on this path:
```text
NSScreen::deviceDescription
gpui_macos:🪟:display_id_for_screen
gpui_macos:🪟:MacWindowState::start_display_link
gpui_macos:🪟:window_did_change_screen
```
`start_display_link` checked the window occlusion state before creating
a display link, but it still assumed `NSWindow.screen()` was non-null.
During display changes, sleep/wake, lid close/open, or monitor
reconfiguration, AppKit can transiently return `nil` for `screen`, and
passing that into `NSScreen::deviceDescription` aborts with a null
pointer dereference.
I observed this on macOS 26.5 after the system entered a rare display
state: once in that state, running GPUI applications would crash
immediately after wake. I have not identified the exact OS/display
condition that triggers it, so the full wake/reconfiguration scenario is
not deterministic, but the crash report consistently points to
`NSWindow.screen()` being `nil` when `start_display_link` tries to
create a display link.
## Solution
Make `display_id_for_screen` explicitly handle a null `NSScreen` by
returning `None`.
Callers now handle that case by either:
- returning early from `start_display_link`, because there is no valid
display id to create a display link for yet
- skipping a null screen while iterating `NSScreen::screens`
The normal non-null screen path is unchanged.
This keeps the nil-screen guard at the FFI boundary where
`NSScreen::deviceDescription` is called.
## Testing
Tested on macOS 26.5.
Commands run:
```bash
cargo fmt --check -p gpui_macos
cargo test -p gpui_macos display_id_for_screen_returns_none_for_null_screen
cargo check -p gpui_macos
```
Added a unit test covering the new boundary behavior:
`window::tests::display_id_for_screen_returns_none_for_null_screen`
## 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 macOS crash that could occur when display configuration
changes while a GPUI window is temporarily not associated with a screen.
## Summary
- Store Windows window minimizable and resizable capabilities on
`WindowsWindowInner`.
- Prevent custom titlebar hit testing from returning minimize/maximize
button hit targets when the corresponding capability is disabled.
- Swallow disabled native non-client minimize/maximize button mouse
events so they cannot fall through to the default window procedure or
GPUI's manual `ShowWindowAsync` handling.
Fixes#52067.
## Validation
- `cargo fmt --package gpui_windows`
- `cargo check -p gpui_windows`
- `git diff --check`
Release Notes:
- Fixed disabled minimize and maximize window controls still activating
on Windows.
On macOS, `load_family` was inserting every font into
`font_ids_by_postscript_name` without checking for duplicates. When two
installed font files claim the same PostScript name — typically an older
Geist Mono left behind alongside the current brew cask — the second
insert overwrote the first. After shaping, `id_for_native_font` looked
up that PostScript name and got back the *other* font's `FontId`, so the
rasterizer drew glyphs from the wrong table
See #55472 for the screenshot
This adds a local `HashSet` to dedup within a single `load_family` call.
Scoping it to one call (rather than the global map) matters: the same
family gets reloaded under different `FontKey`s when `FontFeatures` or
`FontFallbacks` change, and a global check would skip every font on the
reload and break weight selection.
Cross-family PostScript name collisions and CoreText fallback
substitutions that re-introduce a conflict are out of scope here
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:
- Fixed glyph rendering when fonts share a PostScript name on macOS
---------
Co-authored-by: Lukas Wirth <lukas@zed.dev>
Hello, I've been loving using GPUI! Recently I noticed that calling
`.with_animiation()` would give an `AnimationElement<E>` which did not
allow `.child()` to be called on it. It was a quick fix, I hope it's a
quick easy merge but let me know if you'd like anything changed :)
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
Co-authored-by: Lukas Wirth <lukas@zed.dev>
# Objective
Wayland windows have no way to restrict which parts of the surface
accept pointer and touch input. This adds support for setting an input
region, so events outside it pass through to whatever is below the
window. This is useful for shaped or partially click-through windows.
Clicks in green area can pass through the window, clicks in red area do
not:
<img width="1057" height="395" alt="image"
src="https://github.com/user-attachments/assets/2039af62-e43b-4834-b877-edad2a8f5ccf"
/>
## Solution
Add `Window::set_input_region`, which takes `Option<&[Bounds<Pixels>]>`:
- `Some(rects)` restricts pointer and touch input to the union of the
rectangles, in window coordinates.
- `Some(&[])` is an empty region, so the window receives no input at all
and is fully click-through.
- `None` resets the region to the default, so the whole window receives
input again.
On Wayland this maps to `wl_surface.set_input_region`, building a
`wl_region` from the rectangles or clearing it for `None`, and commits
so the change applies immediately rather than waiting for the next
frame. The method is a no-op on other platforms.
## Testing
Tested on Linux with Wayland.
- Tested in my own GPUI application, which uses a fullscreen layer for
overlays while allowing clicks outside of the rendered elements to be
passed through to the underlying windows.
- No automated test was added, since this calls through to the
compositor and is checked by observing input routing.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- N/A
Closes#60162.
MCP servers whose tool `inputSchema` uses `$ref`/`$defs` (e.g., Notion
MCP v2.x, and any server using Zod/Pydantic-generated schemas) are
silently rejected with:
```
ERROR Schema cannot be made compatible because it contains "$ref"
```
The affected tools are dropped from the agent panel — the user never
sees them and there is no user-visible error.
## Root cause
`adapt_to_json_schema_subset` rejects any schema containing `$ref` via
`UNSUPPORTED_KEYS`:
```rust
const UNSUPPORTED_KEYS: [&str; 4] = ["if", "then", "else", "$ref"];
```
This is hit by every provider that uses `JsonSchemaSubset` format
(Google Gemini, xAI Grok, OpenAI-compatible proxies, Vercel AI Gateway,
Copilot Chat for Google/xAI vendors, OpenRouter for gemini/grok models).
Providers using `JsonSchema` (Anthropic direct, OpenAI direct) don't hit
this check — `$ref` is passed through to the API, which may or may not
handle it correctly.
This is not an edge case. Every modern MCP server using Zod
(TypeScript), Pydantic (Python), or JSON Schema with shared definitions
generates `$ref`/`$defs` in tool schemas.
## Fix
Add a `resolve_refs` step in `adapt_schema_to_format` that dereferences
all `$ref` pointers using the document's own `$defs` (or legacy
`definitions`) map, making the schema self-contained before
format-specific processing. Applied at the entry point so both
`JsonSchema` and `JsonSchemaSubset` formats benefit.
**Scope note:** previously `JsonSchema` providers (Anthropic direct,
OpenAI direct) received the raw `$ref`/`$defs` and were expected to
handle it themselves — which most do not. After this change, both paths
receive a self-contained schema with refs inlined. This is intentional:
it fixes the same root cause for both paths and avoids provider-specific
behavior divergence.
Supported `$ref` forms:
- `#/$defs/<name>` (JSON Schema draft 2019-09+)
- `#/definitions/<name>` (draft 4-7 legacy)
Edge cases:
- **Nested `$ref`** (definition references another definition): resolved
recursively.
- **Sibling properties alongside `$ref`** (e.g. `{ "$ref": "...",
"description": "..." }`, legal under draft 2019-09+): merged onto the
resolved definition, with siblings overriding the definition's keys.
- **Cyclic references** (A → B → A, or self-referential schemas like a
Tree node): replaced with an empty schema `{}` ("any JSON value"). The
tool still works, just without type info for that recursive field.
- **Unsupported `$ref` forms** (e.g., external URLs): returns an error
with a clear message.
- **Missing definition target**: returns an error naming the missing
ref.
## Testing
Added 10 unit tests in `crates/language_model_core/src/tool_schema.rs`
that cover the patterns produced by Zod/Pydantic-generated MCP schemas:
- `test_refs_are_resolved_via_adapt_schema_to_format` — basic `$ref` →
`$defs` resolution
- `test_refs_in_defs_are_resolved` — nested `$ref` (definition
references another definition)
- `test_refs_in_array_items_are_resolved` — `$ref` inside `array.items`
- `test_legacy_definitions_prefix_is_supported` — old
`#/definitions/<name>` prefix
- `test_schema_without_defs_is_unchanged` — schemas with no `$defs` are
unaffected
- `test_refs_fail_for_unsupported_prefix` — external URL refs error
clearly
- `test_refs_fail_for_missing_definition` — missing target errors
clearly
- `test_cyclic_refs_are_replaced_with_empty_schema` — A → B → A cycle
replaced with `{}`
- `test_self_referential_ref_is_replaced_with_empty_schema` — Tree-like
self-ref replaced with `{}`
- `test_ref_sibling_properties_are_preserved` — sibling properties
alongside `$ref` are merged onto the resolved definition
Existing tests (which call `adapt_to_json_schema_subset` and
`preprocess_json_schema` directly) are unaffected — the fix is additive
at the `adapt_schema_to_format` level.
## Disclosure
I used an LLM to help draft the implementation and tests. I reviewed and
understand the change — it adds one new function (`resolve_refs`) with
two helpers (`parse_ref`, `resolve_refs_recursive`), plus unit tests.
Release Notes:
- Fixed MCP tools with `$ref`/`$defs` in their `inputSchema` being
silently rejected by providers using the JSON Schema Subset format
(Google Gemini, xAI Grok, OpenAI-compatible proxies, etc.). Tools from
servers like Notion MCP v2.x, and any server using Zod or
Pydantic-generated schemas, now work correctly.
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
Closes#56973, Follow-up for
https://github.com/zed-industries/zed/pull/56976
When a completion's `additionalTextEdits` contained a zero-width
insertion touching the edge of the primary edit, the overlap check
treated it as overlapping and silently skipped it. This broke
rust-analyzer's ref-match completions (`&foo`), which deliver the `&` as
a zero-width additional edit at the primary edit's start (#56973). The
same root cause previously broke auto-imports at the very start of a
file (#26136), which was worked around in #37746 with a special case for
edits at position (0, 0).
This PR replaces that special case with a general rule: a zero-width
additional edit only overlaps the primary edit when it falls strictly
inside it. i.e. touching the boundary is fine.
This one check handles both the file-start auto-import case and the `&`
ref-match case, so the (0, 0) workaround from #37746 is removed.
Non-insertion edits still go through the original overlap check from
#1871.
Added three regression tests:
- the file-start auto-import (#26136)
- the `&` insertion at the primary edit's start (#56973)
- overlapping edits being skipped while non-overlapping ones apply
(general skip case)
Release Notes:
- Fixed rust-analyzer completions like `&some_var` inserting only the
variable name and dropping the leading `&` when accepted.
# Objective
Fixes#59979
With hard tabs on, expanding a multi-line snippet only re-indents the
lines that already start with a tab. Anything with no leading whitespace
(closing brackets, mostly) stays at column 0.
## Solution
Block autoindent shifts each line by the first line's delta, but only
when the line's indent kind matches the target kind. A line with no
indentation defaults to `IndentKind::Space`, so under hard tabs it never
matches and gets skipped. An empty indent doesn't really have a kind, so
I let it adopt the target kind before the check. Lines that actually
have space indentation in a tab buffer are still left alone. Normalizing
those felt like a bigger question than this bug, happy to look at it
separately if wanted.
## Testing
New `test_autoindent_block_mode_with_hard_tabs`, same shape as the
snippet in the issue. Fails on main (closing brace stays at column 0),
passes with the fix. `cargo test -p language` and `-p editor` are green,
clippy and fmt too.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Fixed multi-line snippets leaving unindented lines at column 0 when
`hard_tabs` is enabled.
Zed bundles the markdown grammar's block scanner natively, and its
`serialize()` `memcpy`s the open-block stack into tree-sitter's fixed
1024-byte serialization buffer with no bounds check. Markdown with
roughly 255+ nested blocks overflows that buffer, and because it sits at
the front of `struct TSParser`, the overflow clobbers the adjacent
parse-stack pointer and heap. Debug builds of the tree-sitter runtime
catch this with an assertion, but release builds like Zed's have no
check and silently corrupt parser memory — which is why this surfaced as
wild crashes deep in tree-sitter's parse stack rather than clean
failures. Still open upstream as
tree-sitter-grammars/tree-sitter-markdown#243.
This PR points `tree-sitter-md` at a `zed-industries` fork whose
`serialize()` refuses to write state that doesn't fit (bounded by the
same running counter the header writes advance, so the check can't
drift). The scanner then deserializes to a fresh state, and the
pathologically nested region surfaces as ordinary tree-sitter `ERROR`
nodes — visible, safe degradation for an adversarial input class,
deliberately chosen over the two alternatives: truncating the block
stack would deserialize into a plausible-but-wrong state and produce
silently incorrect trees, and the scanner ABI offers no error channel at
all (`serialize()` returns a length into a fixed buffer; there is no way
to fail a parse). A nesting-depth cap at block-open time would give
fully deterministic semantics, but that's a behavior change across 13
scanner call sites that belongs upstream, not in a hotfix fork.
The pinned branch is upstream's `9a23c1a9` (the revision Zed already
pinned) plus exactly two commits, for easy review: the guard
(zed-industries/tree-sitter-markdown@179422edf8)
and regression tests
(zed-industries/tree-sitter-markdown@b596e73728).
The deep-nesting test aborts on the unguarded scanner and passes with
the guard; a moderate-nesting test pins that inputs fitting the buffer
still parse cleanly. The same change is also up as
zed-industries/tree-sitter-markdown#1 into the fork's default branch
(`split_parser`), so future pin bumps don't lose it; if upstream fixes
#243, we can drop the fork entirely on the next bump.
Closes FR-115
Release Notes:
- Fixed a potential crash when editing Markdown with deeply nested
blocks
This PR explores the addition of a new feature and UI to improve
visibility into partially staged commits.
Currently, the Git panel shows tracked and untracked changes, but it
does not clearly distinguish between staged and unstaged changes.
As a result, it’s difficult to quickly see which changes are not staged
in the current UI. Both staged and unstaged changes are combined into
the `Uncommitted Changes` multibuffer. This developer experience differs
from other editors, most notably VS Code; which presents separate Staged
Changes and Changes lists.
### Staged and unstaged diffs in multibuffers
This PR introduces an alternative UI for unstaged changes that aligns
with the overall Zed experience. Instead of showing changes on a
per-file basis, staged and unstaged diffs are each displayed in their
own multibuffers, similar to how `Uncommitted Changes` currently works.
For example the following screenshot shows the current `Uncommitted
Changes` on the left, the `Staged Changes` in the middle and the
`Unstaged Changes` buffer on the right for comparison
<img width="1408" height="859"
src="https://github.com/user-attachments/assets/aa709f7a-041d-4cb1-95d6-84c0f5fff688"
/>
### Indicators/interactions
The new multibuffers can be opened in two ways:
1. Via a new `U` chip, which appears when a file has unstaged changes
2. Via new menu options
(See screenshots below for both interaction paths.)
<table>
<tr>
<td style="text-align: center; vertical-align: top;">
<p>via the chip</p>
<img
height="400"
src="https://github.com/user-attachments/assets/3ef69f02-b787-499c-959a-25f50b3728e8"
alt="Via the chip"
/>
</td>
<td style="text-align: center; vertical-align: top;">
<p>via the menu</p>
<img
height="400"
src="https://github.com/user-attachments/assets/f5be8b6d-ccdc-4420-bd29-75570b558016"
alt="Via the menu"
/>
</td>
</tr>
</table>
### Design goals
- minimally intrusive UI changes (small new badge and menu items)
- adhere by Zed'ism (use multibuffer where possible)
- avoid disabling any current interactions (Uncommitted Changes ui is
unchanged)
- avoid introducing an app level view mode (no new settings needed)
### Experience goals
- make it easy to see what changes are not staged
- make it easy to see that a file has unstaged changes (avoid developers
accidently leaving out changes in a commit; a personal issue that I have
when using Zed)
- elegantly handle large file's unstaged changes (follows the same
collapse and expanding seen in `Uncommitted Changes`)
### How to try
- Clone the repo and run `cargo run`
- Make a change to a file and stage it
- Make another change to the file (the `U` indicator will appear)
- Click the `U` to see the unstaged view
### Open questions/rough edges
- [ ] determine if this user experience is useful for others
- [ ] ensure all interactions work as expected (response to all update
cases)
In general I'm really interested in hearing the community's feedback
about this interface, more than happy to make any changes or explore a
different solution!
### Related issue:
- https://github.com/zed-industries/zed/pull/36646
- https://github.com/zed-industries/zed/issues/26560
Release Notes:
- Support partially staged commit multibuffers via a staged and unstaged
changes view.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cole Miller <cole@zed.dev>
Stacked on #60502 and targets it — #60502 must land first (its
orchestrator fix is what lets this `tooling/lints/`-touching PR go green
rather than tripping the `rdeps(lints)` nextest filter). Re-creates the
symlinks from #60504, which was accidentally merged into #60502's branch
and reverted.
Follow-up to #60468, which added a `LICENSE-APACHE` symlink to the
`tooling/lints` crate but not to the `test_fixture` sub-crates. Those
five sub-crates each carry a `Cargo.toml`, so `script/check-licenses`
requires a `LICENSE-GPL`/`LICENSE-APACHE` symlink in each, and
`check_licenses` fails on any PR that changes `Cargo.lock`. This adds
`LICENSE-APACHE` symlinks to the five fixture crates, matching #60468;
`script/check-licenses` passes locally afterward.
Release Notes:
- N/A
The `orchestrate` job maps changed directories to root-workspace package
names, and when no mapping was found it fell back to using the raw
directory name as a package. Because `tooling/lints` is a separate
workspace (not a root-workspace member), a change under
`tooling/lints/**` produced the filter `rdeps(lints)`, which `cargo
nextest run --workspace` rejects with "operator didn't match any
packages" — failing `run_tests` for any such PR. This drops that
fallback so an unmapped directory falls through to the existing "no
package changes → run all tests" path instead.
Release Notes:
- N/A
This PR adds the new `id_v2` and `legacy_user_id` fields to the
`AuthenticatedUser` type and starts using them in place of the `id`
field.
Release Notes:
- N/A
PR https://github.com/zed-industries/zed/pull/45892 changed the logic
for rendering completion labels and divided rendering up into the "main"
part (consisting of the completion's `filter_range`) and "details" part
(everything after `filter_range`), but no longer renders text that comes
before the `filter_range`. This text is probably not relevant in too
many cases but makes some Rust completions unclear and ambiguous, as can
be seen in this example:
Without this change:
<img width="1262" height="313" alt="Image"
src="https://github.com/user-attachments/assets/dcdd9da4-c449-445d-b379-771b368c3778"
/>
With this change:
<img width="1328" height="376" alt="grafik"
src="https://github.com/user-attachments/assets/05fdaf95-7a18-4d50-8e3b-35be10d9a1ef"
/>
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes#56973
Release Notes:
- Fix completion labels not being rendered completely
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Follow-up of https://github.com/zed-industries/zed/pull/59994 deals with
<img width="173" height="39" alt="restart to update"
src="https://github.com/user-attachments/assets/5209e15b-cbf9-4530-b0da-7326c8131448"
/>
button overly appearing.
The PR mentioned fixed the redownload issue, but the title bar code
re-surfaced the button on every update check as `is_updated` in this
semantics means a new Nightly update is ready to be installed:
159246f008/crates/auto_update/src/auto_update.rs (L171-L173)
and the old code used this as "can show the button again" reason after
each recheck.
Instead, track dismissed update state better both in the title bar and
the global auto updater, so no new version checks can trigger this and
no new Zed windows get this button again (as it is now).
Release Notes:
- Improved "Restart to Update" button dismissals
Seeing `script/check-license` fail because we forgot to add a license in
#58496
> Error: tooling/lints does not contain a LICENSE-GPL or LICENSE-APACHE
symlink
Release Notes:
- N/A
This PR adds a bunch of design improvements to the toolbar of (tab)
views that display diffs: the uncommitted changes, branch diff, and
agent diff tabs. This involves adjusting spacing, sizing, and other
small tweaks across all of these views, including touching up the
divider component API a little bit, adjusting Git icons design, adding
diff stat numbers to the uncommitted changes tab so its consistent with
the others, and more (e.g., moving the split buttons into its own
component to ensure they look the same across all uses). Ended up also
going on a slight de-tour to make all uses of the split button in the
Git panel consistent design-wise.
All in all, this is just tidying up the design of all of these surfaces
that are all relatively similar.
| Branch Diff | Uncommitted Diff | Single-file Diff | Git Panel |
|--------|--------|--------|--------|
| <img width="800" alt="Screenshot 2026-07-06 at 11 25@2x"
src="https://github.com/user-attachments/assets/4ebf87e7-c52d-41d9-9de3-7944125114a1"
/> | <img width="800" alt="Screenshot 2026-07-06 at 11 25 2@2x"
src="https://github.com/user-attachments/assets/51ccd2eb-904b-455a-a708-b4cb50b3a7cb"
/> | <img width="800" alt="Screenshot 2026-07-06 at 11 26@2x"
src="https://github.com/user-attachments/assets/6ae7df68-d1bd-4d36-a250-ba16e43c4e8e"
/> | <img width="800" alt="Screenshot 2026-07-06 at 11 26 2@2x"
src="https://github.com/user-attachments/assets/e032bc60-4551-41f0-b578-90ed305167c2"
/> |
Release Notes:
- Added diff stat numbers to the uncommitted changes view.
- Fixes#60238.
- The text finder could spike memory into the tens of gigabytes and
crash the app. The finder was copying the full matched line into every
match it kept, so memory grew with the number of matches times the size
of each line. Enough matches carrying enough text and the process runs
out of memory.
## Solution
- Stop storing line text on matches. Each match now keeps only its
position and column, and the text shown in a row is built lazily for the
visible rows, using a bounded slice around the match rather than the
whole line. Line boundaries come from the buffer's line index instead of
re-scanning the content per match.
- Cap the number of matches the finder builds while streaming results,
using the same ceiling project search already applies. This keeps the
finder from ever assembling an unbounded match list on the UI thread.
- Behavior is unchanged: every occurrence is still its own row, jumping
to a match lands on the exact line and column, and the rendered text and
highlighting look the same. The finder just no longer holds text
proportional to the match count.
## Testing
- Added tests covering the new behavior: the match count is capped while
streaming, every occurrence below the ceiling still becomes its own
match at the correct line and column, and the rendered slice around a
match stays bounded on a huge line while still covering a whole short
line.
- Verified by hand with the reproduction from the issue. The screenshot
below shows the memory usage and the matching against the issue data:
<img width="1277" height="1053" alt="Screenshot 2026-07-03 at 17 43 11"
src="https://github.com/user-attachments/assets/71c0aa3b-2423-427d-91ba-792951d9ba31"
/>
## 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 and high memory usage in the text finder.
## Objective
- Provide initial preview layout information for the picker delegate.
This ensures that when match items with different styles need to be
displayed under horizontal or vertical layouts, they render correctly on
the first display.
## Solution
- Pass the initial layout information to the delegate in the picker
constructor
## 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
# Objective
Fixes#60436
Pressing Escape (or triggering any workspace-level action that closes
modals, such as `pane::DeploySearch` / `search::NewSearch`) while the
Text Finder was open crashes the app with:
```
cannot read workspace::Workspace while it is already being updated
crates/gpui/src/app/entity_map.rs:164
```
`TextFinder::on_before_dismiss` read the `Workspace` entity
(`workspace.read(cx).database_id()`) to persist the last search query.
Dismissal can be initiated from inside a `workspace.register_action`
handler — e.g. `buffer_search`'s `SearchActionsRegistrar` calls
`workspace.hide_modal(...)` while the `Workspace` entity is leased — so
the modal layer invokes `on_before_dismiss` synchronously under that
lease, and the read trips GPUI's re-entrancy guard. Since the finder
seeds the last query on open, the query is non-empty immediately, making
the crash reproducible on the very first Escape.
Dump file analysis confirms the diagnosis.
## Solution
Stop reading the `Workspace` entity in the dismiss path. Both places
that create the modal (`TextFinder::open` and
`TextFinder::open_from_project_search`) already run inside
`workspace.update_in`, where `workspace.database_id()` is a plain field
access on `&mut Workspace`. Capture the `Option<WorkspaceId>` there,
store it on `TextFinder`, and have `on_before_dismiss` use the stored
id. The dismiss path now touches no entity that can be mid-update, so it
is safe regardless of which code path initiates `hide_modal`. The
remaining reads in `on_before_dismiss` (`picker` and its query editor)
are never leased in any dismissal chain.
The `id` is captured once at creation; a workspace that gains a database
id during the modal's lifetime would persist under `None` (i.e. skip
persistence), which matches the previous behavior for unpersisted
workspaces.
## Testing
- Verified the fix by code review and re-tested in a rebuilt bundle
(local build).
- To reproduce/verify: open the Text Finder (`text_finder::Toggle`),
type a query (or rely on a seeded one), then press Escape with a binding
that resolves to `editor::Cancel`, or press `cmd-shift-f`
(`DeploySearch`) while the finder is open. Before this change the app
crashes. After it, the modal dismisses and the query is persisted.
- Tested on macOS 26.5.1 (arm64); the affected code is
platform-independent.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks — no unsafe blocks
- [x] The content adheres to Zed's UI standards — no UI changes
- [x] Tests cover the new/changed behavior — added
- [x] Performance impact has been considered and is acceptable — one
`Option<WorkspaceId>` copy at modal creation
---
Release Notes:
- Fixed a crash when closing the Text Finder while a workspace action
(e.g. Escape via `editor::Cancel`, or deploying search) triggered the
dismissal
Document how to configure the Tailwind CSS language server for Go
(Templ).
Ref #43969.
Release Notes:
- N/A
---------
Co-authored-by: Kunall Banerjee <hey@kimchiii.space>
This PR makes it so that when you install an agent from the registry we
set it as the default agent. We apply the same behaviour when installing
an ACP agent from the onboarding page, which should make it less
confusing for new users (they won't see the Zed agent when they see a
new thread, but instead see the agent they installed)
https://github.com/user-attachments/assets/81f4711f-4c9a-4606-882a-9f944c90dfa3
Release Notes:
- agent: Improved onboarding experience when installing ACP agents
Documents how to configure the Tailwind CSS language server for Gleam,
and updates the link on the Tailwind landing page to anchor at the new
section so it matches the pattern used for Astro, ERB, HEEx, HTML,
TypeScript, JavaScript, PHP, Svelte, and Vue.
The config follows what was documented in #43968 when Gleam was added to
`tailwind.rs`.
Refs: #43969
Release Notes:
- N/A
---------
Co-authored-by: Kaedin Hano-Hollis <180361443+kaedinhanohano@users.noreply.github.com>
Co-authored-by: Kunall Banerjee <hey@kimchiii.space>
Removing the feature flag now that the API is stable.
Release Notes:
- acp: Support boolean toggles for ACP session configuration in agents
that use them.
---------
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
● Closes#50285
## Summary
When `inline_blame` is disabled in settings and `editor::BlameHover` is
triggered,
the blame popover fails to appear on the first invocation because
`start_git_blame`
kicks off an async task to fetch blame data, but `blame_hover`
immediately tries to
read that data synchronously before it's available.
This fix registers a one-shot observation on the blame entity when it's
freshly
created, so the popover is shown once blame data has finished
generating.
Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [ ] ~Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)~
(No UI changes)
## Videos:
### Before:
https://github.com/user-attachments/assets/d2ca4f8e-8186-49af-9908-5a82bfca0de2
Ps: pressed the keybind two times
### After:
https://github.com/user-attachments/assets/08575cd9-2bbc-462d-92fa-f0e7ef23d8d6
## Release Notes:
- Fixed blame hover popover not appearing on first trigger when inline
blame is disabled (#50285)
---------
Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com>
This PR improves the onboarding experience when using the agent panel.
Previously we would pick a fallback model in case the provider failed to
authenticate/was slow to resolve models. However, this code had a race
condition (for providers that resolve models dynamically like
Anthropic/GitHub Copilot), since we pick a fallback immediately after
all providers have been authenticated. However, at that point the
configured provider might not have resolved its models, so we would fall
back to a different provider. At some point we added a workaround for
the zed.dev provider.
We landed on a much simpler approach that eliminates the race condition:
We only pick a fallback model in case the user actually has no model
configured in his settings. In case the user has a model configured, we
won't fallback and show an actionable error message:
1. Model is not set and no fallback available
<img width="862" height="78" alt="image"
src="https://github.com/user-attachments/assets/e8e7472a-1c05-4cd2-8efc-49e6d921b0a4"
/>
2. Model is set, but provider is not authenticated
<img width="865" height="65" alt="image"
src="https://github.com/user-attachments/assets/3c8cbf1d-7dd5-4b2e-809a-61e6662721a3"
/>
3. Model is set, provider is authenticated, but model is not in model
list
<img width="863" height="63" alt="image"
src="https://github.com/user-attachments/assets/b97defa2-3fb4-4ec0-b3c6-26878d1c815c"
/>
4. Model is set, but provider is not recognised
<img width="865" height="67" alt="image"
src="https://github.com/user-attachments/assets/ecff1e3f-4f6b-47eb-a25d-125a104baafc"
/>
This plays well with the reason why we have the fallback model in the
first place: We only want to pick a fallback for users that open Zed for
a first time and e.g. have an Anthropic API key present in their
environment. As soon as the user manually changes his provider/model we
won't apply the fallback anymore.
Release Notes:
- agent: Improved error messaging when provider is not configured
- agent: Improve fallback model selection
---------
Co-authored-by: cameron <cameron.studdstreet@gmail.com>
## Summary
- The default settings in `assets/settings/default.json` disable
`kotlin-language-server` (prefixed with `!`) and enable `kotlin-lsp` as
the primary LSP, but the Kotlin documentation listed them the other way
around. This swaps the order so the docs match reality.
## Test plan
- [x] Verified `assets/settings/default.json` has
`["!kotlin-language-server", "kotlin-lsp", "..."]` for Kotlin
- [x] Confirmed the documentation now lists `kotlin-lsp` as the primary
Language Server and `kotlin-language-server` as the alternate
Release Notes:
- N/A
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
# Objective
Prevent collapsing a folder in one Git panel section from also
collapsing the same folder path in other sections.
## Solution
Key directory expansion state by `TreeKey`, which includes both the
section and repository path, instead of by `RepoPath` alone. This also
keeps persisted tree state, stale-state cleanup, and selected-file
reveal behavior section-aware.
## Testing
- Added `test_tree_view_directory_expansion_is_scoped_to_section` to
cover the same folder path under Tracked and Untracked independently.
- `cargo fmt -p git_ui -- --check`
- `git diff --check`
- Attempted `cargo test -p git_ui
test_tree_view_directory_expansion_is_scoped_to_section --lib`, but
dependency compilation could not complete because the local disk ran out
of space. Reviewers can run that command to execute the focused
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
## Suggested .rules additions
- In the Git panel tree view, key per-row UI state by `TreeKey` rather
than `RepoPath`, because the same directory can appear in multiple
sections.
---
Release Notes:
- Fixed collapsing a folder in one Git panel section also collapsing it
in other sections.
When `disable_ai` is set, bindings to AI-namespaced actions (assistant,
agent, inline_assistant, acp, edit_prediction, zeta) are now dropped at
keymap load time. Previously the bindings stayed active and their
handlers silently no-op'd, which shadowed lower-precedence editor
defaults — pressing ctrl-enter in an editor was captured by
`assistant::InlineAssist` and did nothing instead of falling through to
`editor::NewlineBelow`.
The filter applies to both default and user bindings, and a settings
observer reloads the keymap when `disable_ai` toggles at runtime.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes#56692
Release Notes:
- Fixed AI keybindings remaining active and shadowing editor defaults
when AI features are disabled
---------
Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com>
# Objective
Zed ignores files marked by Git as type-changed (T), causing commits
containing only these changes to show 0 Changed Files.
For example, commit d7cc949e61 changes
crates/eval_utils/LICENSE-GPL from a regular file to a symlink, but Zed
displays no changes.
## Solution
Handle TypeChanged files like modified files by loading both their old
and new contents.
## Testing
- Added parser coverage for the T status.
- Added a repository test for a regular-file-to-symlink change.
- Ran cargo test -p git.
- Ran ./script/clippy -p git.
- Manually verified the example commit on macOS.
## 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="200" alt="image"
src="https://github.com/user-attachments/assets/277c5938-5c2c-47b7-902d-9061a14c062a"
/>
After:
<img width="200" alt="image"
src="https://github.com/user-attachments/assets/f99d0005-0712-4843-814e-d73b11f64782"
/>
———
Release Notes:
- Fixed type-changed files not appearing in Git Graph and commit views.
# Objective
Before this change, we watched the themes directory itself. During
rescans, this could generate events for the directory, causing Zed to
attempt to load `event.path` as a file via
`fs.load_bytes(&event.path).await.log_err()`. This happened because
`fs.metadata(&event.path).await.ok().flatten().is_some()` also returns
true for directories.
Since load_bytes will always fail for a directory, we can avoid the
unnecessary call by skipping it whenever `event.path` refers to a
directory.
The rescan happens when Zed lost sync with the filesystem. That will
produce the following logs:
```
2026-07-01T16:03:35+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more
2026-07-01T16:03:35+02:00 ERROR [crates/zed/src/main.rs:1891] Is a directory (os error 21)
```
## Solution
The solution is that we just can check if the `event.path` is a
directory if so we can skip the
`fs.load_bytes(&event.path).await.log_err()` part described aboth.
## Testing
All tests should still pass and there should be no impact since this
only happens on rescans when the system is under pressure with alot of
fs events.
**Note**: No regression test was added since there's nothing to test
here.
## 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
Fixes#57766
Allow users to select and copy the text of commit messages from the git
graph details panel.
## Solution
Implements selectable commit message in the detail panel using
`MarkdownElement`s a la `commit_view`.
### Affordances considered and abandoned:
- "copy message" and/or "copy subject" buttons, but the UI here is
already pretty tight and I don't feel confident enough in my design
chops or familiarity with Zed to propose bigger UI changes here.
- Default-collapsed message with expand button, a la `commit_view`. The
use-case of this panel is to get the gist of a commit while moving
somewhat quickly through the graph. One might argue that the subject
line alone is enough for that, but often it isn't and the thought of
having to constantly expand commit messages rather than making the
message immediately visible with scroll felt like it would produce a
repetitious experience.
## Testing
- Adds two unit tests to verify that selection works and is idempotent
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
Video demonstrating the functionality:
https://github.com/user-attachments/assets/b910aaa3-2db2-4da8-904b-21b270677ca9
---
Release Notes:
- Show full commit message as markdown in details panel of the git graph
## Summary
Fix agent message hyperlinks that point to Windows file paths but are
not parsed as openable project paths.
This covers links like:
```md
[Cargo.toml](</C:/Projects/Example Workspace/Cargo.toml:2>)
[filename.ext](C:\Projects\Example%20Workspace\path\to\filename.ext:42)
[AGENTS.md](</c/Projects/Example Workspace/AGENTS.md>)
```
## Problem
Agent responses can emit Markdown hyperlinks whose targets are Windows
paths rather than `file://` URLs. Some of those targets include a
leading slash before the drive (`/C:/...`), Git Bash/MSYS-style drive
prefixes (`/c/...`), percent-escaped spaces, or line suffixes. These
were not normalized before mention parsing, so clicking the hyperlink
could do nothing instead of opening the file.
## Solution
- Normalize hyperlink path targets before parsing them as `MentionUri`
paths.
- Decode percent escapes in bare path targets so `%20` becomes a literal
space before path/line parsing.
- Convert Windows-compatible hyperlink paths such as `/C:/...` and
`/c/...` into native Windows paths.
- Generate file resource links from `find_path_tool` through
`MentionUri::to_uri()` instead of hand-building `file://` strings.
## Result
Before: clicking agent path hyperlinks did not open the referenced file.
After:
https://github.com/user-attachments/assets/6c7fad77-4a1e-4497-a4f9-4a4fdf86d527
## Validation
- `cargo test -p acp_thread test_parse_windows --features test-support`
- `cargo fmt --check`
## Follow-up changes
Additions on top of the original work above:
- Moved the hyperlink heuristics into a dedicated
`MentionUri::parse_hyperlink` entry point. `MentionUri::parse` stays
strict, so canonical mention URIs round-trip verbatim and other callers
(message editor, thread deserialization, resource links) are unaffected.
- Percent escapes that decode to path separators (`%2F`, `%5C`) are left
encoded, so decoding can never change which directories a path
traverses.
- Bare paths with escapes are ambiguous (a file may literally be named
`a%20b.rs`): `open_link` prefers the decoded interpretation and falls
back to `MentionUri::parse_hyperlink_literal` when the decoded path
doesn't resolve in the project but the literal one does.
- Links to files outside the project's worktrees now open, gated by an
async existence check through the project `Fs` (correct for remote
projects; broken links no longer create empty buffers or add worktrees).
`open_link` and the mention-crease open path are unified into one
`open_abs_path_at_point`, which now also places the cursor for
out-of-project selection/symbol links.
- `grep_tool` resource links also go through `MentionUri::to_uri()` now,
fixing malformed `file://C:\...` URIs and unencoded spaces.
- Added tests: percent-escape disambiguation, out-of-project link
opening, drive-letter normalization, UNC paths, and literal-path parsing
(`cargo test -p acp_thread mention`, `cargo test -p agent_ui open_link`,
`cargo test -p agent grep_tool`); manually verified the link spellings
above on Windows.
Release Notes:
- Fixed agent path hyperlinks on Windows when paths contain spaces or
shell-style drive prefixes.
---------
Co-authored-by: Martin Ye <martin@zed.dev>
Follow-up to #60360 and #59016 (cc @NeelChotai @bennetbo).
Claude Sonnet 5 and Claude Fable 5 cannot be invoked with on-demand
throughput — AWS requires an inference profile, and only `us.*` and
`global.*` profiles exist for these models ([Sonnet 5 model
card](https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-sonnet-5.html),
[Fable 5 model
card](https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-fable-5.html)).
For users in EU/APAC regions without `allow_global` enabled, Zed
generated profile IDs like `eu.anthropic.claude-sonnet-5`, which fail
with:
```
ResourceNotFoundException: Model not found.
```
And falling back to the bare model ID isn't an option either, since
direct invocation fails with:
```
Invocation of model ID anthropic.claude-fable-5 with on-demand throughput isn't supported.
Retry your request with the ID or ARN of an inference profile that contains this model.
```
Confirmed against the live AWS API — only `us.*` and `global.*` profiles
exist:
```
❯ aws bedrock list-inference-profiles --region eu-west-1 \
--query "inferenceProfileSummaries[?contains(inferenceProfileId, 'sonnet-5') || contains(inferenceProfileId, 'fable')].[inferenceProfileId,status]" \
--output table
------------------------------------------------
| ListInferenceProfiles |
+------------------------------------+---------+
| global.anthropic.claude-sonnet-5 | ACTIVE |
| global.anthropic.claude-fable-5 | ACTIVE |
+------------------------------------+---------+
```
This change removes both models from the EU match arm and routes them
through the global inference profile when no regional profile is
available. US regions continue to use the `us.*` profile. Verified
working from `eu-west-1`.
Note: this means requests from non-US regions route through the global
profile, which may dispatch inference to any supported AWS region.
That's inherent to how AWS exposes these models — there is no
EU-resident option today.
Release Notes:
- Fixed Claude Sonnet 5 and Claude Fable 5 failing on Amazon Bedrock in
non-US regions.
---------
Co-authored-by: Neel <neel@zed.dev>
If one gets a Nightly update but does not restart and rather dismisses
the notification, the next update check will do the update again.
This is caused by the fact that the version check is done based on
semver, but Nightly has the very same version that differs by the SHA
only.
Adjust the Nightly update check to skip re-downloads if SHA parts of the
version match also.
https://github.com/zed-industries/zed/pull/59852 does more changes, this
PR extracts the part I am sure about.
Release Notes:
- N/A
Adds a dylint library under tooling/lints that flags Zed-specific
anti-patterns:
* shared_string_from_str_literal,
* async_block_without_await,
* entity_update_in_render,
* notify_in_render,
* owned_string_into_shared,
* len_in_loop_condition, and
* blocking_io_on_foreground.
Includes UI tests, a single-lint helper, and workspace.metadata.dylint
registration so cargo dylint --all discovers it. The library pins its
own nightly toolchain (kept out of the main workspace) and tracks dylint
6.
Release Notes:
- N/A
Self-Review Checklist:
- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] 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
- [ ] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A or Added/Fixed/Improved ...
# Objective
As mentioned in #59514, the git panel currently runs a full `git status`
for checking whether `git` has access to the repository. This was
introduced in #43693 and currently runs on every file save which is
problematic for large repos where `git status` runs a lot of
computation.
## Solution
This PR caches the `git_access` on the git panel such that we don't need
re-check for git access on every file save.
## Testing
I manually verified that the git panel doesn't trigger anymore
unnecessary git status commands.
**main:**
```
2026-06-18T00:04:24+01:00 DEBUG [project::git_store] start recalculating diffs for buffer 4294967643
2026-06-18T00:04:24+01:00 DEBUG [project::git_store] finished recalculating diffs for buffer 4294967643
2026-06-18T00:04:24+01:00 DEBUG [project::git_store] start recalculating diffs for buffer 4294967643
2026-06-18T00:04:24+01:00 DEBUG [project::git_store] finished recalculating diffs for buffer 4294967643
2026-06-18T00:04:24+01:00 DEBUG [project.format.local] formatting buffer '"/Users/lgeiger/code/zed/CONTRIBUTING.md"'
2026-06-18T00:04:24+01:00 DEBUG [project.format.local] no changes made while formatting
2026-06-18T00:04:24+01:00 DEBUG [git::repository] Checking for git status in ["CONTRIBUTING.md"]
2026-06-18T00:04:25+01:00 DEBUG [git::repository] Checking for git status in []
```
```
/opt/homebrew/bin/git -c core.fsmonitor=false -c log.showSignature=false --no-optional-locks --no-pager status --porcelain=v1 --untracked-files=all --no-renames -z -- CONTRIBUTING.md
/opt/homebrew/bin/git -c core.fsmonitor=false -c log.showSignature=false --no-optional-locks --no-pager diff --numstat --no-renames HEAD -- CONTRIBUTING.md
/opt/homebrew/bin/git -c core.fsmonitor=false -c log.showSignature=false --no-optional-locks --no-pager config --get commit.template
/opt/homebrew/bin/git -c core.fsmonitor=false -c log.showSignature=false --no-optional-locks --no-pager status --porcelain=v1 --untracked-files=all --no-renames -z --
```
**This PR:**
```
2026-06-18T02:47:54+01:00 DEBUG [project::git_store] start recalculating diffs for buffer 4294967656
2026-06-18T02:47:54+01:00 DEBUG [project::git_store] finished recalculating diffs for buffer 4294967656
2026-06-18T02:47:55+01:00 DEBUG [project.format.local] formatting buffer '"/Users/lgeiger/code/zed/CONTRIBUTING.md"'
2026-06-18T02:47:55+01:00 DEBUG [project.format.local] no changes made while formatting
2026-06-18T02:47:55+01:00 DEBUG [git::repository] Checking for git status in ["CONTRIBUTING.md"]
```
```
/opt/homebrew/bin/git -c core.fsmonitor=false -c log.showSignature=false --no-optional-locks --no-pager diff --numstat --no-renames HEAD -- CONTRIBUTING.md
/opt/homebrew/bin/git -c core.fsmonitor=false -c log.showSignature=false --no-optional-locks --no-pager status --porcelain=v1 --untracked-files=all --no-renames -z -- CONTRIBUTING.md
/opt/homebrew/bin/git -c core.fsmonitor=false -c log.showSignature=false --no-optional-locks --no-pager config --get commit.template
```
## 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
/cc @dinocosta
---
Release Notes:
- Improved Git Panel performance by no longer re-checking repository
access on every file change
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
# Objective
Fix IME candidate window not following cursor position in the integrated
terminal when running fullscreen TUI applications like opencode.
When using IME (Input Method Editor) in Zed's terminal with fullscreen
TUI applications (e.g., opencode), the candidate window does not follow
the cursor position. This issue does not occur in normal terminal usage
(e.g., bash shell).
# Solution
The root cause was three-layer blocking preventing IME position updates
in ALT_SCREEN mode:
1. **ALT_SCREEN blocking**: `selected_text_range()` returned `None` in
ALT_SCREEN mode, causing `selected_bounds()` to return `None`
2. **Missing trigger**: `Event::Wakeup` did not call
`invalidate_character_coordinates()`, so cursor movement did not trigger
IME position updates
3. **Composition blocking**: `update_ime_position()` skipped updates
when `state.composing` was true
Fixes:
- Remove ALT_SCREEN check in `selected_text_range()` so IME position
updates work in fullscreen TUI apps
- Add `window.invalidate_character_coordinates()` in `Event::Wakeup` to
trigger IME position updates when terminal cursor moves
- Remove `state.composing` check in `update_ime_position()` to allow IME
position updates during text-input-v3 composition
- Remove unused `terminal` field from `TerminalInputHandler` struct
# Testing
**Did you test these changes? If so, how?**
- Yes, tested on Linux GNOME Wayland with iBus input method
- Verified IME candidate window correctly follows cursor in opencode
- Verified normal terminal usage with IME still works correctly
**Are there any parts that need more testing?**
- Other IMEs (fcitx, etc.) may need testing
**How can other people (reviewers) test your changes?**
1. Open Zed's integrated terminal
2. Run a fullscreen TUI application like `opencode`
3. Activate IME (e.g., iBus with Chinese input)
4. Type text and observe the IME candidate window follows the cursor
**What platforms did you test these changes on?**
- Linux (GNOME Wayland) - tested
- macOS - not tested (may have different IME behavior)
- Windows - not tested (different code path)
# 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 and icon
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
# Showcase
<details>
<summary>Before</summary>
<video
src="https://github.com/user-attachments/assets/ee2fac4e-801b-49af-a57e-32ce25e01db5"
width="320" height="180" />
</details>
<details>
<summary>After</summary>
<video
src="https://github.com/user-attachments/assets/c669ea99-2ffc-4179-b587-a47873e33e70"
width="320" height="180" />
</details>
---
Release Notes:
- Fixed IME candidate window not following cursor in terminal TUI apps
I encountered an issue where I couldn't sign in to ChatGPT Subscription
on Windows:
```
ChatGPT subscription sign-in failed to persist credentials:
Failed to write credentials to Windows Credential Manager:
占位程序接收到错误数据。 (0x800706F7)
```
Interestingly, only one of my three ChatGPT accounts had this problem —
the other two signed in successfully.
## What I Found
After investigating, I noticed that the OAuth scope in
`openai_subscribed.rs` requests 6 permissions:
```
openid profile email offline_access api.connectors.read api.connectors.invoke
```
I wrote a test script to measure token sizes and found that:
- With 6 scopes: my problematic account's token was **2578 bytes**
- With 4 scopes (removing `api.connectors.read` and
`api.connectors.invoke`): the same token was **2516 bytes**
Since Windows Credential Manager has a 2560-byte limit
(`CRED_MAX_CREDENTIAL_BLOB_SIZE`), this could explain why some accounts
fail while others succeed — it depends on the base token size.
## My Hypothesis
The issue seems to be that:
- Error code `0x800706F7` = `RPC_X_BAD_STUB_DATA` (size limit, not
format)
- Accounts with larger base tokens exceed the limit when the extra 48
chars are added
- Accounts with smaller base tokens still fit, which is why this isn't
universally reproducible
I'm not 100% certain this is the root cause, but the evidence seems to
point in this direction.
## Proposed Fix
I removed `api.connectors.read` and `api.connectors.invoke` from the
OAuth scope, since:
- These scopes don't appear to be used by Zed's current ChatGPT
integration
- OpenCode (another tool using the same OAuth provider) successfully
uses only 4 scopes
- This fix allowed my problematic account to sign in
However, I'd like to ask the maintainers:
- Are there plans to use OpenAI Connectors API in the future?
- Are there security or compliance reasons for requesting these extra
scopes?
If the answer is yes to either, we'd need a different approach (e.g.,
splitting credentials, using encrypted file storage, or checking token
size before storage).
## Testing
- [x] Verified my problematic account now signs in successfully
- [x] Verified my other accounts still work
- [x] Ran `cargo check -p language_models` (compilation successful)
- [ ] Would appreciate help testing on macOS/Linux
## Questions
1. Does this analysis make sense?
2. Are there any other considerations I'm missing?
3. Would it be helpful to add a size check with a clearer error message
for future cases?
I'm happy to adjust this approach based on your feedback.
Release Notes:
- Fixed ChatGPT Subscription sign-in failing on Windows for some
accounts by removing unused OAuth scopes (`api.connectors.read`,
`api.connectors.invoke`) that pushed JWT tokens over Windows Credential
Manager's limit.
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
the jetbrains overlay binds `ctrl-alt-l` (linux) / `cmd-alt-l` (macos)
to `editor::Format` (jetbrains "reformat code"), which collides with the
default `agent::OpenRulesLibrary` binding. the agent panel shows the
conflicting key as the rules tooltip but pressing it formats the buffer
instead of opening rules. mirrors the windows keymap by switching the
linux/macos overlays to `shift-alt-l` in the AgentPanel context (with
the colliding key explicitly null-ed), so the hint and the action stay
in sync.
closes#49764.
Release Notes:
- Fixed the JetBrains keymap so the agent panel "Rules" entry uses
`shift-alt-l` and no longer flickers a non-functional `ctrl-alt-l` /
`cmd-alt-l` shortcut.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes#56189
Release Notes:
- Fixed bug to handle dev container Dockerfiles with chained aliases
---------
Co-authored-by: Martin Ye <martin@zed.dev>
MCP servers may accept `initialize` unauthenticated but return `401`
with a `WWW-Authenticate` challenge only on a later request such as
`tools/list` or `tools/call`. Previously Zed only started the Auth flow
when `initialize` itself was challenged, so these servers failed
opaquely and stayed stuck in `Running` with a dead client.
`ContextServerStore` now handles `TransportError::AuthRequired` returned
from any request, not just startup: it runs Auth discovery and
transitions the server into `AuthRequired` so the UI offers to
authenticate. The discovery logic is shared with the startup path, and
the tool/prompt request sites route their errors through it.
Release Notes:
- Fixed MCP servers that require auth only on tool calls (not on
`initialize`) failing to prompt for authentication.
---------
Co-authored-by: Tom Houlé <tom@tomhoule.com>
Renders the per-file added/removed line counts (already tracked per
buffer by the multibuffer) in each diff multibuffer header, matching the
overall stat shown in the branch diff toolbar.
### Before
<img width="1624" height="1061" alt="Screenshot 2026-07-02 at 11 50 42
AM"
src="https://github.com/user-attachments/assets/3e46a90c-a4e1-4b72-8ec9-80931a7e4a9a"
/>
### After
<img width="1624" height="1061" alt="Screenshot 2026-07-02 at 12 03 58
PM"
src="https://github.com/user-attachments/assets/25900790-82f5-44e9-9a3b-731a3090f23e"
/>
Release Notes:
- Improved diff multibuffer headers to show per-file added/removed line
counts
---------
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
When prompt caching is enabled, `into_bedrock` pushes a `CachePoint`
block
onto a message's content whenever `message.cache && supports_caching` is
true.
This push happens before the `if bedrock_message_content.is_empty() {
continue; }`
guard. As a result, a message whose content filters down to empty (for
example,
a message that contained only content stripped during conversion) still
gets
appended to the request carrying nothing but a `cachePoint`. Bedrock
rejects such
a message with a `ValidationException`, breaking the whole request.
The Anthropic path is already internally consistent here: in
`crates/language_models/src/provider/anthropic.rs` (around the message
assembly
in `completion.rs`) the empty-content check runs first, so an empty
message is
dropped before any cache marker is attached. The Bedrock path should
behave the
same way.
This change gates the `CachePoint` push on non-empty content
(`&& !bedrock_message_content.is_empty()`), so an empty message is left
empty and
then skipped by the existing `continue`, exactly mirroring the Anthropic
ordering.
The fix is minimal and touches only the cache-point condition.
Release Notes:
- Fixed Bedrock requests failing with ValidationException when the last
message filtered to empty content while prompt caching was enabled.
---------
Signed-off-by: Yi LIU <yi@quantstamp.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Also orders the Claude models from most to least capable.
Release Notes:
- Added Sonnet 5 to Bedrock provider
Co-authored-by: David Irvine <aviddiviner@gmail.com>
## Summary
- describe agent notifications as OS desktop notifications instead of
claiming a fixed screen position
- keep the agent panel docs accurate across platforms and desktop
environments
Fixes#53588
Release Notes:
- N/A
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
This actually makes it possible for Linux headless client to have
working windows for computation (well, we are in headless mode after
all). This also matches macOS headless client behaviour.
Release Notes:
- N/A
At larger font sizes (e.g. I have profiles that increase the font sizes
when screen sharing) the commit button in the git panel editor does not
stay aligned to the bottom-right of the screen, and overlaps the other
elements:
<details>
<summary>Click to show showcase</summary>
https://github.com/user-attachments/assets/e73034db-07a6-42d7-b993-9d8091e50b70
Notice the extra clipping of the commit message editor element:
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/67d59405-57e2-4547-87c2-1c4728515c26"
/>
</details>
This was happening because the floating icons on the right and the
footer containing the commit button were positioned absolutely, used
pixels instead of responsive rems, and had an offset that was calculated
and did not account for changes in user rem size.
So I've moved things around to be more "flexy"... basically just
removing the absolute positioning and making the elements flush against
one another.
## Testing
I have tested this manually on macOS 27 beta 2 and Windows 11, both
using various light/dark themes, window sizes, commit text, etc.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] ~~Tests cover the new/changed behavior~~
- [x] Performance impact has been considered and is acceptable
## Showcase
Here's a demo at different zoom levels, with and without text, when
expanding the commit window, etc.
<details>
<summary>Click to view showcase</summary>
https://github.com/user-attachments/assets/bea82da1-0c7f-4d51-a73b-d4bc4581a2b7
And here's another demo with debug colors for the different elements to
see what's going on:
https://github.com/user-attachments/assets/12fd3e95-5089-4e34-a84c-c352219ee197
</details>
---
Release Notes:
- Git Panel: Fixed overlapping elements in commit message container
---------
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
When no LLM provider is configured, hovering the disabled "Generate
Commit Message" button in the git panel/commit modal previously showed a
plain, non-interactive tooltip with no way to act on it.
This tooltip now includes two links:
- **Configure Provider** — jumps directly to the "LLM Providers"
settings sub-page.
- **See Docs** — opens `https://zed.dev/docs/ai/llm-providers`.
Also adds `IconButton::hoverable_tooltip`, mirroring the existing
`.tooltip(...)` forwarding, since `IconButton` didn't previously expose
the underlying `ButtonLike::hoverable_tooltip` capability needed for
interactive tooltip content.
Release Notes:
- Improved the commit message tooltip to link directly to LLM provider
settings and documentation when no provider is configured.
---------
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
## Summary
- Fixed the terminal inline assist tab bar tooltip so it resolves the
keybinding from the active terminal view.
- The bug happened when the initial terminal was closed and a new
terminal was opened: the tab bar button kept a cached focus handle for
the old terminal, so tooltip keybinding lookup could no longer find the
terminal key context.
- The button is now created while rendering the tab bar with the current
terminal view's focus handle, avoiding stale focus handles as terminals
are closed and recreated.
## Validation
- `cargo fmt --package terminal_view --check`
- `cargo check -p terminal_view --message-format short`
Release Notes:
- Fixed the terminal inline assist toolbar tooltip not showing its
keybinding after reopening terminals.
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
# Objective
ACP agents (Cursor, Claude Code, Codex, etc.) start a display-only
`terminal::Terminal` for each bash tool call. After the call exits, the
terminal stays alive so that its output stays visible. But the
`alacritty` `Grid` keeps a `Storage` cache that's never reclaimed.
Across a long session this memory adds up.
Partially fixes#57099
## Solution
Added `Terminal::shrink_to_used`, called from the `Exit` branch of
`on_terminal_provider_event`. This calls `Grid::truncate()` on the
alacritty grid, shrinking the `Storage` cache portion while leaving
user-visible scrollback.
## Testing
- `cargo nextest run -p terminal
shrink_to_used_preserves_user_visible_scrollback` passes — exercises the
path with >10K rows of scrollback.
- `./script/clippy` clean.
- Manually verified: ran a noisy bash tool call via an ACP agent,
observed exit, then scrolled back through the output, no regression.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Improved memory usage of ACP terminals after the tool call exits
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Candidate fix for #59822.
Adds a live preview pane to the project symbols picker, matching the
file finder and project search pickers. When the selection moves, the
selected symbol's file is shown in the preview with its declaration line
highlighted and vertically centered.
## Changes
- `project_symbols`: construct the picker with
`uniform_list_with_preview`; asynchronously open the buffer for the
selected symbol (cached by candidate id, cleared on each new query) and
implement `try_get_preview_data_for_match` to return the buffer plus the
symbol's anchor range.
- `picker`: add a public `refresh_preview` so a delegate can push
preview data once a buffer finishes opening asynchronously (the symbol
buffer is not available synchronously, unlike text search which already
holds open buffers).
#### Before
<img width="1725" height="712" alt="image"
src="https://github.com/user-attachments/assets/21dcd7d6-7cc4-4de8-ab7d-2f1ce143e814"
/>
##### After
<img width="1725" height="712" alt="image"
src="https://github.com/user-attachments/assets/db981db8-0a61-43bc-9838-c01b7bdbbf78"
/>
Can turn preview pane off by clicking at the bottom button!
## AI disclosure
AI was used for understanding the codebase and formatting this PR.
Release Notes:
- Added a preview pane to the project symbols picker
---------
Co-authored-by: Yara <git@yara.blue>
LiveKit Cloud rejects room-join tokens as revoked when their `nbf`
predates a participant revocation. Zed generated those LiveKit JWTs with
`nbf: 0`, so a fresh participant token minted after stale connection
cleanup could still appear older than the cleanup and leave a user
joined at the collab layer without audio or screen sharing. This sets
`nbf` to the issuance time for room-join tokens while leaving admin/API
tokens unchanged, and adds regression coverage at the token, mock
LiveKit, and channel rejoin layers.
Closes FR-83
Release Notes:
- Fixed calls getting stuck without audio or screen sharing after
restarting Zed and rejoining a channel.
# Objective
On a few occasions I have been wondering if the Zed update download had
stalled, when in fact it's just taking a while. This PR adds a progress
bar to the "Downloading Zed Update..." button to give users feedback
that the download is indeed progressing.
## Solution
Add a custom progress bar inside the button. I tried reusing the
existing ProgressBar component but that has a fixed height of 8px which
caused the button to become taller.
## Testing
- Tested manually using the command palette `collab: simulate update
available` and stepping through the different UI states.
- Added unit 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
## Showcase
<img width="1350" height="748" alt="Image 02-07-2026 at 17 23"
src="https://github.com/user-attachments/assets/ed23c97a-01b8-446b-9693-0c53a69b419d"
/>
Release Notes:
- Added a download progress indicator to the update button.
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Adds a `zed: get merch` command palette action that opens
https://merch.zed.dev/ in the system browser.
Just thought it'd be fun!
Release Notes:
- Added a `zed: get merch` action that opens the Zed merch store.
StreamReader::poll_next dropped the reader it take()s whenever the read
returned Poll::Pending, so the next poll reported end-of-stream. And
poll_read_buf measured bytes via ReadBuf::filled(), which never advances
when the reader writes through initialize_unfilled(), so every read
counted as zero bytes. Together these made any AsyncBody::from_reader
request body (e.g. one backed by async_fs::File, which returns Pending
on its first read) upload as an empty or truncated body.
Keep the reader across Pending polls and use the byte count that
futures::AsyncRead::poll_read returns.
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
Co-authored-by: Marshall <marshall@zed.dev>
This reverts commit 2882636c06.
This was causing Zed to crash immediately on startup with the following
error:
```
thread 'main' (74835290) panicked at /Users/maxdeviant/.cargo/git/checkouts/reqwest-dc13ba947e7b959e/c156624/src/async_impl/body.rs:365:33:
there is no reactor running, must be called from the context of a Tokio 1.x runtime
```
Closes FR-118.
Release Notes:
- Reverted https://github.com/zed-industries/zed/pull/60301
# Objective
In the keymap editor, bindings whose `keymap.json` entries use a
deprecated action alias (e.g. `"editor::CopyRelativePath"` for
`workspace::CopyRelativePath`) could not be deleted or edited. Aliases
resolve to the canonical action on load, so the row displays normally,
but file updates searched for the canonical name and never matched the
alias entry. Deleting failed ("Failed to find keybinding to remove"),
and editing silently fell back to appending a new binding — leaving the
user with duplicate identical-looking rows they couldn't remove.
## Solution
- Thread gpui's deprecated-alias map
(`App::deprecated_actions_to_preferred_actions`) into
`KeymapFile::update_keybinding`, and resolve aliases in file entries
when matching bindings.
- As a side effect, editing an alias-based binding now rewrites the
entry with the canonical action name instead of duplicating it.
## Testing
- Unit tests in `settings` for removing and replacing alias-based
entries.
- End-to-end keymap editor tests (fake fs → keymap load → `KeymapEditor`
→ delete) covering both the alias case and plain duplicate bindings.
## 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 keymap editor failing to delete or edit key bindings whose
keymap file entries use deprecated action names
When Zed was downloading an update and the machine went to sleep, the
download would hang indefinitely on wake because the in-flight TCP
connection had silently died and nothing ever timed it out or retried.
This adds an inactivity `read_timeout` to the HTTP client so a stalled
response body errors out instead of hanging forever
(slow-but-progressing downloads are unaffected, since the timeout resets
on each chunk). It also promotes `App::on_system_wake` to a
multi-subscriber `Subscription` API and uses it in the auto-updater to
cancel an interrupted check/download on wake and start a fresh attempt.
## 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:
- Fix hanging Zed update downloads after system sleep
Makes cmd-f work for terminal threads.
Since the agent panel is not part of the normal pane system we have to
manually hook up the search bar.
Closes#57309
Release Notes:
- agent: Added support for searching (cmd-f) inside of terminal threads
Summary
- Track whether a worktree root is itself a linked Git worktree.
- Use that metadata when computing project group keys so bare checkout
worktrees group under the repository identity path.
- Propagate the metadata through remote worktree protocols and add
local/remote regression coverage.
Background
Bare checkout layouts can place linked worktrees under the repository
identity directory, e.g. `/monty/.bare` with worktrees like
`/monty/feature-a`. We were treating those linked worktree paths as
separate project identities, which caused the sidebar to move agent
threads under the active worktree instead of the shared repository
group.
We also exclude adding this to collab intentionally, we can open a
different PR for that if we need to.
Closes#59910
Closes AI-431
Test Plan
- `cargo fmt --package project --package worktree --package
remote_server --package workspace --package collab --package proto`
- `git --no-pager diff --check`
- `cargo test -p project test_project_group_key -- --nocapture`
- `cargo test -p remote_server test_remote_root_repo_common_dir --
--nocapture`
- `cargo test -p worktree remote_worktree -- --nocapture`
- `cargo test -p workspace
test_remote_project_root_dir_changes_update_groups -- --nocapture`
- `cargo check -p collab`
Self-Review Checklist:
I've reviewed my own diff for quality, security, and reliability
Unsafe blocks (if any) have justifying comments
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
Performance impact has been considered and is acceptable
Release Notes:
- Fixed agent thread/sidebar grouping for Git worktrees backed by bare
checkouts.
---------
Co-authored-by: Anthony Eid <anthony@zed.dev>
## Summary
On Windows, `trash::delete_with_info` can return an error even when the
file was successfully moved to the Recycle Bin.
This is caused by a race condition in the `trash` crate: after
`IFileOperation::PerformOperations` completes, it re-binds the trashed
item via `SHCreateItemFromParsingName` to read its metadata, but the
shell's virtual namespace cache may not yet reflect the new item,
producing an error. Previously, the delete loop propagated this error
and aborted the entire batch. The outer `detach_and_log_err` swallowed
the error, so the user received no feedback.
This PR handles each entry independently: entries that fail to delete
are skipped, the loop continues, and undo history is recorded for the
entries that succeeded.
The upstream root cause (the post-operation re-bind race in
`zed-industries/trash-rs`) will be addressed separately.
Before:
https://github.com/user-attachments/assets/59a95ac5-098b-42dc-bffb-f3240c6b966d
After:
https://github.com/user-attachments/assets/0cd6c0b3-2ee3-4bf2-a243-3e8a83d05dd7
## Test plan
- [ ] Select multiple files in the project panel on Windows
- [ ] Trash them (right-click > Move to Trash, or `Delete` key)
- [ ] Confirm all selected files are deleted, even if one triggers the
trash-crate race
- [ ] Undo restores the files that were successfully trashed
Release Notes:
- Improved handling of failed trash or delete operations in the Project
Panel in order to display a toast informing the user that some files
could not be trashed or deleted
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
## Why / What
Fixes#58702.
In Helix mode the block cursor is a one-character selection that can
rest on the
trailing newline at the end of a line. When you select to the end of a
line
(excluding the newline) and press `d`, Helix leaves the cursor on the
newline.
Zed instead clamped the cursor onto the character immediately to the
left of the
deleted selection.
The shared `visual_delete` always re-clipped the post-deletion cursor at
line
ends (`set_clip_at_line_ends(true)`), which is correct for Vim normal
mode but
contradicts Helix mode, where `Vim::clip_at_line_ends()` is `false`.
This also
made Helix inconsistent with itself: deleting a whole line left the
cursor on the
newline, but deleting to the end of a non-empty line clamped it onto the
previous
character. The fix honors the active mode's clip policy, so Helix keeps
the
cursor on the newline.
## Testing
- Added `test_delete_to_end_of_line_keeps_cursor_on_newline` reproducing
#58702.
- Corrected `test_helix_select_end_of_line`; its mid-line assertion had
captured
the pre-fix (clamped) cursor position. Its whole-line assertion already
expected the cursor on the newline, so the test is now internally
consistent.
- `cargo test -p vim`: 540 passed, 0 failed.
- Vim visual-mode delete is unchanged (`is_helix()` is false for Vim
modes).
Release Notes:
- Fixed Helix mode placing the cursor on the wrong character after
deleting a selection that ends at the end of a line.
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
## Context
The gutter hover button can represent either adding a breakpoint or
adding a bookmark depending on whether the secondary modifier is
pressed. Previously, the visible tooltip did not update when the
modifier changed while the popover was already open. This moves the
gutter hover intent into a shared state used by both the button and
tooltip, so the label, metadata, icon, color, click action, and
displayed keybinding all stay in sync.
Closes#58821
## How to Review
`crates/editor/src/editor.rs` adds a shared gutter hover intent type and
a custom tooltip view that reads the current modifier state while
rendering. Review that bookmark and breakpoint behavior map consistently
across the button icon/color, click action, tooltip label, metadata, and
keybinding.
`crates/editor/src/editor_tests.rs` adds a regression test covering the
tooltip intent selection with and without the secondary modifier.
Manual test after the fix below :
[Screencast from 2026-06-09
01-30-23.webm](https://github.com/user-attachments/assets/ef465eb9-e659-44da-93a6-a511c84e9401)
## Self-Review Checklist
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the UI/UX checklist
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Fixed the breakpoint/bookmark gutter popover not updating when
modifier keys change.
---------
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This PR removes/simplifies some APIs that were leftover after moving the
agent settings from the agent panel into the agent settings UI.
Behavior/UI should be identical.
- Removed `ConfigurationViewTargetAgent` since only a single variant was
used
- Removed `configuration_view` and `configuration_view_v2` and replaced
it with `settings_view`
- Removed all the custom configuration views that were replaced by the
API key view abstraction
- Removed `intitial_title` and `initial_description` and moved it to
`ProviderSettingsView`
Release Notes:
- N/A
## Summary
Add a chevron (`Disclosure`) to each file group header in Text Finder,
letting you fold/unfold that file's matches. Fold state is cleared
whenever a new search starts.
## Test plan
- [x] Open Text Finder, run a search with matches in multiple files
- [x] Click a header's chevron, confirm its matches hide/show and
selection lands on a visible row
- [x] Start a new search, confirm folded state doesn't carry over
Release Notes:
- Added the ability to collapse per-file match groups in Text Finder
---------
Co-authored-by: ozacod <ozacod@users.noreply.github.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Enable the command palette picker scrollbar while preserving the
one-shot reopen behavior.
---
Release Notes:
- N/A or Added/Fixed/Improved ...
Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
To reproduce:
1. Open a git project with the agent sidebar, ideally with no other
threads in the sidebar.
2. Create a thread in a new git worktree and type into it without
sending.
3. While still in that worktree workspace, hit New Thread. The parked
draft must now be the last activatable entry in the sidebar.
4. Hover the parked draft → click Discard Draft
5. 💥
Release Notes:
- Fixed a panic when discarding a draft workspace.
In multi-buffer context, `do_completion` resolved the completion's
replace range anchors against whichever buffer currently held the newest
selection. If that selection had moved into a different file's excerpt
(while the menu remained open), the anchors belonged to a different
buffer and caused a panic.
This PR adds a guard to confirm the completion buffer and selection
buffer is the same.
Closes FR-76.
---
Release Notes:
- Fixed a panic that could occur when confirming a completion in a
multibuffer if the cursor had moved into a different file
# Objective
Fixes a crash when trashing a large number of untracked files from the
Git Panel, caused by unbounded OS thread spawning (thread explosion).
## Solution
`GitPanel::clean_all()` builds one deletion task per untracked file and
then awaits them one by one. Since GPUI tasks start executing as soon as
they are spawned, not when they are awaited, all deletions run
concurrently. Each of them called `RealFs::trash`, which spawned a
dedicated OS thread per call, so a large enough batch exceeded the OS
per-process thread limit and panicked at the `.expect("The os can spawn
threads")` in `RealFs::trash`.
This PR replaces the dedicated thread with `smol::unblock`, which runs
the blocking `trash::delete_with_info` call on a shared thread pool that
is capped and queues additional work instead of spawning more. This is
the same idiom already used elsewhere in the file (e.g. `atomic_write`)
for blocking filesystem work.
## Testing
- Did you test these changes? If so, how?
yes, reproduced using steps in the issue before and after and confirmed
fix works.
https://github.com/user-attachments/assets/d3bcd049-485a-486f-877a-0e5c21b7917c
Reproduction steps here:
https://github.com/zed-industries/zed/issues/60216
## 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
Closes#60216
---
Release Notes:
- Fixed a crash caused by spawning one thread per file when trashing a
large number of untracked files from the Git Panel.
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
Closes#22490
## Summary
• Keep kill-ring state as a dedicated global structure (`text`,
`metadata`, `row`, `column`, `buffer_id`) instead of a raw clipboard
wrapper.
• Update `editor::KillRingCut` to append consecutive empty-line kills
only when all are true:
- one selection only
- previous selection was empty
- same row/column
- same buffer
• Preserve existing behavior for non-empty and multi-selection cuts by
replacing the ring payload instead of appending.
• Preserve yank metadata behavior through the existing clipboard
selection metadata path.
• Add regression coverage in
`editor_tests::test_kill_ring_cut_accumulates_multi_line_kills`.
## Verification
• `cargo fmt --all -- --check`
• `./script/check-keymaps`
• `./script/clippy -p editor`
• `cargo test -p editor -- --nocapture`
• `cargo test -p editor
editor_tests::test_kill_ring_cut_accumulates_multi_line_kills --
--exact`
• `cargo test -p editor test_cut_line_ends -- --exact`
## Manual
• `Ctrl+K`, `Ctrl+K`, `Ctrl+Y` with repeated line-end kills at same
caret location pastes concatenated text.
• Non-empty selection cut and multi-selection still replace as before
(no unintended append).
• Cut in one buffer then cut in another does not cross-append.
• Moving caret between kills does not append.
• Undo/redo around cut+yank path works cleanly.
Release Notes:
- Fixed Emacs-style kill-ring behavior so successive `KillRingCut`
operations at the same caret context are accumulated and pasted together
via one `KillRingYank`, while preserving existing non-empty selection
and multi-selection semantics.
---------
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
fixes#53726
Compute the base delta from the first visible, non-filtered row instead
of using rows.start. This avoids off-by-one numbering when rows.start is
a wrapped continuation that gets filtered out. Ensure deleted lines
still advance the relative counter even when they are not numbered. Add
a test for the wrapped scrolling case.
P.S. This is my first time using rust for a serious pull request and
let's say I'm a little bit rusty with it.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A or Added/Fixed/Improved ...
---------
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
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
This PR makes two changes:
1. detect() now returns the line number of the first match instead of
just a boolean.
2. This line number (line_hint) is threaded through the pipeline and
passed as a subrange to query.search in full buffer scan, so the full
buffer scan starts from where the first match was already found instead
of the beginning of the file.
## Scope
- This optimization only applies to **local search** (not remote).
- It is only effective for **single-line text and single-line regex**
queries. For multiline queries, `detect()` returns `line_hint = 0`, so
the full buffer is scanned from the beginning as before.
- It does not apply to already-open buffers, which always receive
`line_hint = 0`.
## Test
i tried to search the word "typeably" in the file
"pkgs/development/haskell-modules/hackage-packages.nix" of the repo
[nixpkgs](https://github.com/NixOS/nixpkgs) given in the comment:
[comment](https://github.com/zed-industries/zed/issues/38799#issuecomment-4088491295).
Result seems promising:
**with line_hint:
search: 45.21ms**
**without line_hint:
search: 508.05ms**
I measured the time within the function: "handle_find_all_matches"
## Assumption
The core assumption is: if the search query is long enough (after 4 or 5
chars), the probability of finding multiple matches in a single file is
low. In that case, re-scanning the content before line_hint is
unnecessary — detect() function already confirmed there is no match
there.
Changes maybe can make an improvement on issues #38799 and #58905
---------
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Automates the Guild contributor program on project board #74 so the
cohort is recognized and kept unblocked without manual babysitting.
- Move a board issue to In Progress when a Guild member self-assigns it,
and post a friendly heads-up when they are assigned an issue that is not
on the board.
- Flag when a Guild member opens a new PR while another of theirs is
still open, to help them land work before spreading thin.
- Check in on quiet assignments and, if an assignee stays silent, free
the issue back to a to-do column so others can pick it up; a "guild
hold" label lets maintainers pause check-ins after they have followed
up.
- Share a weekly digest of what the Guild shipped.
- Label PRs from Guild members and recognize a Guild contributor tier on
the community PR board.
Release Notes:
- N/A
# Objective
Microsoft has decoupled the MSVC version from the Visual Studio version
starting with Visual Studio 2026 (see [this
blog](https://devblogs.microsoft.com/cppblog/new-release-cadence-and-support-lifecycle-for-msvc-build-tools/)),
meaning that the previous traditional naming conventions were obsolete.
For Visual Studio 2026 (including future newer versions of VS) users,
the existing docs will probably confuse them, as they cannot find out
the components named like "MSVC v145 - VS 2026 C++ ..." in present
Visual Studio Installer.
## Solution
Add a note next to the dependencies.
## Testing
Docs are no need to test. (Maybe?)
## 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
## Objective
While profiling https://github.com/zed-industries/zed/issues/60102 I
noticed `GitStore::update_repositories_from_worktree` does unneeded
work. It fires on every `WorktreeUpdatedGitRepositories` event, which
under filesystem pressure (`rescan: user dropped`) repeats roughly once
per second and can fire more frequently in a large monorepo with many
repositories, since the redundant work scales with the number of
repositories.
## Solution
In `crates/project/src/git_store.rs`:
- Drop the per-iteration `Arc<Path>` clone in the repo lookup; compare
the borrowed reference instead.
- Hoist the `is_trusted` (`can_trust`) computation out of the loop since
it only depends on `worktree_id`.
No behavior change is intended; this is purely reducing redundant work
per event.
## Notes
The *frequency* of these events originates upstream in
`LocalWorktree::changed_repos` (`crates/worktree/src/worktree.rs`),
which re-emits an `UpdatedGitRepository` whenever `git_dir_scan_id`
changes on a full rescan. That path is intentionally left untouched here
since it involves scan semantics; this PR only addresses the redundant
work on the consumer side.
## 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
# Objective
Make images nested inside markdown links (e.g., README badges)
clickable, so they behave like normal markdown links.
Previously, linked images rendered correctly but were not interactive in
markdown previews: they did not show a pointer cursor, could not be
clicked to open their target URL, and did not expose link actions
through the context menu.
## Solution
Changed the `MarkdownElement` to detect when an `<img>` tag is inside a
`<a>` tag during rendering. When a linked image is rendered:
- The wrapper `div` gets a `cursor_pointer` style and an `on_click`
handler that opens the link URL (or delegates to the `on_url_click`
callback if set).
- The wrapper also captures right-click events to populate the context
menu with "Copy Link" via `capture_for_context_menu`.
- The `on_url_click` closure type was changed from `Box<dyn Fn>` to
`Rc<dyn Fn>` to allow cloning across multiple handlers.
- When a linked image fails to load, the fallback element's `on_click`
now calls `cx.stop_propagation()` to prevent the event from bubbling to
the parent link wrapper and opening the URL twice.
The feature is gated behind `!self.style.prevent_mouse_interaction` so
it respects existing interaction-prevention settings.
## Testing
- `cargo test -p markdown`: 95 tests passed.
- Manually verified in markdown preview that badge/image links open
correctly and right-click shows "Copy Link".
- Verified existing markdown tests continue to 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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed images inside markdown links not being clickable.
https://github.com/user-attachments/assets/df2a8d8f-b002-47f9-b139-815ee713700b
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
This PR makes the `markdown_preview_font_size` setting be based on the
UI font size instead of the buffer UI font size. I typically use a much
smaller code font than UI font, and that made reading the markdown
preview super small. I don't think this will be uncommon because the
dimensions between mono and non-mono typefaces are different...
Release Notes:
- Changed the markdown preview to fall back to the UI font size instead
of the buffer font size when `markdown_preview_font_size` is unset.
WSL downloads a linux zed binary for the sandbox helper. But the flag is
set on the `zed-editor` binary, not the `zed` cli binary. This fixes
that
---
Release Notes:
- N/A or Added/Fixed/Improved ...
## Summary
When formatting specific ranges (e.g. via Format Selections), trailing
whitespace removal and final newline enforcement currently operate on
the entire file. This PR makes these operations range-aware, so they
only affect lines within the target ranges.
As noted in #16509, users want `remove_trailing_whitespace_on_save` and
`ensure_final_newline_on_save` to suppress jitter by only touching
changed lines. This PR provides the foundational infrastructure for that
— range-based whitespace and newline operations — which a follow-up PR
will wire into the `format_on_save` modifications mode.
## Changes
- Added `remove_trailing_whitespace_in_ranges` and
`ensure_final_newline_in_range` to `Buffer`
- When a `FormattableBuffer` has ranges set, whitespace and newline
operations now use the range-based variants
- `format_ranges_via_lsp` returns `Ok(None)` instead of an error when
the LSP doesn't support range formatting
- Extracted `anchor_ranges_to_row_ranges` helper to deduplicate
conversion logic
<details><summary>Tests (4)</summary>
- `test_trailing_whitespace_in_ranges` — only modified lines have
whitespace removed
- `test_trailing_whitespace_empty_ranges` — empty ranges → no changes
- `test_final_newline_modified_last_line` — newline added when last line
is in range
- `test_final_newline_unmodified_last_line` — newline not added when
last line is outside range
</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 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:
- Improved Format Selection to scope trailing whitespace removal and
final newline enforcement to the selected range instead of the entire
file
- Improved handling of LSP servers that do not support range formatting
by gracefully skipping instead of producing an error
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Closes#50210
Bracket colorization currently runs theme accents through
`ensure_minimum_contrast`, which can significantly alter authored
palette colors in order to satisfy a single foreground-vs-background
target. In practice, that makes rainbow brackets drift away from the
theme colors they are supposed to reflect.
This PR keeps bracket colorization as a single automatic behavior, but
changes how the palette is derived:
- preserve authored accent colors when they already work
- adjust only the specific accents that are too weak against the editor
background
- only reorder the palette when adjacent bracket levels are still too
similar after that
The implementation is staged because bracket colorization has to handle
two separate problems:
- **Bracket-vs-Background Readability**: is an individual bracket
visible against the editor background?
- **Adjacent Bracket Separation**: can you still distinguish one nesting
level from the next?
Those two constraints are also why the implementation cannot simply
defer to whatever accent colors the theme provides. Some themes,
including the bundled One and Ayu, do not define explicit accents at
all, so bracket colorization falls back to Zed's shared default accent
palette. That palette was not designed with any particular theme's
background in mind, which makes the background contrast check especially
important for those cases.
That is why the previous `ensure_minimum_contrast(..., 55.0)` path is
not enough on its own. It addresses foreground-vs-background
readability, but if lightness-only adjustment cannot satisfy that target
it escalates to saturation reduction and then black/white fallback.
This PR instead keeps the bracket-specific background fix bounded and
lightness-only in OKLCH, so the resulting color stays much closer to the
authored accent.
### How it works
- **Background Contrast Correction**: accents that fall below the APCA
floor against the editor background are adjusted by changing OKLCH
lightness only, preserving hue and chroma
- **Adjacency Correction**: if the resulting palette is still too weak
between neighboring nesting levels, the palette is reordered
conservatively to improve adjacent separation
That ordering matters. Doing the reorder first can easily be undone by
the background-fix step.
### Color measurement
- background contrast uses APCA, via the existing `apca_contrast` path
- adjacent bracket separation uses OKLab Euclidean distance
APCA is the right tool for bracket-vs-background readability, but it is
not designed to compare two foreground colors against each other. OKLab
distance is a better fit for detecting when neighboring bracket levels
are perceptually too close.
### Thresholds
- background APCA floor: `30` on dark themes, `35` on light themes
- adjacent OKLab target: `0.08` on dark themes, `0.10` on light themes
- light-theme reorder tolerance band: reorder only triggers below
`0.095`, avoiding threshold-artifact reordering on palettes that are
visually fine but narrowly miss `0.10`
### Validation
- `cargo test -p editor bracket_colorization`
- manual comparison on bundled One, Ayu, and Gruvbox theme families
Release Notes:
- Improved bracket colorization by preserving theme accent colors when
possible and applying targeted contrast fixes only when needed.
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Closes AI-432
The turn-end action buttons in the agent panel (scroll to top, scroll to
most recent prompt, open as markdown, thumbs up/down, share/sync) are
now exposed as local slash commands in the message editor instead of
buttons rendered at the end of each turn. The actions themselves are
unchanged — this is just a different way to access them.
Release Notes:
- Changed some of the agent panel's turn-end action buttons into slash
commands in the message editor.
---------
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
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
- [ ] Performance impact has been considered and is acceptable
When files are dragged into the terminal, paths were formatted using
Rust's Debug format (`{path:?}`), which wraps them in double quotes.
This caused issues with programs like Claude Code that parse bare paths
from terminal input — quoted paths were treated as plain text instead of
file references.
This change replaces double-quote wrapping with backslash escaping for
special shell characters on Unix, which is both valid shell syntax and
compatible with path-parsing tools running in the terminal. On non-Unix
platforms the previous behavior is preserved.
Tested on macOS only. Unable to verify Windows behavior.
Fixes#57471
Release Notes:
- Fixed/ file drag-and-drop into terminal inserting double-quoted paths,
which prevented tools like Claude Code from recognizing them as file
references.
---------
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
# Objective
Zed uses `pet` to auto-discover Python virtual environments. When
opening Python projects, there is a large amount of log spam from `pet`
output, which is not useful:
```
2026-07-01T22:32:07+08:00 INFO [pet::find] find_and_report_envs; search_scope=None
2026-07-01T22:32:07+08:00 INFO [lsp] starting language server process. binary path: "/usr/bin/ty", working directory: "/home/xin/works/test/test_python", args: ["server"]
2026-07-01T22:32:07+08:00 INFO [pet::find] locators_phase;
2026-07-01T22:32:07+08:00 INFO [pet::find] path_search_phase;
2026-07-01T22:32:07+08:00 INFO [pet::find] locator_find; locator=PipEnv
2026-07-01T22:32:07+08:00 INFO [pet::find] workspace_search_phase;
2026-07-01T22:32:07+08:00 INFO [pet::find] locator_find; locator=PyEnv
2026-07-01T22:32:07+08:00 INFO [pet::find] locator_find; locator=Pixi
2026-07-01T22:32:07+08:00 INFO [pet::find] locator_find; locator=Conda
2026-07-01T22:32:07+08:00 INFO [pet::find] locator_find; locator=Uv
2026-07-01T22:32:07+08:00 INFO [pet::find] locator_find; locator=Poetry
2026-07-01T22:32:07+08:00 INFO [pet::find] global_virtualenvs_phase;
2026-07-01T22:32:07+08:00 INFO [pet::find] locator_find; locator=Venv
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=[] executable_count=0
2026-07-01T22:32:07+08:00 INFO [pet::find] locator_find; locator=VirtualEnv
2026-07-01T22:32:07+08:00 INFO [pet::find] find_python_environments_in_workspace_folder_recursive; workspace_folder="/home/xin/works/test/test_python" workspace=/home/xin/works/test/test_python
2026-07-01T22:32:07+08:00 INFO [pet::find] locator_find; locator=Homebrew
2026-07-01T22:32:07+08:00 INFO [lsp] starting language server process. binary path: "/usr/bin/ruff", working directory: "/home/xin/works/test/test_python", args: ["server"]
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=[] executable_count=0
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=[] executable_count=0
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=[] executable_count=0
2026-07-01T22:32:07+08:00 INFO [pet::find] locator_find; locator=LinuxGlobal
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=[] executable_count=0
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=[] executable_count=0
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=[] executable_count=0
2026-07-01T22:32:07+08:00 INFO [pet::find] locator_find; locator=VirtualEnvWrapper
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=[] executable_count=0
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=["/home/xin/works/test/test_python/.venv/bin/python"] executable_count=1
2026-07-01T22:32:07+08:00 INFO [pet::locators] identify_python_environment_using_locators; env=PythonEnv { executable: "/home/xin/works/test/test_python/.venv/bin/python", prefix: Some("/home/xin/works/test/test_python/.venv"), version: None, symlinks: None } executable=/home/xin/works/test/test_python/.venv/bin/python
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=PyEnv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Pixi
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Conda
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Uv
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=[] executable_count=0
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=[] executable_count=0
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=[] executable_count=0
2026-07-01T22:32:07+08:00 INFO [pet::find] identify_python_executables_using_locators; executables=["/usr/bin/python", "/usr/bin/python3", "/usr/bin/python3.14"] executable_count=3
2026-07-01T22:32:07+08:00 INFO [pet::locators] identify_python_environment_using_locators; env=PythonEnv { executable: "/usr/bin/python", prefix: None, version: None, symlinks: None } executable=/usr/bin/python
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=PyEnv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Pixi
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Conda
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Uv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Poetry
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=PipEnv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=VirtualEnvWrapper
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Venv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=VirtualEnv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Homebrew
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=LinuxGlobal
2026-07-01T22:32:07+08:00 INFO [pet::locators] resolve_python_env; executable=/usr/bin/python
2026-07-01T22:32:07+08:00 INFO [pet::locators] identify_python_environment_using_locators; env=PythonEnv { executable: "/usr/bin/python3", prefix: None, version: None, symlinks: None } executable=/usr/bin/python3
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=PyEnv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Pixi
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Conda
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Uv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Poetry
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=PipEnv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=VirtualEnvWrapper
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Venv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=VirtualEnv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Homebrew
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=LinuxGlobal
2026-07-01T22:32:07+08:00 INFO [pet::locators] resolve_python_env; executable=/usr/bin/python3
2026-07-01T22:32:07+08:00 INFO [pet::locators] identify_python_environment_using_locators; env=PythonEnv { executable: "/usr/bin/python3.14", prefix: None, version: None, symlinks: None } executable=/usr/bin/python3.14
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=PyEnv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Pixi
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Conda
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Uv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Poetry
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=PipEnv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=VirtualEnvWrapper
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Venv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=VirtualEnv
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=Homebrew
2026-07-01T22:32:07+08:00 INFO [pet::locators] try_from_locator; locator=LinuxGlobal
2026-07-01T22:32:07+08:00 INFO [pet::locators] resolve_python_env; executable=/usr/bin/python3.14
```
This PR is intended to remove this log spam.
## Solution
Add `pet` to the default log filters. Now, `INFO` logs from `pet` are
suppressed by default.
## Testing
Tested locally, and verified that there is no longer any `pet` log spam
in the Zed logs.
## 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 or Added/Fixed/Improved ...
2026-07-01 15:18:54 +00:00
535 changed files with 41811 additions and 11108 deletions
tree-sitter-md={git="https://github.com/zed-industries/tree-sitter-markdown",rev="b596e737286780d7bfa9fcddceaeeb754574b352"}# fork of 9a23c1a with serialize() buffer-overflow fix; https://github.com/tree-sitter-grammars/tree-sitter-markdown/issues/243
@ -198,7 +198,7 @@ You can request elevated permissions on individual `terminal` calls:
- `allow_hosts: ["github.com", "*.npmjs.org"]` — allow outbound HTTP/HTTPS to specific hosts (exact hostnames or leading-`*.` subdomain wildcards; no IP literals). Prefer this whenever you know which hosts the command needs.
- `allow_all_hosts: true` — lift the network restriction entirely: outbound access to any host over any protocol, so SSH, FTP, and raw sockets work too (unlike `allow_hosts`, which is HTTP/HTTPS-only). Use only when the specific hosts can't be enumerated up front.
{{/if}}
- `fs_write_paths: ["/abs/or/worktree-relative/path", ...]` — allow writes to specific paths (each directory grants its whole subtree). Prefer this whenever you know which paths the command needs to write. Git metadata paths cannot be requested and will never be made writable while sandboxed.
- `fs_write_paths: ["/abs/or/worktree-relative/path", ...]` — allow writes to specific paths (each directory grants its whole subtree). Prefer this whenever you know which paths the command needs to write. Each path must be an existing directory. To write into a directory that doesn't exist yet, first create it with the `create_directory` tool (which creates it and grants write access to exactly that directory) rather than requesting write access to a broad existing parent. Git metadata paths cannot be requested and will never be made writable while sandboxed.
- `allow_fs_write_all: true` — allow unrestricted filesystem writes except protected Git metadata. Only use this when the specific paths can't be enumerated up front.
- `unsandboxed: true` — run the command with no sandbox at all. Use only when none of the above suffice, including when a command must write Git metadata.
/// Creates a new directory at the specified path within the project. Returns confirmation that the directory was created.
/// Creates a new directory at the specified path, and all necessary parent directories. Returns confirmation that the directory was created.
///
/// This tool creates a directory and all necessary parent directories. It should be used whenever you need to create new directories within the project.
/// The only supported path outside the project is `~/.agents/skills` or a descendant, for global agent skills.
/// Use this whenever you need to create new directories. Paths inside the project are created directly.
///
/// This tool can also create a directory **outside** the project. When agent terminal commands are sandboxed, doing so grants those commands write access to exactly that new directory — so, rather than requesting write access to a broad existing parent (e.g. your home directory) just to create something inside it, create the specific directory here first and then write into it. The only other supported path outside the project is `~/.agents/skills` or a descendant, for global agent skills.