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:
brooklyn! 2026-05-06 14:50:31 -07:00 committed by GitHub
parent 53a024994a
commit 5ccab51fa8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 196 additions and 34 deletions

View file

@ -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)
})
})