assistant2: Ensure errors are also displayed in populated new thread view (#27869)
Follow-up to https://github.com/zed-industries/zed/pull/27812 This PR makes sure these errors cases also show up in the panel's empty state even when there is past data. | No ToS | Missing Provider | |--------|--------| |  |  | Release Notes: - N/A
This commit is contained in:
parent
92059803fb
commit
192097f58f
4 changed files with 243 additions and 143 deletions
|
@ -26,7 +26,9 @@ use prompt_library::{PromptLibrary, open_prompt_library};
|
||||||
use prompt_store::PromptBuilder;
|
use prompt_store::PromptBuilder;
|
||||||
use settings::{Settings, update_settings_file};
|
use settings::{Settings, update_settings_file};
|
||||||
use time::UtcOffset;
|
use time::UtcOffset;
|
||||||
use ui::{ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle, Tab, Tooltip, prelude::*};
|
use ui::{
|
||||||
|
Banner, ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle, Tab, Tooltip, prelude::*,
|
||||||
|
};
|
||||||
use util::ResultExt as _;
|
use util::ResultExt as _;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
use workspace::dock::{DockPosition, Panel, PanelEvent};
|
use workspace::dock::{DockPosition, Panel, PanelEvent};
|
||||||
|
@ -838,6 +840,7 @@ impl AssistantPanel {
|
||||||
v_flex()
|
v_flex()
|
||||||
.size_full()
|
.size_full()
|
||||||
.when(recent_history.is_empty(), |this| {
|
.when(recent_history.is_empty(), |this| {
|
||||||
|
let configuration_error_ref = &configuration_error;
|
||||||
this.child(
|
this.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.size_full()
|
.size_full()
|
||||||
|
@ -852,7 +855,8 @@ impl AssistantPanel {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.when(no_error, |parent| {
|
.when(no_error, |parent| {
|
||||||
parent.child(
|
parent
|
||||||
|
.child(
|
||||||
h_flex().child(
|
h_flex().child(
|
||||||
Label::new("Ask and build anything.")
|
Label::new("Ask and build anything.")
|
||||||
.color(Color::Muted)
|
.color(Color::Muted)
|
||||||
|
@ -929,7 +933,7 @@ impl AssistantPanel {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map(|parent| {
|
.map(|parent| {
|
||||||
match configuration_error {
|
match configuration_error_ref {
|
||||||
Some(ConfigurationError::ProviderNotAuthenticated)
|
Some(ConfigurationError::ProviderNotAuthenticated)
|
||||||
| Some(ConfigurationError::NoProvider) => {
|
| Some(ConfigurationError::NoProvider) => {
|
||||||
parent
|
parent
|
||||||
|
@ -958,19 +962,23 @@ impl AssistantPanel {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => parent
|
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => {
|
||||||
.children(
|
parent.children(
|
||||||
provider.render_accept_terms(
|
provider.render_accept_terms(
|
||||||
LanguageModelProviderTosView::ThreadEmptyState,
|
LanguageModelProviderTosView::ThreadFreshStart,
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
|
}
|
||||||
None => parent,
|
None => parent,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.when(!recent_history.is_empty(), |parent| {
|
.when(!recent_history.is_empty(), |parent| {
|
||||||
|
let focus_handle = focus_handle.clone();
|
||||||
|
let configuration_error_ref = &configuration_error;
|
||||||
|
|
||||||
parent
|
parent
|
||||||
.p_1p5()
|
.p_1p5()
|
||||||
.justify_end()
|
.justify_end()
|
||||||
|
@ -992,18 +1000,23 @@ impl AssistantPanel {
|
||||||
Button::new("view-history", "View All")
|
Button::new("view-history", "View All")
|
||||||
.style(ButtonStyle::Subtle)
|
.style(ButtonStyle::Subtle)
|
||||||
.label_size(LabelSize::Small)
|
.label_size(LabelSize::Small)
|
||||||
.key_binding(KeyBinding::for_action_in(
|
.key_binding(
|
||||||
|
KeyBinding::for_action_in(
|
||||||
&OpenHistory,
|
&OpenHistory,
|
||||||
&self.focus_handle(cx),
|
&self.focus_handle(cx),
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
).map(|kb| kb.size(rems_from_px(12.))),)
|
).map(|kb| kb.size(rems_from_px(12.))),
|
||||||
|
)
|
||||||
.on_click(move |_event, window, cx| {
|
.on_click(move |_event, window, cx| {
|
||||||
window.dispatch_action(OpenHistory.boxed_clone(), cx);
|
window.dispatch_action(OpenHistory.boxed_clone(), cx);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(v_flex().gap_1().children(
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.gap_1()
|
||||||
|
.children(
|
||||||
recent_history.into_iter().map(|entry| {
|
recent_history.into_iter().map(|entry| {
|
||||||
// TODO: Add keyboard navigation.
|
// TODO: Add keyboard navigation.
|
||||||
match entry {
|
match entry {
|
||||||
|
@ -1017,7 +1030,64 @@ impl AssistantPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
.map(|parent| {
|
||||||
|
match configuration_error_ref {
|
||||||
|
Some(ConfigurationError::ProviderNotAuthenticated)
|
||||||
|
| Some(ConfigurationError::NoProvider) => {
|
||||||
|
parent
|
||||||
|
.child(
|
||||||
|
Banner::new()
|
||||||
|
.severity(ui::Severity::Warning)
|
||||||
|
.children(
|
||||||
|
Label::new(
|
||||||
|
"Configure at least one LLM provider to start using the panel.",
|
||||||
|
)
|
||||||
|
.size(LabelSize::Small),
|
||||||
|
)
|
||||||
|
.action_slot(
|
||||||
|
Button::new("settings", "Configure Provider")
|
||||||
|
.style(ButtonStyle::Tinted(ui::TintColor::Warning))
|
||||||
|
.label_size(LabelSize::Small)
|
||||||
|
.key_binding(
|
||||||
|
KeyBinding::for_action_in(
|
||||||
|
&OpenConfiguration,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.map(|kb| kb.size(rems_from_px(12.))),
|
||||||
|
)
|
||||||
|
.on_click(|_event, window, cx| {
|
||||||
|
window.dispatch_action(
|
||||||
|
OpenConfiguration.boxed_clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => {
|
||||||
|
parent
|
||||||
|
.child(
|
||||||
|
Banner::new()
|
||||||
|
.severity(ui::Severity::Warning)
|
||||||
|
.children(
|
||||||
|
h_flex()
|
||||||
|
.w_full()
|
||||||
|
.children(
|
||||||
|
provider.render_accept_terms(
|
||||||
|
LanguageModelProviderTosView::ThreadtEmptyState,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
None => parent,
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -353,7 +353,10 @@ pub trait LanguageModelProvider: 'static {
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub enum LanguageModelProviderTosView {
|
pub enum LanguageModelProviderTosView {
|
||||||
ThreadEmptyState,
|
/// When there are some past interactions in the Agent Panel.
|
||||||
|
ThreadtEmptyState,
|
||||||
|
/// When there are no past interactions in the Agent Panel.
|
||||||
|
ThreadFreshStart,
|
||||||
PromptEditorPopup,
|
PromptEditorPopup,
|
||||||
Configuration,
|
Configuration,
|
||||||
}
|
}
|
||||||
|
|
|
@ -401,39 +401,30 @@ fn render_accept_terms(
|
||||||
|
|
||||||
let accept_terms_disabled = state.read(cx).accept_terms.is_some();
|
let accept_terms_disabled = state.read(cx).accept_terms.is_some();
|
||||||
|
|
||||||
|
let thread_fresh_start = matches!(view_kind, LanguageModelProviderTosView::ThreadFreshStart);
|
||||||
|
let thread_empty_state = matches!(view_kind, LanguageModelProviderTosView::ThreadtEmptyState);
|
||||||
|
|
||||||
let terms_button = Button::new("terms_of_service", "Terms of Service")
|
let terms_button = Button::new("terms_of_service", "Terms of Service")
|
||||||
.style(ButtonStyle::Subtle)
|
.style(ButtonStyle::Subtle)
|
||||||
.icon(IconName::ArrowUpRight)
|
.icon(IconName::ArrowUpRight)
|
||||||
.icon_color(Color::Muted)
|
.icon_color(Color::Muted)
|
||||||
.icon_size(IconSize::XSmall)
|
.icon_size(IconSize::XSmall)
|
||||||
|
.when(thread_empty_state, |this| this.label_size(LabelSize::Small))
|
||||||
.on_click(move |_, _window, cx| cx.open_url("https://zed.dev/terms-of-service"));
|
.on_click(move |_, _window, cx| cx.open_url("https://zed.dev/terms-of-service"));
|
||||||
|
|
||||||
let thread_view = match view_kind {
|
let button_container = h_flex().child(
|
||||||
LanguageModelProviderTosView::ThreadEmptyState => true,
|
|
||||||
LanguageModelProviderTosView::PromptEditorPopup => false,
|
|
||||||
LanguageModelProviderTosView::Configuration => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let form = v_flex()
|
|
||||||
.w_full()
|
|
||||||
.gap_2()
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.flex_wrap()
|
|
||||||
.when(thread_view, |this| this.justify_center())
|
|
||||||
.child(Label::new(
|
|
||||||
"To start using Zed AI, please read and accept the",
|
|
||||||
))
|
|
||||||
.child(terms_button),
|
|
||||||
)
|
|
||||||
.child({
|
|
||||||
let button_container = h_flex().w_full().child(
|
|
||||||
Button::new("accept_terms", "I accept the Terms of Service")
|
Button::new("accept_terms", "I accept the Terms of Service")
|
||||||
|
.when(!thread_empty_state, |this| {
|
||||||
|
this.full_width()
|
||||||
.style(ButtonStyle::Tinted(TintColor::Accent))
|
.style(ButtonStyle::Tinted(TintColor::Accent))
|
||||||
.icon(IconName::Check)
|
.icon(IconName::Check)
|
||||||
.icon_position(IconPosition::Start)
|
.icon_position(IconPosition::Start)
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
.full_width()
|
})
|
||||||
|
.when(thread_empty_state, |this| {
|
||||||
|
this.style(ButtonStyle::Tinted(TintColor::Warning))
|
||||||
|
.label_size(LabelSize::Small)
|
||||||
|
})
|
||||||
.disabled(accept_terms_disabled)
|
.disabled(accept_terms_disabled)
|
||||||
.on_click({
|
.on_click({
|
||||||
let state = state.downgrade();
|
let state = state.downgrade();
|
||||||
|
@ -445,12 +436,48 @@ fn render_accept_terms(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let form = if thread_empty_state {
|
||||||
|
h_flex()
|
||||||
|
.w_full()
|
||||||
|
.flex_wrap()
|
||||||
|
.justify_between()
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.child(
|
||||||
|
Label::new("To start using Zed AI, please read and accept the")
|
||||||
|
.size(LabelSize::Small),
|
||||||
|
)
|
||||||
|
.child(terms_button),
|
||||||
|
)
|
||||||
|
.child(button_container)
|
||||||
|
} else {
|
||||||
|
v_flex()
|
||||||
|
.w_full()
|
||||||
|
.gap_2()
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.flex_wrap()
|
||||||
|
.when(thread_fresh_start, |this| this.justify_center())
|
||||||
|
.child(Label::new(
|
||||||
|
"To start using Zed AI, please read and accept the",
|
||||||
|
))
|
||||||
|
.child(terms_button),
|
||||||
|
)
|
||||||
|
.child({
|
||||||
match view_kind {
|
match view_kind {
|
||||||
LanguageModelProviderTosView::PromptEditorPopup => button_container.justify_end(),
|
LanguageModelProviderTosView::PromptEditorPopup => {
|
||||||
LanguageModelProviderTosView::Configuration => button_container.justify_start(),
|
button_container.w_full().justify_end()
|
||||||
LanguageModelProviderTosView::ThreadEmptyState => button_container.justify_center(),
|
|
||||||
}
|
}
|
||||||
});
|
LanguageModelProviderTosView::Configuration => {
|
||||||
|
button_container.w_full().justify_start()
|
||||||
|
}
|
||||||
|
LanguageModelProviderTosView::ThreadFreshStart => {
|
||||||
|
button_container.w_full().justify_center()
|
||||||
|
}
|
||||||
|
LanguageModelProviderTosView::ThreadtEmptyState => div().w_0(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
Some(form.into_any())
|
Some(form.into_any())
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,7 @@ impl RenderOnce for Banner {
|
||||||
.child(content_area)
|
.child(content_area)
|
||||||
.child(action_slot);
|
.child(action_slot);
|
||||||
} else {
|
} else {
|
||||||
container = container.px_2().child(content_area);
|
container = container.px_2().child(div().w_full().child(content_area));
|
||||||
}
|
}
|
||||||
|
|
||||||
container
|
container
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue