ui: Scroll stabilization

frdel committed Jan 31, 2026 at 12:23 UTC 7cd17b6f0cb9edd156f2e2138bfede71173ac2fb
1 file changed +45
webui/js/scroller.js
+45
@@ -17,6 +17,51 @@ export class Scroller {
17 parseFloat(getComputedStyle(document.documentElement).fontSize);
18 this.wasAtBottom = this.isAtBottom();
19 this._scrollListener = null;
20 +
21 + this._resizeObserver = null;
22 + this._lastClientHeight = null;
23 +
24 + this._initScrollStabilization();
25 + }
26 +
27 + _initScrollStabilization() {
28 + if (!this.element) return;
29 +
30 + const hasStabilizationFlag =
31 + this.element?.dataset?.scrollStabilization != null;
32 + if (hasStabilizationFlag) return;
33 +
34 + if (typeof ResizeObserver === "undefined") return;
35 +
36 + this._lastClientHeight = this.element.clientHeight;
37 + this._resizeObserver = new ResizeObserver(() => {
38 + const prevHeight = this._lastClientHeight;
39 + const nextHeight = this.element.clientHeight;
40 +
41 + if (prevHeight == null) {
42 + this._lastClientHeight = nextHeight;
43 + return;
44 + }
45 +
46 + if (nextHeight === prevHeight) return;
47 +
48 + const delta = nextHeight - prevHeight;
49 + this._lastClientHeight = nextHeight;
50 +
51 + if (this.wasAtBottom) {
52 + this.scrollToBottom();
53 + return;
54 + }
55 +
56 + if (delta !== 0) {
57 + this._clearScrollingTo();
58 + this.element.scrollTop = Math.max(0, this.element.scrollTop - delta);
59 + }
60 + });
61 +
62 + this._resizeObserver.observe(this.element);
63 +
64 + this.element.dataset.scrollStabilization = "1";
65 }
66
67 _getEffectiveScrollTop() {