assistant: Show error messages in popover notification (#15808)
<img width="644" alt="image" src="https://github.com/user-attachments/assets/ee6f2e60-e50a-481e-98b4-6c4b72a9b882"> Release Notes: - N/A --------- Co-authored-by: Thorsten <thorsten@zed.dev>
This commit is contained in:
parent
294892c470
commit
be0ccf47ee
2 changed files with 98 additions and 40 deletions
|
@ -34,9 +34,9 @@ use fs::Fs;
|
|||
use gpui::{
|
||||
div, percentage, point, Action, Animation, AnimationExt, AnyElement, AnyView, AppContext,
|
||||
AsyncWindowContext, ClipboardItem, Context as _, DismissEvent, Empty, Entity, EventEmitter,
|
||||
FocusHandle, FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Pixels,
|
||||
Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task, Transformation,
|
||||
UpdateGlobal, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
FocusHandle, FocusableView, FontWeight, InteractiveElement, IntoElement, Model, ParentElement,
|
||||
Pixels, Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task,
|
||||
Transformation, UpdateGlobal, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
};
|
||||
use indexed_docs::IndexedDocsStore;
|
||||
use language::{
|
||||
|
@ -1316,6 +1316,7 @@ pub struct ContextEditor {
|
|||
_subscriptions: Vec<Subscription>,
|
||||
active_edit_step: Option<ActiveEditStep>,
|
||||
assistant_panel: WeakView<AssistantPanel>,
|
||||
error_message: Option<SharedString>,
|
||||
}
|
||||
|
||||
const DEFAULT_TAB_TITLE: &str = "New Context";
|
||||
|
@ -1373,6 +1374,7 @@ impl ContextEditor {
|
|||
_subscriptions,
|
||||
active_edit_step: None,
|
||||
assistant_panel,
|
||||
error_message: None,
|
||||
};
|
||||
this.update_message_headers(cx);
|
||||
this.insert_slash_command_output_sections(sections, cx);
|
||||
|
@ -1406,7 +1408,9 @@ impl ContextEditor {
|
|||
|
||||
fn assist(&mut self, _: &Assist, cx: &mut ViewContext<Self>) {
|
||||
if !self.apply_edit_step(cx) {
|
||||
self.error_message = None;
|
||||
self.send_to_model(cx);
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1804,6 +1808,9 @@ impl ContextEditor {
|
|||
}
|
||||
}
|
||||
ContextEvent::Operation(_) => {}
|
||||
ContextEvent::AssistError(error_message) => {
|
||||
self.error_message = Some(SharedString::from(error_message.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2142,7 +2149,10 @@ impl ContextEditor {
|
|||
div()
|
||||
.id("error")
|
||||
.tooltip(move |cx| Tooltip::text(error.clone(), cx))
|
||||
.child(Icon::new(IconName::XCircle)),
|
||||
.child(
|
||||
Icon::new(IconName::ExclamationTriangle)
|
||||
.color(Color::Error),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
|
@ -2327,25 +2337,33 @@ impl ContextEditor {
|
|||
.unwrap_or_else(|| Cow::Borrowed(DEFAULT_TAB_TITLE))
|
||||
}
|
||||
|
||||
fn render_notice(&self, cx: &mut ViewContext<Self>) -> Option<impl IntoElement> {
|
||||
fn dismiss_error_message(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.error_message = None;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn render_notice(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
|
||||
let nudge = self
|
||||
.assistant_panel
|
||||
.upgrade()
|
||||
.map(|assistant_panel| assistant_panel.read(cx).show_zed_ai_notice);
|
||||
|
||||
if nudge.unwrap_or(false) {
|
||||
if let Some(error) = self.error_message.clone() {
|
||||
Some(Self::render_error_popover(error, cx).into_any_element())
|
||||
} else if nudge.unwrap_or(false) {
|
||||
Some(
|
||||
v_flex()
|
||||
.elevation_3(cx)
|
||||
.p_4()
|
||||
.p_2()
|
||||
.gap_2()
|
||||
.child(Label::new("Use Zed AI"))
|
||||
.child(
|
||||
div()
|
||||
.id("sign-in")
|
||||
.child(Label::new("Sign in to use Zed AI"))
|
||||
.cursor_pointer()
|
||||
.on_click(cx.listener(|this, _event, cx| {
|
||||
Label::new("Use Zed AI")
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(h_flex().justify_end().child(
|
||||
Button::new("sign-in", "Sign in to use Zed AI").on_click(cx.listener(
|
||||
|this, _event, cx| {
|
||||
let client = this
|
||||
.workspace
|
||||
.update(cx, |workspace, _| workspace.client().clone())
|
||||
|
@ -2358,8 +2376,10 @@ impl ContextEditor {
|
|||
})
|
||||
.detach_and_log_err(cx)
|
||||
}
|
||||
})),
|
||||
),
|
||||
},
|
||||
)),
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
} else if let Some(configuration_error) = configuration_error(cx) {
|
||||
let label = match configuration_error {
|
||||
|
@ -2369,27 +2389,51 @@ impl ContextEditor {
|
|||
Some(
|
||||
v_flex()
|
||||
.elevation_3(cx)
|
||||
.p_4()
|
||||
.p_2()
|
||||
.gap_2()
|
||||
.child(Label::new(label))
|
||||
.child(Label::new(label).size(LabelSize::Small).color(Color::Muted))
|
||||
.child(
|
||||
div()
|
||||
.id("open-configuration")
|
||||
.child(Label::new("Open configuration"))
|
||||
.cursor_pointer()
|
||||
.on_click({
|
||||
let focus_handle = self.focus_handle(cx).clone();
|
||||
move |_event, cx| {
|
||||
focus_handle.dispatch_action(&ShowConfiguration, cx);
|
||||
}
|
||||
}),
|
||||
),
|
||||
h_flex().justify_end().child(
|
||||
Button::new("open-configuration", "Open configuration")
|
||||
.icon(IconName::Settings)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click({
|
||||
let focus_handle = self.focus_handle(cx).clone();
|
||||
move |_event, cx| {
|
||||
focus_handle.dispatch_action(&ShowConfiguration, cx);
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
.into_any_element(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn render_error_popover(error: SharedString, cx: &mut ViewContext<Self>) -> Div {
|
||||
v_flex()
|
||||
.p_2()
|
||||
.elevation_2(cx)
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
.min_w_24()
|
||||
.occlude()
|
||||
.child(
|
||||
Label::new("Error interacting with language model")
|
||||
.size(LabelSize::Small)
|
||||
.weight(FontWeight::BOLD)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(Label::new(error).size(LabelSize::Small))
|
||||
.child(
|
||||
h_flex().justify_end().child(
|
||||
Button::new("dismiss", "Dismiss")
|
||||
.on_click(cx.listener(|this, _, cx| this.dismiss_error_message(cx))),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_send_button(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let focus_handle = self.focus_handle(cx).clone();
|
||||
let button_text = match self.workflow_step_for_cursor(cx) {
|
||||
|
@ -2487,23 +2531,32 @@ impl Render for ContextEditor {
|
|||
div()
|
||||
.flex_grow()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(self.editor.clone())
|
||||
.child(self.editor.clone()),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_none()
|
||||
.relative()
|
||||
.when_some(self.render_notice(cx), |this, notice| {
|
||||
this.child(
|
||||
div()
|
||||
.absolute()
|
||||
.w_3_4()
|
||||
.min_w_24()
|
||||
.max_w_128()
|
||||
.right_4()
|
||||
.bottom_9()
|
||||
.child(notice),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.absolute()
|
||||
.bottom_0()
|
||||
.p_4()
|
||||
.right_4()
|
||||
.bottom_2()
|
||||
.justify_end()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.items_end()
|
||||
.when_some(self.render_notice(cx), |this, notice| {
|
||||
this.child(notice)
|
||||
})
|
||||
.child(self.render_send_button(cx)),
|
||||
),
|
||||
.child(self.render_send_button(cx)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -281,6 +281,7 @@ impl ContextOperation {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ContextEvent {
|
||||
AssistError(String),
|
||||
MessagesEdited,
|
||||
SummaryChanged,
|
||||
EditStepsChanged,
|
||||
|
@ -1580,6 +1581,10 @@ impl Context {
|
|||
.err()
|
||||
.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()));
|
||||
}
|
||||
|
||||
this.update_metadata(assistant_message_id, cx, |metadata| {
|
||||
if let Some(error_message) = error_message.as_ref() {
|
||||
metadata.status =
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue