App Model
A zero-native app provides a name, optional WebView content, and optional lifecycle callbacks. The runtime owns the event loop, windows, native views, and native services; the platform owns the web engine and OS host.
The App struct
| Field | Type | Description |
|---|---|---|
context | *anyopaque | Pointer to your app state (required) |
name | []const u8 | App name used in traces and automation snapshots (required) |
source | WebViewSource | Initial WebView content for compatibility startup windows; defaults to empty HTML |
source_fn | ?fn(*anyopaque) !WebViewSource | Dynamic source resolver (overrides source when set) |
scene_fn | ?fn(*anyopaque) !ShellConfig | Declarative native window and view tree; when set, startup uses the scene instead of the compatibility window list |
start_fn | ?fn(*anyopaque, *Runtime) !void | Called after the runtime starts, before startup scene or source loading |
event_fn | ?fn(*anyopaque, *Runtime, Event) !void | Called on every runtime event (lifecycle + commands) |
stop_fn | ?fn(*anyopaque, *Runtime) !void | Called before the runtime shuts down |
All callback fields are optional. A minimal app only needs context and name; provide source or source_fn when the app has WebView content, and scene_fn when startup should use a declarative native shell.
Startup shape
Without scene_fn, zero-native uses the compatibility path: it loads source or source_fn into the configured startup window list.
With scene_fn, zero-native materializes the returned ShellConfig as native shell windows and views. The first scene window adopts the startup native window; additional scene windows are created through the window service. App.source or source_fn still provides the main WebView content for those windows.
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 = [_]zero_native.ShellView{
.{ .label = "toolbar", .kind = .toolbar, .edge = .top, .height = 52 },
.{ .label = "refresh", .kind = .button, .parent = "toolbar", .text = "Refresh", .command = "app.refresh" },
.{ .label = "main", .kind = .webview, .url = "zero://inline", .fill = true },
.{ .label = "status", .kind = .statusbar, .edge = .bottom, .height = 28, .text = "Ready" },
};
const shell_windows = [_]zero_native.ShellWindow{.{
.label = "main",
.title = "Acme",
.width = 1100,
.height = 760,
.views = &shell_views,
}};
const shell_scene: zero_native.ShellConfig = .{ .windows = &shell_windows };
fn scene(context: *anyopaque) anyerror!zero_native.ShellConfig {
_ = context;
return shell_scene;
}
fn app(self: *AppState) zero_native.App {
return .{
.context = self,
.name = "acme",
.source = zero_native.WebViewSource.html("<main>Content</main>"),
.scene_fn = scene,
.event_fn = event,
};
}WebViewSource
Three constructors for specifying what the WebView loads:
.html(content)-- inline HTML string, served aszero://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 = zero_native.WebViewSource.assets(.{
.root_path = "dist",
.entry = "index.html", // default
.origin = "zero://app", // default
.spa_fallback = true, // default
}),| Field | Default | Description |
|---|---|---|
root_path | required | Path 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_fallback | true | Serve entry for unknown routes (SPA mode) |
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 runactivate-- the app became the active foreground appdeactivate-- the app resigned active foreground statusframe-- a frame has been requested (for animations or state updates)stop-- the app is shutting down
Native hosts also emit app:activate and app:deactivate to each open window.zero instance:
window.zero.on("app:activate", () => {
refreshForegroundState();
});Native file drops dispatch Event.files_dropped to event_fn and emit drop:files to trusted window.zero instances:
window.zero.on("drop:files", (event) => {
console.log(event.paths);
});The runner pattern
The generated src/runner.zig wires the runtime with platform services:
- Selects the platform (macOS, Linux, or null for headless tests)
- Sets up trace sinks (stdout + file) via
FanoutTraceSink - Installs panic capture so crashes write
last-panic.txt - Initializes window state persistence from
windows.zon - Creates the
Runtimewith all options and callsruntime.run(app)
var runtime = zero_native.Runtime.init(.{
.platform = my_platform,
.trace_sink = fanout.sink(),
.bridge = my_app.bridge(),
.builtin_bridge = .{ .enabled = true, .commands = &builtin_policies },
.security = .{
.permissions = &app_permissions,
.navigation = .{ .allowed_origins = &.{ "zero://app" } },
},
.js_window_api = true,
.window_state_store = state_store,
.automation = if (build_options.automation) automation_server else null,
});
try runtime.run(my_app.app());RuntimeOptions
| Field | Type | Default | Description |
|---|---|---|---|
platform | Platform | required | Platform abstraction (macOS, Linux, or NullPlatform) |
trace_sink | ?trace.Sink | null | Destination for structured trace records |
log_path | ?[]const u8 | null | Path for persistent log file |
extensions | ?ModuleRegistry | null | Extension modules with lifecycle hooks |
bridge | ?BridgeDispatcher | null | App-defined bridge commands and handlers |
builtin_bridge | BridgePolicy | . | Policy for built-in commands (dialogs, windows) |
security | SecurityPolicy | . | Navigation allowlist, external links, permissions |
automation | ?automation.Server | null | File-based automation server for testing |
window_state_store | ?window_state.Store | null | Persistent window geometry and state |
js_window_api | bool | false | Expose 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
| Method | Description |
|---|---|
init(options) Runtime | Create a runtime |
run(app) !void | Enter the platform event loop |
createWindow(options) !WindowInfo | Open a new window |
listWindows() []WindowInfo | List open windows |
focusWindow(id) !void | Bring a window to front |
closeWindow(id) !void | Close a window |
invalidate() | Request a redraw |
invalidateFor(reason, dirty_region) | Request a redraw with reason and optional dirty region |
frameDiagnostics() FrameDiagnostics | Return stats from the last frame |
dispatchEvent(event) | Inject a synthetic event |
dispatchPlatformEvent(app, event) | Forward a platform event |
automationSnapshot() | Write state to automation directory |