agent: Adjust the thread generation design (#28193)

Danilo Leal and Bennet Bo Fenner created

This PR simplifies the button to send a new message as well as the
"generation" display design.

Release Notes:

- N/A

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>

Change summary

Cargo.lock                         |   1 
assets/icons/send.svg              |   4 
assets/icons/stop_filled.svg       |   3 
crates/agent/Cargo.toml            |   1 
crates/agent/src/message_editor.rs | 210 ++++++++++---------------------
crates/icons/src/icons.rs          |   2 
6 files changed, 76 insertions(+), 145 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -119,7 +119,6 @@ dependencies = [
  "ui_input",
  "util",
  "uuid",
- "vim_mode_setting",
  "workspace",
  "workspace-hack",
  "zed_actions",

assets/icons/send.svg 🔗

@@ -0,0 +1,4 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M3.09666 3.02263C3.0567 3.00312 3.01178 2.9961 2.96778 3.0025C2.92377 3.00889 2.88271 3.02839 2.84995 3.05847C2.8172 3.08854 2.79426 3.12778 2.78413 3.17108C2.77401 3.21439 2.77716 3.25973 2.79319 3.30121L4.05638 6.69C4.13088 6.89005 4.13088 7.11022 4.05638 7.31027L2.79363 10.6991C2.77769 10.7405 2.77457 10.7858 2.78469 10.829C2.79481 10.8722 2.8177 10.9114 2.85038 10.9414C2.88306 10.9715 2.92402 10.991 2.96794 10.9975C3.01186 11.0039 3.05671 10.997 3.09666 10.9776L11.0943 7.20097C11.1324 7.18297 11.1645 7.15455 11.187 7.11899C11.2096 7.08344 11.2215 7.04222 11.2215 7.00014C11.2215 6.95805 11.2096 6.91683 11.187 6.88128C11.1645 6.84573 11.1324 6.8173 11.0943 6.79931L3.09666 3.02263Z" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M4.11255 7.00014H11.2216" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

assets/icons/stop_filled.svg 🔗

@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4 9.8V4.2C4 4.08954 4.08954 4 4.2 4H9.8C9.91046 4 10 4.08954 10 4.2V9.8C10 9.91046 9.91046 10 9.8 10H4.2C4.08954 10 4 9.91046 4 9.8Z" fill="#C56757" stroke="#C56757" stroke-width="1.25" stroke-linejoin="round"/>
+</svg>

crates/agent/Cargo.toml 🔗

@@ -84,7 +84,6 @@ ui.workspace = true
 ui_input.workspace = true
 util.workspace = true
 uuid.workspace = true
-vim_mode_setting.workspace = true
 workspace.workspace = true
 zed_actions.workspace = true
 workspace-hack.workspace = true

crates/agent/src/message_editor.rs 🔗

@@ -8,7 +8,7 @@ use file_icons::FileIcons;
 use fs::Fs;
 use gpui::{
     Animation, AnimationExt, App, DismissEvent, Entity, Focusable, Subscription, TextStyle,
-    WeakEntity, linear_color_stop, linear_gradient, point,
+    WeakEntity, linear_color_stop, linear_gradient, point, pulsating_between,
 };
 use language::Buffer;
 use language_model::{ConfiguredModel, LanguageModelRegistry};
@@ -18,12 +18,8 @@ use project::Project;
 use settings::Settings;
 use std::time::Duration;
 use theme::ThemeSettings;
-use ui::{
-    ButtonLike, Disclosure, KeyBinding, PlatformStyle, PopoverMenu, PopoverMenuHandle, Tooltip,
-    prelude::*,
-};
+use ui::{Disclosure, KeyBinding, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*};
 use util::ResultExt as _;
-use vim_mode_setting::VimModeSetting;
 use workspace::Workspace;
 
 use crate::assistant_model_selector::AssistantModelSelector;
@@ -355,24 +351,6 @@ impl Render for MessageEditor {
         let total_token_usage = thread.total_token_usage(cx);
         let is_model_selected = self.is_model_selected(cx);
         let is_editor_empty = self.is_editor_empty(cx);
-        let needs_confirmation =
-            thread.has_pending_tool_uses() && thread.tools_needing_confirmation().next().is_some();
-
-        let submit_label_color = if is_editor_empty {
-            Color::Muted
-        } else {
-            Color::Default
-        };
-
-        let vim_mode_enabled = VimModeSetting::get_global(cx).0;
-        let platform = PlatformStyle::platform();
-        let linux = platform == PlatformStyle::Linux;
-        let windows = platform == PlatformStyle::Windows;
-        let button_width = if linux || windows || vim_mode_enabled {
-            px(82.)
-        } else {
-            px(64.)
-        };
 
         let action_log = self.thread.read(cx).action_log();
         let changed_buffers = action_log.read(cx).changed_buffers(cx);
@@ -420,70 +398,6 @@ impl Render for MessageEditor {
                     ),
                 )
             })
