docs: Windows 客户端 bring-up + 安装包设计方案(spec)
brainstorming 定稿:真机测试 / MVP+Inno Setup 深度 / 子进程+UAC 数据面 / 复用 DesktopShell 原生标题栏 / 拉丁全量+Noto Sans SC 7000字子集打包。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
# Windows 客户端 bring-up + 安装包 — 设计方案
|
||||
|
||||
- 日期:2026-06-21
|
||||
- 分支:`feature/windows`
|
||||
- 范围:把已写好但从未在真 Windows 上构建/运行的 Windows 客户端跑通端到端,并产出 Inno Setup 安装包。
|
||||
|
||||
## 1. 背景与现状
|
||||
|
||||
Windows 客户端的代码已**基本就绪**,但 `client/windows/README.md` 明确标注「未在真 Windows 机器上构建/验证过」(开发在 macOS)。本方案不是从零开发,而是 **bring-up(真机构建跑通)+ productionization(安装包)**。
|
||||
|
||||
### 已存在且看起来正确的部分
|
||||
|
||||
| 组件 | 文件 | 状态 |
|
||||
|---|---|---|
|
||||
| Dart 桥接层(含完整 Windows 分支) | `client/lib/bridge/desktop_vpn_bridge.dart`、`kernel_process.dart` | 已写:wintun.dll 检查、`%LOCALAPPDATA%\Pangolin\kernel` 配置目录、`.exe` 解析、`Process.kill` 停止 |
|
||||
| 平台分派 | `client/lib/bridge/vpn_bridge_provider.dart` | Windows → `DesktopVpnBridge` |
|
||||
| Flutter runner 脚手架 | `client/windows/runner/`、`main.cpp` | 已写,默认窗口 1280×720 |
|
||||
| UAC manifest | `client/windows/runner/runner.exe.manifest` | `requireAdministrator`(sing-box 子进程继承管理员权建 TUN) |
|
||||
| CMake 打包内核 | `client/windows/CMakeLists.txt` | install 步骤把 `sing-box.exe`+`wintun.dll` 拷到 exe 同级 |
|
||||
| 二进制拉取脚本 | `app/kernel/fetch-desktop-bin.sh` | `windows amd64`:sing-box.exe + wintun 0.14.1,SHA256 pin |
|
||||
| UI / 页面 | `client/lib/shell/`、`screens/`、`widgets/` | 跨平台,复用 desktop shell(见 §4) |
|
||||
| 系统托盘 / 单实例 | `client/lib/system_tray.dart` | 跨平台,已声明支持 Windows |
|
||||
|
||||
### 从未做的部分(本方案目标)
|
||||
|
||||
- ❌ 在真 Windows 机器上 `flutter build windows` / `flutter run`。
|
||||
- ❌ 端到端连通验证(登录→选节点→connect→出网 IP 为节点 IP)。
|
||||
- ❌ Inno Setup 安装包。
|
||||
|
||||
### 明确不做(本轮范围外)
|
||||
|
||||
- 代码签名(EV/OV 证书)。
|
||||
- Windows CI job。
|
||||
- 免 UAC 的特权 helper 服务(类比 macOS SMJobBless)→ 列入 BACKLOG。
|
||||
- frameless 自定义标题栏 → 本轮用原生标题栏。
|
||||
|
||||
## 2. 决策记录(来自本次 brainstorming)
|
||||
|
||||
1. **测试环境**:用户有实体 Windows 机器;Claude 在 macOS 改代码并给逐条 PowerShell 指令,用户真机执行并回贴日志,循环迭代。
|
||||
2. **深度**:MVP bring-up + Inno Setup 安装包。
|
||||
3. **数据面架构**:沿用「整 app UAC 提权 + sing-box.exe 子进程继承管理员权 + wintun TUN 全局代理」的子进程方案(`DesktopVpnBridge`),**不**改 native helper。
|
||||
4. **UI**:复用现有 `DesktopShell`,无 Windows 专属页面设计。
|
||||
5. **窗口标题栏**:原生 Windows 标题栏(系统最小/最大/关闭按钮),app 内 `ContentTopBar` 位于其下。
|
||||
|
||||
## 3. 数据面架构(沿用既有)
|
||||
|
||||
```
|
||||
Flutter app (pangolin_vpn.exe, requireAdministrator)
|
||||
└─ DesktopVpnBridge (Dart) ── 写 config_*.json → %LOCALAPPDATA%\Pangolin\kernel\
|
||||
└─ DesktopKernelProcess ── Process.start("sing-box.exe", run -c config)
|
||||
├─ sing-box.exe ── 加载同目录 wintun.dll → 建 TUN → 全局代理
|
||||
└─ Clash API (127.0.0.1:随机端口) ← Dart 轮询状态/流量/切节点/kill-switch
|
||||
```
|
||||
|
||||
与 macOS 的差异:macOS 走原生 System Extension(`kUseNativeVpnMacOS`),Windows 本轮继续用子进程 + UAC。两者共用 `DesktopVpnBridge` 之外的所有上层(UI / 状态 / API)。
|
||||
|
||||
## 4. UI / 页面方案:复用 desktop shell,零新设计
|
||||
|
||||
`core/responsive/form_factor.dart` 按平台 + 窗口宽度判形态:Windows 为桌面平台,窗口宽 ≥680 → `DesktopShell`,与 macOS 桌面**同一份代码、同一套设计 token**(`design/colors_and_type.css` 单源)。
|
||||
|
||||
```
|
||||
┌─ 原生标题栏 ───────────────── – □ × ┐
|
||||
├─────────┬───────────────────────────┤
|
||||
│ 侧栏 204 │ ContentTopBar (52, 标题/返回) │
|
||||
│ 6 项导航 ├───────────────────────────┤
|
||||
│ ·连接 │ 内容区:Connect / Nodes / │
|
||||
│ ·节点 │ Stats / Account / Contact / │
|
||||
│ ·统计 │ Settings(+账户下钻子页) │
|
||||
│ ·我的 │ │
|
||||
│ ·联系 │ │
|
||||
│ ·设置 │ │
|
||||
└─────────┴───────────────────────────┘
|
||||
```
|
||||
|
||||
页面内容、配色、字体全部来自单源,**Windows 不单独画页面**。系统托盘 + 关闭即最小化 + 单实例锁已是跨平台代码。
|
||||
|
||||
本轮属于 Windows 的窗口层调整(小工作量,并入 Phase 1):
|
||||
|
||||
- **窗口尺寸**:默认 920×640,最小宽度 720(低于 680 会掉进 `MobileShell` 致侧栏消失)。在 `system_tray.dart` 的 `windowManager.ensureInitialized()` 后用 `setMinimumSize` / 默认尺寸设定,或在 `main.cpp` 调整初始尺寸并设最小尺寸。
|
||||
- **字体(决定:打包,全平台离线)**:放弃 `google_fonts` 运行时拉取,改本地打包 ttf,生产置 `useBundled=true`(影响全平台,顺带消除 macOS 联网拉字体隐患)。
|
||||
- 拉丁三件套全量变量字体:Sora ~0.3MB + Manrope ~0.15MB + JetBrains Mono ~0.2MB(合计 <1MB)。
|
||||
- **Noto Sans SC 取 7000 字子集**(通用规范汉字表,用 `fonttools pyftsubset` 生成)≈ 1.5–2.5MB,覆盖几乎全部 UI 文案 + 节点名。
|
||||
- 配 `fontFamilyFallback` 兜到 Windows 自带微软雅黑(macOS 兜苹方),子集外生僻字不显示豆腐块。
|
||||
- 总增量 ≈ 2–3MB。落地:ttf 入 `client/fonts/` → 恢复 `pubspec.yaml` 的 `fonts:` 段(4 family)→ `useBundled` 改为生产默认 true → 主题设 `fontFamilyFallback`。
|
||||
- **托盘/应用图标**:`assets/tray_icon.png` + `app_icon.ico`(已有),真机验证显示即可。
|
||||
- **标题栏**:保持原生,不做 frameless。
|
||||
- **DPI**:manifest 已 PerMonitorV2,真机验证高分屏不糊/不溢出。
|
||||
|
||||
## 5. 已识别的构建风险(真机迭代时盯)
|
||||
|
||||
1. **`jni` FFI 插件**:`client/windows/flutter/generated_plugins.cmake` 的 `FLUTTER_FFI_PLUGIN_LIST` 含 `jni`,对 Windows 可疑,可能编译失败 → 先查来源(哪个依赖传递引入),必要时确认其 Windows 支持或移除该依赖。
|
||||
2. **`/WX`(warnings as errors)+ `_HAS_EXCEPTIONS=0`**:插件源码有告警即 fail,可能需 per-target 放宽。
|
||||
3. **插件 Windows 兼容性**:`tray_manager` / `window_manager` / `flutter_secure_storage_windows` / `screen_retriever_windows` / `launch_at_startup` 在当前锁定版本下需能编过、运行正常。
|
||||
4. **内核二进制下载**:`fetch-desktop-bin.sh` 依赖 wintun.net 与 sing-box GitHub release 可达,国内网络可能需代理。
|
||||
5. **`path_provider` 目录一致性**:代码硬编码 `%LOCALAPPDATA%\Pangolin\kernel`,验证与运行时实际目录一致、可写。
|
||||
|
||||
## 6. 工作分解
|
||||
|
||||
### Phase 1 — MVP bring-up(真机跑通端到端)
|
||||
|
||||
1. **macOS 侧预检**(Claude 可独立做):`flutter pub get`、`flutter analyze`、`dart analyze` 确认无编译期/分析错误;查 `jni` 来源,必要时从 Windows 插件列表处理。
|
||||
2. **窗口层调整**:默认窗口尺寸 920×640 + 最小宽 720。
|
||||
3. **真机构建**(用户执行,Claude 给指令):装 Flutter + Visual Studio(C++ 桌面负载)→ `app/kernel/fetch-desktop-bin.sh windows amd64` → 管理员终端 `flutter build windows` / `flutter run -d windows --dart-define=PANGOLIN_API_URL=...`。
|
||||
4. **按报错迭代**:用户贴 build/运行日志,Claude 改代码,循环到 build 通过。
|
||||
5. **端到端连通**:登录 → 选节点 → connect → `curl https://api.ipify.org` 出口为节点公网 IP。
|
||||
6. **功能验证**:停止、自动重连、kill-switch、托盘(关闭最小化/显示/退出)、开机自启、UAC 单次弹窗、退出时内核拆隧道无残留 TUN/孤儿进程。
|
||||
7. **字体打包**(见 §4,全平台生效):拉丁三件套全量 + Noto Sans SC 7000 字子集(`pyftsubset`)入 `client/fonts/`,恢复 pubspec `fonts:` 段,生产置 `useBundled=true`,主题设 `fontFamilyFallback`(Windows 微软雅黑 / macOS 苹方)。增量 ≈ 2–3MB。
|
||||
|
||||
### Phase 2 — Inno Setup 安装包
|
||||
|
||||
8. 写 `client/windows/installer/pangolin.iss`:
|
||||
- 源:`build\windows\x64\runner\Release\`(含 `pangolin_vpn.exe` + Flutter 运行时 + `sing-box.exe` + `wintun.dll` + `data\`)。
|
||||
- 装到 `Program Files\Pangolin`。
|
||||
- 开始菜单 + 桌面快捷方式(启动项继承 exe 的 `requireAdministrator`)。
|
||||
- 卸载清理 `%LOCALAPPDATA%\Pangolin`。
|
||||
9. 文档化:更新 `client/windows/README.md` 增加打包/出安装包步骤。
|
||||
|
||||
### 打包方案选型
|
||||
|
||||
- **A) Inno Setup**(采纳):免费、脚本化、最主流,与「含安装包」一致。
|
||||
- B) MSIX:与 `requireAdministrator` 沙箱冲突,不合适。
|
||||
- C) 绿色 zip:无开始菜单/卸载,不满足需求。
|
||||
|
||||
## 7. 协作模式
|
||||
|
||||
- Claude 无法在 macOS 构建 Windows:所有真机步骤提供逐条可复制的 PowerShell 指令;用户回贴报错/日志 → Claude 改代码 → 循环。
|
||||
- 主要不确定性是插件 Windows 编译兼容性(尤其 `jni`),可能需数轮迭代。
|
||||
|
||||
## 8. 验收标准
|
||||
|
||||
- [ ] `flutter build windows`(Release)在真机成功,无报错。
|
||||
- [ ] 运行后单次 UAC 提权,主界面为 `DesktopShell`(原生标题栏 + 侧栏 + 内容区),无布局溢出。
|
||||
- [ ] 登录 → 选节点 → connect 成功,`curl https://api.ipify.org` 返回节点公网 IP。
|
||||
- [ ] 停止 / 重连 / kill-switch / 托盘最小化与退出 / 开机自启均正常;退出无残留 TUN 适配器与孤儿 sing-box 进程。
|
||||
- [ ] Inno Setup 安装包可安装、生成快捷方式、运行、卸载干净。
|
||||
Reference in New Issue
Block a user