OpenTUI:使用 React、Bun 和 Zig 构建终端应用

BBetter Stack
Computing/SoftwareInternet Technology

Transcript

00:00:00这是 OpenTui,一个基于 Zig 的库,用于构建终端界面,
00:00:04并提供 React、Solid 和 FreeJS 绑定,让你能像构建 Web 应用一样
00:00:09构建高性能的终端用户界面(TUI)。它由 Anomaly 开发,
00:00:14该团队也是热门开源编码代理 OpenCode 的幕后推手,旨在为 OpenCode 提供用户界面支持,
00:00:19目前已有数百万人正在使用它。但这个新库与另一个更流行的 React
00:00:24终端界面工具相比如何呢?后者出现的时间更早,且被许多热门的
00:00:29编码代理所采用。点击订阅,让我们一探究竟!
00:00:35Ink 是构建 React 终端界面的首选库。我之前做过
00:00:40一期关于它的视频,你可以点击这里观看。但它确实存在一些问题。首先,
00:00:44简单的应用就要占用超过 50MB 的内存,并且硬编码了 30fps 的帧率上限,这对
00:00:51大多数应用来说没问题。但如果你构建的是像编码代理那样需要流式传输大量文本的应用,
00:00:56那么帧率限制会让整个界面感觉迟钝。因此,Anomaly 团队
00:01:00原本使用 Golang 和 Bubble Tea 来构建 OpenCode,他们想用 TypeScript 重写它,
00:01:06而不使用 Ink。于是他们创造了 OpenTui。我的意思是,他们收购了 OpenTui。他们并没有
00:01:13完全从零开始构建它,因为 Commander FX 已经在开发一个基于 Zig 的终端
00:01:18库了。Anomaly 赞助了他,并在此基础上构建了 OpenTui。Zig 核心负责所有
00:01:24繁重的渲染工作,而 TypeScript 绑定允许你用 React 或 Solid 编写 UI 组件。但真正巧妙的是
00:01:30BUN 的外部函数接口(FFI),它允许你直接从 TypeScript
00:01:36与 Zig 原生代码通信,几乎没有延迟,这正是 OpenTui 如此
00:01:42高性能的原因。它使用 Yoga 进行 Flexbox 布局,内置了输入框和选择框等组件,
00:01:47甚至还有一个 3.js 包,可以让你在终端内渲染 WebGPU 3D 图形,
00:01:54这简直太疯狂了。实际上,让我们通过一个非常简单的演示来看看 OpenTui 的实际表现。有很多
00:01:59方法可以设置一个基本的 OpenTui 项目。我个人喜欢使用 “bun create-tui”,
00:02:04因为它提供了一个非常实用的向导,你可以用来为项目命名并选择
00:02:09你想要使用的模板。现在我选择 React,但我稍后会在视频中解释
00:02:13这三个模板之间的区别。完成后,你会得到一些标准文件,
00:02:17以及一个运行基本全屏项目的 index 文件。如果我们查看代码,
00:02:21可以看到在第 15 行,它正在使用 CreateCLI 渲染器来利用 OpenTui 的渲染
00:02:27引擎。在此之下是 CreateRoot,它负责渲染应用组件。如果你熟悉
00:02:32React,这就像是将它挂载到 HTML 文件中的代码。但因为我们是渲染到终端,
00:02:37这个项目没有 index.html,而是使用了一个自定义的 React 调解器,它使用
00:02:42终端框和文本而不是 HTML 组件。在此之上,我们有使用 Box 组件的 JSX,
00:02:49以及一些 Yoga Flexbox 属性,用于对齐其中的盒装文本,渲染 ASCII 字体和一些基本文本。
00:02:55现在,如果你不想让它成为一个全屏应用,OpenTui 支持多种屏幕模式。
00:03:00我们可以将其更改为 “split footer” 模式并设置页脚高度,将渲染固定在终端底部的
00:03:05保留区域。但让我们尝试做一些比仅仅更改屏幕模式更有趣的事情。
00:03:10这里是一个带有标题和姓名输入框的基本 TUI。现在,看看这个。使用 OpenTui,你可以免费获得
00:03:17响应式支持。所以无论我的终端宽度如何,一切看起来都很好。如果我们
00:03:21看一下代码,它使用了非常熟悉的 React 语法。在这里,我们有一个用于设置
00:03:26姓名的 useState。在输入框中,我们会在 onInput 属性中更新姓名。这
00:03:32会反映在文本中。现在,OpenTui 做的另一件很酷的事情是类似不需要 HMR 的实时重载。
00:03:37我们可以看到,如果我把文本改成 “Goodbye” 然后保存文件,它会自动
00:03:43更新。拥有响应式输入固然很好,但有时文本无法显示。这可以通过
00:03:48添加宽度属性来修复,这会让事情简单得多。使用 Box 组件,我们
00:03:52拥有 border 和 backgroundColor 等非常酷的属性,这让终端应用看起来立刻高级了许多。
00:03:56同样,我们可以使用 ASCII 字体代替普通文本,让标题更加突出。
00:04:01当然,有很多 ASCII 字体可供选择。记住,因为这是
00:04:05终端应用,而不是网页,它不会渲染系统上的所有字体,仅支持那些
00:04:10终端支持的字体。我们还可以做一些其他的事情,比如像在普通 React
00:04:15应用中那样,在提交时通过更改状态和显示不同的 JSX 来更改视图。但这里
00:04:20有一件你意想不到终端能做到的事。如果我提交了我的名字,状态现在会淡入
00:04:25到位。这是通过 OpenTui 的 useTimeline 钩子完成的,它用于设置动画
00:04:30持续时间和目标。在这种情况下,组件的透明度为零,偏移量为 8,
00:04:36意味着它在中心下方有一个上边距。在这里,动画结束时的偏移量为 0,
00:04:40透明度为 1,通过更新这里设置的偏移量和透明度状态。而这些
00:04:45值会在 Box 属性中更新。现在它看起来有点卡顿,因为终端动画需要
00:04:50逐行移动。所以有点像在网格上一样,但 OpenTui 能让你如此轻松地做到这一点
00:04:55真是太酷了。更酷的是,我们可以结合目前学到的所有知识,利用 Flexbox
00:05:00布局将盒子并排放置。所以我们一边有一个输入框,另一边是另一个盒子。
00:05:05如果我们输入到输入框中并提交,我们可以将数值动画化并传入到另一个盒子中,
00:05:10这是一个非常好的细节。从这里开始,我们还可以用 OpenTui 做很多事情,
00:05:14比如使用 useKeyboard 钩子启用键盘导航、使用 node OS 显示系统数据、
00:05:19启用鼠标支持和虚拟列表。而且因为这一切都运行在 BUN 之上,
00:05:24你可以像在任何网站上一样使用 BUN SQLite、BUN Postgres 或获取外部信息。
00:05:28最酷的是,在我编译我的应用后,虽然它是 71
00:05:34MB,因为它包含了 BUN 运行时和 React 调解器,但当我运行它时,
00:05:39你可以看到它使用的内存少于 50MB。由于 React 非常流行,
00:05:43大语言模型(LLM)对它了如指掌。因此,在 OpenTui 中构建应用可以非常简单,
00:05:49无需反复查阅文档。以上就是对 OpenTui 的一个非常、非常简单的
00:05:53介绍。让我们回到视频开头,当时我承诺会解释
00:05:58React、Solid 和 Core 之间的区别。如果你了解前端
00:06:02Web 开发,其实很简单。基本上,它们都通过同一个 Zig 核心进行渲染。
00:06:07所以实际绘制到你终端的内容几乎是完全一样的。唯一的区别在于你如何编写组件
00:06:11以及如何应用更新。React 会重新运行你的组件并在每次更改时对比虚拟树,
00:06:17这与虚拟 DOM 的工作方式非常相似。Solid 使用细粒度的更新,
00:06:22所以它只更新发生变化的部分。而 Core 跳过了所有这些,意味着你只是直接
00:06:27修改对象。因此,理论上 React 最重,Core 最轻。但在实践中,
00:06:33对于大多数终端应用来说,差距很小,因为所有繁重的工作都由 Zig 核心完成,
00:06:38这意味着在这种情况下,框架只是个人偏好。尽管一个 OpenTui
00:06:44应用会携带整个 BUN 运行时和框架特定的调解器(如果它有的话),
00:06:50其性能和大小仍然比 Ink 小得多,当然不如
00:06:56Ratatouille 或 Bubble Tea 那样原生高效。但在我看来,JSX 是组合任何 UI
00:07:02最棒且最直观的方式。我宁愿接受轻微的内存和大小影响,以获得
00:07:09比那种虽然体量轻、内存占用低,但编写和更新起来却很痛苦的方案
00:07:15更好的 TUI 开发体验。因此,考虑到这一点,如果我要构建终端应用,
00:07:20我绝对每次都会选择 OpenTui 而不是 Ink,
00:07:25我保证我很快就会去做。

Key Takeaway

OpenTui 结合了 Zig 的渲染性能与 Bun 的快速交互,为开发者提供了一种比 Ink 更轻量、更流畅且易于使用 JSX 构建终端应用的新方案。

Highlights

  • OpenTui 使用 Zig 作为核心渲染引擎,并通过 Bun 的外部函数接口(FFI)实现高性能交互。

  • OpenTui 支持 React 和 Solid 等前端框架,开发者可像构建 Web 应用一样创建 TUI。

  • 相比传统的 Ink 库,OpenTui 通过更高效的架构显著降低了内存占用并解除了 30fps 的帧率限制。

  • OpenTui 内置了 Yoga Flexbox 布局引擎、输入组件以及支持终端内渲染 3D 图形的 3.js 包。

  • 应用运行时的内存占用通常低于 50MB,且支持实时重载(Live Reload)功能。

Timeline

OpenTui 的开发背景与性能优势

  • OpenTui 由 Anomaly 团队开发,旨在为编码代理提供高性能的 UI 支持。
  • 该库通过 Zig 核心负责繁重的渲染,Bun 的 FFI 实现 TypeScript 与原生代码的低延迟通信。
  • 传统的 React 终端库 Ink 存在 50MB 以上的内存消耗及 30fps 的帧率限制。

