Charmland Go TUI
Use this skill for Go terminal UI work based on Charm's charm.land ecosystem. Focus on the core TUI stack:
charm.land/bubbletea/v2 for the Elm-style TUI runtime.
charm.land/lipgloss/v2 for styling, layout, tables, lists, and trees.
charm.land/bubbles/v2 for reusable interactive components.
charm.land/huh/v2 for higher-level interactive forms and prompts.
charm.land/glamour/v2 for Markdown rendering in the terminal.
charm.land/log/v2 for structured logging that fits terminal apps.
Do not introduce harmonica or wish by default. They are optional adjacent tools: use animation libraries only when the user explicitly needs physics animation, and use SSH-serving libraries only when the user explicitly wants to serve a TUI over SSH.
When to Use This Skill
- The user asks to build a Go TUI, CLI UI, terminal dashboard, terminal wizard, text UI, or interactive terminal app.
- The user mentions Bubble Tea, Lip Gloss, Bubbles, Huh, Glamour, or Charmbracelet libraries.
- The task involves terminal layout, styles, key handling, forms, lists, tables, text inputs, viewport scrolling, Markdown display, progress/spinner UI, or TUI logging.
- The task is debugging or upgrading a Charm TUI app, especially v1 to v2 API changes.
Default Workflow
- Inspect
go.mod first to confirm module paths and versions.
- Prefer existing project patterns over new abstractions.
- Use Bubble Tea for app state and event flow; use Lip Gloss only for rendering and layout.
- Use Bubbles for interactive widgets instead of rebuilding text inputs, lists, tables, spinners, viewports, or paginators.
- Use Huh when the main task is collecting structured user input through prompts/forms.
- Use Glamour when terminal output includes Markdown, docs, help pages, changelogs, or rich prose.
- Use
charm.land/log/v2 for debugging/logging; avoid printing to stdout while Bubble Tea owns the terminal.
- Run
gofmt and the relevant tests or build command after edits.
Version Rules
This workspace uses the Charm v2 import paths for the core stack:
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"charm.land/bubbles/v2/textinput"
"charm.land/bubbles/v2/viewport"
"charm.land/huh/v2"
"charm.land/glamour/v2"
"charm.land/log/v2"
Important Bubble Tea v2 rule: application models implement View() tea.View, not View() string. Most Bubbles components still render View() string; compose those strings in the parent model and return tea.NewView(content).
func (m model) View() tea.View {
content := m.input.View() + "\n" + m.viewport.View()
return tea.NewView(content)
}
Library Selection
| Need |
Use |
Notes |
| App runtime, key handling, messages, async commands |
Bubble Tea |
Model owns state; Update handles events; View renders from state. |
| Colors, borders, padding, alignment, layout |
Lip Gloss |
Keep styling in small reusable lipgloss.Style values. |
| Text inputs, tables, lists, spinners, progress, viewport |
Bubbles |
Compose component models inside your app model. |
| Structured prompts and multi-step forms |
Huh |
Faster than hand-building forms from individual Bubbles. |
| Markdown output |
Glamour |
Render Markdown, then style/place with Lip Gloss if needed. |
| Debug and structured logging |
Log |
Prefer file/stderr logging; do not corrupt the TUI renderer. |
Bubble Tea Architecture Rules
- Keep all mutable UI state in the model.
- Only mutate state inside
Update; keep View deterministic and side-effect free.
- Represent external results as custom
tea.Msg types.
- Return
tea.Cmd for I/O, timers, subprocesses, and async work; never block in Update.
- Use
tea.Batch for independent concurrent commands and tea.Sequence for ordered commands.
- Always handle quit keys like
ctrl+c and usually q where appropriate.
- Handle
tea.WindowSizeMsg for responsive layouts.
Minimal shape:
type model struct {
ready bool
width int
height 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.KeyPressMsg:
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
}
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
m.ready = true
}
return m, nil
}
func (m model) View() tea.View {
if !m.ready {
return tea.NewView("initializing...\n")
}
return tea.NewView("Hello from Bubble Tea\n")
}
Component Composition
When embedding Bubbles components:
- Store component models as fields on the parent model.
- Call the component's
Init command from parent Init when it needs one.
- In parent
Update, delegate messages to focused or relevant child components and store the returned child model.
- In parent
View, call child View() methods and compose their strings with Lip Gloss.
Use references for specifics:
- Bubble Tea architecture
- Lip Gloss styling and layout
- Bubbles components
- Huh forms
- Glamour Markdown rendering
- Logging
- Patterns and pitfalls
Quality Bar
- Prefer the smallest correct implementation.
- Do not hand-roll terminal escape codes unless the library lacks the feature.
- Keep style definitions separate from business logic when the view grows.
- Avoid global state except package-level immutable styles.
- Add tests for pure state transitions, parsing, filtering, validation, and formatting logic.
- For visual behavior, prefer small deterministic helpers that can be unit-tested outside the terminal runtime.
1---2name: charmland-go-tui3description: Build Go terminal user interfaces with the charm.land ecosystem: Bubble Tea v2, Lip Gloss v2, Bubbles v2, Huh, Glamour, and charmbracelet/log. Use when creating, debugging, refactoring, or explaining Go TUI applications, terminal forms, terminal components, Markdown rendering, styling/layout, or logging in TUI programs.4license: MIT5---67# Charmland Go TUI89Use this skill for Go terminal UI work based on Charm's `charm.land` ecosystem. Focus on the core TUI stack:1011- `charm.land/bubbletea/v2` for the Elm-style TUI runtime.12- `charm.land/lipgloss/v2` for styling, layout, tables, lists, and trees.13- `charm.land/bubbles/v2` for reusable interactive components.14- `charm.land/huh/v2` for higher-level interactive forms and prompts.15- `charm.land/glamour/v2` for Markdown rendering in the terminal.16- `charm.land/log/v2` for structured logging that fits terminal apps.1718Do not introduce `harmonica` or `wish` by default. They are optional adjacent tools: use animation libraries only when the user explicitly needs physics animation, and use SSH-serving libraries only when the user explicitly wants to serve a TUI over SSH.1920## When to Use This Skill2122- The user asks to build a Go TUI, CLI UI, terminal dashboard, terminal wizard, text UI, or interactive terminal app.23- The user mentions Bubble Tea, Lip Gloss, Bubbles, Huh, Glamour, or Charmbracelet libraries.24- The task involves terminal layout, styles, key handling, forms, lists, tables, text inputs, viewport scrolling, Markdown display, progress/spinner UI, or TUI logging.25- The task is debugging or upgrading a Charm TUI app, especially v1 to v2 API changes.2627## Default Workflow28291. Inspect `go.mod` first to confirm module paths and versions.302. Prefer existing project patterns over new abstractions.313. Use Bubble Tea for app state and event flow; use Lip Gloss only for rendering and layout.324. Use Bubbles for interactive widgets instead of rebuilding text inputs, lists, tables, spinners, viewports, or paginators.335. Use Huh when the main task is collecting structured user input through prompts/forms.346. Use Glamour when terminal output includes Markdown, docs, help pages, changelogs, or rich prose.357. Use `charm.land/log/v2` for debugging/logging; avoid printing to stdout while Bubble Tea owns the terminal.368. Run `gofmt` and the relevant tests or build command after edits.3738## Version Rules3940This workspace uses the Charm v2 import paths for the core stack:4142```go43tea "charm.land/bubbletea/v2"44"charm.land/lipgloss/v2"45"charm.land/bubbles/v2/textinput"46"charm.land/bubbles/v2/viewport"47"charm.land/huh/v2"48"charm.land/glamour/v2"49"charm.land/log/v2"50```5152Important Bubble Tea v2 rule: application models implement `View() tea.View`, not `View() string`. Most Bubbles components still render `View() string`; compose those strings in the parent model and return `tea.NewView(content)`.5354```go55func (m model) View() tea.View {56 content := m.input.View() + "\n" + m.viewport.View()57 return tea.NewView(content)58}59```6061## Library Selection6263| Need | Use | Notes |64| -------------------------------------------------------- | ------------ | ----------------------------------------------------------------- |65| App runtime, key handling, messages, async commands | `Bubble Tea` | Model owns state; Update handles events; View renders from state. |66| Colors, borders, padding, alignment, layout | `Lip Gloss` | Keep styling in small reusable `lipgloss.Style` values. |67| Text inputs, tables, lists, spinners, progress, viewport | `Bubbles` | Compose component models inside your app model. |68| Structured prompts and multi-step forms | `Huh` | Faster than hand-building forms from individual Bubbles. |69| Markdown output | `Glamour` | Render Markdown, then style/place with Lip Gloss if needed. |70| Debug and structured logging | `Log` | Prefer file/stderr logging; do not corrupt the TUI renderer. |7172## Bubble Tea Architecture Rules7374- Keep all mutable UI state in the model.75- Only mutate state inside `Update`; keep `View` deterministic and side-effect free.76- Represent external results as custom `tea.Msg` types.77- Return `tea.Cmd` for I/O, timers, subprocesses, and async work; never block in `Update`.78- Use `tea.Batch` for independent concurrent commands and `tea.Sequence` for ordered commands.79- Always handle quit keys like `ctrl+c` and usually `q` where appropriate.80- Handle `tea.WindowSizeMsg` for responsive layouts.8182Minimal shape:8384```go85type model struct {86 ready bool87 width int88 height int89}9091func (m model) Init() tea.Cmd { return nil }9293func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {94 switch msg := msg.(type) {95 case tea.KeyPressMsg:96 switch msg.String() {97 case "ctrl+c", "q":98 return m, tea.Quit99 }100 case tea.WindowSizeMsg:101 m.width = msg.Width102 m.height = msg.Height103 m.ready = true104 }105 return m, nil106}107108func (m model) View() tea.View {109 if !m.ready {110 return tea.NewView("initializing...\n")111 }112 return tea.NewView("Hello from Bubble Tea\n")113}114```115116## Component Composition117118When embedding Bubbles components:119120- Store component models as fields on the parent model.121- Call the component's `Init` command from parent `Init` when it needs one.122- In parent `Update`, delegate messages to focused or relevant child components and store the returned child model.123- In parent `View`, call child `View()` methods and compose their strings with Lip Gloss.124125Use references for specifics:126127- [Bubble Tea architecture](references/bubbletea.md)128- [Lip Gloss styling and layout](references/lipgloss.md)129- [Bubbles components](references/bubbles.md)130- [Huh forms](references/huh.md)131- [Glamour Markdown rendering](references/glamour.md)132- [Logging](references/logging.md)133- [Patterns and pitfalls](references/patterns.md)134135## Quality Bar136137- Prefer the smallest correct implementation.138- Do not hand-roll terminal escape codes unless the library lacks the feature.139- Keep style definitions separate from business logic when the view grows.140- Avoid global state except package-level immutable styles.141- Add tests for pure state transitions, parsing, filtering, validation, and formatting logic.142- For visual behavior, prefer small deterministic helpers that can be unit-tested outside the terminal runtime.