Commit Graph
7 Commits
Author SHA1 Message Date
PMon Dev 1d76c3b8ac feat(design-system): 合入 V2 线并统一命名为 Inkwatch,补齐深色主题与状态符号图标化
Design System Guard / guard (push) Failing after 7m11s
本提交收口本轮四件事。三者改动在同一批文件里交织(改名触及 theme.dart、
深色化也在 theme.dart),按文件无法干净切分,故合为一个可审计的提交,
下面按事由分述。

一、命名统一:Pmon* → Inkwatch*(合入 V2 线后的收尾)
  · 包名 pmon_client → inkwatch_client,Rust crate pmon-core → inkwatch-core。
    改名后必须 flutter pub get 重建 package_config,否则 dart analyze 会报
    1688 个"找不到 package:pmon_client"的假错误。
  · 符号:PmonColors→InkwatchColors、PmonPalette→InkwatchPalette、
    PmonTextStyles→InkwatchTextStyles、PmonRadius→InkwatchRadius、
    PmonEinkText→InkwatchEinkText、PMON_LOG_*→INKWATCH_LOG_*。
  · 覆盖 server/ client/ rust-core/ tests/ 全树;已用
    git grep -In 'Pmon\|PMON\|pmon' 复核,零残留(tokens.css 里最后一处
    §2.8 的类名注释也在本提交修掉)。
  · rust-core/Cargo.lock 的变动只是包名变化导致 [[package]] 字典序重排。

二、深色主题:58 键同名覆写 + Flutter 整屏深色化
  · tokens.css 增 dark 块(58 键),键名与浅色**逐一相同**——
    这是 token_parity_test.dart 的深色 parity 组在守的约束。
  · theme.dart:新增 InkwatchDarkColors / _assemble(palette, brightness),
    把语义色从"常量"改为"随 brightness 装配"。
  · main.dart:darkTheme + 显式 themeMode(ValueNotifier 持久化,不用
    ThemeMode.system —— 水墨屏用户要的是自己定,不是跟系统)。
  · theme_guard_test.dart C-15 → C-15′:从"禁止出现深色"改为"允许深色,
    但必须 58 键同名",护栏跟着设计意图一起改,而不是删掉。
  · 新增 dark_theme_test.dart / dark_ui_test.dart 覆盖装配与整屏渲染。

三、状态符号图标化:Unicode 字形 → Material Icons
  · symbol_guard_test.dart 按**码位**扫 lib/(注释之外),命中 5 处:
    transfer_card.dart 的 ⚠️/✓/✓/⚠,viewer_page.dart 的 ✓。
  · 只删字形会留下不可见的 U+FE0F,源码看不出、界面渲染成豆腐块,
    且按 emoji 区间匹配的旧护栏看不见它 —— 故护栏按码位判,且判据选
    "扫注释之外全部字符"(错了只会多报,不会漏)。
  · 改法:状态图标单独一槽,不拼进文案。
    - transfer_card.dart:_offerHint 由 String? 改为
      ({IconData? icon, Color? tint, String text})?,渲染为 Row(Icon+Text);
      「已收下」结果块同理(check_circle / error_outline)。
      图标着色:warning_amber→_p.suspect,check_circle→_p.online。
    - viewer_page.dart:_lanSummary() 返回 ({IconData? icon, String text}),
      诊断行由 ('对端内网', _lanSummary(), null) 改为 (…, lan.text, lan.icon)。
      调用点提到 return 之前只算一次。
  · 理由不只是"好看":字符符号在水墨屏上会渲染成方框,且无法随主题着色。

验证
  · client/scripts/test.sh:674 项全过(+674,0 失败,23s)
  · tests/run-guards.sh(RUN_AXE=1):
      web-token-guard.js              PASS  21/0
      phase3b-static-guard.js         PASS  87/0
      dockerfile-coverage-test.js     PASS  53/0
      schema-consistency-test.js      PASS 116/0
      client Dart 全量                PASS  674 项
      admin-axe-audit.mjs             PASS  42/42 格 viol=0(inc=12 有台账)
    6 套件 PASS 6 / FAIL 0 / SKIPPED 0
  · dart analyze:9 issues / 0 error(全部为既有 info/warning,非本次引入)
  · 两处 tokens.css(仓库内 admin/assets 与设计系统交付目录)逐字节一致
    sha256 088873cf6ece8da78135f96ca4633a537ac7802ea5f0fc8bd9c9294bb8a7bcaa

