Platform Support
Native SDK is the complete toolkit for building native desktop applications, with experimental mobile support riding the same runtime — one runtime model, with native affordances landing per platform. This page states exactly what each platform gets today, at what tier, with the caveats attached. Unsupported operations return explicit runtime errors instead of pretending every host can do the same thing.
Support Matrix
macOS, Linux, and Windows run full desktop apps through their own platform hosts. Mobile is experimental — the marked columns are verified on the simulator and emulator, but APIs and tooling there may still change, and desktop is the mature surface. iOS and Android canvas apps are host-tier: the toolkit owns the entire mobile application — native dev --target ios|android and native package --target ios|android build and generate the platform host from app.zon, and your project carries zero host code. Embedding in a host app you own works on both mobile platforms and shares the same experimental status — see Mobile below.
- First-class — implemented and exercised
- Works with caveats — real support, footnote applies
- Embed-level — runs inside a host app you own
- Not available today
- ExperimentalPlatform is experimental — verified on the simulator/emulator; APIs and tooling may still change
| What you get | macOS | Windows | Linux | iOS Experimental | Android Experimental |
|---|---|---|---|---|---|
| Real OS windows | 1 | 1 | |||
| Native rendering | 2 | 2 | 2 | 2 | 2 |
| Pointer, keyboard & IME input | 3 | 3 | 3 | ||
| Menus | 4 | 4 | |||
| System tray | 5 | ||||
| Web content (WebViews) | 6 | 6 | 6 | 6 | 6 |
| Packaging | 7 | 7 | 7 | 7 | 7 |
| Code signing | 8 | ||||
| Automation | 9 | 9 | 9 | 9 |
- iOS and Android canvas apps run full screen inside toolkit-owned hosts, which the SDK builds ON the embed C ABI —
native devandnative packagewith--target ios|androidgenerate everything from app.zon, so the app project carries zero host code. Multi-window scenes are desktop-only. Embedding the runtime in a host app you control, driven over the same embed C ABI, works on both platforms and shares the mobile experimental status. - macOS presents through Metal and hands scrolling to OS scroll drivers. Linux and Windows present the deterministic software renderer through the platform blit path and report
backend=software; a manifest that requests another backend falls back to software there instead of erroring. The iOS toolkit host reads the software renderer's pixels over the ABI and presents them through Metal with the macOS host's presentation discipline — presents are gated on the canvas revision (an idle app acquires no drawables and uploads nothing), the drawable is acquired only after the frame's CPU work is done, and the pump pauses in the background and re-presents the retained canvas on return; the Android toolkit host copies the same pixels into its surface's window buffer (mobile GPU rendering is a later phase); embed hosts present the pixels in their own surfaces. - Windows maps native IME composition onto the shared IME events, with real-hardware IME verification still pending. The iOS toolkit host forwards touch, the system keyboard, and IME composition, verified with real injected input on the simulator; the Android toolkit host forwards touch, shows and hides the soft keyboard from the runtime's focus state, and routes committed text and IME composition through the same embed IME events, verified with injected input on the emulator.
- App menus are native on all three desktops. Hosts without a native context-menu presenter — Linux and Windows today — present the same declared context menu as an anchored canvas surface; authors declare one menu either way.
- Tray support is implemented on macOS (
NSStatusItem) and Windows; Linux tray calls returnUnsupportedServiceuntil a portable status-notifier implementation is selected. - Web engines only apply to apps that embed web content; native-rendered apps carry none. The system WebView is the default engine on every desktop platform; bundled Chromium through CEF is available on macOS only, and Linux/Windows Chromium builds fail early instead of silently substituting an engine. The Windows system engine is WebView2: its Evergreen runtime ships with Windows 11 and current Windows 10 (older machines need the runtime installer;
native doctorchecks for it), and the build stages the vendored loader next to the executable automatically. See Web Engines. The mobile shell examples embed the platform WebView as the content workspace. native packagetargets all five platforms: macOS gets a.appbundle (pluszig build dmg), Linux an install tree, Windows a distributable directory with a per-user file-type registration script, iOS a complete generated Xcode project — toolkit host sources, Info.plist, asset catalog, shared scheme, and the device-slice embed library — thatxcodebuild archivebuilds with zero edits (code signing stays a manual step, like notarization), and Android a complete generated host project whose debug APK assembles with zero edits, directly with the SDK's build tools (store signing keys stay a manual step). Every platform's icons generate from one square source image. See Packaging.- macOS signing supports
adhocandidentitymodes with entitlements; notarization is submitted manually with the platform tools after packaging. No signing tooling exists yet for the other platforms. See Code Signing. - The automation server is a file-based protocol the runtime serves on every desktop platform: snapshots, assertions, synthetic input, screenshots, record/replay. Engine screenshots render through the deterministic CPU reference renderer on every platform, so they are byte-comparable across hosts. Mobile exposes accessibility snapshots and actions through the embed ABI; the iOS and Android toolkit hosts serve the same file-based protocol inside the app's data container when launched with automation enabled.
Desktop Hosts
| Area | macOS system WebView | macOS Chromium | Linux system WebView | Linux Chromium | Windows system WebView |
|---|---|---|---|---|---|
| Main WebView | Supported | Supported | Supported | Unsupported | Supported |
| Child WebViews | Supported | Supported | Supported | Unsupported | Supported |
| Native views | Supported | Unsupported | Supported | Unsupported | Supported |
| Native control commands | Supported | Unsupported | Supported | Unsupported | Supported |
| App menus | Supported | Unsupported | Supported | Unsupported | Supported |
| Native context menus | Supported | Supported | Anchored fallback | Anchored fallback | Anchored fallback |
| System tray | Supported | Supported | Unsupported | Unsupported | Supported |
| Keyboard shortcuts | Supported | Supported | Supported | Unsupported | Supported |
| Dialogs | Supported | Supported | Supported | Unsupported | Supported |
| Clipboard text | Supported | Supported | Supported | Unsupported | Supported |
| Clipboard rich data | Supported | Supported | Supported | Unsupported | Supported |
| Open URL / reveal path | Supported | Supported | Supported | Unsupported | Supported |
| Notifications | Supported | Supported | Supported | Unsupported | Supported |
| Recent documents | Supported | Supported | Supported | Unsupported | Supported |
| Credentials | Supported | Supported | Supported when a secret service is present | Unsupported | Supported |
| File drops | Supported | Unsupported | Supported | Unsupported | Supported |
| App activation events | Supported | Supported | Supported | Unsupported | Supported |
| Audio playback + streaming | Supported (one AVPlayer for local files, cache entries, and streams) | Unsupported | Supported when GStreamer with playbin is present (runtime-loaded, probed live) | Unsupported | Supported (one Media Foundation session) |
| Audio spectrum analysis | Supported (MTAudioProcessingTap + vDSP on the single player) | Unsupported | Supported when GStreamer's spectrum element is present (gst-plugins-good, probed live) | Unsupported | Supported on Windows 10 2004+ (process-scoped WASAPI loopback of this app only, probed live) |
"Anchored fallback" for native context menus means the declared menu still presents — as an anchored canvas surface on the widget — because those hosts have no native menu presenter yet (GTK popover menus and the Win32 popup-menu API are the documented future seams). Authors declare one menu either way; the platform decides presentation.
Audio spectrum analysis reads the app's OWN playback and nothing else: macOS taps the app's single player pre-effects, Windows captures the app's own audio session through process-scoped loopback (never system-wide — other apps' audio can never appear in the bands), and Linux analyzes inside the app's own playbin. Hosts that cannot analyze report audio_spectrum unsupported and simply never deliver .spectrum events; see the capabilities page for the band shape and cadence contract.
How Support Is Verified
macOS is the primary development platform and carries the deepest support. The Linux and Windows columns are not aspirational: the repository carries reproducible live-verification loops that build every showcase app and drive it for real. tools/linux-truth runs the apps in real windows against the platform toolkit under Xvfb — clicks, keys, wheel input, multi-window flows, resize clamps, window-close paths, and both engine and X-server screenshots. tools/windows-truth drives the same scenarios on a real Windows desktop, plus clipboard round-trips, effect-stream cancellation, record/replay, and launching the packaged artifact. CI additionally runs headless Linux and Windows canvas smokes on every change. iOS is exercised on the simulator through the toolkit host and the embed library — the native dev --target ios loop launches real apps, and the input and layout verification scripts inject hardware-true touches and keyboard events; a fresh native package --target ios output is archived with xcodebuild as part of verification. Android is exercised on the emulator the same way: the native dev --target android loop assembles, installs, and launches real apps, input is injected over adb (taps, keys, soft-keyboard text), and a fresh native package --target android output assembles the debug APK; the embed ABI additionally cross-compiles for both Android architectures in CI.
Runtime Queries
Use Runtime.supports(...) when native code needs to branch on the current host:
if (runtime.supports(.native_views)) {
try runtime.createView(.{
.label = "sidebar",
.kind = .sidebar,
});
}JavaScript can query the same support model through the built-in bridge when js_window_api or an explicit builtin_bridge policy allows native-sdk.platform.supports:
const hasTray = await window.zero.platform.supports("tray");Feature names match the Zig PlatformFeature enum and the TypeScript NativeSdkPlatformFeature union. JavaScript callers may use either snake_case names such as native_views or camelCase aliases such as nativeViews. Unsupported operations still reject explicitly if called; support checks are intended for choosing UI affordances before making those calls.
Native Surfaces
The macOS, Linux, and Windows system-WebView hosts support toolbar, titlebar_accessory, sidebar, statusbar, split, stack, button, icon_button, list_item, checkbox, toggle, segmented_control, text_field, search_field, label, spacer, and progress_indicator native view kinds. The macOS system-WebView host also supports gpu_surface as a Metal-backed child view, and the Linux and Windows system-WebView hosts support gpu_surface as a software (CPU reference-rendered) child view presented through the pixel blit path (cairo on Linux, GDI on Windows).
ViewKind.webview is the compatibility path for WebView-backed views. Existing runtime.createWebView(...) and window.zero.webviews.* APIs remain available.
Window chrome control spans all three desktop hosts: titlebar = "hidden_inset"/"hidden_inset_tall", the window-drag region channel, and the on_chrome inset hook are implemented on the macOS hosts (AppKit and Chromium), the Win32 host (the full system frame stays: the caption band is reclaimed for the app while DWM composites the real min/max/close buttons, and drag regions answer the native hit test as caption), and the GTK host (client-side decorations: a header bar with the desktop-themed window controls, honoring the user's gtk-decoration-layout for button side and order, with drag regions consumed at the press gesture). On every host the on_chrome hook reports where window controls land — top band height plus a leading or trailing controls inset — so a header pads the correct edge with no per-platform code. Mobile embed hosts answer the same on_chrome channel with the viewport's safe-area insets (notch, status bar, home indicator), and subscribing transfers safe-area padding ownership to the app — see Mobile.
Chromium builds are currently enabled on macOS only. Linux and Windows Chromium host files are placeholders and build/package tooling rejects those targets until the native hosts are implemented.
Mobile
Experimental Mobile support is experimental — the host tier and the embedding path alike. Everything below is real and verified — iOS on the simulator, Android on the emulator — but the APIs and tooling on these platforms may still change, and desktop is the mature surface. The label describes maturity: what each platform does today is stated exactly.
The iOS host tier
iOS canvas apps ship without you ever opening Xcode as an editor: the toolkit owns the entire iOS application, built on the same embed C ABI that standalone embedding uses. native dev --target ios builds the app's embed static library for the simulator, wraps it in the SDK's UIKit host — a full-screen canvas surface view with touch/keyboard/IME forwarding, CoreText text measurement, and safe-area reporting — installs and launches it via simctl, and streams the app log to your terminal. native package --target ios emits a complete, deterministic Xcode project around the same host (generated Info.plist, asset catalog from the single-source icon pipeline, shared scheme, device-slice library) that xcodebuild archive builds with zero edits; code signing stays a manual step, like macOS notarization.
Safe-area insets (notch, status bar, home indicator) arrive through the same window-chrome channel macOS uses for the titlebar band: an app that subscribes to on_chrome pads with one code path on every platform and owns the safe-area padding; an app without the hook keeps the automatic runtime inset. The full audio effect family plays for real: the host registers the platform audio service over the embed ABI — AVAudioPlayer for local files and verified cache entries, AVPlayer for progressive URL streams filling the track cache in the app container's Library/Caches/audio/ — with an honest playback audio session (activated on first play; a system interruption pauses the transport and reports the paused state through the ordinary event stream). Background audio and now-playing-center integration are not wired. Runtime-registered images decode for real too: the host registers the platform image decoder over the embed ABI — CGImageSource (ImageIO), the same codec family the macOS host decodes through — so fx.registerImageBytes registers real pixels (the soundboard's committed album covers, fetched avatars) instead of declining to the initials fallback. The remaining honest caveats: frames render through the deterministic CPU reference renderer (GPU rendering on iOS is a later phase), markup hot reload does not reach the simulator yet (edit, then rerun native dev --target ios), and device workflows beyond xcodebuild archive are not toolkit-managed.
The Android host tier
Android canvas apps get the same ownership: the toolkit owns the entire Android application, built on the same embed C ABI. native dev --target android builds the app's embed static library for aarch64-linux-android, compiles the SDK's Android host around it — an edge-to-edge canvas SurfaceView with touch/soft-keyboard/IME forwarding, Paint-backed text measurement, and safe-area/keyboard inset reporting — assembles a debug-signed APK, installs and launches it on a running (or freshly booted) emulator via adb, and streams the app log to your terminal. native package --target android emits a complete generated host project and assembles the same debug APK with zero edits, directly with the Android SDK's build tools (aapt2, javac, d8, zipalign, apksigner) and the NDK compiler — no build-system project to maintain, no plugin/version matrix, and the dev and package paths share one assembly so they cannot drift.
Display-cutout and system-bar insets ride the same on_chrome window-chrome channel, and the soft keyboard follows the runtime's focus state: the host shows the keyboard when an editable widget takes focus, routes committed text and composition through the embed IME events, and reports the keyboard's viewport inset so layout clears it (apps that own safe areas keep only the keyboard's residual overlap). Rotation keeps the activity alive, so the embedded runtime survives orientation changes as a resize. The full audio effect family plays for real: the host registers the platform audio service over the embed ABI — one MediaPlayer for local files, verified cache entries, and progressive URL streams filling the track cache in the app data directory's cache/audio/ — with honest audio focus (requested on play; a focus loss pauses the transport and reports the paused state through the ordinary event stream, and regain never auto-resumes). Media-session and notification integration (lock screen controls, background playback beyond the cached process) are not wired. Runtime-registered images decode for real too: the host registers the platform image decoder over the embed ABI — BitmapFactory, the platform's in-box codec stack — so fx.registerImageBytes registers real pixels (the soundboard's committed album covers, fetched avatars) instead of declining to the initials fallback. The remaining honest caveats: one ABI ships today (arm64-v8a, every Android 11+ device; minSdk 30), the APK is debug-signed (store distribution keys and app bundles stay a manual step, like macOS notarization), and markup hot reload does not reach the device yet (edit, then rerun native dev --target android).
Embedded hosts
Embedding works on both mobile platforms and carries the same experimental status as the rest of the mobile experience. The mobile examples own native header/navigation layout in UIKit or Android views, embed a WKWebView or Android WebView as the workspace, and drive the Native SDK through the C/JNI lifecycle, activation, resize, and command calls documented in Embedded App.
| Area | iOS embedded host | Android embedded host |
|---|---|---|
| Native header/navigation | UIKit header with Back and Refresh command buttons | Android view header with Back and Refresh command buttons |
| WebView content area | WKWebView workspace | Android WebView workspace |
| Runtime lifecycle | native_sdk_app_create, start, activate, deactivate, stop, and destroy from Swift | JNI bridge for create, start, activate, deactivate, stop, and destroy |
| Resize and orientation | viewDidLayoutSubviews forwards WebView size and screen scale | Orientation/screen-size changes stay in the activity; surfaceChanged forwards size, density, and Surface |
| Keyboard avoidance | Keyboard frame notifications adjust the WebView bottom constraint | windowSoftInputMode="adjustResize" lets Android relayout the content area |
| Back/navigation command | Native Back button dispatches mobile.back | Native Back button and system Back dispatch mobile.back |
| Touch forwarding | UIKit/WebKit own touch handling for the WebView workspace | MotionEvent pointer data is forwarded through native_sdk_app_touch |
Generic ShellView mapping | Mobile shell metadata feeds generated UIKit host config for header labels, command buttons, and WebView workspace | Mobile shell metadata feeds generated Android host config for header labels, command buttons, and WebView workspace |
Mobile hosts own safe areas, orientation, keyboard avoidance, back gestures, and platform lifecycle integration. The current mobile mapping is package-time shell metadata for native header/workspace structure; dynamic generic native view bridge commands should still be treated as desktop APIs until UIKit and Android runtime view mutation is added.
Canvas-scene apps also run through a hand-written embed host: addMobileLib compiles a Model/Msg/update/view app into the embed static library with a single gpu_surface scene, frames render through the deterministic CPU reference renderer, and the host shim blits the presented pixels (native_sdk_app_render_pixels) into its own surface. The examples/mobile-canvas iOS shim — the same architecture the toolkit's own UIKit host uses — is exercised on the simulator: rendering, safe-area layout, real touch/keyboard/IME input, and accessibility snapshots. The Android NativeActivity shim cross-compiles for both Android arches from the same library build.
gpu_surface is implemented for the macOS system-WebView host as a Metal-backed child surface and for the Linux and Windows system-WebView hosts as a software-rendered (CPU reference renderer + GTK pixel blit on Linux, GDI DIB blit on Windows) child surface; Linux and Windows frame events report backend=software, and a manifest that declares another backend (for example gpu_backend = "metal") falls back to software on those hosts instead of erroring. Windows maps native IME composition (WM_IME_COMPOSITION) onto the same shared IME events the macOS and Linux hosts emit — inline preedit, cursor position, and the commit contract — with real-hardware IME verification still pending. Other current hosts report unsupported operations for that view kind and runtime.supports(.gpu_surfaces) / window.zero.platform.supports("gpuSurfaces") return false.