Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:47:30 +08:00
commit 48d6099939
30 changed files with 5747 additions and 0 deletions

View 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

View 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

View 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()
}
```

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