跨端: 品牌蓝深色下没提亮(24 处字/图标看不见)+ 补深色可读性设备判据
## 一、真 bug:WebUI 深色下把品牌蓝**提亮**了,这边没有 WebUI 的强调色是**双通道**(`tailwind.config.js` 的 `backgroundColor` /`textColor` 覆盖 + `index.css` 两段定义): | 通道 | 用途 | 浅色 | 深色 | |---|---|---|---| | `--s-blue-600` | **实心按钮底** | `37 99 235` | `37 99 235`(**同值**)| | `--c-blue-600` | 内容/交互的**蓝字与图标** | `37 99 235` | **`128 175 249`** | `index.css:353` 写了理由:主按钮底跟着变「会让主按钮在深色页面上 失去『这是主操作』的视觉重量」;而蓝字必须提亮,否则深底上读不动。 鸿蒙只有一个 `Theme.accent = '#2563EB'` ⇒ 24 处字/图标在深色下 对比度 **2.61:1**(设备实测:管理页返回箭头 `‹`),低于 WCAG 图形下限 3:1。 **取证方式**:在跑着的 WebUI 上用 CDP 读**计算样式**(不是读 CSS 源)—— 浅色 `37 99 235` / 深色 `128 175 249`,实测确认。 ## 二、修法:加前景专用的深色档 + 唯一入口 - `Theme.accentDark = '#80AFF9'`(= WebUI `.dark --c-blue-600`) - `Theme.accentFor(dark?)` 作为**前景**唯一入口 - **当背景的 20 处保持 `Theme.accent` 不动**(跟 WebUI 的 `--s-*` 一致) 24 处 `.fontColor/.iconColor(Theme.accent)` → `Theme.accentFor()`。 ## 三、顺手修掉「转述一层就会漏」这个结构问题 `accentSoftFor(isDark)` 原本的约定是"页面算好深浅色传进来"。 给 `accentFor` 做准备时一数:**8 处**直接用了 `Theme.accentSoft`(没走入口) —— 约定**已经漏了**,而漏掉的症状正是上一轮那个"深色下白底卡片刺眼"。 于是把两个 `For()` 的参数都改成**可选**:`AppStorage` 是 ArkTS 全局键值存储, 静态类可以直接读(原来"拿不到 Context"的理由不成立)。 ⇒ `Theme.isDarkNow()` 成为唯一判断点,调用方不必再各自转述。 ## 四、设备判据:深色可读性**扫一屏** 原来只有悬浮球那一条(单个点)。这个 bug 类一天撞到**两批**(12 处 + 24 处), 逐处写判据追不上 ⇒ 改成把当前页所有小段文字都量一遍对比度。 三个实现要点(第一版全踩了,都写进注释): - **不能只取中心一个像素**:中心多半落在笔画之间 ⇒ 读到的是底色, 报出一片 ratio=1.00 的假红。改成**框内网格扫描取极值**(最亮=底/最暗=墨)。 - **整屏解码一次**:每点 spawn 一次 ffmpeg 太慢 ⇒ 新增 `readPixels()`(157ms 解整屏,比逐点快三个数量级)。 - **只判"有真实墨迹"的框**(`hi.L - lo.L >= 0.02`),否则跳过而不是判红。 实测:修前 1 处低对比(2.61:1),修后 **40 段文字全部 ≥3:1**。 ## 五、判据自身的三个修正 - `accentSoftFor(dark:)` 的签名断言跟着放宽成 `dark?`,并**补上 `accentFor` 的**。 - **孤儿令牌判据从"一跳"改成"走整条链"**:`KEY_IS_DARK` ← `isDarkNow()` ← `accentFor()` ← 24 处页面。只查一跳时它假红 —— ★ **"有没有人用"是可达性问题,不是邻接问题**;加中间层(抽 `For()` 入口) 恰恰是我们鼓励的写法,而旧判据会因此假红。 - 手写色登记表补 `accentDark` 一行理由。 ## 六、`baseline.sha` 重算(先核过不是残留) 三个文件哈希对不上。逐个 `git diff --quiet HEAD -- <f>` 取证: - `AdminUsersPage.ets` / `SettingsPage.ets` —— 本次**有意编辑**; - `api/AppearanceApi.ets` —— **与 HEAD 逐字节相同** ⇒ 底本取完后被**合法改过** (提交 `f811c98`),属 `stale` 不是 `residue`。 按该文件自己那条纪律(「重算必须是一次有记录的动作」)在文件里记了理由。 `run-all.mjs` → `checks=514 pass=514 fail=0 skip=0 red=0 broken=0 unreported=0`。
This commit is contained in:
@ -235,9 +235,16 @@ const SELF_OWNED_COLORS = [
|
||||
* `chrome-600` 刻意不透明(`index.css:1097` 有注释:15px 小控件叠透明度
|
||||
* 会让数字掉到 4.46:1,低于 WCAG AA)。
|
||||
*
|
||||
* 三者的取值理由都已写在 `Theme.ets` 各自的注释里(本判据的要求)。
|
||||
* · `accentDark` #80AFF9 —— WebUI 的 `.dark --c-blue-600`(`index.css:481`)。
|
||||
* **这是"前景专用"的品牌深色档**,不是 `accent` 的替代:
|
||||
* WebUI 那边是双通道(`--s-blue-*` 实心按钮底两模式同值 /
|
||||
* `--c-blue-*` 内容用蓝在 `.dark` 段提亮),这里跟着分。
|
||||
* 不提亮的话深色下品牌蓝字在近黑底上只有 **2.61:1**(设备实测),
|
||||
* 低于 WCAG 图形下限 3:1。使用入口是 `Theme.accentFor()`。
|
||||
*
|
||||
* 各条取值的理由都已写在 `Theme.ets` 各自的注释里(本判据的要求)。
|
||||
*/
|
||||
'navActiveBg', 'navBrandFg', 'badgePlain', 'accentSoftDark',
|
||||
'navActiveBg', 'navBrandFg', 'badgePlain', 'accentSoftDark', 'accentDark',
|
||||
/*
|
||||
* SSE 连接指示器的四个状态色(`WideSidebar.ets` 的 `sseColorOf`)。
|
||||
* 逐档对齐 WebUI 的 Tailwind 类(`ConnectionIndicator.tsx:22-25`):
|
||||
@ -614,12 +621,49 @@ test('遮罩:交给系统的遮罩语义色("随主题换向"这件事现在
|
||||
* 否则仍然算死:这正是 `navMaterial` 的形状 —— 它当时那个"内部消费者"是
|
||||
* `MainPage` 里的一张**局部表**,不提供任何担保。
|
||||
*/
|
||||
/*
|
||||
* ★★ 2026-09-19:改成**走整条链**,不只一跳。
|
||||
*
|
||||
* 原来的写法只问"读它的那个方法在外部有没有调用点"。加了 `KEY_IS_DARK`
|
||||
* 之后立刻假红:它的链是
|
||||
* KEY_IS_DARK ← Theme.isDarkNow() ← Theme.accentFor() ← 24 处页面
|
||||
* `isDarkNow` 自己**只被 Theme 内部的两个 `For` 方法读**,外部一处都没有
|
||||
* ⇒ 一跳就判成孤儿。而它显然不是孤儿(24 处页面在用)。
|
||||
*
|
||||
* ★ 这个坑值得记:**"有没有人用"是可达性问题,不是邻接问题。**
|
||||
* 只查一跳的判据在"中间加了一层"时必然假红 —— 而加中间层
|
||||
* (抽个 `For()` 入口)恰恰是我们鼓励的写法。
|
||||
*/
|
||||
if (internalUsers.length > 0) {
|
||||
const ownersAlive = internalUsers.some((u) => {
|
||||
const methodName = u.replace('Theme.', '').replace('()', '');
|
||||
return othersSrc.some((o) => new RegExp(`Theme\\.${methodName}\\b`).test(o.src));
|
||||
});
|
||||
if (ownersAlive) continue;
|
||||
const alive = new Set();
|
||||
const isUsedExternally = (name) =>
|
||||
othersSrc.some((o) => new RegExp(`Theme\\.${name}\\b`).test(o.src));
|
||||
const queue = internalUsers.map((u) => u.replace('Theme.', '').replace('()', ''));
|
||||
let hops = 0;
|
||||
while (queue.length > 0 && hops < 12) { // 环/自引用保护
|
||||
hops++;
|
||||
const cur = queue.shift();
|
||||
if (alive.has(cur)) continue;
|
||||
alive.add(cur);
|
||||
if (isUsedExternally(cur)) {
|
||||
alive.add('__externally_reached__');
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* 它自己没人从外面调 —— 继续往上找"谁在 Theme.ets 内部读它",
|
||||
* 直到某个环节在外部有调用点(或链条断掉)。
|
||||
*/
|
||||
const re3 = new RegExp(`Theme\\.${cur}\\b`, 'g');
|
||||
let m3;
|
||||
while ((m3 = re3.exec(themeSrc)) !== null) {
|
||||
const before = themeSrc.slice(0, m3.index);
|
||||
const owners = [...before.matchAll(/static\s+(?:readonly\s+)?(\w+)/g)];
|
||||
if (owners.length === 0) continue;
|
||||
const owner = owners[owners.length - 1][1];
|
||||
if (owner !== cur && !alive.has(owner)) queue.push(owner);
|
||||
}
|
||||
}
|
||||
if (alive.has('__externally_reached__')) continue;
|
||||
deadTokens.push(`${name}(只在 ${internalUsers.join('、')} 内部被读,而那些方法**外部也没有调用点**)`);
|
||||
continue;
|
||||
}
|
||||
@ -855,8 +899,23 @@ test('★ 品牌浅底必须有深色变体(深色下白底卡片 = 刺眼的
|
||||
'深色下用写死的浅底会让选中卡片在深色页上刺眼');
|
||||
|
||||
/* ② 有按主题选值的入口 */
|
||||
assert.match(themeSrc, /static accentSoftFor\(dark: boolean\): string/,
|
||||
'★ 要有 `accentSoftFor(dark)` 入口 —— 页面各自写 `isDark ? a : b` 迟早漏一处');
|
||||
/*
|
||||
* ★ 2026-09-19:参数从 `dark: boolean` 改成 **`dark?: boolean`**(可选)——
|
||||
* 因为 `Theme.isDarkNow()` 现在能直接读 `AppStorage` 的全局键,
|
||||
* 调用方不必再各自把"当前是不是深色"转述一遍(那个约定已经漏过 8 处)。
|
||||
* 判据跟着放宽成"参数可选",但**入口必须存在**这一点不变。
|
||||
*/
|
||||
assert.match(themeSrc, /static accentSoftFor\(dark\?: boolean\): string/,
|
||||
'★ 要有 `accentSoftFor()` 入口 —— 页面各自写 `isDark ? a : b` 迟早漏一处');
|
||||
|
||||
/*
|
||||
* ②b 品牌**前景**色的深色入口 —— 与 ② 同一理由(2026-09-19 加)。
|
||||
* `accent` 是两用令牌,但只有"当前景"那一半需要在深色下提亮;
|
||||
* 当背景时保持饱和(WebUI 的 `--s-blue-*` 两模式同值)。
|
||||
*/
|
||||
assert.match(themeSrc, /static accentFor\(dark\?: boolean\): string/,
|
||||
'★ 要有 `accentFor()` 入口 —— 深色下品牌蓝字/图标必须提亮,' +
|
||||
'否则近黑底上只有 2.61:1(设备实测)。页面各自写 `isDark ? a : b` 迟早漏一处。');
|
||||
|
||||
/* ③ 用到它的地方真的走入口(不是还在直接用浅色那个) */
|
||||
const pages = readdirSync(HARMONY_ETS + '/pages').filter((f) => f.endsWith('.ets'));
|
||||
@ -1163,3 +1222,116 @@ test('C|「会跟随主题翻转的 Resource」不得当前景色(含矛盾
|
||||
' 若某个令牌**确实**两边都该用(有具体理由),加进 `ALLOW_BOTH_BG_AND_FG` 并写明理由。\n' +
|
||||
conflicts.map((c) => ` · Theme.${c.name}\n 当背景:${c.bg}\n 当前景:${c.fg}`).join('\n'));
|
||||
});
|
||||
|
||||
|
||||
test('★ 设备:深色下短文本必须都读得动(扫一屏,不只查一个元素)', async (t) => {
|
||||
/*
|
||||
* ★★ 这条是上面"品牌色真的画成那个色"的**一般化**。
|
||||
*
|
||||
* 那条只钉住悬浮球一个点(品牌色 + 图标可分辨)。但"深色下看不见"
|
||||
* 这个 bug 类**不止一处** —— 2026-09-19 一天里撞到两批:
|
||||
* · 12 处 `Theme.surface` 当前景色(会翻转的面色当墨水)
|
||||
* · 24 处 `Theme.accent` 当字色而**深色下没跟着调亮**
|
||||
* (WebUI `.dark --c-blue-600` 是 `128 175 249`,这边还是 `#2563EB`
|
||||
* ⇒ 近黑底上 **2.61:1**,低于 WCAG 图形下限 3:1)
|
||||
*
|
||||
* 逐处写判据是追不上的(每加一个页面就漏一处)。**扫一屏**才追得上:
|
||||
* 把当前页面所有"小段文字"都量一遍对比度。
|
||||
*
|
||||
* ## 三个关键实现细节(第一版全踩了)
|
||||
*
|
||||
* ① **不能只取文字中心一个像素**:中心多半落在**笔画之间**,
|
||||
* 读到的是底色 ⇒ ratio=1.00 的假红一片(第一版 20 个里报了 12 个)。
|
||||
* 正确做法是**框内网格扫描取极值**:最亮=底、最暗=墨。
|
||||
* ② **整屏解码一次**:`pixelAt()` 每点 spawn 一次 ffmpeg,
|
||||
* 扫 40 段文字要几百次进程(一条判据几十秒)。用 `readPixels()`。
|
||||
* ③ **只判"有真实墨迹"的框**:`hi.L - lo.L < 0.02` 说明框里全是底色
|
||||
* (文字被裁掉、或采样的不是文字),那种**跳过而不是判红** ——
|
||||
* 否则会报一堆"看不见"而其实是我没采到。
|
||||
*
|
||||
* 阈值取 **3:1**:这是 WCAG 对图形/大字的下限。小正文该 4.5:1,
|
||||
* 但设备上有一堆 10–12px 的辅助文字(时间戳、计数),那档本身就在
|
||||
* 4.5 附近晃 —— 先按 3:1 收住"真的看不见"这一类,别把噪声一次全报出来。
|
||||
*/
|
||||
const D = await import('./lib/harmony-device.mjs');
|
||||
const hdc = D.findHdc();
|
||||
if (!hdc || !D.hasTarget(hdc)) {
|
||||
return t.skip('设备不在 —— 本条的设备半边本次不跑');
|
||||
}
|
||||
assert.ok(await D.launchOurApp(hdc), '要能拉起应用');
|
||||
assert.ok(await D.backToMain(hdc), '要能回到主界面');
|
||||
await new Promise((r) => setTimeout(r, 1500));
|
||||
|
||||
/*
|
||||
* ★ 只在**深色**下判。浅色下这些令牌本来就是为浅底配的,
|
||||
* 量它们没有信息量(也不会红),但会拖慢判据。
|
||||
*/
|
||||
const img0 = D.readPixels(await D.screenshot(hdc, '/tmp/dark-legibility.png'));
|
||||
assert.ok(img0, '要能截屏并解码');
|
||||
const corner = img0.at(4, 4);
|
||||
const isDark = corner && corner.r < 128 && corner.g < 128 && corner.b < 128;
|
||||
if (!isDark) {
|
||||
return t.skip(`当前不是深色主题(左上角像素 rgb(${corner?.r},${corner?.g},${corner?.b}))—— 本条只在深色下有意义`);
|
||||
}
|
||||
|
||||
const lum = (c) => {
|
||||
const f = (v) => { const x = v / 255; return x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4; };
|
||||
return 0.2126 * f(c.r) + 0.7152 * f(c.g) + 0.0722 * f(c.b);
|
||||
};
|
||||
|
||||
const root = D.dumpLayout(hdc);
|
||||
let scanned = 0;
|
||||
const bad = [];
|
||||
for (const n of D.walk(root)) {
|
||||
const a = n.attributes || {};
|
||||
const txt = (a.text || '').trim();
|
||||
if (!txt || txt.length > 14) continue;
|
||||
const m = /\[(\d+),(\d+)\]\[(\d+),(\d+)\]/.exec(a.bounds || '');
|
||||
if (!m) continue;
|
||||
const [x1, y1, x2, y2] = m.slice(1).map(Number);
|
||||
const w = x2 - x1;
|
||||
const h = y2 - y1;
|
||||
/* 只要"小段文字"的量级:太小的采不到笔画,太大的多半是容器 */
|
||||
if (w < 20 || h < 14 || w > 700 || h > 130) continue;
|
||||
if (x2 > img0.w - 4 || y2 > img0.h - 4) continue;
|
||||
|
||||
let lo = null;
|
||||
let hi = null;
|
||||
const sx = Math.max(2, Math.floor(w / 14));
|
||||
const sy = Math.max(2, Math.floor(h / 7));
|
||||
for (let y = y1 + 1; y < y2; y += sy) {
|
||||
for (let x = x1 + 1; x < x2; x += sx) {
|
||||
const p = img0.at(x, y);
|
||||
if (!p) continue;
|
||||
const L = lum(p);
|
||||
if (!lo || L < lo.L) lo = { L, p };
|
||||
if (!hi || L > hi.L) hi = { L, p };
|
||||
}
|
||||
}
|
||||
/* 没有真实墨迹对比的框跳过(见上面 ③) */
|
||||
if (!lo || !hi || hi.L - lo.L < 0.02) continue;
|
||||
scanned++;
|
||||
const ratio = (hi.L + 0.05) / (lo.L + 0.05);
|
||||
if (ratio < 3) {
|
||||
bad.push(`「${txt}」 对比度 ${ratio.toFixed(2)}:1 ` +
|
||||
`墨 rgb(${lo.p.r},${lo.p.g},${lo.p.b}) / 底 rgb(${hi.p.r},${hi.p.g},${hi.p.b}) ${a.bounds}`);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ★ 自检:扫到的段数不能太少 —— 太少说明遍历/尺寸过滤写错了,
|
||||
* 那样下面那条断言**恒绿**(判据静默失效的典型形状)。
|
||||
*/
|
||||
assert.ok(scanned >= 5,
|
||||
`要扫到足够多的小段文字(实测 ${scanned} 段)—— ` +
|
||||
'太少说明遍历或尺寸过滤写错了,那样下面的检查恒绿。');
|
||||
|
||||
assert.deepStrictEqual(bad, [],
|
||||
`★ 深色下这些文字读不动(对比度 <3:1):\n ${bad.join('\n ')}\n` +
|
||||
' 深色下品牌色/面色都要换深色变体:\n' +
|
||||
' · 文字/图标用 `Theme.accentFor()`(深色自动给 `#80AFF9`,' +
|
||||
'对齐 WebUI 的 `.dark --c-blue-600`)\n' +
|
||||
' · 品牌浅底用 `Theme.accentSoftFor()`\n' +
|
||||
' · **别**把 `Theme.surface` 这类"会翻转的面"当字色(用 `accentFg`)\n' +
|
||||
' 2026-09-19 真撞到的两批:12 处 surface 当字色 + 24 处 accent 深色没调亮。');
|
||||
});
|
||||
|
||||
@ -552,3 +552,55 @@ export function closeColor(a, b, tol = 12) {
|
||||
if (!a || !b) return false;
|
||||
return Math.abs(a.r - b.r) <= tol && Math.abs(a.g - b.g) <= tol && Math.abs(a.b - b.b) <= tol;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把整张 PNG 解码成内存里的 RGB 网格,返回一个 `{ w, h, at(x, y) }` 句柄。
|
||||
*
|
||||
* ★ 为什么要有它(2026-09-19 加):
|
||||
* 上面那个 `pixelAt()` 每读**一个**点就 spawn 一次 `ffmpeg`。单点够用,
|
||||
* 但"扫一个文字框找最亮/最暗"这种用法要读几百个点 ⇒ 几百次进程启动,
|
||||
* 一条判据就跑了几十秒(实测 19 个短文本 ~700 次 spawn)。
|
||||
*
|
||||
* 整屏解码是**一次** ffmpeg 调用(3184×2232×3 ≈ 21MB 内存),
|
||||
* 之后所有采样都是数组下标 —— 快三个数量级。
|
||||
*
|
||||
* 坐标系与 `dumpLayout` 的 `bounds` 一致(屏幕像素,原点左上)。
|
||||
*/
|
||||
export function readPixels(pngPath, screenW = 3184, screenH = 2232) {
|
||||
if (!existsSync(pngPath)) return null;
|
||||
const out = join(tmpdir(), `hm-rgb-${process.pid}-${Date.now()}.raw`);
|
||||
const r = spawnSync('ffmpeg', [
|
||||
'-loglevel', 'error', '-y', '-i', pngPath,
|
||||
'-f', 'rawvideo', '-pix_fmt', 'rgb24', out,
|
||||
], { encoding: 'utf8', timeout: 60000 });
|
||||
if (r.error || !existsSync(out)) return null;
|
||||
let buf;
|
||||
try {
|
||||
buf = bytes(out);
|
||||
} catch {
|
||||
return null;
|
||||
} finally {
|
||||
try { unlinkSync(out); } catch { /* 清不掉就算了 */ }
|
||||
}
|
||||
/* 尺寸从数据长度反推 —— 这样调用方不必传对屏幕尺寸 */
|
||||
const px = buf.length / 3;
|
||||
let w = screenW;
|
||||
let h = Math.round(px / w);
|
||||
if (h * w !== px) {
|
||||
/* 退一步:按常见屏宽找整除(设备可能是折叠态/别的分辨率) */
|
||||
w = 0;
|
||||
for (const cand of [3184, 2232, 2560, 1920, 1280, 1080]) {
|
||||
if (px % cand === 0) { w = cand; h = px / cand; break; }
|
||||
}
|
||||
if (!w) return null;
|
||||
}
|
||||
return {
|
||||
w,
|
||||
h,
|
||||
at(x, y) {
|
||||
if (x < 0 || y < 0 || x >= w || y >= h) return null;
|
||||
const i = (y * w + x) * 3;
|
||||
return { r: buf[i], g: buf[i + 1], b: buf[i + 2] };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -37,10 +37,21 @@
|
||||
# ⚠️ 这次漂移正是 `baseline-stale`(rc=0、只提示)那一档的又一次实例:
|
||||
# 它**不假红**(正常提交不会天天红),但也**不会被自动发现** ——
|
||||
# 我是靠"核每个文件的登记/实际读数"这条主动核查撞上的,不是它自己报的。
|
||||
#
|
||||
# 2026-09-19 18:2x baseline 重算(**先核过不是残留,才重算的**):
|
||||
# ① `pages/AdminUsersPage.ets`、`pages/SettingsPage.ets` —— 我本次的**有意编辑**:
|
||||
# 给所有"压在彩色底上的字/图标"换前景色(`Theme.surface` → `Theme.accentFg`),
|
||||
# 并把品牌蓝前景接入 `Theme.accentFor()`(深色下提亮到 WebUI 的 `--c-blue-600`)。
|
||||
# ② `api/AppearanceApi.ets` —— **与 HEAD 逐字节相同**(`git diff --quiet HEAD` 为空),
|
||||
# 但底本哈希对不上 ⇒ 说明它在底本取完之后被**合法改过**(提交 `3b5cc63`,
|
||||
# 09-19 那次"外观保存 400"的修:`payloadFromLocal` 改用 `AppearancePayload`)。
|
||||
# ⇒ 这是 `stale`(底本过期),不是 `residue`(变异残留)。
|
||||
# ★ 三个都逐个跑过 `git diff --quiet HEAD -- <f>` 取证,没有一个是"改动忘了还原"。
|
||||
# ★ 这正是这份文件里那条纪律的第 3 次执行:"重算必须是一次有记录的动作"。
|
||||
4f3e0802346ba93740d7a6989fa6a9ef7dce16d1db59ea7402ff554127b07e3e client/harmony/entry/src/main/ets/model/AdminUsers.ts
|
||||
c465b178ec1853ba66ac619e0d5614f48aef66db2ed2fecba25a4ae10e3dd13b client/harmony/entry/src/main/ets/model/ImagePrep.ts
|
||||
163d05009010ff985b2e1aa5a3b2d277d2dbb43cb75060c281a8401eef4fbf23 client/harmony/entry/src/main/ets/pages/AdminUsersPage.ets
|
||||
efd598149cb52400f1120f1ba4c7fb41ad9706f82005770479f571614a539e73 client/harmony/entry/src/main/ets/pages/AdminUsersPage.ets
|
||||
4681c5f2d57870d64b09a10433f5ea518d777a27f6cb7ece3bbcc29c5f11012a client/harmony/entry/src/main/ets/common/BackgroundPicker.ets
|
||||
22d247ab615b96bb57d20df8a3b95ed2b2ccaac4f9d216361c2ecba8a78df7e0 client/harmony/entry/src/main/ets/pages/SettingsPage.ets
|
||||
3baed1fac54fef5f924c17f658ad05105b8be3ac35b110e2277b1365a84bb15a client/harmony/entry/src/main/ets/pages/SettingsPage.ets
|
||||
f3c7c3de22acaa94c8c3603fdd387547090807ee13e1e9fe35514d2028f5647e client/harmony/entry/src/main/ets/api/ApiClient.ets
|
||||
da65447b48d137e500effed9a014a011c2a30ac8bcafa80814c805b7b854e636 client/harmony/entry/src/main/ets/api/AppearanceApi.ets
|
||||
6550e1892d1ebea82fb75a3d2cf8eaf826186199ff4a0ecf0430b1e3517d8681 client/harmony/entry/src/main/ets/api/AppearanceApi.ets
|
||||
|
||||
@ -68,7 +68,7 @@ const SUITE = [
|
||||
// 「只有通信页深色正常」的回归锁 —— 38 条里后 8 条是这次新增)。
|
||||
['test/theme.test.mjs', [], 38],
|
||||
['test/background.test.mjs', [], 44],
|
||||
['test/cross-client-theme.test.mjs', [], 19],
|
||||
['test/cross-client-theme.test.mjs', [], 20],
|
||||
// 左右滑动翻页的**语义契约**(两端逐项相同、数值各自定)—— 这是
|
||||
// `docs/DEBTS.json` 的 `gesture-semantics` 那条债:它写着「鸿蒙侧出现滑动
|
||||
// 手势代码时**立即建**(此前建 = 只有一端存在的假判据)」。
|
||||
|
||||
Reference in New Issue
Block a user