Files
MailUI4Agents/client/electron/test/manual
JianFeeeee 76ce201c3a feat(webui): 导航合并 + 悬浮玻璃 + 页面动画 + 控件档透明度 + 圆角无条件生效
用户四封信(同一线索)提的六件事,都在这里:

## ① 导航合并(「导航栏的内容有点多了」)

收件箱/发件箱/授权 → 一项「通信」,进去由**内部页签**区分(CommTabs);
新建 → 「通信」页内**悬浮圆形加号**(ComposeFab);导航只剩 通信/日历/联系人;
管理 → 合进「我的」,管理员在页面**最下面**看得到(非管理员不渲染,不是禁用)。

`viewMode` 仍是原来那三个值(几十处调用点不用改),新增 `commTab` 记住上次用的子页签;
通信项的徽标 = 未读 + 待决策(授权信息不能因为合并而消失)。

## ② 窄屏底部导航:悬浮玻璃(「还是固定底部延伸,没有玻璃效果,也没有悬浮」)

原来是 `border-t bg-chrome-900` 通栏贴底。改成:左右/离底各留 10px、圆角、
深色半透明 + `blur(18px)`、投影,安全区并入下外边距。

## ③ 页面动画(「页面极度缺少动画,所有页面都是直接出现」)

在 `<html>` 上挂一个短命类触发外壳直接子面板的入场动画。**没有用 key 重挂载**:
面板是 flex 链上的一环,套 wrapper 会改掉 flex 传递(窄屏覆盖层最先坏)。
并且尊重 `prefers-reduced-motion`(关了就一点都不动)。

## ④ 控件档透明度(「复选框和地址猜测项…他们才是真正需要拉低透明度的地方」)

新增第三档 `--bg-glass-control`(0.5/0.55):授权多选胶囊、地址建议菜单。
三档递减:正文面 0.88 > 嵌套 0.82 > 控件 0.5。

## ⑤ 打底 vs 透明(上一封「该打底的地方透了、该透的空白处糊了」)

正文面回到 0.88(读得清优先)、空白/页面底透明**且不模糊**、面板模糊降到 10px。

## ⑥ 圆角与玻璃**无条件**生效(「大面积缺失圆角与玻璃效果…都是硬截断」)

根因:`.app-shell` 的留缝/圆角/投影原先全写在 `html[data-bg='on']` 里
⇒ **没开壁纸的账号**看到的是硬边不透明面板。现在几何下沉为无条件基线,
壁纸相关的加强仍叠加在上面。窄屏内容面板同样浮起来。

## 判据

- `test/nav-merge.test.mjs` 8 条(含自检:重构前的写法必须判红)——
  改完跑老套件 254 条**全绿却一条都没测导航结构**,结构改动必须自带判据。
- `test/manual/nav-restructure-verify.mjs`:真浏览器 9 条(导航项数、页签切换真的换内容、
  加号 56×56 且 `elementFromPoint` 命中自己、我的页底部管理入口在退出登录之后、
  控件档 α=0.5 对正文面 α=0.88 的反向对照)。
- `test/manual/narrow-glass-verify.mjs` 6 条:悬浮几何(三边留缝 10px、圆角 14px、
  缝隙处命中的是页面底 ⇒ 真的浮着)、玻璃(α=0.82 + blur18)、触摸高度最小 49px、
  动画真的在跑(静止时 `animationName=none`,切换后 =pane-in)且 reduced-motion 下不动。
- `test/background.test.mjs`:把三条**过时判据**(早前那版"越透越好")按现行契约重写 ——
  旧判据留着只会把我拽回错误方向。30 条全绿。

## 顺带

窄屏探针原先只调视口、**没模拟触屏**,于是 `(hover: hover)` 仍为真,
把「悬停才显形的次要动作」误报成透明 —— 差点去"修"一个本来就对的规则。
已改成 hasTouch + isMobile + DPR 的触屏上下文,且收尾只关自己的 context
(CDP 连的是共享 Chromium,`browser.close()` 会把别人的标签页一起关掉)。
2026-09-14 10:35:05 +08:00
..

手工浏览器实测脚本

不进 npm test —— 它们需要一个跑着的 Chromium 与一个活的 Gateway。 日常回归靠 ../narrow-layout.test.mjs(读源码验形态,无外部依赖)。

为什么两套都要

结构性断言守住「代码写成了什么形态」,量不出「按钮实际多大、点下去命中谁」。

窄屏那轮修复里最严重的一个 bug 是抽屉式侧栏(fixed ... z-50 铺满视口高度) 把底部导航最左那一项盖住 —— 按钮在那里、尺寸也够、md:hidden 之类的规则也 没写错,只有 elementFromPoint 才能发现它命中的是抽屉里的 SVG

