## Summary
Replace GPUI's shared asset futures with owning cache entries. The
mutex-backed waiter collection in futures::future::Shared can contend
between a worker and the browser main thread, where blocking waits are
forbidden.
- Keep loading on the background executor, with one foreground task
publishing the cached result and notifying interested views.
- Deduplicate loads and view registrations; cancel pending work on
eviction without allowing completion to update a replacement entry.
- Change App::fetch_asset to return Option<A::Output>, remove
ImageLoadingTask, and make ImageCacheItem an opaque owning entry.
- Migrate the retain-all image cache and image gallery LRU example.
Repository-wide search found no further callers requiring migration.
Results become visible after the foreground completion task publishes
them. Entries expose neither cloneable nor awaitable task handles.
Release Notes:
- N/A
#### Fixes#52579
When quickly pressing and releasing Ctrl+Tab, the tab switcher popover
would briefly appear, and any mouse movement over it would select a
random tab instead of the intended recently-focused file.
### Fix :
This adds a 300ms delay before showing the tab switcher UI when opened
via modifier keys. If the modifier is released before the delay elapses,
the selected item is confirmed directly without ever showing the
popover. This matches the behavior of system window switchers (e.g.
macOS Cmd+Tab), where quick presses never flash the UI.
- `visible` and `_show_task` fields track whether the popover should be
rendered
- When not yet visible, the picker is mounted in a zero-size
overflow-hidden container to preserve focus and keyboard state
- When modifiers are released before the popover is visible, `confirm()`
is called directly instead of dispatching `menu::Confirm`, avoiding any
mouse-hover-based selection
### 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
blocks*
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
— *Matches system window switcher behavior (e.g. macOS Cmd+Tab); no
visual change for slow/held switches*
- [x] Tests cover the new/changed behavior — *Added
`test_quick_switch_before_popover_visible`*
- [x] Performance impact has been considered and is acceptable — *Only
adds a lightweight 300ms timer task; no extra allocations or rendering
cost*
### Video
[Screencast from 2026-03-29
11-52-07.webm](https://github.com/user-attachments/assets/8e148a74-75ae-4c36-8d44-594c3c9097e6)
### Note : Reopens previous work from closed PR #52648 (fork was deleted
by mistake)
Release Notes:
- Fixed Ctrl+Tab jumping to random documents when the mouse is moved
during a quick tab switch
---------
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Release Notes:
- Fixed injected language highlighting for strings containing
interpolations
current | updated
:-------------------------:|:-------------------------:

|

<details>
<summary> test snippet </summary>
```python
my_regex = r"[0-9]"
spark.sql( # sql
f"""
SELECT
col1,
CASE WHEN col2 RLIKE '{my_regex}' THEN '1' ELSE '2' END col4,
ROW_NUMBER() OVER(PARTITION BY col1, col2 ORDER BY col3) as rn
FROM tbl
"""
)
```
</details>
## Summary
Strings with interpolations like `f"SELECT col1 FROM tbl WHERE col2 =
'{my_var}'"` in Python produce multiple `string_content` nodes. Without
grouping, each fragment gets its own injection layer which breaks syntax
highlighting.
This PR adds an `@injection.host` capture to group `@injection.content`
ranges by their host node. This was implemented by extending the
existing `combined_injection_ranges` hash map with a node key per host.
Added tests for this behavior covering simple strings, f-strings with
single/multiple interpolations, and multi-string isolation.
---------
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Resubmit of #59731, which got auto-closed when I accidentally deleted
its branch. It wasn't rejected, it had already been reviewed. This is
rebased onto latest `main`, and osiewicz's review feedback (using `_`
for the unused param in the trait default) is already in.
Follow-up to #58510. Profiling multi-cursor typing on current main
showed the per-keystroke `set_active_selections` broadcast does an
O(selections) multi-buffer anchor remap and runs even with zero
collaborators (about 0.9ms/call at 1k cursors, 8.4ms at 10k, fired ~3x
per edited iteration).
This skips the broadcast when the buffer has no audience, and
re-publishes on share so there's no regression:
- Add `CollaborationHub::should_broadcast_selections`, defaulting to
`true`. `Entity<Project>` overrides it to `Project::is_shared()`, which
is true for a host that shared the project and for a collab guest, and
stays correct before peer-join notifications have propagated locally
(unlike a live collaborator count). A purely local project has no
audience.
- Gate `set_active_selections` in `selections_did_change` on it.
Follow-mode is unaffected since it uses the separate
UpdateFollowers/UpdateView channel, and the focused user's own cursors
render from `editor.selections`, not `remote_selections`.
- When the project becomes (re)shared (`RemoteIdChanged(Some)`,
`Reshared`, `HostReshared`), the focused editor re-publishes its current
selections, so a peer joining after a share still sees the host's cursor
without waiting for the next selection change.
- Reuse the post-edit display snapshot in the `hard_wrap` branch instead
of taking a redundant fresh buffer snapshot.
Multi-cursor type+delete benchmark: about 5% faster at both 1k and 10k
cursors (615 to 583 ms at 10k, significant). All 773 editor tests pass
locally. Collab and channel-buffer integration tests need the test DB,
so they run on CI.
Discussion and profiling:
https://github.com/zed-industries/zed/issues/32051
Release Notes:
- Improved multi cursor editing performance
# Objective
Files matching the `private_files` setting are meant not to be shared
with collaborators. That is true when a guest asks to open one directly,
`respond_to_open_buffer_request` refuses with `ErrorCode::UnsharedItem`.
Project search reaches the same `create_buffer_for_peer` without passing
through that check. In `handle_search_candidate_buffers`, every matching
buffer was shared with the requesting peer unconditionally, so a guest
searching for a string that happens to appear in a private file received
the buffer containing it.
## Solution
Check `is_private` before sharing a matched buffer with a peer
## Testing
added new test `test_project_search_excludes_private_files` in the
collab integration suite:"
The host configures `private_files: ["**/.env"]` and has two files
`.env` containing `API_KEY=secret` and `a.txt` containing `the secret is
out`. The guest joins the shared project and searches for `secret`.
---
Release Notes:
- Fixed private files being shared with collaborators through project
search.
Crash repro:
```
A
1234567890
B
C
```
1. place cursors before `A`, `B`, and `C`
2. run `editor: rotate selections forward`
Caused by the current `Editor::rotate_selections` logic assuming that
the text between the end of one of the lines being rotated and the start
of the next one is just the newline:
```rs
let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
```
which fails when those lines aren't consecutive in the editor.
## 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 crash in `editor: rotate selections forward` and `editor: rotate
selections backward` using cursors on nonconsecutive lines.
# Objective
- Fixes#33819
- The `preview_tabs` documentation says preview tabs "close
automatically when you switch to another file unless you explicitly pin
them". They do not close. Moving to another file leaves the preview tab
open — either still as the preview tab, or converted into a regular one.
Four people ran into this on the issue over the past year.
- The same section then lists the ways to turn a preview tab into a
regular tab, and two of them are missing, including the pinning that the
sentence above uses as its counter-example.
## Solution
Two corrections to the `Preview tabs` section of
`docs/src/reference/all-settings.md`. No behaviour was touched.
**The description.** A pane holds a single `preview_item_id`, so there
is at most one preview tab at a time. What happens to it when you move
to another file depends on the path:
- Opening a not-yet-open file in preview mode from the project panel or
file finder goes through `Pane::open_item`, which calls
`close_current_preview_item` — that really does close the old tab
(`remove_item`) and hands back its index, and the new file is inserted
there.
- Code navigation with the default
`enable_keep_preview_on_code_navigation: false` instead calls
`unpreview_item_if_preview` on the old item *before*
`replace_preview_item_id`, so the previous file stays open as a regular
tab.
A third path, the file finder with the default
`enable_preview_from_file_finder: false`, opens the new file as a
regular tab and leaves the existing preview tab alone entirely.
Rather than spell out every path in a settings description — the
`enable_preview_*` settings immediately below already cover them — the
new wording sticks to what holds in all of them: a pane keeps at most
one preview tab and opening another file in preview mode takes over that
slot, and switching to a file that is already open does not close the
preview tab. That second half is the case the issue actually reports.
**The list.** Two more ways to convert a preview tab into a regular tab:
- Pinning it — `change_tab_pin_state` calls `unpreview_item_if_preview`
on the `Pin` branch.
- `pane::TogglePreviewTab` — the action dedicated to exactly this,
registered whenever preview tabs are enabled.
## Testing
- Built a debug Zed and walked through the steps from the issue on a
scratch project of `a.txt` / `b.txt` / `c.txt`: with `a.txt` open as a
regular tab and `b.txt` opened as a preview tab, clicking back to
`a.txt` leaves the `b.txt` preview tab open, which is what the current
wording says should not happen. Single-clicking `c.txt` takes over the
same slot rather than adding a second preview tab, and pinning the
preview tab drops the italics.
- Ran the docs steps from `run_tests.yml` locally: `zed
--dump-all-actions > crates/docs_preprocessor/actions.json`, then
`mdbook build ./docs` on mdbook 0.4.37 to match CI. Exit 0 with no
preprocessor errors, and both new `{#action ...}` references resolve —
the rendered page reads "pane: toggle pin tab" and "pane: toggle preview
tab" in the same style as the existing "project panel: open permanent".
- `cargo test -p workspace --lib` — 272 passed, 0 failed, including
`test_close_other_items_unpreviews_active_item`,
`test_reopening_closed_item_after_unpreview`, and the proptest that
opens random paths with random `allow_preview` and asserts a pane never
holds more than one preview item.
## Notes for review
- Several people on the issue would rather the *behaviour* changed, so
that preview tabs close on any tab switch the way the docs currently
describe. I left that alone deliberately: the issue is typed `Docs`, so
this PR only makes the prose match the implementation. Happy to open a
discussion separately if it is worth pursuing.
- Saving a file also unpreviews it (`Pane::save_item`), but I left it
off the list as redundant — you have to edit a file before you can save
it, and editing is already there. Say the word if you would rather it
were explicit.
- The comment on `preview_tabs.enabled` in
`assets/settings/default.json` words this as "close automatically when
you open another preview tab". That is right for the project-panel path
and carries the same code-navigation caveat as above; I left it alone to
keep this diff to one file, and am happy to align it in a follow-up.
## 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
- [x] The content adheres to Zed's UI standards — N/A, docs only
- [x] Tests cover the new/changed behavior — N/A, no behaviour change;
the docs build is verified locally
- [x] Performance impact has been considered and is acceptable — N/A
---
Release Notes:
- N/A
# Objective
Video overview of this PR, maybe it helps with the review:
https://youtu.be/-JhKcZttXp8Fixes: #63828
Building Zed from source and opening a project in a dev container fails
with `Remote server exited with status 1`.
Zed compiles `remote_server` and uploads it into the container. The
binary loads Zed's default settings from the Zed source directory, which
doesn't exist there, and panics in `settings::init`:
```
thread 'main' panicked at crates/util/src/util.rs:729:10:
dev asset loading requires running from within the checkout
```
The build passes `--features debug-embed` to embed those assets instead,
but `remote_server/debug-embed` only enabled its own (unused)
`rust-embed` dependency. It never reached `util/debug-embed`, which is
what `fs_embed!` gates on. Regression from #63396.
SSH and WSL remotes are affected the same way. Release builds are not:
assets are embedded whenever `debug_assertions` is off.
## Solution
Forward the feature.
```diff
-debug-embed = ["dep:rust-embed"]
+debug-embed = ["dep:rust-embed", "util/debug-embed"]
```
The same one-line change is already included as a secondary fix in
#63613, whose subject is unrelated; its author offered to split it out.
## Testing
- `cargo tree --package remote_server --features debug-embed` now
resolves `util` with `debug-embed`. Before:
`rand,test-support,util_macros`.
- `cargo check -p remote_server --features debug-embed --target
x86_64-unknown-linux-musl` passes clean.
- Manually: built Zed from source on Linux x86_64, opened a project in a
dev container (`mcr.microsoft.com/devcontainers/base:ubuntu`). Before:
panic at startup. After: connects.
Not tested on macOS or Windows hosts.
No test added: this is feature wiring, and #63577 notes that nothing in
Zed's graph enables `util/debug-embed`, so CI can't exercise 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 adheres to Zed's UI standards
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- N/A
# Objective
Fixes#63869 — "Copy Value" in the debugger panel copied the trimmed
display preview instead of the full value.
## Solution
Evaluate the variable's `evaluateName` via the DAP `evaluate` request
using the `clipboard` context (falling back to `variables`), and copy
the returned result. Falls back to the display value if evaluation
fails.
## Testing
- `cargo test -p debugger_ui --lib` passes.
---
Release Notes:
- Fixed debugger "Copy Value" copying the truncated preview instead of
the full value.
Before, `GlobalWatcher` held a native backend and a polling backend, and
its methods dispatched to one or the other.
After this PR, we have `OsWatcher` which wraps a single watch backend,
and `RealFs` and `FakeFs` hold a native `OsWatcher` and a polling
`OsWatcher`.
This moves the split closer to the place where we decide which backend
should be used, and lets us clean up some indirection. A nice bonus is
that the "is this backend recursive" data exists in exactly one place.
Release Notes:
- N/A
This is a pure refactoring PR to make our FS watching code more
testable. It does two things:
1. Make the `GlobalWatcher` be owned by `RealFs` or `FakeFs` instead of
being a process-wide global.
2. Delete `FakeWatcher` and its `Watcher` implementation, and instead
mock the notify crate itself in `FakeFs` tests via the existing
`WatchBackend` trait. Now all our FS watching code down to the notify
boundary runs the same in tests and production.
Release Notes:
- N/A
# Objective
This removes an extra filesystem metadata lookup from
`RealFs::metadata`.
On Unix, `is_executable` performs a full extra stat(2)
(std::fs::metadata) just to read `mode() & 0o111`, even though the mode
bits are already present in the `symlink_metadata` result. On Windows
its impl also starts with `self.exists()`, another stat. This costs one
extra syscall plus one extra executor spawn per entry during the
worktree background scan - I experienced millions of avoidable syscalls
on a large monorepo.
## 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 worktree scan performance by removing redundant executable
checks.
---------
Co-authored-by: Hamish <hamishc@users.noreply.github.com>
Closes#63620
Count Agent Skill descriptions by Unicode characters rather than UTF-8
bytes across validation, load-time warnings, the skill creator counter,
and imported-description truncation. This aligns Zed with the Agent
Skills specification and reference validator.
Testing:
- `cargo test -p agent_skills --lib`
- `cargo test -p settings_ui --lib`
- `cargo build -p zed`
- Manually verified that a 342-character CJK description containing
1,026 UTF-8 bytes can be saved successfully
- `./script/clippy -p agent_skills -p settings_ui --tests` is blocked by
an unrelated existing `clippy::drain_collect` diagnostic in
`crates/rpc/src/proto_client.rs`
<img width="667" height="210" alt="Successfully created Unicode test
skill"
src="https://github.com/user-attachments/assets/2427fce2-9f0f-462d-959f-e08ad160b3d1"
/>
Confirmed that the Codex agent panel discovers the skill and exposes it
in command completion without a length warning.
<img width="953" height="367" alt="image"
src="https://github.com/user-attachments/assets/8c100197-2e6e-4627-b3f8-862bdfaac918"
/>
Release Notes:
- Fixed valid multibyte Agent Skill descriptions being rejected for
exceeding a byte-based length limit.
---------
Co-authored-by: Chara <249489759+cmdr-chara@users.noreply.github.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
I think this is the final attempt of mine to do anything about the
binary size, as those fixes become relatively intricate and I like them
less and less.
Each commit has its own improvement stats and mainly reduce generic-code
duplication through shared non-generic helpers and `#[inline(never)]`
boundaries.
Total reduction stats:
With sccache disabled, `cargo clean` and `cargo build --release -p zed`:
Before: zed binary 396716472 bytes
After: zed binary 359416504 bytes (-9.40%, -37.29MB)
Release Notes:
- N/A
---------
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
Previously, on macOS, we set the [WatchRoot
flag](https://developer.apple.com/documentation/coreservices/kfseventstreamcreateflagwatchroot)
to `FsEventStreamCreate` (via our notify fork). This causes the
resulting stream to produce an event when any segment of the watched
path changes due to a rename (e.g. `/Users/cole/zed/crates/gpui` ->
`/Users/cole/zed2/crates/gpui`).
The cost of this is that for each path we watch on macOS, we hold open
file descriptors for that path and all its parents (and these are not
deduplicated across watched paths).
This especially blows up our file descriptor usage with non-visible
worktrees from GTD jumps:
```
zed 25415 cole 721r REG 1,14 99826 168007500 /Users/cole/.rustup/toolchains/1.97.1-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs
zed 25415 cole 722r DIR 1,14 2432 168008864 /Users/cole/.rustup/toolchains/1.97.1-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src
zed 25415 cole 723r DIR 1,14 128 168008863 /Users/cole/.rustup/toolchains/1.97.1-aarch64-apple-darwin/lib/rustlib/src/rust/library/core
zed 25415 cole 724r DIR 1,14 928 168008723 /Users/cole/.rustup/toolchains/1.97.1-aarch64-apple-darwin/lib/rustlib/src/rust/library
zed 25415 cole 725r DIR 1,14 128 168008722 /Users/cole/.rustup/toolchains/1.97.1-aarch64-apple-darwin/lib/rustlib/src/rust
zed 25415 cole 726r DIR 1,14 96 168008721 /Users/cole/.rustup/toolchains/1.97.1-aarch64-apple-darwin/lib/rustlib/src
zed 25415 cole 727r DIR 1,14 704 168004176 /Users/cole/.rustup/toolchains/1.97.1-aarch64-apple-darwin/lib/rustlib
zed 25415 cole 728r DIR 1,14 256 168004175 /Users/cole/.rustup/toolchains/1.97.1-aarch64-apple-darwin/lib
zed 25415 cole 729r DIR 1,14 224 168004164 /Users/cole/.rustup/toolchains/1.97.1-aarch64-apple-darwin
zed 25415 cole 730r DIR 1,14 576 292591 /Users/cole/.rustup/toolchains
zed 25415 cole 731r DIR 1,14 224 292480 /Users/cole/.rustup
zed 25415 cole 732r DIR 1,14 8576 43880 /Users/cole
zed 25415 cole 733r DIR 1,14 160 18221 /Users
```
This PR bumps our notify dependency so that we no longer set that flag,
and instead just wake the background scanner loop every 5s to check
whether the worktree root path has changed. Note that both before and
after this change, the process_events arm of that loop checks the
worktree root path in the same way before doing anything else, so there
is no risk of descending further into the background scanner code with
an incorrect root path.
Release Notes:
- Made Zed use fewer file descriptors on macOS.
> **AI disclosure:** I used Claude (Anthropic) to write this change, its
test, and draft this PR description, after my own app shipped a
rendering bug that only reproduces on 1x displays — every test built on
gpui's test platform renders at a fixed 2.0 scale factor, so the bug was
invisible to an otherwise thorough offscreen test suite. I reviewed the
diff, ran the tests myself, and understand and take responsibility for
the change, per Zed's [AI
Policy](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#ai-policy).
# Objective
- `TestWindow::scale_factor()` has returned a hardcoded `2.0` since the
test platform's inception (`75f0670dd`, 2024-01-03), with no way for a
test to change it. Rendering behavior that only appears on 1x displays —
device-grid rounding, pixel snapping — can't be exercised: an error of
one device pixel is the full width of a hairline at 1x and half a
logical pixel at 2x, so this class of bug passes every test and ships.
- `Window::set_scale_factor` (`24058c865`, #61348, 2025-08) exists but
doesn't cover this: it overrides only the `Window`'s cached copy, so the
next `bounds_changed` re-reads the platform window and silently reverts
the override to 2.0.
## Solution
- Back the value with `TestWindowState`, still defaulting to `2.0`, so
existing tests are unchanged.
- Add `TestWindow::simulate_scale_factor_change`, which stores the new
factor and reports it through the resize callback — the same channel a
real DPI change reaches the window on — plus the matching wrappers on
`TestAppContext`, `VisualTestContext`, and `TestAppWindow`, mirroring
the `simulate_resize` family.
- Because the platform window owns the value, it survives later bounds
changes (unlike `set_scale_factor`); the test pins that.
## Testing
- Added `test_simulate_scale_factor_change` in
`crates/gpui/src/window.rs`: asserts the unchanged 2.0 default, that a
simulated change propagates to `Window::scale_factor()` through the
resize callback, and that it persists across a subsequent
`simulate_resize`.
- Reverting `scale_factor()` to the hardcoded `2.0` while keeping the
test reproduces the failure (see Showcase), so the test pins the
behaviour rather than passing vacuously.
- `cargo test -p gpui --lib` — 299 passed, 0 failed:
```
running 299 tests
...
test window::tests::test_simulate_scale_factor_change ... ok
...
test result: ok. 299 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.23s
```
- Tested on macOS. The change is confined to test-support code
(`platform/test/`, test contexts), which is platform independent, but I
have not built the Linux or Windows platform crates.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
Before — the new test against the hardcoded `2.0`:
```
thread 'window::tests::test_simulate_scale_factor_change' panicked at crates/gpui/src/window.rs:7230:9:
assertion `left == right` failed
left: 2.0
right: 1.0
test result: FAILED. 0 passed; 1 failed
```
After — see the full run above.
Release Notes:
- N/A
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Closes#63725
Honor `preview_tabs.enable_preview_from_project_panel` when opening
files with `project_panel::Open` (Space), matching the setting-aware
single-click behavior. Add a regression test covering previews disabled.
Testing:
- `cargo test -p project_panel --lib`
- `cargo build -p zed`
- Manually verified with the setting disabled that Space opens two
permanent tabs
- Manually verified with the setting enabled that Space replaces the
previous preview tab
- `./script/clippy -p project_panel --tests` is blocked by an unrelated
existing `clippy::drain_collect` diagnostic in
`crates/rpc/src/proto_client.rs`
Manual verification:
Preview disabled — Space opens both files as permanent tabs:
<img width="1920" height="1032" alt="Preview disabled: Space opens both
files as permanent tabs"
src="https://github.com/user-attachments/assets/4b772aad-031e-401d-ac85-5a7f82a74bc1"
/>
Preview enabled — Space replaces the previous preview tab:
<img width="1920" height="1032" alt="Preview enabled: Space replaces the
previous preview tab"
src="https://github.com/user-attachments/assets/cfef36af-0cd5-4522-99b5-99d3bb2c3f9d"
/>
Release Notes:
- Fixed the project panel preview-tab setting being ignored when opening
files with the keyboard.
---------
Co-authored-by: Chara <249489759+cmdr-chara@users.noreply.github.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
# Objective
Fixes#63774
## Solution
On Wayland, the IME remained enabled when the focused control did not
register a text input handler. This allowed IME to intercept ABC...Z
character shortcuts in non-text UI, including the Project Panel outside
filename editing. However Windows 11 doesn't have this problem.
Fixed it by treating a missing input handler as not accepting text
input.
## Testing
I've tested on both Vim/Helix mode and normal mode with Vim/Helix
keybinds. All clear now.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
[after.webm](https://github.com/user-attachments/assets/91743d71-05c5-49d2-b032-4aebc8e91496)
ABC...Z character shortcuts are not going to be intercepted by IME
unexpectedly in project panel now.
---
Release Notes:
- Fixed IME remains enabled in project panel without a focused text
input in Wayland #63774
Follow up on #63775: disabling WebKit touch callouts did not fully
prevent iOS Safari from selecting the entire GPUI canvas alongside the
app's text selection.
Remote Web Inspector captured a viewport-sized DOM range containing the
canvas while its computed `-webkit-touch-callout` was `none` but
`-webkit-user-select` was `text`. Set both standard and WebKit-prefixed
`user-select` to `none` directly when GPUI creates the canvas, without
relying on the embedding application's stylesheet. This leaves
touch-event handling and the hidden IME textarea unchanged.
Closes DEL-2016
## Validation
- On an iPhone, injecting ordinary inline `-webkit-user-select: none`
prevented the issue; switching it back to `text` reproduced it. No
`!important` was necessary. This tested the equivalent runtime style,
not a rebuilt app containing this commit.
- `cargo fmt -p gpui_web -- --check` passed.
- `RUSTC_BOOTSTRAP=1 cargo check -p gpui_web --target
wasm32-unknown-unknown` passed. The bootstrap flag is required by the
existing `wasm_thread` dependency.
Release Notes:
- Fixed unintended whole-canvas selection when long-pressing in GPUI web
applications on iOS.
# Objective
Fixes#52884
On macOS, with **"Double-click a window's title bar"** set to **Fill**
and **"Tiled windows have margins"** enabled (System Settings > Desktop
& Dock), double-clicking Zed's title bar fills the whole visible frame
with no margins, while native apps leave the margin in place.
The `"Fill"` arm of `titlebar_double_click` currently falls back to
`zoom:`, with a comment stating that there is no documented API for the
Fill action. `zoom:` is the classic maximize: it targets the screen's
visible frame and never goes through the system tiling path, so the
margin preference cannot apply.
## Solution
AppKit does implement the action: `_zoomFill:` is the selector behind
**Window > Move & Resize > Fill**, and it goes through the system tiling
path. Prefer it for the `"Fill"` case, keeping `zoom:` as a fallback
when the window does not respond to it.
This means the margin geometry stays owned by the OS: neither
`EnableTiledWindowMargins` nor `TiledWindowSpacing` has to be read or
reimplemented in gpui, and the behavior follows the setting when the
user toggles it.
The selector is underscore-prefixed, hence the `respondsToSelector:`
guard and the `zoom:` fallback.
## Testing
Measured on macOS 26.6.2 (25G76), aarch64, on a 2560x1440 display with
the Dock on the left, so `visibleFrame` is `(80, 0, 2480, 1410)`. Probe:
a plain `NSWindow` (`titled`, `resizable`) in a standalone AppKit
binary, with `EnableTiledWindowMargins = 1` and `TiledWindowSpacing`
unset (default 8).
| action | resulting frame | insets vs `visibleFrame` |
| --- | --- | --- |
| `zoom:` | `(80, 0, 2480, 1410)` | 0 on all four sides |
| `_zoomFill:` | `(88, 8, 2464, 1394)` | 8 pt on all four sides |
`respondsToSelector: _zoomFill:` returns `YES` on that build. `cargo
check -p gpui_macos` and `cargo fmt -p gpui_macos -- --check` both pass.
To reproduce as a reviewer: enable both settings above, then
double-click the title bar. Before this change the window touches the
screen edges, after it the margin matches Finder or Safari. With "Tiled
windows have margins" off, both behave the same.
One behavior difference worth flagging: `zoom:` toggles, so a second
double-click used to restore the previous frame, while `_zoomFill:`
stays filled. AppKit exposes `_zoomUntile:` for the reverse direction,
which restores the pre-fill frame in my testing. I left it out to keep
the diff minimal, but I am happy to add the toggle if you would prefer
that a second double-click untiles.
I could not find a way to cover this with an automated test, since the
assertion would be about AppKit's own tiling geometry.
## 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 double-clicking the title bar ignoring the macOS "Tiled windows
have margins" setting when "Double-click a window's title bar" is set to
"Fill".
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01Um4zWW8chJ5rwbGXhW8APh
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
# Objective
- Improve Go's syntax highlighting by distinguishing control-flow
keywords from other keywords, in the same way it was done for other
languages.
## Solution
- Split keywords in `highlights.scm` into two groups: `keyword.control`
and `keyword`. This change affects tree-sitter highlighting, enabling
`Semantic Tokens` can override it.
## Testing
- Tested locally on Windows using a development build, with `Semantic
Tokens` disabled. Opened a Go file with a theme that assigns different
colors to `keyword` and `keyword.control`
## 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
- Screenshot showing a build without this change and one with it side by
side
<img width="930" height="154" alt="after"
src="https://github.com/user-attachments/assets/bf3f612f-0ffc-40e4-b4c3-7002fff49fb7"
/>
Release Notes:
- Improved Go syntax highlighting by separating control-flow keywords
from regular keywords
---------
Co-authored-by: Finn Evers <finn.evers@outlook.de>
Before this change, two different guests requesting the same kind of
request would cancel each other's requests and only the latest one would
have arrived to the latest to query that.
Release Notes:
- Fixed collab LSP requests cancelling each other sometimes
OpenAI's Codex endpoint reports an exhausted ChatGPT subscription with
HTTP 429 and `error.type = "usage_limit_reached"`, but no `error.code`.
The Responses parser discarded the type and classified the response from
its status alone, making it appear to be transient request throttling.
This preserves `error.type` in both Responses and Chat Completions
parsing, considers both OpenAI discriminator fields during
classification, and falls back from the more-specific `error.code` to
`error.type` when populating the existing provider rejection code.
Usage-limit errors retain OpenAI's message and are not retried as
transient 429s.
The regression coverage exercises initial HTTP failures, Responses
stream errors, Chat Completions stream errors, unknown OpenAI-compatible
provider types, and precedence when both code and type are present.
Release Notes:
- Fixed ChatGPT subscription usage limits being shown as temporary
OpenAI request throttling.
When working with multiple windows, one agent worked in the external
hard drive's directory.
After finishing working on that external dir, I've closed the entire
window (the other one, unrelated, remained) only to discover I cannot
unmount the external hard drive.
Investigated, and seems that that manual `ref_count` is buggy — replaced
that with `WeakEntry` for both agent and ACP parts.
Also added leaked project logging into reliability.
Release Notes:
- Fixed projects leaking via agents panel
On iOS, WebKit presents a native touch callout when a user long-presses
a canvas. For GPUI Web this selected the full application canvas while
GPUI independently selected the intended word, producing two pairs of
selection handles, a native edit menu, and haptic feedback.
Set `-webkit-touch-callout: none` on the canvas created by `gpui_web`.
GPUI already owns touch gesture recognition on this surface, so WebKit
should not provide a competing canvas interaction.
Tested with:
- `cargo fmt --check --package gpui_web`
- `RUSTC_BOOTSTRAP=1 cargo check -p gpui_web --target
wasm32-unknown-unknown`
- A Delta Web WASM build using the local Zed checkout
- Manual verification on an iPhone that long-press word selection no
longer selects the full canvas
Release Notes:
- N/A
# Objective
- Removes Agent Panel menu item when AI disabled toggle is on.
- Fixes#63497
## Solution
- Use the global DisableAiSettings - disable_ai check in `app_menus.rs`.
To keep the order the same I
1. Check the disable AI setting
2. If true push to the `view_items` vec![]
3. Extend the vec![] with the remaining Menu Items and Separators.
## Testing
This can be tested visually:
1. Open Zed with AI settings enabled
2. Click View when Zed is focused
3. Noting where Agent Panel is on the list and to make sure clicking it
opens up the Agent Panel.
4. Open settings
5. Navigate to AI settings
6. Toggle disable AI on
7. Click View
Expected: Agent Panel is gone from the menu. The opposite is also
expected.
- This was tested on macOS 26.6.2 (25G83).
## 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
To keep this PR simple, I did not add any testing to this, as there were
no tests in this file already.
- [x] Performance impact has been considered and is acceptable
## Showcase
<details>
<summary>Click to view showcase</summary>
https://github.com/user-attachments/assets/50db44ac-6c11-4101-a064-fd4e3b285620
</details>
---
Release Notes:
- Fixed Agent Panel appearing in the "View" menu when disable AI
settings is on.
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
# Objective
Add Gemini 3.8 Flash to Zed's built-in Google AI provider so it can be
selected from the model dropdown.
## Solution
Register `gemini-3.8-flash` with its 1 million token context window, 64k
output limit, and Low, Medium, and High thinking levels. Medium is the
default, and Low is used when thinking is disabled because the model
rejects Minimal.
Add tests covering the model metadata, serialization, token limits,
thinking levels, and disabled-thinking behavior.
## Testing
Tested on Windows using Rust 1.98 GNU:
- `cargo test -p google_ai` — 23 passed
- `cargo fmt --all --check`
- `cargo +stable-x86_64-pc-windows-gnu clippy -p google_ai --release
--all-targets --all-features -- --deny warnings`
Reviewers can run `cargo test -p google_ai`. The pinned MSVC toolchain
was not tested locally because `link.exe` was unavailable.
## 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:
- Added Gemini 3.8 Flash to the Google AI models
`KeyBinding::set_default_visibility` lets applications suppress
keybinding labels, but the current visibility value is private to
`KeyBinding::render`. Applications that compose related modifier hints
from lower-level UI elements cannot follow the same presentation policy.
Expose the existing visibility query without changing its behavior. This
lets those callers omit their complete hint, including accompanying text
and spacing, while sharing the same application-wide source of truth.
Release Notes:
- N/A
`replace_value_in_json_text` spelled a newly inserted key with
hand-written quotes, so a key carrying a `"` or a `\` produced invalid
JSON. The removal and rename paths then could not find such a key again:
both walked backwards to the nearest raw `"`, which lands inside an
escape, and ate the neighbouring entry while leaving JSON that still
parses.
The parser already hands back the key's byte range, so the three sites
use it, and serde for the spelling, the way key lookup and the
nested-construction path always did.
Worth knowing beyond the escaping: `update_value_in_json_text` pushes
every map key into the key path verbatim, so any settings map whose keys
are not authored by hand reaches this. Most are typed by the user, but
`favorite_config_option_values` is keyed by a `SessionConfigId` that an
ACP agent supplies over the protocol.
Tests cover insertion into a single-line and a multi-line object,
removal and rename, all with a key carrying a quote and a backslash.
Release Notes:
- Fixed a settings key containing a quote or a backslash corrupting
`settings.json`.
## Summary
- Add `window_title_format` and `window_title_separator` settings for
customizing the window title
- Expose `${projectName}`, `${fileName}`, `${filePath}`,
`${relativePath}`, `${fileStem}`, `${remoteName}`, `${remoteHost}`,
`${appName}`, and `${separator}` as the native Zed window-title
variables
- Keep the collab `↗/↙` indicator hard-appended
- Translate VS Code `window.title` / `window.titleSeparator` aliases
into Zed's canonical variables during settings import, including
`${appName}` passthrough support and mapping VS Code's
`${activeEditorMedium}` onto Zed's `${relativePath}`
- Gate path-, remote-, and release-channel-derived lookups on whether
the active template references the corresponding variable, so the
default template avoids the extra work and still uses the existing
`last_window_title` cache to skip redundant platform updates
An alternative approach is at #49638. I went with `${variable}` names
over `$ZED_*`, canonical Zed variable names with VS Code aliases handled
at import time, kept `${separator}` as the only structural token, and
left `${dirty}` out of scope.
## Implementation reasoning and notes
### Variables considered but not included
- **collab/shared indicator variable**: kept hard-appended instead.
Folding it into the template would need whitespace/separator logic the
other variables don't need, and I'd rather not bake those semantics in
without a concrete need for it.
- **dirty variable**: The glyph choice and interaction with OS-level
edited indicators (like macOS's titlebar dot via `set_window_edited`)
felt like a separate design call I wasn't confident making here.
A separator-style config for each of collab/shared/dirty would solve the
above, but I didn't want to add too many additional settings initially.
### Performance
`update_window_title` is on a hot path, so two short-circuits apply:
- Path-, remote-, and release-channel-derived lookups (`absolute_path`,
relative-path string, `file_stem`, `remote_connection_options`,
`ReleaseChannel::try_global`) are gated on `template.contains(...)` via
`WindowTitleNeeds::from_template`, so the default template avoids the
extra work.
- The existing `last_window_title` cache still skips the platform call
when the rendered title is unchanged.
## Screenshots
Settings Editor entries for `window_title_format` and
`window_title_separator`:
<img width="774" height="324" alt="Settings Editor screenshot"
src="https://github.com/user-attachments/assets/b3378ec7-5649-4de3-9de6-164a42bce3fd"
/>
Customized Zed window titles in AltTab app switcher (remote, local,
empty):
<img width="774" height="324" alt="AltTab app switcher screenshot"
src="https://github.com/user-attachments/assets/a189945e-fc68-41b9-aab9-6dc04d5ef4ab"
/>
## 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#14534
Release Notes:
- Added configurable window title formatting with `window_title_format`
/ `window_title_separator`, including `${appName}` / `${remoteName}` /
`${remoteHost}` variables and VS Code `window.title` import support
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
The particular edge case that is covered here is closing a community PR,
then labeling it with an `area:`, and _then_ reopening it.
Release Notes:
- N/A
It's been fun for a while to have the same function calling GitHub API
in every single script, but we're going to have fun in different ways
now.
This one's for you, @MrSubidubi! 🥂
## Testing
I've ran the things that had `--dry-run` locally and nothing blew up,
but the ultimate smoke test will be carried out in production.
---
Release Notes:
- N/A
# Objective
In Vim mode, pressing `o` to open a line inside a documentation-style
block comment preserves indentation but drops the `* ` prefix.
The behavior for pressing Return is already correct as it continues this
prefix in the next line.
This change makes both actions behave consistently.
Fixes#55049
## Solution
Make Vim’s `o` and `O` commands copy documentation-comment prefixes such
as `* ` onto the new line. This happens only when the current line is
parsed as a comment.
Also added regression coverage for this and folded it into the existing
`test_o_comment` test.
## Testing
- `CC=clang CXX=clang++ cargo test -p vim test_o_comment -- --nocapture`
- `CC=clang CXX=clang++ cargo test -p vim`
- `CC=clang CXX=clang++ cargo test -p multi_buffer`
- `cargo fmt --all -- --check`
- `CC=clang CXX=clang++ ./script/clippy -p vim -p multi_buffer`
- Tested on Linux aarch64.
## Self-Review Checklist:
- [x] I reviewed the diff for quality, security, and reliability
- [x] Unsafe blocks, if any, have justifying comments
- [x] The content adheres to the Zed UI standards
- [x] Tests cover the changed behavior
- [x] Performance impact is acceptable
---
Release Notes:
- Fixed Vim `o` comment continuation in C-style multiline comments.
# Objective
The project panel doesn't show a file or folder's actual location on
disk anywhere in the UI — you have to rely on the tab title or context
menu ("Copy Path") to find out where a file actually lives, which is
especially unclear for files with the same name in different folders, or
when a project has multiple worktrees.
## Solution
- Adds a tooltip to each file/folder row in the project panel that shows
the entry's path on hover, with the home directory abbreviated to `~`
(via the existing `PathExt::compact()` utility, consistent with how
paths are already displayed elsewhere, e.g. the Recent Projects list).
## Testing
- Manually tested by hovering over files and folders at various nesting
depths in the project panel and confirming the tooltip shows the correct
path.
- Tested with a project both inside and outside the home directory to
confirm the `~` substitution only applies when appropriate.
- Confirmed existing row behavior (selection, click-to-open,
drag-and-drop, right-click context menu) is unaffected by the added
tooltip.
- Reviewers: hover over any row in the project panel and confirm the
tooltip appears after the normal hover delay and shows the expected
path.
- Tested on macOS only — haven't verified on Linux/Windows.
## Self-Review Checklist:
- [ ] 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
<img width="1512" height="871" alt="image"
src="https://github.com/user-attachments/assets/ec86f9ab-0a8e-40f7-b783-8a115203f11d"
/>
- Help others understand the result of this PR by showcasing your
awesome work!
- If this PR includes a visual change, consider adding a screenshot,
GIF, or video
- A before/after comparison is very useful for changes to existing
features!
While a showcase should aim to be brief and digestible, you can use a
toggleable section to save space on longer showcases:
<details>
<summary>Click to view showcase</summary>
My super cool demos here
</details>
---
Release Notes:
- Added a tooltip showing the file path when hovering over files and
folders in the project panel.
---------
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
## Objective
Markdown preview appearance settings are split between root theme
settings and
`markdown_preview`, and Markdown files always open as source editors
even when a user primarily
wants the rendered preview.
## Solution
- Group the Markdown preview font settings under `markdown_preview`,
migrate their legacy keys
across supported settings scopes, and update defaults, Settings UI, and
documentation.
- Add `markdown_preview.open_markdown_files_in_preview`, defaulting to
false. When enabled, clean
Markdown opens are replaced by a rendered preview while preserving dirty
editors, temporary-tab
behavior, pane activation and focus, targeted navigation, workspace
restoration, and the escape
back to source.
## Testing
- Manually verified on macOS that enabling the setting opens Markdown
files as preview-only.
- `cargo test -p markdown_preview` (three consecutive runs)
- `cargo test -p migrator nest_markdown_preview`
- `cargo test -p settings_content`
- `cargo check -p markdown -p markdown_preview -p migrator -p settings
-p settings_content -p settings_ui -p theme_settings -p workspace`
- `./script/clippy -p gpui_apple -p markdown -p markdown_preview -p
migrator -p settings -p settings_content -p settings_ui -p
theme_settings -p workspace --features gpui_apple/runtime_shaders`
- `cargo fmt --all -- --check`
- `git diff --check`
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([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
## Recordings
https://github.com/user-attachments/assets/032ab36d-f934-4ef7-aa18-53f3955a7df8https://github.com/user-attachments/assets/dc872265-2fe0-45ac-95eb-ff820d3a8164
Release Notes:
- Added a setting to open Markdown files directly in preview mode and
grouped Markdown preview settings together.
## Summary
- Reuse the existing Zed clone activity indicator, which previously
showed only a static `Cloning <URL>` message.
- Add filesystem job updates so the existing indicator can change while
a job is running.
- Stream exact `git clone --progress` messages into that indicator
without maintaining a phase allowlist.
- Preserve the final Git stderr message when cloning fails.
https://github.com/user-attachments/assets/a9b8cbfd-b87f-4d83-a24b-992eb1c4de1f
## Testing
- `cargo test -p fs git_clone_progress::tests -- --nocapture`
- `CCACHE_DISABLE=1 ./script/clippy -p fs -p activity_indicator`
- Manually cloned a repository and verified live Git progress in the
activity indicator.
Release Notes:
- Improved repository cloning by showing live Git progress in the
activity indicator.
# Objective
- Fixes#63433
- Currently, when there is no logged in user, the Organization menu item
shows up even though it should not.
## Solution
- This PR simply does an early return when there is no logged in user,
which skips the logic to add the organization header into the menu drop
down.
## Testing
- Open a Zed window
- Sign out of Zed
- Click the menu bar chevron (top-right corner) and ensure that the
Organization Menu item (like in image below is not there anymore.
- Login to make sure it does display properly when a user is logged in.
- I tested this on a Windows Machine as I use that for development, but
I believe the reproduction steps should be the same across Operating
Systems. I don't have access to other operating systems, so that might
be something that I'd need the communities help with to make sure
everything is working properly.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments - Not Applicable
- [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 - Not Applicable.
- [ ] Performance impact has been considered and is acceptable - Not
applicable
## Showcase
Before: Image is from the Original issue
<img width="438" height="458" alt="image"
src="https://github.com/user-attachments/assets/c4852e86-1b68-42d8-ad79-68904214eed7"
/>
After:
https://github.com/user-attachments/assets/520f2564-36da-433b-9339-8c0210695ee0
---
Release Notes:
- Fixed issue where "Organization" section in dropdown menu was being
rendered empty when no user was logged in.
---------
Co-authored-by: MrSubidubi <finn@zed.dev>
# Objective
Case conversion commands deleted the newline at the end of the
selection,
merging the selected line into the line below it.
To reproduce:
1. A buffer with two lines:
```
hello world
goodbye
```
2. Put the cursor on line 1 and run `editor: select line` — this selects
`hello world\n`, newline included.
3. Run `editor: convert to upper camel case`.
Before: `HelloWorldgoodbye`, one line. After: `HelloWorld` / `goodbye`.
## Testing
Added a regression test to `test_manipulate_text`.
Release Notes:
- Fixed the case conversion commands deleting the line break when the
selection
ended at the start of the next line.
# Objective
Add an optional smooth cursor movement animation to make cursor position
changes easier to follow, especially during larger jumps and
multi-cursor editing.
Related to #4991.
## Solution
- Add a new `cursor_animation.enabled` editor setting, disabled by
default.
- Animate local bar and block cursors using a damped spring while
preserving momentum during rapid cursor movement.
- Support multiple local cursors independently.
- Continue using the cursor color provided by the active theme.
- Snap immediately when scrolling, resizing, or other viewport changes
would make interpolation misleading.
- Respect the `reduce_motion` setting.
- Request animation frames only while an enabled cursor animation is
active.
Enabling the feature causes additional editor redraws while the cursor
is moving, so it is opt-in and the performance cost is mentioned in the
setting documentation.
### Attribution
The cursor spring physics and four-corner deformation algorithm are
adapted from
[vscode-neovide-cursor](https://github.com/LengineerC/vscode-neovide-cursor),
which is available under the MIT License and inspired by Neovide.
The implementation adapts the algorithm to Zed's editor and GPUI
architecture, including viewport handling, selection state
reconciliation, multi-cursor support, theme integration, and
reduced-motion behavior. The attribution and license information are
also retained in the source file.
## Testing
Automated checks run on Windows:
- `cargo fmt --all --check`
- `cargo test -p editor cursor_animation --lib`
- 15 cursor animation tests passed.
The tests cover:
- Spring convergence.
- Short and long cursor movements.
- Mid-animation retargeting and momentum preservation.
- Multiple independent cursors.
- Selection replacement and state cleanup.
- Viewport and scroll changes.
- Invalid or unusually large timing values.
- Supported cursor shapes.
To test manually:
1. Add the following to the settings file:
```json
"cursor_animation": {
"enabled": true
}
```
2. Move the cursor using typing, arrow keys, mouse clicks, and
multi-cursor actions.
3. Toggle the setting off and confirm that cursor rendering immediately
returns to its normal behavior.
Manual testing was performed on Windows and Linux using a release build
from this exact PR branch.
Verified that:
- Cursor animation is disabled by default.
- Enabling `cursor_animation.enabled` is hot-reloaded without restarting
Zed.
- Disabling the setting immediately restores the original cursor
behavior.
- Cursor movement remains responsive during typing, keyboard navigation,
and mouse navigation.
This exact PR branch has not yet been manually 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] 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
https://github.com/user-attachments/assets/17450529-491e-4982-b539-c46e3d5df467
---
Release Notes:
- Added an optional cursor movement animation setting.
---------
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>