thread view: Scroll to the bottom when sending new messages + adjust controls display (#35586)
Release Notes: - N/A
This commit is contained in:
parent
bb5af6f76d
commit
d577ef52cb
1 changed files with 71 additions and 49 deletions
|
@ -424,11 +424,14 @@ impl AcpThreadView {
|
||||||
let mention_set = self.mention_set.clone();
|
let mention_set = self.mention_set.clone();
|
||||||
|
|
||||||
self.set_editor_is_expanded(false, cx);
|
self.set_editor_is_expanded(false, cx);
|
||||||
|
|
||||||
self.message_editor.update(cx, |editor, cx| {
|
self.message_editor.update(cx, |editor, cx| {
|
||||||
editor.clear(window, cx);
|
editor.clear(window, cx);
|
||||||
editor.remove_creases(mention_set.lock().drain(), cx)
|
editor.remove_creases(mention_set.lock().drain(), cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.scroll_to_bottom(cx);
|
||||||
|
|
||||||
self.message_history.borrow_mut().push(chunks);
|
self.message_history.borrow_mut().push(chunks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2022,15 +2025,15 @@ impl AcpThreadView {
|
||||||
.icon_color(Color::Accent)
|
.icon_color(Color::Accent)
|
||||||
.style(ButtonStyle::Filled)
|
.style(ButtonStyle::Filled)
|
||||||
.disabled(self.thread().is_none() || is_editor_empty)
|
.disabled(self.thread().is_none() || is_editor_empty)
|
||||||
.on_click(cx.listener(|this, _, window, cx| {
|
|
||||||
this.chat(&Chat, window, cx);
|
|
||||||
}))
|
|
||||||
.when(!is_editor_empty, |button| {
|
.when(!is_editor_empty, |button| {
|
||||||
button.tooltip(move |window, cx| Tooltip::for_action("Send", &Chat, window, cx))
|
button.tooltip(move |window, cx| Tooltip::for_action("Send", &Chat, window, cx))
|
||||||
})
|
})
|
||||||
.when(is_editor_empty, |button| {
|
.when(is_editor_empty, |button| {
|
||||||
button.tooltip(Tooltip::text("Type a message to submit"))
|
button.tooltip(Tooltip::text("Type a message to submit"))
|
||||||
})
|
})
|
||||||
|
.on_click(cx.listener(|this, _, window, cx| {
|
||||||
|
this.chat(&Chat, window, cx);
|
||||||
|
}))
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
} else {
|
} else {
|
||||||
IconButton::new("stop-generation", IconName::StopFilled)
|
IconButton::new("stop-generation", IconName::StopFilled)
|
||||||
|
@ -2245,6 +2248,14 @@ impl AcpThreadView {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn scroll_to_bottom(&mut self, cx: &mut Context<Self>) {
|
||||||
|
if let Some(thread) = self.thread() {
|
||||||
|
let entry_count = thread.read(cx).entries().len();
|
||||||
|
self.list_state.reset(entry_count);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn notify_with_sound(
|
fn notify_with_sound(
|
||||||
&mut self,
|
&mut self,
|
||||||
caption: impl Into<SharedString>,
|
caption: impl Into<SharedString>,
|
||||||
|
@ -2392,17 +2403,9 @@ impl AcpThreadView {
|
||||||
self.notification_subscriptions.remove(&window);
|
self.notification_subscriptions.remove(&window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Focusable for AcpThreadView {
|
fn render_thread_controls(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
fn focus_handle(&self, cx: &App) -> FocusHandle {
|
let open_as_markdown = IconButton::new("open-as-markdown", IconName::FileText)
|
||||||
self.message_editor.focus_handle(cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Render for AcpThreadView {
|
|
||||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
|
||||||
let open_as_markdown = IconButton::new("open-as-markdown", IconName::DocumentText)
|
|
||||||
.icon_size(IconSize::XSmall)
|
.icon_size(IconSize::XSmall)
|
||||||
.icon_color(Color::Ignored)
|
.icon_color(Color::Ignored)
|
||||||
.tooltip(Tooltip::text("Open Thread as Markdown"))
|
.tooltip(Tooltip::text("Open Thread as Markdown"))
|
||||||
|
@ -2421,6 +2424,28 @@ impl Render for AcpThreadView {
|
||||||
this.scroll_to_top(cx);
|
this.scroll_to_top(cx);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
h_flex()
|
||||||
|
.mt_1()
|
||||||
|
.mr_1()
|
||||||
|
.py_2()
|
||||||
|
.px(RESPONSE_PADDING_X)
|
||||||
|
.opacity(0.4)
|
||||||
|
.hover(|style| style.opacity(1.))
|
||||||
|
.flex_wrap()
|
||||||
|
.justify_end()
|
||||||
|
.child(open_as_markdown)
|
||||||
|
.child(scroll_to_top)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Focusable for AcpThreadView {
|
||||||
|
fn focus_handle(&self, cx: &App) -> FocusHandle {
|
||||||
|
self.message_editor.focus_handle(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for AcpThreadView {
|
||||||
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
v_flex()
|
v_flex()
|
||||||
.size_full()
|
.size_full()
|
||||||
.key_context("AcpThread")
|
.key_context("AcpThread")
|
||||||
|
@ -2456,42 +2481,39 @@ impl Render for AcpThreadView {
|
||||||
.items_center()
|
.items_center()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.child(self.render_error_state(e, cx)),
|
.child(self.render_error_state(e, cx)),
|
||||||
ThreadState::Ready { thread, .. } => v_flex().flex_1().map(|this| {
|
ThreadState::Ready { thread, .. } => {
|
||||||
if self.list_state.item_count() > 0 {
|
let thread_clone = thread.clone();
|
||||||
this.child(
|
|
||||||
list(self.list_state.clone())
|
v_flex().flex_1().map(|this| {
|
||||||
.with_sizing_behavior(gpui::ListSizingBehavior::Auto)
|
if self.list_state.item_count() > 0 {
|
||||||
.flex_grow()
|
let is_generating =
|
||||||
.into_any(),
|
matches!(thread_clone.read(cx).status(), ThreadStatus::Generating);
|
||||||
)
|
|
||||||
.child(
|
this.child(
|
||||||
h_flex()
|
list(self.list_state.clone())
|
||||||
.group("controls")
|
.with_sizing_behavior(gpui::ListSizingBehavior::Auto)
|
||||||
.mt_1()
|
.flex_grow()
|
||||||
.mr_1()
|
.into_any(),
|
||||||
.py_2()
|
)
|
||||||
.px(RESPONSE_PADDING_X)
|
.when(!is_generating, |this| {
|
||||||
.opacity(0.4)
|
this.child(self.render_thread_controls(cx))
|
||||||
.hover(|style| style.opacity(1.))
|
})
|
||||||
.flex_wrap()
|
.children(match thread_clone.read(cx).status() {
|
||||||
.justify_end()
|
ThreadStatus::Idle | ThreadStatus::WaitingForToolConfirmation => {
|
||||||
.child(open_as_markdown)
|
None
|
||||||
.child(scroll_to_top)
|
}
|
||||||
.into_any_element(),
|
ThreadStatus::Generating => div()
|
||||||
)
|
.px_5()
|
||||||
.children(match thread.read(cx).status() {
|
.py_2()
|
||||||
ThreadStatus::Idle | ThreadStatus::WaitingForToolConfirmation => None,
|
.child(LoadingLabel::new("").size(LabelSize::Small))
|
||||||
ThreadStatus::Generating => div()
|
.into(),
|
||||||
.px_5()
|
})
|
||||||
.py_2()
|
.children(self.render_activity_bar(&thread_clone, window, cx))
|
||||||
.child(LoadingLabel::new("").size(LabelSize::Small))
|
} else {
|
||||||
.into(),
|
this.child(self.render_empty_state(cx))
|
||||||
})
|
}
|
||||||
.children(self.render_activity_bar(&thread, window, cx))
|
})
|
||||||
} else {
|
}
|
||||||
this.child(self.render_empty_state(cx))
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
.when_some(self.last_error.clone(), |el, error| {
|
.when_some(self.last_error.clone(), |el, error| {
|
||||||
el.child(
|
el.child(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue