Skip to main content

Cross-Platform Runtime Model

Hosanna separates the app layout expression from the concrete host platform. Product code should usually ask what kind of experience it is rendering (tv, web, phone, or tablet) and use capability checks for platform services, rather than branching directly on a device name.

Runtime Vocabulary

  • expression: app/layout intent: tv, web, phone, or tablet.
  • platformId: concrete host target such as roku, web, web-pwa, ios-capacitor, android-capacitor, apple-native, or android-native.
  • runtime: renderer family such as roku-scenegraph, web-dom, capacitor-webview, apple-native, or android-native.
  • os: host OS family such as roku, web, ios, android, tvos, or androidtv.
  • formFactor: physical UX class: tv, desktop, phone, or tablet.
  • orientation: current design-surface orientation. It does not select a platform or config by itself.

This model lets Capacitor reuse the web renderer while still reporting ios-capacitor or android-capacitor as the platform, and lets TV targets stay on their native renderers while sharing the same app-level expression model.

Cross-platform runtime and config selection

Launch parameters and device presets resolve runtime identity, config variant, device metrics, and capabilities before app code consumes them.

Config Variants

The base config remains assets/meta/app.config.json. Variants use app.config.<name>.json, for example:

  • app.config.phone.json
  • app.config.tablet.json
  • app.config.web.json
  • app.config.roku.json
  • app.config.mobile.json

Runtime selection is expression-first:

  1. Explicit appConfig launch/query override.
  2. Expression variant such as app.config.phone.json.
  3. Platform variant such as app.config.web.json, when no expression variant exists.
  4. Base app.config.json.

Device presets expand into expressions. For example, ?device=iphone-15 selects expression=phone, and ?device=galaxy-tab-s9 selects expression=tablet; the device name does not directly select a config file.

Variants can inherit another file with root-level $extendFile:

{
"$extendFile": "./app.config.json",
"appSettings": {
"configVariant": "phone"
}
}

Objects merge deeply with child values winning. Arrays and scalar values replace the parent value. $extends keeps its existing in-file style-reference meaning and is separate from $extendFile.

Device Metrics

Use IHosannaDevice for cross-platform device and screen data. Avoid scattering direct window, user-agent, or platform checks through view code.

Important fields include:

  • expression: active app/layout expression.
  • deviceLayoutOrientation: portrait or landscape design orientation.
  • deviceDpi: physical pixel width for the active design surface.
  • devicePreset: optional preset name that supplied the metrics.
  • resolutionInfo: resource-resolution suffix, scale, width, and height.
  • screenInfo.viewportWidth / screenInfo.viewportHeight: live host viewport size.
  • screenInfo.screenWidth / screenInfo.screenHeight: host screen size.
  • screenInfo.devicePixelRatio: host DPR.
  • screenInfo.safeAreaInsets: top, right, bottom, and left safe-area values.

On web and Capacitor, screenInfo comes from browser/WebView metrics and CSS safe-area env values. Native targets can supply platform-native screen, cutout, and safe-area values through the same facade.

Web Launch Parameters

The web entry point accepts launch parameters for expression, device presets, design size, DPI, orientation, and input adapters:

http://localhost:5170/?device=iphone-15&orientation=portrait&adapters=touch,keyboard

Common parameters:

ParameterPurpose
`expression=tvweb
device=<preset>Expands to a known device profile, including expression, design size, DPI, adapter profile, and orientation defaults.
`orientation=portraitlandscape`
designWidth / designHeightLogical design surface size.
displayWidth / displayHeightAliases for designWidth / designHeight.
dpiPhysical output width. The renderer computes dpiScale = dpi / designWidth.
adapters=touch,keyboard,mouse,rokuLimits active input adapters.
showRemoteOverlay=trueShows the on-screen remote overlay. Use tiny or mini for compact overlays.

For existing TV-authored screens, keep the TV design surface and change the expression/output:

http://localhost:5170/?expression=phone&orientation=portrait&designWidth=1920&designHeight=1080&dpi=1179&adapters=touch,keyboard

For mobile-authored screens, use phone-sized logical values:

http://localhost:5170/?expression=phone&orientation=portrait&designWidth=393&designHeight=852&dpi=1179&adapters=touch,keyboard

See Web Expression, DPI, and Touch Input for input adapter and pointer-normalization details.

Orientation

Orientation changes go through the orientation controller instead of direct view mutation. The controller:

  1. Normalizes the target orientation.
  2. Checks whether the platform adapter supports it.
  3. Asks the app whether the change is allowed.
  4. Calls app and visible-view onWillChangeOrientation hooks.
  5. Updates device/global screen metrics.
  6. Calls app and visible-view onDidChangeOrientation hooks.
  7. Invalidates the visible root view.

Views can override:

canChangeOrientation(event): boolean
onWillChangeOrientation(event): void
onDidChangeOrientation(event): void

Hosanna also dispatches HosannaOrientationWillChange and HosannaOrientationDidChange through the notification center. Use those notifications when a service or view needs to observe orientation without owning the visible root.

Capabilities

Use platform capabilities for feature decisions. Current capability groups include:

  • video: HTML element playback, WebKit AirPlay, Chromecast Web Sender, native cast bridge, native media session.
  • input: touch, mouse, keyboard, and remote availability.
  • orientation: portrait lock and landscape-video support.
  • sharing: native share sheet availability.
  • notifications: push notification support.
  • storage: offline asset support.
  • layout: safe-area insets.

Web and Capacitor share the web DOM renderer. Capacitor adds native shell affordances through runtime=capacitor-webview and platform IDs such as ios-capacitor or android-capacitor; app code should still prefer expression and capability checks.

Cross-Platform Color Values

Color strings are normalized to #RRGGBBAA across renderers. #RRGGBB receives FF alpha, and 0xRRGGBB / 0xRRGGBBAA are accepted. Do not treat a leading FF or 00 as ARGB; alpha is last, so #FFFFFF00 is transparent white.