NYC
skills/dicklesworthstone/meta_skill/building-glamorous-tuis

building-glamorous-tuis

SKILL.md

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 brew install mods git diff | mods "review this"
Glow brew install glow glow README.md
Freeze brew install freeze freeze code.go -o screenshot.png

Full Shell Reference →


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 bubbletea tea.NewProgram(model).Run()
Components bubbles list.New(), textinput.New()
Styling lipgloss style.Foreground(lipgloss.Color("212"))
Forms (simple) huh huh.NewInput().Title("Name").Run()
Markdown glamour glamour.Render(md, "dark")
Animation harmonica harmonica.NewSpring()

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 brew install soft-serve
Pop Send email brew install pop
Skate Key-value store brew install skate
Melt SSH key backup brew install melt
Wishlist SSH gateway go install github.com/charmbracelet/wishlist

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: mytool | grep → plain text
  • CI/CD: No terminal → use flags/env vars
  • One simple prompt: Maybe fmt.Scanf is fine

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
Weekly Installs
9
First Seen
Jan 28, 2026
Installed on
codex9
gemini-cli8
opencode7
amp7
github-copilot7
kimi-cli7