已关闭
fix(tuix): inflight 重绘改走 cell-diff, 消除光标闪烁 #1104
郑育强创建于 9月21日关闭于 6 天前
fix(tuix): inflight 重绘改走 cell-diff, 消除光标闪烁 #1104
已关闭
郑育强创建于 9月21日关闭于 6 天前
郑育强
9月21日

问题

工具调用进行中(如长 bash 命令), TUI 的 Running liveness 行每 ~100ms 重绘。实测(本机与高延迟链路均可复现)可见光标在 Running 行尾与输入框之间以 ~10Hz 乒乓闪烁。

根因: render_inflight_tool 的 in-place 分支在更新模型之外, 还直接向终端裸写 \x1b[{r};1H\x1b[2K + 行内容(retained.rs)。这段裸写:

  • 绕过 render_diff 的 DECSET 2026 同步帧与 ?25l 光标隐藏;
  • 写完后物理光标滞留在 Running 行尾, 要等 ≤5ms 后的合并帧才归位;
  • 裸写与归位之间终端会画出滞留的可见光标 —— 帧不原子, 延迟越高越明显。

live spinner 此前已修复过同款问题(push_or_update_live_spinner, 改走 cell-diff), inflight 路径是它遗漏的孪生分支。

修复

in-place 分支只做模型更新(body_lines 替换 + sentinel invalidate_rows_from + dirty), 重绘统一由 cell-diff 在 DECSET 2026 同步帧内完成。sentinel 本就强制 strip 每格出 patch, 裸写删除后视觉无损失; 唯一差异是重绘推迟到 ≤5ms 的合并帧, 不可感知。

验证

  • 新增回归测试 retained_inflight_inplace_rewrite_stays_inside_sync_envelope: 断言 inflight tick 输出不再出现裸写签名 \x1b[2K(该序列全仓仅裸写路径使用), 且 Running 行仍正常重绘 —— 修复前红、修复后绿
  • cargo test -p atomcode-tuix: 2134 通过; 3 个失败(dir_picker ×2、kitty_keyboard ×1)经 stash 基线对照确认是 main 上已有的环境相关失败, 与本改动无关
  • cargo fmt --all -- --check 通过
  • Windows 本机构建实测: 修复后长工具调用期间光标稳定钉在输入框, 不再乒乓

已知未验证范围

  • 未在 macOS/Linux 本机终端逐一复测(渲染路径跨平台共享, 风险低)
  • 高延迟 SSH 场景仅做机理推断(帧原子化后裸写窗口消失), 未单独抓包验证
likedislike
当前Pull Request已关闭, 关闭人@郑育强
atomgit-bot
atomgit-bot成员
9月21日 评论:

变更摘要

本 PR 修复了工具调用进行中时 TUI 光标闪烁的问题。根因是 render_inflight_tool 的 in-place 分支在更新模型之外,还直接向终端裸写 \x1b[{r};1H\x1b[2K 加行内容,绕过了 render_diff 的 DECSET 2026 同步帧与光标隐藏,导致物理光标滞留在 Running 行尾,在高延迟链路上以约 10Hz 在 Running 行与输入框之间乒乓闪烁。修复方式是让 in-place 分支只做模型更新(body_lines 替换、sentinel invalidate_rows_from、dirty),重绘统一交由 cell-diff 在同步帧内完成,与 live spinner 路径 push_or_update_live_spinner 保持一致,并补充回归测试。

主要改动

  • 移除 inflight in-place 分支的终端裸写: 删除了逐行 \x1b[{r};1H\x1b[2K 与 serialize_row 的直写循环,避免绕过屏幕模型与 DECSET 2026 同步帧。
  • in-place 分支改为纯模型更新 + sentinel 失效: 仅替换 body_lines 尾部切片并触发 invalidate_rows_from,由后续 flush_deferred 经 cell-diff 在同步帧内重绘,保持帧原子化。
  • 新增回归测试: 新增 retained_inflight_inplace_rewrite_stays_inside_sync_envelope,断言 inflight tick 输出不再包含裸写签名 \x1b[2K,同时校验 Running 行仍正常重绘。
likedislike
不准确?
atomgit-bot
atomgit-bot成员
9月21日 评论:

代码审查

✅ 未发现问题

likedislike
不准确?
atomgit-bot
atomgit-bot成员
9月21日 评论:

AtomGit AI 助手使用指南

在 PR 评论中使用命令即可触发。支持在普通评论和代码行评论中使用。

功能 命令 说明
代码审查 /ai review 检查代码质量、潜在问题、安全风险
PR 摘要 /ai summary 生成 PR 变更内容的结构化摘要
代码解释 /ai explain 解释代码变更的逻辑和意图
自由提问 @atomgit-bot <问题> 询问关于本 PR 的任何问题
帮助 /ai help 显示此帮助信息

免责声明

AI 助手可能存在误判,请结合自身判断。可以对评论点 👍 或 👎 帮助我们改进。

likedislike
不准确?
郑育强
6 天前 评论:

关闭说明: 本 MR 修复的 render_inflight_tool in-place 裸写路径已随 atomcode-tuix 整体退役 (f465a43ec) 从 main 删除, 目标文件 crates/atomcode-tuix/src/render/retained.rs 在上游已不存在, 冲突形态为 modify/delete。

对替代实现 crates/atomcode-tui 的核查结论: 不存在同类问题, 无需迁移重做。

  • 渲染路径上没有任何裸写 \x1b[2K 的 in-place 更新 (仅测试断言与终端能力探测使用该序列);
  • DECSET 2026 同步帧是新架构的不变量: SYNC_BEGIN/SYNC_END 包裹整帧, 且有测试 a_frame_is_one_synchronized_update 守护;
  • spinner/运行状态行走同一帧 diff 路径 (ansi.rs 按行 diff, 文档以"两帧只差一个 spinner 行"为典型用例), 不存在绕过同步帧的第二通道。

旧 bug 的成因通道 (在同步帧外裸写导致物理光标滞留) 已被新架构从设计上消除, 本修复无迁移价值, 故关闭。

likedislike
郑郑育强
6 天前 关闭了 pull request