@bunli/tui

中文文档
0.6.0发布于 2026/03/28MITTypeScriptES Module近 30 天下载 3129

Bunli CLI 框架的终端用户界面插件

中文使用文档
文档目录33 节
  1. 特征
  2. 安装
  3. 快速启动
  4. TUI 执行语义
  5. Render 寿命周期( 运行时退出)
  6. 缓冲模式( 替代对标准)
  7. 使用量
  8. 模块分割
  9. Clack 迁移快速地图
  10. 基本TUI组件
  11. 使用窗体组件
  12. 使用 OpenTUI 钩
  13. 构成部分库
  14. 键盘合同
  15. 对话框管理器
  16. 表单
  17. 形状
  18. 表单字段
  19. 选择字段
  20. 进度栏
  21. 图表初级
  22. 主题提供和托肯斯
  23. OpenTUI 钩
  24. 使用键盘
  25. 使用渲染器
  26. 使用终端数字
  27. 使用时间线
  28. 仅使用回复
  29. 渲染器配置
  30. OpenTUI 组件
  31. 实例
  32. 类型脚本支持
  33. 许可证

@bunli/图伊

一个基于反应的终端用户界面库,用于Bunli CLI框架,由OpenTUI的React渲染器提供动力.

特征

  • 基于反应的组件: 使用熟悉的反应模式和JSX构建 TUIs
  • 构成部分库: 交替缓冲 TUI 的格式、布局、反馈、数据显示和图表组件
  • OpenTUI 整合: 完全访问 OpenTUI 的回放钩和组件
  • 类型安全: 完整类型脚本支持,并有适当的类型推论
  • 动画支持:平滑动画的内置时间系统
  • 键盘处理: 方便键盘事件管理 useKeyboard
  • 第一级别 TUI 支持: TUI 渲染是一个一等特性, 不是插件
  • 主题系统: 预设主题并带有符号覆盖 ThemeProvider编号 :createTheme

安装

bun add @bunli/tui react

快速启动

import { createCLI, defineCommand } from '@bunli/core'

const cli = await createCLI({
  name: 'my-app',
  version: '1.0.0'
})

// Define a command with TUI using the render property
const myCommand = defineCommand({
  name: 'deploy',
  description: 'Deploy application',
  render: () => (
    <box title="Deployment" style={{ border: true, padding: 2 }}>
      <text>Deploying...</text>
    </box>
  ),
  handler: async () => {
    // Non-TUI fallback when render is skipped
    console.log('Deploying application...')
  }
})

cli.command(myCommand)
await cli.run()

Bunli自动接通OpenTUI运行时间. 运行时间/文本 API 现在来自 @bunli/runtime/app,即时/会话API来自 @bunli/runtime/prompt,以及 @bunli/tui 专注于UI组件和钩.

TUI 执行语义

  • 带有 render 在交互式终端中运行.
  • 非交互终端返回到 handler 当在现相.
  • 明确配置全屏流量 bufferMode: 'alternate'。 。 。 .

Render 寿命周期( 运行时退出)

使用的命令 render 最终必须呼叫 useRuntime().exit() (例如,在提交/取消/请求时),或命令不会退出.

import { useRuntime } from '@bunli/runtime/app'
import { useKeyboard } from '@bunli/tui'

function DeployTUI() {
  const runtime = useRuntime()

  useKeyboard((key) => {
    if (key.name === 'q' || (key.ctrl && key.name === 'c')) {
      runtime.exit()
    }
  })

  return <text>Press q to quit</text>
}

缓冲模式( 替代对标准)

OpenTUI可以使用可替换的屏幕缓冲器(全屏TUI)或标准终端缓冲器(以回滚方式输出)来渲染.

配置此内容 createCLI() 配置 :

const cli = await createCLI({
  name: 'my-app',
  version: '1.0.0',
  tui: {
    renderer: {
      bufferMode: 'alternate' // or 'standard'
    }
  }
})

注释:

  • bufferMode: 'standard' 默认值.
  • 使用 bufferMode: 'alternate' 用于全屏/屏蔽体验.
  • 鼠标跟踪默认已禁用( S)useMouse: false),以避免在标准缓冲工作流程中退出后泄露生鼠逃生序列. 需要时明确启用 .

使用量

模块分割

根据模式使用子路径导出 :

  • @bunli/tui/interactive: 交替缓冲交互组件.
  • @bunli/tui/charts:终端-本土图原始.
  • @bunli/tui: root 导出后再出口共享组件/套接字.
  • @bunli/runtime/app:运行时间生命周期/文本 API.
  • @bunli/runtime/prompt: 即时/会话API.

Clack 迁移快速地图

用 Bunli- native API 替换 clack 智能模型时使用此功能 :

// clack
// import { intro, outro, confirm, select, multiselect, log } from '@clack/prompts'

// bunli
// prompt is provided via handler args by Bunli

prompt.intro('Setup')
const confirmed = await prompt.confirm('Continue?', { default: true })
const env = await prompt.select('Environment', {
  options: [
    { label: 'Development', value: 'dev' },
    { label: 'Production', value: 'prod' }
  ]
})
const features = await prompt.multiselect('Features', {
  options: [
    { label: 'Testing', value: 'testing' },
    { label: 'Docker', value: 'docker' }
  ],
  initialValues: ['testing']
})
prompt.log.success(`Selected ${env}`)
prompt.outro('Done')

基本TUI组件

import { defineCommand } from '@bunli/core'

function MyTUI() {
  return (
    <box title="My App" style={{ border: true, padding: 2 }}>
      <text>Hello from My App!</text>
    </box>
  )
}

export const myCommand = defineCommand({
  name: 'my-command',
  description: 'My command with TUI',
  render: () => <MyTUI />,
  handler: async () => {
    console.log('Running my-command in CLI mode')
  }
})

使用窗体组件

import { defineCommand } from '@bunli/core'
import { useRuntime } from '@bunli/runtime/app'
import { SchemaForm } from '@bunli/tui'
import { z } from 'zod'

const configSchema = z.object({
  apiUrl: z.string().url('Enter a valid URL'),
  region: z.enum(['us-east', 'us-west'])
})

function ConfigTUI() {
  const runtime = useRuntime()
  const regions = [
    { label: 'US East', value: 'us-east', hint: 'US East region' },
    { label: 'US West', value: 'us-west', hint: 'US West region' }
  ]

  return (
    <SchemaForm
      title="Configure Settings"
      schema={configSchema}
      fields={[
        {
          kind: 'text',
          name: 'apiUrl',
          label: 'API URL',
          placeholder: 'https://api.example.com',
          required: true
        },
        {
          kind: 'select',
          name: 'region',
          label: 'Region',
          options: regions
        }
      ]}
      onSubmit={(values) => {
        console.log('Validated form values:', values)
      }}
      onCancel={() => runtime.exit()}
    />
  )
}

export const configureCommand = defineCommand({
  name: 'configure',
  description: 'Configure application settings',
  render: () => <ConfigTUI />
})

使用 OpenTUI 钩

import { useRuntime } from '@bunli/runtime/app'
import { useKeyboard, useTimeline, useTerminalDimensions } from '@bunli/tui'

function InteractiveTUI({ command }) {
  const [count, setCount] = useState(0)
  const { width, height } = useTerminalDimensions()
  const runtime = useRuntime()
  
  const timeline = useTimeline({ duration: 2000 })
  
  useKeyboard((key) => {
    if (key.name === 'q') {
      runtime.exit()
    }
    if (key.name === 'space') {
      setCount(prev => prev + 1)
    }
  })
  
  useEffect(() => {
    timeline.add({ count: 0 }, {
      count: 100,
      duration: 2000,
      onUpdate: (anim) => setCount(anim.targets[0].count)
    })
  }, [])
  
  return (
    <box title="Interactive Demo" style={{ border: true, padding: 2 }}>
      <text>Count: {count}</text>
      <text>Terminal: {width}x{height}</text>
      <text>Press SPACE to increment, Q to quit</text>
    </box>
  )
}

构成部分库

交互式组件可从 @bunli/tui/interactive 和根出口.

包含原始人 :

  • 表单 : Form, (中文(简体) ), SchemaForm, (中文(简体) ), FormField, (中文(简体) ), SelectField
  • 表单 v2: NumberField, (中文(简体) ), PasswordField, (中文(简体) ), TextareaField, (中文(简体) ), CheckboxField, (中文(简体) ), MultiSelectField
  • 布局 : Container, (中文(简体) ), Stack, (中文(简体) ), Grid, (中文(简体) ), Panel, (中文(简体) ), Card, (中文(简体) ), Divider, (中文(简体) ), SectionHeader
  • 导航/流程 : Tabs, (中文(简体) ), Menu, (中文(简体) ), CommandPalette, (中文(简体) ), Modal
  • 反馈 : Alert, (中文(简体) ), Badge, (中文(简体) ), Toast, (中文(简体) ), ProgressBar, (中文(简体) ), EmptyState
  • 数据显示 : List, (中文(简体) ), Table, (中文(简体) ), DataTable, (中文(简体) ), KeyValueList, (中文(简体) ), Stat, (中文(简体) ), Markdown, (中文(简体) ), Diff
  • 图: BarChart, (中文(简体) ), LineChart, (中文(简体) ), Sparkline 从 @bunli/tui/charts
  • 运行时管弦乐/吼: 从 @bunli/runtime/app (单位:千美元)DialogProvider, (中文(简体) ), useDialogManager, (中文(简体) ), FocusScopeProvider, (中文(简体) ), useScopedKeyboard页:1

键盘合同

交互式原始文件的默认键盘绑定 :

  • Modal数字 : Esc 编号 : Ctrl+C 关上, Tab编号 :Shift+Tab 焦点陷阱
  • Dialog confirm数字 : Left编号 :h编号 :y - > 确认, Right编号 :l编号 :n - > 取消选择, Tab 切换, Enter 提交
  • Dialog choose数字 : Up编号 :k 先前, Down编号 :j 接下来, Enter 提交。 为选择/导航而跳过已禁用的选项 .
  • Menu数字 : Up编号 :k, (中文(简体) ), Down编号 :j, (中文(简体) ), Enter
  • Tabs数字 : Left编号 :h 上一个标签, Right编号 :l 下一个标签
  • CommandPalette数字 : Up编号 :k, (中文(简体) ), Down编号 :j, (中文(简体) ), Enter
  • DataTable数字 : Left编号 :h 上一个排序列, Right编号 :l 下一个排序列, Up编号 :k 上一行,请, Down编号 :j 下一个行, Enter 选择行
  • Form数字 : Tab编号 :Shift+Tab 实地导航, Ctrl+S 提交, Ctrl+R 重设, F8编号 :Shift+F8 跳转出错字段, Esc 取消( e)

对话框管理器

使用对话框管理器来堆放确认/选择流 , 具有一致的优先处理和解职语义 .

import { useDialogManager, DialogDismissedError } from '@bunli/runtime/app'

function Screen() {
  const dialogs = useDialogManager()

  async function deploy() {
    try {
      const confirmed = await dialogs.confirm({
        title: 'Deploy',
        message: 'Ship this release now?'
      })
      if (!confirmed) return

      const target = await dialogs.choose({
        title: 'Target',
        options: [
          { label: 'Staging', value: 'staging', section: 'General' },
          { label: 'Production', value: 'production', section: 'Protected', disabled: true }
        ]
      })

      console.log('Deploying to', target)
    } catch (error) {
      if (error instanceof DialogDismissedError) {
        console.log('Dialog dismissed')
      }
    }
  }

  return <box />
}

表单

用于受控交互式形式的由计划驱动的容器.

const runtime = useRuntime()

<Form 
  title="My Form"
  schema={schema}
  onSubmit={(values) => console.log(values)}
  onCancel={() => runtime.exit()}
>
  {/* Form fields */}
</Form>

道具 :

  • title: string - 表格标题
  • schema: StandardSchemaV1 - 验证计划(支持Zod和其他标准Schema适配器)
  • onSubmit: (values) => void | Promise<void> - 提交具有计划验证值的处理器
  • onCancel?: () => void - 取消处理器(可选)
  • onValidationError?: (errors: Record<string, string>) => void - 验证错误召回
  • initialValues?: Partial<InferOutput<schema>> - 初始控制值
  • validateOnChange?: boolean - 输入/选择时验证(默认) true(中文(简体) )
  • submitHint?: string - 页脚提示覆盖
  • onReset?: () => void - 重置回调
  • onDirtyChange?: (isDirty, dirtyFields) => void - 肮脏的状态召回
  • onSubmitStateChange?: ({ isSubmitting, isValidating }) => void - 自动调用状态
  • scopeId?: string - 嵌入式交互式流动的键盘范围边界 id

形状

更高层次的造型建构器,使字段来自描述符.

<SchemaForm
  title="Deploy"
  schema={schema}
  fields={[
    { kind: 'text', name: 'service', label: 'Service' },
    { kind: 'select', name: 'env', label: 'Environment', options: envOptions },
    { kind: 'checkbox', name: 'telemetry', label: 'Enable telemetry' },
    {
      kind: 'textarea',
      name: 'notes',
      label: 'Release notes',
      visibleWhen: (values) => values.env === 'production'
    }
  ]}
  onSubmit={(values) => console.log(values)}
/>

支助 SchemaForm 字段类型 :

  • text
  • select
  • multiselect
  • number
  • password
  • textarea
  • checkbox

场级行为 :

  • visibleWhen(values) => boolean 用于有条件渲染.
  • deriveDefault(values) => unknown 用于依赖性默认初始化.

表单字段

受控文本字段必须形成上下文 .

<FormField
  label="Username"
  name="username"
  placeholder="Enter username"
  required
  defaultValue=""
/>

道具 :

  • label: string - 实地标签
  • name: string - 字段名称
  • placeholder?: string - 占位符文本
  • required?: boolean - 是否需要字段
  • description?: string - 帮助文本
  • defaultValue?: string - 形式状态初始值
  • onChange?: (value: string) => void - 更改处理器
  • onSubmit?: (value: string) => void - 提交处理器

选择字段

受控的选定字段将形成上下文 .

<SelectField
  label="Environment"
  name="env"
  options={[
    { label: 'Development', value: 'dev', hint: 'Development environment' },
    { label: 'Production', value: 'prod', hint: 'Production environment' }
  ]}
  defaultValue="dev"
  onChange={setEnvironment}
/>

道具 :

  • label: string - 实地标签
  • name: string - 字段名称
  • options: SelectOption[] - 可用的选项
  • required?: boolean - 是否需要字段
  • description?: string - 帮助文本
  • defaultValue?: SelectOption['value'] - 初始选定值
  • onChange?: (value: SelectOption['value']) => void - 更改处理器

进度栏

显示完成状态的进度栏组件 .

<ProgressBar 
  value={75} 
  label="Upload Progress" 
  color="#00ff00" 
/>

道具 :

  • value: number - 进度值(0-100)
  • label?: string - 进度标签
  • color?: string - 进步栏颜色

图表初级

@bunli/tui/charts 支持负和稀有值,轴标签,以及多序列调色板.

import { BarChart, LineChart } from '@bunli/tui/charts'

<BarChart
  series={[
    { name: 'build', points: [{ label: 'Mon', value: -2 }, { label: 'Tue', value: 6 }] },
    { name: 'test', points: [{ label: 'Mon', value: null }, { label: 'Tue', value: 4 }] }
  ]}
  axis={{ yLabel: 'Jobs', xLabel: 'Day', showRange: true }}
/>

<LineChart
  series={{ name: 'latency', points: [{ value: 120 }, { value: 98 }, { value: null }, { value: 104 }] }}
  axis={{ yLabel: 'ms' }}
/>

主题提供和托肯斯

使用 ThemeProvider 以应用内置主题预设或符号覆盖.

import { ThemeProvider, createTheme } from '@bunli/tui/interactive'

