fix(ui): add accelerated scrolling on alternate buffer mode (#23940)

Co-authored-by: jacob314 <jacob314@gmail.com>
This commit is contained in:
Dev Randalpura
2026-04-01 12:23:40 -04:00
committed by GitHub
parent 7d1848d578
commit 066da2a1d1
4 changed files with 246 additions and 3 deletions
@@ -16,6 +16,7 @@ import {
} from 'react';
import { getBoundingBox, type DOMElement } from 'ink';
import { useMouse, type MouseEvent } from '../hooks/useMouse.js';
import { terminalCapabilityManager } from '../utils/terminalCapabilityManager.js';
export interface ScrollState {
scrollTop: number;
@@ -125,8 +126,33 @@ export const ScrollProvider: React.FC<{ children: React.ReactNode }> = ({
}
}, []);
const scrollMomentumRef = useRef({
count: 0,
lastTime: 0,
});
const handleScroll = (direction: 'up' | 'down', mouseEvent: MouseEvent) => {
const delta = direction === 'up' ? -1 : 1;
let multiplier = 1;
const now = Date.now();
if (!terminalCapabilityManager.isGhosttyTerminal()) {
const timeSinceLastScroll = now - scrollMomentumRef.current.lastTime;
// 50ms threshold to consider scrolls consecutive
if (timeSinceLastScroll < 50) {
scrollMomentumRef.current.count += 1;
// Accelerate up to 3x, starting after 5 consecutive scrolls.
// Each consecutive scroll increases the multiplier by 0.1.
multiplier = Math.min(
3,
1 + Math.max(0, scrollMomentumRef.current.count - 5) * 0.1,
);
} else {
scrollMomentumRef.current.count = 0;
}
}
scrollMomentumRef.current.lastTime = now;
const delta = (direction === 'up' ? -1 : 1) * multiplier;
const candidates = findScrollableCandidates(
mouseEvent,
scrollablesRef.current,
@@ -142,15 +168,16 @@ export const ScrollProvider: React.FC<{ children: React.ReactNode }> = ({
const canScrollUp = effectiveScrollTop > 0.001;
const canScrollDown =
effectiveScrollTop < scrollHeight - innerHeight - 0.001;
const totalDelta = Math.round(pendingDelta + delta);
if (direction === 'up' && canScrollUp) {
pendingScrollsRef.current.set(candidate.id, pendingDelta + delta);
pendingScrollsRef.current.set(candidate.id, totalDelta);
scheduleFlush();
return true;
}
if (direction === 'down' && canScrollDown) {
pendingScrollsRef.current.set(candidate.id, pendingDelta + delta);
pendingScrollsRef.current.set(candidate.id, totalDelta);
scheduleFlush();
return true;
}