Event reference
Every event topic a widget can subscribe to, with payload shapes, permission gating, and delivery rules.
Widgets subscribe to events through the SDK's on* methods (dd.time.onTick,
dd.system.onVitals, ...); every one returns an unsubscribe function. Events
deliver only while the widget is mounted and holds the gating permission;
hidden widgets unmount entirely and receive nothing.
The topics
| Topic | Needs | Payload | Delivery |
|---|---|---|---|
time.tick | time | { epochMs } | second-aligned wall clock |
settings.changed | nothing | effective settings object | on every edit, including live previews from the settings dialog |
theme.tokens | nothing | full --dd-* token map | on every theme change, to every widget; the SDK re-applies it to :root |
cursor.hover | nothing | { inside } | enter and leave over the widget-plus-pane footprint |
cursor.move | nothing | { x, y } viewport fractions | roughly 30 Hz while the cursor is inside |
desktop.changed | desktop | {} ping | desktop item set changed; re-list |
folders.changed | folders | {} ping | a granted folder changed; only to affected instances; debounced ~400 ms |
system.vitals | system | SystemVitals | 1 Hz sampler snapshot |
media.nowPlaying | media | NowPlaying | change-driven, not fixed-rate |
audio.spectrum | audio | { bins: number[] } | ~30 Hz while media plays (20 Hz low-spec) |
widgets.changed | control | {} ping | some approved instance's state changed; re-list |
popout.changed | manifest popout | { active, side? } | open, reshape, close, including host force-closes; both documents |
popout.message | manifest popout | whatever the other document sent | main and pane relay, up to 8 KB per message |
peers.drag | peers | { active, self } | drag lifecycle, to same-type frames |
peers.drop | peers | { data, x, y } | targeted: only the drop target sees the payload |
peers.end | peers | { accepted, outside } | targeted to the drag's source |
Payload notes
settings.changed carries the effective settings. While the user tries
values in the settings dialog it fires with live-previewed (unsaved) values,
and once more with the stored values if they dismiss; treat every event the
same and re-apply. dd.settings.get() always returns the persisted state.
theme.tokens is delivered to every widget, subscribed or not, and the
SDK re-applies the map to :root automatically, so plain CSS needs nothing.
Subscribe (via dd.theme.onChange) only when you resolve token values in
JS; never cache across it.
cursor.hover and cursor.move work for non-interactive widgets too:
the window stays click-through, you get the signal without capturing input.
While a popout is open the footprint is the bounding box of widget plus pane
(crossing the gap never flickers a leave), both documents receive hover,
and move goes to the document the cursor is actually over. For simple
element hover, skip the subscription: mark elements data-dd-hover and the
SDK toggles a dd-hover class.
The {} pings (desktop.changed, folders.changed, widgets.changed)
carry no data on purpose: they tell you your last listing is stale, and the
matching list call is the read path.
system.vitals: the payload shape and its nullability contract are on
the system page. Every field
fails independently; render nulls as absent, not zero.
media.nowPlaying: shape, the session selection policy, and the local
position interpolation recipe are on
the media page. Seed
with dd.media.status().
audio.spectrum: 500 loudness bands, the silence marker frame, the
demand-driven capture rule, and the render-loop contract are on
the audio section.
popout.changed and popout.message: lifecycle causes and the 8 KB
relay cap are on
the popouts page.
peers.*: the full drag lifecycle, including what accepted and
outside mean, is on
the peers section.