add-module

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Add Module Skill

模块添加技能

Automates creation of new modules in the CLY project following established patterns.
按照既定模式自动完成CLY项目中新模块的创建工作。

Module Isolation Principle (CRITICAL)

模块隔离原则(至关重要)

The Portability Test: Before creating a module, ask: "If I copy this module folder to a new repo, how hard would it be to make it work?"
The answer should be: trivially easy.
可移植性测试:创建模块前,请自问:“如果我将这个模块文件夹复制到新仓库中,让它正常工作有多难?”
答案应该是:极其简单

Requirements for Isolation

隔离性要求

  • Self-contained: All module logic lives within its directory
  • Minimal dependencies: Only depend on stdlib, Bubbletea/Bubbles/Lipgloss, and Cobra
  • No cross-module imports: Modules NEVER import from other modules
  • Single registration point: Only touch parent's
    cmd.go
    for registration
  • Own types: Define types locally, don't reach into other packages
  • 自包含:所有模块逻辑都存放在其目录内
  • 最小依赖:仅依赖标准库、Bubbletea/Bubbles/Lipgloss和Cobra
  • 禁止跨模块导入:模块绝对不能从其他模块导入代码
  • 单一注册点:仅需修改父级的
    cmd.go
    完成注册
  • 本地类型定义:在本地定义类型,不要引用其他包的类型

What a Module Can Import

模块允许导入的内容

