From 681c88d4e75b4cc0de0408df5bc1b1cc13152193 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Sun, 15 Jun 2025 12:45:44 -0600 Subject: [PATCH] Fix clicking in to agent message editor and tighten up vertical spacing (#32765) * Adds `min_lines` to `EditorMode::AutoHeight` and use `min_lines: 4` in agent message editor. This makes it so that clicks in the blank space below the first line of the editor also focus it, instead of needing to click the very first line. * Removes the div wrapping the editor, as it was only there to set `min_h_16()`. This also tightens up the min space given to the editor - before it was not evenly dividing the number of lines. * Further tightens up vertical spacing by using `gap_1` instead of `gap_4` between editor and controls below At 4 line min height (after on the left, before on the right): ![image](https://github.com/user-attachments/assets/e8eefb1b-9ea3-4f98-ad55-25f95760d61f) At 5 lines, one more than min height (after on the left, before on the right): ![image](https://github.com/user-attachments/assets/a6ba737c-6a56-4343-a55a-d264f2a06377) Release Notes: - Agent: Fixed clicking to focus the message editor to also work for clicks below the last line. --- crates/agent/src/active_thread.rs | 5 +- .../configure_context_server_modal.rs | 2 +- crates/agent/src/inline_prompt_editor.rs | 4 +- crates/agent/src/message_editor.rs | 68 +++++++++---------- crates/collab_ui/src/chat_panel.rs | 2 +- crates/editor/src/editor.rs | 16 ++++- crates/editor/src/element.rs | 14 +++- crates/git_ui/src/git_panel.rs | 5 +- crates/repl/src/notebook/cell.rs | 5 +- .../src/stories/auto_height_editor.rs | 2 +- 10 files changed, 75 insertions(+), 48 deletions(-) diff --git a/crates/agent/src/active_thread.rs b/crates/agent/src/active_thread.rs index 24061488274c7baba21430ea119baf42fc884751..ce7388530915a25bbcc40278cd4ba876a6c4c79e 100644 --- a/crates/agent/src/active_thread.rs +++ b/crates/agent/src/active_thread.rs @@ -1681,7 +1681,10 @@ impl ActiveThread { let editor = cx.new(|cx| { let mut editor = Editor::new( - editor::EditorMode::AutoHeight { max_lines: 4 }, + editor::EditorMode::AutoHeight { + min_lines: 1, + max_lines: 4, + }, buffer, None, window, diff --git a/crates/agent/src/agent_configuration/configure_context_server_modal.rs b/crates/agent/src/agent_configuration/configure_context_server_modal.rs index c916e7dc323a0e950bd57f31265aba76f85eec83..3bb44501322f5d2850384aba5f72262f1e33ee17 100644 --- a/crates/agent/src/agent_configuration/configure_context_server_modal.rs +++ b/crates/agent/src/agent_configuration/configure_context_server_modal.rs @@ -89,7 +89,7 @@ impl ConfigureContextServerModal { }), settings_validator, settings_editor: cx.new(|cx| { - let mut editor = Editor::auto_height(16, window, cx); + let mut editor = Editor::auto_height(1, 16, window, cx); editor.set_text(config.default_settings.trim(), window, cx); editor.set_show_gutter(false, cx); editor.set_soft_wrap_mode( diff --git a/crates/agent/src/inline_prompt_editor.rs b/crates/agent/src/inline_prompt_editor.rs index af83b3ad76f84fa8abde6d884b16882de0ded893..e9d1f46b17da9aa3cd6dab155671227b40e2a39c 100644 --- a/crates/agent/src/inline_prompt_editor.rs +++ b/crates/agent/src/inline_prompt_editor.rs @@ -261,7 +261,7 @@ impl PromptEditor { let focus = self.editor.focus_handle(cx).contains_focused(window, cx); self.editor = cx.new(|cx| { - let mut editor = Editor::auto_height(Self::MAX_LINES as usize, window, cx); + let mut editor = Editor::auto_height(1, Self::MAX_LINES as usize, window, cx); editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx); editor.set_placeholder_text("Add a prompt…", cx); editor.set_text(prompt, window, cx); @@ -872,6 +872,7 @@ impl PromptEditor { let prompt_editor = cx.new(|cx| { let mut editor = Editor::new( EditorMode::AutoHeight { + min_lines: 1, max_lines: Self::MAX_LINES as usize, }, prompt_buffer, @@ -1050,6 +1051,7 @@ impl PromptEditor { let prompt_editor = cx.new(|cx| { let mut editor = Editor::new( EditorMode::AutoHeight { + min_lines: 1, max_lines: Self::MAX_LINES as usize, }, prompt_buffer, diff --git a/crates/agent/src/message_editor.rs b/crates/agent/src/message_editor.rs index cd5fd591d03f0c9f531d4742ecd3019ebd754b7d..b10a20b3e0d1391c834797cbb524a38c8ca09a78 100644 --- a/crates/agent/src/message_editor.rs +++ b/crates/agent/src/message_editor.rs @@ -79,6 +79,7 @@ pub struct MessageEditor { _subscriptions: Vec, } +const MIN_EDITOR_LINES: usize = 4; const MAX_EDITOR_LINES: usize = 8; pub(crate) fn create_editor( @@ -102,6 +103,7 @@ pub(crate) fn create_editor( let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); let mut editor = Editor::new( editor::EditorMode::AutoHeight { + min_lines: MIN_EDITOR_LINES, max_lines: MAX_EDITOR_LINES, }, buffer, @@ -253,6 +255,7 @@ impl MessageEditor { }) } else { editor.set_mode(EditorMode::AutoHeight { + min_lines: MIN_EDITOR_LINES, max_lines: MAX_EDITOR_LINES, }) } @@ -671,44 +674,39 @@ impl MessageEditor { .child( v_flex() .size_full() - .gap_4() + .gap_1() .when(is_editor_expanded, |this| { this.h(vh(0.8, window)).justify_between() }) - .child( - v_flex() - .min_h_16() - .when(is_editor_expanded, |this| this.h_full()) - .child({ - let settings = ThemeSettings::get_global(cx); - let font_size = TextSize::Small - .rems(cx) - .to_pixels(settings.agent_font_size(cx)); - let line_height = settings.buffer_line_height.value() * font_size; - - let text_style = TextStyle { - color: cx.theme().colors().text, - font_family: settings.buffer_font.family.clone(), - font_fallbacks: settings.buffer_font.fallbacks.clone(), - font_features: settings.buffer_font.features.clone(), - font_size: font_size.into(), - line_height: line_height.into(), - ..Default::default() - }; - - EditorElement::new( - &self.editor, - EditorStyle { - background: editor_bg_color, - local_player: cx.theme().players().local(), - text: text_style, - syntax: cx.theme().syntax().clone(), - ..Default::default() - }, - ) - .into_any() - }), - ) + .child({ + let settings = ThemeSettings::get_global(cx); + let font_size = TextSize::Small + .rems(cx) + .to_pixels(settings.agent_font_size(cx)); + let line_height = settings.buffer_line_height.value() * font_size; + + let text_style = TextStyle { + color: cx.theme().colors().text, + font_family: settings.buffer_font.family.clone(), + font_fallbacks: settings.buffer_font.fallbacks.clone(), + font_features: settings.buffer_font.features.clone(), + font_size: font_size.into(), + line_height: line_height.into(), + ..Default::default() + }; + + EditorElement::new( + &self.editor, + EditorStyle { + background: editor_bg_color, + local_player: cx.theme().players().local(), + text: text_style, + syntax: cx.theme().syntax().clone(), + ..Default::default() + }, + ) + .into_any() + }) .child( h_flex() .flex_none() diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index 92f20581aea15fcf2643a6ab5a361c57f641b22d..54c45a9fec39569d06d7dc45140affe4f6c27d5b 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -90,7 +90,7 @@ impl ChatPanel { languages.clone(), user_store.clone(), None, - cx.new(|cx| Editor::auto_height(4, window, cx)), + cx.new(|cx| Editor::auto_height(1, 4, window, cx)), window, cx, ) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 9900e68471c59b1b2135d53b0883344d2e1aa8a9..dea78cd0111d11c546e3b10d38755f23ee0802aa 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -475,6 +475,7 @@ pub enum EditorMode { auto_width: bool, }, AutoHeight { + min_lines: usize, max_lines: usize, }, Full { @@ -1609,11 +1610,19 @@ impl Editor { ) } - pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context) -> Self { + pub fn auto_height( + min_lines: usize, + max_lines: usize, + window: &mut Window, + cx: &mut Context, + ) -> Self { let buffer = cx.new(|cx| Buffer::local("", cx)); let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); Self::new( - EditorMode::AutoHeight { max_lines }, + EditorMode::AutoHeight { + min_lines, + max_lines, + }, buffer, None, window, @@ -21884,7 +21893,7 @@ impl Render for Editor { let background = match self.mode { EditorMode::SingleLine { .. } => cx.theme().system().transparent, - EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent, + EditorMode::AutoHeight { .. } => cx.theme().system().transparent, EditorMode::Full { .. } => cx.theme().colors().editor_background, EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7), }; @@ -22542,6 +22551,7 @@ impl BreakpointPromptEditor { let prompt = cx.new(|cx| { let mut prompt = Editor::new( EditorMode::AutoHeight { + min_lines: 1, max_lines: Self::MAX_LINES as usize, }, buffer, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a6cbd5a847c80610826e1c2c37c22210797d4d1d..40ce3fd05bfd719001e208eaf909248442142517 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -7681,7 +7681,10 @@ impl Element for EditorElement { window.request_layout(style, None, cx) } } - EditorMode::AutoHeight { max_lines } => { + EditorMode::AutoHeight { + min_lines, + max_lines, + } => { let editor_handle = cx.entity().clone(); let max_line_number_width = self.max_line_number_width(&editor.snapshot(window, cx), window, cx); @@ -7692,6 +7695,7 @@ impl Element for EditorElement { .update(cx, |editor, cx| { compute_auto_height_layout( editor, + min_lines, max_lines, max_line_number_width, known_dimensions, @@ -9864,6 +9868,7 @@ pub fn register_action( fn compute_auto_height_layout( editor: &mut Editor, + min_lines: usize, max_lines: usize, max_line_number_width: Pixels, known_dimensions: Size>, @@ -9911,7 +9916,7 @@ fn compute_auto_height_layout( let scroll_height = (snapshot.max_point().row().next_row().0 as f32) * line_height; let height = scroll_height - .max(line_height) + .max(line_height * min_lines as f32) .min(line_height * max_lines as f32); Some(size(width, height)) @@ -10214,7 +10219,10 @@ mod tests { for editor_mode_without_invisibles in [ EditorMode::SingleLine { auto_width: false }, - EditorMode::AutoHeight { max_lines: 100 }, + EditorMode::AutoHeight { + min_lines: 1, + max_lines: 100, + }, ] { for show_line_numbers in [true, false] { let invisibles = collect_invisibles_from_new_editor( diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 0eca1356379f28c5375f7f0ebc93f173caf2f224..2a3674d775b6036ad7413e36d098cb3363cef5fe 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -377,7 +377,10 @@ pub(crate) fn commit_message_editor( let buffer = cx.new(|cx| MultiBuffer::singleton(commit_message_buffer, cx)); let max_lines = if in_panel { MAX_PANEL_EDITOR_LINES } else { 18 }; let mut commit_editor = Editor::new( - EditorMode::AutoHeight { max_lines }, + EditorMode::AutoHeight { + min_lines: 1, + max_lines, + }, buffer, None, window, diff --git a/crates/repl/src/notebook/cell.rs b/crates/repl/src/notebook/cell.rs index b8071d41776668e0b07bad85c2ced60b5b688eda..30e26579f390cf0882dcdd87c35ad5575a77164e 100644 --- a/crates/repl/src/notebook/cell.rs +++ b/crates/repl/src/notebook/cell.rs @@ -177,7 +177,10 @@ impl Cell { let editor_view = cx.new(|cx| { let mut editor = Editor::new( - EditorMode::AutoHeight { max_lines: 1024 }, + EditorMode::AutoHeight { + min_lines: 1, + max_lines: 1024, + }, multi_buffer, None, window, diff --git a/crates/storybook/src/stories/auto_height_editor.rs b/crates/storybook/src/stories/auto_height_editor.rs index 9abee60238a3934e5ba8cd6e04dc48d8bc299089..702d5774f2c7f353ae9ea600e9b17309f7051ead 100644 --- a/crates/storybook/src/stories/auto_height_editor.rs +++ b/crates/storybook/src/stories/auto_height_editor.rs @@ -17,7 +17,7 @@ impl AutoHeightEditorStory { )]); cx.new(|cx| Self { editor: cx.new(|cx| { - let mut editor = Editor::auto_height(3, window, cx); + let mut editor = Editor::auto_height(1, 3, window, cx); editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx); editor }),