Implements "Active Request" pattern for cursor animation:
- Add requested_cursor_blink flag to Context (reset each frame)
- Add requestCursorBlink() function for widgets to call
- needsCursorAnimation() now returns false if no widget requested it
- TextInput calls requestCursorBlink() when focused and editable
- CellEditor calls requestCursorBlink() when editing
This eliminates unnecessary redraws when no cursor is visible
(e.g., in Config tab with tables but no active text input).
Also raised CURSOR_BLINK_PERIOD_MS from 300ms to 600ms (GTK/Linux standard).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
GTK/Linux standard rate. Reduces unnecessary redraws by ~50%.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Añade derived_bg al resultado de drawPanelFrame para que los paneles
puedan usar el color exacto del fondo para las tablas (mimetismo visual).
Las tablas deben usar frame_result.derived_bg como row_normal para
integrarse visualmente con el panel.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Nueva API BevelStyle para control fino de efectos 3D:
- effect: raised (elevado) o sunken (hundido)
- inset: 0 (exterior) o 1 (interior)
- light_top, light_left: intensidad de luz por borde
- dark_bottom, dark_right: intensidad de sombra por borde
- use_hsl: HSL (true) o RGB (false)
Presets incluidos:
- BevelStyle.raised_inset (default, botones)
- BevelStyle.sunken_inset (botones presionados)
- BevelStyle.raised_outer (estilo Windows 95)
- BevelStyle.sunken_outer (estilo Windows 95)
API:
- ctx.drawBevel(x, y, w, h, color, style) - nueva función principal
- ctx.drawBeveledRect() - wrapper compatibilidad (usa raised_inset)
- ctx.drawBeveledRectPressed() - wrapper compatibilidad (usa sunken_inset)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- FrameStats: nuevo campo executed_cmds para comandos realmente ejecutados
- SoftwareRenderer: contador que se incrementa en execute()
- Métodos resetExecutedCount() y getExecutedCount()
- CLAUDE.md: sección OPTIMIZACIONES DE RENDIMIENTO documentada
Permite comparar Total (generados) vs Exec (ejecutados) para
diagnosticar efectividad del dirty regions filtering.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- fillRect: disable runtime safety for hot path (bounds pre-validated)
- drawGlyphBitmap: disable runtime safety for hot path
- Context: add burst detection (isSelectionBurstActive, markNavigationEvent)
- PanelFrameConfig: add burst_sensitive field
- PanelFrameResult: new struct for frame drawing results
Note: burst_sensitive mechanism available but not actively used
(causes visual flickering when suppressing panel content)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Nuevo flag para "Dibujo Fantasma":
- suppress_commands: bool en Context
- pushCommand() y pushOverlayCommand() retornan si está activo
- Permite procesar input (draw) sin generar comandos (ahorra CPU)
Solución de Gemini para immediate-mode + dirty panels
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Añade el sistema de dirty panels para optimización de rendering:
- panel_areas: HashMap para registrar rectángulos de paneles
- dirty_panels: HashMap con flags de invalidación por panel
- registerPanelArea(id, rect): Registrar panel con nombre
- invalidatePanel(id): Marcar panel como dirty
- isPanelDirty(id): Consultar si necesita redraw
- markAllPanelsDirty(): Para resize/tab change
- getDirtyPanelRects(): Obtener rects dirty para renderer
- clearDirtyPanels(): Limpiar flags (automático en endFrame)
Diseño:
- La aplicación registra paneles al inicio
- La aplicación notifica cambios via invalidatePanel()
- El renderer usa getDirtyPanelRects() para limpieza selectiva
- Los flags se limpian automáticamente en endFrame()
Parte de la implementación de Dirty Panels Fase 1.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- drawBeveledRect: bordes ahora en x+1,y+1 (interior del rect)
- drawBeveledRectPressed: mismo fix para estado presionado
- inner_w/inner_h calculados como w-2/h-2 para correcta insetación
- Fix identificado en verificación MASTER_RESCATE punto C
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Título: x+28 para dejar espacio al semáforo (antes x+10)
- Semáforo: "Viendo ■" alineado a la derecha (antes ■ izquierda, texto derecha)
- Botones: visual_adjust +2px para compensar baseline TTF
- contrastTextColor como fallback para títulos sin base_color
Layout final: "[N] Título Estado ■"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix legibilidad títulos:
- title_color: 85% soft_white + 15% tinte del base_color
- Antes: base.lightenHsl(90) → azul claro sobre azul oscuro (ilegible)
- Ahora: blanco con tinte sutil → máximo contraste + identidad visual
Fix centrado vertical botones:
- Añadido char_height al Context (default 14px para TTF)
- button.zig: usa char_height en vez de char_width
- Offset visual -1px para compensar efecto 3D del bisel
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Z-Design V5 - Títulos Adaptativos:
- derivePanelFrameColors: title_color según luminosidad del fondo
- Fondo oscuro (L < 0.5) → base.lightenHsl(90) (blanco teñido)
- Fondo claro → base.darkenHsl(90) (negro teñido)
- drawPanelFrame: siempre usa title_color (no border_unfocus)
- Márgenes título mejorados: x+10, y+5
Resultado: máximo contraste y legibilidad en todos los paneles.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add PanelFrameConfig struct with has_focus, focus_bg, unfocus_bg, border_color options
- Add drawPanelFrame() method that encapsulates common panel rendering pattern:
1. Update ColorTransition and request animation frame if needed
2. Draw shadow when focused
3. Draw beveled background
4. Draw border outline
- Export ColorTransition from context module
- Reduces ~15 lines of repeated code per panel to single function call
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- ColorTransition: 200ms → 500ms (transiciones más perceptibles)
- deriveDarkPalette: 4%/20% base color (mayor contraste focus/unfocus)
- deriveLightPalette: 1%/6% base color (proporcional)
- Context: añadir requestAnimationFrame/needsAnimationFrame
- Tests actualizados para nuevos umbrales
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Helpers para dibujar rectángulos con efecto 3D:
- drawBeveledRect: luz desde arriba-izquierda (normal)
- drawBeveledRectPressed: bisel invertido (presionado)
Usa lightenHsl(10) y darkenHsl(15) para bordes.
Parte del plan 'Acabado Espectacular'.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Tab/Shift+Tab with wrap navigation in editing mode
- tab_out/tab_shift result fields for application handling
- Cursor position, blinking, background color fixes
- markRowSaved() to clear dirty state after save
- last_edited_row only set when actual changes made
OVERLAY SYSTEM:
- Context: nuevo campo overlay_commands para capa superior
- Context: nueva función pushOverlayCommand()
- mainloop: renderiza overlay_commands DESPUÉS de commands
- autocomplete: dropdown usa overlay (se dibuja encima de otros widgets)
MEJORAS AUTOCOMPLETE:
- Dropdown se abre inmediatamente al escribir (sin delay de 1 frame)
- Dropdown se abre al borrar con backspace/delete
- Click en flecha del dropdown hace toggle abrir/cerrar
- Área clicable de flecha: 20px
ESTADO: En progreso - funcionalidad básica operativa, pendiente:
- Keyboard handlers completos
- Más testing de casos edge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Table clipping:
- Add clip region around table content area
- Prevents cell text from drawing outside table bounds
- Header and scrollbar render outside clip region
Cursor animation API:
- Add CURSOR_IDLE_TIMEOUT_MS (5s) and CURSOR_BLINK_PERIOD_MS (500ms) constants
- Add needsCursorAnimation() to check if cursor should blink
- Add getAnimationTimeout() for dynamic event loop timeout
- Update TextInput to use constants from Context
The application can now query ctx.getAnimationTimeout() to determine
if a short timeout is needed for cursor animation, or if it can wait
indefinitely for events.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 1: Frame timing in Context
- Added current_time_ms and frame_delta_ms to Context
- Added setFrameTime() method for applications to provide timing
Phase 2: Centralized shortcuts system
- Added StandardShortcut enum with common shortcuts (copy, paste, etc.)
- Added isStandardActive() function for checking shortcuts
- Updated TextInput to use centralized shortcuts
Phase 3: Incremental search in table
- Added search_buffer, search_len, search_last_time to TableState
- Added addSearchChar(), getSearchTerm(), clearSearch() methods
- Typing in focused table searches first column (case-insensitive)
- 1 second timeout resets search buffer
Phase 4: Blinking cursor in TextInput
- Cursor blinks every 500ms when field is focused
- Uses current_time_ms from Context for timing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Cambios principales:
- Nuevo FocusSystem unificado en core/focus.zig
- Separación registration_group / active_group para multi-panel
- Focus implícito para primer widget del grupo activo
- Table inicializa selected_row/col a 0 cuando tiene datos
- Corregido test navKeyPressed (usaba setKeyState en vez de handleKeyEvent)
Bug resuelto: tabla no respondía a teclado sin clic previo
Causa: selected_col quedaba en -1, selectedCell() retornaba null
Documentación: docs/FOCUS_TRANSITION_2025-12-11.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PROBLEMA DETECTADO:
- Existían dos sistemas de focus paralelos que no se comunicaban
- FocusManager (widgets/focus.zig) usaba IDs u32
- FocusGroupManager (core/focus_group.zig) usaba IDs u64
- Esto causaba que Tab no funcionara y clics no cambiaran focus
SOLUCIÓN CONSENSUADA:
- Usar SOLO FocusGroupManager como fuente de verdad
- Integrar en Context con métodos públicos
- Widgets se auto-registran en el grupo activo al dibujarse
- Tab navega DENTRO del grupo activo
- F6 (u otro) cambia entre grupos/paneles
CAMBIOS:
- context.zig: Añadidos createFocusGroup(), setActiveFocusGroup(),
hasFocus(), requestFocus(), registerFocusable(), handleTabKey()
- text_input.zig: Usa @intFromPtr para ID único, se auto-registra
- table.zig: Ahora se registra como widget focusable
- widgets.zig/zcatgui.zig: Eliminadas referencias antiguas a FocusManager
- CLAUDE.md: Documentado el trabajo en progreso
ESTADO: EN PROGRESO - Compila pero requiere más testing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>