mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-07-19 06:00:46 +00:00
fix(streaming): restore mobile scroll-jank guard during live renders
A later streaming parse-cache change dropped the #MOBILESCROLL guard from the live-stream render tick while leaving the helper and CSS in place. On iOS PWA, Safari can paint a transient scrollTop=0 frame during streamed DOM rebuilds, forcing the reader to the first/oldest message. Re-add _fixMobileScrollJank() before streaming DOM work begins and pin it with a regression test so the third layer of the mobile scroll fix cannot be silently dropped again.
This commit is contained in:
committed by
nesquena-hermes
parent
c70105cf35
commit
2a40ca4240
@@ -3844,6 +3844,9 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
||||
_renderPending=false;
|
||||
// Guard: a pending setTimeout+rAF can outlive stream finalization.
|
||||
if(_streamFinalized) return;
|
||||
// Mobile scroll-jank guard: enable overflow-anchor before DOM writes so
|
||||
// the browser preserves scroll position during streaming content growth.
|
||||
if(typeof window._fixMobileScrollJank==='function') window._fixMobileScrollJank();
|
||||
_lastRenderMs=performance.now();
|
||||
const parsed=_cachedParsed&&_cachedParsedText===assistantText&&_cachedParsedReasoning===liveReasoningText ? _cachedParsed : _parseStreamState();
|
||||
_cachedParsed=null;
|
||||
|
||||
@@ -4,6 +4,7 @@ from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).parent.parent
|
||||
UI_JS = (REPO / "static" / "ui.js").read_text(encoding="utf-8")
|
||||
MESSAGES_JS = (REPO / "static" / "messages.js").read_text(encoding="utf-8")
|
||||
STYLE_CSS = (REPO / "static" / "style.css").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
@@ -43,6 +44,28 @@ def test_messages_scroller_uses_overflow_anchor_auto_on_mobile():
|
||||
)
|
||||
|
||||
|
||||
def test_streaming_render_enables_mobile_scroll_jank_guard_before_dom_writes():
|
||||
"""Streaming must re-enable mobile scroll anchoring before every DOM write.
|
||||
|
||||
Regression: #MOBILESCROLL originally added _fixMobileScrollJank() in the
|
||||
live-stream render tick, but a later streaming parse-cache change dropped
|
||||
that call while leaving the helper/CSS in place. On iOS PWA this lets Safari
|
||||
paint a transient scrollTop=0 frame during streamed DOM rebuilds.
|
||||
"""
|
||||
guard_idx = MESSAGES_JS.find("window._fixMobileScrollJank")
|
||||
assert guard_idx != -1, (
|
||||
"attachLiveStream() must call window._fixMobileScrollJank() during the "
|
||||
"streaming render tick, before DOM writes, so iOS PWA does not jump to "
|
||||
"the first/oldest message while assistant output streams."
|
||||
)
|
||||
|
||||
render_idx = MESSAGES_JS.find("_lastRenderMs=performance.now()")
|
||||
assert render_idx != -1, "streaming render timestamp not found"
|
||||
assert guard_idx < render_idx, (
|
||||
"The mobile scroll-jank guard must run before streaming DOM work begins."
|
||||
)
|
||||
|
||||
|
||||
def test_scroll_repin_dead_zone_is_wider_for_mac_app_windows():
|
||||
assert "clientHeight<250" in UI_JS or "bottomDistance<250" in UI_JS, (
|
||||
"The near-bottom re-pin threshold should be at least 250px so small "
|
||||
|
||||
Reference in New Issue
Block a user