Skip to content

feat(macos): 终端标签任意拖拽分屏并保留 PTY - #1638

Merged
xiaoyumuxi merged 3 commits into
1lck:previewfrom
icefery:feat/macos-terminal-tab-splits
Nov 1, 2026
Merged

xiaoyumuxi merged 3 commits into
1lck:previewfrom
icefery:feat/macos-terminal-tab-splits

Conversation

@icefery

@icefery icefery commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

终端标签任意拖拽分屏并保留 PTY

背景

Issue #1622:终端标签只能在自身标签栏内重排,不能像编辑器页签一样拖到分组边缘分屏;工具窗里的终端也无法拖进编辑器。

根因:

  • 终端标签仍使用 SwiftUI .onDrag,Monaco 所在的 WKWebView 会消费拖放目标,祖先视图的 .onDrop 收不到终端 payload。
  • 旧 payload 带 text/plain 兜底,拖到编辑器时会把 “Terminal” 当文本插入。
  • EditorTabOrderFeatureModel.drop 只接受已在编辑器中的页签,缺少从外部插入的入口。

本次只处理终端标签拖拽/分屏;关闭确认、分栏比例持久化和 TerminalPlacementFeatureModel 双列表结构不变。

实现

  • 模型层:新增 group(containing:)、insert(_:into:side:) 和 AppModel.moveTerminalToEditor(_:into:side:),支持外部标签加入分组或在边缘分屏。
  • 分屏几何:新增 EditorGroupLayout.splitting(_:by:newGroup:),drop 与 insert 共用,删除重复的内联 split。
  • 拖拽层:TerminalTabInteraction 改用 AppKit NSDraggingSession,pasteboard 只写私有 com.lithe.terminal-tab,不再提供文本表示。
  • 落点层:编辑器工作区、空编辑器、工具窗各有一个仅在终端拖拽期间可命中的 AppKit drop overlay;编辑器内复用 EditorGroupDragState 的分栏预览,标签栏仍交给 SwiftUI。
  • 终端会话与 PTY 在移动中保持不变,只重写 editor placement。

影响

  • 终端标签可拖入任意编辑器分组边缘形成上下/左右分屏,或拖到中心加入标签栏;编辑器终端可拖回工具窗。
  • 拖拽显示分屏预览;选中、右键菜单和关闭按钮行为不变。
  • 已知边界:编辑器显示 Git 对比/Diff 视图时没有该 overlay;拖拽预览是通用终端图标,不是标签快照。
  • 不包含:关闭确认逻辑、分栏比例持久化、TerminalPlacementFeatureModel 双列表重构。

验证

iShot_2026-11-01_18.12.31.mp4

- 终端标签改用 AppKit 拖拽会话与私有 pasteboard,避免 Monaco 接收文本拖放
- 工具窗终端可拖入编辑器边缘或空编辑器,编辑器终端可拖回工具窗
- 抽出 EditorGroupLayout.splitting 复用分屏几何,补充模型、拖拽与 PTY 测试

Refs 1lck#1622
@ghfind-review ghfind-review Bot added the review: medium ghfind author score; see https://ghfind.com label Nov 1, 2026

@xiaoyumuxi xiaoyumuxi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

审查范围:preview ← feat/macos-terminal-tab-splits,head dc29d667d35244a17103449502373999a2b4ab00,共 12 个变更文件。

发现 2 项 P2 问题,具体见行内评论,均为本次引入,建议修改;未发现需要阻塞合并的问题。

  1. 工具窗整窗 drop overlay 抢占标签栏落点,拖回终端时无法按目标标签前后插入,只能追加到末尾。
  2. 编辑器 drop overlay 在目标没有变化时仍反复发布 @Published target,引起整个编辑分组布局的不必要刷新。

补充建议:补齐平台功能矩阵

本 PR 自身没有更新终端拖拽/分屏对应的能力记录。以 merge-base e2e7f5cad92e69d0b635642570697d1da56f5b35 到本次 head 运行矩阵变更门禁,会报告缺少实质能力更新。现有矩阵 CI 绿灯包含目标分支推进造成的其他能力记录差异,不能证明本功能已登记。建议更新终端会话或编辑分组对应的能力记录,补充边缘分屏、中心加入及工具窗往返保留 PTY 的验证动作;没有完成的平台验收继续保持 pending。

验证范围

  • 已静态追踪分组布局、标签选择、工具窗/编辑器 placement、原生拖放和终端 surface 生命周期;未发现移动时重建会话或进程的路径。
  • 已查阅 macOS CI:3,589 项 Swift 测试通过,新增测试通过,但未覆盖上述原生 overlay 路由和相同落点通知次数。
  • 本地对 PR head 的临时源码快照运行 scripts/macos/verify/verify-runtime-bundle-immutability.sh,通过;PR diff 的 git diff --check 通过。
  • 未启动产品、未执行本地完整测试,以上交互问题基于静态调用链审查,尚未做本机真实拖放复现。未修改源码。


required init?(coder: NSCoder) { nil }

override func hitTest(_ point: NSPoint) -> NSView? {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 工具窗覆盖层应保留标签落点顺序

这个 overlay 覆盖整个 TerminalView,且编辑器终端拖拽期间没有避让标签栏,因此会先于原有 TerminalTabDropDelegate 接收落点。performDragOperation 随后统一调用 moveTerminalToTool(sessionID),只会把会话追加到工具窗列表末尾,丢失原有按目标标签左/右半区插入的行为。

可构造的复现路径:工具窗已有 A、B,从编辑器将 C 拖到 A 的左半边,最终得到 A、B、C,而不是 C、A、B。

建议像编辑区 overlay 一样排除标签栏命中,交还已有前后插入 delegate;或者在原生目标中解析目标标签和插入侧,再调用带 before/after 的移动入口。补一项跨容器拖到已有标签前后的聚焦回归即可。来源:本次引入。

currentTarget = nil
return []
}
drag.target = target

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 发布预览目标前应比较是否变化

draggingUpdated 每次都会走到这里,并无条件给 @Published target 赋值;即使指针仍在同一个边缘区域,生成的 Target 完全相同,也会发送通知,使观察它的 EditorGroupContainer 重新计算编辑分组布局。无效落点分支反复赋 nil 也有同样问题。普通标签拖动的 updateTabDrag 已经用新旧目标比较避免这项开销。

建议仅在 drag.target != target 时发布,清空目标时也去重;用重复输入同一落点并检查通知次数的聚焦回归保护该边界,不需要跑整机性能测试。来源:本次引入。

- Limit the native tool-window drop overlay to the canvas so tab-strip
  before/after insertion remains available for terminals dragged from editors.
- Publish editor-group preview targets only when they change, including
  repeated clearing and drag lifecycle cleanup.
- Add focused native overlay geometry and preview publication regressions.
- Update the docking Agent Note and terminal capability matrix evidence and
  verification steps without claiming unperformed runtime verification.

Validation: six focused Swift tests passed on Swift 6.3.3 with macOS 26.5 SDK;
runtime bundle, architecture, i18n, Agent Notes and matrix static gates passed.
The exact PR-based push tree also passed the static gates.

Refs 1lck#1638
@xiaoyumuxi
xiaoyumuxi merged commit 35c8aad into 1lck:preview Nov 1, 2026
3 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: medium ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants