Files
pangolin/design/CONTRACT.md
T
wangjia 143104b861 docs(design): toast 规格入真相源 + 铁律14「UI 组件必须取自真相源」
- design/CLAUDE.md §1 加铁律 14:新增/改 UI 组件必须用真相源已定义的组件与令牌;
  真相源没有的先补规格再用,绝不在业务代码凭空造样式(如 Material 默认外观);须符合当前设计。
- design/CONTRACT.md §2 加 Toast/SnackBar 组件规格(surface 底/fg1 字/border 描边/md 圆角/floating,
  明暗自动适配),canonical 实现=pangolin_theme snackBarTheme。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 19:43:54 +08:00

90 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 设计契约 · iPad / Tabletdesign-distill
> 像素基准:`design/ui_kits/tablet/tabapp.jsx`1180×820 横屏侧栏布局)+ `index.html`。
> 数值取自原型源码,非目测。token 单源:`design/colors_and_type.css` → `client/lib/pangolin_tokens.gen.dart`(勿手改)。
> 目标实现:`client/lib/shell/tablet_shell.dart` + 四页 `isWide` 分支 + `client/lib/widgets/`。
> 验收闸:`client/test/golden/tablet_pages_golden_test.dart`1180×820 / iOS 平台 → formFactor=tablet)。
## 0. 归一化产物
- 类型 A(结构化包):token 单源已存在、codegen 已接(`design/codegen/gen_flutter_tokens.mjs`)。本契约只补 **tablet 页面像素规格 + 验收清单**
- 断点:触屏宽 **≥900 → tablet 侧栏**,否则 mobile 底 Tab`client/lib/core/responsive/form_factor.dart`,对齐 README「≥900 切侧栏」)。
## 1. token 映射(复用既有语义层)
| 原型 CSS var | Flutter 语义层 |
|---|---|
| `--bg` / `--bg-subtle` | `c.bg` / `c.bgSubtle` |
| `--surface` | `c.surface` |
| `--fg1/2/3` | `c.fg1/fg2/fg3` |
| `--accent` / `--accent-subtle` / `--accent-border` | `c.accent` / `c.accentSubtle` / `c.accentBorder` |
| `--success` `--warning` | `c.success` `c.warning` |
| `--border` / `--border-strong` | `c.border` / `c.borderStrong` |
| `--radius-{sm,md,lg,xl,full}` | `PangolinRadius.{sm,md,lg,xl,full}` |
| `--shadow-{sm,md,lg}` | `PangolinShadow.{sm,md,lg}` |
| `--font-{display,sans,mono}` | `PangolinText.{h*/body...}` / `PangolinText.mono` |
## 2. 组件映射
| 原型 | Flutter | 状态 |
|---|---|---|
| `TabRail` 侧栏 | `TabletShell` + `widgets/nav_sidebar.dart` | 已接,宽 232、行高≥48 |
| 连接键三态 | `widgets/connect_button.dart` | 复用(off 态 canonical 走「虚线轨道环」,见 design/CLAUDE.md §5,优先于原型实心环简化) |
| 免费额度卡 | `widgets/quota_card.dart` | 复用 |
| 智能选择卡 | `widgets/smart_select_card.dart` | 复用 |
| 节点行 | `widgets/server_tile.dart` / `nodes_page` `_NodeGridTile` | 双列网格 |
| 指标卡 | `stats_page` `_MetricCard` | 复用 |
| 段控/开关/行 | `account_page` 内组件 | 复用 |
| Toast / 通知条 | `pangolin_theme.dart` `snackBarTheme`(全局) | 新增组件(无原型) |
### Toast / SnackBar(通知条)规格
跨平台轻提示(如自动连接「正在自动连接到 X」)。**无 React 原型 → 此处即真相源**;全部走语义 token,明暗自动适配:
- 底色 `--surface`(浅=白 / 深=`#221E19`);文字 `--fg1`,Manrope 14/600;描边 `--border` 1px。
- 圆角 `md`(10);`SnackBarBehavior.floating`;阴影柔和暖调(elevation≈6,`rgba(45,30,20,…)` 体系)。
- 绝不用 Material 默认黑底。Flutter canonical:`PangolinTheme._build``snackBarTheme`(改样式只改此一处,全 app 一致)。
## 3. 页面像素规格(取自 tabapp.jsx
### 外壳 TabletApp
- 内屏 1180×820`data-theme` 切明暗。侧栏 + 内容列(顶栏 56 + 主体)。
- 内容顶栏:高 56padding `0 28`,标题 `font-display 700 / 19 / fg1`,右侧连接状态(mono 12.5on=success「● {code}」/ off=fg3「○」)。
### TabRail 侧栏
- 宽 232`bg-subtle`,右 1px `border`padding `18 14 16`
- 品牌头:Mark 30 + 「穿山甲」(display 700/17) + 「PANGOLIN」(9/600/字距0.2em/accent)gap 10padding `0 8 20`
- 导航项:gap 4;每项 padding `13 14`、radius md、字号 15、active 700+accent+accent-subtle 底 / inactive 500+fg2、**minHeight 48**、图标 20active stroke 2.4)、gap 12。
- 底部套餐徽章:free=surface+border / pro=`linear-gradient(150deg,clay-600,clay-800)`+白字;radius lgpadding `12 13`;头像圈 32 + 名(12.5/700) + 邮箱(10.5/opacity.65)。
### 连接视图 TabConnect(双栏)
- 左列 flex **1.25**,居中,gap 26:按钮 216×216 圆、`box-shadow 0 0 0 11px {ring}, shadow-lg`、图标 60、label mono 700/14/字距0.08em;下方 caption(16/600/fg2)on 态计时 mono 28/fg1。
- 右列宽 **332**gap 14,垂直居中,paddingRight 28
- 免费额度卡(card padding `14 16`)clock + 「今日剩余 {n} 分钟」+ 「免费版」pill + 进度条(高6,≤3 变 warning) + 看广告按钮(accent-subtle/accent-border/full/minHeight44)。
- 当前节点卡(padding `13 15`, minHeight 48)CC 码块 + 「当前节点」(11/600/fg3) + 名(15/600) + 副(12/fg3) + chevron。
- on 态速率行:3 卡(下载/上传/延迟),各 padding `11 13`、值 mono 16。
### 节点视图 TabNodes
- padding `6 28 24`overflow auto。
- 智能卡:accent-subtle 底、border(active=accent)、radius xl、padding `15 16`、minHeight 56、marginBottom 14zap 在 clay 渐变盒 42×42 + 「智能选择」(15.5/700) + 「推荐」pill + 副(12/fg2) + active check。
- 搜索框:bg-subtle、radius md、padding `11 14`、maxWidth 380、marginBottom 14。
- 网格:**2 列 `1fr 1fr`gap 11**;每格 padding `13 15`、minHeight 56、active=accent-subtle+accent-borderCC + 名(14.5/600) + 副 + ping(mono 12) + Signal + active check。
### 统计视图 TabStats(上聚合 · 下分设备)
- padding `6 28 24`
- **上聚合**3 指标卡一行,gap 14marginBottom 20;各 padding `18 20`label(12.5/fg3/600) + 值(mono 26/fg1)。周柱卡 padding `20 22`:标题(13.5/600) + 7 柱(gap 22, 容器高150, 柱 maxWidth 40, accent opacity .85, radius `6 6 0 0`) + 值(mono 10.5) + 星期(11.5)。
- **下分设备**「设备明细 / By device」(统一组件 `DeviceStatRow`,四端复用):周柱卡下 marginTop 20,标题(13/600/fg1);设备卡 surface+border+radius-lg+shadow-sm,行内分隔线 border。每行 padding `14 16/15 18`:左平台图标盒 38×38(accent-subtle, radius md, Lucide smartphone/laptop/monitor-smartphone, clay 2.2px) + 中(名 14.515/600 省略号 + 占比迷你条 height 4 radius 999, track=border, fill=accent .85, 宽=本设备流量/最忙设备) + 右(流量 mono 14.515 + " GB" caption;时长 caption "X h")。流量降序;空数据显示占位「暂无设备用量 / No device usage yet」。
- 数据源:`GET /v1/usage/devices?days=N`(账户内每设备窗口聚合,busiest-first)→ `deviceUsageProvider`
### 账户视图 TabAccount
- padding `6 28 24`maxWidth 640(生产页功能更丰富,沿用同设计语言:套餐横幅 + 分区卡 + 行 + 段控/开关)。
- 套餐横幅 radius xl padding 20;行 minHeight 48,图标盒 34 radius sm accent-subtleTabToggle 48×29(on=accent)。
## 4. 逐屏验收清单(每屏 × 明/暗 × 中/英)
- [ ] 连接 / 节点 / 统计 / 账户 四视图 × light/dark × zh/en:无溢出、侧栏 232 比例正确、触控目标 ≥48。
- [ ] 连接页双栏左右比例 ≈ 1.25 : 332;on 态速率行 3 卡出现。
- [ ] 节点页 2 列网格 + 置顶智能卡;搜索框 maxWidth 380。
- [ ] 颜色全部语义 token、无硬编码 hexdesign/CLAUDE.md §1)。
- [ ] 文案无红线词(§13)、双语单显不并排。
- [ ] golden 回归闸 `tablet_pages_golden_test.dart` 通过;真机/模拟器 mini/11″/12.9″ 横竖屏目测过。
## 5. 已知差异(记录,非缺陷)
- 连接键 off 态:原型用实心 11px 环,Flutter 用「虚线轨道环」(design/CLAUDE.md §5 canonical 连接键),以 §5 为准。
- 账户页:生产实现比原型多设备管理/兑换/联系/协议行,属真实功能扩展,沿用同设计语言。
- golden 测试环境未打包 Noto Sans SC → CJK 显示为方块,仅影响 golden 文字、不影响真机;布局可判。节点网格/周柱在未登录测试态无数据(需注入演示数据方显内容)。