跨端: 补齐「我的」页深色可读性(4 页 219 段全 ≥3:1)+ 判据自己漏报的那一类

上一条把通信/日历/联系扫干净了,但**「我的」页没扫到**
(扫描脚本按文案点不到它 —— 那是侧栏**底部的头像按钮**,不是导航项)。
补上后立刻又抓出问题,并把判据自身的**漏报形状**一并修了。

## 一、「我的」页两处真 bug

1. **主题分段按钮 `SettingsPage.ets:834`**:
   `.fontColor(... ? Theme.surface : Theme.textPrimary)` —— `surface` 是
   **会翻转的面色**,压在 `Theme.accent` 蓝底上,深色下变成近黑。
   设备读数:`深色 2.93:1  ink rgb(32,34,36) bg rgb(34,96,228)`。
2. **权限档徽标 `MailDetailPage.ets:391`**:同一个错法(第 13 处)。
3. **账号名 `SettingsPage.ets:1179`**:三元里的裸 `Theme.accent`
   压在 `accentSoftFor()` 底上 ⇒ `jianf 2.83:1`。
   `AdminUsersPage.ets:386` 同形状(角色徽标)。

## 二、判据自己的漏报形状(比 bug 本身更值得记)

静态防线(`C|会翻转的 Resource 不得当前景色`)**在 bug 存在时是绿的**。
原因是它的提取正则:

    /\.fontColor\(Theme\.([A-Za-z0-9_]+)\)/      ← 要求令牌是**唯一实参**

于是**三元里的令牌全被漏掉**:

    .fontColor(this.appearanceTheme === t ? Theme.surface : Theme.textPrimary)

改成"在 `fontColor(` 之后的整段实参里找所有 `Theme.X`"之后,
它**立刻报出上面第 1、2 两处**(此前一直绿)。

★ **判据漏报的常见形状是它自己的正则太窄,而不是被测代码太隐蔽。**
  这条写成注释留在判据里了。

## 三、我自己犯的批量替换错误(已加判据钉住)

把裸 `Theme.accent` 换成 `accentFor()` 时,**误把 6 处 `backgroundColor` 也换了**。
`accentFor()` 深色给浅蓝 `#80AFF9`,而搭档前景是白色 `accentFg`
⇒ 白字压浅蓝 ≈ **1.4:1**,主按钮文字会彻底看不见。

- 6 处已逐处还原(复核:`grep -c "backgroundColor(Theme.accentFor())"` = 0)。
- 新增判据 **`C2`**:`accentFor / dangerFor / approveFor / warnFgFor / textSubtleFor`
  **只能用于前景**,`backgroundColor(Theme.XFor(...))` 直接判红。
  (`accentSoftFor` 是例外 —— 它本来就是"面"。)
  ★ 修法不是"下次小心点":批量替换一定会再犯,**一行判据把它变成不可能**。

## 四、设备复扫(修后)

```
[通信]      扫 42 段,低对比 0
[日历]      扫 85 段,低对比 0
[联系人]    扫 51 段,低对比 0
[我的]      扫 41 段,低对比 0      ← 新增
```
**219 段文字全部 ≥3:1**(本轮全部针对深色)。

## 五、底本

`baseline.sha` 第 5 次重算。重算前**专门复核**了"那 6 处误改有没有残留"
(不只看 `git diff` 非空就放行):`backgroundColor(accentFor)` 计数为 0、
`git diff HEAD~1` 里 backgroundColor 只有 calendar 那一处(有意改动)。

`run-all.mjs` → `checks=515 pass=515 fail=0 skip=0 red=0 broken=0 unreported=0`。

**未验**:浅色主题下的对比度没扫(本轮全部针对深色)。
This commit is contained in:
2026-09-19 20:43:59 +08:00
parent 4ecddfc2b6
commit 1da4e15a7b
9 changed files with 133 additions and 18 deletions

View File

@ -383,7 +383,8 @@ struct AdminUsersPage {
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
this.Chip(user.role === 'admin' ? '管理员' : '用户',
user.role === 'admin' ? Theme.chipNeutralBg : Theme.surfaceMuted,
user.role === 'admin' ? Theme.accent : Theme.textMuted)
/* ★ 2026-09-19 修:三元里的裸 `Theme.accent`(同批 2 处,见 SettingsPage 那条)。 */
user.role === 'admin' ? Theme.accentFor() : Theme.textMuted)
Text(' ').width(4)
this.Chip(user.status === 'active' ? '启用' : '禁用',
user.status === 'active' ? Theme.approveBg : Theme.surfaceMuted,

View File

@ -1245,12 +1245,12 @@ export struct CalendarPage {
*/
Text('导入')
.fontSize(Theme.fontSmall)
.fontColor(this.icsBusy ? Theme.textSubtle : Theme.accent)
.fontColor(this.icsBusy ? Theme.textSubtle : Theme.accentFor())
.margin({ left: 8 })
.onClick(() => { this.importIcs(); })
Text('导出')
.fontSize(Theme.fontSmall)
.fontColor(this.icsBusy ? Theme.textSubtle : Theme.accent)
.fontColor(this.icsBusy ? Theme.textSubtle : Theme.accentFor())
.margin({ left: 8 })
.onClick(() => { this.exportIcs(); })
/*

View File

@ -254,7 +254,7 @@ struct ComposePage {
Row() {
Text(acct.displayName)
.fontSize(13)
.fontColor(this.selectedAccountId === acct.id ? Theme.accent : Theme.textPrimary)
.fontColor(this.selectedAccountId === acct.id ? Theme.accentFor() : Theme.textPrimary)
.fontWeight(this.selectedAccountId === acct.id ? FontWeight.Bold : FontWeight.Normal)
.layoutWeight(1)
if (this.selectedAccountId === acct.id) {

View File

@ -388,7 +388,13 @@ export struct MailDetailView {
ForEach(['plan', 'workspace', 'full'], (mode: string) => {
Text(permissionLabel(mode))
.fontSize(11)
.fontColor(this.permissionMode === mode ? Theme.surface : Theme.textMuted)
/*
* ★ 2026-09-19 修:选中态前景是 `Theme.accentFg`(白),不是 `Theme.surface`。
* `surface` 是**会跟随主题翻转的面色**(浅色近白 / 深色近黑)——
* 压在 `Theme.accent` 蓝底上,浅色下碰巧对、**深色下变成近黑** ⇒ 字看不见。
* 这是本仓同一个错法的第 N 次出现(同批一共修了 12 处)。
*/
.fontColor(this.permissionMode === mode ? Theme.accentFg : Theme.textMuted)
.backgroundColor(this.permissionMode === mode ? Theme.accent : Theme.chipNeutralBg)
.borderRadius(4)
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
@ -896,7 +902,7 @@ export struct MailDetailView {
/* 「抄送」是个**折叠开关**(WebUI 同形:默认收起,点它才出现输入框) */
Text(this.forwardCcOpen ? '收起抄送' : '抄送')
.fontSize(11)
.fontColor(this.forwardCcOpen ? Theme.accent : Theme.textMuted)
.fontColor(this.forwardCcOpen ? Theme.accentFor() : Theme.textMuted)
.onClick(() => { this.forwardCcOpen = !this.forwardCcOpen; })
}
.width('100%')

View File

@ -438,7 +438,7 @@ struct InboxTab {
if (this.showAccountPicker && this.accountList.length > 1) {
Column() {
Text('全部邮箱')
.fontSize(14).fontColor(this.accountFilter === 'all' ? Theme.accent : Theme.textPrimary)
.fontSize(14).fontColor(this.accountFilter === 'all' ? Theme.accentFor() : Theme.textPrimary)
.fontWeight(this.accountFilter === 'all' ? FontWeight.Bold : FontWeight.Normal)
.width('100%').height(40).padding({ left: 16 })
.backgroundColor(this.accountFilter === 'all' ? Theme.accentSoftFor(this.isDarkNow) : Theme.surface)
@ -453,7 +453,7 @@ struct InboxTab {
Column() {
Text(acct.displayName)
.fontSize(14)
.fontColor(this.accountFilter === acct.id ? Theme.accent : Theme.textPrimary)
.fontColor(this.accountFilter === acct.id ? Theme.accentFor() : Theme.textPrimary)
.fontWeight(this.accountFilter === acct.id ? FontWeight.Bold : FontWeight.Normal)
Text(acct.username)
.fontSize(11).fontColor(Theme.textSubtleFor())
@ -1384,7 +1384,7 @@ struct CommPage {
Text(commTabLabel(key))
.fontSize(13)
.fontWeight(this.commTab === key ? FontWeight.Bold : FontWeight.Normal)
.fontColor(this.commTab === key ? Theme.accent : Theme.textMuted)
.fontColor(this.commTab === key ? Theme.accentFor() : Theme.textMuted)
if (badgeText(badgeCount(key, this.unreadCount, this.pendingCount)).length > 0) {
Text(badgeText(badgeCount(key, this.unreadCount, this.pendingCount)))
.fontSize(10).fontColor(Theme.accentFg)

View File

@ -831,7 +831,14 @@ export struct SettingsPane {
ForEach(['system', 'light', 'dark'], (t: string) => {
Text(t === 'system' ? '跟随系统' : (t === 'light' ? '浅色' : '深色'))
.fontSize(13)
.fontColor(this.appearanceTheme === t ? Theme.surface : Theme.textPrimary)
/*
* ★ 2026-09-19 修:同上 —— 选中态压在 `Theme.accent` 蓝底上,
* 前景必须是**不翻转**的 `accentFg`,不能用 `surface`。
* ★ 这处是".fontColor(三元)"形状,判据的第一版正则只认
* `.fontColor(Theme.X)`(令牌是唯一实参)⇒ **漏掉了它**,
* 直到设备扫描报出「深色 2.93:1 ink rgb(32,34,36) bg rgb(34,96,228)」。
*/
.fontColor(this.appearanceTheme === t ? Theme.accentFg : Theme.textPrimary)
.backgroundColor(this.appearanceTheme === t ? Theme.accent : Theme.surfaceMuted)
.borderRadius(Theme.radiusControl)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
@ -1169,7 +1176,9 @@ export struct SettingsPane {
Row() {
Text(account.displayName)
.fontSize(15).fontWeight(FontWeight.Bold)
.fontColor(account.id === this.activeId ? Theme.accent : Theme.textPrimary)
/* ★ 2026-09-19 修:三元里的裸 `Theme.accent` —— 批量替换漏了这种形状(同批共 2 处)。
* 深色下压在 `accentSoftFor()` 底上只有 2.83:1(设备实测)。 */
.fontColor(account.id === this.activeId ? Theme.accentFor() : Theme.textPrimary)
if (account.id === this.activeId) {
Text(' 默认')
.fontSize(11).fontColor(Theme.accentFor())