## Context 桌面 StudyDeck(`studydeck/`)是 Tauri 2 + React 应用:本地资料库(`app_data/studydeck/library/` + `catalog.json` / `sessions.json` / `albums.json`)、Import → Explorer → Lesson → Study 闭环,支持专辑(playlist)/课程(combo)、会话进度(媒体时间、PDF 页、分屏比例/方向、浮层位置)与多种 `StudyMode`。 `studydeck-ios/` 目前为空目录。目标是用原生 SwiftUI 在 iPad 上复刻该体验。iPad 沙盒无法作为「本机文件选择器主导入」的可靠路径(尤其大文件夹、电脑侧批量整理),因此导入改为 **App 内建局域网文件服务器**,由同网电脑推送文件。 约束: - 不用 Tauri / WebView 壳;不用 Core Data(用 SwiftData)。 - 媒体文件落 Documents/`library/`;元数据在 SwiftData。 - MVP 对齐 Import / Explorer / Lesson / Study;播放用 AVKit + PDFKit。 - 服务仅局域网、前台为主;需 Local Network 权限;上传无 token(仅信任网络使用)。 ## Goals / Non-Goals **Goals:** - 功能对等桌面 MVP(见下方矩阵):浏览库、开课、分屏学习、进度恢复、专辑/课程。 - iPad 适配的导航与分屏 UI(NavigationSplitView / 多栏)。 - 内嵌 LAN 上传服务器:发现(IP+端口+二维码/Bonjour)、多文件扁平上传、写入 `library/` 并登记 `CatalogEntry`;目录整理在 Explorer。 - 可选次要导入:Files / document picker。 - 清晰的工程结构与分阶段交付,便于 `/opsx:apply` 落地。 **Non-Goals:** - 与桌面库双向同步 / iCloud 同步(后续 change)。 - Phase B 双视频中英对比的完整产品化(模型可保留 `dual_video`,UI 可后置)。 - Phase C ASR 断句复读机。 - Android / iPhone 优化(允许 iPhone 编译,但布局以 iPad 为准)。 - WebDAV 完整客户端兼容作为必须项(可选增强;MVP 用简单 HTTP 上传 UI)。 - 后台持续文件服务(系统限制下不承诺)。 ## Feature parity matrix(桌面 vs iPad) | 能力 | 桌面 Tauri | iPad MVP | 备注 | |------|------------|----------|------| | Import 多文件 | 系统文件对话框 / 拖拽 | **LAN 服务器上传为主**;可选 Files picker | 关键差异替换点 | | Import 文件夹 | 选目录,保留相对路径 | **不支持**整文件夹/zip 导入;上传后在 Explorer 建目录整理 | 先上传再整理 | | Explorer 目录浏览 | `list_dir` | 同等:文件夹树 + kind 过滤 | | | 新建/重命名/删除/移动 | 有 | 有 | SwiftData + FS 联动 | | 勾选 → Start lesson | 有 | 有 | `resolveSelection` 逻辑移植 | | 专辑 playlist / 课程 combo | 有 | 有 | | | Lesson 历史与恢复 | `sessions` + `activeSessionId` | SwiftData Session + active | | | Study 分屏 | SplitPane + 浮层播放器 | HSplit / VStack + 可调 divider;浮层简化为工具条或可拖控件 | | | StudyMode | single_* / video_pdf / pdf_audio / dual_* | MVP:**single_***、**video_pdf**、**pdf_audio**;dual_* 可登记后置 | | | 进度保存 | 点击学习区 / 关闭应用 | scenePhase 后台 / 离开 Study / 周期节流保存 | | | Preferences / Library path | 有 | 简化:显示 Documents/`library` 路径与清空/占用;自定义根路径非必须 | | | 系列 chip | 动态推断(非写死主导航) | 同等:按文件夹/可选 series 字段 | | ## Decisions ### D1. 工程落点与技术栈 - **选择**:`studydeck-ios/` 下新建 Xcode 工程(SwiftUI App,iOS 17+ 以启用 SwiftData 稳定 API),目标 iPad。 - **栈**:SwiftUI · SwiftData · AVKit · PDFKit · Network(`NWListener`)或轻量嵌入式 HTTP(如 Hummingbird/Vapor-lite / 自研最小 HTTP)。 - **替代**:Tauri Mobile — 否决(需求明确原生 SwiftUI,且桌面已是 Tauri)。 - **替代**:Core Data — 否决(需求明确 SwiftData)。 ### D2. 存储布局 ``` Documents/ library/ # 媒体文件(相对路径与桌面一致语义) … SwiftData store … # CatalogEntry / Session / Album 等 ``` - 媒体路径:`CatalogEntry.relativePath`(posix 风格,相对 `library/`)。 - 不再以 JSON 为运行时真相;若需调试导出,可另做「导出 JSON」工具(非 MVP)。 - 导入时扩展名归类规则对齐桌面 `ResourceKind::from_extension`。 ### D3. SwiftData 实体草稿 ``` CatalogEntry id: String name: String series: String? // 可选,兼容遗留 kind: String // video|audio|pdf|other relativePath: String importedAt: Date Session id: String title: String mode: String // StudyMode snake_case // resource ids 可嵌套为 Codable 或拆字段 videoId / video2Id / audioId / pdfId / pdf2Id: String? mediaTime: Double pdfPage: Int splitRatio: Double orientation: String // horizontal|vertical playerExpanded: Bool playerPosX/Y, parentBarPosX/Y: Double updatedAt: Date albumId: String? albumIndex: Int? isActive: Bool // 或单独 AppSettings.activeSessionId Album id: String title: String mode: String // playlist|combo mediaKind: String? itemsJSON: Data/String // AlbumItem 数组 Codable updatedAt: Date AppSettings (单例) activeSessionId: String? lanServerPort: Int ``` ### D4. UI 导航(iPad) - **选择**:`NavigationSplitView` 三栏(sidebar | content | detail),对应桌面左栏导航 + 中主舞台 + 右详情/PDF。 - Sidebar:Import / Explorer / Lesson / Study(及 Settings)。 - Explorer:左目录列表,中选区/草稿 tab,右 Detail(选中项 / 专辑编辑)。 - Study:主区为分屏舞台(视频|PDF 或 PDF|音频控件);右侧可固定 PDF 第二页或工具栏——按模式收敛为「一个主分屏 + 可选 inspector」。 - 不用桌面 Cursor 式可拖左右栏宽作为必须;用系统 split 与可调 `splitRatio`。 ### D5. LAN 文件服务器(导入主路径) **协议(MVP):简单 HTTP + 浏览器上传页** | 端点 | 行为 | |------|------| | `GET /` | 返回简易 HTML:多文件选择、可选目标子目录;无 token 表单 | | `GET /health` | 200 + 服务信息 | | `POST /upload` | `multipart/form-data`:文件流 + 可选 `targetDir`;按 basename 扁平写入 | | `GET /status` | 当前上传进度(可选) | - **发现**:启动服务后 UI 显示 `http://<局域网IPv4>:`、**二维码**(纯地址)。可选 Bonjour(`_studydeck._tcp`)便于同网发现。 - **鉴权**:不做上传 token/密码;依赖「仅信任局域网 + 默认关闭服务 + 前台生命周期」。UI/README 明示勿对公网开放。 - **绑定**:仅监听局域网接口(非公网暴露意图;实际绑定 `0.0.0.0`/`::` 时依赖局域网与防火墙)。 - **写入**:校验路径不逃逸 `library/`;扩展名过滤与桌面一致;冲突命名 `_1` 后缀;成功后 upsert `CatalogEntry`。忽略客户端嵌套相对路径,只落 basename。 - **不支持**:浏览器 `webkitdirectory` 整文件夹上传、zip 解压导入;嵌套目录在 App 内 Explorer 整理。 - **次要路径**:Import 页提供「从文件 App 选取」调用 `fileImporter` / UIDocumentPicker。 **替代考虑**:完整 WebDAV — 对 Finder/Cyberduck 友好,但实现更重;列为 Phase 2 可选。MVP 以自带上传页降低电脑侧依赖。 ### D6. 服务生命周期与安全 - 默认 **停止**;用户在 Import 显式「开启局域网导入」。 - 仅 **前台** 保持监听;`scenePhase != .active` 时停止或暂停接受新连接,并 UI 提示「回到前台后重新开启」。 - Info.plist:`NSLocalNetworkUsageDescription`;Bonjour services 列表(若用)。 - 不实现账户体系;无 token,须强调仅信任网络使用。 - 大文件:流式写入磁盘,避免整文件进内存;显示传输中状态。 ### D7. 学习播放与进度 - 视频/音频:`AVPlayer` / `VideoPlayer`;PDF:`PDFView`(UIViewRepresentable)或 PDFKit SwiftUI 包装。 - 进度字段语义对齐桌面 `SessionProgress`。 - 保存触发:离开 Study、切后台、用户手动、播放进度节流(如 5s)。 ### D8. 模块目录结构(建议) ``` studydeck-ios/ StudyDeck/ App/ StudyDeckApp.swift RootSplitView.swift Features/ Import/ # LAN 面板 + Files picker Explorer/ Lesson/ Study/ Settings/ Domain/ Models/ # SwiftData @Model StudyMode.swift SelectionResolver.swift AlbumDraft.swift Services/ LibraryStore.swift FileLibrary.swift LanImportServer/ HTTPServer.swift UploadHandlers.swift BonjourAdvertiser.swift Playback/ Resources/ UploadPage.html Info.plist ``` ## Risks / Trade-offs - **[Risk] iOS 后台杀掉监听** → Mitigation:前台-only;UI 明确状态;可选本地通知提醒服务已停。 - **[Risk] 同网不可信设备扫描端口** → Mitigation:默认关闭服务;仅信任网络提示;前台生命周期短窗。 - **[Risk] 大批量多文件上传中断** → Mitigation:按文件提交、失败列表可重试。 - **[Risk] SwiftData 与文件不一致(删文件未删条目)** → Mitigation:Explorer 刷新时校验存在性;提供「清理失效条目」。 - **[Risk] 分屏手势与 AVKit 全屏冲突** → Mitigation:Study 内限制系统全屏或提供明确「专注模式」。 - **[Trade-off] HTTP 上传页 vs WebDAV** → MVP 选上传页,兼容成本低;专业用户后续再加 WebDAV。 - **[Trade-off] 不兼容桌面 JSON 直接拷贝** → 换 SwiftData 更贴 iOS;需要迁移时再写导入工具。 - **[Trade-off] 无上传鉴权** → 简化家长操作;以信任局域网 + 默认关闭换取易用性。 ## Migration Plan 1. 绿场:`studydeck-ios` 空工程搭建 → 存储 → LAN Import → Explorer → Lesson/Study。 2. 无生产用户数据迁移问题;桌面库需手动经 LAN 重传。 3. 回滚:删除 App / 清 Documents;规划产物保留在 `openspec/changes/studydeck-ipad/`。 ## Open Questions 1. 最低系统版本锁定 iOS 17 还是 18?(建议 17+) 2. 是否 MVP 就必须 Bonjour,还是 IP+二维码足够? 3. (已决)不做 zip / webkitdirectory;仅多文件扁平上传,整理走 Explorer。 4. `dual_video` / `dual_pdf` 是否在第一版 UI 露出? 5. 工程用独立 `.xcodeproj` 还是 Tuist/XcodeGen 生成?