Skip to content

fix(components): reveal conversation after measured end restore - #803

Open
zxch3n wants to merge 3 commits into
mainfrom
fix/conversation-stuck-hidden-initial-reveal
Open

zxch3n wants to merge 3 commits into
mainfrom
fix/conversation-stuck-hidden-initial-reveal

Conversation

@zxch3n

@zxch3n zxch3n commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Problem / pressure

Opening a Chat Session could leave the conversation pane permanently black while the message DOM was fully present. The first-open viewport stays visibility: hidden until initialScrollRestored. That flag never flipped when the ready check treated use-stick-to-bottom's ~70px near-bottom lock as "flush with the last row", and compared descendant getBoundingClientRect against a scroller that is itself visibility: hidden with contain: strict. The group-toggle follow-lock fix then stopped observers from correcting to the real bottom until a commit snapshot said the reader was already following — with no settle timer allowed, the pane stayed hidden.

Summary

Keep the visibility gate. Ready now uses restore intent and viewport distance from the DOM bottom, not the library lock or last-row boxes. Until reveal, an end restore may keep writing the real bottom; a reader who already followed and escaped is not pulled back. After reveal, observers still cannot re-arm follow.

Visual explanation

sequenceDiagram
  participant View as SessionChatStreamView
  participant Hook as useStickyScroll
  participant Ready as isInitialScrollLayoutReady
  View->>View: visibility hidden until initialScrollRestored
  Hook->>Hook: apply cached end or offset
  alt end restore and lock never held
    Hook->>Hook: keep scrollToRealBottom from observers
  else reader already followed then escaped
    Hook->>Hook: do not pull to end
  end
  Hook->>Ready: following = restore intent is end
  Ready-->>Hook: destination row measured and distanceFromBottom <= 2
  Hook-->>View: initialScrollRestored
  View->>View: visibility visible
Loading

Before / after

Before After
Measured conversation stays visibility: hidden when the 2px last-row flush fails or observers stopScroll() during first end-restore Destination row measured and viewport at the DOM bottom (or cached offset) reveals; group-toggle follow-lock still holds after reveal

Test plan

  • pnpm --filter @lody/components exec vitest run tests/use-sticky-scroll.test.ts tests/sticky-scroll-virtua.test.tsx — 32 passed, including last-row box 50px off still reveals at DOM bottom, near-bottom offset restore without a 2px flush, unmeasured destination stays hidden, and escaped readers are not re-armed on collapse
  • Device-scale cold-open flash (e2e/scripts/capture-conversation-open-flicker.mjs) was not re-measured

Related issue

Same-repository maintainer branch; no intake Issue.

Keep the first-open visibility gate, but stop treating the sticky
library's near-bottom lock as flush-to-bottom readiness. End restore
may keep correcting to the real DOM bottom until reveal; after reveal,
observers still cannot re-arm follow.

Model: grok-4.6
Skip geometry-observer stopScroll only for the first end restore.
An unrestored offset restore must still release a near-bottom relock
so a content shrink cannot pull the cached reading position to the end.

Model: grok-4.6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant