From 40599bb7514bc2cc53f23df5fd86f2544808d2d8 Mon Sep 17 00:00:00 2001 From: zhayujie Date: Mon, 20 Apr 2026 21:43:21 +0800 Subject: [PATCH] fix(web): smart auto-scroll for chat #2775 --- agent/protocol/agent_stream.py | 4 ++-- channel/web/chat.html | 14 +++++++++++++- channel/web/static/js/console.js | 28 ++++++++++++++++++++++++---- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/agent/protocol/agent_stream.py b/agent/protocol/agent_stream.py index 1124135f..569fcd5a 100644 --- a/agent/protocol/agent_stream.py +++ b/agent/protocol/agent_stream.py @@ -191,8 +191,8 @@ class AgentStreamExecutor: # Log user message with model info thinking_enabled = self._is_thinking_enabled() - thinking_label = "💭 thinking" if thinking_enabled else "⚡ fast" - logger.info(f"🤖 {self.model.model} | {thinking_label} | 👤 {user_message}") + thinking_label = " | 💭 thinking" if thinking_enabled else "" + logger.info(f"🤖 {self.model.model}{thinking_label} | 👤 {user_message}") # Add user message (Claude format - use content blocks for consistency) self.messages.append({ diff --git a/channel/web/chat.html b/channel/web/chat.html index 0326482d..01e1d0e4 100644 --- a/channel/web/chat.html +++ b/channel/web/chat.html @@ -288,7 +288,7 @@ -
+
@@ -364,6 +364,18 @@
+ + +
diff --git a/channel/web/static/js/console.js b/channel/web/static/js/console.js index 099ef32a..60fb4245 100644 --- a/channel/web/static/js/console.js +++ b/channel/web/static/js/console.js @@ -462,6 +462,16 @@ const sendBtn = document.getElementById('send-btn'); const messagesDiv = document.getElementById('chat-messages'); const fileInput = document.getElementById('file-input'); +// Smart auto-scroll: pause when user scrolls up, resume when near bottom +let _autoScrollEnabled = true; +const _SCROLL_THRESHOLD = 80; // px from bottom to re-enable auto-scroll + +messagesDiv.addEventListener('scroll', () => { + const distFromBottom = messagesDiv.scrollHeight - messagesDiv.scrollTop - messagesDiv.clientHeight; + _autoScrollEnabled = distFromBottom <= _SCROLL_THRESHOLD; + _updateScrollToBottomBtn(); +}); + // Intercept internal navigation links in chat messages messagesDiv.addEventListener('click', (e) => { const copyBtn = e.target.closest('.copy-msg-btn'); @@ -1438,7 +1448,8 @@ function createBotMessageEl(content, timestamp, requestId, msg) { function addUserMessage(content, timestamp, attachments) { const el = createUserMessageEl(content, timestamp, attachments); messagesDiv.appendChild(el); - scrollChatToBottom(); + _autoScrollEnabled = true; + scrollChatToBottom(true); } function addBotMessage(content, timestamp, requestId) { @@ -1531,7 +1542,7 @@ function loadHistory(page) { if (isFirstLoad) { // Use requestAnimationFrame to ensure the DOM has fully rendered // before scrolling, otherwise scrollHeight may not reflect new content. - requestAnimationFrame(() => scrollChatToBottom()); + requestAnimationFrame(() => scrollChatToBottom(true)); } else { // Restore scroll position so loading older messages doesn't jump the view messagesDiv.scrollTop = messagesDiv.scrollHeight - prevScrollHeight; @@ -2049,8 +2060,17 @@ function formatToolArgs(args) { } } -function scrollChatToBottom() { - messagesDiv.scrollTop = messagesDiv.scrollHeight; +function scrollChatToBottom(force) { + if (force || _autoScrollEnabled) { + messagesDiv.scrollTop = messagesDiv.scrollHeight; + } +} + +function _updateScrollToBottomBtn() { + const btn = document.getElementById('scroll-to-bottom-btn'); + if (!btn) return; + const distFromBottom = messagesDiv.scrollHeight - messagesDiv.scrollTop - messagesDiv.clientHeight; + btn.classList.toggle('hidden', distFromBottom <= _SCROLL_THRESHOLD); } function applyHighlighting(container) {