Loading...
Loading...
Build terminal UIs with Charmbracelet (Bubble Tea, Lip Gloss, Gum). Use when: Go TUI, shell prompts/spinners, "make CLI prettier", adaptive layouts, async rendering, focus state machines, sparklines, heatmaps, kanban boards, SSH apps.
npx skill4agent add dicklesworthstone/meta_skill building-glamorous-tuis| 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 |
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 Teabrew 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 get github.com/charmbracelet/bubbletea github.com/charmbracelet/lipglosspackage 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()
}| Need | Library | Example |
|---|---|---|
| TUI framework | | |
| Components | | |
| Styling | | |
| Forms | | |
| Markdown | | |
| Animation | | |
s, _ := wish.NewServer(
wish.WithAddress(":2222"),
wish.WithHostKeyPath(".ssh/key"),
wish.WithMiddleware(
bubbletea.Middleware(handler),
logging.Middleware(),
),
)
s.ListenAndServe()ssh localhost -p 2222| 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 |
| 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 |
| 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 | |
| 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 |
| 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 |
tea.WindowSizeMsgctrl+c--no-tuiNO_TUINO_COLOR=1TERM=dumbmytool | grepfmt.Scanfif !term.IsTerminal(os.Stdin.Fd()) || os.Getenv("NO_TUI") != "" {
runPlainMode()
return
}| 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 |