Skip to content

[Bug]: 长会话中手动向上滚动查看历史时,偶发自动跳回底部 #1381

Description

@oceaman

Summary

在多轮对话的长会话中,使用鼠标向上滚动查看历史消息时,会话视图会突然自动刷新并跳回最底部(最新消息位置),导致之前滚动定位的历史内容丢失,需要重新手动翻找。

Area

Web UI / Flow Chat / VirtualMessageList

Environment

  • BitFun 版本:0.2.11
  • 操作系统:Windows 11 x64
  • 模型:DeepSeek V4-Pro

Reproduction

  1. 在一个已有较多轮次对话的会话中(10 轮以上效果更明显)
  2. 使用鼠标滚轮向上滚动,查看较早轮次的内容
  3. 持续向上翻阅一段时间(期间可能有新的 tool event 产生、token 统计更新、或 Agent 仍在后台执行任务)
  4. 实际结果: 会话视图突然刷新,滚动位置被重置到最底部(最新消息处),之前翻阅到的位置丢失
  5. 期望结果: 滚动位置保持稳定,不受后台数据更新影响;仅当用户主动滚动到底部或点击"回到底部"按钮时才回到最新消息

Observed Behavior Details

  • 问题并非每次必现,但在长会话中触发频率较高
  • 跳回底部时没有明显的加载动画或提示,表现为"闪回"
  • 左右箭头按钮的逐轮跳转可以正常工作(说明底层 turn 定位能力存在)
  • 跳回后需要重新手动向上翻找,体验极差

Likely Root Cause

推测与 VirtualMessageList 的 scroll position 管理有关,可能的触发路径:

1. Re-render 导致 virtual list state 丢失

// ModernFlowChatContainer.tsx 中,以下状态变化可能触发 VirtualMessageList 重新渲染:
// - dialogTurns 数组更新(新 turn 产生)
// - turnSummaries 更新(轮次摘要重新计算)
// - tool event 到达(工具卡片状态变化)
// - token usage 更新(用量统计刷新)

VirtualMessageList 使用虚拟滚动(react-virtuoso 或自研),当 items 数组引用变化时可能触发 scrollToIndex(0) 或重置 scrollTop。在用户手动滚动到历史位置时,新事件的到达会导致 items 重新计算,可能意外触发滚动重置。

2. Auto-scroll-to-bottom 缺少"用户正在查看历史"的判断

// 推测存在类似逻辑:
// 新消息到达 → 自动滚动到底部(这是正确的默认行为)
// 但缺少检查:用户当前是否正在手动浏览历史?
// 如果用户 scrollTop 距离底部超过一定阈值,不应强制滚动

3. 与 #1281 的共同根因

VirtualMessageList.pinTurnToTop()#1281 中暴露了"列表不完全就绪时返回 false"的问题。同一个组件可能在以下场景中同样不稳定:

  • items 重建时 scroll position 保持
  • 异步更新期间的 scroll 状态一致性
  • scrollToIndex / scrollTo 在 reconcile 期间的可靠性

Relevant Code

  • src/web-ui/src/flow_chat/components/modern/VirtualMessageList.tsx — 虚拟滚动核心,pinTurnToTop、scroll position 管理
  • src/web-ui/src/flow_chat/components/modern/ModernFlowChatContainer.tsxhandleJumpToTurn、状态管理和重渲染触发
  • src/web-ui/src/flow_chat/components/modern/FlowChatHeader.tsx — turn 导航入口
  • src/web-ui/src/flow_chat/components/modern/useFlowChatNavigation.ts — 导航事件流

Suggested Fix Direction

  1. 判断用户是否在浏览历史:在 VirtualMessageListModernFlowChatContainer 中追踪 isUserViewingHistory 状态——当 scrollTop 距离底部超过一定阈值(如 200px)时设为 true,仅当用户主动回到底部或点击"回到底部"按钮时才清除
  2. 新消息到达时:如果 isUserViewingHistory === true,显示一个浮动的"新消息 ↓"按钮,而不是强制滚动
  3. items 更新时保持 scroll position:在 VirtualMessageList 的 items 重建逻辑中,保存并恢复 scrollTop 或目标 turn 的锚点位置
  4. 增加回归测试:在 FlowChatHeader.test.tsx 或 container 测试中补充 scroll position 保持的场景

Relation

可能与 #1281(轮次列表导航)共享 VirtualMessageList 的 scroll 状态管理根因。PR #1294 修复了 turn list 的面板关闭和 header 状态问题,但底层 pinTurnToTop 的滚动执行仍不稳定。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions