Multi-module monorepo - 10 independent libraries. Full metrics in CI.
Next-generation Terminal User Interface framework for Go
Organization: github.com/phoenix-tui Go Version: 1.25+
Phoenix rises from the ashes of legacy TUI frameworks, solving critical problems:
- Perfect Unicode/Emoji support - No more layout bugs
- High Performance - Differential rendering, caching, zero allocations
- DDD Architecture - Clean, testable, extendable
- Rich Component Library - Everything you need out of the box
- Public Cursor API - Full control for shell applications
- Inline & Alt-Screen rendering - Per-line diffing without alt screen, or full-screen mode
- TTY Control - Run editors, shells, and other processes from within your TUI
- Easy Migration from Charm - Comprehensive migration guide included
Phoenix is a modular framework with 10 independent libraries:
| Library | Description |
|---|---|
| phoenix/core | Terminal primitives, Unicode/Emoji support (correct width calculation) |
| phoenix/terminal | ANSI terminal operations, raw mode, capabilities |
| phoenix/style | CSS-like styling + Theme System (presets, runtime switching) |
| phoenix/layout | Flexbox & grid layout (box model, responsive sizing) |
| phoenix/tea | Elm Architecture + TTY Control + Inline Renderer |
| phoenix/render | High-performance differential renderer |
| phoenix/components | UI components: TextArea, TextInput, List, Viewport, Table, Modal, Progress, Select, MultiSelect, Confirm, Form |
| phoenix/mouse | Mouse events (click, scroll, drag-drop, right-click) |
| phoenix/clipboard | Cross-platform clipboard (OSC 52 for SSH) |
| phoenix/testing | Mock terminal and test utilities |
go get github.com/phoenix-tui/phoenix@latestThis installs the umbrella module with convenient access to all Phoenix libraries through a single import:
import "github.com/phoenix-tui/phoenix"
// Use convenience API
term := phoenix.AutoDetectTerminal()
style := phoenix.NewStyle().Foreground("#00FF00").Bold()
p := phoenix.NewProgram(myModel, phoenix.WithAltScreen[MyModel]())go get github.com/phoenix-tui/phoenix/tea@latest # Elm Architecture
go get github.com/phoenix-tui/phoenix/components@latest # UI Components
go get github.com/phoenix-tui/phoenix/style@latest # Styling
go get github.com/phoenix-tui/phoenix/core@latest # Terminal primitivesIndividual imports give you more control and smaller dependencies:
import (
tea "github.com/phoenix-tui/phoenix/tea/api"
"github.com/phoenix-tui/phoenix/components/input/api"
)go get github.com/phoenix-tui/phoenix@latestpackage main
import (
"fmt"
"os"
"github.com/phoenix-tui/phoenix"
tea "github.com/phoenix-tui/phoenix/tea/api"
)
type Model struct {
count int
}
func (m Model) Init() tea.Cmd { return nil }
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
if msg.String() == "q" {
return m, phoenix.Quit()
}
m.count++
}
return m, nil
}
func (m Model) View() string {
style := phoenix.NewStyle().Foreground("#00FF00").Bold()
return style.Render(fmt.Sprintf("Count: %d\n", m.count))
}
func main() {
p := phoenix.NewProgram(Model{}, phoenix.WithAltScreen[Model]())
if err := p.Run(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}go get github.com/phoenix-tui/phoenix/tea@latestpackage main
import (
"fmt"
"os"
"github.com/phoenix-tui/phoenix/tea/api"
)
type Model struct {
count int
}
func (m Model) Init() api.Cmd { return nil }
func (m Model) Update(msg api.Msg) (Model, api.Cmd) {
switch msg := msg.(type) {
case api.KeyMsg:
if msg.String() == "q" {
return m, api.Quit()
}
m.count++
}
return m, nil
}
func (m Model) View() string {
return fmt.Sprintf("Count: %d\nPress any key to increment, 'q' to quit\n", m.count)
}
func main() {
p := api.New(Model{}, api.WithAltScreen[Model]())
if err := p.Run(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}- MIGRATION_GUIDE.md - Migrate from Charm ecosystem (Bubbletea/Lipgloss/Bubbles)
- CHANGELOG.md - Version history and changes
- ROADMAP.md - Public roadmap
- CONTRIBUTING.md - Development guide
- GoDoc - API reference for all modules
Problem: Charm's Lipgloss has broken emoji width calculation (issue #562) Solution: Phoenix uses grapheme cluster detection with correct East Asian Width (UAX #11)
// Phoenix: CORRECT
text := "Hello π World π"
width := style.Width(text) // Returns 17 (correct!)
// Charm Lipgloss: BROKEN
width := lipgloss.Width(text) // Returns 19 (wrong!)Techniques: Differential rendering, virtual buffer, caching, zero allocations on hot paths
library/
βββ domain/ # Business logic (highest coverage)
βββ application/ # Use cases
βββ infrastructure/ # Technical details
βββ api/ # Public interface
Problem: Bubbles TextArea has private cursor - syntax highlighting impossible
Solution: Phoenix TextInput exposes CursorPosition() and ContentParts()
// Phoenix: PUBLIC API (syntax highlighting works!)
before, at, after := input.ContentParts()
highlighted := syntax.Highlight(before) +
cursor.Render(at) +
syntax.Highlight(after)
// Bubbles: PRIVATE (syntax highlighting impossible!)
// cursor is internal field - no accessRun external processes (vim, shells, pagers) from within your TUI:
case api.KeyMsg:
if msg.String() == "e" {
return m, api.ExecProcess("vim", "file.txt")
}
case api.ExecProcessFinishedMsg:
// Editor closed, TUI restored automaticallyRun without alt screen - per-line diffing renders updates in place:
p := api.New(myModel) // inline mode by default - no alt screen neededMouse: All buttons (Left, Right, Middle, Wheel), drag-drop, click detection (single/double/triple) Clipboard: Cross-platform (Windows/macOS/Linux), SSH support (OSC 52)
Progress bars and 15 animated spinner styles:
import progress "github.com/phoenix-tui/phoenix/components/progress/api"
bar := progress.NewBar(100).SetWidth(40).SetLabel("Downloading").SetValue(65)
spinner := progress.NewSpinner(progress.SpinnerDots).SetLabel("Loading")| Feature | Phoenix | Charm (Bubbletea/Lipgloss) |
|---|---|---|
| Unicode/Emoji | Correct width (UAX #11) | Broken (#562) |
| Type Safety | Generic constraints | interface{} casts |
| Inline Rendering | Per-line diffing | Basic |
| TTY Control | ExecProcess + Suspend/Resume | ExecProcess only |
| Cursor API | Public | Private |
| Architecture | DDD layers | Monolithic |
| Theme System | Built-in (4 presets) | Manual |
Phoenix is part of an active development effort. See CONTRIBUTING.md for contribution guidelines and GoDoc for API documentation.
MIT License - see LICENSE file for details
Professor Ancha Baranova - This project would not have been possible without her invaluable help and support. Her assistance was crucial in bringing Phoenix to life.
Rising from the ashes of legacy TUI frameworks
