zcatui/docs/ARCHITECTURE.md
reugenio 560ed1b355 zcatui v1.0 - Implementacion completa de todos los widgets ratatui
Widgets implementados (13):
- Block, Paragraph, List, Table
- Gauge, LineGauge, Tabs, Sparkline
- Scrollbar, BarChart, Canvas, Chart
- Calendar (Monthly), Clear

Modulos:
- src/text.zig: Span, Line, Text, Alignment
- src/symbols/: line, border, block, bar, braille, half_block, scrollbar, marker
- src/widgets/: todos los widgets con tests

Documentacion:
- docs/ARCHITECTURE.md: arquitectura tecnica
- docs/WIDGETS.md: guia completa de widgets
- docs/API.md: referencia rapida
- CLAUDE.md: actualizado con estado v1.0

Tests: 103+ tests en widgets, todos pasan

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

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

413 lines
12 KiB
Markdown

# zcatui - Arquitectura
> Documentación técnica de la arquitectura de zcatui
## Visión General
zcatui es una librería TUI (Terminal User Interface) para Zig, inspirada en ratatui de Rust. Utiliza un patrón de **renderizado inmediato con buffers intermedios**.
## Diagrama de Arquitectura
```
┌─────────────────────────────────────────────────────────────────┐
│ Application │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Widget1 │ │ Widget2 │ │ Widget3 │ │ Widget4 │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │ │
│ └────────────┴─────┬──────┴────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Buffer │ Grid de Cells │
│ │ (current)│ Cada Cell: char + style │
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Diff │ Compara con buffer anterior │
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Terminal │ Solo envía cambios │
│ └────┬─────┘ │
│ │ │
└─────────────────────────┼────────────────────────────────────────┘
┌──────────┐
│ stdout │ ANSI escape sequences
└──────────┘
```
## Componentes Core
### 1. Cell (`buffer.zig`)
La unidad mínima de renderizado. Representa un único carácter en la terminal.
```zig
pub const Cell = struct {
symbol: Symbol, // UTF-8 grapheme (hasta 4 bytes)
style: Style, // Foreground, background, modifiers
pub fn reset(self: *Cell) void { ... }
pub fn setChar(self: *Cell, ch: u21) void { ... }
pub fn setSymbol(self: *Cell, symbol: []const u8) void { ... }
};
```
### 2. Buffer (`buffer.zig`)
Grid bidimensional de Cells. Maneja el estado de renderizado.
```zig
pub const Buffer = struct {
area: Rect,
cells: []Cell,
allocator: Allocator,
pub fn init(allocator: Allocator, area: Rect) !Buffer { ... }
pub fn getCell(self: *Buffer, x: u16, y: u16) ?*Cell { ... }
pub fn setString(self: *Buffer, x: u16, y: u16, text: []const u8, style: Style) u16 { ... }
pub fn setSpan(self: *Buffer, x: u16, y: u16, span: Span, width: u16) u16 { ... }
};
```
### 3. Rect (`buffer.zig`)
Representa un área rectangular en la terminal.
```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 { ... }
pub fn inner(self: Rect, margin: u16) Rect { ... }
pub fn intersection(self: Rect, other: Rect) Rect { ... }
pub fn isEmpty(self: Rect) bool { ... }
pub fn left(self: Rect) u16 { ... }
pub fn right(self: Rect) u16 { ... }
pub fn top(self: Rect) u16 { ... }
pub fn bottom(self: Rect) u16 { ... }
};
```
### 4. Style (`style.zig`)
Combinación de colores y modificadores de texto.
```zig
pub const Style = struct {
foreground: ?Color = null,
background: ?Color = null,
add_modifiers: Modifier = .{},
sub_modifiers: Modifier = .{},
pub const default: Style = .{};
pub fn fg(self: Style, color: Color) Style { ... }
pub fn bg(self: Style, color: Color) Style { ... }
pub fn bold(self: Style) Style { ... }
pub fn italic(self: Style) Style { ... }
pub fn patch(self: Style, other: Style) Style { ... }
};
```
### 5. Color (`style.zig`)
Soporte para colores de 16, 256 y RGB.
```zig
pub const Color = union(enum) {
reset,
black, red, green, yellow, blue, magenta, cyan, white,
light_black, light_red, light_green, light_yellow,
light_blue, light_magenta, light_cyan, light_white,
indexed: u8, // 256 colores
rgb: struct { r: u8, g: u8, b: u8 },
};
```
### 6. Text Types (`text.zig`)
Tipos para manejar texto estilizado.
```zig
// Span: texto con estilo único
pub const Span = struct {
content: []const u8,
style: Style,
};
// Line: múltiples spans en una línea
pub const Line = struct {
spans: []const Span,
alignment: Alignment,
};
// Text: múltiples líneas
pub const Text = struct {
lines: []const Line,
alignment: Alignment,
};
```
### 7. Layout (`layout.zig`)
Sistema de distribución de espacio.
```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 split(self: Layout, area: Rect, result: []Rect) void { ... }
};
pub const Constraint = union(enum) {
length: u16, // Exactamente N celdas
min: u16, // Mínimo N celdas
max: u16, // Máximo N celdas
percentage: u16, // N% del espacio disponible
ratio: struct { num: u32, den: u32 },
fill: u16, // Llenar espacio restante
};
```
## Sistema de Widgets
### Patrón de Widget
Todos los widgets implementan el método `render`:
```zig
pub fn render(self: WidgetType, area: Rect, buf: *Buffer) void {
// 1. Validar área
if (area.isEmpty()) return;
// 2. Renderizar block/wrapper si existe
const inner = if (self.block) |b| blk: {
b.render(area, buf);
break :blk b.inner(area);
} else area;
// 3. Renderizar contenido
// ...
}
```
### StatefulWidget Pattern
Para widgets con estado mutable:
```zig
pub fn renderStateful(self: WidgetType, area: Rect, buf: *Buffer, state: *State) void {
// Similar a render, pero puede modificar state
// Útil para: scroll position, selection, etc.
}
```
### Fluent Builder Pattern
Los widgets usan setters encadenables:
```zig
const list = List.init(items)
.setBlock(block)
.setHighlightStyle(style)
.setHighlightSymbol("> ")
.setDirection(.top_to_bottom);
```
## Sistema de Símbolos
### Estructura
```
symbols/
├── line.zig # ─ │ ┌ ┐ └ ┘ ├ ┤ etc.
├── border.zig # Sets de bordes: plain, rounded, double, thick
├── block.zig # █ ▀ ▄ ▌ ▐ etc.
├── bar.zig # ▏▎▍▌▋▊▉█ (barras horizontales)
├── braille.zig # Patrones braille (256 combinaciones)
├── half_block.zig # ▀ ▄ para resolución 1x2
├── scrollbar.zig # Símbolos para scrollbars
└── marker.zig # Marcadores para charts: dot, block, braille, etc.
```
### Braille Grid (2x4 dots per cell)
```
┌───┬───┐
│ 0 │ 3 │ Bit layout:
├───┼───┤
│ 1 │ 4 │ byte = Σ (2^bit) para cada dot activo
├───┼───┤
│ 2 │ 5 │ Base: U+2800 (braille blank)
├───┼───┤ Resultado: chr(0x2800 + byte)
│ 6 │ 7 │
└───┴───┘
```
## Backend ANSI
### Escape Sequences Soportadas
| Función | Secuencia |
|---------|-----------|
| Clear screen | `\x1b[2J` |
| Move cursor | `\x1b[{row};{col}H` |
| Hide cursor | `\x1b[?25l` |
| Show cursor | `\x1b[?25h` |
| Reset style | `\x1b[0m` |
| Bold | `\x1b[1m` |
| Dim | `\x1b[2m` |
| Italic | `\x1b[3m` |
| Underline | `\x1b[4m` |
| FG color (16) | `\x1b[{30-37}m` |
| BG color (16) | `\x1b[{40-47}m` |
| FG color (256) | `\x1b[38;5;{n}m` |
| BG color (256) | `\x1b[48;5;{n}m` |
| FG color (RGB) | `\x1b[38;2;{r};{g};{b}m` |
| BG color (RGB) | `\x1b[48;2;{r};{g};{b}m` |
| Alternate screen | `\x1b[?1049h` |
| Main screen | `\x1b[?1049l` |
## Algoritmos Clave
### Bresenham's Line Algorithm (Canvas)
Usado para dibujar líneas en el canvas:
```zig
fn drawLine(x0: i32, y0: i32, x1: i32, y1: i32, color: Color) void {
var dx = @abs(x1 - x0);
var dy = @abs(y1 - y0);
var sx: i32 = if (x0 < x1) 1 else -1;
var sy: i32 = if (y0 < y1) 1 else -1;
var err = dx - dy;
while (true) {
self.set(x0, y0, color);
if (x0 == x1 and y0 == y1) break;
const e2 = 2 * err;
if (e2 > -dy) { err -= dy; x0 += sx; }
if (e2 < dx) { err += dx; y0 += sy; }
}
}
```
### Zeller's Congruence (Calendar)
Para calcular el día de la semana:
```zig
pub fn dayOfWeek(year: i16, month: u4, day: u5) u3 {
var y = year;
var m = month;
if (m < 3) { m += 12; y -= 1; }
const q = day;
const k = @mod(y, 100);
const j = @divFloor(y, 100);
var h = q + @divFloor(13 * (m + 1), 5) + k + @divFloor(k, 4) + @divFloor(j, 4) - 2 * j;
return @intCast(@mod(@mod(h, 7) + 6, 7)); // 0=Sunday
}
```
## Consideraciones de Memoria
### Stack Allocation
La mayoría de estructuras usan stack allocation con tamaños fijos:
- `CalendarEventStore`: máximo 32 eventos
- `Symbol`: máximo 4 bytes UTF-8
- Arrays de constraints: tamaño fijo en compilación
### Heap Allocation
Solo se usa heap para:
- `Buffer.cells`: array de celdas (puede ser grande)
- Strings dinámicos pasados por el usuario
### Sin GC
Zig no tiene garbage collector. Los widgets no poseen memoria, solo referencias. El usuario es responsable de la lifetime de los datos.
## Testing
### Estrategia
1. **Unit tests** en cada módulo
2. **Render tests** comparando buffers
3. **Property-based** donde aplica (ej: Rect.intersection es conmutativa)
### Ejecutar tests
```bash
zig build test # Todos los tests
zig build test --summary all # Con resumen detallado
```
## Extensibilidad
### Crear un Widget Personalizado
```zig
const MyWidget = struct {
data: []const u8,
style: Style,
block: ?Block = null,
pub fn init(data: []const u8) MyWidget {
return .{ .data = data, .style = Style.default };
}
pub fn setStyle(self: MyWidget, s: Style) MyWidget {
var w = self;
w.style = s;
return w;
}
pub fn setBlock(self: MyWidget, b: Block) MyWidget {
var w = self;
w.block = b;
return w;
}
pub fn render(self: MyWidget, area: Rect, buf: *Buffer) void {
if (area.isEmpty()) return;
const inner = if (self.block) |b| blk: {
b.render(area, buf);
break :blk b.inner(area);
} else area;
_ = buf.setString(inner.left(), inner.top(), self.data, self.style);
}
};
```
## Performance
### Optimizaciones Actuales
1. **Diff-based rendering**: Solo se envían cambios a la terminal
2. **Pre-computed symbols**: Braille patterns pre-calculados
3. **Inline functions**: Funciones críticas marcadas como inline
4. **Saturating arithmetic**: Uso de `-|` para evitar overflow checks
### Áreas de Mejora Futuras
1. Buffer pooling para reutilización
2. Lazy widget evaluation
3. Dirty region tracking
4. SIMD para operaciones de buffer