Style header for assistant2 (#11570)

Nate Butler created

Release Notes:

- N/A

Change summary

crates/assistant2/src/assistant2.rs  |  50 ++++++++++--
crates/assistant2/src/ui/composer.rs | 113 +++++++++++++++--------------
crates/ui/src/styles/spacing.rs      |   6 +
3 files changed, 105 insertions(+), 64 deletions(-)

Detailed changes

crates/assistant2/src/assistant2.rs 🔗

@@ -975,6 +975,8 @@ impl AssistantChat {
 
 impl Render for AssistantChat {
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        let header_height = Spacing::Small.rems(cx) * 2.0 + ButtonSize::Default.rems();
+
         div()
             .relative()
             .flex_1()
@@ -983,23 +985,51 @@ impl Render for AssistantChat {
             .on_action(cx.listener(Self::submit))
             .on_action(cx.listener(Self::cancel))
             .text_color(Color::Default.color(cx))
+            .child(list(self.list_state.clone()).flex_1().pt(header_height))
             .child(
                 h_flex()
-                    .gap_2()
+                    .absolute()
+                    .top_0()
+                    .justify_between()
+                    .w_full()
+                    .h(header_height)
+                    .p(Spacing::Small.rems(cx))
                     .child(
-                        Button::new("open-saved-conversations", "Saved Conversations").on_click(
-                            |_event, cx| cx.dispatch_action(Box::new(ToggleSavedConversations)),
-                        ),
+                        IconButton::new("open-saved-conversations", IconName::ChevronLeft)
+                            .on_click(|_event, cx| {
+                                cx.dispatch_action(Box::new(ToggleSavedConversations))
+                            })
+                            .tooltip(move |cx| {
+                                Tooltip::with_meta(
+                                    "Switch Conversations",
+                                    Some(&ToggleSavedConversations),
+                                    "UI will change, temporary.",
+                                    cx,
+                                )
+                            }),
                     )
                     .child(
-                        IconButton::new("new-conversation", IconName::Plus)
-                            .on_click(cx.listener(move |this, _event, cx| {
-                                this.new_conversation(cx);
-                            }))
-                            .tooltip(move |cx| Tooltip::text("New Conversation", cx)),
+                        h_flex()
+                            .gap(Spacing::Large.rems(cx))
+                            .child(
+                                IconButton::new("new-conversation", IconName::Plus)
+                                    .on_click(cx.listener(move |this, _event, cx| {
+                                        this.new_conversation(cx);
+                                    }))
+                                    .tooltip(move |cx| Tooltip::text("New Conversation", cx)),
+                            )
+                            .child(
+                                IconButton::new("assistant-menu", IconName::Menu)
+                                    .disabled(true)
+                                    .tooltip(move |cx| {
+                                        Tooltip::text(
+                                            "Coming soon – Assistant settings & controls",
+                                            cx,
+                                        )
+                                    }),
+                            ),
                     ),
             )
-            .child(list(self.list_state.clone()).flex_1())
             .child(Composer::new(
                 self.composer_editor.clone(),
                 self.project_index_button.clone(),

crates/assistant2/src/ui/composer.rs 🔗

@@ -48,68 +48,73 @@ impl RenderOnce for Composer {
     fn render(mut self, cx: &mut WindowContext) -> impl IntoElement {
         let font_size = TextSize::Default.rems(cx);
         let line_height = font_size.to_pixels(cx.rem_size()) * 1.3;
+        let mut editor_border = cx.theme().colors().text;
+        editor_border.fade_out(0.90);
+
+        // Remove the extra 1px added by the border
+        let padding = Spacing::XLarge.rems(cx) - rems_from_px(1.);
 
         h_flex()
             .p(Spacing::Small.rems(cx))
             .w_full()
             .items_start()
             .child(
-                v_flex().size_full().gap_1().child(
-                    v_flex()
-                        .w_full()
-                        .p_3()
-                        .bg(cx.theme().colors().editor_background)
-                        .rounded_lg()
-                        .child(
-                            v_flex()
-                                .justify_between()
-                                .w_full()
-                                .gap_2()
-                                .child({
-                                    let settings = ThemeSettings::get_global(cx);
-                                    let text_style = TextStyle {
-                                        color: cx.theme().colors().editor_foreground,
-                                        font_family: settings.buffer_font.family.clone(),
-                                        font_features: settings.buffer_font.features.clone(),
-                                        font_size: font_size.into(),
-                                        font_weight: FontWeight::NORMAL,
-                                        font_style: FontStyle::Normal,
-                                        line_height: line_height.into(),
-                                        background_color: None,
-                                        underline: None,
-                                        strikethrough: None,
-                                        white_space: WhiteSpace::Normal,
-                                    };
+                v_flex()
+                    .w_full()
+                    .rounded_lg()
+                    .p(padding)
+                    .border_1()
+                    .border_color(editor_border)
+                    .bg(cx.theme().colors().editor_background)
+                    .child(
+                        v_flex()
+                            .justify_between()
+                            .w_full()
+                            .gap_2()
+                            .child({
+                                let settings = ThemeSettings::get_global(cx);
+                                let text_style = TextStyle {
+                                    color: cx.theme().colors().editor_foreground,
+                                    font_family: settings.buffer_font.family.clone(),
+                                    font_features: settings.buffer_font.features.clone(),
+                                    font_size: font_size.into(),
+                                    font_weight: FontWeight::NORMAL,
+                                    font_style: FontStyle::Normal,
+                                    line_height: line_height.into(),
+                                    background_color: None,
+                                    underline: None,
+                                    strikethrough: None,
+                                    white_space: WhiteSpace::Normal,
+                                };
 
-                                    EditorElement::new(
-                                        &self.editor,
-                                        EditorStyle {
-                                            background: cx.theme().colors().editor_background,
-                                            local_player: cx.theme().players().local(),
-                                            text: text_style,
-                                            ..Default::default()
-                                        },
+                                EditorElement::new(
+                                    &self.editor,
+                                    EditorStyle {
+                                        background: cx.theme().colors().editor_background,
+                                        local_player: cx.theme().players().local(),
+                                        text: text_style,
+                                        ..Default::default()
+                                    },
+                                )
+                            })
+                            .child(
+                                h_flex()
+                                    .flex_none()
+                                    .gap_2()
+                                    .justify_between()
+                                    .w_full()
+                                    .child(
+                                        h_flex().gap_1().child(
+                                            h_flex()
+                                                .gap_2()
+                                                .child(self.render_tools(cx))
+                                                .child(Divider::vertical())
+                                                .child(self.render_attachment_tools(cx)),
+                                        ),
                                     )
-                                })
-                                .child(
-                                    h_flex()
-                                        .flex_none()
-                                        .gap_2()
-                                        .justify_between()
-                                        .w_full()
-                                        .child(
-                                            h_flex().gap_1().child(
-                                                h_flex()
-                                                    .gap_2()
-                                                    .child(self.render_tools(cx))
-                                                    .child(Divider::vertical())
-                                                    .child(self.render_attachment_tools(cx)),
-                                            ),
-                                        )
-                                        .child(h_flex().gap_1().child(self.model_selector)),
-                                ),
-                        ),
-                ),
+                                    .child(h_flex().gap_1().child(self.model_selector)),
+                            ),
+                    ),
             )
     }
 }

crates/ui/src/styles/spacing.rs 🔗

@@ -27,7 +27,13 @@ pub enum Spacing {
     ///
     /// Relative to the user's `ui_font_size` and [UiDensity] setting.
     Large,
+    /// Extra Large spacing - @16px/rem: `8px`|`12px`|`16px`
+    ///
+    /// Relative to the user's `ui_font_size` and [UiDensity] setting.
     XLarge,
+    /// 2X Large spacing - @16px/rem: `12px`|`16px`|`20px`
+    ///
+    /// Relative to the user's `ui_font_size` and [UiDensity] setting.
     XXLarge,
 }