assistant: Show errors without mouse interaction (#16226)

https://github.com/user-attachments/assets/a8e5e0ce-349d-4836-afe6-fc960a307c9f


Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
Co-authored-by: Danilo <danilo@zed.dev>
This commit is contained in:
Bennet Bo Fenner 2024-08-14 17:18:39 +02:00 committed by GitHub
parent f5532afaa7
commit caf222a71d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 87 additions and 75 deletions

View File

@ -1973,6 +1973,8 @@ impl ContextEditor {
}
fn cancel(&mut self, _: &editor::actions::Cancel, cx: &mut ViewContext<Self>) {
self.error_message = None;
if self
.context
.update(cx, |context, cx| context.cancel_last_assist(cx))
@ -2348,8 +2350,8 @@ impl ContextEditor {
}
}
ContextEvent::Operation(_) => {}
ContextEvent::AssistError(error_message) => {
self.error_message = Some(SharedString::from(error_message.clone()));
ContextEvent::ShowAssistError(error_message) => {
self.error_message = Some(error_message.clone());
}
}
}
@ -3045,40 +3047,36 @@ impl ContextEditor {
.gap_1()
.child(sender)
.children(match &message.status {
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(
PopoverMenu::new("show-error-popover")
.menu({
let error = error.clone();
move |cx| {
Some(cx.new_view(|cx| ErrorPopover {
error: error.clone(),
focus_handle: cx.focus_handle(),
}))
}
})
.trigger(error_popover_trigger)
.into_any_element(),
)
}
MessageStatus::Error(error) => Some(
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,
)
})
.on_click({
let context = context.clone();
let error = error.clone();
move |_, cx| {
context.update(cx, |_, cx| {
cx.emit(ContextEvent::ShowAssistError(
error.clone(),
));
});
}
})
.into_any_element(),
),
MessageStatus::Canceled => Some(
ButtonLike::new("canceled")
.child(
@ -3579,10 +3577,13 @@ impl ContextEditor {
};
let provider = LanguageModelRegistry::read_global(cx).active_provider();
let disabled = self.show_accept_terms
let needs_to_accept_terms = self.show_accept_terms
&& provider
.as_ref()
.map_or(false, |provider| provider.must_accept_terms(cx));
let has_active_error = self.error_message.is_some();
let disabled = needs_to_accept_terms || has_active_error;
ButtonLike::new("send_button")
.disabled(disabled)
@ -3677,6 +3678,48 @@ impl Render for ContextEditor {
.child(element),
)
})
.when_some(self.error_message.clone(), |this, error_message| {
this.child(
div()
.absolute()
.right_4()
.bottom_10()
.max_w_96()
.py_2()
.px_3()
.elevation_2(cx)
.occlude()
.child(
v_flex()
.gap_0p5()
.child(
h_flex()
.gap_1()
.items_center()
.child(Icon::new(IconName::XCircle).color(Color::Error))
.child(
Label::new("Error interacting with language model")
.weight(FontWeight::SEMIBOLD),
),
)
.child(
div()
.id("error-message")
.max_h_24()
.overflow_y_scroll()
.child(Label::new(error_message)),
)
.child(h_flex().justify_end().mt_1().child(
Button::new("dismiss", "Dismiss").on_click(cx.listener(
|this, _, cx| {
this.error_message = None;
cx.notify();
},
)),
)),
),
)
})
.child(
h_flex().flex_none().relative().child(
h_flex()
@ -3691,41 +3734,6 @@ impl Render for ContextEditor {
}
}
struct ErrorPopover {
error: SharedString,
focus_handle: FocusHandle,
}
impl EventEmitter<DismissEvent> for ErrorPopover {}
impl FocusableView for ErrorPopover {
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
self.focus_handle.clone()
}
}
impl Render for ErrorPopover {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
v_flex()
.mt_2()
.max_w_96()
.py_2()
.px_3()
.gap_0p5()
.elevation_2(cx)
.bg(cx.theme().colors().surface_background)
.occlude()
.child(Label::new("Error interacting with language model").weight(FontWeight::SEMIBOLD))
.child(Label::new(self.error.clone()))
.child(
h_flex().justify_end().mt_1().child(
Button::new("dismiss", "Dismiss")
.on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))),
),
)
}
}
impl FocusableView for ContextEditor {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
self.editor.focus_handle(cx)

View File

@ -285,7 +285,7 @@ impl ContextOperation {
#[derive(Debug, Clone)]
pub enum ContextEvent {
AssistError(String),
ShowAssistError(SharedString),
MessagesEdited,
SummaryChanged,
WorkflowStepsRemoved(Vec<Range<language::Anchor>>),
@ -1790,7 +1790,9 @@ impl Context {
.map(|error| error.to_string().trim().to_string());
if let Some(error_message) = error_message.as_ref() {
cx.emit(ContextEvent::AssistError(error_message.to_string()));
cx.emit(ContextEvent::ShowAssistError(SharedString::from(
error_message.clone(),
)));
}
this.update_metadata(assistant_message_id, cx, |metadata| {
@ -1843,7 +1845,9 @@ impl Context {
pub fn cancel_last_assist(&mut self, cx: &mut ModelContext<Self>) -> bool {
if let Some(pending_completion) = self.pending_completions.pop() {
self.update_metadata(pending_completion.assistant_message_id, cx, |metadata| {
metadata.status = MessageStatus::Canceled;
if metadata.status == MessageStatus::Pending {
metadata.status = MessageStatus::Canceled;
}
});
true
} else {