Runtime.focusView ships and native-sdk.view.focus ships, but there is no fx effect for either — so a Zig update cannot move focus between views at all.
The capability is already there — twice
Runtime.focusView is complete (src/runtime/window_view_runtime.zig:161):
pub fn focusView(self: *Runtime, window_id: platform.WindowId, label: []const u8) anyerror!void {
try Self.validateViewParent(self, window_id);
try validateViewLabel(label);
if (!Self.viewLabelExists(self, window_id, label)) return error.ViewNotFound;
try self.options.platform.services.focusView(window_id, label);
try Self.setFocusedView(self, window_id, label);
self.invalidateFor(.command, null);
}
And it is reachable from JavaScript: focusViewFromJson (src/runtime/builtin_bridge.zig:322) is the native-sdk.view.focus bridge command, with focusNextViewFromJson / focusPreviousViewFromJson right beside it.
The effects surface has nothing
Effects(Msg) exposes exactly four window-ish verbs, all deferred through WindowActionBinding:
closeWindow (src/runtime/effects.zig:7757)
minimizeWindow (:7770)
showWindow (:7787)
quitApp (:7804)
plus windowActionState (:7814) as the fake-executor mirror. There is no view verb of any kind, and no focus verb of any kind.
There is no workaround inside the model, either: update is pure and never receives the Runtime, so the only two routes to focusView are a bridge call from a page (which requires a WebView that is already focused and a page willing to cooperate) and a host call. Neither is a route a native-rendered app can take to focus its own canvas or terminal pane.
Why it bites rather than merely annoys
Consider the layout the SDK now supports directly: a canvas, one or more <terminal> elements, and a WebView in one window. Keyboard focus does not reach across those panes any other way.
There is no key-view-loop wiring in src/platform/macos/appkit_host.m — no nextKeyView, no recalculateKeyViewLoop, no initialFirstResponder — and doCommandBySelector's insertTab: (:7032) synthesizes a tab key into the canvas widget ring instead of calling selectNextKeyView:, so Tab walks canvas widgets and never arrives at a WebView. Measured on that layout: eight Tab presses from a canvas text field produced zero page events. Independently, a focused <terminal> is a Tab trap, correctly and deliberately — Tab belongs to the shell running in the pty — so once focus reaches a terminal it never leaves.
That leaves a chord the app owns as the only workable answer, and Runtime.focusView is precisely the primitive for it. It is just not spendable from update.
The ask
One effect, shaped like the four that already exist:
/// Focus a view by its declared label. Fire-and-forget, same
/// contract as `closeWindow`: no event echoes beyond the focus
/// change the runtime already publishes, an unknown label is a
/// no-op, and the fake executor records the request in the mirror.
pub fn focusView(self: *Self, window_label: []const u8, view_label: []const u8) void
That is one focus_view_fn on WindowActionBinding (src/runtime/effects.zig), one counter and record() on WindowActionState for test assertions, and a host-side hop into the Runtime.focusView that already exists. focusNextView / focusPreviousView would follow the same shape if you want the trio, but the by-label one is the load-bearing case.
The signature is not the point — fx.focusView(label) resolving against the primary window would serve a single-window app fine. What is impossible today is getting there from update at all.
Verified against ea98365.
A patch is up: #218. Filed separately as the defect record — close whichever of the two is the redundant one.
Runtime.focusViewships andnative-sdk.view.focusships, but there is nofxeffect for either — so a Zigupdatecannot move focus between views at all.The capability is already there — twice
Runtime.focusViewis complete (src/runtime/window_view_runtime.zig:161):And it is reachable from JavaScript:
focusViewFromJson(src/runtime/builtin_bridge.zig:322) is thenative-sdk.view.focusbridge command, withfocusNextViewFromJson/focusPreviousViewFromJsonright beside it.The effects surface has nothing
Effects(Msg)exposes exactly four window-ish verbs, all deferred throughWindowActionBinding:closeWindow(src/runtime/effects.zig:7757)minimizeWindow(:7770)showWindow(:7787)quitApp(:7804)plus
windowActionState(:7814) as the fake-executor mirror. There is no view verb of any kind, and no focus verb of any kind.There is no workaround inside the model, either:
updateis pure and never receives theRuntime, so the only two routes tofocusVieware a bridge call from a page (which requires a WebView that is already focused and a page willing to cooperate) and a host call. Neither is a route a native-rendered app can take to focus its own canvas or terminal pane.Why it bites rather than merely annoys
Consider the layout the SDK now supports directly: a canvas, one or more
<terminal>elements, and a WebView in one window. Keyboard focus does not reach across those panes any other way.There is no key-view-loop wiring in
src/platform/macos/appkit_host.m— nonextKeyView, norecalculateKeyViewLoop, noinitialFirstResponder— anddoCommandBySelector'sinsertTab:(:7032) synthesizes atabkey into the canvas widget ring instead of callingselectNextKeyView:, so Tab walks canvas widgets and never arrives at a WebView. Measured on that layout: eight Tab presses from a canvas text field produced zero page events. Independently, a focused<terminal>is a Tab trap, correctly and deliberately — Tab belongs to the shell running in the pty — so once focus reaches a terminal it never leaves.That leaves a chord the app owns as the only workable answer, and
Runtime.focusViewis precisely the primitive for it. It is just not spendable fromupdate.The ask
One effect, shaped like the four that already exist:
That is one
focus_view_fnonWindowActionBinding(src/runtime/effects.zig), one counter andrecord()onWindowActionStatefor test assertions, and a host-side hop into theRuntime.focusViewthat already exists.focusNextView/focusPreviousViewwould follow the same shape if you want the trio, but the by-label one is the load-bearing case.The signature is not the point —
fx.focusView(label)resolving against the primary window would serve a single-window app fine. What is impossible today is getting there fromupdateat all.Verified against
ea98365.A patch is up: #218. Filed separately as the defect record — close whichever of the two is the redundant one.