Anomaly 团队在构建 OpenCode 过程中,为了解决 Ink 在处理大规模流式文本时的迟钝感,引入了基于 Zig 和 Bun 的 OpenTui。Zig 承担渲染核心,使得界面交互不再受限于 JavaScript 的性能瓶颈。此外,其内置的 Yoga 布局引擎和 3.js 支持扩展了终端应用的表现力。

开发流程与核心组件功能

  • 使用 bun create-tui 可通过向导快速生成项目模板。
  • OpenTui 使用自定义的 React 调解器,支持 JSX 语法与标准的 React Hooks 如 useState。
  • useTimeline 钩子支持在终端界面中实现平滑的动画效果。

开发者可以使用熟悉的 React 语法通过 Box 和 Flexbox 组件构建布局。系统支持实时重载,无需传统的 HMR 即可即时查看变更。此外,得益于 Bun 运行环境,应用可以直接调用 SQLite 或 Postgres 等数据库,并利用大语言模型降低开发门槛。

框架差异与实践评价

  • OpenTui 的 React、Solid 和 Core 模板共享相同的 Zig 渲染核心。
  • React 模式使用虚拟树对比,Solid 模式提供细粒度更新,Core 模式则直接修改对象。
  • 尽管引入了 Bun 运行时,OpenTui 在复杂度和性能表现上依然优于 Ink。

虽然原生工具如 Bubble Tea 在极低内存占用上表现更佳,但 OpenTui 提供的 JSX 开发体验和更直观的 UI 组合方式具有极高价值。对于大多数终端应用,不同框架模式带来的性能差异微小,开发者可以根据个人偏好选择最适合的开发体验。

Community Posts

No posts yet. Be the first to write about this video!

Write about this video