core

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 4, 2025 License: MIT Imports: 4 Imported by: 0

README

phoenix/core

Part of the Phoenix TUI Framework.

Status: 🔴 Not Started Module: github.com/phoenix-tui/phoenix/core

Documentation

Coming soon.

License

MIT (planned)

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

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func StringWidth

func StringWidth(s string) int

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

type Cell struct {
	Content string
	Width   int
}

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

func NewCell(content string, width int) Cell

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

func NewCellAuto(content string) Cell

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

type Position struct {
	Row int
	Col int
}

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

func NewPosition(row, col int) Position

NewPosition creates a new Position with validation (non-negative).

func (Position) Add

func (p Position) Add(deltaRow, deltaCol int) Position

Add returns a new Position offset by delta row/column.

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

func NewRawMode(originalState interface{}) (*RawMode, error)

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) IsEnabled

func (r *RawMode) IsEnabled() bool

IsEnabled returns whether raw mode is currently enabled.

func (*RawMode) OriginalState

func (r *RawMode) OriginalState() interface{}

OriginalState returns the original terminal state for restoration.

type Size

type Size struct {
	Width  int
	Height int
}

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

func NewSize

func NewSize(width, height int) Size

NewSize creates a new Size with validation (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

func (t *Terminal) IsRawModeEnabled() bool

IsRawModeEnabled returns whether raw mode is currently enabled.

func (*Terminal) Size

func (t *Terminal) Size() Size

Size returns the current terminal size.

func (*Terminal) WithRawMode

func (t *Terminal) WithRawMode(rm *RawMode) *Terminal

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.

func (*Terminal) WithSize

func (t *Terminal) WithSize(size Size) *Terminal

WithSize returns a new Terminal with the specified size. Original Terminal is not modified (immutable operation).

Example:

term := core.NewTerminal()
resized := term.WithSize(core.NewSize(120, 40))

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL