zcatui/docs/API.md
reugenio 5556ee1370 zcatui v1.2 - Sistema de eventos integrado (crossterm-style)
Eventos de teclado:
- Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind
- Soporte para todas las teclas: caracteres, F1-F12, navegación, control
- Modificadores: Ctrl, Alt, Shift, Super

Eventos de ratón:
- MouseEvent, MouseEventKind, MouseButton
- Click, release, drag, scroll (up/down/left/right)
- Posición (column, row) con modificadores
- Protocolos: SGR extended y X10 legacy

Parser de escape sequences:
- CSI sequences (arrows, F-keys, navigation)
- SS3 sequences (F1-F4 alternativo)
- SGR mouse protocol (mejores coordenadas)
- X10 mouse protocol (compatibilidad)
- Focus events, bracketed paste

Cursor control:
- Visibility: show/hide
- Blinking: enable/disable
- Styles: block, underline, bar (blinking/steady)
- Position: moveTo, moveUp/Down/Left/Right
- Save/restore position

Terminal integrado:
- pollEvent(timeout_ms) - polling con timeout
- readEvent() - blocking read
- enableMouseCapture/disableMouseCapture
- enableFocusChange/disableFocusChange
- enableBracketedPaste/disableBracketedPaste

Ejemplo interactivo:
- examples/events_demo.zig

Tests: 47 (29 nuevos para eventos y cursor)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 12:55:54 +01:00

21 KiB
Raw Permalink Blame History

zcatui - API Reference

Referencia rapida de la API publica de zcatui

Importar la Libreria

const zcatui = @import("zcatui");

// Core types
const Color = zcatui.Color;
const Style = zcatui.Style;
const Modifier = zcatui.Modifier;
const Cell = zcatui.Cell;
const Buffer = zcatui.Buffer;
const Rect = zcatui.Rect;
const Span = zcatui.Span;
const Line = zcatui.Line;
const Text = zcatui.Text;
const Alignment = zcatui.Alignment;

// Layout
const Layout = zcatui.Layout;
const Constraint = zcatui.Constraint;
const Direction = zcatui.Direction;

// Terminal
const Terminal = zcatui.Terminal;

// Events (crossterm-style)
const Event = zcatui.Event;
const KeyEvent = zcatui.KeyEvent;
const KeyCode = zcatui.KeyCode;
const KeyModifiers = zcatui.KeyModifiers;
const MouseEvent = zcatui.MouseEvent;
const MouseEventKind = zcatui.MouseEventKind;
const MouseButton = zcatui.MouseButton;

// Cursor
const Cursor = zcatui.Cursor;
const CursorStyle = zcatui.CursorStyle;

// Widgets
const widgets = zcatui.widgets;
const Block = widgets.Block;
const Paragraph = widgets.Paragraph;
const List = widgets.List;
const ListState = widgets.ListState;
// ... etc

Core Types

Color

pub const Color = union(enum) {
    // Reset
    reset,

    // Basic 16 colors
    black, red, green, yellow, blue, magenta, cyan, white,
    light_black, light_red, light_green, light_yellow,
    light_blue, light_magenta, light_cyan, light_white,

    // 256 color palette
    indexed: u8,

    // True color (24-bit)
    rgb: struct { r: u8, g: u8, b: u8 },

    // Constructor helpers
    pub fn indexed(n: u8) Color;
    pub fn rgb(r: u8, g: u8, b: u8) Color;
};

Ejemplos:

const red = Color.red;
const gray = Color.indexed(240);
const custom = Color.rgb(255, 128, 0);

Style

pub const Style = struct {
    foreground: ?Color = null,
    background: ?Color = null,
    add_modifiers: Modifier = .{},
    sub_modifiers: Modifier = .{},

    pub const default: Style = .{};

    // Fluent setters
    pub fn fg(self: Style, color: Color) Style;
    pub fn bg(self: Style, color: Color) Style;
    pub fn bold(self: Style) Style;
    pub fn dim(self: Style) Style;
    pub fn italic(self: Style) Style;
    pub fn underlined(self: Style) Style;
    pub fn slow_blink(self: Style) Style;
    pub fn rapid_blink(self: Style) Style;
    pub fn reversed(self: Style) Style;
    pub fn hidden(self: Style) Style;
    pub fn crossed_out(self: Style) Style;

    // Remove modifiers
    pub fn notBold(self: Style) Style;
    pub fn notDim(self: Style) Style;
    // ... etc

    // Combine styles
    pub fn patch(self: Style, other: Style) Style;
};

