feat(ios,android): P2 wave, git panel, token UX, per-host WS token, docs
App layer, four sequential slices (a shared .xcodeproj means adding files regenerates it, so these could not run in parallel): - token UX end to end: pairing prompts for a token when a host 401s, POST /auth validates it, and 204-without-Set-Cookie is correctly read as "this server has auth disabled" rather than "authenticated". A host paired before the token was turned on recovers by re-pairing in place. Remove-host now exists and finally gives PushRegistrar.handleHostRemoved a caller. - project git panel + worktree lifecycle (T-iOS-32) + claude --resume history — the parity gap with Android and the web front end. - terminal search (T-iOS-33) and voice PTT (T-iOS-31) with an epoch guard so a session switch between dictation and confirm cannot inject into the wrong session. - theme + Dynamic Type (T-iOS-34) and web ?join= interop (T-iOS-35). RootView no longer hard-locks .preferredColorScheme(.dark). Also unpins SwiftTerm to 1.15.0 by dropping the local hasActiveSelection that collided with the upstream one, verified green from a fresh derivedDataPath. Includes the two HIGH fixes the security review found: - iOS resolved the WS token host-independently, so a token-gated host sitting next to an open one could never open a terminal and no on-screen remedy could fix it. Now one transport per host; cross-host leakage is structurally impossible since both read paths return only that host's own value. - Android reported the host's own git-credential 401 (git-ops.ts:108, "Push authentication required on the host.") as "your access token is wrong", because a blanket 401 mapping ran ahead of the per-route one. Git-write routes are now ROUTE_DEFINED and keep the server's message. And the doc sync: README/ios README no longer claim the client is unmerged on feat/ios-client, the Clients section finally lists Android, and the plan checkboxes reflect what is actually built. iOS 534 app tests + 452 package tests; Android 687 tests.
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
# PLAN_IOS_IPAD.md — iPad 适配(自适应布局,非分叉)
|
||||
|
||||
> 落地方案文档。目标:让已完成的 iPhone 客户端([PLAN_IOS_CLIENT.md](./PLAN_IOS_CLIENT.md),P0+P1 已交付,分支 `feat/ios-client`)**原生适配 iPad**——大屏分栏、双向布局、指针/硬件键盘,而**不分叉出第二套 UI**。
|
||||
> 落地方案文档。目标:让已完成的 iPhone 客户端([PLAN_IOS_CLIENT.md](./PLAN_IOS_CLIENT.md),P0+P1+P2 已交付,**已合入 `develop`**)**原生适配 iPad**——大屏分栏、双向布局、指针/硬件键盘,而**不分叉出第二套 UI**。
|
||||
> 拓扑决策:**单一代码库 + size-class 自适应**——`NavigationSplitView` 在 regular 宽度(iPad 全屏/大分屏)给 sidebar+detail,在 compact 宽度(iPhone、iPad Slide Over/小分屏)**自动退化为现有 stack**。iPhone 行为字节级不变。
|
||||
> 状态:**规划中(2026-07-05,未开工)**。
|
||||
> 状态:**已交付并合入 `develop`**(T-iPad-1…4 全部落地;T-iPad-5 验收 PASS_WITH_FINDINGS,4/4 findings 已修,真机项 DEFERRED)。
|
||||
> 逐任务状态见 §5 各任务标题上的方框;**Steps 里的 `[ ]` 是原始规格清单,不是状态**(执行记录在 `PROGRESS_LOG.md`)。
|
||||
> 2026-07-30 由 ios-completion 收尾波按源码核对:`Wiring/{AdaptiveRootView,SplitRootView,LayoutMode}.swift`、
|
||||
> `Components/TerminalContextMenu.swift`、`Screens/ProjectsLayout.swift` 与 `LayoutPolicyTests`/`SidebarSelectionTests`/
|
||||
> `KeyBarVisibilityTests`/`TerminalContextMenuTests`/`ProjectsLayoutTests`/`ProjectsLayoutUITests` 均在树内;
|
||||
> `project.yml` 三个 target 的 `TARGETED_DEVICE_FAMILY` 均为 `"1,2"`;`ios.yml` 有 iPad 单测腿与 iPad UI-test 腿。
|
||||
> 本文是 iPhone 计划之上的**布局适配层**,不改协议/会话模型/纯逻辑包;沿用 [PLAN_IOS_CLIENT.md](./PLAN_IOS_CLIENT.md) 的 §3 契约、§4 工程标准、§5 安全模型、§6 并行规则。冲突以 iPhone 计划为准。
|
||||
> **G1 日志铁律**:`PROGRESS_LOG.md` 由 orchestrator 独写;被派 subagent 不写 LOG,在返回消息末尾附可粘贴条目。
|
||||
|
||||
@@ -118,7 +123,7 @@ enum SidebarItem: Hashable { case session(UUID), newSession, projects }
|
||||
|
||||
### W0 · 可安装性(串行,先行)
|
||||
|
||||
#### T-iPad-1 · device family + iPad plist/方向 `[ ]` · ~0.5 pd
|
||||
#### T-iPad-1 · device family + iPad plist/方向 `[x]` · ~0.5 pd
|
||||
- **Owns**: `ios/project.yml`(device family、iPad 方向、必要 plist)、`.github/workflows/ios.yml`(加 iPad 模拟器测试腿)
|
||||
- **Depends**: 无
|
||||
- **Steps(测试先行)**:
|
||||
@@ -132,7 +137,7 @@ enum SidebarItem: Hashable { case session(UUID), newSession, projects }
|
||||
|
||||
### W1 · 自适应导航壳(核心)
|
||||
|
||||
#### T-iPad-2 · AdaptiveRootView + NavigationSplitView + LayoutPolicy `[ ]` · ~2 pd
|
||||
#### T-iPad-2 · AdaptiveRootView + NavigationSplitView + LayoutPolicy `[x]` · ~2 pd
|
||||
- **Owns**: `Wiring/{AdaptiveRootView,SplitRootView,LayoutMode}.swift`(新)、`Wiring/RootView.swift`(改:现有 stack 抽成 `StackRootView` 子视图供 compact 复用)、`Wiring/AppCoordinator.swift`(改:sidebar 选中 ↔ 路由最小桥)、对应测试
|
||||
- **Depends**: T-iPad-1
|
||||
- **Steps(测试先行, RED)** — `LayoutPolicyTests` + `SidebarSelectionTests`:
|
||||
@@ -149,7 +154,7 @@ enum SidebarItem: Hashable { case session(UUID), newSession, projects }
|
||||
|
||||
### W2 · 逐面适配(并行,文件互斥)
|
||||
|
||||
#### T-iPad-3 · 终端面板:KeyBar 自适应 + 指针上下文菜单 `[ ]` · ~1 pd
|
||||
#### T-iPad-3 · 终端面板:KeyBar 自适应 + 指针上下文菜单 `[x]` · ~1 pd
|
||||
- **Owns**: `Components/{KeyBar,TerminalContextMenu}.swift`、`Screens/TerminalScreen.swift`(增量)、测试
|
||||
- **Depends**: T-iPad-2 · **Parallel-safe**: T-iPad-4
|
||||
- **Steps(测试先行)**:
|
||||
@@ -159,7 +164,7 @@ enum SidebarItem: Hashable { case session(UUID), newSession, projects }
|
||||
- **Accept**: iPad 接键盘时 KeyBar 自动隐、去键盘复现;右键菜单在 iPad 生效;iPhone 无硬件键盘时 KeyBar 行为不变
|
||||
- **安全注**: 上下文菜单的 kill/开会话是既有 G/RO 通道的又一触发面——测试名标「复用 APIClient,无绕过」
|
||||
|
||||
#### T-iPad-4 · Projects/Timeline/sheet 大屏化 `[ ]` · ~1 pd
|
||||
#### T-iPad-4 · Projects/Timeline/sheet 大屏化 `[x]` · ~1 pd
|
||||
- **Owns**: `Screens/ProjectsScreen.swift`(增量)、Projects/Timeline 呈现方式(regular 下 `.sheet` → 列/`.presentationDetents` 或 sidebar section)、测试
|
||||
- **Depends**: T-iPad-2 · **Parallel-safe**: T-iPad-3
|
||||
- **Steps(测试先行)**:
|
||||
@@ -170,7 +175,9 @@ enum SidebarItem: Hashable { case session(UUID), newSession, projects }
|
||||
|
||||
### W3 · 验收(report-only, G4)
|
||||
|
||||
#### T-iPad-5 · iPad 验收 + iPhone 回归 + 安全核对 `[ ]` · ~0.5 pd
|
||||
#### T-iPad-5 · iPad 验收 + iPhone 回归 + 安全核对 `[~]` · ~0.5 pd
|
||||
- **已执行**:模拟器侧 PASS_WITH_FINDINGS(iPhone 16 / iPad Pro 11 双套件绿,iPhone 零回归硬门守住),4 个 findings(2 MED / 2 LOW)由 orchestrator 修完 4/4;iPad 分栏 sidebar+detail 截图确认。
|
||||
- **缺口**:① 真机 iPad(分栏手势 / 硬件键盘全键位 / 指针 hover 右键 / Stage Manager)**DEFERRED**,手工清单在 `PROGRESS_LOG.md`;② iPad happy-path XCUITest **接受推迟**(split 选中逻辑已由 `SidebarSelectionTests` 覆盖,单跑 7–11 min 且脆);③ **release ipa 层**核对未做(免费个人 team 无分发通道)——与 T-iOS-19 同一缺口,且 P2 新增的麦克风/语音识别 usage description 也应一并核。
|
||||
- **Depends**: T-iPad-2/3/4 · **Owns**: 无源码(report-only,findings 派回 owner)
|
||||
- **Steps**:
|
||||
- [ ] **F-iPad 走查**(§6 清单)逐条:分栏、横竖屏、Split View/Slide Over/Stage Manager 尺寸切换、硬件键盘/指针、终端更宽列数、遮罩覆盖 detail
|
||||
@@ -214,10 +221,10 @@ enum SidebarItem: Hashable { case session(UUID), newSession, projects }
|
||||
| SwiftTerm 在超宽 detail 的性能/选择手感 | 低 | 复用 iPhone 已测路径;真机目视 |
|
||||
| 多窗口诱惑导致 scope 膨胀 | 中 | 本期明确单场景(§0 非目标);多窗口另立计划 |
|
||||
|
||||
**待你拍板**:
|
||||
1. iPad 最低系统版本:iPadOS 17(与 iPhone 一致)还是抬到 18/26?
|
||||
2. 本期是否要指针右键上下文菜单(T-iPad-3 后半)——纯锦上添花,可砍到后续。
|
||||
3. 多窗口(拖会话开新窗并排两终端)确认放到下一期?
|
||||
**待你拍板** —— 三项均已落定(2026-07-30 按源码核对):
|
||||
1. ~~iPad 最低系统版本~~ **已定**:iPadOS **17**,与 iPhone 一致(`project.yml` 单一 `deploymentTarget: iOS 17.0`,无独立 iPad 下限)。
|
||||
2. ~~本期是否要指针右键上下文菜单~~ **已做**:`Components/TerminalContextMenu.swift`(复制选区 / 在 cwd 开新会话 / 结束会话,全部路由既有通道,kill 仍经 APIClient 带 Origin)+ `TerminalContextMenuTests`。
|
||||
3. ~~多窗口~~ **确认推迟**:本期单场景(§0 非目标);拖会话开新窗并排两终端仍未开工,另立计划。
|
||||
|
||||
**工作量合计**:W0 0.5 + W1 2 + W2(3∥4)2 + W3 0.5 ≈ **5 人日**(并行后墙钟更短)。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user