assistant2: Refine empty states design (#27812)
| No LLM provider | Fresh Start | No ToS | |--------|--------|--------| |  |  |  | Release Notes: - N/A
This commit is contained in:
parent
a1bef28da3
commit
dce824f095
2 changed files with 154 additions and 61 deletions
|
@ -20,6 +20,7 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
use language_model::{LanguageModelProviderTosView, LanguageModelRegistry};
|
use language_model::{LanguageModelProviderTosView, LanguageModelRegistry};
|
||||||
|
use language_model_selector::ToggleModelSelector;
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use prompt_library::{PromptLibrary, open_prompt_library};
|
use prompt_library::{PromptLibrary, open_prompt_library};
|
||||||
use prompt_store::PromptBuilder;
|
use prompt_store::PromptBuilder;
|
||||||
|
@ -40,7 +41,7 @@ use crate::thread_history::{PastContext, PastThread, ThreadHistory};
|
||||||
use crate::thread_store::ThreadStore;
|
use crate::thread_store::ThreadStore;
|
||||||
use crate::{
|
use crate::{
|
||||||
AssistantDiff, InlineAssistant, NewPromptEditor, NewThread, OpenActiveThreadAsMarkdown,
|
AssistantDiff, InlineAssistant, NewPromptEditor, NewThread, OpenActiveThreadAsMarkdown,
|
||||||
OpenAssistantDiff, OpenConfiguration, OpenHistory,
|
OpenAssistantDiff, OpenConfiguration, OpenHistory, ToggleContextPicker,
|
||||||
};
|
};
|
||||||
|
|
||||||
action_with_deprecated_aliases!(
|
action_with_deprecated_aliases!(
|
||||||
|
@ -830,66 +831,150 @@ impl AssistantPanel {
|
||||||
.history_store
|
.history_store
|
||||||
.update(cx, |this, cx| this.recent_entries(6, cx));
|
.update(cx, |this, cx| this.recent_entries(6, cx));
|
||||||
|
|
||||||
let create_welcome_heading = || {
|
|
||||||
h_flex()
|
|
||||||
.w_full()
|
|
||||||
.child(Headline::new("Welcome to the Assistant Panel").size(HeadlineSize::Small))
|
|
||||||
};
|
|
||||||
|
|
||||||
let configuration_error = self.configuration_error(cx);
|
let configuration_error = self.configuration_error(cx);
|
||||||
let no_error = configuration_error.is_none();
|
let no_error = configuration_error.is_none();
|
||||||
|
let focus_handle = self.focus_handle(cx);
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.p_1p5()
|
|
||||||
.size_full()
|
.size_full()
|
||||||
.justify_end()
|
.when(recent_history.is_empty(), |this| {
|
||||||
.gap_1()
|
this.child(
|
||||||
.map(|parent| {
|
v_flex()
|
||||||
match configuration_error {
|
.size_full()
|
||||||
Some(ConfigurationError::ProviderNotAuthenticated)
|
.max_w_80()
|
||||||
| Some(ConfigurationError::NoProvider) => {
|
.mx_auto()
|
||||||
parent.child(
|
.justify_center()
|
||||||
v_flex()
|
.items_center()
|
||||||
.px_1p5()
|
.gap_1()
|
||||||
.gap_0p5()
|
.child(
|
||||||
.child(create_welcome_heading())
|
h_flex().child(
|
||||||
.child(
|
Headline::new("Welcome to the Assistant Panel")
|
||||||
Label::new(
|
|
||||||
"To start using the assistant, configure at least one LLM provider.",
|
|
||||||
)
|
|
||||||
.color(Color::Muted),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
h_flex().mt_1().w_full().child(
|
|
||||||
Button::new("open-configuration", "Configure a Provider")
|
|
||||||
.size(ButtonSize::Compact)
|
|
||||||
.icon(Some(IconName::Sliders))
|
|
||||||
.icon_size(IconSize::Small)
|
|
||||||
.icon_position(IconPosition::Start)
|
|
||||||
.on_click(cx.listener(|this, _, window, cx| {
|
|
||||||
this.open_configuration(window, cx);
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => parent
|
|
||||||
.child(v_flex().px_1p5().gap_0p5().child(create_welcome_heading()).children(
|
|
||||||
provider.render_accept_terms(
|
|
||||||
LanguageModelProviderTosView::ThreadEmptyState,
|
|
||||||
cx,
|
|
||||||
),
|
),
|
||||||
)),
|
)
|
||||||
None => parent,
|
.when(no_error, |parent| {
|
||||||
}
|
parent.child(
|
||||||
})
|
h_flex().child(
|
||||||
.when(recent_history.is_empty() && no_error, |parent| {
|
Label::new("Ask and build anything.")
|
||||||
parent.child(v_flex().gap_0p5().child(create_welcome_heading()).child(
|
.color(Color::Muted)
|
||||||
Label::new("Start typing to chat with your codebase").color(Color::Muted),
|
.mb_2p5(),
|
||||||
))
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("new-thread", "Start New Thread")
|
||||||
|
.icon(IconName::Plus)
|
||||||
|
.icon_position(IconPosition::Start)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.icon_color(Color::Muted)
|
||||||
|
.full_width()
|
||||||
|
.key_binding(KeyBinding::for_action_in(
|
||||||
|
&NewThread,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
|
.on_click(|_event, window, cx| {
|
||||||
|
window.dispatch_action(NewThread.boxed_clone(), cx)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("context", "Add Context")
|
||||||
|
.icon(IconName::FileCode)
|
||||||
|
.icon_position(IconPosition::Start)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.icon_color(Color::Muted)
|
||||||
|
.full_width()
|
||||||
|
.key_binding(KeyBinding::for_action_in(
|
||||||
|
&ToggleContextPicker,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
|
.on_click(|_event, window, cx| {
|
||||||
|
window.dispatch_action(ToggleContextPicker.boxed_clone(), cx)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("mode", "Switch Model")
|
||||||
|
.icon(IconName::DatabaseZap)
|
||||||
|
.icon_position(IconPosition::Start)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.icon_color(Color::Muted)
|
||||||
|
.full_width()
|
||||||
|
.key_binding(KeyBinding::for_action_in(
|
||||||
|
&ToggleModelSelector,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
|
.on_click(|_event, window, cx| {
|
||||||
|
window.dispatch_action(ToggleModelSelector.boxed_clone(), cx)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("settings", "View Settings")
|
||||||
|
.icon(IconName::Settings)
|
||||||
|
.icon_position(IconPosition::Start)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.icon_color(Color::Muted)
|
||||||
|
.full_width()
|
||||||
|
.key_binding(KeyBinding::for_action_in(
|
||||||
|
&OpenConfiguration,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
|
.on_click(|_event, window, cx| {
|
||||||
|
window.dispatch_action(OpenConfiguration.boxed_clone(), cx)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|parent| {
|
||||||
|
match configuration_error {
|
||||||
|
Some(ConfigurationError::ProviderNotAuthenticated)
|
||||||
|
| Some(ConfigurationError::NoProvider) => {
|
||||||
|
parent
|
||||||
|
.child(
|
||||||
|
h_flex().child(
|
||||||
|
Label::new("To start using the assistant, configure at least one LLM provider.")
|
||||||
|
.color(Color::Muted)
|
||||||
|
.mb_2p5()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("settings", "Configure a Provider")
|
||||||
|
.icon(IconName::Settings)
|
||||||
|
.icon_position(IconPosition::Start)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.icon_color(Color::Muted)
|
||||||
|
.full_width()
|
||||||
|
.key_binding(KeyBinding::for_action_in(
|
||||||
|
&OpenConfiguration,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
|
.on_click(|_event, window, cx| {
|
||||||
|
window.dispatch_action(OpenConfiguration.boxed_clone(), cx)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => parent
|
||||||
|
.children(
|
||||||
|
provider.render_accept_terms(
|
||||||
|
LanguageModelProviderTosView::ThreadEmptyState,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
None => parent,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.when(!recent_history.is_empty(), |parent| {
|
.when(!recent_history.is_empty(), |parent| {
|
||||||
parent
|
parent
|
||||||
|
.p_1p5()
|
||||||
|
.justify_end()
|
||||||
|
.gap_1()
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.pl_1p5()
|
.pl_1p5()
|
||||||
|
@ -912,7 +997,7 @@ impl AssistantPanel {
|
||||||
&self.focus_handle(cx),
|
&self.focus_handle(cx),
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
))
|
).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);
|
||||||
}),
|
}),
|
||||||
|
@ -920,7 +1005,7 @@ impl AssistantPanel {
|
||||||
)
|
)
|
||||||
.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 {
|
||||||
HistoryEntry::Thread(thread) => {
|
HistoryEntry::Thread(thread) => {
|
||||||
PastThread::new(thread, cx.entity().downgrade(), false)
|
PastThread::new(thread, cx.entity().downgrade(), false)
|
||||||
|
|
|
@ -410,7 +410,11 @@ fn render_accept_terms(
|
||||||
.icon_size(IconSize::XSmall)
|
.icon_size(IconSize::XSmall)
|
||||||
.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 text = "To start using Zed AI, please read and accept the";
|
let thread_view = match view_kind {
|
||||||
|
LanguageModelProviderTosView::ThreadEmptyState => true,
|
||||||
|
LanguageModelProviderTosView::PromptEditorPopup => false,
|
||||||
|
LanguageModelProviderTosView::Configuration => false,
|
||||||
|
};
|
||||||
|
|
||||||
let form = v_flex()
|
let form = v_flex()
|
||||||
.w_full()
|
.w_full()
|
||||||
|
@ -418,14 +422,20 @@ fn render_accept_terms(
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.flex_wrap()
|
.flex_wrap()
|
||||||
.items_start()
|
.when(thread_view, |this| this.justify_center())
|
||||||
.child(Label::new(text))
|
.child(Label::new(
|
||||||
|
"To start using Zed AI, please read and accept the",
|
||||||
|
))
|
||||||
.child(terms_button),
|
.child(terms_button),
|
||||||
)
|
)
|
||||||
.child({
|
.child({
|
||||||
let button_container = h_flex().w_full().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")
|
||||||
.style(ButtonStyle::Tinted(TintColor::Accent))
|
.style(ButtonStyle::Tinted(TintColor::Accent))
|
||||||
|
.icon(IconName::Check)
|
||||||
|
.icon_position(IconPosition::Start)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.full_width()
|
||||||
.disabled(accept_terms_disabled)
|
.disabled(accept_terms_disabled)
|
||||||
.on_click({
|
.on_click({
|
||||||
let state = state.downgrade();
|
let state = state.downgrade();
|
||||||
|
@ -439,10 +449,8 @@ fn render_accept_terms(
|
||||||
|
|
||||||
match view_kind {
|
match view_kind {
|
||||||
LanguageModelProviderTosView::PromptEditorPopup => button_container.justify_end(),
|
LanguageModelProviderTosView::PromptEditorPopup => button_container.justify_end(),
|
||||||
LanguageModelProviderTosView::Configuration
|
LanguageModelProviderTosView::Configuration => button_container.justify_start(),
|
||||||
| LanguageModelProviderTosView::ThreadEmptyState => {
|
LanguageModelProviderTosView::ThreadEmptyState => button_container.justify_center(),
|
||||||
button_container.justify_start()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue