Librería TUI inspirada en ratatui (Rust), implementada en Zig 0.15.2. Estructura inicial: - src/style.zig: Color, Style, Modifier - src/buffer.zig: Cell, Buffer, Rect - src/layout.zig: Layout, Constraint, Direction - src/terminal.zig: Terminal abstraction - src/backend/backend.zig: ANSI escape sequences - src/widgets/block.zig: Block con borders y título - src/widgets/paragraph.zig: Paragraph con wrapping - examples/hello.zig: Demo funcional 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
13 KiB
zcatui - TUI Library para Zig
Última actualización: 2025-12-08 Lenguaje: Zig 0.15.2 Inspiración: ratatui (Rust TUI library)
Descripción del Proyecto
zcatui es una librería para crear interfaces de usuario en terminal (TUI) en Zig puro, inspirada en ratatui de Rust.
Objetivo: Proveer una API idiomática Zig para construir aplicaciones TUI con widgets, layouts, y estilos, manteniendo la filosofía de Zig: simple, explícito, y sin magia.
Nombre: "zcat" + "ui" (un guiño a ratatui y la mascota de Zig)
Arquitectura Objetivo
Diseño: Immediate Mode Rendering
Como ratatui, usamos renderizado inmediato con buffers intermedios:
┌─────────────┐ ┌────────┐ ┌──────────┐
│ Application │───▶│ Buffer │───▶│ Terminal │
│ (widgets) │ │ (cells)│ │ (output) │
└─────────────┘ └────────┘ └──────────┘
- Cada frame, la aplicación renderiza TODOS los widgets al buffer
- El buffer se compara con el anterior (diff)
- Solo se envían cambios a la terminal (eficiencia)
Módulos Principales (Objetivo)
zcatui/
├── src/
│ ├── root.zig # Entry point, re-exports públicos
│ ├── terminal.zig # Terminal abstraction
│ ├── buffer.zig # Buffer + Cell types
│ ├── layout.zig # Layout, Constraint, Rect
│ ├── style.zig # Color, Style, Modifier
│ ├── text.zig # Text, Line, Span
│ ├── backend/
│ │ ├── backend.zig # Backend interface
│ │ └── ansi.zig # ANSI escape sequences (default)
│ └── widgets/
│ ├── widget.zig # Widget trait/interface
│ ├── block.zig # Block (borders, titles)
│ ├── paragraph.zig # Text paragraphs
│ ├── list.zig # Selectable lists
│ ├── table.zig # Tables with columns
│ ├── gauge.zig # Progress bars
│ ├── chart.zig # Line/bar charts
│ ├── canvas.zig # Arbitrary drawing
│ └── tabs.zig # Tab navigation
├── build.zig
└── examples/
├── hello.zig # Minimal example
├── demo.zig # Feature showcase
└── counter.zig # Interactive counter
Fases de Implementación
Fase 1: Core (Mínimo Viable)
- Buffer + Cell (almacenamiento de caracteres + estilos)
- Style + Color (colores 16, 256, RGB)
- Rect (área rectangular)
- Backend ANSI (escape sequences para cualquier terminal)
- Terminal (init, draw, restore)
- Widget trait básico
Fase 2: Layout
- Constraint (Min, Max, Percentage, Length, Ratio)
- Layout (horizontal, vertical splitting)
- Direction (Horizontal, Vertical)
Fase 3: Widgets Básicos
- Block (borders, titles, padding)
- Paragraph (texto con wrapping)
- List (items seleccionables)
Fase 4: Widgets Avanzados
- Table (columnas, headers, selección)
- Gauge (barra de progreso)
- Tabs (navegación por pestañas)
- Chart (gráficos simples)
- Canvas (dibujo libre con braille/block chars)
Fase 5: Extras
- Input handling (keyboard events)
- Mouse support
- Scrolling
- Animations (opcional)
Conceptos Clave de ratatui a Replicar
1. Cell
Unidad mínima del buffer: un carácter + su estilo.
const Cell = struct {
char: u21, // Unicode codepoint
fg: Color, // Foreground color
bg: Color, // Background color
modifiers: Modifiers, // Bold, italic, underline, etc.
};
2. Buffer
Grid de celdas que representa el estado de la terminal.
const Buffer = struct {
area: Rect,
cells: []Cell,
pub fn get(self: *Buffer, x: u16, y: u16) *Cell { ... }
pub fn set_string(self: *Buffer, x: u16, y: u16, text: []const u8, style: Style) void { ... }
pub fn diff(self: *Buffer, other: *Buffer) []Update { ... }
};
3. Rect
Área rectangular en la terminal.
const Rect = struct {
x: u16,
y: u16,
width: u16,
height: u16,
pub fn inner(self: Rect, margin: Margin) Rect { ... }
pub fn intersection(self: Rect, other: Rect) Rect { ... }
};
4. Style
Combinación de colores y modificadores.
const Style = struct {
fg: ?Color = null,
bg: ?Color = null,
modifiers: Modifiers = .{},
pub fn fg(color: Color) Style { ... }
pub fn bg(color: Color) Style { ... }
pub fn bold() Style { ... }
pub fn patch(self: Style, other: Style) Style { ... }
};
5. Layout
Sistema de distribución de espacio.
const Layout = struct {
direction: Direction,
constraints: []const Constraint,
pub fn split(self: Layout, area: Rect) []Rect { ... }
};
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
ratio: struct { num: u32, den: u32 },
};
6. Widget Interface
Trait que deben implementar todos los widgets.
const Widget = struct {
ptr: *anyopaque,
render_fn: *const fn(*anyopaque, Rect, *Buffer) void,
pub fn render(self: Widget, area: Rect, buf: *Buffer) void {
self.render_fn(self.ptr, area, buf);
}
};
// Ejemplo implementación:
const Block = struct {
title: ?[]const u8 = null,
borders: Borders = .all,
style: Style = .{},
pub fn widget(self: *Block) Widget {
return .{
.ptr = self,
.render_fn = render,
};
}
fn render(ptr: *anyopaque, area: Rect, buf: *Buffer) void {
const self: *Block = @ptrCast(@alignCast(ptr));
// ... render logic
}
};
Referencia: ratatui Widgets
| Widget | Descripción | Prioridad |
|---|---|---|
| Block | Contenedor con bordes y título | Alta |
| Paragraph | Texto con wrap y scroll | Alta |
| List | Lista seleccionable | Alta |
| Table | Tabla con columnas | Media |
| Gauge | Barra de progreso | Media |
| Sparkline | Gráfico mini de línea | Baja |
| Chart | Gráficos de línea/barras | Baja |
| Canvas | Dibujo libre (braille) | Baja |
| BarChart | Gráfico de barras | Baja |
| Tabs | Navegación por tabs | Media |
| Scrollbar | Indicador de scroll | Media |
| Calendar | Widget de calendario | Baja |
Stack Técnico
| Componente | Elección |
|---|---|
| Lenguaje | Zig 0.15.2 |
| Zig path | /mnt/cello2/arno/re/recode/zig/zig-0.15.2/zig-x86_64-linux-0.15.2/zig |
| Backend | ANSI escape sequences (portable) |
| Sin dependencias externas | Solo stdlib de Zig |
| Target | Linux primario, cross-platform objetivo |
Equipo y Metodología
Quiénes Somos
- Usuario: Desarrollador independiente, proyectos comerciales propios
- Claude: Asistente de programación (Claude Code)
Normas de Trabajo Centralizadas
IMPORTANTE: Todas las normas de trabajo están en:
/mnt/cello2/arno/re/recode/TEAM_STANDARDS/
Archivos clave a leer:
LAST_UPDATE.md- LEER PRIMERO - Cambios recientes en normasNORMAS_TRABAJO_CONSENSUADAS.md- Metodología fundamentalQUICK_REFERENCE.md- Cheat sheet rápidoINFRASTRUCTURE/- Documentación de servidores
Protocolo de Inicio de Conversación
- Leer
TEAM_STANDARDS/LAST_UPDATE.md(detectar cambios recientes) - Leer este archivo
CLAUDE.md - Verificar estado del proyecto (
git status,zig build) - Continuar desde donde se dejó
Principios de Desarrollo
Estándares Zig Open Source (#24 de NORMAS)
Este proyecto será público. El código debe ser ejemplar.
| Aspecto | Estándar |
|---|---|
| Claridad | Código autoexplicativo, nombres descriptivos |
| Comentarios | Doc comments (///) en TODAS las funciones públicas |
| Estructura | Organización lógica, separación de responsabilidades |
| Idiomático | snake_case, error handling explícito, sin magia |
/// Renderiza un widget Block en el área especificada.
///
/// Dibuja los bordes según `borders` y el título si está definido.
/// El área interior queda disponible para contenido hijo.
pub fn render(self: *Block, area: Rect, buf: *Buffer) void {
// ...
}
Principios Generales
- DRY: Una sola función por tarea
- Fragmentación: <400 líneas core, <200 líneas utils
- Testing progresivo: Compilar y probar cada cambio
- Funcionalidad > Performance: Primero que funcione, luego optimizar
API de Zig 0.15.2 - Cambios Importantes
Ver guía completa:
TEAM_STANDARDS/INFRASTRUCTURE/ZIG_0.15_GUIA.md
Cambios Clave para este Proyecto
| Componente | Zig 0.15 |
|---|---|
| stdout | std.fs.File.stdout().deprecatedWriter() |
| ArrayList | std.array_list.Managed(T).init(alloc) |
| file.reader() | file.deprecatedReader() |
| sleep | std.Thread.sleep() |
Terminal I/O
// Escribir a stdout
const stdout = std.fs.File.stdout();
const writer = stdout.deprecatedWriter();
try writer.print("\x1b[2J", .{}); // Clear screen
// Leer de stdin (para eventos)
const stdin = std.fs.File.stdin();
const reader = stdin.deprecatedReader();
Otros Proyectos del Ecosistema
Proyectos Zig
| Proyecto | Descripción | Estado |
|---|---|---|
| service-monitor | Monitor HTTP/TCP con notificaciones | Completado |
Proyectos Go (referencia)
| Proyecto | Descripción |
|---|---|
| simifactu | API facturación electrónica |
| ms-web (mundisofa) | Web e-commerce |
| 0fiS | Aplicación desktop Fyne |
Infraestructura
| Recurso | Ubicación |
|---|---|
| Git server | git.reugenio.com (Forgejo) |
| Servidor | Simba (188.245.244.244) |
| Docs infra | TEAM_STANDARDS/INFRASTRUCTURE/ |
Control de Versiones
# Remote (cuando se cree el repo)
git remote: git@git.reugenio.com:reugenio/zcatui.git
# Comandos frecuentes
zig build # Compilar
zig build test # Tests
zig build run -- examples/hello.zig # Ejecutar ejemplo
Ejemplo de Uso (Objetivo Final)
const std = @import("std");
const zcatui = @import("zcatui");
const Terminal = zcatui.Terminal;
const Block = zcatui.widgets.Block;
const Paragraph = zcatui.widgets.Paragraph;
const Layout = zcatui.Layout;
const Constraint = zcatui.Constraint;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Inicializar terminal
var term = try Terminal.init(allocator);
defer term.deinit();
// Loop principal
while (true) {
try term.draw(struct {
pub fn render(frame: *Frame) void {
// Layout: dividir en 2 áreas
const chunks = Layout.vertical(&.{
Constraint.length(3),
Constraint.min(0),
}).split(frame.area);
// Header
var header = Block.init()
.title("zcatui Demo")
.borders(.all);
frame.render(header.widget(), chunks[0]);
// Content
var content = Paragraph.init("Hello from zcatui!")
.block(Block.init().borders(.all));
frame.render(content.widget(), chunks[1]);
}
}.render);
// Handle input
if (try term.pollEvent()) |event| {
if (event.key == .q) break;
}
}
}
Recursos y Referencias
ratatui (Rust)
- Repo: https://github.com/ratatui/ratatui
- Docs: https://docs.rs/ratatui/latest/ratatui/
- Website: https://ratatui.rs/
Zig
- Docs 0.15: https://ziglang.org/documentation/0.15.0/std/
- Guía migración:
TEAM_STANDARDS/INFRASTRUCTURE/ZIG_0.15_GUIA.md
ANSI Escape Codes
- Referencia: https://en.wikipedia.org/wiki/ANSI_escape_code
- Colores: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
Estado del Proyecto
| Componente | Estado |
|---|---|
| CLAUDE.md | ✅ Creado |
| build.zig | ⏳ Pendiente |
| Fase 1 (Core) | ⏳ Pendiente |
| Fase 2 (Layout) | ⏳ Pendiente |
| Fase 3 (Widgets básicos) | ⏳ Pendiente |
| Fase 4 (Widgets avanzados) | ⏳ Pendiente |
| Fase 5 (Input/extras) | ⏳ Pendiente |
Próximos pasos sugeridos para la primera sesión:
- Crear
build.zigbásico - Implementar
src/style.zig(Color, Style, Modifiers) - Implementar
src/buffer.zig(Cell, Buffer, Rect) - Implementar
src/backend/ansi.zig(escape sequences) - Crear ejemplo mínimo que pinte algo en pantalla