Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 51 additions & 18 deletions crates/agent_ui/src/conversation_view/thread_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6442,9 +6442,12 @@ impl ThreadView {
primary
};

let primary = if matches!(entry, AgentThreadEntry::AssistantMessage(_))
&& !assistant_message_is_blank
{
let is_generating = matches!(thread.read(cx).status(), ThreadStatus::Generating);

let is_turn_end = Self::entry_is_finalized_turn_end(thread.read(cx).entries(), entry_ix)
.unwrap_or(!is_generating);

let primary = if is_turn_end && !assistant_message_is_blank {
let user_message_index = thread
.read(cx)
.entries()
Expand Down Expand Up @@ -6660,6 +6663,30 @@ impl ThreadView {
)
}

/// A turn ends when no further assistant output (message or tool call)
/// follows before the next user message, and it's finalized once a user
/// message follows it.
fn entry_is_finalized_turn_end(entries: &[AgentThreadEntry], entry_ix: usize) -> Option<bool> {
if !matches!(
entries.get(entry_ix),
Some(AgentThreadEntry::AssistantMessage(_))
) {
return Some(false);
}

for entry in &entries[entry_ix + 1..] {
match entry {
AgentThreadEntry::UserMessage(_) => return Some(true),
AgentThreadEntry::AssistantMessage(_) | AgentThreadEntry::ToolCall(_) => {
return Some(false);
}
_ => {}
}
}

None
}

fn render_thread_controls(
&self,
thread: &Entity<AcpThread>,
Expand Down Expand Up @@ -6702,13 +6729,15 @@ impl ThreadView {
this.scroll_to_user_message_index(user_message_index, cx);
}));

let scroll_to_top = IconButton::new(("scroll_to_top", entry_ix), IconName::ArrowUp)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.tooltip(Tooltip::text("Scroll to Top"))
.on_click(cx.listener(move |this, _, _, cx| {
this.scroll_to_top(cx);
}));
let scroll_to_top = is_thread_bottom.then(|| {
IconButton::new(("scroll_to_top", entry_ix), IconName::ArrowUp)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.tooltip(Tooltip::text("Scroll to Top"))
.on_click(cx.listener(move |this, _, _, cx| {
this.scroll_to_top(cx);
}))
});

let show_stats = is_thread_bottom && AgentSettings::get_global(cx).show_turn_stats;

Expand Down Expand Up @@ -6797,6 +6826,13 @@ impl ThreadView {
})
.flatten();

let separator_dots = || {
Label::new("•")
.size(LabelSize::Small)
.color(Color::Muted)
.alpha(0.5)
};

h_flex()
.w_full()
.py_1p5()
Expand All @@ -6812,21 +6848,18 @@ impl ThreadView {
.px_1()
.gap_1()
.when_some(last_turn_tokens_label, |this, label| {
this.child(label).child(
Label::new("•")
.size(LabelSize::Small)
.color(Color::Muted)
.alpha(0.5),
)
this.child(label).child(separator_dots())
})
.when_some(last_turn_clock, |this, label| this.child(label)),
.when_some(last_turn_clock, |this, label| {
this.child(label).child(separator_dots())
}),
)
},
)
.when_some(feedback_buttons, |this, buttons| this.child(buttons))
.when_some(copy_response_button, |this, button| this.child(button))
.child(scroll_to_recent_user_prompt)
.child(scroll_to_top)
.when_some(scroll_to_top, |this, button| this.child(button))
.into_any_element()
}

Expand Down
Loading