已知未做(写下来,不藏)
  · flutter analyze 在本机因仓库路径含中文(水墨屏工具)触发 analysis_server
    的 LSP 分帧 UTF-8 错误(FormatException: Unterminated string)。
    绕法是用 dart analyze,或把工作树放到纯 ASCII 路径下。
  · 窄屏降级态 + 失败态的组合真机复核:仍未做。
  · VoiceOver / NVDA 实机验证:NOT RUN。
  · 管理台登录框仍缺持久 <label>(现靠 aria-labelledby 指向标题)。
2026-11-09 17:24:56 +08:00
PMon Dev 38c3d8ffea feat(ui): Flutter 整屏页面级深色化(ThemeExtension<PmonPalette>)
theme.dart 新增 PmonPalette(24 语义色 + copyWith/lerp + light/dark/eink 三实例)与 PmonTextStyles 集中出口;statusStyle / PmonCard 改为接收色板(缺省回退 light 保住旧调用);三主题均挂 extensions。11 个消费文件改从 PmonPalette.of(context) 取色,不再直接引用静态 PmonColors/PmonText;含 const 去化以支持运行时色板(不改颜色/尺寸/布局)。E-Ink 挂 PmonPalette.eink(PmonEink 六色,R=G=B 无彩色、圆角 0)。新增 dark_ui_test.dart(13 项)并登记 test.sh。深色 tertiary 保持 #8A93A3;浅色 18 原色与 Core 45 项未动。验证:Dart 全量 +640、run-guards 5/5、dart analyze 9 issues 0 error。
2026-11-08 22:27:09 +08:00
PMon Dev 0d332f6e9a feat(ui): Flutter 正式深色皮肤(ThemeData 层)与显式偏好装配
- theme.dart 新增 PmonDarkColors:转录 CSS dark 块 30 键到 26 个常量
  (含两处反相 text-on-accent #0B0E14 / snackbar-bg #F2F4F9),
  28 个 Web 专属键登记 kDarkExemptTokens 豁免;几何/排印/动效/端侧派生/
  E-Ink 六色不进 Dart,沿用 PmonSpace/PmonRadius/PmonText,不新增刻度
- buildPmonTheme() 重构为 _assemble(palette, brightness),
  buildPmonDarkTheme() 复用同一控件装配,只在 theme.dart 内 new ThemeData;
  浅色分支 fromSeed 逐字段未变,深色分支 copyWith 使 colorScheme.primary
  严格等于 #6E9CFF
- main.dart 装 darkTheme + themeMode(顶层 ValueNotifier,默认 light,
  不用 ThemeMode.system),采集页加最小切换入口
- AppConfig.themeMode 持久化(零依赖 JSON,值域 light|dark,默认不预写键)
- 护栏:theme_guard C-15 改为 C-15'(允许深色但要求 58 键同名、
  不污染 E-Ink、不新增刻度、浅色 18 原色未动);token_parity 新增深色
  parity 组(覆盖 58 / 转录逐项相等 / 反向缺项 / 豁免缺席);
  新增 client/test/dark_theme_test.dart 并登记进 scripts/test.sh

关键约束:深色 tertiary 保持 #8A93A3,显式不同步浅色点修值 #646D7D;
浅色 18 品牌原色与 Core 45 项一个不动;E-Ink 仍纯黑白 / 圆角 0 / 无动画,
看板以 Theme(data: einkTheme()) 局部覆盖,与 themeMode 解耦。

验证:Dart 全量 +627 All tests passed(基线 610,新增 17)、
定向 35/35、run-guards.sh 5 套件 PASS 5 / FAIL 0、dart analyze 9 issues
(2 warning + 7 info,0 error,与基线一致)。

