4.3k

App & Runtime

App and Runtime are the layer underneath UiApp: the app describes what to run, the runtime owns the event loop, windows, native views, security checks, automation, tracing, and platform services. Most native-rendered apps never touch this layer directly — UiApp and the generated runner wire it for you. Reach for it when you need custom lifecycle callbacks, imperative window and view management, or a WebView-based frontend.

The App struct

FieldTypeDescription
context*anyopaquePointer to your app state (required)
name[]const u8App name used in traces and automation snapshots (required)
sourceWebViewSourceInitial WebView content for compatibility startup windows; defaults to empty HTML
source_fn?fn(*anyopaque) !WebViewSourceDynamic source resolver (overrides source when set)
scene_fn?fn(*anyopaque) !ShellConfigDeclarative native window and view tree; when set, startup uses the scene instead of the compatibility window list
start_fn?fn(*anyopaque, *Runtime) !voidCalled after the runtime starts, before startup scene or source loading
event_fn?fn(*anyopaque, *Runtime, Event) !voidCalled on every runtime event (lifecycle + commands)
stop_fn?fn(*anyopaque, *Runtime) !voidCalled before the runtime shuts down

All callback fields are optional. A minimal app only needs context and name; provide scene_fn when startup should build a declarative native shell, and source or source_fn only when the app has WebView content.

Startup shape

With scene_fn, the runtime materializes the returned ShellConfig as native shell windows and views — this is the native-first path, and the shape UiApp uses through its scene option. The first scene window adopts the startup native window; additional scene windows are created through the window service.

Scene windows and views are kept as resize layout bindings, so return slices backed by static or app-owned storage that lives as long as the window.

const shell_views = [_]native_sdk.ShellView{
    .{ .label = "main-canvas", .kind = .gpu_surface, .fill = true, .gpu_backend = .metal },
    .{ .label = "status", .kind = .statusbar, .edge = .bottom, .height = 28, .text = "Ready" },
};
const shell_windows = [_]native_sdk.ShellWindow{.{
    .label = "main",
    .title = "Acme",
    .width = 1100,
    .height = 760,
    .views = &shell_views,
}};
const shell_scene: native_sdk.ShellConfig = .{ .windows = &shell_windows };

fn scene(context: *anyopaque) anyerror!native_sdk.ShellConfig {
    _ = context;
    return shell_scene;
}

Without scene_fn, the runtime uses the WebView compatibility path: it loads source or source_fn into the configured startup window list.

WebViewSource

For apps that embed web content, three constructors specify what a WebView loads:

  • .html(content) -- inline HTML string, served as zero://inline
  • .url(address) -- load a remote or local URL
  • .assets(options) -- serve a local file tree through a custom origin

The assets constructor takes a WebViewAssetSource:

.source = native_sdk.WebViewSource.assets(.{
    .root_path = "dist",
    .entry = "index.html",      // default
    .origin = "zero://app",     // default
    .spa_fallback = true,       // default
}),
FieldDefaultDescription
root_pathrequiredPath to the directory containing frontend assets
entry"index.html"HTML entry point within the root path
origin"zero://app"Origin used for asset URLs
spa_fallbacktrueServe entry for unknown routes (SPA mode)

See Frontend Projects for the dev-server/bundled-assets workflow.

Lifecycle events

The runtime dispatches LifecycleEvent values through your event_fn:

  • start -- the app runtime has started and startup scene or source loading is about to run
  • activate -- the app became the active foreground app
  • deactivate -- the app resigned active foreground status
  • frame -- a frame has been requested (for animations or state updates)
  • stop -- the app is shutting down

Native file drops dispatch Event.files_dropped to event_fn. Apps with WebView content also receive app:activate, app:deactivate, and drop:files on each trusted window.zero instance:

window.zero.on("drop:files", (event) => {
  console.log(event.paths);
});

The runner pattern

The generated src/runner.zig wires the runtime with platform services:

  1. Selects the platform (macOS, Linux, or null for headless tests)
  2. Sets up trace sinks (stdout + file) via FanoutTraceSink
  3. Installs panic capture so crashes write last-panic.txt
  4. Initializes window state persistence from windows.zon
  5. Creates the Runtime with all options and calls runtime.run(app)
var runtime = native_sdk.Runtime.init(.{
    .platform = my_platform,
    .trace_sink = fanout.sink(),
    .security = .{
        .permissions = &app_permissions,
        .navigation = .{ .allowed_origins = &.{ "zero://app" } },
    },
    .window_state_store = state_store,
    .automation = if (build_options.automation) automation_server else null,
});
try runtime.run(my_app.app());

RuntimeOptions

FieldTypeDefaultDescription
platformPlatformrequiredPlatform abstraction (macOS, Linux, or NullPlatform)
trace_sink?trace.SinknullDestination for structured trace records
log_path?[]const u8nullPath for persistent log file
extensions?ModuleRegistrynullExtension modules with lifecycle hooks
bridge?BridgeDispatchernullApp-defined bridge commands and handlers (WebView apps; see Bridge)
builtin_bridgeBridgePolicy.Policy for built-in bridge commands (dialogs, windows)
securitySecurityPolicy.Navigation allowlist, external links, permissions
automation?automation.ServernullFile-based automation server for testing
window_state_store?window_state.StorenullPersistent window geometry and state
js_window_apiboolfalseExpose built-in window.zero helper namespaces for trusted app chrome. Command helpers require origin and command checks, view helpers require origin and view checks, and window, WebView, and platform helpers require origin and window checks. Dialog, OS, clipboard, and credential helpers still require explicit builtin_bridge policy.

Runtime methods

MethodDescription
init(options) RuntimeCreate a runtime
run(app) !voidEnter the platform event loop
createWindow(options) !WindowInfoOpen a new window
listWindows() []WindowInfoList open windows
focusWindow(id) !voidBring a window to front
closeWindow(id) !voidClose a window
invalidate()Request a redraw
invalidateFor(reason, dirty_region)Request a redraw with reason and optional dirty region
frameDiagnostics() FrameDiagnosticsReturn stats from the last frame
setGpuSurfaceInputLatencyBudget(window_id, label, budget_ns)Set the input-to-frame latency budget reported by GPU surface diagnostics
dispatchEvent(event)Inject a synthetic event
dispatchPlatformEvent(app, event)Forward a platform event
automationSnapshot()Write state to automation directory