From 624b7ad66d09eff6dc1909292b0daec00d5608b7 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Fri, 7 Aug 2026 10:21:53 +0200 Subject: [PATCH] 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 --- gui/main.qml | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/gui/main.qml b/gui/main.qml index a0b2aec..8d62fcd 100644 --- a/gui/main.qml +++ b/gui/main.qml @@ -96,9 +96,12 @@ ApplicationWindow { // Deferred scroll timer — fires once after model changes settle Timer { id: scrollTimer - interval: 0 + interval: 16 // ~1 frame at 60fps, allows layout to settle repeat: false - onTriggered: chatView.positionViewAtEnd() + onTriggered: { + if (chatView.shouldAutoScroll) + chatView.positionViewAtEnd() + } } // Session ID to kill — set on right-click, consumed by context menu @@ -485,6 +488,7 @@ ApplicationWindow { target: ollie function onActiveSessionIdChanged() { chatModel.reset() + chatView.shouldAutoScroll = true // Reset scroll state on session switch if (ollie.activeSessionId && ollie.activeAgentId) { var log = ollie.readLogForSession(ollie.activeSessionId, ollie.activeAgentId) if (log) { @@ -496,6 +500,7 @@ ApplicationWindow { } function onActiveAgentIdChanged() { chatModel.reset() + chatView.shouldAutoScroll = true // Reset scroll state on agent switch if (ollie.activeSessionId && ollie.activeAgentId) { var log = ollie.readLogForSession(ollie.activeSessionId, ollie.activeAgentId) if (log) { @@ -1244,15 +1249,32 @@ ApplicationWindow { boundsBehavior: Flickable.StopAtBounds 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: { - if (count > 0) - scrollTimer.start() + // New block added - always scroll if we were following + if (count > 0) { + if (shouldAutoScroll) + scrollTimer.start() + } } Connections { target: chatModel function onDataChanged() { - if (chatView.atYEnd && chatView.count > 0) + // Content updated (streaming) - scroll if following + if (chatView.shouldAutoScroll && chatView.count > 0) scrollTimer.start() } }