assistant2: Adjust "generating" state design (#23299)

Danilo Leal created

To ensure message readability is not affected in any way.


https://github.com/user-attachments/assets/9a2ad949-1a8a-4c31-ad3c-db70f48e5d98

Release Notes:

- N/A

Change summary

crates/assistant2/src/active_thread.rs   | 81 +------------------------
crates/assistant2/src/assistant_panel.rs |  3 
crates/assistant2/src/message_editor.rs  | 76 +++++++++++++++++++-----
3 files changed, 65 insertions(+), 95 deletions(-)

Detailed changes

crates/assistant2/src/active_thread.rs 🔗

@@ -1,20 +1,18 @@
 use std::sync::Arc;
-use std::time::Duration;
 
 use assistant_tool::ToolWorkingSet;
 use collections::HashMap;
 use gpui::{
-    linear_color_stop, linear_gradient, list, percentage, AbsoluteLength, Animation, AnimationExt,
-    AnyElement, AppContext, DefiniteLength, EdgesRefinement, Empty, FocusHandle, Length,
+    list, AbsoluteLength, AnyElement, AppContext, DefiniteLength, EdgesRefinement, Empty, Length,
     ListAlignment, ListOffset, ListState, Model, StyleRefinement, Subscription,
-    TextStyleRefinement, Transformation, UnderlineStyle, View, WeakView,
+    TextStyleRefinement, UnderlineStyle, View, WeakView,
 };
 use language::LanguageRegistry;
 use language_model::Role;
 use markdown::{Markdown, MarkdownStyle};
 use settings::Settings as _;
 use theme::ThemeSettings;
-use ui::{prelude::*, Divider, KeyBinding};
+use ui::prelude::*;
 use workspace::Workspace;
 
 use crate::thread::{MessageId, Thread, ThreadError, ThreadEvent};
@@ -29,7 +27,6 @@ pub struct ActiveThread {
     list_state: ListState,
     rendered_messages_by_id: HashMap<MessageId, View<Markdown>>,
     last_error: Option<ThreadError>,
-    focus_handle: FocusHandle,
     _subscriptions: Vec<Subscription>,
 }
 
@@ -39,7 +36,6 @@ impl ActiveThread {
         workspace: WeakView<Workspace>,
         language_registry: Arc<LanguageRegistry>,
         tools: Arc<ToolWorkingSet>,
-        focus_handle: FocusHandle,
         cx: &mut ViewContext<Self>,
     ) -> Self {
         let subscriptions = vec![
@@ -62,7 +58,6 @@ impl ActiveThread {
                 }
             }),
             last_error: None,
-            focus_handle,
             _subscriptions: subscriptions,
         };
 
@@ -280,9 +275,7 @@ impl ActiveThread {
                 .child(
                     v_flex()
                         .bg(colors.editor_background)
-                        .rounded_t_lg()
-                        .rounded_bl_lg()
-                        .rounded_br_none()
+                        .rounded_lg()
                         .border_1()
                         .border_color(colors.border)
                         .shadow_sm()
@@ -326,74 +319,10 @@ impl ActiveThread {
 }
 
 impl Render for ActiveThread {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let is_streaming_completion = self.thread.read(cx).is_streaming();
-        let panel_bg = cx.theme().colors().panel_background;
-        let focus_handle = self.focus_handle.clone();
-
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_flex()
             .size_full()
             .pt_1p5()
             .child(list(self.list_state.clone()).flex_grow())
-            .when(is_streaming_completion, |parent| {
-                parent.child(
-                    h_flex()
-                        .w_full()
-                        .pb_2p5()
-                        .absolute()
-                        .bottom_0()
-                        .flex_shrink()
-                        .justify_center()
-                        .bg(linear_gradient(
-                            180.,
-                            linear_color_stop(panel_bg.opacity(0.0), 0.),
-                            linear_color_stop(panel_bg, 1.),
-                        ))
-                        .child(
-                            h_flex()
-                                .flex_none()
-                                .p_1p5()
-                                .bg(cx.theme().colors().editor_background)
-                                .border_1()
-                                .border_color(cx.theme().colors().border)
-                                .rounded_md()
-                                .shadow_lg()
-                                .gap_1()
-                                .child(
-                                    Icon::new(IconName::ArrowCircle)
-                                        .size(IconSize::Small)
-                                        .color(Color::Muted)
-                                        .with_animation(
-                                            "arrow-circle",
-                                            Animation::new(Duration::from_secs(2)).repeat(),
-                                            |icon, delta| {
-                                                icon.transform(Transformation::rotate(percentage(
-                                                    delta,
-                                                )))
-                                            },
-                                        ),
-                                )
-                                .child(
-                                    Label::new("Generating…")
-                                        .size(LabelSize::Small)
-                                        .color(Color::Muted),
-                                )
-                                .child(Divider::vertical())
-                                .child(
-                                    Button::new("cancel-generation", "Cancel")
-                                        .label_size(LabelSize::Small)
-                                        .key_binding(KeyBinding::for_action_in(
-                                            &editor::actions::Cancel,
-                                            &self.focus_handle,
-                                            cx,
-                                        ))
-                                        .on_click(move |_event, cx| {
-                                            focus_handle
-                                                .dispatch_action(&editor::actions::Cancel, cx);
-                                        }),
-                                ),
-                        ),
-                )
-            })
     }
 }

crates/assistant2/src/assistant_panel.rs 🔗

@@ -122,7 +122,6 @@ impl AssistantPanel {
                     workspace,
                     language_registry,
                     tools.clone(),
-                    message_editor.focus_handle(cx),
                     cx,
                 )
             }),
