From c372f246a0790da012dddbe3935ee2758a3a9c3b Mon Sep 17 00:00:00 2001
From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Date: Wed, 8 Apr 2026 10:43:22 -0300
Subject: [PATCH] agent_panel: Prevent content from overlapping with buttons in
message editor (#53384)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Follow up to https://github.com/zed-industries/zed/pull/53349 as I was
still seeing cases where content would overlap with buttons.
I believe this PR makes the whole message editor more solid against
these cases because we're relying on `flex_basis` now to set the
max-width, which runs before we do any editor mode calculations. Tested
this a bunch and it seems to be pretty stable.
Release Notes:
- N/A
---
.../src/conversation_view/thread_view.rs | 102 +++++++++---------
crates/gpui/src/styled.rs | 7 ++
2 files changed, 59 insertions(+), 50 deletions(-)
diff --git a/crates/agent_ui/src/conversation_view/thread_view.rs b/crates/agent_ui/src/conversation_view/thread_view.rs
index f82149c8471cc08840c393138c92316fc69e0ee5..882390f6330a57b0d6b857f4ff78be8079dcf3ee 100644
--- a/crates/agent_ui/src/conversation_view/thread_view.rs
+++ b/crates/agent_ui/src/conversation_view/thread_view.rs
@@ -1544,6 +1544,9 @@ impl ThreadView {
_window: &mut Window,
cx: &mut Context,
) {
+ if self.list_state.item_count() == 0 {
+ return;
+ }
self.set_editor_is_expanded(!self.editor_expanded, cx);
cx.stop_propagation();
cx.notify();
@@ -3147,9 +3150,8 @@ impl ThreadView {
let focus_handle = self.message_editor.focus_handle(cx);
let editor_bg_color = cx.theme().colors().editor_background;
+
let editor_expanded = self.editor_expanded;
- let has_messages = self.list_state.item_count() > 0;
- let v2_empty_state = !has_messages;
let (expand_icon, expand_tooltip) = if editor_expanded {
(IconName::Minimize, "Minimize Message Editor")
} else {
@@ -3157,43 +3159,48 @@ impl ThreadView {
};
let max_content_width = AgentSettings::get_global(cx).max_content_width;
+ let has_messages = self.list_state.item_count() > 0;
+ let fills_container = !has_messages || editor_expanded;
- v_flex()
- .on_action(cx.listener(Self::expand_message_editor))
+ h_flex()
.p_2()
- .gap_2()
- .when(!v2_empty_state, |this| {
- this.border_t_1().border_color(cx.theme().colors().border)
- })
.bg(editor_bg_color)
- .when(v2_empty_state, |this| this.flex_1().size_full())
- .when(editor_expanded && !v2_empty_state, |this| {
- this.h(vh(0.8, window)).size_full().justify_between()
+ .justify_center()
+ .map(|this| {
+ if has_messages {
+ this.on_action(cx.listener(Self::expand_message_editor))
+ .border_t_1()
+ .border_color(cx.theme().colors().border)
+ .when(editor_expanded, |this| this.h(vh(0.8, window)))
+ } else {
+ this.flex_1().size_full()
+ }
})
.child(
v_flex()
- .flex_1()
- .min_h_0()
- .w_full()
- .max_w(max_content_width)
- .mx_auto()
+ .flex_basis(max_content_width)
+ .flex_shrink()
+ .flex_grow_0()
+ .when(fills_container, |this| this.h_full())
+ .justify_between()
+ .gap_2()
.child(
v_flex()
.relative()
+ .w_full()
.min_h_0()
- .size_full()
- .when(v2_empty_state, |this| this.flex_1())
+ .when(fills_container, |this| this.flex_1())
.pt_1()
.pr_2p5()
.child(self.message_editor.clone())
- .when(!v2_empty_state, |this| {
+ .when(has_messages, |this| {
this.child(
h_flex()
.absolute()
.top_0()
.right_0()
.opacity(0.5)
- .hover(|this| this.opacity(1.0))
+ .hover(|s| s.opacity(1.0))
.child(
IconButton::new("toggle-height", expand_icon)
.icon_size(IconSize::Small)
@@ -3218,39 +3225,34 @@ impl ThreadView {
),
)
}),
- ),
- )
- .child(
- h_flex()
- .w_full()
- .max_w(max_content_width)
- .mx_auto()
- .flex_none()
- .flex_wrap()
- .justify_between()
- .child(
- h_flex()
- .gap_0p5()
- .child(self.render_add_context_button(cx))
- .child(self.render_follow_toggle(cx))
- .children(self.render_fast_mode_control(cx))
- .children(self.render_thinking_control(cx)),
)
.child(
h_flex()
- .gap_1()
- .children(self.render_token_usage(cx))
- .children(self.profile_selector.clone())
- .map(|this| {
- // Either config_options_view OR (mode_selector + model_selector)
- match self.config_options_view.clone() {
- Some(config_view) => this.child(config_view),
- None => this
- .children(self.mode_selector.clone())
- .children(self.model_selector.clone()),
- }
- })
- .child(self.render_send_button(cx)),
+ .w_full()
+ .flex_none()
+ .flex_wrap()
+ .justify_between()
+ .child(
+ h_flex()
+ .gap_0p5()
+ .child(self.render_add_context_button(cx))
+ .child(self.render_follow_toggle(cx))
+ .children(self.render_fast_mode_control(cx))
+ .children(self.render_thinking_control(cx)),
+ )
+ .child(
+ h_flex()
+ .gap_1()
+ .children(self.render_token_usage(cx))
+ .children(self.profile_selector.clone())
+ .map(|this| match self.config_options_view.clone() {
+ Some(config_view) => this.child(config_view),
+ None => this
+ .children(self.mode_selector.clone())
+ .children(self.model_selector.clone()),
+ })
+ .child(self.render_send_button(cx)),
+ ),
),
)
.into_any()
diff --git a/crates/gpui/src/styled.rs b/crates/gpui/src/styled.rs
index 687e71a94ce4d19a1795baed3381e0452c376a89..e3c79fdb405d6ffeb33a6c5baa4cc7bd70753d50 100644
--- a/crates/gpui/src/styled.rs
+++ b/crates/gpui/src/styled.rs
@@ -208,6 +208,13 @@ pub trait Styled: Sized {
self
}
+ /// Sets the element to prevent a flex item from growing.
+ /// [Docs](https://tailwindcss.com/docs/flex-grow#dont-grow)
+ fn flex_grow_0(mut self) -> Self {
+ self.style().flex_grow = Some(0.);
+ self
+ }
+
/// Sets the element to allow a flex item to shrink if needed.
/// [Docs](https://tailwindcss.com/docs/flex-shrink)
fn flex_shrink(mut self) -> Self {