@@ -26,7 +26,9 @@ use prompt_library::{PromptLibrary, open_prompt_library};
use prompt_store::PromptBuilder;
use settings::{Settings, update_settings_file};
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 workspace::Workspace;
use workspace::dock::{DockPosition, Panel, PanelEvent};
@@ -838,6 +840,7 @@ impl AssistantPanel {
v_flex()
.size_full()
.when(recent_history.is_empty(), |this| {
+ let configuration_error_ref = &configuration_error;
this.child(
v_flex()
.size_full()
@@ -852,84 +855,85 @@ impl AssistantPanel {
),
)
.when(no_error, |parent| {
- parent.child(
- h_flex().child(
- Label::new("Ask and build anything.")
- .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)
- }),
- )
+ parent
+ .child(
+ h_flex().child(
+ Label::new("Ask and build anything.")
+ .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 {
+ match configuration_error_ref {
Some(ConfigurationError::ProviderNotAuthenticated)
| Some(ConfigurationError::NoProvider) => {
parent
@@ -958,23 +962,27 @@ impl AssistantPanel {
}),
)
}
- Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => parent
- .children(
+ Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => {
+ parent.children(
provider.render_accept_terms(
- LanguageModelProviderTosView::ThreadEmptyState,
+ LanguageModelProviderTosView::ThreadFreshStart,
cx,
),
- ),
+ )
+ }
None => parent,
}
})
)
})
.when(!recent_history.is_empty(), |parent| {
+ let focus_handle = focus_handle.clone();
+ let configuration_error_ref = &configuration_error;
+
parent
.p_1p5()
- .justify_end()
- .gap_1()
+ .justify_end()
+ .gap_1()
.child(
h_flex()
.pl_1p5()
@@ -992,32 +1000,94 @@ impl AssistantPanel {
Button::new("view-history", "View All")
.style(ButtonStyle::Subtle)
.label_size(LabelSize::Small)
- .key_binding(KeyBinding::for_action_in(
- &OpenHistory,
- &self.focus_handle(cx),
- window,
- cx,
- ).map(|kb| kb.size(rems_from_px(12.))),)
+ .key_binding(
+ KeyBinding::for_action_in(
+ &OpenHistory,
+ &self.focus_handle(cx),
+ window,
+ cx,
+ ).map(|kb| kb.size(rems_from_px(12.))),
+ )
.on_click(move |_event, window, cx| {
window.dispatch_action(OpenHistory.boxed_clone(), cx);
}),
),
)
- .child(v_flex().gap_1().children(
- recent_history.into_iter().map(|entry| {
- // TODO: Add keyboard navigation.
- match entry {
- HistoryEntry::Thread(thread) => {
- PastThread::new(thread, cx.entity().downgrade(), false)
- .into_any_element()
- }
- HistoryEntry::Context(context) => {
- PastContext::new(context, cx.entity().downgrade(), false)
- .into_any_element()
- }
+ .child(
+ v_flex()
+ .gap_1()
+ .children(
+ recent_history.into_iter().map(|entry| {
+ // TODO: Add keyboard navigation.
+ match entry {
+ HistoryEntry::Thread(thread) => {
+ PastThread::new(thread, cx.entity().downgrade(), false)
+ .into_any_element()
+ }
+ HistoryEntry::Context(context) => {
+ PastContext::new(context, cx.entity().downgrade(), false)
+ .into_any_element()
+ }
+ }
+ }),
+ )
+ )
+ .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,
+ }
+ })
})
}
@@ -401,56 +401,83 @@ fn render_accept_terms(
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")
.style(ButtonStyle::Subtle)
.icon(IconName::ArrowUpRight)
.icon_color(Color::Muted)
.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"));
- let thread_view = match view_kind {
- 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")
+ let button_container = h_flex().child(
+ Button::new("accept_terms", "I accept the Terms of Service")
+ .when(!thread_empty_state, |this| {
+ this.full_width()
.style(ButtonStyle::Tinted(TintColor::Accent))
.icon(IconName::Check)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
- .full_width()
- .disabled(accept_terms_disabled)
- .on_click({
- let state = state.downgrade();
- move |_, _window, cx| {
- state
- .update(cx, |state, cx| state.accept_terms_of_service(cx))
- .ok();
- }
- }),
- );
-
- match view_kind {
- LanguageModelProviderTosView::PromptEditorPopup => button_container.justify_end(),
- LanguageModelProviderTosView::Configuration => button_container.justify_start(),
- LanguageModelProviderTosView::ThreadEmptyState => button_container.justify_center(),
- }
- });
+ })
+ .when(thread_empty_state, |this| {
+ this.style(ButtonStyle::Tinted(TintColor::Warning))
+ .label_size(LabelSize::Small)
+ })
+ .disabled(accept_terms_disabled)
+ .on_click({
+ let state = state.downgrade();
+ move |_, _window, cx| {
+ state
+ .update(cx, |state, cx| state.accept_terms_of_service(cx))
+ .ok();
+ }
+ }),
+ );
+
+ 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 {
+ LanguageModelProviderTosView::PromptEditorPopup => {
+ button_container.w_full().justify_end()
+ }
+ 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())
}