fix(aiui): stop fighting the virtualizer over scrollTop
"Failed to scroll to index N after 10 attempts" appeared in the console on every send. It was not a real failure — it was two scroll controllers arguing. scrollToBottom() called virtualizer.scrollToIndex(last) AND then assigned el.scrollTop on the next tick. scrollToIndex runs a retry loop that nudges scrollTop toward the target row's measured offset and re-checks, up to ten times, because dynamically-measured rows move the target as they settle. The manual assignment overwrote each nudge, so the loop never observed itself converge and always exhausted its attempts. For "go to the end" the index-settling machinery buys nothing: scrollHeight already is the bottom, the virtualizer renders whatever window that offset implies, and it keeps working while a response streams and the last row grows — the case the manual fallback was added for in the first place. scrollToMessageIndex still uses scrollToIndex, which is the right tool for jumping to an arbitrary row. Console-only change; needs a device check that the chat still pins to the bottom while streaming. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3ac59a73b3
commit
7567c25c00
@@ -466,15 +466,26 @@ function handlePromptSelect(_userMsg: Message, assistantMsg: Message | null) {
|
||||
}
|
||||
|
||||
function scrollToBottom() {
|
||||
// Container scroll ONLY — deliberately not virtualizer.scrollToIndex.
|
||||
//
|
||||
// Doing both is what produced "Failed to scroll to index N after 10
|
||||
// attempts" in the console on every send. scrollToIndex runs a retry loop
|
||||
// that nudges scrollTop toward the measured offset of the target row and
|
||||
// re-checks, up to 10 times, because dynamically-measured rows move the
|
||||
// target as they settle. Assigning scrollTop ourselves on the next tick
|
||||
// overwrote each of those nudges, so the loop never saw itself converge and
|
||||
// always exhausted its attempts — the warning was the two of us fighting
|
||||
// over the same scrollTop, not a real failure.
|
||||
//
|
||||
// For "go to the end" the virtualizer's index-settling machinery buys
|
||||
// nothing: scrollHeight is already the bottom, and the virtualizer renders
|
||||
// whatever window that offset implies. It also keeps working while a
|
||||
// response streams and the last row keeps growing, which is exactly the
|
||||
// case the old fallback was added for. scrollToMessageIndex still uses
|
||||
// scrollToIndex, which is the right tool for jumping to an arbitrary row.
|
||||
nextTick(() => {
|
||||
if (messages.value.length > 0) {
|
||||
virtualizer.value.scrollToIndex(messages.value.length - 1, { align: 'end' })
|
||||
}
|
||||
// Fallback: also scroll the container directly for streaming updates
|
||||
nextTick(() => {
|
||||
const el = messageListRef.value
|
||||
if (el) el.scrollTop = el.scrollHeight
|
||||
})
|
||||
const el = messageListRef.value
|
||||
if (el) el.scrollTop = el.scrollHeight
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user