|
|
eeecaad79e
|
鸿蒙|日历翻月滑动动画修好(.transition 不播 → 显式属性驱动)
用户:「日历页面还是没有左右滑动动画」。
## 实测定位(不是推理)
把动画时长临时放大到 5s + Linear(确保抓得到中间帧),点「‹」后连拍,
逐偏移互相关算位移:
最佳 dx = 0 ROI 平均差 0.00
只留 `translate`、去掉 `opacity` 后直拍三帧:帧1 与帧3 **逐像素相同**。
⇒ 一点横向位移都没产生。
## 根因
`.transition()` 只在组件**挂载/卸载**(`if` 条件切换)时触发。
而翻月只改 `year`/`month`,网格 `Column` **一直存在** ⇒ 过渡永远不触发。
代码里那句注释("键带 monthKey() 前缀 ⇒ 节点重建 ⇒ 过渡必播")说的是
`ForEach` 的**子项**键 —— 外层容器并不会因此重挂。这是我当初写错的假设。
## 修法:改用本仓已验证可播的那套
`MainPage` 的日历窗格入场(`calPaneIn`)用的是
① `@State` 数值;② 显式 `.translate()`/`.opacity()`;③ `animateTo` 同改。
这里照它做(`slideInPct` / `slideInOpacity`),并与 WebUI `cal-in-next/prev`
逐字同源:**12% + 淡入**、方向由 `forward` 决定。
顺序纪律(与 `calPaneIn` 同):**起点值写在 animateTo 外面**,
写进闭包会让渲染层只看见最终值 ⇒ 退化成瞬移。
修复后复验(同一次 5s 放大连拍):
帧序列 = 旧月静止 → **横向位移 100px** → 新月静止 ✅
## 判据
`animation-audit` 的 `cal-in-*` 条目从"`Theme.calendarSlide` 有调用点"
改为"页面里有 `slideInPct`/`slideInOpacity` 驱动 + `.translate` 接线"。
★ 删掉死方法 `Theme.calendarSlide`(它已无调用点 —— 判据当场抓到,
这正是"必须盯调用点"那条纪律的价值)。
变异(两条都能红):
· 删掉 `.translate({x: this.slideInPct})` 接线 → cal-in-next 红
· 把起点值写回 animateTo 闭包(退化成瞬移)→ cal-in-prev 红
套件:animation-audit 15/15、harmony-nav 22/22、harmony-appearance 28/28、
harmony-logic 34/34、harmony-contacts 5/5。
|
2026-09-24 13:56:52 +08:00 |
|
|
|
7151d3f91c
|
鸿蒙|动画对齐 all_five 落地 + 收敛 pageEnter 死方法
用户裁定「动画对齐 = 五条逐个对照」(all_five, 2026-09-23)。
本轮把五条核对完并把结果**沉淀成常驻判据**(本仓做法:审计不落判据就会漂)。
核对结果(WebUI 侧剥注释后实测,不是靠记忆):
rise-in 定义✓ 使用✓ → Theme.paneRiseIn() ×6 + Motion.pageEnter() ×3
pane-in 定义✗ 使用✗ → 9aa702b 删掉的死规则(碑文 index.css:1239)
用户 09-14 明确否掉「整屏一起淡」,两侧都不得复活
menu-in 定义✓ 使用✓ → Theme.menuIn() ×2
cal-in-next 定义✓ 使用✓ → Theme.calendarSlide(true)
cal-in-prev 定义✓ 使用✓ → Theme.calendarSlide(false)
⇒ 五条全部落地;无缺口需要新写动画(避免了"自己发明一条")。
顺带修一处真问题:
Motion.pageEnter() 定义了、返回的正是三个 @Entry 页各自手写的
那份 {duration, curve} 字面量,却**一个调用点都没有**(死方法)。
三个页面(AdminUsersPage/ComposePage/MailDetailPage)改为走它。
这正是本仓反复出现的形状:一处定义 + 三处同源各自演化。
判据(animation-audit.test.mjs 12 → 15):
① WebUI 每个**在用**的 keyframe 都有鸿蒙实现,且实现**真的有人穿**
—— 名单从 CSS 动态取(liveKeyframes),不是写死,将来加删会跟着动。
「有调用点」是关键:本仓的失败形状是"定义了却没人用"(menuIn 一度如此),
"方法存在"是弱断言 —— 删掉最后一个调用点它照样绿。
② pane-in 不得在**任一侧**复活(只查鸿蒙不够,WebUI 复活同样会两端不一致)。
负面断言走剥注释版,碑文里的引用不会让它假红。
③ 一般化:**任何**返回 TransitionEffect 的动画辅助方法都必须有调用点
—— 防的是"下一条",pageEnter 就是这里被抓出来的。
变异验证(三条新判据逐条打红,还原后回绿):
· 删掉 calendarSlide 调用点 → ①③ 红(cal-in-next/prev 无调用点)
· 鸿蒙复活 paneIn() → ② 红
· WebUI 复活 @keyframes pane-in + 使用 → ①② 红
⇒ 判据有齿,不是橡皮图章。
套件:animation-audit 15/15、harmony-logic 34/34、harmony-nav 22/22、
harmony-appearance 27/27。hvigorw BUILD SUCCESSFUL。
|
2026-09-24 11:02:05 +08:00 |
|
|
|
65de1c3884
|
跨端对齐:授权栏 navigator_only + 组件按页拆分 + 服务器补 permission_options
用户两项裁定落地(均为 ask_user 明确选择):
① 授权栏口径 = navigator_only(照 WebUI 架构)
· 新建 pages/PermissionPanel.ets —— 详情页的决策面板,
对应 MailView.tsx:693 的 PermissionPanel(审批型 / 主动提问 / 已处理 三态)
· 决策入口从授权栏移到 MailDetailPage;MailDetailPage 原来只显示一个
「权限请求」小标签、根本没有决策入口(比 WebUI 少一整块,且反了:
栏里能决策、点进详情反而不能)
· PermissionTab 删掉内联「同意/拒绝」+ 备注框 + decide():
整卡可点 → onOpenMail(对齐 WebUI PermissionList.tsx:81 的 pick())
· PermissionRequest 补 source_account_id(客户端侧记来源,跳详情要定位网关)
② 服务器补 permission_options —— 修一条真实的、跨端共有的缺口
· mails.permission_options 从 INSERT 起就写进去,但**从来没有任何读路径
选过它** ⇒ 详情端点永远返回空。WebUI 的决策面板读 mail.permission_options,
所以提问型的预设选项**两端全部落空**(审批型靠 ['同意','拒绝'] 兜底蒙混)
· GetMailByID 补选该列 + JSON 反序列化(与 cc_list 同款)
③ 组件按页封装(用户要求「以便与 WebUI 一一对应」)
MainPage.ets 4592 → 3192 行
· pages/PermissionTab.ets 720 行 ↔ PermissionList.tsx
· pages/ContactsTab.ets 796 行 ↔ ContactPanel.tsx
· pages/NavDestinations.ets 181 行 ↔ Navigation 壳
· pages/NavShared.ets 65 行 ↔ 跨栏共用件
④ 判据跟着组件搬家(否则静默失效,不是红)
harmony-logic 的 pageCode / harmony-nav 的 navSrc 改为显式文件名单;
harmony-appearance 的 PANE_SOURCES 补 ContactsTab;harmony-contacts 三个
test 并入 ContactsTab;harmony-logic 的决策断言改指 PermissionPanel,
并新增「授权栏不许再有内联决策」两条(navigator_only 的正形状)。
animation-audit:共享元素转场判据从「同文件共址」改为「按 id 找驱动」。
旧形状把 in/out 端必须在同一文件当成代理,而两端**天然在两处**;
抽出写信页(NavDestinations 持有 in 端)后误报。新判据仍要求每个 id
都有 Motion.morph 驱动 —— 变异实测:把驱动换成裸 animateTo 仍判红。
判据:files=34 checks=556 red=1(仅 build-stamp,产物待重构建)
|
2026-09-24 10:10:32 +08:00 |
|
|
|
2f80e1102d
|
跨端: 写信 FAB → 写信页 共享元素转场 + morph 收成单一入口(判据两版错法都记了)
用户 2026-09-21:「webui 行为是按钮变成对应的写邮件页面或输入框吧,你做的啥?」
「都做啊」—— 两处 morph 现在都在了。
## ① 写信 FAB → 写信页(跨 NavDestination)
官方 FAQ `faqs-arkui-991` 给的正是"在 NavDestination 子页面里做共享元素转场"
的完整步骤,逐步照做:
· 两端绑同一 id `compose-morph`(FAB / ComposeDestination 的 NavDestination);
· **`pushPath` 放进 `animateTo` 闭包**(FAQ 步骤 3 原文就是这个形状);
· `follow: false`(两端互斥出现,不是"始终在树上跟随")。
## ② 把 morph 收成**单一入口** `Motion.morph(ui, mutate)`
这一步不是为了少写代码,是为了**让判据能判**。过程值得记:
**第一版判据** —— 全仓 `any()`:
/animateTo\(/.test(allHarmony) && /durMorph/.test(allHarmony) && …
变异实测(把 morph 那处的 `animateTo` 改名、把 `Theme.durMorph` 就地写 `220`)
**三条全绿** —— 因为全仓**别处**还有这些名字,"删掉这一处"永远命中不了。
**第二版判据** —— 逐站点取"文本邻域"看有没有 animateTo:
**全假红**。因为 `geometryTransition(id)` 绑在**组件树**上,而 `animateTo`
写在**另一个方法**里,文本邻域取不到隔壁的方法。
⇒ 结论不是"把判据写得更聪明",而是**把结构改成可判的**:
把"带 morph 的状态切换"收进 `Motion.morph(ui, mutate)` 一处
(`animateTo` + 时长 + 曲线都在里面),调用点只剩「我要改哪个状态」。
这与本仓既有解法同型(`PressEffectModifier` / `GlassCardModifier`:
把"每处都得记得写"收敛成"一处定义、处处引用")。
3 个调用点已全部改走它(`MainPage.openComposeWithMorph`、
`MailDetailPage.openReplyWithMorph` / `closeReplyWithMorph`)。
★ `Motion.morph` 必须收 `UIContext`:全局 `animateTo` **已废弃**
(编译器告警 `'animateTo' has been deprecated`),而静态方法里拿不到
`this.getUIContext()`(本仓纪律:静态方法里不用 `this`)。
## 判据:6 条,三条变异逐个验过
通过 每个 id 恰好绑两处(一 in 一 out) [变异:删一端 → 红 ✓]
通过 morph 只有一个入口且内部有 animateTo [变异:换成普通调用 → 红 ✓]
通过 用 ui.animateTo 而非废弃的全局 animateTo
通过 每个用 geometryTransition 的文件都走 helper [变异:自己写 animateTo → 红 ✓]
通过 页面里不再直接出现 Theme.durMorph
通过 Theme.durMorph 存在且 = 220 [变异:改成 450 → 红 ✓]
★ 期间还抓到一个**判据自己的 bug**:我重写那一段时把 `themeSrc` 的定义
一起删了 ⇒ 第 6 条抛 `ReferenceError`、**整条判据根本没跑**
(而其余 9 条照常打印"通过",退出码 1 但没人看得到那条)。
这与"守具有齿但不在位"同形:**判据崩了不会显示成失败**。
已补回定义并重跑确认。
计数棘轮 4 → 10(显式编辑,理由写在 `run-all.mjs` 里)。
## 设备验证
✓ 点 FAB → 写信页到场、取消 → 回列表,进程存活(17827),无新 jscrash
(`faultlogger` 里最新仍是 15:08 那条,即修复前的)
✗ 220ms 的**中间帧**仍看不到(`snapshot_display` 往返 1.5-3s 慢一个数量级)——
与上一条提交同样的诚实交代:动画本体只能由用户在真机上看
|
2026-09-21 15:50:00 +08:00 |
|
|
|
a47b42b36e
|
修复: 套件读数不再取决于"你怎么调用它"—— 子进程钉 cwd: ROOT + read.mjs 相对路径按包目录解析(第二层);并把"崩了"从 red 里分出来(结构判据,不加错误关键字)
pi 2026-09-17 实测报的:**同一份代码、两种调用法、两个结论**。我复现并修了两层。
## 一、复现(同 HEAD,只换 cwd)
| 调用法 | 修前 RESULT |
|---|---|
| `cd client/electron && node test/run-all.mjs`(`npm test` 入口)| `ran=29 checks=440 red=8 unreported=0` |
| `node client/electron/test/run-all.mjs`(仓库根)| `ran=27 checks=348 red=10 unreported=2` |
差 **92 条**(= 88 + 4),红 8 → 10。两次都稳定复现。
**根因**:`run-all.mjs` 的 `spawnSync` **不传 `cwd`** ⇒ 子进程继承调用者的 cwd。
而全仓有 **12 处**判据按**包内相对路径**读文件(`code('src/index.css')` 这种)
—— 它们本来就假定 `cwd = client/electron`,只是没人把那个假定钉住。
从仓库根跑 ⇒ `ENOENT` **崩在 import 期**。
## 二、修(两层,各管一段)
1. **运行器**:`spawnSync(..., { cwd: ROOT })` —— 一行覆盖**现有的与将来的所有**判据。
方向是"让运行器去满足判据的既有假定",不是改判据去迁就调用者。
2. **`read.mjs`**(第二层):`code/prose/bytes` 里的相对路径一律按**包目录**解析
(基准取自 `import.meta.url`,**不是** `process.cwd()` —— 前者是代码里的结构事实,
后者正是这个 bug 的成因)。pi 报的复现步骤是**直接跑单个判据文件**,
那条路**不经过运行器**,所以这层不是重复。
基准 `PKG` 也**导出**了:`readdirSync`/`existsSync` 不经过 `read.mjs`,
各处自己拼一次就是"同一个事实多份实现"(本仓反复消的形状)。
**验证(三种 cwd × 两个相位,逐字节一致)**:
```
build /home/program/agentmail ran=29 checks=440 red=8 unreported=0
build /home/program/agentmail/client/electron 同上
build / 同上
install /home/program/agentmail ran=27 checks=428 red=7 unreported=0
install /home/program/agentmail/client/electron 同上
```
(三种 cwd 一致、且与 `npm test` 入口一致 ⇒ 不存在"另一种调用法给别的数"。)
## 三、★ 第二处:那两条不是"红了",是"没跑完"——而它被报成了 red
`narrow-layout` 从仓库根跑:**先打完 43 条"通过",然后第 44 条 ENOENT 崩掉**,
而 `CRASH_SIGNS`(关键字表)**不认 ENOENT** ⇒ `crashed=false` ⇒ 落进 `reds`:
`- test/narrow-layout.test.mjs(退出码 1)`,**一条 `↳` 自述都没有**。
读者看到的是"这条判据不成立",真相是"**它有多少条根本没测**"。
同一次运行里真红是带 `↳ 自报 88 > 登记 64` 自述的 —— **同一种红,两种含义**,
而 `red=10` 把两者混着报。
**★ 我没有照 pi 说的往 `CRASH_SIGNS` 里加 `ENOENT|EACCES|no such file`**:
第 329 行那条注释早把方向定死了 —— **"我不再往里加更多错误关键字:那是往文本解析里加补丁"**,
而且那个表**必然漏**(今天漏 ENOENT,明天漏 ENOTDIR/ELOOP…),加上去就是**第六次**修同一形状。
**改用结构事实**:`exitCode !== 0 && checks === null` ⇒ broken。
一个跑完的文件**必然**自报条数;"跑了但没自报"是另一条**单独记账**的情形(`unreported`)。
两者不会同时成立 ⇒ 只吃掉"崩了且毫无自报"那一格。
**要害是这个区分**:`exitCode !== 0` 本身**不足以**判 broken ——
不然真红会被一起吞掉,那是**假绿方向的错**,比误报成红更坏。
(实测 `build-stamp` 是真红且有 `# tests 7` ⇒ `checks=7≠null` ⇒ **不吃它** ✓。)
**变异验证**(撤掉 `cwd: ROOT`,模拟 pi 报的原状):
`files=29 ran=27 checks=348 red=8 broken=2 unreported=2` ⇒
那两条正确归 **broken**(`(退出码 1,且一条条数都没自报)`),而 **`red` 仍是 8 —— 没吃掉任何真红**。
还原逐字节一致(sha256 相同)。
## 四、★ 我自己在实现里踩的两个坑(都实测抓到)
1. **`abspath()` 用 `isAbsolute(p)` 判绝对路径 ⇒ 传 `URL` 对象抛 `ERR_INVALID_ARG_TYPE`。**
`narrow-layout.test.mjs:8` 正是 `prose(new URL(p, import.meta.url))` ——
一个**本来正确且与 cwd 无关**的调用,被我改崩了(两个 cwd 都崩,所以**看起来像"我修好了"**)。
⇒ 判据改成 `URL` 对象**原样透传**:它已是解析过的位置,转换才是引入 bug 的那一步。
教训:**改一个共用入口时,要先把调用方的所有形态列全**,否则"修 A 破 B",而 B 看起来无关。
2. **`join` 忘了 import** ⇒ `ReferenceError`。补上。
(正是 `run-all.mjs` 里那条 `CRASH_SIGNS` 注释讲的老故事。)
另外:我在 `run-all.mjs` 新注释里写了带括号的入口名,**被 `criteria-hygiene` 第 3 条抓到**
(它不剥注释)—— 这是我上一轮刚记下的坑的**第二次**,已改掉。
## 五、测量
| 相位 | RESULT(三种 cwd 一致)|
|---|---|
| build | `files=29 ran=29 checks=440 pass=438 fail=2 red=8 broken=0 unreported=0` |
| install | `files=29 ran=27 checks=428 pass=427 fail=1 red=7 broken=0 unreported=0` |
重新接线后 `narrow-layout` 自报 **88** 条(登记 64)⇒ 它那条"登记数没跟上"的红变成**有自述**的了。
剩下 8 条红都不是本次改动(清一色并发会话的工作:`harmony-nav` 正在被改等)。
|
2026-09-17 19:00:11 +08:00 |
|
|
|
f14f2d6fa7
|
chore: 动画盘点判据接入套件 + 让其合规 + 对齐参照物重新登记(清工程收尾)
用户:「清理一下tmp和工程吧」。
- 新判据 test/animation-audit.test.mjs 接入 test/run-all.mjs:原先**写了却不会跑**
(套件自带的那条闸门当场报「这些判据文件没接进套件」)。
- 该文件改用 test/lib/read.mjs 的具名入口(code/prose/bytes),不再裸用 readFileSync ——
criteria-hygiene 抓到:读原文判代码会被解释性注释骗,今天已踩过两次。
- docs/ALIGN-REFS.json:CalendarView 的对齐登记按规矩**重新核对后再登记**
(差异只有动画类 rise-in → pane-rise,骨架/布局/圆角来源未变;鸿蒙侧本无日历动效
⇒ 不产生新的对齐义务),不是抄一处新哈希。
|
2026-09-15 09:16:05 +08:00 |
|
|
|
d81ab319e4
|
fix(webui): 动画全量盘点 —— 两处死动画 + 一处过宽;并把盘点变成常驻判据
用户:「全面检查整体的动画」。查出来的不是"好不好看",而是**接线断了**(三种形态都很难靠肉眼发现):
① @keyframes pane-in **定义了两处**、挂在 `html.view-switch .pane-enter` 上,而 `.pane-enter`
**没有任何组件在穿** ⇒ 规则看着像"页面有入场动画",一次都不会播(9aa702b 删整面板入场时的遗留)。
已连规则与 keyframes 一起删干净(注释里我把"连它一起删"写了却没做,被新判据当场抓到)。
② `.animate-menu-in`(菜单入场)keyframes 与规则都写好了,**同样没人穿** ⇒ 所有下拉/候选菜单
其实是"啪"地出现。已接到 AddressInput 的候选菜单上;线上实测捕捉到 menu-in @140ms。
③ `html.view-switch .glass-control` 把"菜单入场"错当成"控件档入场" ⇒ 每次切视图,页面上
**所有**按钮与输入框一起淡入位移(几十个元素同时动,闪与卡顿的现成来源)。已收窄为
只给 `.animate-menu-in`;线上实测切视图只剩 pane-rise + 导航项的颜色过渡。
判据化:新增 test/animation-audit.test.mjs —— 每个 @keyframes 都必须有人穿(死动画闸门)、
弹层必须带菜单入场类、不得再出现"整档控件一起动"的切视图规则、reduced-motion 必须显式
覆盖挂载即播那档。写判据的过程本身抓到两处我自己的不一致:删了规则却留下孤儿 keyframes;
以及正则把 reduced-motion 名单里的 `html.view-switch .glass-control` 误判成"还在让它动"
(已改为只扫顶层规则,媒体查询块里的"关掉名单"不算)。
|
2026-09-15 08:51:18 +08:00 |
|