Preserve focused tool calls in agent panel when scrolling them out of view (#54115)

Release Notes:

- Fixed a bug where the agent panel would sometimes close if you
scrolled up or down while it was zoomed.

Co-authored-by: Lukas <lukas@zed.dev>
This commit is contained in:
Max Brunsfeld 2026-04-16 12:27:29 -07:00 committed by GitHub
parent 54f544430d
commit 496a78e01b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 39 additions and 8 deletions

View file

@ -4760,8 +4760,8 @@ impl ThreadView {
.into_any()
}
}
AgentThreadEntry::ToolCall(tool_call) => self
.render_any_tool_call(
AgentThreadEntry::ToolCall(tool_call) => {
let tool_call = self.render_any_tool_call(
self.thread.read(cx).session_id(),
entry_ix,
tool_call,
@ -4769,8 +4769,19 @@ impl ThreadView {
false,
window,
cx,
)
.into_any(),
);
if let Some(handle) = self
.entry_view_state
.read(cx)
.entry(entry_ix)
.and_then(|entry| entry.focus_handle(cx))
{
tool_call.track_focus(&handle).into_any()
} else {
tool_call.into_any()
}
}
AgentThreadEntry::CompletedPlan(entries) => {
self.render_completed_plan(entries, window, cx)
}

View file

@ -130,6 +130,7 @@ impl EntryViewState {
index,
Entry::ToolCall(ToolCallEntry {
content: HashMap::default(),
focus_handle: cx.focus_handle(),
}),
);
let Some(Entry::ToolCall(tool_call)) = self.entries.get_mut(index) else {
@ -262,7 +263,7 @@ impl EntryViewState {
Entry::UserMessage { .. }
| Entry::AssistantMessage { .. }
| Entry::CompletedPlan => {}
Entry::ToolCall(ToolCallEntry { content }) => {
Entry::ToolCall(ToolCallEntry { content, .. }) => {
for view in content.values() {
if let Ok(diff_editor) = view.clone().downcast::<Editor>() {
diff_editor.update(cx, |diff_editor, cx| {
@ -321,6 +322,7 @@ impl AssistantMessageEntry {
#[derive(Debug)]
pub struct ToolCallEntry {
content: HashMap<EntityId, AnyEntity>,
focus_handle: FocusHandle,
}
#[derive(Debug)]
@ -336,7 +338,8 @@ impl Entry {
match self {
Self::UserMessage(editor) => Some(editor.read(cx).focus_handle(cx)),
Self::AssistantMessage(message) => Some(message.focus_handle.clone()),
Self::ToolCall(_) | Self::CompletedPlan => None,
Self::ToolCall(tool_call) => Some(tool_call.focus_handle.clone()),
Self::CompletedPlan => None,
}
}
@ -376,7 +379,7 @@ impl Entry {
fn content_map(&self) -> Option<&HashMap<EntityId, AnyEntity>> {
match self {
Self::ToolCall(ToolCallEntry { content }) => Some(content),
Self::ToolCall(ToolCallEntry { content, .. }) => Some(content),
_ => None,
}
}
@ -384,12 +387,29 @@ impl Entry {
#[cfg(test)]
pub fn has_content(&self) -> bool {
match self {
Self::ToolCall(ToolCallEntry { content }) => !content.is_empty(),
Self::ToolCall(ToolCallEntry { content, .. }) => !content.is_empty(),
Self::UserMessage(_) | Self::AssistantMessage(_) | Self::CompletedPlan => false,
}
}
}
impl Focusable for ToolCallEntry {
fn focus_handle(&self, _cx: &App) -> FocusHandle {
self.focus_handle.clone()
}
}
impl Focusable for Entry {
fn focus_handle(&self, cx: &App) -> FocusHandle {
match self {
Self::UserMessage(editor) => editor.read(cx).focus_handle(cx),
Self::AssistantMessage(message) => message.focus_handle.clone(),
Self::ToolCall(tool_call) => tool_call.focus_handle.clone(),
Self::CompletedPlan => cx.focus_handle(),
}
}
}
fn create_terminal(
workspace: WeakEntity<Workspace>,
project: WeakEntity<Project>,