Merge pull request #4733 from nesquena/stage-4730-release

Release v0.51.592 — Release UY (gateway-poll skip + smd re-init, #4730)
This commit is contained in:
nesquena-hermes 2026-06-22 16:46:18 -07:00 committed by GitHub
commit 9430bda43d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 35 additions and 1 deletions

View file

@ -3,6 +3,12 @@
## [Unreleased]
## [v0.51.592] — 2026-06-22 — Release UY (skip gateway poll when hidden/streaming + smd re-init)
### Fixed
- **The sidebar's gateway-poll fallback no longer burns CPU re-rendering while you're streaming or the tab is hidden, and long streamed responses render more efficiently.** The 5s gateway-poll fallback now skips its `renderSessionList` pass when the tab is hidden or a turn is actively streaming (it catches up immediately on tab refocus via a one-time `visibilitychange` listener, so no gateway updates are dropped). Separately, when the streaming markdown parser was torn down by a prior segment but `window.smd` is still available, the live renderer now recreates the parser on the cleared element instead of falling back to repeated full-`innerHTML` rebuilds — avoiding O(n²) DOM churn on long responses. Thanks @akrhin. (#4730)
## [v0.51.591] — 2026-06-22 — Release UX (stop transcript jumping to first message on completion)
### Fixed

View file

@ -3352,6 +3352,18 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
: _stripXmlToolCalls(assistantText.slice(segmentStart));
if(_smdParser){
_smdWrite(displayText);
} else if(window.smd){
// Parser was nulled out (e.g. by a prior segment end) but smd is
// available — recreate it on the existing element. Uses the non-fade
// renderer to match standard rendering, avoiding O(n²) innerHTML
// churn on long responses (#4704). Clear any content the renderMd()
// fallback already wrote first: _smdNewParser resets _smdWrittenText to
// '' but does NOT clear the element, so a following _smdWrite(displayText)
// would append the full accumulated segment ON TOP of the existing
// fallback render and duplicate the live text.
assistantBody.innerHTML='';
_smdNewParser(assistantBody, false);
if(_smdParser) _smdWrite(displayText);
} else if(renderMd){
assistantBody.innerHTML=renderMd(displayText);
} else {

View file

@ -4101,7 +4101,23 @@ if(typeof window!=='undefined') window.refreshSessionList = refreshSessionList;
function startGatewayPollFallback(ms){
const intervalMs = Math.max(5000, Number(ms) || _gatewayFallbackPollMs);
if(_gatewayPollTimer) clearInterval(_gatewayPollTimer);
_gatewayPollTimer = setInterval(() => { renderSessionList({deferWhileInteracting:true}); }, intervalMs);
_gatewayPollTimer = setInterval(() => {
// Skip poll when tab is hidden or a stream is active — saves CPU
// and avoids redundant DOM renders during active streaming (#4704).
if(typeof document !== 'undefined' && document.hidden) return;
if(typeof S !== 'undefined' && (S.busy || S.activeStreamId)) return;
renderSessionList({deferWhileInteracting:true});
}, intervalMs);
// Visibility catch-up: refresh immediately when tab re-gains focus,
// so no gateway updates are dropped during hidden-skip periods.
if(typeof document !== 'undefined' && !document._hermesGatewayPollVisibilityHook){
document.addEventListener('visibilitychange', () => {
if(!document.hidden && typeof renderSessionList === 'function'){
void renderSessionList({deferWhileInteracting:false});
}
});
document._hermesGatewayPollVisibilityHook = true;
}
}
function stopGatewayPollFallback(){