判据: 把「面当字」的防线从死名单改成判矛盾(三次迭代都记下来了)
上一版加的静态防线只 grep `fontColor(Theme.surface)` —— **一个名字**。 那是死名单:以后加了新令牌它不会跟上(本仓已记录过多次这类错法)。 改成从源码推: **条件 A**:值是 `Resource` 且指向 `sys.color.*` ⇒ **跟随系统主题翻转** **条件 B**:既当 `backgroundColor` 又当 `fontColor/iconColor` ⇒ **被当成「面」也被当成「字」** 判决 = **A ∩ B**,不需要维护任何名单。 ## 三次迭代(每次错得很典型,所以都写进注释了) - **① 死名单**(只盯 `surface`):新增令牌不会跟上。 - **② 只要 A**("是翻转的 Resource 就不许当前景色"):**太宽** —— 实测立刻 报出 20+ 处"违规",全是 `textMuted` / `textSubtle` / `textPrimary`。 那些**本来就该**当前景色(语义就是文字色,跟随主题翻转是对的)⇒ 假红一片。 - **④ 只要 B**("矛盾就报"):**也宽** —— `accent` / `danger` / `approve` / `warnFg` 两边都用是正常设计(蓝底白字主按钮 + 白底蓝字返回箭头)。 和 `surface` 的**关键区别**:那些是**饱和的字符串常量**(不翻转、语义是"色"), 而 `surface` 是**中性面 + 跟随主题翻转**(当字色 = 把底板当墨水,深色下必然看不见)。 - **最终 = A ∩ B**:恰好命中 `surface` 这一类,且**不随新增令牌失效**。 允许清单现在是**空表**(修完 `surface` 后没有例外), 并写明「已审过、不需要进这张表的」那几个令牌与理由。 ## 变异验证(两个都做了,第二个是关键) 1. 把一处 `.accentFg` 改回 `.surface` ⇒ **判红**;还原 ⇒ 绿。 2. **造一个判据从没见过的新令牌**(`surfaceAlt`,同样 `Resource` + `sys.color.*`), 让它同时当背景与前景 ⇒ **判红**(报文里点名 `Theme.surfaceAlt`);还原 ⇒ 绿。 —— 这条比 ① 重要:它证明判据**不是靠记住 `surface` 这个名字**在工作。 ## 顺带确认 `pageBg` / `surfaceMuted` / `wallpaperScrim` / `border` / `overlay` 这五个 同类"会翻转的面"**一处都没被当字色用过** —— 这一类现在全清了。 `run-all.mjs` → `checks=513 pass=513 fail=0 skip=0 red=0 broken=0 unreported=0`。
This commit is contained in:
@ -1051,21 +1051,48 @@ test('★ 设备:品牌色**真的画成那个色**(令牌写对了 ≠ 渲
|
||||
});
|
||||
|
||||
|
||||
test('C|`Theme.surface` 不得当代的前景色(它是会翻转的「面」)', () => {
|
||||
test('C|「会跟随主题翻转的 Resource」不得当前景色(含矛盾信号)', () => {
|
||||
/*
|
||||
* ★★ 这是设备判据(上面那条)抓到的 bug 的**静态防线** ——
|
||||
* 设备条只能看一处(悬浮球),而这个错法当时有 **12 处**。
|
||||
*
|
||||
* 错法:`Theme.surface` 是 `sys.color.ohos_id_color_list_card_bg`,
|
||||
* **跟随系统主题翻转**(浅色近白 / 深色近黑)。把它当
|
||||
* `fontColor` / `iconColor`("压在彩色底上的字"):
|
||||
* 错法(真实现场):`Theme.surface` 是
|
||||
* `sys.color.ohos_id_color_list_card_bg` —— 一个**跟随系统主题翻转**的
|
||||
* Resource(浅色近白 / **深色近黑**)。它被当成"压在彩色底上的字/图标色"用:
|
||||
* · 浅色下碰巧对(白字压蓝底)
|
||||
* · **深色下字变黑**,压在蓝/红/绿底上几乎看不见
|
||||
* · **深色下字变黑**,压在品牌蓝/红/绿底上几乎看不见
|
||||
* 正确的前景色是 `Theme.accentFg`(`'#FFFFFF'`,字符串常量,不翻转)。
|
||||
*
|
||||
* 正确:`Theme.accentFg`(#FFFFFF,注释原话就是"品牌底上的字/图标")。
|
||||
* ★★ 判据形状的**三次迭代**(都写下来,因为每一次都错得很典型):
|
||||
*
|
||||
* 判据形状:全局 grep「`fontColor(Theme.surface)` / `iconColor: Theme.surface`」,
|
||||
* 一处都不许有。`backgroundColor(Theme.surface)` 是合法用法(那才是"面")。
|
||||
* ① grep `fontColor(Theme.surface)` —— **一个名字**。
|
||||
* 错在这是死名单:以后加了新令牌它不会跟上(本仓已记录过多次)。
|
||||
*
|
||||
* ② 从 `Theme.ets` 读"所有 `sys.color.*` 的 Resource",一律不许当前景色。
|
||||
* **太宽** —— 实测立刻报出 20+ 处"违规",全是
|
||||
* `Theme.textMuted` / `textSubtle` / `textPrimary`。
|
||||
* 那些**本来就该**当前景色(它们的语义就是文字色,跟随主题翻转是对的)。
|
||||
* ⇒ 只按"是不是 Resource"判,把正确用法也判红 ⇒ 假红一片。
|
||||
*
|
||||
* ③(本版)判**矛盾**:同一令牌既当 `backgroundColor` 又当
|
||||
* `fontColor/iconColor` ⇒ 它同时被当成"面"和"字",其中一种必然错。
|
||||
* 这个信号从**实际用法**推出,不依赖任何名单。
|
||||
*
|
||||
* ★ 但④实测又发现它**太宽**:`accent` / `danger` / `approve` / `warnFg`
|
||||
* 这些**饱和的字符串常量**色两边都用是正常设计(蓝底白字的主按钮 +
|
||||
* 白底蓝字返回箭头)。它们和 `surface` 的**关键区别**是:
|
||||
*
|
||||
* · `surface`(危险):**中性面**(近白/近黑,"承载内容的底板")
|
||||
* + **跟随主题翻转**。当字色 = 把底板当墨水 ⇒ 深色下看不见。
|
||||
* · `accent`(正常):**饱和色**(品牌/语义身份)+ **字符串常量**
|
||||
* (不翻转)。两边都站得住。
|
||||
*
|
||||
* ⇒ 最终判据 = **翻转的 Resource** ∩ **既当背景又当前景**。
|
||||
* 两个条件都要,缺一个都会假红:
|
||||
* · 只要"翻转" ⇒ 把 `textPrimary` 判红(假红)
|
||||
* · 只要"矛盾" ⇒ 把 `accent` 判红(假红)
|
||||
* 两个都要 ⇒ 恰好命中 `surface` 这一类,且**不随新增令牌失效**
|
||||
* (新令牌只要符合这个形状就会被抓,不需要维护名单)。
|
||||
*/
|
||||
const files = [];
|
||||
const walkDir = (d) => {
|
||||
@ -1077,21 +1104,62 @@ test('C|`Theme.surface` 不得当代的前景色(它是会翻转的「面」
|
||||
};
|
||||
walkDir(HARMONY_ETS);
|
||||
|
||||
const bad = [];
|
||||
/*
|
||||
* 条件 A:从 `Theme.ets` 读出**会翻转的**令牌 —— 值是 `Resource`
|
||||
* 且指向 `sys.color.*`(跟随系统主题)。
|
||||
*/
|
||||
const themeSrc = prose(join(HARMONY_ETS, 'common/Theme.ets'));
|
||||
const flip = new Set();
|
||||
for (const m of themeSrc.matchAll(
|
||||
/static readonly ([A-Za-z0-9_]+)\s*:\s*Resource\s*=\s*\$r\('(sys\.color\.[^']+)'/g)) {
|
||||
flip.add(m[1]);
|
||||
}
|
||||
assert.ok(flip.size >= 5,
|
||||
`要从 Theme.ets 读出「会翻转的令牌」名单(实测 ${flip.size} 个)—— ` +
|
||||
'读出 0 个说明正则是错的,那样下面的检查恒绿。');
|
||||
|
||||
/* 条件 B:收集每个令牌"当背景 / 当前景"的首个出处 */
|
||||
const asBg = new Map();
|
||||
const asFg = new Map();
|
||||
for (const f of files) {
|
||||
const src = prose(f); // 保留注释:注释里举例不算错
|
||||
const lines = src.split('\n');
|
||||
lines.forEach((ln, i) => {
|
||||
if (/\.fontColor\(Theme\.surface\)/.test(ln) || /iconColor:\s*Theme\.surface/.test(ln)) {
|
||||
bad.push(`${f.replace(HARMONY_ETS, '')}:${i + 1} ${ln.trim().slice(0, 90)}`);
|
||||
const rel = f.replace(HARMONY_ETS, '');
|
||||
src.split('\n').forEach((ln, i) => {
|
||||
const where = `${rel}:${i + 1} ${ln.trim().slice(0, 80)}`;
|
||||
for (const m of ln.matchAll(/backgroundColor\(Theme\.([A-Za-z0-9_]+)\)/g)) {
|
||||
if (!asBg.has(m[1])) asBg.set(m[1], where);
|
||||
}
|
||||
for (const m of ln.matchAll(/\.fontColor\(Theme\.([A-Za-z0-9_]+)\)/g)) {
|
||||
if (!asFg.has(m[1])) asFg.set(m[1], where);
|
||||
}
|
||||
for (const m of ln.matchAll(/iconColor:\s*Theme\.([A-Za-z0-9_]+)/g)) {
|
||||
if (!asFg.has(m[1])) asFg.set(m[1], where);
|
||||
}
|
||||
});
|
||||
}
|
||||
assert.ok(asBg.size >= 5 && asFg.size >= 5,
|
||||
`要能从源码读出背景色/前景色两边的用法(实测 ${asBg.size} / ${asFg.size})—— ` +
|
||||
'有一边读出 0 个说明正则是错的,那样下面的"交集"恒空、判据恒绿。');
|
||||
|
||||
assert.deepStrictEqual(bad, [],
|
||||
'★ `Theme.surface` 是**会跟随系统主题翻转的面色**(浅色近白 / 深色近黑),' +
|
||||
'只能当背景,不能当字/图标色。\n' +
|
||||
' 压在彩色底上的前景要用 `Theme.accentFg`(#FFFFFF)。\n' +
|
||||
' 深色下症状:字/图标几乎看不见(2026-09-19 真撞到 12 处)。\n' +
|
||||
' 违规处:\n ' + bad.join('\n '));
|
||||
/*
|
||||
* 判决 = A ∩ B ∩(不在允许清单里)。
|
||||
* 允许清单目前**空**:修完 `surface` 之后,没有任何"翻转的 Resource"
|
||||
* 被当成前景色用过。留空表比留理由表更好读 —— 它直接说明"没有例外"。
|
||||
*/
|
||||
const conflicts = [];
|
||||
for (const name of asFg.keys()) {
|
||||
if (!flip.has(name)) continue; // 条件 A:必须是会翻转的 Resource
|
||||
if (!asBg.has(name)) continue; // 条件 B:必须也被当背景用过(矛盾)
|
||||
if (ALLOW_BOTH_BG_AND_FG.has(name)) continue;
|
||||
conflicts.push({ name, bg: asBg.get(name), fg: asFg.get(name) });
|
||||
}
|
||||
|
||||
assert.deepStrictEqual(conflicts, [],
|
||||
'★ 这些令牌**同时**满足两个条件 ⇒ 是 `surface` 那一类错:\n' +
|
||||
' A. 会跟随系统主题翻转的 `sys.color.*` Resource(浅色近白 / 深色近黑)\n' +
|
||||
' B. 既当 `backgroundColor` 又当 `fontColor/iconColor` —— 被当成"面"也被当成"字"\n' +
|
||||
' 浅色下碰巧对,**深色下字/图标几乎看不见**(2026-09-19 真撞到 12 处)。\n' +
|
||||
' 压在彩色底上的前景请用 `Theme.accentFg`(#FFFFFF,字符串常量、不翻转)。\n' +
|
||||
' 若某个令牌**确实**两边都该用(有具体理由),加进 `ALLOW_BOTH_BG_AND_FG` 并写明理由。\n' +
|
||||
conflicts.map((c) => ` · Theme.${c.name}\n 当背景:${c.bg}\n 当前景:${c.fg}`).join('\n'));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user