hermes-agent/ui-tui/src/__tests__
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
..
asCommandDispatch.test.ts fix(tui): slash.exec _pending_input commands, tool ANSI, terminal title 2026-04-18 09:30:48 -07:00
clipboard.test.ts fix(tui): raise picker selection contrast with inverse + bold 2026-04-21 14:31:21 -05:00
constants.test.ts refactor(tui): store-driven turn state + slash registry + module split 2026-04-16 12:34:45 -05:00
createGatewayEventHandler.test.ts feat(tui): anchor todo panel above streaming output 2026-04-26 16:26:50 -05:00
createSlashHandler.test.ts Merge remote-tracking branch 'origin/main' into bb/tui-long-session-perf 2026-04-26 13:39:57 -05:00
details.test.ts fix(tui): apply details mode live 2026-04-26 13:34:33 -05:00
emoji.test.ts fix(tui): inject VS16 so text-default emoji render as color glyphs 2026-04-21 15:52:39 -05:00
markdown.test.ts fix(tui): don't swallow Kimi/Qwen ~! ~? kaomoji as subscript spans 2026-04-21 17:34:48 -05:00
messages.test.ts fix(tui): restore resumed transcript lineage 2026-04-26 15:16:12 -05:00
osc52.test.ts fix(tui): raise picker selection contrast with inverse + bold 2026-04-21 14:31:21 -05:00
paths.test.ts feat(tui): append git branch to cwd label in status bar 2026-04-18 17:17:05 -05:00
platform.test.ts fix(tui): honor client copy shortcut over ssh 2026-04-25 14:44:39 -05:00
providers.test.ts chore(tui): /clean pass — inline one-off locals, tighten ConfirmPrompt 2026-04-19 07:55:38 -05:00
reasoning.test.ts fix(tui): filter thinking status noise 2026-04-26 13:59:56 -05:00
rpc.test.ts fix(ui-tui): surface RPC errors and guard invalid gateway responses 2026-04-13 14:17:52 -05:00
scroll.test.ts fix(tui): stabilize live progress rendering 2026-04-26 15:23:43 -05:00
stateIsolation.test.ts fix(tui): isolate turn state from app render 2026-04-26 15:40:38 -05:00
streamingMarkdown.test.ts perf(tui): incremental markdown during streaming 2026-04-26 16:21:34 -05:00
subagentTree.test.ts style(debug): add missing blank line between LogSnapshot and helpers 2026-04-22 16:34:05 -05:00
syntax.test.ts feat(tui): per-language syntax highlighting in markdown code fences 2026-04-18 09:48:38 -05:00
terminalParity.test.ts fix(tui): raise picker selection contrast with inverse + bold 2026-04-21 14:31:21 -05:00
terminalSetup.test.ts fix(tui): raise picker selection contrast with inverse + bold 2026-04-21 14:31:21 -05:00
text.test.ts fix(tui): format thinking paragraphs 2026-04-26 15:38:18 -05:00
textInputLineNav.test.ts fix(tui): up-arrow inside a multi-line buffer moves cursor, not history 2026-04-21 18:31:35 -05:00
textInputWrap.test.ts fix(tui): preserve composer width on narrow panes 2026-04-26 04:35:54 -05:00
theme.test.ts fix(tui): route update-behind banner through theme + auto-detect light terminals (#11300) 2026-04-20 11:12:13 -05:00
turnStore.test.ts feat(tui): collapse completed todo panel on turn end 2026-04-26 16:24:15 -05:00
useComposerState.test.ts fix(tui): raise picker selection contrast with inverse + bold 2026-04-21 14:31:21 -05:00
useConfigSync.test.ts feat(tui): per-section visibility for the details accordion 2026-04-24 02:34:32 -05:00
useSessionLifecycle.test.ts fix(tui): report actual session on exit 2026-04-26 15:55:01 -05:00
viewport.test.ts refactor(ui-tui): clean touched resize and sticky prompt paths 2026-04-23 14:37:00 -05:00
viewportStore.test.ts style(tui): apply formatter 2026-04-26 01:48:10 -05:00
virtualHistoryClamp.test.ts fix(tui): stabilize live progress rendering 2026-04-26 15:23:43 -05:00
wheelAccel.test.ts feat(tui): port claude-code's wheel accel state machine 2026-04-26 17:16:11 -05:00