goli

package module
v0.1.12 Latest Latest
Warning

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

Go to latest
Published: Mar 4, 2026 License: MIT Imports: 18 Imported by: 3

README

goli

A React-like terminal UI framework for Go, using gox for JSX syntax.

Overview

goli provides:

  • Flexbox layout engine - Familiar CSS-like layout
  • Reactive primitives - Fine-grained signals and effects
  • Cell-level diffing - Minimal ANSI output for efficient rendering
  • Focus management - Tab navigation, global key handlers
  • Input components - Text input with scrolling, select dropdowns
  • JSX syntax via gox - Write components using JSX

Installation

go get github.com/germtb/goli

You'll also need gox for JSX preprocessing:

go install github.com/germtb/gox/cmd/gox@latest

Quick Start

1. Create a component (app.gox)
package main

import (
    "github.com/germtb/goli"
    "github.com/germtb/gox"
)

func Greeting(props gox.Props) gox.VNode {
    name := props["name"].(string)
    return <box direction="column">
        <text style={map[string]any{"color": "green", "bold": true}}>
            Hello, {name}!
        </text>
        <text>Welcome to goli</text>
    </box>
}

func App() gox.VNode {
    return <box width={40} height={10}>
        <Greeting name="World" />
    </box>
}

func main() {
    goli.Run(func() gox.VNode {
        return App()
    }, goli.RunOptions{})
}
2. Run with gox
gox run ./

Architecture

JSX (.gox) → gox preprocess → Go code → VNode tree → Layout → Buffer → Diff → ANSI
Package Structure
Package Description
goli Core rendering, layout, app lifecycle, focus, input components, reactive primitives

Reactive Primitives

goli uses fine-grained reactive primitives:

import "github.com/germtb/goli"

// Create a signal
count, setCount := goli.CreateSignal(0)

// Read value
fmt.Println(count()) // 0

// Update value
setCount(1)

// Update based on previous value
goli.SetWith(setCount, func(prev int) int { return prev + 1 }, count)

// Create derived state
doubled := goli.CreateMemo(func() int {
    return count() * 2
})

// Create side effects
goli.CreateEffect(func() goli.CleanupFunc {
    fmt.Println("Count changed to:", count())
    return nil // cleanup function
})

// Batch updates
goli.Batch(func() {
    setCount(1)
    setCount(2)
}) // Only triggers effects once

Layout Props

Boxes support flexbox-like layout:

<box
    direction="row"       // "row" | "column"
    justify="center"      // "start" | "center" | "end" | "space-between"
    align="center"        // "start" | "center" | "end" | "stretch"
    gap={1}               // Space between children
    padding={1}           // Inner spacing (or paddingTop/Right/Bottom/Left)
    width={20}            // Fixed width
    height={5}            // Fixed height
    flex={1}              // Flex grow factor
    border="rounded"      // "single" | "double" | "rounded" | "bold"
    position="absolute"   // "relative" | "absolute"
    x={5} y={3}           // Position for absolute elements
    style={map[string]any{
        "color": "red",
        "background": "blue",
        "bold": true,
    }}
>
    {children}
</box>

Focus & Key Handling

goli provides focus management with Tab/Shift+Tab navigation and global key handlers:

// Register a global key handler for app-wide shortcuts
cleanup := goli.Manager().SetGlobalKeyHandler(func(key string) bool {
    switch key {
    case goli.CtrlQ, "q":
        app.Quit()
        return true
    case goli.F1:
        showHelp()
        return true
    }
    return false // Let other handlers process this key
})
defer cleanup()

// Available key constants
goli.Enter, goli.Escape, goli.Tab, goli.Space
goli.Left, goli.Right, goli.Up, goli.Down
goli.Home, goli.End, goli.PageUp, goli.PageDown
goli.Backspace, goli.Delete, goli.Insert
goli.CtrlA - goli.CtrlZ
goli.ShiftTab, goli.ShiftEnter, goli.ShiftLeft, goli.ShiftRight, ...
goli.AltLeft, goli.AltRight, goli.CtrlLeft, goli.CtrlRight, ...
goli.F1 - goli.F12

Input Components

// Create a text input field
inp := goli.NewInput(goli.InputOptions{
    InitialValue: "",
    MaxLength:    50,
    Placeholder:  "Enter text...",
    Mask:         '*',  // For password fields
})

// Use in JSX - supports horizontal scrolling for long text
<input
    input={inp}
    width={20}
    style={map[string]any{"color": "white"}}
    cursorStyle={map[string]any{"background": "cyan"}}
    placeholderStyle={map[string]any{"dim": true}}
/>

// Create a select dropdown
sel := goli.NewSelect(goli.SelectOptions[string]{
    InitialValue: "option1",
})

// Use in JSX
<select select={sel} pointerWidth={2}>
    <option value="option1">First Option</option>
    <option value="option2">Second Option</option>
</select>

Custom Intrinsic Elements

goli uses a registry pattern for intrinsic elements. You can register your own:

func init() {
    goli.RegisterIntrinsic("mywidget", &goli.IntrinsicHandler{
        Measure: func(node gox.VNode, ctx *goli.LayoutContext) (int, int) {
            return 10, 3 // width, height
        },
        Layout: func(node gox.VNode, availWidth, availHeight int, ctx *goli.LayoutContext) *goli.LayoutBox {
            return &goli.LayoutBox{
                X: ctx.X, Y: ctx.Y,
                Width: 10, Height: 3,
                Node: node,
            }
        },
        Render: func(box *goli.LayoutBox, buf *goli.CellBuffer, clip *goli.ClipRegion) {
            // Draw your widget to the buffer
        },
        RenderLogical: func(box *goli.LayoutBox, buf *goli.LogicalBuffer, clip *goli.ClipRegion) {
            // Draw your widget (for diffing)
        },
    })
}

Then use in JSX:

<mywidget someProp={value} />

Examples

See the examples/ directory:

Example Description
counter/ Interactive counter with reactive state
select-demo/ Multiple selects, inputs, and Tab navigation
vim/ Vim-like editor with modes and commands
nerdtree/ File tree browser with expand/collapse
console/ Console capture with log viewer (Ctrl+L)

Run any example:

gox run ./examples/counter

Benchmarks

Comparison against Ink (React for CLIs) rendering a 100-item file tree:

Metric goli (Go) Ink (React/Bun) Difference
Binary size 2.8 MB 40 MB ~14x smaller
Startup time 0.08 ms 17 ms ~210x faster
Memory usage 0.36 MB 37 MB ~100x less
Idle CPU 0.00% 1.0% No overhead
Max FPS 28,000 2,800 ~10x faster

Tested on Apple M3 Max, Go 1.25, Bun 1.3. See benchmarks/ for reproduction.

Large Screen Performance
Screen Size Cells Components FPS
60×40 2,400 100 28,000
200×50 10,000 10,000 280
Why the difference?
  • No runtime overhead: Go compiles to native code, no VM/JIT
  • Signals vs React: Fine-grained reactivity avoids full tree reconciliation
  • Synchronous rendering: No async scheduling overhead
  • Cell-level diffing: Only changed characters are written to terminal

Run benchmarks yourself:

cd benchmarks/comparison-ink && ./run.sh

License

MIT

Documentation

Overview

Package ansi provides ANSI escape code generation for terminal output.

Package goli provides the reactive TUI application lifecycle.

Package goli provides buffer implementations for terminal rendering.

Package goli provides a button primitive for interactive UI.

Package cell provides the fundamental Cell type representing a terminal "pixel". Each Cell holds a character and its styling attributes.

Package goli provides the diff engine for comparing cell buffers.

Package goli provides text input handling for terminal UI.

Package goli provides intrinsic element handlers for box and text.

Package goli provides focus management for terminal UI components.

Package goli provides the flexbox layout engine for terminal UI.

Package goli provides a link primitive for clickable URLs.

Package goli provides intrinsic element registration.

Package goli provides buffer rendering functions.

Package goli provides the main rendering orchestrator for terminal UI.

Package goli provides the reactive TUI framework runtime.

Package goli provides a select primitive for list selection.

Package goli provides fine-grained reactive primitives.

Key principles: - Components run ONCE (setup phase) - Signals created inside components are local to that instance - Fine-grained reactivity: only re-run what depends on changed signals - No rules of hooks - signals are just values

Package term provides terminal handling utilities.

Package goli provides VNode helper functions.

Index

Constants

View Source
const (
	ESC = "\x1b"
	CSI = ESC + "["
	OSC = ESC + "]"
	ST  = ESC + "\\" // String Terminator
)
View Source
const (
	// Basic keys
	Space   = " "
	Enter   = "\r"
	EnterLF = "\n"
	Tab     = "\t"
	Escape  = "\x1b"

	// Editing keys
	Backspace     = "\x7f"
	BackspaceCtrl = "\b"
	Delete        = "\x1b[3~"
	Insert        = "\x1b[2~"

	// Navigation keys
	Left     = "\x1b[D"
	Right    = "\x1b[C"
	Up       = "\x1b[A"
	Down     = "\x1b[B"
	Home     = "\x1b[H"
	HomeAlt  = "\x1b[1~"
	End      = "\x1b[F"
	EndAlt   = "\x1b[4~"
	PageUp   = "\x1b[5~"
	PageDown = "\x1b[6~"

	// Shift combinations
	ShiftTab   = "\x1b[Z"
	ShiftEnter = "\x1b[13;2u"
	ShiftUp    = "\x1b[1;2A"
	ShiftDown  = "\x1b[1;2B"
	ShiftLeft  = "\x1b[1;2D"
	ShiftRight = "\x1b[1;2C"

	// Alt combinations
	AltBackspace = "\x1b\x7f"
	AltLeft      = "\x1bb"
	AltLeftCSI   = "\x1b[1;3D"
	AltRight     = "\x1bf"
	AltRightCSI  = "\x1b[1;3C"
	AltUp        = "\x1b[1;3A"
	AltDown      = "\x1b[1;3B"

	// Ctrl combinations (alphabetical)
	CtrlA = "\x01"
	CtrlB = "\x02"
	CtrlC = "\x03"
	CtrlD = "\x04"
	CtrlE = "\x05"
	CtrlF = "\x06"
	CtrlG = "\x07"
	CtrlH = "\x08" // Same as BackspaceCtrl
	CtrlI = "\x09" // Same as Tab
	CtrlJ = "\x0a" // Same as EnterLF
	CtrlK = "\x0b"
	CtrlL = "\x0c"
	CtrlM = "\x0d" // Same as Enter
	CtrlN = "\x0e"
	CtrlO = "\x0f"
	CtrlP = "\x10"
	CtrlQ = "\x11"
	CtrlR = "\x12"
	CtrlS = "\x13"
	CtrlT = "\x14"
	CtrlU = "\x15"
	CtrlV = "\x16"
	CtrlW = "\x17"
	CtrlX = "\x18"
	CtrlY = "\x19"
	CtrlZ = "\x1a"

	// Ctrl+Arrow combinations
	CtrlUp    = "\x1b[1;5A"
	CtrlDown  = "\x1b[1;5B"
	CtrlLeft  = "\x1b[1;5D"
	CtrlRight = "\x1b[1;5C"

	// Function keys
	F1  = "\x1bOP"
	F2  = "\x1bOQ"
	F3  = "\x1bOR"
	F4  = "\x1bOS"
	F5  = "\x1b[15~"
	F6  = "\x1b[17~"
	F7  = "\x1b[18~"
	F8  = "\x1b[19~"
	F9  = "\x1b[20~"
	F10 = "\x1b[21~"
	F11 = "\x1b[23~"
	F12 = "\x1b[24~"
)

Common terminal key codes.

View Source
const (

	// Input mode flags
	ICRNL  = 0x00000100
	IXON   = 0x00000400
	BRKINT = 0x00000002
	INPCK  = 0x00000010
	ISTRIP = 0x00000020

	// Local mode flags
	ECHO   = 0x00000008
	ICANON = 0x00000002
	ISIG   = 0x00000001
	IEXTEN = 0x00008000

	// Output mode flags
	OPOST = 0x00000001

	// Control mode flags
	CS8 = 0x00000030
)
View Source
const MaxBufferHeight = 10000

MaxBufferHeight is the maximum height a LogicalBuffer can auto-grow to. This prevents runaway memory usage from unbounded growth. 10,000 lines is generous for most TUI applications.

View Source
const PipelineThreshold = 3000 // ~80x40 or 60x50

PipelineThreshold is the minimum cell count where the pipeline renderer helps. Below this, goroutine/channel overhead outweighs the parallelization benefit.

Variables

View Source
var BorderCharSets = map[BorderStyle]BorderChars{
	BorderSingle: {
		TopLeft:     '┌',
		TopRight:    '┐',
		BottomLeft:  '└',
		BottomRight: '┘',
		Horizontal:  '─',
		Vertical:    '│',
	},
	BorderDouble: {
		TopLeft:     '╔',
		TopRight:    '╗',
		BottomLeft:  '╚',
		BottomRight: '╝',
		Horizontal:  '═',
		Vertical:    '║',
	},
	BorderRounded: {
		TopLeft:     '╭',
		TopRight:    '╮',
		BottomLeft:  '╰',
		BottomRight: '╯',
		Horizontal:  '─',
		Vertical:    '│',
	},
	BorderBold: {
		TopLeft:     '┏',
		TopRight:    '┓',
		BottomLeft:  '┗',
		BottomRight: '┛',
		Horizontal:  '━',
		Vertical:    '┃',
	},
}

Border character sets for different styles.

View Source
var ButtonCornerCharSets = map[ButtonCornerStyle]ButtonCornerChars{
	ButtonCornerPill:  {Left: '▐', Right: '▌'},
	ButtonCornerRound: {Left: '\uE0B6', Right: '\uE0B4'},
	ButtonCornerArrow: {Left: '\uE0B2', Right: '\uE0B0'},
	ButtonCornerPixel: {Left: '▟', Right: '▙'},
}

ButtonCornerCharSets for different button styles. All use the button's background color as foreground for a shaped effect.

DefaultInputHandler implements standard text editing behavior.

View Source
var EmptyCell = Cell{Char: ' ', Style: EmptyStyle}

EmptyCell is a Cell with a space character and no styling.

View Source
var EmptyStyle = Style{}

EmptyStyle is a Style with no attributes set.

View Source
var NameToColor = map[string]Color{
	"default":       ColorDefault,
	"black":         ColorBlack,
	"red":           ColorRed,
	"green":         ColorGreen,
	"yellow":        ColorYellow,
	"blue":          ColorBlue,
	"magenta":       ColorMagenta,
	"cyan":          ColorCyan,
	"white":         ColorWhite,
	"grey":          ColorBrightBlack,
	"gray":          ColorBrightBlack,
	"brightBlack":   ColorBrightBlack,
	"brightRed":     ColorBrightRed,
	"brightGreen":   ColorBrightGreen,
	"brightYellow":  ColorBrightYellow,
	"brightBlue":    ColorBrightBlue,
	"brightMagenta": ColorBrightMagenta,
	"brightCyan":    ColorBrightCyan,
	"brightWhite":   ColorBrightWhite,
}

NameToColor converts a string color name to Color

Functions

func Batch added in v0.1.1

func Batch[T any](fn func() T) T

Batch batches multiple signal updates into a single update cycle. All effects are deferred until the batch completes.

Example:

count, setCount := CreateSignal(0)
name, setName := CreateSignal("")

Batch(func() {
    setCount(1)
    setName("test")
    // Effects run only once after both updates
})

func BatchVoid added in v0.1.1

func BatchVoid(fn func())

BatchVoid is a convenience wrapper for Batch when there's no return value.

func BeginRender added in v0.1.5

func BeginRender()

BeginRender increments the generation counter. Call at start of each render.

func BufferToSequentialAnsi added in v0.1.1

func BufferToSequentialAnsi(buf *CellBuffer) string

BufferToSequentialAnsi renders a CellBuffer line-by-line with newlines. This is used for overflow content where ANSI cursor positioning doesn't work. Outputs from cursor position (0,0) downward, using newlines to advance rows.

func ClearScreen

func ClearScreen() string

ClearScreen returns the ANSI code to clear the screen.

func CollectTextContent

func CollectTextContent(node gox.VNode) string

