assistant2: Sketch in chat editor (#21116)

Marshall Bowers created

This PR sketches in the chat editor for `assistant2`.

<img width="1136" alt="Screenshot 2024-11-23 at 1 56 23 PM"
src="https://github.com/user-attachments/assets/6e979995-c0cf-4a46-8694-fc4a8646336f">

Release Notes:

- N/A

Change summary

Cargo.lock                               |  3 +
crates/assistant2/Cargo.toml             |  3 +
crates/assistant2/src/assistant.rs       |  1 
crates/assistant2/src/assistant_panel.rs | 15 ++++
crates/assistant2/src/chat_editor.rs     | 76 ++++++++++++++++++++++++++
5 files changed, 97 insertions(+), 1 deletion(-)

Detailed changes

Cargo.lock 🔗

@@ -456,11 +456,14 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "command_palette_hooks",
+ "editor",
  "feature_flags",
  "gpui",
  "language_model",
  "language_model_selector",
  "proto",
+ "settings",
+ "theme",
  "ui",
  "workspace",
 ]

crates/assistant2/Cargo.toml 🔗

@@ -15,10 +15,13 @@ doctest = false
 [dependencies]
 anyhow.workspace = true
 command_palette_hooks.workspace = true
+editor.workspace = true
 feature_flags.workspace = true
 gpui.workspace = true
 language_model.workspace = true
 language_model_selector.workspace = true
 proto.workspace = true
+settings.workspace = true
+theme.workspace = true
 ui.workspace = true
 workspace.workspace = true

crates/assistant2/src/assistant.rs 🔗

@@ -1,4 +1,5 @@
 mod assistant_panel;
+mod chat_editor;
 
 use command_palette_hooks::CommandPaletteFilter;
 use feature_flags::{Assistant2FeatureFlag, FeatureFlagAppExt};

crates/assistant2/src/assistant_panel.rs 🔗

@@ -9,6 +9,7 @@ use ui::{prelude::*, ButtonLike, Divider, IconButtonShape, Tab, Tooltip};
 use workspace::dock::{DockPosition, Panel, PanelEvent};
 use workspace::{Pane, Workspace};
 
+use crate::chat_editor::ChatEditor;
 use crate::{NewChat, ToggleFocus, ToggleModelSelector};
 
 pub fn init(cx: &mut AppContext) {
@@ -24,6 +25,7 @@ pub fn init(cx: &mut AppContext) {
 
 pub struct AssistantPanel {
     pane: View<Pane>,
+    chat_editor: View<ChatEditor>,
 }
 
 impl AssistantPanel {
@@ -54,7 +56,10 @@ impl AssistantPanel {
             pane
         });
 
-        Self { pane }
+        Self {
+            pane,
+            chat_editor: cx.new_view(ChatEditor::new),
+        }
     }
 }
 
@@ -231,10 +236,18 @@ impl Render for AssistantPanel {
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_flex()
             .key_context("AssistantPanel2")
+            .justify_between()
             .size_full()
             .on_action(cx.listener(|_this, _: &NewChat, _cx| {
                 println!("Action: New Chat");
             }))
             .child(self.render_toolbar(cx))
+            .child(v_flex().bg(cx.theme().colors().panel_background))
+            .child(
+                h_flex()
+                    .border_t_1()
+                    .border_color(cx.theme().colors().border_variant)
+                    .child(self.chat_editor.clone()),
+            )
     }
 }

crates/assistant2/src/chat_editor.rs 🔗

@@ -0,0 +1,76 @@
+use editor::{Editor, EditorElement, EditorStyle};
+use gpui::{TextStyle, View};
+use settings::Settings;
+use theme::ThemeSettings;
+use ui::prelude::*;
+
+pub struct ChatEditor {
+    editor: View<Editor>,
+}
+
+impl ChatEditor {
+    pub fn new(cx: &mut ViewContext<Self>) -> Self {
+        Self {
+            editor: cx.new_view(|cx| {
+                let mut editor = Editor::auto_height(80, cx);
+                editor.set_placeholder_text("Ask anything…", cx);
+
+                editor
+            }),
+        }
+    }
+}
+
+impl Render for ChatEditor {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        let font_size = TextSize::Default.rems(cx);
+        let line_height = font_size.to_pixels(cx.rem_size()) * 1.3;
+
+        v_flex()
+            .size_full()
+            .gap_2()
+            .p_2()
+            .bg(cx.theme().colors().editor_background)
+            .child({
+                let settings = ThemeSettings::get_global(cx);
+                let text_style = TextStyle {
+                    color: cx.theme().colors().editor_foreground,
+                    font_family: settings.ui_font.family.clone(),
+                    font_features: settings.ui_font.features.clone(),
+                    font_size: font_size.into(),
+                    font_weight: settings.ui_font.weight,
+                    line_height: line_height.into(),
+                    ..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()
+                    .justify_between()
+                    .child(
+                        h_flex().child(
+                            Button::new("add-context", "Add Context")
+                                .style(ButtonStyle::Filled)
+                                .icon(IconName::Plus)
+                                .icon_position(IconPosition::Start),
+                        ),
+                    )
+                    .child(
+                        h_flex()
+                            .gap_2()
+                            .child(Button::new("codebase", "Codebase").style(ButtonStyle::Filled))
+                            .child(Label::new("or"))
+                            .child(Button::new("chat", "Chat").style(ButtonStyle::Filled)),
+                    ),
+            )
+    }
+}