|
|
7b3028342a
|
跨端: 宽屏侧栏根本不像 WebUI —— 因为我上一版"复刻"的依据是编的
用户:「你自己看看宽屏的侧边栏和webui有哪怕一丁点的相似之处嘛?」
并排截图(WebUI 1100×700 @2x vs 三折叠展开态 3184×2232)之后,差异一眼可见:
| | WebUI | 鸿蒙(改前) |
|---|---|---|
| 文字标签 | **有**(通信/日历/联系) | 没有 |
| 选中态 | **浅蓝底块** | 只换颜色 |
| 「我的」 | 底部头像按钮进入 | 甩给 `onSettings` → **pushUrl 推页** |
| 品牌标颜色 | `#475569` 石板灰 | 品牌蓝 |
| 项间距 | 48px 项 + 4px gap,**贴顶一簇** | `layoutWeight(1)` 等分铺满(395px/项) |
## 根因:`WideSidebar` 里那段"复刻 WebUI"的注释是**编的**
```
* WebUI 的 `Sidebar`(60px 宽)是**纯图标轨**(无 label 文字)……
* 选中态:图标变色(`navFgActive`),**不加背景块、不加指示条、不加文字**
* (用户 2026-09-16:「底部导航栏不允许有文字」⇒ 侧栏同样按纯图标走)
```
两条都错,而且都能在源码里当场证伪:
- `Sidebar.tsx:110` 明明有 `<span className="text-3xs leading-none">{short}</span>`
—— 通信/日历/联系三个标签一直都在;
- `index.css:1590` 的 `.nav-item[data-active='true'] { background-color: … }`
就是底块,而且 CSS 注释**专门**说了侧栏必须有它:
「宽屏侧栏是 48px 宽的竖条,图标底下那一块底色是它**唯一的选中线索**,
所以"只变色"不能无差别推广到所有 `.nav-item`。」
我犯的错是**把底栏那条纪律套到了侧栏上**:用户 2026-09-14 说「底部导航栏选中
对应的文字和图标变色即可」、2026-09-16 说「底部导航栏不允许有文字」——
两句都针对**底部导航栏**,而侧栏是另一种东西(`index.css:1595-1606` 把这个区别
写得很清楚)。更糟的是我把这个错误**写进了判据**(`harmony-widescreen` ②③ 与
`harmony-nav` 的宽屏分支),于是判据锁住的是我编的理由,一路全绿。
## 修
- 侧栏项 = **图标 + 文字标签 + 选中底块**(`navActiveBg` = `--nav-active-bg` #DBEAFE,
判据**直接读 WebUI 的 CSS** 取值,不写死、更不引自己的注释)。
- 品牌标:`navBrandFg` = `#475569`(**像素取证**:2x 截图里品牌标附近最常见的墨色
是 `rgb(71,85,105) ×206` = `--nav-fg-muted`,即中性石板灰,**不是**品牌蓝);
尺寸/圆角按 WebUI `w-10 h-10 rounded-xl`(40×40、圆角 16);点它回收件箱。
- 项**贴顶一簇**(`Column({ space: 4 })` = WebUI 的 `gap-1`),不再 `layoutWeight(1)`。
- 删掉单列的"设置"入口(`onSettings` 回调一并删除)—— 那正是用户 2026-09-17 报过的
「我的页面完全没有遵守 nav 的导航规则」(push 页 ⇒ 侧栏整条消失)。
「我的」由 `ForEach(NAV_CONTENT_ITEMS)` 覆盖(该常量**含第 4 项**,
走 `onSelect(3)` = 窗格,与底栏同一套)。
- 补避让:侧栏原先**完全没有** `topInset` ⇒ 全屏之后品牌标被状态栏时钟压住。
## 判据(并修掉它们锁住的错误)
- `harmony-widescreen` ②③ **重写**:从"纯图标 / 只变色"改成
"有文字标签 / 有选中底块 / 不许留 `onSettings`",并读 WebUI `index.css` 拿真实色值。
- `harmony-nav` 宽屏分支:原来断言「侧栏项**不该有文字**」—— 同一条编造。
改成"图标(Path)画出来了 **且** 文字命中源码 `NAV_ITEMS`"。
- `harmony-nav` 宽屏形状阈值 `boxH > screenH*0.08` 是**错的**:48vp 项在密度 2.875 下
是 138px,而阈值要求 >178px ⇒ 四项全被滤掉(当时"通过"只是因为项被另一个 bug
压成了 39vp)。改成 `*0.04`,并补一条"必须有文字"把**品牌标**(40vp 无文字的可点方块)
排除在外。
**变异测试 3 个方向全咬**:去掉文字标签 ⇒ 红;去掉选中底块 ⇒ 红;Theme 色值写错 ⇒ 红。
★ 顺带记一条**我差点犯的错**:我一度按 density 3.5 换算,算出"60vp 侧栏被压成 49.4vp",
去查 flex 压缩、加 `.flexShrink(0)` —— 全是假的。实测密度是 **2.875**
(`138px ÷ 48vp = 2.875`),侧栏 173px ÷ 2.875 = **60.2vp**,与声明完全一致。
**没有压缩,是我除错了。** 已撤回那笔改动并把口径写进注释。
harmony-widescreen 6/6、harmony-nav 18/18、harmony-window 9/9、harmony-arkts 5/5、
harmony-contacts 5/5、harmony-calendar 30/30、harmony-system-api 5/5、harmony-logic 28/28。
|
2026-09-18 12:52:48 +08:00 |
|
|
|
af2d2b5cad
|
跨端: 鸿蒙动画补齐 —— 并且发现原来的「逐字一致」是假的(令牌存在 ≠ 动画用了它)
用户:「A,同时把鸿蒙app的动画补齐」。
## 先纠一条错的前提(这是本轮最有价值的发现)
`Theme.ets` 的注释与 `harmony-nav` 的判据**都**写着:「三个数与 WebUI **逐字一致**:
`--ease-out-soft: cubic-bezier(0.22,1,0.36,1)`、`--dur-fast: 120ms`、`--dur-base: 180ms`」,
`paneRiseIn()` 就按 `durBase(180)` + `easeOutSoft` 做。
三个令牌**确实存在**(`index.css:310-312`)—— 但这句话把「令牌存在」当成了「动画用了它」:
| WebUI 里 | 真实用途 |
|---|---|
| `--dur-base: 180ms` | **只**用在壁纸淡入(`:747`) |
| `--ease-out-soft (0.22,1,0.36,1)` | **只**用在 transition(壁纸、控件变色) |
| **所有 @keyframes 动画** | 硬编码 **150ms / 200ms** + `cubic-bezier(0.22, 0.61, 0.36, 1)` |
全仓 `(0.22,0.61,0.36,1)` 出现 **5 次**(rise-in×3 / cal-in×2),`(0.22,1,0.36,1)` 只出现
**1 次**(令牌定义处)。**是两根不同的曲线** —— 旧代码的面板入场比 WebUI 慢 30ms 且曲线偏软。
所以令牌拆成两组,各对齐各的:控件类 `durFast + easeOutSoft`;动画类
`durRise(150)/durMenu(140)/durCal(200) + easeRise(0.22,0.61,0.36,1)`。
## 补的动画(对齐 WebUI 三个 @keyframes)
- `menuIn()` —— 弹层:下移 4vp + 缩到 0.985 + 淡入(`@keyframes menu-in`)。
★ 适用范围照搬 WebUI 注释那条窄口径(「只给真正是弹层的东西」):那条规则曾挂着
`glass-control`,于是每次切视图**所有按钮与输入框一起淡入位移**,09-15 被摘掉。
挂到写邮件页的账号候选列表上。
- `calendarSlide(forward)` —— 日历翻月:从 ±12% 横向滑入(`cal-in-next/prev`)。
方向由新增 `@State slideForward` 带进 `animateTo` 闭包;网格键加 `monthKey()` 前缀
保证月份一变所有键全变(否则"跨年同名月"那类边角会静默不播)。
- 写信页整页 `rise-in`、回复框 `rise-in`(WebUI 挂在回复框本身,不是外层遮罩 ——
挂遮罩上会让整个屏幕一起位移,看起来是"页面在动"而不是"框弹出来")。
**实测确认真的会播**(不是"编译过了"):按本仓记录的手法把 `durCal` 临时改成 8000
做慢动作,连拍三帧 —— 截图硬证**两张月历同时在屏**(九月淡出、十月从右侧 12% 滑入),
验完还原成 200ms。
## 判据:从「令牌存在」改成「动画真的用了那个令牌」
`harmony-nav` 那条判据原文锚在令牌上,所以它对上面那个 bug **一辈子全绿**。
重写为:
- 从 WebUI `index.css` **读出** `rise-in` 的真实时长与曲线(`150ms` + 那条 bezier),
再断言鸿蒙的 `durRise` 与曲线字面量与之逐字一致 —— 而不是"仓库里有没有 180 这个数";
- ★ 把 `paneRiseIn()` 的**函数体抠出来**单独断言它引的是 `durRise/easeRise`,
且**不得出现** `durBase/easeOutSoft`。
**这一步不能省 —— 我第一版就漏了它**:断言了令牌存在、也断言了曲线字面量存在,
但没断言函数用了它们;于是把函数体换回旧的错值后判据**仍然全绿**。
变异自检抓住了这一点,补上函数体断言后同一个变异 ⇒ 判红 ✓。
与今天修的另一处同源:**判据要锚在"这个东西被用在哪",不是"它存在"**。
|
2026-09-18 10:56:57 +08:00 |
|
|
|
2166f0ed81
|
fix(harmony): 补齐窗格切换动画(transition 挂在会换的那棵子树上)
用户(2026-09-17):「一方面一点动画都没有」。
★ 第一版写错了,这里记下来 —— 它的错法很典型,下次还会踩:
只在 onClick 里把 `currentIndex = ...` 包了一层 `getUIContext().animateTo(...)`,
并把 `.transition(...)` 挂在**内容容器(稳定父节点)**上。
编译过、判据(当时只钉"有 animateTo")全绿,**但动画是静默失效的**:
`animateTo` 只负责开一个动画窗口,被换掉的子树自己不声明 transition 就什么都不会动;
而挂 transition 的那个 Column 在新旧两种状态下**都是同一个节点**,永远不会触发。
实测取证:把时长临时改成 20s,6 秒后截图仍是硬切(收件箱整版清晰、没有叠影)。
修法(对齐 WebUI `.pane-rise` / `.rise-in`,`index.css:1208`):
· `Theme.paneRiseIn()` —— 4vp 上浮 + 淡入;`riseInOffset = 4` 与
`@keyframes rise-in { from { opacity:0; transform: translateY(4px) } }` 逐字一致。
· 挂到**if/else 各自的子树根**上(CommPage / ContactsTab / SettingsPane,
以及常驻+visibility 的日历那一支)—— 这才是会被插入/移除的节点。
· `TransitionEffect.asymmetric`:入场 180ms(durBase)、出场 120ms(durFast)。
出场更快是刻意的:同长会让新旧两层半透明地叠着,看起来像"闪一下",
而用户对"闪"敏感(09-14 否掉过整屏淡入)。
· 续用令牌 `durFast=120` / `durBase=180` / `easeOutSoft=cubic-bezier(0.22,1,0.36,1)`。
判据(219 passed / 0 failed):
· 新增「窗格切换有真的过场动画」6 条:令牌数值逐个钉死(120 / 180 /
cubic-bezier(0.22,1,0.36,1));`transition` 必须挂在 if/else 分支根(≥2 处,
含日历那一支);必须 asymmetric。
· **变异自检**:删掉那 4 处 `.transition(...)` ⇒ 该条立刻变红;还原 ⇒ 全绿。
留给以后的坑(写进 Theme 注释):`snapshot_display` 单次往返约 1s,
180ms 的过渡**抓不到**(连拍三帧像素级一致,会得出"动画没做"的错误结论)。
要取证就得把时长临时调到 20s 再取样,验完还原。本文件注释里留了这条与那次实测值。
未验:真机手感(模拟器已确认过渡链路生效);出场动画与详情 push 的叠加观感。
|
2026-09-17 21:33:15 +08:00 |
|
|
|
686193458a
|
fix(harmony): 底栏黑带 / 联系人点不开 / 我的页不守导航 / 顶栏硬截断
用户(2026-09-17)连报五条:「一点动画都没有」「底部那个黑条是啥意思」
「日历页面、我的页面、联系人页面哪个跟 webui 对齐了」「联系人页面连点都点不开」
「你的顶栏为什么还是硬截断而不是渐变」。逐条查证后修:
── ① 底部黑条 = 系统导航栏区域,不是我们画的 ──
根因:窗口默认给系统导航条留位,那块在深色下是黑的;界面于是看起来底下多一条黑带。
修法:`setWindowSystemBarEnable(['status'])` 隐藏系统导航条(底部那条本就是我们自绘的
悬浮玻璃条),内容铺满全高。
★ 刻意**不用** `setWindowLayoutFullScreen(true)`:实测试过,它也能消掉黑带,但会连
状态栏区域一起吃进布局,页签栏被时钟/电量盖住(截图硬证「07:43」与「收件箱」重叠)。
── ② 联系人点不开(用户原话「连点都点不开」)──
根因:`ContactItem` / `WorkCard` **根本没有 onClick** —— 卡片画出来了,
但没有任何点击路径。当时判据只钉了"字段与 WebUI 一致",没钉"点了会发生什么"。
修法:点卡片打开那条会话的邮件列表,就地在**本窗格内**展示(新增 `SessionMailsView`
+ `GET /sessions/{id}/mails`),底部导航保留;每封可再点进详情。
── ③ 我的页不遵守导航规则 ──
根因:点底栏「我的」走 `pushUrl('pages/SettingsPage')` 推**独立 @Entry 页** ⇒
底部导航整条消失,要按返回才能再切窗格。WebUI 里 `account` 只是一个 `viewMode`,
与收件箱同级、导航常驻。
修法:`SettingsPage` → `SettingsPane`(去掉 @Entry 与返回键,从 main_pages.json 摘除),
作为**第 4 个内容窗格**挂到 `currentIndex === 3`;`NAV_CONTENT_COUNT` 3 → 4,
`NAV_ITEMS` 第 4 项去掉 `route`;拨动动画包 `getUIContext().animateTo`。
实测:`我的` 页底部导航可见,可滚到「退出登录」「管理」。
── ④ 顶栏硬截断 ──
根因:`Scroll` 与 `List` 都能挂 `fadingEdge`(同在 `ScrollableCommonMethod` 上),
但**只有 MainPage 的 List 挂了** —— MailDetailPage / CalendarPage / SettingsPage /
AdminUsersPage / SessionsPage / InboxPage 的滚动容器全漏。同一次滚动里
列表是渐隐、正文是硬切,看起来就是"顶栏没做渐变"。
修法:13 个容器全部补齐(LoginPage 例外:它的 Scroll 是整页根、不是列表,
写了理由)。实测:视口上沿被切断的文字最暗 170–196,而下方不透明正文是 **19**。
── ⑤ 一点动画都没有 ──
实测确认改造前全仓 `animateTo` / `transition` / `animation` **一次都没用过**。
补:Theme 加令牌(durFast 120 / durBase 180 / easeOutSoft = cubic-bezier(0.22,1,0.36,1),
与 WebUI `--dur-*`/`--ease-out-soft` 同值同曲线);切窗格包 `animateTo`;
导航项选中变色加 `.animation`。
判据(218 passed / 0 failed):
· 新增「渐隐覆盖**所有**页面滚动容器」:按文件枚举 + 计数配平,
并自检扫到的容器总数(防正则写坏 ⇒ 全绿)。**这条一写就抓到 LoginPage**。
· ①/②/⑤ 与 headless 那几条按新事实改写:内容窗格 3→4、
`normalizeNavIndex(3)` 归一到自己、导航项里**禁止**再出现 `pushUrl`
(那正是导航消失的形状)。
· 修掉自己引入的两处判据缺陷:`itemCode` 未定义;以及一条**窗口式断言**
(从内容层 `}` 往后扫,靠数花括号定界,而注释里就有大量 `.padding({`,
切片长到 1700+ 字符一路扫进 `onAreaChange` ⇒ 假红),改为纯负向断言。
未验:真机观感;横屏 Auto Split 双栏;日历页深度对齐(本轮只补了渐隐)。
|
2026-09-17 21:15:11 +08:00 |
|
|
|
fd4b475920
|
ke: 鸿蒙导航纯图标 + 图标加大 + 玻璃几何令牌
用户反馈:① 图标太小 ② 底部导航栏不允许有文字。
- NavItem(底部导航):去掉 Text(item.label) 纯图标,AmIcon 22→28vp
- WideSidebar(宽屏侧栏):去掉 label 文字(WebUI Sidebar 本就是 60px 纯图标轨),
导航图标 22→26vp、品牌标 26→30vp;NavItemBuilder 去掉 label 参数
- Theme:新增 glassRadius=14 / paneGap=10(与 WebUI app-shell CSS 变量同值,
用于宽屏玻璃面板几何;原有 radiusCard 保持系统跟随不动)
- 判据同步:harmony-nav ⑤ 改纯图标(1 处三元式 + 无文字 + 28vp)+ 变异自检三项;
harmony-widescreen ③ 同样改纯图标(1 处 + 无文字 + 26vp)
|
2026-09-16 07:54:44 +08:00 |
|
|
|
d1e0ba32f9
|
跨端: 壁纸遮盖色改用页面底色系(mask 两套主题都深,浅色下方向与 WebUI 相反);模态遮罩保持 mask
pi 提了一个**方向性**疑问,让我先把值读出来再定 —— 读完确认**他是对的**,而且这是"机制上确定不同"。
## 实测(两张 SDK 表交叉验证,不靠记忆)
`ets/build-tools/ets-loader/sysResource.js` 给名字→id,`previewer/.../resources.txt` 给 id→值:
| 令牌 | 浅色主题 | 深色主题 |
|---|---|---|
| `ohos_id_color_mask_regular`(原 `Theme.overlay`) | `#99182431` **深蓝灰** | `#b2000000` 黑 |
| `ohos_id_color_background` | `#ffffffff` **白** | `#ff18181a` 近黑 |
⇒ mask 的 light/regular/thick 是**浓度档**(同一深色的三个 alpha),不是深浅两套值:
它在**两套主题下都是深色**(模态遮罩语义)。而 WebUI 的 `--bg-scrim` 浅色是**白**
(原话「浅色下用白把花哨的图案洗淡」)—— 壁纸遮盖用 mask 就是**反方向**:
浅色主题下把预设压暗,而 WebUI 是把它洗淡。
## 改法
- 新增 `Theme.wallpaperScrim = $r('sys.color.ohos_id_color_background')`(页面底色系:
浅色白、深色近黑,**自动换向**,与"朝底色淡化"同一意图);预设档与图片档的遮盖层都用它。
- `Theme.overlay`(mask)**只留给模态弹层**(那里语义确实是压暗背后)。
- 不复用 `pageBg` 的原因写进注释:页面底在背景开启时会被换成**透明**
(`bgActive ? Color.Transparent : Theme.pageBg`),而遮盖层**永远要一个真实颜色** ——
两个用途生命周期不同,共用一个名字迟早坏一头(这个仓库撞过四次的模式)。
## 判据(从 SDK 读真值再判,不写死结论)
断言「mask 两套主题都深」「页面底色系浅白深黑」「WebUI 的 `--bg-scrim` 浅色是白」,
再落到代码:两处遮盖层必须用 `wallpaperScrim`、弹层仍用 `overlay`、两个令牌不许混用。
**这样"令牌选错方向"以后不能靠记性避免。**
变异:遮盖退回 mask → 红 1 条;只改一层 → 红 2 条。
## 文档
§7.17b-2 记这次读数(表格 + 两张表怎么读 + 为什么不复用 pageBg + 变异结果);
§7.12 有意差异表加一行(遮盖色令牌:两个语义两个令牌,"不允许混用")。
## 验证
`hvigorw assembleHap` BUILD SUCCESSFUL;`npm test` 退出码 0
(12 个判据文件全绿 + vitest 258/258;harmony-appearance 23 → 24 条)。
|
2026-09-14 15:10:15 +08:00 |
|
|
|
a6b5e52f45
|
test(harmony): 判据按 pi 复核意见补强 —— 手写色登记表、玻璃叠用形状、按标题取小节、遮罩必须被用、废弃 API 清单从 SDK 生成
pi 逐行读了 `cross-client-theme.test.mjs` 与 `Theme.ets` 后指出五处(第一处是真缺口),
外加一条建议。全部处理,并且**每一处都用变异验证过**。
## 一(真缺口):枚举 11 个"必须是系统资源"的名字,挡不住第 12 个新写死的手写色
`static readonly brandSecondary: string = '#123456'` 这种新增**三条判据都碰不到**:
A(不在名单里)、B(六位、不是半透明)、裸色值那条(只管 `pages/`)。
原理与当初 14 处 Google 色逃出去是同一条 —— **枚举挡实例,类才挡漂移**,
只是这次枚举的单位是**名字**。补 `A2` 条:
- 枚举 `Theme.ets` 里所有 `static readonly X: string = '#……'`,未登记的 → 红;
- 名单里已不存在的名字 → 红(名单不能烂成化石);
- `Theme.ets` 里的「手写色登记表」段必须逐个列出这些名字(理由不能只存在于记忆里);
- 自检:把一个**新的**手写色塞进源码字符串,确认这条抓得到。
`Theme.ets` 因此新增登记表(17 项,每项一行理由,按品牌 / 业务语义 / 档位胶囊分组)。
变异:Theme.ets 加 `brandSecondary` → 红。
## 二:C 的"玻璃只有一处"**说错了自己断言的东西**
`assert.equal(glassCalls.length, 1)` 断言的是"全仓共一处",**不是**"没有嵌套":
同页两个**并列**玻璃面(没问题)会让它红,真嵌套它没在判。P5 正是悬浮玻璃导航,
到时若把 1 改成 2 就等于不判。改成形状判据:
- 收集每个调用点所作用的**组件块**(按括号配对回溯;注意 ArkUI 修饰符是链式的,
`X.blur(A).blur(B)` 前面是 `)` 不是 `}` —— 第一版只看一个字符,对这种写法**静默失效**,
是判据自检抓出来的);
- 判两种叠法:同一组件上叠多次(同一块)、以及套在另一层玻璃的子树里;
- 每一处玻璃都要在 `GLASS_REGISTRY` 里登记(附一句为什么),名单里的位置若已不存在 → 红。
变异:链式叠两层 → 红;通信页多开一处未登记玻璃 → 红;导航条块内嵌玻璃 → 红。
## 三:文档小节用 `indexOf('有意差异')` 会**拿错段落**
别的段落正文里出现这四个字,切片就从那里开始,后面所有断言都在**别的段落**上判。
改成**按标题**定位(正则匹配 `^#{2,4}…有意差异…$`),并加了表头列名断言
(WebUI / 鸿蒙 / 为什么)—— 拿错段落时表头不会是这个形状,于是它自己会红。
顺带修掉一个**我自己的**同类毛病:品牌色那行原来用"关键词 + 80 字符窗口"判,
窗口宽度在赌表格单元格字符数(该行两格之和 > 80)。改成**按行取**那一行再断言。
另外把偏移算术的切片换成**按行**切片(偏移差一个字符就会把最后一行拦腰截断,
现象是"品牌色那行只剩 57 字符"这种看着像文案、其实像切片的怪事)。
变异:文件前面插入含「有意差异」的段落 → 仍绿(按标题定位生效);
再改坏真表里的品牌色行 → 红(确实读的是那一张表)。
## 四:`Theme.overlay` 只判了"存在",没判"被用"
没使用点的令牌是自证。补断言:它必须在 `Theme.ets` **之外**有真实使用点
(实测 `SettingsPage.ets` / `MailDetailPage.ets` 两处自绘弹层在用),
并把 `overlayColor`/`overlayAlpha` 消失的理由写进 §7.12 —— 否则下一个人会当成漏改补回来。
## 五:材质档次是这次替换里唯一"判据绿但可能观感错"的地方
`COMPONENT_THICK` 的依据(对 THIN / BACKGROUND_* / ULTRA_THICK 的取舍)写进 §7.12,
并把"材质档次在导航条上的实际观感"列为**模拟器起来后第一个要看的项**(间距是数字,材质是判断)。
## 六(建议):废弃 API 判据**类化** —— 清单从 SDK 生成
原来是手写"不得再用全局 `promptAction.showToast`"(只挡已踩过的那个)。
现在从 SDK 生成:顶层(花括号深度 0)被标 `@deprecated` 的 `declare function`
—— 实测 68 个名字(含 `animateTo` / `getContext` / `px2vp`),配一份**空**的 allow-list。
深度判定是必要的:`declare namespace fileIo { declare function open() }` 里的 `open`
是命名空间成员,算进来会造一堆假红。只算全局调用(排除 `x.name(`)。
变异:调 `px2vp(10)` → 红;`this.px2vp(10)`(成员调用)→ 绿(假阳性自检)。
## 验证
`hvigorw assembleHap` BUILD SUCCESSFUL;`npm test` 退出码 0
(11 个判据文件全绿 + vitest 258/258)。cross-client-theme 13 → 14 条,harmony-system-api 4 → 5 条。
|
2026-09-14 14:26:45 +08:00 |
|
|
|
36f3183bba
|
feat(harmony): 系统方案第一批 —— 表面/文字/圆角交给系统、删手写玻璃、每项一张卡;跨端判据改"意图相同"
jianf:「鸿蒙也同步,但是鸿蒙要求用系统方案」。按 pi 对齐的形状(A/B/C + 品牌色防线)落地。
## 鸿蒙侧改了什么
- `Theme.ets` 的表面/文字/分隔/遮罩/圆角**来源换成系统**:
`pageBg→sys.color.ohos_id_color_background`、`surface→…_list_card_bg`、
`surfaceMuted→…_sub_background`、`border→…_list_separator`、三级文字 `→…_text_primary/secondary/tertiary`、
`overlay→…_mask_regular`、`radiusCard/Control→sys.float.ohos_id_corner_radius_card/button`。
于是这些维度自动跟随深色模式与无障碍设置 —— 这正是"手抄 WebUI 色值"做不到的事。
- **删掉手写玻璃** `#B8FFFFFF`/`#B80F172A`:那两个值等于"我们替系统猜了深色该怎么做"。
换成一个**档次**声明 `navMaterial: BlurStyle = BlurStyle.COMPONENT_THICK` + 导航条上的
`.backgroundBlurStyle(...)`;深浅两套颜色与模糊半径由系统按主题给。
遮罩的两段式(色 + 透明度)同样删掉:拆两段本就是为了"随主题换向",而这件事系统已经做了。
- **每项一张卡/气泡**:收件箱行、会话组头、联系人列表行改成卡片(圆角 + 卡片底色 + 行间距),
联系人列表那条贯通分隔线删除。
- 仍然自己写的只有两类:**品牌色**(`accent = #2563EB`,跨客户端身份)与**业务语义色**
(权限三档、预算三档 —— 系统只有 warning/alert 两个情绪色,凑不出三档,硬套会丢语义)。
## 判据:从"取值相同"改"意图相同"(两侧一起改)
- 品牌蓝**唯一保留取值钉**,并新增防线:不得退化成 `$r('sys.color.*')`
(系统强调色随主题/厂商皮肤变,"两个客户端是同一个产品"就靠不住了)。
- 圆角/材质/遮罩:改成"WebUI 自声明令牌 + 鸿蒙来自系统 + 差异被记录"(§7.12 有意差异表)。
- 新增 A(系统拥有的维度唯一来源是 `$r('sys.*')`,且不得退回 string/number)、
B(旧机制不得回来:`navBg*`、8 位半透明色、`rgb(`/`rgba(`、写死的 14/8)、
C(玻璃位置必须调 `backgroundBlurStyle` 且**只许一层**)。
- pi 指出的洞已补:裸色值判据原来只扫 `#RRGGBB(AA)`,抓不到 `rgba(`/`0xRRGGBBAA` ——
而这几种恰是"改用系统材质"时最容易混进来的形态。现在四种一起扫,且**先剥注释**
(注释里正当地引用旧写法不该被judged红)。
- pi 的 §5 建议也已落地:新增"版本库不得跟踪缓存/构建产物"判据(`.tmp/` 那次 554 个文件的事故判据化),
并放行 `server/internal/static/static/placeholder.html`(go:embed 落点的有意占位,删了 Go 侧编不过)。
## 变异验证(能红,且红在对的地方)
| 变异 | 结果 |
|---|---|
| 品牌蓝 → 系统强调色 | 红 3 条 |
| 手写玻璃 `navBgLight` 回来 | 红 2 条 |
| 导航改用写死半透明色、不调 `backgroundBlurStyle` | 红 2 条 |
| 卡片上再开一层模糊 | 红 1 条("玻璃应只出现在一处,实际 2 处") |
## 验证 / 未验
`hvigorw assembleHap` BUILD SUCCESSFUL;`npm test` 退出码 0
(10 个判据文件全绿:跨端 13 条、系统资源名 4 条、harmony-logic 19 条… + vitest 258/258)。
**未验**:观感(卡片间距、系统材质在导航条上的实际效果、深色模式)—— 需真机/模拟器;
模拟器要人在命令行跑一次 `harmony-emu start`。`sys.*` 名字全部对着 SDK 名表核过,且判据持续盯着。
|
2026-09-14 14:04:04 +08:00 |
|
|
|
3729afd96f
|
feat(harmony): P2a —— 收件箱按会话折叠 + 联系人页卡片视图(判据直接跑同一份逻辑)
按 pi 的结论落地 P2a 的前一半:**先补视图与折叠,再删平级「会话」tab**(tab 本轮保留)。
## 判据怎么"点用户真正会点的那一层"
鸿蒙侧没有设备(`hdc list targets` 为空、模拟器在本机沙箱下起不来),"点一下"暂时
无法自动验。应对不是编个能过的新判据,而是把会点的那一层的内核抽成纯逻辑:
`entry/src/main/ets/model/MailGrouping.ts`(无 UI 依赖),判据用 node 的
`--experimental-strip-types` **执行同一份代码**(`test/harmony-logic.test.mjs`,14 条),
断言的是行为而不是"源码里出现过某个字符串":
- 折叠后组头是不是**最新一封**、组内是否时间倒序、组间排序、同一时刻用 `mail_id` 倒序兜底;
- 时间解析失败**不能让顺序依赖入参**(WebUI 侧踩过的 NaN 比较坑);
- 多账号合并下同名 `session_id` 不能被错并成一组;`session_id` 缺失时各自成组;
- 预算档位与 WebUI `BudgetChip` 完全一致(剩 0 用尽 / ≤1 将尽 / 上限 0 不显示);
- 视图切换与卡片上"最新一封是人还是 Agent"的判据。
页面那一层另用源码判据钉"确实调了这些函数",两层合起来覆盖「逻辑对」+「页面接上了」。
**变异验证 4 处全部判红**:去掉组内排序(2 条红)、预算阈值 `<=1` 改 `<1`、
分组键去掉账号前缀、页面不再区分单封组。
## 收件箱折叠
- 组头取组内最新一封的别名与主题,带未读数徽标与「N 封」,点它展开/收起;
- **单封不成组、平铺**(与 WebUI `isFlatGroup` 同结论:给孤立一封信套组头只是多一次点击);
- 多账号是鸿蒙特有:分组键带账号前缀;`session_id` 缺失按 `mail:<id>` 各自成组。
## 顺带修掉一个"看起来是总数、其实是未读数"的显示
`/me/mail/inbox` 的 `total` 是 **`CountUnread`(未读总数)**,不是总封数
(`server/internal/handler/me.go`)。鸿蒙底部原写「共 N 封」⇒ 同一屏出现
「共 7 封」和「未读 7」两行自相矛盾的字。改成:未读数用服务端 `total`(权威,
原来数这一页会少报);「共 N 封」→「已加载 N 封」;**这一页取满时如实提示
「已加载 50 封(本页上限 50,可能还有更多)」** —— 客户端没有可信总封数,
就不能把 50 封说成全部(pi 提醒的"别让只取 50 封伪装成只有这么多会话")。
WebUI 侧不读这个字段,故只影响鸿蒙。
## 联系人页补卡片视图(撤 tab 的前置)
- 右上角切列表/卡片,标题「联系人」/「工作列表」(与 WebUI 同词),切换规则在
`nextContactView()`;
- 卡片对应 WebUI 的 `WorkCard`:Agent 名 + 工作目录 + 未读徽标、会话别名、
**主题当主角**、最新摘要 + 人/Agent 标记、`N 封 · 时间`、权限档位徽标、
**往返预算条**(同一档位判据)。
- 平级「会话」tab 暂留:撤 tab 按 pi 的顺序排在后面单独一步(撤早了预算/status/from_agent 没处看)。
## 验证
- `hvigorw assembleHap` **BUILD SUCCESSFUL**(`.ts` 纯逻辑模块被 `.ets` 引用,实测可行)。
- `npm test` **退出码 0**:窄屏布局全通过、主题 30、背景 34、cross-client 8、
harmony-logic 14、packaging 3、vitest 258/258;新判据已接进 `npm test`。
- **视觉与点击仍未验**(无设备):展开手感、卡片间距、组头命中区没有任何自动判据
能代替人眼 —— 交付按"结构/逻辑已验证、观感未验"写,未写成已完成。
|
2026-09-14 13:36:05 +08:00 |
|
|
|
b041ea51e4
|
fix(harmony): 令牌收尾 —— 裸色值清零、遮罩拆两段式、权限档位配色对齐 WebUI
接手核对时发现:「不许写死颜色」那条判据**只挡得住枚举的 8 个旧值** —— 判据全绿的
同期,pages/ 里还留着 14 处另一套写死的色(Google/Material:#E8F0FE、#E8F5E9、
#FFF3E0、#D93025、#777777×2、#555555、#444444、#cccccc、#aaaaaa、
遮罩 #80000000×2、透明 #00000000×2)。枚举挡不住漂移,只有"类"能挡。
## 改法
- 14 处全部换成令牌。新增 accentStrong / warnBg / warnFg(取值对齐 WebUI `:root`
的 blue-700 / amber-50 / amber-700)与 `Theme.permBg/permFg` —— 权限档位徽标与
WebUI 的 `PermissionChip.tsx` **同一映射**(plan=蓝 / workspace=绿 / full=琥珀);
原先写成 `full ? 绿 : 橙`(Material 色),与 WebUI **反着来**。
- 遮罩拆成 `overlayColor` + `overlayAlpha`(照 WebUI 的 `--bg-scrim` + `--bg-dim`
两段式):遮罩色要能随主题换向,色与透明度焊死成一个 `#AARRGGBB` 等于把枚举写回
代码。ArkUI 只认单值,故由 `Theme.overlay()` 组装。
- 判据从"枚举旧值"改成"按类挡":pages/ 下**一个裸色值都不许有**(含 8 位
`#AARRGGBB`),页面清单从硬编码 7 个文件名改成**扫目录** —— 旧写法下,
接下来要加的发件箱/授权/日历会自动逃出判据。另加一条判据:权限档位配色与
WebUI `:root` 变量逐一比对,防"看起来差不多"。
## 验证
- 变异测试:往 `InboxPage.ets` 塞一个 `#E8F0FE` → 判据红;撤回 → 绿。
- cross-client 判据 6 → 8 条全绿;`hvigorw assembleHap` BUILD SUCCESSFUL。
- **视觉未验**(模拟器在本机文件沙箱下起不来,见 `docs/HARMONY-ALIGN-PLAN.md` 5.4),
未写成"已完成"。
|
2026-09-14 13:29:50 +08:00 |
|
|
|
5434bc9e4e
|
feat(harmony): 对齐第一阶段 + 对齐计划文档(差距/分期/验收纪律)
用户:「安排对齐」。
## 先量差距(不靠感觉)
鸿蒙侧的调色板与 WebUI **根本不同**:`#1A73E8`(Google 蓝)vs 品牌 `#2563EB`、
`#333333` vs slate-900 `#0F172A`、`#F5F7FA` vs `#F8FAFC`、`#FF4444` vs red-600…
共 217 处硬编码色值散在 7 个页面里。
功能面:鸿蒙是 收件箱/会话/联系人 三个 tab,**缺 发件箱 / 授权 / 日历 / 管理**,
也没有玻璃悬浮底栏、主题壁纸同步、Composer 共用组件。
## 第一阶段(已完成并可验收)
- `common/Theme.ets` 补齐文字/浅底/语义令牌,页面里的旧调色板**全量替换为令牌**
(共 203 处),`hvigorw assembleHap` **BUILD SUCCESSFUL**。
- 判据:`cross-client-theme.test.mjs` 新增"鸿蒙页面里不得再出现旧调色板色值"
(6 条全绿,含扰动自检)。这条防的是**新页面又随手写个"差不多"的颜色** ——
漂移就是这么开始的,而此前没有任何判据会红。
## 计划文档:docs/HARMONY-ALIGN-PLAN.md
写清两件事,免得每轮重新猜"还差什么":
- **差距表**(逐项,标出"缺页面/交互不同/观感不同"的性质);
- **分期**:P2 发件箱(与收件箱同构,风险最低)→ P3 授权页(备注必须随决策送达模型
—— WebUI 侧踩过这个坑)→ P4 主题/壁纸同步(服务端"无记录"时以本地为准)
→ P5 玻璃悬浮导航 → P6 日历(最大,单独排)。
## 如实说明
鸿蒙**视觉未验证**:本机 `hdc list targets` 为空、HAP 未签名 ⇒ 只能保证编译通过 +
令牌一致,观感需要设备或签名后由人眼确认。文档里也把这条写进"验收纪律"。
|
2026-09-14 12:49:40 +08:00 |
|
|
|
cefd96a6a3
|
feat(clients): UI 设计同步到客户端 —— Electron 包重建 + 鸿蒙设计令牌
用户:「下一步就是同步 ui 设计到客户端了」。
## ① Electron 客户端(之前严重滞后)
打包产物停在 **09:11**,而前端 dist 是 **12:23** ⇒ 今天所有 UI 工作(导航合并、悬浮玻璃、
圆桌语言、Composer、动画、模糊分层…)**一个都不在包里**。已重建 AppImage + deb。
**验证**(关键:AppImage/deb 是压缩容器,`grep` 直接扫是扫不到的 ——
我第一次就差点因此得出"包里没有"的结论):把 deb 解到 /tmp 再查 `app.asar`:
comm-tabs=2 compose-fab=1 nav-rail=2 glass-control=2 cal-slide-next=2
构建戳 "5ce25f6·0914-1240"(与当前提交一致)
## ② 鸿蒙客户端
它是**原生 ArkTS 应用**(22 个 .ets、自带 API 层),不是 WebView 壳 ⇒ 设计要移植。
第一步做的是**共用词表**:新增 `common/Theme.ets`(品牌蓝、页面底、面、分隔线、
导航玻璃不透明度、圆角 14/8、语义色、字号),全部注明与 WebUI 令牌的对应关系
(含一个易错点:CSS 是 `rgb(r g b / a)`,鸿蒙是 `#AARRGGBB`,0.72×255≈184=0xB8)。
顺带修掉一个真 bug:TabBar 的选中色写成 `this.currentIndex === 0` ⇒
**只有第一个 tab 会高亮**。现在按每个 tab 自己的下标判断。
**编译验证**:`hvigorw assembleHap` → **BUILD SUCCESSFUL**(HAP 已打包)。
(无法在设备上跑:本机 `hdc list targets` 为空、HAP 未签名 ⇒ 视觉未验证,如实说明。)
## ③ 判据:跨客户端令牌一致性
新增 `test/cross-client-theme.test.mjs` 5 条:品牌蓝、卡片圆角(0.875rem=14px)、
导航玻璃 0.72 —— 断言的是"两边对同一件事取值一致",不约束实现方式
(CSS 变量 vs ArkTS 常量本来就该不同),并带一条扰动自检。
这种漂移**没有任何判据会红**,所以必须显式钉住。
## 还没做的(如实说明)
鸿蒙端只同步了**设计语言**,功能面不对等:鸿蒙是 收件箱/会话/联系人 三个 tab,
没有 日历/授权/管理;也没有玻璃悬浮底栏与 Compose 共用组件。要做功能对齐是另一件事,
需要单独排期(我可以按你的优先级来)。
|
2026-09-14 12:43:08 +08:00 |
|