1
0
mirror of https://github.com/ohwgiles/laminar.git synced 2026-09-23 04:20:41 +00:00

Add sticky auto-scroll for build output

Implements #220 - if the page is scrolled to the very bottom while a
build log is streaming, the scroll position "sticks" there and follows
new output as it is rendered, like tail -f. Any upward scroll
disengages this behaviour, and scrolling back to the bottom re-engages
it, so no toggle button is needed.

The sticky state is tracked with wheel/scroll event listeners rather
than sampled from window.scrollY at render time, because scrolling is
handled outside the main thread: while the main thread is busy
rendering a large log chunk, window.scrollY may still report the old
(bottom) position, which would wrongly yank the page back down and
swallow the user's upward scroll.

AI-assisted implementation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Benoit
2026-07-02 19:59:20 +09:00
committed by Oliver Giles
parent b1ae75e259
commit ffe473b082

View File

@@ -669,6 +669,23 @@ const Run = templateId => {
latestNum: null,
logComplete: false,
};
// if the page is scrolled to the bottom, the scroll position "sticks"
// there and follows new log output as it is rendered, like tail -f.
// Any upward scroll disengages this, scrolling back to the bottom
// re-engages it. This must be tracked with event listeners rather than
// sampled when rendering, because scrolling is handled outside the main
// thread: while the main thread is busy rendering a large log chunk,
// window.scrollY may still report the old (bottom) position, which
// would wrongly yank the page back down and swallow the user's scroll.
let stickToBottom = true;
window.addEventListener('wheel', evt => {
if (evt.deltaY < 0)
stickToBottom = false;
});
window.addEventListener('scroll', () => {
stickToBottom = window.innerHeight + window.scrollY >=
document.documentElement.scrollHeight - 1;
});
const logFetcher = (vm, name, num) => {
const abort = new AbortController();
fetch('log/'+name+'/'+num, {signal:abort.signal}).then(res => {
@@ -705,6 +722,9 @@ const Run = templateId => {
state.logComplete = true;
}
if (stickToBottom)
window.scrollTo(0, document.documentElement.scrollHeight);
lastUiUpdate = Date.now();
tid = null;
}