perf(agent-manager): optimize tab switching and context transition latency - #13156
Conversation
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (8 files)
Previous Review Summaries (2 snapshots, latest commit 14dcfba)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 14dcfba)Status: No Issues Found | Recommendation: Merge Files Reviewed (8 files)
Previous review (commit fae399b)Status: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
The persistent ChatView wrapper, batched selection/session actions, guarded draft writes, deferred history seeding, tail pre-hydration, and single-frame scroll restore all look correct — semantics of Files Reviewed (8 files)
Reviewed by kimi-k3 · Input: 43.2K · Output: 5.3K · Cached: 301.1K Review guidance: REVIEW.md from base branch |
fae399b to
14dcfba
Compare
14dcfba to
be8b85c
Compare
| // the messagesLoaded batch. The second frame restores scroll position | ||
| // without forcing a synchronous layout reflow mid-paint. | ||
| requestAnimationFrame(() => { | ||
| requestAnimationFrame(() => { |
There was a problem hiding this comment.
@marius-kilocode We found out that messages don’t auto-scroll anymore. The most likely reason for this is the removal of double-rAF. Should we investigate further and see if we can fix it without using double-rAF?
https://discord.com/channels/1349288496988160052/1349288496988160055/1539696538325098656
https://discord.com/channels/1349288496988160052/1391109167275577464/1539874684693254144
Problem
Switching between contexts (Local and Worktrees) or switching session tabs in Agent Manager suffered from noticeable latency and main-thread hitching:
<ChatView>was conditionally rendered under<Show when={!contextEmpty()}>. When transitioning between an empty context (such as Local with 0 open tabs) and a populated worktree, the entire<ChatView>component tree, its providers, Virtualizer,PromptInput, and DOM were destroyed and recreated, producing over 13,000 transient DOM nodes, 7,300 layout objects, and a ~190ms main-thread freeze.<KobalteTooltip.Portal>and<KobalteTooltip.Content>unconditionally. Kobalte'screatePresenceinvokedgetComputedStyle(element)to readanimationNameon every reactive tick, and each tooltip attached a dedicatedMutationObserver.getAnimationNamewas the top CPU hotspot in traces.PromptInputsynchronously scanned all user message parts on every tab switch to seed prompt history, whilesaveDraftexecuted Map writes even when drafts were empty or unchanged.MessageListdeferred scroll restoration across two nestedrequestAnimationFramecycles (introducing a 33-50ms visual delay), while session and selection actions dispatched multiple unbatched reactive signal updates.Solution
<Show when={!contextEmpty()}>around<ChatView>with a persistent container toggled via.am-chat-wrapper-hidden(display: none;). TheChatViewinstance and virtualizer stay warm in memory across context switches.<div>trigger when idle and closed, lazily mountingKobalteTooltiponly on pointer enter / focus in or whenforceOpenis set. Guarded the portal with<Show when={local.forceOpen || state.open}>to completely bypasscreatePresencestyle calculations when tooltips are closed, and madeMutationObserverregistration conditional.history.seedto an idle timeout and guardedsaveDraftagainst unnecessary Map allocations for empty/unmodified drafts.batch(() => ...)to coalesce reactive updates. Pre-hydrated the visible tail messages (last 15 messages) directly intostore.partson load, and reduced scroll restoration inMessageListto a single frame.Performance Measurements
getAnimationNameSelf-TimegetBoundingClientRectSelf-Time