mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-07-20 14:40:35 +00:00
release #5516: composer-grow re-pin — hot-path optimization + CHANGELOG
Addresses Greptile P2 nit: autoResize() now only calls the re-pin when the composer actually grew (offsetHeight before/after compare), skipping the DOM read on a no-height-change keystroke. The #composerWrap ResizeObserver remains the safety net for growth paths that bypass autoResize. Test tightened to assert the grow-guard. CHANGELOG entry added (self-built, release owns changelog).
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- **The transcript no longer scrolls up when the composer grows.** When you were pinned to the bottom of a conversation and the composer got taller — multi-line typing, `Shift+Enter`, or a multi-line / dictation paste — the transcript viewport shrank by the same amount and left you stranded a row or two above the bottom (it read as the chat "randomly scrolling up" during normal use). The transcript now re-pins to the bottom whenever the composer grows, but only when you were genuinely still pinned there — if you'd scrolled up to read history, nothing yanks you back down. (#5516, #5514, #5515)
|
||||
- **A deleted conversation no longer comes back as a read-only "ghost" after a restart.** Deleting a WebUI session removed its file but left a recoverable row in the agent's state database, so after a server restart and page reload the deleted conversation could reappear as a read-only "Agent" session still showing its old transcript. Deleted WebUI sessions are now recorded in a durable tombstone that the sidebar projection, startup backup-recovery, audit, and session-claim paths all honor, so a deleted session stays gone (the backend keeps returning the 404 the client relies on to clear the stale route). Genuine crash recovery is unaffected — a session whose sidecar is lost without a delete is still restored — imported external-channel (messaging) sessions are exempt and keep their transcript, and a deleted id is automatically un-tombstoned if the same session is later re-created or re-imported. Thanks @rodboev. (#5504, #5498)
|
||||
- **A drag that ends over the sidebar no longer switches conversations by accident.** Releasing the mouse (a stray `pointerup`) over a session row after a drag that began elsewhere in the main content area could switch you into that conversation unintentionally. Session switching now ignores a `pointerup` that isn't a genuine click on the row. Thanks @webtecnica. (#5487, #5462)
|
||||
- **Extension gallery downloads no longer hang on networks with broken IPv6.** Gallery installs and registry fetches followed the OS address order (IPv6-first); on a network advertising dead IPv6 routes, every install stalled for the full TCP timeout before falling back to IPv4. Downloads now try IPv4 first (via a non-global, thread-safe per-connection override that preserves the existing redirect allowlist and timeouts), so installs are fast again on such networks. Thanks @rumotoshino. (#5458)
|
||||
|
||||
+7
-2
@@ -6181,13 +6181,18 @@ function autoResize(){
|
||||
_composerAutoResizeRaf=0;
|
||||
}
|
||||
const el=$('msg');
|
||||
const _prevComposerH=el.offsetHeight;
|
||||
el.style.height='auto';
|
||||
el.style.height=Math.min(el.scrollHeight,200)+'px';
|
||||
updateSendBtn();
|
||||
// #5514/#5515: growing the composer shrinks the flex:1 transcript viewport, so
|
||||
// a reader pinned to the bottom would be stranded above it ("scrolls up 1 row
|
||||
// per composer row"). Re-pin the transcript when it's genuinely still pinned.
|
||||
if(typeof _repinMessagesAfterComposerResize==='function') _repinMessagesAfterComposerResize();
|
||||
// per composer row"). Re-pin the transcript when it's genuinely still pinned —
|
||||
// but only when the composer actually GREW (a shrink enlarges the viewport and
|
||||
// can't strand a reader), so the common no-height-change keystroke skips the
|
||||
// re-pin's DOM read entirely. The #composerWrap ResizeObserver is the safety
|
||||
// net for any growth path that doesn't route through here.
|
||||
if(el.offsetHeight>_prevComposerH && typeof _repinMessagesAfterComposerResize==='function') _repinMessagesAfterComposerResize();
|
||||
}
|
||||
function scheduleComposerAutoResize(){
|
||||
if(typeof requestAnimationFrame!=='function'){autoResize();return;}
|
||||
|
||||
@@ -65,8 +65,10 @@ def test_autoresize_calls_the_repin():
|
||||
body = MESSAGES_JS[start:end]
|
||||
# The height write must still be there...
|
||||
assert "el.style.height=Math.min(el.scrollHeight,200)+'px'" in body
|
||||
# ...and the re-pin must be called after it.
|
||||
# ...and the re-pin must be called after it, guarded so it only fires when the
|
||||
# composer actually grew (skips the DOM read on a no-height-change keystroke).
|
||||
assert "_repinMessagesAfterComposerResize()" in body
|
||||
assert "el.offsetHeight>_prevComposerH" in body
|
||||
|
||||
|
||||
def test_resize_observer_installed_on_composer():
|
||||
|
||||
Reference in New Issue
Block a user