Widget and ZIP Package Handling
TomorrowOS treats widgets and ZIP packages as playlist content, not as a separate package management API. A widget can unlock rich signage experiences, but it can also create playback failures, black screens, missing assets and inconsistent behaviour across runtimes. This guide explains how widgets work in TomorrowOS today: how they appear in policy, how the player downloads and extracts packages, how they are shown, and what happens when they fail.Why package handling matters
Digital signage content is often more than a single image or video. A package may include:- HTML
- CSS
- JavaScript
- Fonts
- Images
- Videos
- JSON data
- Local assets
packages.validate / packages.install / packages.activate commands.
Instead:
- The CMS hosts a
.zip/.wgt(or a remote HTML URL) - The playlist item points at that URL
- The player prepares and shows the widget when that item plays
Core principle
The core principle is:A widget is just another playlist item. Prepare it at play time. If it fails, fall back to brand idle.There is no separate package install / activate / rollback surface in
@tomorrowos/sdk yet.
How widgets appear in policy
Widgets live insidedevice.content.setPolicy playlist items.
Typed playlist fields today:
Example: ZIP / WGT package
type is omitted, the player still treats .zip / .wgt URLs as widgets by extension.
Example: hosted HTML widget (no ZIP)
type: "widget", a normal HTML URL is treated as web content. It still loads in an iframe, but it skips package download / extract / widget cache.
Content type detection
The player resolves item type in this order:- Explicit
item.typeif present - Otherwise infer from the URL:
- image extensions →
image - video extensions →
video .wgt/.zip→widget- else →
web
- image extensions →
Package lifecycle (as implemented)
For.zip / .wgt package URLs, the player does this at play time:
- Resolve content type as
widget - Check local widget cache (
url+version) - If missing, download the archive into staging storage
- Extract into a local widget folder
- Find the entry HTML (
entryFileor defaultindex.html) - Remember the local entry path in
localStorage - Mount the entry in an iframe
- Fade-swap onto the visible content layer
- Keep it on screen for
durationMs(or 20s default) - On failure, stop and show brand idle
- No separate validate / install / activate commands
- No required package manifest
- No staged “ready but inactive” package registry
- No package-level rollback API
- Widgets are not prefetched by the image/video download queue; they are prepared when the item is about to play
Entrypoint handling
Default entry file:entryFile:
- Looks for
{extractDir}/{entryFile} - If missing, searches recursively for that filename (limited depth)
- Fails with an error if the entry file cannot be found
entryFile expects it, or keep the default index.html.
Caching
Widget packages use a local cache index keyed by:- Cache hit → reuse local entry path if the file still exists
- Missing local file → drop that cache entry and download again
- Concurrent prepares for the same key are deduped
version forces a fresh download / extract for the same URL.
How widgets are shown
Widgets are shown in an iframe inside the player content layers.- Local packages launch from a local file URL after extract
- Hosted HTML widgets launch from the remote URL
- Layer handoff uses a fade swap
- Stopping playback clears the active widget iframe
Failure handling
If prepare or mount fails, the player:- Logs the failure
- Stops the current widget / playback path
- Shows brand idle (not a previous-good package restore)
- Download URL is not absolute
http(s) - Archive download failed
- Archive extract failed
- Entry HTML not found
- Widget layer unavailable / playback cancelled mid-prepare
- Runtime / browser incompatibility inside the widget itself
package.failure and no packages.rollback(...) command.
Offline behaviour
- Already-cached widget packages can still launch from local storage
- Uncached packages need network access to download
- Hosted HTML widgets that depend on remote assets may fail offline
- Image / video playlist prefetch does not warm widget packages ahead of time
Security reality
TomorrowOS currently focuses on getting widgets onto screen safely enough for signage use. Several stronger package-security controls are not shipped yet:
Practical guidance for CMS builders:
- Only publish packages you trust
- Prefer self-contained packages with a clear
index.html - Avoid relying on unpackaged remote scripts when offline matters
- Use
versionwhen you intentionally replace package contents at the same URL
Capability checks
There is no dedicatedpackage.zip capability flag today.
Practical checks before relying on widgets:
- Confirm the device can receive policy:
- Publish a small test widget playlist and verify it renders on the real device / firmware.
setPolicy itself is supported.
API surface used for widgets
Widgets are published through the normal content policy path:BrightSign player zip vs content widget zip
Do not confuse these two:
This guide is about content widgets, not the BrightSign player install zip.
Practical checklist
Before shipping a widget to screens:- Package has a clear entry HTML (
index.htmlor explicitentryFile) - Playlist item uses absolute
http(s)URL -
type: "widget"is set when the URL is not obviously.zip/.wgt -
durationMsis set intentionally -
versionis bumped when package bytes change at the same URL - Widget is tested on the target OS / firmware
- Failure path is acceptable (brand idle, not a custom package rollback)
Goal
TomorrowOS should make packaged signage content practical to deploy across supported runtimes. Today that means:- Publish widgets as playlist items
- Let the player download, extract, cache and iframe them
- Fall back to brand idle when preparation fails
device.content.setPolicy.