diff --git a/crates/assistant2/src/assistant_panel.rs b/crates/assistant2/src/assistant_panel.rs index c4f9bf747871c7d250a65eb0112a31c634349a68..b3ca227e1bb18552fed3a9f8e435cb1244bcdeb9 100644 --- a/crates/assistant2/src/assistant_panel.rs +++ b/crates/assistant2/src/assistant_panel.rs @@ -20,6 +20,7 @@ use gpui::{ }; use language::LanguageRegistry; use language_model::{LanguageModelProviderTosView, LanguageModelRegistry}; +use language_model_selector::ToggleModelSelector; use project::Project; use prompt_library::{PromptLibrary, open_prompt_library}; use prompt_store::PromptBuilder; @@ -40,7 +41,7 @@ use crate::thread_history::{PastContext, PastThread, ThreadHistory}; use crate::thread_store::ThreadStore; use crate::{ AssistantDiff, InlineAssistant, NewPromptEditor, NewThread, OpenActiveThreadAsMarkdown, - OpenAssistantDiff, OpenConfiguration, OpenHistory, + OpenAssistantDiff, OpenConfiguration, OpenHistory, ToggleContextPicker, }; action_with_deprecated_aliases!( @@ -830,66 +831,150 @@ impl AssistantPanel { .history_store .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 no_error = configuration_error.is_none(); + let focus_handle = self.focus_handle(cx); v_flex() - .p_1p5() .size_full() - .justify_end() - .gap_1() - .map(|parent| { - match configuration_error { - Some(ConfigurationError::ProviderNotAuthenticated) - | Some(ConfigurationError::NoProvider) => { - parent.child( - v_flex() - .px_1p5() - .gap_0p5() - .child(create_welcome_heading()) - .child( - 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, + .when(recent_history.is_empty(), |this| { + this.child( + v_flex() + .size_full() + .max_w_80() + .mx_auto() + .justify_center() + .items_center() + .gap_1() + .child( + h_flex().child( + Headline::new("Welcome to the Assistant Panel") ), - )), - None => parent, - } - }) - .when(recent_history.is_empty() && no_error, |parent| { - parent.child(v_flex().gap_0p5().child(create_welcome_heading()).child( - Label::new("Start typing to chat with your codebase").color(Color::Muted), - )) + ) + .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) + }), + ) + }) + .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| { parent + .p_1p5() + .justify_end() + .gap_1() .child( h_flex() .pl_1p5() @@ -912,7 +997,7 @@ impl AssistantPanel { &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); }), @@ -920,7 +1005,7 @@ impl AssistantPanel { ) .child(v_flex().gap_1().children( recent_history.into_iter().map(|entry| { - // TODO: Add keyboard navigation. + // TODO: Add keyboard navigation. match entry { HistoryEntry::Thread(thread) => { PastThread::new(thread, cx.entity().downgrade(), false) diff --git a/crates/language_models/src/provider/cloud.rs b/crates/language_models/src/provider/cloud.rs index ee6ab37e5f3db1c94e0cfeb4d27410a498b1daf6..d75a9a38c5f5f363f07cb7cc71c6183fb5936e5c 100644 --- a/crates/language_models/src/provider/cloud.rs +++ b/crates/language_models/src/provider/cloud.rs @@ -410,7 +410,11 @@ fn render_accept_terms( .icon_size(IconSize::XSmall) .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() .w_full() @@ -418,14 +422,20 @@ fn render_accept_terms( .child( h_flex() .flex_wrap() - .items_start() - .child(Label::new(text)) + .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") .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(); @@ -439,10 +449,8 @@ fn render_accept_terms( match view_kind { LanguageModelProviderTosView::PromptEditorPopup => button_container.justify_end(), - LanguageModelProviderTosView::Configuration - | LanguageModelProviderTosView::ThreadEmptyState => { - button_container.justify_start() - } + LanguageModelProviderTosView::Configuration => button_container.justify_start(), + LanguageModelProviderTosView::ThreadEmptyState => button_container.justify_center(), } });