diff --git a/client/lib/bridge/desktop_vpn_bridge.dart b/client/lib/bridge/desktop_vpn_bridge.dart index 7656ace..ee5b6ab 100644 --- a/client/lib/bridge/desktop_vpn_bridge.dart +++ b/client/lib/bridge/desktop_vpn_bridge.dart @@ -213,8 +213,8 @@ class DesktopVpnBridge implements VpnBridge { _reconnectTimer = null; _kernelStatusSub?.cancel(); _kernelStatusSub = null; - if (_kernel is DesktopKernelProcess) { - (_kernel as DesktopKernelProcess).dispose(); + if (_kernel case final DesktopKernelProcess k) { + k.dispose(); } } @@ -350,7 +350,7 @@ class DesktopVpnBridge implements VpnBridge { Future _resolveConfigDir() async { if (_configDirOverride != null) { - return Directory(_configDirOverride!); + return Directory(_configDirOverride); } return _defaultConfigDir(); } diff --git a/client/lib/bridge/vpn_bridge_mock.dart b/client/lib/bridge/vpn_bridge_mock.dart index 3cc075a..3f5ba7f 100644 --- a/client/lib/bridge/vpn_bridge_mock.dart +++ b/client/lib/bridge/vpn_bridge_mock.dart @@ -121,6 +121,9 @@ class VpnBridgeMock implements VpnBridge { _killSwitch = on; } + /// 测试可读:mock 记录的 kill-switch 状态。 + bool get killSwitchEnabled => _killSwitch; + @override Stream get statusStream => _statusCtrl.stream; diff --git a/client/lib/widgets/nav_sidebar.dart b/client/lib/widgets/nav_sidebar.dart index 83e310b..6679353 100644 --- a/client/lib/widgets/nav_sidebar.dart +++ b/client/lib/widgets/nav_sidebar.dart @@ -8,7 +8,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../pangolin_theme.dart'; import '../state/app_providers.dart'; import '../state/navigation_provider.dart'; -import 'pangolin_icons.dart'; import 'pangolin_logo.dart'; import 'plan_badge_card.dart'; diff --git a/client/macos/Flutter/GeneratedPluginRegistrant.swift b/client/macos/Flutter/GeneratedPluginRegistrant.swift index 16bb469..62fded1 100644 --- a/client/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/client/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,10 +7,16 @@ import Foundation import flutter_secure_storage_macos import package_info_plus +import screen_retriever_macos import shared_preferences_foundation +import tray_manager +import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) + ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) + WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) } diff --git a/client/windows/README.md b/client/windows/README.md index c9896ea..af9347a 100644 --- a/client/windows/README.md +++ b/client/windows/README.md @@ -49,10 +49,23 @@ flutter run -d windows --dart-define=PANGOLIN_API_URL=http://<后端IP>:8080 ## 打包(release) +**推荐:一键脚本**(拉内核 → 带 dart-define 构建 → 出安装包,自动避开漏 `--dart-define` 的坑): + +```powershell +powershell -ExecutionPolicy Bypass -File client\windows\build.ps1 -ApiUrl http://<后端IP>:8080 +# 只构建不出安装包:加 -SkipInstaller +``` + +或手动分步: + ```powershell cd client -flutter build windows +# 必须带 --dart-define 指定后端,否则默认连 localhost:8080,登录会「网络请求失败」 +flutter build windows --dart-define=PANGOLIN_API_URL=http://<后端IP>:8080 ``` +> ⚠️ `kApiBaseUrl` 默认值是 `http://localhost:8080`(见 `lib/services/api_config.dart`)。 +> Release 包必须显式 `--dart-define=PANGOLIN_API_URL=...`,构建期烘进二进制;漏了会复现登录失败。 + CMake 的 install 步骤会自动把 `app/kernel/dist/desktop/windows-*/{sing-box.exe, wintun.dll}` 拷到 app `.exe` 同级目录(见 `windows/CMakeLists.txt` 的「Pangolin sing-box kernel」段)。 **wintun.dll 必须与 sing-box.exe 同目录**,否则 sing-box 无法创建 TUN。 diff --git a/client/windows/build.ps1 b/client/windows/build.ps1 new file mode 100644 index 0000000..75a3210 --- /dev/null +++ b/client/windows/build.ps1 @@ -0,0 +1,97 @@ +<# +.SYNOPSIS + Pangolin Windows 客户端一键构建脚本(拉内核 → 带 dart-define 构建 → 出安装包)。 + +.DESCRIPTION + 串起 client/windows/README.md 里的三步手动流程,避免漏掉 --dart-define + (漏了会默认连 localhost:8080,登录「网络请求失败」)。 + + 步骤: + 1. bash app/kernel/fetch-desktop-bin.sh windows (拉 sing-box.exe + wintun.dll,SHA 校验) + 2. flutter build windows --release --dart-define=PANGOLIN_API_URL= + 3. ISCC.exe installer\pangolin.iss (除非 -SkipInstaller) + +.PARAMETER ApiUrl + 必填。后端地址,会在构建期烘进二进制,例:http://103.119.13.48:8080 + +.PARAMETER Arch + CPU 架构,默认 amd64。 + +.PARAMETER SkipInstaller + 只构建,不出 Inno Setup 安装包。 + +.EXAMPLE + powershell -ExecutionPolicy Bypass -File client\windows\build.ps1 -ApiUrl http://103.119.13.48:8080 +#> +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string]$ApiUrl, + + [string]$Arch = "amd64", + + [switch]$SkipInstaller +) + +$ErrorActionPreference = "Stop" + +# 路径解析:本脚本在 client/windows/ 下 +$WindowsDir = $PSScriptRoot +$ClientDir = Split-Path -Parent $WindowsDir +$RepoRoot = Split-Path -Parent $ClientDir + +function Step([string]$msg) { Write-Host "`n==> $msg" -ForegroundColor Cyan } + +# ── 1. 拉内核(sing-box.exe + wintun.dll)───────────────────────────────────── +Step "拉取 sing-box.exe + wintun.dll (windows $Arch)" +$bash = Get-Command bash -ErrorAction SilentlyContinue +if (-not $bash) { + throw "未找到 bash(fetch 脚本需 Git Bash)。请安装 Git for Windows 后重试。" +} +$fetchScript = Join-Path $RepoRoot "app/kernel/fetch-desktop-bin.sh" +& $bash.Source $fetchScript windows $Arch +if ($LASTEXITCODE -ne 0) { throw "内核拉取失败 (exit $LASTEXITCODE)" } + +# ── 2. Flutter 构建(必须带 dart-define)────────────────────────────────────── +Step "flutter build windows --release (PANGOLIN_API_URL=$ApiUrl)" +Push-Location $ClientDir +try { + flutter build windows --release --dart-define=PANGOLIN_API_URL=$ApiUrl + if ($LASTEXITCODE -ne 0) { throw "flutter build 失败 (exit $LASTEXITCODE)" } +} +finally { + Pop-Location +} + +$releaseDir = Join-Path $ClientDir "build\windows\x64\runner\Release" +Write-Host "构建产物:$releaseDir" -ForegroundColor Green + +# ── 3. 出安装包(Inno Setup)────────────────────────────────────────────────── +if ($SkipInstaller) { + Step "跳过安装包(-SkipInstaller)" + return +} + +Step "编译 Inno Setup 安装包" +$iscc = $null +foreach ($p in @( + "C:\Program Files (x86)\Inno Setup 6\ISCC.exe", + "C:\Program Files\Inno Setup 6\ISCC.exe")) { + if (Test-Path $p) { $iscc = $p; break } +} +if (-not $iscc) { + $cmd = Get-Command ISCC.exe -ErrorAction SilentlyContinue + if ($cmd) { $iscc = $cmd.Source } +} +if (-not $iscc) { + Write-Warning "未找到 ISCC.exe(Inno Setup 6 未安装)。已完成构建,跳过安装包。" + Write-Warning "装好 Inno Setup 6 后可手动:ISCC.exe `"$WindowsDir\installer\pangolin.iss`"" + return +} + +$iss = Join-Path $WindowsDir "installer\pangolin.iss" +& $iscc $iss +if ($LASTEXITCODE -ne 0) { throw "ISCC 编译失败 (exit $LASTEXITCODE)" } + +$output = Join-Path $WindowsDir "installer\Output" +Write-Host "`n✓ 全部完成。安装包在:$output" -ForegroundColor Green diff --git a/client/windows/flutter/generated_plugin_registrant.cc b/client/windows/flutter/generated_plugin_registrant.cc index 0c50753..a55a983 100644 --- a/client/windows/flutter/generated_plugin_registrant.cc +++ b/client/windows/flutter/generated_plugin_registrant.cc @@ -7,8 +7,17 @@ #include "generated_plugin_registrant.h" #include +#include +#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); + ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); + TrayManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("TrayManagerPlugin")); + WindowManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowManagerPlugin")); } diff --git a/client/windows/flutter/generated_plugins.cmake b/client/windows/flutter/generated_plugins.cmake index d0b33f8..6adb6fa 100644 --- a/client/windows/flutter/generated_plugins.cmake +++ b/client/windows/flutter/generated_plugins.cmake @@ -4,6 +4,9 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_secure_storage_windows + screen_retriever_windows + tray_manager + window_manager ) list(APPEND FLUTTER_FFI_PLUGIN_LIST