110 lines
4.1 KiB
Markdown
110 lines
4.1 KiB
Markdown
|
|
# 如梦 — 架构文档
|
|||
|
|
|
|||
|
|
## 总览
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
iPhone (如梦.app)
|
|||
|
|
│
|
|||
|
|
├─ HTTP ── Tailscale ── Mac (push.py :8795)
|
|||
|
|
│ ├── /tmux/capture ─→ tmux capture-pane
|
|||
|
|
│ ├── /tmux/send ─→ tmux send-keys
|
|||
|
|
│ └── /health ─→ 健康检查
|
|||
|
|
│
|
|||
|
|
└─ 网络层: ServerConfig → URLSession → push.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 文件清单
|
|||
|
|
|
|||
|
|
| 文件 | 职责 | 依赖 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `如梦App.swift` | @main 入口,WindowGroup → ContentView | ContentView |
|
|||
|
|
| `ContentView.swift` | 页面路由 + 手势导航菜单 + ZStack 层级 | 所有页面 View |
|
|||
|
|
| `ChatPlaceholderView.swift` | 聊天占位(待开发) | 无 |
|
|||
|
|
| `TerminalView.swift` | tmux 终端 UI(黑底浅灰字) | TerminalViewModel |
|
|||
|
|
| `TerminalViewModel.swift` | tmux 网络层:轮询 capture-pane + 发送 send-keys | ServerConfig |
|
|||
|
|
| `ServerConfig.swift` | endpoint 列表 + 当前选中 + URL 构建 | UserDefaults |
|
|||
|
|
| `SettingsView.swift` | 设置页:endpoint 管理 + 主题切换 | ServerConfig, EndpointChecker |
|
|||
|
|
| `EndpointResolver.swift` | 异步 ping /health,返回每个 endpoint 状态 | ServerConfig |
|
|||
|
|
| `ThemeManager.swift` | 白天/夜间两套色板定义 | UserDefaults |
|
|||
|
|
|
|||
|
|
## 数据流
|
|||
|
|
|
|||
|
|
### 1. 网络请求
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
TerminalView / SettingsView
|
|||
|
|
→ ServerConfig.makeRequest(path:)
|
|||
|
|
→ ServerConfig.activeHost (从 UserDefaults 读)
|
|||
|
|
→ URLSession 请求 push.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `ServerConfig.endpoints` 是预定义列表,顺序可被 SettingsView 的上下箭头重排
|
|||
|
|
- 重排结果持久化到 `endpoint_urls` / `endpoint_labels`(UserDefaults)
|
|||
|
|
- `active_endpoint`(UserDefaults)决定当前用哪个
|
|||
|
|
- `EndpointChecker` 定期 ping 所有 endpoint,更新状态灯(绿/红/灰)
|
|||
|
|
|
|||
|
|
### 2. 主题
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
SettingsView 点击「夜间/白天」
|
|||
|
|
→ setTheme() → UserDefaults("active_theme")
|
|||
|
|
→ ContentView 监听 UserDefaults.didChangeNotification
|
|||
|
|
→ theme = currentTheme()
|
|||
|
|
→ 所有颜色从 theme.bg / theme.text / theme.textDim 读取
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `AppTheme.dark` 和 `AppTheme.light` 各自定义完整色板
|
|||
|
|
- 目前只有 dark 在用,light 是 placeholder
|
|||
|
|
|
|||
|
|
### 3. 菜单导航
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
用户左边缘右滑
|
|||
|
|
→ DragGesture → menuX 跟踪手势
|
|||
|
|
→ 松手 → snapTo(.open) 或 snapTo(.closed)
|
|||
|
|
→ spring 动画 → menuX 归位
|
|||
|
|
→ ContentView 根据 currentPage 切换页面
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `menuX` 是菜单唯一的状态源(-200 隐藏,0 打开,>0 弹性过冲)
|
|||
|
|
- `menuOpen` 是辅助状态(控制触控条激活/禁用)
|
|||
|
|
- 页面切换是 `@State currentPage` 驱动 `@ViewBuilder pageView`
|
|||
|
|
|
|||
|
|
## ZStack 层级(自上而下)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
4. 左边缘触控条 (HStack 24pt) — allowsHitTesting(!menuOpen)
|
|||
|
|
3. 页面标题 (VStack) — menuX < -150 时显示
|
|||
|
|
2. 菜单层 (if menuX > -200):
|
|||
|
|
├── 遮罩 Color.black 0.15 — allowsHitTesting(false)
|
|||
|
|
├── 关闭热区 Color.clear.contentShape — onTapGesture → snapTo(.closed)
|
|||
|
|
└── 图标列 VStack — Button × 4,点击切换页面
|
|||
|
|
1. 当前页面 (pageView) — TerminalView / Chat / 阅读 / 设置
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**规则**:
|
|||
|
|
- 菜单打开时触控条失活,避免抢手势
|
|||
|
|
- 遮罩背景不拦截触摸,关闭热区在图标列下面
|
|||
|
|
- 图标列在最上层,按钮始终可点
|
|||
|
|
|
|||
|
|
## 各页面状态
|
|||
|
|
|
|||
|
|
| 页面 | 状态 | 备注 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| 聊天 | 占位 | 纯黑 + "聊天" 文字,待开发 |
|
|||
|
|
| 终端 | 完成 | tmux capture-pane 轮询 + send-keys 发送,带时间戳 |
|
|||
|
|
| 阅读 | 占位 | 待开发 Coffee Time |
|
|||
|
|
| 设置 | 开发中 | endpoint 管理 + 主题切换,功能可用但 UI 需继续 |
|
|||
|
|
|
|||
|
|
## 设计原则
|
|||
|
|
|
|||
|
|
- **单一状态源**:menuX 驱动菜单,currentPage 驱动页面,UserDefaults 驱动配置
|
|||
|
|
- **无 Combine**:不用 @StateObject/@Published,用 @State + UserDefaults 通知
|
|||
|
|
- **零第三方依赖**:纯 SwiftUI + Foundation
|
|||
|
|
- **不向后兼容**:部署目标 iOS 26.4,不考虑旧版本 API
|
|||
|
|
- **所有网络走 push.py**:不直连 DeepSeek,不引入新服务端
|
|||
|
|
|
|||
|
|
## 更新日志
|
|||
|
|
|
|||
|
|
- 2026-05-22 — 初始架构。ZStack 菜单导航 + endpoint 切换 + 主题系统骨架。
|