泡泡茶
一种有趣、实用且具有状态的构建终端应用程序的方式。这是一个基于Elm架构的Go框架。泡泡茶非常适合构建简单和复杂的终端应用程序,无论是内联、全窗口还是两者的混合。
泡泡茶已在生产环境中使用,并包含了我们在开发过程中添加的许多功能和性能优化。其中包括基于标准帧率的渲染器、与主渲染器一起工作的高性能可滚动区域渲染器以及鼠标支持。
要开始使用,请参阅下面的教程、[示例][examples]、[文档][docs]、视频教程以及一些常见的资源。
顺便说一下
请务必查看Bubbles,这是一个用于泡泡茶的常用UI组件库。
教程
泡泡茶基于Elm架构的函数式设计范式,这与Go语言非常契合。这是一种令人愉悦的构建应用程序的方式。
本教程假设您已经具备Go语言的工作知识。
顺便说一下,这个程序的未注释源代码可在GitHub上获得。
够了!让我们开始吧。
在本教程中,我们将制作一个购物清单。
首先,我们定义包并导入一些库。我们唯一的外部导入将是泡泡茶库,我们将其简称为tea
。
package main
import (
"fmt"
"os"
tea "github.com/charmbracelet/bubbletea"
)
泡泡茶程序由描述应用程序状态的模型和该模型上的三个简单方法组成:
- Init,一个返回应用程序初始命令的函数。
- Update,一个处理传入事件并相应更新模型的函数。
- View,一个根据模型中的数据渲染UI的函数。
模型
让我们从定义我们的模型开始,它将存储我们应用程序的状态。它可以是任何类型,但通常结构体最有意义。
type model struct {
choices []string // 待办事项列表中的项目
cursor int // 光标指向的待办事项
selected map[int]struct{} // 被选中的待办事项
}
初始化
接下来,我们将定义应用程序的初始状态。在这个例子中,我们定义了一个函数来返回我们的初始模型,但我们也可以在其他地方将初始模型定义为变量。
func initialModel() model {
return model{
// 我们的待办事项列表是一个购物清单
choices: []string{"买胡萝卜", "买芹菜", "买大头菜"},
// 一个表示哪些选项被选中的映射。我们将这个映射用作数学集合。
// 键指的是上面`choices`切片的索引。
selected: make(map[int]struct{}),
}
}
接下来,我们定义Init
方法。Init
可以返回一个Cmd
来执行一些初始I/O操作。现在,我们不需要进行任何I/O操作,所以对于命令,我们只返回nil
,这意味着"现在不需要I/O操作"。
func (m model) Init() tea.Cmd {
// 只返回`nil`,意味着"现在不需要I/O操作,谢谢"。
return nil
}
Update方法
接下来是update方法。当"有事情发生"时,update函数会被调用。它的工作是查看发生了什么,并相应地返回一个更新后的模型。它还可以返回一个Cmd
来触发更多事件,但现在不用担心这部分。
在我们的例子中,当用户按下向下箭头键时,Update
的工作是注意到向下箭头被按下,并相应地移动光标(或不移动)。
"发生的事情"以Msg
的形式出现,可以是任何类型。消息是某些I/O操作的结果,比如按键、定时器滴答或服务器响应。
我们通常使用类型switch来确定收到了哪种类型的Msg
,但您也可以使用类型断言。
现在,我们只处理tea.KeyMsg
消息,当按下键时,这些消息会自动发送到update函数。
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
// 是按键操作吗?
case tea.KeyMsg:
// 好的,实际按下的是哪个键?
switch msg.String() {
// 这些键应该退出程序。
case "ctrl+c", "q":
return m, tea.Quit
// "上"和"k"键向上移动光标
case "up", "k":
if m.cursor > 0 {
m.cursor--
}
// "下"和"j"键向下移动光标
case "down", "j":
if m.cursor < len(m.choices)-1 {
m.cursor++
}
// "回车"键和空格键(字面上的空格)切换
// 光标所指项目的选中状态。
case "enter", " ":
_, ok := m.selected[m.cursor]
if ok {
delete(m.selected, m.cursor)
} else {
m.selected[m.cursor] = struct{}{}
}
}
}
// 将更新后的模型返回给泡泡茶运行时进行处理。
// 注意我们没有返回命令。
return m, nil
}
您可能注意到上面的ctrl+c和q与模型一起返回了一个tea.Quit
命令。这是一个特殊命令,它指示泡泡茶运行时退出,结束程序。
View方法
最后,是时候渲染我们的UI了。在所有方法中,视图是最简单的。我们查看模型的当前状态,并使用它返回一个string
。那个字符串就是我们的UI!
因为视图描述了应用程序的整个UI,所以您不必担心重绘逻辑之类的东西。泡泡茶会为您处理这些。
func (m model) View() string {
// 标题
s := "我们应该在市场上买什么?\n\n"
// 遍历我们的选项
for i, choice := range m.choices {
// 光标是否指向这个选项?
cursor := " " // 无光标
if m.cursor == i {
cursor = ">" // 有光标!
}
// 这个选项是否被选中?
checked := " " // 未选中
if _, ok := m.selected[i]; ok {
checked = "x" // 已选中!
}
// 渲染行
s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
}
// 页脚
s += "\n按q退出。\n"
// 发送UI进行渲染
return s
}
现在把它们放在一起
最后一步是简单地运行我们的程序。我们将初始模型传递给tea.NewProgram
并让它运行:
func main() {
p := tea.NewProgram(initialModel())
if _, err := p.Run(); err != nil {
fmt.Printf("哎呀,出错了:%v", err)
os.Exit(1)
}
}
接下来是什么?
本教程介绍了构建交互式终端UI的基础知识,但在实际应用中,您还需要执行I/O操作。要了解这方面的内容,请查看[命令教程][cmd]。这很简单。
还有几个[泡泡茶示例][examples]可供参考,当然,还有[Go文档][docs]。 [cmd]: http://github.com/charmbracelet/bubbletea/tree/master/tutorials/commands/ [examples]: http://github.com/charmbracelet/bubbletea/tree/master/examples [docs]: https://pkg.go.dev/github.com/charmbracelet/bubbletea?tab=doc
调试
使用 Delve 进行调试
由于 Bubble Tea 应用会接管标准输入和标准输出,你需要在无头模式下运行 delve,然后连接到它:
# 启动调试器
$ dlv debug --headless --api-version=2 --listen=127.0.0.1:43000 .
API 服务器监听于:127.0.0.1:43000
# 从另一个终端连接到它
$ dlv connect 127.0.0.1:43000
如果你没有明确提供 --listen
标志,每次运行时使用的端口都会变化,所以传入这个参数可以让调试器更容易从脚本或你选择的 IDE 中使用。
另外,我们传入 --api-version=2
,因为 delve 出于向后兼容的原因默认使用版本 1。然而,delve 建议所有新的开发都使用版本 2,而且某些客户端可能不再支持版本 1。
更多信息,请参阅 Delve 文档。
记录日志
你不能真的在 Bubble Tea 中将日志记录到标准输出,因为你的 TUI 正忙于占用它!但是,你可以通过在启动 Bubble Tea 程序之前包含类似以下内容来将日志记录到文件:
if len(os.Getenv("DEBUG")) > 0 {
f, err := tea.LogToFile("debug.log", "debug")
if err != nil {
fmt.Println("fatal:", err)
os.Exit(1)
}
defer f.Close()
}
要实时查看正在记录的内容,在运行程序的同时,在另一个窗口中运行 tail -f debug.log
。
我们与 Bubble Tea 一起使用的库
- Bubbles:常用的 Bubble Tea 组件,如文本输入、视口、加载动画等
- Lip Gloss:终端应用的样式、格式和布局工具
- Harmonica:一个弹簧动画库,用于平滑、自然的动作
- BubbleZone:Bubble Tea 组件的简易鼠标事件跟踪
- ntcharts:为 Bubble Tea 和 Lip Gloss 构建的终端图表库
- Termenv:终端应用的高级 ANSI 样式
- Reflow:用于处理文本的高级 ANSI 感知方法
实际应用中的 Bubble Tea
要查看一些生产环境中的 Bubble Tea 程序,请参阅:
- ASCII Movie:一个星球大战ASCII艺术电影播放器
- AT CLI:通过串口连接执行AT命令
- Aztify:将Microsoft Azure资源纳入Terraform管理
- brows:GitHub发布版本浏览器
- Canard:一个RSS客户端
- charm:官方Charm用户账户管理器
- chatgpt-cli:ChatGPT的命令行界面
- chatgpt-tui:带有SQLite会话的ChatGPT文本用户界面
- ChatGPTUI:ChatGPT的文本用户界面
- chezmoi:安全地跨多台机器管理你的配置文件
- chip-8:一个CHIP-8解释器
- chtop:无需离开终端即可监控ClickHouse节点
- circumflex:在终端阅读Hacker News
- clidle:Wordle游戏的克隆版
- cLive:自动化终端操作并在浏览器中实时查看
- container-canary:容器验证器
- countdown:多事件倒计时器
- CRT:一个简单的终端模拟器,用于在专用窗口中运行Bubble Tea,可选择使用着色器
- cueitup:以简单和有意的方式检查AWS SQS队列中的消息
- Daytona:开发环境管理器
- dns53:使用Amazon Route53的动态DNS;快速、安全且私密地公开你的EC2
- eks-node-viewer:用于可视化EKS集群内动态节点使用情况的工具
- End Of Eden:一款类似"杀戮尖塔"的roguelike卡牌构建游戏
- enola:通过用户名在各社交网络中查找社交媒体账号
- flapioca:命令行版Flappy Bird游戏!
- fm:基于终端的文件管理器
- fork-cleaner:清理GitHub账户中的旧的和不活跃的分支
- fractals-cli:多平台终端分形探索器
- fztea:Flipper Zero文本用户界面
- gama:从终端管理GitHub Actions
- gambit:终端中的国际象棋
- gembro:一个鼠标驱动的Gemini浏览器
- gh-b:用于管理分支的GitHub CLI扩展
- gh-dash:用于PR和问题的GitHub CLI扩展
- gitflow-toolkit:GitFlow提交工具
- Glow:Markdown阅读器、浏览器和在线Markdown存储
- go-sweep:终端版扫雷游戏
- gocovsh:从命令行探索Go覆盖率报告
- got:基于simplytranslate API构建的简单翻译和文本转语音应用
- gum:为shell和shell脚本提供交互性和样式
- hiSHtory:上下文化的shell历史记录:同步且可查询
- httpit:快速http(s)基准测试工具
- Huh?:交互式提示和表单工具包
- IDNT:批量软件卸载器
- json-log-viewer:交互式JSON日志查看器
- kboard:打字游戏
- kplay:检查Kafka主题中的消息
- laboon:类Docker桌面风格的容器管理器
- mc:官方MinIO客户端
- mergestat:在git仓库上运行SQL查询
- meteor:高度可定制的常规提交消息工具
- mods:命令行上的AI,为管道设计
- nachrichten:访问Tagesschau提供的最新德语新闻
- Neon Modem Overdrive:用于Discourse、Lemmy、Lobste.rs和Hacker News的BBS风格TUI客户端
- nom:RSS阅读器和管理器
- Noted:笔记查看器和管理器
- outtasync:识别与模板文件不同步的CloudFormation堆栈
- pathos:PATH环境变量编辑器
- Plandex:用于复杂任务的基于终端的AI编码引擎
- portal:计算机之间的安全传输
- prs:跟踪你的PR更新
- puffin:用于管理财务的hledger TUI
- pug:terraform任务管理器
- punchout:简化JIRA上的时间记录
- redis-viewer:Redis数据库浏览器
- redis_tui:Redis数据库浏览器
- schemas:让你在终端中检查postgres模式
- scrabbler:拼字游戏自动抽取工具
- sku:命令行版数独游戏
- Slides:基于Markdown的演示工具
- SlurmCommander:Slurm工作负载管理器
- Soft Serve:一个命令行优先的Git服务器,通过SSH运行TUI
- solitaire-tui:终端版纸牌游戏
- StormForge Optimize Controller:用于在Kubernetes中试验应用程序配置的工具
- Storydb:改进的bash/zsh风格ctrl+r命令历史查找器
- STTG:瑞典国家公共电视台SVT的图文电视客户端
- sttr:通用文本转换器
- superfile:一个精美、现代的基于终端的文件管理器
- tasktimer:简单易用的任务计时器
- termdbms:键盘和鼠标驱动的数据库浏览器
- tgpt:命令行对话式AI;无需API密钥
- ticker:终端股票查看器和股票头寸跟踪器
- trainer:带学习材料的Go并发编程面试模拟器
- tran:在计算机之间安全传输内容(基于portal)
- Trufflehog:查找泄露的凭证
- Typer:打字测试
- typioca:打字测试
- tz:跨多个时区的日程安排辅助工具
- ugm:unix用户和组浏览器
- walk:终端导航器
- wander:HashiCorp Nomad终端客户端
- WG Commander:简单WireGuard VPN设置的TUI
- wishlist:一个SSH目录
反馈
我们很乐意听到您对这个项目的看法。欢迎给我们留言!
致谢
Bubble Tea 基于 Evan Czaplicki 等人的 The Elm Architecture 范式和 TJ Holowaychuk 的优秀作品 go-tea。它受到过去许多伟大的字符界面的启发。
许可证
Charm 的一部分。
Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة