Skip to content

fix(chat): coalesce rapid 'Load older history' clicks and stop re-sort churn - #44

Merged
marcelormendes merged 1 commit into
mainfrom
fix/load-older-coalesce-no-churn
Aug 15, 2026
Merged

fix(chat): coalesce rapid 'Load older history' clicks and stop re-sort churn#44
marcelormendes merged 1 commit into
mainfrom
fix/load-older-coalesce-no-churn

Conversation

@marcelormendes

Copy link
Copy Markdown
Owner

Summary

Fixes the UI flicker caused by a hammered "Load older history" button (loading an old conversation). Three sources of churn, all addressed:

  1. Trailing coalesce for loadOlder (ConversationChatPanel.tsx): clicks arriving while an older read is in flight, or during a short settle window right after it resolves, are folded into a single follow-up read. Residual queued clicks can no longer issue one read per click and a new read never fires immediately after a resolve. The coalesce window constant LOAD_OLDER_COALESCE_MS (300ms) is exported for tests.

  2. No re-sort churn in the store (conversation-model.ts): replacePage now merges an incoming older/newer page into the existing sequence-sorted item list with a linear two-pointer insert instead of re-sorting the entire collection (O(N log N) localeCompare). Unchanged items keep their exact object identity so memoized turn rendering does not re-run when a page is prepended.

  3. Stable scroll observer (ConversationChatPanel.tsx): the follow-latest ResizeObserver is attached once per content element instead of being torn down and recreated on every items/pending identity change; the per-commit layout effect only schedules one requestAnimationFrame scroll per committed batch.

Validation

  • npm run typecheck — green.
  • npm run lint (biome check .) — green.
  • npm test — 587 tests pass (64 files), including new coverage:
    • ConversationChatPanel.test.tsx: trailing-burst coalescing (30 synchronous clicks → one in-flight + one coalesced follow-up read, never 30); existing in-flight dedup test still green.
    • conversation-model.test.ts: prepending older history preserves unchanged item object identity and relative order.
    • ConversationTimeline.performance.test.tsx: historical Markdown is not re-rendered after an older-history prepend.
  • npm run site:build — green.
  • No lint regressions from biome.

Checklist

  • The change is focused and does not include unrelated work.
  • Tests cover new behavior or the PR explains why tests are unnecessary.
  • npm run verify passes locally.
  • No credentials, tokens, private data, or generated build artifacts are included.

A hammered 'Load older history' button produced UI flicker from three
sources, now addressed:

- Trailing coalesce: loadOlder now folds clicks arriving while an older
  read is in flight (or during a short settle window after it resolves)
  into a single follow-up read, so residual queued clicks can never issue
  one read per click and right after a resolve no new read fires.
  LOAD_OLDER_COALESCE_MS (300ms) is exported for the regression test.
- No re-sort churn: replacePage merges an incoming older/newer page into
  the existing sequence-sorted item list with a linear two-pointer insert
  instead of re-sorting the entire collection. Unchanged items keep their
  object identity so memoized turns do not re-render when a page arrives.
- Stable scroll observer: the follow-latest ResizeObserver is attached
  once per content element instead of being torn down and recreated on
  every items/pending identity change; the per-commit layout effect only
  schedules one requestAnimationFrame per committed batch.

Adds coverage: trailing-burst coalescing (one in-flight + one follow-up
read), identity preservation in conversation-model, and a
ConversationTimeline performance test asserting historical Markdown is
not re-rendered after an older-history prepend.
@marcelormendes
marcelormendes merged commit 0b8459e into main Aug 15, 2026
2 checks passed
@marcelormendes
marcelormendes deleted the fix/load-older-coalesce-no-churn branch August 15, 2026 03:34
@marcelormendes marcelormendes mentioned this pull request Aug 15, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant