// Pin-to-bottom controller for the document scroll. // // Tracks whether the user is "near" the bottom of the page. While pinned, new // content (e.g. streaming tokens) scrolls the viewport to keep up. Once the // user scrolls up, pinning releases and updates stop forcing scroll until the // user returns to the bottom. const PIN_THRESHOLD_PX = 80; export interface AutoScroll { isPinned(): boolean; scrollToBottom(behavior?: ScrollBehavior): void; maybeScrollToBottom(): void; } export function createAutoScroll(): AutoScroll { let pinned = true; const scrollEl = () => document.scrollingElement || document.documentElement; const distanceFromBottom = () => { const el = scrollEl(); return el.scrollHeight - el.scrollTop - el.clientHeight; }; const onScroll = () => { pinned = distanceFromBottom() < PIN_THRESHOLD_PX; }; window.addEventListener('scroll', onScroll, { passive: true }); return { isPinned: () => pinned, scrollToBottom(behavior: ScrollBehavior = 'auto') { window.scrollTo({ top: scrollEl().scrollHeight, behavior }); pinned = true; }, maybeScrollToBottom() { if (!pinned) return; requestAnimationFrame(() => { window.scrollTo({ top: scrollEl().scrollHeight }); }); }, }; }