mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-12 03:42:08 +00:00
fix(tui): steady transcript scrollbar (#20917)
* fix(tui): steady transcript scrollbar Keep the visible scrollbar tied to committed viewport position while virtual history can still prefetch against pending scroll targets, and preserve drag grab offset synchronously for native-feeling scrollbar drags. * fix(tui): smooth precision wheel scroll Replace the opt-scroll throttle with frame-sized coalescing so modifier wheel gestures stay line-precise without stepping.
This commit is contained in:
parent
53a024994a
commit
5ccab51fa8
6 changed files with 196 additions and 34 deletions
44
ui-tui/src/__tests__/precisionWheel.test.ts
Normal file
44
ui-tui/src/__tests__/precisionWheel.test.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { computePrecisionWheelStep, initPrecisionWheel } from '../lib/precisionWheel.js'
|
||||
|
||||
describe('precisionWheel', () => {
|
||||
it('passes the first modifier-held wheel event', () => {
|
||||
const s = initPrecisionWheel()
|
||||
|
||||
expect(computePrecisionWheelStep(s, 1, true, 1000)).toEqual({ active: true, entered: true, rows: 1 })
|
||||
})
|
||||
|
||||
it('coalesces same-frame events without throttling line-by-line scroll', () => {
|
||||
const s = initPrecisionWheel()
|
||||
|
||||
computePrecisionWheelStep(s, 1, true, 1000)
|
||||
|
||||
expect(computePrecisionWheelStep(s, 1, true, 1008).rows).toBe(0)
|
||||
expect(computePrecisionWheelStep(s, 1, true, 1016).rows).toBe(1)
|
||||
})
|
||||
|
||||
it('keeps queued momentum in precision mode briefly after modifier release', () => {
|
||||
const s = initPrecisionWheel()
|
||||
|
||||
computePrecisionWheelStep(s, 1, true, 1000)
|
||||
|
||||
expect(computePrecisionWheelStep(s, 1, false, 1050)).toMatchObject({ active: true, rows: 1 })
|
||||
})
|
||||
|
||||
it('leaves precision mode once modifier-free momentum goes idle', () => {
|
||||
const s = initPrecisionWheel()
|
||||
|
||||
computePrecisionWheelStep(s, 1, true, 1000)
|
||||
|
||||
expect(computePrecisionWheelStep(s, 1, false, 1100)).toEqual({ active: false, entered: false, rows: 0 })
|
||||
})
|
||||
|
||||
it('does not coalesce immediate reversals', () => {
|
||||
const s = initPrecisionWheel()
|
||||
|
||||
computePrecisionWheelStep(s, 1, true, 1000)
|
||||
|
||||
expect(computePrecisionWheelStep(s, -1, true, 1008).rows).toBe(1)
|
||||
})
|
||||
})
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { getViewportSnapshot, viewportSnapshotKey } from '../lib/viewportStore.js'
|
||||
import { getScrollbarSnapshot, getViewportSnapshot, scrollbarSnapshotKey, viewportSnapshotKey } from '../lib/viewportStore.js'
|
||||
|
||||
describe('viewportStore', () => {
|
||||
it('normalizes absent scroll handles', () => {
|
||||
|
|
@ -51,4 +51,35 @@ describe('viewportStore', () => {
|
|||
expect(snap.atBottom).toBe(true)
|
||||
expect(snap.scrollHeight).toBe(20)
|
||||
})
|
||||
|
||||
it('keeps scrollbar position tied to committed scrollTop, not pending target', () => {
|
||||
const handle = {
|
||||
getPendingDelta: () => 24,
|
||||
getScrollHeight: () => 100,
|
||||
getScrollTop: () => 10,
|
||||
getViewportHeight: () => 20,
|
||||
isSticky: () => false
|
||||
}
|
||||
|
||||
const viewport = getViewportSnapshot(handle as any)
|
||||
const scrollbar = getScrollbarSnapshot(handle as any)
|
||||
|
||||
expect(viewport.top).toBe(34)
|
||||
expect(scrollbar).toEqual({
|
||||
scrollHeight: 100,
|
||||
top: 10,
|
||||
viewportHeight: 20
|
||||
})
|
||||
expect(scrollbarSnapshotKey(scrollbar)).toBe('10:20:100')
|
||||
})
|
||||
|
||||
it('clamps scrollbar position to committed scroll bounds', () => {
|
||||
const handle = {
|
||||
getScrollHeight: () => 30,
|
||||
getScrollTop: () => 50,
|
||||
getViewportHeight: () => 20
|
||||
}
|
||||
|
||||
expect(getScrollbarSnapshot(handle as any).top).toBe(10)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue