Ink

Ink 模块是 Claude Code 的终端渲染引擎核心,远超简单的 Ink 框架封装。它包含自定义 React Reconciler(reconciler.ts)、基于 Yoga 的 Flexbox 布局引擎(layout/)、双缓冲帧渲染器(renderer.ts)、ANSI 转义序列处理(Ansi.tsx)、文本测量与换行(measure-text.ts、wrap-text.ts)、终端事件系统(events/)、滚动与选择(selection.ts)、搜索高亮(searchHighlight.ts)、超链接支持(supports-hyperlinks.ts)等 40+ 个文件。它本质上是一个完整的终端 GUI 框架。

职责与设计理念

职责说明

深度定制的终端渲染引擎,基于 Yoga 布局 + React Reconciler 实现终端 UI

设计理念

终端即画布——不是在终端上"打印文本",而是在终端上"渲染界面"。双缓冲 + 差分更新 + Yoga 布局,让终端 UI 拥有与浏览器 DOM 同等的表达力。

架构决策

为什么要自己写 React Reconciler 而不是直接用 blessed/ink?

自定义 Reconciler + Yoga 布局 + 双缓冲渲染

blessed 是命令式 API,与 React 声明式范式冲突。原版 Ink 的渲染性能不满足 Claude Code 的需求(长对话、大 diff、实时流式输出)。自定义 Reconciler 让团队完全控制渲染管线,实现帧间缓存、脏帧检测等优化。

⚖️ 维护成本极高——相当于维护一个终端浏览器引擎。但对于 Claude Code 这种重度终端应用,性能收益远超维护成本。

为什么用双缓冲而不是直接写入终端?

前后帧双缓冲 + 差分输出

直接写入终端会产生闪烁(先清屏再写入的间隙可见)。双缓冲在内存中完成新帧渲染,然后只输出与旧帧的差异部分,消除闪烁且减少 I/O 量。

文件清单

文件名 用途
renderer.ts 双缓冲帧渲染器,管理前后帧切换和差分输出
reconciler.ts 自定义 React Reconciler,桥接 React 虚拟 DOM 与终端 DOM
dom.ts 终端 DOM 实现,定义 DOMElement/TextNode 节点模型
render-node-to-output.ts 节点渲染管线,将 DOM 树转换为屏幕缓冲区
output.ts 屏幕输出缓冲区,管理字符网格和样式属性
layout/ Yoga Flexbox 布局引擎适配层
components/ 内置终端组件(ScrollBox、Box、Text 等)
events/ 终端事件系统,处理键盘、鼠标、焦点事件

使用场景

AI 流式输出文本

每个 token 到达时触发 React 重渲染,Reconciler 计算最小 DOM 变更,渲染器输出差分

API Stream → React setState → Reconciler diff → Renderer 差分输出

用户调整终端窗口大小

Yoga 重新计算所有节点的 Flexbox 布局,渲染器输出完整新帧

依赖关系

无外部模块依赖

关键代码片段

双缓冲渲染器

export default function createRenderer(
  node: DOMElement, stylePool: StylePool
): Renderer {
  let output: Output | undefined
  return (options) => {
    const { frontFrame, backFrame, terminalWidth } = options
    // 复用 Output 的 charCache(tokenize + grapheme clustering)
    if (output) output.reset(width, height, screen)
    else output = new Output({ width, height, stylePool, screen })

    renderNodeToOutput(node, output, {
      prevScreen: options.prevFrameContaminated
        ? undefined : prevScreen,
    })
    return { screen: output.get(), viewport, cursor }
  }
}

双缓冲 + 帧间缓存复用 + 脏帧检测,实现高性能终端渲染

自定义 React Reconciler

// 桥接 React 虚拟 DOM 与终端 DOM
const reconciler = ReactReconciler({
  createInstance(type, props) {
    // React 创建元素 → 终端 DOMElement
    return new DOMElement(type, props)
  },
  appendChildToContainer(container, child) {
    container.appendChild(child)
    // 触发 Yoga 重新布局
    container.yogaNode.markDirty()
  },
  commitUpdate(instance, updatePayload) {
    // React 更新 → 终端 DOM 属性更新
    instance.applyProps(updatePayload)
  },
})

React Reconciler 将 JSX 声明式 UI 翻译为终端 DOM 操作,Yoga 处理布局

技术笔记

performance

charCache 缓存策略

Output 对象的 charCache 缓存了 tokenize 和 grapheme clustering 的结果。大多数行在帧间不变,缓存命中率通常 > 90%,显著减少 CPU 开销。

edge-case

Unicode 宽字符处理

measure-text.ts 使用 grapheme clustering 正确处理 emoji、CJK 字符等宽字符的终端列宽计算,避免布局错位。