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

914 lines
21 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# zcatui - API Reference
> Referencia rapida de la API publica de zcatui
## Importar la Libreria
```zig
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
```zig
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:**
```zig
const red = Color.red;
const gray = Color.indexed(240);
const custom = Color.rgb(255, 128, 0);
```
### Style
```zig
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:**
```zig
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
```zig
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
```zig
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:**
```zig
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.
```zig
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
```zig
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
```zig
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:**
```zig
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
```zig
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
```zig
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
```zig
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
```zig
pub const Alignment = enum {
left,
center,
right,
};
```
**Ejemplos:**
```zig
// 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
```zig
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
```zig
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
```zig
pub const Direction = enum {
horizontal,
vertical,
};
```
**Ejemplos:**
```zig
// 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
```zig
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
```zig
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
```zig
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
```zig
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
```zig
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
```zig
pub const Marker = enum {
dot,
block,
bar,
braille,
half_block,
};
```
---
## Terminal
```zig
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:**
```zig
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
```zig
pub const Event = union(enum) {
key: KeyEvent,
mouse: MouseEvent,
resize: ResizeEvent,
focus_gained,
focus_lost,
paste: []const u8,
};
```
### KeyEvent
```zig
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
```zig
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
```zig
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
```zig
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
```zig
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
```zig
pub const MouseButton = enum {
left, right, middle, none,
};
```
**Ejemplo de manejo de eventos:**
```zig
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
```zig
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`.
```zig
// 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
```zig
const widget = SomeWidget.init()
.setOption1(value1)
.setOption2(value2)
.setBlock(Block.bordered());
```
### Stateful Rendering
```zig
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
```zig
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);
```