mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-11 03:31:55 +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
|
|
@ -1,6 +1,6 @@
|
|||
import { Box, type ScrollBoxHandle, Text } from '@hermes/ink'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { type ReactNode, type RefObject, useEffect, useMemo, useState } from 'react'
|
||||
import { type ReactNode, type RefObject, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import unicodeSpinners from 'unicode-animations'
|
||||
|
||||
import { $delegationState } from '../app/delegationStore.js'
|
||||
|
|
@ -13,7 +13,7 @@ import { fmtDuration } from '../domain/messages.js'
|
|||
import { stickyPromptFromViewport } from '../domain/viewport.js'
|
||||
import { buildSubagentTree, treeTotals, widthByDepth } from '../lib/subagentTree.js'
|
||||
import { fmtK } from '../lib/text.js'
|
||||
import { useViewportSnapshot } from '../lib/viewportStore.js'
|
||||
import { useScrollbarSnapshot, useViewportSnapshot } from '../lib/viewportStore.js'
|
||||
import type { Theme } from '../theme.js'
|
||||
import type { Msg, Usage } from '../types.js'
|
||||
|
||||
|
|
@ -377,7 +377,8 @@ export function StickyPromptTracker({ messages, offsets, scrollRef, onChange }:
|
|||
export function TranscriptScrollbar({ scrollRef, t }: TranscriptScrollbarProps) {
|
||||
const [hover, setHover] = useState(false)
|
||||
const [grab, setGrab] = useState<number | null>(null)
|
||||
const { scrollHeight: total, top: pos, viewportHeight: vp } = useViewportSnapshot(scrollRef)
|
||||
const grabRef = useRef<number | null>(null)
|
||||
const { scrollHeight: total, top: pos, viewportHeight: vp } = useScrollbarSnapshot(scrollRef)
|
||||
|
||||
if (!vp) {
|
||||
return <Box width={1} />
|
||||
|
|
@ -405,15 +406,20 @@ export function TranscriptScrollbar({ scrollRef, t }: TranscriptScrollbarProps)
|
|||
onMouseDown={(e: { localRow?: number }) => {
|
||||
const row = Math.max(0, Math.min(vp - 1, e.localRow ?? 0))
|
||||
const off = row >= thumbTop && row < thumbTop + thumb ? row - thumbTop : Math.floor(thumb / 2)
|
||||
|
||||
grabRef.current = off
|
||||
setGrab(off)
|
||||
jump(row, off)
|
||||
}}
|
||||
onMouseDrag={(e: { localRow?: number }) =>
|
||||
jump(Math.max(0, Math.min(vp - 1, e.localRow ?? 0)), grab ?? Math.floor(thumb / 2))
|
||||
jump(Math.max(0, Math.min(vp - 1, e.localRow ?? 0)), grabRef.current ?? Math.floor(thumb / 2))
|
||||
}
|
||||
onMouseEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
onMouseUp={() => setGrab(null)}
|
||||
onMouseUp={() => {
|
||||
grabRef.current = null
|
||||
setGrab(null)
|
||||
}}
|
||||
width={1}
|
||||
>
|
||||
{!scrollable ? (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue