跳转到内容

06 — 流式 TUI 渲染:ratatui 状态机与终端架构

流式 TUI 渲染:ratatui 状态机与终端架构

学习目标

理解 CodeWhale 的 TUI 架构:ratatui 流式渲染、事件循环、三模式 UI 表现、键盘快捷键体系与 CJK 字符处理。

前置知识

下文假设你已理解上述概念,直接聚焦 CodeWhale 的具体实现。

项目实践

组件布局

流式文本收集器

streaming.rs 负责将 LLM 的流式输出收集并渲染到转记面板:

  • 接收 contentthinking 块的增量更新
  • 缓冲中间渲染,避免频繁屏幕刷新
  • 流结束后最终渲染

事件循环

关键状态:

  • 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/--continueCtrl+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 最初包含一切。渐进式拆分正在进行中——coretoolsconfig 等已拆分为独立 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 — 无障碍文档