wangjia
|
aee9ba7b72
|
feat(client): 登录体验 — 记住邮箱 + 密码显隐 + 7天免登陆
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
1. 记住邮箱:登录页预填上次邮箱(TokenStore.saveLastEmail,登录/注册成功时存,
退出登录不清除)。
2. 密码显隐:登录/注册密码框加眼睛按钮切换明文(PangolinIcons.eye/eyeOff)。
3. 7天免登陆:RootFlow 改为响应 authProvider——启动时有有效会话直接进主界面;
AuthNotifier.refresh() 在服务端拒绝(refresh 过期/超 7 天)时 logout 回登录页,
网络错误则保留会话。会话恢复期间显示极简启动屏。
flutter analyze 0 error;114 tests passed。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-19 06:28:49 +08:00 |
|
wangjia
|
f35bbea0a0
|
fix(client): 连接后延迟用内核 urltest 实测 + 侧栏套餐到期日接真
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
- 延迟:TCP 探针在隧道开启时会被路由进隧道而测不到(显示 —)。改为连接后
(phase==on)取内核 statsStream 的 urltest delayMs 最小正值(代理真实 RTT),
未连接时仍用节点 TCP 探针。连接页 _SpeedRow/_NodePill/_CurrentNodeCard
与统计页平均延迟统一此口径。
- plan_badge_card(侧栏)接 me.expiresAt 显示真实到期日(原只显示「有效期至」
无日期);account_page 之外漏改的最后一处。
flutter analyze 0 error;114 tests passed。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-19 00:44:46 +08:00 |
|
wangjia
|
93ec421c41
|
fix(plans): /v1/plans 500 修复 — plans 表无 name_zh/name_en 列
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
ListPlans 历来 SELECT name_zh/name_en,但 plans 表从未有这两列(各迁移/seed
均无)→ 该端点一直 500(无 live 测试故未暴露)。改为不查名称列(套餐名是 UI
文案,客户端 PlansScreen 按 code 取 l10n:free/pro/team)。价格等真实字段保留。
已在节点实测:/v1/plans 200,返回 free ¥0 / pro ¥25 / team ¥99。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-19 00:32:40 +08:00 |
|
wangjia
|
6ee7114ff8
|
feat(client): P6 设置项落地真实行为(#6 6F,本地部分)
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
- settings_provider:三开关 shared_preferences 持久化 + 副作用落地:
killSwitch→VpnBridge.setKillSwitch(on:);autostart→桌面原生登录项
(launch_at_startup);smartRoute 持久化为偏好(连接时作 split_cn,见 #5)。
- settings_page → ConsumerWidget,开关接 settingsProvider;协议标签
WireGuard→REALITY/Hysteria2;版本号取 pubspec(package_info_plus)。
- 新依赖:shared_preferences / package_info_plus / launch_at_startup。
flutter analyze 0 error;114 tests passed;settings golden 重生成。
待办:#5 智能分流的 server 端 geoip-cn 下发 + autostart 真机验证(需 release .app)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-19 00:13:41 +08:00 |
|
wangjia
|
ce5155d4ca
|
feat(client): P5 连接页实时速度 + 连接错误冒泡 UI(#6 6E,并入 #4)
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
- vpnStatsProvider:暴露内核 statsStream(上/下行瞬时速率);连接页速度行
下/上行改用实时数据(B/s→Mb/s),不再硬编码 86.4/12.1;延迟用实测节点 ping。
- ConnectionState 加 error 字段;_connect 失败不再静默回 off,而是按语言
冒泡 ConnectApiException 文案;节点未就绪(无 uuid)给出提示并删除旧 1.2s
mock 连接路径。
- 连接页 caption 下/桌面 pill 上方显示错误文案(红)。
- 重写 connection_controller_test 适配(无网络:未就绪→off+error)。
flutter analyze 0 error;114 tests passed。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-19 00:09:31 +08:00 |
|
wangjia
|
f20eddad55
|
feat(client): P4 节点实测延迟/设备/套餐价/兑换/统计接真(#6 6D)
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
- latency_probe.dart:并行 TCP 握手实测各节点 per-client RTT(服务端无法代知)。
- nodes_provider:解析 /v1/nodes 的 host/port,后台测延迟回填 Node.ping;智能选择
按实测最小;去掉 8 个伪造演示节点(空列表用中性占位,不再伪造服务器)。
- Node:+host/port/copyWith/pingLabel(未测显示 —);各端 ping 显示改 pingLabel。
- account_screens:PlansScreen 接 /v1/plans(价格 priceLabel + me 标当前档);
DevicesScreen 接 /v1/me/devices + 移除 DELETE + last_seen;RedeemScreen 接
/v1/redeem(成功刷新 me,失败显示后端文案)。
- stats_page:周柱接 me.weekly_gb,本月流量/时长接 usage(30),延迟用生效节点实测。
- 顺带补登记早前 log_time.dart 的删除(log.dart 重构遗漏 stage)。
flutter analyze 0 error;115 tests passed;受影响 4 golden 重生成。
本机设备高亮 + 真实 device id 随 P6(device_info_plus)落地。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-19 00:04:25 +08:00 |
|
wangjia
|
bb39b36d84
|
feat(client): P3 账号/套餐视角/配额接真(#6 6C)
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
- account_providers:新增 meProvider(GET /v1/me,登录态变化自动重取)、
plansProvider、usageProvider(family by days)。
- isFreePlanProvider 改为派生自 meProvider.plan(去掉可写演示开关)。
- quotaProvider:总额取 plans free.daily_minutes,今日剩余取 me.quota_today_min
(后端已算好);adUnlocked 保留本地态(ad SDK 未接)。
- account_page:邮箱/套餐/到期接 me;删「演示:免费版视角」开关 + kDemoEmail;
协议标签 WireGuard→REALITY/Hysteria2。
- 重写 quota_controller_test 为 provider 驱动;account/quota golden 重生成。
flutter analyze 0 error;113 tests passed。统计页(weekly/月流量/延迟)随 P4
一并接(其均延迟依赖 P4 实测探针)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 23:51:28 +08:00 |
|
wangjia
|
ba53a0d478
|
feat(client): P1 真实数据接入地基(#6 6A)
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
- api_config.dart:API 基址单源(消除各处重复 _kApiUrl)
- api_client.dart:统一鉴权 HTTP 客户端,401→AuthNotifier.refresh→重试一次,
错误统一 AuthApiException;+ 单测覆盖 401 刷新重试
- 模型 me/device/plan/usage_point(对齐后端 snake_case 契约)
- account_api.dart:/v1/me、/v1/plans、/v1/me/devices(列表+删)、/v1/usage、
/v1/redeem、/v1/ads/unlock 封装
- auth_provider:注入 AuthApi + refresh();account_providers 装配 ApiClient/AccountApi
- 顺带修复 onboarding 提交遗留的测试 stub(_NullTokenStore 缺 isOnboarded/markOnboarded)
flutter analyze 0 error;114 tests passed。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 23:32:57 +08:00 |
|
wangjia
|
b9e5dbec9b
|
refactor(client): 抽统一日志函数 logLine + 去掉重复时间戳
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
- log_time.dart → log.dart:提供 logLine(tag, msg),格式 `[时:分:秒.毫秒] [tag] msg`,
DesktopKernel / DesktopVpnBridge 散落的 print+拼接全部收敛到此。
- 下发配置 log.timestamp=false:sing-box 不再自带时间戳,避免一行打印两个时间
(之前 `[22:10:35.213] [stderr] +0800 2026-06-18 22:10:35 ...` 重复)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 22:17:09 +08:00 |
|
wangjia
|
df75d3a135
|
feat(client): 桌面端日志加本地时间戳前缀 [HH:MM:SS.mmm]
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
新增 log_time.dart(共享 logTime() helper),DesktopKernel / DesktopVpnBridge
所有日志行首打 [时:分:秒.毫秒],便于排查连接时序。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 21:56:38 +08:00 |
|
wangjia
|
df72ad60f4
|
fix: DNS 用 sing-box 1.12+ 新格式 + 引导仅首次显示
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
- clientconfig/mockserver:客户端配置 DNS 从 legacy address 串格式改为
type+server 新格式(sing-box 1.13 对 legacy DNS 直接 FATAL,导致连不上)
- 引导页只首次:TokenStore 加 markOnboarded/isOnboarded;main.dart 登录后
若已引导过直接进主界面,不再每次都弹引导
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 21:20:11 +08:00 |
|
wangjia
|
25c7bd75aa
|
feat(client+scripts): release 本地真测 — local_test.sh + 内核查找加 /opt/homebrew
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
- scripts/local_test.sh:一键构建 release macOS 包 + 配 sing-box 免密 sudoers
(TUN 需 root)+ 前台运行真身(日志直出),让本地测试 = release 线上行为
- kernel_process 二进制查找兜底加 /opt/homebrew/bin/sing-box(Apple Silicon
brew),release 包从 Finder/直接启动也能找到内核,免设 PANGOLIN_SINGBOX_BIN
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 21:09:08 +08:00 |
|
wangjia
|
d341c1c6e5
|
fix(client/auth): 验证码字段去掉点状占位(hintText 改空)
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
'······' 占位被误以为是已填/遮蔽;验证码本就明文输入,清空占位更清晰。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 09:23:12 +08:00 |
|
wangjia
|
748a3b8a45
|
chore(client/macos): bundle id 改为 com.pangolin.pangolin
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
去掉 Vpn 后缀:macOS 主 app + RunnerTests + Debug/Release entitlements 的
keychain-access-groups 统一为 com.pangolin.pangolin(便于 Apple 签名注册)。
iOS/Android 暂未改(iOS 涉 App Group + PacketTunnel 扩展,需统一时单独处理)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-18 08:57:35 +08:00 |
|
wangjia
|
caeef20df3
|
feat(client): 新增 Windows 桌面端(sing-box TUN 全局代理)
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / Unit Tests — nginx cfg + compose (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
ci-pangolin / Image Build — pangolin-edge (push) Has been cancelled
Windows 与 macOS 桌面同路线:Flutter 应用 + sing-box.exe 子进程 + wintun TUN
接管整机流量。Dart/桥接层此前已 Windows-ready(kernel_process._buildCommand /
_resolveBinaryPath、desktop_vpn_bridge 配置路径、fetch-desktop-bin.sh),本次补
原生外壳 + 提权 + 打包:
- flutter create --platforms=windows 生成 windows/ runner(flutter_secure_storage
已注册 windows 插件,token 存储可用)。
- runner.exe.manifest 设 requestedExecutionLevel=requireAdministrator:启动 UAC
提权,sing-box 子进程继承管理员权以创建 wintun TUN(与 _buildCommand 既有设计一致)。
- windows/CMakeLists.txt 增 install 规则:打包时把 app/kernel/dist/desktop/windows-*/
{sing-box.exe,wintun.dll} 拷到 app exe 同级目录(存在才拷,缺失告警,类比 android
build.gradle 占位)。
- kernel_process.dart 启动时校验 wintun.dll 与 sing-box.exe 同目录,缺失早警。
- windows/README.md:拉内核二进制 / 管理员终端 flutter run / 打包 / 出网验证步骤。
提权用 requireAdministrator(每次 UAC),正式版可换特权 helper(BACKLOG,类比 SMJobBless)。
本机(macOS)验证:flutter analyze 无 error。Windows 真机 build/连通验证待后续
(无 Windows 机器,状态同 Android/iOS:代码就绪、未真机验证)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-17 07:49:26 +08:00 |
|
wangjia
|
bcee7f92ed
|
merge: maestro/tsk__bm21nctbhWF into main (bulk integration)
# Conflicts:
# client/lib/widgets/auth_screen.dart
|
2026-06-17 00:47:54 +08:00 |
|
wangjia
|
9f2f96d722
|
merge: maestro/tsk_xAQhC1xuCd8x into main (bulk integration)
|
2026-06-17 00:41:30 +08:00 |
|
wangjia
|
6979411c1d
|
feat: 档2 接真后端 — mac app 真注册/登录/拉节点
dev 本机置备(新增 dev/):
- docker-compose.yml: MySQL :13306 + Redis :16379(避让本机已占的 3306/6379)
- run-local.sh 一键: 起容器→openssl 生成密钥(dev/.local gitignore)→migrate→
seed→启动 server :18080(避让 8080);LogMailer 把验证码打日志
- seed.sql(1 provider + 1 HK 节点)、run-local.md 手册、.gitignore
后端两处修复(本地无 gRPC 场景,注释意图与代码不符的 bug):
- main.go: 无 gRPC 时真正构造 Hub-only nodeSvc(原只建 hub 没赋值),/v1/nodes 才能挂
- main.go: /me 改 Route 子路由根 Get,修 Get("/me")+Route("/me") 冲突致 404
客户端接真后端:
- token_store: MacOsOptions(useDataProtectionKeyChain:false) 修 keychain -34018
- auth_screen: 删 dev 旁路(test 账户内存登录),所有登录走真 API
验收: 后端 curl 端到端全通(验证码→注册→登录→/nodes→/me);
server go build/vet/test 通过, client analyze 0 + test 84
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-16 13:27:58 +08:00 |
|
wangjia
|
f3cc2dfc2d
|
fix(client): 去 dev 旁路 + 修 macOS keychain -34018 [tsk__bm21nctbhWF]
1. 去 dev 旁路:删除 auth_screen.dart 中 test@pangolin.dev 预填 + devLogin
跳过后端分支,以及 auth_provider.dart 的 devLogin() 方法。
2. 修 keychain -34018(errSecMissingEntitlement):
在 DebugProfile.entitlements / Release.entitlements 中添加
keychain-access-groups($(AppIdentifierPrefix)com.pangolin.pangolinVpn),
使 flutter_secure_storage 的 Data Protection Keychain API 获得正确 entitlement。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-16 13:21:38 +08:00 |
|
wangjia
|
fe16f89bc3
|
feat(client): 账户子页 desktop 内容区下钻(档1)
- desktop 上套餐选择/兑换购买/设备管理改为内容区下钻(侧栏保留、shell 顶栏带
返回箭头),不再全屏 push 盖住侧栏;mobile/tablet 仍全屏 push
- _SubScaffold 加 embedded 参数(true 只返回内容),PlansScreen/DevicesScreen/
RedeemScreen 透传;NavView 加 devices + kAccountSubViews 集合
- desktop_shell 渲染子页(embedded) + 顶栏 onBack→account;account_page 按
formFactor 选下钻(切 navView)或 push
- desktop 整页 golden 补 套餐/兑换/设备 三子页(共 8 页)
测试: flutter test 84 通过(+3 子页 golden), analyze 0 error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-16 12:36:14 +08:00 |
|
wangjia
|
4886416e8b
|
feat(client): settings 完整版(开关组) + 联系/设置整页 golden
- settings_page 补全对照 dapp.jsx DSettings: 功能开关组(开机自启/智能分流/
Kill Switch, 本地演示态) + 配置组补检查更新行; 改 ConsumerStatefulWidget 持开关态
- l10n 新增 autostart/smartRoute/killSwitch(+Sub) + checkUpdate(中英)
- desktop 整页 golden 补 联系页 + 设置页(凑齐可截 5 页: 节点/统计/账户/联系/设置)
测试: flutter test 81 通过(+2 golden), analyze 0 error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-16 12:22:28 +08:00 |
|
wangjia
|
22e4eb1ead
|
feat(client): desktop 其他页精还原 + 整页 golden 闸
- stats_page 对照 dapp.jsx DStats 精对齐: padding 32/8、指标卡 padding 16/18·
gap 14·值 mono 24、柱状图卡 padding 20/18·标题 13、柱 height 140(容 bar 86)·宽 34
- nodes_page 对照 DServers 微调: padding top 4、网格 gap 10(智能卡置顶保留, 符 §5)
- 新增 test/golden/desktop_pages_golden_test.dart: 用 macOS 平台覆盖 + 920×600 surface
让 formFactor=desktop, 截 节点/统计/账户 三页整页 golden 作回归闸(连接页含旋转动画
由组件级 golden 覆盖, 不在此截)
测试: flutter test 79 通过(+3 desktop 整页 golden), analyze 0 error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-16 12:02:24 +08:00 |
|
wangjia
|
1340b477a7
|
feat(client): 字体可注入 + 启用 golden 视觉回归闸
- pangolin_theme: PangolinFonts 加 useBundled 开关 + sora/manrope/jetBrainsMono/
manropeTextTheme 封装(生产走 google_fonts / 测试走 bundled family);
PangolinText 与 _build 改调封装,移除散落的 GoogleFonts 直接调用
- test/flutter_test_config.dart: 全测试前置,FontLoader 注册 test/fonts 真实
Sora/Manrope/JetBrainsMono + Lucide 图标字体,置 useBundled=true
- 启用 test/golden(去 @Skip):连接键三态+推荐卡+额度卡×明暗 12 基准图
- tool/visual-diff.md: 标注 golden 严格闸已落地
测试: flutter test 76 通过(原 64+1skip → 76 无 skip), analyze 0 error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-16 11:36:50 +08:00 |
|
wangjia
|
281a6261fb
|
feat(client): tablet 外壳 + 视觉 diff 工作流文档
- tablet_shell: 侧栏 232·4 项触控(minHeight 48, 字号 15) + 顶栏无主题切换 + 连接页双栏
对照 ui_kits/tablet/tabapp.jsx;home_shell 分发 tablet→TabletShell
- nav_sidebar 加 dense 参数(desktop 紧凑 / tablet 触控)
- content_top_bar 加 showThemeToggle + titleSize 参数
- tool/visual-diff.md: 截图 diff 工作流(design-distill 工具)+ 实现侧三路径
现状(golden 被 google_fonts 阻塞 / screencapture 受 macOS chrome 限制)+ 推荐
注: tablet 形态在 macOS 桌面平台不触发(只 desktop/mobile),运行验证需 iPad。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-16 09:38:18 +08:00 |
|
wangjia
|
4dc4127252
|
feat(client): 三端布局架构 + macOS 桌面端 + app 图标
三端布局(mobile/tablet/desktop):
- core/responsive/form_factor.dart 形态判定 + shell/ 分发器(home_shell→desktop/mobile)
- desktop_shell 对照 ui_kits/desktop/dapp.jsx: 侧栏204·6项 + 套餐卡 + 顶栏(标题/状态/主题切换) + 连接页居中单列
- 新增组件 nav_sidebar / plan_badge_card / content_top_bar / bottom_tab_bar
- 新增一级页 contact_page / settings_page; navigation_provider(NavView)
- 删除旧 widgets/home_shell.dart(逻辑迁入 shell/)
macOS 桌面端:
- 窗口默认 920×600 + 最小 720×560(MainFlutterWindow.swift)
- app 图标替换为穿山甲(AppIcon.appiconset 全套, 由 app-icon.svg 渲染)
其余(本会话):
- Phase2 接线: auth_api/token_store/auth_provider/vpn_bridge_provider + 真实 connection/nodes
- lucide_icons 兼容补丁(packages/lucide_icons_patched) 修复 IconData final 报错
- 测试修复: connect_passthrough(UTF-8) / harness / golden @Skip
- l10n 新增 settingsTitle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-16 09:23:20 +08:00 |
|
wangjia
|
83dd5369ab
|
feat(M4/M5): URLTest 自动选线 + Kill-switch + 弹性重连 [tsk_xAQhC1xuCd8x]
M4 URLTest 自动选线:
- ClashApiClient.getGroupDelay() — GET /group/<name>/delay 触发按需测速
- ClashApiClient.extractUrltestResults() — 从 /proxies 响应解析 URLTest
组成员最新延迟,填充 stats 帧的 urltestResults 字段
- DesktopKernelProcess._startStatsPoll() — 每秒并行拉取连接统计 + URLTest
延迟(_clashApi.getProxies()),URLTest 失败不影响主统计
- DesktopVpnBridge.selectOutbound(tag) — Clash API PUT /proxies/proxy,
tag 可传节点 tag 或 urltest 组 tag(如 "auto-select")恢复自动
- DesktopVpnBridge.getActiveOutbound() — GET /proxies 读 proxy.now
- app/kernel/poc/reality_client.config.json.tmpl — 增加 urltest("auto-select")
+ selector("proxy") 出口组,route.final 改为 "proxy"
M5 Kill-switch + 弹性重连:
- DesktopVpnBridge.setKillSwitch({required bool on}) — 将 _killSwitchEnabled
写入 TUN inbound strict_route 字段(applyKillSwitchToConfig);内核运行中
触发静默重载(kill → start(lastConfig))
- DesktopVpnBridge.applyKillSwitchToConfig() — static,killSwitch=true ↔
TUN strict_route=true,killSwitch=false ↔ strict_route=false
- 自动退避重连:kernel 崩溃推 error → _scheduleReconnect() 退避序列
1s/2s/4s/8s/16s/30s(上限),stop() 取消,重连成功后重置计数器
- DesktopKernelProcess 意外退出清理 _clashApi(避免下次 spawn 残留)
- app/pangolin/test/killswitch_checklist.md — 三端故障注入验收清单
(Desktop ✅,iOS/Android 欠账说明)
新增测试(client/test/bridge/desktop_vpn_bridge_m4m5_test.dart):
- M4: extractUrltestResults 解析/空/无 history 边界
- M4: getGroupDelay HTTP 请求格式验证
- M4: selectOutbound / getActiveOutbound Clash API 调用断言
- M5: applyKillSwitchToConfig 字段覆盖 + 无 TUN 边界
- M5: setKillSwitch 状态注入 + 同值幂等
- M5: 崩溃→error 不崩 / 首次重连在退避延迟内触发 / stop 后不再重连
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-16 00:11:09 +08:00 |
|
wangjia
|
be44ef1226
|
merge: iOS M3 PoC — PacketTunnelProvider + entitlement [tsk_nsobbj_rJdy0]
|
2026-06-15 20:56:46 +08:00 |
|
wangjia
|
2ebd8c4054
|
merge: Android M2 VPN 隧道 PangolinVpnService [tsk_w1NvZdmwGPyd]
|
2026-06-15 20:56:37 +08:00 |
|
wangjia
|
323e1da04d
|
merge: 桌面端 PoC M1 — sing-box 子进程 TUN 接线 [tsk_SLCsjNgtmng3]
|
2026-06-15 20:56:37 +08:00 |
|
wangjia
|
b04cef3cc4
|
chore(design): 建立 token 单源管线,删除 design/flutter fork
- 新增 design/codegen/gen_flutter_tokens.mjs:
从 colors_and_type.css codegen 出 client/lib/pangolin_tokens.gen.dart
覆盖 PangolinColors/Spacing/Radius/Motion/Shadow(纯数值层)
- 重构 client/lib/pangolin_theme.dart:
删除手抄数值,import+export pangolin_tokens.gen.dart
保留实现层 PangolinScheme/PangolinText/PangolinTheme/PangolinContext
19 个引用方零改动,对外 API 完全不变
- 新增 web/usercenter/scripts/build-tokens.mjs:
仿 website 样板,prebuild/predev 自动从 css 生成 public/colors_and_type.css
去除 usercenter 手抄副本的漂移风险
- 更新 CLAUDE.md:补 token codegen 使用说明与单源模型
- 更新 design/SKILL.md:移除已删 flutter/ 引用,补 codegen 入口
验证:改 clay-500 → 三端 codegen 同步变化;flutter analyze 无新增 error。
注:design/flutter/ fork 手动删除(git rm 需用户执行)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-15 18:27:22 +08:00 |
|
wangjia
|
3c6a8a517a
|
feat: 桌面端 PoC M1 — sing-box 子进程 TUN 接线 [tsk_SLCsjNgtmng3]
实现桌面端(macOS PoC)内核子进程管理,打通 sing-box TUN 模式全链路:
### client/lib/bridge/kernel_process.dart(完整实现,替换原有 stub)
- ClashApiClient: HTTP 客户端,支持 /connections / /proxies / /traffic(SSE) / /traffic(plain)
- Bearer Token 鉴权;getConnections 用于就绪探测与流量统计;getTraffic SSE 读首帧后断开
- KernelProcess 接口: 新增 statusStream / statsStream 至接口定义
- DesktopKernelProcess:
- spawn(configPath): 解析 Clash API 端口/secret → sudo sing-box run(macOS PoC)→
轮询 /connections 等待就绪(20s 超时)→ emit connecting→on
- kill(): SIGTERM + 等待 gracePeriod(5s) → SIGKILL → emit off
- 意外退出: emit error(UI 可一键重连,不崩溃)
- 统计轮询: 每秒 GET /connections,差分算 uploadSpeed/downloadSpeed
- 二进制解析: ENV > exe同目录 > macOS Bundle Resources > 开发目录 > /usr/local/bin
- 辅助函数: generateClashApiPort(高位随机)、generateClashApiSecret(32B hex)
### client/lib/bridge/desktop_vpn_bridge.dart(新文件)
- DesktopVpnBridge implements VpnBridge:
- start(configJson): injectClashApi(注入随机端口+secret)→ writeConfig(0600)→ kernel.spawn
- stop(): kernel.kill(5s)
- statusStream / statsStream: 代理 KernelProcess 事件流
- selectOutbound: Clash API PUT /proxies/{group}
- getActiveOutbound: 从 /proxies 读 now 字段
- configDirOverride: 测试注入支持
- injectClashApi: 静态方法,尊重已有 clash_api 配置,合并保留 experimental 其他字段
### client/test/bridge/kernel_process_test.dart(新文件)
- ClashApiClient 完整测试: headers / 解析 / PUT body / DELETE / 非 200 抛 HttpException
- generateClashApiPort / generateClashApiSecret 生成范围和格式测试
- DesktopVpnBridge.injectClashApi: 注入 / 尊重已有 / 保留字段 / 保留其他 experimental / 非法 JSON
- DesktopVpnBridge + FakeKernelProcess 集成: connecting→on / stop→off / statsStream / 意外退出 / spawn失败
### app/kernel/poc/(新目录)
- reality_client.config.json.tmpl: VLESS+REALITY+TUN 客户端配置模板
- TUN inbound: auto_route + strict_route(macOS kill-switch 基础保护)
- DNS: 防泄露(remote via VPN + cn 直连)
- experimental.clash_api: 随机端口 + secret 占位符
- gen-poc-config.sh: 渲染模板为可用 JSON(从环境变量读 REALITY 参数)
- README.md: 完整 PoC 接线与 M1 验收步骤
提权说明(macOS PoC):
· sudo 提权(开发机需配 /etc/sudoers.d/pangolin-singbox 或有 sudo 缓存)
· 正式版: SMJobBless Helper + 公证(BACKLOG-11D-HELPER)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-13 17:53:50 +08:00 |
|
wangjia
|
83c23f968a
|
feat(android): 实现 M2 Android VPN 隧道 — PangolinVpnService 接入 libbox [tsk_w1NvZdmwGPyd]
接线 11B libbox.aar + 11C Dart 桥接骨架,完成 Android 端 M2 PoC:
### PangolinVpnService(完整实现)
- `handleStart(configJson)` → 后台线程 → `Libbox.newBoxService(platformInterface, json)` → `start()`
- 内 inner class `PangolinPlatformInterface` 实现 libbox `PlatformInterface`:
- `openTun(TunOptions)`: 调用 `VpnService.Builder()` 配置地址/MTU/路由,`establish()` 取 TUN fd
- `autoDetectInterfaceControl(fd)`: `VpnService.protect(fd)` 防环路
- 安全 getter 兜底:TunOptions 方法名与 libbox.aar 不符时降级到 PoC 默认值
- 统计双轨:优先 libbox `CommandClient`(status/traffic)→ 失败时退回 `TrafficStats` 定时器
- 幂等 `doStop()`:commandClient.disconnect → boxService.close → tunPfd.close → stopForeground
- `onRevoke()` 推 error 状态再 doStop,不留假 on
### VpnEventBus
- 新增 `currentStatus` 字段(volatile),供 `getStatus()` 一次性查询
### MainActivity
- `VpnService.prepare()` 权限流程:未授权 → 弹 prepare Intent → `onActivityResult` 后启动
- `getStatus()` 从 `VpnEventBus.currentStatus` 返回真实状态
- 首次连接时引导 `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`(后台存活)
### AndroidManifest.xml
- `android:foregroundServiceType="specialUse"`(Android 14 API 34 强制要求)
- 新增权限:`FOREGROUND_SERVICE_SPECIAL_USE`、`REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`
### build.gradle
- `implementation files('../../../app/kernel/dist/android/libbox.aar')`(条件存在检查)
- `minSdkVersion` 提升至 21(gomobile -androidapi 21 要求)
### 新增文件
- `assets/poc_config.json`: PoC 单节点测试配置模板(含占位符 __NODE_HOST__ 等)
通知文案已过红线词扫描:「加速已开启」「正在连接…」「穿山甲加速」,无 VPN/翻墙等禁词。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-13 17:45:13 +08:00 |
|
wangjia
|
e129f094c2
|
feat(ios): M3 PoC — PacketTunnelProvider + entitlement + 内存监控 [tsk_nsobbj_rJdy0]
### 新增文件
- client/ios/PacketTunnel/MemoryMonitor.swift
周期打点 NE 进程内存(os_proc_available_memory iOS13+ / mach_task_basic_info 兼容
iOS12);每 10s 采样一次;availableMemory < 5MB 时输出 ⚠️ 警告;提供
summaryString() 供 handleAppMessage/getMemory IPC 返回。
- client/ios/PacketTunnel/PacketTunnel.entitlements
Extension 的 NE + App Group entitlement 文件;申请说明写入注释。
- client/ios/Runner/Runner.entitlements
主 App 的 NE + App Group entitlement 文件;申请说明写入注释。
- doc/ne-entitlement-申请指引.md
完整申请操作手册:Bundle ID 清单、Apple Developer Portal 步骤、
专项审批英文申请文案(已脱敏,无红线词)、审批 lead time 说明。
### 修改文件
- client/ios/PacketTunnel/PacketTunnelProvider.swift
完整 M3 实现(替换原骨架 TODO):
· startTunnel:解析 configJson(options/App Group 两路回退)→
buildNetworkSettings → setTunnelNetworkSettings → setupTunBridge
→ startLibbox → startPacketBridging → memoryMonitor.start()
· stopTunnel:memoryMonitor.stop() → stopLibbox → closeTunBridge
· handleAppMessage:JSON IPC 协议(getStatus/getMemory/selectOutbound)
· LibboxPlatformInterface 扩展(#if canImport(Libbox)):
openTun 返回 socketpair libbox 侧 fd,autoDetectInterfaceControl no-op,
writeLog 转 os.log,useProcFS 返回 false
· packetFlow 双向桥接(readFromPacketFlow/writeToPacketFlow 循环)
· parseTunAddress/parseDNSServers 从 JSON 提取网络参数
- client/ios/Runner/VpnManager.swift
· localizedDescription "穿山甲 Pangolin VPN" → "Pangolin 加速"(红线词修复)
· 新增 appGroup 常量(group.com.pangolin.pangolinVpn)
· start() 写入 App Group UserDefaults 缓存 configJson
- client/ios/Runner/Info.plist
NSVPNUsageDescription "穿山甲使用 VPN…" → "Pangolin 使用网络加速通道…"(红线词修复)
- client/ios/PacketTunnel/Info.plist
CFBundleDisplayName "穿山甲 Tunnel" → "Pangolin 加速通道"(红线词修复)
- client/ios/Runner.xcodeproj/project.pbxproj
· Runner + PacketTunnel 所有 build config 添加 CODE_SIGN_ENTITLEMENTS
· 新增 MemoryMonitor.swift 到 PacketTunnel Sources 构建阶段
· 新增 Runner.entitlements / PacketTunnel.entitlements / MemoryMonitor.swift
文件引用及分组
- app/kernel/build-ios.sh
添加 M3 内存裁剪决策记录:gVisor→grpc→QUIC 优先裁减顺序及内存估算,
等待真机 10min 压测结果后更新最终结论
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-13 17:44:11 +08:00 |
|
wangjia
|
bf1717a369
|
merge: 控制面联调 M6 [tsk_nuoKSM4Vt-zK]
# Conflicts:
# .gitignore
# client/lib/widgets/home_shell.dart
# client/lib/widgets/server_tile.dart
# client/pubspec.yaml
|
2026-06-13 17:32:26 +08:00 |
|
wangjia
|
6f6cb97509
|
merge: Flutter 逐屏还原 + iPad 断点 [tsk_gpPXi-icyeOE]
# Conflicts:
# client/lib/widgets/connect_button.dart
# client/lib/widgets/home_shell.dart
# todo/todo.html
# todo/todo.json
|
2026-06-13 17:30:36 +08:00 |
|
wangjia
|
c84b6ea7da
|
merge: maestro/tsk_iP5-_Ztq5THx [桥接 API 面 Channel 契约 + 三端原生骨架] [tsk_rcNKc3GQHBUd]
合并冲突补救:原分支从 merge base 7871512 分叉,仅修改 client/ 目录,
与 main 新增的 server/(apierr/idgen/CONVENTIONS.md) 完全不重叠,
无真实文件冲突,手动应用 feature branch 的全部 client/ 改动。
client 侧新增:
- lib/bridge/vpn_bridge.dart — Dart↔原生通道契约(冻结)
- lib/bridge/vpn_bridge_mock.dart — 假内核,UI 联调用
- lib/bridge/kernel_process.dart — 桌面子进程管理接口
- ios/PacketTunnel/{Info.plist,PacketTunnelProvider.swift} — NEPacketTunnel 骨架
- ios/Runner/VpnManager.swift — NETunnelProviderManager 封装
- android/.../PangolinVpnService.kt — VpnService 骨架
- android/.../VpnEventBus.kt — Application 级状态总线
- test/bridge/vpn_bridge_mock_test.dart — 桥接层单元测试
client 侧修改:
- android/.../MainActivity.kt — 注册三通道(MethodChannel + 2×EventChannel)
- android/.../AndroidManifest.xml — 声明 FOREGROUND_SERVICE + PangolinVpnService
- ios/Runner/AppDelegate.swift — 注册通道 + VpnManager 初始化
- ios/Runner/Info.plist — 新增 NSVPNUsageDescription
- ios/Runner.xcodeproj/project.pbxproj — 添加 PacketTunnel extension target
- lib/widgets/connect_button.dart — VpnStatus 迁移到 bridge/vpn_bridge.dart
- lib/widgets/home_shell.dart — error 状态 UI 分支补全
|
2026-06-13 14:58:38 +08:00 |
|
wangjia
|
f97ae8186b
|
feat(client): Flutter 逐屏还原 + iPad ≥900 断点 (tsk_gpPXi-icyeOE)
对照 design/ui_kits mobile+tablet 原型逐屏补齐 Flutter 客户端:
- l10n 资源层(strings_zh/en,单显)+ Riverpod 状态层(连接状态机/免费额度/
节点选择/语言/主题),UI 与数据解耦,mock 数据接 API 不动 UI。
- 连接键严格三态(off 虚线轨道环 / connecting 旋转弧 / on 满环+计时+光晕),
状态来自 connectionProvider,点击只派发事件——禁止乐观显示。
- 节点页置顶「智能选择」推荐卡(clay 渐变 zap + 推荐胶囊,默认选中);
免费额度卡(剩余分钟+进度条 ≤3 分钟切 warning + 看广告解锁变绿)。
- Tab 左右滑动切换(手势竞技场仲裁,子页滚动不误触发,200ms 方向感知滑入)。
- iPad/宽屏 ≥900 LayoutBuilder 切侧栏分栏(导航行高 ≥48,连接页双栏/节点双列网格),
复用同一批原子组件,不 fork 页面。
- 语义 token 零硬编码;文案全部走 l10n;套餐数字对齐 §7;无支付表单/emoji 国旗。
- CI 红线词扫描扩展到 client/lib;新增 Flutter analyze+test CI 任务。
- 测试:连接状态机/额度/节点单测、连接键三态与卡片组件测试、
golden(连接键三态/推荐卡/额度卡 × 明暗)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-13 14:32:36 +08:00 |
|
wangjia
|
bd8f974a25
|
feat(M6): 控制面联调 — mock connect server + 客户端 ConnectApi 透传
tsk_nuoKSM4Vt-zK
## 服务端(server/cmd/mockserver)
- `main.go`(79 行):独立 mock HTTP server,实现 POST /v1/nodes/:id/connect
- 路径/请求体/响应体字段名与 §3.1 契约逐字一致
- Bearer token 鉴权(-token 参数,空值跳过,不入库)
- 缺少 device_id → 400;未授权 → 401;非 POST → 405
- 返回完整 sing-box config JSON(tun + REALITY/Hy2 outbound + urltest + route/dns)
- 注:mock 联调;待 #5/#6 真实 connect 接口就绪后替换
- `main_test.go`:6 项单测,覆盖 §3.1 结构校验、auth、method、path
## 客户端(client/)
- `lib/services/connect_api.dart`:ConnectApi 类
- fetchConfig(nodeId, deviceId) → 原始响应体字符串(不做任何修改)
- 错误路径:HTTP 非 200 / 超时 / 非法 JSON → ConnectApiException(含双语 message)
- `lib/services/vpn_bridge.dart`:VpnBridge stub(M6 联调,libbox 绑定待 11C)
- start(configJson) 原样存储,不修改;stop() 清空
- `lib/widgets/home_shell.dart`:_toggle/_pick 替换为真实 API 流程
- ConnectApi.fetchConfig → VpnBridge.start(透传,无中间变换)
- 错误路径:ConnectApiException → SnackBar,status 回 off,无残留半开隧道
- API URL/token/deviceId 由 --dart-define 注入(不入库)
- `lib/widgets/server_tile.dart`:ServerInfo 新增 nodeId 字段
- `pubspec.yaml`:新增 http: ^1.2.1 依赖
- `test/connect_passthrough_test.dart`:4 项透传断言单测
- 核心:fetchConfig 返回字符串 === VpnBridge.start 接收字符串(逐字节相等)
- 空格/格式原样保留(不经 jsonEncode 重序列化)
- HTTP 错误、非法 JSON 错误路径覆盖
## 运行方式(M6 联调)
```
# 启动 mock server(无 auth)
go run ./server/cmd/mockserver -addr :8081
# 启动客户端(指向 mock server)
flutter run \
--dart-define=PANGOLIN_API_URL=http://localhost:8081 \
--dart-define=PANGOLIN_API_TOKEN=dev-mock-token \
--dart-define=PANGOLIN_DEVICE_ID=demo-device-001
```
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-13 14:16:29 +08:00 |
|
wangjia
|
526e7f2f33
|
feat(tsk_iRa5iBC4v7Tu): Flutter 客户端工程搭建(演示态)
基于 design/flutter/ 起步包在 client/ 下建立 Flutter 工程:
- pubspec.yaml:flutter_svg / lucide_icons / google_fonts 依赖;
SVG 资产注册;字体由 google_fonts 运行时加载,无需本地 ttf
- lib/pangolin_theme.dart:完整设计令牌(色阶 / 间距 / 圆角 /
动效 / 文字阶),PangolinText 改用 GoogleFonts getter,
PangolinTheme.light / .dark 开箱即用
- lib/main.dart:runApp + 明暗主题 + 登录 → 引导 → 主框架 RootFlow
- lib/widgets/:全套组件雏形
pangolin_icons Lucide 图标映射
pangolin_button 胶囊按钮四态
country_code 国家码块 + 信号条
status_pill 色点胶囊
connect_button 核心连接键三态(off/connecting/on + 动画)
server_tile 服务器列表行
plan_card 套餐卡(专业版渐变高亮)
auth_screen 登录 / 注册(邮箱验证码 + 设密码)
onboarding_screen 首次引导 PageView(3 屏可滑动 + 进度点)
home_shell 底部 4 Tab(连接/节点/统计/账户 + 状态机)
account_screens 套餐选择 / 设备管理 / 兑换 / 联系我们
pangolin_logo PangolinMark / BrandLockup / AppIcon(SVG)
- assets/:logo-mark / logo-mark-white / logo-wordmark / app-icon(SVG)
- android/:Kotlin 主 Activity + Manifest + Gradle 配置
- ios/:Runner.xcodeproj + xcscheme + Podfile + AppDelegate + storyboard
运行方式(cd client/):
flutter pub get && flutter run
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-13 02:15:42 +08:00 |
|