From f53250b5e1c5f43af01b580d37352b5eabae6e0e Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Fri, 17 Apr 2026 11:33:14 -0500 Subject: [PATCH] fix(tui): tighten /resume render, follow-up to 42721dbe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- ui-tui/src/app/useSessionLifecycle.ts | 2 +- ui-tui/src/hooks/useVirtualHistory.ts | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/ui-tui/src/app/useSessionLifecycle.ts b/ui-tui/src/app/useSessionLifecycle.ts index e4da3e1bf8..acd10135e1 100644 --- a/ui-tui/src/app/useSessionLifecycle.ts +++ b/ui-tui/src/app/useSessionLifecycle.ts @@ -186,7 +186,7 @@ export function useSessionLifecycle(opts: UseSessionLifecycleOptions) { status: 'ready', usage: usageFrom(r.info ?? null) }) - queueMicrotask(() => scrollRef.current?.scrollToBottom()) + setTimeout(() => scrollRef.current?.scrollToBottom(), 0) }) .catch((e: Error) => { sys(`error: ${e.message}`) diff --git a/ui-tui/src/hooks/useVirtualHistory.ts b/ui-tui/src/hooks/useVirtualHistory.ts index d33971fede..efa2642df3 100644 --- a/ui-tui/src/hooks/useVirtualHistory.ts +++ b/ui-tui/src/hooks/useVirtualHistory.ts @@ -38,6 +38,7 @@ export function useVirtualHistory( const refs = useRef(new Map 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)),