# cdrop web ↔ iOS 同步检查单

> 机制，不是清单快照。web（React）与 iOS（SwiftUI）视图层**有意分叉**（液态玻璃决定不可能像素一致）。一致性**优先靠强约束同步**（单一真源、机械消费、漂移不可能，见 `ios/PLAN.md` §5）；本文件**只装强约束管不住、需人判定的残余**。
> 关联：`ios/PLAN.md` 决策 D / §5。

---

## 何时跑（触发）

- 任一 PR 改了用户可见 UI / 流程 / 元素 → 过下方“残余项”。
- 改了后端 API 字段 → 过残余项 3。
- 发布任一端前 → 全表过一遍 + 更新“漂移日志”。

---

## 残余项（强约束之外，逐项人工核对）

### 1. 元素 / 屏 / 功能存在性对等

任一端新增用户可见的元素 / 屏 / 功能 → 确认各端都有对应（或登记为残余项 4 的“允许分叉”）。机制无法逼一个 SwiftUI View 自动长出，只能此处提醒。

### 2. 非引擎驱动的新流程 / 状态

不在共享传输引擎里的纯 UI 流程 / 状态（设置项、登录分支、空 / 错 / 加载态）→ 两端手对。**引擎驱动的传输行为不在此列**（强约束已保，无需手对）。

### 3. 后端 API 字段变更

后端改了请求 / 响应字段 → 提醒**两端瘦客户端都跟改**（后端是契约源，但客户端类型手维护）。

### 4. 允许分叉登记（明确 OK 不一致）

- **视觉材质**：液态玻璃 vs Theme B。
- **导航外壳**：原生 `NavigationStack` / `TabView` vs web 布局。
- **字体 / 排版**：系统字体 + Dynamic Type vs Orbit Gothic / Noto；间距 / 圆角 / 字阶各用平台母语。
- **平台专属**：控制中心控件、Share Extension（仅 iOS）；Web Push 订阅 UI、桌面设置区块（非 iOS）。
- **控制中心控件标签**：运行时控件标签（用户点按的那个）经 `t()` 跟随设备语言；但「添加控件」库名 + Shortcuts 标题是 `LocalizedStringResource` 静态值、`CDropWidgets` target 无 String Catalog，故对 en / zh-TW 用户回退简体字面量（审查 L7，平台专属功能可接受分叉；要彻底本地化须为 Widgets target 加 `.xcstrings`）。

---

## PR 自检片段（复制进 PR 描述）

- [ ] 新增 / 改动的用户可见元素 / 屏 / 功能，各端都有或已登记允许分叉？
- [ ] 新增非引擎流程 / 状态，两端已对齐？
- [ ] 改了后端字段？两端客户端都跟改？
- [ ] 新增文案走了 i18n 单一源（非硬编码）？

---

## 漂移日志

记录每次“已同步”的两端基准，使漂移可见：

