mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-26 19:50:15 +00:00
fix: cap stream fade done drain
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- **PR #2450** by @Michaelyklam (fixes #2447) — Cap the optional streaming word-fade drain after the final `done` SSE event so very large or bursty completed responses are rendered from the canonical session promptly instead of keeping the chat in a live/working state until Stop is pressed.
|
||||
|
||||
## [v0.51.82] — 2026-05-17 — Release BF (stage-375 — 2-PR batch — table renderer pipe protection + Catppuccin appearance skin)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -697,6 +697,7 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
||||
const _STREAM_FADE_MAX_MS=350;
|
||||
const _STREAM_FADE_STAGGER_MS=16;
|
||||
const _STREAM_FADE_DONE_MAX_MS=320;
|
||||
const _STREAM_FADE_DONE_DRAIN_MAX_MS=900;
|
||||
const _streamFadeEnabledForStream=window._fadeTextEffect===true;
|
||||
|
||||
// rAF-throttled rendering: buffer tokens, render at most once per frame
|
||||
@@ -1086,6 +1087,8 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
||||
: _stripXmlToolCalls(assistantText.slice(segmentStart));
|
||||
}
|
||||
function _drainStreamFadeBeforeDone(onDone){
|
||||
const drainStartedAt=performance.now();
|
||||
let forcedDone=false;
|
||||
const step=()=>{
|
||||
if(!assistantBody){onDone();return;}
|
||||
const target=_streamFadeCurrentDisplayText();
|
||||
@@ -1101,6 +1104,15 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
||||
setTimeout(onDone, Math.min(remainingAnimationMs, _STREAM_FADE_DONE_MAX_MS));
|
||||
return;
|
||||
}
|
||||
// Final SSE `done` means the canonical completed session is available.
|
||||
// The optional word-fade playout must not keep that completed answer
|
||||
// hidden behind the live Thinking state for large/bursty responses.
|
||||
if(!forcedDone&&performance.now()-drainStartedAt>=_STREAM_FADE_DONE_DRAIN_MAX_MS){
|
||||
forcedDone=true;
|
||||
if(_smdParser) _smdEndParser();
|
||||
onDone();
|
||||
return;
|
||||
}
|
||||
setTimeout(()=>requestAnimationFrame(step), 33);
|
||||
};
|
||||
step();
|
||||
|
||||
@@ -82,6 +82,7 @@ const _STREAM_FADE_MS=200;
|
||||
const _STREAM_FADE_MAX_MS=350;
|
||||
const _STREAM_FADE_STAGGER_MS=16;
|
||||
const _STREAM_FADE_DONE_MAX_MS=320;
|
||||
const _STREAM_FADE_DONE_DRAIN_MAX_MS=900;
|
||||
const performance={performance_stub};
|
||||
{helpers}
|
||||
"""
|
||||
@@ -178,6 +179,20 @@ def test_stream_fade_uses_incremental_renderer_without_changing_default_path():
|
||||
assert "_wrapStreamingFadeWords" not in MESSAGES_JS
|
||||
|
||||
|
||||
def test_stream_fade_done_drain_has_hard_cap_for_large_buffered_responses():
|
||||
drain_block = function_block(MESSAGES_JS, "_drainStreamFadeBeforeDone")
|
||||
assert "const _STREAM_FADE_DONE_DRAIN_MAX_MS=900" in MESSAGES_JS
|
||||
assert_contains_all(
|
||||
drain_block,
|
||||
[
|
||||
"const drainStartedAt=performance.now();",
|
||||
"performance.now()-drainStartedAt>=_STREAM_FADE_DONE_DRAIN_MAX_MS",
|
||||
"if(_smdParser) _smdEndParser();",
|
||||
"onDone();",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def test_stream_fade_css_is_opacity_only_and_hides_live_cursor():
|
||||
fade_css = STYLE_CSS[STYLE_CSS.index("OpenWebUI-style streaming word fade") :]
|
||||
assert "filter:" not in STYLE_CSS[STYLE_CSS.index("OpenWebUI-style streaming word fade") :].split(
|
||||
|
||||
Reference in New Issue
Block a user