assistant panel: Stop animation & show explicit state if canceled (#16200)

This fixes a bug by stopping the animation when a completion is canceled
and it also makes the state more explicit, which I think is very
valuable.



https://github.com/user-attachments/assets/9ede9b25-86ac-4901-8434-7407896bb799


Release Notes:

- N/A
This commit is contained in:
Thorsten Ball 2024-08-14 11:18:40 +02:00 committed by GitHub
parent 97469cd049
commit 8b8335f449
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 75 additions and 31 deletions

View file

@ -102,6 +102,7 @@ pub enum MessageStatus {
Pending, Pending,
Done, Done,
Error(SharedString), Error(SharedString),
Canceled,
} }
impl MessageStatus { impl MessageStatus {
@ -112,6 +113,7 @@ impl MessageStatus {
Some(proto::context_message_status::Variant::Error(error)) => { Some(proto::context_message_status::Variant::Error(error)) => {
MessageStatus::Error(error.message.into()) MessageStatus::Error(error.message.into())
} }
Some(proto::context_message_status::Variant::Canceled(_)) => MessageStatus::Canceled,
None => MessageStatus::Pending, None => MessageStatus::Pending,
} }
} }
@ -135,6 +137,11 @@ impl MessageStatus {
}, },
)), )),
}, },
MessageStatus::Canceled => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Canceled(
proto::context_message_status::Canceled {},
)),
},
} }
} }
} }

View file

@ -1975,7 +1975,7 @@ impl ContextEditor {
fn cancel(&mut self, _: &editor::actions::Cancel, cx: &mut ViewContext<Self>) { fn cancel(&mut self, _: &editor::actions::Cancel, cx: &mut ViewContext<Self>) {
if self if self
.context .context
.update(cx, |context, _| context.cancel_last_assist()) .update(cx, |context, cx| context.cancel_last_assist(cx))
{ {
return; return;
} }
@ -3042,22 +3042,6 @@ impl ContextEditor {
} }
}); });
let trigger = Button::new("show-error", "Error")
.color(Color::Error)
.selected_label_color(Color::Error)
.selected_icon_color(Color::Error)
.icon(IconName::XCircle)
.icon_color(Color::Error)
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.tooltip(move |cx| {
Tooltip::with_meta(
"Error interacting with language model",
None,
"Click for more details",
cx,
)
});
h_flex() h_flex()
.id(("message_header", message_id.as_u64())) .id(("message_header", message_id.as_u64()))
.pl(cx.gutter_dimensions.full_width()) .pl(cx.gutter_dimensions.full_width())
@ -3066,22 +3050,63 @@ impl ContextEditor {
.relative() .relative()
.gap_1() .gap_1()
.child(sender) .child(sender)
.children( .children(match &message.status {
if let MessageStatus::Error(error) = message.status.clone() { MessageStatus::Error(error) => {
let error_popover_trigger =
Button::new("show-error", "Error")
.color(Color::Error)
.selected_label_color(Color::Error)
.selected_icon_color(Color::Error)
.icon(IconName::XCircle)
.icon_color(Color::Error)
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.tooltip(move |cx| {
Tooltip::with_meta(
"Error interacting with language model",
None,
"Click for more details",
cx,
)
});
Some( Some(
PopoverMenu::new("show-error-popover") PopoverMenu::new("show-error-popover")
.menu(move |cx| { .menu({
Some(cx.new_view(|cx| ErrorPopover { let error = error.clone();
error: error.clone(), move |cx| {
focus_handle: cx.focus_handle(), Some(cx.new_view(|cx| ErrorPopover {
})) error: error.clone(),
focus_handle: cx.focus_handle(),
}))
}
}) })
.trigger(trigger), .trigger(error_popover_trigger)
.into_any_element(),
) )
} else { }
None MessageStatus::Canceled => Some(
}, ButtonLike::new("canceled")
) .child(
Icon::new(IconName::XCircle).color(Color::Disabled),
)
.child(
Label::new("Canceled")
.size(LabelSize::Small)
.color(Color::Disabled),
)
.tooltip(move |cx| {
Tooltip::with_meta(
"Canceled",
None,
"Interaction with the assistant was canceled",
cx,
)
})
.into_any_element(),
),
_ => None,
})
.into_any_element() .into_any_element()
} }
}), }),

View file

@ -401,6 +401,7 @@ impl PartialEq for ImageAnchor {
struct PendingCompletion { struct PendingCompletion {
id: usize, id: usize,
assistant_message_id: MessageId,
_task: Task<()>, _task: Task<()>,
} }
@ -1806,6 +1807,7 @@ impl Context {
self.pending_completions.push(PendingCompletion { self.pending_completions.push(PendingCompletion {
id: post_inc(&mut self.completion_count), id: post_inc(&mut self.completion_count),
assistant_message_id: assistant_message.id,
_task: task, _task: task,
}); });
@ -1827,8 +1829,15 @@ impl Context {
} }
} }
pub fn cancel_last_assist(&mut self) -> bool { pub fn cancel_last_assist(&mut self, cx: &mut ModelContext<Self>) -> bool {
self.pending_completions.pop().is_some() if let Some(pending_completion) = self.pending_completions.pop() {
self.update_metadata(pending_completion.assistant_message_id, cx, |metadata| {
metadata.status = MessageStatus::Canceled;
});
true
} else {
false
}
} }
pub fn cycle_message_roles(&mut self, ids: HashSet<MessageId>, cx: &mut ModelContext<Self>) { pub fn cycle_message_roles(&mut self, ids: HashSet<MessageId>, cx: &mut ModelContext<Self>) {

View file

@ -2310,6 +2310,7 @@ message ContextMessageStatus {
Done done = 1; Done done = 1;
Pending pending = 2; Pending pending = 2;
Error error = 3; Error error = 3;
Canceled canceled = 4;
} }
message Done {} message Done {}
@ -2319,6 +2320,8 @@ message ContextMessageStatus {
message Error { message Error {
string message = 1; string message = 1;
} }
message Canceled {}
} }
message ContextMessage { message ContextMessage {