# zcatui A Terminal User Interface (TUI) library for Zig, inspired by [ratatui](https://github.com/ratatui/ratatui). > **zcatui** = "zcat" + "ui" (a nod to ratatui and Zig's mascot) ## Features ### Core - **Immediate mode rendering** with double buffering and diff-based updates - **Flexible layout system** with constraints (Length, Min, Max, Percentage, Ratio) - **Rich styling** with 16/256/RGB colors and modifiers (bold, italic, underline, etc.) - **Full event handling** for keyboard and mouse input - **Cross-terminal compatibility** via ANSI escape sequences ### Widgets (30+) | Category | Widgets | |----------|---------| | **Basic** | Block, Paragraph, List, Table, Tabs | | **Data** | Gauge, LineGauge, Sparkline, BarChart, Chart, Canvas | | **Input** | Input (text field), TextArea, Checkbox, RadioGroup, Select, Slider | | **Navigation** | Menu, MenuBar, ContextMenu, Tree, FilePicker | | **Overlays** | Popup, Modal, Tooltip, Toast | | **Layout** | Panel, PanelSplit, TabbedPanel, DockingPanel, ScrollView, VirtualList | | **Utilities** | Scrollbar, Calendar, StatusBar, Clear | ### Terminal Extensions - **Clipboard** (OSC 52) - Read/write system clipboard - **Hyperlinks** (OSC 8) - Clickable links in terminal - **Notifications** (OSC 9/777) - Desktop notifications - **Images** (Kitty/iTerm2) - Display images in terminal - **Cursor control** - Style, visibility, position ### Advanced Features - **Animation system** with easing functions - **Lazy rendering** with caching and throttling - **Virtual scrolling** for large datasets - **LEGO panel system** for complex layouts ## 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(); // Main loop while (true) { try term.draw(render); if (try term.pollEvent(100)) |event| { if (event == .key) { if (event.key.code == .char and event.key.code.char == 'q') { break; } } } } } fn render(area: zcatui.Rect, buf: *zcatui.Buffer) void { const block = zcatui.widgets.Block.init() .title(" Hello zcatui! ") .setBorders(zcatui.widgets.Borders.all) .style(zcatui.Style.default.fg(zcatui.Color.cyan)); block.render(area, buf); } ``` ## Installation Add zcatui to your `build.zig.zon`: ```zig .dependencies = .{ .zcatui = .{ .url = "https://git.reugenio.com/reugenio/zcatui/archive/main.tar.gz", // Add hash after first build attempt }, }, ``` Then in `build.zig`: ```zig const zcatui = b.dependency("zcatui", .{ .target = target, .optimize = optimize, }); exe.root_module.addImport("zcatui", zcatui.module("zcatui")); ``` ## Examples Run examples with: ```bash zig build hello # Basic hello world zig build events-demo # Keyboard/mouse events zig build list-demo # List widget zig build table-demo # Table widget zig build dashboard # Dashboard with multiple widgets zig build input-demo # Text input zig build animation-demo # Animations zig build menu-demo # Menus and modals zig build form-demo # Form widgets zig build panel-demo # Panel system ``` ## Widget Examples ### Layout ```zig const Layout = zcatui.Layout; const Constraint = zcatui.Constraint; // Split area vertically const chunks = Layout.vertical(&.{ Constraint.length(3), // Fixed 3 rows Constraint.percentage(50), // 50% of remaining Constraint.min(5), // At least 5 rows }).split(area); // Split horizontally const cols = Layout.horizontal(&.{ Constraint.ratio(1, 3), // 1/3 of width Constraint.ratio(2, 3), // 2/3 of width }).split(area); ``` ### Block with Borders ```zig const Block = zcatui.widgets.Block; const Borders = zcatui.widgets.Borders; const block = Block.init() .title(" My Panel ") .setBorders(Borders.all) .style(Style.default.fg(Color.cyan)); block.render(area, buf); const inner = block.inner(area); // Get content area ``` ### List ```zig const List = zcatui.widgets.List; const ListItem = zcatui.widgets.ListItem; const items = &[_]ListItem{ ListItem.init("Item 1"), ListItem.init("Item 2").style(Style.default.fg(Color.green)), ListItem.init("Item 3"), }; var list = List.init(items) .block(Block.init().title("List").setBorders(Borders.all)) .highlightStyle(Style.default.bg(Color.blue)); list.renderStateful(area, buf, &list_state); ``` ### Form Widgets ```zig // Checkbox const checkbox = Checkbox.init("Enable feature") .setChecked(true) .setFocused(is_focused); checkbox.render(area, buf); // Radio buttons var radio = RadioGroup.init(&.{"Option A", "Option B", "Option C"}) .setSelected(1); radio.render(area, buf); // Dropdown select var select = Select.init(&.{"Small", "Medium", "Large"}) .setPlaceholder("Choose size..."); select.render(area, buf); // Slider const slider = Slider.init(0, 100) .setValue(50) .setLabel("Volume"); slider.render(area, buf); ``` ### Charts ```zig // Bar chart const BarChart = zcatui.widgets.BarChart; const chart = BarChart.init() .data(&.{ .{ .label = "A", .value = 10 }, .{ .label = "B", .value = 20 }, .{ .label = "C", .value = 15 }, }) .barWidth(5); chart.render(area, buf); // Sparkline const Sparkline = zcatui.widgets.Sparkline; const sparkline = Sparkline.init(&.{1, 4, 2, 8, 5, 3, 9, 2}); sparkline.render(area, buf); ``` ### Popups and Modals ```zig const Modal = zcatui.widgets.Modal; const confirmDialog = zcatui.widgets.confirmDialog; // Quick confirm dialog const modal = confirmDialog("Confirm", &.{"Are you sure?"}); modal.render(area, buf); // Handle button press if (modal.getFocusedButton() == 0) { // OK pressed } ``` ### Toast Notifications ```zig const ToastManager = zcatui.widgets.ToastManager; var toasts = ToastManager.init(); // Show notifications toasts.info("Information message"); toasts.success("Operation completed!"); toasts.warning("Warning!"); toasts.showError("Error occurred"); // In render loop toasts.update(); toasts.render(area, buf); ``` ## Terminal Extensions ### Clipboard ```zig const Clipboard = zcatui.Clipboard; // Write to clipboard try Clipboard.write(writer, "Hello clipboard!"); // Read (async - response comes via terminal) try Clipboard.requestRead(writer); ``` ### Hyperlinks ```zig const Hyperlink = zcatui.Hyperlink; const link = Hyperlink.init("https://example.com", "Click here"); try link.write(writer); ``` ### Notifications ```zig const notification = zcatui.notification; try notification.notify(writer, "Build complete!"); try notification.notifyWithTitle(writer, "zcatui", "Task finished"); ``` ### Images ```zig const image = zcatui.image; // Display image (Kitty protocol) try image.Kitty.displayFile(writer, "/path/to/image.png", .{ .width = 40, .height = 20, }); ``` ## Architecture ``` ┌─────────────┐ ┌────────┐ ┌──────────┐ │ Application │───▶│ Buffer │───▶│ Terminal │ │ (widgets) │ │ (diff) │ │ (output) │ └─────────────┘ └────────┘ └──────────┘ ``` 1. Application renders widgets to a Buffer 2. Buffer is compared (diff) with previous frame 3. Only changes are sent to terminal (efficient) ## Requirements - Zig 0.15.x - POSIX terminal (Linux, macOS) or Windows Terminal - Terminal with ANSI escape sequence support ## License MIT ## Credits - Inspired by [ratatui](https://github.com/ratatui/ratatui) (Rust) - Built with Zig