feat: 智能滚动 - 流式回复时可向上查看历史,浮动按钮回到底部

- 流式输出时不再强制锁定底部,用户可自由滚动查看历史
- 滚动离开底部时显示「回到底部」浮动按钮(sticky 定位)
- 点击按钮平滑滚动并恢复自动跟踪
- 用户手动滚回底部时自动恢复跟踪
- 发送消息/新建会话时自动重置为跟踪状态
- 首次渲染(加载历史)强制滚到底部
This commit is contained in:
Metona
2026-04-04 21:41:26 +08:00
parent d61764a5e9
commit 12843f09a5
5 changed files with 105 additions and 4 deletions
+43 -2
View File
@@ -39,10 +39,38 @@ function getFileIcon(filename) {
return map[ext] || '📄';
}
let chatAreaEl, messagesContainerEl, emptyStateEl, scrollBtnEl;
let autoScroll = true; // 是否自动滚动到底部
export function initChatArea() {
chatAreaEl = document.querySelector('#chatArea');
messagesContainerEl = document.querySelector('#messagesContainer');
emptyStateEl = document.querySelector('#emptyState');
scrollBtnEl = document.querySelector('#scrollToBottom');
// 监听用户手动滚动
chatAreaEl.addEventListener('scroll', () => {
const atBottom = isAtBottom();
if (atBottom) {
autoScroll = true;
scrollBtnEl.style.display = 'none';
} else {
autoScroll = false;
scrollBtnEl.style.display = '';
}
});
// 点击滚动到底部按钮
scrollBtnEl.addEventListener('click', () => {
autoScroll = true;
scrollBtnEl.style.display = 'none';
chatAreaEl.scrollTo({ top: chatAreaEl.scrollHeight, behavior: 'smooth' });
});
}
/** 判断是否在底部附近(60px 容差) */
function isAtBottom() {
return chatAreaEl.scrollHeight - chatAreaEl.scrollTop - chatAreaEl.clientHeight < 60;
}
/** 安全地将 Markdown 转为 HTML */
@@ -58,8 +86,9 @@ export function safeMarkdown(text) {
}
}
/** 滚动到底部 */
/** 滚动到底部(仅当 autoScroll 开启时) */
export function scrollToBottom() {
if (!autoScroll) return;
requestAnimationFrame(() => {
chatAreaEl.scrollTop = chatAreaEl.scrollHeight;
});
@@ -80,11 +109,17 @@ export function renderMessages() {
messagesContainerEl.style.display = '';
const existingCount = messagesContainerEl.children.length;
const isNewMessage = existingCount > 0 && existingCount < msgs.length;
for (let i = existingCount; i < msgs.length; i++) {
appendMessageDOM(msgs[i], i);
}
scrollToBottom();
// 新消息按 autoScroll 决定;首次渲染(加载历史)强制滚到底部
if (isNewMessage) {
scrollToBottom();
} else if (msgs.length > 0) {
chatAreaEl.scrollTop = chatAreaEl.scrollHeight;
}
}
/** 追加单条消息 DOM */
@@ -274,6 +309,12 @@ export function clearMessages() {
messagesContainerEl.innerHTML = '';
}
/** 重置自动滚动(用户发送消息、新建会话时调用) */
export function enableAutoScroll() {
autoScroll = true;
if (scrollBtnEl) scrollBtnEl.style.display = 'none';
}
// ── 导出功能 ──
/** 导出为 Markdown */