@@ -163,7 +162,6 @@ impl AssistantPanel {
                 self.workspace.clone(),
                 self.language_registry.clone(),
                 self.tools.clone(),
-                self.focus_handle(cx),
                 cx,
             )
         });
@@ -200,7 +198,6 @@ impl AssistantPanel {
                 self.workspace.clone(),
                 self.language_registry.clone(),
                 self.tools.clone(),
-                self.focus_handle(cx),
                 cx,
             )
         });

crates/assistant2/src/message_editor.rs 🔗

@@ -4,17 +4,16 @@ use editor::actions::MoveUp;
 use editor::{Editor, EditorElement, EditorEvent, EditorStyle};
 use fs::Fs;
 use gpui::{
-    AppContext, DismissEvent, FocusableView, Model, Subscription, TextStyle, View, WeakModel,
-    WeakView,
+    pulsating_between, Animation, AnimationExt, AppContext, DismissEvent, FocusableView, Model,
+    Subscription, TextStyle, View, WeakModel, WeakView,
 };
 use language_model::{LanguageModelRegistry, LanguageModelRequestTool};
 use language_model_selector::LanguageModelSelector;
 use rope::Point;
 use settings::Settings;
+use std::time::Duration;
 use theme::ThemeSettings;
-use ui::{
-    prelude::*, ButtonLike, ElevationIndex, KeyBinding, PopoverMenu, PopoverMenuHandle, Switch,
-};
+use ui::{prelude::*, ButtonLike, KeyBinding, PopoverMenu, PopoverMenuHandle, Switch, TintColor};
 use workspace::Workspace;
 
 use crate::assistant_model_selector::AssistantModelSelector;
@@ -261,6 +260,8 @@ impl Render for MessageEditor {
         let focus_handle = self.editor.focus_handle(cx);
         let inline_context_picker = self.inline_context_picker.clone();
         let bg_color = cx.theme().colors().editor_background;
+        let is_streaming_completion = self.thread.read(cx).is_streaming();
+        let button_width = px(64.);
 
         v_flex()
             .key_context("MessageEditor")
@@ -340,21 +341,64 @@ impl Render for MessageEditor {
                                         cx,
                                     )),
                             )
-                            .child(
-                                h_flex().gap_1().child(self.model_selector.clone()).child(
-                                    ButtonLike::new("chat")
+                            .child(h_flex().gap_1().child(self.model_selector.clone()).child(
+                                if is_streaming_completion {
+                                    ButtonLike::new("cancel-generation")
+                                        .width(button_width.into())
+                                        .style(ButtonStyle::Tinted(TintColor::Accent))
+                                        .child(
+                                            h_flex()
+                                                .w_full()
+                                                .justify_between()
+                                                .child(
+                                                    Label::new("Cancel")
+                                                        .size(LabelSize::Small)
+                                                        .with_animation(
+                                                            "pulsating-label",
+                                                            Animation::new(Duration::from_secs(2))
+                                                                .repeat()
+                                                                .with_easing(pulsating_between(
+                                                                    0.4, 0.8,
+                                                                )),
+                                                            |label, delta| label.alpha(delta),
+                                                        ),
+                                                )
+                                                .children(
+                                                    KeyBinding::for_action_in(
+                                                        &editor::actions::Cancel,
+                                                        &focus_handle,
+                                                        cx,
+                                                    )
+                                                    .map(|binding| binding.into_any_element()),
+                                                ),
+                                        )
+                                        .on_click(move |_event, cx| {
+                                            focus_handle
+                                                .dispatch_action(&editor::actions::Cancel, cx);
+                                        })
+                                } else {
+                                    ButtonLike::new("submit-message")
+                                        .width(button_width.into())
                                         .style(ButtonStyle::Filled)
-                                        .layer(ElevationIndex::ModalSurface)
-                                        .child(Label::new("Submit").size(LabelSize::Small))
-                                        .children(
-                                            KeyBinding::for_action_in(&Chat, &focus_handle, cx)
-                                                .map(|binding| binding.into_any_element()),
+                                        .child(
+                                            h_flex()
+                                                .w_full()
+                                                .justify_between()
+                                                .child(Label::new("Submit").size(LabelSize::Small))
+                                                .children(
+                                                    KeyBinding::for_action_in(
+                                                        &Chat,
+                                                        &focus_handle,
+                                                        cx,
+                                                    )
+                                                    .map(|binding| binding.into_any_element()),
+                                                ),
                                         )
                                         .on_click(move |_event, cx| {
                                             focus_handle.dispatch_action(&Chat, cx);
-                                        }),
-                                ),
-                            ),
+                                        })
+                                },
+                            )),
                     ),
             )
     }