已知边界:本轮仅 ThemeData 层深色皮肤。页面自绘卡片/文字仍直接引用静态
PmonColors.* / PmonText.* / PmonCard.*(11 / 9 / 4 个文件),不走 ThemeData,
深色下整屏仍呈浅色;整屏页面级深色化需改为 Theme.of(context) 查询,
属独立后续阶段。Web localStorage 与 Flutter AppConfig 为两端独立持久化。
2026-11-08 21:16:30 +08:00
PMon Dev e7481a43d9 feat(ui): 墨观 InkWatch 大前端设计系统落地(跨端令牌 + 深色皮肤 + 管理台/Flutter 同步)
将单端 Flutter 浅色令牌升级为覆盖 Web 桌面 / 移动 PWA / macOS 桌面 /
数据大屏的统一 --iw-* 令牌层,并落地到管理台与 Flutter 客户端。

新增
- server/business/admin/assets/tokens.css:Core 45 项 + 四端派生 +
  深色 58 个同名键覆写 + E-Ink 独立分支;color-scheme: light 锁定,
  深色由 html[data-iw-theme] 显式控制,不跟随系统
- 管理台 6 页引入 tokens.css 与首屏防闪脚本(7 处逐字一致)
- admin.js 主题控制器(inkwatch.theme / storage 跨标签 / E-Ink 互斥)
  与两处 ThemeToggle
- client/test/token_parity_test.dart:CSS :root 与 Dart theme.dart
  双向护栏,webOnly 白名单,深色键不进 parity
- tests/phase3b-static-guard.js:静态护栏(零硬编码色值 / 键名 / 层叠)

收敛
- admin.css 删除旧 :root 18 变量与全部硬编码色值,全量改为 var(--iw-*);
  10px/14px 圆角收敛到 8/16,删除 filter: brightness,
  Toast 文字改用 var(--iw-surface) 以适配深色反相
- Flutter:PmonColors 补 6 个 *-text,statusStyle() 扩为 4 字段,
  PmonText.micro,刻度收敛(6/10/10.5/11.5px 收敛到 8/11px)
- 浅色辅助文字色 #8A93A3 点修为 #646D7D(WCAG AA:白底 5.22:1 /
  page 4.87:1 / muted 4.74:1);--iw-chart-axis 同步副本。
  这是 Core 45 项中唯一被点修的取值,18 个品牌原色一个不动;
  深色 58 键与 E-Ink 分支显式不同步。依据 08 裁决(皇上批准路径 1)

验证
- Dart 610/610、定向护栏 18/18、静态护栏 87/0、Docker 53/0、Schema 116/0
- 管理台 6 页 x 浅/深 目视 217/0,12 张截图
- 设计系统侧:axe 42/42 格 violations=0、真 1920 视口 A-01~A-20 20/20、
  verify 15/15、tsc 0 errors、file:// 直开 exit 0
- 两份 tokens.css sha256 一致,git diff --check 通过

本轮 Flutter 不引入深色 ThemeData;E-Ink 保持纯黑白 / 圆角 0 / 无动画。
2026-11-08 19:47:55 +08:00
PMon Dev 5a860cd555 feat(eink): 水墨屏专属版面 —— 横屏默认 / 极简 / 字号统一(1.1.1)
<user> 2026-09-16 的四条要求,逐条落地并各有可执行断言:

① 横屏为默认、可切竖屏
   · 新增配置 einkLandscape(默认 true);看板里的「转屏」按钮切换并落盘
   · 屏幕方向由宿主锁(SystemChrome),看板只发指令、保持"纯展示"以利测试
   · 列数按**可用空间宽高比**决定(宽→3 列 / 高→2 列),不写死方向 ——
     Flutter 只拿得到一个矩形,分屏/折叠/多窗口下方向与矩形并不一致

② 聚焦核心数据、隐藏次要信息与冗余控件
   · 在线时顶部只有「实心圆 + 设备名」:形状已表达在线,
     "在线"二字与"数据新鲜"都是冗余;一旦离线/疑似/未知才把状态与原因铺出来
   · 底部两行说明合并为一行("最后刷新 xx" + "每 N 秒自动刷新" → "xx · 每 N 秒")

