fix(#1297): prevent scroll jump on loadMore prepend

Two cooperating fixes:

1. overflow-anchor: none on .live — disables browser scroll anchoring
   so the manual scrollTop compensation in loadMore() is the sole
   mechanism. Without this the browser auto-adjusts scrollTop when
   rows are prepended above the viewport, then our += delta doubles
   the compensation, causing the erratic jump.

2. updateLoadMoreBtn() moved before the beforeH baseline capture —
   if histHasMore becomes false the load-more button is removed here
   rather than after the delta is applied, so the button height is
   already baked into beforeH and doesn't shift the viewport post-
   compensation.
This commit is contained in:
iris 2026-06-04 19:48:18 +02:00 committed by mara
commit 0f77fc3598
2 changed files with 13 additions and 1 deletions

View file

@ -36,6 +36,11 @@
overflow-y: auto;
max-height: 32em;
font-family: inherit;
/* Disable browser scroll anchoring the terminal manages scroll
position manually (snapToBottom + loadMore compensation). Scroll
anchoring would double-compensate scrollTop during loadMore()
prepends, causing an erratic jump. */
overflow-anchor: none;
}
.live.terminal {
background: transparent;

View file

@ -316,6 +316,12 @@ export function create(opts) {
histHasMore = body.has_more || false;
if (typeof body.min_id === 'number') histMinId = body.min_id;
// Resolve load-more button state before capturing the scroll
// baseline so that any button removal is already reflected in
// beforeH — otherwise the button's height would be missing from
// the delta and the viewport would drift up by that amount.
updateLoadMoreBtn();
if (events.length > 0) {
// Render into a detached element; `log` is temporarily redirected
// so that row/details/etc. append there instead of rootLog.
@ -338,9 +344,10 @@ export function create(opts) {
const beforeH = rootLog.scrollHeight;
while (tempEl.firstChild) rootLog.insertBefore(tempEl.firstChild, anchor);
// Compensate scroll so the viewport stays on the same content.
// overflow-anchor: none on .live ensures the browser does not
// also auto-adjust scrollTop (which would double the delta).
rootLog.scrollTop += rootLog.scrollHeight - beforeH;
}
updateLoadMoreBtn();
} catch (err) {
console.warn('loadMore failed', err);
updateLoadMoreBtn();