hermes-agent-features/ui-tui/src/app
Brooklyn Nicholson 4395c2b007 feat(tui): port claude-code's wheel accel state machine
Replaces the static WHEEL_SCROLL_STEP=1 multiplier on wheel events
with an adaptive accel state machine that infers user intent from
inter-event timing.

Algorithm ported straight from claude-code's
src/components/ScrollKeybindingHandler.tsx.  All tuning constants,
the native/xterm.js path split, the encoder-bounce detection, the
trackpad-burst signature → all theirs.  This file is a mechanical
port into our module structure.

What it does:

  precision click (>500ms gap)   1 row/event   (deliberate scan)
  sustained mouse (40-200ms)     2-6 rows      (decay curve)
  detected wheel bounce          ramps to 15   (sticky wheel-mode)
  trackpad flick (5+ <5ms)       1 row/event   (burst detect)
  direction reversal             reset to base

Two implementation paths:

  * native terminals (ghostty, iTerm2, Kitty, WezTerm) — linear
    window-ramp + optional wheel-mode curve triggered by detected
    encoder bounce.  SGR proportional reporting handled via the
    burst-count guard.

  * xterm.js (VS Code / Cursor / browser terminals) — pure
    exponential-decay curve with fractional carry.  Events arrive
    1-per-notch with no pre-amplification, so the curve is more
    aggressive.

Selected at construction via isXtermJs() from @hermes/ink (now
exported).  Per-user tune via HERMES_TUI_SCROLL_SPEED (alias
CLAUDE_CODE_SCROLL_SPEED for portability).

13 unit tests covering direction flip/bounce/reversal, idle
disengage, trackpad-burst disengage, frac invariants, and the
native vs xterm.js branches.

Profiled under --rate 30 (stress test) and --rate 10 (realistic
sustained scroll): accel ramps to cap=6 at 30Hz burst, decays to
1-3 rows at sparse 10Hz clicks.  Perf is comparable to baseline
because accel IS multiplying step — the win is perceptual (fast
flicks cover distance, slow clicks keep precision), not raw fps.

Companion to the earlier WHEEL_SCROLL_STEP=1 change: that set the
base; this modulates around it.
2026-04-26 17:16:11 -05:00
..
slash fix(tui): stabilize live progress rendering 2026-04-26 15:23:43 -05:00
createGatewayEventHandler.ts feat(tui): anchor todo panel above streaming output 2026-04-26 16:26:50 -05:00
createSlashHandler.ts feat(tui): replace /clear double-press gate with a proper confirm overlay 2026-04-18 18:04:08 -05:00
delegationStore.ts feat(tui): subagent spawn observability overlay 2026-04-22 10:38:17 -05:00
gatewayContext.tsx feat: add inline token count etc and fix venv 2026-04-15 10:20:56 -05:00
inputSelectionStore.ts fix(tui): Ctrl+C with input selection actually preserves input (lift handler to app level) 2026-04-18 16:28:51 -05:00
interfaces.ts fix(tui): stabilize live progress rendering 2026-04-26 15:23:43 -05:00
overlayStore.ts feat(tui): subagent spawn observability overlay 2026-04-22 10:38:17 -05:00
scroll.ts fix(tui): keep stream cadence responsive while typing 2026-04-26 04:32:55 -05:00
setupHandoff.ts feat(tui): /model and /setup slash commands with in-place CLI handoff 2026-04-17 10:58:18 -05:00
spawnHistoryStore.ts feat(tui): subagent spawn observability overlay 2026-04-22 10:38:17 -05:00
turnController.ts chore(tui): sort imports 2026-04-26 15:56:47 -05:00
turnStore.ts feat(tui): collapse completed todo panel on turn end 2026-04-26 16:24:15 -05:00
uiStore.ts fix(tui): apply details mode live 2026-04-26 13:34:33 -05:00
useComposerState.ts fix(tui): address Copilot review on editor handoff 2026-04-25 20:34:24 -05:00
useConfigSync.ts fix(tui): apply details mode live 2026-04-26 13:34:33 -05:00
useInputHandlers.ts feat(tui): port claude-code's wheel accel state machine 2026-04-26 17:16:11 -05:00
useLongRunToolCharms.ts chore(tui): clean live progress lint 2026-04-26 15:42:07 -05:00
useMainApp.ts chore(tui): clean live progress lint 2026-04-26 15:42:07 -05:00
useSessionLifecycle.ts fix(tui): report actual session on exit 2026-04-26 15:55:01 -05:00
useSubmission.ts fix(tui): stabilize live progress rendering 2026-04-26 15:23:43 -05:00