215 lines
3.6 KiB
Markdown
215 lines
3.6 KiB
Markdown
# Bubble Tea Design Patterns
|
|
|
|
Common architectural patterns for TUI development.
|
|
|
|
## Pattern 1: Single-View Application
|
|
|
|
**When**: Simple, focused TUIs with one main view
|
|
**Components**: 1-3 components, single model struct
|
|
**Complexity**: Low
|
|
|
|
```go
|
|
type model struct {
|
|
mainComponent component.Model
|
|
ready bool
|
|
}
|
|
```
|
|
|
|
## Pattern 2: Multi-View State Machine
|
|
|
|
**When**: Multiple distinct screens (setup, main, done)
|
|
**Components**: State enum + view-specific components
|
|
**Complexity**: Medium
|
|
|
|
```go
|
|
type view int
|
|
const (
|
|
setupView view = iota
|
|
mainView
|
|
doneView
|
|
)
|
|
|
|
type model struct {
|
|
currentView view
|
|
// Components for each view
|
|
}
|
|
```
|
|
|
|
## Pattern 3: Composable Views
|
|
|
|
**When**: Complex UIs with reusable sub-components
|
|
**Pattern**: Embed multiple bubble models
|
|
**Example**: Dashboard with multiple panels
|
|
|
|
```go
|
|
type model struct {
|
|
panel1 Panel1Model
|
|
panel2 Panel2Model
|
|
panel3 Panel3Model
|
|
}
|
|
|
|
// Each panel is itself a Bubble Tea model
|
|
```
|
|
|
|
## Pattern 4: Master-Detail
|
|
|
|
**When**: Selection in one pane affects display in another
|
|
**Example**: File list + preview, Email list + content
|
|
**Layout**: Two-pane or three-pane
|
|
|
|
```go
|
|
type model struct {
|
|
list list.Model
|
|
detail viewport.Model
|
|
selectedItem int
|
|
}
|
|
```
|
|
|
|
## Pattern 5: Form Flow
|
|
|
|
**When**: Multi-step data collection
|
|
**Pattern**: Array of inputs + focus management
|
|
**Example**: Configuration wizard
|
|
|
|
```go
|
|
type model struct {
|
|
inputs []textinput.Model
|
|
focusIndex int
|
|
step int
|
|
}
|
|
```
|
|
|
|
## Pattern 6: Progress Tracker
|
|
|
|
**When**: Long-running sequential operations
|
|
**Pattern**: Queue + progress per item
|
|
**Example**: Installation, download manager
|
|
|
|
```go
|
|
type model struct {
|
|
items []Item
|
|
currentIndex int
|
|
progress progress.Model
|
|
spinner spinner.Model
|
|
}
|
|
```
|
|
|
|
## Layout Patterns
|
|
|
|
### Vertical Stack
|
|
```go
|
|
lipgloss.JoinVertical(lipgloss.Left,
|
|
header,
|
|
content,
|
|
footer,
|
|
)
|
|
```
|
|
|
|
### Horizontal Panels
|
|
```go
|
|
lipgloss.JoinHorizontal(lipgloss.Top,
|
|
leftPanel,
|
|
separator,
|
|
rightPanel,
|
|
)
|
|
```
|
|
|
|
### Three-Column (File Manager Style)
|
|
```go
|
|
lipgloss.JoinHorizontal(lipgloss.Top,
|
|
parentDir, // 25% width
|
|
currentDir, // 35% width
|
|
preview, // 40% width
|
|
)
|
|
```
|
|
|
|
## Message Passing Patterns
|
|
|
|
### Custom Messages
|
|
```go
|
|
type myCustomMsg struct {
|
|
data string
|
|
}
|
|
|
|
func doSomethingCmd() tea.Msg {
|
|
return myCustomMsg{data: "result"}
|
|
}
|
|
|
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case myCustomMsg:
|
|
// Handle custom message
|
|
}
|
|
}
|
|
```
|
|
|
|
### Async Operations
|
|
```go
|
|
func fetchDataCmd() tea.Cmd {
|
|
return func() tea.Msg {
|
|
// Do async work
|
|
data := fetchFromAPI()
|
|
return dataFetchedMsg{data}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Error Handling Pattern
|
|
|
|
```go
|
|
type errMsg struct{ err error }
|
|
|
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case errMsg:
|
|
m.err = msg.err
|
|
m.errVisible = true
|
|
return m, nil
|
|
}
|
|
}
|
|
```
|
|
|
|
## Keyboard Navigation Pattern
|
|
|
|
```go
|
|
case tea.KeyMsg:
|
|
switch msg.String() {
|
|
case "up", "k":
|
|
m.cursor--
|
|
case "down", "j":
|
|
m.cursor++
|
|
case "enter":
|
|
m.selectCurrent()
|
|
case "q", "ctrl+c":
|
|
return m, tea.Quit
|
|
}
|
|
```
|
|
|
|
## Responsive Layout Pattern
|
|
|
|
```go
|
|
case tea.WindowSizeMsg:
|
|
m.width = msg.Width
|
|
m.height = msg.Height
|
|
|
|
// Update component dimensions
|
|
m.viewport.Width = msg.Width
|
|
m.viewport.Height = msg.Height - 5 // Reserve space for header/footer
|
|
```
|
|
|
|
## Help Overlay Pattern
|
|
|
|
```go
|
|
type model struct {
|
|
showHelp bool
|
|
help help.Model
|
|
}
|
|
|
|
func (m model) View() string {
|
|
if m.showHelp {
|
|
return m.help.View()
|
|
}
|
|
return m.mainView()
|
|
}
|
|
```
|