-
Notifications
You must be signed in to change notification settings - Fork 88
perf(chat): add MessageCellHeightCache to eliminate recursive sizeThatFits on completed cells (LUM-720) #23612
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -184,6 +184,18 @@ struct MessageListContentView: View, Equatable { | |
| let cellActivePendingRequestId: String? = | ||
| (message.confirmation != nil || !message.toolCalls.isEmpty) | ||
| ? state.activePendingRequestId : nil | ||
| // A cell is height-cacheable when its rendered size is stable: | ||
| // not streaming, no pending confirmation, all tool calls done, | ||
| // not the latest assistant cell (may still grow), not highlighted. | ||
| // Exempt cells always re-measure; cached cells get a definite | ||
| // frame(height:) so LazyVStack.sizeThatFits returns immediately | ||
| // without recursing into the cell subtree. | ||
| let isCellHeightCacheable = !message.isStreaming | ||
| && message.confirmation?.state != .pending | ||
| && message.toolCalls.allSatisfy { $0.isComplete } | ||
| && !isLatestAssistant | ||
| && highlightedMessageId != message.id | ||
| let cachedHeight = isCellHeightCacheable ? scrollState.cellHeightCache[message.id] : nil | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Using Useful? React with 👍 / 👎. |
||
| MessageCellView( | ||
| message: message, | ||
| showTimestamp: state.showTimestamp.contains(message.id), | ||
|
|
@@ -226,6 +238,20 @@ struct MessageListContentView: View, Equatable { | |
| providerCatalogHash: providerCatalogHash | ||
| ) | ||
| .equatable() | ||
| // Measure natural content height BEFORE applying the cached frame | ||
| // so the cache is always updated from unconstrained layout, not | ||
| // from the pinned frame. This makes the cache self-correcting: | ||
| // if anything changes cell height (window resize, showTimestamp | ||
| // toggle, dismissedDocumentSurfaceIds) the next layout pass | ||
| // writes the correct height and the pinned frame updates on the | ||
| // following render. | ||
| .onGeometryChange(for: CGFloat.self) { proxy in | ||
| proxy.size.height | ||
| } action: { height in | ||
| guard isCellHeightCacheable, height > 0 else { return } | ||
| scrollState.cellHeightCache[message.id] = height | ||
| } | ||
| .frame(height: cachedHeight) | ||
|
Comment on lines
+248
to
+254
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Stale cached height persists after cell content changes because @ObservationIgnored cache write cannot trigger re-render When a completed cell's height-affecting content changes (e.g., The comment on lines 241-247 claims the cache is "self-correcting" and "the pinned frame updates on the following render" — but nothing schedules that "following render." In a static (non-streaming) conversation, a user toggling a timestamp on an older message sees the Trigger scenario
Prompt for agentsWas this helpful? React with 👍 or 👎 to provide feedback. |
||
| } | ||
|
|
||
| ForEach(state.orphanSubagents) { subagent in | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.