feat(tabs): move tabs into detached windows - #4805
Conversation
� Conflicts: � apps/desktop/src/App.vue
t8y2
left a comment
There was a problem hiding this comment.
这个 PR 暂时不能合并,当前有三个 P1 blocker:
-
分离窗口禁用了 open-tabs 持久化,而主窗口在转移后会持久化删除原 tab。分离后的 SQL、固定状态和 tab 元数据没有新的持久化 owner,正常重启或异常退出后会直接丢失。
-
detached runtime 的表导航会用
closeTabAndWait(..., { force: true })替换当前唯一 tab,绕过已有的 dirty/save/discard 确认。未保存 SQL、结构编辑或 DataGrid pending changes 可能无提示丢失。 -
transfer accepted 与主窗口 timeout 不是原子提交。子窗口把
emitTo("main", accepted)成功当作主窗口已经消费;若 accepted 落在主窗口 timeout/unlisten 之后,主窗口会恢复原 tab 并关闭子窗口,但子窗口不会 rollback,形成双 owner,关闭流程还可能释放恢复后 tab 仍依赖的 session。
当前与 main 的机械冲突虽然只集中在 App.vue 和 queryStore.ts,但它们正好是 ownership、持久化和 session 生命周期核心,不能安全地用小型维护者 patch 解决。请先明确跨窗口持久化 owner、可靠的两阶段 transfer coordinator,以及 dirty navigation 的保存/丢弃/取消行为,并补充 timeout race、重启恢复和 session 生命周期测试后再更新。
…indow # Conflicts: # apps/desktop/src/App.vue # apps/desktop/src/stores/queryStore.ts
|
针对 Review 提出的三个问题,分别进行了以下修复:
其他优化
|
t8y2
left a comment
There was a problem hiding this comment.
request changes: The dirty-navigation path and the previous transfer timeout race are now fixed, but one P1 persistence-owner issue remains.
-
apps/desktop/src/stores/queryStore.ts:558and:1194lose the detached owner when the main WebView reloads while the detached window is still alive.initOpenTabs()restores every persisted tab into the main window. The child can then report a newer snapshot throughupdateDetachedOpenTab(), butsaveTabs()serializes main tabs first and skips detached snapshots with duplicate tab IDs.This produces two live owners. More importantly, the child receives a successful persistence acknowledgement even though the on-disk document still contains the stale main-window copy. A later quit/restart can therefore restore old SQL, pinned state, or tab metadata.
Please persist enough owner information to distinguish main and detached tabs, or reconcile currently existing detached windows before restoring tabs into the main store. Add a regression test covering:
- detach a tab and persist it;
- reload/recreate only the main store while the child remains alive;
- update the tab from the child;
- flush persistence;
- verify the child snapshot wins and the tab is not simultaneously restored into the main owner.
t8y2
left a comment
There was a problem hiding this comment.
The current head 80021289 fixes the previous dirty-navigation and timeout issues, but three cross-WebView ownership and performance blockers remain:
-
apps/desktop/src/App.vue:2274acknowledges a detached-tab transfer even when the first durable ownership write fails. After repeated persistence failures, reloading the main WebView can restore a second live owner while the child still believes the transfer committed. The acknowledgement must follow a successful durable owner transition, with recovery tests for failed writes and main-store recreation. -
apps/desktop/src/App.vue:2789lets a detached window's independent Pinia snapshot replace the main window's entireconnectionsarray. A stale child can therefore delete newly added connections or resurrect removed/edited ones. Synchronize connection changes through an authoritative owner or merge versioned per-connection updates instead of replacing the collection. -
apps/desktop/src/App.vue:2380sends the fullQueryTabthrough a Tauri event, including result caches that can reach roughly 128 MiB. A transfer can duplicate hundreds of MiB across serialization/deserialization and block or terminate a WebView. Transfer only lightweight tab identity/state and keep large results in shared/native storage, or enforce a proven bounded payload strategy.
This is a major UI/architecture change and the PR currently conflicts with main in App.vue. The conflict is not the blocker by itself; please resolve it only after the ownership protocol is corrected so neither side's behavior is lost.
变更说明
支持将主窗口中的标签页拖拽分离到独立窗口,并完成标签页所有权转移:
变更类型
涉及前端
验证
make check通过make cargo-check-fast通过已执行:
验证结果:
4 个测试文件通过,共 12 个测试通过。
手动验证步骤:
关联 Issue
Closes #4656