Ink 引擎
源码:
components/|cli/| Ink 3.x (react for terminal)
1. Ink 框架基础
Claude Code 使用 Ink 构建终端 UI。Ink 是 React 的渲染器,将 React 组件渲染到终端而不是浏览器 DOM。
React Components (Ink)
│
↓
┌──────────────────┐
│ Terminal Output │ ← stdout
│ ┌──────────────┐ │
│ │ 消息列表 │ │
│ │ 工具调用状态 │ │
│ │ 输入提示符 │ │
│ └──────────────┘ │
└──────────────────┘
│
↓
┌──────────────────┐
│ Terminal Input │ ← stdin
│ (键盘事件) │
└──────────────────┘
2. 主应用组件
cli.tsx 是入口组件,负责组装整个 UI:
// 主应用组件
function App() {
// 1. 订阅 AppStateStore 状态变化
// 2. 渲染消息列表
// 3. 渲染工具调用状态
// 4. 渲染输入提示符
return (
<Box flexDirection="column" height="100%">
<Messages />
<ToolCalls />
<InputPrompt />
</Box>
)
}
3. 终端渲染
Ink 使用 render() 将 React 树渲染为终端输出:
import { render } from 'ink'
const { unmount } = render(
<App />,
{
patchConsole: true, // 拦截 console.log
exitOnCtrlC: true, // Ctrl+C 退出
}
)
4. 流式输出处理
LLM 响应是流式的,Ink 需要高效处理频繁更新:
// 流式文本组件
function StreamingText({ text }: { text: string }) {
return <Text wrap="wrap">{text}</Text>
}
关键优化:
- Ink 只重新渲染变化的部分(React reconciliation)
- 文本流式追加时不重绘整个屏幕
- 使用
Box的flexGrow实现自适应高度
5. 交互模式
// 用户输入组件
function InputPrompt() {
const [input, setInput] = useState('')
const { stdin } = useStdin()
// 处理 Enter 提交
// 处理 Tab 补全
// 处理 Ctrl+C 中断
}
支持的交互:
- 多行输入(Shift+Enter 换行)
- 命令补全(Tab)
- 历史导航(上下箭头)
- 中断当前请求(Ctrl+C)