-            .when(is_generating, |parent| {
-                let focus_handle = self.editor.focus_handle(cx).clone();
-                parent.child(
-                    h_flex().py_3().w_full().justify_center().child(
-                        h_flex()
-                            .flex_none()
-                            .pl_2()
-                            .pr_1()
-                            .py_1()
-                            .bg(editor_bg_color)
-                            .border_1()
-                            .border_color(cx.theme().colors().border_variant)
-                            .rounded_lg()
-                            .shadow_md()
-                            .gap_1()
-                            .child(
-                                Icon::new(IconName::ArrowCircle)
-                                    .size(IconSize::XSmall)
-                                    .color(Color::Muted)
-                                    .with_animation(
-                                        "arrow-circle",
-                                        Animation::new(Duration::from_secs(2)).repeat(),
-                                        |icon, delta| {
-                                            icon.transform(gpui::Transformation::rotate(
-                                                gpui::percentage(delta),
-                                            ))
-                                        },
-                                    ),
-                            )
-                            .child({
-
-
-                                Label::new(if needs_confirmation {
-                                    "Waiting for confirmation…"
-                                } else {
-                                    "Generating…"
-                                })
-                                .size(LabelSize::XSmall)
-                                .color(Color::Muted)
-                            })
-                            .child(ui::Divider::vertical())
-                            .child(
-                                Button::new("cancel-generation", "Cancel")
-                                    .label_size(LabelSize::XSmall)
-                                    .key_binding(
-                                        KeyBinding::for_action_in(
-                                            &editor::actions::Cancel,
-                                            &focus_handle,
-                                            window,
-                                            cx,
-                                        )
-                                        .map(|kb| kb.size(rems_from_px(10.))),
-                                    )
-                                    .on_click(move |_event, window, cx| {
-                                        focus_handle.dispatch_action(
-                                            &editor::actions::Cancel,
-                                            window,
-                                            cx,
-                                        );
-                                    }),
-                            ),
-                    ),
-                )
-            })
             .when(changed_buffers_count > 0, |parent| {
                 parent.child(
                     v_flex()
@@ -734,7 +648,6 @@ impl Render for MessageEditor {
                                             ..Default::default()
                                         },
                                     ).into_any()
-
                             })
                             .child(
                                 PopoverMenu::new("inline-context-picker")
@@ -742,7 +655,6 @@ impl Render for MessageEditor {
                                         inline_context_picker.update(cx, |this, cx| {
                                             this.init(window, cx);
                                         });
-
                                         Some(inline_context_picker.clone())
                                     })
                                     .attach(gpui::Corner::TopLeft)
