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.
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
hit_behavior del nodo alcanzadosetText / setTextContent? Esas API comparan y marcan sizing / render dirty por sí mismasnode.style directamente no marca nada como dirty; usa node.setStyle(alloc, .width, v), que elige el nivel de dirty correcto para cada campoEl 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 para que nunca tape la UI del producto. El target debe seguir vivo mientras el panel está en uso.
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.
Cuatro vistas, seis pestañas de detalle
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.
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.
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
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:
Debug.debug.debugReleaseSafe.info.infoReleaseFast / ReleaseSmall.warnnull (desactivada)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.
Puntos de entrada de pruebas
zig build test-headless
zig build test-ui
zig build test-render
zig build hello-buttonAntes 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.



