Version: ALPHA — Section structure and mapping notes. Not yet a complete appendix.
Package: mono-tui
This document maps the Monospace Design TUI Standard to Bubble Tea (Go), including the companion libraries Bubbles (components), Lip Gloss (styling), Huh? (forms), and Gum (shell scripts). It assumes familiarity with the standard and the Rendering Reference.
Architecture: Bubble Tea follows The Elm Architecture (TEA) — Init, Update, View. State is immutable; Update returns a new model. View returns a string. There is no widget tree, no CSS, and no built-in layout engine. All rendering is manual string composition via Lip Gloss.
| Monospace TUI Component (Standard §4) | Charm Component | Package | Notes |
|---|---|---|---|
| Entry field | textinput.Model |
bubbles/textinput |
Placeholder, validation, character limit |
| Multi-line entry | textarea.Model |
bubbles/textarea |
Line numbers, word wrap |
| List box | list.Model |
bubbles/list |
Built-in fuzzy filtering, pagination, status bar |
| Data table | table.Model |
bubbles/table |
Column widths, row selection, header |
| Progress bar | progress.Model |
bubbles/progress |
Percentage, gradient colors |
| Spinner | spinner.Model |
bubbles/spinner |
Multiple spinner styles |
| Footer key strip | help.Model |
bubbles/help |
Renders key.Binding slices as help bar |
| Scrollable content | viewport.Model |
bubbles/viewport |
Content paging, scroll indicators |
| File picker | filepicker.Model |
bubbles/filepicker |
Directory browsing, filtering |
| Timer / stopwatch | timer.Model, stopwatch.Model |
bubbles/timer, bubbles/stopwatch |
Countdown and elapsed |
| Monospace TUI Component | Huh? Field | Notes |
|---|---|---|
| Entry field | huh.Input |
Validation, placeholder, char limit |
| Multi-line entry | huh.Text |
Editor mode |
| Radio group | huh.Select |
Single selection from options |
| Checkbox group | huh.MultiSelect |
Multiple selection |
| Toggle / confirm | huh.Confirm |
Yes/No boolean |
| File picker | huh.FilePicker |
Directory navigation |
| Read-only text | huh.Note |
Instructions, summaries |
TODO: Implementation guidance for components not provided by Bubbles or Huh?.
| Monospace TUI Component | Implementation Strategy |
|---|---|
| Push button | Custom model: styled text block, Enter to activate |
| Toggle / Switch | Custom model: [X]/[ ] rendering, Space to toggle |
| Radio group (non-form) | Custom model or embed huh.Select |
| Metric card | Lip Gloss styled block with border |
| Sparkline | Custom model using Braille characters (§R6) |
| Tabs | Custom model: styled tab bar, [/] to switch |
| Action bar / menus | Custom model: horizontal items, Alt to activate |
| Tree view | Custom model: indented list with expand/collapse |
| Monospace TUI Component | Gum Command | Usage |
|---|---|---|
| Entry field | gum input |
result=$(gum input --placeholder "Name") |
| Multi-line entry | gum write |
result=$(gum write --header "Description") |
| Select (exclusive) | gum choose |
result=$(gum choose "opt1" "opt2" "opt3") |
| Multi-select | gum choose --limit N |
result=$(gum choose --no-limit "a" "b" "c") |
| Confirm | gum confirm |
gum confirm "Continue?" && echo yes |
| Fuzzy finder | gum filter |
result=$(cat items.txt | gum filter) |
| File picker | gum file |
result=$(gum file /path) |
| Data table | gum table |
gum table < data.csv |
| Scrollable viewer | gum pager |
cat long.txt | gum pager |
| Spinner / progress | gum spin |
gum spin --title "Working" -- command |
TODO: Map Standard §1.3 to Lip Gloss JoinHorizontal and JoinVertical.
func (m model) View() string {
width, height := m.width, m.height
// Region A — Navigation sidebar
navWidth := 16
nav := lipgloss.NewStyle().
Width(navWidth).
Height(height - 2). // minus header and footer
Border(lipgloss.NormalBorder(), false, true, false, false).
Render(m.navView())
// Region C — Context panel
ctxWidth := 30
ctx := lipgloss.NewStyle().
Width(ctxWidth).
Height(height - 2).
Render(m.contextView())
// Region B — Content (fills remaining)
contentWidth := width - navWidth - ctxWidth
content := lipgloss.NewStyle().
Width(contentWidth).
Height(height - 2).
Render(m.contentView())
body := lipgloss.JoinHorizontal(lipgloss.Top, nav, content, ctx)
footer := m.footerView()
return lipgloss.JoinVertical(lipgloss.Left, body, footer)
}TODO: Handle tea.WindowSizeMsg and branch layout in View().
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
}
// ...
}
func (m model) View() string {
switch {
case m.width < 80: // Compact: B only
case m.width < 120: // Standard: A + B
case m.width < 160: // Expanded: A + B + C
default: // Wide: full layout
}
}TODO: Map Standard §2.2 three-tier system to Bubble Tea key.Binding.
import "github.com/charmbracelet/bubbles/key"
type keyMap struct {
// Tier 1 — Global
Help key.Binding
Quit key.Binding
Back key.Binding
Filter key.Binding
Refresh key.Binding
// Tier 1 — Scrolling
Up key.Binding
Down key.Binding
Top key.Binding
Bottom key.Binding
// Tier 2 — Common actions
Sort key.Binding
Delete key.Binding
Edit key.Binding
Add key.Binding
Yank key.Binding
}
func defaultKeyMap() keyMap {
return keyMap{
Help: key.NewBinding(key.WithKeys("?", "f1"), key.WithHelp("?", "help")),
Quit: key.NewBinding(key.WithKeys("q", "Q"), key.WithHelp("q", "quit")),
Back: key.NewBinding(key.WithKeys("esc", "f3"), key.WithHelp("esc", "back")),
Filter: key.NewBinding(key.WithKeys("/"), key.WithHelp("/", "filter")),
Refresh: key.NewBinding(key.WithKeys("r", "R", "f5"), key.WithHelp("r", "refresh")),
Sort: key.NewBinding(key.WithKeys("s", "S"), key.WithHelp("s", "sort")),
// ... etc
}
}TODO: Manual key routing in Update() based on which panel has focus.
TODO: Map Standard §1.4 to help.Model rendering active key bindings.
TODO: Standard §2.2 requires case-insensitive keys. Bubble Tea's key.WithKeys() accepts both cases.
TODO: Map Standard §5.1 to a Lip Gloss theme struct.
type Theme struct {
Primary lipgloss.AdaptiveColor
Secondary lipgloss.AdaptiveColor
Tertiary lipgloss.AdaptiveColor
Error lipgloss.AdaptiveColor
Success lipgloss.AdaptiveColor
Warning lipgloss.AdaptiveColor
Surface lipgloss.AdaptiveColor
Text lipgloss.AdaptiveColor
Disabled lipgloss.AdaptiveColor
}
var DefaultTheme = Theme{
Primary: lipgloss.AdaptiveColor{Light: "63", Dark: "75"},
Secondary: lipgloss.AdaptiveColor{Light: "245", Dark: "109"},
// ...
}TODO: Map Standard §6.1 to Lip Gloss border styles.
| Elevation | Lip Gloss Border | Shadow |
|---|---|---|
| Level 0 | None | None |
| Level 1 | lipgloss.NormalBorder() |
None |
| Level 2 | lipgloss.NormalBorder() |
TODO: custom shadow rendering |
| Level 3 | lipgloss.DoubleBorder() |
TODO: custom shadow rendering |
| Level 4 | lipgloss.DoubleBorder() |
TODO: custom shadow + scrim |
TODO: Map Standard §8.1 states to Lip Gloss style variants.
| State | Lip Gloss Style |
|---|---|
| Enabled | Normal foreground |
| Focused | Reverse(true) or bracket markers |
| Selected | Reverse(true) + marker |
| Disabled | Faint(true) |
| Error | Foreground(theme.Error) |
TODO: Map Standard §3.1 and §12 workflow archetypes to Bubble Tea state management.
type screenState int
const (
screenList screenState = iota
screenDetail
screenEdit
screenConfirm
)
type model struct {
state screenState
listModel listModel
detailModel detailModel
editModel editModel
// ...
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch m.state {
case screenList:
return m.updateList(msg)
case screenDetail:
return m.updateDetail(msg)
// ...
}
}
func (m model) View() string {
switch m.state {
case screenList:
return m.listModel.View()
case screenDetail:
return m.detailModel.View()
// ...
}
}TODO: Mapping for each workflow archetype (§12).
| Workflow (§12) | Bubble Tea Pattern |
|---|---|
| Wizard (§12.1) | State enum per step; Huh? Form with groups for form steps |
| CRUD (§12.2) | State enum: list/detail/edit; list state preserved on return |
| Monitor-Respond (§12.3) | tea.Tick cmd for auto-refresh; state enum for detail/action |
| Search-Act (§12.4) | list.Model with filtering; state for preview/action |
| Drill-Down (§12.5) | Stack of level states; push/pop on Enter/Esc |
| Pipeline (§12.6) | State enum per stage; loop between preview and config states |
| Review-Approve (§12.7) | Queue index in model; advance index on decision |
| Configuration (§12.8) | Huh? Form with multiple groups; or custom tab model |
TODO: Map Standard §10.2 to Bubble Tea tea.Cmd.
func fetchData() tea.Msg {
// Runs in a goroutine
result, err := api.GetData()
if err != nil {
return errMsg{err}
}
return dataMsg{result}
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case dataMsg:
m.data = msg.data
case errMsg:
m.err = msg.err
}
return m, nil
}TODO: Map Monitor-Respond (§12.3) auto-refresh to tea.Tick.
TODO: Map Standard §11.2 and §12.8 to Huh? forms.
form := huh.NewForm(
huh.NewGroup(
huh.NewInput().Title("Application Name").Value(&name),
huh.NewSelect[string]().
Title("Log Level").
Options(
huh.NewOption("Info", "info"),
huh.NewOption("Debug", "debug"),
huh.NewOption("Error", "error"),
).Value(&logLevel),
huh.NewConfirm().Title("Enable Caching").Value(&caching),
).Title("General"),
huh.NewGroup(
huh.NewInput().Title("API Host").Value(&apiHost),
huh.NewInput().Title("API Port").Value(&apiPort),
).Title("Network"),
)TODO: Map Wizard workflow (§12.1) to Huh? sequential groups with LayoutDefault.
TODO: Huh? supports form.WithAccessible(true) for screen reader compatibility — aligns with Standard §9.1.
TODO: Map Wizard (§12.1) to sequential Gum commands.
#!/bin/bash
# Wizard workflow (§12.1) via Gum
# Step 1 — Project name
name=$(gum input --placeholder "Project name")
# Step 2 — Select archetype
archetype=$(gum choose "Dashboard" "Admin" "File Manager" "Editor" "Fuzzy Finder")
# Step 3 — Select palette
palette=$(gum choose "Default" "Monochrome" "OS/2" "Turbo Pascal" "Amber Phosphor" "Green Phosphor" "Airlock")
# Step 4 — Confirm
gum confirm "Create project '$name' with $archetype archetype and $palette palette?" || exit 1
echo "Creating project..."
gum spin --title "Setting up..." -- setup_command "$name" "$archetype" "$palette"Gum cannot build persistent full-screen TUI applications. It provides individual interaction components for shell scripts. Not suitable for screen archetypes, three-region layouts, or continuous keyboard interaction.
| Standard Rule | Bubble Tea | Huh? | Gum |
|---|---|---|---|
| §1.3 Three-region layout | Manual (Lip Gloss) | N/A | N/A |
| §1.4 Footer key strip | Help Bubble | Inline | N/A |
| §2.2 Key assignments | Manual routing | Built-in per-field | Per-command |
| §5.1 Semantic colors | Manual theme struct | Lip Gloss themes | CLI flags |
| §6.1 Elevation | Lip Gloss borders | N/A | --border |
| §6.4 Shadow rendering | Custom implementation needed | N/A | N/A |
| §6.5 Scrim | Custom implementation needed | N/A | N/A |
| §8.1 State model | Manual style switching | Focused/blurred | N/A |
| §8.2 Focus invariant | Manual focus tracking | Built-in | N/A |
| §9.1 Accessible mode | Not built-in | WithAccessible(true) |
N/A |
| §11 Screen archetypes | Manual composition | Form-only | N/A |
| §12 Workflow archetypes | State enum pattern | Groups (wizard/config) | Sequential shell |