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>
This commit is contained in:
wangjia
2026-06-16 09:23:20 +08:00
parent 71a4cddf0b
commit 4dc4127252
92 changed files with 4536 additions and 1187 deletions
+5 -22
View File
@@ -1,12 +1,3 @@
// connect_button.dart — 核心连接键(三态:off / connecting / on)
// VpnStatus 枚举定义在 lib/bridge/vpn_bridge.dart(含 error 扩展态)
import 'package:flutter/material.dart';
import '../bridge/vpn_bridge.dart' show VpnStatus;
import '../pangolin_theme.dart';
import 'pangolin_icons.dart';
// 重导出便于其他 widget 从 connect_button.dart 引入(向后兼容)
export '../bridge/vpn_bridge.dart' show VpnStatus;
// connect_button.dart — 核心连接键(严格三态:off / connecting / on)
//
// 纯展示组件:状态由外部状态机(connection_provider)注入,点击只回调
@@ -70,20 +61,12 @@ class _ConnectButtonState extends State<ConnectButton> with SingleTickerProvider
final s = widget.phase;
final Color fill = switch (s) {
VpnStatus.off => c.bgSubtle,
VpnStatus.connecting => c.accent,
VpnStatus.on => c.success,
VpnStatus.error => c.danger,
VpnPhase.off => c.bgSubtle,
VpnPhase.connecting => c.accent,
VpnPhase.on => c.success,
};
final Color fg = s == VpnPhase.off ? c.accent : PangolinColors.white;
final IconData icon = switch (s) {
VpnStatus.off => PangolinIcons.power,
VpnStatus.connecting => PangolinIcons.loader,
VpnStatus.on => PangolinIcons.shieldCheck,
VpnStatus.error => PangolinIcons.power,
VpnPhase.off => PangolinIcons.power,
VpnPhase.connecting => PangolinIcons.loader,
VpnPhase.on => PangolinIcons.shieldCheck,
@@ -94,7 +77,7 @@ class _ConnectButtonState extends State<ConnectButton> with SingleTickerProvider
? PangolinShadow.md
: [
BoxShadow(
color: (s == VpnPhase.on ? c.success : c.accent).withOpacity(0.18),
color: (s == VpnPhase.on ? c.success : c.accent).withValues(alpha: 0.18),
blurRadius: 0,
spreadRadius: 9,
),
@@ -121,7 +104,7 @@ class _ConnectButtonState extends State<ConnectButton> with SingleTickerProvider
builder: (_, __) => CustomPaint(
painter: _RingPainter(
phase: s,
track: c.sand200,
track: PangolinColors.sand200,
progress: PangolinColors.white,
turns: s == VpnPhase.connecting ? _spin.value : 0,
),
@@ -146,7 +129,7 @@ class _ConnectButtonState extends State<ConnectButton> with SingleTickerProvider
.copyWith(color: fg, fontSize: 18, fontWeight: FontWeight.w600)),
Text(widget.secureLabel,
style: PangolinText.caption.copyWith(
color: fg.withOpacity(0.9),
color: fg.withValues(alpha: 0.9),
fontSize: 10,
fontWeight: FontWeight.w700,
letterSpacing: 1.0)),
@@ -192,7 +175,7 @@ class _RingPainter extends CustomPainter {
final base = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 4
..color = progress.withOpacity(0.3);
..color = progress.withValues(alpha: 0.3);
canvas.drawArc(rect, 0, 6.283, false, base);
final arc = Paint()
..style = PaintingStyle.stroke
@@ -205,7 +188,7 @@ class _RingPainter extends CustomPainter {
..style = PaintingStyle.stroke
..strokeWidth = 4
..strokeCap = StrokeCap.round
..color = progress.withOpacity(0.85);
..color = progress.withValues(alpha: 0.85);
canvas.drawArc(rect, 0, 6.283, false, p);
}
}