CollectTextContent recursively collects all text content from a node.

func ColorToAnsi

func ColorToAnsi(color Color, rgb *RGB, isFg bool) string

ColorToAnsi converts a Color to ANSI escape code.

func ContainsAnsi added in v0.1.11

func ContainsAnsi(s string) bool

ContainsAnsi returns true if the string contains ANSI escape sequences.

func CreateRoot added in v0.1.1

func CreateRoot[T any](fn func(dispose DisposeFunc) T) T

CreateRoot creates a reactive root. All reactive primitives created inside will be cleaned up when the root is disposed.

Example:

result := CreateRoot(func(dispose DisposeFunc) string {
    count, setCount := CreateSignal(0)
    CreateEffect(func() CleanupFunc {
        fmt.Println("Count:", count())
        return nil
    })
    setCount(1)
    return "done"
})

func CreateSignal added in v0.1.1

func CreateSignal[T any](initialValue T) (Accessor[T], Setter[T])

CreateSignal creates a reactive signal.

Example:

count, setCount := CreateSignal(0)
fmt.Println(count()) // 0
setCount(1)
fmt.Println(count()) // 1

func CreateSignalWithEquals added in v0.1.1

func CreateSignalWithEquals[T any](initialValue T, equals func(a, b T) bool) (Accessor[T], Setter[T])

CreateSignalWithEquals creates a signal with a custom equality function. If the new value equals the old value according to the equality function, subscribers are not notified.

func CreateTextNode

func CreateTextNode(text string) gox.VNode

CreateTextNode creates a text node.

func DebugLayout added in v0.1.10

func DebugLayout(box *LayoutBox)

DebugLayout prints the layout tree to stdout for debugging.

func Expand

func Expand(v gox.VNode) gox.VNode

Expand recursively expands functional components into their rendered output.

func FilterAbsoluteChildren

func FilterAbsoluteChildren(node gox.VNode) []gox.VNode

FilterAbsoluteChildren returns children with absolute positioning.

func FilterChildren

func FilterChildren(node gox.VNode, typeStr string) []gox.VNode

func FilterRelativeChildren

func FilterRelativeChildren(node gox.VNode) []gox.VNode

FilterRelativeChildren returns children with relative positioning.

func FormatMessage

func FormatMessage(msg LogMessage) string

FormatMessage formats a log message for display

func Fprint added in v0.1.6

func Fprint(w io.Writer, node gox.VNode, opts PrintOptions)

Fprint renders a VNode tree to a writer with ANSI styling.

func FprintLayout added in v0.1.10

func FprintLayout(w io.Writer, box *LayoutBox)

FprintLayout writes the layout tree to the given writer for debugging.

func GetBoolProp

func GetBoolProp(props gox.Props, key string, defaultVal bool) bool

GetBoolProp gets a boolean property with a default value.

func GetIntProp

func GetIntProp(props gox.Props, key string, defaultVal int) int

func GetSize

func GetSize(fd int) (width, height int, err error)

GetSize returns the terminal dimensions.

func GetTextContent

func GetTextContent(v gox.VNode) (string, bool)

GetTextContent returns the text content if this is a text node.

func GroupChangesByRow

func GroupChangesByRow(changes []CellChange) map[int][]CellChange

GroupChangesByRow groups changes by row for more efficient cursor movement.

func HandleKey

func HandleKey(key string) bool

HandleKey routes a keypress using the global manager.

func HasIntrinsicHandler

func HasIntrinsicHandler(name string) bool

HasIntrinsicHandler returns true if a handler is registered for the given type.

func HideCursor

func HideCursor() string

HideCursor returns the ANSI code to hide the cursor.

func HyperlinkEnd added in v0.1.1

func HyperlinkEnd() string

HyperlinkEnd returns the OSC 8 sequence to end a hyperlink.

func HyperlinkStart added in v0.1.1

func HyperlinkStart(url string) string

HyperlinkStart returns the OSC 8 sequence to start a hyperlink.

func IsInClip

func IsInClip(x, y int, clip *ClipRegion) bool

IsInClip checks if a position is within the clip region.

func IsTerminal

func IsTerminal(fd int) bool

IsTerminal returns whether the file descriptor is a terminal.

func IsTextNode

func IsTextNode(v gox.VNode) bool

IsTextNode returns true if this is a text node.

func IsTracking added in v0.1.1

func IsTracking() bool

IsTracking returns true if we're currently inside a reactive tracking context.

func MeasureNode

func MeasureNode(node gox.VNode) (width, height int)

MeasureNode measures the natural size of a node (before flex distribution).

func Memo added in v0.1.5

func Memo[K comparable, P Keyed[K]](
	render func(P, ...gox.VNode) gox.VNode,
	equal func(a, b P) bool,
) func(P, ...gox.VNode) gox.VNode

Memo creates a memoized component that skips re-rendering when props haven't changed.

Props must implement the Keyed[K] interface to provide a cache key. K is the key type (typically int or string), inferred from GetKey().

Parameters:

  • render: the component function to memoize
  • equal: equality function to compare props (use goli.ShallowEquals for comparable types)

Usage:

type CellProps struct {
    Key   int  // use int for zero allocation!
    Index int
}

func (p CellProps) GetKey() int { return p.Key }

var Cell = goli.Memo(
    func(props CellProps, children ...gox.VNode) gox.VNode {
        return <text>{props.Value}</text>
    },
    goli.ShallowEquals[CellProps],
)

func MoveCursor

func MoveCursor(x, y int) string

MoveCursor returns the ANSI code to move the cursor to (x, y). ANSI uses 1-based coordinates.

func OnCleanup added in v0.1.1

func OnCleanup(fn func())

OnCleanup registers a cleanup function to run when the current owner is disposed.

func OpenURL added in v0.1.1

func OpenURL(url string) error

OpenURL opens the given URL in the default browser. Works on macOS, Linux, and Windows.

func Print added in v0.1.6

func Print(node gox.VNode)

Print renders a VNode tree to stdout with ANSI styling.

func Register

func Register(f Focusable)

Register adds a focusable to the global manager.

func RegisterIntrinsic

func RegisterIntrinsic(name string, handler *IntrinsicHandler)

RegisterIntrinsic registers a handler for an intrinsic element type. This should be called from init() functions in component packages. The name corresponds to the JSX element name (e.g., "input", "select").

func RenderButtonToBuffer added in v0.1.1

func RenderButtonToBuffer(box *LayoutBox, buf *CellBuffer, clip *ClipRegion)

RenderButtonToBuffer renders a button to a CellBuffer.

func RenderButtonToLogicalBuffer added in v0.1.1

func RenderButtonToLogicalBuffer(box *LayoutBox, buf *LogicalBuffer, clip *ClipRegion)

RenderButtonToLogicalBuffer renders a button to a LogicalBuffer.

func RenderInputToBuffer

func RenderInputToBuffer(box *LayoutBox, buf *CellBuffer, clip *ClipRegion)

func RenderInputToLogicalBuffer

func RenderInputToLogicalBuffer(box *LayoutBox, buf *LogicalBuffer, clip *ClipRegion)

func RenderLinkToBuffer added in v0.1.1

func RenderLinkToBuffer(box *LayoutBox, buf *CellBuffer, clip *ClipRegion)

RenderLinkToBuffer renders a link to a CellBuffer. Links use OSC 8 escape sequences for terminal hyperlinks.

func RenderLinkToLogicalBuffer added in v0.1.1

func RenderLinkToLogicalBuffer(box *LayoutBox, buf *LogicalBuffer, clip *ClipRegion)

RenderLinkToLogicalBuffer renders a link to a LogicalBuffer.

func RenderSelectToBuffer

func RenderSelectToBuffer(box *LayoutBox, buf *CellBuffer, clip *ClipRegion)

func RenderSelectToLogicalBuffer

func RenderSelectToLogicalBuffer(box *LayoutBox, buf *LogicalBuffer, clip *ClipRegion)

func RenderToBuffer

func RenderToBuffer(box *LayoutBox, buf *CellBuffer, clip *ClipRegion)

RenderToBuffer renders a LayoutBox tree to a CellBuffer.

func RenderToLogicalBuffer

func RenderToLogicalBuffer(box *LayoutBox, buf *LogicalBuffer, clip *ClipRegion)

RenderToLogicalBuffer renders a LayoutBox tree to a LogicalBuffer.

func RequestBlur

func RequestBlur(f Focusable)

RequestBlur blurs a specific focusable using the global manager.

func RequestFocus

func RequestFocus(f Focusable)

RequestFocus focuses a specific focusable using the global manager.

func Reset added in v0.1.1

func Reset()

Reset clears and reinitializes the global runtime. Call this at the start of tests for clean isolation.

func Restore

func Restore(fd int, state *State) error

Restore restores the terminal to a previous state.

func Run

func Run(appFn func() gox.VNode, opts RunOptions)

Run runs a TUI app with full terminal handling.

func RunToAnsi

func RunToAnsi(run CellRun, sb *strings.Builder)

RunToAnsi renders a run of cells to ANSI, writing directly to builder.

func RunWithOwner added in v0.1.1

func RunWithOwner[T any](owner *Owner, fn func() T) T

RunWithOwner runs a function with a specific owner.

func RuneWidth

func RuneWidth(s string) int

RuneWidth returns the display width of a string, accounting for wide characters like emojis. ANSI escape sequences are stripped before measuring.

func RunsToAnsi

func RunsToAnsi(runs []CellRun) string

RunsToAnsi renders all runs to a single ANSI string.

func RunsToAnsiBuilder

func RunsToAnsiBuilder(runs []CellRun, sb *strings.Builder)

RunsToAnsiBuilder renders all runs to the provided strings.Builder. This avoids allocation when the caller manages the builder.

func SetWith added in v0.1.1

func SetWith[T any](setter Setter[T], fn SetterFunc[T], getter Accessor[T])

SetWith updates a signal using a function that receives the previous value.

func ShallowEquals added in v0.1.5

func ShallowEquals[P comparable](a, b P) bool

ShallowEquals returns a == b. Use this with Memo for comparable prop types.

Usage:

var Cell = goli.Memo(renderCell, goli.ShallowEquals[CellProps])

func ShowCursor

func ShowCursor() string

ShowCursor returns the ANSI code to show the cursor.

func Sprint added in v0.1.6

func Sprint(node gox.VNode) string

Sprint renders a VNode tree to a string with ANSI styling. Width/height auto-detected from terminal (falls back to 80x24).

func SprintLayout added in v0.1.10

func SprintLayout(box *LayoutBox) string

SprintLayout returns the layout tree as a string for debugging.

func Stdin

func Stdin() int

Stdin returns the file descriptor for stdin.

func Stdout

func Stdout() int

Stdout returns the file descriptor for stdout.

func StripAnsi added in v0.1.11

func StripAnsi(s string) string

StripAnsi removes ANSI escape sequences from a string, returning only the visible text content.

func StyleToAnsi

func StyleToAnsi(style Style, sb *strings.Builder)

StyleToAnsi generates ANSI codes for a style, writing directly to builder.

func TypeString

func TypeString(v gox.VNode) (string, bool)

TypeString returns the type as a string (for intrinsic elements).

func Unregister

func Unregister(f Focusable)

Unregister removes a focusable from the global manager.

func Untrack added in v0.1.1

func Untrack[T any](fn func() T) T

Untrack reads signals without tracking them as dependencies.

Example:

count, _ := CreateSignal(0)
other, _ := CreateSignal(0)

CreateEffect(func() CleanupFunc {
    // This effect only depends on 'count', not 'other'
    fmt.Println(count(), Untrack(func() int { return other() }))
    return nil
})

func WrapText

func WrapText(text string, maxWidth int) []string

WrapText wraps text to fit within a given width. Handles ANSI escape sequences correctly: they don't count toward width and are preserved across wrapped lines.

Types

type Accessor added in v0.1.1

type Accessor[T any] func() T

Accessor is a function that reads a signal value.

func CreateMemo added in v0.1.1

func CreateMemo[T any](fn func() T) Accessor[T]

CreateMemo creates a memoized computation. Only re-computes when dependencies change.

Example:

count, _ := CreateSignal(5)
doubled := CreateMemo(func() int {
    return count() * 2
})
fmt.Println(doubled()) // 10

type Align

type Align string

Align specifies alignment along the cross axis.

const (
	AlignStart   Align = "start"
	AlignCenter  Align = "center"
	AlignEnd     Align = "end"
	AlignStretch Align = "stretch"
)

func GetAlign

func GetAlign(props gox.Props) Align

GetAlign returns the align-items from props.

type AnsiSegment added in v0.1.11

type AnsiSegment struct {
	Text  string
	Style Style
}

AnsiSegment represents a piece of text with associated style from ANSI codes.

func ParseAnsiLine added in v0.1.11

func ParseAnsiLine(line string, baseStyle Style) []AnsiSegment

ParseAnsiLine parses a line containing ANSI escape codes into styled segments. The baseStyle is the element's own style; ANSI styles are merged on top.

type App

type App struct {
	// contains filtered or unexported fields
}

App represents a reactive TUI application.

func Render

func Render(appFn func() gox.VNode, opts Options) *App

Render creates a reactive TUI application from a gox component.

func (*App) Dispose

func (a *App) Dispose()

Dispose cleans up the app.

func (*App) Quit

func (a *App) Quit()

Quit signals the application to exit.

func (*App) Renderer

func (a *App) Renderer() *Renderer

Renderer returns the underlying renderer.

func (*App) Rerender

func (a *App) Rerender()

Rerender forces a re-render.

func (*App) Resize

func (a *App) Resize(width, height int)

Resize resizes the terminal.

type BorderChars

type BorderChars struct {
	TopLeft     rune
	TopRight    rune
	BottomLeft  rune
	BottomRight rune
	Horizontal  rune
	Vertical    rune
}

BorderChars holds the characters for drawing a border.

type BorderStyle

type BorderStyle string

BorderStyle specifies the border appearance.

const (
	BorderNone    BorderStyle = "none"
	BorderSingle  BorderStyle = "single"
	BorderDouble  BorderStyle = "double"
	BorderRounded BorderStyle = "rounded"
	BorderBold    BorderStyle = "bold"
)

func GetBorderStyle

func GetBorderStyle(border any) BorderStyle

GetBorderStyle normalizes border prop to BorderStyle.

type Button added in v0.1.1

type Button struct {
	// contains filtered or unexported fields
}

Button represents a clickable button component.

func NewButton added in v0.1.1

func NewButton(opts ButtonOptions) *Button

NewButton creates a new button.

func (*Button) Blur added in v0.1.1

func (b *Button) Blur()

Blur removes focus from this button.

func (*Button) Click added in v0.1.1

func (b *Button) Click()

Click programmatically triggers the button's onClick handler.

func (*Button) Dispose added in v0.1.1

func (b *Button) Dispose()

Dispose unregisters from the focus manager.

func (*Button) Focus added in v0.1.1

func (b *Button) Focus()

Focus gives focus to this button.

func (*Button) Focused added in v0.1.1

func (b *Button) Focused() bool

Focused returns whether the button is focused.

func (*Button) HandleKey added in v0.1.1

func (b *Button) HandleKey(key string) bool

HandleKey processes a key press. Returns true if the key was consumed.

func (*Button) SetFocused added in v0.1.1

func (b *Button) SetFocused(f bool)

SetFocused sets the focused state (called by focus manager).

type ButtonCornerChars added in v0.1.1

type ButtonCornerChars struct {
	Left  rune
	Right rune
}

ButtonCornerChars holds the characters for button corners.

type ButtonCornerStyle added in v0.1.1

type ButtonCornerStyle string

ButtonCornerStyle specifies the button corner appearance.

const (
	ButtonCornerNone  ButtonCornerStyle = "none"
	ButtonCornerPill  ButtonCornerStyle = "pill"  // ▐ text ▌ - half blocks
	ButtonCornerRound ButtonCornerStyle = "round" //  text  - Nerd Font
	ButtonCornerArrow ButtonCornerStyle = "arrow" //  text  - Nerd Font
	ButtonCornerPixel ButtonCornerStyle = "pixel" // ▙ text ▟ - quadrant blocks
)

func GetButtonCornerStyle added in v0.1.1

func GetButtonCornerStyle(corner any) ButtonCornerStyle

