# zcatui API Reference v2.2 > Manual de referencia completo para la libreria TUI de Zig. > Equivalente a ratatui + crossterm de Rust, en un solo paquete. --- ## Tabla de Contenidos 1. [Quick Start](#quick-start) 2. [Core Types](#core-types) 3. [Terminal](#terminal) 4. [Layout System](#layout-system) 5. [Widgets](#widgets) 6. [Events](#events) 7. [Styling](#styling) 8. [Animation](#animation) 9. [Focus Management](#focus-management) 10. [Themes](#themes) 11. [Utilities](#utilities) 12. [v2.2 Features](#v22-features) --- ## Quick Start ```zig const std = @import("std"); const zcatui = @import("zcatui"); pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); // Initialize terminal var term = try zcatui.Terminal.init(allocator); defer term.deinit(); // Enable features try term.enableMouseCapture(); term.enableAutoResize(); // Main loop while (true) { try term.draw(render); if (try term.pollEvent(100)) |event| { switch (event) { .key => |key| { if (key.code == .esc) break; }, else => {}, } } } } fn render(area: zcatui.Rect, buf: *zcatui.Buffer) void { const block = zcatui.widgets.Block.bordered() .title("Hello zcatui!"); block.render(area, buf); } ``` --- ## Core Types ### Buffer (`src/buffer.zig`) Buffer de renderizado con diff para actualizaciones eficientes. | Type | Description | |------|-------------| | `Buffer` | Grid de celdas para renderizado | | `Cell` | Celda individual (simbolo + estilo) | | `Rect` | Area rectangular (x, y, width, height) | | `Symbol` | UTF-8 compacto (hasta 4 bytes) | | `Margin` | Margenes para `Rect.inner()` | | `CellUpdate` | Update de celda para diff | | `DiffIterator` | Iterador de diferencias entre buffers | ```zig // Rect const area = Rect.init(0, 0, 80, 24); area.isEmpty() // bool area.area() // u32 (width * height) area.left(), .right() // u16 area.top(), .bottom() // u16 area.inner(margin) // Rect con margenes aplicados area.intersection(other) // Rect interseccion area.contains(x, y) // bool // Margin Margin.uniform(2) // 2 en todos lados Margin.symmetric(h, v) // horizontal, vertical // Buffer var buf = try Buffer.init(allocator, rect); defer buf.deinit(); buf.setChar(x, y, 'X', style) buf.setString(x, y, "text", style) // returns u16 chars written buf.fill(rect, char, style) buf.clear() buf.get(x, y) // ?Cell buf.getPtr(x, y) // ?*Cell buf.diff(&other_buffer) // DiffIterator buf.merge(&other_buffer) buf.setStyle(rect, style) try buf.resize(new_rect) // Symbol Symbol.fromSlice("X") Symbol.fromCodepoint('X') symbol.slice() // []const u8 symbol.eql(other) // bool // Cell Cell.init('X') Cell.fromStr("X") cell.setStyle(style) cell.setChar(codepoint) cell.setSymbol(string) cell.reset() cell.eql(other) cell.char() // u21 ``` ### Style (`src/style.zig`) Colores y modificadores de texto. | Type | Description | |------|-------------| | `Color` | Color (ANSI, 256, RGB) | | `Style` | Estilo completo (fg, bg, modifiers) | | `Modifier` | Modificadores (bold, italic, etc) | ```zig // Color Color.reset // Reset to default Color.black, .red, .green, .yellow, .blue, .magenta, .cyan, .white Color.rgb(255, 128, 0) // 24-bit RGB Color.indexed(200) // 256 palette // Style (builder pattern) const style = Style{} .fg(Color.red) .bg(Color.black) .bold() .italic() .underlined() .dim() .reversed(); style.add_modifier(.{ .bold = true }) style.remove_modifier(.{ .bold = true }) style.patch(other_style) // Modifier Modifier.BOLD Modifier.DIM Modifier.ITALIC Modifier.UNDERLINED Modifier.REVERSED Modifier.CROSSED_OUT modifier.insert(other) // union modifier.remove(other) // difference ``` ### Text (`src/text.zig`) Primitivas de texto con estilos. | Type | Description | |------|-------------| | `Span` | String con un solo estilo | | `Line` | Linea de Spans | | `Text` | Multiples Lines | | `StyledGrapheme` | Grafema con estilo | | `Alignment` | left, center, right | ```zig // Span Span.raw("hello") Span.styled("hello", style) span.fg(Color.red).bold() span.width() // usize span.render(area, buf) // u16 chars written // Line Line.raw("hello world") Line.styled("hello", style) Line.fromSpans(&[_]Span{ span1, span2 }) line.centered() line.rightAligned() line.leftAligned() line.fg(Color.blue).bold() line.width() line.render(area, buf) // Text Text.fromLine(line) Text.fromLines(&[_]Line{ line1, line2 }) text.centered() text.width() // max width of lines text.height() // number of lines text.render(area, buf) ``` --- ## Terminal ### Terminal (`src/terminal.zig`) Interfaz principal para aplicaciones TUI. ```zig // Initialization var term = try Terminal.init(allocator); defer term.deinit(); // Properties term.area() // Rect term.buffer() // *Buffer // Drawing try term.draw(render_fn); // fn(Rect, *Buffer) void try term.drawWithContext(ctx, render_fn); // fn(T, Rect, *Buffer) void try term.clear(); // Resize term.enableAutoResize() term.disableAutoResize() term.getSize() // Size { width, height } try term.checkAndHandleResize() // bool term.isResizePending() // bool try term.resize(width, height) // Events try term.pollEvent(timeout_ms) // ?Event (null = timeout) try term.readEvent() // Event (blocking) // Mouse try term.enableMouseCapture() try term.disableMouseCapture() // Focus try term.enableFocusChange() try term.disableFocusChange() // Paste try term.enableBracketedPaste() try term.disableBracketedPaste() // Cursor try term.showCursor() try term.hideCursor() try term.setCursorPosition(col, row) ``` --- ## Layout System ### Layout (`src/layout.zig`) Sistema de layout para dividir areas. | Type | Description | |------|-------------| | `Layout` | Divisor de areas | | `Constraint` | Restriccion de tamano | | `Direction` | horizontal, vertical | | `Flex` | Layout CSS-like | | `JustifyContent` | start, end, center, space_between, space_around, space_evenly | | `AlignItems` | stretch, start, end, center | | `SplitResult` | Resultado con hasta 16 Rects | ```zig // Basic Layout const result = Layout.vertical(&.{ Constraint.length(3), // 3 rows exactas Constraint.min(0), // resto del espacio }).split(area); const header = result.get(0); // Rect const content = result.get(1); // Rect // Constraint types Constraint.length(10) // exactly 10 cells Constraint.min(5) // at least 5 cells Constraint.max(20) // at most 20 cells Constraint.percentage(50) // 50% of space Constraint.ratio(1, 3) // 1/3 of space Constraint.fill() // fill remaining (= min(0)) // Ratio helpers Constraint.half() // 1/2 Constraint.third() // 1/3 Constraint.twoThirds() // 2/3 Constraint.quarter() // 1/4 Constraint.threeQuarters() // 3/4 Constraint.fifth() // 1/5 Constraint.goldenLarge() // ~61.8% Constraint.goldenSmall() // ~38.2% Constraint.prop(2, 5) // 2 parts of 5 // Layout with margin Layout.vertical(&constraints).withMargin(2) // Flex layout (CSS-like) const flex = Flex.horizontal() .setJustify(.center) .setAlign(.stretch) .setGap(2) .setMargin(1) .items(&.{ 20, 30, 20 }); const result = flex.split(area); // Helper functions centerRect(outer, width, height) // Rect centered alignBottom(outer, height) // Rect at bottom alignRight(outer, width) // Rect at right alignBottomRight(outer, width, height) ``` --- ## Widgets ### Widget Index | Widget | File | Description | |--------|------|-------------| | Block | `widgets/block.zig` | Container con bordes y titulos | | Paragraph | `widgets/paragraph.zig` | Texto con wrapping | | List | `widgets/list.zig` | Lista seleccionable | | Table | `widgets/table.zig` | Tabla multi-columna | | Gauge | `widgets/gauge.zig` | Barra de progreso | | LineGauge | `widgets/gauge.zig` | Progreso en linea | | Tabs | `widgets/tabs.zig` | Navegacion por tabs | | Sparkline | `widgets/sparkline.zig` | Mini graficos | | Scrollbar | `widgets/scrollbar.zig` | Indicador de scroll | | BarChart | `widgets/barchart.zig` | Graficos de barras | | Canvas | `widgets/canvas.zig` | Dibujo libre | | Chart | `widgets/chart.zig` | Graficos con ejes | | Calendar | `widgets/calendar.zig` | Calendario mensual | | Clear | `widgets/clear.zig` | Limpiar area | | Input | `widgets/input.zig` | Campo de texto | | TextArea | `widgets/textarea.zig` | Editor multi-linea | | Popup | `widgets/popup.zig` | Ventana emergente | | Modal | `widgets/popup.zig` | Dialogo modal | | Menu | `widgets/menu.zig` | Menu desplegable | | MenuBar | `widgets/menu.zig` | Barra de menu | | ContextMenu | `widgets/menu.zig` | Menu contextual | | Tooltip | `widgets/tooltip.zig` | Tooltips | | Tree | `widgets/tree.zig` | Vista de arbol | | FilePicker | `widgets/filepicker.zig` | Selector de archivos | | ScrollView | `widgets/scroll.zig` | Vista con scroll | | VirtualList | `widgets/scroll.zig` | Lista virtualizada | | Panel | `widgets/panel.zig` | Panel basico | | TabbedPanel | `widgets/panel.zig` | Panel con tabs | | Checkbox | `widgets/checkbox.zig` | Checkbox | | RadioGroup | `widgets/checkbox.zig` | Radio buttons | | Select | `widgets/select.zig` | Dropdown select | | MultiSelect | `widgets/select.zig` | Multi-select | | Slider | `widgets/slider.zig` | Slider de valor | | RangeSlider | `widgets/slider.zig` | Slider de rango | | StatusBar | `widgets/statusbar.zig` | Barra de estado | | Toast | `widgets/statusbar.zig` | Notificaciones toast | | Spinner | `widgets/spinner.zig` | Indicador de carga | | Help | `widgets/help.zig` | Panel de keybindings | | Viewport | `widgets/viewport.zig` | Scroll generico | | Progress | `widgets/progress.zig` | Progreso con ETA | | Markdown | `widgets/markdown.zig` | Renderizado markdown | | DirectoryTree | `widgets/dirtree.zig` | Navegador de archivos | | SyntaxHighlighter | `widgets/syntax.zig` | Resaltado de codigo | | Logo | `widgets/logo.zig` | Logo ASCII art | ### Block ```zig const block = zcatui.widgets.Block.default() .title("Title") .titleBottom("Footer") .titleAlignment(.center) .borders(.all) // .none, .top, .bottom, .left, .right, .all .borderType(.rounded) // .plain, .rounded, .double, .thick .borderStyle(style) .style(style); block.render(area, buf); const inner = block.inner(area); // area inside borders ``` ### Paragraph ```zig const para = zcatui.widgets.Paragraph.init(text) .block(block) .style(style) .alignment(.center) .wrap(.{ .trim = true }); // word wrapping para.render(area, buf); ``` ### List ```zig const items = [_]zcatui.widgets.ListItem{ zcatui.widgets.ListItem.init("Item 1"), zcatui.widgets.ListItem.init("Item 2").style(style), }; var state = zcatui.widgets.ListState{}; state.select(0); const list = zcatui.widgets.List.init(&items) .block(block) .highlightStyle(style) .highlightSymbol("> "); list.renderStateful(area, buf, &state); // State methods state.selected() // ?usize state.select(index) state.selectNext() state.selectPrevious() state.selectFirst() state.selectLast() ``` ### Table ```zig const rows = [_]zcatui.widgets.TableRow{ zcatui.widgets.TableRow.init(&.{ zcatui.widgets.TableCell.init("Cell 1"), zcatui.widgets.TableCell.init("Cell 2"), }), }; var state = zcatui.widgets.TableState{}; const table = zcatui.widgets.Table.init(&rows) .block(block) .header(header_row) .widths(&.{ Constraint.percentage(50), Constraint.percentage(50) }) .highlightStyle(style) .highlightSymbol("> "); table.renderStateful(area, buf, &state); ``` ### Gauge / LineGauge ```zig // Full gauge const gauge = zcatui.widgets.Gauge.default() .ratio(0.5) // 0.0 - 1.0 .percent(50) // alternative: 0-100 .label("50%") .gaugeStyle(style); gauge.render(area, buf); // Line gauge (single line) const line_gauge = zcatui.widgets.LineGauge.default() .ratio(0.75) .lineSet(.thick) // .normal, .thick .filledStyle(style) .unfilledStyle(style); line_gauge.render(area, buf); ``` ### Input ```zig var state = zcatui.widgets.InputState.init(allocator); defer state.deinit(); const input = zcatui.widgets.Input.default() .placeholder("Enter text...") .style(style) .cursorStyle(style); input.renderStateful(area, buf, &state); // State methods state.insert(char) state.deleteBack() state.deleteForward() state.moveCursorLeft() state.moveCursorRight() state.moveCursorStart() state.moveCursorEnd() state.getText() // []const u8 state.clear() ``` ### Popup / Modal ```zig // Popup (positioned) const popup = zcatui.widgets.Popup.init() .title("Popup") .content("Content here") .percentWidth(50) .percentHeight(30); popup.render(area, buf); // Modal dialog const modal = zcatui.widgets.Modal.init() .title("Confirm") .message("Are you sure?") .buttons(&.{ zcatui.widgets.ModalButton.init("Yes", .confirm), zcatui.widgets.ModalButton.init("No", .cancel), }); modal.render(area, buf); // Pre-built dialogs zcatui.widgets.confirmDialog("Title", "Message") zcatui.widgets.alertDialog("Title", "Message") zcatui.widgets.yesNoCancelDialog("Title", "Message") ``` ### Spinner ```zig const spinner = zcatui.widgets.Spinner.init() .spinnerStyle(.dots) // .dots, .line, .arc, .box, .bounce, etc (17 styles) .label("Loading...") .style(style); spinner.render(area, buf, frame_count); // frame_count for animation ``` ### Progress ```zig const progress = zcatui.widgets.Progress.init() .ratio(0.5) .format(.percentage) // .percentage, .ratio, .bytes, .eta .showEta(true) .showSpeed(true) .style(style); progress.render(area, buf); ``` ### Markdown ```zig const md = zcatui.widgets.Markdown.init( \\# Title \\ \\Some **bold** and *italic* text. \\ \\- List item 1 \\- List item 2 ) .theme(.default); // .default, .github, .dracula md.render(area, buf); ``` ### SyntaxHighlighter ```zig const highlighter = zcatui.widgets.SyntaxHighlighter.init(code) .language(.zig) // .zig, .rust, .python, .javascript, etc .theme(.monokai) // .monokai, .dracula, .github .showLineNumbers(true); highlighter.render(area, buf); ``` ### DirectoryTree ```zig var tree = try zcatui.widgets.DirectoryTree.init(allocator, "/path/to/dir"); defer tree.deinit(); tree.toggleHidden(); // show/hide hidden files tree.expand(); tree.collapse(); tree.selectNext(); tree.selectPrev(); tree.getSelectedPath() // ?[]const u8 tree.render(area, buf); ``` ### Logo ```zig const logo = zcatui.widgets.Logo.init() .text(zcatui.widgets.predefined_logos.zcatui) .style(style) .animation(.pulse) // .none, .pulse, .wave, .rainbow .alignment(.center); logo.render(area, buf, frame_count); ``` --- ## Events ### Event Types (`src/event.zig`) ```zig const Event = union(enum) { key: KeyEvent, mouse: MouseEvent, resize: ResizeEvent, focus_gained, focus_lost, paste: []const u8, }; ``` ### KeyEvent ```zig // Handling switch (event) { .key => |key| { // Check specific key if (key.code == .esc) return; if (key.code == .enter) submit(); if (key.code == .{ .char = 'q' }) quit(); if (key.code == .{ .f = 1 }) showHelp(); // Check modifiers if (key.isCtrl() and key.getChar() == 'c') quit(); if (key.isAlt()) handleAlt(key); // Get character if (key.getChar()) |c| { insertChar(c); } }, } // KeyCode variants .char: u21 // Unicode character .f: u8 // F1-F12 .backspace, .enter, .tab, .backtab .left, .right, .up, .down .home, .end, .page_up, .page_down .insert, .delete .esc // KeyModifiers key.modifiers.ctrl key.modifiers.alt key.modifiers.shift key.modifiers.super ``` ### MouseEvent ```zig switch (event) { .mouse => |mouse| { if (mouse.kind == .down and mouse.button == .left) { handleClick(mouse.column, mouse.row); } if (mouse.kind == .scroll_up) scrollUp(); if (mouse.kind == .scroll_down) scrollDown(); if (mouse.kind == .drag) handleDrag(mouse); }, } // MouseEventKind .down, .up, .drag, .moved .scroll_down, .scroll_up, .scroll_left, .scroll_right // MouseButton .left, .right, .middle, .none ``` ### Event Reader (`src/event/reader.zig`) ```zig var reader = zcatui.EventReader.init(); // Poll with timeout (non-blocking) if (try reader.poll(100)) |event| { // handle event } // Read blocking const event = try reader.read(); ``` --- ## Styling ### Color Examples ```zig // ANSI 16 colors Color.black, Color.red, Color.green, Color.yellow Color.blue, Color.magenta, Color.cyan, Color.white // 256 palette Color.indexed(196) // bright red Color.indexed(46) // bright green Color.indexed(236) // dark gray // True color (24-bit) Color.rgb(255, 128, 0) // orange Color.rgb(30, 30, 30) // dark background ``` ### Style Composition ```zig // Builder pattern const title_style = Style{} .fg(Color.yellow) .bold(); const error_style = Style{} .fg(Color.red) .bg(Color.black) .bold(); const selected = Style{} .fg(Color.white) .bg(Color.blue) .reversed(); // Patching styles const combined = base_style.patch(overlay_style); ``` --- ## Animation ### Animation System (`src/animation.zig`) ```zig // Basic animation var anim = zcatui.Animation.init(0, 100, 1000); // from, to, duration_ms anim.easing = zcatui.Easing.easeInOut; while (!anim.isComplete()) { const value = anim.getValue(); // current interpolated value render(value); anim.advance(delta_ms); } // Easing functions zcatui.Easing.linear(t) zcatui.Easing.easeIn(t) zcatui.Easing.easeOut(t) zcatui.Easing.easeInOut(t) zcatui.Easing.easeInCubic(t) zcatui.Easing.easeOutCubic(t) zcatui.Easing.easeInOutCubic(t) zcatui.Easing.easeInExpo(t) zcatui.Easing.easeOutExpo(t) zcatui.Easing.easeOutBounce(t) // Timer var timer = zcatui.Timer.init(1000); // 1000ms if (timer.tick(delta_ms)) { // Timer fired } timer.reset(); ``` --- ## Focus Management ### Focus System (`src/focus.zig`) ```zig // FocusRing - manages focus within widget group var ring = zcatui.FocusRing{}; ring.add(widget1.focusable()); ring.add(widget2.focusable()); ring.focusNext(); ring.focusPrev(); ring.focusFirst(); ring.focusLast(); if (ring.getFocused()) |focused| { // render focused widget differently } // FocusManager - global focus management var manager = zcatui.FocusManager.init(allocator); defer manager.deinit(); manager.register("input1", input.focusable()); manager.setFocus("input1"); manager.moveFocus(.next); manager.moveFocus(.prev); // Focusable interface pub const Focusable = struct { ptr: *anyopaque, vtable: *const VTable, pub const VTable = struct { onFocusChange: ?*const fn (*anyopaque, FocusEvent) void, canFocus: *const fn (*anyopaque) bool, getId: *const fn (*anyopaque) []const u8, getOrder: ?*const fn (*anyopaque) i32, getGroup: ?*const fn (*anyopaque) ?[]const u8, }; }; // FocusDirection .next, .prev, .up, .down, .left, .right // FocusEvent .gained, .lost ``` --- ## Themes ### Theme System (`src/theme.zig`) ```zig // Built-in themes const theme = zcatui.Theme.default; const theme = zcatui.Theme.dark; const theme = zcatui.Theme.light; const theme = zcatui.Theme.nord; const theme = zcatui.Theme.dracula; const theme = zcatui.Theme.solarized_dark; const theme = zcatui.Theme.solarized_light; const theme = zcatui.Theme.monokai; const theme = zcatui.Theme.gruvbox; const theme = zcatui.Theme.one_dark; // Theme colors theme.background theme.foreground theme.primary theme.secondary theme.success theme.warning theme.error_color theme.info theme.border theme.border_focused theme.selection_bg theme.selection_fg // Theme styles (return Style) theme.default() theme.primaryStyle() theme.secondaryStyle() theme.successStyle() theme.warningStyle() theme.errorStyle() theme.infoStyle() theme.disabledStyle() theme.borderStyle() theme.borderFocusedStyle() theme.selectionStyle() theme.highlightStyle() theme.surfaceStyle() theme.statusBarStyle() ``` ### Theme Hot-Reload (`src/theme_loader.zig`) ```zig var loader = zcatui.ThemeLoader.init(allocator); defer loader.deinit(); // Load from file const theme = try loader.loadFromFile("theme.json"); // Watch for changes var watcher = try zcatui.ThemeWatcher.init(allocator, "theme.json"); defer watcher.deinit(); if (watcher.hasChanged()) { theme = try loader.reload(); } // Export theme try zcatui.exportTheme(theme, "theme.json"); ``` --- ## Utilities ### Clipboard (`src/clipboard.zig`) ```zig // Copy to clipboard (OSC 52) try zcatui.Clipboard.copy(writer, "text to copy"); // Selection types zcatui.ClipboardSelection.clipboard // system clipboard zcatui.ClipboardSelection.primary // X11 primary zcatui.ClipboardSelection.secondary // X11 secondary ``` ### Hyperlinks (`src/hyperlink.zig`) ```zig // Create clickable link (OSC 8) const link = zcatui.Hyperlink.init("Click here", "https://example.com"); link.render(area, buf); ``` ### Notifications (`src/notification.zig`) ```zig // Send desktop notification (OSC 9/777) try zcatui.Notification.send(writer, "Title", "Body"); ``` ### Images (`src/image.zig`) ```zig // Kitty protocol try zcatui.Kitty.display(writer, image_data, options); // iTerm2 protocol try zcatui.Iterm2.display(writer, image_data, options); // Options const options = zcatui.ImageOptions{ .width = 40, .height = 20, .format = .png, }; ``` ### Unicode (`src/unicode.zig`) ```zig zcatui.charWidth(codepoint) // u2 (0, 1, or 2) zcatui.stringWidth("hello") // usize zcatui.truncateToWidth(str, max) // []const u8 ``` ### Terminal Capabilities (`src/termcap.zig`) ```zig const caps = zcatui.detectCapabilities(); caps.color_support // .none, .ansi16, .ansi256, .truecolor caps.unicode // bool caps.mouse // bool caps.bracketed_paste // bool caps.kitty_keyboard // bool caps.sixel // bool caps.kitty_graphics // bool caps.iterm2_images // bool ``` ### Lazy Rendering (`src/lazy.zig`) ```zig // Render cache var cache = zcatui.RenderCache.init(allocator); defer cache.deinit(); if (cache.isValid(key)) { // use cached } else { // render and cache cache.store(key, rendered); } // Throttle var throttle = zcatui.Throttle.init(16); // 16ms = ~60fps if (throttle.shouldRun()) { render(); } // Debounce var debounce = zcatui.Debounce.init(100); // 100ms delay debounce.trigger(); if (debounce.isReady()) { execute(); } ``` ### Serialization (`src/serialize.zig`) ```zig // State snapshot var snapshot = zcatui.StateSnapshot.init(allocator); defer snapshot.deinit(); snapshot.save("key", value); const restored = snapshot.load("key", T); // Undo stack var undo = zcatui.UndoStack.init(allocator); defer undo.deinit(); undo.push(state); if (undo.undo()) |prev| { state = prev; } if (undo.redo()) |next| { state = next; } // JSON export const json = try zcatui.toJson(allocator, value); defer allocator.free(json); ``` ### Accessibility (`src/accessibility.zig`) ```zig // Accessible info const info = zcatui.AccessibleInfo{ .role = .button, .label = "Submit", .description = "Submit the form", }; // Announcer for screen readers var announcer = zcatui.Announcer.init(); announcer.announce("Form submitted successfully"); // Check preferences if (zcatui.prefersReducedMotion()) { // disable animations } if (zcatui.prefersHighContrast()) { theme = zcatui.high_contrast_theme; } // Skip links var skip = zcatui.SkipLinks.init(); skip.add("main", "Skip to main content"); ``` --- ## v2.2 Features ### Resize Handler (`src/resize.zig`) ```zig // Automatic (recommended) term.enableAutoResize(); // Resize is handled automatically in draw() // Manual var handler = zcatui.ResizeHandler.init(); defer handler.deinit(); if (handler.hasResized()) { const size = handler.getLastKnownSize(); try term.resize(size.width, size.height); } // Check pending if (zcatui.resize.isResizePending()) { // handle resize } ``` ### Drag & Drop (`src/drag.zig`) ```zig // Drag state var drag = zcatui.DragState{}; // On mouse down drag.start(.resize, mouse.column, mouse.row); // On mouse move drag.update(mouse.column, mouse.row); // Get delta const dx = drag.deltaX(); // i32 const dy = drag.deltaY(); // i32 // On mouse up drag.stop(); // Splitter panel var splitter = zcatui.Splitter{ .direction = .vertical, .position = 50, // percentage }; const areas = splitter.split(area); // areas.first, areas.second, areas.handle // Adjust on drag splitter.adjustPosition(area, delta); ``` ### Debug Overlay (`src/debug.zig`) ```zig var debug = zcatui.DebugOverlay.init(); debug.setEnabled(true); debug.setFlags(.{ .show_fps = true, .show_render_time = true, .show_boundaries = true, .show_mouse = true, }); // In render loop debug.beginFrame(); // ... render ... debug.endFrame(); debug.render(area, buf); // Log events debug.logEvent("Button clicked: {s}", .{button_id}); // Get metrics debug.current_fps // f32 debug.getFrameTimeMs() // f32 debug.widget_count // u32 // Global debug zcatui.debug.toggleDebug(); zcatui.debug.isDebugEnabled(); ``` ### Profiler (`src/profile.zig`) ```zig var profiler = zcatui.Profiler.init(); // Time a section profiler.begin("render"); // ... code ... profiler.end("render"); // Get stats if (profiler.getStats("render")) |stats| { stats.avg_ns // average time stats.min_ns // minimum time stats.max_ns // maximum time stats.count // call count } // Scoped timer (RAII-style) { var timer = zcatui.ScopedTimer.start(&profiler, "section"); defer timer.stop(); // ... code ... } // Reset profiler.reset(); ``` ### Diagnostic Messages (`src/diagnostic.zig`) ```zig // Create diagnostic const diag = zcatui.Diagnostic.err("INVALID VALUE", "Value must be positive") .withSnippet(.{ .lines = &.{ "let x = -5;", " ^^^^" }, .start_line = 10, .highlight_line = 0, .highlight_col = 8, .highlight_len = 2, }) .withHint("Use a positive number instead") .withSeeAlso("https://docs.example.com/values"); // Render to buffer diag.render(area, buf); // Format as string const text = try diag.format(allocator); defer allocator.free(text); // Severity levels zcatui.Severity.hint zcatui.Severity.warning zcatui.Severity.@"error" // Pre-built diagnostics zcatui.diagnostic.invalidPercentage(150) zcatui.diagnostic.emptyConstraints() zcatui.diagnostic.widgetOutOfBounds("Button") zcatui.diagnostic.invalidColor("red", 300) ``` ### Sixel Graphics (`src/sixel.zig`) ```zig var encoder = zcatui.SixelEncoder.init(allocator); defer encoder.deinit(); // Encode image const pixels: []const zcatui.Pixel = // ... const sixel_data = try encoder.encode(pixels, width, height); defer allocator.free(sixel_data); // Write to terminal try writer.writeAll(sixel_data); ``` ### Async Event Loop (`src/async_loop.zig`) ```zig var loop = try zcatui.AsyncLoop.init(allocator); defer loop.deinit(); // Add stdin try loop.addStdin(); // Add timer (100ms interval) const timer_id = try loop.addTimer(100); // Add custom fd try loop.addFd(fd, true, false); // readable, writable // Event loop while (running) { const events = try loop.wait(1000); // 1s timeout for (events) |event| { switch (event.source) { .stdin => handleInput(), .timer => |id| if (id == timer_id) animate(), .fd => |f| handleFd(f), .signal => |sig| handleSignal(sig), } } } // Remove timer loop.removeTimer(timer_id); // Ticker helper var ticker = try zcatui.Ticker.init(&loop, 16); // 16ms = ~60fps defer ticker.deinit(); ``` ### Shortcuts (`src/shortcuts.zig`) ```zig var shortcuts = zcatui.ShortcutMap.init(allocator); defer shortcuts.deinit(); // Register shortcuts try shortcuts.register(.{ .key = .{ .char = 's' }, .modifiers = .{ .ctrl = true }, .action = .save, .description = "Save file", }); // Match event if (shortcuts.match(key_event)) |action| { switch (action) { .save => saveFile(), .quit => quit(), // ... } } // Context-aware shortcuts var ctx = zcatui.ShortcutContext.init(allocator); ctx.push("editor"); // enter editor context // shortcuts for editor... ctx.pop(); // exit context ``` ### Widget Composition (`src/compose.zig`) ```zig // Vertical stack const areas = zcatui.vstack(area, &.{ Constraint.length(3), Constraint.fill(), Constraint.length(1), }); // Horizontal stack const cols = zcatui.hstack(area, &.{ Constraint.percentage(30), Constraint.fill(), }); // Splits const split = zcatui.splitV(area, 50); // 50% // split.first, split.second const split3 = zcatui.splitV3(area, 3, 1); // header, footer sizes // split3.top, split3.middle, split3.bottom // Sizing const sized = zcatui.sized(area, 40, 10); // width, height (centered) // Flex children const child = zcatui.flexChild(2); // flex: 2 const filler = zcatui.fillChild(); // flex: 1 (fill remaining) const space = zcatui.spacer(5); // fixed 5 cells ``` --- ## File Index | File | Description | |------|-------------| | `src/root.zig` | Entry point, re-exports | | `src/buffer.zig` | Buffer, Cell, Rect, Symbol | | `src/style.zig` | Color, Style, Modifier | | `src/text.zig` | Span, Line, Text | | `src/layout.zig` | Layout, Constraint, Flex | | `src/terminal.zig` | Terminal interface | | `src/event.zig` | Event types | | `src/event/reader.zig` | EventReader | | `src/event/parse.zig` | Escape sequence parser | | `src/animation.zig` | Animation, Easing, Timer | | `src/focus.zig` | FocusRing, FocusManager | | `src/theme.zig` | Theme definitions | | `src/theme_loader.zig` | Theme hot-reload | | `src/cursor.zig` | Cursor control | | `src/clipboard.zig` | OSC 52 clipboard | | `src/hyperlink.zig` | OSC 8 hyperlinks | | `src/notification.zig` | OSC 9/777 notifications | | `src/image.zig` | Kitty/iTerm2 images | | `src/unicode.zig` | Unicode width | | `src/termcap.zig` | Terminal capabilities | | `src/lazy.zig` | Render cache, throttle | | `src/testing.zig` | Widget testing framework | | `src/serialize.zig` | JSON, undo/redo | | `src/accessibility.zig` | A11y support | | `src/resize.zig` | SIGWINCH handler | | `src/drag.zig` | Drag state, Splitter | | `src/debug.zig` | Debug overlay | | `src/profile.zig` | Performance profiler | | `src/diagnostic.zig` | Elm-style errors | | `src/sixel.zig` | Sixel encoding | | `src/async_loop.zig` | Async epoll loop | | `src/shortcuts.zig` | Shortcut registry | | `src/compose.zig` | Widget composition | | `src/backend/backend.zig` | ANSI backend | | `src/symbols/*.zig` | Unicode symbols | | `src/widgets/*.zig` | All widgets | --- ## Examples Los ejemplos estan en `examples/`: ```bash zig build hello # Hello World basico zig build events-demo # Manejo de eventos zig build list-demo # Lista interactiva zig build table-demo # Tabla con datos zig build dashboard # Dashboard completo zig build input-demo # Campos de entrada zig build animation-demo # Animaciones zig build clipboard-demo # Clipboard zig build menu-demo # Menus zig build form-demo # Formularios zig build panel-demo # Paneles zig build resize-demo # Manejo resize zig build splitter-demo # Paneles redimensionables zig build dirtree-demo # Navegador archivos zig build spinner-demo # Spinners zig build progress-demo # Barras progreso zig build markdown-demo # Markdown zig build syntax-demo # Syntax highlighting zig build help-demo # Panel de ayuda zig build viewport-demo # Scroll viewport ``` --- ## Version - **zcatui v2.2** - **Zig 0.15.2** - **70+ source files** - **35 widgets** - **186+ tests** - **20 demos** --- *Generated 2025-12-08*