agent: Make terminal threads searchable (#60292)

Makes cmd-f work for terminal threads.

Since the agent panel is not part of the normal pane system we have to
manually hook up the search bar.

Closes #57309

Release Notes:

- agent: Added support for searching (cmd-f) inside of terminal threads
This commit is contained in:
Bennet Bo Fenner 2026-07-02 20:38:37 +02:00 committed by GitHub
parent ea87b05794
commit 4aa8ad9742
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 87 additions and 5 deletions

View file

@ -298,6 +298,13 @@
"alt-r": "search::ToggleRegex",
},
},
{
"context": "AgentTerminalThread",
"use_key_equivalents": true,
"bindings": {
"ctrl-f": "agent::ToggleSearch",
},
},
{
"context": "AcpThreadSearchBar",
"use_key_equivalents": true,

View file

@ -336,6 +336,13 @@
"alt-cmd-x": "search::ToggleRegex",
},
},
{
"context": "AgentTerminalThread",
"use_key_equivalents": true,
"bindings": {
"cmd-f": "agent::ToggleSearch",
},
},
{
"context": "AcpThreadSearchBar",
"use_key_equivalents": true,

View file

@ -299,6 +299,13 @@
"alt-r": "search::ToggleRegex",
},
},
{
"context": "AgentTerminalThread",
"use_key_equivalents": true,
"bindings": {
"ctrl-f": "agent::ToggleSearch",
},
},
{
"context": "AcpThreadSearchBar",
"use_key_equivalents": true,

View file

@ -85,6 +85,7 @@ use project::{Project, ProjectPath, Worktree};
use settings::TerminalDockPosition;
use settings::{NotifyWhenAgentWaiting, Settings, update_settings_file};
use search::{BufferSearchBar, buffer_search::Deploy as DeployBufferSearch};
use terminal::{Event as TerminalEvent, terminal_settings::TerminalSettings};
use terminal_view::{TerminalView, terminal_panel::TerminalPanel};
use text::OffsetRangeExt;
@ -96,9 +97,9 @@ use ui::{
use util::ResultExt as _;
use workspace::{
CollaboratorId, DraggedSelection, DraggedTab, MultiWorkspace, PathList, SerializedPathList,
ToggleWorkspaceSidebar, ToggleZoom, Workspace, WorkspaceId,
ToggleWorkspaceSidebar, ToggleZoom, ToolbarItemView, Workspace, WorkspaceId,
dock::{DockPosition, Panel, PanelEvent},
item::ItemEvent,
item::{ItemEvent, ItemHandle},
};
const AGENT_PANEL_KEY: &str = "agent_panel";
@ -991,6 +992,7 @@ struct AgentTerminal {
working_directory: Option<PathBuf>,
created_at: DateTime<Utc>,
has_notification: bool,
search_bar: Option<Entity<BufferSearchBar>>,
notification_windows: Vec<WindowHandle<AgentNotification>>,
notification_subscriptions: Vec<Subscription>,
_subscriptions: Vec<Subscription>,
@ -2174,6 +2176,7 @@ impl AgentPanel {
working_directory,
created_at: created_at.unwrap_or_else(Utc::now),
has_notification: false,
search_bar: None,
notification_windows: Vec::new(),
notification_subscriptions: Vec::new(),
_subscriptions: vec![view_subscription, terminal_subscription],
@ -3949,6 +3952,37 @@ impl AgentPanel {
}
}
fn toggle_terminal_thread_search(
&mut self,
_: &crate::ToggleSearch,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(terminal) = self
.active_terminal_id()
.and_then(|terminal_id| self.terminals.get_mut(&terminal_id))
else {
cx.propagate();
return;
};
let terminal_view = terminal.view.clone();
let search_bar = terminal
.search_bar
.get_or_insert_with(|| cx.new(|cx| BufferSearchBar::new(None, window, cx)))
.clone();
let deployed = search_bar.update(cx, |search_bar, cx| {
let terminal_item: &dyn ItemHandle = &terminal_view;
search_bar.set_active_pane_item(Some(terminal_item), window, cx);
search_bar.deploy(&DeployBufferSearch::find(), None, window, cx)
});
if deployed {
cx.stop_propagation();
} else {
cx.propagate();
}
}
pub fn conversation_view_for_id(
&self,
thread_id: &ThreadId,
@ -6415,6 +6449,7 @@ impl Render for AgentPanel {
.on_action(cx.listener(Self::decrease_font_size))
.on_action(cx.listener(Self::reset_font_size))
.on_action(cx.listener(Self::toggle_zoom))
.on_action(cx.listener(Self::toggle_terminal_thread_search))
.on_action(cx.listener(|this, _: &ReauthenticateAgent, window, cx| {
if let Some(conversation_view) = this.active_conversation_view() {
conversation_view.update(cx, |conversation_view, cx| {
@ -6439,9 +6474,35 @@ impl Render for AgentPanel {
VisibleSurface::AgentThread(conversation_view) => parent
.child(conversation_view.clone())
.child(self.render_drag_target(cx)),
VisibleSurface::Terminal(terminal_view) => parent
.child(terminal_view.clone())
.child(self.render_drag_target(cx)),
VisibleSurface::Terminal(terminal_view) => {
let search_bar = self
.active_terminal_id()
.and_then(|terminal_id| self.terminals.get(&terminal_id))
.and_then(|terminal| terminal.search_bar.clone());
let terminal_content = v_flex()
.key_context("AgentTerminalThread")
.size_full()
.when_some(search_bar, |this, search_bar| {
this.when(!search_bar.read(cx).is_dismissed(), |this| {
this.child(
v_flex()
.group("toolbar")
.relative()
.py(DynamicSpacing::Base06.rems(cx))
.px(DynamicSpacing::Base08.rems(cx))
.border_b_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().toolbar_background)
.child(search_bar),
)
})
})
.child(terminal_view.clone());
parent
.child(terminal_content)
.child(self.render_drag_target(cx))
}
})
.children(self.render_trial_end_upsell(window, cx));