Wire up logic for thinking animation
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This commit is contained in:
parent
b61b62ac25
commit
6f29c562cc
2 changed files with 97 additions and 15 deletions
|
@ -6,7 +6,7 @@ use agent2::HistoryStore;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use editor::{Editor, EditorMode, MinimapVisibility};
|
use editor::{Editor, EditorMode, MinimapVisibility};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyEntity, App, AppContext as _, Entity, EntityId, EventEmitter, Focusable,
|
AnyEntity, App, AppContext as _, Entity, EntityId, EventEmitter, Focusable, ScrollHandle,
|
||||||
TextStyleRefinement, WeakEntity, Window,
|
TextStyleRefinement, WeakEntity, Window,
|
||||||
};
|
};
|
||||||
use language::language_settings::SoftWrap;
|
use language::language_settings::SoftWrap;
|
||||||
|
@ -154,10 +154,22 @@ impl EntryViewState {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AgentThreadEntry::AssistantMessage(_) => {
|
AgentThreadEntry::AssistantMessage(message) => {
|
||||||
if index == self.entries.len() {
|
let entry = if let Some(Entry::AssistantMessage(entry)) =
|
||||||
self.entries.push(Entry::empty())
|
self.entries.get_mut(index)
|
||||||
}
|
{
|
||||||
|
entry
|
||||||
|
} else {
|
||||||
|
self.set_entry(
|
||||||
|
index,
|
||||||
|
Entry::AssistantMessage(AssistantMessageEntry::default()),
|
||||||
|
);
|
||||||
|
let Some(Entry::AssistantMessage(entry)) = self.entries.get_mut(index) else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
entry
|
||||||
|
};
|
||||||
|
entry.sync(message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -177,7 +189,7 @@ impl EntryViewState {
|
||||||
pub fn settings_changed(&mut self, cx: &mut App) {
|
pub fn settings_changed(&mut self, cx: &mut App) {
|
||||||
for entry in self.entries.iter() {
|
for entry in self.entries.iter() {
|
||||||
match entry {
|
match entry {
|
||||||
Entry::UserMessage { .. } => {}
|
Entry::UserMessage { .. } | Entry::AssistantMessage { .. } => {}
|
||||||
Entry::Content(response_views) => {
|
Entry::Content(response_views) => {
|
||||||
for view in response_views.values() {
|
for view in response_views.values() {
|
||||||
if let Ok(diff_editor) = view.clone().downcast::<Editor>() {
|
if let Ok(diff_editor) = view.clone().downcast::<Editor>() {
|
||||||
|
@ -208,9 +220,29 @@ pub enum ViewEvent {
|
||||||
MessageEditorEvent(Entity<MessageEditor>, MessageEditorEvent),
|
MessageEditorEvent(Entity<MessageEditor>, MessageEditorEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct AssistantMessageEntry {
|
||||||
|
scroll_handles_by_chunk_index: HashMap<usize, ScrollHandle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AssistantMessageEntry {
|
||||||
|
pub fn scroll_handle_for_chunk(&self, ix: usize) -> Option<ScrollHandle> {
|
||||||
|
self.scroll_handles_by_chunk_index.get(&ix).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sync(&mut self, message: &acp_thread::AssistantMessage) {
|
||||||
|
if let Some(acp_thread::AssistantMessageChunk::Thought { .. }) = message.chunks.last() {
|
||||||
|
let ix = message.chunks.len() - 1;
|
||||||
|
let handle = self.scroll_handles_by_chunk_index.entry(ix).or_default();
|
||||||
|
handle.scroll_to_bottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Entry {
|
pub enum Entry {
|
||||||
UserMessage(Entity<MessageEditor>),
|
UserMessage(Entity<MessageEditor>),
|
||||||
|
AssistantMessage(AssistantMessageEntry),
|
||||||
Content(HashMap<EntityId, AnyEntity>),
|
Content(HashMap<EntityId, AnyEntity>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,7 +250,7 @@ impl Entry {
|
||||||
pub fn message_editor(&self) -> Option<&Entity<MessageEditor>> {
|
pub fn message_editor(&self) -> Option<&Entity<MessageEditor>> {
|
||||||
match self {
|
match self {
|
||||||
Self::UserMessage(editor) => Some(editor),
|
Self::UserMessage(editor) => Some(editor),
|
||||||
Entry::Content(_) => None,
|
Self::AssistantMessage(_) | Self::Content(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +271,16 @@ impl Entry {
|
||||||
.map(|entity| entity.downcast::<TerminalView>().unwrap())
|
.map(|entity| entity.downcast::<TerminalView>().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn scroll_handle_for_assistant_message_chunk(
|
||||||
|
&self,
|
||||||
|
chunk_ix: usize,
|
||||||
|
) -> Option<ScrollHandle> {
|
||||||
|
match self {
|
||||||
|
Self::AssistantMessage(message) => message.scroll_handle_for_chunk(chunk_ix),
|
||||||
|
Self::UserMessage(_) | Self::Content(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn content_map(&self) -> Option<&HashMap<EntityId, AnyEntity>> {
|
fn content_map(&self) -> Option<&HashMap<EntityId, AnyEntity>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Content(map) => Some(map),
|
Self::Content(map) => Some(map),
|
||||||
|
@ -254,7 +296,7 @@ impl Entry {
|
||||||
pub fn has_content(&self) -> bool {
|
pub fn has_content(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Content(map) => !map.is_empty(),
|
Self::Content(map) => !map.is_empty(),
|
||||||
Self::UserMessage(_) => false,
|
Self::UserMessage(_) | Self::AssistantMessage(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1615,9 +1615,15 @@ impl AcpThreadView {
|
||||||
let card_header_id = SharedString::from("inner-card-header");
|
let card_header_id = SharedString::from("inner-card-header");
|
||||||
let key = (entry_ix, chunk_ix);
|
let key = (entry_ix, chunk_ix);
|
||||||
let is_open = self.expanded_thinking_blocks.contains(&key);
|
let is_open = self.expanded_thinking_blocks.contains(&key);
|
||||||
|
let scroll_handle = self
|
||||||
|
.entry_view_state
|
||||||
|
.read(cx)
|
||||||
|
.entry(entry_ix)
|
||||||
|
.and_then(|entry| entry.scroll_handle_for_assistant_message_chunk(chunk_ix));
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
// .debug_bg_cyan()
|
.border_1()
|
||||||
|
.border_color(cx.theme().colors().border)
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.id(header_id)
|
.id(header_id)
|
||||||
|
@ -1631,11 +1637,9 @@ impl AcpThreadView {
|
||||||
.h(window.line_height())
|
.h(window.line_height())
|
||||||
.gap_1p5()
|
.gap_1p5()
|
||||||
.child(
|
.child(
|
||||||
// div().debug_bg_magenta().child(
|
|
||||||
Icon::new(IconName::ToolThink)
|
Icon::new(IconName::ToolThink)
|
||||||
.size(IconSize::Small)
|
.size(IconSize::Small)
|
||||||
.color(Color::Muted),
|
.color(Color::Muted),
|
||||||
// ),
|
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
@ -1671,8 +1675,8 @@ impl AcpThreadView {
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.when(is_open, |this| {
|
.map(|this| {
|
||||||
this.child(
|
this.child(if is_open {
|
||||||
div()
|
div()
|
||||||
.relative()
|
.relative()
|
||||||
.mt_1p5()
|
.mt_1p5()
|
||||||
|
@ -1681,11 +1685,47 @@ impl AcpThreadView {
|
||||||
.border_l_1()
|
.border_l_1()
|
||||||
.border_color(self.tool_card_border_color(cx))
|
.border_color(self.tool_card_border_color(cx))
|
||||||
.text_ui_sm(cx)
|
.text_ui_sm(cx)
|
||||||
|
.child(self.render_markdown(
|
||||||
|
chunk,
|
||||||
|
default_markdown_style(false, false, window, cx),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
let editor_bg = cx.theme().colors().editor_background;
|
||||||
|
let gradient_overlay = div()
|
||||||
|
.rounded_b_lg()
|
||||||
|
.h_full()
|
||||||
|
.absolute()
|
||||||
|
.w_full()
|
||||||
|
.bottom_0()
|
||||||
|
.left_0()
|
||||||
|
.bg(linear_gradient(
|
||||||
|
180.,
|
||||||
|
linear_color_stop(editor_bg, 1.),
|
||||||
|
linear_color_stop(editor_bg.opacity(0.2), 0.),
|
||||||
|
));
|
||||||
|
|
||||||
|
div()
|
||||||
|
.relative()
|
||||||
|
.bg(editor_bg)
|
||||||
|
.rounded_b_lg()
|
||||||
|
.mt_2()
|
||||||
|
.pl_4()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.id(("thinking-content", chunk_ix))
|
||||||
|
.max_h_20()
|
||||||
|
.when_some(scroll_handle, |this, scroll_handle| {
|
||||||
|
this.track_scroll(&scroll_handle)
|
||||||
|
})
|
||||||
|
.text_ui_sm(cx)
|
||||||
|
.overflow_hidden()
|
||||||
.child(self.render_markdown(
|
.child(self.render_markdown(
|
||||||
chunk,
|
chunk,
|
||||||
default_markdown_style(false, false, window, cx),
|
default_markdown_style(false, false, window, cx),
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
|
.child(gradient_overlay)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue