构建终端应用程序的有趣、实用且有状态的方式。基于 Elm 架构的 Go 框架。 Bubble Tea 非常适合简单和复杂的终端应用程序,无论是内联、全窗口还是两者的混合。
Bubble Tea 已在生产中使用,并包含我们在此过程中添加的许多功能和性能优化。其中包括基于标准帧速率的渲染器、与主渲染器一起工作的高性能可滚动区域的渲染器以及鼠标支持。
要开始使用,请参阅下面的教程、示例、文档、视频教程和一些常用资源。
请务必查看 Bubbles,这是 Bubble Tea 的常见 UI 组件库。
Bubble Tea 基于 Elm 架构的功能设计范式,恰好与 Go 配合得很好。这是构建应用程序的一种令人愉快的方式。
本教程假设您具备 Go 的应用知识。
顺便说一句,该程序的未注释源代码可以在 GitHub 上找到。
在本教程中,我们将制作一份购物清单。
首先,我们将定义我们的包并导入一些库。我们唯一的外部导入是 Bubble Tea 库,我们简称为tea
。
package main
import (
"fmt"
"os"
tea "github.com/charmbracelet/bubbletea"
)
Bubble Tea 程序由一个描述应用程序状态的模型和该模型上的三个简单方法组成:
因此,让我们首先定义存储应用程序状态的模型。它可以是任何类型,但struct
通常最有意义。
type model struct {
choices [] string // items on the to-do list
cursor int // which to-do list item our cursor is pointing at
selected map [ int ] struct {} // which to-do items are selected
}
接下来,我们将定义应用程序的初始状态。在本例中,我们定义一个函数来返回初始模型,但是,我们也可以在其他地方轻松地将初始模型定义为变量。
func initialModel () model {
return model {
// Our to-do list is a grocery list
choices : [] string { "Buy carrots" , "Buy celery" , "Buy kohlrabi" },
// A map which indicates which choices are selected. We're using
// the map like a mathematical set. The keys refer to the indexes
// of the `choices` slice, above.
selected : make ( map [ int ] struct {}),
}
}
接下来,我们定义Init
方法。 Init
可以返回一个可以执行一些初始 I/O 的Cmd
。目前,我们不需要执行任何 I/O,因此对于命令,我们只需返回nil
,这意味着“无命令”。
func ( m model ) Init () tea. Cmd {
// Just return `nil`, which means "no I/O right now, please."
return nil
}
接下来是更新方法。当“事情发生”时,更新函数就会被调用。它的工作是查看发生的情况并返回更新的模型作为响应。它还可以返回一个Cmd
来使更多的事情发生,但现在不用担心这部分。
在我们的例子中,当用户按下向下箭头时, Update
的工作是注意到向下箭头被按下并相应地(或不移动)光标。
“发生了什么事”以Msg
的形式出现,可以是任何类型。消息是发生的某些 I/O 的结果,例如按键、计时器滴答或服务器的响应。
我们通常使用类型开关来确定收到的Msg
类型,但您也可以使用类型断言。
现在,我们只处理tea.KeyMsg
消息,当按下按键时,这些消息会自动发送到更新函数。
func ( m model ) Update ( msg tea. Msg ) (tea. Model , tea. Cmd ) {
switch msg := msg .( type ) {
// Is it a key press?
case tea. KeyMsg :
// Cool, what was the actual key pressed?
switch msg . String () {
// These keys should exit the program.
case "ctrl+c" , "q" :
return m , tea . Quit
// The "up" and "k" keys move the cursor up
case "up" , "k" :
if m . cursor > 0 {
m . cursor --
}
// The "down" and "j" keys move the cursor down
case "down" , "j" :
if m . cursor < len ( m . choices ) - 1 {
m . cursor ++
}
// The "enter" key and the spacebar (a literal space) toggle
// the selected state for the item that the cursor is pointing at.
case "enter" , " " :
_ , ok := m . selected [ m . cursor ]
if ok {
delete ( m . selected , m . cursor )
} else {
m . selected [ m . cursor ] = struct {}{}
}
}
}
// Return the updated model to the Bubble Tea runtime for processing.
// Note that we're not returning a command.
return m , nil
}
您可能已经注意到上面的ctrl+c和q返回带有模型的tea.Quit
命令。这是一个特殊的命令,它指示 Bubble Tea 运行时退出,退出程序。
最后,是时候渲染我们的 UI 了。在所有方法中,视图是最简单的。我们查看模型的当前状态并使用它返回一个string
。该字符串就是我们的 UI!
因为视图描述了应用程序的整个 UI,所以您不必担心重绘逻辑之类的事情。珍珠奶茶为您打理一切。
func ( m model ) View () string {
// The header
s := "What should we buy at the market? n n "
// Iterate over our choices
for i , choice := range m . choices {
// Is the cursor pointing at this choice?
cursor := " " // no cursor
if m . cursor == i {
cursor = ">" // cursor!
}
// Is this choice selected?
checked := " " // not selected
if _ , ok := m . selected [ i ]; ok {
checked = "x" // selected!
}
// Render the row
s += fmt . Sprintf ( "%s [%s] %s n " , cursor , checked , choice )
}
// The footer
s += " n Press q to quit. n "
// Send the UI for rendering
return s
}
最后一步是简单地运行我们的程序。我们将初始模型传递给tea.NewProgram
并让它撕裂:
func main () {
p := tea . NewProgram ( initialModel ())
if _ , err := p . Run (); err != nil {
fmt . Printf ( "Alas, there's been an error: %v" , err )
os . Exit ( 1 )
}
}
本教程涵盖了构建交互式终端 UI 的基础知识,但在现实世界中,您还需要执行 I/O。要了解相关内容,请查看命令教程。这很简单。
还有一些可用的 Bubble Tea 示例,当然还有 Go Docs。
由于 Bubble Tea 应用程序控制了 stdin 和 stdout,因此您需要在无头模式下运行 delve,然后连接到它:
# Start the debugger
$ dlv debug --headless --api-version=2 --listen=127.0.0.1:43000 .
API server listening at: 127.0.0.1:43000
# Connect to it from another terminal
$ 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
。
有超过 8k 个使用 Bubble Tea 构建的应用程序!这是其中的一小部分。
有关使用 Bubble Tea 构建的更多应用程序,请参阅 Charm & Friends。您有什么用珍珠奶茶制作的很酷的东西想分享吗?欢迎 PR!
请参阅贡献。
我们很想听听您对此项目的想法。请随时给我们留言!
Bubble Tea 基于 Evan Czaplicki 等人的 The Elm Architecture 范例以及 TJ Holowaychuk 的优秀 go-tea。它的灵感来自过去许多伟大的Zeichenorientierte Benutzerschnittstellen 。
麻省理工学院
魅力的一部分。
Charm 热爱开源 • Charm 热爱开源 • Charm 热爱开源