From d9bb4f766b605502a3d8d493725afa7bef796d01 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Sun, 20 Sep 2026 13:05:59 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B7=A8=E7=AB=AF:=20=E4=B8=89=E6=9D=A1?= =?UTF-8?q?=E5=88=A4=E6=8D=AE=E8=BD=AC=E7=BB=BF=EF=BC=88=E7=99=BB=E8=AE=B0?= =?UTF-8?q?=E6=96=B0=E4=BB=A4=E7=89=8C=20+=20=E6=8A=8A=E4=B8=80=E6=9D=A1?= =?UTF-8?q?=E6=96=AD=E8=A8=80=E9=94=99=E5=AE=9E=E7=8E=B0=E7=9A=84=E5=88=A4?= =?UTF-8?q?=E6=8D=AE=E6=94=B9=E6=88=90=E6=96=AD=E8=A8=80=E7=9C=9F=E4=B8=8D?= =?UTF-8?q?=E5=8F=98=E5=BC=8F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一步(写邮件右栏)之后整套跑红 3 条,逐个查清: ══ ① cross-client-theme —— 判据是对的,我漏登记 新加的 `accentEdge` / `accentEdgeDark` 是**手写色**,而这个仓有一条硬规矩: `Theme.ets` 里每个 `static readonly X: string = '#……'` 都必须在 `SELF_OWNED_COLORS` 名单里 + 在文件内的「手写色登记表」注释里写一行理由。 (这条规矩本身就是为防"新写死一个色悄悄溜过去"而立的 —— 第二处断言 会检查名单里没有化石名,所以名单不能只加不改。) 按规矩补两处: · 名单里登记,并写清它对齐的是 WebUI 的哪个 token (`MailList.tsx:280` 的 `border-blue-200`,与 `bg-blue-50` 成对) · `Theme.ets` 登记表里写一行理由 —— 重点记下**为什么"选中"必须两个 token**: 选中与未读的底色相同(都是 `accentSoft`),只靠底色的话 "选中一封未读邮件"看不出任何变化,那圈边才是信息。 并把表头计数 24 → 26 同步改掉。 ══ ② harmony-widescreen —— 判据错了,它断言的是一个 WebUI 明令禁止的实现 原断言:`assert.match(code_, /clip\(this\.isWide\)/, '面板内容要被圆角裁剪')` —— 它把"圆角"和"裁切"当成**同一件事**。而 WebUI `index.css:968` 在**同一个规则块**里就写明了这条教训: .app-shell > * { border-radius: var(--radius-card); // ★ 这里**不能**写 overflow: hidden(2026-09-14 用户: // 「通信页面完全无法上下滑动」)。 // 面板自己就是滚动容器,而这条规则的特异性比 Tailwind 的 .overflow-y-auto // 高 ⇒ 滚动被静默干掉:实测当时**一个可滚动容器都不存在**(scrollerCount=0)。 // 圆角仍然生效(border-radius 不影响滚动) 鸿蒙这边同一个形状:那层是内容列的根、`Navigation` 的 `List` 在它里面, 写了 `.clip(this.isWide)` 就是 `List` 节点还在(高 1750px)但**滚不动** (实测 fling 后第一封仍在 y=526)——这正是用户当时报的"列表没法滚动"。 ⇒ 判据改成断言**真正的不变式**:宽屏「圆角开(14)+ 面来自设计令牌 + **不写** `.clip(this.isWide)`」。原来那句测的是"某句实现写着没写着", 而且那个实现是错的 —— 判据跟着错误实现一起钉住了 bug。 ══ ③ build-stamp —— 判据要求重构建,照做(**没有**去改 BUILD_INFO.json) 产物记的是 `c0ab3f5`,HEAD 已是新提交。这条判据自己的文本写得很清楚: > **别去改 BUILD_INFO.json 里的 gitRev / srcHash 了事** —— 那是把这条判据废掉。 > 正确修法只有一个:重跑构建。 所以 `npm run build` 重跑(rev 0)。注意 srcHash 本来就没变 (`6d1195a4008ebca7`)—— 因为 TRACKED 只扫 electron 侧(`src`/`index.html`/ `vite.config.ts`/…),我这几步改的全是 harmony;**变的只有 gitRev**。 这正说明这条判据在比对"产物来自哪个提交",而不是"文件内容有没有动"。 ★ 三条各自的性质不同,值得分开记:① 是判据对、我漏登记(补); ② 是判据错、钉住了一个反向实现(改判据);③ 是判据对且给了唯一正确修法(照办)。 --- .../electron/test/cross-client-theme.test.mjs | 14 +++++++++ .../electron/test/harmony-widescreen.test.mjs | 29 ++++++++++++++++++- .../entry/src/main/ets/common/Theme.ets | 12 +++++++- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/client/electron/test/cross-client-theme.test.mjs b/client/electron/test/cross-client-theme.test.mjs index 53b978f..47b7eb3 100644 --- a/client/electron/test/cross-client-theme.test.mjs +++ b/client/electron/test/cross-client-theme.test.mjs @@ -217,6 +217,20 @@ test('A|系统拥有的维度,鸿蒙侧的唯一来源是系统资源(不 const SELF_OWNED_COLORS = [ // 品牌(跨客户端身份,必须与 WebUI 逐字一致的那一个 + 它的前景/浅底/深色变体) 'accent', 'accentFg', 'accentSoft', 'accentStrong', + /* + * ★★ 2026-09-20 补登记(对齐 WebUI 的「选中态」两个 token)。 + * + * WebUI `MailList.tsx:280` 的选中态是 **底 + 边成对**: + * active ? 'bg-blue-50 border-blue-200' : 'border-transparent ...' + * · 底 blue-50 = #EFF6FF(= 我们的 `accentSoft`) + * · 边 blue-200 = #BFDBFE(= 这里的 `accentEdge`) + * + * 为什么需要单独的"边"token:选中与未读的**底色相同**(都是 accentSoft), + * 仅靠底色的话"选中一封未读邮件"看不出任何变化 ⇒ 必须靠那圈边区分。 + * 我第一版只搬了底忘了边,选中态淡到在浅壁纸上几乎看不见。 + * 深色变体 `accentEdgeDark` 对齐 WebUI `.dark` 段的 `--c-blue-200`。 + */ + 'accentEdge', 'accentEdgeDark', // 业务语义色:系统没有对应物(同意 / 拒绝 / 警示) 'approve', 'danger', 'approveBg', 'approveFg', 'dangerBg', 'warnBg', 'warnFg', // 权限档位与预算档位的胶囊配色(档位是产品语义,系统不认识"plan/workspace/full") diff --git a/client/electron/test/harmony-widescreen.test.mjs b/client/electron/test/harmony-widescreen.test.mjs index 5350dfa..9852998 100644 --- a/client/electron/test/harmony-widescreen.test.mjs +++ b/client/electron/test/harmony-widescreen.test.mjs @@ -286,7 +286,34 @@ test('⑤ 宽屏 app-shell 几何:padding/gap/radius 与 WebUI 同值(一比 assert.match(code_, /Row\(\{ space: Theme\.paneGap \}\)/, '宽屏 Row 用 space=paneGap(与 WebUI gap 同值)'); // 内容面板圆角只在宽屏启用(窄屏贴合全屏) assert.match(code_, /borderRadius\(this\.isWide \? Theme\.glassRadius : 0\)/, '内容面板圆角宽屏 14 / 窄屏 0'); - assert.match(code_, /clip\(this\.isWide\)/, '面板内容要被圆角裁剪(clip 宽屏才开)'); + /* + * ★★ 2026-09-20 改:原来这条断言的是 + * assert.match(code_, /clip\(this\.isWide\)/, '面板内容要被圆角裁剪(clip 宽屏才开)'); + * —— 它把"圆角"和"裁切"当成同一件事,而这正是 **WebUI 明令禁止**的做法。 + * + * `index.css` 在**同一个规则块**里就写着这条教训(`.app-shell > *` 那段): + * border-radius: var(--radius-card); + * // ★ 这里**不能**写 overflow: hidden(2026-09-14 用户: + * // 「通信页面完全无法上下滑动」)。 + * // 面板自己就是滚动容器,而这条规则的特异性比 Tailwind 的 .overflow-y-auto + * // 高 ⇒ 滚动被静默干掉:实测当时**一个可滚动容器都不存在**(scrollerCount=0)。 + * // 圆角仍然生效(border-radius 不影响滚动) + * + * 鸿蒙这边是同一个形状:那一层是内容列的根,`Navigation` 的 `List` 在它里面, + * 而 `.clip(this.isWide)` 把子树的滚动裁掉了 —— `List` 节点还在(高 1750px, + * 只放得下 6 张卡)但**滚不动**,实测 fling 后第一封仍在 y=526。 + * + * ⇒ 判据改为断言**真正的不变式**:宽屏「圆角开 + 裁切不写」。 + * 原来那条测的是"某句实现写着没写着"(且那实现是错的), + * 现在测的是"与 WebUI 同一条几何纪律":圆角在、`overflow:hidden` 不在。 + */ + assert.match(code_, /borderRadius\(this\.isWide \? Theme\.glassRadius : 0\)/, + '宽屏圆角开(14)/ 窄屏 0 —— 与 WebUI `.app-shell > *` 同值'); + assert.match(code_, /attributeModifier\(GlassCardModifier\.of\(this\.bgActive\)\)/, + '宽屏内容列的面来自设计令牌(不是就地写死一个实心 surface)'); + assert.ok(!/\.clip\(this\.isWide\)/.test(code_), + '宽屏内容列**不能**写 `.clip(this.isWide)` —— 会把 `Navigation` 里 List 的滚动整条裁掉' + + '(WebUI `index.css:968` 在同一个规则块里写明了这条,2026-09-14 用户实测"无法上下滑动")'); // 容器 padding 宽屏 paneGap / 窄屏 0(壁纸从缝隙露出) assert.match(code_, /left: this\.isWide \? Theme\.paneGap : 0/, '宽屏容器左右 padding paneGap(窄屏 0 贴合全屏)'); }); diff --git a/client/harmony/entry/src/main/ets/common/Theme.ets b/client/harmony/entry/src/main/ets/common/Theme.ets index 3d3d126..a7d2b53 100644 --- a/client/harmony/entry/src/main/ets/common/Theme.ets +++ b/client/harmony/entry/src/main/ets/common/Theme.ets @@ -277,7 +277,7 @@ export class Theme { * 第 12 个**新加的手写色**(它不在名单里,于是 A/B/裸色值三条都碰不到它)。 * 「枚举挡实例,类才挡漂移」—— 这次枚举的是**名字**,所以要有名单。 * - * 登记项(24 个,名字与判据里的 SELF_OWNED_COLORS 逐字一致 —— 逐个列出而不是缩写, + * 登记项(26 个,名字与判据里的 SELF_OWNED_COLORS 逐字一致 —— 逐个列出而不是缩写, * 这样 grep 一个名字就能找到它的理由): * * 品牌(跨客户端身份,系统给不了): @@ -289,6 +289,16 @@ export class Theme { * ★ 不登记会怎样:深色下选中卡片是接近纯白的浅蓝, * 在深色页面上刺眼得像 bug(2026-09-19 设备实测到) * · accentStrong #1D4ED8 品牌深色变体(选中文字/边框) + * · accentEdge #BFDBFE 品牌浅底的**边框** —— 对齐 WebUI + * `MailList.tsx:280` 的 `border-blue-200` + * (与 `accentSoft` = `bg-blue-50` 成对)。 + * ★ 为什么"选中"必须是**底+边**两个 token: + * 选中与未读的**底色相同**(都是 accentSoft), + * 只靠底色的话"选中一封未读邮件"看不出任何变化 + * ⇒ 那圈边才是区分"我正在读这一封"的信息。 + * 第一版只搬了底忘了边,选中态淡到几乎看不见。 + * · accentEdgeDark #2B3B57 上者的**深色**取值 —— 对齐 WebUI `.dark` + * 段的 `--c-blue-200`。入口 `accentEdgeFor()`。 * · dangerDark #F8A4A4 危险/拒绝在**深色**下当前景的取值 —— * 对齐 WebUI `.dark --c-red-700`(`248 164 164`)。 * ★ 入口 `Theme.dangerFor()`。