diff --git a/docs/superpowers/specs/2026-06-21-windows-client-design.md b/docs/superpowers/specs/2026-06-21-windows-client-design.md new file mode 100644 index 0000000..5b2fe68 --- /dev/null +++ b/docs/superpowers/specs/2026-06-21-windows-client-design.md @@ -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 安装包可安装、生成快捷方式、运行、卸载干净。