3.8 KiB
App Model and Runtime
Use this when editing src/main.zig, src/runner.zig, lifecycle behavior, runtime setup, or tests.
App
A Native SDK app returns a native_sdk.App value:
const App = struct {
fn app(self: *@This()) native_sdk.App {
return .{
.context = self,
.name = "my-app",
.source = native_sdk.WebViewSource.html("<h1>Hello</h1>"),
.source_fn = source,
.start_fn = start,
.event_fn = event,
.stop_fn = stop,
};
}
};
Required fields:
context: pointer to app state.name: app name for traces and automation snapshots.source: initial WebView source. Overridden bysource_fnwhen present.
Optional callbacks:
source_fn: dynamic source resolver.start_fn: called after runtime start and initial load.event_fn: receives lifecycle and runtime events.stop_fn: called before shutdown.
WebViewSource
Choose one:
native_sdk.WebViewSource.html("<p>Inline</p>")
native_sdk.WebViewSource.url("http://127.0.0.1:5173/")
native_sdk.WebViewSource.assets(.{
.root_path = "frontend/dist",
.entry = "index.html",
.origin = "zero://app",
.spa_fallback = true,
})
Use inline HTML only for small examples and smoke tests. Use URL sources for explicit local/remote loading. Use assets for packaged apps.
Runtime setup
Generated runners create a Runtime with platform services:
var runtime = native_sdk.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 fields agents commonly touch:
platform: macOS, Linux, Windows, orNullPlatform.trace_sink: stdout/file/fanout trace destination.bridge: app-defined bridge dispatcher.builtin_bridge: policy for built-in windows, WebViews, and dialogs.security: permissions, navigation allowlist, external links.automation: file-based automation server.window_state_store: persisted window geometry.js_window_api: exposeswindow.zero.windowsandwindow.zero.webviews.
Windows from Zig
Use runtime methods for native window management:
const info = try runtime.createWindow(.{
.label = "tools",
.title = "Tools",
.default_frame = native_sdk.geometry.RectF.init(80, 80, 420, 320),
});
try runtime.focusWindow(info.id);
Window limits:
- Max windows: 16.
- Max label bytes: 64.
- Max title bytes: 128.
Persisted window state is keyed primarily by label, so labels should be stable.
EmbeddedApp
Use EmbeddedApp when another host owns the main loop:
var embedded = native_sdk.embed.EmbeddedApp.init(my_app.app(), my_platform);
try embedded.start();
try embedded.frame();
try embedded.resize(new_surface);
try embedded.stop();
This is useful for mobile hosts, game engines, custom render loops, and headless tests. The repository includes iOS and Android examples that link libnative-sdk.a through Swift/Kotlin host apps.
Headless tests
Use NullPlatform or TestHarness when GUI behavior is not required:
var null_platform = native_sdk.NullPlatform.init(.{});
var runtime = native_sdk.Runtime.init(.{
.platform = null_platform.platform(),
});
Good headless test targets:
- source selection
- bridge handler logic
- bridge policy enforcement
- lifecycle callbacks
- manifest/tooling behavior
Use automation smoke tests for real WebView/window integration.