Initial commit
This commit is contained in:
168
references/architecture-best-practices.md
Normal file
168
references/architecture-best-practices.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# Bubble Tea Architecture Best Practices
|
||||
|
||||
## Model Design
|
||||
|
||||
### Keep State Flat
|
||||
❌ Avoid: Deeply nested state
|
||||
✅ Prefer: Flat structure with clear fields
|
||||
|
||||
```go
|
||||
// Good
|
||||
type model struct {
|
||||
items []Item
|
||||
cursor int
|
||||
selected map[int]bool
|
||||
}
|
||||
|
||||
// Avoid
|
||||
type model struct {
|
||||
state struct {
|
||||
data struct {
|
||||
items []Item
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Separate Concerns
|
||||
- UI state in model
|
||||
- Business logic in separate functions
|
||||
- Network/IO in commands
|
||||
|
||||
### Component Ownership
|
||||
Each component owns its state. Don't reach into component internals.
|
||||
|
||||
## Update Function
|
||||
|
||||
### Message Routing
|
||||
Route messages to appropriate handlers:
|
||||
|
||||
```go
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
return m.handleKeyboard(msg)
|
||||
case tea.WindowSizeMsg:
|
||||
return m.handleResize(msg)
|
||||
}
|
||||
return m.updateComponents(msg)
|
||||
}
|
||||
```
|
||||
|
||||
### Command Batching
|
||||
Batch multiple commands:
|
||||
|
||||
```go
|
||||
var cmds []tea.Cmd
|
||||
cmds = append(cmds, cmd1, cmd2, cmd3)
|
||||
return m, tea.Batch(cmds...)
|
||||
```
|
||||
|
||||
## View Function
|
||||
|
||||
### Cache Expensive Renders
|
||||
Don't recompute on every View() call:
|
||||
|
||||
```go
|
||||
type model struct {
|
||||
cachedView string
|
||||
dirty bool
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
if m.dirty {
|
||||
m.cachedView = m.render()
|
||||
m.dirty = false
|
||||
}
|
||||
return m.cachedView
|
||||
}
|
||||
```
|
||||
|
||||
### Responsive Layouts
|
||||
Adapt to terminal size:
|
||||
|
||||
```go
|
||||
if m.width < 80 {
|
||||
// Compact layout
|
||||
} else {
|
||||
// Full layout
|
||||
}
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
### Minimize Allocations
|
||||
Reuse slices and strings where possible
|
||||
|
||||
### Defer Heavy Operations
|
||||
Move slow operations to commands (async)
|
||||
|
||||
### Debounce Rapid Updates
|
||||
Don't update on every keystroke for expensive operations
|
||||
|
||||
## Error Handling
|
||||
|
||||
### User-Friendly Errors
|
||||
Show actionable error messages
|
||||
|
||||
### Graceful Degradation
|
||||
Fallback when features unavailable
|
||||
|
||||
### Error Recovery
|
||||
Allow user to retry or cancel
|
||||
|
||||
## Testing
|
||||
|
||||
### Test Pure Functions
|
||||
Extract business logic for easy testing
|
||||
|
||||
### Mock Commands
|
||||
Test Update() without side effects
|
||||
|
||||
### Snapshot Views
|
||||
Compare View() output for visual regression
|
||||
|
||||
## Accessibility
|
||||
|
||||
### Keyboard-First
|
||||
All features accessible via keyboard
|
||||
|
||||
### Clear Indicators
|
||||
Show current focus, selection state
|
||||
|
||||
### Help Text
|
||||
Provide discoverable help (? key)
|
||||
|
||||
## Code Organization
|
||||
|
||||
### File Structure
|
||||
```
|
||||
main.go - Entry point, model definition
|
||||
update.go - Update handlers
|
||||
view.go - View rendering
|
||||
commands.go - Command definitions
|
||||
messages.go - Custom message types
|
||||
```
|
||||
|
||||
### Component Encapsulation
|
||||
One component per file for complex TUIs
|
||||
|
||||
## Debugging
|
||||
|
||||
### Log to File
|
||||
```go
|
||||
f, _ := os.OpenFile("debug.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
log.SetOutput(f)
|
||||
log.Printf("Debug: %+v", msg)
|
||||
```
|
||||
|
||||
### Debug Mode
|
||||
Toggle debug view with key binding
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Forgetting tea.Batch**: Returns only last command
|
||||
2. **Not handling WindowSizeMsg**: Fixed-size components
|
||||
3. **Blocking in Update()**: Freezes UI - use commands
|
||||
4. **Direct terminal writes**: Use tea.Println for above-TUI output
|
||||
5. **Ignoring ready state**: Rendering before initialization complete
|
||||
141
references/bubbletea-components-guide.md
Normal file
141
references/bubbletea-components-guide.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Bubble Tea Components Guide
|
||||
|
||||
Complete reference for Bubble Tea ecosystem components.
|
||||
|
||||
## Core Input Components
|
||||
|
||||
### textinput.Model
|
||||
**Purpose**: Single-line text input
|
||||
**Use Cases**: Search boxes, single field forms, command input
|
||||
**Key Methods**:
|
||||
- `Focus()` / `Blur()` - Focus management
|
||||
- `SetValue(string)` - Set text programmatically
|
||||
- `Value()` - Get current text
|
||||
|
||||
**Example Pattern**:
|
||||
```go
|
||||
input := textinput.New()
|
||||
input.Placeholder = "Search..."
|
||||
input.Focus()
|
||||
```
|
||||
|
||||
### textarea.Model
|
||||
**Purpose**: Multi-line text editing
|
||||
**Use Cases**: Message composition, text editing, large text input
|
||||
**Key Features**: Line wrapping, scrolling, cursor management
|
||||
|
||||
### filepicker.Model
|
||||
**Purpose**: File system navigation
|
||||
**Use Cases**: File selection, file browsers
|
||||
**Key Features**: Directory traversal, file type filtering, path resolution
|
||||
|
||||
## Display Components
|
||||
|
||||
### viewport.Model
|
||||
**Purpose**: Scrollable content display
|
||||
**Use Cases**: Log viewers, document readers, large text display
|
||||
**Key Methods**:
|
||||
- `SetContent(string)` - Set viewable content
|
||||
- `GotoTop()` / `GotoBottom()` - Navigation
|
||||
- `LineUp()` / `LineDown()` - Scroll control
|
||||
|
||||
### table.Model
|
||||
**Purpose**: Tabular data display
|
||||
**Use Cases**: Data tables, structured information
|
||||
**Key Features**: Column definitions, row selection, styling
|
||||
|
||||
### list.Model
|
||||
**Purpose**: Filterable, navigable lists
|
||||
**Use Cases**: Item selection, menus, file lists
|
||||
**Key Features**: Filtering, pagination, custom item delegates
|
||||
|
||||
### paginator.Model
|
||||
**Purpose**: Page-based navigation
|
||||
**Use Cases**: Paginated content, chunked display
|
||||
|
||||
## Feedback Components
|
||||
|
||||
### spinner.Model
|
||||
**Purpose**: Loading/waiting indicator
|
||||
**Styles**: Dot, Line, Minidot, Jump, Pulse, Points, Globe, Moon, Monkey
|
||||
|
||||
### progress.Model
|
||||
**Purpose**: Progress indication
|
||||
**Modes**: Determinate (0-100%), Indeterminate
|
||||
**Styling**: Gradient, solid color, custom
|
||||
|
||||
### timer.Model
|
||||
**Purpose**: Countdown timer
|
||||
**Use Cases**: Timeouts, timed operations
|
||||
|
||||
### stopwatch.Model
|
||||
**Purpose**: Elapsed time tracking
|
||||
**Use Cases**: Duration measurement, time tracking
|
||||
|
||||
## Navigation Components
|
||||
|
||||
### tabs
|
||||
**Purpose**: Tab-based view switching
|
||||
**Pattern**: Lipgloss-based tab rendering
|
||||
|
||||
### help.Model
|
||||
**Purpose**: Help text and keyboard shortcuts
|
||||
**Modes**: Short (inline), Full (overlay)
|
||||
|
||||
## Layout with Lipgloss
|
||||
|
||||
**JoinVertical**: Stack components vertically
|
||||
**JoinHorizontal**: Place components side-by-side
|
||||
**Place**: Position with alignment
|
||||
**Border**: Add borders and padding
|
||||
|
||||
## Component Initialization Pattern
|
||||
|
||||
```go
|
||||
type model struct {
|
||||
component1 component1.Model
|
||||
component2 component2.Model
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
return tea.Batch(
|
||||
m.component1.Init(),
|
||||
m.component2.Init(),
|
||||
)
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmds []tea.Cmd
|
||||
|
||||
// Update each component
|
||||
var cmd tea.Cmd
|
||||
m.component1, cmd = m.component1.Update(msg)
|
||||
cmds = append(cmds, cmd)
|
||||
|
||||
m.component2, cmd = m.component2.Update(msg)
|
||||
cmds = append(cmds, cmd)
|
||||
|
||||
return m, tea.Batch(cmds...)
|
||||
}
|
||||
```
|
||||
|
||||
## Message Handling
|
||||
|
||||
**Standard Messages**:
|
||||
- `tea.KeyMsg` - Keyboard input
|
||||
- `tea.MouseMsg` - Mouse events
|
||||
- `tea.WindowSizeMsg` - Terminal resize
|
||||
- `tea.QuitMsg` - Quit signal
|
||||
|
||||
**Component Messages**:
|
||||
- `progress.FrameMsg` - Progress/spinner animation
|
||||
- `spinner.TickMsg` - Spinner tick
|
||||
- `textinput.ErrMsg` - Input errors
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always delegate**: Let components handle their own messages
|
||||
2. **Batch commands**: Use `tea.Batch()` for multiple commands
|
||||
3. **Focus management**: Only one component focused at a time
|
||||
4. **Dimension tracking**: Update component sizes on `WindowSizeMsg`
|
||||
5. **State separation**: Keep UI state in model, business logic separate
|
||||
214
references/design-patterns.md
Normal file
214
references/design-patterns.md
Normal file
@@ -0,0 +1,214 @@
|
||||
# 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()
|
||||
}
|
||||
```
|
||||
98
references/example-designs.md
Normal file
98
references/example-designs.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Example TUI Designs
|
||||
|
||||
Real-world design examples with component selections.
|
||||
|
||||
## Example 1: Log Viewer
|
||||
|
||||
**Requirements**: View large log files, search, navigate
|
||||
**Archetype**: Viewer
|
||||
**Components**:
|
||||
- viewport.Model - Main log display
|
||||
- textinput.Model - Search input
|
||||
- help.Model - Keyboard shortcuts
|
||||
|
||||
**Architecture**:
|
||||
```go
|
||||
type model struct {
|
||||
viewport viewport.Model
|
||||
searchInput textinput.Model
|
||||
searchMode bool
|
||||
matches []int
|
||||
currentMatch int
|
||||
}
|
||||
```
|
||||
|
||||
**Key Features**:
|
||||
- Toggle search with `/`
|
||||
- Navigate matches with n/N
|
||||
- Highlight matches in viewport
|
||||
|
||||
## Example 2: File Manager
|
||||
|
||||
**Requirements**: Three-column navigation, preview
|
||||
**Archetype**: File Manager
|
||||
**Components**:
|
||||
- list.Model (x2) - Parent + current directory
|
||||
- viewport.Model - File preview
|
||||
- filepicker.Model - Alternative approach
|
||||
|
||||
**Layout**: Horizontal three-pane
|
||||
**Complexity**: Medium-High
|
||||
|
||||
## Example 3: Package Installer
|
||||
|
||||
**Requirements**: Sequential installation with progress
|
||||
**Archetype**: Installer
|
||||
**Components**:
|
||||
- list.Model - Package list
|
||||
- progress.Model - Per-package progress
|
||||
- spinner.Model - Download indicator
|
||||
|
||||
**Pattern**: Progress Tracker
|
||||
**Workflow**: Queue-based sequential processing
|
||||
|
||||
## Example 4: Configuration Wizard
|
||||
|
||||
**Requirements**: Multi-step form with validation
|
||||
**Archetype**: Form
|
||||
**Components**:
|
||||
- textinput.Model array - Multiple inputs
|
||||
- help.Model - Per-step help
|
||||
- progress/indicator - Step progress
|
||||
|
||||
**Pattern**: Form Flow
|
||||
**Navigation**: Tab between fields, Enter to next step
|
||||
|
||||
## Example 5: Dashboard
|
||||
|
||||
**Requirements**: Multiple views, real-time updates
|
||||
**Archetype**: Dashboard
|
||||
**Components**:
|
||||
- tabs - View switching
|
||||
- table.Model - Data display
|
||||
- viewport.Model - Log panel
|
||||
|
||||
**Pattern**: Composable Views
|
||||
**Layout**: Tabbed with multiple panels per tab
|
||||
|
||||
## Component Selection Guide
|
||||
|
||||
| Use Case | Primary Component | Alternative | Supporting |
|
||||
|----------|------------------|-------------|-----------|
|
||||
| Log viewing | viewport | pager | textinput (search) |
|
||||
| File selection | filepicker | list | viewport (preview) |
|
||||
| Data table | table | list | paginator |
|
||||
| Text editing | textarea | textinput | viewport |
|
||||
| Progress | progress | spinner | - |
|
||||
| Multi-step | views | tabs | help |
|
||||
| Search/Filter | textinput | autocomplete | list |
|
||||
|
||||
## Complexity Matrix
|
||||
|
||||
| TUI Type | Components | Views | Estimated Time |
|
||||
|----------|-----------|-------|----------------|
|
||||
| Simple viewer | 1-2 | 1 | 1-2 hours |
|
||||
| File manager | 3-4 | 1 | 3-4 hours |
|
||||
| Installer | 3-4 | 3 | 2-3 hours |
|
||||
| Dashboard | 4-6 | 3+ | 4-6 hours |
|
||||
| Editor | 2-3 | 1-2 | 3-4 hours |
|
||||
Reference in New Issue
Block a user