@@ -759,60 +671,72 @@ impl Render for MessageEditor {
                                     .justify_between()
                                     .child(h_flex().gap_2().child(self.profile_selector.clone()))
                                     .child(
-                                        h_flex().gap_1().child(self.model_selector.clone()).child(
-                                            ButtonLike::new("submit-message")
-                                                .width(button_width.into())
-                                                .style(ButtonStyle::Filled)
-                                                .disabled(
-                                                    is_editor_empty
-                                                        || !is_model_selected
-                                                        || is_generating
-                                                        || self.waiting_for_summaries_to_send
-                                                )
-                                                .child(
-                                                    h_flex()
-                                                        .w_full()
-                                                        .justify_between()
-                                                        .child(
-                                                            Label::new("Submit")
-                                                                .size(LabelSize::Small)
-                                                                .color(submit_label_color),
-                                                        )
-                                                        .children(
-                                                            KeyBinding::for_action_in(
-                                                                &Chat,
-                                                                &focus_handle,
-                                                                window,
-                                                                cx,
+                                        h_flex().gap_1().child(self.model_selector.clone())
+                                            .map(|parent| {
+                                                if is_generating {
+                                                    parent.child(
+                                                        IconButton::new("stop-generation", IconName::StopFilled)
+                                                            .icon_color(Color::Error)
+                                                            .style(ButtonStyle::Tinted(ui::TintColor::Error))
+                                                            .tooltip(move |window, cx| {
+                                                                Tooltip::for_action(
+                                                                    "Stop Generation",
+                                                                    &editor::actions::Cancel,
+                                                                    window,
+                                                                    cx,
+                                                                )
+                                                            })
+                                                            .on_click(move |_event, window, cx| {
+                                                                focus_handle.dispatch_action(
+                                                                    &editor::actions::Cancel,
+                                                                    window,
+                                                                    cx,
+                                                                );
+                                                            })
+                                                            .with_animation(
+                                                                "pulsating-label",
+                                                                Animation::new(Duration::from_secs(2))
+                                                                    .repeat()
+                                                                    .with_easing(pulsating_between(0.4, 1.0)),
+                                                                |icon_button, delta| icon_button.alpha(delta),
+                                                            ),
+                                                    )
+                                                } else {
+                                                    parent.child(
+                                                        IconButton::new("send-message", IconName::Send)
+                                                            .icon_color(Color::Accent)
+                                                            .style(ButtonStyle::Filled)
+                                                            .disabled(
+                                                                is_editor_empty
+                                                                    || !is_model_selected
+                                                                    || self.waiting_for_summaries_to_send
                                                             )
-                                                            .map(|binding| {
-                                                                binding
-                                                                    .when(vim_mode_enabled, |kb| {
-                                                                        kb.size(rems_from_px(12.))
-                                                                    })
-                                                                    .into_any_element()
-                                                            }),
-                                                        ),
-                                                )
-                                                .on_click(move |_event, window, cx| {
-                                                    focus_handle.dispatch_action(&Chat, window, cx);
-                                                })
-                                                .when(is_editor_empty, |button| {
-                                                    button.tooltip(Tooltip::text(
-                                                        "Type a message to submit",
-                                                    ))
-                                                })
-                                                .when(is_generating, |button| {
-                                                    button.tooltip(Tooltip::text(
-                                                        "Cancel to submit a new message",
-                                                    ))
-                                                })
-                                                .when(!is_model_selected, |button| {
-                                                    button.tooltip(Tooltip::text(
-                                                        "Select a model to continue",
-                                                    ))
-                                                }),
-                                        ),
+                                                            .on_click(move |_event, window, cx| {
+                                                                focus_handle.dispatch_action(&Chat, window, cx);
+                                                            })
+                                                            .when(!is_editor_empty && is_model_selected, |button| {
+                                                                button.tooltip(move |window, cx| {
+                                                                    Tooltip::for_action(
+                                                                        "Send",
+                                                                        &Chat,
+                                                                        window,
+                                                                        cx,
+                                                                    )
+                                                                })
+                                                            })
+                                                            .when(is_editor_empty, |button| {
+                                                                button.tooltip(Tooltip::text(
+                                                                    "Type a message to submit",
+                                                                ))
+                                                            })
+                                                            .when(!is_model_selected, |button| {
+                                                                button.tooltip(Tooltip::text(
+                                                                    "Select a model to continue",
+                                                                ))
+                                                            })
+                                                    )
+                                                }
+                                            })
                                     ),
                             ),
                     )

crates/icons/src/icons.rs 🔗

@@ -191,6 +191,7 @@ pub enum IconName {
     SearchCode,
     SearchSelection,
     SelectAll,
+    Send,
     Server,
     Settings,
     SettingsAlt,
@@ -212,6 +213,7 @@ pub enum IconName {
     Star,
     StarFilled,
     Stop,
+    StopFilled,
     Strikethrough,
     Supermaven,
     SupermavenDisabled,