fix(tui): tighten /resume render, follow-up to 42721dbe

- useVirtualHistory: track last-seen ScrollBox metrics in a ref inside
  the post-layout effect and bump ver when sticky/top/vp change — the
  subscribe-based rearm was sufficient for fresh clicks but not for the
  "hydrated mid-commit, measured empty, then metrics settle" path where
  nothing re-triggered the hook until the next unrelated keystroke
- useSessionLifecycle: resume scrollToBottom from queueMicrotask to
  setTimeout(..., 0) so the fresh transcript has a full task turn to
  commit + measure before we try to land at the newest content
This commit is contained in:
Brooklyn Nicholson 2026-04-17 11:33:14 -05:00
parent 00591e3801
commit f53250b5e1
2 changed files with 22 additions and 2 deletions

View file

@ -38,6 +38,7 @@ export function useVirtualHistory(
const refs = useRef(new Map<string, (el: unknown) => void>())
const [ver, setVer] = useState(0)
const [hasScrollRef, setHasScrollRef] = useState(false)
const metrics = useRef({ sticky: true, top: 0, vp: 0 })
useLayoutEffect(() => {
setHasScrollRef(Boolean(scrollRef.current))
@ -141,10 +142,29 @@ export function useVirtualHistory(
}
}
const s = scrollRef.current
if (s) {
const next = {
sticky: s.isSticky(),
top: Math.max(0, s.getScrollTop() + s.getPendingDelta()),
vp: Math.max(0, s.getViewportHeight())
}
if (
next.sticky !== metrics.current.sticky ||
next.top !== metrics.current.top ||
next.vp !== metrics.current.vp
) {
metrics.current = next
dirty = true
}
}
if (dirty) {
setVer(v => v + 1)
}
}, [end, items, start])
}, [end, hasScrollRef, items, scrollRef, start])
return {
bottomSpacer: Math.max(0, total - (offsets[end] ?? total)),