GetButtonCornerStyle normalizes corner prop to ButtonCornerStyle.

type ButtonOptions added in v0.1.1

type ButtonOptions struct {
	// OnClick is called when the button is activated (Enter/Space).
	OnClick func()
	// OnKeypress is a custom key handler (called before default handling).
	OnKeypress func(key string) bool
	// DisableFocus disables focus management registration (default: false, meaning focusable by default).
	DisableFocus bool
}

ButtonOptions configures button creation.

type Cell

type Cell struct {
	Char  rune
	Style Style
}

Cell represents a single "pixel" in the terminal. It holds a character and its styling attributes.

func New

func New(char rune, style Style) Cell

New creates a new Cell with the given character and style.

func (Cell) Equal

func (a Cell) Equal(b Cell) bool

Equal returns true if two Cells are identical.

type CellBuffer

type CellBuffer struct {
	// contains filtered or unexported fields
}

CellBuffer is a fixed-size 2D grid of cells representing the terminal screen. This is the core data structure for diffing.

func NewCellBuffer

func NewCellBuffer(width, height int) *CellBuffer

NewCellBuffer creates a new buffer filled with empty cells.

func (*CellBuffer) Clear

func (b *CellBuffer) Clear()

Clear clears the entire buffer with empty cells.

func (*CellBuffer) Get

func (b *CellBuffer) Get(x, y int) Cell

Get returns the cell at (x, y), or EmptyCell if out of bounds.

func (*CellBuffer) Height

func (b *CellBuffer) Height() int

Height returns the buffer height.

func (*CellBuffer) Set

func (b *CellBuffer) Set(x, y int, c Cell)

Set sets the cell at (x, y). Does nothing if out of bounds.

func (*CellBuffer) SetChar

func (b *CellBuffer) SetChar(x, y int, char rune, style Style)

SetChar sets a character with style at (x, y).

func (*CellBuffer) SetCharMerge

func (b *CellBuffer) SetCharMerge(x, y int, char rune, style Style)

SetCharMerge sets a character, merging style with existing cell. Preserves background if the new style doesn't specify one.

func (*CellBuffer) ToDebugString

func (b *CellBuffer) ToDebugString() string

ToDebugString returns a debug string representation (characters only).

func (*CellBuffer) Width

func (b *CellBuffer) Width() int

Width returns the buffer width.

func (*CellBuffer) WriteString

func (b *CellBuffer) WriteString(x, y int, text string, style Style) int

WriteString writes a string starting at (x, y), going right. Text is clipped at buffer edge. Returns number of characters written.

type CellChange

type CellChange struct {
	X    int
	Y    int
	Cell Cell
}

CellChange represents a change at a specific position.

func DiffBuffers

func DiffBuffers(from, to *CellBuffer) []CellChange

DiffBuffers computes the diff between two buffers. Returns an array of cell changes needed to transform `from` into `to`.

func DiffBuffersInto

func DiffBuffersInto(from, to *CellBuffer, result []CellChange) []CellChange

DiffBuffersInto computes the diff between two buffers, appending to the provided slice. This avoids allocation when the caller pre-allocates the result slice.

type CellRun

type CellRun struct {
	X     int
	Y     int
	Cells []Cell
}

CellRun represents a run of consecutive cells.

func FindRuns

func FindRuns(changes []CellChange) []CellRun

FindRuns detects consecutive runs in changes for efficient output. A run is a sequence of consecutive x positions.

func FindRunsInto

func FindRunsInto(changes []CellChange, result []CellRun) []CellRun

FindRunsInto detects consecutive runs in changes, appending to the provided slice. This avoids allocation when the caller pre-allocates the result slice.

type ChildMeasurement

type ChildMeasurement struct {
	Node   gox.VNode
	Width  int
	Height int
}

ChildMeasurement holds a measured child node.

type CleanupFunc added in v0.1.1

type CleanupFunc func()

CleanupFunc is a function called to clean up an effect.

type ClipRegion

type ClipRegion struct {
	MinX int // Inclusive
	MinY int // Inclusive
	MaxX int // Exclusive
	MaxY int // Exclusive
}

ClipRegion defines the visible area for clipping content.

func IntersectClip

func IntersectClip(a, b *ClipRegion) *ClipRegion

IntersectClip intersects two clip regions, returning the overlapping area.

type Color

type Color uint8

Color represents terminal colors using a compact uint8 representation. Values 0-9 are named colors, 10+ reserved for future use. RGB colors use a separate type.

const (
	ColorNone    Color = iota // No color set (transparent)
	ColorDefault              // Terminal default
	ColorBlack
	ColorRed
	ColorGreen
	ColorYellow
	ColorBlue
	ColorMagenta
	ColorCyan
	ColorWhite
	// Bright variants (ANSI 90-97)
	ColorBrightBlack // aka grey/gray
	ColorBrightRed
	ColorBrightGreen
	ColorBrightYellow
	ColorBrightBlue
	ColorBrightMagenta
	ColorBrightCyan
	ColorBrightWhite
)

type Direction

type Direction string

Direction specifies the main axis for flex layout.

const (
	Row    Direction = "row"
	Column Direction = "column"
)

func GetDirection

func GetDirection(props gox.Props) Direction

GetDirection returns the flex direction from props.

type DisposeFunc added in v0.1.1

type DisposeFunc func()

DisposeFunc is a function that disposes an effect.

func CreateEffect added in v0.1.1

func CreateEffect(fn func() CleanupFunc) DisposeFunc

CreateEffect creates a reactive effect that runs when its dependencies change. Returns a dispose function to stop the effect.

The effect function can optionally return a cleanup function that runs before each re-execution and when the effect is disposed.

Example:

count, setCount := CreateSignal(0)

dispose := CreateEffect(func() CleanupFunc {
    fmt.Println("Count is:", count())
    return func() { fmt.Println("Cleaning up") }
})

func CreateEffectSimple added in v0.1.1

func CreateEffectSimple(fn func()) DisposeFunc

CreateEffectSimple creates an effect without cleanup.

type FocusManager

type FocusManager struct {
	// contains filtered or unexported fields
}

FocusManager manages focus state for terminal UI components.

func Manager

func Manager() *FocusManager

Manager returns the global focus manager. This is a convenience function that accesses Global.FocusManager().

func (*FocusManager) Clear

func (m *FocusManager) Clear()

Clear removes all registered focusables and handlers.

func (*FocusManager) Current

func (m *FocusManager) Current() Focusable

Current returns the currently focused element.

func (*FocusManager) GetAll

func (m *FocusManager) GetAll() []Focusable

GetAll returns all registered focusable elements.

func (*FocusManager) HandleKey

func (m *FocusManager) HandleKey(key string) bool

HandleKey routes a keypress to the focused element. Handles Tab/Shift+Tab for focus navigation. Returns true if the key was consumed.

func (*FocusManager) Next

func (m *FocusManager) Next()

Next focuses the next element in registration order.

func (*FocusManager) Prev

func (m *FocusManager) Prev()

Prev focuses the previous element in registration order.

func (*FocusManager) Register

func (m *FocusManager) Register(f Focusable)

Register adds a focusable to the manager.

func (*FocusManager) RequestBlur

func (m *FocusManager) RequestBlur(f Focusable)

RequestBlur blurs a specific focusable.

func (*FocusManager) RequestFocus

func (m *FocusManager) RequestFocus(f Focusable)

RequestFocus focuses a specific focusable.

func (*FocusManager) Set

func (m *FocusManager) Set(f Focusable)

Set manually sets the focused element. Pass nil to blur all.

func (*FocusManager) SetGlobalKeyHandler

func (m *FocusManager) SetGlobalKeyHandler(handler func(key string) bool) func()

SetGlobalKeyHandler sets a handler for app-wide keyboard shortcuts. This handler is called for keys that no focused element consumes. Returns a cleanup function to remove the handler.

func (*FocusManager) Unregister

func (m *FocusManager) Unregister(f Focusable)

Unregister removes a focusable from the manager.

type Focusable

type Focusable interface {
	Focused() bool
	Focus()
	Blur()
	Dispose()
	HandleKey(key string) bool
	SetFocused(focused bool)
}

Focusable is the interface for any focusable element (input, button, etc).

type Input

type Input struct {
	// contains filtered or unexported fields
}

Input represents a text input field.

func NewInput

func NewInput(opts InputOptions) *Input

NewInput creates a new input field.

func (*Input) Blur

func (i *Input) Blur()

Blur removes focus from this input.

func (*Input) Clear

func (i *Input) Clear()

Clear clears the input.

func (*Input) CursorPos

func (i *Input) CursorPos() int

CursorPos returns the cursor position.

func (*Input) DisplayValue

func (i *Input) DisplayValue() string

DisplayValue returns the display text (with masking/placeholder).

func (*Input) Dispose

func (i *Input) Dispose()

Dispose unregisters from the focus manager.

func (*Input) Focus

func (i *Input) Focus()

Focus gives focus to this input.

func (*Input) Focused

func (i *Input) Focused() bool

Focused returns whether the input is focused.

func (*Input) GetState

func (i *Input) GetState() InputState

GetState returns the current state snapshot.

func (*Input) HandleKey

func (i *Input) HandleKey(key string) bool

HandleKey processes a key press. Returns true if the key was consumed.

func (*Input) SetCursorPos

func (i *Input) SetCursorPos(pos int)

SetCursorPos updates the cursor position.

func (*Input) SetFocused

func (i *Input) SetFocused(f bool)

SetFocused sets the focused state (called by focus manager).

func (*Input) SetValue

func (i *Input) SetValue(value string)

SetValue updates the text value.

func (*Input) ShowingPlaceholder

func (i *Input) ShowingPlaceholder() bool

ShowingPlaceholder returns true if displaying placeholder text.

func (*Input) Value

func (i *Input) Value() string

Value returns the current text value.

type InputKeyHandler

type InputKeyHandler func(key string, state InputState) *InputState

InputKeyHandler is a keypress handler. Return new state to consume the key, or nil to let it bubble up.

func ComposeInputHandlers

func ComposeInputHandlers(handlers ...InputKeyHandler) InputKeyHandler

ComposeInputHandlers combines multiple handlers into one. Handlers are tried in order until one returns non-nil.

type InputOptions

type InputOptions struct {
	// InitialValue is the starting text.
	InitialValue string
	// MaxLength limits the number of characters (0 = unlimited).
	MaxLength int
	// Mask character for passwords (e.g., "*").
	Mask rune
	// Placeholder text shown when input is empty.
	Placeholder string
	// OnKeypress is a custom keypress handler.
	OnKeypress InputKeyHandler
}

InputOptions configures input creation.

type InputState

type InputState struct {
	Value     string
	CursorPos int
}

InputState represents the state of an input field.

func InputDeletionHandler

func InputDeletionHandler(key string, state InputState) *InputState

InputDeletionHandler handles backspace, delete, word delete.

func InputNavigationHandler

func InputNavigationHandler(key string, state InputState) *InputState

InputNavigationHandler handles arrow keys, home/end, word navigation.

func InputNewlineHandler

func InputNewlineHandler(key string, state InputState) *InputState

InputNewlineHandler inserts newline on Enter (for multiline editors).

func InputPrintableHandler

func InputPrintableHandler(key string, state InputState) *InputState

InputPrintableHandler inserts printable characters at cursor.

func InputShiftEnterHandler

func InputShiftEnterHandler(key string, state InputState) *InputState

InputShiftEnterHandler inserts newline only on Shift+Enter.

type IntrinsicHandler

type IntrinsicHandler struct {
	// Layout computes the layout for this element type.
	// If nil, default box layout is used.
	Layout IntrinsicLayoutFunc

	// Measure returns the intrinsic size of this element.
	// If nil, size is determined by props or children.
	Measure IntrinsicMeasureFunc

	// Render draws this element to a CellBuffer.
	// If nil, children are rendered with default box behavior.
	Render IntrinsicRenderFunc

	// RenderLogical draws this element to a LogicalBuffer.
	// If nil, children are rendered with default box behavior.
	RenderLogical IntrinsicRenderLogicalFunc
}

IntrinsicHandler defines how to layout and render an intrinsic element type.

func GetIntrinsicHandler

func GetIntrinsicHandler(name string) *IntrinsicHandler

GetIntrinsicHandler returns the handler for an intrinsic element type. Returns nil if no handler is registered.

type IntrinsicLayoutFunc

type IntrinsicLayoutFunc func(node gox.VNode, availWidth, availHeight int, ctx *LayoutContext) *LayoutBox

IntrinsicLayoutFunc handles layout for an intrinsic element type. It receives the node, available width/height, and layout context. Returns the computed LayoutBox for this element.

type IntrinsicMeasureFunc

type IntrinsicMeasureFunc func(node gox.VNode, ctx *LayoutContext) (int, int)

IntrinsicMeasureFunc measures the intrinsic size of an element. Returns (width, height).

type IntrinsicRenderFunc

type IntrinsicRenderFunc func(box *LayoutBox, buf *CellBuffer, clip *ClipRegion)

IntrinsicRenderFunc renders an element to a CellBuffer.

type IntrinsicRenderLogicalFunc

type IntrinsicRenderLogicalFunc func(box *LayoutBox, buf *LogicalBuffer, clip *ClipRegion)

IntrinsicRenderLogicalFunc renders an element to a LogicalBuffer.

type Justify

type Justify string

Justify specifies alignment along the main axis.

const (
	JustifyStart        Justify = "start"
	JustifyCenter       Justify = "center"
	JustifyEnd          Justify = "end"
	JustifySpaceBetween Justify = "space-between"
	JustifySpaceAround  Justify = "space-around"
)

func GetJustify

func GetJustify(props gox.Props) Justify

GetJustify returns the justify-content from props.

type Keyed added in v0.1.5

type Keyed[K comparable] interface {
	GetKey() K
}

Keyed is the interface that memoized component props must implement. It provides the cache key for identifying component instances across renders. K must be comparable (int, string, etc.)

type LayoutBox

type LayoutBox struct {
	// Position (absolute, after all calculations)
	X      int
	Y      int
	Width  int
	Height int

	// Content area (inside padding/border)
	InnerX      int
	InnerY      int
	InnerWidth  int
	InnerHeight int

	// The node this box represents
	Node gox.VNode

	// Child boxes
	Children []*LayoutBox

	// For z-index sorting
	ZIndex int
}

LayoutBox represents a computed layout for a node.

func ComputeLayout

func ComputeLayout(node gox.VNode, ctx LayoutContext) *LayoutBox

ComputeLayout computes layout for a VNode tree.

func LayoutFlexChildren

func LayoutFlexChildren(
	children []ChildMeasurement,
	ctx LayoutContext,
	direction Direction,
	justify Justify,
	align Align,
	gap int,
	absoluteBoxes *[]*LayoutBox,
) []*LayoutBox

LayoutFlexChildren lays out children using flexbox rules.

type LayoutContext

type LayoutContext struct {
	X      int
	Y      int
	Width  int
	Height int
}

LayoutContext provides the available space for layout.

type LayoutResult

type LayoutResult struct {
	Box           *LayoutBox
	AbsoluteBoxes []*LayoutBox
}

LayoutResult holds the result of layout computation.

func LayoutNode

func LayoutNode(node gox.VNode, ctx LayoutContext) LayoutResult

LayoutNode computes layout for a single node.

type Link struct {
	// contains filtered or unexported fields
}

Link represents a clickable hyperlink component.

func NewLink(opts LinkOptions) *Link

NewLink creates a new link.

func (*Link) Activate added in v0.1.1

func (l *Link) Activate()

Activate opens the URL and calls the onClick handler.

func (*Link) Blur added in v0.1.1

func (l *Link) Blur()

Blur removes focus from this link.

func (*Link) Dispose added in v0.1.1

func (l *Link) Dispose()

Dispose unregisters from the focus manager.

func (*Link) Focus added in v0.1.1

func (l *Link) Focus()

Focus gives focus to this link.

func (*Link) Focused added in v0.1.1

func (l *Link) Focused() bool

Focused returns whether the link is focused.

func (*Link) HandleKey added in v0.1.1

func (l *Link) HandleKey(key string) bool

HandleKey processes a key press. Returns true if the key was consumed.

func (*Link) SetFocused added in v0.1.1

