diff --git a/crates/agent_ui/src/conversation_view/thread_view.rs b/crates/agent_ui/src/conversation_view/thread_view.rs index 53e63268c51aa1aa5537a87b6055dea62ecd630e..3af422ff2dcef7a043e0848efa030f159b22c795 100644 --- a/crates/agent_ui/src/conversation_view/thread_view.rs +++ b/crates/agent_ui/src/conversation_view/thread_view.rs @@ -4936,7 +4936,8 @@ impl ThreadView { } pub fn scroll_to_end(&mut self, cx: &mut Context) { - self.list_state.scroll_to_end(); + // Setting follow mode to Tail will scroll to the bottom of the list + self.list_state.set_follow_mode(gpui::FollowMode::Tail); cx.notify(); } @@ -4958,6 +4959,7 @@ impl ThreadView { } pub(crate) fn scroll_to_top(&mut self, cx: &mut Context) { + self.list_state.set_follow_mode(gpui::FollowMode::Normal); self.list_state.scroll_to(ListOffset::default()); cx.notify(); } @@ -4969,8 +4971,7 @@ impl ThreadView { cx: &mut Context, ) { let page_height = self.list_state.viewport_bounds().size.height; - self.list_state.scroll_by(-page_height * 0.9); - cx.notify(); + self.manual_scroll_by(-page_height * 0.9, cx); } fn scroll_output_page_down( @@ -4980,8 +4981,7 @@ impl ThreadView { cx: &mut Context, ) { let page_height = self.list_state.viewport_bounds().size.height; - self.list_state.scroll_by(page_height * 0.9); - cx.notify(); + self.manual_scroll_by(page_height * 0.9, cx); } fn scroll_output_line_up( @@ -4990,8 +4990,7 @@ impl ThreadView { window: &mut Window, cx: &mut Context, ) { - self.list_state.scroll_by(-window.line_height() * 3.); - cx.notify(); + self.manual_scroll_by(-window.line_height() * 3., cx); } fn scroll_output_line_down( @@ -5000,7 +4999,17 @@ impl ThreadView { window: &mut Window, cx: &mut Context, ) { - self.list_state.scroll_by(window.line_height() * 3.); + self.manual_scroll_by(window.line_height() * 3., cx); + } + + fn manual_scroll_by(&mut self, amount: Pixels, cx: &mut Context) { + self.list_state.scroll_by(amount); + let mode = if self.list_state.is_at_bottom() { + gpui::FollowMode::Tail + } else { + gpui::FollowMode::Normal + }; + self.list_state.set_follow_mode(mode); cx.notify(); } @@ -5034,6 +5043,7 @@ impl ThreadView { .rev() .find(|&i| matches!(entries.get(i), Some(AgentThreadEntry::UserMessage(_)))) { + self.list_state.set_follow_mode(gpui::FollowMode::Normal); self.list_state.scroll_to(ListOffset { item_ix: target_ix, offset_in_item: px(0.), @@ -5053,6 +5063,7 @@ impl ThreadView { if let Some(target_ix) = (current_ix + 1..entries.len()) .find(|&i| matches!(entries.get(i), Some(AgentThreadEntry::UserMessage(_)))) { + self.list_state.set_follow_mode(gpui::FollowMode::Normal); self.list_state.scroll_to(ListOffset { item_ix: target_ix, offset_in_item: px(0.), diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index 5525f5c17d2ad33e1ce9696afded1cea5447020c..50c2441dccd88f1e5f7314522a723867e519052d 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -527,6 +527,13 @@ impl ListState { ) } + /// Returns whether the list is scrolled to the bottom + pub fn is_at_bottom(&self) -> bool { + let current_offset = self.scroll_px_offset_for_scrollbar().y.abs(); + let max_offset = self.max_offset_for_scrollbar().y; + current_offset >= max_offset - px(1.0) + } + /// Scroll the list to the given offset pub fn scroll_to(&self, mut scroll_top: ListOffset) { let state = &mut *self.0.borrow_mut();