index ↓
launchers and overlays
the hub and the system overlay are just webapps. you can build your own and mark them as launchers and overlays.
s1 - two slots
a slot names one installed bundle for a system role. there are two:
- launcher - the home screen, accessed by pressing the menu button 5 times.
- overlay - the ui drawn on top of every webapp: notification toasts, the call banner, the pairing pin, the disconnected banner, the volume indicator.
you set both from the companion phone app. an unset slot means the built-in provider.
s2 - build a launcher
bun create bridgething my-hub --launcher
the scaffold declares "role": "launcher". that does two things: the daemon hides the bundle from
client.webapp.list, and it makes the bundle eligible for the launcher slot.
{
"id": "…",
"name": "My Hub",
"version": "0.1.0",
"role": "launcher"
} everything a home screen needs is on the normal client sdk. the grid is four calls and two events:
// the daemon hides launcher-role bundles from this list
const list = await client.webapp.list();
const current = await client.webapp.current();
const icon = await client.webapp.icon({ id: info.id });
await client.webapp.activate({ id: info.id });
// handle new installations and uninstalls
client.webapp.onWebappInstalled(reload);
client.webapp.onWebappUninstalled(reload); the built-in hub also does bluetooth bonds and pairing name, display brightness, system health, power, and ota progress - we recommend yours does as well.
s3 - build an overlay
bun create bridgething my-overlay --overlay {
"id": "…",
"name": "My Overlay",
"version": "0.1.0",
"overlay": "overlay.js"
} overlay points at one self-contained script. when your bundle holds the overlay slot, the daemon injects
that file into every webapp's document as it loads. any bundle can carry an overlay, but it's usually something a hub
would do.
the daemon prepends one global before your code:
// the daemon prepends this before your bundle
window.__bridgethingOverlay = {
origin: 'http://127.0.0.1:8891',
surfaces: {
notifications: true,
call: true,
pairing: true,
connection: true,
volume: false, // this app draws its own volume indicator
},
}; honor surfaces. an app that draws its own volume indicator sets
volume: false. the daemon cannot enforce this, but you will get duplicate ui if you ignore it.
the starter encodes four things worth keeping:
const cfg = window.__bridgethingOverlay;
if (!cfg || !location.origin.startsWith(cfg.origin)) return; // not a page the daemon served
if (window.__bridgethingOverlayMounted) return; // double injection is a no-op
window.__bridgethingOverlayMounted = true;
if (cfg.surfaces.volume) wireVolume(client);
if (cfg.surfaces.pairing) wirePairing(client); - origin guard - never run in a page the daemon did not serve.
- mount guard - a second injection must be a no-op.
- closed shadow root - so your styles and the host app's cannot reach each other.
- escape-only, capture-phase keys, and only while something is showing.
the hard constraint: overlay.js must be one self-contained file
under 512 KiB.
s4 - assign it, and get back
install your bundle the usual way, then open the companion app and pick it under home screen and overlay.
there are three ways back to the built-in:
- clear the slot in the companion app.
- uninstall the bundle
- factory reset