JianFeeeee
1f517eca4b
fix(webui)★★: 对话树改成画布式真树 + 修两个线上 bug;日历翻月卡死
移植自本地 main(含 3536689..e670f6f 的最终状态)。
基于 origin/main 而非 rebase 本地 —— 本地那条链上的 6 个提交
(0a9287b/d52be3f/74aacc3/646cb3f/6e198d2/010c8ec)是远端已推内容的等价改写,
重复移植只会制造冲突;远端另有一条线上的成果(main.cjs/preload.cjs/
index.css/main.tsx/TitleBar.tsx/run-all.mjs 与 4 份 docs)本地没有,
**不能覆盖**,故从 origin/main 起只取本次真正改动的 5 个文件。
## 对话树:换成画布式真树
用户连续三轮反馈「这不还是老版的对话树」,最后明确要求:
「直接用你之前写的那个**可拖动缩放的对话树界面**替换现在这个对话树界面」。
我前两轮改错了方向 —— 一直在**缩进列表**上打补丁(改排序 → 改竖线 →
整份重写),而缩进列表**表达不了分支**:实测最深的线索 205 封、59 层,
多个兄弟追问并排时缩进线断成一片;画布上每条边都是独立的一条线。
现在:可拖动平移 / 滚轮缩放(20%–250%,锚定光标)/ 点节点看正文 /
分层 Reingold–Tilford 布局(不用力导向:位置会变、点不准)。
## 两个线上 bug(用户实测报告,我第一轮没看出来)
① **页面没撑满**:打开后 zoom=140%、pan.x=-9393,内容包围盒 11340×7193,
屏幕上只看得到左上角。而手动点「适应」是对的(0.1217)——
同一份公式、同一个容器,一个对一个错 ⇒ 差别只在**拿到的是哪份数据**。
逐层实测揪出三个连环坑:`nodes` 是**根数组**(只 1 个根)、
自动 fit 的 effect 没在数据到达后重跑、`fittedFor` 守卫置位太早把自己挡掉。
另:`MailView` 的 `threadOf` 分支裸返回 `<ThreadView/>`,缺
`flex-1 min-w-0 min-h-0` ⇒ 高度链断。
② **一缩放就崩**:`onWheel` 直接解引用 `e.currentTarget`,
组件卸载后滚轮事件仍在飞 ⇒ `TypeError: Cannot read properties of null`。
实测放大 20 次后面板消失。改成先取可空引用 + 空值守卫。
真机复测:200 节点 / 55 层,打开即 fit 到 12%,放大 20 次 + 缩小 25 次
全部 0 崩溃 0 错误。
## 日历:翻月卡死
用户实测「点击切换月份的按钮,直接从10月跳到八月卡死」。
CDP 复现:
第1跳起:opacity="0",transform="translateX(-94.8px)",元素 x = -15
右侧工具栏(导入/导出/新建)整块消失
根因:注释写着「用**声明式** key + 类」,实现却是两个 ref ——
**ref 改动不触发重渲染** ⇒ slideClass 只能用上一次的方向,且 class 名永不变
⇒ 动画只播一次不再重放。而 `animation-fill-mode: both` 把元素留在帧之间,
动画一旦没跑到终点就永久停在 `translateX(-12%) + opacity:0`。
修:ref → `useState<{dir,seq}>`;`seq` 进 key 让动画真重放;
加 260ms 自清理兜底(最坏只是没动画,不会卡死)。
复测:12 次连点,每次都是「class 已摘掉 OK」,侧栏/网格/工具栏全好。
## 判据
`thread-view-tree.test.mjs` 15 格 + `calendar-slide.test.mjs` 6 格,
每个 bug 都有对应的**可复现变异**(退回 ref / 去掉清理 / 去掉 seq …),
实测全部转红。
## 教训
两轮都只跑 `tsc` + 判据就宣布完成,然后请用户去看 ——
**「类型没错」不等于「页面能用」**。日历那一轮我更是盲巡检了十几轮
(还把「看起来被切」误判成 bug,最后自己量出 scrollHeight==clientHeight 才撤回)。
**先问清楚,比继续猜便宜得多。**
2026-10-05 09:09:36 +08:00
..
2026-10-03 11:33:51 +08:00
2026-09-15 12:09:37 +08:00
2026-10-03 10:43:11 +08:00
2026-09-15 07:46:54 +08:00
2026-09-28 08:46:02 +08:00
2026-09-28 08:46:01 +08:00
2026-09-15 11:17:23 +08:00
2026-09-24 13:56:52 +08:00
2026-10-03 10:43:11 +08:00
2026-09-24 10:10:32 +08:00
2026-10-03 11:17:18 +08:00
2026-10-05 09:09:36 +08:00
2026-10-03 23:02:58 +08:00
2026-10-03 10:43:11 +08:00
2026-10-03 11:17:18 +08:00
2026-09-28 10:17:09 +08:00
2026-09-20 10:51:28 +08:00
2026-09-24 10:10:32 +08:00
2026-09-28 08:46:01 +08:00
2026-10-03 10:56:47 +08:00
2026-10-01 20:37:19 +08:00
2026-09-24 10:10:32 +08:00
2026-09-21 17:36:40 +08:00
2026-09-24 13:13:46 +08:00
2026-10-03 12:11:25 +08:00
2026-09-20 22:35:35 +08:00
2026-09-24 10:10:32 +08:00
2026-09-18 08:39:21 +08:00
2026-09-28 08:46:01 +08:00
2026-10-03 12:11:25 +08:00
2026-09-24 10:10:32 +08:00
2026-09-15 12:06:15 +08:00
2026-09-16 08:29:24 +08:00
2026-09-17 19:38:15 +08:00
2026-09-17 18:43:00 +08:00
2026-09-14 16:21:27 +08:00
2026-09-25 12:21:00 +08:00
2026-10-01 18:49:06 +08:00
2026-10-03 10:43:11 +08:00
2026-10-04 11:08:57 +08:00
2026-10-03 21:21:55 +08:00
2026-09-15 08:45:54 +08:00
2026-09-15 07:46:54 +08:00
2026-09-14 17:33:05 +08:00
2026-10-04 11:08:57 +08:00
2026-10-04 12:03:58 +08:00
2026-10-03 10:43:11 +08:00
2026-09-18 01:08:04 +08:00
2026-10-05 09:09:36 +08:00
2026-10-02 13:58:42 +08:00