③ 对比度与刷新
   · 延续零动画(静止等待图标、NoSplash、无无限动画),有源码结构断言守着
   · 支撑"永不溢出":横屏/竖屏/极窄/极矮/极宽多套尺寸全部覆盖

④ 统一字体与样式(避免字号大小不一)
   · PmonEinkText 收敛为 5 档(label 与 action 同号,靠字重而非字号区分);
     界面里**不得写就地字号**,有源码断言(`fontSize: 数字` 即红)
   · 数值改为「大数值 + 小单位」:此前 `1.42 Mb/s` 整串按 40px 渲染宽 360px,
     而 3 列时每格仅 207px → 统一比例被拖到 0.575,**一屏数值全被它压小**
   · 全屏数值共用**同一个**缩放比例(按最长那条算);
     并改用**实测字形二次收敛**替代纯线性估算(渲染期文本宽度非线性,
     实测在极窄格 + 极长值时溢出 3.7~9.3px);最外层留一层 FittedBox 兜底,
     保证"绝不裁掉内容"(正常尺寸下它不生效,同号不受影响)
   · 数值加**无损紧凑化**(`310.00` → `310`):只删数学上无信息的零尾,绝不四舍五入

实测(F50 代跑):
  · 横屏 3 列 × 2 行、大数值 + 小单位、数值明显大于标签 —— 见 dist/水墨屏走查/03
  · 竖屏**未能在 F50 干净验证**(它的 wm size/density 覆盖 + 强制旋转使 Flutter 视口
    变成近正方形,于是按设计走了 3 列)。竖屏布局由 widget 测试覆盖
    (536×724 → 2 列),待真机(Neo2)上机复核。

测试:客户端 321/321;其中 eink_board_test 32 项。
变异测试:把"统一缩放"退回"逐格 FittedBox"会被"渲染高度一致"那条抓住。

主干 main 不受影响 —— 本分支相对 main 只新增、未修改任何主干代码。
2026-09-16 01:27:49 +08:00
PMon Dev b585d780e0 feat(eink): 水墨屏专属版本(iReader Neo 2 副屏)
从主干(1.0.9)长出,**只新增、不改动主干**。本提交带过来的是
已交付的水墨屏看板 1.1.0 那批改动(下一步会在此基础上做横屏与统一字号):

  · ui/eink_board.dart     单台设备大字看板(6 大格、一屏放下)
  · ui/eink_mode.dart      全局零动画开关 + 静止等待指示
  · ui/theme.dart          PmonEink 三档明度令牌 + statusGlyph 形状化状态
  · host/app_config.dart   einkMode / einkRefreshSec(默认关,不影响彩色版)
  · 平台通道               setKeepScreenOn(副屏常亮)
  · 刷新节流               数据每秒到,只在间隔到点重建

之后本分支会继续做:横屏为默认、竖屏可切、隐藏次要信息、统一字体与样式。
主干(main)不含以上任何代码。
2026-09-16 01:06:51 +08:00
PMon Dev f632672a82 feat: PMon V4.0 功能开发收口(1.0.9)—— 主干
把 V4.0 骨架以来的全部功能开发固化为主干基线,内容包括:
  · client/            全量 Android/macOS 客户端(Flutter + Rust 核心)
  · server/            网关、业务服务、仓储层
  · deploy/            compose、Dockerfile、init.sql + migrate.sql(幂等迁移)
  · shared/ tests/     协议、编解码、指标清单与全部测试套件
  · docs/ README       架构方案与运维手册

本轮之前修复的主要缺陷(详见各文件注释与 client/README.md):
  · 首次部署路径(init.sql 的 device_tokens 建表顺序)
  · 每 20 分钟硬切以外的设备身份漂移与**设备归并**(shared/device-identity.js)
  · 配置无版本号、无校验导致的病态残留(host/config_migration.dart)
  · 数据库迁移从不执行(deploy/migrate.sql + compose 的 migrate 服务)

验证:客户端 282/282、全量回归 20 套件 0 失败。

⚠️ 水墨屏专属实现(看板、水墨屏令牌、einkMode、常亮)**不在本分支**,
   由 feature/eink 承载,主干不受其影响。
2026-09-16 01:06:24 +08:00