diff --git a/crates/assistant2/src/assistant_configuration.rs b/crates/assistant2/src/assistant_configuration.rs index 45fde2686c..b5598d0243 100644 --- a/crates/assistant2/src/assistant_configuration.rs +++ b/crates/assistant2/src/assistant_configuration.rs @@ -158,8 +158,16 @@ impl Render for AssistantConfiguration { .child( v_flex() .p(DynamicSpacing::Base16.rems(cx)) - .gap_1() - .child(Headline::new("Prompt Library").size(HeadlineSize::Small)) + .gap_2() + .child( + v_flex() + .gap_0p5() + .child(Headline::new("Prompt Library").size(HeadlineSize::Small)) + .child( + Label::new("Create reusable prompts and tag which ones you want sent in every LLM interaction.") + .color(Color::Muted), + ), + ) .child( Button::new("open-prompt-library", "Open Prompt Library") .style(ButtonStyle::Filled) diff --git a/crates/assistant2/src/assistant_panel.rs b/crates/assistant2/src/assistant_panel.rs index 221c8562d2..0f610260a0 100644 --- a/crates/assistant2/src/assistant_panel.rs +++ b/crates/assistant2/src/assistant_panel.rs @@ -14,7 +14,7 @@ use client::zed_urls; use editor::Editor; use fs::Fs; use gpui::{ - prelude::*, px, svg, Action, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter, + prelude::*, Action, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter, FocusHandle, Focusable, FontWeight, Pixels, Subscription, Task, UpdateGlobal, WeakEntity, }; use language::LanguageRegistry; @@ -596,7 +596,6 @@ impl AssistantPanel { h_flex() .id("assistant-toolbar") - .px(DynamicSpacing::Base08.rems(cx)) .h(Tab::container_height(cx)) .flex_none() .justify_between() @@ -604,72 +603,86 @@ impl AssistantPanel { .bg(cx.theme().colors().tab_bar_background) .border_b_1() .border_color(cx.theme().colors().border) + .child( + div() + .id("title") + .overflow_x_scroll() + .px(DynamicSpacing::Base08.rems(cx)) + .child(Label::new(title).text_ellipsis()), + ) .child( h_flex() - .w_full() - .gap_1() - .justify_between() - .child(Label::new(title)) + .h_full() + .pl_2() + .gap_2() + .bg(cx.theme().colors().tab_bar_background) .children(if matches!(self.active_view, ActiveView::PromptEditor) { self.context_editor .as_ref() .and_then(|editor| render_remaining_tokens(editor, cx)) } else { None - }), - ) - .child( - h_flex() - .h_full() - .pl_1p5() - .border_l_1() - .border_color(cx.theme().colors().border) - .gap(DynamicSpacing::Base02.rems(cx)) + }) .child( - PopoverMenu::new("assistant-toolbar-new-popover-menu") - .trigger_with_tooltip( - IconButton::new("new", IconName::Plus) - .icon_size(IconSize::Small) - .style(ButtonStyle::Subtle), - Tooltip::text("New…"), - ) - .anchor(Corner::TopRight) - .with_handle(self.new_item_context_menu_handle.clone()) - .menu(move |window, cx| { - Some(ContextMenu::build(window, cx, |menu, _window, _cx| { - menu.action("New Thread", NewThread.boxed_clone()) - .action("New Prompt Editor", NewPromptEditor.boxed_clone()) - })) - }), - ) - .child( - IconButton::new("open-history", IconName::HistoryRerun) - .icon_size(IconSize::Small) - .style(ButtonStyle::Subtle) - .tooltip({ - let focus_handle = self.focus_handle(cx); - move |window, cx| { - Tooltip::for_action_in( - "History", - &OpenHistory, - &focus_handle, - window, - cx, + h_flex() + .h_full() + .px(DynamicSpacing::Base08.rems(cx)) + .border_l_1() + .border_color(cx.theme().colors().border) + .gap(DynamicSpacing::Base02.rems(cx)) + .child( + PopoverMenu::new("assistant-toolbar-new-popover-menu") + .trigger_with_tooltip( + IconButton::new("new", IconName::Plus) + .icon_size(IconSize::Small) + .style(ButtonStyle::Subtle), + Tooltip::text("New…"), ) - } - }) - .on_click(move |_event, window, cx| { - window.dispatch_action(OpenHistory.boxed_clone(), cx); - }), - ) - .child( - IconButton::new("configure-assistant", IconName::Settings) - .icon_size(IconSize::Small) - .style(ButtonStyle::Subtle) - .tooltip(Tooltip::text("Assistant Settings")) - .on_click(move |_event, window, cx| { - window.dispatch_action(OpenConfiguration.boxed_clone(), cx); - }), + .anchor(Corner::TopRight) + .with_handle(self.new_item_context_menu_handle.clone()) + .menu(move |window, cx| { + Some(ContextMenu::build( + window, + cx, + |menu, _window, _cx| { + menu.action("New Thread", NewThread.boxed_clone()) + .action( + "New Prompt Editor", + NewPromptEditor.boxed_clone(), + ) + }, + )) + }), + ) + .child( + IconButton::new("open-history", IconName::HistoryRerun) + .icon_size(IconSize::Small) + .style(ButtonStyle::Subtle) + .tooltip({ + let focus_handle = self.focus_handle(cx); + move |window, cx| { + Tooltip::for_action_in( + "History", + &OpenHistory, + &focus_handle, + window, + cx, + ) + } + }) + .on_click(move |_event, window, cx| { + window.dispatch_action(OpenHistory.boxed_clone(), cx); + }), + ) + .child( + IconButton::new("configure-assistant", IconName::Settings) + .icon_size(IconSize::Small) + .style(ButtonStyle::Subtle) + .tooltip(Tooltip::text("Assistant Settings")) + .on_click(move |_event, window, cx| { + window.dispatch_action(OpenConfiguration.boxed_clone(), cx); + }), + ), ), ) } @@ -711,12 +724,11 @@ impl AssistantPanel { ) -> impl IntoElement { let recent_history = self .history_store - .update(cx, |this, cx| this.recent_entries(3, cx)); + .update(cx, |this, cx| this.recent_entries(6, cx)); let create_welcome_heading = || { h_flex() .w_full() - .justify_center() .child(Headline::new("Welcome to the Assistant Panel").size(HeadlineSize::Small)) }; @@ -724,36 +736,27 @@ impl AssistantPanel { let no_error = configuration_error.is_none(); v_flex() - .gap_2() - .child( - v_flex().w_full().child( - svg() - .path("icons/logo_96.svg") - .text_color(cx.theme().colors().text) - .w(px(40.)) - .h(px(40.)) - .mx_auto() - .mb_4(), - ), - ) + .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( - h_flex().mb_2().w_full().justify_center().child( - Label::new( - "To start using the assistant, configure at least one LLM provider.", - ) - .color(Color::Muted), - ), + Label::new( + "To start using the assistant, configure at least one LLM provider.", + ) + .color(Color::Muted), ) .child( - h_flex().w_full().justify_center().child( + h_flex().mt_1().w_full().child( Button::new("open-configuration", "Configure a Provider") .size(ButtonSize::Compact) .icon(Some(IconName::Sliders)) @@ -767,7 +770,7 @@ impl AssistantPanel { ) } Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => parent - .child(v_flex().gap_0p5().child(create_welcome_heading()).children( + .child(v_flex().px_1p5().gap_0p5().child(create_welcome_heading()).children( provider.render_accept_terms( LanguageModelProviderTosView::ThreadEmptyState, cx, @@ -778,21 +781,40 @@ impl AssistantPanel { }) .when(recent_history.is_empty() && no_error, |parent| { parent.child(v_flex().gap_0p5().child(create_welcome_heading()).child( - h_flex().w_full().justify_center().child( - Label::new("Start typing to chat with your codebase").color(Color::Muted), - ), + Label::new("Start typing to chat with your codebase").color(Color::Muted), )) }) .when(!recent_history.is_empty(), |parent| { parent .child( - h_flex().w_full().justify_center().child( - Label::new("Recent Threads:") - .size(LabelSize::Small) - .color(Color::Muted), - ), + h_flex() + .pl_1p5() + .pb_1() + .w_full() + .justify_between() + .border_b_1() + .border_color(cx.theme().colors().border_variant) + .child( + Label::new("Past Interactions") + .size(LabelSize::Small) + .color(Color::Muted), + ) + .child( + 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, + )) + .on_click(move |_event, window, cx| { + window.dispatch_action(OpenHistory.boxed_clone(), cx); + }), + ), ) - .child(v_flex().mx_auto().w_4_5().gap_2().children( + .child(v_flex().gap_1().children( recent_history.into_iter().map(|entry| { // TODO: Add keyboard navigation. match entry { @@ -807,22 +829,6 @@ impl AssistantPanel { } }), )) - .child( - h_flex().w_full().justify_center().child( - Button::new("view-all-past-threads", "View All Past Threads") - .style(ButtonStyle::Subtle) - .label_size(LabelSize::Small) - .key_binding(KeyBinding::for_action_in( - &OpenHistory, - &self.focus_handle(cx), - window, - cx, - )) - .on_click(move |_event, window, cx| { - window.dispatch_action(OpenHistory.boxed_clone(), cx); - }), - ), - ) }) } diff --git a/crates/assistant2/src/message_editor.rs b/crates/assistant2/src/message_editor.rs index 0be0af53a4..fe0e1351fc 100644 --- a/crates/assistant2/src/message_editor.rs +++ b/crates/assistant2/src/message_editor.rs @@ -314,7 +314,7 @@ impl Render for MessageEditor { .child(self.context_strip.clone()) .child( v_flex() - .gap_4() + .gap_5() .child({ let settings = ThemeSettings::get_global(cx); let text_style = TextStyle { diff --git a/crates/assistant2/src/thread_history.rs b/crates/assistant2/src/thread_history.rs index 460fb6f420..80cb8dd44b 100644 --- a/crates/assistant2/src/thread_history.rs +++ b/crates/assistant2/src/thread_history.rs @@ -254,18 +254,28 @@ impl RenderOnce for PastThread { ); ListItem::new(SharedString::from(self.thread.id.to_string())) - .outlined() + .rounded() .toggle_state(self.selected) - .start_slot( - Icon::new(IconName::MessageCircle) - .size(IconSize::Small) - .color(Color::Muted), - ) .spacing(ListItemSpacing::Sparse) - .child(Label::new(summary).size(LabelSize::Small).text_ellipsis()) + .start_slot( + div() + .max_w_4_5() + .child(Label::new(summary).size(LabelSize::Small).text_ellipsis()), + ) .end_slot( h_flex() .gap_1p5() + .child( + Label::new("Thread") + .color(Color::Muted) + .size(LabelSize::XSmall), + ) + .child( + div() + .size(px(3.)) + .rounded_full() + .bg(cx.theme().colors().text_disabled), + ) .child( Label::new(thread_timestamp) .color(Color::Muted) @@ -340,18 +350,28 @@ impl RenderOnce for PastContext { ListItem::new(SharedString::from( self.context.path.to_string_lossy().to_string(), )) - .outlined() + .rounded() .toggle_state(self.selected) - .start_slot( - Icon::new(IconName::Code) - .size(IconSize::Small) - .color(Color::Muted), - ) .spacing(ListItemSpacing::Sparse) - .child(Label::new(summary).size(LabelSize::Small).text_ellipsis()) + .start_slot( + div() + .max_w_4_5() + .child(Label::new(summary).size(LabelSize::Small).text_ellipsis()), + ) .end_slot( h_flex() .gap_1p5() + .child( + Label::new("Prompt Editor") + .color(Color::Muted) + .size(LabelSize::XSmall), + ) + .child( + div() + .size(px(3.)) + .rounded_full() + .bg(cx.theme().colors().text_disabled), + ) .child( Label::new(context_timestamp) .color(Color::Muted) diff --git a/crates/language_models/src/provider/bedrock.rs b/crates/language_models/src/provider/bedrock.rs index 4294a1ba72..bcb4c28d41 100644 --- a/crates/language_models/src/provider/bedrock.rs +++ b/crates/language_models/src/provider/bedrock.rs @@ -960,17 +960,30 @@ impl Render for ConfigurationView { ]; let env_var_set = self.state.read(cx).credentials_from_env; + let bg_color = cx.theme().colors().editor_background; + let border_color = cx.theme().colors().border_variant; + let input_base_styles = || { + h_flex() + .w_full() + .px_2() + .py_1() + .bg(bg_color) + .border_1() + .border_color(border_color) + .rounded_md() + }; + if self.load_credentials_task.is_some() { div().child(Label::new("Loading credentials...")).into_any() } else if self.should_render_editor(cx) { v_flex() .size_full() - .on_action(cx.listener(Self::save_credentials)) + .on_action(cx.listener(ConfigurationView::save_credentials)) .child(Label::new(INSTRUCTIONS[0])) .child(h_flex().child(Label::new(INSTRUCTIONS[1])).child( Button::new("iam_console", IAM_CONSOLE_URL) .style(ButtonStyle::Subtle) - .icon(IconName::ExternalLink) + .icon(IconName::ArrowUpRight) .icon_size(IconSize::XSmall) .icon_color(Color::Muted) .on_click(move |_, _window, cx| cx.open_url(IAM_CONSOLE_URL)) @@ -978,11 +991,12 @@ impl Render for ConfigurationView { ) .child(Label::new(INSTRUCTIONS[2])) .child( - h_flex() + v_flex() + .my_2() .gap_1() - .child(self.render_aa_id_editor(cx)) - .child(self.render_sk_editor(cx)) - .child(self.render_region_editor(cx)) + .child(input_base_styles().child(self.render_aa_id_editor(cx))) + .child(input_base_styles().child(self.render_sk_editor(cx))) + .child(input_base_styles().child(self.render_region_editor(cx))) ) .child( Label::new( diff --git a/crates/language_models/src/provider/cloud.rs b/crates/language_models/src/provider/cloud.rs index 8037e8c337..499d6da9db 100644 --- a/crates/language_models/src/provider/cloud.rs +++ b/crates/language_models/src/provider/cloud.rs @@ -386,17 +386,10 @@ fn render_accept_terms( let form = v_flex() .w_full() .gap_2() - .when( - view_kind == LanguageModelProviderTosView::ThreadEmptyState, - |form| form.items_center(), - ) .child( h_flex() .flex_wrap() - .when( - view_kind == LanguageModelProviderTosView::ThreadEmptyState, - |form| form.justify_center(), - ) + .items_start() .child(Label::new(text)) .child(terms_button), ) @@ -416,9 +409,11 @@ fn render_accept_terms( ); match view_kind { - LanguageModelProviderTosView::ThreadEmptyState => button_container.justify_center(), LanguageModelProviderTosView::PromptEditorPopup => button_container.justify_end(), - LanguageModelProviderTosView::Configuration => button_container.justify_start(), + LanguageModelProviderTosView::Configuration + | LanguageModelProviderTosView::ThreadEmptyState => { + button_container.justify_start() + } } }); diff --git a/crates/ui/src/components/list/list_item.rs b/crates/ui/src/components/list/list_item.rs index 2d2e506e62..7045ae87d8 100644 --- a/crates/ui/src/components/list/list_item.rs +++ b/crates/ui/src/components/list/list_item.rs @@ -38,6 +38,7 @@ pub struct ListItem { children: SmallVec<[AnyElement; 2]>, selectable: bool, outlined: bool, + rounded: bool, overflow_x: bool, focused: Option, } @@ -63,6 +64,7 @@ impl ListItem { children: SmallVec::new(), selectable: true, outlined: false, + rounded: false, overflow_x: false, focused: None, } @@ -147,6 +149,11 @@ impl ListItem { self } + pub fn rounded(mut self) -> Self { + self.rounded = true; + self + } + pub fn overflow_x(mut self) -> Self { self.overflow_x = true; self @@ -210,13 +217,13 @@ impl RenderOnce for ListItem { }) }) }) + .when(self.rounded, |this| this.rounded_md()) .child( h_flex() .id("inner_list_item") .group("list_item") .w_full() .relative() - .items_center() .gap_1() .px(DynamicSpacing::Base06.rems(cx)) .map(|this| match self.spacing {