fix: repair pinned message navigation for large sessions (#187)#188
fix: repair pinned message navigation for large sessions (#187)#188MCBoarder289 wants to merge 1 commit intowesm:mainfrom
Conversation
Navigating to a pinned message in a long session (ascending sort, oldest first) would silently fail — the session would open but the viewport would not reach the target message. Root cause: TanStack Virtual's scrollToIndex reconcile loop exits after just 1 stable frame (STABLE_FRAMES=1). When the target item is outside the current render window, scrollToIndex scrolls to an estimated position. The reconcile then fires once, sees that the estimated offset is unchanged (because ResizeObserver measurements are delayed behind bumpVersion's setTimeout(0)), declares the position "stable", and exits. The viewport lands at the wrong position with no further correction. A secondary issue: the virtualizer's postUpdate callback (which preserves scroll position during infinite-scroll prepends) would call scrollToOffset(currentScrollTop), resetting scrollState.index to null and breaking any in-flight reconcile loop early. Fix 1 (MessageList.svelte): after calling scrollToIndex for an unrendered target, schedule a retry in 2 animation frames. By then ResizeObserver and bumpVersion's setTimeout have fired, updating measurementsCache with real item sizes. Each retry either finds the item rendered and snaps to its exact measured offset via getOffsetForIndex, or calls scrollToIndex again with a more accurate estimate. A reqId guard cancels stale retries if a new navigation request arrives before the loop completes. Up to 10 render retries (~320 ms) are allowed. Fix 2 (createVirtualizer.svelte.ts): skip the postUpdate position-preserve when scrollState.index is non-null, i.e. when TanStack has an active scrollToIndex reconcile in progress. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
roborev: Combined Review (
|
|
I'm thinking the review from roborev here isn't too critical. The current/previous behavior was that the navigation fails entirely and viewport stays at the top. Now it navigates to the message, which is rendered and visible. I think in general this definitely improves the user experience, and if it's off slightly, that's basically on par with how it works today. But in my local testing of it, it seems to be a far better navigation accuracy. |
|
Probably not a big deal. I'll take a closer look at this when I can |
This PR addresses #187
Navigating to a pinned message in a long session (ascending sort, oldest first) would silently fail — the session would open but the viewport would not reach the target message.
Root cause: TanStack Virtual's scrollToIndex reconcile loop exits after
just 1 stable frame (STABLE_FRAMES=1). When the target item is outside
the current render window, scrollToIndex scrolls to an estimated
position. The reconcile then fires once, sees that the estimated offset
is unchanged (because ResizeObserver measurements are delayed behind
bumpVersion's setTimeout(0)), declares the position "stable", and
exits. The viewport lands at the wrong position with no further
correction.
A secondary issue: the virtualizer's postUpdate callback (which
preserves scroll position during infinite-scroll prepends) would call
scrollToOffset(currentScrollTop), resetting scrollState.index to null
and breaking any in-flight reconcile loop early.
Fix 1 (MessageList.svelte): after calling scrollToIndex for an
unrendered target, schedule a retry in 2 animation frames. By then
ResizeObserver and bumpVersion's setTimeout have fired, updating
measurementsCache with real item sizes. Each retry either finds the
item rendered and snaps to its exact measured offset via
getOffsetForIndex, or calls scrollToIndex again with a more accurate
estimate. A reqId guard cancels stale retries if a new navigation
request arrives before the loop completes. Up to 10 render retries
(~320 ms) are allowed.
Fix 2 (createVirtualizer.svelte.ts): skip the postUpdate
position-preserve when scrollState.index is non-null, i.e. when
TanStack has an active scrollToIndex reconcile in progress.