Add a send button to the assistant (#12171)

Nathan Sobo created

![CleanShot 2024-05-22 at 18 20
45@2x](https://github.com/zed-industries/zed/assets/1789/dac9fcde-9fcb-4c40-b5da-ebdc847b3962)

Include the keybinding to help people discover how to submit from the
keyboard.

I'm also shifting from the word "Conversation" to "Context".

Release Notes:

- Added a send button to the assistant panel.

Change summary

assets/settings/default.json               |  2 
crates/assistant/src/assistant_panel.rs    | 52 +++++++++++++++++------
crates/assistant/src/assistant_settings.rs |  4 
crates/assistant2/src/assistant2.rs        |  2 
docs/src/assistant-panel.md                |  4 
5 files changed, 44 insertions(+), 20 deletions(-)

Detailed changes

assets/settings/default.json πŸ”—

@@ -316,7 +316,7 @@
     // AI provider.
     "provider": {
       "name": "openai",
-      // The default model to use when starting new conversations. This
+      // The default model to use when creating new contexts. This
       // setting can take three values:
       //
       // 1. "gpt-3.5-turbo"

crates/assistant/src/assistant_panel.rs πŸ”—

@@ -61,7 +61,10 @@ use std::{
 };
 use telemetry_events::AssistantKind;
 use theme::ThemeSettings;
-use ui::{popover_menu, prelude::*, ButtonLike, ContextMenu, Tab, TabBar, Tooltip};
+use ui::{
+    popover_menu, prelude::*, ButtonLike, ContextMenu, ElevationIndex, KeyBinding, Tab, TabBar,
+    Tooltip,
+};
 use util::{paths::CONVERSATIONS_DIR, post_inc, ResultExt, TryFutureExt};
 use uuid::Uuid;
 use workspace::{
@@ -1057,15 +1060,25 @@ impl AssistantPanel {
             })
     }
 
-    fn render_assist_button(cx: &mut ViewContext<Self>) -> impl IntoElement {
-        IconButton::new("assist_button", IconName::MagicWand)
-            .on_click(cx.listener(|this, _event, cx| {
-                if let Some(active_editor) = this.active_conversation_editor() {
-                    active_editor.update(cx, |editor, cx| editor.assist(&Default::default(), cx));
-                }
-            }))
-            .icon_size(IconSize::Small)
-            .tooltip(|cx| Tooltip::for_action("Assist", &Assist, cx))
+    fn render_send_button(&self, cx: &mut ViewContext<Self>) -> Option<impl IntoElement> {
+        self.active_conversation_editor
+            .as_ref()
+            .map(|conversation| {
+                let focus_handle = conversation.editor.focus_handle(cx);
+                ButtonLike::new("send_button")
+                    .style(ButtonStyle::Filled)
+                    .layer(ElevationIndex::ModalSurface)
+                    .children(
+                        KeyBinding::for_action_in(&Assist, &focus_handle, cx)
+                            .map(|binding| binding.into_any_element()),
+                    )
+                    .child(Label::new("Send"))
+                    .on_click(cx.listener(|this, _event, cx| {
+                        if let Some(active_editor) = this.active_conversation_editor() {
+                            active_editor.update(cx, |editor, cx| editor.assist(&Assist, cx));
+                        }
+                    }))
+            })
     }
 
     fn render_saved_conversation(
@@ -1194,8 +1207,7 @@ impl AssistantPanel {
                                             this.show_prompt_manager(cx)
                                         }))
                                         .tooltip(|cx| Tooltip::text("Prompt Library…", cx)),
-                                )
-                                .child(Self::render_assist_button(cx)),
+                                ),
                         ),
                 );
 
@@ -1262,7 +1274,19 @@ impl AssistantPanel {
                     .into_any_element()
                 } else if let Some(editor) = self.active_conversation_editor() {
                     let editor = editor.clone();
-                    div().size_full().child(editor.clone()).into_any_element()
+                    div()
+                        .size_full()
+                        .child(editor.clone())
+                        .child(
+                            h_flex()
+                                .w_full()
+                                .absolute()
+                                .bottom_0()
+                                .p_4()
+                                .justify_end()
+                                .children(self.render_send_button(cx)),
+                        )
+                        .into_any_element()
                 } else {
                     div().into_any_element()
                 },
@@ -3503,7 +3527,7 @@ impl ConversationEditor {
             .summary
             .as_ref()
             .map(|summary| summary.text.clone())
-            .unwrap_or_else(|| "New Conversation".into())
+            .unwrap_or_else(|| "New Context".into())
     }
 }
 

crates/assistant/src/assistant_settings.rs πŸ”—

@@ -339,11 +339,11 @@ pub struct LegacyAssistantSettingsContent {
     ///
     /// Default: 320
     pub default_height: Option<f32>,
-    /// The default OpenAI model to use when starting new conversations.
+    /// The default OpenAI model to use when creating new contexts.
     ///
     /// Default: gpt-4-1106-preview
     pub default_open_ai_model: Option<OpenAiModel>,
-    /// OpenAI API base URL to use when starting new conversations.
+    /// OpenAI API base URL to use when creating new contexts.
     ///
     /// Default: https://api.openai.com/v1
     pub openai_api_url: Option<String>,

crates/assistant2/src/assistant2.rs πŸ”—

@@ -1108,7 +1108,7 @@ impl Render for AssistantChat {
                                     .on_click(cx.listener(move |this, _event, cx| {
                                         this.new_conversation(cx);
                                     }))
-                                    .tooltip(move |cx| Tooltip::text("New Conversation", cx)),
+                                    .tooltip(move |cx| Tooltip::text("New Context", cx)),
                             )
                             .child(
                                 IconButton::new("assistant-menu", IconName::Menu)

docs/src/assistant-panel.md πŸ”—

@@ -46,7 +46,7 @@ After submitting a message, the assistant's response will be streamed below, in
 
 The stream can be canceled at any point with `escape`. This is useful if you realize early on that the response is not what you were looking for.
 
-If you want to start a new conversation at any time, you can use the `New Conversation` button located at the top-right corner of the assistant panel.
+If you want to start a new conversation at any time, you can hit `cmd-n` or use the `New Context` menu option in the hamburger menu at the top left of the panel.
 
 Simple back-and-forth conversations work well with the assistant. However, there may come a time when you want to modify the previous text in the conversation and steer it in a different direction.
 
@@ -62,7 +62,7 @@ The assistant gives you the flexibility to have control over the conversation. Y
 6. Add additional context to your original message
 7. Submit the message with `cmd-enter`
 
-Being able to edit previous messages gives you control over how tokens are used. You don't need to start up a new conversation to correct a mistake or to add additional context and you don't have to waste tokens by submitting follow-up corrections.
+Being able to edit previous messages gives you control over how tokens are used. You don't need to start up a new context to correct a mistake or to add additional context and you don't have to waste tokens by submitting follow-up corrections.
 
 Some additional points to keep in mind: