Skip to content

allow scene (canvas-first) apps to configure the zero://app asset root #102

Description

@jamesslock

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions