---
title: "DevTools — Docs de zenit Zig UI"
description: "Localiza problemas de layout, hit-testing, renderizado y rendimiento con un overlay de una línea, un panel independiente y una Console estructurada y acotada."
url: https://zenit.z.express/es/docs/advanced/devtools
language: es
alternate_en: https://zenit.z.express/docs/advanced/devtools.md
alternate_zh: https://zenit.z.express/zh/docs/advanced/devtools.md
alternate_ja: https://zenit.z.express/ja/docs/advanced/devtools.md
alternate_ko: https://zenit.z.express/ko/docs/advanced/devtools.md
alternate_fr: https://zenit.z.express/fr/docs/advanced/devtools.md
alternate_de: https://zenit.z.express/de/docs/advanced/devtools.md
project: zenit v0.1.0-alpha (Zig 0.15.2, macOS)
source: https://github.com/version-next/zenit
---

# DevTools

Localiza problemas de layout, hit-testing, renderizado y rendimiento con un overlay de una línea, un panel independiente y una Console estructurada y acotada.

## Inspector en la ventana

Durante el desarrollo, adjunta el overlay a tu raíz. Al pasar el cursor, delimita el rect del nodo con un recuadro discontinuo y muestra su tamaño y el nombre del componente. El overlay es pass-through —no intercepta clics ni scroll— y el hover solo provoca actualizaciones a nivel de pintado, nunca un relayout.

`main.zig`

```zig
fn mountUi(cx: *ui.Cx, scope: *ui.Scope) anyerror!*ui.Node {
    const root = try mountProductUi(cx, scope);

    // Development only: hover highlight with size + component name.
    _ = try ui.devtools.overlay.attach(cx, scope, root, .{});
    return root;
}
```

El estado del overlay vive en el `scope` que pasas; cuando ese Scope se libera, el overlay desaparece con él. Úsalo en la ventana del producto para responder «¿quién ocupa este espacio?»; usa el panel para un diagnóstico completo. Ambos leen el estado real de Node / Cx: no hay ningún modelo espejo que mantener para depurar.

## Diagnóstico por síntoma

| Síntoma | Revisa primero |
| --- | --- |
| Contenido desalineado | Rect, padding, gap y direction del padre (Elements → Layout) |
| Las zonas vacías reciben clics | El tamaño y el `hit_behavior` del nodo alcanzado |
| El texto cambió, la pantalla no | ¿Modificaste campos de TextProps sin llamar a `setText` / `setTextContent`? Esas API comparan y marcan sizing / render dirty por sí mismas |
| El layout ignora un ancho nuevo | Asignar `node.style` directamente no marca nada como dirty; usa `node.setStyle(alloc, .width, v)`, que elige el nivel de dirty correcto para cada campo |
| Se vuelve más lento con el tiempo | Mounts repetidos, Effects que no se liberan con su Scope, asignaciones por frame (Performance → Summary / Rebuild) |

## El panel de DevTools

Para las vistas completas de Elements, Components, Console y Performance, usa `ui.devtools.mountPanel(cx, target, opts)`. El panel tiene su propio Cx y observa otro Cx target; lo habitual es ponerlo en su propia ventana con [MultiWindowApp](https://zenit.z.express/es/docs/advanced/multi-window) para que nunca tape la UI del producto. El target debe seguir vivo mientras el panel está en uso.

`main.zig`

```zig
const std = @import("std");
const ui = @import("ui");
const zenit_app = @import("zenit_app");

// mountProductUi: your app's ordinary mount function (see above).
var g_target_cx: ?*ui.Cx = null;

fn mountDevTools(cx: *ui.Cx, scope: *ui.Scope) anyerror!*ui.Node {
    _ = scope;
    const target = g_target_cx orelse return error.TargetNotReady;
    return ui.devtools.mountPanel(cx, target, .{ .title = "My App DevTools" });
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    var application = zenit_app.MultiWindowApp.init(gpa.allocator(), .{});
    defer application.deinit();

    const product = try application.createWindowWith(.{
        .window = .{ .width = 900, .height = 640, .title = "My App" },
    }, mountProductUi);
    g_target_cx = product.cx;

    const tools = try application.createWindowWith(.{
        .window = .{ .width = 760, .height = 560, .title = "DevTools" },
    }, mountDevTools);
    _ = ui.devtools.setViewMode(tools.cx, "performance"); // optional start tab

    try application.run();
}
```

Así está estructurado `zig build devtools-probe` en el repo. `ui.devtools` también expone `setViewMode`, `setTreeFilter`, `setConsoleFilter` y otras funciones para que los probes y las pruebas E2E controlen el panel por código.

[![Zenit DevTools Elements panel with a box selected and its layout details](https://zenit.z.express/media/devtools-elements.png?v=1c99ec3edc)](https://zenit.z.express/media/devtools-elements.png?v=1c99ec3edc)

A la izquierda, el árbol de UI en vivo; selecciona un nodo y la pestaña Layout de la derecha muestra su rect calculado, padding, sizing y propiedades flex. El cursor de mano de la captura es el cursor virtual del harness.

### Cuatro vistas, seis pestañas de detalle

| Vista | Responde |
| --- | --- |
| **Elements** | ¿Cuál es la jerarquía real de nodos: tag / id / text, layout y área de hit? |
| **Components** | ¿Qué nodos forman los límites de componente y dónde están su estado y su Scope propietario? |
| **Console** | ¿Qué logs estructurados capturó el Cx target y se desalojó o descartó algo? |
| **Performance** | ¿El target está renderizando o inactivo? ¿Cuánto cuestan layout / render / cache / interaction? |

Con una fila de Elements / Components seleccionada, alterna entre **Layout, Style, State, Events, Render y Trace**. Algunos valores de Style se pueden editar en vivo; Render / Trace muestran por qué un nodo quedó dirty y sus eventos recientes. La búsqueda en el árbol coincide con tags, `#id` y nombres de componente. Con `ui.devtools.source_link` configurado, las filas de componente pueden saltar a su definición en tu editor.

## Registro con Console

Cada `ui.Cx` tiene su propia Console thread-safe y acotada. Una sola llamada puede imprimir en la terminal y guardar un evento estructurado para DevTools y el harness E2E; el historial capturado antes de abrir el panel también aparece.

`logging.zig`

```zig
const log = cx.console();

log.info("application ready", .{});
log.scoped("network").warn("retry {d}", .{attempt});

// Plain level methods don't record a call site; writeAt does.
log.writeAt(.err, @src(), "save failed: {s}", .{@errorName(err)});
```

Los niveles son `debug`, `log`, `info`, `warn` y `err`. También están los equivalentes de la consola del navegador: `group` / `groupEnd`, `count`, `time` / `timeEnd`, `assert`, `trace`, `inspect` y `table`.

| Zona del panel | Uso |
| --- | --- |
| **Clear** | Vacía el propio almacén de Console del target, no solo la vista. |
| **Filter** | Coincidencia sin distinguir mayúsculas en message y scope; se combina con el filtro de nivel. |
| **Levels** | Activa Debug / Log / Info / Warn / Error en cualquier combinación. |
| **Lista de logs** | Nivel, scope, mensaje, sangría de group y ubicación opcional en el código; el seguimiento automático se pausa al desplazarte fuera del final. |
| **Barra de estado** | shown / captured / evicted / dropped: distingue filtrado, desalojo y pérdida. |

[![Zenit DevTools Console with debug, info, warn and error entries](https://zenit.z.express/media/devtools-console.png?v=1411f611cd)](https://zenit.z.express/media/devtools-console.png?v=1411f611cd)

Los logs llegaron al almacén acotado del Cx target antes de abrir el panel; se conservan el scope, el nivel y la ubicación en el código.

[![Zenit DevTools Console filtered to the network scope](https://zenit.z.express/media/devtools-console-filter.png?v=acfa3d9446)](https://zenit.z.express/media/devtools-console-filter.png?v=acfa3d9446)

Filter y Levels se combinan localmente en DevTools; no se elimina nada del almacén del target.

[Video](https://zenit.z.express/media/devtools-console.mp4?v=05e8c163e3)

Una app real de dos ventanas: el cursor virtual pasa de Elements a Console, enfoca Filter y escribe network. Grabado directamente desde el Metal drawable de la ventana de DevTools.

Los métodos de nivel simples no registran el punto de llamada. Para hacer clic en una línea de log y abrir tu editor, usa `writeAt(level, @src(), …)` y define la raíz del código y el comando del editor con `ui.devtools.source_link.configure` (por defecto: `code --goto`).

### Configurar la captura

`main.zig`

```zig
const app = try zenit_app.App.init(allocator, .{
    .console = .{
        .terminal_level = .info, // null disables the terminal sink
        .capture_level = .debug, // null disables in-memory capture
        .max_entries = 10_000,
        .max_bytes = 8 * 1024 * 1024,
        .max_entry_bytes = 64 * 1024,
    },
});
```

Sin un `console` explícito, `zenit_app` elige valores por defecto según el modo de build:

| Modo de build | Terminal | Captura |
| --- | --- | --- |
| `Debug` | `.debug` | `.debug` |
| `ReleaseSafe` | `.info` | `.info` |
| `ReleaseFast / ReleaseSmall` | `.warn` | `null` (desactivada) |

> WARNING
> 
> **Los builds Release no capturan por defecto.** En ReleaseFast / ReleaseSmall, DevTools no ve historial. Si un build de producción lo necesita, define `capture_level` explícitamente y deja tokens, contraseñas y datos personales fuera de tus logs.

La Console replica la parte de captura y visualización de la consola del navegador; no es un REPL de Zig / JavaScript. Por ahora los groups se sangran pero no se pueden plegar de forma interactiva, y `table` se muestra como texto. La API completa, las reglas de hilos y ciclo de vida y los ejemplos del harness están en `docs/CONSOLE.md` del repo.

## Performance: inactivo frente a bloqueado

La vista Performance no se congela en «los últimos N frames renderizados». Sigue observando el target en buckets de 100 ms de tiempo real —64 en total, una ventana móvil de unos 6.4 s— y calcula los FPS como la media de los últimos 10 buckets (aprox. 1 s). Cuando el target no produce ningún frame durante más de 0.7 s, la lectura indica `FPS: 0 — idle (not rendering)`: el framework omite frames a propósito para ahorrar energía, no está atascado a 0 FPS.

100MS BUCKETS

Cuando se detienen los frames, el gráfico sigue avanzando y registra ceros; la lectura baja a medida que llegan buckets vacíos y pasa a idle tras 0.7 s: nunca hace pasar un valor antiguo por actual.

[![Zenit DevTools Performance showing FPS 0 idle (not rendering) with timing metrics](https://zenit.z.express/media/devtools-performance.png?v=41f18a12a0)](https://zenit.z.express/media/devtools-performance.png?v=41f18a12a0)

El target estático dejó de renderizar; DevTools se refresca a unos 10 Hz para que el gráfico siga avanzando, y las métricas de layout, render, cache, focus e interaction siguen siendo legibles.

| Lectura | Significado |
| --- | --- |
| FPS + gráfico móvil | Buckets de tiempo real; un bucket sin frames registra 0 y se dibuja como línea base neutra, nunca como un valor antiguo |
| Timing | Tiempo real de CPU del último frame del target: layout, generación de comandos de render, etc. |
| Summary / Interaction | Hit registry, mouse-hit, redraw streak, focus y rebuilds de interaction |
| Cache / Rebuild | Aciertos / fallos de la retained cache, rebuilds full / partial |

## Puntos de entrada de pruebas

```sh
zig build test-headless
zig build test-ui
zig build test-render
zig build hello-button
```

> WARNING
> 
> **No ejecutes zig test sobre archivos internos.** Los módulos de zenit se importan entre directorios, así que usa los steps de prueba definidos en `build.zig`; de lo contrario puedes toparte con `import of file outside module path`. La lista completa está en [Comandos](https://zenit.z.express/es/docs/reference/commands); para pruebas en ventana real, consulta [Harness E2E](https://zenit.z.express/es/docs/advanced/e2e).

## Antes de publicar

-   **Desactiva la UI de depuración.** Quita el overlay del inspector o móntalo solo con una configuración de debug.
    
-   **Verifica la entrada en una ventana real.** Teclado, IME, portapapeles y VoiceOver.
    
-   **Ejecuta las compuertas de build.** Los steps de prueba headless, UI y render.
    
-   **Revisa la política de Console.** El nivel de terminal y la captura en Release son los que pretendes, y los logs no contienen datos sensibles.
    
-   **Fija las versiones.** Bloquea la revisión de zenit y anota la versión de Zig objetivo.