✓ Standard library (fmt, strings, etc.)
✓ github.com/charmbracelet/bubbletea
✓ github.com/charmbracelet/bubbles/*
✓ github.com/charmbracelet/lipgloss
✓ github.com/spf13/cobra
✓ github.com/yurifrl/cly/pkg/*  (shared utilities - see below)
✗ github.com/yurifrl/cly/modules/*  (NEVER)
✓ 标准库(fmt、strings等)
✓ github.com/charmbracelet/bubbletea
✓ github.com/charmbracelet/bubbles/*
✓ github.com/charmbracelet/lipgloss
✓ github.com/spf13/cobra
✓ github.com/yurifrl/cly/pkg/* (共享工具类 - 见下文)
✗ github.com/yurifrl/cly/modules/* (绝对禁止)

Avoiding Duplication (The Balance)

避免重复代码(平衡之道)

Isolation doesn't mean blind copy-paste. Use
pkg/
for genuinely shared code:
LocationPurposeExample
pkg/style/
Shared Lipgloss stylesColors, borders, common styles
pkg/keys/
Common keybindingsQuit keys, navigation patterns
pkg/tui/
TUI utilitiesScreen helpers, common components
Rule of Three: Only extract to
pkg/
when 3+ modules need the same code.
Duplication is OK when:
  • Variations exist between modules
  • Extraction would create tight coupling
Extract to pkg/ when:
  • Exact same code in 3+ places
  • Code is substantial and stable
  • Changes should propagate everywhere
隔离性不意味着盲目复制粘贴。可将真正需要共享的代码放入
pkg/
目录:
位置用途示例
pkg/style/
共享Lipgloss样式颜色、边框、通用样式
pkg/keys/
通用按键绑定退出键、导航模式
pkg/tui/
TUI工具类屏幕辅助函数、通用组件
三次原则:只有当3个及以上模块需要相同代码时,才将其提取到
pkg/
中。
以下情况允许重复代码
  • 模块之间存在功能差异
  • 提取代码会导致强耦合
以下情况需提取到pkg/中
  • 3个及以上位置存在完全相同的代码
  • 代码具备一定规模且稳定
  • 代码变更需要同步到所有使用处

Module Directory = Complete Unit

模块目录 = 完整单元

modules/demo/spinner/
├── cmd.go        # Registration only
├── spinner.go    # All logic here
└── (optional)    # Helpers if needed, but keep in same package
Copy this folder → paste in new project → change import path → works.
modules/demo/spinner/
├── cmd.go        # 仅用于注册
├── spinner.go    # 所有逻辑存放于此
└── (可选)        # 辅助文件(如需),但需保持在同一个包内
复制该文件夹 → 粘贴到新项目 → 修改导入路径 → 即可正常运行。

When to Use This Skill

何时使用本技能

  • User wants to add a new demo module showcasing a Bubbletea component
  • User wants to create a new utility command
  • User mentions "create a module", "add a command", "new demo"
  • 用户希望添加展示Bubbletea组件的新演示模块
  • 用户希望创建新的实用工具命令
  • 用户提到“创建模块”、“添加命令”、“新演示”等需求

Module Types

模块类型

Demo Modules (
modules/demo/<name>/
)

演示模块(
modules/demo/<name>/

Purpose: Showcase Charm UI components and patterns Examples: chat, spinner, table, list-simple (48 total) Parent: Registered under
demo
namespace
用途:展示Charm UI组件和模式 示例:chat、spinner、table、list-simple(共48个) 父级:注册在
demo
命名空间下

Utility Modules (
modules/<name>/
)

实用工具模块(
modules/<name>/

Purpose: Provide real functionality Examples: uuid (UUID generator) Parent: Registered directly under root command
用途:提供实际功能 示例:uuid(UUID生成器) 父级:直接注册在根命令下

Step-by-Step Workflow

分步工作流程

Determine Module Type

确定模块类型

Ask user if unclear:
  • "Is this a demo (showcase component) or utility (real functionality)?"
若不确定,询问用户:
  • “这是演示模块(展示组件)还是实用工具模块(提供实际功能)?”

Find Reference (for demos)

查找参考示例(针对演示模块)

  • Check if component exists in
    references/bubbletea/examples/<name>/
  • Read the reference implementation
  • Note initialization code in main() function
  • 检查
    references/bubbletea/examples/<name>/
    中是否存在对应组件
  • 阅读参考实现代码
  • 记录main()函数中的初始化代码

Create Directory Structure

创建目录结构

bash
undefined
bash
undefined

For demo:

演示模块:

mkdir -p modules/demo/<name>
mkdir -p modules/demo/<name>

For utility:

实用工具模块:

mkdir -p modules/<name>
undefined
mkdir -p modules/<name>
undefined

Create cmd.go (Command Registration)

创建cmd.go(命令注册)

Template for demos:
go
package <packagename>

import (
	tea "github.com/charmbracelet/bubbletea"
	"github.com/spf13/cobra"
)

func Register(parent *cobra.Command) {
	cmd := &cobra.Command{
		Use:   "<name>",
		Short: "<description>",
		RunE:  run,
	}
	parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
	p := tea.NewProgram(initialModel())
	if _, err := p.Run(); err != nil {
		return err
	}
	return nil
}
Add tea.Program options if needed:
  • tea.WithAltScreen()
    - For fullscreen demos
  • tea.WithMouseAllMotion()
    - For mouse tracking
  • tea.WithReportFocus()
    - For focus/blur events
演示模块模板:
go
package <packagename>

import (
	tea "github.com/charmbracelet/bubbletea"
	"github.com/spf13/cobra"
)

func Register(parent *cobra.Command) {
	cmd := &cobra.Command{
		Use:   "<name>",
		Short: "<description>",
		RunE:  run,
	}
	parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
	p := tea.NewProgram(initialModel())
	if _, err := p.Run(); err != nil {
		return err
	}
	return nil
}
如需添加tea.Program选项:
  • tea.WithAltScreen()
    - 用于全屏演示
  • tea.WithMouseAllMotion()
    - 用于鼠标追踪
  • tea.WithReportFocus()
    - 用于获取焦点/失焦事件

Create Implementation File

创建实现文件

Extract from reference:
  • Copy type definitions (model struct, custom types)
  • Copy Init(), Update(), View() methods
  • Create initialModel() from main() function's initialization code
  • Remove unused imports (fmt, os, log often unused after main() removal)
Template:
go
package <packagename>

import (
	tea "github.com/charmbracelet/bubbletea"
	// Component imports as needed
)

type model struct {
	// State fields
}

func initialModel() model {
	// Initialization from reference's main()
	return model{}
}

func (m model) Init() tea.Cmd {
	return nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.String() {
		case "q", "ctrl+c":
			return m, tea.Quit
		}
	}
	return m, nil
}

func (m model) View() string {
	return "Your UI\n"
}
从参考示例中提取内容:
  • 复制类型定义(model结构体、自定义类型)
  • 复制Init()、Update()、View()方法
  • 从main()函数的初始化代码中创建initialModel()
  • 移除未使用的导入(移除main()后,fmt、os、log通常不再被使用)
模板:
go
package <packagename>

import (
	tea "github.com/charmbracelet/bubbletea"
	// 根据需要导入组件
)

type model struct {
	// 状态字段
}

func initialModel() model {
	// 从参考示例的main()中提取初始化逻辑
	return model{}
}

func (m model) Init() tea.Cmd {
	return nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.String() {
		case "q", "ctrl+c":
			return m, tea.Quit
		}
	}
	return m, nil
}

func (m model) View() string {
	return "Your UI\n"
}

Register Module

注册模块

For demos - Edit
modules/demo/cmd.go
:
go
import (
	yourmodule "github.com/yurifrl/cly/modules/demo/your-module"
)

func init() {
	// ... existing registrations
	yourmodule.Register(DemoCmd)
}
For utilities - Edit
cmd/root.go
:
go
import (
	"github.com/yurifrl/cly/modules/yourutil"
)

func init() {
	// ... existing registrations
	yourutil.Register(RootCmd)
}
演示模块 - 编辑
modules/demo/cmd.go
go
import (
	yourmodule "github.com/yurifrl/cly/modules/demo/your-module"
)

func init() {
	// ... 已有的注册代码
	yourmodule.Register(DemoCmd)
}
实用工具模块 - 编辑
cmd/root.go
go
import (
	"github.com/yurifrl/cly/modules/yourutil"
)

func init() {
	// ... 已有的注册代码
	yourutil.Register(RootCmd)
}

Validation Checklist

验证检查清单

  • Compiles:
    go build
  • Shows in help:
    go run main.go --help
    or
    go run main.go demo --help
  • Runs:
    go run main.go <command>
    (or
    go run main.go demo <name>
    )
  • Quits cleanly with 'q' or Ctrl+C
  • No unused imports
  • 可编译:
    go build
  • 显示在帮助中:
    go run main.go --help
    go run main.go demo --help
  • 可运行:
    go run main.go <command>
    (或
    go run main.go demo <name>
  • 可通过'q'或Ctrl+C正常退出
  • 无未使用的导入

Common Patterns

通用模式

Package Naming

包命名

  • Directory with hyphens:
    list-simple/
  • Package name with underscores:
    package list_simple
  • Import alias:
    listsimple "github.com/yurifrl/cly/modules/demo/list-simple"
  • 目录名含连字符:
    list-simple/
  • 包名含下划线:
    package list_simple
  • 导入别名:
    listsimple "github.com/yurifrl/cly/modules/demo/list-simple"

Extracting initialModel()

提取initialModel()

In reference main():
go
func main() {
	s := spinner.New()
	s.Spinner = spinner.Dot
	m := model{spinner: s}
	tea.NewProgram(m).Run()
}
Extract to:
go
func initialModel() model {
	s := spinner.New()
	s.Spinner = spinner.Dot
	return model{spinner: s}
}
参考示例的main()函数:
go
func main() {
	s := spinner.New()
	s.Spinner = spinner.Dot
	m := model{spinner: s}
	tea.NewProgram(m).Run()
}
提取后:
go
func initialModel() model {
	s := spinner.New()
	s.Spinner = spinner.Dot
	return model{spinner: s}
}

Helper Functions

辅助函数

If reference has helpers (like getPackages(), filter(), etc.), copy them to the implementation file.
若参考示例中有辅助函数(如getPackages()、filter()等),将其复制到实现文件中。

Examples to Reference

参考示例

Simple:
modules/demo/spinner/
- Basic component Complex:
modules/demo/list-fancy/
- Multiple files (delegate.go, randomitems.go) Utility:
modules/uuid/
- Real functionality with list UI Advanced:
modules/demo/chat/
- Multiple components (textarea + viewport)
简单示例
modules/demo/spinner/
- 基础组件 复杂示例
modules/demo/list-fancy/
- 多文件(delegate.go、randomitems.go) 实用工具示例
modules/uuid/
- 带列表UI的实际功能 高级示例
modules/demo/chat/
- 多组件(textarea + viewport)

Quick Reference Commands

快速参考命令

bash
undefined
bash
undefined

Test compilation

测试编译

go build
go build

View help

查看帮助

go run main.go --help go run main.go demo --help
go run main.go --help go run main.go demo --help

Run demo

运行演示模块

go run main.go demo <name>
go run main.go demo <name>

Run utility

运行实用工具模块

go run main.go <name>
go run main.go <name>

Clean dependencies

清理依赖

go mod tidy
undefined
go mod tidy
undefined

Best Practices

最佳实践

Start from reference - All 48 Bubbletea examples available in references/ Copy existing module - Fastest way to get structure right Test incrementally - Build and run after each file Clean imports early - Remove fmt/os/log before testing Follow naming - Hyphens in names, underscores in packages
从参考示例开始 - references/中提供了全部48个Bubbletea示例 复制现有模块 - 快速获取正确结构的最佳方式 增量测试 - 每完成一个文件就编译运行测试 尽早清理导入 - 测试前移除fmt/os/log等未使用的导入 遵循命名规范 - 名称用连字符,包名用下划线

Troubleshooting

故障排查

Build Errors

编译错误

  • "undefined: initialModel" → Function not created or private (make sure it's
    initialModel
    , not
    InitialModel
    )
  • "unused import" → Remove it from imports
  • "package name mismatch" → Check hyphens vs underscores
  • "undefined: initialModel" → 未创建该函数或函数为私有(确保是
    initialModel
    而非
    InitialModel
  • "unused import" → 从导入中移除该包
  • "package name mismatch" → 检查连字符与下划线的对应关系

Runtime Errors

运行时错误

  • "could not open TTY" → Normal in non-interactive shells, try in terminal
  • Component not responding → Check Update() delegates to component's Update()
  • Can't quit → Verify KeyMsg handling for "q" and "ctrl+c"

  • "could not open TTY" → 在非交互式Shell中属于正常情况,请在终端中尝试运行
  • 组件无响应 → 检查Update()是否委托给了组件的Update()
  • 无法退出 → 验证KeyMsg是否处理了"q"和"ctrl+c"

Module Template

模块模板

Use this template to add new commands quickly.

使用本模板快速添加新命令。

Module Categories

模块分类

Demo Modules (UI Component Showcases)

演示模块(UI组件展示)

Location:
modules/demo/<name>/
Purpose: Demonstrate Charm components and patterns Examples:
chat
,
spinner
,
table
,
list-simple
When to use: Showcasing UI components, TUI patterns, Bubbletea features
位置
modules/demo/<name>/
用途:演示Charm组件和模式 示例
chat
spinner
table
list-simple
适用场景:展示UI组件、TUI模式、Bubbletea特性

Utility Modules (Real Functionality)

实用工具模块(实际功能)

Location:
modules/<name>/
Purpose: Provide actual utility commands Examples:
uuid
(UUID generator)
When to use: Commands users will actually use for work

位置
modules/<name>/
用途:提供实用命令 示例
uuid
(UUID生成器)
适用场景:用户实际工作中会用到的命令

Quick Steps

快速步骤

For Demo Modules

演示模块

  • Find reference:
    references/bubbletea/examples/<component>/
  • Copy pattern:
    cp -r modules/demo/spinner modules/demo/<newname>
  • Adapt implementation from reference
  • Register in
    modules/demo/cmd.go
    init()
  • Test
  • 查找参考示例:
    references/bubbletea/examples/<component>/
  • 复制模板:
    cp -r modules/demo/spinner modules/demo/<newname>
  • 根据参考示例调整实现代码
  • modules/demo/cmd.go
    的init()中注册
  • 测试

For Utility Modules

实用工具模块

  • Copy pattern:
    cp -r modules/uuid modules/<newname>
  • Implement functionality
  • Register in
    cmd/root.go
    init()
  • Test

  • 复制模板:
    cp -r modules/uuid modules/<newname>
  • 实现功能
  • cmd/root.go
    的init()中注册
  • 测试

Demo Module Template

演示模块模板

File:
modules/demo/<name>/cmd.go

文件:
modules/demo/<name>/cmd.go

go
package <packagename>  // Use underscores for hyphens: list_simple for list-simple

import (
	tea "github.com/charmbracelet/bubbletea"
	"github.com/spf13/cobra"
)

func Register(parent *cobra.Command) {
	cmd := &cobra.Command{
		Use:   "<name>",
		Short: "<short description>",
		Long:  "<detailed description>",
		RunE:  run,
	}

	parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
	p := tea.NewProgram(initialModel())
	if _, err := p.Run(); err != nil {
		return err
	}
	return nil
}
go
package <packagename>  // 含连字符的目录对应下划线的包名:list-simple对应list_simple

import (
	tea "github.com/charmbracelet/bubbletea"
	"github.com/spf13/cobra"
)

func Register(parent *cobra.Command) {
	cmd := &cobra.Command{
		Use:   "<name>",
		Short: "<简短描述>",
		Long:  "<详细描述>",
		RunE:  run,
	}

	parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
	p := tea.NewProgram(initialModel())
	if _, err := p.Run(); err != nil {
		return err
	}
	return nil
}

File:
modules/demo/<name>/<name>.go

文件:
modules/demo/<name>/<name>.go

go
package <packagename>

import (
	tea "github.com/charmbracelet/bubbletea"
	// Add component imports as needed:
	// "github.com/charmbracelet/bubbles/spinner"
	// "github.com/charmbracelet/bubbles/list"
	// "github.com/charmbracelet/bubbles/table"
	// "github.com/charmbracelet/lipgloss"
)

type model struct {
	// Component state
	quitting bool
	err      error
}

func initialModel() model {
	// Initialize your model here
	// Extract this from reference example's main() function
	return model{}
}

func (m model) Init() tea.Cmd {
	return nil
	// Or return component's Init: spinner.Tick, textarea.Blink, etc.
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.String() {
		case "q", "ctrl+c":
			m.quitting = true
			return m, tea.Quit
		}
	}

	return m, nil
}

func (m model) View() string {
	if m.quitting {
		return "Goodbye!\n"
	}

	return "Your UI here\nPress q to quit\n"
}

go
package <packagename>

import (
	tea "github.com/charmbracelet/bubbletea"
	// 根据需要添加组件导入:
	// "github.com/charmbracelet/bubbles/spinner"
	// "github.com/charmbracelet/bubbles/list"
	// "github.com/charmbracelet/bubbles/table"
	// "github.com/charmbracelet/lipgloss"
)

type model struct {
	// 组件状态
	quitting bool
	err      error
}

func initialModel() model {
	// 在此处初始化模型
	// 从参考示例的main()函数中提取逻辑
	return model{}
}

func (m model) Init() tea.Cmd {
	return nil
	// 或返回组件的Init:spinner.Tick、textarea.Blink等
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.String() {
		case "q", "ctrl+c":
			m.quitting = true
			return m, tea.Quit
		}
	}

	return m, nil
}

func (m model) View() string {
	if m.quitting {
		return "Goodbye!\n"
	}

	return "Your UI here\nPress q to quit\n"
}

Registration Patterns

注册模式

Demo Module Registration

演示模块注册

File:
modules/demo/cmd.go
go
import (
	// ...
	yourmodule "github.com/yurifrl/cly/modules/demo/your-module"
)

func init() {
	// ...
	yourmodule.Register(DemoCmd)
}
文件
modules/demo/cmd.go
go
import (
	// ...
	yourmodule "github.com/yurifrl/cly/modules/demo/your-module"
)

func init() {
	// ...
	yourmodule.Register(DemoCmd)
}

Utility Module Registration

实用工具模块注册

File:
cmd/root.go
go
import (
	// ...
	"github.com/yurifrl/cly/modules/yourutil"
)

func init() {
	uuid.Register(RootCmd)
	demo.Register(RootCmd)
	yourutil.Register(RootCmd)  // Add here
}

文件
cmd/root.go
go
import (
	// ...
	"github.com/yurifrl/cly/modules/yourutil"
)

func init() {
	uuid.Register(RootCmd)
	demo.Register(RootCmd)
	yourutil.Register(RootCmd)  // 在此添加
}

Bubbletea Program Options

Bubbletea Program选项

Some demos require special options when creating the tea.Program:
部分演示在创建tea.Program时需要特殊选项:

AltScreen (Fullscreen Mode)

AltScreen(全屏模式)

go
func run(cmd *cobra.Command, args []string) error {
	p := tea.NewProgram(initialModel(), tea.WithAltScreen())
	_, err := p.Run()
	return err
}
Use when: Demo should use alternate screen buffer (fullscreen, eyes, cellbuffer) Examples:
modules/demo/fullscreen/
,
modules/demo/eyes/
go
func run(cmd *cobra.Command, args []string) error {
	p := tea.NewProgram(initialModel(), tea.WithAltScreen())
	_, err := p.Run()
	return err
}
适用场景:演示需要使用备用屏幕缓冲区(全屏、eyes、cellbuffer) 示例
modules/demo/fullscreen/
modules/demo/eyes/

Mouse Support

鼠标支持

go
p := tea.NewProgram(initialModel(), tea.WithMouseAllMotion())
Use when: Demo needs mouse tracking Example:
modules/demo/mouse/
go
p := tea.NewProgram(initialModel(), tea.WithMouseAllMotion())
适用场景:演示需要追踪鼠标 示例
modules/demo/mouse/

Focus Reporting

焦点报告

go
p := tea.NewProgram(initialModel(), tea.WithReportFocus())
Use when: Demo needs to know when terminal gains/loses focus Example:
modules/demo/focus-blur/
go
p := tea.NewProgram(initialModel(), tea.WithReportFocus())
适用场景:演示需要知道终端何时获取/失去焦点 示例
modules/demo/focus-blur/

Input Filtering

输入过滤

go
p := tea.NewProgram(initialModel(), tea.WithFilter(filterFunc))
Use when: Need to intercept/modify messages before Update() Example:
modules/demo/prevent-quit/

go
p := tea.NewProgram(initialModel(), tea.WithFilter(filterFunc))
适用场景:需要在Update()之前拦截/修改消息 示例
modules/demo/prevent-quit/

Reference Examples (48 Available)

参考示例(共48个)

All 48 Bubbletea examples are in
references/bubbletea/examples/
and
modules/demo/
:
全部48个Bubbletea示例位于
references/bubbletea/examples/
modules/demo/
中:

Core Components

核心组件

DemoShowsReference
spinner
Animated loading
references/bubbletea/examples/spinner
list-simple
Selection lists
references/bubbletea/examples/list-simple
table
Data tables
references/bubbletea/examples/table
textinput
Single-line input
references/bubbletea/examples/textinput
textarea
Multi-line input
references/bubbletea/examples/textarea
progress-static
Progress bars
references/bubbletea/examples/progress-static
演示模块展示内容参考位置
spinner
动画加载效果
references/bubbletea/examples/spinner
list-simple
选择列表
references/bubbletea/examples/list-simple
table
数据表格
references/bubbletea/examples/table
textinput
单行输入框
references/bubbletea/examples/textinput
textarea
多行输入框
references/bubbletea/examples/textarea
progress-static
进度条
references/bubbletea/examples/progress-static

Advanced

高级示例

DemoShowsReference
chat
Textarea + Viewport
references/bubbletea/examples/chat
file-picker
File selection
references/bubbletea/examples/file-picker
credit-card-form
Complex forms
references/bubbletea/examples/credit-card-form
split-editors
Multiple panes
references/bubbletea/examples/split-editors
All 48 examples are available - explore
modules/demo/
for implementations.

演示模块展示内容参考位置
chat
文本框 + 视口
references/bubbletea/examples/chat
file-picker
文件选择器
references/bubbletea/examples/file-picker
credit-card-form
复杂表单
references/bubbletea/examples/credit-card-form
split-editors
多面板
references/bubbletea/examples/split-editors
全部48个示例均可使用 - 可浏览
modules/demo/
查看实现代码。

Adapting Reference Examples

适配参考示例的步骤

Step-by-Step Process

分步流程

Find reference:
references/bubbletea/examples/<component>/main.go
Read main() function: This has initialization code Extract to initialModel(): Move setup from main() to initialModel() Copy Model implementation: Copy type definitions, Init(), Update(), View() Clean imports: Remove
fmt
,
os
,
log
if unused Create cmd.go: Use template above with Register() function
查找参考示例
references/bubbletea/examples/<component>/main.go
阅读main()函数:此处包含初始化代码 提取到initialModel():将main()中的设置逻辑移至initialModel() 复制Model实现:复制类型定义、Init()、Update()、View() 清理导入:若未使用则移除
fmt
os
log
创建cmd.go:使用上述模板编写Register()函数

Example: Adapting Spinner

示例:适配Spinner模块

Reference:
references/bubbletea/examples/spinner/main.go
Extract this from main():
go
s := spinner.New()
s.Spinner = spinner.Dot
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
return model{spinner: s}
Becomes initialModel():
go
func initialModel() model {
	s := spinner.New()
	s.Spinner = spinner.Dot
	s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
	return model{spinner: s}
}

参考示例
references/bubbletea/examples/spinner/main.go
从main()中提取以下内容
go
s := spinner.New()
s.Spinner = spinner.Dot
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
return model{spinner: s}
转换为initialModel()
go
func initialModel() model {
	s := spinner.New()
	s.Spinner = spinner.Dot
	s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
	return model{spinner: s}
}

Naming Conventions

命名规范

Command Names

命令名称

  • Lowercase only
  • Hyphens for multi-word:
    list-simple
    ,
    credit-card-form
    ,
    altscreen-toggle
  • 仅使用小写字母
  • 多词名称使用连字符分隔:
    list-simple
    credit-card-form
    altscreen-toggle

Package Names

包名称

  • Lowercase, no hyphens
  • Use underscores:
    list_simple
    ,
    credit_card_form
    ,
    altscreen_toggle
  • Go converts hyphens automatically during import
  • 仅使用小写字母禁止使用连字符
  • 使用下划线分隔:
    list_simple
    credit_card_form
    altscreen_toggle
  • Go在导入时会自动转换连字符

File Names

文件名称

  • cmd.go
    - Always this name (command registration)
  • <name>.go
    - Main implementation (e.g.,
    spinner.go
    ,
    list-simple.go
    )
  • Additional files:
    delegate.go
    ,
    helpers.go
    ,
    types.go
    (if needed)

  • cmd.go
    - 固定名称(用于命令注册)
  • <name>.go
    - 主实现文件(如
    spinner.go
    list-simple.go
  • 额外文件:
    delegate.go
    helpers.go
    types.go
    (如需拆分复杂逻辑)

Checklist

检查清单

Before Implementation

实现前

  • Decided: demo or utility module?
  • Found reference example (if demo)
  • Command name chosen (lowercase, hyphens if multi-word)
  • 确定:是演示模块还是实用工具模块?
  • 找到参考示例(针对演示模块)
  • 选定命令名称(小写,多词用连字符)

During Implementation

实现中

  • Created directory in correct location
  • Created cmd.go with Register() function
  • Created implementation file with initialModel()
  • Package name matches conventions
  • Imports are clean (no unused)
  • 在正确位置创建目录
  • 创建带Register()函数的cmd.go
  • 创建带initialModel()的实现文件
  • 包名称符合规范
  • 导入列表已清理(无未使用的包)

After Implementation

实现后

  • Registered in parent cmd.go init()
  • Import added to parent cmd.go
  • Compiles:
    go build
  • Appears in help:
    go run main.go --help
    or
    go run main.go demo --help
  • Runs:
    go run main.go <command>
  • Quits cleanly with 'q' or Ctrl+C

  • 在父级cmd.go的init()中完成注册
  • 父级cmd.go中已添加对应导入
  • 可编译:
    go build
  • 显示在帮助中:
    go run main.go --help
    go run main.go demo --help
  • 可运行:
    go run main.go <command>
  • 可通过'q'或Ctrl+C正常退出

Tips

技巧

Start with existing demos - 48 working examples to learn from Copy working code - Don't reinvent, adapt from references Test frequently - Build and run after each change Keep it simple - Single file until complexity demands splitting Use shared styles - Import
pkg/style
for consistent theming Follow the pattern - Look at 3-4 similar modules before starting

从现有演示模块开始 - 48个可运行的示例可供学习 复制可运行代码 - 不要重复造轮子,从参考示例中适配 频繁测试 - 每完成一处修改就编译运行测试 保持简洁 - 除非复杂度要求,否则使用单一文件 使用共享样式 - 导入
pkg/style
以保持主题一致性 遵循现有模式 - 开始前先查看3-4个同类模块

Troubleshooting

故障排查

"undefined: initialModel"

"undefined: initialModel"

  • Make sure initialModel() function exists in implementation file
  • Check it's exported (lowercase 'i' makes it package-private)
  • 确保实现文件中存在initialModel()函数
  • 检查函数是否为包级可见(小写的'i'表示私有函数)

"package name mismatch"

"package name mismatch"

  • Directory name with hyphens → package name with underscores
  • Example:
    list-simple/
    package list_simple
  • 含连字符的目录名对应带下划线的包名
  • 示例:
    list-simple/
    package list_simple

"unused import"

"unused import"

  • Remove
    fmt
    ,
    os
    ,
    log
    if not actually used
  • Check your View() and Update() functions
  • Common after removing main() function
  • 若未实际使用,移除
    fmt
    os
    log
  • 检查View()和Update()函数
  • 移除main()后经常会出现此类情况

"command not showing in help"

"command not showing in help"

  • Verify Register() called in parent's init()
  • Check import path is correct
  • Run
    go mod tidy

  • 验证父级的init()中是否调用了Register()
  • 检查导入路径是否正确
  • 运行
    go mod tidy

Advanced Patterns

高级模式

Multiple Files (Complex Modules)

多文件(复杂模块)

modules/demo/list-fancy/
├── cmd.go           # Command registration
├── list-fancy.go    # Model and main logic
├── delegate.go      # Custom item delegate
└── randomitems.go   # Helper functions
modules/demo/list-fancy/
├── cmd.go           # 命令注册
├── list-fancy.go    # 模型和主逻辑
├── delegate.go      # 自定义项委托
└── randomitems.go   # 辅助函数

With Flags

带参数的模块

go
var demoType string

func Register(parent *cobra.Command) {
	cmd := &cobra.Command{
		Use:   "demo",
		Short: "Demo with flag",
		RunE:  run,
	}

	cmd.Flags().StringVarP(&demoType, "type", "t", "default", "Demo type")
	parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
	// Use demoType variable in initialModel()
	p := tea.NewProgram(initialModel(demoType))
	_, err := p.Run()
	return err
}
go
var demoType string

func Register(parent *cobra.Command) {
	cmd := &cobra.Command{
		Use:   "demo",
		Short: "Demo with flag",
		RunE:  run,
	}

	cmd.Flags().StringVarP(&demoType, "type", "t", "default", "Demo type")
	parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
	// 在initialModel()中使用demoType变量
	p := tea.NewProgram(initialModel(demoType))
	_, err := p.Run()
	return err
}

With Required Args

带必填参数的模块

go
func Register(parent *cobra.Command) {
	cmd := &cobra.Command{
		Use:   "download <url>",
		Short: "Download with progress",
		Args:  cobra.ExactArgs(1),  // Require 1 argument
		RunE:  run,
	}
	parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
	url := args[0]
	p := tea.NewProgram(initialModel(url))
	_, err := p.Run()
	return err
}
go
func Register(parent *cobra.Command) {
	cmd := &cobra.Command{
		Use:   "download <url>",
		Short: "Download with progress",
		Args:  cobra.ExactArgs(1),  // 必填1个参数
		RunE:  run,
	}
	parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
	url := args[0]
	p := tea.NewProgram(initialModel(url))
	_, err := p.Run()
	return err
}