diff --git a/assets/icons/message_circle.svg b/assets/icons/message_circle.svg new file mode 100644 index 0000000000..e44c8607ea --- /dev/null +++ b/assets/icons/message_circle.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/person.svg b/assets/icons/person.svg index f6133478d1..93bee97a5f 100644 --- a/assets/icons/person.svg +++ b/assets/icons/person.svg @@ -1,3 +1,4 @@ - - + + + diff --git a/crates/assistant2/src/active_thread.rs b/crates/assistant2/src/active_thread.rs index bf0086ee11..7f491cce33 100644 --- a/crates/assistant2/src/active_thread.rs +++ b/crates/assistant2/src/active_thread.rs @@ -213,26 +213,33 @@ impl ActiveThread { div() .id(("message-container", ix)) - .p_2() + .py_1() + .px_2() .child( v_flex() .border_1() - .border_color(cx.theme().colors().border_variant) + .border_color(cx.theme().colors().border) + .bg(cx.theme().colors().editor_background) .rounded_md() .child( h_flex() .justify_between() - .p_1p5() + .py_1() + .px_2() .border_b_1() .border_color(cx.theme().colors().border_variant) .child( h_flex() - .gap_2() - .child(Icon::new(role_icon).size(IconSize::Small)) - .child(Label::new(role_name).size(LabelSize::Small)), + .gap_1p5() + .child( + Icon::new(role_icon) + .size(IconSize::XSmall) + .color(Color::Muted), + ) + .child(Label::new(role_name).size(LabelSize::XSmall)), ), ) - .child(v_flex().p_1p5().text_ui(cx).child(markdown.clone())) + .child(v_flex().px_2().py_1().text_ui(cx).child(markdown.clone())) .when_some(context, |parent, context| { parent.child( h_flex().flex_wrap().gap_2().p_1p5().children( @@ -249,6 +256,6 @@ impl ActiveThread { impl Render for ActiveThread { fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { - list(self.list_state.clone()).flex_1() + list(self.list_state.clone()).flex_1().py_1() } } diff --git a/crates/assistant2/src/assistant_panel.rs b/crates/assistant2/src/assistant_panel.rs index 1d21413583..8d6cc50d81 100644 --- a/crates/assistant2/src/assistant_panel.rs +++ b/crates/assistant2/src/assistant_panel.rs @@ -10,7 +10,7 @@ use gpui::{ }; use language::LanguageRegistry; use time::UtcOffset; -use ui::{prelude::*, Divider, IconButtonShape, KeyBinding, Tab, Tooltip}; +use ui::{prelude::*, KeyBinding, Tab, Tooltip}; use workspace::dock::{DockPosition, Panel, PanelEvent}; use workspace::Workspace; @@ -202,7 +202,7 @@ impl Panel for AssistantPanel { fn set_position(&mut self, _position: DockPosition, _cx: &mut ViewContext) {} fn size(&self, _cx: &WindowContext) -> Pixels { - px(640.) + px(550.) } fn set_size(&mut self, _size: Option, _cx: &mut ViewContext) {} @@ -238,15 +238,17 @@ impl AssistantPanel { .px(DynamicSpacing::Base08.rems(cx)) .bg(cx.theme().colors().tab_bar_background) .border_b_1() - .border_color(cx.theme().colors().border_variant) + .border_color(cx.theme().colors().border) .child(h_flex().children(self.thread.read(cx).summary(cx).map(Label::new))) .child( h_flex() - .gap(DynamicSpacing::Base08.rems(cx)) - .child(Divider::vertical()) + .h_full() + .pl_1() + .border_l_1() + .border_color(cx.theme().colors().border) + .gap(DynamicSpacing::Base02.rems(cx)) .child( IconButton::new("new-thread", IconName::Plus) - .shape(IconButtonShape::Square) .icon_size(IconSize::Small) .style(ButtonStyle::Subtle) .tooltip({ @@ -266,7 +268,6 @@ impl AssistantPanel { ) .child( IconButton::new("open-history", IconName::HistoryRerun) - .shape(IconButtonShape::Square) .icon_size(IconSize::Small) .style(ButtonStyle::Subtle) .tooltip({ @@ -286,7 +287,6 @@ impl AssistantPanel { ) .child( IconButton::new("configure-assistant", IconName::Settings) - .shape(IconButtonShape::Square) .icon_size(IconSize::Small) .style(ButtonStyle::Subtle) .tooltip(move |cx| Tooltip::text("Configure Assistant", cx)) @@ -312,7 +312,6 @@ impl AssistantPanel { v_flex() .gap_2() - .mx_auto() .child( v_flex().w_full().child( svg() @@ -334,7 +333,7 @@ impl AssistantPanel { ), ) .child( - v_flex().gap_2().children( + v_flex().mx_auto().w_4_5().gap_2().children( recent_threads .into_iter() .map(|thread| PastThread::new(thread, cx.view().downgrade())), @@ -541,7 +540,7 @@ impl Render for AssistantPanel { .child( h_flex() .border_t_1() - .border_color(cx.theme().colors().border_variant) + .border_color(cx.theme().colors().border) .child(self.message_editor.clone()), ) .children(self.render_last_error(cx)), diff --git a/crates/assistant2/src/context_picker.rs b/crates/assistant2/src/context_picker.rs index 9e6086f86a..48bb30a5f5 100644 --- a/crates/assistant2/src/context_picker.rs +++ b/crates/assistant2/src/context_picker.rs @@ -9,7 +9,7 @@ use gpui::{ WeakModel, WeakView, }; use picker::{Picker, PickerDelegate}; -use ui::{prelude::*, ListItem, ListItemSpacing, Tooltip}; +use ui::{prelude::*, ListItem, ListItemSpacing}; use util::ResultExt; use workspace::Workspace; @@ -41,27 +41,23 @@ impl ContextPicker { ) -> Self { let mut entries = vec![ ContextPickerEntry { - name: "directory".into(), - description: "Insert any directory".into(), + name: "Directory".into(), icon: IconName::Folder, }, ContextPickerEntry { - name: "file".into(), - description: "Insert any file".into(), + name: "File".into(), icon: IconName::File, }, ContextPickerEntry { - name: "fetch".into(), - description: "Fetch content from URL".into(), + name: "Fetch".into(), icon: IconName::Globe, }, ]; if thread_store.is_some() { entries.push(ContextPickerEntry { - name: "thread".into(), - description: "Insert any thread".into(), - icon: IconName::MessageBubbles, + name: "Thread".into(), + icon: IconName::MessageCircle, }); } @@ -119,7 +115,6 @@ impl Render for ContextPicker { #[derive(Clone)] struct ContextPickerEntry { name: SharedString, - description: SharedString, icon: IconName, } @@ -161,7 +156,7 @@ impl PickerDelegate for ContextPickerDelegate { self.context_picker .update(cx, |this, cx| { match entry.name.to_string().as_str() { - "file" => { + "File" => { this.mode = ContextPickerMode::File(cx.new_view(|cx| { FileContextPicker::new( self.context_picker.clone(), @@ -171,7 +166,7 @@ impl PickerDelegate for ContextPickerDelegate { ) })); } - "fetch" => { + "Fetch" => { this.mode = ContextPickerMode::Fetch(cx.new_view(|cx| { FetchContextPicker::new( self.context_picker.clone(), @@ -181,7 +176,7 @@ impl PickerDelegate for ContextPickerDelegate { ) })); } - "thread" => { + "Thread" => { if let Some(thread_store) = self.thread_store.as_ref() { this.mode = ContextPickerMode::Thread(cx.new_view(|cx| { ThreadContextPicker::new( @@ -226,34 +221,13 @@ impl PickerDelegate for ContextPickerDelegate { .inset(true) .spacing(ListItemSpacing::Dense) .toggle_state(selected) - .tooltip({ - let description = entry.description.clone(); - move |cx| cx.new_view(|_cx| Tooltip::new(description.clone())).into() - }) .child( - v_flex() - .group(format!("context-entry-label-{ix}")) - .w_full() - .py_0p5() + h_flex() .min_w(px(250.)) .max_w(px(400.)) - .child( - h_flex() - .gap_1p5() - .child(Icon::new(entry.icon).size(IconSize::XSmall)) - .child( - Label::new(entry.name.clone()) - .single_line() - .size(LabelSize::Small), - ), - ) - .child( - div().overflow_hidden().text_ellipsis().child( - Label::new(entry.description.clone()) - .size(LabelSize::Small) - .color(Color::Muted), - ), - ), + .gap_2() + .child(Icon::new(entry.icon).size(IconSize::Small)) + .child(Label::new(entry.name.clone()).single_line()), ), ) } diff --git a/crates/assistant2/src/context_strip.rs b/crates/assistant2/src/context_strip.rs index c5b6164b4a..fa0f7cab8a 100644 --- a/crates/assistant2/src/context_strip.rs +++ b/crates/assistant2/src/context_strip.rs @@ -1,7 +1,7 @@ use std::rc::Rc; use gpui::{Model, View, WeakModel, WeakView}; -use ui::{prelude::*, IconButtonShape, PopoverMenu, PopoverMenuHandle, Tooltip}; +use ui::{prelude::*, PopoverMenu, PopoverMenuHandle, Tooltip}; use workspace::Workspace; use crate::context_picker::ContextPicker; @@ -44,14 +44,14 @@ impl Render for ContextStrip { h_flex() .flex_wrap() - .gap_2() + .gap_1() .child( PopoverMenu::new("context-picker") .menu(move |_cx| Some(context_picker.clone())) .trigger( IconButton::new("add-context", IconName::Plus) - .shape(IconButtonShape::Square) - .icon_size(IconSize::Small), + .icon_size(IconSize::Small) + .style(ui::ButtonStyle::Filled), ) .attach(gpui::AnchorCorner::TopLeft) .anchor(gpui::AnchorCorner::BottomLeft) @@ -76,7 +76,6 @@ impl Render for ContextStrip { .when(!context.is_empty(), |parent| { parent.child( IconButton::new("remove-all-context", IconName::Eraser) - .shape(IconButtonShape::Square) .icon_size(IconSize::Small) .tooltip(move |cx| Tooltip::text("Remove All Context", cx)) .on_click({ diff --git a/crates/assistant2/src/message_editor.rs b/crates/assistant2/src/message_editor.rs index b8d7b4d1be..179909d919 100644 --- a/crates/assistant2/src/message_editor.rs +++ b/crates/assistant2/src/message_editor.rs @@ -35,7 +35,8 @@ impl MessageEditor { thread, editor: cx.new_view(|cx| { let mut editor = Editor::auto_height(80, cx); - editor.set_placeholder_text("Ask anything or type @ to add context", cx); + editor.set_placeholder_text("Ask anything, @ to add context", cx); + editor.set_show_indent_guides(false, cx); editor }), @@ -112,8 +113,8 @@ impl MessageEditor { } fn render_language_model_selector(&self, cx: &mut ViewContext) -> impl IntoElement { - let active_provider = LanguageModelRegistry::read_global(cx).active_provider(); let active_model = LanguageModelRegistry::read_global(cx).active_model(); + let focus_handle = self.language_model_selector.focus_handle(cx).clone(); LanguageModelSelectorPopoverMenu::new( self.language_model_selector.clone(), @@ -128,16 +129,8 @@ impl MessageEditor { .overflow_x_hidden() .flex_grow() .whitespace_nowrap() - .child(match (active_provider, active_model) { - (Some(provider), Some(model)) => h_flex() - .gap_1() - .child( - Icon::new( - model.icon().unwrap_or_else(|| provider.icon()), - ) - .color(Color::Muted) - .size(IconSize::XSmall), - ) + .child(match active_model { + Some(model) => h_flex() .child( Label::new(model.name().0) .size(LabelSize::Small) @@ -156,7 +149,9 @@ impl MessageEditor { .size(IconSize::XSmall), ), ) - .tooltip(move |cx| Tooltip::for_action("Change Model", &ToggleModelSelector, cx)), + .tooltip(move |cx| { + Tooltip::for_action_in("Change Model", &ToggleModelSelector, &focus_handle, cx) + }), ) } } @@ -170,8 +165,9 @@ impl FocusableView for MessageEditor { impl Render for MessageEditor { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { let font_size = TextSize::Default.rems(cx); - let line_height = font_size.to_pixels(cx.rem_size()) * 1.3; + let line_height = font_size.to_pixels(cx.rem_size()) * 1.5; let focus_handle = self.editor.focus_handle(cx); + let bg_color = cx.theme().colors().editor_background; v_flex() .key_context("MessageEditor") @@ -179,9 +175,9 @@ impl Render for MessageEditor { .size_full() .gap_2() .p_2() - .bg(cx.theme().colors().editor_background) + .bg(bg_color) .child(self.context_strip.clone()) - .child({ + .child(div().id("thread_editor").overflow_y_scroll().h_12().child({ let settings = ThemeSettings::get_global(cx); let text_style = TextStyle { color: cx.theme().colors().editor_foreground, @@ -196,17 +192,17 @@ impl Render for MessageEditor { EditorElement::new( &self.editor, EditorStyle { - background: cx.theme().colors().editor_background, + background: bg_color, local_player: cx.theme().players().local(), text: text_style, ..Default::default() }, ) - }) + })) .child( h_flex() .justify_between() - .child(h_flex().gap_2().child(CheckboxWithLabel::new( + .child(CheckboxWithLabel::new( "use-tools", Label::new("Tools"), self.use_tools.into(), @@ -216,10 +212,10 @@ impl Render for MessageEditor { ToggleState::Unselected | ToggleState::Indeterminate => false, }; }), - ))) + )) .child( h_flex() - .gap_2() + .gap_1() .child(self.render_language_model_selector(cx)) .child( ButtonLike::new("chat") diff --git a/crates/assistant2/src/thread_history.rs b/crates/assistant2/src/thread_history.rs index 3eb333688a..b7e792e15b 100644 --- a/crates/assistant2/src/thread_history.rs +++ b/crates/assistant2/src/thread_history.rs @@ -120,7 +120,11 @@ impl RenderOnce for PastThread { ListItem::new(("past-thread", self.thread.entity_id())) .outlined() - .start_slot(Icon::new(IconName::MessageBubbles)) + .start_slot( + Icon::new(IconName::MessageCircle) + .size(IconSize::Small) + .color(Color::Muted), + ) .spacing(ListItemSpacing::Sparse) .child(Label::new(summary).size(LabelSize::Small)) .end_slot( diff --git a/crates/assistant2/src/ui/context_pill.rs b/crates/assistant2/src/ui/context_pill.rs index dd74465ad0..aa27e82b27 100644 --- a/crates/assistant2/src/ui/context_pill.rs +++ b/crates/assistant2/src/ui/context_pill.rs @@ -29,9 +29,12 @@ impl RenderOnce for ContextPill { fn render(self, cx: &mut WindowContext) -> impl IntoElement { h_flex() .gap_1() - .px_1() + .pl_1p5() + .pr_0p5() + .pb(px(1.)) .border_1() - .border_color(cx.theme().colors().border) + .border_color(cx.theme().colors().border.opacity(0.5)) + .bg(cx.theme().colors().element_background) .rounded_md() .child(Label::new(self.context.name.clone()).size(LabelSize::Small)) .when_some(self.on_remove, |parent, on_remove| { diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index 66601a853c..6a01ca0b73 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -218,6 +218,7 @@ pub enum IconName { Maximize, Menu, MessageBubbles, + MessageCircle, Mic, MicMute, Microscope,