Cline CLI 的 OpenTUI React 项目配置全解:tsconfig、Renderer、Bun 打包与 DevTools 调试验证
本文以仓库中 .agents/skills/opentui/references/react/configuration.md 配置指南为骨架,完整覆盖 OpenTUI React(@opentui/react)从项目脚手架、TypeScript 与包配置、Renderer 参数,到 Bun 打包分发、环境变量、React DevTools 和测试配置的全部实操内容。Cline 的 CLI 应用正是基于 OpenTUI React 构建的终端界面,读完本文你既能按文档搭建一个标准的 OpenTUI React 项目,也能对照 Cline 仓库的真实配置(apps/cli/tsconfig.json、apps/cli/src/tui/index.tsx、apps/cli/bun.mts)理解每个配置项在生产项目中的实际取值与原因。
项目初始化
使用 create-tui 快速创建
官方脚手架一条命令生成项目:
bunx create-tui@latest -t react my-app
cd my-app && bun install
两点约束需要注意:
- CLI 会替你创建
my-app目录,该目录不能已存在; - 可选参数:
--no-git(跳过 git init)、--no-install(跳过 bun install)。
同系列的 OpenTUI 技能说明 还强调了一条 Agent 使用场景下的规则:create-tui 的选项参数必须放在位置参数之前,即 bunx create-tui -t react my-app 有效,而 bunx create-tui my-app -t react 不生效。
手动搭建
不走脚手架时,手动安装三个依赖即可:
mkdir my-tui && cd my-tui
bun init
bun install @opentui/react @opentui/core react
TypeScript 配置
tsconfig.json 完整示例
{
"compilerOptions": {
"lib": ["ESNext", "DOM"],
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"jsx": "react-jsx",
"jsxImportSource": "@opentui/react",
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"types": ["bun-types"]
},
"include": ["src/**/*"]
}
关键配置项及原因:
| 配置 | 取值 | 作用 |
|---|---|---|
jsx |
"react-jsx" |
使用新版 JSX 转换,无需手动 import React |
jsxImportSource |
"@opentui/react" |
让 JSX runtime 从 OpenTUI 导入,<box>、<text> 等 intrinsic 元素由此获得类型 |
module / moduleResolution |
"NodeNext" |
官方推荐,保证 OpenTUI 兼容性 |
lib |
["ESNext", "DOM"] |
终端环境也需要 DOM lib,因为 React 的类型依赖它,OpenTUI 的 JSX 类型是在 React 类型基础上扩展的 |
Cline 仓库的真实取值
对照 apps/cli/tsconfig.json,Cline CLI 的编译器配置为:
{
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022"],
"jsx": "react-jsx",
"jsxImportSource": "@opentui/react",
"strict": true,
"skipLibCheck": true
}
可以看出两个关键项与指南完全一致:jsx: "react-jsx" 与 jsxImportSource: "@opentui/react",这是 OpenTUI React 项目获得 JSX 类型支持的前提。差异在于 Cline 没有引用 DOM lib(其类型体系通过 workspace 继承和 skipLibCheck 处理),且 moduleResolution 采用 "bundler" 而非 "NodeNext"——这与后文"Module Resolution Errors"一节中针对 Bun 生态建议使用 bundler 的建议相互印证。
包配置
package.json
{
"name": "my-tui-app",
"type": "module",
"scripts": {
"start": "bun run src/index.tsx",
"dev": "bun --watch run src/index.tsx",
"test": "bun test",
"build": "bun build src/index.tsx --outdir=dist --target=bun"
},
"dependencies": {
"@opentui/core": "latest",
"@opentui/react": "latest",
"react": ">=19.0.0"
},
"devDependencies": {
"@types/bun": "latest",
"@types/react": ">=19.0.0",
"typescript": "latest"
}
}
其中 "type": "module" 声明 ESM,dev 脚本用 --watch 提供热重载式开发体验。
作为参考,Cline CLI 在 apps/cli/package.json 中对 OpenTUI 的依赖是精确锁定的:@opentui/core 与 @opentui/react 均为 0.4.3,react 为 19.2.4,@types/react 为 19.2.14,满足"React 19+"的最低要求;另有一个 dev 脚本 CLINE_BUILD_ENV=development bun --conditions=development ./src/index.ts,演示了用构建条件区分开发/生产代码的用法。
项目结构
推荐目录结构:
my-tui-app/
├── src/
│ ├── components/
│ │ ├── Header.tsx
│ │ ├── Sidebar.tsx
│ │ └── MainContent.tsx
│ ├── hooks/
│ │ └── useAppState.ts
│ ├── App.tsx
│ └── index.tsx
├── package.json
└── tsconfig.json
Cline 的 apps/cli/src/tui 目录按同一思路组织:root.tsx、components/、hooks/、themes.ts 等分层清晰,入口则由 index.tsx 统一装配 Renderer 与 React Root。
入口文件 src/index.tsx
import { createCliRenderer } from "@opentui/core"
import { createRoot } from "@opentui/react"
import { App } from "./App"
const renderer = await createCliRenderer({
exitOnCtrlC: true,
})
createRoot(renderer).render(<App />)
模式是固定的两步:先从 @opentui/core 创建 CLI Renderer,再用 @opentui/react 的 createRoot(renderer) 把 React 树挂载到该 Renderer 上。
App 组件 src/App.tsx
import { Header } from "./components/Header"
import { Sidebar } from "./components/Sidebar"
import { MainContent } from "./components/MainContent"
export function App() {
return (
<box flexDirection="column" width="100%" height="100%">
<Header />
<box flexDirection="row" flexGrow={1}>
<Sidebar />
<MainContent />
</box>
</box>
)
}
这里体现了 OpenTUI 的 Flexbox 布局语义:根 <box> 纵向铺满(width="100%" height="100%"),第二层横向排列侧边栏与主内容区,主区用 flexGrow={1} 占满剩余宽度。
Renderer 配置:createCliRenderer 完整选项
import { createCliRenderer, ConsolePosition } from "@opentui/core"
const renderer = await createCliRenderer({
// 渲染
targetFPS: 60,
// 行为
exitOnCtrlC: true, // 设为 false 可自己接管 Ctrl+C
autoFocus: true, // 点击元素时自动聚焦(默认 true)
useMouse: true, // 启用鼠标支持(默认 true)
// 调试控制台
consoleOptions: {
position: ConsolePosition.BOTTOM,
sizePercent: 30,
startInDebugMode: false,
},
// 清理
onDestroy: () => {
// 清理代码
},
})
各选项的语义:
targetFPS:渲染帧率上限,60 为默认目标值;exitOnCtrlC:true时按 Ctrl+C 直接退出进程;false时事件交给你处理,适合需要优雅退出(先销毁 Renderer、恢复 stdio)的场景;autoFocus/useMouse:均默认开启,分别控制点击聚焦与鼠标事件;consoleOptions:内置调试控制台,可配置位置(ConsolePosition)、占屏百分比、是否启动即进入调试模式;onDestroy:Renderer 销毁时的清理回调。
Cline 中的真实用法
apps/cli/src/tui/index.tsx 展示了生产环境如何取舍这些选项:
const renderer = await createCliRenderer({
exitOnCtrlC: false,
autoFocus: false,
enableMouseMovement: true,
});
Cline 选择 exitOnCtrlC: false,自行监听 renderer.on("destroy") 事件来卸载 React Root、恢复 stdio 并解析退出 Promise(见同文件 L55-L73);autoFocus 关闭则是为了让 TUI 自己掌控焦点流转。此外该文件还演示了 renderer.getPalette({ timeout: 150 }) 探测终端默认配色、renderer.setBackgroundColor() 在首帧前铺背景,避免主题闪屏——这些都是在 Renderer 配置层面可做的进阶操作。
构建与分发
用 Bun 打包
// build.ts
await Bun.build({
entrypoints: ["./src/index.tsx"],
outdir: "./dist",
target: "bun",
minify: true,
})
运行 bun run build.ts 得到压缩后的 dist/ 产物,目标运行时为 Bun。
编译为独立可执行文件
// build.ts
await Bun.build({
entrypoints: ["./src/index.tsx"],
outdir: "./dist",
target: "bun",
compile: {
target: "bun-darwin-arm64", // 或 bun-linux-x64 等平台
outfile: "my-app",
},
})
compile.target 指定目标平台(bun-darwin-arm64、bun-linux-x64 等),产物是无需安装 Bun 的单一可执行文件。
Cline 的生产构建脚本
apps/cli/bun.mts 给出了一个更完整的 Bun.build 实战样例。它的几个关键决策值得参考:
const result = await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
target: "node",
format: "esm",
sourcemap,
packages: "bundle",
external: [
// OpenTUI resolves a platform-specific native package at runtime.
// Bundling through that resolution path rewrites the import in a way that
// breaks Linux e2e runs from dist/. Keep React external too so OpenTUI and
// the CLI share one React runtime instead of ending up with duplicate hook
// dispatchers in the bundle.
"@opentui/core",
"@opentui/react",
"@opentui-ui/dialog",
"opentui-spinner",
"react",
"react/jsx-runtime",
"react/jsx-dev-runtime",
"react-devtools-core",
],
// ...
})
源码注释解释了两条重要经验:其一,OpenTUI 在运行时解析平台相关的原生包,若让打包器跟随这条解析链改写 import,会破坏从 dist/ 出发的 Linux e2e 运行,因此 @opentui/core、@opentui/react 必须保持 external;其二,React 也必须外置,否则 bundle 内会出现两份 React 运行时(重复的 hook dispatcher),导致 OpenTUI 与业务代码"各自为政"。这与"React Version Mismatch"一节同属一类问题——TUI 应用里绝不能让两份 React 共存。
环境变量
开发期创建 .env 文件:
# 调试设置
OTUI_SHOW_STATS=false
SHOW_CONSOLE=false
# 应用设置
API_URL=https://api.example.com
Bun 会自动加载 .env 文件,代码内通过 process.env 读取:
const apiUrl = process.env.API_URL
其中 OTUI_SHOW_STATS 控制 OpenTUI 的渲染统计展示,SHOW_CONSOLE 控制调试控制台开关;自定义变量(如 API_URL)则属于应用自身配置。
React DevTools 集成
OpenTUI React 支持接入标准 React DevTools,用于检查组件树:
-
安装 DevTools 核心包作为 dev 依赖(必须使用 7 版):
bun add react-devtools-core@7 -d -
单独启动 DevTools 应用:
npx react-devtools@7 -
以
DEV=true环境变量启动应用:DEV=true bun run src/index.tsx
重要:只有设置 DEV=true 时才会自动连接 DevTools。OpenTUI 在启动时检查 process.env["DEV"] === "true",为真才动态 import("react-devtools-core") 并连接独立运行的 DevTools 应用;未设置该变量时,DevTools 连接代码根本不会加载。
Cline 仓库同样遵循这一约定:apps/cli/package.json 依赖了 react-devtools-core: "^7.0.1",apps/cli/DEVELOPMENT.md 中的开发指引也明确"Run with React DevTools (requires react-devtools-core@7)",并像上文构建脚本一样把它列入 external 以保持单一副本。
测试配置
测试工具封装
// src/test-utils.tsx
import { createTestRenderer } from "@opentui/core/testing"
import { createRoot } from "@opentui/react"
export async function renderForTest(
element: React.ReactElement,
options = { width: 80, height: 24 }
) {
const testSetup = await createTestRenderer(options)
createRoot(testSetup.renderer).render(element)
return testSetup
}
核心是 @opentui/core/testing 导出的 createTestRenderer:它创建一个可离屏渲染的测试 Renderer,默认尺寸 80×24,可自定义宽高以模拟不同终端大小。
测试用例示例
// src/components/Counter.test.tsx
import { test, expect } from "bun:test"
import { renderForTest } from "../test-utils"
import { Counter } from "./Counter"
test("Counter renders initial value", async () => {
const { snapshot } = await renderForTest(<Counter initialValue={5} />)
expect(snapshot()).toContain("Count: 5")
})
模式是"渲染 → snapshot() 取终端帧文本 → 断言文本包含",属于典型的 TUI 快照测试。指南默认使用 bun test 运行;如果你用 Vitest,Cline CLI 的 apps/cli/vitest.config.ts 提供了一个可借鉴的配置形态:environment: "node"、pool: "forks"、串行执行(maxWorkers: 1、fileParallelism: false),以避免 TUI 测试中进程级资源竞争,并用 resolve.alias 把 workspace 包指向源码目录。快照测试与交互测试的完整能力,可参考同技能的 testing 参考。
常见问题排查
JSX 类型不生效
确认 jsxImportSource 已指向 OpenTUI:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@opentui/react"
}
}
缺少该项时,<box> 等 intrinsic 元素会报"属性不存在"一类错误,因为 TS 去的是标准 React JSX 命名空间而非 OpenTUI 的扩展命名空间。
React 版本不匹配
确保 React 19+:
bun install react@19 @types/react@19
@types/react 版本应与 react 主版本对齐,否则类型层面会出现 JSX 命名空间冲突。
模块解析错误
针对 Bun 生态,使用 moduleResolution: "bundler":
{
"compilerOptions": {
"moduleResolution": "bundler"
}
}
这与"关键配置"一节中推荐 NodeNext 的建议并不矛盾:指南默认推荐 NodeNext,而当你遇到 Bun 打包链的解析差异时,bundler 是官方给出的回退方案——Cline 的 apps/cli/tsconfig.json 正是采用了 bundler 这一组合(module: "ESNext" + moduleResolution: "bundler")。
小结
OpenTUI React 项目的配置主线可以归纳为五个层次:脚手架或手动安装三件套(@opentui/react、@opentui/core、react@19+);tsconfig 中 jsx/jsxImportSource 两个关键开关;createCliRenderer 的行为选项(退出策略、焦点、鼠标、调试控制台);Bun 打包时把 OpenTUI 与 React 外置以保证单一运行时;以及 DEV=true 门控的 DevTools 与 createTestRenderer 快照测试。文档中的完整速查可回看 configuration 原文,组件与 Hook 细节见 React API 参考 与 避坑指南。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00