Detailed changes
@@ -308,12 +308,16 @@
"context": "AcpThread",
"bindings": {
"ctrl--": "pane::GoBack",
+ "ctrl-pageup": "agent::ScrollOutputPageUp",
+ "ctrl-pagedown": "agent::ScrollOutputPageDown",
},
},
{
"context": "AcpThread > Editor",
"use_key_equivalents": true,
"bindings": {
+ "ctrl-pageup": "agent::ScrollOutputPageUp",
+ "ctrl-pagedown": "agent::ScrollOutputPageDown",
"ctrl-shift-r": "agent::OpenAgentDiff",
"ctrl-shift-d": "git::Diff",
"shift-alt-y": "agent::KeepAll",
@@ -354,12 +354,16 @@
"context": "AcpThread",
"bindings": {
"ctrl--": "pane::GoBack",
+ "ctrl-pageup": "agent::ScrollOutputPageUp",
+ "ctrl-pagedown": "agent::ScrollOutputPageDown",
},
},
{
"context": "AcpThread > Editor",
"use_key_equivalents": true,
"bindings": {
+ "ctrl-pageup": "agent::ScrollOutputPageUp",
+ "ctrl-pagedown": "agent::ScrollOutputPageDown",
"shift-ctrl-r": "agent::OpenAgentDiff",
"shift-ctrl-d": "git::Diff",
"shift-alt-y": "agent::KeepAll",
@@ -310,12 +310,16 @@
"context": "AcpThread",
"bindings": {
"ctrl--": "pane::GoBack",
+ "ctrl-pageup": "agent::ScrollOutputPageUp",
+ "ctrl-pagedown": "agent::ScrollOutputPageDown",
},
},
{
"context": "AcpThread > Editor",
"use_key_equivalents": true,
"bindings": {
+ "ctrl-pageup": "agent::ScrollOutputPageUp",
+ "ctrl-pagedown": "agent::ScrollOutputPageDown",
"ctrl-shift-r": "agent::OpenAgentDiff",
"ctrl-shift-d": "git::Diff",
"shift-alt-y": "agent::KeepAll",
@@ -179,6 +179,10 @@ actions!(
ToggleThinkingEffortMenu,
/// Toggles fast mode for models that support it.
ToggleFastMode,
+ /// Scroll the output by one page up.
+ ScrollOutputPageUp,
+ /// Scroll the output by one page down.
+ ScrollOutputPageDown,
]
);
@@ -84,8 +84,9 @@ use crate::{
AuthorizeToolCall, ClearMessageQueue, CycleFavoriteModels, CycleModeSelector,
CycleThinkingEffort, EditFirstQueuedMessage, ExpandMessageEditor, Follow, KeepAll, NewThread,
OpenAddContextMenu, OpenAgentDiff, OpenHistory, RejectAll, RejectOnce,
- RemoveFirstQueuedMessage, SendImmediately, SendNextQueuedMessage, ToggleFastMode,
- ToggleProfileSelector, ToggleThinkingEffortMenu, ToggleThinkingMode, UndoLastReject,
+ RemoveFirstQueuedMessage, ScrollOutputPageDown, ScrollOutputPageUp, SendImmediately,
+ SendNextQueuedMessage, ToggleFastMode, ToggleProfileSelector, ToggleThinkingEffortMenu,
+ ToggleThinkingMode, UndoLastReject,
};
const STOPWATCH_THRESHOLD: Duration = Duration::from_secs(30);
@@ -549,17 +549,10 @@ impl ThreadView {
let scroll_top = list_state.logical_scroll_top();
let _ = thread_view.update(cx, |this, cx| {
if !is_following_tail {
- let is_at_bottom = {
- let current_offset =
- list_state.scroll_px_offset_for_scrollbar().y.abs();
- let max_offset = list_state.max_offset_for_scrollbar().y;
- current_offset >= max_offset - px(1.0)
- };
-
let is_generating =
matches!(this.thread.read(cx).status(), ThreadStatus::Generating);
- if is_at_bottom && is_generating {
+ if list_state.is_at_bottom() && is_generating {
list_state.set_follow_tail(true);
}
}
@@ -4977,6 +4970,33 @@ impl ThreadView {
cx.notify();
}
+ fn scroll_output_page_up(
+ &mut self,
+ _: &ScrollOutputPageUp,
+ _window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ let page_height = self.list_state.viewport_bounds().size.height;
+ self.list_state.set_follow_tail(false);
+ self.list_state.scroll_by(-page_height * 0.9);
+ cx.notify();
+ }
+
+ fn scroll_output_page_down(
+ &mut self,
+ _: &ScrollOutputPageDown,
+ _window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ let page_height = self.list_state.viewport_bounds().size.height;
+ self.list_state.set_follow_tail(false);
+ self.list_state.scroll_by(page_height * 0.9);
+ if self.list_state.is_at_bottom() {
+ self.list_state.set_follow_tail(true);
+ }
+ cx.notify();
+ }
+
pub fn open_thread_as_markdown(
&self,
workspace: Entity<Workspace>,
@@ -8448,6 +8468,8 @@ impl Render for ThreadView {
.on_action(cx.listener(Self::handle_toggle_command_pattern))
.on_action(cx.listener(Self::open_permission_dropdown))
.on_action(cx.listener(Self::open_add_context_menu))
+ .on_action(cx.listener(Self::scroll_output_page_up))
+ .on_action(cx.listener(Self::scroll_output_page_down))
.on_action(cx.listener(|this, _: &ToggleFastMode, _window, cx| {
this.toggle_fast_mode(cx);
}))
@@ -427,6 +427,13 @@ impl ListState {
self.0.borrow().follow_tail
}
+ /// Returns whether the list is scrolled to the bottom (within 1px).
+ 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();