Skip to content

体验优化:PR 评审界面切换/刷新抖动消除与加载态平滑#79

Merged
huhamhire merged 8 commits into
devfrom
fix/state-store-concurrent-write
Jun 19, 2026
Merged

体验优化:PR 评审界面切换/刷新抖动消除与加载态平滑#79
huhamhire merged 8 commits into
devfrom
fix/state-store-concurrent-write

Conversation

@huhamhire

Copy link
Copy Markdown
Owner

PR 评审界面的一系列体验与稳定性优化,聚焦切换 / 刷新时的渲染抖动加载态平滑。归入里程碑 体验优化

前端体验改动(tasklist)

  • 详情页排版:内容限宽 960px 并居中、滚动条回到容器右缘;reviewers 列表按字典序固定排序(消除刷新顺序抖动)
  • 评论 tab 排版:顶层评论列表限宽 960px 居中
  • DiffEditor poll 防抖options 收进 useMemo,避免 @monaco-editor/react 每次 poll 重渲染都 updateOptions 触发折叠布局重算
  • 评论内嵌 Monaco 刷新抖动(根因):i18n 语言 effect 改为仅依赖 config.language,避免 poll 时对同一语言反复 changeLanguaget 引用翻新 → 依赖 t 的 effect 被无谓重跑
  • 主面板 tab keep-alive:tab 首访才挂载、之后保活仅 CSS 显隐,切走再切回瞬时、无重拉、保留滚动/展开态;Monaco automaticLayout 处理显隐重排
  • 评论切换 stale-while-loading:切 PR 保留旧评论 + 加载遮罩(延迟 150ms),新数据 ready 整体替换,消除「先闪加载中再渲新」
  • tab 角标防抖:计数加载占位 chip(横向)+ .pr-tab flex 固定行高(竖向 1~2px)
  • 切 PR diff stale-while-loading:引入 loadedPrId,切 PR 保留旧树/内容 + 遮罩,门控 content/comments/blame 拉取,新文件列表 ready 整体替换
  • 文件树图标占位槽:固定 16px 槽位,消除图标晚渲染推移文件名的抖动

附带稳定性修复

  • state-store 并发写 ENOENTdiff-base.json 等同 key 并发写共用临时文件致 rename ENOENT(表现为「拉取变更文件列表失败」、重试可恢复);临时文件名加进程内序号去重

验证

lint / typecheck 均通过。建议 dev 回归:切 PR、切 tab、刷新(手动 / 聚焦)、评论增改删、blame 开关,确认无回归。

🤖 Generated with Claude Code

huhamhire and others added 8 commits June 18, 2026 21:50
JsonFileStateStore.write 的 tmp 文件名原仅带 process.pid,同进程内对同一
key 的两次并发写共用同一 tmp 路径:先完成者 rename 走文件后,后完成者
rename 即 ENOENT。diff-base.json 恰会被前台 listChangedFiles 触发的
resolveDiffBaseSha 与 poller 后台刷新并发固化,表现为「拉取变更文件列表
失败」、重试可恢复的瞬时报错。

tmp 文件名追加进程内自增计数器(<file>.<pid>.<seq>.tmp),各并发写各用
唯一 tmp。补一条 20 路并发写回归测试。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
详情 tab 原把 max-width 加在滚动容器 .pr-info-view 上,滚动条因此贴在
960px 内容右缘、停在布局中间。改为滚动容器铺满(滚动条回到右缘),内容
包进 .pr-info-content 限宽 960px 并居中。

