跨端: 导航条材质选 (a) 固定档(推翻我的 (b))—— 并修掉"注释说 (a)、代码是 (b)"的自相矛盾

pi 2026-09-15 裁定:**推翻 (b),选 (a)**。我原先给 (b) 的理由不成立,他逐条驳了:

1. **WebUI 的导航条根本不读 `--bg-blur`**:`index.css:1114` 的 `.narrow-nav` 是硬编码
   `backdrop-filter: blur(18px) saturate(1.5)`。我引这条支持"两个量不同",
   而同一条也说明**它不由用户偏好驱动**。
2. **WebUI 那个滑杆的语义是"背景"**:`BackgroundPicker.tsx:183` —— `label="模糊"`、
   `hint="虚化细节,避免背景与正文抢注意力"`、`min=0 max=24`,只作用在
   `.app-backdrop{filter:blur(var(--bg-blur))}` 上。
3. WebUI 自己留了**分开的**令牌 `--bg-blur-panel`(`index.css:267`,注释写明
   "与壁纸自身的 `--bg-blur` 分开")—— 它的词汇表本身就拒绝把两者等同。
4. **★ 我给 (b) 的理由不成立**:我写"(a) 会让那个滑杆在导航条上变成死控件",
   可那个滑杆**已经**被壁纸消费了(`MainPage.ets` 壁纸层的 `.blur(bgPlan.blurPx)`)——
   它从来**不是**导航条的控件。(a) 之下它照样是活的。
5. §7.12 的「材质(玻璃)」行原本写的就是固定档 ⇒ (a) 是**回到**已登记契约。

产品向还有一条:**导航条是 chrome,材质应当稳定**,不该因为用户换张壁纸而变厚变薄。

## 最该记的是:我的注释和代码**互相矛盾**

`MainPage.ets` 里那段注释论证的是 (a)、并明确写着"跟随是错的,pi 抓出来了",
而它下面那一行代码是 (b)。**下一个读者会照注释把代码改回去,并引我那句话当权威。**
这是这一路反复在消的形状(说的与做的不一致、而判据看不见),这次落在**注释**上 ——
而注释正是"理由要写清"那条纪律的证据源。已整段重写为真实的 (a) 版本,并把
"(a) 会让滑杆变死控件"这个**错的理由**连同它为什么错一起留在注释里。

## 做掉的东西

- `MainPage.ets`:导航条回到 `.backgroundBlurStyle(Theme.navMaterial)`;
  删掉 `NAV_MATERIAL_OF` 表与 `navMaterialFor` 的 import((a) 之下都是孤儿)。
- `model/Appearance.ts`:删 `navMaterialFor`(它存在的唯一理由就是方案 (b))。
  `blurStyleFor` 现在**没有任何调用点** —— 如实登记在它的文档注释里
  ("有测试"不等于"有人用",上一轮我刚因同形状被抓过),不假装它活着。
- **五处"整条字面表达式"断言改成语义断言**(pi §四):`cross-client-theme`、`harmony-appearance`、
  `harmony-nav`(3 处)此前都在钉
  `/\.backgroundBlurStyle\(NAV_MATERIAL_OF\[navMaterialFor\(…\)\] \?\? Theme\.navMaterial\)/`
  —— 字面换字面,正是 `CRITERIA.md` 不许的"对源码形状的匹配"。
  现在判:① 那一处的材质**来自 `Theme.navMaterial` 这个系统令牌**;
  ② **不许跟随** `bg_blur`(NavBar 真代码里不许出现 `blurPx`/`blurStyleFor`);
  ③ 令牌是 `BlurStyle` 枚举值、不是 `NONE`、且**成员名真实存在于 SDK 枚举**。
- "可达性"那条判据**随契约作废**(它守的是方案 (b)):换成判 (a) 的契约。
  **判据随契约走,不随实现走。**
- `harmony-appearance`:原先判"页面里那张表的键必须是 SDK 成员"。表删了,
  改成**枚举 `blurStyleFor` 的整个值域**(0..40 + 界外 + NaN),逐个核 SDK 成员 ——
  比原来只核表里那三行**更严**。

## 判据自己先错了一次,记下来

新判据第一版**没剥注释**就断言"NavBar 里不许出现 `blurPx`",当场红了 ——
而红的原因不是代码错,是 `NavBar` 的**文档注释**里正好写着
"我一度把档位接过用户偏好(`navMaterialFor(this.bgPlan.blurPx)`)"这句历史说明。
**注释说明禁令 ≠ 违反禁令**;不剥注释,这条判据就会变成"逼人删掉解释",
恰好与本仓库"理由要写清"的纪律相反。改成 `stripComments(bar)` 后再判,并加了一条
"注释里确实留着那处说明"的自检前提。

## 变异体:45 个全部被抓(含 3 个新判据的专属变异)

方案 (b) 落地时配的 13 个变异体**整体作废**(它们锚的代码被删了),标注 `retired`
并写清理由 —— 不是"没跑成"。另有 5 个锚点漂移(我在注释里逐字引用了被锚的那句代码,
污染了通用正则)的**重锚**到真代码;注释里那句逐字引用也一并去掉了
(**注释里逐字抄代码**正是让"按字面锚定"的变异体反复失效的根因)。
新增 3 个针对 (a) 契约的变异体(绕过令牌 / 又跟随 `bg_blur` / 令牌变 `NONE`),全部被抓。

## 未验

- **真机观感仍未验**:三档材质在真机上能不能看出差别、滑杆手感、管理页布局,
  只有真机能答。本机模拟器已起(`hdc list targets` 有目标),但
  `run-all.mjs` 的**设备闸已经到期**(8 个静态判据的前提成立)⇒ 套件现在会挡在
  那道闸上、不打 `RESULT`。**这不是本笔引入的**(前提是环境变了),已单独报给 pi。
- Go 侧 `debt_registry_test.go` 仍未在本机跑(无 Go 模块缓存);pi 已在别处跑过,绿。
This commit is contained in:
2026-09-15 11:58:19 +08:00
parent e917b8782a
commit c12744e8c3
12 changed files with 257 additions and 195 deletions

View File

