首页
/ Cline CLI 的 OpenTUI 文本显示组件:text、ascii-font 与 RGBA 颜色体系实战指南

Cline CLI 的 OpenTUI 文本显示组件:text、ascii-font 与 RGBA 颜色体系实战指南

2026-09-05 16:35:41作者:尤辰城Agatha

Cline 的 CLI(TUI)界面基于 OpenTUI 构建,其中承载所有可见信息的正是文本显示组件体系:text、内联修饰标签(span/strong/em 等)以及 ascii-font 大标题横幅。本文基于仓库内技能参考文档 text-display.md 完整展开,并结合 apps/cli 中真实组件代码,讲清这三种 API 范式(Core 命令式、React、Solid)下如何正确编写样式文本、使用 t 模板与 RGBA 颜色对象,以及如何避开“样式不生效”等典型陷阱。读完你能够独立为终端界面编写带颜色、富文本与动态内容的显示层。

一、背景:Cline CLI 中的 OpenTUI 版本与文本组件定位

Cline CLI 通过以下依赖引入 OpenTUI(见 根 package.jsonapps/cli/package.json):

  • @opentui/core 0.4.3 —— 命令式核心渲染 API;
  • @opentui/react 0.4.3 —— React 调和器(reconciler),Cline CLI 的 TUI 主体即采用此范式;
  • @opentui-ui/dialog 0.1.2(仓库在 patches/ 中对其打了本地补丁)。

在该技能体系里,OpenTUI 提供了三种编写 TUI 的路径(参见 SKILL.md 的决策树):Core 适合需要完全控制与极致性能的场景,React 适合熟悉组件化开发的人,Solid 适合需要细粒度响应式的场景。文本显示文档正是“我需要展示内容”这一分支下的第一站:纯文本或带样式文本走 text,ASCII 艺术横幅走 ascii-font

二、Text 组件基础用法

text 是显示文本的主力组件,三种范式各有一套写法。

React/Solid(声明式):

// React/Solid
<text>Hello, World!</text>

// 用 content prop 传内容
<text content="Hello, World!" />

Core(命令式):

const text = new TextRenderable(renderer, {
  id: "greeting",
  content: "Hello, World!",
})

在 Cline CLI 源码中可以找到与上述完全一致的用法,例如 chat-entry.tsx 中的 <text fg="red">x</text><text fg={planAccent} content="* " />——属性与子节点两种传内容方式在真实业务组件里混用。

三、React/Solid 样式化:嵌套修饰标签(最重要的规则)

这是文档中最容易被违反的一条规则:React/Solid 中不要给 <text> 直接传 bolditalicunderlinedimstrikethrough 属性——它们不生效。必须使用嵌套的修饰标签:

<text fg="#FFFFFF" bg="#000000">
  <strong>Bold</strong>, <em>italic</em>, and <u>underlined</u>
</text>

可用的内联修饰元素完整清单如下,它们只能出现在 <text> 内部

元素 作用 示例
<span fg="..." /> 内联着色片段 Normal text with <span fg="red">red text</span> inline
<strong> / <b> 加粗 <strong>Bold text</strong>
<em> / <i> 斜体 <em>Italic text</em>
<u> 下划线 <u>Underlined text</u>
<br /> 换行 Line one<br />Line two
<a href="..." /> 链接 Visit <a href="https://example.com">our website</a>

修饰符可以任意组合嵌套,例如:

<text>
  <span fg="#00FF00">
    <strong>Bold green</strong>
  </span>
  and
  <span fg="#FF0000">
    <em><u>italic underlined red</u></em>
  </span>
</text>

仓库佐证chat-entry.tsx 展示了真实的组合用法——用 <span fg={accent}><strong>{toolName}</strong></span> 把工具名以主题强调色加粗渲染,用 <em>Thinking...</em> 表现推理流式状态;autocomplete-dropdown.tsx 则展示了条件颜色的典型写法:

<span fg={isSelected ? theme.textOnSelection : "gray"}>{prefix}</span>

即颜色属性既接受字符串("red""#FF0000"),也接受运行时计算出的变量或 RGBA 对象。

四、Core 范式的文本属性:TextAttributes

不走声明式标签时,Core API 通过位掩码常量设置文本属性:

import { TextRenderable, TextAttributes } from "@opentui/core"

const text = new TextRenderable(renderer, {
  content: "Styled",
  attributes: TextAttributes.BOLD | TextAttributes.UNDERLINE,
})

可用属性共 8 个,可用位运算叠加:

  • TextAttributes.BOLDTextAttributes.DIMTextAttributes.ITALICTextAttributes.UNDERLINE
  • TextAttributes.BLINKTextAttributes.INVERSETextAttributes.HIDDENTextAttributes.STRIKETHROUGH

五、文本选中与复制

selectable 属性控制用户能否框选文本(配合终端 OSC 52 剪贴板能力):

<text selectable>
  This text can be selected by the user
</text>

<text selectable={false}>
  This text cannot be selected
</text>

完整的选区与 copy-on-selection API 在键盘参考文档 keyboard/REFERENCE.md 中(selection 一节)。Cline CLI 在展示助手回答、ClinePass 权益列表等需要用户复制的场景中,大量使用了 <text fg={...} selectable> 形式,例如 chat-entry.tsx 中链接文本的 <text fg={linkColor} selectable>

六、Core 的样式化模板:t 模板字符串

对复杂排版,Core 提供 t 模板字面量与一组样式函数,返回的字符串可直接赋给 content

import { t, bold, italic, underline, fg, bg, dim } from "@opentui/core"

const styled = t`
  ${bold("Bold")} and ${italic("italic")} text.
  ${fg("#FF0000")("Red text")} with ${bg("#0000FF")("blue background")}.
  ${dim("Dimmed")} and ${underline("underlined")}.
`

const text = new TextRenderable(renderer, {
  content: styled,
})

样式函数一览:

函数 说明
bold(text) 加粗
italic(text) 斜体
underline(text) 下划线
dim(text) 变暗
strikethrough(text) 删除线
fg(color)(text) 设置前景色(柯里化,先传色再传文本)
bg(color)(text) 设置背景色

七、ascii-font 组件:ASCII 大标题横幅

用于在终端展示大型 ASCII 艺术字,适合做启动横幅、页面标题:

// React
<ascii-font text="TITLE" font="tiny" />

// Solid(注意 kebab-case 组件名差异)
<ascii_font text="TITLE" font="tiny" />

// Core
const title = new ASCIIFontRenderable(renderer, {
  id: "title",
  text: "TITLE",
  font: "tiny",
})

支持 4 种字体:

字体 风格
tiny 紧凑 ASCII 字体
block 块状字母
slick 现代流线风格
shade 带阴影的 3D 效果

着色方式与 text 相同——React 直接传颜色字符串,Core 传 RGBA 对象:

// React
<ascii-font
  text="HELLO"
  font="block"
  color="#00FF00"
/>

// Core
import { RGBA } from "@opentui/core"

const title = new ASCIIFontRenderable(renderer, {
  text: "HELLO",
  font: "block",
  color: RGBA.fromHex("#00FF00"),
})

文档给出的渲染效果示意(tiny 与 block 两种字体渲染 "HELLO"):

Font: tiny
╭─╮╭─╮╭─╮╭╮╭╮╭─╮╶╮╶ ╶╮
│ ││─┘├┤ │╰╯││  │  │
╰─╯╵  ╰─╯╵  ╵╰─╯╶╯╶╰─╯

Font: block
█▀▀█ █▀▀█ █▀▀ █▀▀▄
█  █ █▀▀▀ █▀▀ █  █
▀▀▀▀ ▀    ▀▀▀ ▀  ▀

八、颜色体系:字符串格式与 RGBA 类

颜色字符串格式(用于 JSX 属性):

// 十六进制
<text fg="#FF0000">Red</text>
<text fg="#F00">Short hex</text>

// 命名色
<text fg="red">Red</text>
<text fg="blue">Blue</text>

// 透明背景
<text bg="transparent">No background</text>

注意十六进制必须带 #fg="FF0000" 是错误写法。

RGBA 类@opentui/core 导出的 RGBA 可以在所有框架(Core、React、Solid)中用于程序化颜色运算,三种构造方式对应不同数据来源:

import { RGBA } from "@opentui/core"

// 从十六进制字符串(最常用)
const red = RGBA.fromHex("#FF0000")
const shortHex = RGBA.fromHex("#F00")      // 支持短形式

// 从 0-255 整数(r, g, b, a)
const green = RGBA.fromInts(0, 255, 0, 255)
const semiGreen = RGBA.fromInts(0, 255, 0, 128)   // 50% 透明

// 从 0.0-1.0 归一化浮点
const blue = RGBA.fromValues(0.0, 0.0, 1.0, 1.0)
const overlay = RGBA.fromValues(0.1, 0.1, 0.1, 0.7) // 半透明深色

选择依据:对接设计规范/CSS 色值用 fromHex();持有 8 位通道值用 fromInts();做颜色插值与运算用 fromValues()。在 React/Solid 中,几乎所有接受颜色字符串的属性都直接接受 RGBA 对象:

import { RGBA } from "@opentui/core"

const primaryColor = RGBA.fromHex("#7aa2f7")

function MyComponent() {
  return (
    <box backgroundColor={primaryColor} borderColor={primaryColor}>
      <text fg={RGBA.fromHex("#c0caf5")}>Styled with RGBA</text>
    </box>
  )
}

仓库佐证:Cline CLI 的主题系统正是这条链路的生产级实践。themes.ts 定义了多套主题(各自的 accents 强调色与 syntax 语法调色板),而 syntax-style.ts 将主题中的十六进制色值统一经 RGBA.fromHex 转换为 RGBA 对象,再包装成 { fg, bold }{ fg, italic }StyleDefinition,最终通过 SyntaxStyle.fromStyles(...) 构建代码高亮样式——从文档的 RGBA.fromHex("#...") 到仓库中 color(hex) { return RGBA.fromHex(hex) } 的映射一目了然。

九、文本换行与动态内容

换行由父容器决定:文本在触达父级容器宽度时自动折行:

<box width={40}>
  <text>
    This long text will wrap when it reaches the edge of the
    40-character wide parent container.
  </text>
</box>

动态内容按范式区分更新方式:

// React:状态驱动
function Counter() {
  const [count, setCount] = useState(0)
  return <text>Count: {count}</text>
}

// Solid:信号驱动
function Counter() {
  const [count, setCount] = createSignal(0)
  return <text>Count: {count()}</text>
}
// Core:命令式更新
const text = new TextRenderable(renderer, {
  id: "counter",
  content: "Count: 0",
})

// 之后任意时刻更新
text.setContent("Count: 1")

十、常见陷阱(Gotchas)

文档专门列出三类高频错误,逐一给出对照写法:

1. 修饰标签放到 <text> 之外不生效——<strong><em> 等只能在 <text> 内部解析:

// WRONG - modifiers only work inside <text>
<box>
  <strong>Won't work</strong>
</box>

// CORRECT
<box>
  <text>
    <strong>This works</strong>
  </text>
</box>

2. 空文本可能引发布局问题——用空格占位或条件渲染兜底:

// May cause layout issues
<text></text>

// Better - use space or conditional
<text>{content || " "}</text>

3. 十六进制颜色漏写 #

// WRONG
<text fg="FF0000">Missing #</text>

// CORRECT
<text fg="#FF0000">With #</text>

另外,SKILL.md 的 Troubleshooting Index 也把“文本样式不生效(Text styling not applying)”直接指向本文档,可见它是 TUI 排障的第一参考。

十一、延伸阅读与相关路径

围绕本文主题,仓库内可直接深入的路径:

综上,OpenTUI 的文本显示层规则可以浓缩为三句话:React/Solid 用嵌套标签做样式、Core 用 TextAttributest 模板做样式、颜色统一经字符串或 RGBA 对象传递;Cline CLI 的主题系统与消息渲染组件是这套规则在真实项目中的完整落地样本。

登录后查看全文
热门项目推荐
相关项目推荐