building-glamorous-tuis
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBuilding Glamorous TUIs with Charmbracelet
用 Charmbracelet 构建美观的 TUI
Quick Router — Start Here
快速导航 — 从这里开始
| I need to... | Use | Reference |
|---|---|---|
| Add prompts/spinners to a shell script | Gum (no Go) | Shell Scripts |
| Build a Go TUI | Bubble Tea + Lip Gloss | Go TUI |
| Build a production-grade Go TUI | Above + elite patterns | Production Architecture |
| Serve a TUI over SSH | Wish + Bubble Tea | Infrastructure |
| Record a terminal demo | VHS | Shell Scripts |
| Find a Bubbles component | list, table, viewport, spinner, progress... | Component Catalog |
| Get a copy-paste pattern | Layouts, forms, animation, testing | Quick Reference / Advanced Patterns |
| 我想要... | 使用工具 | 参考文档 |
|---|---|---|
| 给 Shell 脚本添加提示/加载动画 | Gum(无需 Go) | Shell 脚本 |
| 开发 Go TUI | Bubble Tea + Lip Gloss | Go TUI |
| 开发生产级 Go TUI | 以上工具 + 高级模式 | 生产架构 |
| 通过 SSH 提供 TUI 服务 | Wish + Bubble Tea | 基础设施 |
| 录制终端演示视频 | VHS | Shell 脚本 |
| 查找 Bubbles 组件 | list、table、viewport、spinner、progress... | 组件目录 |
| 获取可直接复制的代码模式 | 布局、表单、动画、测试 | 快速参考 / 高级模式 |
Decision Guide
决策指南
Is it a shell script?
├─ Yes → Use Gum
│ Need recording? → VHS
│ Need AI? → Mods
│
└─ No (Go application)
│
├─ Just styled output? → Lip Gloss only
├─ Simple prompts/forms? → Huh standalone
├─ Full interactive TUI? → Bubble Tea + Bubbles + Lip Gloss
│ │
│ └─ Production-grade? → Also add elite patterns:
│ (multi-view, data- two-phase async, immutable snapshots,
│ dense, must be adaptive layout, focus state machine,
│ fast & polished) semantic theming, pre-computed styles
│ → See Production Architecture reference
│
└─ Need SSH access? → Wish + Bubble Tea是 Shell 脚本吗?
├─ 是 → 使用 Gum
│ 需要录屏? → VHS
│ 需要 AI 功能? → Mods
│
└─ 否(Go 应用)
│
├─ 仅需要带样式的输出? → 仅用 Lip Gloss
├─ 简单提示/表单? → 独立使用 Huh
├─ 完整交互式 TUI? → Bubble Tea + Bubbles + Lip Gloss
│ │
│ └─ 生产级 TUI? → 额外加入高级模式:
│ (多视图、数据密集、必须快速流畅且体验精致) 两阶段异步、不可变快照、自适应布局、焦点状态机、语义化主题、预计算样式
│ → 查看生产架构参考文档
│
└─ 需要 SSH 访问? → Wish + Bubble TeaShell Scripts (No Go Required)
Shell 脚本(无需使用 Go)
bash
brew install gum # One-time installbash
undefinedbash
brew install gum # 仅需安装一次bash
undefinedInput
输入
NAME=$(gum input --placeholder "Your name")
NAME=$(gum input --placeholder "Your name")
Selection
选择
COLOR=$(gum choose "red" "green" "blue")
COLOR=$(gum choose "red" "green" "blue")
Fuzzy filter from stdin
从标准输入模糊筛选
BRANCH=$(git branch | gum filter)
BRANCH=$(git branch | gum filter)
Confirmation
确认操作
gum confirm "Continue?" && echo "yes"
gum confirm "Continue?" && echo "yes"
Spinner
加载动画
gum spin --title "Working..." -- long-command
gum spin --title "Working..." -- long-command
Styled output
带样式的输出
gum style --border rounded --padding "1 2" "Hello"
**[Full Gum Reference →](references/shell-scripts.md#gum-the-essential-tool)**
**[VHS Recording →](references/shell-scripts.md#vhs-terminal-recording)**
**[Mods AI →](references/shell-scripts.md#mods-ai-in-terminal)**
---gum style --border rounded --padding "1 2" "Hello"
**[完整 Gum 参考 →](references/shell-scripts.md#gum-the-essential-tool)**
**[VHS 录屏 →](references/shell-scripts.md#vhs-terminal-recording)**
**[Mods AI →](references/shell-scripts.md#mods-ai-in-terminal)**
---Go Applications
Go 应用
bash
go get github.com/charmbracelet/bubbletea github.com/charmbracelet/lipglossbash
go get github.com/charmbracelet/bubbletea github.com/charmbracelet/lipglossMinimal TUI (Copy & Run)
最简 TUI(复制即可运行)
go
package main
import (
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
var highlight = lipgloss.NewStyle().Foreground(lipgloss.Color("212")).Bold(true)
type model struct {
items []string
cursor int
}
func (m model) Init() tea.Cmd { return nil }
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.items)-1 { m.cursor++ }
case "enter":
fmt.Printf("Selected: %s\n", m.items[m.cursor])
return m, tea.Quit
}
}
return m, nil
}
func (m model) View() string {
s := ""
for i, item := range m.items {
if i == m.cursor {
s += highlight.Render("▸ "+item) + "\n"
} else {
s += " " + item + "\n"
}
}
return s + "\n(↑/↓ move, enter select, q quit)"
}
func main() {
m := model{items: []string{"Option A", "Option B", "Option C"}}
tea.NewProgram(m).Run()
}go
package main
import (
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
var highlight = lipgloss.NewStyle().Foreground(lipgloss.Color("212")).Bold(true)
type model struct {
items []string
cursor int
}
func (m model) Init() tea.Cmd { return nil }
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.items)-1 { m.cursor++ }
case "enter":
fmt.Printf("Selected: %s\n", m.items[m.cursor])
return m, tea.Quit
}
}
return m, nil
}
func (m model) View() string {
s := ""
for i, item := range m.items {
if i == m.cursor {
s += highlight.Render("▸ "+item) + "\n"
} else {
s += " " + item + "\n"
}
}
return s + "\n(↑/↓ move, enter select, q quit)"
}
func main() {
m := model{items: []string{"Option A", "Option B", "Option C"}}
tea.NewProgram(m).Run()
}Library Cheat Sheet
工具库速查表
| Need | Library | Example |
|---|---|---|
| TUI framework | | |
| Components | | |
| Styling | | |
| Forms | | |
| Markdown | | |
| Animation | | |
Full Go TUI Guide →
All Bubbles Components →
Layout & Animation Patterns →
| 需求 | 依赖库 | 示例 |
|---|---|---|
| TUI 开发框架 | | |
| UI 组件 | | |
| 样式处理 | | |
| 表单开发 | | |
| Markdown 渲染 | | |
| 动画效果 | | |
完整 Go TUI 指南 →
所有 Bubbles 组件 →
布局与动画模式 →
SSH Apps (Infrastructure)
SSH 应用(基础设施)
go
s, _ := wish.NewServer(
wish.WithAddress(":2222"),
wish.WithHostKeyPath(".ssh/key"),
wish.WithMiddleware(
bubbletea.Middleware(handler),
logging.Middleware(),
),
)
s.ListenAndServe()Connect:
ssh localhost -p 2222Full Infrastructure Guide →
go
s, _ := wish.NewServer(
wish.WithAddress(":2222"),
wish.WithHostKeyPath(".ssh/key"),
wish.WithMiddleware(
bubbletea.Middleware(handler),
logging.Middleware(),
),
)
s.ListenAndServe()连接命令:
ssh localhost -p 2222完整基础设施指南 →
Production TUI Architecture (Elite Patterns)
生产级 TUI 架构(高级模式)
Beyond basic Bubble Tea: patterns that make TUIs feel fast, polished, and professional.
Each links to a full code example in Production Architecture.
基础 Bubble Tea 之外的模式,可让 TUI 更流畅、精致、专业。每个模式都链接到生产架构中的完整代码示例。
My TUI is slow or janky
我的 TUI 运行缓慢或卡顿
| Symptom | Pattern | Fix |
|---|---|---|
| UI blocks during computation | Two-Phase Async | Phase 1 instant, Phase 2 background goroutine |
| Render path holds mutex | Immutable Snapshots | Pre-build snapshot, atomic pointer swap |
| File changes cause stutter | Background Worker | Debounced watcher + coalescing |
| Thousands of allocs per frame | Pre-Computed Styles | Allocate delegate styles once at startup |
| O(n²) string concat in View() | strings.Builder | Pre-allocated Builder with Grow() |
| Glamour re-renders every frame | Cached Markdown | Cache by content hash, invalidate on width change |
| GC pauses during interaction | Idle-Time GC | Trigger GC during idle periods |
| Large dataset = high memory | Object Pooling | sync.Pool with pre-allocated slices |
| Rendering off-screen items | Viewport Virtualization | Only render visible rows |
| 症状 | 模式 | 解决方案 |
|---|---|---|
| 计算过程中 UI 阻塞 | 两阶段异步架构 | 第一阶段即时响应,第二阶段后台 goroutine 执行 |
| 渲染路径持有互斥锁 | 不可变快照模式 | 预构建快照,原子指针交换 |
| 文件变更导致卡顿 | 带文件监听的后台 worker | 防抖监听 + 事件合并 |
| 每帧渲染产生数千次内存分配 | 预计算样式提升性能 | 启动时一次性分配所有样式 |
| View() 中存在 O(n²) 字符串拼接 | 在 View() 中使用 strings.Builder | 通过 Grow() 预分配 Builder 内存 |
| Glamour 每帧重复渲染 | Markdown 渲染缓存 | 按内容哈希缓存,宽度变更时失效 |
| 交互过程中出现 GC 停顿 | 空闲时段 GC 管理 | 在空闲时段触发 GC |
| 大数据集导致内存占用过高 | 对象池提升内存效率 | 配合预分配切片使用 sync.Pool |
| 渲染屏幕外的元素 | 视口虚拟化 | 仅渲染可见行 |
My layout breaks on different terminals
我的布局在不同终端上显示异常
| Symptom | Pattern | Fix |
|---|---|---|
| Hardcoded widths break | Adaptive Layout | 3-4 responsive breakpoints (80/100/140/180 cols) |
| Colors wrong on light terminals | Semantic Theming | |
| Items have equal priority → list shuffles | Deterministic Sorting | Stable sort with tie-breaking secondary key |
| Sort mode not visible | Dynamic Status Bar | Left/right segments with gap-fill |
| 症状 | 模式 | 解决方案 |
|---|---|---|
| 硬编码宽度导致显示错误 | 自适应布局引擎 | 3-4 个响应式断点(80/100/140/180 列) |
| 浅色终端下颜色显示错误 | 语义化主题系统 | |
| 元素优先级相同导致列表顺序随机 | 确定性稳定排序 | 带次级排序键的稳定排序 |
| 排序模式不可见 | 带动态分段的状态栏 | 左右分段,中间自动填充间隙 |
My TUI has multiple views and it's getting messy
我的 TUI 有多个视图,代码越来越混乱
| Symptom | Pattern | Fix |
|---|---|---|
| Key routing chaos | Focus State Machine | Explicit focus enum + modal priority layer |
| User gets lost in nested views | Breadcrumb Navigation | |
| Overlay dismiss loses position | Focus Restoration | Save focus before overlay, restore on dismiss |
| Old async results overwrite new data | Stale Message Detection | Compare data hash before applying results |
| Multiple component updates per frame | tea.Batch Accumulation | Collect cmds in slice, return |
| Background goroutine panic kills TUI | Error Recovery | |
| 症状 | 模式 | 解决方案 |
|---|---|---|
| 按键路由逻辑混乱 | 多视图焦点状态机 | 显式焦点枚举 + 模态优先级层级 |
| 用户在嵌套视图中迷路 | 面包屑导航 | |
| 关闭浮层后焦点位置丢失 | 焦点恢复 | 打开浮层前保存焦点,关闭后恢复 |
| 旧的异步结果覆盖新数据 | 过期消息检测 | 应用结果前对比数据哈希 |
| 每帧多次更新组件 | tea.Batch 命令累加 | 将命令收集到切片中,返回 |
| 后台 goroutine panic 导致 TUI 崩溃 | 后台 goroutine 错误恢复 | 所有 goroutine 加 |
I want to add data-rich visualizations
我想要添加数据丰富的可视化效果
| Want | Pattern | Code |
|---|---|---|
| Bar charts in list columns | Unicode Sparklines | |
| Color-by-intensity | Perceptual Heatmaps | gray → blue → purple → pink gradient |
| Dependency graph in terminal | ASCII Graph Renderer | Canvas + Manhattan routing (╭─╮│╰╯) |
| Age at a glance | Age Color Coding | Fresh=green, aging=yellow, stale=red |
| Borders that mean something | Semantic Borders | Red=blocked, green=ready, yellow=high-impact |
| 需求 | 模式 | 代码 |
|---|---|---|
| 列表列中的柱状图 | Unicode 迷你折线图 | 使用 8 级块字符实现 |
| 按强度着色 | 感知热力图 | 灰 → 蓝 → 紫 → 粉渐变 |
| 终端中的依赖图 | ASCII 图渲染器 | 画布 + 曼哈顿路由 (╭─╮│╰╯) |
| 快速查看内容新旧程度 | 年龄颜色编码 | 新鲜=绿色,即将过期=黄色,过期=红色 |
| 边框携带状态信息 | 语义化边框 | 红色=阻塞,绿色=就绪,黄色=高影响 |
I want my TUI to feel polished and professional
我想让我的 TUI 体验更精致专业
| Want | Pattern | Key Idea |
|---|---|---|
Vim-style | Vim Key Combos | Track |
| Search without jank | Debounced Search | 150ms timer, fire only when typing stops |
| Search across all fields at once | Composite FilterValue | Flatten all fields into one string |
| 4-line cards with metadata | Rich Delegates | Custom delegate with Height()=4 |
| Expand detail inline | Inline Expansion | Toggle with |
| Copy to clipboard | Clipboard Integration | |
| Multi-Tier Help | Quick ref + tutorial + persistent sidebar |
| Kanban with mode switching | Kanban Swimlanes | Pre-computed board states, O(1) switch |
| Collapsible tree with h/l | Tree Navigation | Flatten tree to visible list for j/k nav |
| Suspend TUI for vim edit | Editor Dispatch | |
| Remember expand/collapse | Persistent State | Save to JSON, graceful degradation on corrupt |
| Tune via env vars | Env Preferences | |
| Optional feature missing? | Graceful Degradation | Detect at startup, hide unavailable features |
Full Production Architecture Guide →
| 需求 | 模式 | 核心思路 |
|---|---|---|
Vim 风格 | Vim 按键组合追踪 | 两次按键之间追踪 |
| 搜索无卡顿 | 防抖搜索 | 150ms 计时器,仅在输入停止时触发 |
| 同时搜索所有字段 | 零分配模糊搜索的复合过滤值 | 将所有字段扁平化为单个字符串 |
| 带元数据的 4 行卡片 | 丰富的多行列表代理 | 自定义代理,Height()=4 |
| 行内展开详情 | 行内展开 | 按 |
| 复制到剪贴板 | 剪贴板集成 | 按 |
| 多层级帮助系统 | 快速参考 + 教程 + 持久化侧边栏 |
| 带模式切换的看板 | 带泳道模式的看板 | 预计算看板状态,O(1) 切换 |
| 按 h/l 折叠/展开树 | 树导航 | 将树扁平化为可见列表,支持 j/k 导航 |
| 挂起 TUI 打开 vim 编辑 | 编辑器调度 | 终端用 |
| 记住展开/折叠状态 | 持久化状态 | 保存到 JSON,损坏时优雅降级 |
| 通过环境变量调优 | 环境变量配置 | |
| 可选功能缺失? | 优雅降级 | 启动时检测,隐藏不可用功能 |
完整生产架构指南 →
Pre-Flight Checklist (Every TUI)
上线前检查清单(所有 TUI 适用)
- Handle — resize all components
tea.WindowSizeMsg - Handle — cleanup, restore terminal state
ctrl+c - Detect piped stdin/stdout — fall back to plain text
- Test on 80×24 minimum terminal
- Provide /
--no-tuiescape hatchNO_TUI - Test with both light AND dark backgrounds
- Test with and
NO_COLOR=1TERM=dumb
For production TUIs, see the full checklist (16 must-have + 20 polish items).
- 处理 — 调整所有组件大小
tea.WindowSizeMsg - 处理 — 清理资源,恢复终端状态
ctrl+c - 检测管道输入/输出 — 降级为纯文本模式
- 在最小 80×24 尺寸的终端上测试
- 提供 /
--no-tui降级开关NO_TUI - 在浅色和深色背景下都进行测试
- 在 和
NO_COLOR=1环境下测试TERM=dumb
生产级 TUI 请查看完整清单(16 项必备 + 20 项优化项)。
When NOT to Use Charm
什么时候不应该使用 Charm
- Output is piped: → plain text
mytool | grep - CI/CD: No terminal → use flags/env vars
- One simple prompt: Maybe is fine
fmt.Scanf
Escape hatch:
go
if !term.IsTerminal(os.Stdin.Fd()) || os.Getenv("NO_TUI") != "" {
runPlainMode()
return
}- 输出被管道传输: → 用纯文本
mytool | grep - CI/CD 环境: 无终端 → 使用参数/环境变量
- 单个简单提示: 可能 就足够了
fmt.Scanf
降级开关:
go
if !term.IsTerminal(os.Stdin.Fd()) || os.Getenv("NO_TUI") != "" {
runPlainMode()
return
}All References
所有参考资料
| I need... | Read this |
|---|---|
| Copy-paste one-liners | Quick Reference |
| Prompts to give Claude for TUI tasks | Prompts |
| Gum / VHS / Mods / Freeze / Glow | Shell Scripts |
| Bubble Tea architecture, debugging, anti-patterns | Go TUI |
| Bubbles component APIs (list, table, viewport...) | Component Catalog |
| Theming, layouts, animation, Huh forms, testing | Advanced Patterns |
| Elite patterns: async, snapshots, focus machines, adaptive layout, sparklines, kanban, trees, caching | Production Architecture |
| Wish SSH server, Soft Serve, teatest | Infrastructure |
| 我需要... | 阅读本文 |
|---|---|
| 可直接复制的单行代码 | 快速参考 |
| 给 Claude 的 TUI 开发提示词 | 提示词 |
| Gum / VHS / Mods / Freeze / Glow 用法 | Shell 脚本 |
| Bubble Tea 架构、调试、反模式 | Go TUI |
| Bubbles 组件 API(list、table、viewport...) | 组件目录 |
| 主题、布局、动画、Huh 表单、测试 | 高级模式 |
| 高阶模式:异步、快照、焦点状态机、自适应布局、迷你折线图、看板、树、缓存 | 生产架构 |
| Wish SSH 服务端、Soft Serve、teatest | 基础设施 |