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. Screenshot 2026-04-08 at 6  53@2x 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 {