Skip to content

agent_ui: Add thread search to the agent panel#54816

Closed
Tahinli wants to merge 12 commits into
zed-industries:mainfrom
Tahinli:agent-panel-search
Closed

agent_ui: Add thread search to the agent panel#54816
Tahinli wants to merge 12 commits into
zed-industries:mainfrom
Tahinli:agent-panel-search

Conversation

@Tahinli

@Tahinli Tahinli commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

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
  • Tests cover the new/changed behavior
  • Performance impact has been considered and is acceptable

Closes #ISSUE #39338 partially

Release Notes:

Adds in-thread search to the agent panel: Cmd/Ctrl+F (and / in vim mode) deploys a BufferSearchBar over the active thread, matches the query against assistant messages, user messages, tool-call labels, plan entries, and terminal commands, and highlights the matches inline. The bar's existing "next / previous / case / whole-word / regex" controls work unchanged; an "Include Hidden Context" toggle extends search into collapsed tool calls and thinking blocks on demand, auto-expanding only what the match lives inside and collapsing again on dismiss.

2026-04-24.21-11-04.mp4

Tahinli added 12 commits April 24, 2026 20:41
Adds the Item and SearchableItem trait impls needed to host a
BufferSearchBar targeting the agent panel's thread view. Matches are
keyed by (entry_index, byte_range) against each entry's markdown
rendering, and the active match's byte position is tracked so that
updating the query re-anchors to the nearest remaining match.

No UI is wired yet; the bar deployment and rendering come in the next
commit.
Wires the standard search::BufferSearchBar into ThreadView: the bar is
lazily constructed the first time buffer_search::Deploy fires, bound to
the thread view as its searchable target, and rendered inline above the
entry list when visible.

DivRegistrar routes the rest of the buffer_search actions (next/prev,
toggle case, etc.) to the bar so they keep working when focus is inside
the conversation area rather than the query editor.
Pushes search matches into each entry's Markdown entities so the actual
matched text is highlighted inline, not just the containing entry.

find_matches walks UserMessage content, each AssistantMessage chunk,
ToolCall labels + content-block outputs, and plan entries, searching
each Markdown entity's source independently. Matches carry the owning
Markdown handle so update_matches can group per-entity and call
set_search_highlights once per markdown. The active match is activated
on its own markdown with its per-markdown local index while active
highlights on the previously active markdown are cleared, mirroring how
markdown_preview_view wires up its own search.

Tracks the set of currently-highlighted markdowns on ThreadView so
clear_matches and shrinking match sets can wipe the right entities.
Binds the standard buffer-search keys to the new in-thread search:
Ctrl+F / the Find key on Linux & Windows, Cmd+F on macOS, and '/' when
vim mode is active inside the thread view's editor.

Renames test_vim_search_does_not_steal_focus_from_agent_panel to
test_vim_slash_deploys_thread_search_not_center_pane to reflect the new
expected behavior — pressing '/' now deploys the thread's own search bar
instead of silently being swallowed, while still not leaking through to
the center pane's BufferSearchBar.
WrappedLineLayout::position_for_index treated an index equal to a
soft-wrap boundary as still belonging to the preceding visual line
(returning end-of-prev-line instead of start-of-next-line). For callers
painting selections or search highlights, this made the reported
start.y of a highlight land one line above where the character is
actually rendered — which in turn caused Markdown's paint_highlight_range
to take its multi-line branch and paint a spurious rectangle from that
phantom start to the element's right edge whenever a match began at a
wrap boundary.

For wrap boundaries the index belongs to the next line; keep the
previous strict comparison only for the final synthesized entry whose
"line_end_ix" equals self.len() and represents the end-of-text cursor.
Adds a new SearchOptions flag plus supporting UI plumbing so a
SearchableItem can opt in to exposing an "include hidden context"
toggle in the buffer search bar. The option renders as an eye icon
next to Case / Word / Regex when supported_options().include_hidden is
true, and otherwise stays hidden for items that don't need it.

The flag is read back by callers via BufferSearchBar::search_options().

Release Notes:

- N/A
The thread search bar now exposes the new "Include Hidden Context"
eye toggle. When it's off, search only indexes visible content so the
match counter matches what the user sees on screen. When it's on,
search indexes thought chunks and tool-call bodies regardless of
expansion, and activating a match inside a collapsed disclosure auto-
expands just that section so the highlight is visible. Auto-expanded
sections are collapsed back when the toggle flips off or the search
bar is dismissed, skipping any thinking blocks the user had expanded
themselves.

Release Notes:

- Added an "Include Hidden Context" toggle to the agent thread search bar that, when enabled, searches thought blocks and collapsed tool-call output and auto-expands the matching section when navigating into it.
Toggling Include Hidden may reveal matches that live inside content
that was previously collapsed (e.g. agent thread tool calls and
thought blocks). Other searchable items only fold/expand when the
active match is activated, so without this the counter ticks up
without the user seeing anything new. Await the resulting search
and call `activate_current_match` when it completes; mirror the
pattern from the query-edit handler. Narrowing toggles (case
sensitive, whole word, regex) leave the cursor alone.
Thread search was collecting matches from `UserMessage::content` — a
markdown entity used for serialization that is not rendered on screen.
The `MessageEditor` that actually renders each past user message never
received the highlights, so matches inside user messages were counted
but invisible.

Introduce a `MatchTarget` enum (`Markdown` or `Editor`) on each match
and a `MatchOrigin::UserMessage` variant. For user messages, search the
inner `Editor`'s buffer text directly — its text can diverge from the
markdown source for image chunks, where the markdown renders `` `Image` ``
but the editor renders a mention link, so the editor is the source of
truth. Route highlights for editor targets through
`highlight_background(HighlightKey::BufferSearchHighlights, ..)` using
the same active-vs-inactive color pattern that `BufferSearchBar` uses
for normal editor search, and autoscroll the editor to the active match
via `request_autoscroll`. `activate_match` re-applies editor highlights
on every index change because the color closure captures the active
index at insertion time.

Release Notes:

- Fixed thread search matches inside user messages not being visually
  highlighted (matches were counted but invisible on screen).
Thread search was indexing `ToolCall.label` — a plain-text markdown —
but the visible rendering of an Execute tool call's command is a fenced
monospace block. For the pre-approval "Run Command" preview and for
rejected commands the block was rendered as stacked `Label`s, which
have no highlight mechanism; for running/completed commands the block
uses `Terminal::command()`, a separate fenced markdown entity. Either
way, highlights attached to the label never showed up.

Add `ToolCall::command_markdown` — an `Entity<Markdown>` that mirrors
the label wrapped in ``` fences, populated for Execute kind and kept in
sync on title updates. Thread search indexes the terminal's command
markdown when one exists, otherwise the tool call's `command_markdown`.
Unify `render_collapsible_command` to render that entity via
`MarkdownElement` with code-block chrome stripped, so both the preview
and the post-execution command box flow matches through the existing
markdown highlight pathway.

Release Notes:

- Fixed thread search matches inside terminal tool-call command boxes
  not being visually highlighted (matches were counted but invisible on
  screen).
Follow-up fixes for the thread-search feature:

- Release the BufferSearchBar <-> ThreadView strong-handle cycle from
  AgentPanel so archived and cleaned-up threads actually free their
  views.
- Strip fence bytes before indexing terminal command markdown and
  offset match byte ranges so highlights land on the visible command
  rather than the fence characters.
- Make AlwaysCollapsed and AlwaysExpanded thinking-display modes
  honor search_auto_expanded_thinking_blocks, and drop that tracker
  when the user toggles a thinking block by hand.
- Protect streaming-auto-expanded thinking blocks from search
  cleanup so they don't snap shut mid-stream.
- Fall back to message.content.markdown() for user messages whose
  editor hasn't been synced into EntryViewState yet.
- Switch MatchTarget and the highlighted_* collections to WeakEntity
  so entities dropped from EntryViewState aren't retained purely for
  their search-highlight membership.
- Collect markdown sources as SharedString clones (via new
  Markdown::source_shared) to avoid per-keystroke O(N x source)
  String allocations on the foreground thread.
- Coalesce deploy_search double defers via a search_deploy_pending
  flag.
- Early-return from BufferSearchBar::on_query_editor_event when the
  bar is dismissed so a stray Edited event doesn't re-run matches on
  a hidden bar.
- Add unit tests for LineLayout::position_for_index covering the
  wrap-boundary, multi-wrap, end-of-last-line, and no-wrap cases.
Extend the Include-Hidden re-activate behavior to case sensitive,
whole word, and regex as well. Narrowing toggles can drop the
current active match from the result set, leaving the counter and
highlight pointing at nothing — the user sees "2 of 4" but the
cursor sits on stale ground. Always re-activating keeps the scroll
position, counter, and active highlight in sync with whatever set
the toggle produces.
@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Apr 24, 2026
@Tahinli

Tahinli commented Apr 24, 2026

Copy link
Copy Markdown
Contributor Author

Please review carefully guys. It's a bit big update. I don't want to make any mistake that causes problem for ZED in no case. I will try my best to cooperate and fix each and every request that ZED staff provides.

@SomeoneToIgnore SomeoneToIgnore added the area:ai Related to Agent Panel, Edit Prediction, Copilot, or other AI features label Apr 24, 2026
@benbrandt

Copy link
Copy Markdown
Member

Thanks for taking a look, but given we don't have search over historic external agents we'll need to think about how to communicate this feature, and I don't think this is the approach we want to take. Thanks though!

@benbrandt benbrandt closed this May 6, 2026
dandv added a commit to dandv/zed that referenced this pull request May 20, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request May 20, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request May 30, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request May 30, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request May 30, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request May 30, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request Jun 1, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request Jun 2, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request Jun 3, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request Jun 3, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request Jun 5, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request Jun 6, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request Jun 8, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
dandv added a commit to dandv/zed that referenced this pull request Jun 9, 2026
…ghts, action forwarding

Several fixes layered on top of the initial custom-bar implementation,
all confined to `agent_ui`:

- Forward `search::Toggle{CaseSensitive,WholeWord,Regex}` and
  `search::FocusSearch` from `ThreadView` so they fire when the bar is
  visible but focus is in the message editor. Without these the keymap
  resolved the binding (because `AcpThreadSearchBar` is contributed at
  the `ThreadView` level when the bar is visible) but the dispatched
  action found no handler on the bubble path.

- Defer the bar's `on_activate_match` callback's `view.update` via
  `cx.defer` to avoid re-entering `ThreadView`'s update from inside the
  bar's `select_next_match`. Reproduced as a double-borrow panic
  ("cannot update X while it is already being updated") on Enter and
  on Ctrl+F while the bar was open.

- Catch `editor::actions::Cancel` at the `ThreadView` level when the
  bar is visible. The keymap binds `escape` to `editor::Cancel` at the
  `Editor` context, which shadows `AcpThreadSearchBar`'s `escape ->
  agent::DismissThreadSearch`. `Editor::cancel` propagates when there's
  nothing to cancel locally; without the new handler, the propagated
  `Cancel` walked up to `Workspace`, where `BufferSearchBar::register`
  has a workspace-wide handler that dismissed the active pane's search
  bar instead of ours.

- Highlight user-message search hits via
  `Editor::highlight_background(HighlightKey::BufferSearchHighlights, …)`
  on the inner `Editor` backing each `MessageEditor`. Past user messages
  are rendered through `MessageEditor`, not through the markdown
  attached to the entry, so highlights painted on the markdown were
  counted but invisible. Introduces a `MatchTarget::{Markdown, Editor}`
  enum on `ThreadMatch` so active-vs-inactive re-paint dispatches to
  the right entity; splits the bar's `highlighted` field into
  `highlighted_markdowns` and `highlighted_editors` for clean clearing.
  `collect_markdowns` no longer pulls the user message's markdown,
  avoiding double-counting.

- Mute the zero-match counter color (was `Color::Error`; mirror MPS /
  `BufferSearchBar`). Red feedback comes from the query text alone, not
  also from the counter.

- Search placeholder: "Search thread…" → "Search this thread…" to make
  the per-thread scope explicit. Addresses the "we'll need to think
  about how to communicate this feature" line from the PR zed-industries#54816
  rejection — the bar searches only the currently-loaded thread,
  never history or cross-agent context.

- Three new gpui tests in `crates/agent_ui/src/conversation_view.rs`:
  - `test_thread_search_select_next_from_thread_view_update_does_not_panic`
    drives `select_next_match` from inside a `ThreadView::update_in`
    closure, reproducing the action-dispatch nesting that motivated
    the `cx.defer` fix.
  - `test_thread_search_editor_cancel_dismisses_bar` dispatches
    `editor::actions::Cancel` while the bar is visible and asserts the
    bar is dismissed before propagation reaches the workspace.
  - `test_thread_search_highlights_user_message_editor` searches a
    query that only matches the user message and asserts the user
    message's inner `Editor` carries `BufferSearchHighlights` after
    the matcher runs, and that `clear_highlights` removes them.

- Promote `MessageEditor::editor()` from `#[cfg(test)] pub(crate)` to
  always-`pub(crate)` so the search bar can paint highlights on the
  inner `Editor` outside tests. Kept the accessor narrow (crate-local)
  and documented the contract.

Release Notes:

- Added in-thread search for the agent panel (Ctrl/Cmd+F). Searches the
  currently-loaded thread only; not cross-thread or cross-agent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ai Related to Agent Panel, Edit Prediction, Copilot, or other AI features cla-signed The user has signed the Contributor License Agreement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants