关于 building-glamorous-tuis
这是一份使用 Charmbracelet 库构建终端界面的参考技能,涵盖 Shell 脚本提示交互、Go TUI、SSH 托管界面及生产实践。
主要功能
- 01库的选型指南
- 02Gum Shell 脚本提示交互
- 03使用 Bubble Tea 和 Lip Gloss 构建 Go TUI
- 04SSH 托管 TUI 模式
- 05生产架构与性能优化
- 06布局、表单、动画和测试
适用场景
- 01为 Shell 脚本或 Go TUI 选择库
- 02为脚本添加交互式提示和选项
- 03构建响应式多视图终端界面
- 04提升 TUI 性能与可靠性
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dicklesworthstone/ meta_skill --skill building-glamorous-tuis安装后在 Claude Code 中输入/building-glamorous-tuis确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/advanced-patterns.mdcomponent-catalog.mdgo-tui.mdinfrastructure.mdproduction-architecture.mdPROMPTS.mdQUICK-REFERENCE.mdshell-scripts.md详细介绍
使用 Charmbracelet 构建精美的 TUI
快速导航 — 从这里开始
| 我需要…… | 使用 | 参考 |
|---|---|---|
| 为 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…… | 组件目录 |
| 获取可直接复制粘贴的模式 | 布局、表单、动画、测试 | 快速参考 / 高级模式 |
决策指南
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 脚本(无需 Go)
brew install gum # One-time install
# Input
NAME=$(gum input --placeholder "Your name")
# Selection
COLOR=$(gum choose "red" "green" "blue")
# Fuzzy filter from stdin
BRANCH=$(git branch | gum filter)
# Confirmation
gum confirm "Continue?" && echo "yes"
# Spinner
gum spin --title "Working..." -- long-command
# Styled output
gum style --border rounded --padding "1 2" "Hello"
Go 应用
go get github.com/charmbracelet/bubbletea github.com/charmbracelet/lipgloss
最简 TUI(复制并运行)
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()
}
库速查表
| 需求 | 库 | 示例 |
|---|---|---|
| TUI 框架 | bubbletea | tea.NewProgram(model).Run() |
| 组件 | bubbles | list.New()、textinput.New() |
| 样式 | lipgloss | style.Foreground(lipgloss.Color("212")) |
| 表单 | huh | huh.NewInput().Title("Name").Run() |
| Markdown | glamour | glamour.Render(md, "dark") |
| 动画 | harmonica | harmonica.NewSpring() |
Go TUI 完整指南 → 所有 Bubbles 组件 → 布局与动画模式 →
SSH 应用(基础设施)
s, _ := wish.NewServer(
wish.WithAddress(":2222"),
wish.WithHostKeyPath(".ssh/key"),
wish.WithMiddleware(
bubbletea.Middleware(handler),
logging.Middleware(),
),
)
s.ListenAndServe()
连接:ssh localhost -p 2222
生产级 TUI 架构(高级模式)
超越基础 Bubble Tea:让 TUI 感觉快速、精致且专业的模式。 每个链接都指向生产级架构中的完整代码示例。
我的 TUI 很慢或卡顿
| 症状 | 模式 | 解决方法 |
|---|---|---|
| 计算期间 UI 阻塞 | 两阶段异步 | 阶段 1 即时完成,阶段 2 使用后台 goroutine |
| 渲染路径持有互斥锁 | 不可变快照 | 预先构建快照,通过原子指针交换 |
| 文件变更导致卡顿 | 后台工作线程 | 防抖监视器 + 合并处理 |
| 每帧分配数千次 | 预计算样式 | 在启动时一次性分配 delegate 样式 |
| View() 中存在 O(n²) 字符串拼接 | strings.Builder | 使用预分配并调用 Grow() 的 Builder |
| Glamour 每帧都重新渲染 | 缓存 Markdown | 按内容哈希缓存,宽度变化时失效 |
| 交互期间发生 GC 暂停 | 空闲时间 GC | 在空闲期间触发 GC |
| 数据集过大导致内存占用高 | 对象池化 | 使用带预分配切片的 sync.Pool |
| 渲染屏幕外的项目 | 视口虚拟化 | 仅渲染可见行 |
我的布局在不同终端上显示异常
| 症状 | 模式 | 修复方案 |
|---|---|---|
| 硬编码宽度导致布局异常 | 自适应布局 | 设置 3-4 个响应式断点(80/100/140/180 列) |
| 浅色终端上的颜色不正确 | 语义化主题 | 使用 lipgloss.AdaptiveColor + 符合 WCAG AA 标准的对比度 |
| 项目优先级相同 → 列表顺序变化 | 确定性排序 | 使用稳定排序,并通过次级键打破平局 |
| 排序模式不可见 | 动态状态栏 | 使用左右片段并填充间隙 |
我的 TUI 有多个视图,结构变得混乱
| 症状 | 模式 | 修复方案 |
|---|---|---|
| 按键路由混乱 | 焦点状态机 | 明确的焦点枚举 + 模态优先级层 |
| 用户在嵌套视图中迷失方向 | 面包屑导航 | 使用 Home > Board > Priority 路径指示器 |
| 关闭覆盖层后焦点位置丢失 | 焦点恢复 | 显示覆盖层前保存焦点,关闭时恢复 |
| 旧的异步结果覆盖新数据 | 过期消息检测 | 应用结果前比较数据哈希 |
| 每帧更新多个组件 | tea.Batch 累积 | 将 cmd 收集到切片中,并返回 tea.Batch(cmds...) |
| 后台 goroutine panic 导致 TUI 退出 | 错误恢复 | 为所有 goroutine 添加 defer/recover 包装器 |
我想添加数据丰富的可视化效果
| 需求 | 模式 | 代码 |
|---|---|---|
| 在列表列中显示条形图 | Unicode 迷你图 | 使用 8 级块状字符 ▇▅▂ |
| 按强度着色 | 感知热力图 | 灰色 → 蓝色 → 紫色 → 粉色渐变 |
| 在终端中显示依赖关系图 | ASCII 图形渲染器 | 画布 + 曼哈顿路径布线(╭─╮│╰╯) |
| 一眼看出年龄 | 年龄颜色编码 | 新鲜=绿色,渐旧=黄色,陈旧=红色 |
| 让边框传达含义 | 语义化边框 | 红色=受阻,绿色=就绪,黄色=高影响 |
我希望 TUI 显得精致且专业
| 需求 | 模式 | 核心思路 |
|---|---|---|
Vim 风格的 gg/G | Vim 按键组合 | 在按键之间跟踪 waitingForG 状态 |
| 搜索流畅不卡顿 | 防抖搜索 | 设置 150ms 计时器,仅在停止输入后触发 |
| 一次搜索所有字段 | 复合 FilterValue | 将所有字段合并为一个字符串 |
| 带元数据的 4 行卡片 | 丰富的委托项 | 使用 Height()=4 的自定义委托项 |
| 行内展开详情 | 行内展开 | 按 d 切换,按 j/k 时自动折叠 |
| 复制到剪贴板 | 剪贴板集成 | 按 y 复制 ID,按 C 复制 Markdown,并显示 Toast 反馈 |
? / ` / ; 帮助 | 多层级帮助 | 快速参考 + 教程 + 持久侧边栏 |
| 支持模式切换的看板 | 看板泳道 | 预计算看板状态,以 O(1) 复杂度切换 |
| 使用 h/l 导航的可折叠树 | 树形导航 | 将树展平为可见列表,以便使用 j/k 导航 |
| 挂起 TUI 并用 vim 编辑 | 编辑器调度 | 终端使用 tea.ExecProcess,GUI 使用后台方式 |
| 记住展开/折叠状态 | 持久化状态 | 保存为 JSON,损坏时优雅降级 |
| 通过环境变量调整 | 环境变量偏好设置 | NO_COLOR、主题、防抖时间、分栏比例 |
| 可选功能缺失怎么办? | 优雅降级 | 启动时检测,并隐藏不可用的功能 |
启动前检查清单(每个 TUI)
- 处理
tea.WindowSizeMsg— 调整所有组件的大小 - 处理
ctrl+c— 执行清理并恢复终端状态 - 检测通过管道传入的 stdin/stdout — 回退到纯文本
- 在至少 80×24 的终端上测试
- 提供
--no-tui/NO_TUI退出方式 - 在浅色和深色背景下都进行测试
- 在设置
NO_COLOR=1和TERM=dumb时进行测试
有关生产级 TUI,请参阅完整检查清单(16 项必备内容 + 20 项润色内容)。
何时不使用 Charm
- 输出通过管道传递:
mytool | grep→ 纯文本 - CI/CD: 没有终端 → 使用标志/环境变量
- 只有一个简单提示: 也许用
fmt.Scanf就够了
退出方式:
if !term.IsTerminal(os.Stdin.Fd()) || os.Getenv("NO_TUI") != "" {
runPlainMode()
return
}
所有参考资料
| 我需要…… | 查看此文档 |
|---|---|
| 可复制粘贴的单行命令 | 快速参考 |
| 提供给 Claude 的 TUI 任务提示词 | 提示词 |
| Gum / VHS / Mods / Freeze / Glow | Shell 脚本 |
| Bubble Tea 架构、调试、反模式 | Go TUI |
| Bubbles 组件 API(列表、表格、视口……) | 组件目录 |
| 主题、布局、动画、Huh 表单、测试 | 高级模式 |
| 专业模式:异步、快照、焦点状态机、自适应布局、迷你图、看板、树、缓存 | 生产架构 |
| Wish SSH 服务、Soft Serve、teatest | 基础设施 |
评论
支持 Markdown