bubbletea
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBubbletea 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 for the 4 Golden Rules. These rules prevent the most common and frustrating TUI layout bugs.
references/golden-rules.md至关重要:在实现任何布局之前,请查阅 中的4条黄金法则。这些法则能避免最常见且棘手的TUI布局bug。
references/golden-rules.mdThe 4 Golden Rules (Summary)
4条黄金法则(摘要)
- Always Account for Borders - Subtract 2 from height calculations BEFORE rendering panels
- Never Auto-Wrap in Bordered Panels - Always truncate text explicitly
- Match Mouse Detection to Layout - Use X coords for horizontal, Y coords for vertical
- Use Weights, Not Pixels - Proportional layouts scale perfectly
Full details and examples in .
references/golden-rules.md- 始终考虑边框 - 在渲染面板前,先从高度计算中减去2
- 不要在带边框的面板中自动换行 - 始终显式截断文本
- 鼠标检测与布局匹配 - 水平布局使用X坐标,垂直布局使用Y坐标
- 使用权重而非像素 - 比例布局可完美缩放
详细内容及示例请见 。
references/golden-rules.mdCreating New Projects
创建新项目
This project includes a production-ready template system. When this skill is bundled with a new project (via ), use the existing template structure as the starting point.
new_project.sh本项目包含一套可直接用于生产环境的模板系统。当通过 将本技能捆绑到新项目时,请以现有模板结构作为起点。
new_project.shProject 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 minimal (entry point only, ~21 lines)
main.go - All types in (structs, enums, constants)
types.go - 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
- 保持 极简(仅作为入口,约21行)
main.go - 所有类型定义放在 中(结构体、枚举、常量)
types.go - 将键盘和鼠标处理分离到独立文件
- 单一文件单一职责
- 文件最大行数:800行(理想情况下<500行)
- 支持基于YAML的配置及热重载
Available Components
可用组件
See for the complete catalog of reusable components:
references/components.md- 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 for usage examples and integration patterns.
references/effects.md模板中包含基于物理的精美动画:
- 🔮 Metaballs - 熔岩灯风格的浮动 blob 效果
- 🌊 波浪特效 - 正弦波扭曲效果
- 🌈 彩虹循环 - 动画渐变色彩
- 🎭 图层合成器 - 支持ANSI的多层渲染
使用示例及集成模式请见 。
references/effects.mdLayout 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 - leftWidthgo
// 根据焦点/折叠模式计算权重
leftWeight, rightWeight := 1, 1
if m.accordionMode && m.focusedPanel == "left" {
leftWeight = 2 // 获焦面板占用2倍权重
}
// 根据权重计算实际宽度
totalWeight := leftWeight + rightWeight
leftWidth := (availableWidth * leftWeight) / totalWeight
rightWidth := availableWidth - leftWidth3. 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 for detailed solutions to common issues:
references/troubleshooting.md常见问题的详细解决方案请见 :
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:
- Check height accounting - Verify contentHeight calculation subtracts all UI elements + borders
- Check text wrapping - Ensure all strings are truncated to maxTextWidth
- Check mouse detection - Verify X/Y coordinate usage matches layout orientation
- Check border consistency - Use same border style for all panels
See for the complete debugging decision tree.
references/troubleshooting.md当面板对齐错误或渲染异常时:
- 检查高度计算 - 确认contentHeight的计算已减去所有UI元素及边框
- 检查文本换行 - 确保所有字符串均已截断至maxTextWidth
- 检查鼠标检测 - 验证X/Y坐标的使用是否与布局方向匹配
- 检查边框一致性 - 所有面板使用相同的边框样式
完整的调试决策树请见 。
references/troubleshooting.mdConfiguration 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: trueConfiguration files are loaded from:
- (user config)
~/.config/your-app/config.yaml - (local override)
./config.yaml
所有项目均支持带热重载的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配置文件加载顺序:
- (用户配置)
~/.config/your-app/config.yaml - (本地覆盖配置)
./config.yaml
Dependencies
依赖项
Required:
github.com/charmbracelet/bubbletea
github.com/charmbracelet/lipgloss
github.com/charmbracelet/bubbles
gopkg.in/yaml.v3Optional (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
最佳实践总结
- Always consult golden-rules.md before implementing layouts
- Always use weight-based sizing for flexible layouts
- Always truncate text explicitly (never rely on auto-wrap)
- Always match mouse detection to layout orientation
- Always account for borders in height calculations
- Never set explicit Height() on bordered Lipgloss styles
- Never assume layout orientation in mouse handlers
Follow these patterns and you'll avoid 90% of TUI layout bugs.
- 务必在实现布局前查阅golden-rules.md
- 务必使用基于权重的尺寸计算以实现灵活布局
- 务必显式截断文本(绝不依赖自动换行)
- 务必使鼠标检测与布局方向匹配
- 务必在高度计算中考虑边框
- 绝不为带边框的Lipgloss样式设置显式Height()
- 绝不在鼠标处理程序中假设布局方向
遵循这些模式,你将避免90%的TUI布局bug。