Merge upstream Zed (764 commits) + fix ACP silent-prompt wedge - #73
Merged
Conversation
The issue with aws-lc that necessitated this workaround has been fixed upstream: aws/aws-lc#3197. Release Notes: - N/A
On Linux with client-side decorations, opening the workspace sidebar squared off the window corners on the sidebar's side while the rest of the window stayed rounded: Before: <img width="366" height="239" alt="image" src="https://github.com/user-attachments/assets/e0ee8d28-6143-42ea-bfd2-10d069df5492" /> After: <img width="426" height="219" alt="image" src="https://github.com/user-attachments/assets/51d2979d-0103-4742-90bd-a97f5837d99b" /> Previously, `client_side_decorations` took a `border_radius_tiling` override that `MultiWorkspace` used to deliberately square the window shape on the sidebar's side, since the sidebar painted a square background that would otherwise poke out past the rounded window border. The title bar and status bar already skip rounding their corners on the sidebar side, implying the sidebar is expected to own those window corners — it just never rounded them. This PR makes the sidebar round its outer corners the same way the title bar and status bar do (including overlapping the 1px window border on untiled edges to avoid a transparent gap in the rounded corners), and removes the now-unneeded `border_radius_tiling` parameter so the window border and backdrop round purely based on actual tiling state. Only applies when the window uses client-side decorations, so macOS and Windows are unaffected. Fixes zed-industries#54724 Release Notes: - Fixed square window corners on the sidebar side of client-decorated (Linux) windows when the workspace sidebar is open. --------- Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
# Objective Fixes zed-industries#60518. Zed sends an empty `name` for entries in `initialize.params.workspaceFolders`. Language servers such as basedpyright use this field to identify workspace service instances, resulting in an unnamed instance. ## Solution Derive each workspace folder name from its URI, preferring the decoded filesystem basename. For basename-less URIs, fall back to a non-empty path, URI path segment, or the full URI. Use the same construction for initialization parameters, dynamic workspace-folder notifications, and `workspace/workspaceFolders` responses so all LSP messages remain consistent. ## Testing - `cargo fmt --all -- --check` - `cargo test -p lsp` - `cargo check -p project --all-targets --all-features` - `./script/clippy -p lsp` - `./script/clippy -p project` Reproduced with basedpyright 1.39.9 over an LSP stdio session. Before this change it logged: ```text Starting service instance "" ``` With a derived workspace folder name it logged: ```text Starting service instance "zed-60518-project" ``` 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 Release Notes: - Fixed language servers receiving empty workspace folder names.
…dustries#61257) This is a lot of computation work which otherwise hangs the ui thread Closes FR-122 Release Notes: - Fixed a cause for ui hangs when interacting with the agent panel
…es#61045) ## Context Shift-click sometimes extended a selection from an earlier word boundary after double-clicking a word and moving the cursor with the arrow keys. Cursor movement changed the selection but left the word or line selection mode active, so the next Shift-click reused stale selection bounds. Selection movement now resets the mode to character mode whenever it changes the selection, while preserving valid empty line selections that have not been moved. Closes zed-industries#59913. Behavior before the fix : [Screencast from 2026-07-15 13-21-51.webm](https://github.com/user-attachments/assets/c126adfe-f9db-4a04-91c2-6db6e8070182) Behavior after the fix : [Screencast from 2026-07-15 13-19-38.webm](https://github.com/user-attachments/assets/19c9f5b6-aaf8-4e8b-b4c5-70327411d1b9) ## How to Review **`crates/editor/src/selections_collection.rs`**: `MutableSelectionsCollection::move_with` now resets the selection mode to character mode when movement changes a selection. **`crates/editor/src/editor_tests.rs`**: Regression tests cover Shift-click after collapsing a word selection and confirm that a valid empty line selection retains line-wise extension behavior. ## 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 Shift-click sometimes extending selections from a previous word or line boundary.
…ruption (zed-industries#60584) Closes zed-industries#60424 ## Problem With the repro from zed-industries#60424, staging the first deletion hunk marked the second deletion as staged in the UI even though git still had it unstaged, and clicking the (incorrectly shown) Unstage button inserted a duplicate copy of the hunk's contents into the git index on every click. The root cause is ambiguous hunk placement. The committed text contains repeated `end\n\n` line runs, so the deletion hunks can "slide": more than one placement produces a minimal diff. The uncommitted diff (HEAD vs worktree) anchored the remaining deletion at one row while the unstaged diff (index vs worktree), recomputed after the partial stage, anchored the same logical deletion at a different row. Everything that correlates hunks across those two diffs assumes they agree on positions: - the secondary-status matching in `hunks_intersecting_range_impl` found no unstaged hunk at the uncommitted hunk's rows and reported it as staged (`NoSecondaryHunk`); - the worktree→index projection in `compute_uncommitted_index_edits` treated the hunk's position as unchanged text and, on unstage, inserted the hunk's HEAD content at an index position that already contained it, duplicating it on every request. ## Fix Upgrade `imara-diff` from 0.1.8 to 0.2.0 and run `Diff::postprocess_lines` (imara-diff's port of git's xdiff slider/indent heuristic) after computing hunks in `buffer_diff`. This canonicalizes the placement of ambiguous hunks based only on their local content, so diffs of the same buffer against different base texts anchor the same logical change at the same rows. A bonus is that Zed's hunk placement now matches `git diff`'s output for such cases (git has used the indent heuristic by default since 2.11). As defense in depth, `compute_uncommitted_index_edits` now drops a pure-insertion index edit whose content is already present at the target position, so a stale secondary status can no longer duplicate index content. The imara-diff 0.2 API removed the `Sink` trait and the top-level `diff()` function, so the other call sites (`language/text_diff.rs`, `zeta_prompt/udiff.rs`, `edit_prediction_metrics/reversal.rs`) are migrated mechanically to `Diff::compute` + `hunks()` with unchanged behavior (0.2 also renamed `lines_with_terminator` to `lines` and changed the default `&str` tokenization to include terminators; the unified-diff builders keep terminator-less tokens via `str::lines()`). ## Testing - New regression test `test_staging_hunks_with_ambiguous_placement` replays the exact repro from zed-industries#60424 (same file contents): stages the first deletion, asserts the remaining hunks keep their unstaged status and the index matches exactly, then issues repeated unstage requests for the unstaged hunk and asserts the index is unchanged. Before the fix this test showed the second hunk flipping to staged and the index growing by one copy of the deleted block per unstage request. - `buffer_diff`, `language`, `zeta_prompt`, `edit_prediction_metrics`, `multi_buffer`, `git_ui`, `editor`, and the `project` integration suite pass. - One test expectation updated: `editor::test_fold_function_bodies` asserted the old placement of an ambiguous deletion (blank line before comment); the canonicalized placement (comment before blank line) matches what `git diff` produces for the same texts. Release Notes: - Fixed staging a hunk sometimes marking a different hunk as staged (and subsequent unstaging corrupting the git index) when the diff contained repeated lines ([zed-industries#60424](zed-industries#60424)). --------- Co-authored-by: Cole Miller <cole@zed.dev>
# Objective - Show accurate diff stats for each staged and unstaged projection of a partially staged file in the Git panel. - This was originally considered for zed-industries#59884, but was scoped out of that already-large PR and is being submitted separately as discussed there. ## Solution - Collect HEAD-to-index and index-to-worktree diff stats alongside the existing combined HEAD-to-worktree stats. - Carry the staged and unstaged stats through repository status snapshots and remote status serialization. - Use the stat matching the projected Git panel section while preserving the combined stat for the other grouping modes. - Update the fake Git repository and add regression coverage with deliberately different staged and unstaged counts. ## Testing - `cargo check -p git_ui` - `cargo check -p collab` - `cargo test -p git_ui test_group_by_staging_section_membership_and_order --lib` - `cargo test -p project --lib --no-run` - `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 --- Release Notes: - Fixed diff stats for partially staged files in the Git panel
…ed-industries#45499) this pr enables opening a specific line in a diff using zed's cli Release Notes: - refactor: add possibility to open specific line in a diff using the cli --------- Co-authored-by: Cole Miller <cole@zed.dev>
…industries#51727) Release Notes: - Add disable commit actions during commit message generation --------- Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com> Co-authored-by: Cole Miller <cole@zed.dev> Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
…tries#56368) Release Notes: - Fixed remote support for adding paths to .gitignore The problem solved by this PR is: when opening a remote project, right-clicking a new file in the git panel and clicking "Add to .gitignore" does not work. --------- Co-authored-by: Cole Miller <cole@zed.dev>
# Objective Fixes zed-industries#61244 In the Agent Panel, after a multi-turn conversation, the action buttons ("Copy this Agent response", "Scroll to User Message", and "Scroll to top") were only shown after the last assistant response. This was inconvenient for users who wanted to copy or navigate from earlier responses. Additionally, the "Scroll to User Message" button always jumped to the most recent user message in the entire thread, even when clicked from a historical assistant response. This PR addresses both issues by rendering the controls after every non-blank assistant response, and by making "Scroll to User Message" jump to the user prompt associated with that specific response. ## Solution - Refactored `render_thread_controls` in `crates/agent_ui/src/conversation_view/thread_view.rs` to accept an explicit `entry_ix`, `copy_response_index`, `is_thread_bottom`, and `user_message_index`, making it reusable per assistant message. - Rendered the control bar after each non-blank `AssistantMessage` entry, not just the last entry. - Made button IDs unique by using tuple IDs like `("name", entry_ix)`, matching the existing style in this file and avoiding `format!` allocations. - Keyed the copy button ID by the control bar location (`entry_ix`) while still copying `response_index`'s content, so the bottom fallback bar doesn't duplicate IDs with the per-response bar. - Gated the generation/confirmation early return to `is_thread_bottom`, so historical controls remain accessible while the thread is generating or waiting for confirmation. - Gated turn stats and thread feedback buttons to the bottom of the thread so they only appear on the last response. - Replaced `scroll_to_most_recent_user_prompt` with a more general `scroll_to_user_message_index` that takes an optional target user message index. - For each per-response button, computed the associated user message index as the most recent user message before that assistant response. - For the bottom fallback (when the last entry is not an assistant message), passed `None` so the button still scrolls to the most recent user message. - Updated the tooltip from "Scroll to Most Recent User Message" to "Scroll to User Message". - Updated existing tests and added assertions for `scroll_to_user_message_index(Some(ix), cx)`. ## Testing - `cargo check -p agent_ui` - `./script/clippy -p agent_ui` - `cargo test -p agent_ui --lib` (399 passed) - Added assertions in `test_scroll_to_most_recent_user_prompt` to verify `scroll_to_user_message_index(Some(0), cx)` and `scroll_to_user_message_index(Some(2), cx)` scroll to the correct user message indices. - Manual testing recommended: open the Agent Panel, send multiple messages, and verify that each assistant response has the three buttons and that "Scroll to User Message" jumps to the corresponding user prompt. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments (no unsafe blocks) - [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: Only the last assistant response in the Agent Panel showed the "Copy this Agent response", "Scroll to User Message", and "Scroll to top" buttons. Clicking "Scroll to User Message" always jumped to the most recent user message. After: Every non-blank assistant response displays these buttons. Clicking "Scroll to User Message" on a historical response scrolls to the user prompt that triggered that specific response. https://github.com/user-attachments/assets/46e02bcc-8bed-4cf3-a08a-9ef91a206d08 --- Release Notes: - Added "Copy this Agent response", "Scroll to User Message", and "Scroll to top" buttons to every agent response in the Agent Panel. - Fixed "Scroll to User Message" so it jumps to the user prompt associated with the current response, rather than the most recent user message.
…ries#61275) # Objective Land five small, independent performance fixes found during an audit of hot paths (anchor resolution, line shaping, worktree scanning, and sorting in multibuffers). ## Solution Each fix is its own commit, so **this PR is best reviewed commit-by-commit** — every commit message contains the full reasoning for that change: - `text`: Avoid redundant rope traversal in `Anchor → usize` conversion — call `offset_for_anchor` directly instead of `summary_for_anchor`, which recomputed the same byte offset with ~4 extra O(log n) tree walks. This is the hottest anchor-resolution path. - `editor`: Avoid double allocation per shaped line — `line.as_str().into()` instead of `line.clone().into()`, which allocated twice per visible line, every frame. - `text`: Use `sort_unstable_by_key` in operation queue insertion — Lamport timestamps are unique keys and duplicates are deduped right after, so stability buys nothing. - `multi_buffer`: Sort with an explicit comparator instead of `sort_unstable_by_key`, which cloned a `PathKey` (`Arc` refcount bump) on every comparison. - `worktree`: Replace O(n²) `Vec::remove` in the deferred-directory pass with an O(1) `None` assignment — the vec is already `Vec<Option<ScanJob>>` and is consumed with `.flatten()`. ## Testing - No behavior changes intended; all changes are mechanical and tests affected crates pass: `text`, `editor`, `multi_buffer`, `worktree`. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Improved editor performance through several micro-optimizations in anchor resolution, line shaping, and worktree scanning.
Restore an optimization that I mistakenly removed in zed-industries#54728. Drawing a quad with a border but no fill color will run the quad fragment shader for every transparent interior pixel, which is especially costly when the quad is large. Instead, split these quads into four non-overlapping strips that cover the regions where borders are painted. The side strips own the straight left and right edges, while the top and bottom strips own the horizontal edges and the rounded corners. Previously, this optimization only applied to borders drawn by the Taffy layout. I've decided to reinstate the logic directly in `paint_quad` so that the optimization can be applied more generally... though if this feels like too much policy, we can move it back. This reduces the GPU time spent painting a representative Zed scene by about 20% or so. Release Notes: - N/A
…#61283) Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
…ies#61298) # Objective When joining lines, Zed checks the next line and strips comment, documentation-comment, and unordered-list delimiters. The logic trims whitespace from these delimiters, then checks whether the next line starts with the trimmed delimiter. If it does, Zed removes the delimiter before joining the lines. This works for comment and documentation-comment delimiters, where trailing whitespace does not affect their meaning. However, it causes unexpected behavior for unordered-list delimiters, which, as far as I know, are unique to Markdown. In Markdown, `* ` is an unordered-list marker, while `*` is also used as an emphasis delimiter, such as `*italics*` and `**bold**`. The current logic strips the leading `*` from these emphasis cases, which it should not. ## Solution For unordered-list delimiters, Zed should strip the exact delimiter, such as `* `, rather than the trimmed `*`. It should strip a bare `*` only when the next line contains only `*`. ## Testing Tested locally and new GPUI tests were added. ## 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 markdown emphasis delimiters being removed when joining lines.
…zed-industries#60054) # Objective When you make a line selection in a row that is long enough to multiple lines long while wrapped the selection box will disappear when the cursor (or just origin of the line selection goes off screen). ## Solution The when you are in line selection mode the `SelectionsCollection` just enables `line_mode`, instead of storing the whole ranges of those line selections, so the Anchor of the selection is a single point. `disjoint_in_range` filters all the disjoint selections of the collection to fit inside some range, this is used to render the selection boxes on screen. `disjoint_in_range` had no special case for `line_mode` causing it to filter on the point instead of the bounds of the selection. My fix is too add a case for `line_mode` that changes the filtering `Anchor` -> `Point` and then only filters on the row. Then use the proper bounds of the selection with that: ```rust let (start_ix, end_ix) = if self.line_mode { let start_row = range.start.to_point(buffer).row; let end_row = range.end.to_point(buffer).row; let start_ix = self .disjoint .partition_point(|probe| probe.end.to_point(buffer).row < start_row); let end_ix = self .disjoint .partition_point(|probe| probe.start.to_point(buffer).row <= end_row); (start_ix, end_ix) } else { ... ``` ## Testing Before: https://github.com/user-attachments/assets/a443de39-53d7-4ee4-b3af-d3be99ca1fcf After: https://github.com/user-attachments/assets/18c1c7ef-45cc-4dc8-9a8b-241252c376ab I also added some tests `disjoint_in_range_line_mode_matches_whole_row`, being the one that really tests this behavior. the other two are just for `disjoint_in_range` I am using macOS 27.0 Beta (26A5368g), but I noticed this bug before upgrading. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - editor: Fixed bug where selection boxes wouldn't render when the cursor was offscreen.
# Objective When using column Git blame with avatars enabled, blame entries whose author name is the longest in the buffer can exceed the blame border. For example, line 10 of `crates/vim/src/visual.rs` is displayed as follows: <img width="998" height="121" alt="issue" src="https://github.com/user-attachments/assets/eff3e1ff-bd1c-42af-ae9b-da8efb0a7c22" /> The blame contents exceeds the width. The root cause is in the blame width calculation: https://github.com/zed-industries/zed/blob/0c51c7fd2481859e9da5c490ef8e41ddbcf1a341/crates/editor/src/editor.rs#L11583-L11598 The calculation accounts for the commit SHA, author name, and timestamp. Other elements, including spacing, margins, and the avatar, are represented by the fixed `SPACING_WIDTH` constant. For typical fonts, `ch_advance` is approximately `0.5rem` to `0.6rem`, so `SPACING_WIDTH` provides approximately `2rem` to `2.4rem` for non-text content. However, the avatar itself occupies `1rem`: https://github.com/zed-industries/zed/blob/0c51c7fd2481859e9da5c490ef8e41ddbcf1a341/crates/ui/src/components/avatar.rs#L81 When avatars are displayed, the entry also contains three `0.5rem` gaps and also one `0.5rem` right margin: https://github.com/zed-industries/zed/blob/0c51c7fd2481859e9da5c490ef8e41ddbcf1a341/crates/git_ui/src/blame_ui.rs#L170-L188 This requires approximately `3.0rem`, which can exceed the space provided by `SPACING_WIDTH`. When an entry contains both the longest author name in the buffer and a long timestamp, its content can therefore exceed the blame border. ## Solution The simplest fix would be to increase `SPACING_WIDTH`, but that would still rely on an approximate conversion between editor character widths and UI dimensions. Instead, this PR adds a method to the blame renderer for calculating the non-text width of a blame entry. The editor uses this value together with the measured text width when calculating the final blame width. ## Testing Tested locally. A before-and-after comparison is included below. ## 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 | Before | After | | :--: | :--: | | <img width="551" height="94" alt="Before" src="https://github.com/user-attachments/assets/b6741a41-6531-4fae-adaa-f9ae0b298e0f" /> |<img width="566" height="93" alt="After" src="https://github.com/user-attachments/assets/e8d75d2a-6b07-43f6-b2a8-bfb76d118611" /> | Release Notes: - Fixed Git blame entries overflowing the gutter when avatars are displayed.
Found some unnecessary allocations and an `.unwrap()` while working on a separate issue. The PR makes no behavior changes and should be trivial to review. ## Testing All tests pass. These changes are not expected to change any behavior. None of the changes are interleaving, so it should be easy to review top-down. Reasoning for each change can be read from individual commit messages. Release Notes: - N/A or Added/Fixed/Improved ...
…ansparent themes (zed-industries#61268) # Objective As part of the transparent-window artifact fixes in zed-industries#58981, multibuffer header backgrounds stopped rendering whenever the window was transparent or blurred. This caused a regression for transparent themes, which commonly use an opaque multibuffer header background to prevent editor text from showing through sticky headers. ## Solution Render `editor_subheader_background` when either the window or the configured background color is opaque. This restores opaque multibuffer header backgrounds as a mask for the editor content beneath them, while preserving the zed-industries#58981 behavior for translucent backgrounds that would otherwise create a darker layered region. ## Testing Manually verified locally with a transparent theme that an opaque multibuffer header background is rendered and prevents editor text from showing through sticky headers. ## 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 **Nightfox - opaque / blurred** Before: <img width="2566" height="1771" alt="图片" src="https://github.com/user-attachments/assets/55b333bc-b029-4079-9bbc-86462a3f41a7" /> After: <img width="2444" height="1687" alt="图片" src="https://github.com/user-attachments/assets/217e1ee7-717c-4961-8db0-5eac8104e5bb" /> --- Release Notes: - Fixed editor text showing through multibuffer headers and overlapping header text in transparent themes.
…ustries#61253) # Objective Closes zed-industries#41743 When selecting text on the last line, excluding the trailing newline, Vim and Helix modes behave differently from native Vim and Helix when the file has a trailing newline: 1. We cannot use `l` (move right) to include the final `\n` in the selection, while native Vim and Helix allow this. 2. By default, the rendered cursor should appear on the selected character. When that character is the final `\n`, however, Zed renders the cursor on the synthetic empty line after it rather than at the end of the preceding line. This differs from native Vim and Helix, as well as from Zed's behavior for newlines elsewhere in the file. 3. When the final `\n` is selected, it affects subsequent selection motions, as reported in zed-industries#41743. The cause is several special-case guards for trailing newlines in Visual and Select modes. These guards exist in the cursor rendering logic: https://github.com/zed-industries/zed/blob/c9e8e611dbc279afa0914d28c4d37ad07f38c03b/crates/editor/src/element.rs#L201-L213 They also exist in the Visual motion logic for Vim mode and the Select motion logic for Helix mode (the Helix implementation was adopted directly from Vim in zed-industries#43234): https://github.com/zed-industries/zed/blob/c9e8e611dbc279afa0914d28c4d37ad07f38c03b/crates/vim/src/visual.rs#L254-L259 Finally, they exist in the selection extension logic for Vim and Helix (the Helix logic was also adopted from Vim): https://github.com/zed-industries/zed/blob/c9e8e611dbc279afa0914d28c4d37ad07f38c03b/crates/vim/src/visual.rs#L273-L284 These guards cause trailing-newline selections to behave inconsistently with selections containing newlines elsewhere in the file. ## Solution Remove all the guards mentioned above, including those in cursor rendering, Visual and Select motions, and selection extension. And due to the cursor rendering logic is also changed, an exsisting test is updated. After removing these guards, I could not reproduce the unexpected behavior described in the existing comments: https://github.com/zed-industries/zed/blob/c9e8e611dbc279afa0914d28c4d37ad07f38c03b/crates/vim/src/visual.rs#L247-L253 The only remaining difference is that, when the cursor is on the last line in Visual mode, pressing `j` moves it to the final `\n` rather than to the synthetic empty line after it. In comparison, native Vim and Helix do nothing in this case. ## Testing - Built and tested locally. - Added new GPUI tests. ## 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 Vim Visual and Helix Select modes when selections include a trailing newline.
…roup (zed-industries#61062) # Objective Before this PR we would show inside the recent project picker the projects per group (window) which is great, but when you were trying to remove a project from the group it would show you a label that includes the just removed project and also when you clicked on it, it would bring you back to the 2 project's in the same window in this case. ## Solution My solution is when we actually remove the project from a group, we also update the group key that includes the newly updated path list, that should no longer have the removed project path in it. I also refactored it a bit so we have one method that does the project removing part so we don't have to duplicate this logic. ## Testing You can produce this when you do the following: 1. Open Zed 2. Add 2 projects to your window using the project panel 3. Open the recent project picker 4. See that the `This window` header shows 2 projects 5. Remove one of the projects 6. See that below the `This window` header we would show you a entry that shows you `projectA, projectB` where **projectb** should be removed from the group. I also added a regression test that covers this issue, so we can make sure the change actually works and does not regress of couse :). ## 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** (**Note** before this change we showed the group project names comma seperated) https://github.com/user-attachments/assets/750358f4-f347-4f91-86b7-6500b2e09af9 **After** (**Note** after we now updated the groups key so it does not include the removed project) https://github.com/user-attachments/assets/f15acea8-d936-4aab-b5c2-324ee49f74a2 --- Release Notes: - Recent projects: Fixed that it shows you a removed project from a group
…ries#61161) # Objective Fixes zed-industries#59983. Reducing the Zed window height with the terminal open can clip the Bottom Dock and terminal content. The dock should shrink to remain within the workspace instead of extending beyond the window. ## Solution Restore height clamping for Bottom Dock panels by skipping `clamp_panel_size` only when a panel actually uses flexible width. Flexible sizing remains unchanged for the Left and Right Docks, while Bottom Dock panels once again reduce their stored height to fit the available workspace. ## Testing Added a regression test that distinguishes flexible Bottom and Right Dock behavior. It confirms that the Bottom Dock is clamped while the flexible-width Right Dock is not. The test failed with the previous condition and passed with this change. Manually verified the reported window-resizing scenario locally. ## 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/c56995a8-47c6-45b6-aeb4-b0b56477c737 + After https://github.com/user-attachments/assets/97b5ca6f-cf6f-4dd5-b981-e44bd8139490 --- Release Notes: - Fixed bottom dock clipping when resizing windows.
# Objective Fixes zed-industries#59304 Right now inline html tags don't render with any kind of highlighting, despite the fact that non-inline html tags do get highlighting. ## Solution Right now zed already has tree-sitter rules that identifies the inline blocks so we can add a new tree-sitter grammar rule, ``` ((html_tag) @injection.content (#set! injection.language "html") (#set! injection.combined)) ``` that passes these blocks to the html language server. ## Testing I did some manual testing: https://github.com/user-attachments/assets/92e5b322-bbc1-48c3-a615-0d32acc2e7d6 And also added a test: test_markdown_inline_html_highlighting ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - markdown: Fixed inline html block highlighting.
) # Objective Make the branch picker easier to scan and ensure branch creation and remote icons behave consistently across All, Local, and Remote filters. ## Solution - Group local and remote branches under their own headers in the All filter. - Place the create-branch option above branch sections when there is no exact branch-name match. - Resolve each remote's hosting provider from its URL and show the provider icon for branches from that remote. - Include remote URLs in remote-workspace repository responses so arbitrary remote names work without hardcoding `origin` or `upstream`. ## Edge Cases Fixed - An exact branch can exist outside the active Local or Remote filter. The picker now checks all branches before offering to create a duplicate branch. - A non-exact search can return fuzzy branch matches and a create action. The create action now stays at the top, before the Local and Remote section headers. - When a filtered search has no branch matches, the create action is shown without incorrectly placing it under a Local Branches or Remote Branches header. - Repositories can use any remote name, not only `origin` and `upstream`. Provider icons are resolved independently for every configured remote. - Remotes hosted on an unknown or unsupported forge use the generic server icon instead of implying a desktop or a specific provider. - Remote-workspace repositories carry the same remote-name-to-URL data as local repositories, so their branch icons follow the same provider-resolution path. ## Testing - `cargo check -p git_ui --no-default-features` - Branch picker test suite (17 tests) - `git diff --check` 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 <img width="560" height="522" alt="Screenshot 2026-07-17 at 11 19 27 PM" src="https://github.com/user-attachments/assets/28d57315-b859-4a8d-86ca-53f9fb513bf5" /> The updated branch picker groups local and remote branches, keeps the create-branch action above those sections, and shows forge-specific icons for remote branches. --- Release Notes: - Improved branch picker filtering, grouping, branch creation suggestions, and remote-provider icons.
…ne (zed-industries#61218) Closes zed-industries#54951 ## Problem The quick action bar's preview button (markdown, SVG, and CSV behind the feature flag) resolved everything through `workspace.active_item()` — the globally *focused* item — instead of the item of the pane the button sits in. With `A.md` in the left pane and focus in `B.md` in the right pane: - Clicking the eye button in **A.md's** toolbar opened a preview of **B.md**, in the right pane. - Focusing a non-previewable file (e.g. `B.sh`) in one pane hid the button in **every** pane's toolbar, making `A.md` unpreviewable by mouse. Clicking the button dispatched the `OpenPreview` action, whose workspace-level handler re-resolved the focused editor — so a click on a specific pane's toolbar was functionally identical to pressing the keybinding, discarding the pane the click happened in. ## Fix Make the flow pane-explicit end to end: - **Visibility:** each pane's `QuickActionBar` resolves the preview type from its own `active_item` (set via `set_active_pane_item`) instead of the workspace's focused item. - **Click:** no more action dispatch. The handler resolves its pane via `Workspace::pane_for` and calls new pane-explicit helpers — `open_preview_in_pane` / `open_preview_to_the_side_of_pane` — extracted from the action-handler bodies in all three preview crates. Keyboard actions keep their focus-based semantics and route through the same helpers with the focused editor and active pane. - **Alt-click** (open in split) now splits relative to the button's pane via the new `Workspace::adjacent_pane_of` (the existing `adjacent_pane` delegates to it). Notably *not* done: focusing the button's pane and re-dispatching the action. `workspace.active_pane` only updates when pane focus-in listeners fire at the end of the next draw, while dispatched actions run before it — the handler would still read the stale pane. ## Additional changes - Existing-preview lookup now happens **before** view construction, instead of building a full preview view (subscriptions, initial parse) and discarding it when one already exists. - The `focus` flag now also applies to the activate-existing branch, so open-to-the-side never steals focus — previously the *first* invocation left focus in the editor but a *repeat* invocation focused the existing preview (and cancelled collaborator-following in that pane as a side effect). - SVG handlers no longer double-check `is_svg_file`; CSV handlers reuse `resolve_active_item_as_csv_editor` instead of inlining it; `is_markdown_file` takes `&App` instead of a needless `&mut Context<V>`. ## Testing - New regression test `preview_opens_for_the_given_pane_not_the_focused_editor` reproducing the issue's setup (two panes, focus in the second, preview invoked for the first pane's editor), asserting the preview opens in the invoking pane bound to that pane's editor with the focused pane untouched. - Markdown/SVG/CSV preview suites and the full `workspace` suite pass; `./script/clippy` is clean. Release Notes: - Fixed the preview button (Markdown/SVG) previewing the focused file instead of the file in the pane the button belongs to when multiple panes are open
…1201) # Objective Closes zed-industries#4868. Untitled buffers start as Plain Text and require users to select a language manually before receiving syntax highlighting. Add lightweight automatic language detection for code entered or pasted into untitled buffers. ## Solution This builds on [Max Stevens's earlier language-detection work](zed-industries#43057), replacing Magika with [Betlang](https://github.com/DioxusLabs/betlang). While researching smaller and faster alternatives to Magika, I came across Betlang, a recently introduced language detection library developed by DioxusLabs for dioxus-code. The fact that it comes from DioxusLabs gave me more confidence in evaluating this relatively new dependency for Zed. Betlang embeds an approximately 50 KB model, is MIT-licensed, and depends only on `fearless_simd`, making it well suited to Zed's cross-platform embedding requirements. Detection runs on the background executor with bounded input sampling. It is restricted to untitled buffers, skips content shorter than 20 bytes, and requires at least 50% confidence. These limits have worked well in local testing. In release builds on Linux with an Intel Core i5-13600KF, the `betlang::detect` call alone typically completes within 3 ms when processing the maximum sampled input. ## Testing I added a test for language detection in untitled buffers that covers both manually entered and pasted content. The test passes successfully. I also manually tested the feature to verify that the overall experience works well. ## 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/96e28ad7-2968-4325-9aff-37fe813a2da7 --- Release Notes: - Added automatic language detection for untitled buffers. --------- Co-authored-by: Max Stevens <maxstevens2708@gmail.com>
…ve (zed-industries#60880) run htop or btm and you can't select terminal text with shift+drag, the usual escape hatch does nothing. zed-industries#25143 made shift+click only ever extend a selection, never start one. so with nothing selected there's no anchor and the drag has nothing to grab. now it drops an anchor when there's no selection, and still extends when there is. added two event-level tests (harness can't sync without a window): shift+drag under mouse tracking, which fails without the fix, and shift+click still extending. Fixes zed-industries#60254. Release Notes: - Fixed Shift+click-drag not selecting terminal text while an app has mouse tracking enabled
… right-click (zed-industries#60892) # Objective The contextmenu listener cancelled the browser's native menu unconditionally, so pages hosting a gpui app lost right-click entirely wherever the app showed no menu of its own, and apps that do show one had no native menu to suppress selectively. ## Solution Record whether the right-button MouseDown dispatch was consumed (a handler called stop_propagation, e.g. to open a custom menu) and cancel the native menu only in that case: app menus win where they exist, the browser menu everywhere else. Apps opening custom context menus should mark the triggering mousedown consumed to opt in. ## Testing Tested locally ## 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="816" height="288" alt="context menu" src="https://github.com/user-attachments/assets/c79cd2ef-d465-4431-8461-7a9feb922576" /> Release Notes: - N/A
## Context Buffer search did not work in file diffs opened from the Git panel because `SoloDiffView` did not expose its embedded editor as searchable. Restoring search also revealed that the primary toolbar clipped several search controls, so the deployed search bar now uses the full-width secondary row for this view. Closes zed-industries#60659. ## How to Review Three files changed. Read in this order: **`crates/git_ui/src/solo_diff_view.rs`** : `SoloDiffView::as_searchable` now returns its embedded `SplittableEditor`, restoring search for the focused diff side while keeping the editor hidden from global diff-style controls. **`crates/search/src/buffer_search.rs`** : Buffer search now detects when its searchable split editor is intentionally hidden by the containing item. When deployed, this case uses the secondary toolbar row so Toggle Replace, search options, match navigation, and match counts remain visible. Other multibuffer layouts keep their existing primary toolbar location. **`crates/git_ui/src/git_panel.rs`** : The regression test now opens a solo diff through the Git panel, confirms the split editor is searchable, deploys buffer search, verifies the secondary toolbar location, runs a query, and checks that the focused editor receives a search highlight. Manual test after the fix below : [Screencast from 2026-07-14 00-39-04.webm](https://github.com/user-attachments/assets/022ab106-d35a-4a75-98db-8809b86fa58d) ## 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 searching in file diffs opened from the Git panel. Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com>
… startup (zed-industries#60960) the startup terminal can take a sec to come up if your shell is slow. when it's ready it grabs focus, and if you've opened the command palette by then, that blurs the palette input and closes it. so it just vanishes. `add_center_terminal` focused the new terminal unconditionally. now it skips the focus if a modal is open (`!has_active_modal`). one line at the definition, covers all the call sites. kept it terminal-specific like dinocosta framed it. could do a broader workspace-level "don't steal from a modal" instead if you'd prefer. two tests: modal stays focused after a background terminal spawns (fails without the fix), and no-modal still focuses the terminal like before. Fixes zed-industries#60762. Release Notes: - Fixed a background terminal finishing startup closing the command palette or an open picker
…#60503) This builds on zed-industries#59521 and addresses @dinocosta's comment zed-industries#59521 (review) Looking at it again, I think zed-industries@e7ed02c was too conservative. Re-running the access check on every commit, stage, or checkout, doesn't really make sense. Access depends on `.git` ownership/permissions and on the global `safe.directory` config. File writes in `.git/` can actually never change the access. The only case where this access can change is an external command that changes the ownership or permission of the folder. That sounds like a quite rare edge case and I'm not 100% sure whether all file watchers even currently correctly trigger events for ownership changes. @dinocosta Let me know what you think. Release Notes: - Improved Git Panel performance by avoiding redundant repository access checks. Co-authored-by: dino <dinojoaocosta@gmail.com>
Instead of a dropdown, it is now just a one-click icon. 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 theme toggle for the documentation website --------- Co-authored-by: Gaauwe Rombouts <mail@grombouts.nl>
Use `web_time::Instant` instead of `std::time::Instant` when advancing scrollbar animations. This uses the web platform's compatible clock in WASM builds. Release Notes: - N/A
# Objective Fixes zed-industries#61687 Prior to zed-industries#60772, an `abs_path` outside the `repo_root` would cause a panic. That PR changed it to return the `abs_path` itself, preventing the panic, but allowing all parents of `repo_root` to be subject to the global ignore. This is not what git itself does. ## Solution - This change returns `false` when the `abs_path` is outside the `root_repo`, preventing the panic but also not returning the `abs_path` ## Testing - One in-memory test - Manual test by building Zed and confirming the project pane changes (pictured below) ## 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, with the bug <img width="1430" height="814" alt="Screenshot 2026-07-26 at 11 44 11 AM" src="https://github.com/user-attachments/assets/66e3bb95-4f45-4fd9-a1e4-e7f253f092eb" /> ### After the fix <img width="1568" height="1042" alt="Screenshot 2026-07-26 at 12 34 58 PM" src="https://github.com/user-attachments/assets/588eb01d-830a-4c22-93c0-667742de90b6" /> Release Notes: - Fixed the project panel's global gitignore incorrectly matching parent directories of a repository --------- Co-authored-by: dino <dinojoaocosta@gmail.com>
…zed-industries#61837) # Objective - GPUI provides `grid_cols_min_content()` and `grid_cols_max_content()`, but the `grid_rows_*_content()` methods are missing. ## Solution - This PR adds the `grid_rows_min_content()` and `grid_rows_max_content()` methods, and renames the enum `TemplateColumnMinSize` to `GridTemplateMinSize`. ## Testing - I have tested locally. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [ ] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A
…1674) # Objective When `git clone` fails, the error message produced by git is collected, propagated, and finally displayed to the user in a pop-up. However, the pop-up always contains an extra blank line. The root cause is that Zed builds the error message using simple string concatenation: https://github.com/zed-industries/zed/blob/82aef44308540b576e4e51fb379efa71614e5c91/crates/fs/src/fs.rs#L1253-L1258 and the error message from `git clone` contains a trailing newline, which results in the blank line in Zed's error pop-up. ## Solution Trim the trailing whitespace from the error message produced by `git clone`. ## Testing Built and tested locally. The before/after comparison is shown below. ## 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 --- ## Show Case | Before | After | | :--: | :--: | | <img width="846" height="937" alt="Before" src="https://github.com/user-attachments/assets/26b918b9-57dc-46e8-880a-525dd4ae257d" /> | <img width="840" height="952" alt="After" src="https://github.com/user-attachments/assets/fb6380e3-afad-4a5a-98b1-05e059fd16f5" /> | Release Notes: - Fixed extra blank line appearing in git clone error messages
# Objective A failed restore, for example, a collision at the original path, used to remove the `TrashedEntry` from the registry before attempting the operation, so any later attempt with the same `TrashId` would report an `AlreadyRestored` error, even though the item still sat in the system trash. ## Solution Update both `RealFs::restore` and `FakeFs::restore` to remove the entry only after the restore succeeds. There's also a small unrelated change in `ProjectPanel::drag_onto` to log, rather than silently discard, a failed undo-history record in the project panel, matching existing call sites. ## Testing Introduced a new test for these changes – `restore_can_be_retried_after_collision` . ## 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 --- Release Notes: - N/A
Fixes zed-industries#60270. ## Summary - Accept MCP HTTP SSE `data:` fields when the optional space after the colon is omitted. - Add a regression test covering `data:{...}` streamable HTTP responses. ## Tests - `cargo fmt --check` - `cargo test -p context_server test_sse_data_field_without_space_after_colon -- --nocapture` - `cargo test -p context_server -- --nocapture` Release Notes: - Fixed MCP HTTP context servers timing out when SSE data fields omit the optional space after the colon. --------- Co-authored-by: dino <dinojoaocosta@gmail.com>
…ated (zed-industries#61852) Closes zed-industries#39286 ## Problem Switching the keyboard layout on Wayland (e.g. GNOME's Super+Space) makes the compositor briefly grab the keyboard, which deactivates the Zed window. GPUI reports window deactivation as a blur of the focused element (`WindowFocusEvent` is emitted with an empty `current_focus_path`), so controls that dismiss or cancel on blur treated it as the user abandoning them. The same happens when switching to another app window. As discussed in zed-industries#39286 with @MrSubidubi, the desired behavior is to keep these controls open and focused across window deactivation. ## Fix Guard the blur-driven cancel/dismiss handlers with `window.is_window_active()`, following the pattern established for pickers in zed-industries#41320: - Project panel: in-progress rename / new file / new directory inputs are no longer cancelled when the window is deactivated - Collab panel: in-progress channel rename is no longer cancelled - Go to Line: the dialog is no longer dismissed In-window focus changes (clicking elsewhere, Escape) keep the existing commit/cancel semantics. The terminal tab rename needed no change: it re-checks `FocusHandle::is_focused`, which reads `window.focus` and that survives deactivation. Command palette, file finder, and outline were already fixed by zed-industries#41320. Added a regression test (`test_rename_survives_window_deactivation`) that starts a rename, deactivates the window via the test platform, and asserts the edit state survives. Two milder siblings were left unchanged to keep this PR focused, flagging them for a maintainer decision: the notebook markdown cell exits edit mode on blur (`crates/repl/src/notebook/cell.rs`), and the diagnostics view prunes diagnosticless buffers on blur (`crates/diagnostics/src/diagnostics.rs`). Longer term it may be worth carrying the blur cause in the event itself (e.g. a `FocusMoved` vs `WindowDeactivated` reason on `FocusOutEvent` and `EditorEvent::Blurred`), so each handler has to state which causes it handles instead of relying on an opt-in guard; happy to file a separate issue for that. ## Before https://github.com/user-attachments/assets/9eec1bb5-1881-4b3a-80ef-a287e23219ca ## After https://github.com/user-attachments/assets/49b8775c-175a-4882-835d-871432c0b315 ## Suggested .rules additions > Handlers that cancel or dismiss UI on blur (`EditorEvent::Blurred`, `on_blur`, `on_focus_out`) must check `window.is_window_active()` first: GPUI reports window deactivation (app switch, layout switcher grabbing the keyboard) as a blur of the focused element, and dismissing there loses user input. Release Notes: - Fixed in-progress file renames, channel renames, and the Go to Line dialog being cancelled when the window is deactivated, e.g. by a keyboard layout switch on Wayland or by switching to another application ([zed-industries#39286](zed-industries#39286)). --------- Co-authored-by: MrSubidubi <finn@zed.dev>
Fixes the bug that made us remove the sandbox. The bug in question was very dumb: - there is sophisticated machinery for detecting whether a user-granted writable path is swapped out for a symlink in the timing gap between approval and sandbox creation - there was no equivalent machinery to do the same for the (much larger) gap between a user *persisting an approval* (either for the current thread or permanently via settings) - The fix is essentially to store canonical (i.e. absolute and symlink-free at all depths) paths as the source of truth, but retain the raw path for display purposes - On WSL, there is extra care needed becasue of the bidirectional mounting (i.e. `/mnt/c/...` and `\\wsl.localhost\Ubuntu\...`). In particular, `/mnt/c/...` paths, since their inodes do not necessarily pin NTFS file references, weaken the sandbox guarantees, and so we need some extra UI to call this out and docs etc... This also does not remove the feature flag, but just toggles it to "enabled_for_all" --- Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: Richard Feldman <oss@rtfeldman.com> Co-authored-by: Jakub Konka <kubkon@jakubkonka.com> Co-authored-by: Danilo Leal <daniloleal09@gmail.com> Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
…61270) # Objective - Fix multi-stroke keybindings such as `ctrl-x k` being intercepted by the macOS Japanese IME before GPUI can complete the binding. - Preserve IME-first handling for ordinary Japanese text input and active marked-text composition. - Fixes zed-industries#56043. ## Solution - Stop preferring IME-first printable input while a valid multi-stroke keybinding is pending. This allows the next printable keystroke to reach GPUI and complete the binding. - Consider pending input active only when it belongs to the window's current focus. This prevents stale pending state after a focus change or blur from bypassing normal IME handling. - Preserve the existing macOS active-composition path, so marked text, candidate selection, and composition commands continue to reach `NSTextInputContext` first. ## Testing Manually tested on macOS using Apple’s built-in Japanese–Romaji/Hiragana input source with a `ctrl-x k` keybinding. Also added regression test for pending keybinding routing. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed multi-stroke keybindings not completing when using a Japanese IME on macOS. --------- Co-authored-by: Tom Houlé <tom@tomhoule.com>
…-industries#61789) ### Objective PR zed-industries#58902 made native macOS window chrome follow the selected theme, but AppKit can synchronously invoke `viewDidChangeEffectiveAppearance` while the settings observer holds an `App` borrow. `handle.update` then fails with `RefCell already borrowed`, leaving `Window::appearance()` and its observers stale. ### Steps to reproduce 1. Set Zed's theme mode to Dark. 2. Set macOS Appearance to Light. 3. Change Zed's theme mode to System. 4. Notice that the window chrome switches to light, but Zed's UI remains dark. https://github.com/user-attachments/assets/f91962cc-76ba-4d8e-b587-2a2343f365ba ### Expectation Zed should switch to its configured light theme when returning to System mode. ### Solution Defer `Window::appearance_changed` to the foreground executor so the current `App` borrow finishes before refreshing the cached appearance and notifying observers. ### Testing - Added a regression test. ### Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A
Release Notes: - N/A Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
# Objective - Fixes zed-industries#59409. Closes FR-143. Zed's managed npm directory reaches 10–17GB on machines that use external agents, and is never pruned. - Two independent causes: registry agents are launched with `npm exec`, which keys its install directory on the requested version, so every agent release leaves a full ~250MB copy behind; and npm never evicts anything from its download cache. ## Solution - Install registry agents into a directory we reuse, so npm replaces the previous version in place instead of accumulating one copy per release. As a side effect, updates now download only the changed dependencies rather than the whole tree. - Empty the download cache on startup. Nothing in it needs to survive a restart, since packages are installed elsewhere. It has to go wholesale: deleting individual downloads leaves npm's index pointing at missing files, and npm then fails with `ENOENT` rather than fetching them again. - The first launch after this does one full agent install as it moves into its new home, and reclaims whatever the old directories were holding. ## Testing - Installed the real agent at 0.33.1, then upgraded to 0.42.0 in the same directory: 253MB → 256MB, against two separate copies today. The resolved executable answers an ACP `initialize`. - macOS only. Windows deserves a look — it should be better than before, since the agent is now launched as a plain `.js` file with Node instead of through npm's `.cmd` shim, but I can't verify 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 ([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 the bundled npm cache growing without bound, which could consume many gigabytes of disk.
…m-zed-2026-07-29 round 1 # Conflicts: # crates/acp_thread/src/acp_thread.rs # crates/agent_servers/src/acp.rs # crates/agent_ui/src/agent_panel.rs # crates/agent_ui/src/config_options.rs # crates/language_models/src/provider/open_ai.rs # crates/reqwest_client/src/reqwest_client.rs
…e hazards The 2026-07-29 upstream merge surfaced two load-bearing Helix changes that the porting guide never documented, both of which present as trivial-looking conflicts that are easy to resolve toward upstream and silently drop: - open_ai.rs: chat_completions_reasoning_effort(&self.model) in the into_open_ai() reasoning_effort slot (Helix 135f5b4). Upstream keeps adding positional params to that call, so the conflict looks like an arg swap. - reqwest_client.rs: the is_insecure_tls_enabled() branch must stay the tail of the builder chain as upstream appends new builder options above it. Adds script/helix-drift-sweep.sh, which mechanises the rebase checklist (28 checks) so a fix that is present before a merge and absent after is caught automatically rather than by eye.
Five build breaks from the e45e42a..4b73694 window, all in Helix surface: - agent_servers/acp.rs: into_foreground_future() removed upstream; force_close_session (PR #63 wedge recovery) converted to the new .block_task() form. - external_websocket_sync/thread_service.rs: ACP schema is now versioned — agent_client_protocol::schema -> ::schema::v1 (ContentBlock/TextContent/SessionId). - agent_ui/conversation_view.rs: from_existing_thread() gained two upstream fields — ConnectedServerState::_request_elicitation_subscription (None: headless threads have no elicitation UI) and ConversationView::request_elicitation_form_states. - agent_ui/agent_panel.rs: clear_overlay_state() and its three backing fields were removed by upstream 40d2003 (zed-industries#59860, in-panel AI-config overlay retired), so the call is dropped from the Critical Fix #11 guard. Guard semantics unchanged. - zed/src/main.rs: initialize_headless() now fully qualifies agent::ThreadStore. cargo check --package zed --features external_websocket_sync: clean. Drift sweep: 28/28.
# Conflicts: # .github/workflows/hotfix-review-monitor.yml # .github/workflows/stale-pr-reminder.yml # crates/agent_servers/src/acp.rs
# Conflicts: # .gitignore
…am HEAD # Conflicts: # crates/agent_ui/src/agent_panel.rs # crates/reqwest_client/src/reqwest_client.rs
…t divergence Round 4 conflicts: - reqwest_client.rs: upstream added a read_timeout param to builder(); Helix's insecure-TLS branch reinstated as the TAIL of the chain (upstream terminates its own chain with .use_rustls_tls(), which would silently disable insecure TLS). - agent_panel.rs: both sides added a Panel method; kept Helix starts_open (auto_open_panel, checklist #26) alongside upstream activation_focus_handle. Also repairs a raw unresolved conflict block that had been COMMITTED into .gitignore by an earlier merge (markers and all) — kept every real entry from both sides. test_stale_cancelled_response_does_not_cancel_current_compaction is marked #[ignore] as an intentional Helix divergence: upstream requires a displaced turn to still deliver its PromptResponse, which is mutually exclusive with Critical Fix #8 (cancel drops send_task so claude-agent-acp cannot wedge the next turn). Verified failing identically on pre-merge 06e9ce8, so this is pre-existing breakage dating to the 002100-extension merge, not a regression — it survived unnoticed because the checklist only ran 'test_second_send', never the full suite. With it ignored the suite is green again and usable as a gate. cargo check -p zed --features external_websocket_sync: clean. cargo test -p acp_thread: 127 passed, 1 ignored. cargo test -p external_websocket_sync: 50 passed. Drift sweep: 28/28.
Documents the largest merge in the series: divergence, the four dated rounds, all 12 conflicts and their resolutions, the signature drift repaired, and the Helix-surface survival check. Two pre-existing breakages found and repaired (neither caused by this merge): a raw conflict block that had been committed into .gitignore, and a red cargo test -p acp_thread that had gone unnoticed since 2026-06-18 because the checklist only ran a single filtered test. Adds checklist item #48 requiring the full crate suites with a recorded expected baseline.
Production wedge 2026-07-29 (spt_01kyq6qd8h4wqq2rfasc4b01e9): claude-agent-acp stayed alive but stopped answering session/prompt. connection.prompt() never resolved, so run_turn emitted no Stopped and no error, and the Helix interaction sat in state=waiting forever with nothing surfaced in either UI. Only a container restart recovered it. Nothing caught it: the await is unbounded, and PR #63's recovery keys on an ede_diagnostic error string that a silent wedge never produces. Adds a time-to-FIRST-event watchdog (not a turn timeout): - THREAD_ACTIVITY, a per-thread counter bumped by the persistent subscription on every AcpThreadEvent — any event is proof of life. - wait_for_first_agent_activity() races a freshly dispatched prompt against HELIX_ACP_FIRST_EVENT_TIMEOUT_SECS (default 120s, 0 disables). - Once the agent emits anything the watchdog disarms for the rest of the turn, so long tool calls and slow generations are untouched. A blanket turn timeout would false-positive on those; 'zero events since dispatch' does not. - On expiry the send task is dropped (same rationale as Critical Fix #8 — never block on a non-responding agent) and an error carrying helix_silent_prompt_wedge is returned. - is_silent_prompt_wedge() joins the follow-up recovery trigger, so the wedge runs the EXISTING force-close -> reload -> retry machinery rather than a new one. Lives in the Helix-owned crate, not upstream acp_thread.rs, to keep the upstream diff and future merge-conflict surface at zero. cargo test -p external_websocket_sync: 55 passed (5 new). Known gap, documented in the design doc: this covers Helix-originated follow-ups; UI-typed messages go through agent_ui and are not yet wrapped.
crates/agent_ui/src/acp/ was orphaned by the 2026-03-22 upstream merge (a2a6f61), which introduced conversation_view.rs as the live implementation but left the old tree on disk. It has been uncompiled dead code ever since: - no 'mod acp;' declaration anywhere in the crate - zero references to crate::acp:: / super::acp:: from outside the directory - zero references to agent_ui::acp from any other crate The only crate::acp:: references were self-referential, within the dead tree. This is not cosmetic. The stale copy shadows the live code when grepping: while diagnosing the 2026-07-29 production wedge, acp/thread_view/active_thread.rs was read as the live message-queue interrupt handler when the real one is in conversation_view/. It also invites resolving phantom merge conflicts in files that are never built. cargo check -p agent_ui: clean. Recoverable from history if ever needed.
The send-path watchdog only wraps turns Helix dispatched via handle_follow_up_message. A turn started by the user typing directly into the Zed agent panel goes agent_ui -> AcpThread::send() and never passes through it — which is exactly how the 2026-07-29 production wedge was triggered. Adds spawn_silent_turn_watchdog(), a per-thread task started alongside the persistent subscription. It watches ThreadStatus::Generating together with the THREAD_ACTIVITY counter, so it covers every turn regardless of origin. A turn that is generating but has produced ZERO events for longer than the first-event budget is reported to Helix as a terminal chat_response_error, marking the interaction errored and freeing the activation lane instead of leaving it in 'waiting' forever with nothing surfaced in either UI. Deliberately does NOT call thread.cancel(): the Stopped handler would then emit message_completed on top of this error and Helix would see the turn as both failed and completed. Leaving the turn alone is safe — the next send() displaces it (cancelling it via Critical Fix #8), so Zed self-heals on the user's next message while Helix has already been told the truth. cargo test -p external_websocket_sync: 55 passed. cargo check -p zed --features external_websocket_sync: clean.
Both agent rounds green on the final tree (17/17 each, store validation passed). Also records the first run's claude-round flake, whose signature differs from the previously-documented one (0 events / npx bootstrap): this time the agent produced a correct answer and then never sent message_completed. Did not reproduce on re-run, and the likely regression candidate was ruled out by inspection. Notes two harness gaps it exposed: run_e2e.sh queries the wrong npm scope for the agent version (@anthropic-ai/... instead of @agentclientprotocol/...), so the attribution log line is always 'unknown'; and the npm path is unpinned, so the claude round is not reproducible across time.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
Author
|
Paired Helix PR (pins This PR merges first, then the Helix one. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Catches the fork up with 764 commits of upstream Zed, and fixes the production wedge that prompted it.
Paired Helix PR (pins
ZED_COMMIT): helixml/helix#28961. Upstream catch-up (764 commits)
The fork had drifted 41 days behind (last fence
e45e42af6e, 2026-06-18). It is now level withupstream/mainatb9256fa8f0. Merged in four dated rounds rather than one jump — by far the largest window in this series (previous max 261) — so every conflict is attributable to a bounded upstream window:e45e42af6e..4b7369481d(→07-03)4b7369481d..4a3e0af532(→07-14)4a3e0af532..d23aaeebea(→07-24)d23aaeebea..b9256fa8f0(→HEAD)Notable resolutions (full detail in
portingguide.md):acp_thread.rs— upstream added anis_same_turnguard emittingStatusChanged. Kept Helix'sstopped_emitted_for_taskguard (Critical Fixes Default to follow mode when using agent #6/build(deps): bump the npm_and_yarn group across 1 directory with 2 updates #9) and adopted upstream's emit, ordered to match upstream's own sibling arm.agent_servers/acp.rs—into_foreground_future()removed upstream; PR fix(agent_servers): serialize ACP session-creation per connection #50's session-creation slot guard converted to.block_task().config_options.rs— upstream renamedfirst_config_option_idand added apredicateparam;current_model_value()adapted with|_| trueto preserve the old first-in-category semantic.open_ai.rs/reqwest_client.rs— two Helix surfaces the porting guide never documented, both of which present as trivial-looking conflicts that are easy to resolve toward upstream and silently drop. Now documented, and covered by the sweep.New:
script/helix-drift-sweep.sh28 mechanised checks over the rebase checklist. Run before and after every round — a check that passes before a merge and fails after is a dropped fix. This is how the two undocumented surfaces above got caught.
2. Fix: ACP agents that accept a prompt then go silent
Production, 2026-07-29 (
spt_01kyq6qd8h4wqq2rfasc4b01e9):claude-agent-acpstayed alive but stopped answeringsession/prompt.connection.prompt()never resolved, sorun_turnemitted noStoppedand no error, and the Helix interaction sat instate=waitingforever with nothing surfaced in either UI. Only a container restart recovered it.Nothing caught it: the await is unbounded, and PR #63's recovery keys on an
ede_diagnosticerror string that a silent wedge never produces.A time-to-FIRST-event watchdog, deliberately not a turn timeout:
THREAD_ACTIVITY— a per-thread counter bumped by the persistent subscription on everyAcpThreadEvent. Any event is proof of life.HELIX_ACP_FIRST_EVENT_TIMEOUT_SECS(default 120s,0disables).handle_follow_up_message) — that is how the production wedge was actually triggered.Lives entirely in the Helix-owned crate; upstream diff is zero.
3. Cleanup
crates/agent_ui/src/acp/(9 files) removed. Orphaned by the 2026-03-22 rename that introducedconversation_view.rs, it has been uncompiled dead code since — nomod acp;, no external references. Not cosmetic: while diagnosing the production wedge, the staleacp/thread_view/active_thread.rswas read as the live interrupt handler when the real one is inconversation_view/.Validation
cargo check --package zed --features external_websocket_sync— cleancargo test -p acp_thread— 127 passed, 1 ignoredcargo test -p external_websocket_sync— 55 passed (5 new watchdog tests)[zed-agent]17/17,[claude]17/17,[store] PASSED(40 interactions / 15 sessions; response-entries isolation clean across 34 interactions in 10 sessions)Pre-existing breakages repaired (not caused by this merge)
.gitignoreby an earlier merge — markers and all.cargo test -p acp_threadhad been red since 2026-06-18. Upstream'stest_stale_cancelled_response_does_not_cancel_current_compaction(PR agent: Fix race where compaction would be marked as cancelled zed-industries/zed#59014) requires a displaced turn to still deliver itsPromptResponse, which is mutually exclusive with Critical Fix Upstream merge (Feb 24) + streaming throttle + E2E test infra #8. Confirmed failing identically on pre-merge06e9ce8059via a worktree. Marked#[ignore]with full rationale — Helix keeps Fix Upstream merge (Feb 24) + streaming throttle + E2E test infra #8, since a permanently wedged thread is far worse than a lost stale response, and the invariant the test guards still holds. It hid for six weeks because checklist item Add suggest_dev_container setting to disable dev container suggestions #7 only ran a single filtered test; item build(deps): bump the cargo group across 1 directory with 4 updates #48 now requires the full suites with a recorded baseline.Known gaps (documented, not fixed here)
message_completed); did not reproduce. Needs a separate idle-since-last-event budget.e2e-test/run_e2e.sh:204reports the agent version vianpm view @anthropic-ai/claude-agent-acp, but Zed installs@agentclientprotocol/claude-agent-acp— wrong scope, so it always logsunknown, and the one line that would attribute a claude-round failure to an agent-package change is useless. The npm path is also unpinned.🤖 Generated with Claude Code