| 日期 | web commit | iOS commit | 同步内容 |
|---|---|---|---|
| 2026-06-24 | 建档（iOS 未开工） | — | 初始：架构 A 定、引擎 / i18n / 品牌 / API 四强约束确立，检查单建立 |
| 2026-06-24 | 引擎入口 net/ios.ts + engine/main.ts（未提交） | — | web 侧无头引擎入口 + iOS 桥 + 接收 sink 接入 + Vite 多入口；tsc / build 双过 |
| 2026-06-24 | — | ios/CDrop/ scaffold（未提交） | iOS 原生壳：液态玻璃 TabView + 离屏引擎 WebView 宿主 + 桥 + 落盘管理器；编过 iphonesimulator26.5 |
| 2026-06-24 | — | ios/CDrop/ 登录+发送（未提交） | 扫码登录新设备侧（AuthManager + LoginView，对 prod qr/start 验通显码）+ 发送路径（cdrop-file scheme 供文件 + 文件选择器）+ 会话注入引擎 boot |
| 2026-06-24 | i18n locales + scripts/emit-i18n.mjs（未提交） | ios/CDrop i18n（未提交） | **强约束②落地**：web locales 加 `ios.*` 键 → `emit-i18n.mjs` 产出 JSON → iOS `t()` 读（loader I18n.swift）。用户可见**去代号 cdrop**（品牌用 `app.brand`=Commilitia Drop），显示名改 Commilitia Drop。**改 locale 后须重跑 `node web/scripts/emit-i18n.mjs`**（已记入脚本头） |
| 2026-06-24 | engine/main.ts 加 presence 推送 + 初始快照（未提交） | RootView / EngineController / AuthManager 真数据绑定（未提交） | **A 任务**：引擎订阅 `store.devices` 推 `presence` 事件 + boot 后补发 presence/transfers 初始快照；原生 `EngineController` 解码进 `@Observable` devices/transfers/history；`RootView` 三屏全替 demo→真数据 + 新增 `TransferDetailView`（TODO #5）。i18n 加 `ios.detail.*` / `ios.settings.account|user|logout` / `ios.transfer.empty|incoming|outgoing` / `ios.send.noDevices` / `ios.devices.empty`，传输态文案复用共享 `transfer.state|phase|mode|bytesRate`。iOS `t()` 加 `{{name}}` 插值变体对齐 web。Keychain 会话持久化 + 设置页登出（闭合 TODO #4）。web tsc/build + xcodebuild iphonesimulator26.5 双过，模拟器三屏空态截图验。**已部署 prod**：`engine.html` → `engine-BB8iav7K.js`（含 presence 推送），真会话下即生效 |
| 2026-06-24 | engine/main.ts：transfers 推送节流 ~3Hz + toWire 加 ice 摘要（未提交，**待部署**） | EngineController 手动容忍解析 + ICE 字段；RootView 详情页 ICE 行 + 收到文件视图 + 设备名编辑 + 设备计数；DownloadManager listFiles/delete；新 DeviceNameStore / QuickLookPreview；project.yml 加文件共享键 + 品牌化权限串（未提交） | **首轮真机测试反馈修复**：① 速度数字 ~10Hz 闪 → 节流活跃传输推送到 3Hz（iOS-only，亦降 P2P 接收主线程抖动）；② 设备列表空 → 原生改手动容忍解析（严格 JSONDecoder 遇 bool/int 失配整组失败的隐患），加「已知设备」计数诊断；③ 收到文件无处开 / Files 不可见 / 阻塞发送 → `UIFileSharingEnabled`+`LSSupportsOpeningDocumentsInPlace`（Documents 已是落地目录）+ 应用内「收到的文件」视图（QuickLook 预览 / 系统分享 / 转发在线设备 / 删除）；④ 设备名不可改 → `DeviceNameStore`（UserDefaults）+ 设置页编辑框（后端令牌烤名、改名下次登录生效）；⑤ P2P <100KB/s（relay 1MB/s）→ 详情页暴露选中 ICE 候选对诊断是否 TURN 中继（不盲改共享 p2p.ts 水位）。i18n 加 `ios.files.*`/`ios.settings.deviceName|deviceNameNote|deviceCount`，ICE 行复用 `transfer.debug.*`。web 双过 + xcodebuild 编过 + 模拟器设置 / 传输屏截图验。**已部署 prod**：`engine.html` → `engine-BC6mZ9zP.js`（含节流 + ice 摘要） |
| 2026-06-27 | source.ts（新 FileSource）+ p2p/relay/transfer 改 src + engine/main.ts 加 registerPush/provisionWidgetSession + i18n 加 `ios.bg.*`/`ios.share.*`/`ios.control.*`（本提交） | 新 BackgroundTaskManager / AppDelegate / PushRegistry / Shared(AppGroup,I18n,CDropAPI,WidgetSession) / Share(ShareViewController) / Widgets(控件+intents+client)；EngineController 加 Range handler / 后台 sync / 分享导入 / 控件代铸；project.yml 加两扩展 target + 真机签名 scaffold；包名改 `net.commilitia.Commilitia-Drop`（本提交） | **计划完成（I3–I7）**：发送端流式（FileSource + 原生 Range，新增**分享发送 + 控件**两条非引擎流程，对应残余项 1/2）/ 后台续传 / APNs（后端 internal/apns + 原生注册，新后端字段 `push_subscriptions.platform` + 端点 `POST /api/push/apns/register` 对应残余项 3）/ Share Extension / 控制中心两控件（新允许分叉：控件标签见上）。经 ultracode 多 agent 审查（0 HIGH，修 2 MED + 7 LOW）。web tsc/build + go build/test + xcodebuild 全过；模拟器验登录 / 分享弹窗 / 控件嵌入 / App Group 容器。**真机 + 真发推送 + 分发账号门控**（见 REALDEVICE.md）。**待部署 prod**：engine.html（新桥）+ cdrop binary（APNs 端点 + platform 列） |
