docs: refine tui optimization design

This commit is contained in:
秦奇 2026-04-20 21:18:29 +08:00
parent 4eb19a94c0
commit 88efd775db
4 changed files with 342 additions and 153 deletions

View file

@ -4,14 +4,16 @@
## 1. 背景与动机
qwen-code 的 TUI 层基于 **Ink 6.2.3 + React 19** 构建,当前面临三个系统性挑战:
qwen-code 的 TUI 层基于 **Ink 6.2.3 + React 19** 构建,当前面临三个系统性挑战。下列问题需要先用源码口径校准后再实施,避免优化目标与真实瓶颈错位
1. **启动性能**:启动流程串行执行,配置 MCP Server 时尤为缓慢,用户需等待所有 Server 连接完成后才能使用工具
1. **启动性能**:启动流程包含多段串行初始化;交互式模式下 `config.initialize()` 在 UI 首次渲染后执行,配置 MCP Server 时工具声明和实际可用性仍会被慢 Server、工具注册刷新和 Gemini tools 更新路径影响
2. **屏幕闪烁**Ink 的全量重绘机制导致流式输出时严重闪烁,在 tmux/SSH 环境下尤为突出(社区报告高达 4,000-6,700 次/秒的滚动事件)
3. **渲染能力与可扩展性**:自定义正则 Markdown 解析器功能受限,缺少 LaTeX 数学公式、终端超链接等支持,主题系统硬编码 hex 颜色导致部分终端兼容性问题
3. **渲染能力与可扩展性**:自定义正则 Markdown 解析器功能受限,缺少 LaTeX 数学公式、终端超链接等支持;主题系统默认 hex 主题可能影响透明背景终端
这些问题在 GitHub Issues 中被大量报告qwen-code#1778, #2748, #2877; claude-code#9935, #37283, #14641 等),是当前最主要的用户体验痛点。
**重要校准**:当前启动分析器只覆盖 UI render 之前的 checkpoint尚未覆盖交互式 `config.initialize()`、MCP 首个工具注册、全部 MCP 发现完成、Gemini tools 声明刷新等阶段。因此本文档的实施顺序必须先补观测,再用真实数据确认优先级。
## 2. 现状分析
### 2.1 当前架构
@ -35,8 +37,8 @@ Entry (gemini.tsx)
| 代码高亮 | lowlight (基于 highlight.js) | `packages/cli/src/ui/utils/CodeColorizer.tsx` |
| 防闪烁 | stdout 拦截器,折叠重复 ANSI 序列 | `packages/cli/src/ui/utils/terminalRedrawOptimizer.ts` |
| 主题 | ThemeManager 单例15+ 内置主题 | `packages/cli/src/ui/themes/theme-manager.ts` |
| MCP | Promise.all 并行连接10分钟超时 | `packages/core/src/tools/mcp-client-manager.ts` |
| 启动分析 | 环境变量开启的 checkpoint 记录器 | `packages/cli/src/utils/startupProfiler.ts` |
| MCP | 跨 Server 并行发现;整体仍等待全部完成,默认 10 分钟超时;工具注册和 Gemini tools 刷新需要拆开设计 | `packages/core/src/tools/mcp-client-manager.ts` |
| 启动分析 | 环境变量开启的 checkpoint 记录器;当前主要覆盖 render 前阶段 | `packages/cli/src/utils/startupProfiler.ts` |
### 2.2 竞品分析Claude Code
@ -50,7 +52,7 @@ Claude Code 使用**自研的 Ink 深度定制版本**(非 npm 库),包含
| 布局检测 | 布局稳定时窄范围 diff变化时全量重绘 | 无 diff始终全量 |
| 样式池化 | StylePool 整数 ID 内化 + 转换缓存 | 无,每次重新计算 |
| Markdown | marked 库 + LRU 令牌缓存500条 | 自定义正则,无缓存 |
| MCP 启动 | 提前并行启动 + Promise.race 超时 | UI 渲染后才启动 |
| MCP 启动 | 提前并行启动 + Promise.race 超时 | UI 渲染后初始化,跨 Server 并行但整体等待 |
### 2.3 社区反馈汇总
@ -58,7 +60,7 @@ Claude Code 使用**自研的 Ink 深度定制版本**(非 npm 库),包含
| ---------- | ----------------------------------------------- | -------- |
| 屏幕闪烁 | qwen-code#1778, #2748; claude-code#9935, #37283 | 高 |
| 启动慢 | qwen-code#2748; claude-code#5653, #29201 | 高 |
| 表格渲染 | claude-code#14641, #22311 | 中 |
| 表格渲染 | claude-code#14641, #22311qwen-code 当前已有 ANSI/CJK 回归测试,需以可复现缺陷为准 | 中 |
| 主题/颜色 | qwen-code#2877; claude-code#34702, #15771 | 中 |
| 窄屏问题 | claude-code#13504, #18493, #5408 | 中 |
| LaTeX 支持 | claude-code#21433 | 低 |
@ -67,33 +69,40 @@ Claude Code 使用**自研的 Ink 深度定制版本**(非 npm 库),包含
| 工作流 | 核心问题 | 关键指标 | 依赖关系 |
| -------------- | -------------------------------------- | ------------------------------ | -------------------------- |
| **启动性能** | 串行启动流程MCP 阻塞工具可用性 | 可交互时间 (TTI) | 独立,最先启动 |
| **屏幕闪烁** | Ink 全量重绘;无同步输出 | 闪烁事件/秒stdout 字节/帧 | 部分依赖启动性能(节流) |
| **渲染与扩展** | 正则解析器脆弱;缺少格式支持;主题限制 | 格式覆盖率,渲染耗时,可配置性 | 依赖闪烁修复(稳定输出层) |
| **观测基线** | 现有 profile 不覆盖 render 后初始化和输出层 | first paint、TTI、MCP 首工具、stdout writes/sec | 所有优化的前置条件 |
| **启动性能** | 串行启动流程MCP 工具声明刷新不完整 | first paint、input enabled、首个 MCP 工具可被模型使用 | 依赖观测基线 |
| **屏幕闪烁** | Ink 全量重绘;无同步输出 | 闪烁事件/秒stdout writes/sec、clearTerminal 次数 | 依赖输出层观测 |
| **渲染与扩展** | 正则解析器脆弱;缺少格式支持;主题限制 | 格式覆盖率parse/highlight 耗时,可配置性 | 依赖稳定输出层 |
**执行顺序**启动性能(最独立)-> 屏幕闪烁(解锁渲染改进)-> 渲染与扩展(基于稳定输出层)
**执行顺序**观测基线 -> 屏幕闪烁低风险治理 -> 启动/MCP 渐进可用 -> 渲染缓存与扩展。MCP 与渲染可并行推进,但必须共享同一套指标口径。
## 4. 分阶段实施计划
### Phase 1快速见效第 1-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 周)
| 周次 | 变更 | 工作流 | 风险 | 预期收益 |
| ---- | ----------------------------------------------------------- | ------ | ---- | --------------------------------- |
| 1 | 同步输出 DECSET 2026 | 闪烁 | 低 | 消除大部分可见闪烁 |
| 1 | 流式更新节流60ms 批处理) | 闪烁 | 低 | stdout.write 从 50+/秒降至 <20/ |
| 2 | Markdown 解析结果缓存 | 渲染 | 低 | 缓存命中时渲染耗时降低 70%+ |
| 2 | 代码高亮缓存 + 语法库懒加载 | 渲染 | 低 | 启动加速 + 重复渲染消除 |
| 3 | 并行配置加载(异步 I/O | 性能 | 低 | 配置加载耗时降低 30-50% |
| 3 | 启动分析器增强 | 性能 | 低 | 持续监控回归 |
| 4 | 并行化 UI 前初始化i18n 与 config 并行 + auth 与其他并行) | 性能 | 低 | 启动时间减少 200-400ms |
| 4 | ANSI 16 色默认主题检测 | 渲染 | 中 | 修复透明终端兼容性 |
| 2 | 同步输出 DECSET 2026先 instrumentation再默认开启或特性开关 | 闪烁 | 中 | 消除大部分可见帧撕裂 |
| 2 | 流式更新节流content + thought结束/取消/工具调用时立即 flush | 闪烁 | 低 | stdout.write 从 50+/秒降至 <20/ |
| 3 | Markdown token/block 缓存(不缓存 ReactNode | 渲染 | 低 | 缓存命中时解析耗时显著下降 |
| 3 | 代码高亮缓存 + `highlightAuto` 限制/预热策略 | 渲染 | 中 | 重复渲染消除,降低大块代码成本 |
| 4 | `loadSettingsAsync` 渐进引入,保留同步 wrapper | 性能 | 中 | 配置加载耗时降低,避免大范围破坏 |
| 5 | 并行化 UI 前初始化i18n 与 config 并行 + auth 与其他并行) | 性能 | 低 | 启动时间减少 200-400ms |
| 5 | ANSI 16 色默认主题检测 | 渲染 | 中 | 改善透明终端兼容性 |
### Phase 2架构改进第 5-10 周)
| 周次 | 变更 | 工作流 | 风险 |
| ---- | ------------------------------------ | ------ | ---- |
| 5-6 | 渐进式 MCP 可用性 + 超时控制 | 性能 | 中 |
| 6-7 | 动态内容高度管理 + 渐进提升到 Static | 闪烁 | 中 |
| 6-7 | 渐进式 MCP 可用性 + Gemini tools debounce 刷新 | 性能 | 中 |
| 7 | 动态内容高度阈值优化 + 现有渐进提升增强 | 闪烁 | 中 |
| 7-8 | 切换到 marked 解析器(特性开关) | 渲染 | 中 |
| 8-9 | 智能 refreshStatic()(定向更新) | 闪烁 | 中 |
| 9-10 | OSC 8 终端超链接 | 渲染 | 低 |
@ -114,13 +123,13 @@ Claude Code 使用**自研的 Ink 深度定制版本**(非 npm 库),包含
- **已有兼容**`QWEN_CODE_LEGACY_ERASE_LINES=1` 保留用于擦除行优化的回退
- **主题**:仅默认选择变更,所有 hex 颜色主题保留可用
- **解析器**:特性开关控制,旧解析器作为过渡期回退
- **MCP**:所有 Server 快速响应时行为不变
- **MCP**:所有 Server 快速响应时行为等价;慢 Server 不再阻塞快 Server但工具声明只保证从下一次模型请求开始生效
## 6. 验证策略
1. **自动化基准测试**启动耗时、渲染时间、stdout 字节/帧
1. **自动化基准测试**:启动分段耗时、渲染时间、stdout writes/sec、stdout 字节/帧
2. **多终端视觉测试**iTerm2、Terminal.app、WezTerm、kitty、Windows Terminal、tmux
3. **回归检测**:滚动启动 profile 对比(增强后的分析器)
3. **回归检测**:滚动启动 profile 对比MCP 首工具/全工具可用时间对比
4. **边界场景**:窄终端 (< 40 )、超长输出 (5000+ )、CJK 内容tmux/SSH
5. **特性开关**Phase 2+ 所有变更可安全回滚