qwen-code/docs/design/tui-optimization/00-overview.md

17 KiB
Raw Blame History

TUI 优化方案总览

本文档是 qwen-code TUI 优化的整体方案概览。除三份实施设计外,现已补充 Gemini CLI 与 Claude Code 的独立源码调研文档,用来校准方案优先级和技术路线。

1. 背景与动机

qwen-code 的 TUI 层基于 Ink 6.2.3 + React 19 构建,当前面临三个系统性挑战。下列问题需要先用源码口径校准后再实施,避免优化目标与真实瓶颈错位:

  1. 启动性能:启动流程包含多段串行初始化;交互式模式下 config.initialize() 在 UI 首次渲染后执行,配置 MCP Server 时工具声明和实际可用性仍会被慢 Server、工具注册刷新和 Gemini tools 更新路径影响
  2. 屏幕闪烁Ink 的全量重绘机制导致流式输出时严重闪烁,在 tmux/SSH 环境下尤为突出(社区报告高达 4,000-6,700 次/秒的滚动事件)
  3. 渲染能力与可扩展性:自定义正则 Markdown 解析器功能受限,缺少 LaTeX 数学公式、终端超链接等支持;主题系统默认 hex 主题可能影响透明背景终端

这些问题在 GitHub Issues 中被大量报告,而且已经不再只是“泛泛的体验抱怨”,而是形成了几个清晰的故障簇:

  • 动态区流式闪烁 / 滚动条抖动qwen-code#1184, #1491, #2748, #3007, #3144
  • refreshStatic() 型整屏闪烁qwen-code#938, #1861, #2924
  • 窄屏重复输出 / 无限滚动qwen-code#2912, #2972, #1591
  • 大输出不可读 / 工具输出预算不足qwen-code#1479, #2818, #1008, #355

这些反馈已在新文档 07-issue-backed-failure-taxonomy.md 中重新按症状、源码证据和修复方案分类。

重要校准:当前启动分析器只覆盖 UI render 之前的 checkpoint尚未覆盖交互式 config.initialize()、MCP 首个工具注册、全部 MCP 发现完成、Gemini tools 声明刷新等阶段。因此本文档的实施顺序必须先补观测,再用真实数据确认优先级。

2. 现状分析

2.1 当前架构

Entry (gemini.tsx)
  -> Ink render() 挂载 React 组件树
    -> AppContainer (状态管理中枢, ~2400行)
      -> DefaultAppLayout
        -> MainContent (Static/Dynamic 分离)
          -> MarkdownDisplay (自定义正则解析器)
          -> CodeColorizer (lowlight 语法高亮)
          -> TableRenderer (Markdown 表格)
        -> Composer (输入区)
模块 技术方案 关键文件
渲染框架 Ink 6.2.3 (npm 库) + React 19 packages/cli/src/gemini.tsx
Markdown 逐行正则解析器 packages/cli/src/ui/utils/MarkdownDisplay.tsx
代码高亮 lowlight (基于 highlight.js) packages/cli/src/ui/utils/CodeColorizer.tsx
防闪烁 stdout 拦截器,折叠重复 ANSI 序列 packages/cli/src/ui/utils/terminalRedrawOptimizer.ts
主题 ThemeManager 单例15 个内置主题(另有 no-color fallback packages/cli/src/ui/themes/theme-manager.ts
MCP 跨 Server 并行发现;已有单 server 重发现与增量发现基础,但启动 wiring、运行期 refresh 路径和 Gemini tools 刷新仍需补齐 packages/core/src/tools/mcp-client-manager.ts
启动分析 环境变量开启的 checkpoint 记录器;当前仅在 sandbox child process 中生效,且主要覆盖 render 前阶段 packages/cli/src/utils/startupProfiler.ts

2.2 外部源码调研结论

本轮补充调研覆盖两个代码库:

  • Gemini CLI/Users/gawain/Documents/codebase/opensource/gemini-cli
  • Claude Code/Users/gawain/Documents/codebase/opensource/claude-code

两者给 qwen-code 的启发点并不相同:

维度 Gemini CLI Claude Code 对 qwen-code 的意义
启动策略 入口动态导入交互 UIrender 前后初始化分层 顶层并行预取feature-gated requiredeferred prefetch 先瘦冷启动,再拆关键/非关键初始化
渲染模式 alternate buffer / terminal buffer / render process / incrementalRendering 自定义 Ink + screen buffer + diff pipeline 短期优先做“模式化渲染”,长期再评估自研渲染内核
防闪烁 findLastSafeSplitPoint() + useFlickerDetector() + ScrollableList synchronized output + diff + DECSTBM + output buffer Phase 1 借鉴 Gemini 的中层优化Phase 3 参考 Claude 的底层路线
长会话滚动 ScrollableList / VirtualizedList / StaticRender ScrollBox / useVirtualScroll / VirtualMessageList qwen-code 需要正式设计滚动/虚拟化层,而不是继续把它藏在 MainContent
Markdown 仍是自定义正则解析器 marked + token cache + streaming stable prefix parser 迁移应更多参考 Claude不应把 Gemini 当 parser 终局
代码高亮 同步 lowlight(common) Suspense + fallback +宽度测量 qwen-code 需要“同步基线 + 异步增强”而非直接 await grammar
MCP 生命周期 UI 可先起来MCP 状态事件化展示 批量状态更新、list-changed 增量刷新、远端重连 MCP 设计要从“启动 discover”升级为“运行期生命周期管理”

2.3 新增调研文档

为避免把竞品经验压缩成几行摘要,现已将外部源码分析独立成两份文档:

文档 说明
04-gemini-cli-research.md Gemini CLI 的启动、渲染模式、防闪烁、滚动、Markdown、MCP 调研
05-claude-code-research.md Claude Code 的自定义 Ink、diff 输出、虚拟滚动、Markdown、MCP 生命周期调研
15-complete-tui-flicker-closure-plan.md PR1-4 之后剩余闪烁 / 重复输出 issue 的彻底闭环方案,以及 Claude 魔改 Ink 迁移可行性评估

2.4 社区反馈汇总

问题类别 代表性 Issues 严重程度
屏幕闪烁 qwen-code#1778, #2748; claude-code#9935, #37283
启动慢 qwen-code#2748; claude-code#5653, #29201
表格渲染 claude-code#14641, #22311qwen-code 当前已有 ANSI/CJK 回归测试,需以可复现缺陷为准
主题/颜色 qwen-code#2877; claude-code#34702, #15771
窄屏问题 claude-code#13504, #18493, #5408
LaTeX 支持 claude-code#21433

2.5 Issue-backed 故障分类

本轮新增了一份专门把 qwen-code issue、当前源码和竞品源码对齐的分类文档

文档 说明
07-issue-backed-failure-taxonomy.md 按“闪烁 / 窄屏重复 / 大输出 / 工具与子 agent 展开”分类,给出症状、源码证据、修复路线与验收方式

3. 核心工作流概览

工作流 核心问题 关键指标 依赖关系
观测基线 现有 profile 不覆盖 render 后初始化和输出层 first paint、TTI、MCP 首工具、stdout writes/sec 所有优化的前置条件
启动性能 串行启动流程MCP 工具声明刷新不完整 first paint、input enabled、首个 MCP 工具可被模型使用 依赖观测基线
屏幕闪烁 Ink 全量重绘;无同步输出 闪烁事件/秒stdout writes/sec、clearTerminal 次数 依赖输出层观测
渲染与扩展 正则解析器脆弱;缺少格式支持;主题限制 格式覆盖率parse/highlight 耗时,可配置性 依赖稳定输出层

执行顺序:观测基线 -> issue-backed P0 问题治理(动态闪烁、refreshStatic()、大输出预裁剪、窄屏回归 harness -> 启动/MCP 渐进可用 -> 渲染缓存与扩展。MCP 与渲染可并行推进,但必须共享同一套指标口径。

PR 编排原则:从这一版开始,闪屏优化不再以 #3013 这类“大杂烩 PR”作为实施基线而是按用户 issue 归纳出的故障类来组织 PR。#3013 只保留为参考样本,用来印证哪些 patch 确实有收益、哪些边界条件需要补齐;执行层当前建议压成 4 条主 PR另把 tool budgeting 和 markdown-heavy 降峰保留为后续 follow-up具体顺序和边界以 10-issue-oriented-flicker-plan.md 为准。

视角说明:本文档中的 Phase / 周次表负责表达技术演进顺序,不等于实际开 PR 的粒度。凡是涉及 flicker 主线的 issue 归属、PR 边界、非目标和关闭口径,统一以 10-issue-oriented-flicker-plan.md 为准;真正准备开工时,再落到 11-pr1-implementation-checklist.md

实施约束:从这一版开始,所有落地工作默认都应同时参考 06-implementation-rollout-checklist.md。如果某项优化没有满足对应的验收清单、灰度顺序和回滚条件,就不应直接进入默认开启阶段。

4. 分阶段实施计划

Phase 0观测基线第 1 周)

变更 工作流 风险 预期收益
扩展 startup profilerfirst paint、input enabled、config.initialize()、首个/全部 MCP 工具、Gemini tools 刷新 性能 避免用 render 前指标误判启动瓶颈
为 stdout 输出层增加 counterswrites/sec、bytes/sec、clearTerminal 次数、eraseLines 优化次数、BSU/ESU 平衡 闪烁 后续防闪烁方案可量化验收

Phase 1快速见效第 2-5 周)

