From f1a63dd2766ca03aed00eba70c552188aec36f15 Mon Sep 17 00:00:00 2001 From: judy Date: Thu, 25 Jun 2026 19:41:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20post-list=20=E9=97=AA=E5=8A=A8=20?= =?UTF-8?q?=E2=80=94=20keyed-diff=20renderPosts=20+=20SSE-aware=20poll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit renderPosts 之前每次 refresh 都 innerHTML='' 然后整列重建,叠加 SSE + 8s poll + BroadcastChannel + scheduleRefIndexRefresh 四条管道几乎同时 触发 refreshCurrentThread → renderDetail,导致 thread message 页面经常 肉眼可见闪动(mermaid/markdown/滚动条都会抖一下)。 彻底修复方案: 1. _postSig + _threadSig:把 post 渲染输出依赖的所有字段(speaker / phase / edited_at / parent / superseded / reactions / inlineDuration / content / 前一条 pid 等)拼成 signature。 2. refreshCurrentThread 收到新 thread 后先比 _threadSig,相等就直接 return,不走 renderDetail。SSE 跟 poll 重叠那批冗余刷新全部 noop。 3. renderPosts 改成 keyed diff:用 Map 缓存上一 轮的 DOM 节点。新一轮按 target 列表对齐 postList 子节点 —— sig 相同就复用节点(mermaid SVG / code highlight / event handlers / scroll anchor 全部保留),不同才重建,多余的尾部节点 remove。 4. renderDetail 接受 force:true,settings 改 avatar/color 之类需要全 量重画的场景传 force 清缓存;其它路径走 diff。 5. SSE 收到 hello/event 后 state.threadEventHealthy=true,startPolling 只在 SSE 不健康时对当前 thread 跑 refetch(unread sweep 不动), 消除 SSE+poll 200ms 内打架。 后续可以再做 renderPostNode 内部 header/body/footer 分区 patch、以及 让 SSE 直接推 patch payload(post added / phase changed),那是后续 PR 的事。 --- CHANGELOG.md | 1 + web/app.js | 258 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 218 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 466cb2f..af2e518 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ auto-generated notes — but stable semver tags must pass. ## Unreleased +- Thread view no longer flickers on every refresh. The post list is now diffed in place (keyed by post id), so unchanged posts keep their existing DOM nodes — SSE bursts, the 8s poll fallback, and cross-tab broadcasts can all fire together without repainting the column. When SSE is healthy the per-thread poll is suppressed entirely. - _Add entries here as they ship. They get cut into the next `v` section at release time._