Ejemplos:

const style1 = Style.default.fg(Color.red).bold();
const style2 = Style.default.bg(Color.blue).italic();
const combined = style1.patch(style2);  // red fg, blue bg, bold+italic

Modifier

pub const Modifier = packed struct {
    bold: bool = false,
    dim: bool = false,
    italic: bool = false,
    underlined: bool = false,
    slow_blink: bool = false,
    rapid_blink: bool = false,
    reversed: bool = false,
    hidden: bool = false,
    crossed_out: bool = false,

    pub const empty: Modifier = .{};
    pub const all: Modifier = .{ .bold = true, ... };

    pub fn contains(self: Modifier, other: Modifier) bool;
    pub fn insert(self: Modifier, other: Modifier) Modifier;
    pub fn remove(self: Modifier, other: Modifier) Modifier;
};

Rect

pub const Rect = struct {
    x: u16,
    y: u16,
    width: u16,
    height: u16,

    pub fn init(x: u16, y: u16, width: u16, height: u16) Rect;

    // Getters
    pub fn left(self: Rect) u16;    // x
    pub fn right(self: Rect) u16;   // x + width
    pub fn top(self: Rect) u16;     // y
    pub fn bottom(self: Rect) u16;  // y + height
    pub fn area(self: Rect) u32;    // width * height

    // Queries
    pub fn isEmpty(self: Rect) bool;
    pub fn contains(self: Rect, x: u16, y: u16) bool;

    // Transformations
    pub fn inner(self: Rect, margin: u16) Rect;
    pub fn innerMargins(self: Rect, top: u16, right: u16, bottom: u16, left_: u16) Rect;
    pub fn intersection(self: Rect, other: Rect) Rect;
    pub fn union_(self: Rect, other: Rect) Rect;
};

Ejemplos:

const area = Rect.init(0, 0, 80, 24);
const inner = area.inner(1);  // Rect.init(1, 1, 78, 22)

Symbol

Almacenamiento compacto UTF-8 (hasta 4 bytes). Optimizado para evitar conversiones en render.

pub const Symbol = struct {
    data: [4]u8 = .{ ' ', 0, 0, 0 },
    len: u3 = 1,

    pub const default_val: Symbol = .{};
    pub const space: Symbol = .{};

    // Constructores
    pub fn fromSlice(bytes: []const u8) Symbol;
    pub fn fromCodepoint(cp: u21) Symbol;

    // Acceso
    pub fn slice(self: Symbol) []const u8;  // Para output directo
    pub fn eql(self: Symbol, other: Symbol) bool;
};

Cell

pub const Cell = struct {
    symbol: Symbol = Symbol.space,
    fg: Color = .reset,
    bg: Color = .reset,
    modifiers: Modifier = .{},

    pub const empty: Cell = .{};

    // Constructores
    pub fn init(cp: u21) Cell;
    pub fn fromStr(s: []const u8) Cell;

    // Modificadores
    pub fn setStyle(self: *Cell, s: Style) void;
    pub fn setChar(self: *Cell, cp: u21) void;
    pub fn setSymbol(self: *Cell, s: []const u8) void;
    pub fn reset(self: *Cell) void;

    // Comparación (para diff)
    pub fn eql(self: Cell, other: Cell) bool;

    // Legacy accessor
    pub fn char(self: Cell) u21;  // Decodifica symbol a codepoint
};

Buffer

pub const Buffer = struct {
    area: Rect,
    cells: []Cell,
    allocator: Allocator,

    pub fn init(allocator: Allocator, area: Rect) !Buffer;
    pub fn deinit(self: *Buffer) void;

    // Cell access
    pub fn getPtr(self: *Buffer, x: u16, y: u16) ?*Cell;
    pub fn getCell(self: *Buffer, x: u16, y: u16) ?*Cell;  // Alias
    pub fn get(self: *const Buffer, x: u16, y: u16) ?Cell;

    // Setting content
    pub fn setChar(self: *Buffer, x: u16, y: u16, cp: u21, s: Style) void;
    pub fn setString(self: *Buffer, x: u16, y: u16, text: []const u8, style: Style) u16;
    pub fn setStyle(self: *Buffer, area: Rect, style: Style) void;

    // Filling
    pub fn fill(self: *Buffer, rect: Rect, cp: u21, s: Style) void;
    pub fn clear(self: *Buffer) void;

    // Differential rendering (v1.1)
    pub fn diff(self: *const Buffer, other: *const Buffer) DiffIterator;
    pub fn resize(self: *Buffer, new_rect: Rect) !void;
    pub fn merge(self: *Buffer, other: *const Buffer) void;

    // Legacy (no-op, for compatibility)
    pub fn markDirty(self: *Buffer) void;
    pub fn markClean(self: *Buffer) void;
};

// Iterator para renderizado diferencial
pub const DiffIterator = struct {
    pub fn next(self: *DiffIterator) ?CellUpdate;
    pub fn countRemaining(self: *DiffIterator) usize;
};

pub const CellUpdate = struct {
    x: u16,
    y: u16,
    cell: Cell,
};

Ejemplos:

var buf = try Buffer.init(allocator, Rect.init(0, 0, 80, 24));
defer buf.deinit();

_ = buf.setString(10, 5, "Hello, World!", Style.default.fg(Color.green));

if (buf.getCell(10, 5)) |cell| {
    cell.setStyle(Style.default.bold());
}

Text Types

Span

pub const Span = struct {
    content: []const u8,
    style: Style,

    pub fn init(content: []const u8) Span;
    pub fn raw(content: []const u8) Span;
    pub fn styled(content: []const u8, style: Style) Span;
    pub fn setStyle(self: Span, style: Style) Span;
    pub fn width(self: Span) usize;
};

Line

pub const Line = struct {
    spans: []const Span,
    alignment: Alignment,

    pub fn init(spans: []const Span) Line;
    pub fn raw(content: []const u8) Line;
    pub fn styled(content: []const u8, style: Style) Line;
    pub fn setStyle(self: Line, style: Style) Line;
    pub fn setAlignment(self: Line, alignment: Alignment) Line;
    pub fn width(self: Line) usize;
};

Text

pub const Text = struct {
    lines: []const Line,
    alignment: Alignment,

    pub fn init(lines: []const Line) Text;
    pub fn raw(content: []const u8) Text;
    pub fn styled(content: []const u8, style: Style) Text;
    pub fn setStyle(self: Text, style: Style) Text;
    pub fn setAlignment(self: Text, alignment: Alignment) Text;
    pub fn width(self: Text) usize;
    pub fn height(self: Text) usize;
};

Alignment

pub const Alignment = enum {
    left,
    center,
    right,
};

Ejemplos:

// Simple text
const span = Span.styled("Hello", Style.default.fg(Color.red));
const line = Line.raw("Simple line");

// Multi-span line
const multi_line = Line.init(&[_]Span{
    Span.styled("Error: ", Style.default.fg(Color.red).bold()),
    Span.raw("Something went wrong"),
});

// Multi-line text
const text = Text.raw("Line 1\nLine 2\nLine 3");

Layout

Layout

pub const Layout = struct {
    direction: Direction,
    constraints: []const Constraint,

    pub fn horizontal(constraints: []const Constraint) Layout;
    pub fn vertical(constraints: []const Constraint) Layout;
    pub fn init(direction: Direction, constraints: []const Constraint) Layout;

    pub fn split(self: Layout, area: Rect, result: []Rect) void;
};

Constraint

pub const Constraint = union(enum) {
    length: u16,      // Exactly N cells
    min: u16,         // At least N cells
    max: u16,         // At most N cells
    percentage: u16,  // N% of available space (0-100)
    ratio: struct { num: u32, den: u32 },
    fill: u16,        // Fill remaining space (weight)

    pub fn length(n: u16) Constraint;
    pub fn min(n: u16) Constraint;
    pub fn max(n: u16) Constraint;
    pub fn percentage(n: u16) Constraint;
    pub fn ratio(num: u32, den: u32) Constraint;
    pub fn fill(weight: u16) Constraint;
};