周次 变更 工作流 风险 预期收益
2 流式更新节流content + thought结束/取消/工具调用时立即 flush 闪烁 stdout.write 从 50+/秒降至 <20/秒
2-3 refreshStatic() 安全语义拆分 闪烁 已清屏路径不再重复 clear
3-5 大输出 pre-slicing + detail stability 闪烁 大结果与展开场景不再引发 layout 风暴
3 Markdown token/block 缓存(不缓存 ReactNode 渲染 缓存命中时解析耗时显著下降
3 代码高亮缓存 + highlightAuto 限制/预热策略 渲染 重复渲染消除,降低大块代码成本
4 loadSettingsAsync 渐进引入,保留同步 wrapper 性能 配置加载耗时降低,避免大范围破坏
5 并行化 UI 前初始化i18n 与 config 并行 + auth 与其他并行) 性能 启动时间减少 200-400ms
5 ANSI 16 色默认主题检测 渲染 改善透明终端兼容性

Phase 2架构改进第 5-10 周)

周次 变更 工作流 风险
6-7 渐进式 MCP 可用性 + Gemini tools debounce 刷新 性能
6-7 运行期 MCP refresh/reload 路径增量化(避免 restartMcpServers() 全量重启) 性能
7-9 同步输出 DECSET 2026按终端家族灰度 闪烁 中低
7 动态内容高度阈值优化 + 现有渐进提升增强 闪烁
7-8 切换到 marked 解析器(特性开关) 渲染
8-9 智能 refreshStatic()(定向更新) 闪烁
9-10 OSC 8 终端超链接 渲染
10 产物体积优化 性能

Phase 3深度结构性改造第 11-16 周)

周次 变更 工作流 风险
11-13 双缓冲 + diff patchInk 扩展) 闪烁
13-15 消息历史虚拟滚动 渲染
15-16 LaTeX/数学公式渲染 渲染
远期 Web 渲染探索(混合架构) 渲染 探索性

5. 向后兼容策略

  • 环境变量QWEN_CODE_LEGACY_RENDERING=1 可整体关闭所有渲染优化
  • 已有兼容QWEN_CODE_LEGACY_ERASE_LINES=1 保留用于擦除行优化的回退
  • 主题:仅默认选择变更,所有 hex 颜色主题保留可用
  • 解析器:特性开关控制,旧解析器作为过渡期回退
  • MCP:所有 Server 快速响应时行为等价;慢 Server 不再阻塞快 Server但工具声明只保证从下一次模型请求开始生效

6. 实施门禁

除各子文档自己的验证章节外,还应统一遵守以下门禁:

  1. 先补观测,再改行为 没有 first_paintinput_enabledmcp_server_ready、输出层 counters 的变更,不应宣称性能收益。

  2. 先加开关,再做灰度 同步输出、渐进式 MCP、parser 切换、虚拟滚动都应先具备独立回退能力。

  3. 先做主路径,后做高风险路径 冷启动并行化、流式节流、token cache 应先于 DECSTBM、自研 diff renderer、全量虚拟滚动。

  4. 运行期路径必须和启动路径一起设计 MCP 如果只优化首次启动,而保留 runtime refresh 的全量重启,方案仍然不完整。

7. 验证策略

  1. 自动化基准测试启动分段耗时、渲染时间、stdout writes/sec、stdout 字节/帧;启动 profile 需明确在 sandbox child process 中采集
  2. 多终端视觉测试iTerm2、Terminal.app、WezTerm、kitty、Windows Terminal、tmux
  3. 回归检测:滚动启动 profile 对比MCP 首工具/全工具可用时间对比
  4. 边界场景:窄终端 (< 40 列)、超长输出 (5000+ 行)、CJK 内容、tmux/SSH
  5. 特性开关Phase 2+ 所有变更可安全回滚

8. 子文档索引

文档 说明
01-performance.md 启动性能与 MCP 优化详细设计
02-screen-flickering.md 屏幕闪烁问题分析与解决方案
03-rendering-extensibility.md 渲染性能与可扩展性设计
04-gemini-cli-research.md Gemini CLI 源码调研
05-claude-code-research.md Claude Code 源码调研
06-implementation-rollout-checklist.md 实施门禁、验收、灰度与回滚清单
07-issue-backed-failure-taxonomy.md 基于 issue 与当前源码的故障分类和修复路线
08-execution-plan-and-test-matrix.md 按文件落点、阶段拆解和测试矩阵整理的执行稿
09-pr-3013-gap-analysis.md 专门分析 #3013 已修复范围、剩余缺口与可复用 patch仅作为参考样本
10-issue-oriented-flicker-plan.md 按用户 issue 类别组织的闪屏 4-PR 实施路线、边界、验证场景与推荐顺序
11-pr1-implementation-checklist.md PR-1 的可执行开发清单、测试顺序、commit 切分与验收步骤
12-pr2-implementation-checklist.md PR-2 的大输出与详情展开稳定性实操清单
13-pr3-implementation-checklist.md PR-3 的窄屏 / interactive shell 专项实操清单
14-pr4-implementation-checklist.md PR-4 的 synchronized output 灰度实操清单
15-complete-tui-flicker-closure-plan.md PR-1PR-4 之后剩余 TUI 闪烁 / 重复输出 issue 的彻底闭环方案