Skip to content

[功能建议] 服务器模式支持窗口级独立会话标签,避免 opentabs 强制同步 #547

Description

@keh4l

背景与问题

在 Codeg 服务器模式下,同时用多个浏览器窗口连接同一个实例时,希望窗口 A、窗口 B 分别处理不同任务。

当前任一窗口打开、选择或关闭一个已持久化会话标签后,其他窗口也会同步打开、切换焦点或关闭对应标签。结果是多个窗口无法维持各自独立的工作上下文,难以并行处理多个任务。

复现步骤

  1. 以服务器模式部署 Codeg。
  2. 用两个浏览器窗口打开同一个 Codeg 实例,记为窗口 A 和窗口 B。
  3. 在窗口 A 打开一个已有会话,或新建会话并使其完成持久化。
  4. 观察窗口 B:对应会话标签也被加入,并可能同步成为活动标签。
  5. 在窗口 A 关闭该会话标签。
  6. 观察窗口 B:对应标签也被移除。

实际行为

所有客户端共享一套“已打开的持久会话标签 + 当前活动标签”状态:

  • 一个窗口打开标签,其他窗口也打开;
  • 一个窗口切换当前会话,其他窗口的活动标签也跟随;
  • 一个窗口关闭标签,其他窗口也关闭;
  • 刷新或 WebSocket 重连后,客户端重新读取同一套全局标签快照并继续收敛。

源码核对结论

以下结论基于最新 upstream/main31386f4d

这不是偶发的 WebSocket 串线,而是 #251 / f9e531b1 引入的既定全局同步语义,目前缺少窗口级作用域或关闭同步的选项:

  1. 数据库只有一套全局 opened tabs

  2. 并发控制使用一个 workspace-global 版本号

    • opened_tabs_version是整个工作区共用的逻辑时钟。
    • 保存采用全量替换和全局 CAS;一个客户端写入后,其他客户端的旧版本写入会被拒绝并改用服务器快照。
  3. 后端主动要求所有客户端收敛

    • emit_tabs_changed广播完整标签快照。
    • 事件定义明确说明事件会到达 Tauri webview 和每个 WebSocket 客户端,is_active 也跨客户端同步。
  4. 前端明确合并远端标签并镜像焦点

    • TabProvider在挂载时读取全局快照,并订阅 tabs://changed;重连后再次读取。
    • 远端快照合并会采用其他客户端打开的标签,并移除其他客户端已经关闭的标签。
    • 焦点解析明确写着 “focus is mirrored across clients”。
    • TAB_ORIGIN 只用于忽略自己的广播回声,不是窗口隔离标识,而且每次加载都会重新生成。
  5. 测试把当前行为固定为预期

    • 跨客户端测试明确覆盖远端打开、远端关闭、远端焦点镜像,以及本地焦点持久化同步。

补充边界:split-group 布局没有通过 tabs://changed 实时广播;它目前写在同源共享的单个 localStorage key 中。因此它不是本 Issue 实时标签联动的直接根因,但多个同源窗口可能在保存/刷新恢复时覆盖同一个布局快照,后续设计窗口作用域时也应明确其归属。

建议:设置页增加独立开关

建议在设置页新增一个独立选项,例如:

同步打开的会话标签和焦点

在桌面端与浏览器客户端之间同步已打开的会话标签、顺序、置顶状态和当前活动标签。

建议语义:

  • 开启:保持当前行为,所有客户端实时同步 opened tabs 和 focus,适合跨设备接续工作。
  • 关闭:每个浏览器窗口/客户端维护独立的标签和焦点,适合多窗口并行任务。
  • 可考虑服务器部署默认关闭,或至少在说明中明确开启该项会使所有窗口镜像同一工作状态。
  • 该设置应只控制 UI 工作状态,不应关闭会话数据、消息、运行状态和真实删除事件的同步。

如果要支持刷新后仍保持窗口独立,仅有布尔开关还不够;关闭同步时仍需为窗口或工作台提供稳定的 scope,避免挂载、重连或 CAS 冲突时重新读取全局快照。

期望语义

应继续全局同步

  • 会话列表、消息和运行状态;
  • 新建后已经持久化的会话记录;
  • 会话真实删除、文件夹删除;
  • 其他必须保持数据一致性的后端事件。

关闭开关后应窗口级独立

  • 当前打开的会话标签集合;
  • 当前活动标签;
  • 标签顺序、置顶和普通的“关闭标签”操作;
  • 与该窗口关联的分屏/布局恢复状态。

窗口 A 新建的持久会话可以出现在窗口 B 的会话列表里,但不应自动在 B 打开或抢占 B 的当前任务;真正删除会话时,所有窗口仍应移除失效数据。

验收建议

  1. 设置页能独立开启/关闭“同步打开的会话标签和焦点”。
  2. 关闭后,窗口 A 打开、选择、排序、置顶或关闭标签,不改变窗口 B 的标签和焦点。
  3. A 新建并持久化会话后,B 的会话列表正常出现该会话,但不会自动打开标签。
  4. 真正删除会话/文件夹时,所有窗口仍删除对应无效标签。
  5. 两个独立窗口并发保存时,不争用同一套全局 tab snapshot/CAS version。
  6. 刷新和 WebSocket 重连后,各窗口恢复自己的 scope,不重新收敛到全局标签集合。
  7. 重新开启后,恢复现有同步语义;迁移和模式切换行为有明确规则及回归测试。
  8. draft 当前为本地状态的安全语义继续保留。

可选实现方向

仅在前端忽略 tabs://changed 不足以解决问题,因为挂载、重连和 CAS 冲突仍会读取同一套全局快照。

一种方向是为 persisted tabs、version key 和事件载荷增加稳定的 scope_id / workbench_id,关闭同步时各窗口使用独立 scope,开启时使用共享 scope。现有临时 TAB_ORIGIN 可继续用于 echo suppression,但不能代替可跨刷新恢复的持久作用域。

也可以结合 #460 的“命名工作台”设计,让多个窗口分别绑定不同工作台,同时保留一个可选的全局同步工作台。

相关 Issue

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