Fix scrollback snapping back to bottom after scrolling up
Wheel-up now clears stickToBottom immediately on the raw scroll event, wheel-down re-checks the resting position, and the auto-scroll effect re-checks stickToBottom right before scrollToItem(0). The scroll-state effect could miss a wheel scroll that began and ended within one frame, so the next incoming line pulled the buffer back to the bottom. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JW57ZvxRmxcDF7AFACLSoV
This commit is contained in:
@@ -818,7 +818,7 @@ fun MessageScrollback(
|
|||||||
.first { item ->
|
.first { item ->
|
||||||
item == null || (item.index < capturedList.size && item.key == capturedList[item.index].seq)
|
item == null || (item.index < capturedList.size && item.key == capturedList[item.index].seq)
|
||||||
}
|
}
|
||||||
listState.scrollToItem(0)
|
if (stickToBottom) listState.scrollToItem(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -923,6 +923,31 @@ fun MessageScrollback(
|
|||||||
.height(snappedHeight)
|
.height(snappedHeight)
|
||||||
.onGloballyPositioned { containerCoords[0] = it }
|
.onGloballyPositioned { containerCoords[0] = it }
|
||||||
.pointerHoverIcon(PointerIcon.Text)
|
.pointerHoverIcon(PointerIcon.Text)
|
||||||
|
// Wheel-up means the user is reading backscroll: stop sticking to the bottom RIGHT NOW,
|
||||||
|
// on the raw wheel event. The scroll-state effect up top can miss a wheel scroll that starts
|
||||||
|
// and finishes inside one frame (isScrollInProgress is never seen as true), which left
|
||||||
|
// stickToBottom set and let the next incoming line yank the buffer back down.
|
||||||
|
// Wheel-down re-checks the resting position shortly after and re-enables sticking if the
|
||||||
|
// user is back at the bottom.
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
awaitPointerEventScope {
|
||||||
|
while (true) {
|
||||||
|
val event = awaitPointerEvent(PointerEventPass.Initial)
|
||||||
|
if (event.type == PointerEventType.Scroll) {
|
||||||
|
val dy = event.changes.firstOrNull()?.scrollDelta?.y ?: 0f
|
||||||
|
if (dy < 0f) {
|
||||||
|
stickToBottom = false
|
||||||
|
} else if (dy > 0f) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
delay(200)
|
||||||
|
stickToBottom = listState.firstVisibleItemIndex == 0 &&
|
||||||
|
listState.firstVisibleItemScrollOffset <= stickToBottomThresholdPx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Remembers the row under a right-click for the menu's "Copy line". Watches the
|
// Remembers the row under a right-click for the menu's "Copy line". Watches the
|
||||||
// Initial pass, because ContextMenuArea takes the right-press there, before
|
// Initial pass, because ContextMenuArea takes the right-press there, before
|
||||||
// the handler below would see it.
|
// the handler below would see it.
|
||||||
|
|||||||
Reference in New Issue
Block a user