Charm 终端 UI 库¶
为什么要学 Charm¶
Charm 是一个用 Go 编写的终端 UI 生态系统,包含多个库和工具:Bubble Tea(TUI 框架)、Lip Gloss(样式)、Bubbles(组件)等。它让你能够用 Go 语言构建美观的终端应用。Charm 生态是现代终端工具(Glow、VHS、Slides 等)的基石,学习它可以构建自己的专业级终端工具。
核心概念¶
| 概念 | 白话解释 | 用途 |
|---|---|---|
| Bubble Tea | TUI 框架 | 基于 Elm 架构的终端 UI 框架 |
| Lip Gloss | 样式引擎 | 终端文本的 CSS 式样式 |
| Bubbles | 组件库 | 预构建的 UI 组件(输入框、列表等) |
| Huh | 表单库 | 构建终端交互式表单 |
| Log | 日志库 | 美化的终端日志输出 |
| Wish | SSH 应用 | 通过 SSH 服务 TUI 应用 |
安装配置¶
# 创建 Go 项目
mkdir my-tui && cd my-tui
go mod init my-tui
# 安装核心库
go get github.com/charmbracelet/bubbletea
go get github.com/charmbracelet/lipgloss
go get github.com/charmbracelet/bubbles
go get github.com/charmbracelet/huh
go get github.com/charmbracelet/log
快速上手¶
Lip Gloss 样式¶
package main
import (
"fmt"
"github.com/charmbracelet/lipgloss"
)
func main() {
// 定义样式
style := lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#FAFAFA")).
Background(lipgloss.Color("#7D56F4")).
PaddingTop(1).
PaddingLeft(4).
PaddingRight(4).
PaddingBottom(1).
MarginTop(1).
Width(40).
Align(lipgloss.Center).
BorderStyle(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("#874BFD"))
fmt.Println(style.Render("Hello, Charm! 🎉"))
// 组合布局
left := lipgloss.NewStyle().Width(20).Align(lipgloss.Left).Render("Left")
right := lipgloss.NewStyle().Width(20).Align(lipgloss.Right).Render("Right")
row := lipgloss.JoinHorizontal(lipgloss.Top, left, right)
fmt.Println(row)
}
Bubble Tea 应用¶
package main
import (
"fmt"
tea "github.com/charmbracelet/bubbletea"
)
// Model
type model struct {
choices []string
cursor int
selected map[int]struct{}
}
func initialModel() model {
return model{
choices: []string{"Python", "Go", "Rust", "JavaScript"},
selected: make(map[int]struct{}),
}
}
// Init
func (m model) Init() tea.Cmd { return nil }
// Update
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "q", "ctrl+c":
return m, tea.Quit
case "up", "k":
if m.cursor > 0 { m.cursor-- }
case "down", "j":
if m.cursor < len(m.choices)-1 { m.cursor++ }
case " ", "enter":
if _, ok := m.selected[m.cursor]; ok {
delete(m.selected, m.cursor)
} else {
m.selected[m.cursor] = struct{}{}
}
}
}
return m, nil
}
// View
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"
return s
}
func main() {
p := tea.NewProgram(initialModel())
if _, err := p.Run(); err != nil {
fmt.Printf("Error: %v", err)
}
}
Huh 表单¶
package main
import (
"fmt"
"github.com/charmbracelet/huh"
)
func main() {
var name string
var lang string
var confirm bool
form := huh.NewForm(
huh.NewGroup(
huh.NewInput().
Title("你的名字").
Value(&name),
huh.NewSelect[string]().
Title("首选语言").
Options(
huh.NewOption("Python", "python"),
huh.NewOption("Go", "go"),
huh.NewOption("Rust", "rust"),
).
Value(&lang),
huh.NewConfirm().
Title("确认提交?").
Value(&confirm),
),
)
err := form.Run()
if err != nil {
fmt.Println("Error:", err)
return
}
if confirm {
fmt.Printf("你好 %s,你选择了 %s!\n", name, lang)
}
}
进阶用法¶
使用 Bubbles 组件¶
import (
"github.com/charmbracelet/bubbles/spinner"
"github.com/charmbracelet/bubbles/progress"
"github.com/charmbracelet/bubbles/textinput"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/table"
"github.com/charmbracelet/bubbles/viewport"
)
// Spinner(加载动画)
s := spinner.New()
s.Spinner = spinner.Dot
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
// Progress(进度条)
p := progress.New(progress.WithDefaultGradient())
// TextInput(文本输入)
ti := textinput.New()
ti.Placeholder = "输入搜索关键词..."
ti.Focus()
// Table(表格)
t := table.New(
table.WithColumns(columns),
table.WithRows(rows),
table.WithFocused(true),
table.WithHeight(10),
)
美化日志¶
import "github.com/charmbracelet/log"
log.Info("服务已启动", "port", 8080)
log.Warn("磁盘空间不足", "usage", "85%")
log.Error("连接失败", "err", err)
log.Debug("处理请求", "path", "/api/users")
// 自定义样式
logger := log.NewWithOptions(os.Stderr, log.Options{
ReportTimestamp: true,
TimeFormat: time.Kitchen,
Prefix: "MyApp",
})
常见问题¶
Q: 与其他 TUI 框架比较?¶
- Bubble Tea (Go):Elm 架构、组件丰富、Charm 生态
- Textual (Python):功能最强、CSS 式布局
- Ratatui (Rust):性能最好、更底层
Q: 如何发布终端应用?¶
编译为单一二进制,通过 GitHub Releases、Homebrew、apt 等分发。
参考资源¶
- Charm 官网:https://charm.sh/
- Bubble Tea:https://github.com/charmbracelet/bubbletea
- Lip Gloss:https://github.com/charmbracelet/lipgloss
- Bubbles:https://github.com/charmbracelet/bubbles
- Huh:https://github.com/charmbracelet/huh
- 教程:https://github.com/charmbracelet/bubbletea/tree/master/tutorials