5c9731f3df
brainstorming 定稿:真机测试 / MVP+Inno Setup 深度 / 子进程+UAC 数据面 / 复用 DesktopShell 原生标题栏 / 拉丁全量+Noto Sans SC 7000字子集打包。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.8 KiB
9.8 KiB
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)
- 测试环境:用户有实体 Windows 机器;Claude 在 macOS 改代码并给逐条 PowerShell 指令,用户真机执行并回贴日志,循环迭代。
- 深度:MVP bring-up + Inno Setup 安装包。
- 数据面架构:沿用「整 app UAC 提权 + sing-box.exe 子进程继承管理员权 + wintun TUN 全局代理」的子进程方案(
DesktopVpnBridge),不改 native helper。 - UI:复用现有
DesktopShell,无 Windows 专属页面设计。 - 窗口标题栏:原生 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. 已识别的构建风险(真机迭代时盯)
jniFFI 插件:client/windows/flutter/generated_plugins.cmake的FLUTTER_FFI_PLUGIN_LIST含jni,对 Windows 可疑,可能编译失败 → 先查来源(哪个依赖传递引入),必要时确认其 Windows 支持或移除该依赖。/WX(warnings as errors)+_HAS_EXCEPTIONS=0:插件源码有告警即 fail,可能需 per-target 放宽。- 插件 Windows 兼容性:
tray_manager/window_manager/flutter_secure_storage_windows/screen_retriever_windows/launch_at_startup在当前锁定版本下需能编过、运行正常。 - 内核二进制下载:
fetch-desktop-bin.sh依赖 wintun.net 与 sing-box GitHub release 可达,国内网络可能需代理。 path_provider目录一致性:代码硬编码%LOCALAPPDATA%\Pangolin\kernel,验证与运行时实际目录一致、可写。
6. 工作分解
Phase 1 — MVP bring-up(真机跑通端到端)
- macOS 侧预检(Claude 可独立做):
flutter pub get、flutter analyze、dart analyze确认无编译期/分析错误;查jni来源,必要时从 Windows 插件列表处理。 - 窗口层调整:默认窗口尺寸 920×640 + 最小宽 720。
- 真机构建(用户执行,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=...。 - 按报错迭代:用户贴 build/运行日志,Claude 改代码,循环到 build 通过。
- 端到端连通:登录 → 选节点 → connect →
curl https://api.ipify.org出口为节点公网 IP。 - 功能验证:停止、自动重连、kill-switch、托盘(关闭最小化/显示/退出)、开机自启、UAC 单次弹窗、退出时内核拆隧道无残留 TUN/孤儿进程。
- 字体打包(见 §4,全平台生效):拉丁三件套全量 + Noto Sans SC 7000 字子集(
pyftsubset)入client/fonts/,恢复 pubspecfonts:段,生产置useBundled=true,主题设fontFamilyFallback(Windows 微软雅黑 / macOS 苹方)。增量 ≈ 2–3MB。
Phase 2 — Inno Setup 安装包
- 写
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。
- 源:
- 文档化:更新
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 安装包可安装、生成快捷方式、运行、卸载干净。