agent: Add button to scroll to top of the thread (#33130)

Danilo Leal created

Release Notes:

- agent: Added a button to scroll to top of the thread.

Change summary

assets/icons/arrow_up_alt.svg     |  3 +++
crates/agent/src/active_thread.rs | 26 +++++++++++++++++++++-----
crates/icons/src/icons.rs         |  1 +
3 files changed, 25 insertions(+), 5 deletions(-)

Detailed changes

assets/icons/arrow_up_alt.svg 🔗

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M8 3.5L12.5 8M8 3.5L3.5 8M8 3.5V12.5" stroke="black" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

crates/agent/src/active_thread.rs 🔗

@@ -24,7 +24,7 @@ use editor::{Editor, EditorElement, EditorEvent, EditorStyle, MultiBuffer};
 use gpui::{
     AbsoluteLength, Animation, AnimationExt, AnyElement, App, ClickEvent, ClipboardEntry,
     ClipboardItem, DefiniteLength, EdgesRefinement, Empty, Entity, EventEmitter, Focusable, Hsla,
-    ListAlignment, ListState, MouseButton, PlatformDisplay, ScrollHandle, Stateful,
+    ListAlignment, ListOffset, ListState, MouseButton, PlatformDisplay, ScrollHandle, Stateful,
     StyleRefinement, Subscription, Task, TextStyle, TextStyleRefinement, Transformation,
     UnderlineStyle, WeakEntity, WindowHandle, linear_color_stop, linear_gradient, list, percentage,
     pulsating_between,
@@ -48,8 +48,8 @@ use std::time::Duration;
 use text::ToPoint;
 use theme::ThemeSettings;
 use ui::{
-    Disclosure, IconButton, KeyBinding, PopoverMenuHandle, Scrollbar, ScrollbarState, TextSize,
-    Tooltip, prelude::*,
+    Disclosure, KeyBinding, PopoverMenuHandle, Scrollbar, ScrollbarState, TextSize, Tooltip,
+    prelude::*,
 };
 use util::ResultExt as _;
 use util::markdown::MarkdownCodeBlock;
@@ -1873,6 +1873,14 @@ impl ActiveThread {
                 }
             });
 
+        let scroll_to_top = IconButton::new(("scroll_to_top", ix), IconName::ArrowUpAlt)
+            .icon_size(IconSize::XSmall)
+            .icon_color(Color::Ignored)
+            .tooltip(Tooltip::text("Scroll To Top"))
+            .on_click(cx.listener(move |this, _, _, cx| {
+                this.scroll_to_top(cx);
+            }));
+
         // For all items that should be aligned with the LLM's response.
         const RESPONSE_PADDING_X: Pixels = px(19.);
 
@@ -1982,11 +1990,14 @@ impl ActiveThread {
                                     );
                                 })),
                         )
-                        .child(open_as_markdown),
+                        .child(open_as_markdown)
+                        .child(scroll_to_top),
                 )
                 .into_any_element(),
             None => feedback_container
-                .child(h_flex().child(open_as_markdown))
+                .child(h_flex()
+                    .child(open_as_markdown))
+                    .child(scroll_to_top)
                 .into_any_element(),
         };
 
@@ -3476,6 +3487,11 @@ impl ActiveThread {
         *is_expanded = !*is_expanded;
     }
 
+    pub fn scroll_to_top(&mut self, cx: &mut Context<Self>) {
+        self.list_state.scroll_to(ListOffset::default());
+        cx.notify();
+    }
+
     pub fn scroll_to_bottom(&mut self, cx: &mut Context<Self>) {
         self.list_state.reset(self.messages.len());
         cx.notify();

crates/icons/src/icons.rs 🔗

@@ -28,6 +28,7 @@ pub enum IconName {
     ArrowRight,
     ArrowRightLeft,
     ArrowUp,
+    ArrowUpAlt,
     ArrowUpFromLine,
     ArrowUpRight,
     ArrowUpRightAlt,