Fixes#42787
## Summary
- When "Go to Definition" navigates into a dependency (e.g. `.venv/`,
`node_modules/`), the save dialog for new files defaulted to that
directory
- `most_recent_active_path` now checks the `read_only_files` setting and
skips matching paths, falling back to the next eligible path, the
worktree root, or the home directory
## Design tradeoffs
We considered three approaches:
1. **Filter by `is_ignored`/`is_hidden`/`is_external` on worktree
entries** — catches `.venv` when gitignored or when it's a dotfile, but
also false-positives on directories like `.github/workflows/` that users
intentionally edit.
2. **Use preview tab status** — "Go to Definition" opens files as
preview tabs, so skipping preview paths targets the right intent. But it
doesn't work when preview tabs are disabled, and the signal is transient
(preview status changes as you interact with tabs).
3. **Use `read_only_files` setting** (this PR) — an explicit user
declaration of "I never want to edit files here." If you can't edit
them, you don't want to save new files next to them either. This is the
clearest signal of intent and respects user configuration. The tradeoff
is that `read_only_files` is empty by default, so users need to
configure it. But the kind of user bothered by the save dialog
defaulting to a dependency directory is the same kind of user who
already configures `read_only_files` (see
[#46827](https://github.com/zed-industries/zed/discussions/46827) for an
example).
## Test plan
- [x] Manual test: configured `read_only_files: ["**/.venv/**"]`, opened
project, Go to Definition into `.venv`, created new file — save dialog
defaults to project root
- [x] Added `test_most_recent_active_path_skips_read_only_paths`
- [x] All existing workspace tests pass
Release Notes:
- Fixed save dialog defaulting to dependency directories (e.g. `.venv/`,
`node_modules/`) after using Go to Definition, when those directories
are configured as `read_only_files`.
---------
Co-authored-by: Lukas Wirth <lukas@zed.dev>
Adds basic bookmark functionality to the editor, allowing users to mark
lines and later navigate between them. This is an MVP and will later be
expanded with a picker, vim marks integration and syntax tree based
bookmark positions. In this MVP bookmarks shift under external edits.
# UI
## Adding/Removing bookmarks
To add a bookmark:
- run the toggle bookmark action
- hold secondary and click in the gutter
- open the context menu by right clicking in the gutter and select add
bookmark To remove a bookmark:
- run the toggle bookmark action
- click on the bookmarks icon in the gutter
- open the context menu by right clicking in the gutter and select
remove bookmark
remove all bookmarks with `workspace: clear bookmarks`
# Implementation
This mirrors the implementation of breakpoints. The rendering of the
gutter was refactored to make place for bookmark icons and buttons:
- Code was extracted to a `Gutter` struct
- Runnables, breakpoints and bookmarks are now collected ahead of
layouting. Just before layouting we remove the items that collide and do
not have priority.
- The `phantom_breakpoint` is replaced by a `gutter_hover_button`
## In depth phantom breakpoint discussion:
This was phantom_breakpoint. It worked as follows:
- A fake breakpoint was added to the list of breakpoints.
- While rendering the breakpoints it a breakpoint turned out to be fake
it would get a different description and look.
- The breakpoint list was edited run_indicators ("play buttons")
rendering to removes the fake breakpoint if it collided.
This would not scale to more functionality. Now we only render
breakpoints, bookmarks and run indicators. Then we render a button if
there is not breakpoint, bookmark or run indicator already present. We
can do so since the rendering of such "gutter indicators" has been
refactored into two phases:
- collect the items.
- render them if no higher priority item collides.
This is far easier and more readable which enabled me to easily take the
phantom_breakpoint system and use it for placing bookmarks as well :)
Note: this was previously merged but it needed a better squashed commit
message. For the actual PR see: 51404. This reverts commit
1c3846e76f.
Release Notes:
- Added Bookmarks
Co-authored-by: Austin Cummings <me@austincummings.com>
Closes#4526
Adds basic bookmark functionality to the editor, allowing users to mark
lines and later navigate between them.
### What's new
**Toggling bookmarks**
Users can toggle a bookmark on the current line(s) via the `editor:
toggle bookmark` action. A bookmark icon appears in the gutter for each
bookmarked line.
**Navigation**
Two new actions, `editor: go to next bookmark` and `editor: go to
previous bookmark`, navigate between bookmarks in the current buffer,
wrapping around at the ends of the buffer.
**Viewing all bookmarks**
`editor: view bookmarks` opens all bookmarks across the project in a
multibuffer, similar to how references and diagnostics are surfaced.
**Clearing bookmarks**
`workspace: clear bookmarks` removes all bookmarks in the current
project.
**Persistence**
Bookmarks are persisted to the workspace database and restored when the
workspace is reopened. They are stored as `(path, row)` pairs and
resolved back to text anchors. Out of range or unresolvable bookmarks
are skipped with a logged warning.
**Gutter rendering**
Bookmark icons are rendered in the gutter using the existing gutter
button layout system, consistent with breakpoints. They are suppressed
on lines that already show a breakpoint or phantom breakpoint indicator.
A new `gutter.bookmarks` setting (defaulting to `true`) controls their
visibility.
### What's left
- [x] Lazily load buffers that have bookmarks
- [x] Clean up test boilerplate
- [ ] Assign default keybindings
- [ ] Compare line of saved bookmarks with current buffer (gray out the
"stale" bookmarks)
### What's next (and nice to haves)
- [ ] Resilience against external edits
- [ ] Save column position with the bookmark
- [ ] Bookmarks attached to syntactic structures?
- [ ] Labeled bookmarks?
---
Release Notes:
- Added bookmarks: toggle bookmarks on lines with `editor: toggle
bookmark`, navigate with `editor: go to next bookmark` / `editor: go to
previous bookmark`, view all bookmarks with `editor: view bookmarks`,
and clear with `workspace: clear bookmarks`. Bookmarks are shown in the
gutter and persisted across sessions.
---------
Co-authored-by: Yara <git@yara.blue>
Fixes a bug where we weren't properly recording the last active
workspace to power the cmd-click interaction in the sidebar's header.
This PR introduces a field in the sidebar struct for that, allowing to
store that value and to come back to it when clicking on the header,
which is the single caller of the function introduced here.
Release Notes:
- Agent: Fixed a bug where cmd-clicking on the project header wouldn't
actually take you to the last active workspace.
adds a 'git: view commit' – accepting a ref (e.g. HEAD, an sha, etc) to
more easily navigate to the git commit view.
<img width="3024" height="1888" alt="Screenshot 2025-09-26 at 21 43
09@2x"
src="https://github.com/user-attachments/assets/c001baec-66c2-46e5-b4a7-f691631f4166"
/>
if a bad ref is entered, the user is shown a generic error
<img width="2734" height="1442" alt="Screenshot 2025-09-27 at 21 04
52@2x"
src="https://github.com/user-attachments/assets/abdbd92d-ef0b-4de9-afb9-e9e52607dfdd"
/>
happy to adjust any of that. also worth noting is the `git: branch`
command UI is a bit nicer, can e.g. show you some metadata on the commit
before you select it, so happy to take it further in that direction if
desired, but thought i'd keep it simple to start.
Release Notes:
- Added view commit command palette action
---------
Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
Fixes an issue where we would only display the first path of the project
on the welcome page
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Fixed an issue where incorrect project paths would be displayed on the
welcome tab
This makes it so that `cmd-o` adds the project to the multi-workspace,
rather than opening a new window
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:
- N/A
- Adds a status toast to the announcement banner for surfacing the
layout revert option
- Removes the agent panel banner
A good chunk of the diff here was because I touched up the status toast
component API a little bit.
Release Notes:
- N/A
This fixes a but that @ConradIrwin noticed where the flexible docks
widths are weirdly dependent on the width of the active pane.
Release Notes:
- Fixed a bug where flexible docks resized incorrectly in certain cases.
- Fixed an issue where resizing a flexible-width panel in the left dock
would also resize fixed-width panels.
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:
- Added "New Center Terminal" button to window context menu
"New Terminal" only opens a new terminal window in the dedicated
terminal view, while "New Center Terminal" opens a new terminal in the
currently active view.
"+"/"New..." context menu
<img width="487" height="318" alt="Screenshot_20260411_184840"
src="https://github.com/user-attachments/assets/7c1597cf-5638-4d76-a486-537033cb8da5"
/>
Right click context menu
<img width="422" height="386" alt="Screenshot_20260411_184904"
src="https://github.com/user-attachments/assets/4e557e21-8839-4226-b704-87e37fbd8f46"
/>
Remove the `DEFAULT_THREADS_SHOWN` limit and the View More / Collapse
row from the sidebar thread list. When a project group is expanded, all
of its threads are now shown. Fold/unfold of project group headers is
preserved.
### Changes
- Remove `ListEntry::ViewMore` variant and `render_view_more`
- Remove `DEFAULT_THREADS_SHOWN`, `group_extra_batches`,
`set_group_visible_thread_count`, `expand/collapse_thread_group`
- Remove `visible_thread_count` from `ProjectGroupState`,
`SerializedProjectGroupState`, `ProjectGroup`, and persistence
- Remove `ShowMoreThreads` / `ShowFewerThreads` actions and vim
keybindings
cc @danilo-leal
Release Notes:
- N/A
Track new telemetry:
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:
- N/A
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
## Context
Closes#53252
When using `ToggleAll` to open the tab switcher and then closing the
last tab in the currently active pane via `CloseSelectedItem`, the tab
switcher would unexpectedly dismiss. Closing tabs from an inactive pane
worked correctly.
**Root cause:** `force_remove_pane` in `Workspace` unconditionally calls
`window.focus(fallback_pane)` when the active pane is removed. This
focus change causes the tab switcher picker's editor to receive a
`Blurred` event, which
triggers `Picker::cancel` → `delegate.dismissed` → `DismissEvent`,
dismissing the modal.
**Fix:** When a modal is active, skip the `window.focus` call and
instead call `set_active_pane` directly. This keeps the active pane
pointer up to date without stealing focus from the modal.
Video of manual test after fix :
[Screencast from 2026-04-07
00-24-56.webm](https://github.com/user-attachments/assets/eeb74313-1713-48db-8421-db740ef7a7b2)
## How to Review
- `crates/workspace/src/workspace.rs` : In `force_remove_pane`, when the
removed pane was the active pane, the fallback pane now only receives
focus if no modal is currently open. Otherwise, `set_active_pane` is
called directly, which updates `active_pane`, `last_active_center_pane`,
and the status bar without touching window focus.
- `crates/tab_switcher/src/tab_switcher_tests.rs` : New test
`test_toggle_all_stays_open_after_closing_last_tab_in_active_pane`
reproduces the issue: two panes each with one file, the active pane's
tab is closed via `CloseSelectedItem`, and the test asserts the tab
switcher remains open with the other file still listed.
## 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 tab switcher dismissing when closing the last tab in the active
pane
This fixes a regression where `zed -n .` in a subdirectory of an
already-open
project would redirect to the parent window instead of creating a new
one.
The root cause was that commit b3566fcf55 ("Adjust `zed -n` behavior")
made
`-n` run the worktree matching loop with subdirectory matching enabled,
when
previously `-n` skipped matching entirely.
## Changes
### Bug fix
- **Restore `-n` to always create a new window.** No worktree matching,
no
exceptions. This matches the behavior from when `-n` was first
introduced.
### New `--classic` flag
- Adds a hidden `--classic` CLI flag that explicitly selects the
pre-sidebar
default behavior: new window for directories, reuse existing window for
files already in an open worktree.
- The `cli_default_open_behavior` setting now toggles between `-e` (add
to
sidebar) and `--classic` behavior. When set to `new_window`, the classic
logic is used instead of unconditionally opening a new window.
### Refactor CLI open options
Replaces the old grab-bag of `open_new_workspace: Option<bool>`,
`force_existing_window: bool`, `classic: bool`, and `reuse: bool` with:
- **`cli::CliOpenBehavior` enum** — a single enum on the IPC boundary
with
variants `Default`, `AlwaysNew`, `Add`, `ExistingWindow`, `Classic`, and
`Reuse`.
- **`workspace::WorkspaceMatching` enum** — describes how to match paths
against existing worktrees (`None`, `MatchExact`, `MatchSubdirectory`).
- **`workspace::OpenOptions`** — uses `WorkspaceMatching` plus a simple
`add_dirs_to_sidebar: bool` instead of overlapping boolean flags.
The translation from CLI enum to workspace options happens in
`open_listener.rs`, keeping both layers clean and independent.
Release Notes:
- N/A
Closes https://github.com/zed-industries/zed/issues/53262
- Remove the ability to pick a branch from the agent panel; delegate
this to the title bar picker
- Make the worktree creation earger, just as you selected whether you
want to create it from main or current branch
- Remove flicker when creating a new worktree and switching to a
previously existing one
- Improve some UI stuff: how we display that a worktree is
creating/loading, the branch and worktree icons, etc.
- Fixed a bug where worktrees in a detached HEAD state wouldn't show up
in the worktree pickers
A big part of the diff of this PR is the removal of everything involved
with the `StartThreadIn` enum/the set up involved in only creating the
worktree by the time of the first prompt send.
Release Notes:
- Agent: Improved and simplified the UX of creating threads in Git
worktrees.
- Git: Fixed a bug where worktrees in a detached HEAD state wouldn't
show up in the worktree picker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Remove the ability to add and remove individual folders from project
groups in the sidebar.
## Changes
**Sidebar project group menu:**
- When there are 2+ local project groups, the ellipsis menu shows "Open
Project in New Window" and "Remove Project" (no separators, no folder
listing)
- When there's only one group or it's remote, the ellipsis menu is
replaced with a close X button that removes the project directly
**Project picker labels:**
- "Open Local Project" → "Open Local Folders"
- "Open Remote Project" → "Open Remote Folder"
- "Add Local Project" → "Add Local Folders"
- "Add Remote Project" → "Add Remote Folder"
**Dead code removal:**
- `MultiWorkspace::remove_folder_from_project_group`
- `MultiWorkspace::prompt_to_add_folders_to_project_group`
- `MultiWorkspace::add_folders_to_project_group`
- `MultiWorkspace::update_project_group_key`
- `MultiWorkspaceEvent::ProjectGroupKeyUpdated`
- `Sidebar::move_threads_for_key_change`
- `ThreadMetadataStore::change_worktree_paths_by_main`
- Associated tests for removed functionality
Per-workspace thread regrouping (when a workspace's worktree paths
change) continues to work via the existing `WorktreePathsChanged` →
`move_thread_paths` flow. The existing
`test_worktree_add_only_regroups_threads_for_changed_workspace` test
(renamed and strengthened) verifies that only the changed workspace's
threads are regrouped, while sibling worktree threads remain in their
original group.
Release Notes:
- Improved sidebar project group menu to show a simple close button for
single-project windows and a streamlined two-item menu for multi-project
windows.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
### Summary
Follow up on: https://github.com/zed-industries/zed/pull/53550
Threads in the sidebar are now filtered by remote connection in addition
to path list. Previously, two different machines with the same absolute
project paths (e.g. both at `/home/user/project`) would have their
threads merged into a single sidebar group. Now each remote host gets
its own group.
To support this, this PR introduces `RemoteConnectionIdentity`, a
normalized subset of `RemoteConnectionOptions` that includes only the
fields relevant for identity matching (e.g. SSH host + username + port,
but not nickname or connection timeout). All remote host comparisons in
the sidebar, thread metadata store, and workspace persistence now go
through `same_remote_connection_identity()` instead of raw `==` on
`RemoteConnectionOptions`.
#### Follow Up on remoting for sidebar
1. Make the neighbor in archive thread fallback to the main git worktree
instead of the next thread
2. Start implementing remote git operations that archive feature depends
on
3. Make remote connection pool equality use `RemoteConnectionIdentity`
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
---------
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
### Summary
This PR is the first step in improving Zed's error handling when a user
opens an old thread that's associated with a deleted git worktree.
Before, the thread would open in an empty project with a broken state.
This PR instead opens the thread in the workspace associated with the
thread’s main git worktree when available.
### Follow ups
1. Implement remote support for this fallback
2. Update `ThreadMetadataStore` database to set paths from deleted
worktree to the main worktree
3. If the main git worktree is deleted as well, fallback to the
currently active 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 is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A
## Context
When using a bare-repo-based git worktree layout (e.g. `foo/.bare` as
the bare repository and `foo/my-feature` as a linked worktree), the
"recent projects" modal was showing `foo/.bare` instead of
`foo/my-feature`.
The root cause was in `original_repo_path_from_common_dir` — when
resolving a linked worktree back to its "main" repo, the function reads
the `commondir` file which, for bare repos, points to the bare directory
itself (e.g. `foo/.bare`). Since that path doesn't end in `.git`, the
old code fell back to returning it as-is. This bare repo path was then
substituted into the workspace's stored paths, causing the modal to show
the bare directory instead of the actual worktree the user had opened.
The fix makes `original_repo_path_from_common_dir` return
`Option<PathBuf>`, returning `None` when `common_dir` doesn't end with
`.git`. `resolve_git_worktree_to_main_repo` propagates this `None`,
meaning the original worktree path is preserved in recent projects
rather than being replaced with the bare repo path.
Closes#52931
Video of the manual test after the fix is below :
[Screencast from 2026-04-02
16-05-48.webm](https://github.com/user-attachments/assets/9659c7a7-c095-4c23-af59-17715f84ce3e)
## How to Review
- **`crates/git/src/repository.rs`** :
`original_repo_path_from_common_dir` changed to return
`Option<PathBuf>`. Returns `None` for bare repos (no `.git` suffix). The
existing `original_repo_path` call site falls back to `work_directory`
when `None` is returned, preserving its prior behaviour. Unit test
expectations updated accordingly, with the bare-repo case now asserting
`None`.
- **`crates/project/src/git_store.rs`** :
`resolve_git_worktree_to_main_repo` now simply forwards the
`Option<PathBuf>` returned by `original_repo_path_from_common_dir`
directly, propagating `None` for bare repos so the caller keeps the
original worktree path.
- **`crates/workspace/src/persistence.rs`** : New test
`test_resolve_worktree_workspaces_bare_repo` exercises the exact
scenario from the issue: a workspace entry pointing to a linked worktree
whose `commondir` resolves to a bare repo. Asserts the path is left
unchanged.
## 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 recent projects modal showing `.bare` folder instead of the
worktree directory for bare-repo-based git worktree setups
Previously, the sidebar would automatically create an empty draft thread
for any project group that had no threads (via `reconcile_groups`). This
added complexity and felt janky — the user would see a phantom "Draft"
entry appear for every workspace they opened, even before they intended
to start a conversation.
Now, project groups can simply be empty. When a group has no threads:
- The fold indicator renders as closed and grayed out (disabled — no
click handler or tooltip)
- The plus button is always visible on the right side of the header (not
hidden behind hover)
- Keyboard fold toggling is disabled for the group
- Clicking the plus button creates the first thread and expands the
group
Draft threads are no longer auto-created anywhere:
- `AgentPanel::load` no longer creates a draft when there is no
previously active conversation
- Archiving the last thread in a group clears the panel instead of
creating a replacement draft
- `show_or_create_empty_draft` and `clear_active_thread` are removed
This removes a significant amount of supporting infrastructure that only
existed to maintain the "no group may be empty" invariant:
- `WorkspaceSidebarDelegate` trait and `AgentPanelSidebarDelegate`
struct
- `reconcile_groups` method and its 12 call sites
- `clear_empty_group_drafts` method and its 3 call sites
- `show_or_create_empty_draft` and `clear_active_thread` methods
- `reconciling` field on `Sidebar`
- `sidebar_delegate` field, setter, and getter on `Workspace`
Release Notes:
- Improved the sidebar to show empty project groups with a visible plus
button instead of auto-creating placeholder draft threads.
---------
Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Based on a partial revert of: 42351ee103
The main difference is instead of a feature flag we now check
`ReleaseChannel::Stable` != current_release_channel when the UI checks
if agent v2 (sidebar) features should be enabled.
Self-Review Checklist:
- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A or Added/Fixed/Improved ...
---------
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Update the way `pane::RevealInProjectPanel` is handled to ensure that,
regardless of whether the file belongs to any open project, the project
panel is always activated and focused.
This refactor is a result of some internal feedback after changing its
handling so as to show a notification stating that the item that the
user was trying to reveal didn't belong to an open project
– https://github.com/zed-industries/zed/pull/51246 .
We feel users are probably already used to relying on `cmd-shift-e` (on
macOS), in pretty much every context, in order to open the project
panel, and so having situations where it doesn't actually happen seems
like a bad user experience.
Relates to #23967
Release Notes:
- Improved `pane: reveal in project panel` to open the project panel,
even if working with an unsaved buffer.
When removing a project group, the fallback closure calls
`find_or_create_local_workspace` which searches `self.workspaces` for an
existing match. Because the workspaces to be removed haven't been
removed yet at that point, `workspace_for_paths` can return a doomed
workspace, hitting the `assert!` in `MultiWorkspace::remove` and
crashing the app.
Fix by adding an `excluding` parameter to
`find_or_create_local_workspace` so callers inside removal fallbacks can
skip workspaces that are about to be removed. Both
`remove_project_group` and `sidebar::archive_thread` now pass the
appropriate exclusion set.
Adds a regression test that deterministically reproduces the crash (the
assert fires without the exclusion, passes with it).
Release Notes:
- Fixed a crash when closing a project group whose fallback workspace
matched one being removed.
Re-adds the `multi_workspace::MoveProjectToNewWindow` action
It serializes, then closes, then re-opens the project to avoid issues
with pending tasks
Release Notes:
- N/A or Added/Fixed/Improved ...
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A
> **Foundation PRs:** #53732, #53733, #53734 — These three draft PRs
contain the base refactors (retained workspaces, agent panel overlay
split, ThreadId introduction) that this PR builds on.
## Goal
Remove `DraftId` and merge `draft_threads` + `background_threads` into a
single `retained_threads: HashMap<ThreadId, Entity<ConversationView>>`
in `AgentPanel`. A draft is just a thread that hasn't sent its first
message — no separate identity or storage needed.
## Changes
### agent_panel.rs
- Remove `DraftId` / `DraftIdCounter` / the `Global` impl
- Merge the two maps into `retained_threads`
- Add `thread_id: ThreadId` field to `BaseView::AgentThread`
- Rename methods: `create_draft` → `create_thread`, `activate_draft` →
`activate_retained_thread`, `remove_draft` → `remove_thread`, etc.
- Replace `clear_active_thread` with `show_or_create_empty_draft`
- Update `update_thread_work_dirs` to sync `ThreadMetadataStore` paths
when worktrees change
- Keep `load_agent_thread(...)` cleanup so activating a real thread
removes empty retained drafts
### sidebar.rs
- Remove `active_entry` derivation from `rebuild_contents` (was racing
with deferred effects)
- Add `sync_active_entry_from_panel` called from event handlers instead
- Simplify `ActiveEntry` — remove `ThreadActivation` struct, make
`session_id` optional
- Move `seen_thread_ids` to global scope (was per-group, causing
duplicate thread entries)
- Remove dead code: `clear_draft`, `render_draft_thread`
- Generalize `pending_remote_thread_activation` into
`pending_thread_activation` so all persisted-thread activations suppress
fallback draft reconciliation
- Set the activation guard before local persisted-thread activation
switches workspaces
- Make `reconcile_groups(...)` bail while a persisted-thread activation
is in flight
- On `ActiveViewChanged`, clear empty group drafts as soon as a pending
persisted-thread activation resolves
### thread_metadata_store.rs
- Make `ThreadMetadata.title` an `Option<SharedString>` with
`display_title()` fallback
- Add `update_worktree_paths` for batched path updates when project
worktrees change
### Other crates
- Update all `ThreadMetadata` construction sites and title display sites
across `agent_ui` and `sidebar`
## Fallback draft invariant
This PR now tightens the retained-thread invariant around fallback
drafts vs real thread activation/restoration:
- Fallback drafts are always empty
- User-created drafts worth preserving are non-empty
- While a persisted thread is being activated/restored/loaded, sidebar
reconciliation must not create an empty fallback draft for that target
group/workspace
- Once the real thread becomes active, empty fallback drafts in that
target group are removed
This is enforced by the sidebar-side activation guard plus existing
`AgentPanel` empty-draft cleanup after real-thread load.
## Tests
Added and/or kept focused sidebar regression coverage for:
-
`test_confirm_on_historical_thread_in_new_project_group_opens_real_thread`
-
`test_unarchive_into_inactive_existing_workspace_does_not_leave_active_draft`
-
`test_unarchive_after_removing_parent_project_group_restores_real_thread`
- `test_pending_thread_activation_suppresses_reconcile_draft_creation`
Focused test runs:
- `cargo test -p sidebar activate_archived_thread -- --nocapture`
- `cargo test -p sidebar unarchive -- --nocapture`
- `cargo test -p sidebar archive_last_thread_on_linked_worktree --
--nocapture`
- `cargo test -p sidebar
test_confirm_on_historical_thread_in_new_project_group_opens_real_thread
-- --nocapture`
- `cargo test -p sidebar
test_unarchive_into_inactive_existing_workspace_does_not_leave_active_draft
-- --nocapture`
- `cargo test -p sidebar
test_unarchive_after_removing_parent_project_group_restores_real_thread
-- --nocapture`
- `cargo test -p sidebar
test_pending_thread_activation_suppresses_reconcile_draft_creation --
--nocapture`
## Test fix
Fixed flaky `test_backfill_sets_kvp_flag` — added per-App `AppDatabase`
isolation in `setup_backfill_test` so backfill tests no longer share a
static in-memory DB.
Release Notes:
- N/A
---------
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
A note-worthy thing I'm adding in this PR to fix an agent panel problem
is a trait method allowing to set a min-width in panels. I think some of
them—if not all—could benefit from it, because there is a certain width
these panels can get in that we just can't ensure they will work; things
will break. So we either accept that and let the user's common sense
understand that, or we don't allow them to reach that state... Surely, a
small enough _window_ size can still break things, but then it's out of
the realms of solution...
Release Notes:
- Agent: Fixed multi-line queued messages getting cut-off when the agent
panel is in full screen.
- Agent: Fixed agent panel getting auto-closed after submitting a queued
message when the panel is in full screen.
- Agent: Added a min-width to the agent panel, preventing it from
reaching a small enough width where it would be essentially unusable.
This PR adds the `cli_default_open_behavior` setting and a first-run TUI
prompt
that appears when `zed <path>` is invoked without flags while existing
windows are
open and the setting hasn't been configured yet.
## What it does
### Setting and prompt
- Adds a new `cli_default_open_behavior` workspace setting with two
values:
`existing_window` (default) and `new_window`.
- When the user runs `zed <path>` for the first time with existing Zed
windows
open, a `dialoguer::Select` prompt in the CLI asks them to choose their
preferred behavior. The choice is persisted to `settings.json`.
- The prompt is skipped when:
- An explicit flag (`-n`, `-e`, `-a`) is given
- No existing Zed windows are open
- The setting is already configured in `settings.json`
- The paths being opened are already contained in an existing workspace
### IPC transport abstraction
- Introduces a `CliResponseSink` trait in the `cli` crate that abstracts
`IpcSender<CliResponse>`, with an implementation for the real IPC
sender.
- Replaces `IpcSender<CliResponse>` with `Box<dyn CliResponseSink>` /
`&dyn CliResponseSink` across all signatures in `open_listener.rs`:
`OpenRequestKind::CliConnection`, `handle_cli_connection`,
`maybe_prompt_open_behavior`, `open_workspaces`, `open_local_workspace`.
- Extracts the inline CLI response loop from `main.rs` into a testable
`cli::run_cli_response_loop` function.
- Switches the request channel from bounded `mpsc::channel(16)` to
`mpsc::unbounded()`, eliminating `smol::block_on` in the bridge thread.
### End-to-end tests
Seven new tests exercise both the CLI-side response loop and the
Zed-side
handler connected through in-memory channels, using `allow_parking()` so
the
real `cli::run_cli_response_loop` runs on an OS thread while the GPUI
executor
drives the Zed handler:
- No flags, no windows → no prompt, opens new window
- No flags, existing windows, user picks "existing window" → prompt,
setting persisted
- No flags, existing windows, user picks "new window" → prompt, setting
persisted
- Setting already configured → no prompt
- Paths already in existing workspace → no prompt
- Explicit `-e` flag → no prompt
- Explicit `-n` flag → no prompt
Existing tests that previously used `ipc::channel()` now use a
`DiscardResponseSink`, removing OS-level IPC from all tests.
Release Notes:
- Added a first-run prompt when using `zed <path>` to choose between
opening
in an existing window or a new window. The choice is saved to settings
and
can be changed later via the `cli_default_open_behavior` setting.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Fixes a regression introduced in
https://github.com/zed-industries/zed/issues/49603
Supersedes https://github.com/zed-industries/zed/pull/52638
We want to leave these non-existent workspaces in the database for up to
7 days, in case they are on external drives that are restored later, but
we do *not* want to show them in the UI if they don't exist.
Release Notes:
- Fixed an issue where deleted folders appeared in the recent project
picker
---------
Co-authored-by: ojpro <contact@ojpro.me>
This changes the behavior of the recent project picker in multi-folder
projects, so that when you select a folder that is a repository, it sets
the active repository in the Git panel as well.
Release Notes:
- N/A
Continuation of https://github.com/zed-industries/zed/pull/53566, now
with proper thread root mutation.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A
---------
Co-authored-by: Eric Holk <eric@zed.dev>
Disambiguate project names in the sidebar (and project picker) so that
we don't show e.g. `zed, zed` but rather `foo/zed, bar/zed` if the last
path component is the same but they are different absolute paths.
Release Notes:
- N/A
This PR greatly improves our handling of remote threads in the sidebar.
One primary issue was that many parts of the sidebar were only looking
at a thread's path list and not its remote connection information. The
fix here is to use `ProjectGroupKey` more consistently throughout the
sidebar which also includes remote connection information.
The second major change is to extend the MultiWorkspace with the ability
to initiate the creation of remote workspaces when needed. This involved
refactoring a lot of our remote workspace creation paths to share a
single code path for better consistency.
Release Notes:
- (Preview only) Fixed remote project threads appearing as a separate
local project in the sidebar
---------
Co-authored-by: Anthony Eid <anthony@zed.dev>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
- Hopefully, making the "Try Now" button within the announcement toast
effectively get the layout switched to agent
- Don't dismiss the announcement toast when clicking on "Learn More"
- Add a Learn More button in the agent panel, too
- Hide the "Collab with Agents" card in the launchpad page
Release Notes:
- N/A
Connect the git API and archived worktree data model to the sidebar's
archive/unarchive flow:
- Add `thread_worktree_archive` module: orchestrates the full archive
cycle (WIP commits, DB records, git refs, worktree deletion) and restore
cycle (detached worktree creation, reset to recover staged/unstaged
state, branch restoration)
- Integrate into sidebar: `archive_thread` now persists worktree state
before cleanup; `activate_archived_thread` restores worktrees via git
with targeted path replacement for multi-root threads
- Add pending worktree restore UI state with spinner and cancel button
- Show toast on restore failure instead of silent log
- Switch to surviving workspace when archiving active thread whose
workspace will be deleted
- Deserialize persisted `project_group_keys` on window restore
- Guard `cleanup_empty_workspaces` against dropped entities
- Await rollback DB operations instead of fire-and-forget
Part 3 of 3 in the persist-worktree stack. Stacked on #53214. This wires
up parts 1 and 2.
Release Notes:
- Added worktree state preservation when archiving agent threads,
automatically restoring staged and unstaged changes when the thread is
unarchived
---------
Co-authored-by: Anthony Eid <anthony@zed.dev>
A couple of restore project group/workspaces functions weren't filtering
out project groups with empty paths, leading to an empty being shown in
the recent projects picker. This is the problem this PR solves:
<img width="400" height="1042" alt="Screenshot 2026-04-08 at 11 06@2x"
src="https://github.com/user-attachments/assets/4d96cc53-5b03-445c-968b-8a8edec559da"
/>
Release Notes:
- N/A
Follow up to https://github.com/zed-industries/zed/pull/53283. That PR
replaced _where_ focus goes — from "always the center pane" to "the
saved element." But it didn't fix _when_ focus moves — it still moves
every time the sidebar closes, unconditionally. The problem is the saved
focus can be wrong... it's saved when the sidebar opens, but then it can
go to somewhere else after. An example:
1. Open sidebar → save focus (center pane), focus sidebar
2. Open agent panel from sidebar → focus moves to agent panel
3. Close sidebar → restore saved focus → **focuses center pane** (that's
what was saved in step 1, not the agent panel)
Effectively, this would still cause the agent panel to auto-dismiss.
This PR fixes it by no moving focus at all if the sidebar doesn't have
it. If the user already moved focus to the agent panel, there's nothing
to "restore".
Release Notes:
- N/A
This PR adjusts how project group removal works in the sidebar to be
project group aware. Removal is now a batched operation, and supports a
contextually appropriate fallback active workspace fallback. In the
process of removing this, I had to chase down and fix other broken code:
- Removes the "move to new window" actions
- Changed the sidebar to store things in "most recently added" order
- The recent project UI was still using workspaces, instead of project
groups
- Adjusted the archive command to use the new APIs to remove the
workspace associated with that worktree (cc: @rtfeldman)
- The property tests where still using indexes and workspaces instead of
project groups
Self-Review Checklist:
- [ ] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A