const customTheme = createTheme({
  preset: 'dark',
  tokens: {
    accent: '#3ec7ff',
    textSuccess: '#3cd89b'
  }
})

function App() {
  return (
    <ThemeProvider theme={customTheme}>
      <Panel title="Deploy status">
        <Alert tone="success" message="Ready to ship" />
      </Panel>
    </ThemeProvider>
  )
}

OpenTUI 钩

套件再导出有用的 OpenTUI React hooks :

使用键盘

处理键盘事件.

import { useRuntime } from '@bunli/runtime/app'
import { useKeyboard } from '@bunli/tui'

const runtime = useRuntime()

useKeyboard((key) => {
  if (key.name === 'escape') {
    runtime.exit()
  }
})

使用渲染器

访问 OpenTUI 渲染器实例 .

import { useRenderer } from '@bunli/tui'

const renderer = useRenderer()
renderer.console.show()

使用 useRenderer() 仅限高级出纳器检查/控制。 使用 useRuntime().exit() 用于完成正常命令.

使用终端数字

获取当前终端维度 .

import { useTerminalDimensions } from '@bunli/tui'

const { width, height } = useTerminalDimensions()

使用时间线

创建和管理动画.

import { useTimeline } from '@bunli/tui'

const timeline = useTimeline({ duration: 2000 })

timeline.add({ x: 0 }, {
  x: 100,
  duration: 2000,
  onUpdate: (anim) => setX(anim.targets[0].x)
})

仅使用回复

处理终端调整事件大小 .

import { useOnResize } from '@bunli/tui'

useOnResize((width, height) => {
  console.log(`Terminal resized to ${width}x${height}`)
})

渲染器配置

通过 createCLI({ tui: { renderer } }) 和可选命令级别覆盖 command.tui.renderer。 。 。 .

OpenTUI 组件

您可以直接使用任何 OpenTUI 反应组件 :

import { render } from '@opentui/react'

function MyComponent() {
  return (
    <box style={{ border: true, padding: 2 }}>
      <text>Hello World</text>
      <input placeholder="Type here..." />
      <select options={options} />
    </box>
  )
}

可用组件 :

  • <box> - 有边界和布局的集装箱
  • <text> - 有样式的文本显示
  • <input> - 文本输入字段
  • <select> - 下拉选择
  • <scrollbox> - 可滚动容器
  • <ascii-font> - ASCII 艺术文本
  • <tab-select> - 基于标签的选择

实例

见 examples/tui-demo 完整示例目录 :

  • 部署指挥: 有时间表的动画进度栏
  • 配置命令: 带有输入和选择字段的窗体

类型脚本支持

软件包提供了完整的TypeScript支持:

import type { TuiComponent, TuiComponentProps } from '@bunli/tui'

const MyTUI: TuiComponent = ({ command, args, store }) => {
  // Fully typed props
  return <box>{command.name}</box>
}

许可证

麻省理工学院

版本与发布记录

@bunli/tui 共发布 15 个版本,首次发布于 2025/10/24;过去 12 个月发布了 14 个版本。

版本发布日期距今
0.6.0latest2026/03/287 个月前
0.5.32026/03/088 个月前
0.5.22026/03/058 个月前
0.5.12026/03/058 个月前
0.5.02026/03/048 个月前
0.4.22026/03/018 个月前

关于 @bunli/tui 的常见问题

@bunli/tui 是什么?

Bunli CLI 框架的终端用户界面插件

如何安装 @bunli/tui?

在项目目录运行 npm install @bunli/tui,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@opentui/core(^0.1.90)、react(^19.2.4)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@bunli/tui 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@bunli/tui 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@bunli/tui 可以商用吗?

@bunli/tui 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@bunli/tui 的下载量有多少?

根据 npm 官方下载统计,@bunli/tui 在 2026/10/09 – 2026/11/07 这 30 天内被下载 3,129 次,日均约 104 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bunli/tui 还在维护吗?

最新版本 0.6.0 发布于 2026/03/28(7 个月前);至今共发布 15 个版本,其中过去 12 个月发布了 14 个;首次发布于 2025/10/24。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。