6.7 KiB
6.7 KiB
Refactorización Modular 2025-12-29
Objetivo: Reducir archivos monolíticos (>700 LOC) a estructuras modulares mantenibles.
Resumen Ejecutivo
| Módulo | Antes | Hub | Reducción |
|---|---|---|---|
| autocomplete | 910 LOC | 571 LOC | -37% |
| icon | 805 LOC | 515 LOC | -36% |
| textarea | Ya modular | 404 LOC | - |
| progress | Ya modular | ~300 LOC | - |
Archivos residuales eliminados: textarea.zig, progress.zig (duplicados de carpetas existentes)
Patrón de Modularización
Estructura Estándar
widget/
├── widget.zig # Hub: API pública, re-exports, lógica principal
├── types.zig # Tipos, enums, Config, Colors, Result
├── state.zig # Estado del widget (si es stateful)
└── helpers.zig # Funciones auxiliares específicas
Principios
- Hub Mínimo: El archivo principal solo contiene API pública y lógica core
- Tipos Separados: Todos los
struct,enum,Config,Colorsvan atypes.zig - Estado Aislado: Estado mutable con sus métodos en
state.zig - Helpers Específicos: Funciones de dibujo, filtrado, etc. en su propio archivo
- Re-exports: El hub re-exporta todo para compatibilidad con código existente
autocomplete/ (910 → 571 LOC, -37%)
Estructura
src/widgets/autocomplete/
├── autocomplete.zig (571 LOC) - Hub con API y lógica principal
├── state.zig (200 LOC) - AutoCompleteState + buffer management
├── types.zig (89 LOC) - MatchMode, Config, Colors, Result
└── filtering.zig (106 LOC) - matchesFilter, matchesPrefix, matchesFuzzy
Archivos Modificados
widgets.zig: Import actualizado aautocomplete/autocomplete.zig
Detalles de Extracción
state.zig:
pub const AutoCompleteState = struct {
buffer: [256]u8 = [_]u8{0} ** 256,
cursor: usize = 0,
// ... métodos de gestión de buffer
};
types.zig:
pub const MatchMode = enum { prefix, contains, fuzzy };
pub const AutoCompleteConfig = struct { ... };
pub const AutoCompleteColors = struct { ... };
pub const AutoCompleteResult = struct { ... };
filtering.zig:
pub fn matchesFilter(text: []const u8, filter: []const u8, mode: MatchMode) bool;
pub fn matchesPrefix(text: []const u8, prefix: []const u8) bool;
pub fn matchesContains(text: []const u8, needle: []const u8) bool;
pub fn matchesFuzzy(text: []const u8, pattern: []const u8) bool;
icon/ (805 → 515 LOC, -36%)
Estructura
src/widgets/icon/
├── icon.zig (515 LOC) - Hub con API y switch de ~50 iconos
├── types.zig (146 LOC) - Size, IconType, Config, Colors
└── drawing_helpers.zig (93 LOC) - drawLine, fillCircle, strokeCircle
Archivos Modificados
widgets.zig: Import actualizado aicon/icon.zigiconbutton.zig: Import actualizadoappbar.zig: Import actualizadonavdrawer.zig: Import actualizado
Detalles de Extracción
types.zig:
pub const Size = enum {
small, // 12x12
medium, // 16x16
large, // 24x24
xlarge, // 32x32
pub fn pixels(self: Size) u32 { ... }
};
pub const IconType = enum {
// Navigation (~12)
arrow_up, arrow_down, arrow_left, arrow_right,
chevron_up, chevron_down, chevron_left, chevron_right,
home, menu, more_horizontal, more_vertical,
// Actions (~16)
check, close, plus, minus, edit, delete, refresh,
search, settings, filter, sort, copy, paste, cut, undo, redo,
// Files (~8)
file, folder, folder_open, document, image_file,
download, upload, save,
// Status (~9)
info, warning, error_icon, success, question,
star, star_filled, heart, heart_filled,
// UI elements (~10)
eye, eye_off, lock, unlock, user, users,
calendar, clock, bell, mail,
// Media (~5)
play, pause, stop, volume, volume_off,
// Misc (~8)
grip, drag, expand, collapse, maximize, minimize, external_link,
};
pub const Config = struct {
size: Size = .medium,
custom_size: ?u32 = null,
stroke_width: u32 = 2,
filled: bool = false,
};
pub const Colors = struct {
foreground: Style.Color = Style.Color.rgba(220, 220, 220, 255),
background: ?Style.Color = null,
pub fn fromTheme(theme: Style.Theme) Colors { ... }
};
drawing_helpers.zig:
/// Dibuja línea con grosor variable
pub fn drawLine(ctx: *Context, x1: i32, y1: i32, x2: i32, y2: i32,
thickness: u32, color: Style.Color) void;
/// Rellena círculo
pub fn fillCircle(ctx: *Context, cx: i32, cy: i32, radius: u32, color: Style.Color) void;
/// Dibuja contorno de círculo (Bresenham)
pub fn strokeCircle(ctx: *Context, cx: i32, cy: i32, radius: u32,
thickness: u32, color: Style.Color) void;
fn setPixelThick(ctx: *Context, x: i32, y: i32, thickness: u32, color: Style.Color) void;
Limpieza de Archivos Residuales
Problema Detectado
Existían archivos duplicados:
src/widgets/textarea.zig(26KB) - archivo antiguosrc/widgets/textarea/- carpeta con módulos
Lo mismo con progress.zig / progress/.
Solución
rm src/widgets/textarea.zig
rm src/widgets/progress.zig
Los imports en widgets.zig ya apuntaban a las carpetas, por lo que los archivos sueltos eran código muerto.
Módulos Ya Modulares (No Modificados)
textarea/ (404 LOC hub)
src/widgets/textarea/
├── textarea.zig (404 LOC)
├── state.zig
├── render.zig
└── types.zig
progress/ (~300 LOC hub)
src/widgets/progress/
├── progress.zig
├── bar.zig
├── circle.zig
└── spinner.zig
Beneficios de la Modularización
- Mantenibilidad: Archivos <600 LOC son más fáciles de navegar
- Compilación: Cambios en types.zig no recompilan lógica principal
- Testing: Módulos aislados son más fáciles de testear
- Colaboración: Menos conflictos de merge en archivos grandes
- Documentación: Estructura refleja responsabilidades
Candidatos Futuros
| Archivo | LOC | Prioridad |
|---|---|---|
| font_data.zig | 1157 | Baja (datos estáticos) |
| style.zig | 858 | Media |
| table.zig | ~750 | Media |
Commits
chore: Eliminar archivos residuales textarea.zig y progress.zig
refactor(autocomplete): Modularizar en carpeta (910→571 LOC hub, -37%)
refactor(icon): Modularizar en carpeta (805→515 LOC hub, -36%)
Fecha: 2025-12-29 Autor: Claude (Opus 4.5) + R.Eugenio