Native Surfaces

Native surfaces are the app-owned regions that zero-native composes inside a platform window. A surface can be a WebView, native chrome, a native utility panel, or a future specialized drawing region. The key model is that WebView content is one surface in the window, not the whole app.

Surface Types

SurfaceUse it forBacked by
WebViewRich product UI, frontend frameworks, documents, dashboardsViewKind.webview, WebViewSource, and the selected web engine
Native chromeToolbars, titlebar accessories, sidebars, status barsNative platform views declared through ShellView or runtime.createView(...)
Native controlsButtons, search fields, toggles, segmented controls, labels, progressPlatform controls with command routing and accessibility metadata
OS capability surfacesDialogs, tray, clipboard, notifications, credentials, file dropsGuarded PlatformServices, runtime methods, and built-in bridge commands
GPU surfaceFuture custom drawing regions for editors, timelines, games, or canvas-like workflowsReserved as ViewKind.gpu_surface; current hosts report it unsupported

Window Shape

Use App.scene_fn when a window should start with native structure around WebView content:

const shell_views = [_]zero_native.ShellView{
    .{ .label = "toolbar", .kind = .toolbar, .edge = .top, .height = 52 },
    .{ .label = "refresh", .kind = .button, .parent = "toolbar", .accessibility_label = "Refresh workspace", .text = "Refresh", .command = "app.refresh" },
    .{ .label = "body", .kind = .split, .fill = true, .axis = .row },
    .{ .label = "sidebar", .kind = .sidebar, .parent = "body", .width = 240 },
    .{ .label = "main", .kind = .webview, .parent = "body", .url = "zero://inline", .fill = true },
    .{ .label = "status", .kind = .statusbar, .edge = .bottom, .height = 32, .text = "Ready" },
};

const shell_windows = [_]zero_native.ShellWindow{.{
    .label = "main",
    .title = "Acme",
    .width = 1100,
    .height = 760,
    .views = &shell_views,
}};

This creates native chrome and controls in the platform host while the main WebView owns the workspace content.

Imperative View API

For dynamic surfaces, create and update views at runtime:

const info = try runtime.createView(.{
    .window_id = 1,
    .label = "sync-status",
    .kind = .label,
    .parent = "status",
    .accessibility_label = "Sync status",
    .text = "Syncing",
});

_ = try runtime.updateView(info.window_id, info.label, .{
    .text = "Synced",
});

Trusted WebView code can use the guarded bridge helpers when js_window_api or an explicit builtin_bridge policy allows view commands:

const views = await window.zero.views.list();
await window.zero.views.update("status", { text: "Synced" });
await window.zero.commands.invoke("app.refresh");

Command Routing

Surfaces should share command IDs instead of each UI entry point owning separate behavior. A native menu item, shortcut, toolbar button, tray item, native button, and WebView bridge call can all dispatch app.refresh into the same Event.command handler.

CommandEvent.source records whether the action came from .menu, .shortcut, .toolbar, .tray, .native_view, .bridge, or .runtime. ViewInfo.id is a stable runtime handle for the view lifetime, while label remains the selector used by runtime and bridge APIs.

Compatibility

Existing App.source, runtime.createWebView(...), and window.zero.webviews.* apps remain valid. The generic view model adds a wider vocabulary:

  • ViewKind.webview wraps WebView-backed surfaces.
  • runtime.createView(.{ .kind = .webview, ... }) routes through the WebView backend.
  • runtime.listViews(...) returns WebView-backed and native views through one list.
  • Automation snapshots include native views and WebView-backed views.

Platform Support

macOS, Linux, and Windows system-WebView hosts support native views and controls today. Chromium hosts and mobile hosts expose the same public vocabulary where useful, but unsupported operations reject explicitly instead of silently falling back to WebView-only behavior.

Use support checks before showing optional native affordances:

if (runtime.supports(.native_views)) {
    try runtime.createView(.{ .label = "toolbar", .kind = .toolbar });
}
const hasNativeViews = await window.zero.platform.supports("native_views");

See Platform Support for the current host matrix.