bubbletea

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Bubbletea TUI Development

Bubbletea TUI 开发

Production-ready skill for building beautiful terminal user interfaces with Go, Bubbletea, and Lipgloss.
这是一套可直接用于生产环境的技能指南,教你使用Go、Bubbletea和Lipgloss构建美观的终端用户界面。

When to Use This Skill

何时使用本技能

Use this skill when:
  • Creating new TUI applications with Go
  • Adding Bubbletea components to existing apps
  • Fixing layout/rendering issues (borders, alignment, overflow)
  • Implementing mouse/keyboard interactions
  • Building dual-pane or multi-panel layouts
  • Adding visual effects (metaballs, waves, rainbow text)
  • Troubleshooting TUI rendering problems
在以下场景使用本技能:
  • 使用Go开发全新的TUI应用
  • 为现有应用添加Bubbletea组件
  • 修复布局/渲染问题(边框、对齐、溢出)
  • 实现鼠标/键盘交互
  • 构建双窗格或多面板布局
  • 添加视觉特效(metaballs、波浪、彩虹文字)
  • 排查TUI渲染故障

Core Principles

核心原则

CRITICAL: Before implementing ANY layout, consult
references/golden-rules.md
for the 4 Golden Rules. These rules prevent the most common and frustrating TUI layout bugs.
至关重要:在实现任何布局之前,请查阅
references/golden-rules.md
中的4条黄金法则。这些法则能避免最常见且棘手的TUI布局bug。

The 4 Golden Rules (Summary)

4条黄金法则(摘要)

  1. Always Account for Borders - Subtract 2 from height calculations BEFORE rendering panels
  2. Never Auto-Wrap in Bordered Panels - Always truncate text explicitly
  3. Match Mouse Detection to Layout - Use X coords for horizontal, Y coords for vertical
  4. Use Weights, Not Pixels - Proportional layouts scale perfectly
Full details and examples in
references/golden-rules.md
.
  1. 始终考虑边框 - 在渲染面板前,先从高度计算中减去2
  2. 不要在带边框的面板中自动换行 - 始终显式截断文本
  3. 鼠标检测与布局匹配 - 水平布局使用X坐标,垂直布局使用Y坐标
  4. 使用权重而非像素 - 比例布局可完美缩放
详细内容及示例请见
references/golden-rules.md

Creating New Projects

创建新项目

This project includes a production-ready template system. When this skill is bundled with a new project (via
new_project.sh
), use the existing template structure as the starting point.
本项目包含一套可直接用于生产环境的模板系统。当通过
new_project.sh
将本技能捆绑到新项目时,请以现有模板结构作为起点。

Project Structure

项目结构

All new projects follow this architecture:
your-app/
├── main.go              # Entry point (minimal, ~21 lines)
├── types.go             # Type definitions, structs, enums
├── model.go             # Model initialization & layout calculation
├── update.go            # Message dispatcher
├── update_keyboard.go   # Keyboard handling
├── update_mouse.go      # Mouse handling
├── view.go              # View rendering & layouts
├── styles.go            # Lipgloss style definitions
├── config.go            # Configuration management
└── .claude/skills/bubbletea/  # This skill (bundled)
所有新项目均遵循以下架构:
your-app/
├── main.go              # 入口文件(极简,约21行)
├── types.go             # 类型定义、结构体、枚举
├── model.go             # Model初始化与布局计算
├── update.go            # 消息分发器
├── update_keyboard.go   # 键盘处理
├── update_mouse.go      # 鼠标处理
├── view.go              # 视图渲染与布局
├── styles.go            # Lipgloss样式定义
├── config.go            # 配置管理
└── .claude/skills/bubbletea/  # 本技能(已捆绑)

Architecture Guidelines

架构指南

  • Keep
    main.go
    minimal (entry point only, ~21 lines)
  • All types in
    types.go
    (structs, enums, constants)
  • Separate keyboard and mouse handling into dedicated files
  • One file, one responsibility
  • Maximum file size: 800 lines (ideally <500)
  • Configuration via YAML with hot-reload support
  • 保持
    main.go
    极简(仅作为入口,约21行)
  • 所有类型定义放在
    types.go
    中(结构体、枚举、常量)
  • 将键盘和鼠标处理分离到独立文件
  • 单一文件单一职责
  • 文件最大行数:800行(理想情况下<500行)
  • 支持基于YAML的配置及热重载

Available Components

可用组件

See
references/components.md
for the complete catalog of reusable components:
  • Panel System: Single, dual-pane, multi-panel, tabbed layouts
  • Lists: Simple list, filtered list, tree view
  • Input: Text input, multiline, forms, autocomplete
  • Dialogs: Confirm, input, progress, modal
  • Menus: Context menu, command palette, menu bar
  • Status: Status bar, title bar, breadcrumbs
  • Preview: Text, markdown, syntax highlighting, images, hex
  • Tables: Simple and interactive tables
完整的可复用组件目录请见
references/components.md
  • 面板系统:单面板、双窗格、多面板、标签页布局
  • 列表:简单列表、过滤列表、树形视图
  • 输入组件:文本输入、多行输入、表单、自动补全
  • 对话框:确认框、输入框、进度条、模态框
  • 菜单:上下文菜单、命令面板、菜单栏
  • 状态栏:状态栏、标题栏、面包屑导航
  • 预览组件:文本、Markdown、语法高亮、图片、十六进制预览
  • 表格:简单表格与交互式表格

Effects Library

特效库

Beautiful physics-based animations available in the template:
  • 🔮 Metaballs - Lava lamp-style floating blobs
  • 🌊 Wave Effects - Sine wave distortions
  • 🌈 Rainbow Cycling - Animated color gradients
  • 🎭 Layer Compositor - ANSI-aware multi-layer rendering
See
references/effects.md
for usage examples and integration patterns.
模板中包含基于物理的精美动画:
  • 🔮 Metaballs - 熔岩灯风格的浮动 blob 效果
  • 🌊 波浪特效 - 正弦波扭曲效果
  • 🌈 彩虹循环 - 动画渐变色彩
  • 🎭 图层合成器 - 支持ANSI的多层渲染
使用示例及集成模式请见
references/effects.md

Layout Implementation Pattern

布局实现模式

When implementing layouts, follow this sequence:
实现布局时,请遵循以下步骤:

1. Calculate Available Space

1. 计算可用空间

go
func (m model) calculateLayout() (int, int) {
    contentWidth := m.width
    contentHeight := m.height

    // Subtract UI elements
    if m.config.UI.ShowTitle {
        contentHeight -= 3  // title bar (3 lines)
    }
    if m.config.UI.ShowStatus {
        contentHeight -= 1  // status bar
    }

    // CRITICAL: Account for panel borders
    contentHeight -= 2  // top + bottom borders

    return contentWidth, contentHeight
}
go
func (m model) calculateLayout() (int, int) {
    contentWidth := m.width
    contentHeight := m.height

    // 减去UI元素占用的空间
    if m.config.UI.ShowTitle {
        contentHeight -= 3  // 标题栏(3行)
    }
    if m.config.UI.ShowStatus {
        contentHeight -= 1  // 状态栏
    }

    // 至关重要:考虑边框占用
    contentHeight -= 2  // 顶部+底部边框

    return contentWidth, contentHeight
}

2. Use Weight-Based Panel Sizing

2. 使用基于权重的面板尺寸计算

go
// Calculate weights based on focus/accordion mode
leftWeight, rightWeight := 1, 1
if m.accordionMode && m.focusedPanel == "left" {
    leftWeight = 2  // Focused panel gets 2x weight
}

// Calculate actual widths from weights
totalWeight := leftWeight + rightWeight
leftWidth := (availableWidth * leftWeight) / totalWeight
rightWidth := availableWidth - leftWidth
go
// 根据焦点/折叠模式计算权重
leftWeight, rightWeight := 1, 1
if m.accordionMode && m.focusedPanel == "left" {
    leftWeight = 2  // 获焦面板占用2倍权重
}

// 根据权重计算实际宽度
totalWeight := leftWeight + rightWeight
leftWidth := (availableWidth * leftWeight) / totalWeight
rightWidth := availableWidth - leftWidth

3. Truncate Text to Prevent Wrapping

3. 截断文本以避免自动换行

go
// Calculate max text width to prevent wrapping
maxTextWidth := panelWidth - 4  // -2 borders, -2 padding

// Truncate ALL text before rendering
title = truncateString(title, maxTextWidth)
subtitle = truncateString(subtitle, maxTextWidth)

func truncateString(s string, maxLen int) string {
    if len(s) <= maxLen {
        return s
    }
    return s[:maxLen-1] + "…"
}
go
// 计算最大文本宽度以防止换行
maxTextWidth := panelWidth - 4  // -2边框,-2内边距

// 渲染前截断所有文本
title = truncateString(title, maxTextWidth)
subtitle = truncateString(subtitle, maxTextWidth)

func truncateString(s string, maxLen int) string {
    if len(s) <= maxLen {
        return s
    }
    return s[:maxLen-1] + "…"
}

Mouse Interaction Pattern

鼠标交互模式

Always check layout mode before processing mouse events:
go
func (m model) handleLeftClick(msg tea.MouseMsg) (tea.Model, tea.Cmd) {
    if m.shouldUseVerticalStack() {
        // Vertical stack mode: use Y coordinates
        topHeight, _ := m.calculateVerticalStackLayout()
        relY := msg.Y - contentStartY

        if relY < topHeight {
            m.focusedPanel = "left"  // Top panel
        } else {
            m.focusedPanel = "right" // Bottom panel
        }
    } else {
        // Side-by-side mode: use X coordinates
        leftWidth, _ := m.calculateDualPaneLayout()

        if msg.X < leftWidth {
            m.focusedPanel = "left"
        } else {
            m.focusedPanel = "right"
        }
    }

    return m, nil
}
处理鼠标事件前,请始终检查布局模式:
go
func (m model) handleLeftClick(msg tea.MouseMsg) (tea.Model, tea.Cmd) {
    if m.shouldUseVerticalStack() {
        // 垂直堆叠模式:使用Y坐标
        topHeight, _ := m.calculateVerticalStackLayout()
        relY := msg.Y - contentStartY

        if relY < topHeight {
            m.focusedPanel = "left"  // 顶部面板
        } else {
            m.focusedPanel = "right" // 底部面板
        }
    } else {
        // 并排模式:使用X坐标
        leftWidth, _ := m.calculateDualPaneLayout()

        if msg.X < leftWidth {
            m.focusedPanel = "left"
        } else {
            m.focusedPanel = "right"
        }
    }

    return m, nil
}

Common Pitfalls to Avoid

需避免的常见陷阱

See
references/troubleshooting.md
for detailed solutions to common issues:
常见问题的详细解决方案请见
references/troubleshooting.md

❌ DON'T: Set explicit Height() on bordered panels

❌ 错误做法:为带边框的面板设置显式Height()

go
// BAD: Can cause misalignment
panelStyle := lipgloss.NewStyle().
    Border(border).
    Height(height)  // Don't do this!
go
// 错误:会导致对齐问题
panelStyle := lipgloss.NewStyle().
    Border(border).
    Height(height)  // 不要这样做!

✅ DO: Fill content to exact height

✅ 正确做法:将内容填充至精确高度

go
// GOOD: Fill content lines to exact height
for len(lines) < innerHeight {
    lines = append(lines, "")
}
panelStyle := lipgloss.NewStyle().Border(border)
go
// 正确:将内容行填充至精确高度
for len(lines) < innerHeight {
    lines = append(lines, "")
}
panelStyle := lipgloss.NewStyle().Border(border)

Testing and Debugging

测试与调试

When panels don't align or render incorrectly:
  1. Check height accounting - Verify contentHeight calculation subtracts all UI elements + borders
  2. Check text wrapping - Ensure all strings are truncated to maxTextWidth
  3. Check mouse detection - Verify X/Y coordinate usage matches layout orientation
  4. Check border consistency - Use same border style for all panels
See
references/troubleshooting.md
for the complete debugging decision tree.
当面板对齐错误或渲染异常时:
  1. 检查高度计算 - 确认contentHeight的计算已减去所有UI元素及边框
  2. 检查文本换行 - 确保所有字符串均已截断至maxTextWidth
  3. 检查鼠标检测 - 验证X/Y坐标的使用是否与布局方向匹配
  4. 检查边框一致性 - 所有面板使用相同的边框样式
完整的调试决策树请见
references/troubleshooting.md

Configuration System

配置系统

All projects support YAML configuration with hot-reload:
yaml
theme: "dark"
keybindings: "default"

layout:
  type: "dual_pane"
  split_ratio: 0.5
  accordion_mode: true

ui:
  show_title: true
  show_status: true
  mouse_enabled: true
  show_icons: true
Configuration files are loaded from:
  1. ~/.config/your-app/config.yaml
    (user config)
  2. ./config.yaml
    (local override)
所有项目均支持带热重载的YAML配置:
yaml
theme: "dark"
keybindings: "default"

layout:
  type: "dual_pane"
  split_ratio: 0.5
  accordion_mode: true

ui:
  show_title: true
  show_status: true
  mouse_enabled: true
  show_icons: true
配置文件加载顺序:
  1. ~/.config/your-app/config.yaml
    (用户配置)
  2. ./config.yaml
    (本地覆盖配置)

Dependencies

依赖项

Required:
github.com/charmbracelet/bubbletea
github.com/charmbracelet/lipgloss
github.com/charmbracelet/bubbles
gopkg.in/yaml.v3
Optional (uncomment in go.mod as needed):
github.com/charmbracelet/glamour       # Markdown rendering
github.com/charmbracelet/huh           # Forms
github.com/alecthomas/chroma/v2        # Syntax highlighting
github.com/evertras/bubble-table       # Interactive tables
github.com/koki-develop/go-fzf         # Fuzzy finder
必需依赖
github.com/charmbracelet/bubbletea
github.com/charmbracelet/lipgloss
github.com/charmbracelet/bubbles
gopkg.in/yaml.v3
可选依赖(按需在go.mod中取消注释):
github.com/charmbracelet/glamour       # Markdown渲染
github.com/charmbracelet/huh           # 表单组件
github.com/alecthomas/chroma/v2        # 语法高亮
github.com/evertras/bubble-table       # 交互式表格
github.com/koki-develop/go-fzf         # 模糊查找器

Reference Documentation

参考文档

All reference files are loaded progressively as needed:
  • golden-rules.md - Critical layout patterns and anti-patterns
  • components.md - Complete catalog of reusable components
  • troubleshooting.md - Common issues and debugging decision tree
  • emoji-width-fix.md - Battle-tested solution for emoji alignment across terminals (xterm, WezTerm, Termux, Windows Terminal)
所有参考文件可按需逐步加载:
  • golden-rules.md - 关键布局模式与反模式
  • components.md - 完整的可复用组件目录
  • troubleshooting.md - 常见问题与调试决策树
  • emoji-width-fix.md - 经过验证的跨终端emoji对齐解决方案(支持xterm、WezTerm、Termux、Windows Terminal)

External Resources

外部资源

Best Practices Summary

最佳实践总结

  1. Always consult golden-rules.md before implementing layouts
  2. Always use weight-based sizing for flexible layouts
  3. Always truncate text explicitly (never rely on auto-wrap)
  4. Always match mouse detection to layout orientation
  5. Always account for borders in height calculations
  6. Never set explicit Height() on bordered Lipgloss styles
  7. Never assume layout orientation in mouse handlers
Follow these patterns and you'll avoid 90% of TUI layout bugs.
  1. 务必在实现布局前查阅golden-rules.md
  2. 务必使用基于权重的尺寸计算以实现灵活布局
  3. 务必显式截断文本(绝不依赖自动换行)
  4. 务必使鼠标检测与布局方向匹配
  5. 务必在高度计算中考虑边框
  6. 绝不为带边框的Lipgloss样式设置显式Height()
  7. 绝不在鼠标处理程序中假设布局方向
遵循这些模式,你将避免90%的TUI布局bug。