mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-08 03:01:47 +00:00
fix(tui): add modifier-held precision wheel scrolling
Route Option/Alt or Ctrl wheel input through a gated precision path that scrolls at most one row per short interval, while preserving the existing accelerated behavior for plain wheel input. Keep precision active briefly after modifier release so queued wheel events from the same gesture do not jump into acceleration mid-stream.
This commit is contained in:
parent
b978fd8b26
commit
fc0f358f37
1 changed files with 42 additions and 1 deletions
|
|
@ -21,6 +21,8 @@ import { patchTurnState } from './turnStore.js'
|
|||
import { getUiState } from './uiStore.js'
|
||||
|
||||
const isCtrl = (key: { ctrl: boolean }, ch: string, target: string) => key.ctrl && ch.toLowerCase() === target
|
||||
const PRECISION_WHEEL_MIN_GAP_MS = 80
|
||||
const PRECISION_WHEEL_STICKY_MS = 80
|
||||
|
||||
export function useInputHandlers(ctx: InputHandlerContext): InputHandlerResult {
|
||||
const { actions, composer, gateway, terminal, voice, wheelStep } = ctx
|
||||
|
|
@ -36,6 +38,10 @@ export function useInputHandlers(ctx: InputHandlerContext): InputHandlerResult {
|
|||
// rows = wheelStep × accelMult. State mutates in place across renders.
|
||||
const wheelAccelRef = useRef(initWheelAccelForHost())
|
||||
|
||||
const precisionWheelRef = useRef<{ active: boolean; dir: 0 | -1 | 1; lastEventAtMs: number; lastScrollAtMs: number }>(
|
||||
{ active: false, dir: 0, lastEventAtMs: 0, lastScrollAtMs: 0 }
|
||||
)
|
||||
|
||||
useEffect(() => () => clearTimeout(scrollIdleTimer.current ?? undefined), [])
|
||||
|
||||
const scrollTranscript = (delta: number) => {
|
||||
|
|
@ -284,8 +290,43 @@ export function useInputHandlers(ctx: InputHandlerContext): InputHandlerResult {
|
|||
|
||||
if (key.wheelUp || key.wheelDown) {
|
||||
const dir: -1 | 1 = key.wheelUp ? -1 : 1
|
||||
const now = Date.now()
|
||||
// Modifier-held wheel = precision mode: at most one wheelStep per short
|
||||
// interval. Smooth mice / trackpads emit many raw wheel events for one
|
||||
// intended line step, so raw 1:1 still moves too far.
|
||||
// SGR/X10 mouse encoding only carries shift/meta/ctrl bits; Cmd on
|
||||
// macOS is intercepted by the terminal, so we honor Option (meta) on
|
||||
// Mac / Alt (meta) on Win+Linux / Ctrl as a portable fallback. Shift
|
||||
// is reserved for selection extension.
|
||||
const hasModifier = key.meta || key.ctrl
|
||||
const precision = precisionWheelRef.current
|
||||
// Keep precision active through the current wheel burst after the
|
||||
// modifier is released. Otherwise a stream of queued/momentum wheel
|
||||
// events can hand off mid-burst into the accelerated path and jump.
|
||||
const precisionSticky = now - precision.lastEventAtMs < PRECISION_WHEEL_STICKY_MS
|
||||
|
||||
if (hasModifier || precisionSticky) {
|
||||
if (!precision.active) {
|
||||
precision.active = true
|
||||
wheelAccelRef.current = initWheelAccelForHost()
|
||||
}
|
||||
|
||||
precision.lastEventAtMs = now
|
||||
|
||||
if (dir === precision.dir && now - precision.lastScrollAtMs < PRECISION_WHEEL_MIN_GAP_MS) {
|
||||
return
|
||||
}
|
||||
|
||||
precision.lastScrollAtMs = now
|
||||
precision.dir = dir
|
||||
|
||||
return scrollTranscript(dir * wheelStep)
|
||||
}
|
||||
|
||||
precision.active = false
|
||||
|
||||
// 0 = direction-flip bounce deferred; skip the no-op scroll.
|
||||
const rows = computeWheelStep(wheelAccelRef.current, dir, Date.now())
|
||||
const rows = computeWheelStep(wheelAccelRef.current, dir, now)
|
||||
|
||||
return rows ? scrollTranscript(dir * rows * wheelStep) : undefined
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue