大家好,我是何三,独立开发者

我第一次看到 OpenTUI 的时候,整个人是懵的。

一个用 Zig 写的终端 UI 框架,竟然支持 ReactSolidJS 组件,还能在终端里跑 Three.js WebGPU 渲染

你没看错,终端,命令行那个终端,跑 Three.js。

这不是什么玩具项目。OpenCode(一个 AI 代码编辑器)已经在生产环境用它了,terminal.shop 也在用。一个 Zig 写的底层引擎,加了层 TypeScript 绑定,然后前端框架的组件可以直接扔进终端渲染——这玩意儿说实话,我一开始以为是个整活项目。

结果发现人家是认真的。

为什么说这个项目很"不对劲"

先说底层。

终端 UI 框架其实不少。Node.js 那边有 Ink(用 React 写终端),Python 有 Rich,Go 有 Bubble Tea。但它们都有一个共同问题——性能天花板

因为都是解释型语言写的,想在终端里搞复杂的布局、动画、异步渲染,很快就会碰到瓶颈。帧率上不去,内存下不来。

OpenTUI 的做法就有点不讲道理了。

它用 Zig 写了一个原生核心,通过 C ABI 暴露接口,上层再封装 TypeScript 绑定。Zig 是什么?它是一门系统级编程语言,比 C 更现代,比 Rust 更简洁,编译出来的二进制小得离谱——但这个语言在国内知道的人真不多。

终端UI进化史

等等,说到 Zig,我突然想到一个事。

去年有个叫 Ghostty 的终端模拟器,也是 Zig 写的,火得一塌糊涂。当时我还在想,Zig 这语言是不是要起飞了。结果现在又冒出来一个 OpenTUI,还是 Zig 写的。这俩项目要是联个动,一个 Zig 写的终端跑一个 Zig 写的 TUI 框架——画面太美我不敢看。

说回正题。

OpenTUI 最骚的操作是什么?它搞了 React reconcilerSolidJS reconciler

什么意思?

就是你可以用 React 的 JSX 语法直接写终端界面。状态管理、组件化、响应式更新——这些前端概念,全都能用在命令行里。

import { createCliRenderer, Text, Box } from "@opentui/core"

const renderer = await createCliRenderer({
  exitOnCtrlC: true,
})

renderer.root.add(
  Box({
    direction: "col",
    children: [
      Text({ content: "你好,OpenTUI!", fg: "#00FF00", bold: true }),
      Text({ content: "按 Ctrl+C 退出", fg: "#888888" }),
    ],
  })
)

说白了,以前你想写一个好看的终端工具,得用 ncurses 或者直接跟 ANSI 转义码死磕。现在你写终端界面,就跟写网页一样——JSX 拼组件,CSS 类似的属性调样式。

为什么这么设计?别问我,问作者去。反正我个人觉得,这个思路确实让 TUI 开发的门槛降了一大截。

组件多到离谱

OpenTUI 内置的组件阵容,看得我有点想吐槽——一个终端框架,你搞这么多东西干嘛?

  • Text / Box — 基础组件
  • Input / Textarea — 表单组件
  • Select / TabSelect — 选择器
  • ScrollBox / ScrollBar — 滚动组件
  • Slider — 滑块
  • Code — 代码渲染(带语法高亮)
  • Markdown — Markdown 渲染
  • FrameBuffer — 帧缓冲
  • ASCIIFont — ASCII 艺术字生成
  • Diff — 代码对比
  • QR Code — 二维码生成

对,你没看错,一个终端框架内置了 二维码生成Diff 对比

这个 QR Code 组件让我想了很多。比如你可以写个终端工具,跑完任务直接输出一个二维码,扫码跳到某个页面。这太适合 DevOps 场景了——部署完 CI/CD,终端里直接蹦个二维码,扫码看结果。

还有那个 FrameBuffer 组件,它其实是 Three.js WebGPU 渲染器的底层支持。OpenTUI 有个叫 @opentui/three 的包,可以在终端里用 Three.js 做 3D 渲染。

这个压缩率——算了先不说这个,你先装上看效果。

一分钟上手

装 OpenTUI 很简单,用 Bun 装就行:

bun create tui

或者手动装:

mkdir my-tui && cd my-tui
bun init -y
bun add @opentui/core

然后写个 index.ts

import { createCliRenderer, Text } from "@opentui/core"

const renderer = await createCliRenderer({
  exitOnCtrlC: true,
})

renderer.root.add(
  Text({
    content: "Hello, OpenTUI!",
    fg: "#00FF00",
  })
)

跑起来:

bun run index.ts

终端里就出现了一行绿色的 "Hello, OpenTUI!"。

说实话,这块我也没完全搞懂它的渲染管线是怎么跟 Zig 原生核心交互的。大致流程就是 TypeScript 层把组件树 diff 一下,然后通过 C ABI 把渲染指令发给 Zig 核心,Zig 那边直接用原生方式写终端帧缓冲。细节可能有出入——有懂的大佬欢迎指正。

目前 OpenTUI 是 Bun 独占的,Deno 和 Node 的支持正在开发中。所以想尝鲜的话,得先装个 Bun。

同类项目跟它有什么不一样

终端 UI 框架这块,市面上其实有几条路线:

项目 语言 特点
Ink JavaScript React 写终端,但性能受限于 Node.js
Bubble Tea Go 基于 Elm 架构,生态成熟
Rich Python 纯文本渲染,不适合复杂交互
Ratatui Rust Rust 生态,性能好但上手门槛高
OpenTUI Zig + TS 原生性能 + 前端框架绑定,性能与开发的平衡点

OpenTUI 的定位其实很特别——它不是跟 Ink 抢饭碗,而是给那些觉得 Ink 性能不够、又不想跳进 Rust 深坑的人一个中间选择。

底层用 Zig 保证性能,上层用 TypeScript 保证开发体验。这个组合拳打得挺聪明。

如果你对 Zig 生态感兴趣,我上次还写过一篇关于 Zig 语言生态的文章(公众号回复「Zig」可以看),里面提到了 Ghostty 和 TigerBeetle 等项目,它们跟 OpenTUI 算是同一个技术浪潮。

几点吐槽和期待

文档写得有点抽象。很多概念对刚从网页开发转过来的人不太友好,比如 "Renderables vs Constructs" 那块,我看了三遍才大概明白区别。

不过好在它们有丰富的 Examples 包,可以直接跑起来看效果:

curl -fsSL https://raw.githubusercontent.com/anomalyco/opentui/main/packages/examples/install.sh | sh

还有一件事我觉得挺酷的——OpenTUI 支持 AI Agent Skill。你可以用 npx skills add anomalyco/opentui 把它的 API 知识库装到你的 AI 编码助手里。说白了,你写代码的时候 AI 可以直接帮你生成 OpenTUI 的组件代码。

这个功能我真的吹爆。

总结

OpenTUI 不是那种"装不装都行"的项目。如果你写 CLI 工具、做 DevOps 面板、搞终端交互式应用,它值得你花半小时折腾一下。

核心就一句话:Zig 保性能,React 保体验,一个让你用前端思维写终端的框架。

装不装都行,看你自己。

项目地址:https://github.com/anomalyco/opentui

本文使用 MGO 编辑并发布

关注"何三笔记",回复"mgo" 免费下载使用