Environment
- native-sdk v0.4.2
- macOS aarch64
- Zig 0.16.0
What happens
NativeSdkAssetSchemeHandler and configureWithRootPath: exist in the
macOS host and back the zero://app asset scheme, but the asset root is
only ever configured as a side effect of a main-webview loading an
assets-source URL. A UiApp scene (canvas-first) app never performs that
main-webview assets-source load — the whole point of a scene app is that
the primary surface is the native canvas, not a webview. As a result, any
scene-declared webview pane (UiApp.Options.web_panes, a .webview shell
view anchored into the canvas layout) has no working asset root and cannot
load zero://app/... content.
What I expected
I expected a scene app to be able to opt into the zero://app asset root
independently of whether it ever loads a main webview — e.g. because it
hosts one or more secondary webview panes that need to load bundled static
assets (HTML/CSS/JS/images) from the app bundle.
What we did instead (workaround)
Without a configured asset root, the only way we found to serve static
content into a scene-declared webview pane is to spawn a loopback HTTP
server via the effects channel (fx.spawn sh -c "python3 -m http.server ... --bind 127.0.0.1 ...") and point the pane at
http://127.0.0.1:<port>/..., adding that origin to
security.navigation.allowed_origins. This works (verified end-to-end,
including clean teardown of the spawned process on app exit), but it's
extra moving parts — a subprocess, a startup race between the pane's first
navigation and the server binding (we handle it with a reload_token
bump-until-loaded pattern), and a port to manage — for what should be
static, bundle-local content.
Repro / code pointers
(Line numbers from the v0.4.2 npm-distributed sources.)
- Host-side plumbing that already exists:
NativeSdkAssetSchemeHandler (src/platform/macos/appkit_host.m:628),
configureWithRootPath:entryPath:spaFallback: (appkit_host.m:6201).
- The only caller of
configureWithRootPath: is the main-webview
loadSource: path (appkit_host.m:9207, source kind = assets), which
UiApp scene apps never exercise. Per-window handlers are created only
inside ensureMainWebViewForWindowId: (appkit_host.m:6633, stored at
:6668); scene/child webview creation reuses them via
assetHandlerForWindowId: (appkit_host.m:6691, consumed at :7314) —
so in a canvas-first app the lookup returns nil and the pane's
WKWebViewConfiguration gets no zero scheme handler at all.
- Repro app:
spike/canvas-web/ — a UiApp shell hosting a WKWebView
pane via UiApp.Options.web_panes, a scene-declared .webview shell
view parented to the canvas and anchored to a panel's layout frame. The
pane currently loads content from the loopback server described above
rather than zero://app.
Suggested direction
We're building an image-optimization app where the comparison canvas is a
WKWebView pane (a pure-native tiled canvas tops out around 26–32 fps at
full-canvas coverage in our measurements, well short of a webview pane's
~119 fps sustained; see our companion perf report). Static assets
(comparison-page HTML/CSS/JS) are exactly the kind of bundle-local content
zero://app seems designed for. A manifest field (e.g. under .shell or
per-webview-pane config) or a RunOptions/Runtime API call to set the
asset root explicitly — without requiring a main-webview assets-source
load to happen first — would let scene apps drop the loopback server
entirely for static content. If there's a reason the asset root is tied to
the main-webview load specifically (security scoping, bundle path
resolution timing, etc.), we'd be glad to hear the constraint — happy to
help test an API shape against spike/canvas-web/.
Related: #101, #103 — the companion issues referenced above
Environment
What happens
NativeSdkAssetSchemeHandlerandconfigureWithRootPath:exist in themacOS host and back the
zero://appasset scheme, but the asset root isonly ever configured as a side effect of a main-webview loading an
assets-source URL. A
UiAppscene (canvas-first) app never performs thatmain-webview assets-source load — the whole point of a scene app is that
the primary surface is the native canvas, not a webview. As a result, any
scene-declared webview pane (
UiApp.Options.web_panes, a.webviewshellview anchored into the canvas layout) has no working asset root and cannot
load
zero://app/...content.What I expected
I expected a scene app to be able to opt into the
zero://appasset rootindependently of whether it ever loads a main webview — e.g. because it
hosts one or more secondary webview panes that need to load bundled static
assets (HTML/CSS/JS/images) from the app bundle.
What we did instead (workaround)
Without a configured asset root, the only way we found to serve static
content into a scene-declared webview pane is to spawn a loopback HTTP
server via the effects channel (
fx.spawn sh -c "python3 -m http.server ... --bind 127.0.0.1 ...") and point the pane athttp://127.0.0.1:<port>/..., adding that origin tosecurity.navigation.allowed_origins. This works (verified end-to-end,including clean teardown of the spawned process on app exit), but it's
extra moving parts — a subprocess, a startup race between the pane's first
navigation and the server binding (we handle it with a
reload_tokenbump-until-loaded pattern), and a port to manage — for what should be
static, bundle-local content.
Repro / code pointers
(Line numbers from the v0.4.2 npm-distributed sources.)
NativeSdkAssetSchemeHandler(src/platform/macos/appkit_host.m:628),configureWithRootPath:entryPath:spaFallback:(appkit_host.m:6201).configureWithRootPath:is the main-webviewloadSource:path (appkit_host.m:9207, source kind = assets), whichUiAppscene apps never exercise. Per-window handlers are created onlyinside
ensureMainWebViewForWindowId:(appkit_host.m:6633, stored at:6668); scene/child webview creation reuses them viaassetHandlerForWindowId:(appkit_host.m:6691, consumed at:7314) —so in a canvas-first app the lookup returns nil and the pane's
WKWebViewConfigurationgets nozeroscheme handler at all.spike/canvas-web/— aUiAppshell hosting aWKWebViewpane via
UiApp.Options.web_panes, a scene-declared.webviewshellview parented to the canvas and anchored to a panel's layout frame. The
pane currently loads content from the loopback server described above
rather than
zero://app.Suggested direction
We're building an image-optimization app where the comparison canvas is a
WKWebView pane (a pure-native tiled canvas tops out around 26–32 fps at
full-canvas coverage in our measurements, well short of a webview pane's
~119 fps sustained; see our companion perf report). Static assets
(comparison-page HTML/CSS/JS) are exactly the kind of bundle-local content
zero://appseems designed for. A manifest field (e.g. under.shellorper-webview-pane config) or a
RunOptions/Runtime API call to set theasset root explicitly — without requiring a main-webview assets-source
load to happen first — would let scene apps drop the loopback server
entirely for static content. If there's a reason the asset root is tied to
the main-webview load specifically (security scoping, bundle path
resolution timing, etc.), we'd be glad to hear the constraint — happy to
help test an API shape against
spike/canvas-web/.Related: #101, #103 — the companion issues referenced above