Claude-skill-registry building-glamorous-tuis
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/building-glamorous-tuis" ~/.claude/skills/majiayu000-claude-skill-registry-building-glamorous-tuis && rm -rf "$T"
manifest:
skills/data/building-glamorous-tuis/SKILL.mdsource content
Building Glamorous TUIs with Charmbracelet
Quick Router
What are you building?
| Context | Solution | Reference |
|---|---|---|
| Shell/Bash script | Gum, VHS, Mods, Freeze | → Shell Scripts |
| Go CLI/TUI | Bubble Tea + Lip Gloss | → Go TUI |
| SSH-accessible app | Wish + Bubble Tea | → Infrastructure |
| Recording demos | VHS | → Shell Scripts |
| Testing TUIs | teatest | → Infrastructure |
For Shell Scripts
No Go required. Gum gives you all the UI primitives.
brew install gum
Essential Gum Commands
# Input NAME=$(gum input --placeholder "Your name") # Selection (single) COLOR=$(gum choose "red" "green" "blue") # Selection (multi) ITEMS=$(gum choose --no-limit "a" "b" "c") # 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" # File picker FILE=$(gum file .)
Quick Recipe: Git Commit
TYPE=$(gum choose "feat" "fix" "docs" "refactor") MSG=$(gum input --placeholder "commit message") gum confirm "Commit?" && git commit -m "$TYPE: $MSG"
VHS Quick Start
Record terminal → GIF:
brew install vhs cat > demo.tape << 'EOF' Output demo.gif Set Theme "Catppuccin Mocha" Type "echo hello" Enter Sleep 1s EOF vhs demo.tape
Other Shell Tools
| Tool | Install | One-liner |
|---|---|---|
| Mods | | |
| Glow | | |
| Freeze | | |
For Go Applications
Instant Start
go get github.com/charmbracelet/bubbletea \ github.com/charmbracelet/lipgloss
Minimal TUI (Copy & Run)
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 (simple) | | |
| Markdown | | |
| Animation | | |
Quick Patterns
Styled Output (no TUI):
style := lipgloss.NewStyle().Foreground(lipgloss.Color("205")).Bold(true) fmt.Println(style.Render("Hello!"))
Simple Form (no TUI):
var name string huh.NewInput().Title("Your name?").Value(&name).Run()
Confirmation:
var ok bool huh.NewConfirm().Title("Delete?").Value(&ok).Run()
Full Go TUI Reference → Component API → Advanced Patterns →
For Infrastructure
Wish: SSH Apps
Serve TUI over SSH:
s, _ := wish.NewServer( wish.WithAddress(":2222"), wish.WithHostKeyPath(".ssh/key"), wish.WithMiddleware( bubbletea.Middleware(handler), logging.Middleware(), ), ) s.ListenAndServe()
Connect:
ssh localhost -p 2222
Other Infrastructure
| Tool | Purpose | Install |
|---|---|---|
| Soft Serve | Self-hosted Git | |
| Pop | Send email | |
| Skate | Key-value store | |
| Melt | SSH key backup | |
| Wishlist | SSH gateway | |
Testing TUIs
tm := teatest.NewTestModel(t, model) tm.Send(tea.KeyMsg{Type: tea.KeyEnter}) tm.Type("hello") teatest.WaitFor(t, tm, func(b []byte) bool { return strings.Contains(string(b), "expected") })
Full Infrastructure Reference →
Decision Guide
Is it a shell script? ├─ Yes → Use Gum │ Need recording? → VHS │ Need AI? → Mods │ └─ No (Go application) │ ├─ Just need styled output? │ └─ Lip Gloss only (no Bubble Tea) │ ├─ Need simple prompts/forms? │ └─ Huh standalone │ ├─ Need full interactive TUI? │ └─ Bubble Tea + Bubbles + Lip Gloss │ └─ Need SSH access? └─ Wish + Bubble Tea
When NOT to Use Charm
- Output is piped:
→ plain textmytool | grep - CI/CD: No terminal → use flags/env vars
- One simple prompt: Maybe
is finefmt.Scanf
Escape hatch:
if !term.IsTerminal(os.Stdin.Fd()) || os.Getenv("NO_TUI") != "" { runPlainMode() return }
All References
| Reference | Contents |
|---|---|
| Shell Scripts | Gum, VHS, Mods, Freeze, Glow - complete |
| Go TUI | Bubble Tea patterns, debugging, anti-patterns |
| Infrastructure | Wish, Soft Serve, teatest, x/term |
| Component Catalog | All Bubbles components API |
| Advanced Patterns | Theming, layouts, production architecture |