Agent Skills: Building Glamorous TUIs with Charmbracelet

>-

UncategorizedID: dicklesworthstone/meta_skill/building-glamorous-tuis

Install this agent skill to your local

pnpm dlx add-skill https://github.com/Dicklesworthstone/meta_skill/tree/HEAD/skills/building-glamorous-tuis

Skill Files

Browse the full folder contents for building-glamorous-tuis.

Download Skill

Loading file tree…

skills/building-glamorous-tuis/SKILL.md

Skill Metadata

Name
building-glamorous-tuis
Description
>-

Building Glamorous TUIs with Charmbracelet

Quick Router — Start Here

| 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 |


Decision Guide

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 Scripts (No Go Required)

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"

Full Gum Reference → VHS Recording → Mods AI →


Go Applications

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 | huh | huh.NewInput().Title("Name").Run() | | Markdown | glamour | glamour.Render(md, "dark") | | Animation | harmonica | harmonica.NewSpring() |

Full Go TUI Guide → All Bubbles Components → Layout & Animation Patterns →


SSH Apps (Infrastructure)

s, _ := wish.NewServer(
    wish.WithAddress(":2222"),
    wish.WithHostKeyPath(".ssh/key"),
    wish.WithMiddleware(
        bubbletea.Middleware(handler),
        logging.Middleware(),
    ),
)
s.ListenAndServe()

Connect: ssh localhost -p 2222

Full Infrastructure Guide →


Production TUI Architecture (Elite Patterns)

Beyond basic Bubble Tea: patterns that make TUIs feel fast, polished, and professional. Each links to a full code example in Production Architecture.

My TUI is slow or janky

| 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 |

My layout breaks on different terminals

| Symptom | Pattern | Fix | |---------|---------|-----| | Hardcoded widths break | Adaptive Layout | 3-4 responsive breakpoints (80/100/140/180 cols) | | Colors wrong on light terminals | Semantic Theming | lipgloss.AdaptiveColor + WCAG AA contrast | | 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 |

My TUI has multiple views and it's getting messy

| Symptom | Pattern | Fix | |---------|---------|-----| | Key routing chaos | Focus State Machine | Explicit focus enum + modal priority layer | | User gets lost in nested views | Breadcrumb Navigation | Home > Board > Priority path indicator | | 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 tea.Batch(cmds...) | | Background goroutine panic kills TUI | Error Recovery | defer/recover wrapper for all goroutines |

I want to add data-rich visualizations

| Want | Pattern | Code | |------|---------|------| | Bar charts in list columns | Unicode Sparklines | ▇▅▂ using 8-level block characters | | 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 |

I want my TUI to feel polished and professional

| Want | Pattern | Key Idea | |------|---------|----------| | Vim-style gg/G | Vim Key Combos | Track waitingForG state between keystrokes | | 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 d, auto-collapse on j/k | | Copy to clipboard | Clipboard Integration | y for ID, C for markdown + toast feedback | | ? / ` / ; help | 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 | tea.ExecProcess for terminal, background for GUI | | Remember expand/collapse | Persistent State | Save to JSON, graceful degradation on corrupt | | Tune via env vars | Env Preferences | NO_COLOR, theme, debounce, split ratio | | Optional feature missing? | Graceful Degradation | Detect at startup, hide unavailable features |

Full Production Architecture Guide →


Pre-Flight Checklist (Every TUI)

  • [ ] Handle tea.WindowSizeMsg — resize all components
  • [ ] Handle ctrl+c — cleanup, restore terminal state
  • [ ] Detect piped stdin/stdout — fall back to plain text
  • [ ] Test on 80×24 minimum terminal
  • [ ] Provide --no-tui / NO_TUI escape hatch
  • [ ] Test with both light AND dark backgrounds
  • [ ] Test with NO_COLOR=1 and TERM=dumb

For production TUIs, see the full checklist (16 must-have + 20 polish items).


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

| 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 |