gui: smart auto-scroll follows streaming content
- Add shouldAutoScroll property to track user intent - Auto-scroll when near bottom (within 50px threshold) - Disable auto-scroll when user scrolls up to read history - Re-enable when user scrolls back to bottom - Reset to auto-scroll on session/agent switch - Increase timer interval to 16ms for smoother layout settling
This commit is contained in:
parent
1ca5563fdb
commit
624b7ad66d
32
gui/main.qml
32
gui/main.qml
|
|
@ -96,9 +96,12 @@ ApplicationWindow {
|
||||||
// Deferred scroll timer — fires once after model changes settle
|
// Deferred scroll timer — fires once after model changes settle
|
||||||
Timer {
|
Timer {
|
||||||
id: scrollTimer
|
id: scrollTimer
|
||||||
interval: 0
|
interval: 16 // ~1 frame at 60fps, allows layout to settle
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: chatView.positionViewAtEnd()
|
onTriggered: {
|
||||||
|
if (chatView.shouldAutoScroll)
|
||||||
|
chatView.positionViewAtEnd()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session ID to kill — set on right-click, consumed by context menu
|
// Session ID to kill — set on right-click, consumed by context menu
|
||||||
|
|
@ -485,6 +488,7 @@ ApplicationWindow {
|
||||||
target: ollie
|
target: ollie
|
||||||
function onActiveSessionIdChanged() {
|
function onActiveSessionIdChanged() {
|
||||||
chatModel.reset()
|
chatModel.reset()
|
||||||
|
chatView.shouldAutoScroll = true // Reset scroll state on session switch
|
||||||
if (ollie.activeSessionId && ollie.activeAgentId) {
|
if (ollie.activeSessionId && ollie.activeAgentId) {
|
||||||
var log = ollie.readLogForSession(ollie.activeSessionId, ollie.activeAgentId)
|
var log = ollie.readLogForSession(ollie.activeSessionId, ollie.activeAgentId)
|
||||||
if (log) {
|
if (log) {
|
||||||
|
|
@ -496,6 +500,7 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
function onActiveAgentIdChanged() {
|
function onActiveAgentIdChanged() {
|
||||||
chatModel.reset()
|
chatModel.reset()
|
||||||
|
chatView.shouldAutoScroll = true // Reset scroll state on agent switch
|
||||||
if (ollie.activeSessionId && ollie.activeAgentId) {
|
if (ollie.activeSessionId && ollie.activeAgentId) {
|
||||||
var log = ollie.readLogForSession(ollie.activeSessionId, ollie.activeAgentId)
|
var log = ollie.readLogForSession(ollie.activeSessionId, ollie.activeAgentId)
|
||||||
if (log) {
|
if (log) {
|
||||||
|
|
@ -1244,15 +1249,32 @@ ApplicationWindow {
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
visible: ollie.daemonConnected && ollie.activeAgentId !== ""
|
visible: ollie.daemonConnected && ollie.activeAgentId !== ""
|
||||||
|
|
||||||
|
// Auto-scroll when near bottom (within ~50px or at end)
|
||||||
|
// Reset to true on new blocks, user can scroll up to disable
|
||||||
|
property bool shouldAutoScroll: true
|
||||||
|
readonly property bool nearBottom: contentHeight - contentY - height < 50
|
||||||
|
|
||||||
|
onContentYChanged: {
|
||||||
|
// User scrolled up - disable auto-scroll
|
||||||
|
// User scrolled to bottom - re-enable
|
||||||
|
if (dragging || flicking) {
|
||||||
|
shouldAutoScroll = nearBottom || atYEnd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onCountChanged: {
|
onCountChanged: {
|
||||||
if (count > 0)
|
// New block added - always scroll if we were following
|
||||||
scrollTimer.start()
|
if (count > 0) {
|
||||||
|
if (shouldAutoScroll)
|
||||||
|
scrollTimer.start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: chatModel
|
target: chatModel
|
||||||
function onDataChanged() {
|
function onDataChanged() {
|
||||||
if (chatView.atYEnd && chatView.count > 0)
|
// Content updated (streaming) - scroll if following
|
||||||
|
if (chatView.shouldAutoScroll && chatView.count > 0)
|
||||||
scrollTimer.start()
|
scrollTimer.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Reference in New Issue