Direction

pub const Direction = enum {
    horizontal,
    vertical,
};

Ejemplos:

// Vertical layout: header (3 rows), content (rest), footer (1 row)
const layout = Layout.vertical(&[_]Constraint{
    Constraint.length(3),
    Constraint.min(0),
    Constraint.length(1),
});

var chunks: [3]Rect = undefined;
layout.split(area, &chunks);

// Horizontal split: 30% | 70%
const h_layout = Layout.horizontal(&[_]Constraint{
    Constraint.percentage(30),
    Constraint.percentage(70),
});

Symbols

Line Set

pub const line = struct {
    pub const Set = struct {
        vertical: []const u8,
        horizontal: []const u8,
        top_right: []const u8,
        top_left: []const u8,
        bottom_right: []const u8,
        bottom_left: []const u8,
        vertical_left: []const u8,
        vertical_right: []const u8,
        horizontal_down: []const u8,
        horizontal_up: []const u8,
        cross: []const u8,
    };

    pub const NORMAL: Set;   // ─│┌┐└┘
    pub const ROUNDED: Set;  // ─│╭╮╰╯
    pub const DOUBLE: Set;   // ═║╔╗╚╝
    pub const THICK: Set;    // ━┃┏┓┗┛
};

Border Set

pub const border = struct {
    pub const Set = struct {
        top_left: []const u8,
        top_right: []const u8,
        bottom_left: []const u8,
        bottom_right: []const u8,
        horizontal: []const u8,
        vertical: []const u8,
    };

    pub const PLAIN: Set;
    pub const ROUNDED: Set;
    pub const DOUBLE: Set;
    pub const THICK: Set;
};

Block Characters

pub const block = struct {
    pub const FULL: []const u8 = "█";
    pub const UPPER_HALF: []const u8 = "▀";
    pub const LOWER_HALF: []const u8 = "▄";
    pub const LEFT_HALF: []const u8 = "▌";
    pub const RIGHT_HALF: []const u8 = "▐";
    // ...
};

Bar Characters

pub const bar = struct {
    pub const Set = struct {
        full: []const u8,
        seven_eighths: []const u8,
        three_quarters: []const u8,
        five_eighths: []const u8,
        half: []const u8,
        three_eighths: []const u8,
        one_quarter: []const u8,
        one_eighth: []const u8,
        empty: []const u8,
    };

    pub const NINE_LEVELS: Set;
    pub const THREE_LEVELS: Set;
};

Braille

pub const braille = struct {
    pub const BLANK: []const u8 = "";  // U+2800

    // Bit positions for 2x4 grid:
    // 0 3
    // 1 4
    // 2 5
    // 6 7

    pub const PATTERNS: [256][3]u8;  // Pre-computed UTF-8 patterns

    pub fn fromPattern(pattern: u8) []const u8;
};

Marker

pub const Marker = enum {
    dot,
    block,
    bar,
    braille,
    half_block,
};

Terminal

pub const Terminal = struct {
    pub fn init(allocator: Allocator) !Terminal;
    pub fn deinit(self: *Terminal) void;

    pub fn size(self: Terminal) struct { width: u16, height: u16 };
    pub fn area(self: Terminal) Rect;

    // Rendering
    pub fn draw(self: *Terminal, render_fn: fn(area: Rect, buf: *Buffer) void) !void;
    pub fn drawWithContext(self: *Terminal, ctx: anytype, render_fn: fn(...) void) !void;
    pub fn clear(self: *Terminal) !void;
    pub fn flush(self: *Terminal) !void;

    // Cursor
    pub fn hideCursor(self: *Terminal) !void;
    pub fn showCursor(self: *Terminal) !void;
    pub fn setCursorPosition(self: *Terminal, x: u16, y: u16) !void;

    // Events (crossterm-style)
    pub fn pollEvent(self: *Terminal, timeout_ms: ?u32) !?Event;
    pub fn readEvent(self: *Terminal) !Event;

    // Mouse
    pub fn enableMouseCapture(self: *Terminal) !void;
    pub fn disableMouseCapture(self: *Terminal) !void;

    // Focus
    pub fn enableFocusChange(self: *Terminal) !void;
    pub fn disableFocusChange(self: *Terminal) !void;

    // Bracketed paste
    pub fn enableBracketedPaste(self: *Terminal) !void;
    pub fn disableBracketedPaste(self: *Terminal) !void;
};

