mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-11 21:57:14 +03:00
fix(live): scope scroll lock to session wrapper, let user scroll cancel corrections
Watching document.body caught every mutation on the page — shader animations, Bun HMR indicators, tooltips, anything — and fired a correction on each one, which fought the user when they tried to scroll mid-session. Now the observer only responds to mutations inside the session's wrapper. On user scroll intent (wheel / touchstart / touchmove / arrow & page keys), cancel any pending rAF correction and re-anchor to the element's new position, so momentum scrolls don't get yanked back by a stale correction. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
ad17880af1
commit
b99ab4db2c
@@ -1364,19 +1364,39 @@
|
||||
scrollLockRaf = requestAnimationFrame(correct);
|
||||
};
|
||||
|
||||
scrollLockObserver = new MutationObserver(schedule);
|
||||
// Filter to mutations that touch our session's wrapper. Watching the
|
||||
// whole body means shader animations, HMR indicators, tooltips, and
|
||||
// every other DOM change elsewhere on the page fires corrections —
|
||||
// which fight the user on scroll.
|
||||
scrollLockObserver = new MutationObserver((mutations) => {
|
||||
for (const m of mutations) {
|
||||
if (m.target?.closest?.('[data-impeccable-variants="' + sessionId + '"]')) {
|
||||
schedule();
|
||||
return;
|
||||
}
|
||||
for (const n of m.addedNodes) {
|
||||
if (n.nodeType === 1 && (n.matches?.('[data-impeccable-variants="' + sessionId + '"]') || n.querySelector?.('[data-impeccable-variants="' + sessionId + '"]'))) {
|
||||
schedule();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
scrollLockObserver.observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
// Treat explicit user scroll intent as a re-anchor: update the target
|
||||
// top to wherever the element is now, so we don't fight the user.
|
||||
// Treat explicit user scroll intent as a re-anchor: cancel any pending
|
||||
// correction, then update the target top to the element's new position
|
||||
// so we don't drag them back on the next mutation.
|
||||
scrollLockAbort = new AbortController();
|
||||
const sig = { signal: scrollLockAbort.signal };
|
||||
const reanchor = () => {
|
||||
if (scrollLockRaf != null) { cancelAnimationFrame(scrollLockRaf); scrollLockRaf = null; }
|
||||
const el = resolveScrollLockTarget(sessionId);
|
||||
if (el) scrollLockTargetTop = el.getBoundingClientRect().top;
|
||||
};
|
||||
window.addEventListener('wheel', reanchor, { passive: true, ...sig });
|
||||
window.addEventListener('touchstart', reanchor, { passive: true, ...sig });
|
||||
window.addEventListener('touchmove', reanchor, { passive: true, ...sig });
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (['PageDown', 'PageUp', ' ', 'End', 'Home', 'ArrowDown', 'ArrowUp'].includes(e.key)) reanchor();
|
||||
}, sig);
|
||||
|
||||
Reference in New Issue
Block a user