跨端: 预设色板两套(pi:这不是"观感未验"而是机制上确定不同)+ 手写色清册跨文件 + TMPDIR 按会话分家 + 提交归属可判
pi 读完 `model/Wallpaper.ts` 后指出四处,全部处理。这一提交同时改了
`client/harmony/` 与 `client/electron/`(跨端改动),所以 subject 按新约定自报家门。
## 1 预设色板不随主题 —— **类别判错了:不是"未验",是机制上确定不同**
我上一版把"深色档预设"记成"观感未验"。pi 指出:WebUI 的 `.bg-preset-*` 写的是
`rgb(var(--c-blue-100))`,而 `--c-*` 在 `.dark` 里整体换了一套(blue-100 → `30 43 67`),
所以 **WebUI 的预设自动随主题变**;这边只有浅色那套 = 深色主题下"浅色渐变垫在深色系统表面之下",
正是这一整轮在治的病。**它不需要真机就能判**(机制写在代码里)—— 我把可判的东西
记成了"未验",这跟上一轮把"没做"写成"没验"是同一类错。
选 pi 倾向的那条(跟随主题,与 WebUI 一致):
- 色板两套:`LIGHT_*` 取 CSS `:root`、`DARK_*` 取 CSS `.dark`;`paletteFor(dark)` 选一套,
`layersFor(id, dark)` 按主题出层;
- `isDarkMode(theme, systemColorMode)` 放在纯逻辑里:选了 dark/light 就照办,
`system` 看系统当时的 `colorMode`(锚到 SDK:`COLOR_MODE_DARK = 0` / `COLOR_MODE_LIGHT = 1`;
读不到按浅色,与 WebUI 的 `:root` 默认一致);
- 系统深浅从 `resourceManager.getConfigurationSync().colorMode` 读
(`Context` 基类没有 `config`;`UIAbilityContext.config` 要转型;两个枚举取值一致,都核过 SDK);
- 判据:两套值与 `:root`/`.dark` **逐个相等**;每个预设的深浅两套**必须真的不同**
(否则"两套"是抄了两遍);网格线色也要换;`isDarkMode` 五种输入。
- **未做**:运行期间改系统深浅色不会自动重算(要重进页面)——系统侧正确做法是订阅
`applicationContext.on('environment', …)`,记在 §7.17b。
变异:`DARK_BLUE_100` 偏一位 → 红;`paletteFor` 永远返回浅色(= 我原来那个状态)→ 红;
`isDarkMode` 把系统深浅记反 → 红;页面把深浅写死成 false → 红。
## 2 手写色清册**跨文件按类扫**(原 A2 只保护 `Theme.ets`)
`Wallpaper.ts` 也有手写色。若对照是"按名字枚举"的,第 15 个色就会逃掉 ——
与 A2 要防的是同一件事,只是换了文件。现在一份清册按类扫:全 `ets/` 树里每个
`X: string = '#RRGGBB'` 都必须登记(Theme 的品牌/业务语义色,或预设色板 ——
后者常量名必须带 `LIGHT_`/`DARK_` 前缀,值由 CSS 两段比对负责)。反向也判清册过期。
变异:`Theme.ets` 加未登记色 → 红;`Wallpaper.ts` 加未登记色 → 红;
加一个"看着合规"的 `DARK_EXTRA` → 红。
## 3 `TMPDIR` 互踩(pi 提出)
这个 worktree 可能同时有多个 agent 跑构建,而 fpm 会把 291MB 的 `linux-unpacked`
**整份复制**进 `TMPDIR` —— 撞车就是随机的产物损坏。`whoami` 区分不开(大家都是 root),
所以按**会话**分家:`TMPDIR=$PWD/.tmp/${DSH_SESSION_ID:-$(id -un)-$$}`
(进了 `npm run build:linux` 与 BUILD.md 的手敲命令;普通终端退化成"用户+PID")。
## 4 提交归属变成**跑判据就看得出来**(pi 给的形状)
新的 `test/commit-hygiene.test.mjs`:扫最近 40 条提交,**同时改两侧目录**的提交
必须在 subject 里自报家门(`跨端:`)。两条防腐:基线 = 该判据文件自己的引入提交
(**历史不改**,规则管从今往后);分类逻辑拿合成输入自检
(未标注的混合提交必须判红、标注过的不许红)——否则"解析没跑起来"时它会全绿。
变异:`COMMIT_HYGIENE_BASELINE` 指到老提交 → 历史里那两个被卷进去的提交立刻判红。
## 验证
`hvigorw assembleHap` BUILD SUCCESSFUL;`npm test` 退出码 0
(12 个判据文件全绿 + vitest 258/258;`commit-hygiene` 在本提交落地后基线生效)。
This commit is contained in:
@ -574,3 +574,90 @@ test('C|「有意差异」表列全了允许不同的维度,并点名品牌
|
||||
assert.ok(rows.length >= 5, `差异表应有表头 + 至少 4 行,实际 ${rows.length} 行`);
|
||||
assert.ok(rows.every(r => r.split('|').filter(c => c.trim()).length >= 4), '差异表每行要说清"WebUI / 鸿蒙 / 为什么"');
|
||||
});
|
||||
|
||||
// ───────── 手写色清册**跨文件**(pi 2026-09-14:别一个文件一套枚举) ─────────
|
||||
|
||||
/** 从 index.css 的某个段(:root 或 .dark)里读出调色板变量 */
|
||||
function cssPaletteOf(selector) {
|
||||
const at = selector === ':root' ? web.indexOf(':root') : web.indexOf('.dark {');
|
||||
assert.ok(at >= 0, `CSS 里要有 ${selector} 段`);
|
||||
let depth = 0;
|
||||
let end = at;
|
||||
for (let i = web.indexOf('{', at); i < web.length; i++) {
|
||||
if (web[i] === '{') depth++;
|
||||
else if (web[i] === '}') { depth--; if (depth === 0) { end = i; break; } }
|
||||
}
|
||||
const body = web.slice(web.indexOf('{', at), end);
|
||||
const read = (name) => {
|
||||
const m = new RegExp(`${name}:\\s*(\\d+)\\s+(\\d+)\\s+(\\d+);`).exec(body);
|
||||
assert.ok(m, `${selector} 里要有 ${name}`);
|
||||
return '#' + [m[1], m[2], m[3]].map(n => Number(n).toString(16).padStart(2, '0').toUpperCase()).join('');
|
||||
};
|
||||
return {
|
||||
gray100: read('--c-gray-100'), gray200: read('--c-gray-200'),
|
||||
blue100: read('--c-blue-100'), blue200: read('--c-blue-200'),
|
||||
green100: read('--c-green-100'), amber100: read('--c-amber-100'), orange100: read('--c-orange-100')
|
||||
};
|
||||
}
|
||||
|
||||
test('★ 手写色清册**跨文件**:全 ets 树里每个 `X: string = \'#RRGGBB\'` 都要登记(不在某个文件里各搞一套枚举)', () => {
|
||||
/*
|
||||
* pi 的观察:A2 保护的是 `Theme.ets`,而 `Wallpaper.ts` 也有手写色(预设色板)——
|
||||
* 那儿靠"从 CSS 读出来逐个对照"抓到了 `#BFDCFE`,手法对;
|
||||
* 但如果那份对照是**按名字枚举**的,第 8 个预设色就会逃掉:
|
||||
* 这正是 A2 要防的同一件事,只是换了个文件。
|
||||
* 所以并成**一份清册、按类扫**:全树里每个手写色都得登记 ——
|
||||
* 要么是 Theme 的品牌/业务语义色,要么是"预设色板(值由 CSS 两段比对负责)"。
|
||||
* 这样"哪儿还能写死颜色"的答案是一处清册,而不是"看情况"。
|
||||
*/
|
||||
const tree = [];
|
||||
const walk = (dir) => {
|
||||
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = join(dir, e.name);
|
||||
if (e.isDirectory()) walk(full);
|
||||
else if (/\.(ets|ts)$/.test(e.name)) tree.push(full);
|
||||
}
|
||||
};
|
||||
walk(HARMONY_ETS);
|
||||
assert.ok(tree.length >= 20, `要扫整个 ets 树(至少 20 个文件),实际 ${tree.length}`);
|
||||
|
||||
const declared = [];
|
||||
for (const f of tree) {
|
||||
const rel = f.slice(HARMONY_ETS.length + 1);
|
||||
const src = readFileSync(f, 'utf8').replace(/\/\*[\s\S]*?\*\//g, '').replace(/^\s*\/\/.*$/gm, '');
|
||||
for (const m of src.matchAll(/(\w+)\s*:\s*string\s*=\s*'(#[0-9A-Fa-f]{6})'/g)) {
|
||||
declared.push({ file: rel, name: m[1], value: m[2] });
|
||||
}
|
||||
}
|
||||
assert.ok(declared.length >= 20, `应当扫到一批手写色(Theme 17 + 预设 14),实际 ${declared.length}`);
|
||||
|
||||
const presetNames = new Set([
|
||||
'LIGHT_GRAY_100', 'LIGHT_GRAY_200', 'LIGHT_BLUE_100', 'LIGHT_BLUE_200',
|
||||
'LIGHT_GREEN_100', 'LIGHT_AMBER_100', 'LIGHT_ORANGE_100',
|
||||
'DARK_GRAY_100', 'DARK_GRAY_200', 'DARK_BLUE_100', 'DARK_BLUE_200',
|
||||
'DARK_GREEN_100', 'DARK_AMBER_100', 'DARK_ORANGE_100'
|
||||
]);
|
||||
const themeNames = new Set(SELF_OWNED_COLORS);
|
||||
const unregistered = declared.filter(d =>
|
||||
!(d.file === 'common/Theme.ets' && themeNames.has(d.name)) &&
|
||||
!(d.file === 'model/Wallpaper.ts' && presetNames.has(d.name))
|
||||
);
|
||||
assert.deepEqual(unregistered.map(d => `${d.file}:${d.name}=${d.value}`), [],
|
||||
'这些手写色不在任何清册里 —— 要么挂进 Theme(品牌/业务语义色),要么挂进预设色板');
|
||||
|
||||
const byName = new Map(declared.map(d => [d.name, d]));
|
||||
for (const n of themeNames) assert.ok(byName.has(n), `清册里的 ${n} 已不存在(清册过期)`);
|
||||
for (const n of presetNames) assert.ok(byName.has(n), `预设色板清册里的 ${n} 已不存在`);
|
||||
|
||||
// 预设色板的每个值都要由 CSS 的两段兜住(LIGHT_ → :root,DARK_ → .dark)
|
||||
const lightCss = cssPaletteOf(':root');
|
||||
const darkCss = cssPaletteOf('.dark');
|
||||
const keyOf = { GRAY_100: 'gray100', GRAY_200: 'gray200', BLUE_100: 'blue100', BLUE_200: 'blue200', GREEN_100: 'green100', AMBER_100: 'amber100', ORANGE_100: 'orange100' };
|
||||
for (const n of presetNames) {
|
||||
const m = /^(LIGHT|DARK)_(.+)$/.exec(n);
|
||||
assert.ok(m, `预设色板常量名要带 LIGHT_/DARK_ 前缀(否则判据不知道跟哪一段比):${n}`);
|
||||
const css = m[1] === 'DARK' ? darkCss : lightCss;
|
||||
assert.equal(byName.get(n).value, css[keyOf[m[2]]],
|
||||
`${n} 与 CSS 的 ${m[1] === 'DARK' ? '.dark' : ':root'} 段不一致`);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user