Ejemplo interactivo completo:

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var term = try Terminal.init(allocator);
    defer term.deinit();

    // Enable mouse (optional)
    try term.enableMouseCapture();

    var running = true;
    while (running) {
        try term.draw(render);

        // Poll for events with 100ms timeout
        if (try term.pollEvent(100)) |event| {
            switch (event) {
                .key => |key| {
                    if (key.code == .esc) running = false;
                    if (key.code.isChar('q')) running = false;
                },
                .mouse => |mouse| {
                    if (mouse.kind == .down) handleClick(mouse.column, mouse.row);
                },
                .resize => |size| try term.resize(size.width, size.height),
                else => {},
            }
        }
    }
}

Events (crossterm-style)

Event

pub const Event = union(enum) {
    key: KeyEvent,
    mouse: MouseEvent,
    resize: ResizeEvent,
    focus_gained,
    focus_lost,
    paste: []const u8,
};

KeyEvent

pub const KeyEvent = struct {
    code: KeyCode,
    modifiers: KeyModifiers,
    kind: KeyEventKind,

    pub fn char(c: u21) KeyEvent;
    pub fn withMod(code: KeyCode, mods: KeyModifiers) KeyEvent;
    pub fn getChar(self: KeyEvent) ?u21;
    pub fn isCtrl(self: KeyEvent) bool;
    pub fn isAlt(self: KeyEvent) bool;
    pub fn isShift(self: KeyEvent) bool;
};

KeyCode

pub const KeyCode = union(enum) {
    char: u21,           // Any unicode character
    f: u8,               // F1-F12
    backspace, enter, tab, backtab,
    left, right, up, down,
    home, end, page_up, page_down,
    insert, delete, esc, null_key,
    caps_lock, scroll_lock, num_lock,
    print_screen, pause, menu,
    media: MediaKeyCode,
    modifier: ModifierKeyCode,

    pub fn isChar(self: KeyCode, c: u21) bool;
};

KeyModifiers

pub const KeyModifiers = packed struct {
    shift: bool = false,
    ctrl: bool = false,
    alt: bool = false,
    super: bool = false,

    pub const none: KeyModifiers = .{};
    pub const SHIFT: KeyModifiers = .{ .shift = true };
    pub const CTRL: KeyModifiers = .{ .ctrl = true };
    pub const ALT: KeyModifiers = .{ .alt = true };

    pub fn combine(self: KeyModifiers, other: KeyModifiers) KeyModifiers;
    pub fn any(self: KeyModifiers) bool;
    pub fn isEmpty(self: KeyModifiers) bool;
};

MouseEvent

pub const MouseEvent = struct {
    kind: MouseEventKind,
    button: MouseButton,
    column: u16,
    row: u16,
    modifiers: KeyModifiers,

    pub fn down(button: MouseButton, col: u16, row: u16) MouseEvent;
    pub fn up(button: MouseButton, col: u16, row: u16) MouseEvent;
    pub fn moved(col: u16, row: u16) MouseEvent;
    pub fn isClick(self: MouseEvent) bool;
    pub fn isLeft(self: MouseEvent) bool;
    pub fn isRight(self: MouseEvent) bool;
};

MouseEventKind

pub const MouseEventKind = enum {
    down, up, drag, moved,
    scroll_down, scroll_up,
    scroll_left, scroll_right,

    pub fn isScroll(self: MouseEventKind) bool;
    pub fn isButton(self: MouseEventKind) bool;
};

MouseButton

pub const MouseButton = enum {
    left, right, middle, none,
};

Ejemplo de manejo de eventos:

