本章总览
src/ink/ 是 Claude Code 内置的终端 UI 运行时(100 文件 · 约 19952 行),在开源 Ink 基础上深度定制:Yoga 布局、双缓冲 Screen、增量 diff 写终端、全屏 alt-screen、文本选择与搜索高亮、Kitty/xterm 扩展键与 bracketed paste。Claude Code 的 components/ 与 screens/REPL.tsx 都渲染在这棵 Ink 树上。本模块按子主题拆分:渲染管线、终端事件、Hooks API、核心组件。
总览图
学完本章你应该能
- 区分 ink/ 与上层 components/ 的职责:ink 管「怎么画到终端」,components 管「画什么业务 UI」
- 建立 React reconciler → Yoga → render-node-to-output → LogUpdate diff → writeDiffToTerminal 的心智模型
- 会从子章节进入 stdin/resize/paste 与 useInput/useSearchHighlight 的实现
- 理解 AlternateScreen、ScrollBox、Box 事件模型与 REPL 全屏布局的关系
建议学习步骤
- 浏览下方 SourceTree,点击文件跳转到子章节或源码锚点
- 先读 ink-rendering,理解 onRender 帧循环与 alt-screen 差异
- 再读 terminal-events,对照 App.tsx 的 stdin readable 路径
- 最后读 ink-hooks 与 ink-components,串起 REPL 搜索与滚动
模块在架构中的位置
ink 是 src/ 下的一级目录,共 100 个文件、19,952 行。一句话概括:React + Ink 渲染的 TUI:消息流、权限弹窗、Spinner、任务面板、MCP 配置界面。
概览
| 指标 | 数值 |
|---|---|
| 行数 | 19,952 |
| 文件 | 100 |
子章节导航
| 子章节 | 主题 | 核心路径 |
|---|---|---|
| Ink 渲染管线 | reconciler、Screen、diff、写 stdout | ink.tsx, output.ts, log-update.ts |
| 终端事件 | resize、paste、stdin、键鼠 | components/App.tsx, parse-keypress.ts, events/ |
| Ink Hooks | useInput、搜索高亮、终端焦点 | ink/hooks/ |
| Ink 组件 | Box、Text、ScrollBox、AlternateScreen | ink/components/ |
架构分层(简图):
screens/REPL.tsx + components/*
→ render(<Tree />) // ink.tsx
→ reconciler (custom host, Yoga DOM)
→ renderNodeToOutput → Output collector → Screen buffer
→ LogUpdate.render(prev, next) → Diff patches
→ writeDiffToTerminal(stdout)
上层业务组件只应通过 Ink 导出的 Box、Text、ScrollBox 与 hooks 交互;直接写 ANSI 应限于 RawAnsi 或 TerminalWriteProvider 受控路径。
与上游 Ink 的差异
本仓库 src/ink/ 不是 npm 上的 vadimdemedes/ink 薄封装,而是产品级 fork,主要增量包括:
- 双缓冲 Frame + damage bounds:稳态帧 O(变更单元) diff,布局位移时全帧 damage(PR #20120)
- ScrollBox 硬件滚动:DECSTBM + SU/SD hint,配合 drain 帧加速滚动
- 全屏 alt-screen:选择、OSC 8 超链、鼠标 1003、多击选词/选行
- 搜索高亮:屏幕空间
applySearchHighlight+useSearchHighlight扫描 DOM 子树 - 终端探测:XTVERSION、DEC 2026 同步输出、Kitty keyboard、bracketed paste
- React Compiler:多数组件带
_c缓存,阅读时聚焦 props/状态名
调试终端闪烁时,优先查 frame.ts 的 flicker reason(resize/offscreen/clear)与 REPAINT 调试日志。
目录簇划分
100 个文件可按职责分为五簇,便于在 SourceTree 中定位:
| 簇 | 路径 | 说明 |
|---|---|---|
| 引擎核心 | ink.tsx, reconciler.ts, dom.ts, renderer.ts | 实例生命周期、React 宿主、帧调度 |
| 渲染 | render-node-to-output.ts, output.ts, screen.ts, log-update.ts | 树 → 单元格 → ANSI diff |
| 终端 IO | terminal.ts, termio/*, parse-keypress.ts, components/App.tsx | 能力检测、CSI/OSC、stdin 泵 |
| 事件 | events/*, focus.ts, hit-test.ts | DOM 风格捕获/冒泡、点击悬停 |
| 组件与 Hooks | components/*, hooks/* | 公开 API,供 REPL 消费 |
layout/ 与 native-ts/yoga-layout 衔接 WASM Yoga;测量文本宽度走 measure-text.ts、stringWidth.ts、line-width-cache.ts。
目录树覆盖全部 100 个文件。点击 ink.tsx 进入渲染管线章节;hooks/use-search-highlight.ts 进入 Hooks 章节;components/ScrollBox.tsx 进入组件章节。
本章小结与延伸
ink 是终端渲染引擎。建议阅读顺序:渲染管线 → 终端事件 → Hooks → 组件原语。 继续学习: