fix(chat): coalesce rapid 'Load older history' clicks and stop re-sort churn - #44
Merged
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the UI flicker caused by a hammered "Load older history" button (loading an old conversation). Three sources of churn, all addressed:
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 constantLOAD_OLDER_COALESCE_MS(300ms) is exported for tests.No re-sort churn in the store (
conversation-model.ts):replacePagenow 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.Stable scroll observer (
ConversationChatPanel.tsx): the follow-latestResizeObserveris attached once per content element instead of being torn down and recreated on everyitems/pendingidentity change; the per-commit layout effect only schedules onerequestAnimationFramescroll 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.Checklist
npm run verifypasses locally.