if (try term.pollEvent(100)) |event| {
    switch (event) {
        .key => |key| {
            // Ctrl+C
            if (key.code.isChar('c') and key.isCtrl()) {
                break;
            }
            // Arrow keys
            switch (key.code) {
                .up => state.previous(),
                .down => state.next(),
                .enter => state.select(),
                else => {},
            }
        },
        .mouse => |mouse| {
            switch (mouse.kind) {
                .down => {
                    if (mouse.isLeft()) selectAt(mouse.column, mouse.row);
                },
                .scroll_up => state.scrollUp(),
                .scroll_down => state.scrollDown(),
                else => {},
            }
        },
        .resize => |size| {
            try term.resize(size.width, size.height);
        },
        .focus_gained => {},
        .focus_lost => {},
        .paste => |text| handlePaste(text),
    }
}

Cursor

pub const CursorStyle = enum {
    default,
    blinking_block,
    steady_block,
    blinking_underline,
    steady_underline,
    blinking_bar,
    steady_bar,
};

pub const Cursor = struct {
    pub fn init() Cursor;

    // Visibility
    pub fn hide(self: *Cursor) !void;
    pub fn show(self: *Cursor) !void;

    // Blinking
    pub fn enableBlinking(self: *Cursor) !void;
    pub fn disableBlinking(self: *Cursor) !void;

    // Style
    pub fn setStyle(self: *Cursor, style: CursorStyle) !void;

    // Position
    pub fn moveTo(self: *Cursor, column: u16, row: u16) !void;
    pub fn moveUp(self: *Cursor, n: u16) !void;
    pub fn moveDown(self: *Cursor, n: u16) !void;
    pub fn moveLeft(self: *Cursor, n: u16) !void;
    pub fn moveRight(self: *Cursor, n: u16) !void;

    // Save/Restore
    pub fn savePosition(self: *Cursor) !void;
    pub fn restorePosition(self: *Cursor) !void;
};

Widgets Quick Reference

Widget Constructor Stateful Key Methods
Block Block.init() No title(), borders(), borderStyle()
Paragraph Paragraph.init(text) No setWrap(), setAlignment(), setScroll()
List List.init(items) Yes setHighlightStyle(), setHighlightSymbol()
Table Table.init(rows, widths) Yes setHeader(), setHighlightStyle()
Gauge Gauge.init() No setRatio(), setPercent(), setLabel()
LineGauge LineGauge.init() No setRatio(), setFilledStyle()
Tabs Tabs.init(titles) No select(), setDivider()
Sparkline Sparkline.init() No setData(), setMax()
Scrollbar Scrollbar.init(orientation) Yes setSymbols(), setStyle()
BarChart BarChart.init() No setData(), setBarWidth()
Canvas Canvas.init() No setXBounds(), setYBounds(), paint()
Chart Chart.init(datasets) No setXAxis(), setYAxis()
Monthly Monthly.init(date) No showMonthHeader(), showWeekdaysHeader()
Clear Clear.init() No (none)

Error Handling

zcatui usa el sistema de errores de Zig. Las funciones que pueden fallar retornan !T.

// Errores comunes
const TerminalError = error{
    InitFailed,
    WriteFailed,
    FlushFailed,
};

const BufferError = error{
    OutOfMemory,
};

// Manejo tipico
var term = Terminal.init(allocator) catch |err| {
    std.debug.print("Failed to init terminal: {}\n", .{err});
    return err;
};
defer term.deinit();

Patterns

Builder Pattern

const widget = SomeWidget.init()
    .setOption1(value1)
    .setOption2(value2)
    .setBlock(Block.bordered());

Stateful Rendering

var state = WidgetState.init();

// En el loop de renderizado:
widget.renderStateful(area, buf, &state);

// Actualizar estado basado en input:
state.selectNext(items.len);

Layout Composition

const outer = Layout.vertical(&[_]Constraint{
    Constraint.length(3),
    Constraint.min(0),
});

var outer_chunks: [2]Rect = undefined;
outer.split(area, &outer_chunks);

const inner = Layout.horizontal(&[_]Constraint{
    Constraint.percentage(50),
    Constraint.percentage(50),
});

var inner_chunks: [2]Rect = undefined;
inner.split(outer_chunks[1], &inner_chunks);