reviewers 顺序由各平台 adapter 决定且不稳定(GitHub 按 Map 插入序,随评审
推进 requested_reviewers 被移除/补到末尾),每次 poll 列表抖动。展示层按
displayName 字典序固定排序(name 兜底),与平台无关。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
与详情页一致:内容限宽放在顶层评论列表 .pr-comments-list 上并 margin auto
居中,滚动条留在外层 .pr-comments-panel 右缘。仅约束直接子列表,嵌套 replies
的 .pr-comments-list 不受影响。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@monaco-editor/react 对 options 按引用比对,引用一变即 editor.updateOptions()。
原 options 为每次渲染新建的对象字面量,poll 时 pr 换新引用 → DiffView/DiffPane
重渲染 → 每次 poll 都触发 updateOptions → hideUnchangedRegions 折叠布局重算 →
中央编辑器抖动。改为 useMemo 固定引用,仅并排/空白/字号变化时重建。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
根因:i18n 语言切换 effect 依赖整个 boot 对象,poll/聚焦刷新 setBoot 后对同一
语言反复 i18n.changeLanguage,react-i18next 发 languageChanged 致所有
useTranslation 的 t 换新引用;InlineCodeContext 抓取代码片段的 effect 依赖 t,
被无谓重跑 → setSnippet(null) → 内嵌 Monaco 卸载重建。这是全局隐患:任何
effect 依赖 t 的组件都会在每次 poll 被重跑。

- useBootstrap:语言 effect 改为仅依赖 config.language,语言真正变化时才切换。
- InlineCodeContext:t 移出抓取 effect 依赖;并把 Monaco 抽成独立 memo 子组件
  CodeSnippet(稳定 options/onMount),与父级重渲染隔离。

CHANGELOG 一并补本分支用户可见修复(刷新抖动 / 详情页与评论页排版 / diff-base
并发写 ENOENT)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
原各 tab 内容按 {tab === X && <Panel/>} 条件渲染,切换即卸载旧面板 / 重挂新
面板 → 重新拉数据、闪「加载中」、内嵌 Monaco 重建。新增 KeepAliveTab:tab
首访才挂载(保留 DiffView 等懒加载),之后保活、仅 CSS display 显隐不卸载。
切走再切回瞬时、无重拉,滚动位置与展开态保留。

隐藏期 Monaco 容器尺寸归 0,切回需重排——给 diff 编辑器与评论内嵌代码片段开
automaticLayout(Monaco 自带 ResizeObserver 显隐时自动 layout),避免切回空白
/错位。新增 .pr-tab-pane 让 active 面板铺满。CHANGELOG 同步。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
切不同 PR 时评论页此前先 setComments(null) 闪「加载评论中」再渲新内容。改为
stale-while-loading:评论与配对 pr 一起冻结为 view,切 PR 不清空、旧内容继续
渲染并上盖加载遮罩,新数据 ready 整体替换;遮罩延迟 150ms,命中缓存的快切换
直接换新、零闪。滚动下沉到 .pr-comments-scroll,外层做定位锚让遮罩铺满视口。

tab 栏角标(评论 / 提交计数)异步加载:新增 TabCountBadge,加载中渲染等宽
占位 chip 预留宽度,消除计数到达的横向弹簧;.pr-tab 改 inline-flex + 固定行高,
角标占位 / 出现 / 消失不再改变 tab 高度,消除 1~2px 竖向跳动。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
切不同 PR 时 DiffView 此前一次性置空 files/selected/content 等 → 左栏 blank →
文件树整体弹出。引入 loadedPrId 标记当前已渲染内容所属 PR,改为 stale-while-
loading:切 PR 期间保留旧树/旧内容渲染、上盖加载遮罩(delayMs=150,命中缓存
快切换直接换新),并门控 content/comments/blame 拉取(避免「新 localId + 旧选中
文件」错拉),新 files ready 后整体替换;切 PR 失败显错误面板不冒充。

文件树首次加载文件名被图标渲染推移:图标改用固定 16px 占位槽包裹,iconify 的
svg 晚一帧进 DOM 也不塌缩,文件名位置稳定。CHANGELOG 同步。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@huhamhire huhamhire added this to the 体验优化 milestone Jun 19, 2026
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@huhamhire huhamhire added enhancement New feature or request bug Something isn't working labels Jun 19, 2026
@huhamhire huhamhire merged commit ba8085e into dev Jun 19, 2026
1 check passed
@huhamhire huhamhire deleted the fix/state-store-concurrent-write branch June 19, 2026 03:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant