A widget declares which contexts it can render — named placements in the notch’s world. The engine decides what’s present and where it goes; your widget just states what forms it has. Modders never deal with the engine’s internals (variants, regions, envelopes) — contexts are the whole surface.
"widgets": [
{
"id": "media_player", "label": "Media", "urgency": "ambient",
"contexts": {
"notch_single": { "display": "solo" },
"notch_secondary": { "display": "tail", "with": "clock" },
"expanded": { "display": "card" },
"peek": { "display": "peek" },
"transient": { "display": "hud", "timeout_ms": 2200 }
},
"preview": { "title": "Song", "artist": "Artist", "playing": true }
}
]
The context catalog
| Context | Where it renders | Notes |
|---|---|---|
notch_single | the collapsed notch — alone, or as the LEADING pill of a dual-pill notch | the compact pill form |
notch_secondary | dual-pill notch, trailing seat | optional "with": "<widget id>" — a companion form used only beside that leader |
expanded | a 370×100 slot in the expanded panel | placeable on pages by the user |
peek | the tiny idle peek strip | ordinary stack ambient |
transient | the widened transient HUD | requires timeout_ms; may set "envelope": "tall" for the 265×60 tall pill |
Urgency
One of three tiers, and it’s a class, not a priority number:
transient— interrupts briefly, always wins, retracts on its own (volume, connect events, low battery).event— something happened worth surfacing (a notification, a ring).ambient— steady-state presence (media, a call, a clock).
Within a tier, order comes from the user’s priority stack. Across tiers, transient > event > ambient, always. You declare honestly; the user arbitrates.
The degradation ladder
Context order in your declaration is the fallback ladder. When space
tightens — a second widget arrives, the notch shrinks, a transient
interrupts — the solver walks your contexts in declared order and takes the
first that fits the situation. Declare from most expressive to most
compact. A widget with only expanded simply never competes for the notch.
Transients auto-retract
A transient context’s timeout_ms is enforced by the host: after
set_presence(id, true, data), the widget retracts by itself when the
timeout lapses. Push fresh data to extend the moment (the volume slider
staying up while you keep scrolling); never fake a transient with an
ambient plus your own timer.
Live previews
preview is a demo payload for the settings picker. There are no canned
screenshots — the picker renders your real display tree over this data
(or over the widget’s live last-known data once it has run). Make the
preview payload representative: it’s your widget’s face in the store.