06 — 流式 TUI 渲染:ratatui 状态机与终端架构
流式 TUI 渲染:ratatui 状态机与终端架构
学习目标
理解 CodeWhale 的 TUI 架构:ratatui 流式渲染、事件循环、三模式 UI 表现、键盘快捷键体系与 CJK 字符处理。
前置知识
- 流式传输架构 — Token/块/预览三层流式模型
下文假设你已理解上述概念,直接聚焦 CodeWhale 的具体实现。
项目实践
组件布局
流式文本收集器
streaming.rs 负责将 LLM 的流式输出收集并渲染到转记面板:
- 接收
content和thinking块的增量更新 - 缓冲中间渲染,避免频繁屏幕刷新
- 流结束后最终渲染
事件循环
关键状态:
- Idle — 等待用户输入
- Composing — 用户在 composer 中输入
- Streaming — LLM 响应流式输出中
- Approving — 工具审批对话框弹出
- Picker — 选择器(模型/提供商/模式)
键盘快捷键体系
| 快捷键 | 行为 |
|---|---|
Tab | 补全 / 或 @ 条目;运行中排队草稿;否则循环模式 |
Shift+Tab | 循环推理级别:off → high → max |
F1 | 可搜索的帮助覆盖层 |
Esc | 返回 / 关闭 |
Esc-Esc | 回退转记到之前的用户 prompt |
Ctrl+K | 命令面板 |
Ctrl+R | 恢复早期会话 |
Alt+R | 搜索提示历史并恢复已清除的草稿 |
Ctrl+S | 暂存当前草稿 |
@path | 附加文件/目录上下文 |
↑(composer 起始位置) | 选择附件行以移除 |
v0.8.46+ 新增:
- Composer 文本选择:鼠标拖拽和 Shift+Arrow 选择,Ctrl+C 复制,Ctrl+X 剪切
- Home/End/Ctrl+A/Ctrl+E 清除选择
三模式 UI 表现
| 模式 | UI 指示 | 工具行为 |
|---|---|---|
| Plan | 🔍 只读徽章 | 破坏性工具被阻止 |
| Agent | 🤖 默认徽章 | 工具调用弹出审批对话框 |
| YOLO | ⚡ 闪电徽章 | 工具自动批准,无对话框 |
模式切换:Tab 在 composer 空闲时循环模式,或使用 /mode <name>。
Markdown 渲染
CodeWhale 的 TUI 渲染 Markdown 内容:
- 表格、粗体/斜体、水平规则
- 终端 monospace 字体中表格可能不对齐(尤其 CJK 字符)
- Constitution 明确告诉模型不要输出 Markdown 表格
CJK 处理
NO_ANIMATIONS=1强制无障碍模式(无动画)- CJK 字符在表格对齐中特别容易出问题
- v0.8.46+ 修复了 CJK 包装传播到 diff 和 pager 输出
会话分支与恢复
codewhale fork <SESSION_ID>— 复制现有会话到新的兄弟会话,记录父会话 ID- 会话选择器和
codewhale sessions标记分支会话的父 ID - 崩溃检查点:发送用户输入前写入
~/.codewhale/sessions/checkpoints/latest.json - 默认新鲜启动;旧会话通过
--resume/--continue或Ctrl+R显式恢复 - 降级/离线时新提示排队到内存并镜像到
offline_queue.json
问题与规避
陷阱:终端兼容性
不同终端(macOS Terminal.app、iTerm2、Linux 终端、Windows ConHost)对 ratatui 的支持不同。
规避:
- v0.8.46 修复了 macOS Terminal.app 和 ConHost 的窗口缩放 artifact 问题
NO_ANIMATIONS=1为不支持动画的终端提供回退- 复制/粘贴在 Windows 上有特殊的 Enter/CSI-u 处理
陷阱:流式渲染闪烁
频繁刷新终端可能导致闪烁。
规避:
- 流式收集器缓冲中间渲染,不每个 token 刷新一次
- v0.8.46+ 减少了终端重绘闪烁
- ratatui 的
Terminal::draw()使用缓冲渲染,减少撕裂
陷阱:长行溢出终端宽度
LLM 输出的长代码行或 URL 可能溢出终端宽度。
规避:
- ratatui 的文本组件自动换行
- Constitution 告诉模型避免宽表格
- 视觉包装新行从转记复制中去除(v0.8.46)
设计取舍
为什么选 ratatui
- Rust 生态最成熟的 TUI 框架
- 与 CodeWhale 的 Rust 技术栈一致
- 组件模型适合复杂的布局需求
- 社区活跃,持续改进
为什么 TUI crate 包含引擎
历史原因:CodeWhale 从单 crate 开始,tui crate 最初包含一切。渐进式拆分正在进行中——core、tools、config 等已拆分为独立 crate。
当前状态:
crates/tui/src/core/→ 正在迁移到crates/core/crates/tui/src/tools/→ 正在迁移到crates/tools/- TUI 特定代码(渲染、事件处理)保留在
crates/tui/src/tui/
参考来源
crates/tui/src/tui/— TUI 组件目录crates/tui/src/tui/streaming.rs— 流式文本收集器docs/KEYBINDINGS.md— 完整快捷键目录docs/ACCESSIBILITY.md— 无障碍文档