zcatgui/docs/DEVELOPMENT_PLAN.md
reugenio 8adc93a345 feat: zcatgui v0.6.0 - Phase 1 Optimization Complete
Performance Infrastructure:
- FrameArena: O(1) per-frame allocator with automatic reset
- ObjectPool: Generic object pool for frequently allocated types
- CommandPool: Specialized pool for draw commands
- RingBuffer: Circular buffer for streaming data
- ScopedArena: RAII pattern for temporary allocations

Dirty Rectangle System:
- Context now tracks dirty regions for partial redraws
- Automatic rect merging to reduce overdraw
- invalidateRect(), needsRedraw(), getDirtyRects() API
- Falls back to full redraw when > 32 dirty rects

Benchmark Suite:
- Timer: High-resolution timing
- Benchmark: Stats collection (avg, min, max, stddev, median)
- FrameTimer: FPS and frame time tracking
- AllocationTracker: Memory usage monitoring
- Pre-built benchmarks for arena, pool, and commands

Context Improvements:
- Integrated FrameArena for zero-allocation hot paths
- frameAllocator() for per-frame widget allocations
- FrameStats for performance monitoring
- Context.init() now returns error union (breaking change)

New Widgets (from previous session):
- Slider: Horizontal/vertical with customization
- ScrollArea: Scrollable content region
- Tabs: Tab container with keyboard navigation
- RadioButton: Radio button groups
- Menu: Dropdown menus (foundation)

Theme System Expansion:
- 5 built-in themes: dark, light, high_contrast, nord, dracula
- ThemeManager with runtime switching
- TTF font support via stb_truetype

Documentation:
- DEVELOPMENT_PLAN.md: 9-phase roadmap to DVUI/Gio parity
- Updated WIDGET_COMPARISON.md with detailed analysis
- Lego Panels architecture documented

Stats: 17 widgets, 123 tests, 5 themes

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

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

1611 lines
42 KiB
Markdown

# Plan Maestro de Desarrollo: zcatgui
> **Versión**: 1.0
> **Fecha**: 2025-12-09
> **Objetivo**: Llevar zcatgui a la perfección - paridad completa con DVUI/Gio + features únicas
> **Filosofía**: Calidad sobre velocidad. Código óptimo, mínima memoria, máximo rendimiento.
---
## COMPARATIVA DE PARIDAD
### vs DVUI (Target: 100%)
| Categoría | DVUI | zcatgui actual | Falta | Post-Plan |
|-----------|:----:|:--------------:|:-----:|:---------:|
| Básicos (Label, Button, etc.) | 7 | 7 ✅ | 0 | 7 ✅ |
| Contenedores (Panel, Split, etc.) | 6 | 6 ✅ | 0 | 6 ✅ |
| Datos (List, Table, Tree) | 4 | 2 | 2 | 4 ✅ |
| Input (Text, Number, Editor) | 5 | 3 | 2 | 5 ✅ |
| Navegación (Menu, Tabs) | 4 | 4 ✅ | 0 | 4 ✅ |
| Feedback (Tooltip, Toast) | 3 | 0 | 3 | 3 ✅ |
| Especial (Image, Icon) | 4 | 0 | 4 | 4 ✅ |
| Rendering (AA, Effects) | ✅ | Básico | ✅ | ✅ |
| **TOTAL WIDGETS** | **35** | **17** | **18** | **35 ✅** |
### vs Gio (Target: ~60% - lo útil)
| Feature | Gio | Implementamos | Razón si NO |
|---------|:---:|:-------------:|-------------|
| Widgets básicos | ✅ | ✅ | - |
| Progress/Spinner | ✅ | ✅ | - |
| Charts | ✅ | ✅ | - |
| Clipboard | ✅ | ✅ | - |
| Animaciones | ✅ | ✅ | - |
| Material Design | ✅ | ❌ | No necesario, tenemos themes |
| GPU Rendering | ✅ | Opcional | Software-first es objetivo |
| HarfBuzz/BiDi | ✅ | ❌ | Overkill, no idiomas RTL |
| System Fonts | ✅ | ❌ | TTF embebido suficiente |
### Features ÚNICAS de zcatgui (Ventaja Competitiva)
| Feature | DVUI | Gio | zcatgui | Valor |
|---------|:----:|:---:|:-------:|:-----:|
| Sistema de Macros | ❌ | ❌ | ✅ | ⭐⭐⭐ |
| Lego Panels + DataManager | ❌ | ❌ | ✅ | ⭐⭐⭐ |
| Table Dirty Tracking | ❌ | ❌ | ✅ | ⭐⭐⭐ |
| Table Validation | ❌ | ❌ | ✅ | ⭐⭐ |
| Table Sorting | ❌ | ❌ | ✅ | ⭐⭐ |
| Table Multi-select | ❌ | ❌ | ✅ | ⭐⭐ |
| High Contrast Theme | ❌ | ❌ | ✅ | ⭐⭐ |
| SSH-first Design | Parcial | ❌ | ✅ | ⭐⭐⭐ |
### Conclusión de Paridad
```
Post-Plan:
vs DVUI: 100% paridad + features únicas SUPERIORES
vs Gio: ~60% paridad (lo útil) + features únicas
zcatgui será MEJOR que ambas para aplicaciones empresariales
gracias a: Macros + Lego Panels + Table avanzada + SSH-first
```
---
## ÍNDICE
1. [Estado Actual](#1-estado-actual)
2. [Objetivos de Perfección](#2-objetivos-de-perfección)
3. [Principios de Desarrollo](#3-principios-de-desarrollo)
4. [Plan por Fases](#4-plan-por-fases)
5. [Detalles de Implementación](#5-detalles-de-implementación)
6. [Métricas de Calidad](#6-métricas-de-calidad)
7. [Checklist Final](#7-checklist-final)
---
## 1. ESTADO ACTUAL
### 1.1 Inventario Completado (v0.5.0)
| Categoría | Completado | Items |
|-----------|:----------:|-------|
| **Core** | ✅ 100% | Context, Layout, Style, Input, Command |
| **Render** | ✅ 80% | Framebuffer, SoftwareRenderer, Font, TTF básico |
| **Backend** | ✅ 100% | SDL2 |
| **Macros** | ✅ 100% | Recorder, Player, Storage |
| **Widgets** | ✅ 17/35 | Label, Button, TextInput, Checkbox, Select, List, Focus, Table, Split, Panel, Modal, AutoComplete, Slider, Scroll, Menu, Tabs, Radio |
| **Panels** | ✅ 100% | AutonomousPanel, Composites, DataManager |
| **Themes** | ✅ 100% | 5 themes, ThemeManager |
### 1.2 LOC Actual
```
Core: ~1,700 LOC
Render: ~1,300 LOC
Backend: ~400 LOC
Macro: ~340 LOC
Widgets: ~8,000 LOC
Panels: ~350 LOC
Entry: ~130 LOC
─────────────────────
TOTAL: ~12,220 LOC
```
### 1.3 Tests Actuales
- **85+ tests** pasando
- Cobertura: Core 90%, Widgets 60%, Panels 70%
---
## 2. OBJETIVOS DE PERFECCIÓN
### 2.1 Paridad de Widgets (Target: 35 widgets)
```
Actual: 17 widgets
Objetivo: 35 widgets (+18)
Paridad: 100% DVUI, 60% Gio
```
### 2.2 Rendimiento Target
| Métrica | Target | Razón |
|---------|--------|-------|
| **FPS** | 60+ estable | Fluidez visual |
| **Startup** | <50ms | Percepción instantánea |
| **Memoria base** | <10MB | Eficiencia |
| **Memoria por widget** | <100 bytes | Escalabilidad |
| **Latencia input** | <16ms | Respuesta inmediata |
| **Redraw completo** | <5ms | 60fps con margen |
### 2.3 Calidad de Código
| Métrica | Target |
|---------|--------|
| **LOC total** | <20,000 (eficiente) |
| **Cobertura tests** | >90% |
| **Complejidad ciclomática** | <10 por función |
| **Funciones >50 LOC** | 0 |
| **Comentarios/código** | 15-20% |
| **Warnings** | 0 |
| **Memory leaks** | 0 |
---
## 3. PRINCIPIOS DE DESARROLLO
### 3.1 Optimización de Memoria
```zig
// ❌ MAL: Allocaciones innecesarias
fn process(allocator: Allocator, items: []const Item) ![]Result {
var results = try allocator.alloc(Result, items.len);
// ...
}
// ✅ BIEN: Reutilizar buffers, stack allocation cuando sea posible
fn process(items: []const Item, result_buf: []Result) []Result {
const count = @min(items.len, result_buf.len);
// ...
return result_buf[0..count];
}
```
**Reglas:**
1. Preferir stack allocation sobre heap
2. Usar `comptime` para cálculos en tiempo de compilación
3. Reutilizar buffers entre frames
4. Evitar slices dinámicos cuando el tamaño es conocido
5. Usar `@memset` y `@memcpy` para operaciones bulk
### 3.2 Optimización de Rendimiento
```zig
// ❌ MAL: Múltiples pasadas
for (items) |item| {
if (item.visible) count += 1;
}
for (items) |item| {
if (item.visible) process(item);
}
// ✅ BIEN: Una sola pasada
for (items) |item| {
if (item.visible) {
count += 1;
process(item);
}
}
```
**Reglas:**
1. Minimizar iteraciones (single-pass cuando posible)
2. Early-exit en condiciones frecuentes
3. Cache locality: acceso secuencial a memoria
4. Evitar división (usar multiplicación por recíproco)
5. Usar SIMD implícito via `@Vector` cuando aplique
### 3.3 Inmediato y Determinista
```zig
// ❌ MAL: Estado oculto, efectos secundarios
var global_counter: u32 = 0;
fn doSomething() void {
global_counter += 1;
}
// ✅ BIEN: Estado explícito, función pura
fn doSomething(state: *State) void {
state.counter += 1;
}
```
**Reglas:**
1. Sin globals mutables (excepto ThemeManager/DataManager controlados)
2. Funciones puras cuando posible
3. Estado explícito pasado como parámetro
4. Resultados predecibles dado el mismo input
### 3.4 API Consistente
Todos los widgets siguen el patrón:
```zig
// Variante simple (defaults)
pub fn widget(ctx: *Context, ...) Result { ... }
// Variante extendida (con config)
pub fn widgetEx(ctx: *Context, ..., config: Config) Result { ... }
// Variante con bounds explícitos
pub fn widgetRect(ctx: *Context, bounds: Rect, ..., config: Config) Result { ... }
```
---
## 4. PLAN POR FASES
### FASE 1: FUNDAMENTOS SÓLIDOS
**Duración estimada**: 1 semana
**Objetivo**: Optimizar y solidificar el core existente
#### 1.1 Optimización del Core
| Tarea | Prioridad | LOC Est. | Descripción |
|-------|:---------:|:--------:|-------------|
| Arena allocator para Context | ALTA | 150 | Pool de memoria por frame |
| Object pooling para Commands | ALTA | 100 | Reutilizar command buffers |
| Optimizar Rect operations | MEDIA | 50 | SIMD para intersecciones |
| Benchmark suite | ALTA | 200 | Medir rendimiento base |
#### 1.2 Optimización del Renderer
| Tarea | Prioridad | LOC Est. | Descripción |
|-------|:---------:|:--------:|-------------|
| Dirty rectangles | ALTA | 200 | Solo redibujar lo cambiado |
| Batch draw commands | ALTA | 150 | Agrupar rects del mismo color |
| Clip optimization | MEDIA | 100 | Skip completo si fuera de clip |
| Framebuffer double-buffering | MEDIA | 80 | Evitar tearing |
#### 1.3 Tests y Benchmarks
| Tarea | Prioridad | LOC Est. | Descripción |
|-------|:---------:|:--------:|-------------|
| Test coverage 90%+ | ALTA | 500 | Tests para todo el core |
| Performance benchmarks | ALTA | 300 | Medir todas las operaciones |
| Memory leak tests | ALTA | 100 | Verificar cero leaks |
| Stress tests | MEDIA | 200 | 10K widgets, 1M commands |
**Entregables Fase 1:**
- [ ] Arena allocator funcionando
- [ ] Dirty rectangles implementado
- [ ] Benchmark suite con métricas base
- [ ] 90%+ test coverage en core
- [ ] 0 memory leaks verificado
---
### FASE 2: WIDGETS DE FEEDBACK
**Duración estimada**: 1 semana
**Objetivo**: Completar widgets de feedback visual
#### 2.1 Tooltip
```zig
pub const TooltipConfig = struct {
delay_ms: u32 = 500, // Delay antes de mostrar
max_width: u16 = 300, // Ancho máximo
position: Position = .auto, // auto, above, below, left, right
arrow: bool = true, // Mostrar flecha
};
pub const TooltipState = struct {
hover_start: i64 = 0, // Timestamp inicio hover
visible: bool = false,
target_rect: Rect = .{},
};
pub fn tooltip(ctx: *Context, state: *TooltipState, text: []const u8, config: TooltipConfig) void;
pub fn tooltipArea(ctx: *Context, bounds: Rect, state: *TooltipState, text: []const u8) void;
```
**Características:**
- Delay configurable antes de mostrar
- Posicionamiento inteligente (evitar salir de pantalla)
- Soporte multi-línea con wrapping
- Flecha apuntando al elemento
- Fade in/out suave
**LOC estimadas**: 150
#### 2.2 ProgressBar
```zig
pub const ProgressStyle = enum { bar, circle, dots };
pub const ProgressConfig = struct {
style: ProgressStyle = .bar,
show_percentage: bool = true,
show_label: bool = false,
label: []const u8 = "",
animated: bool = true, // Animación de progreso
indeterminate: bool = false, // Modo indeterminado (loading)
};
pub const ProgressColors = struct {
track: Color,
fill: Color,
text: Color,
};
pub fn progressBar(ctx: *Context, progress: f32, config: ProgressConfig) void;
pub fn progressCircle(ctx: *Context, progress: f32, config: ProgressConfig) void;
```
**Características:**
- Barra horizontal/vertical
- Círculo de progreso
- Modo indeterminado (spinner)
- Porcentaje y label opcional
- Animación suave de transición
**LOC estimadas**: 200
#### 2.3 Toast/Notification
```zig
pub const ToastType = enum { info, success, warning, error };
pub const ToastPosition = enum { top, bottom, top_right, bottom_right };
pub const ToastConfig = struct {
duration_ms: u32 = 3000,
position: ToastPosition = .bottom_right,
max_visible: u8 = 5,
animation: bool = true,
};
pub const ToastManager = struct {
toasts: [MAX_TOASTS]Toast,
count: usize,
pub fn show(self: *ToastManager, message: []const u8, toast_type: ToastType) void;
pub fn showWithAction(self: *ToastManager, message: []const u8, action: []const u8, callback: ActionFn) void;
pub fn dismiss(self: *ToastManager, id: u32) void;
pub fn dismissAll(self: *ToastManager) void;
pub fn render(self: *ToastManager, ctx: *Context) void;
};
```
**Características:**
- Múltiples tipos (info, success, warning, error)
- Posicionamiento configurable
- Auto-dismiss con timer
- Botón de acción opcional
- Stack de múltiples toasts
- Animación slide in/out
**LOC estimadas**: 250
#### 2.4 Spinner/Loader
```zig
pub const SpinnerStyle = enum {
circular, // Círculo girando
dots, // Puntos pulsando
bars, // Barras ecualizador
ring, // Anillo con gap
};
pub const SpinnerConfig = struct {
style: SpinnerStyle = .circular,
size: u16 = 24,
speed: f32 = 1.0, // Multiplicador de velocidad
label: ?[]const u8 = null,
};
pub fn spinner(ctx: *Context, config: SpinnerConfig) void;
pub fn spinnerOverlay(ctx: *Context, bounds: Rect, config: SpinnerConfig) void;
```
**Características:**
- Múltiples estilos de animación
- Tamaño configurable
- Velocidad ajustable
- Label opcional ("Loading...")
- Overlay mode para cubrir contenido
**LOC estimadas**: 150
**Entregables Fase 2:**
- [ ] Tooltip con posicionamiento inteligente
- [ ] ProgressBar (bar + circle + indeterminate)
- [ ] Toast/Notification system
- [ ] Spinner con 4 estilos
- [ ] Tests para cada widget
- [ ] Demo de feedback widgets
---
### FASE 3: WIDGETS ESPECIALIZADOS
**Duración estimada**: 2 semanas
**Objetivo**: Widgets avanzados para aplicaciones complejas
#### 3.1 Tree View
```zig
pub const TreeNode = struct {
id: u64,
label: []const u8,
icon: ?Icon = null,
children: []TreeNode = &.{},
expanded: bool = false,
selected: bool = false,
data: ?*anyopaque = null,
};
pub const TreeConfig = struct {
indent: u16 = 20,
show_lines: bool = true,
show_icons: bool = true,
multi_select: bool = false,
drag_drop: bool = false,
lazy_load: bool = false, // Cargar hijos bajo demanda
};
pub const TreeState = struct {
selected: std.ArrayList(u64),
expanded: std.AutoHashMap(u64, bool),
scroll_offset: i32,
focused_id: ?u64,
};
pub const TreeResult = struct {
selection_changed: bool,
expanded_changed: bool,
activated: bool, // Double-click o Enter
activated_node: ?*TreeNode,
drag_started: bool,
drop_target: ?*TreeNode,
};
pub fn tree(ctx: *Context, bounds: Rect, state: *TreeState, root: *TreeNode, config: TreeConfig) TreeResult;
```
**Características:**
- Expand/collapse con animación
- Selección simple y múltiple
- Keyboard navigation completo
- Drag & drop entre nodos
- Lazy loading para árboles grandes
- Iconos por tipo de nodo
- Líneas de conexión opcionales
- Virtual scrolling para árboles enormes
**LOC estimadas**: 400
#### 3.2 Image Widget
```zig
pub const ImageFormat = enum { rgba, rgb, grayscale, indexed };
pub const ImageFit = enum { none, contain, cover, fill, scale_down };
pub const ImageData = struct {
pixels: []const u8,
width: u32,
height: u32,
format: ImageFormat,
stride: ?u32 = null,
};
pub const ImageConfig = struct {
fit: ImageFit = .contain,
alignment: Alignment = .center,
border_radius: u8 = 0,
placeholder: ?Color = null, // Color mientras carga
error_placeholder: ?[]const u8 = null,
};
pub const ImageCache = struct {
entries: std.AutoHashMap(u64, CachedImage),
max_size: usize,
current_size: usize,
pub fn get(self: *ImageCache, id: u64) ?*CachedImage;
pub fn put(self: *ImageCache, id: u64, image: ImageData) !void;
pub fn evict(self: *ImageCache, bytes: usize) void;
};
pub fn image(ctx: *Context, bounds: Rect, data: ImageData, config: ImageConfig) void;
pub fn imageFromCache(ctx: *Context, bounds: Rect, cache: *ImageCache, id: u64, config: ImageConfig) void;
```
**Características:**
- Soporte RGBA, RGB, grayscale
- Múltiples modos de fit
- Caché de imágenes con LRU eviction
- Border radius para esquinas redondeadas
- Placeholder mientras carga
- Lazy loading desde archivo
- Resize eficiente (nearest/bilinear)
**LOC estimadas**: 350
#### 3.3 ReorderableList
```zig
pub const ReorderableConfig = struct {
item_height: u16 = 32,
drag_handle: bool = true, // Mostrar handle de arrastre
allow_remove: bool = true, // Permitir eliminar items
allow_add: bool = true, // Permitir añadir items
animation: bool = true, // Animar reordenamiento
};
pub const ReorderableState = struct {
dragging_index: ?usize,
drag_offset: i32,
target_index: ?usize,
items_order: []usize, // Índices en orden actual
};
pub const ReorderableResult = struct {
reordered: bool,
removed_index: ?usize,
add_requested: bool,
new_order: []const usize,
};
pub fn reorderableList(
ctx: *Context,
bounds: Rect,
state: *ReorderableState,
items: []const []const u8,
config: ReorderableConfig,
) ReorderableResult;
```
**Características:**
- Drag & drop para reordenar
- Handle visual de arrastre
- Animación suave de reposicionamiento
- Botón eliminar por item
- Botón añadir al final
- Keyboard reorder (Ctrl+Up/Down)
- Callback on change
**LOC estimadas**: 300
#### 3.4 ColorPicker
```zig
pub const ColorPickerMode = enum { rgb, hsl, hex, palette };
pub const ColorPickerConfig = struct {
mode: ColorPickerMode = .rgb,
show_alpha: bool = true,
show_preview: bool = true,
palette: ?[]const Color = null, // Colores predefinidos
recent_colors: bool = true,
};
pub const ColorPickerState = struct {
current: Color,
mode: ColorPickerMode,
hue: f32,
saturation: f32,
lightness: f32,
recent: [8]Color,
};
pub fn colorPicker(ctx: *Context, bounds: Rect, state: *ColorPickerState, config: ColorPickerConfig) bool;
pub fn colorPickerPopup(ctx: *Context, anchor: Rect, state: *ColorPickerState, config: ColorPickerConfig) bool;
```
**Características:**
- Selector visual (cuadrado SL + barra H)
- Inputs RGB/HSL/Hex
- Slider de alpha
- Paleta de colores predefinidos
- Historial de colores recientes
- Preview comparativo (nuevo vs actual)
- Eyedropper (futuro)
**LOC estimadas**: 350
#### 3.5 DatePicker/Calendar
```zig
pub const DatePickerConfig = struct {
min_date: ?Date = null,
max_date: ?Date = null,
disabled_dates: ?[]const Date = null,
first_day_of_week: u8 = 1, // 0=Sunday, 1=Monday
show_week_numbers: bool = false,
range_selection: bool = false,
};
pub const DatePickerState = struct {
selected_date: ?Date,
range_start: ?Date,
range_end: ?Date,
view_month: u8,
view_year: u16,
open: bool,
};
pub const DatePickerResult = struct {
changed: bool,
date: ?Date,
range: ?struct { start: Date, end: Date },
};
pub fn datePicker(ctx: *Context, state: *DatePickerState, config: DatePickerConfig) DatePickerResult;
pub fn calendar(ctx: *Context, bounds: Rect, state: *DatePickerState, config: DatePickerConfig) DatePickerResult;
```
**Características:**
- Vista de mes con navegación
- Selección de fecha única o rango
- Fechas deshabilitadas
- Límites min/max
- Números de semana opcionales
- Navegación por teclado
- Dropdown integrado
**LOC estimadas**: 400
**Entregables Fase 3:**
- [ ] Tree View con lazy loading
- [ ] Image widget con cache
- [ ] ReorderableList con drag & drop
- [ ] ColorPicker completo
- [ ] DatePicker/Calendar
- [ ] Tests exhaustivos
- [ ] Demo de widgets especializados
---
### FASE 4: TEXTO AVANZADO
**Duración estimada**: 2 semanas
**Objetivo**: Sistema de texto profesional
#### 4.1 MultilineText Editor
```zig
pub const EditorConfig = struct {
read_only: bool = false,
word_wrap: bool = true,
show_line_numbers: bool = false,
tab_size: u8 = 4,
max_lines: ?u32 = null,
placeholder: ?[]const u8 = null,
};
pub const EditorState = struct {
lines: std.ArrayList([]u8),
cursor_line: usize,
cursor_col: usize,
selection_start: ?Position,
selection_end: ?Position,
scroll_offset: i32,
undo_stack: UndoStack,
redo_stack: UndoStack,
};
pub const EditorResult = struct {
text_changed: bool,
cursor_moved: bool,
selection_changed: bool,
};
pub fn editor(ctx: *Context, bounds: Rect, state: *EditorState, config: EditorConfig) EditorResult;
```
**Características:**
- Múltiples líneas con scroll
- Selección de texto (mouse y teclado)
- Copy/Cut/Paste (clipboard)
- Undo/Redo con stack
- Word wrap opcional
- Line numbers opcional
- Tab handling
- Find & Replace (básico)
**LOC estimadas**: 600
#### 4.2 NumberEntry
```zig
pub const NumberType = enum { integer, float, currency };
pub const NumberConfig = struct {
number_type: NumberType = .integer,
min: ?f64 = null,
max: ?f64 = null,
step: f64 = 1.0,
precision: u8 = 2, // Decimales para float/currency
prefix: ?[]const u8 = null, // "$", "€"
suffix: ?[]const u8 = null, // "%", "kg"
thousand_separator: bool = true,
allow_negative: bool = true,
spinner: bool = true, // Botones +/-
};
pub const NumberState = struct {
value: f64,
text_buf: [64]u8,
text_len: usize,
editing: bool,
cursor: usize,
};
pub const NumberResult = struct {
changed: bool,
value: f64,
valid: bool,
};
pub fn numberEntry(ctx: *Context, state: *NumberState, config: NumberConfig) NumberResult;
```
**Características:**
- Validación en tiempo real
- Formateo automático (separadores miles)
- Spinner buttons (+/-)
- Arrastrar para cambiar valor
- Límites min/max
- Precisión configurable
- Prefijo/sufijo (moneda, unidades)
**LOC estimadas**: 250
#### 4.3 Font Atlas y Rendering Mejorado
```zig
pub const FontAtlas = struct {
texture: []u8,
width: u32,
height: u32,
glyphs: std.AutoHashMap(GlyphKey, GlyphInfo),
pub fn getGlyph(self: *FontAtlas, codepoint: u32, size: u16) ?GlyphInfo;
pub fn renderGlyph(self: *FontAtlas, codepoint: u32, size: u16) !GlyphInfo;
};
pub const GlyphInfo = struct {
atlas_x: u16,
atlas_y: u16,
width: u16,
height: u16,
bearing_x: i16,
bearing_y: i16,
advance: u16,
};
pub const TextRenderer = struct {
atlas: *FontAtlas,
pub fn measureText(self: *TextRenderer, text: []const u8, size: u16) struct { width: u32, height: u32 };
pub fn renderText(self: *TextRenderer, fb: *Framebuffer, x: i32, y: i32, text: []const u8, size: u16, color: Color) void;
pub fn renderTextWrapped(self: *TextRenderer, fb: *Framebuffer, bounds: Rect, text: []const u8, size: u16, color: Color, align: Alignment) void;
};
```
**Características:**
- Font atlas dinámico
- Caché de glyphs renderizados
- Múltiples tamaños de fuente
- Text measurement preciso
- Word wrapping correcto
- Kerning básico
- Subpixel rendering (opcional)
**LOC estimadas**: 500
#### 4.4 Rich Text (básico)
```zig
pub const TextSpan = struct {
text: []const u8,
style: TextStyle,
};
pub const TextStyle = struct {
color: ?Color = null,
bold: bool = false,
italic: bool = false,
underline: bool = false,
strikethrough: bool = false,
size: ?u16 = null,
link: ?[]const u8 = null,
};
pub fn richText(ctx: *Context, bounds: Rect, spans: []const TextSpan) void;
pub fn richTextInteractive(ctx: *Context, bounds: Rect, spans: []const TextSpan) ?[]const u8; // Returns clicked link
```
**Características:**
- Múltiples estilos en un texto
- Bold, italic, underline
- Links clickables
- Colores por span
- Tamaños mixtos
**LOC estimadas**: 300
**Entregables Fase 4:**
- [ ] Editor multilinea completo
- [ ] NumberEntry con validación
- [ ] Font Atlas funcionando
- [ ] Rich Text básico
- [ ] Clipboard integration
- [ ] Tests de texto
- [ ] Demo de editores
---
### FASE 5: SISTEMA DE ICONOS Y GRÁFICOS
**Duración estimada**: 1 semana
**Objetivo**: Iconos vectoriales y gráficos básicos
#### 5.1 Icon System
```zig
pub const IconSet = enum {
material, // Material Design icons
feather, // Feather icons
custom, // User-defined
};
pub const Icon = struct {
data: []const u8, // Path data o bitmap
width: u16,
height: u16,
pub fn render(self: Icon, fb: *Framebuffer, x: i32, y: i32, size: u16, color: Color) void;
};
pub const IconLibrary = struct {
icons: std.StringHashMap(Icon),
pub fn get(self: *IconLibrary, name: []const u8) ?Icon;
pub fn loadFromSvg(self: *IconLibrary, name: []const u8, svg_data: []const u8) !void;
};
// Built-in icons
pub const icons = struct {
pub const check: Icon = ...;
pub const close: Icon = ...;
pub const arrow_left: Icon = ...;
pub const arrow_right: Icon = ...;
pub const arrow_up: Icon = ...;
pub const arrow_down: Icon = ...;
pub const menu: Icon = ...;
pub const search: Icon = ...;
pub const settings: Icon = ...;
pub const user: Icon = ...;
pub const folder: Icon = ...;
pub const file: Icon = ...;
pub const edit: Icon = ...;
pub const delete: Icon = ...;
pub const add: Icon = ...;
pub const remove: Icon = ...;
// ... 50+ iconos básicos
};
```
**LOC estimadas**: 400
#### 5.2 Canvas/Drawing Primitives
```zig
pub const Canvas = struct {
fb: *Framebuffer,
clip: Rect,
transform: Transform,
// Primitivas básicas
pub fn drawLine(self: *Canvas, x1: i32, y1: i32, x2: i32, y2: i32, color: Color, thickness: u8) void;
pub fn drawRect(self: *Canvas, rect: Rect, color: Color) void;
pub fn drawRectOutline(self: *Canvas, rect: Rect, color: Color, thickness: u8) void;
pub fn drawRoundedRect(self: *Canvas, rect: Rect, radius: u8, color: Color) void;
pub fn drawCircle(self: *Canvas, cx: i32, cy: i32, radius: u16, color: Color) void;
pub fn drawCircleOutline(self: *Canvas, cx: i32, cy: i32, radius: u16, color: Color, thickness: u8) void;
pub fn drawArc(self: *Canvas, cx: i32, cy: i32, radius: u16, start_angle: f32, end_angle: f32, color: Color) void;
pub fn drawPolygon(self: *Canvas, points: []const Point, color: Color) void;
// Path-based drawing
pub fn beginPath(self: *Canvas) void;
pub fn moveTo(self: *Canvas, x: i32, y: i32) void;
pub fn lineTo(self: *Canvas, x: i32, y: i32) void;
pub fn quadraticTo(self: *Canvas, cx: i32, cy: i32, x: i32, y: i32) void;
pub fn closePath(self: *Canvas) void;
pub fn fill(self: *Canvas, color: Color) void;
pub fn stroke(self: *Canvas, color: Color, thickness: u8) void;
};
```
**LOC estimadas**: 500
#### 5.3 Charts (básicos)
```zig
pub const ChartType = enum { line, bar, pie, donut };
pub const ChartData = struct {
labels: []const []const u8,
series: []const Series,
};
pub const Series = struct {
name: []const u8,
values: []const f64,
color: Color,
};
pub const ChartConfig = struct {
chart_type: ChartType,
show_legend: bool = true,
show_grid: bool = true,
show_values: bool = false,
animated: bool = true,
};
pub fn chart(ctx: *Context, bounds: Rect, data: ChartData, config: ChartConfig) void;
pub fn lineChart(ctx: *Context, bounds: Rect, data: ChartData) void;
pub fn barChart(ctx: *Context, bounds: Rect, data: ChartData) void;
pub fn pieChart(ctx: *Context, bounds: Rect, data: ChartData) void;
```
**LOC estimadas**: 400
**Entregables Fase 5:**
- [ ] 50+ iconos built-in
- [ ] Canvas con primitivas
- [ ] Rounded rectangles
- [ ] Charts básicos (line, bar, pie)
- [ ] Documentación de iconos
- [ ] Demo de gráficos
---
### FASE 6: INPUT AVANZADO
**Duración estimada**: 1 semana
**Objetivo**: Clipboard, drag & drop, gestures
#### 6.1 Clipboard Integration
```zig
pub const Clipboard = struct {
pub fn getText(allocator: Allocator) !?[]u8;
pub fn setText(text: []const u8) !void;
pub fn hasText() bool;
pub fn clear() void;
// Formatos adicionales (futuro)
pub fn getImage() ?ImageData;
pub fn setImage(data: ImageData) !void;
};
```
**LOC estimadas**: 150 (SDL2 clipboard)
#### 6.2 Drag & Drop System
```zig
pub const DragData = struct {
source_id: u64,
data_type: []const u8,
data: ?*anyopaque,
preview: ?DragPreview,
};
pub const DropZone = struct {
accepts: []const []const u8, // Tipos aceptados
highlight_on_hover: bool,
};
pub const DragDropManager = struct {
current_drag: ?DragData,
drop_zones: std.ArrayList(DropZone),
pub fn startDrag(self: *DragDropManager, data: DragData) void;
pub fn endDrag(self: *DragDropManager) ?DragData;
pub fn isDragging(self: DragDropManager) bool;
pub fn registerDropZone(self: *DragDropManager, bounds: Rect, zone: DropZone) void;
pub fn getHoveredDropZone(self: DragDropManager) ?*DropZone;
};
pub fn draggable(ctx: *Context, bounds: Rect, id: u64, data_type: []const u8) bool;
pub fn dropZone(ctx: *Context, bounds: Rect, accepts: []const []const u8) ?DragData;
```
**LOC estimadas**: 300
#### 6.3 Keyboard Shortcuts System
```zig
pub const Shortcut = struct {
key: Key,
modifiers: KeyModifiers,
action: []const u8,
};
pub const ShortcutManager = struct {
shortcuts: std.ArrayList(Shortcut),
pub fn register(self: *ShortcutManager, shortcut: Shortcut) void;
pub fn unregister(self: *ShortcutManager, action: []const u8) void;
pub fn check(self: *ShortcutManager, input: *InputState) ?[]const u8;
pub fn getShortcutText(shortcut: Shortcut) []const u8; // "Ctrl+S"
};
```
**LOC estimadas**: 150
#### 6.4 Focus Groups
```zig
pub const FocusGroup = struct {
id: u64,
widgets: [MAX_GROUP_SIZE]u64,
count: usize,
wrap: bool,
pub fn add(self: *FocusGroup, widget_id: u64) void;
pub fn focusNext(self: *FocusGroup, current: u64) ?u64;
pub fn focusPrev(self: *FocusGroup, current: u64) ?u64;
};
pub const FocusManagerEx = struct {
current_focus: ?u64,
groups: std.AutoHashMap(u64, FocusGroup),
pub fn createGroup(self: *FocusManagerEx) u64;
pub fn addToGroup(self: *FocusManagerEx, group_id: u64, widget_id: u64) void;
pub fn handleNavigation(self: *FocusManagerEx, input: *InputState) void;
};
```
**LOC estimadas**: 200
**Entregables Fase 6:**
- [ ] Clipboard texto funcionando
- [ ] Drag & drop básico
- [ ] Sistema de shortcuts
- [ ] Focus groups
- [ ] Tests de input
- [ ] Demo de drag & drop
---
### FASE 7: RENDERIZADO AVANZADO
**Duración estimada**: 2 semanas
**Objetivo**: Anti-aliasing, efectos visuales, rendimiento
#### 7.1 Anti-aliasing
```zig
pub const AAMode = enum {
none, // Sin AA
fast, // 2x supersampling
quality, // 4x supersampling
};
pub const AARenderer = struct {
mode: AAMode,
pub fn drawLineAA(self: *AARenderer, fb: *Framebuffer, x1: f32, y1: f32, x2: f32, y2: f32, color: Color) void;
pub fn drawCircleAA(self: *AARenderer, fb: *Framebuffer, cx: f32, cy: f32, radius: f32, color: Color) void;
};
```
**LOC estimadas**: 300
#### 7.2 Efectos Visuales
```zig
pub const Effect = union(enum) {
shadow: ShadowEffect,
blur: BlurEffect,
gradient: GradientEffect,
opacity: f32,
};
pub const ShadowEffect = struct {
offset_x: i8,
offset_y: i8,
blur_radius: u8,
color: Color,
};
pub const GradientEffect = struct {
start_color: Color,
end_color: Color,
direction: GradientDirection,
};
pub fn applyShadow(fb: *Framebuffer, rect: Rect, shadow: ShadowEffect) void;
pub fn applyGradient(fb: *Framebuffer, rect: Rect, gradient: GradientEffect) void;
pub fn applyBlur(fb: *Framebuffer, rect: Rect, radius: u8) void;
```
**LOC estimadas**: 400
#### 7.3 Animaciones
```zig
pub const EasingFn = *const fn(f32) f32;
pub const Easing = struct {
pub fn linear(t: f32) f32;
pub fn easeInQuad(t: f32) f32;
pub fn easeOutQuad(t: f32) f32;
pub fn easeInOutQuad(t: f32) f32;
pub fn easeInCubic(t: f32) f32;
pub fn easeOutCubic(t: f32) f32;
pub fn easeInOutCubic(t: f32) f32;
pub fn easeInElastic(t: f32) f32;
pub fn easeOutElastic(t: f32) f32;
pub fn easeInBounce(t: f32) f32;
pub fn easeOutBounce(t: f32) f32;
};
pub const Animation = struct {
start_value: f32,
end_value: f32,
duration_ms: u32,
easing: EasingFn,
start_time: i64,
pub fn getValue(self: Animation, current_time: i64) f32;
pub fn isComplete(self: Animation, current_time: i64) bool;
};
pub const AnimationManager = struct {
animations: std.AutoHashMap(u64, Animation),
pub fn start(self: *AnimationManager, id: u64, anim: Animation) void;
pub fn stop(self: *AnimationManager, id: u64) void;
pub fn getValue(self: *AnimationManager, id: u64) ?f32;
pub fn update(self: *AnimationManager, current_time: i64) void;
};
```
**LOC estimadas**: 300
#### 7.4 Virtual Scrolling
```zig
pub const VirtualScroller = struct {
total_items: usize,
item_height: u16,
viewport_height: u32,
scroll_offset: i32,
pub fn getVisibleRange(self: VirtualScroller) struct { start: usize, end: usize };
pub fn getItemY(self: VirtualScroller, index: usize) i32;
pub fn scrollToItem(self: *VirtualScroller, index: usize) void;
pub fn handleScroll(self: *VirtualScroller, delta: i32) void;
};
```
**LOC estimadas**: 150
#### 7.5 GPU Renderer (Opcional)
```zig
pub const GpuBackend = enum { opengl, vulkan, metal, d3d11 };
pub const GpuRenderer = struct {
backend: GpuBackend,
pub fn init(backend: GpuBackend) !GpuRenderer;
pub fn deinit(self: *GpuRenderer) void;
pub fn beginFrame(self: *GpuRenderer) void;
pub fn endFrame(self: *GpuRenderer) void;
pub fn execute(self: *GpuRenderer, commands: []const DrawCommand) void;
};
```
**LOC estimadas**: 800 (solo OpenGL básico)
**Entregables Fase 7:**
- [ ] Anti-aliasing para líneas y círculos
- [ ] Sombras (drop shadow)
- [ ] Gradientes lineales
- [ ] Sistema de animaciones con easing
- [ ] Virtual scrolling para listas
- [ ] GPU renderer básico (opcional)
- [ ] Benchmarks de rendimiento
---
### FASE 8: ACCESIBILIDAD Y TESTING
**Duración estimada**: 1 semana
**Objetivo**: Accesibilidad, testing automatizado, documentación
#### 8.1 Accesibilidad
```zig
pub const AccessibilityRole = enum {
button,
checkbox,
radio,
slider,
textbox,
listbox,
tree,
menu,
dialog,
// ...
};
pub const AccessibilityInfo = struct {
role: AccessibilityRole,
label: []const u8,
description: ?[]const u8,
value: ?[]const u8,
state: AccessibilityState,
};
pub const AccessibilityState = packed struct {
disabled: bool = false,
expanded: bool = false,
selected: bool = false,
checked: bool = false,
focused: bool = false,
};
// Cada widget expone su info de accesibilidad
pub fn getAccessibilityInfo(widget_id: u64) ?AccessibilityInfo;
```
**LOC estimadas**: 200
#### 8.2 Testing Framework
```zig
pub const TestRunner = struct {
ctx: *Context,
input: *InputState,
pub fn click(self: *TestRunner, x: i32, y: i32) void;
pub fn typeText(self: *TestRunner, text: []const u8) void;
pub fn pressKey(self: *TestRunner, key: Key, modifiers: KeyModifiers) void;
pub fn waitFrames(self: *TestRunner, count: u32) void;
pub fn findWidget(self: *TestRunner, label: []const u8) ?WidgetInfo;
pub fn assertVisible(self: *TestRunner, label: []const u8) !void;
pub fn assertText(self: *TestRunner, label: []const u8, expected: []const u8) !void;
pub fn assertEnabled(self: *TestRunner, label: []const u8) !void;
};
pub const SnapshotTest = struct {
pub fn capture(fb: *Framebuffer, name: []const u8) !void;
pub fn compare(fb: *Framebuffer, name: []const u8) !bool;
};
```
**LOC estimadas**: 400
#### 8.3 Documentación
- API Reference generada automáticamente
- Ejemplos para cada widget
- Tutorial de inicio rápido
- Guía de arquitectura
- Guía de contribución
**Entregables Fase 8:**
- [ ] Roles de accesibilidad para todos los widgets
- [ ] Framework de testing
- [ ] Snapshot testing
- [ ] Documentación completa
- [ ] 10+ ejemplos funcionando
---
### FASE 9: INTEGRACIÓN Y POLISH
**Duración estimada**: 1 semana
**Objetivo**: Integración final, pulido, release
#### 9.1 Integración Completa
- Verificar que todos los widgets funcionan juntos
- Verificar rendimiento con UI compleja
- Verificar uso de memoria en escenarios reales
- Verificar funcionamiento SSH/X11
#### 9.2 Polish
- Revisar API consistency
- Optimizar hot paths
- Eliminar código muerto
- Reducir allocaciones innecesarias
#### 9.3 Release Preparation
- Versión 1.0.0
- Changelog completo
- Package en zig package manager
- Anuncio
**Entregables Fase 9:**
- [ ] Integración completa verificada
- [ ] Rendimiento optimizado
- [ ] 0 warnings, 0 leaks
- [ ] v1.0.0 release
- [ ] Documentación publicada
---
## 5. DETALLES DE IMPLEMENTACIÓN
### 5.1 Estructura Final de Archivos
```
zcatgui/
├── src/
│ ├── zcatgui.zig # Entry point
│ │
│ ├── core/
│ │ ├── context.zig # Context + Arena
│ │ ├── layout.zig # Constraints
│ │ ├── style.zig # Colors, Themes
│ │ ├── input.zig # Input state
│ │ ├── command.zig # Draw commands
│ │ └── animation.zig # Animation system
│ │
│ ├── widgets/
│ │ ├── basic/
│ │ │ ├── label.zig
│ │ │ ├── button.zig
│ │ │ ├── checkbox.zig
│ │ │ ├── radio.zig
│ │ │ └── slider.zig
│ │ ├── input/
│ │ │ ├── text_input.zig
│ │ │ ├── number_entry.zig
│ │ │ ├── editor.zig # Multiline
│ │ │ ├── select.zig
│ │ │ └── autocomplete.zig
│ │ ├── data/
│ │ │ ├── list.zig
│ │ │ ├── table.zig
│ │ │ ├── tree.zig
│ │ │ └── reorderable.zig
│ │ ├── navigation/
│ │ │ ├── menu.zig
│ │ │ ├── tabs.zig
│ │ │ └── breadcrumb.zig
│ │ ├── container/
│ │ │ ├── panel.zig
│ │ │ ├── split.zig
│ │ │ ├── modal.zig
│ │ │ └── scroll.zig
│ │ ├── feedback/
│ │ │ ├── tooltip.zig
│ │ │ ├── progress.zig
│ │ │ ├── toast.zig
│ │ │ └── spinner.zig
│ │ ├── special/
│ │ │ ├── image.zig
│ │ │ ├── color_picker.zig
│ │ │ ├── date_picker.zig
│ │ │ └── chart.zig
│ │ ├── focus.zig
│ │ └── widgets.zig # Re-exports
│ │
│ ├── panels/
│ │ ├── panel.zig
│ │ ├── composite.zig
│ │ ├── data_manager.zig
│ │ └── panels.zig
│ │
│ ├── render/
│ │ ├── framebuffer.zig
│ │ ├── software.zig
│ │ ├── gpu.zig # Optional GPU
│ │ ├── font.zig
│ │ ├── font_atlas.zig
│ │ ├── ttf.zig
│ │ ├── canvas.zig
│ │ ├── effects.zig
│ │ └── aa.zig # Anti-aliasing
│ │
│ ├── backend/
│ │ ├── backend.zig
│ │ ├── sdl2.zig
│ │ └── clipboard.zig
│ │
│ ├── macro/
│ │ └── macro.zig
│ │
│ ├── icons/
│ │ ├── icons.zig
│ │ └── material.zig
│ │
│ └── utils/
│ ├── pool.zig # Object pools
│ ├── arena.zig # Arena allocator
│ └── virtual_scroll.zig
├── examples/
│ ├── hello.zig
│ ├── widgets_demo.zig
│ ├── table_demo.zig
│ ├── editor_demo.zig
│ ├── tree_demo.zig
│ ├── charts_demo.zig
│ └── full_app_demo.zig
├── tests/
│ ├── core_tests.zig
│ ├── widget_tests.zig
│ ├── render_tests.zig
│ └── integration_tests.zig
├── docs/
│ ├── ARCHITECTURE.md
│ ├── DEVELOPMENT_PLAN.md
│ ├── API_REFERENCE.md
│ ├── TUTORIAL.md
│ └── research/
├── build.zig
├── build.zig.zon
├── CLAUDE.md
└── README.md
```
### 5.2 Estimación Total de LOC
| Módulo | LOC Actual | LOC Final Est. |
|--------|:----------:|:--------------:|
| Core | 1,700 | 2,500 |
| Render | 1,300 | 3,000 |
| Backend | 400 | 600 |
| Macro | 340 | 400 |
| Widgets | 8,000 | 12,000 |
| Panels | 350 | 500 |
| Icons | 0 | 500 |
| Utils | 0 | 500 |
| Tests | 500 | 2,000 |
| **TOTAL** | **12,590** | **~22,000** |
---
## 6. MÉTRICAS DE CALIDAD
### 6.1 Performance Benchmarks
| Operación | Target |
|-----------|--------|
| Widget simple (button) | <50μs |
| Widget complejo (table 100 rows) | <500μs |
| Full frame (50 widgets) | <5ms |
| Text rendering (1000 chars) | <1ms |
| Command execution (1000 cmds) | <2ms |
### 6.2 Memory Benchmarks
| Escenario | Target |
|-----------|--------|
| Startup (empty) | <5MB |
| 100 widgets | <15MB |
| 1000 widgets | <50MB |
| Table 10K rows | <100MB |
### 6.3 Code Quality
| Métrica | Target |
|---------|--------|
| Test coverage | >90% |
| Functions >50 LOC | 0 |
| Cyclomatic complexity | <10 |
| Duplicate code | <5% |
| TODO/FIXME | 0 (at release) |
---
## 7. CHECKLIST FINAL
### Pre-Release Checklist
#### Core
- [ ] Arena allocator implementado
- [ ] Object pooling funcionando
- [ ] Dirty rectangles optimizado
- [ ] 0 memory leaks
#### Widgets (35 total)
- [ ] Label
- [ ] Button
- [ ] Checkbox
- [ ] Radio
- [ ] Slider
- [ ] TextInput
- [ ] NumberEntry
- [ ] Editor (multiline)
- [ ] Select
- [ ] AutoComplete
- [ ] List
- [ ] Table
- [ ] Tree
- [ ] ReorderableList
- [ ] Menu
- [ ] Tabs
- [ ] Breadcrumb
- [ ] Panel
- [ ] Split
- [ ] Modal
- [ ] Scroll
- [ ] Tooltip
- [ ] ProgressBar
- [ ] Toast
- [ ] Spinner
- [ ] Image
- [ ] ColorPicker
- [ ] DatePicker
- [ ] Chart (line)
- [ ] Chart (bar)
- [ ] Chart (pie)
- [ ] Focus
- [ ] Canvas
- [ ] Icon
- [ ] RichText
#### Rendering
- [ ] Font Atlas
- [ ] Anti-aliasing
- [ ] Shadows
- [ ] Gradients
- [ ] Virtual scrolling
- [ ] GPU renderer (opcional)
#### Input
- [ ] Clipboard
- [ ] Drag & Drop
- [ ] Shortcuts system
- [ ] Focus groups
#### Sistema
- [ ] Animations
- [ ] Accessibility info
- [ ] Testing framework
- [ ] Snapshot tests
#### Documentación
- [ ] API Reference
- [ ] Tutorial
- [ ] 10+ ejemplos
- [ ] Changelog
#### Calidad
- [ ] 90%+ test coverage
- [ ] 0 warnings
- [ ] 0 memory leaks
- [ ] Performance targets met
- [ ] Memory targets met
---
## RESUMEN
| Fase | Duración | Widgets | LOC |
|------|:--------:|:-------:|:---:|
| 1. Fundamentos | 1 sem | 0 | +1,500 |
| 2. Feedback | 1 sem | +4 | +750 |
| 3. Especializados | 2 sem | +5 | +1,800 |
| 4. Texto | 2 sem | +2 | +1,650 |
| 5. Iconos/Gráficos | 1 sem | +4 | +1,300 |
| 6. Input | 1 sem | 0 | +800 |
| 7. Render | 2 sem | 0 | +1,950 |
| 8. Testing | 1 sem | 0 | +600 |
| 9. Polish | 1 sem | 0 | +100 |
| **TOTAL** | **12 sem** | **+18** | **+10,450** |
**Resultado final:**
- **35 widgets** (paridad DVUI)
- **~22,000 LOC** (eficiente)
- **Performance óptimo** (60fps, <16ms latencia)
- **Memoria mínima** (<50MB típico)
- **Código perfecto** (0 warnings, 0 leaks, 90%+ coverage)
---
> **Nota**: Este plan prioriza calidad sobre velocidad. Cada fase debe completarse con todos los tests pasando y métricas cumplidas antes de avanzar a la siguiente.