Documentation
¶
Overview ¶
Package core provides terminal primitives and Unicode/ANSI handling for Phoenix TUI framework.
Overview ¶
Package core is the foundation layer of Phoenix, providing:
- Terminal capability detection (ANSI, color depth, mouse support)
- Correct Unicode/Emoji width calculation (fixes Lipgloss #562)
- Terminal primitives (Size, Position, Cell)
- Raw mode management
- Zero external dependencies (stdlib only)
Features ¶
- Auto-detection of terminal capabilities from environment
- Correct grapheme cluster width (emoji, CJK characters)
- 46x faster Unicode processing than alternatives
- Fluent, immutable API with method chaining
- Type-safe operations with compile-time guarantees
- Cross-platform support (Unix, Windows)
Quick Start ¶
Basic terminal detection:
import "github.com/phoenix-tui/phoenix/core"
term := core.AutoDetect()
caps := term.Capabilities()
if caps.SupportsTrueColor() {
fmt.Println("24-bit color supported!")
}
Unicode width calculation:
width := core.StringWidth("Hello 👋 World 🌍") // Returns 17 (correct!)
// Compare: Lipgloss returns 19 (wrong)
Architecture ¶
This package follows Domain-Driven Design (DDD):
- internal/domain/model - Core business logic (Terminal, Cell)
- internal/domain/value - Value objects (Capabilities, Size)
- internal/domain/service - Domain services (Unicode processing)
- internal/infrastructure - Platform-specific code (platform detection)
- core.go (this file) - Public API (wrapper types)
The public API provides clean wrapper types that delegate to internal domain models, following the Relica pattern for better pkg.go.dev visibility.
Performance ¶
Unicode processing is optimized for speed:
- ASCII strings: <50 ns/op
- Emoji strings: <200 ns/op (46x faster than alternatives)
- CJK strings: <200 ns/op
- Zero allocations in hot paths
Example ¶
Example demonstrates basic terminal capability detection. This shows how to detect terminal features like color support, ANSI capabilities, and terminal size.
Note: This example uses explicit capabilities to ensure consistent output across different environments (local dev, CI, etc.). In real code, use core.AutoDetect() to detect from environment.
package main
import (
"fmt"
"github.com/phoenix-tui/phoenix/core"
)
func main() {
// Create terminal with explicit capabilities (CI-safe)
caps := core.NewCapabilities(
true, // ANSI support
core.ColorDepth256, // 256 color support (not true color)
true, // Mouse support
true, // Alt screen support
true, // Cursor control support
)
term := core.NewTerminalWithCapabilities(caps)
// Set size for consistent output (immutable API)
term = term.WithSize(core.NewSize(80, 24))
// Check color support
termCaps := term.Capabilities()
fmt.Printf("Color support: %t\n", termCaps.SupportsColor())
fmt.Printf("True color: %t\n", termCaps.SupportsTrueColor())
fmt.Printf("ANSI: %t\n", termCaps.SupportsANSI())
// Get terminal size
size := term.Size()
fmt.Printf("Size: %dx%d\n", size.Width, size.Height)
}
Output: Color support: true True color: false ANSI: true Size: 80x24
Index ¶
- func StringWidth(s string) int
- type Capabilities
- func (c *Capabilities) ColorDepth() ColorDepth
- func (c *Capabilities) SupportsANSI() bool
- func (c *Capabilities) SupportsAltScreen() bool
- func (c *Capabilities) SupportsColor() bool
- func (c *Capabilities) SupportsCursorControl() bool
- func (c *Capabilities) SupportsMouse() bool
- func (c *Capabilities) SupportsTrueColor() bool
- type Cell
- type ColorDepth
- type Position
- type RawMode
- type Size
- type Terminal
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func StringWidth ¶
StringWidth returns the visual width of a string in terminal cells. This correctly handles Unicode edge cases that many libraries get wrong:
- Emoji (🔥, 👍, etc.): width 2
- CJK characters (中文, 日本語, 한국어): width 2 per character
- Combining characters (é = e + ́): width 0 for combiner
- Zero-width joiners (ZWJ): width 0
- Control characters: width 0
- ASCII: width 1
This function uses the latest Unicode 16.0 data and implements the Unicode Standard Annex #11 (East Asian Width) correctly.
Performance: Optimized with tiered lookup (9-23x faster than go-runewidth):
- O(1) for ASCII, CJK, simple emoji (90-95% of cases)
- O(log n) for rare characters
- Grapheme clustering only for complex Unicode (ZWJ, modifiers)
Example:
core.StringWidth("Hello") // 5
core.StringWidth("Hello 🔥") // 8 (5 + 1 space + 2 emoji)
core.StringWidth("中文") // 4 (2 + 2)
core.StringWidth("Café") // 4 (C + a + f + é)
core.StringWidth("👋🏻") // 2 (emoji + skin tone modifier)
Use this function when:
- Calculating text layout in TUI
- Truncating strings to fit terminal width
- Aligning text in columns
- Rendering bordered boxes
Example ¶
ExampleStringWidth demonstrates correct Unicode width calculation. This function handles emoji, CJK characters, and combining marks properly, fixing the infamous Lipgloss #562 bug.
package main
import (
"fmt"
"github.com/phoenix-tui/phoenix/core"
)
func main() {
// ASCII string
ascii := "Hello"
fmt.Printf("ASCII width: %d\n", core.StringWidth(ascii))
// Emoji (counts as 2 cells in terminal)
emoji := "👋"
fmt.Printf("Emoji width: %d\n", core.StringWidth(emoji))
// CJK characters (2 cells each)
cjk := "你好"
fmt.Printf("CJK width: %d\n", core.StringWidth(cjk))
// Mixed content
mixed := "Hello 👋 世界"
fmt.Printf("Mixed width: %d\n", core.StringWidth(mixed))
}
Output: ASCII width: 5 Emoji width: 2 CJK width: 4 Mixed width: 13
Types ¶
type Capabilities ¶
type Capabilities struct {
// contains filtered or unexported fields
}
Capabilities wraps domain capabilities with a public API.
Zero value: Capabilities with zero value has nil internal state and will panic if used. Always use NewCapabilities() or AutoDetect() to create valid instances.
var c core.Capabilities // Zero value - INVALID, will panic c2 := core.NewCapabilities(...) // Correct - use constructor
Thread safety: Capabilities is immutable and safe for concurrent reads.
func NewCapabilities ¶
func NewCapabilities(ansi bool, colorDepth ColorDepth, mouse, altScreen, cursor bool) *Capabilities
NewCapabilities creates capabilities with specific features.
func (*Capabilities) ColorDepth ¶
func (c *Capabilities) ColorDepth() ColorDepth
ColorDepth returns the terminal's color depth.
func (*Capabilities) SupportsANSI ¶
func (c *Capabilities) SupportsANSI() bool
SupportsANSI returns whether terminal supports ANSI escape sequences.
func (*Capabilities) SupportsAltScreen ¶
func (c *Capabilities) SupportsAltScreen() bool
SupportsAltScreen returns whether terminal supports alternate screen buffer.
func (*Capabilities) SupportsColor ¶
func (c *Capabilities) SupportsColor() bool
SupportsColor returns whether terminal supports any color.
func (*Capabilities) SupportsCursorControl ¶
func (c *Capabilities) SupportsCursorControl() bool
SupportsCursorControl returns whether terminal supports cursor control.
func (*Capabilities) SupportsMouse ¶
func (c *Capabilities) SupportsMouse() bool
SupportsMouse returns whether terminal supports mouse events.
func (*Capabilities) SupportsTrueColor ¶
func (c *Capabilities) SupportsTrueColor() bool
SupportsTrueColor returns whether terminal supports 24-bit true color.
type Cell ¶
Cell represents a terminal cell with grapheme cluster support.
Zero value: Cell{Content: "", Width: 0} is valid and represents an empty cell. Use NewCellAuto() for automatic Unicode width calculation (recommended).
var c core.Cell // Zero value - empty cell, valid
c2 := core.NewCellAuto("A") // Recommended - auto width
c3 := core.NewCell("A", 1) // Manual width control
func NewCell ¶
NewCell creates a new Cell with the given content and manual width. This is useful when you need explicit control over width (advanced use cases).
For automatic Unicode-aware width calculation (recommended), use NewCellAuto().
Example:
cell := core.NewCell("A", 1) // Manual width control
cell := core.NewCell("👋", 3) // Wrong width, but user controls
func NewCellAuto ¶
NewCellAuto creates a Cell with automatic Unicode width calculation. This is the recommended way to create cells as it handles all Unicode correctly:
- Emoji: "👋" -> width 2
- CJK: "中" -> width 2
- ASCII: "A" -> width 1
- Combining: "é" -> width 1
- Zero-width: correctly handled
This fixes Charm's lipgloss#562 bug with incorrect emoji/Unicode rendering.
Example:
cell := core.NewCellAuto("👋") // content "👋", width 2 (auto)
cell := core.NewCellAuto("中文") // content "中文", width 4 (auto)
cell := core.NewCellAuto("Hello") // content "Hello", width 5 (auto)
cell := core.NewCellAuto("Café") // content "Café", width 4 (auto)
For manual width control (rare cases), use NewCell(content, width).
Example ¶
ExampleNewCellAuto demonstrates creating terminal cells with automatic width detection. Cells represent individual terminal screen positions with content and styling.
package main
import (
"fmt"
"github.com/phoenix-tui/phoenix/core"
)
func main() {
// Create cell with ASCII character
ascii := core.NewCellAuto("A")
fmt.Printf("ASCII cell: '%s' width=%d\n", ascii.Content, ascii.Width)
// Create cell with emoji (automatically detects width=2)
emoji := core.NewCellAuto("👋")
fmt.Printf("Emoji cell: '%s' width=%d\n", emoji.Content, emoji.Width)
// Create cell with CJK character
cjk := core.NewCellAuto("你")
fmt.Printf("CJK cell: '%s' width=%d\n", cjk.Content, cjk.Width)
}
Output: ASCII cell: 'A' width=1 Emoji cell: '👋' width=2 CJK cell: '你' width=2
type ColorDepth ¶
type ColorDepth int
ColorDepth represents terminal color support levels.
const ( // ColorDepthNone - no color support (monochrome). ColorDepthNone ColorDepth = 0 // ColorDepth8 - 8 colors (3-bit: black, red, green, yellow, blue, magenta, cyan, white). ColorDepth8 ColorDepth = 8 // ColorDepth256 - 256 colors (8-bit: 216 colors + 16 system + 24 grayscale). ColorDepth256 ColorDepth = 256 // ColorDepthTrueColor - 16.7 million colors (24-bit RGB). ColorDepthTrueColor ColorDepth = 16777216 )
func (ColorDepth) String ¶
func (cd ColorDepth) String() string
String returns a human-readable color depth description.
type Position ¶
Position represents a position in the terminal (0-based).
Zero value: Position{Row: 0, Col: 0} is valid and represents top-left corner. This is a valid and commonly used position.
var p core.Position // Zero value - (0, 0) top-left, valid p2 := core.NewPosition(5, 10) // Explicit position
func NewPosition ¶
NewPosition creates a new Position with validation (non-negative).
type RawMode ¶
type RawMode struct {
// contains filtered or unexported fields
}
RawMode represents raw mode state with original terminal state preservation. This is a low-level API for platform-specific implementations.
Zero value: RawMode with zero value has nil internal state and will panic if used. Always use NewRawMode() to create a valid RawMode instance.
var rm core.RawMode // Zero value - INVALID, will panic rm2, err := core.NewRawMode(originalState) // Correct - use constructor
func NewRawMode ¶
NewRawMode creates a new RawMode with the original terminal state. The originalState should be platform-specific state (e.g., syscall.Termios on Unix).
func (*RawMode) OriginalState ¶
func (r *RawMode) OriginalState() interface{}
OriginalState returns the original terminal state for restoration.
type Size ¶
Size represents terminal dimensions.
Zero value: Size{Width: 0, Height: 0} is valid but represents an empty terminal. Use NewSize() for validated dimensions (minimum 1x1).
var s core.Size // Zero value - (0, 0), may cause issues s2 := core.NewSize(80, 24) // Validated - ensures minimum 1x1
type Terminal ¶
type Terminal struct {
// contains filtered or unexported fields
}
Terminal represents the public API for terminal operations. This is the main entry point for phoenix/core library.
Design Philosophy:
- Fluent API with method chaining
- Immutable operations (returns new instances)
- Type-safe with compile-time guarantees
- Zero external dependencies (stdlib only)
Zero value: A Terminal with zero value has nil internal state and will panic if used. Always use NewTerminal() or AutoDetect() to create a valid Terminal instance.
var t core.Terminal // Zero value - INVALID, will panic t2 := core.NewTerminal() // Correct - use constructor t3 := core.AutoDetect() // Recommended - auto-detect capabilities
Thread safety: Terminal is safe for concurrent reads (immutable). Concurrent writes are not applicable since methods return new instances.
Example Usage:
term := core.NewTerminal()
caps := term.Capabilities()
if caps.SupportsColor() {
fmt.Println("Terminal supports color!")
}
// Detect capabilities automatically
term = core.AutoDetect()
fmt.Printf("Color depth: %d\n", term.Capabilities().ColorDepth())
func AutoDetect ¶
func AutoDetect() *Terminal
AutoDetect creates a Terminal with automatically detected capabilities based on environment variables (TERM, COLORTERM, NO_COLOR, etc.).
This is the recommended way to create a Terminal for most use cases.
Example:
term := core.AutoDetect()
if term.Capabilities().SupportsTrueColor() {
// Use 24-bit colors
}
func NewTerminal ¶
func NewTerminal() *Terminal
NewTerminal creates a new Terminal with default capabilities. Default assumes no ANSI support, no colors, VT100 size (80x24).
For automatic detection, use AutoDetect() instead.
func NewTerminalWithCapabilities ¶
func NewTerminalWithCapabilities(caps *Capabilities) *Terminal
NewTerminalWithCapabilities creates a Terminal with specific capabilities. Useful for testing or when you know exact terminal capabilities.
func (*Terminal) Capabilities ¶
func (t *Terminal) Capabilities() *Capabilities
Capabilities returns the terminal's capabilities. The returned Capabilities object is immutable.
func (*Terminal) IsRawModeEnabled ¶
IsRawModeEnabled returns whether raw mode is currently enabled.
func (*Terminal) WithRawMode ¶
WithRawMode returns a new Terminal with the specified raw mode. Original Terminal is not modified (immutable operation).
Note: This is a low-level API. Most users should use EnableRawMode/DisableRawMode from the platform-specific packages instead.
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
basic
command
Package main demonstrates basic phoenix/core usage.
|
Package main demonstrates basic phoenix/core usage. |
|
unicode
command
Package main demonstrates Unicode string width calculation capabilities.
|
Package main demonstrates Unicode string width calculation capabilities. |
|
internal
|
|
|
domain/model
Package model provides rich domain models for terminal operations.
|
Package model provides rich domain models for terminal operations. |
|
domain/service
Package service provides domain services for terminal capabilities detection.
|
Package service provides domain services for terminal capabilities detection. |
|
domain/value
Package value provides immutable value objects for terminal operations.
|
Package value provides immutable value objects for terminal operations. |
|
infrastructure/platform
Package platform provides platform-specific infrastructure implementations.
|
Package platform provides platform-specific infrastructure implementations. |