@ -162,28 +162,33 @@ export function localOnly(local: AppearanceSnapshot): AppearanceSync {
}
/**
* 壁纸模糊档 → **系统材质档次**(不是像素半径)。
* 壁纸模糊档 → **系统材质档次名**(不是像素半径)。
*
* 服务端存的是 WebUI 的 `bg_blur`(0~40 的模糊像素),而鸿蒙这边"模糊"由系统材质提供
* (`BlurStyle`)—— 这是"用系统方案"的直接结果:同一个数字在两边含义不同,
* 所以要**显式映射**,而不是把 40 当半径塞进某个 API。映射关系写在这里,
* 判据可以直接跑它(哪个数字落到哪一档,是行为不是注释)。
* 所以要**显式映射**,而不是把 40 当半径塞进某个 API。
*
* ── 为什么返回的是**档位名**(字符串)而不是 SDK 的枚举数值 ──
* 返回**档位名**(字符串)而不是 SDK 枚举数值:这一层是**纯逻辑**(零 `@ohos` 依赖
* ⇒ 判据能用 node 直接跑它),而 `BlurStyle` 只有 `.ets` 里在作用域内。
* 判据把返回值与 SDK 的 `declare enum BlurStyle` 成员名比对
* ("档次必须来自系统枚举,写成自造名字会编译不过/不生效")。
*
* 与同一个文件里的 `colorModeFor`(`'COLOR_MODE_DARK' | …`)**同一种形状**:
* 这一层是**纯逻辑**(零 `@ohos` 依赖 ⇒ 判据能用 node 直接跑它),
* 而 `BlurStyle` 是 SDK 的枚举、只有 `.ets` 里才在作用域内。
* 返回档位名 ⇒ 映射的**分档判断**留在这层可判,`名字 → BlurStyle` 那一步在页面里
* 用一张**四行长**的表做掉(`MainPage.ets` 的 `BLUR_STYLE_OF`)。
* ── ★ 现在**没有任何调用点**(如实登记,别把它读成活的)──
*
* ★ 我一度把它改成"直接返回 SDK 数值(0/9/10/11)"想省掉那张表 —— **被判据挡回来了**,
* 而且挡得对:`harmony-appearance.test.mjs` 有一条判据把**返回值拿去和 SDK 的
* `declare enum BlurStyle` 成员名比对**("档次必须来自系统枚举,写成自造名字会
* 编译不过/不生效")。返回数值就永远对不上成员名,那条判据会一直红 ——
* 它保护的正是"别自己发明档位"这件事。
* ⇒ 回到档位名。那张四行的表不是负担,它是"哪个名字对应哪个枚举"的**唯一**落点,
* 而且页面里能对着 SDK 写(纯逻辑层看不到 BlurStyle)。
* 它的读者曾经有两个,两个都不在了:
* ① `MainPage.ets` 里那张 `名字 → BlurStyle` 的表,随"导航条改回固定档"删了;
* ② 文件名一度是 `navMaterialFor` 的**有下限**入口(导航条专用),
* 它存在的唯一理由是"导航条档位跟随 `bg_blur`"这个方案 —— 而 pi 2026-09-15
* **推翻了那个方案**(导航条是 chrome,材质固定;见 `MainPage.ets` 的 `NavBar` 注释),
* 所以它连同"下限"一起删了。
*
* 于是本函数成了**孤岛**:判据还在、行为还对,但页面里没人调它。
* 这是**故意留着**的(它是目录里那套分档语义的唯一落点,且判据在跑),
* 不是"以为有人在用"。**如果你要用它,请先想清楚是不是又在重造方案 (b)。**
*
* ⚠️ 试金石:**"有测试"不等于"有人用"**。上一轮我刚因为同形状的事被抓过一次
* (`Theme.navMaterial` 的"外部引用"落在一个不可达的 `??` 兜底分支上,
* 而死令牌判据照样绿)。函数名这里没有等价判据 —— 只有这段文字,所以更要写实。
*/
export function blurStyleFor(bgBlur: number): string {
const b: number = clampNumber(bgBlur, 0, 40, 4);
@ -199,32 +204,6 @@ export function blurStyleFor(bgBlur: number): string {
return 'COMPONENT_THICK';
}
/**
* **导航条专用**的材质档:与 `blurStyleFor` 同一张分档表,但**有下限**。
*
* ── 为什么必须分成两个入口(这不是过度设计,是一次真实缺陷的产物)──
*
* 导航条的档位一度**直接**接 `blurStyleFor(bgBlur)`,于是 `bg_blur = 0`(用户把壁纸
* 调清晰)时 `blurStyleFor` 返回 `'NONE'` ⇒ **导航条一点材质都没有**。
* 而"导航条是玻璃"是设计不变量(WebUI 的 `.narrow-nav` 是硬编码 `blur(18px)`,
* **不看** `--bg-blur`),用户要的是"壁纸清晰",不是"导航条变透明"。
*
* 两个量、两个输入、**两个下限**:
* · 壁纸模糊(`bg_blur`):**允许 0**(不模糊是合法选择)⇒ `blurStyleFor` 保留 `'NONE'`;
* · 导航条材质:**不许没有**(玻璃是它的身份)⇒ 本函数把下限抬到最薄档。
*
* 判据在 `harmony-nav.test.mjs`:在**可达输入**上(滑杆 0..40 的每个整数 + 界外值)
* 本函数都不返回 `'NONE'`;且导航条那一处必须**经本函数**、不许直接用 `blurStyleFor`。
*/
export function navMaterialFor(bgBlur: number): string {
const tier: string = blurStyleFor(bgBlur);
if (tier === 'NONE') {
// 壁纸可以清晰,导航条仍然要有玻璃
return 'COMPONENT_THIN';
}
return tier;
}
/**
* 主题偏好 → 系统色彩模式。
*

View File

@ -14,7 +14,7 @@ import { SseService, SseEvent } from '../api/SseService';
import { AppearanceStore } from '../common/AppearanceStore';
import { Configuration, ConfigurationConstant, EnvironmentCallback } from '@kit.AbilityKit';
import { image } from '@kit.ImageKit';
import { AppearanceSnapshot, isDarkMode, scrimOpacity, navMaterialFor } from '../model/Appearance';
import { AppearanceSnapshot, isDarkMode, scrimOpacity } from '../model/Appearance';
import { BackgroundPlan, PresetLayer, TRANSPARENT, resolveBackground } from '../model/Wallpaper';
import { MailSummary, Contact, PermissionRequest, DecideResponse, SentResponse, PendingResponse } from '../model/Models';
import {
@ -49,23 +49,21 @@ import {
emptyHint
} from '../model/CommTabs';
import { MailDetailParams, ComposeParams } from '../model/RouteParams';
/**
* `navMaterialFor` 给的**档位名** → SDK 的 `BlurStyle` 枚举(导航空专用)。
/*
* ⚠️ **`import` 必须在这一行的位置**:ArkTS 要求所有 import 都在**任何其它语句之前**
* (`arkts-no-misplaced-imports`),与它们之间隔的是常量、表还是别的语句无关。
*
* 与 `blurStyleFor` 的关系:同一个分档表,但 `navMaterialFor` **有下限**(永不 NONE),
* 因为"导航条是玻璃"是不变量,而壁纸可以不模糊(见 `model/Appearance.ts` 的说明)。
* 这一笔我犯过:`7647c24` 里我把一张常量表(`NAV_MATERIAL_OF`)插在了这两组 import
* **之前**,于是 `hvigorw assembleHap` 直接报错(`f31bc02`/`7647c24` 上都红)。
* **归属说明**:那条错是我造成的,`git show 7647c24:…/MainPage.ets` 可复核
* (最后一条 import 在第 80 行,而第 63 行已是 `const NAV_MATERIAL_OF`)。
* 此处原先写的是"由 pi 实测" —— 那句话不准确:是本机**某个** pi 会话/别人测的,
* 我无法独立复核"是谁",这类**归属断言**和"锚短哈希"同族(复核方没法复现"谁做的"),
* 所以改成只陈述**可复核的事实**:错误存在、在哪个提交、怎么复核。
*
* ★ 表留在页面里:`BlurStyle` 是 SDK 枚举,只有 `.ets` 里在作用域内;而
* `model/Appearance.ts` 是**纯逻辑、零 `@ohos` 依赖**,判据要靠 node 直接跑它。
* 分工:**分档判断**在纯逻辑层(可判),**名字 → 枚举**这一步在这里(对着 SDK 写)。
* 那张表和它的 import 后来都随"导航条改回固定档"一起删了(见下面 `NavBar` 的注释);
* 这条注释留在 **import 区**而不是跟着表走,因为要守的是"import 在最前"这个位置本身。
*/
const NAV_MATERIAL_OF: Record<string, BlurStyle> = {
'COMPONENT_THIN': BlurStyle.COMPONENT_THIN,
'COMPONENT_REGULAR': BlurStyle.COMPONENT_REGULAR,
'COMPONENT_THICK': BlurStyle.COMPONENT_THICK
};
import { CalendarPage } from './CalendarPage';
import {
NAV_BAR_BOTTOM,
@ -1711,7 +1709,7 @@ struct MainPage {
* ★ 半径直接用服务端给的那个 px 值:WebUI 就是 `blur(var(--bg-blur))`,
* 两边**同一个物理量、同一个数** ⇒ 这一处不需要映射表,也不该有。
* (`blurStyleFor` 那张表服务的是**材质档**,与这里的 px 半径不是一回事;
* 它的调用点在导航条那条链上 —— 经 `navMaterialFor` 复用,见 `NavBar`。)
* 它是**纯逻辑层**的可判依据;页面侧的面板材质走 `Theme.navMaterial`,见 `NavBar`。)
*/
.blur(this.bgPlan.blurPx)
// 压暗用**系统遮罩色** + 服务端给的浓度:换向(浅色洗白/深色压黑)由系统负责
@ -1793,23 +1791,32 @@ struct MainPage {
* 深浅两套颜色与模糊半径都由系统按主题给。
*/
/*
* 导航条的**面板材质**:**固定系统档**(`Theme.navMaterial`),**不跟随 `bg_blur`**。
* 导航条的**面板材质**:**固定系统档**(`Theme.navMaterial` = `COMPONENT_THICK`),
* **不跟随 `bg_blur`**。
*
* ★ 我一度把它接过用户偏好(`blurStyleFor(blurPx)`),**是错的**,pi 抓出来了。
* 两条依据:
* ① WebUI 侧导航条的糊度**不由 `--bg-blur` 驱动** ——
* `index.css:1114` 的 `.narrow-nav` 是硬编码 `backdrop-filter: blur(18px) saturate(1.5)`
* (`html[data-bg='on']` 作用域内),壁纸糊到 0,导航条照样是玻璃。
* 跟随用户偏好是**新增差异**,不是对齐。
* ② 更根本的:我上一笔刚把"**图片内容模糊**"与"**面板材质**"论证成两个不同的物理量
* (见壁纸层那段注释与 `harmony-appearance.test.mjs`),转头却把**面板材质**的档位
* 接到了**壁纸模糊**这个输入上 —— 自己打自己。两个量、两个输入。
* ⇒ 用户把模糊滑杆拖到 0(要壁纸清晰)时,导航条**仍然**是玻璃。
* 这曾是真实缺陷:滑杆 `min: 0` 可达,`blurStyleFor(0) === 'NONE'`,
* 于是导航条一点材质都没有(本文件里那句「NONE 等于没有材质,"玻璃"就名存实亡」
* 说的正是这件事)。判据 `harmony-nav.test.mjs` 现在钉"可达输入上该档**非 NONE**"。
* ── 这里曾经"说的和做的不一致",记下来(pi 2026-09-15 抓到的)──
* 我一度把档位接过用户偏好(`navMaterialFor(this.bgPlan.blurPx)` 查表),
* 但**注释留在了更早那一版**:那段注释论证的是"固定档"、还写着"跟随是错的"。
* 于是**注释说 (a)、代码是 (b)** —— 下一个读者会照注释把代码改回去,
* 而且他会引我那句"pi 抓出来了"当权威。**说的与做的不一致、而判据看不见**,
* 正是这一路反复在消的形状,这次落在注释上(而注释正是"理由要写清"那条纪律的证据源)。
*
* ── 为什么最终是固定档(pi 的裁定,五条依据,我原先的理由被推翻)──
* ① WebUI 的 `.narrow-nav`(`index.css:1114`)是**硬编码** `backdrop-filter: blur(18px)`,
* **不读** `--bg-blur`;
* ② WebUI 那个滑杆的语义是"**背景**"(`BackgroundPicker.tsx:183`:`label="模糊"`、
* `hint="虚化细节,避免背景与正文抢注意力"`,`min=0 max=24`),只作用在
* `.app-backdrop{filter:blur(var(--bg-blur))}` 上;
* ③ WebUI 自己留了**分开的**令牌 `--bg-blur-panel`(`index.css:267`,注释写明
* "与壁纸自身的 `--bg-blur` 分开:那层给照片打底,这层给面板")——
* 它的词汇表本身就把两者分开;
* ④ **我原先的理由不成立**:我说"(a) 会让那个滑杆在导航条上变成死控件",
* 可那个滑杆**已经**被壁纸消费了(本文件下面的壁纸层把 `bgPlan.blurPx` 交给 `.blur(...)`)——
* 它从来**不是**导航条的控件,(a) 之下它照样是活的;
* ⑤ §7.12 的「材质(玻璃)」行原本写的就是固定档 ⇒ (a) 是**回到**已登记契约。
* 产品向还有一条:**导航条是 chrome,材质应当稳定**,不该因为用户换了一张壁纸而变厚变薄。
*/
.backgroundBlurStyle(NAV_MATERIAL_OF[navMaterialFor(this.bgPlan.blurPx)] ?? Theme.navMaterial)
.backgroundBlurStyle(Theme.navMaterial)
}
build() {