Files
hermes-agent/apps
David Metcalfe c06caad8f7 fix(desktop): preserve per-session chat scroll position
Rework per hermes-sweeper review (keep_open, salvageability=medium):

- Capture moves out of the render body into the session-switch layout-effect
  cleanup, so only committed switches persist state (no uncommitted render
  can write localStorage).
- Live state is fed by both scroll events and a ResizeObserver on the
  content element, so distance-from-bottom stays fresh under async
  relayout that changes height without a scroll event (the staleness gap
  #70478's own review threads flagged).
- State is distance-from-bottom (or sticky-bottom), not raw scrollTop:
  the render-budget backfill prepends older turns and main anchors by
  distance-from-bottom, so an absolute offset no longer identifies the
  same reading location after the height change.
- Restore integrates with main's hasGroups/settle/anchor lifecycle: the
  settle loop re-applies the remembered target, defers on a clamped offset
  (content still arriving), hands back locked only for sticky-bottom, and
  leaves mid-read sessions escaped at their offset.
- anchorBeforePrepend no longer records 0 mid-load, which would clobber a
  restored offset once the backfill lands; the settle loop owns the target
  every frame until settled.
- Storage is scoped per profile with the session.ts .profile.<encoded>
  key pattern (no cross-profile bleed, #67709 pattern) and LRU-capped at
  120 sessions per profile.
- Regression tests: state classification, target math, profile isolation,
  LRU eviction, corrupt/invalid payload handling.

Related to #45562 (partial; no automatic closure)

(cherry picked from commit 1d79bef93f63d7b190b471bf652b5109b4288a8e)
2026-09-07 06:14:35 -07:00
..