func (l *Link) SetFocused(f bool)

SetFocused sets the focused state (called by focus manager).

func (*Link) SetURL added in v0.1.1

func (l *Link) SetURL(url string)

SetURL updates the link's target URL.

func (*Link) URL added in v0.1.1

func (l *Link) URL() string

URL returns the link's target URL.

type LinkOptions added in v0.1.1

type LinkOptions struct {
	// URL is the target URL to open.
	URL string
	// OnClick is called when the link is activated (in addition to opening URL).
	OnClick func()
	// DisableFocus disables focus management registration.
	DisableFocus bool
}

LinkOptions configures link creation.

type LogCapture

type LogCapture struct {
	// contains filtered or unexported fields
}

LogCapture captures log output for display in the TUI

func NewLogCapture

func NewLogCapture(maxMessages int) *LogCapture

NewLogCapture creates a new log capture with the specified max message count

func (*LogCapture) Clear

func (lc *LogCapture) Clear()

Clear clears all captured messages

func (*LogCapture) Debug

func (lc *LogCapture) Debug(format string, args ...any)

Debug logs a debug message

func (*LogCapture) Error

func (lc *LogCapture) Error(format string, args ...any)

Error logs an error message

func (*LogCapture) Info

func (lc *LogCapture) Info(format string, args ...any)

Info logs an info message

func (*LogCapture) LastMessages

func (lc *LogCapture) LastMessages(n int) []LogMessage

LastMessages returns the last n messages (reactive)

func (*LogCapture) Log

func (lc *LogCapture) Log(level LogLevel, format string, args ...any)

Log logs a message at the specified level

func (*LogCapture) Messages

func (lc *LogCapture) Messages() []LogMessage

Messages returns the current messages (reactive)

func (*LogCapture) OriginalStdout

func (lc *LogCapture) OriginalStdout() *os.File

OriginalStdout returns the original stdout file

func (*LogCapture) Start

func (lc *LogCapture) Start() error

Start begins capturing stdout and stderr

func (*LogCapture) Stop

func (lc *LogCapture) Stop()

Stop stops capturing and restores original stdout/stderr

func (*LogCapture) Warn

func (lc *LogCapture) Warn(format string, args ...any)

Warn logs a warning message

func (*LogCapture) WriteToOriginal

func (lc *LogCapture) WriteToOriginal(p []byte) (n int, err error)

WriteToOriginal writes directly to the original stdout (bypassing capture) This is useful for TUI rendering

type LogLevel

type LogLevel string

LogLevel represents the severity of a log message

const (
	LogLevelDebug LogLevel = "DEBUG"
	LogLevelInfo  LogLevel = "INFO"
	LogLevelWarn  LogLevel = "WARN"
	LogLevelError LogLevel = "ERROR"
)

type LogMessage

type LogMessage struct {
	Timestamp time.Time
	Level     LogLevel
	Message   string
}

LogMessage represents a captured log message

type LogicalBuffer

type LogicalBuffer struct {
	// contains filtered or unexported fields
}

LogicalBuffer stores content as logical rows with arbitrary length. Terminal wrapping is handled at render time, not storage time.

func NewLogicalBuffer

func NewLogicalBuffer(height int) *LogicalBuffer

NewLogicalBuffer creates a new logical buffer with the given height.

func (*LogicalBuffer) Clear

func (b *LogicalBuffer) Clear()

Clear clears the entire buffer.

func (*LogicalBuffer) ClearRow

func (b *LogicalBuffer) ClearRow(y int)

ClearRow clears a row.

func (*LogicalBuffer) Get

func (b *LogicalBuffer) Get(x, y int) Cell

Get returns the cell at logical position (x, y). Returns EmptyCell if out of bounds.

func (*LogicalBuffer) GetRow

func (b *LogicalBuffer) GetRow(y int) *LogicalRow

GetRow returns a logical row.

func (*LogicalBuffer) Height

func (b *LogicalBuffer) Height() int

Height returns the number of logical rows.

func (*LogicalBuffer) RowLength

func (b *LogicalBuffer) RowLength(y int) int

RowLength returns the length of a logical row.

func (*LogicalBuffer) Set

func (b *LogicalBuffer) Set(x, y int, c Cell)

Set sets the cell at logical position (x, y). Extends the row if needed. Grows the buffer if y exceeds current height. Will not grow beyond MaxBufferHeight.

func (*LogicalBuffer) SetMerge

func (b *LogicalBuffer) SetMerge(x, y int, c Cell)

SetMerge sets a cell, merging style with existing cell. Preserves background color if the new style doesn't specify one. Grows the buffer if y exceeds current height. Will not grow beyond MaxBufferHeight.

func (*LogicalBuffer) ToVisualRows

func (b *LogicalBuffer) ToVisualRows(terminalWidth int) VisualRows

ToVisualRows transforms logical rows to visual rows based on terminal width.

func (*LogicalBuffer) WriteString

func (b *LogicalBuffer) WriteString(x, y int, text string, style Style)

WriteString writes a string starting at (x, y). The row extends as needed - no clipping.

type LogicalRow

type LogicalRow struct {
	Cells []Cell
}

LogicalRow is a variable-length array of cells.

type MemoStats added in v0.1.5

type MemoStats struct {
	Generation int64
}

MemoStats returns cache statistics (for debugging/benchmarking).

func GetMemoStats added in v0.1.5

func GetMemoStats() MemoStats

GetMemoStats returns current memo statistics.

type Options

type Options struct {
	Width           int
	Height          int
	Output          io.Writer
	Pipeline        bool // Force pipeline renderer (auto-detected if not set)
	DisableThrottle bool // Disable frame rate limiting (for tests)
	OnRender        func()
	OnError         func(error)
}

Options configures the renderer and app.

type Overflow

type Overflow string

Overflow specifies overflow behavior.

const (
	OverflowVisible Overflow = "visible"
	OverflowHidden  Overflow = "hidden"
	OverflowScroll  Overflow = "scroll"
)

func GetOverflow

func GetOverflow(props map[string]any) Overflow

GetOverflow returns the overflow mode from props.

type Owner added in v0.1.1

type Owner struct {
	// contains filtered or unexported fields
}

Owner tracks disposables for cleanup.

func GetOwner added in v0.1.1

func GetOwner() *Owner

GetOwner returns the current owner, if any.

type PipelineRenderer

type PipelineRenderer struct {
	// contains filtered or unexported fields
}

PipelineRenderer uses a 4-stage concurrent pipeline for rendering. Each stage runs in its own goroutine:

  1. Layout: VNode → LayoutBox
  2. Buffer: LayoutBox → CellBuffer
  3. Diff: CellBuffer → []CellChange → []CellRun → ANSI string
  4. Output: ANSI string → io.Writer

func NewPipeline

func NewPipeline(opts Options) *PipelineRenderer

NewPipeline creates a new pipelined renderer.

func (*PipelineRenderer) Render

func (p *PipelineRenderer) Render(root gox.VNode)

Render submits a frame to the pipeline (non-blocking if pipeline has capacity).

func (*PipelineRenderer) RenderBlocking

func (p *PipelineRenderer) RenderBlocking(root gox.VNode)

RenderBlocking submits a frame and waits until it enters the pipeline.

func (*PipelineRenderer) Stop

func (p *PipelineRenderer) Stop()

Stop shuts down the pipeline gracefully.

type Position

type Position string

Position specifies positioning mode.

const (
	PositionRelative Position = "relative"
	PositionAbsolute Position = "absolute"
)

type PrintOptions added in v0.1.6

type PrintOptions struct {
	Width  int // 0 = auto-detect terminal width (default 80)
	Height int // 0 = auto-detect terminal height (default 24)
}

PrintOptions configures dimensions for Fprint.

type Props

type Props = gox.Props

Props is an alias for gox.Props.

type RGB

type RGB struct {
	R, G, B uint8
}

RGB represents a 24-bit true color. When used, the Color field should be set to a special marker.

type Renderer

type Renderer struct {
	// contains filtered or unexported fields
}

Renderer is the main orchestrator that ties everything together. Uses LogicalBuffer for content storage, transforms to visual rows for output.

func NewRenderer

func NewRenderer(opts Options) *Renderer

NewRenderer creates a new renderer.

func (*Renderer) CurrentBuffer

func (r *Renderer) CurrentBuffer() *CellBuffer

CurrentBuffer returns the current visual buffer (for testing).

func (*Renderer) Height

func (r *Renderer) Height() int

Height returns the terminal height.

func (*Renderer) Render

func (r *Renderer) Render(root gox.VNode)

Render renders a gox VNode tree to the terminal.

func (*Renderer) Resize

func (r *Renderer) Resize(width, height int)

Resize resizes the renderer.

func (*Renderer) Width

func (r *Renderer) Width() int

Width returns the terminal width.

type RendererInterface

type RendererInterface interface {
	Render(root gox.VNode)
}

RendererInterface defines the common interface for all renderers.

func NewAuto

func NewAuto(opts Options) RendererInterface

NewAuto creates the optimal renderer based on grid size. Uses pipeline renderer for larger grids (>3000 cells) and sequential for smaller ones.

type RunOptions

type RunOptions struct {
	Width              int
	Height             int
	Output             io.Writer
	OnMount            func(*App)
	OnUnmount          func()
	OnRender           func()
	OnError            func(error)
	CaptureConsole     bool // Capture console output (default: true). Press Ctrl+L to toggle log viewer.
	MaxConsoleMessages int  // Maximum number of console messages to keep (default: 1000)
}

RunOptions configures the Run function.

type Runtime added in v0.1.1

type Runtime struct {
	// contains filtered or unexported fields
}

Runtime holds all global mutable state for the goli framework. This enables easy state clearing for tests via Reset().

var Global *Runtime

Global is the package-level runtime instance.

func NewRuntime added in v0.1.1

func NewRuntime() *Runtime

NewRuntime creates a new Runtime with initialized state.

func (*Runtime) FocusManager added in v0.1.1

func (rt *Runtime) FocusManager() *FocusManager

FocusManager returns the focus manager, creating it if needed.

type Select

type Select[T comparable] struct {
	// contains filtered or unexported fields
}

Select represents a list selection component. The select tracks the selected index; option values come from <option> children.

func NewSelect

func NewSelect[T comparable](opts SelectOptions[T]) *Select[T]

NewSelect creates a new select primitive.

func (*Select[T]) Blur

func (s *Select[T]) Blur()

Blur removes focus from this select.

func (*Select[T]) ClearOptions

func (s *Select[T]) ClearOptions()

ClearOptions clears registered options (called during layout).

func (*Select[T]) Dispose

func (s *Select[T]) Dispose()

Dispose unregisters from the focus manager.

func (*Select[T]) Focus

func (s *Select[T]) Focus()

Focus gives focus to this select.

func (*Select[T]) Focused

func (s *Select[T]) Focused() bool

Focused returns whether this select is focused.

func (*Select[T]) HandleKey

func (s *Select[T]) HandleKey(key string) bool

HandleKey processes a key press.

func (*Select[T]) IsSelectedIndex

func (s *Select[T]) IsSelectedIndex(index int) bool

IsSelectedIndex returns true if the given index is selected.

func (*Select[T]) Next

func (s *Select[T]) Next()

Next selects the next option.

func (*Select[T]) Prev

func (s *Select[T]) Prev()

Prev selects the previous option.

func (*Select[T]) RegisterOption

func (s *Select[T]) RegisterOption(index int, value T)

RegisterOption registers an option value at an index (called during layout). This does NOT trigger re-renders.

func (*Select[T]) RegisterOptionAny

func (s *Select[T]) RegisterOptionAny(index int, value any)

RegisterOptionAny registers an option value at an index (type-unsafe version for intrinsic use).

func (*Select[T]) SelectedIndex

func (s *Select[T]) SelectedIndex() int

SelectedIndex returns the currently selected index.

func (*Select[T]) SetFocused

func (s *Select[T]) SetFocused(f bool)

SetFocused sets the focused state (called by focus manager).

func (*Select[T]) SetIndex

func (s *Select[T]) SetIndex(index int)

SetIndex sets selection by index.

func (*Select[T]) SetOptionCount

func (s *Select[T]) SetOptionCount(count int)

SetOptionCount sets the option count (called during layout). This does NOT trigger re-renders.

func (*Select[T]) Value

func (s *Select[T]) Value() T

Value returns the currently selected value.

type SelectOptions

type SelectOptions[T comparable] struct {
	// InitialValue is the starting selection (used to set initial index when option with this value is registered).
	InitialValue T
	// OnChange is called when selection changes.
	OnChange func(value T)
	// OnKeypress is a custom key handler (called before default handling).
	OnKeypress func(key string) bool
	// DisableFocus disables focus management registration (default: false, meaning focusable by default).
	DisableFocus bool
}

SelectOptions configures select creation.

type Setter added in v0.1.1

type Setter[T any] func(T)

Setter is a function that updates a signal value.

type SetterFunc added in v0.1.1

type SetterFunc[T any] func(prev T) T

SetterFunc updates based on previous value.

type Spacing

type Spacing struct {
	Top    int
	Right  int
	Bottom int
	Left   int
}

Spacing represents padding or margin on all sides.

func GetSpacing added in v0.1.2

func GetSpacing(props map[string]any, baseProp string) Spacing

GetSpacing extracts spacing from props, supporting both base prop and directional overrides. For example, GetSpacing(props, "padding") reads "padding" and also "paddingTop", "paddingRight", "paddingBottom", "paddingLeft" as overrides.

func NormalizeSpacing

func NormalizeSpacing(value any) Spacing

NormalizeSpacing converts various spacing inputs to a Spacing struct.

type State

type State struct {
	// contains filtered or unexported fields
}

State holds the terminal state for later restoration.

func MakeRaw

func MakeRaw(fd int) (*State, error)

MakeRaw puts the terminal into raw mode and returns the previous state.

type Style

type Style struct {
	Color         Color
	Background    Color
	Bold          bool
	Dim           bool
	Italic        bool
	Underline     bool
	Inverse       bool
	Strikethrough bool
	// RGB colors (only used when Color/Background need 24-bit)
	ColorRGB      *RGB
	BackgroundRGB *RGB
	// HyperlinkURL for OSC 8 terminal hyperlinks
	HyperlinkURL string
}

Style holds text styling attributes. Uses compact representation: 2 bytes for colors, 1 byte for flags, plus optional RGB.

func GetStyle

func GetStyle(props map[string]any) Style

GetStyle extracts a Style from props. Supports both style map (`style={...}`) and direct attribute props (`color="green"`, `bold`, etc.). Direct props override style map values.

func (Style) Equal

func (a Style) Equal(b Style) bool

Equal returns true if two Styles are identical.

func (Style) HasBackground

func (s Style) HasBackground() bool

HasBackground returns true if the style has a background color set.

func (Style) HasColor

func (s Style) HasColor() bool

HasColor returns true if the style has a foreground color set.

func (Style) Merge

func (base Style) Merge(overlay Style) Style

Merge creates a new Style by combining two styles. The overlay style takes precedence for non-zero values.

type VNode

type VNode = gox.VNode

VNode is an alias for gox.VNode - no wrapper needed.

type VisualRows

type VisualRows struct {
	Rows            [][]Cell // Visual rows
	LogicalToVisual []int    // LogicalToVisual[logicalY] = first visual row index
}

VisualRows holds the result of transforming logical rows to visual rows.

Directories

Path Synopsis
benchmarks
comparison-ratatui/memo_bench command
Benchmark comparing naive vs memoized components
Benchmark comparing naive vs memoized components
cpu command
Package main provides CPU usage benchmarking for goli applications.
Package main provides CPU usage benchmarking for goli applications.
examples
perf-benchmark command
Performance benchmark - measures actual render pipeline timing
Performance benchmark - measures actual render pipeline timing
perf-pipeline command
Pipeline benchmark - compare sequential vs pipelined rendering
Pipeline benchmark - compare sequential vs pipelined rendering
perf-scaling command
Scaling test - see how performance changes with grid size
Scaling test - see how performance changes with grid size
perf-stress command
Performance stress test Tests rendering performance with rapidly changing content and scrolling.
Performance stress test Tests rendering performance with rapidly changing content and scrolling.

Jump to

Keyboard shortcuts

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