Skip to content

会话面板切换终端:偶发 terminal not found,且会话滚动位置被重置到顶部 #23

Description

@crazyAJ

现象

  1. 从聊天框打开会话面板时,有概率提示 terminal not found(终端未找到);关闭面板再重新打开,终端又能正常出现。
  2. 打开/关闭终端面板时,会话消息区的滚动位置会被重置,滚动条被推到顶部。

复现步骤

  1. 在会话中打开会话面板(默认 tab 为终端);
  2. 偶发出现 terminal not found
  3. 关闭面板后重新打开,终端恢复;
  4. 另外:在已滚动到会话中间/底部时打开或关闭终端面板,消息区滚动位置会丢失并回到顶部附近。

问题一:terminal not found

调用链

  • 打开会话面板时默认 tab 是 terminal,于是同时发生两件事:
    1. ensureContext(contextKey, cwd, true) 异步执行 terminal.list
    2. TerminalPane 挂载后立刻对 store 里的 activeTerminalId 调用 terminal.reconnect
  • 后端 TerminalService.getContextSession()sessions 中找不到该 id 时抛出 ErrorCode.terminal.notFound

根因分析

  • 终端 detach 后进入宽限期,默认 graceMs = 45s,超时后 cleanupSession() 会从 sessions 中删除;
  • 后端/容器重启同样会清空所有 session;
  • 但前端 zustand store 里可能仍保留旧 terminalId(未收到 closed 事件或页面未刷新),此时 terminal.reconnect 命中的是不存在的会话,直接报 terminal not found
  • ensureContext() 的处理存在缺口:当服务器返回的终端列表为空、但本地仍有旧 id 时,只把旧 id 标记为 expired,不会自动创建新终端(其自动创建条件为 terminals.length === 0 && previousIds.length === 0);
  • 关闭再打开面板会重新挂载:此时本地 id 已被清空,条件成立,于是自动创建终端,表现为“关掉再开就好了”;
  • 附带竞态:ensureContext() 的 list 请求是异步的,而 TerminalPane 挂载即 reconnect,reconnect 可能先于 list 完成,从而先撞上失效 id。

相关位置

  • web/src/components/chat/session-panel.tsxensureContext() 调用与终端面板挂载
  • web/src/components/terminal/terminal-pane.tsx:挂载时 reconnectTerminal()
  • web/src/stores/terminal-store.tsensureContext() / reconnectTerminal() / markTerminalExpired()
  • src/terminal/terminal.service.tsgetContextSession()terminal.not_foundstartGraceTimer() 到期删除会话

修复方向

  • ensureContext():服务器列表为空但本地有旧 id 时,标记过期后仍按 autoCreate 创建新终端;
  • reconnectTerminal() 遇到 not_found 时自动刷新列表并创建新终端自愈,而不是停留在错误态;
  • SessionPanel 等待 ensureContext 完成(或 context hydrated)后再挂载 TerminalPane,消除并发竞态。

问题二:开关终端面板导致会话滚动被重置到顶部

根因分析

  • 桌面端在 web/src/routes/thread-view.tsx 中使用二选一结构渲染:
    • 面板打开:ResizablePanelGroup 内嵌 ChatTimeline
    • 面板关闭:直接渲染 ChatTimeline
  • 打开/关闭终端会在两个分支间切换,导致 ChatTimeline 被卸载并重新挂载;
  • TanStack Virtual 的滚动位置与测量状态保存在组件实例内部,卸载即丢失,重新挂载从 0 开始;
  • 重新挂载时虚拟列表先按估算高度渲染,初始 scrollToIndex(last) 在测量完成之前执行,最终位置不准确,表现为滚动条被推到顶部附近;
  • 移动端使用底部 Sheet,ChatTimeline 不卸载,因此不受影响。

修复方向

  • 保持 ChatTimeline 单实例:面板开关只改变容器布局/尺寸,不切换渲染分支;
  • 或在 store 中按 thread 记录并恢复滚动偏移,待测量完成后恢复;
  • 或为虚拟列表增加“测量完成后重新贴底/恢复偏移”的逻辑,并把面板开关纳入触发条件。

环境

  • Image: ghcr.io/limlll/codex-webui:latest(release codex-0.153.2-2
  • 会话面板默认终端 tab;桌面端可稳定观察到滚动位置重置问题

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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