building-glamorous-tuis
Build beautiful terminal UIs with Charmbracelet. For SHELL/BASH scripts: use Gum (prompts, spinners, selection), VHS (recording), Mods (AI), Freeze (screenshots), Glow (markdown). For GO applications: use Bubble Tea (framework), Bubbles (components), Lip Gloss (styling), Huh (forms), Glamour (markdown), Harmonica (animation). For INFRASTRUCTURE: Wish (SSH apps), Soft Serve (Git), teatest (testing). Trigger on: shell scripts needing UI, Go CLI tools, terminal dashboards, SSH applications, or "make this CLI prettier."
Installation
Suivez les instructions du dépôt pour votre agent. Consultez les fichiers et dépendances avant l’installation.
Voir la source ↗GuidesInstructions
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 |
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.Scanfis 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 |
Exemples
Consultez les exemples du dépôt source.
Voir la source ↗Historique source
- Date d’indexation
- 2026-01-20
- Mise à jour de l’index
- 2026-01-28
- Branch
- main
Les dates concernent ce répertoire. Consultez le dépôt pour l’historique du code.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!