thread view: Add ability to expand message editor and fix scroll (#34766)

Release Notes:

- N/A
This commit is contained in:
Danilo Leal 2025-07-21 09:26:31 -03:00 committed by GitHub
parent 88af35fe47
commit 56fd950d94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 135 additions and 51 deletions

View file

@ -45,7 +45,8 @@ use ::acp_thread::{
use crate::acp::completion_provider::{ContextPickerCompletionProvider, MentionSet};
use crate::acp::message_history::MessageHistory;
use crate::agent_diff::AgentDiff;
use crate::{AgentDiffPane, Follow, KeepAll, OpenAgentDiff, RejectAll};
use crate::message_editor::{MAX_EDITOR_LINES, MIN_EDITOR_LINES};
use crate::{AgentDiffPane, ExpandMessageEditor, Follow, KeepAll, OpenAgentDiff, RejectAll};
const RESPONSE_PADDING_X: Pixels = px(19.);
@ -65,6 +66,7 @@ pub struct AcpThreadView {
expanded_tool_calls: HashSet<ToolCallId>,
expanded_thinking_blocks: HashSet<(usize, usize)>,
edits_expanded: bool,
editor_is_expanded: bool,
message_history: Rc<RefCell<MessageHistory<acp::SendUserMessageParams>>>,
}
@ -94,6 +96,8 @@ impl AcpThreadView {
workspace: WeakEntity<Workspace>,
project: Entity<Project>,
message_history: Rc<RefCell<MessageHistory<acp::SendUserMessageParams>>>,
min_lines: usize,
max_lines: Option<usize>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
@ -113,8 +117,8 @@ impl AcpThreadView {
let mut editor = Editor::new(
editor::EditorMode::AutoHeight {
min_lines: 4,
max_lines: None,
min_lines,
max_lines: max_lines,
},
buffer,
None,
@ -182,6 +186,7 @@ impl AcpThreadView {
expanded_tool_calls: HashSet::default(),
expanded_thinking_blocks: HashSet::default(),
edits_expanded: false,
editor_is_expanded: false,
message_history,
}
}
@ -321,6 +326,35 @@ impl AcpThreadView {
}
}
pub fn expand_message_editor(
&mut self,
_: &ExpandMessageEditor,
_window: &mut Window,
cx: &mut Context<Self>,
) {
self.set_editor_is_expanded(!self.editor_is_expanded, cx);
cx.notify();
}
fn set_editor_is_expanded(&mut self, is_expanded: bool, cx: &mut Context<Self>) {
self.editor_is_expanded = is_expanded;
self.message_editor.update(cx, |editor, _| {
if self.editor_is_expanded {
editor.set_mode(EditorMode::Full {
scale_ui_elements_with_buffer_font_size: false,
show_active_line_background: false,
sized_by_content: false,
})
} else {
editor.set_mode(EditorMode::AutoHeight {
min_lines: MIN_EDITOR_LINES,
max_lines: Some(MAX_EDITOR_LINES),
})
}
});
cx.notify();
}
fn chat(&mut self, _: &Chat, window: &mut Window, cx: &mut Context<Self>) {
self.last_error.take();
@ -381,6 +415,7 @@ impl AcpThreadView {
let mention_set = self.mention_set.clone();
self.set_editor_is_expanded(false, cx);
self.message_editor.update(cx, |editor, cx| {
editor.clear(window, cx);
editor.remove_creases(mention_set.lock().drain(), cx)
@ -1793,34 +1828,96 @@ impl AcpThreadView {
))
}
fn render_message_editor(&mut self, cx: &mut Context<Self>) -> AnyElement {
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()
fn render_message_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
let focus_handle = self.message_editor.focus_handle(cx);
let editor_bg_color = cx.theme().colors().editor_background;
let (expand_icon, expand_tooltip) = if self.editor_is_expanded {
(IconName::Minimize, "Minimize Message Editor")
} else {
(IconName::Maximize, "Expand Message Editor")
};
EditorElement::new(
&self.message_editor,
EditorStyle {
background: cx.theme().colors().editor_background,
local_player: cx.theme().players().local(),
text: text_style,
syntax: cx.theme().syntax().clone(),
..Default::default()
},
)
.into_any()
v_flex()
.on_action(cx.listener(Self::expand_message_editor))
.p_2()
.gap_2()
.border_t_1()
.border_color(cx.theme().colors().border)
.bg(editor_bg_color)
.when(self.editor_is_expanded, |this| {
this.h(vh(0.8, window)).size_full().justify_between()
})
.child(
v_flex()
.relative()
.size_full()
.pt_1()
.pr_2p5()
.child(div().flex_1().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.message_editor,
EditorStyle {
background: editor_bg_color,
local_player: cx.theme().players().local(),
text: text_style,
syntax: cx.theme().syntax().clone(),
..Default::default()
},
)
}))
.child(
h_flex()
.absolute()
.top_0()
.right_0()
.opacity(0.5)
.hover(|this| this.opacity(1.0))
.child(
IconButton::new("toggle-height", expand_icon)
.icon_size(IconSize::XSmall)
.icon_color(Color::Muted)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
expand_tooltip,
&ExpandMessageEditor,
&focus_handle,
window,
cx,
)
}
})
.on_click(cx.listener(|_, _, window, cx| {
window.dispatch_action(Box::new(ExpandMessageEditor), cx);
})),
),
),
)
.child(
h_flex()
.flex_none()
.justify_between()
.child(self.render_follow_toggle(cx))
.child(self.render_send_button(cx)),
)
.into_any()
}
fn render_send_button(&self, cx: &mut Context<Self>) -> AnyElement {
@ -2132,7 +2229,6 @@ impl Render for AcpThreadView {
.px(RESPONSE_PADDING_X)
.opacity(0.4)
.hover(|style| style.opacity(1.))
.gap_1()
.flex_wrap()
.justify_end()
.child(open_as_markdown)
@ -2166,22 +2262,7 @@ impl Render for AcpThreadView {
),
)
})
.child(
v_flex()
.p_2()
.pt_3()
.gap_1()
.bg(cx.theme().colors().editor_background)
.border_t_1()
.border_color(cx.theme().colors().border)
.child(self.render_message_editor(cx))
.child(
h_flex()
.justify_between()
.child(self.render_follow_toggle(cx))
.child(self.render_send_button(cx)),
),
)
.child(self.render_message_editor(window, cx))
}
}

View file

@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize};
use crate::NewExternalAgentThread;
use crate::agent_diff::AgentDiffThread;
use crate::message_editor::{MAX_EDITOR_LINES, MIN_EDITOR_LINES};
use crate::{
AddContextServer, AgentDiffPane, ContinueThread, ContinueWithBurnMode,
DeleteRecentlyOpenThread, ExpandMessageEditor, Follow, InlineAssistant, NewTextThread,
@ -960,6 +961,8 @@ impl AgentPanel {
workspace.clone(),
project,
message_history,
MIN_EDITOR_LINES,
Some(MAX_EDITOR_LINES),
window,
cx,
)

View file

@ -65,6 +65,9 @@ use agent::{
thread_store::{TextThreadStore, ThreadStore},
};
pub const MIN_EDITOR_LINES: usize = 4;
pub const MAX_EDITOR_LINES: usize = 8;
#[derive(RegisterComponent)]
pub struct MessageEditor {
thread: Entity<Thread>,
@ -88,9 +91,6 @@ pub struct MessageEditor {
_subscriptions: Vec<Subscription>,
}
const MIN_EDITOR_LINES: usize = 4;
const MAX_EDITOR_LINES: usize = 8;
pub(crate) fn create_editor(
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
@ -711,11 +711,11 @@ impl MessageEditor {
cx.listener(|this, _: &RejectAll, window, cx| this.handle_reject_all(window, cx)),
)
.capture_action(cx.listener(Self::paste))
.gap_2()
.p_2()
.bg(editor_bg_color)
.gap_2()
.border_t_1()
.border_color(cx.theme().colors().border)
.bg(editor_bg_color)
.child(
h_flex()
.justify_between()