用法

# 窄屏390pxiPhone 14 Pro+ 320pxiPhone SE
ADMIN_PW=<密码> npm run test:narrow

# 宽屏回归:窄屏修复不能把桌面改坏
ADMIN_PW=<密码> npm run test:wide

环境变量:

变量 默认 说明
ADMIN_PW 无(必填) 管理员密码
ADMIN_USER admin 登录用户名
AGENTMAIL_URL https://mail.jianfgit.xyz 目标地址
CDP_URL http://127.0.0.1:9222 浏览器 CDP 端点
PLAYWRIGHT /usr/lib/node_modules/playwright/index.mjs playwright 入口

浏览器用的是本机 systemd 托管的共享 Chromiumhomeagent-browser.service 通过 CDP 连上去开自己的标签页,用完关掉。没有它时先 systemctl start homeagent-browser

文件

文件 作用
narrow-probe-helper.mjs 连浏览器、登录、量盒子/溢出/命中区/命中测试
narrow-verify.mjs 窄屏 13 项验收
wide-regression.mjs 宽屏 5 项回归
inbox-group-verify.mjs 收件箱按会话分组
theme-verify.mjs 深浅两色的 WCAG 对比度
accent-verify.mjs 强调色(红/绿/橙/黄/蓝17 组配色,两模式各一遍
desktop-phase3-verify.mjs 桌面客户端:写信 + 附件 + 权限面板(见下)

narrow-probe-helper.mjs 里两个函数值得单独知道:

  • tapTargets(page, labels) —— 量 .tap 按钮的真实命中区(::after 伪元素的尺寸)。.tap 刻意不改变视觉尺寸,所以只看 boundingBox 会误判成偏小
  • hitTest(page, selector) —— 每个元素点下去是否命中自己。遮挡类 bug 只能这样查

accent-verify.mjs 存在的理由是一次真实事故:tailwind.config.jscolors 里同时写了固定 hex 与 accent() 两份 red/green/amber/orange/yellowJS 对象 字面量重复键后者胜出(不报错),而 index.css 当时没有对应的 --c-red-* 变量。rgb(var(--c-red-600) / 1) 里变量未定义 → 整条 background-color 声明 失效 → bg-red-600 退回透明、text-white 的白字落在白卡片上: 按钮看不见但点得动。所有静态检查都过,只有肉眼能发现。

因此这个脚本量的是实际计算值:它把类名注入真页面、读 getComputedStyle 把「背景透明」单独判为失败(那正是上述 bug 的指纹),再算 WCAG 对比度。

只以 hover: 变体出现的档(bg-red-700 / bg-blue-700不能放进探针: Tailwind 不生成未被使用的基础类,探它必然得到透明背景 —— 那是假阳性。 它们由 ../theme.test.mjs 的档位断言覆盖。

桌面客户端那一个(desktop-phase3-verify.mjs

它不连共享浏览器,而是自己起打包好的 Electron 应用xvfb + --remote-debugging-port 然后走一条贯穿全流程的链:

ADMIN_PW=<密码> DESKTOP_BIN=release/linux-unpacked/agentmail-web \
  node test/manual/desktop-phase3-verify.mjs
  1. 用桌面 UI 写一封信、带一个附件,发给 zcode
  2. 外部(直接打网关 API核验信真的在、附件真的挂着 —— 界面说「已发送」不算证据
  3. 这封信让 zcode 触发一次真实的授权请求(执行门禁)
  4. 在桌面的授权面板里点「同意」
  5. 外部核验:决策被记录 Agent 真把命令执行了(标记文件出现)

第 5 步是这条链的重点:它证明界面上的那一下点击真的走到了 Agent 那侧。 只验界面变成「已同意」的话,一个只在本地改状态、根本没提交给网关的实现也能全绿。

第一条判据是「应用渲染出内容了吗」(#root 有子节点)—— 白屏时后面每一条都会 以奇怪的方式失败,而真正的原因只以一条资源错误出现。这个脚本第一次跑就靠它抓到了 base 那个白屏缺陷(见 ../packaging.test.mjs)。

已知限制

headless Chromium 报告 hover: none,因此 .reveal(只在支持悬停的设备上隐藏) 在这里永远是可见的 —— 脚本只能验「触摸设备上可见」这一半, 「鼠标设备上隐藏」那一半靠 ../narrow-layout.test.mjs 检查 CSS 规则存在。

没有像素级视觉比对:字体差异下极脆,维护成本高于收益。