diff --git a/ui-tui/src/app/useInputHandlers.ts b/ui-tui/src/app/useInputHandlers.ts index fff73d9cfa..b18dcbbd16 100644 --- a/ui-tui/src/app/useInputHandlers.ts +++ b/ui-tui/src/app/useInputHandlers.ts @@ -296,7 +296,14 @@ export function useInputHandlers(ctx: InputHandlerContext): InputHandlerResult { if (key.pageUp || key.pageDown) { const viewport = terminal.scrollRef.current?.getViewportHeight() ?? Math.max(6, (terminal.stdout?.rows ?? 24) - 8) - const step = Math.max(4, viewport - 2) + // Half-viewport per keystroke. A whole-viewport jump (our old + // `viewport - 2`) fully replaces what's on screen — no visual + // continuity, the user can't scan — AND it lands right at Ink's + // `delta < innerHeight` fast-path threshold, disqualifying the + // DECSTBM blit on every press. Half-viewport keeps 50% continuity, + // well under the threshold, and two presses still scroll the same + // total distance. + const step = Math.max(4, Math.floor(viewport / 2)) return scrollTranscript(key.pageUp ? -step : step) } diff --git a/ui-tui/src/config/limits.ts b/ui-tui/src/config/limits.ts index a2e817d862..889ac4d686 100644 --- a/ui-tui/src/config/limits.ts +++ b/ui-tui/src/config/limits.ts @@ -4,4 +4,20 @@ export const LIVE_RENDER_MAX_LINES = 240 export const LONG_MSG = 300 export const MAX_HISTORY = 800 export const THINKING_COT_MAX = 160 -export const WHEEL_SCROLL_STEP = 6 +// Rows scrolled per wheel-notch event. +// +// One notch of a mechanical wheel emits multiple wheel events (3-5 per +// click in most terminals; trackpad flicks emit 100+). Each event scrolls +// WHEEL_SCROLL_STEP rows. The product = rows-per-click. +// +// 1 = pure line-by-line. Small per-event delta keeps Ink's DECSTBM fast +// path firing (each scroll < viewport-1) and produces smooth visible +// motion — the user can scan content mid-scroll. We were at 6 before +// (= ~20-30 rows per notch) which visually teleported and forced the +// virtualization to reshape the mount range on every event. +// +// If this feels sluggish on precision scrolls, porting claude-code's +// wheel accel state machine (ScrollKeybindingHandler.tsx) is the right +// next step — it ramps step up during sustained fast clicks and decays +// on pause. +export const WHEEL_SCROLL_STEP = 1