charmbracelet/bubbletea
Patterns and conventions
Recurring patterns in the codebase. Knowing them makes reviews faster and PRs more idiomatic.
Public API style
- Every public type and function has a GoDoc comment. Cross-references use
[TypeName]syntax. The lint config (.golangci.yml) enablesreviveandgodot, so missing or unterminated doc comments fail the build. - Examples in doc comments use real, runnable code blocks fenced with triple backticks. See
WithFilterinoptions.gofor the prevailing style. - Public
Cmds come in pairs: the function (tea.Quit,tea.Tick) and the corresponding message type (QuitMsg,TickMsgdefined by the user). A simple convention is "theCmdis a verb, theMsgis a noun".
Message types
- All public messages are exported types with descriptive names ending in
Msg(KeyPressMsg,MouseClickMsg,WindowSizeMsg,ColorProfileMsg, …). - Internal messages used to wire commands have lowercase types (
clearScreenMsg,requestCursorPosMsg,windowSizeMsg). The convention is: lowercase = unstable internal sentinel, capitalized = part of the public API. - The
Msgtype alias (Msg = uv.Event) means user code can use atea.Msginterchangeably with anultraviolet.Event. Adding a new event type usually means adding a case totranslateInputEventininput.go.
Error wrapping
The wrapcheck linter is enabled. Wrapping style:
if err != nil {
return fmt.Errorf("bubbletea: error opening TTY: %w", err)
}The bubbletea: prefix is consistent across tea.go, tty.go, and exec.go. Sentinel errors (ErrProgramKilled, ErrInterrupted, ErrProgramPanic) are exported in tea.go.
Concurrency
- All goroutines started by
Runregister withProgram.handlers(achannelHandlersstruct guarded by anRWMutex).shutdownwaits for them to finish. chan Msg(p.msgs) is the single owner of order. Producers must respect<-p.ctx.Done()so they unblock cleanly on shutdown.- Output bytes go through
Program.outputBufplusProgram.mu. Direct writes top.outputoutside the renderer/flush path are a code smell.
File ownership
Each file in the root package owns one concept:
| File | Concept |
|---|---|
tea.go |
Program runtime, public Model/View/Cursor types |
commands.go |
Cmd, Batch, Sequence, Tick, Every |
options.go |
ProgramOptions |
key.go / keyboard.go / mod.go |
Keyboard model |
mouse.go |
Mouse model |
clipboard.go |
OSC 52 clipboard |
cursor.go |
Cursor, CursorShape, RequestCursorPosition |
color.go / profile.go |
Color profile and color queries |
cursed_renderer.go / nil_renderer.go / renderer.go |
Renderer interface and implementations |
input.go |
Event translation from ultraviolet to tea |
tty.go / tty_unix.go / tty_windows.go |
TTY handling per platform |
signals_unix.go / signals_windows.go |
Signal handling per platform |
termios_*.go |
Termios constants per platform |
exec.go |
Exec, ExecProcess |
screen.go |
WindowSizeMsg, ClearScreen, ModeReportMsg |
paste.go |
Paste messages |
focus.go |
Focus / blur messages |
environ.go |
EnvMsg |
raw.go |
RawMsg for advanced escape sequence injection |
termcap.go |
RequestCapability / CapabilityMsg |
xterm.go |
XTVERSION query and message |
This per-concept layout means a contributor can usually find the right file by the type they need to touch.
Build tags
Platform-specific files use the implicit _unix.go, _windows.go, _bsd.go, _other.go suffix conventions. There is no manual //go:build plumbing in most cases — the file naming is enough.
XXX / TODO / FIXME
XXX:is used sparingly to flag genuine open questions or design quirks the maintainers want to revisit (seemod.go,key.go,tea.go).TODO:appears 2 times.- The codebase does not use
FIXME:orHACK:.
nolint directives
Selective nolint annotations appear next to:
_, _ = …patterns for ANSI writes (errcheckexemption).mnd(magic number detector) for renderer constants like0o600file permissions.
Avoid sprinkling new nolint comments without a paired explanation — nolintlint is enabled.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.