diff --git a/public/app.js b/public/app.js index fccd169..a87b13d 100644 --- a/public/app.js +++ b/public/app.js @@ -4652,7 +4652,9 @@ const welcome = messagesDiv.querySelector('.welcome-msg'); if (welcome) welcome.remove(); messagesDiv.appendChild(createMsgElement('system', message, [], options)); - scrollToBottom(); + if (options.preserveScroll !== true) { + scrollToBottom(); + } } function appendError(message, options = {}) { @@ -4660,6 +4662,7 @@ tone: 'danger', transient: options.transient !== false, autoDismissMs: options.autoDismissMs || 7000, + preserveScroll: options.preserveScroll !== false, }); } @@ -4670,6 +4673,17 @@ }); } + function isNearBottom(threshold = 96) { + const distance = messagesDiv.scrollHeight - messagesDiv.clientHeight - messagesDiv.scrollTop; + return distance <= threshold; + } + + function scrollToBottomIfNear(threshold = 96) { + if (!isNearBottom(threshold)) return false; + scrollToBottom(); + return true; + } + // --- Custom Scrollbar --- const scrollbarEl = document.getElementById('custom-scrollbar'); const thumbEl = document.getElementById('custom-scrollbar-thumb'); @@ -5334,10 +5348,20 @@ } const messageId = createLocalId('user'); const element = createMsgElement('user', text, [], { messageId, codexAppSteerStatus: 'pending' }); - messagesDiv.appendChild(element); + const streamEl = document.getElementById('streaming-msg'); + const shouldFollow = isNearBottom(); + if (streamEl && streamEl.parentNode === messagesDiv) { + messagesDiv.insertBefore(element, streamEl); + } else { + messagesDiv.appendChild(element); + } registerUserMessage(messageId, element, text); updateUserOutlinePanel(); - scrollToBottom(); + if (shouldFollow) { + scrollToBottom(); + } else { + updateScrollbar(); + } send({ type: 'message', text, sessionId: currentSessionId, mode: currentMode, agent: currentAgent, clientMessageId: messageId }); msgInput.value = ''; autoResize();