Commands

Commands are the shared routing layer for native app actions. Menus, shortcuts, toolbar controls, tray items, native controls, bridge calls, and runtime code can all dispatch the same command name into Event.command.

fn event(context: *anyopaque, runtime: *zero_native.Runtime, event_value: zero_native.Event) anyerror!void {
    _ = context;
    switch (event_value) {
        .command => |command| {
            if (std.mem.eql(u8, command.name, "app.refresh")) {
                try runtime.updateView(command.window_id, "status", .{
                    .text = "Refreshed",
                });
            }
        },
        else => {},
    }
}

Sources

CommandEvent.source identifies where the action came from:

SourceEmitted by
.runtimeDirect runtime.dispatchCommand(...) calls
.menuNative menu items
.shortcutNative keyboard shortcuts
.toolbarNative controls inside a toolbar view
.trayTray actions
.native_viewNative controls outside toolbar views
.bridgewindow.zero.commands.invoke(...)

Use command.name for the action and command.source only when the app genuinely needs source-specific behavior. Tray commands also include tray_item_id for apps that still need to distinguish legacy tray items that dispatch "tray.action".

Command names

Command names are stable IDs, not display labels. Use namespaced lowercase IDs:

const shortcuts = [_]zero_native.Shortcut{
    .{ .id = "app.refresh", .key = "r", .modifiers = .{ .primary = true } },
};

const view_items = [_]zero_native.MenuItem{
    .{ .label = "Refresh", .command = "app.refresh", .key = "r", .modifiers = .{ .primary = true } },
};

The same command can be bound to a menu item, shortcut, toolbar button, tray item, and bridge call. The runtime validates command IDs and rejects empty, oversized, or path-like names.

app.zon can also declare shared command metadata:

.commands = .{
    .{ .id = "app.refresh", .title = "Refresh" },
    .{ .id = "app.sidebar.toggle", .title = "Sidebar", .checked = true },
},

This metadata gives native and web entry points a common manifest-level command catalog while runtime dispatch still happens through Event.command.

Generated runners load that catalog into runtime options. Native code can read the active commands without reparsing the manifest:

var buffer: [32]zero_native.Command = undefined;
const commands = runtime.listCommands(&buffer);
for (commands) |command| {
    if (command.enabled) {
        // Bind command.id to native controls or custom UI.
    }
}

Bridge invocation

When js_window_api and the built-in bridge policy allow it, WebView content can dispatch the same command:

await window.zero.commands.invoke("app.refresh");

The resulting event has source = .bridge, the calling native window_id, and the calling view label when the call came from a named child WebView.

WebView content can also read the manifest command catalog:

const commands = await window.zero.commands.list();