背景与问题
在 Codeg 服务器模式下,同时用多个浏览器窗口连接同一个实例时,希望窗口 A、窗口 B 分别处理不同任务。
当前任一窗口打开、选择或关闭一个已持久化会话标签后,其他窗口也会同步打开、切换焦点或关闭对应标签。结果是多个窗口无法维持各自独立的工作上下文,难以并行处理多个任务。
复现步骤
- 以服务器模式部署 Codeg。
- 用两个浏览器窗口打开同一个 Codeg 实例,记为窗口 A 和窗口 B。
- 在窗口 A 打开一个已有会话,或新建会话并使其完成持久化。
- 观察窗口 B:对应会话标签也被加入,并可能同步成为活动标签。
- 在窗口 A 关闭该会话标签。
- 观察窗口 B:对应标签也被移除。
实际行为
所有客户端共享一套“已打开的持久会话标签 + 当前活动标签”状态:
- 一个窗口打开标签,其他窗口也打开;
- 一个窗口切换当前会话,其他窗口的活动标签也跟随;
- 一个窗口关闭标签,其他窗口也关闭;
- 刷新或 WebSocket 重连后,客户端重新读取同一套全局标签快照并继续收敛。
源码核对结论
以下结论基于最新 upstream/main:31386f4d。
这不是偶发的 WebSocket 串线,而是 #251 / f9e531b1 引入的既定全局同步语义,目前缺少窗口级作用域或关闭同步的选项:
-
数据库只有一套全局 opened tabs
-
并发控制使用一个 workspace-global 版本号
-
后端主动要求所有客户端收敛
-
前端明确合并远端标签并镜像焦点
- TabProvider在挂载时读取全局快照,并订阅
tabs://changed;重连后再次读取。
- 远端快照合并会采用其他客户端打开的标签,并移除其他客户端已经关闭的标签。
- 焦点解析明确写着 “focus is mirrored across clients”。
TAB_ORIGIN 只用于忽略自己的广播回声,不是窗口隔离标识,而且每次加载都会重新生成。
-
测试把当前行为固定为预期
- 跨客户端测试明确覆盖远端打开、远端关闭、远端焦点镜像,以及本地焦点持久化同步。
补充边界:split-group 布局没有通过 tabs://changed 实时广播;它目前写在同源共享的单个 localStorage key 中。因此它不是本 Issue 实时标签联动的直接根因,但多个同源窗口可能在保存/刷新恢复时覆盖同一个布局快照,后续设计窗口作用域时也应明确其归属。
建议:设置页增加独立开关
建议在设置页新增一个独立选项,例如:
同步打开的会话标签和焦点
在桌面端与浏览器客户端之间同步已打开的会话标签、顺序、置顶状态和当前活动标签。
建议语义:
- 开启:保持当前行为,所有客户端实时同步 opened tabs 和 focus,适合跨设备接续工作。
- 关闭:每个浏览器窗口/客户端维护独立的标签和焦点,适合多窗口并行任务。
- 可考虑服务器部署默认关闭,或至少在说明中明确开启该项会使所有窗口镜像同一工作状态。
- 该设置应只控制 UI 工作状态,不应关闭会话数据、消息、运行状态和真实删除事件的同步。
如果要支持刷新后仍保持窗口独立,仅有布尔开关还不够;关闭同步时仍需为窗口或工作台提供稳定的 scope,避免挂载、重连或 CAS 冲突时重新读取全局快照。
期望语义
应继续全局同步
- 会话列表、消息和运行状态;
- 新建后已经持久化的会话记录;
- 会话真实删除、文件夹删除;
- 其他必须保持数据一致性的后端事件。
关闭开关后应窗口级独立
- 当前打开的会话标签集合;
- 当前活动标签;
- 标签顺序、置顶和普通的“关闭标签”操作;
- 与该窗口关联的分屏/布局恢复状态。
窗口 A 新建的持久会话可以出现在窗口 B 的会话列表里,但不应自动在 B 打开或抢占 B 的当前任务;真正删除会话时,所有窗口仍应移除失效数据。
验收建议
- 设置页能独立开启/关闭“同步打开的会话标签和焦点”。
- 关闭后,窗口 A 打开、选择、排序、置顶或关闭标签,不改变窗口 B 的标签和焦点。
- A 新建并持久化会话后,B 的会话列表正常出现该会话,但不会自动打开标签。
- 真正删除会话/文件夹时,所有窗口仍删除对应无效标签。
- 两个独立窗口并发保存时,不争用同一套全局 tab snapshot/CAS version。
- 刷新和 WebSocket 重连后,各窗口恢复自己的 scope,不重新收敛到全局标签集合。
- 重新开启后,恢复现有同步语义;迁移和模式切换行为有明确规则及回归测试。
- draft 当前为本地状态的安全语义继续保留。
可选实现方向
仅在前端忽略 tabs://changed 不足以解决问题,因为挂载、重连和 CAS 冲突仍会读取同一套全局快照。
一种方向是为 persisted tabs、version key 和事件载荷增加稳定的 scope_id / workbench_id,关闭同步时各窗口使用独立 scope,开启时使用共享 scope。现有临时 TAB_ORIGIN 可继续用于 echo suppression,但不能代替可跨刷新恢复的持久作用域。
也可以结合 #460 的“命名工作台”设计,让多个窗口分别绑定不同工作台,同时保留一个可选的全局同步工作台。
相关 Issue
背景与问题
在 Codeg 服务器模式下,同时用多个浏览器窗口连接同一个实例时,希望窗口 A、窗口 B 分别处理不同任务。
当前任一窗口打开、选择或关闭一个已持久化会话标签后,其他窗口也会同步打开、切换焦点或关闭对应标签。结果是多个窗口无法维持各自独立的工作上下文,难以并行处理多个任务。
复现步骤
实际行为
所有客户端共享一套“已打开的持久会话标签 + 当前活动标签”状态:
源码核对结论
以下结论基于最新
upstream/main:31386f4d。这不是偶发的 WebSocket 串线,而是 #251 /
f9e531b1引入的既定全局同步语义,目前缺少窗口级作用域或关闭同步的选项:数据库只有一套全局 opened tabs
opened_tabs表没有 client/window/workbench/scope 字段。list_all_tabs读取全部标签,没有按客户端过滤。并发控制使用一个 workspace-global 版本号
opened_tabs_version是整个工作区共用的逻辑时钟。后端主动要求所有客户端收敛
emit_tabs_changed广播完整标签快照。is_active也跨客户端同步。前端明确合并远端标签并镜像焦点
tabs://changed;重连后再次读取。TAB_ORIGIN只用于忽略自己的广播回声,不是窗口隔离标识,而且每次加载都会重新生成。测试把当前行为固定为预期
补充边界:split-group 布局没有通过
tabs://changed实时广播;它目前写在同源共享的单个localStoragekey 中。因此它不是本 Issue 实时标签联动的直接根因,但多个同源窗口可能在保存/刷新恢复时覆盖同一个布局快照,后续设计窗口作用域时也应明确其归属。建议:设置页增加独立开关
建议在设置页新增一个独立选项,例如:
建议语义:
如果要支持刷新后仍保持窗口独立,仅有布尔开关还不够;关闭同步时仍需为窗口或工作台提供稳定的 scope,避免挂载、重连或 CAS 冲突时重新读取全局快照。
期望语义
应继续全局同步
关闭开关后应窗口级独立
窗口 A 新建的持久会话可以出现在窗口 B 的会话列表里,但不应自动在 B 打开或抢占 B 的当前任务;真正删除会话时,所有窗口仍应移除失效数据。
验收建议
可选实现方向
仅在前端忽略
tabs://changed不足以解决问题,因为挂载、重连和 CAS 冲突仍会读取同一套全局快照。一种方向是为 persisted tabs、version key 和事件载荷增加稳定的
scope_id/workbench_id,关闭同步时各窗口使用独立 scope,开启时使用共享 scope。现有临时TAB_ORIGIN可继续用于 echo suppression,但不能代替可跨刷新恢复的持久作用域。也可以结合 #460 的“命名工作台”设计,让多个窗口分别绑定不同工作台,同时保留一个可选的全局同步工作台。
相关 Issue