Reference

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

TopicNeedsPayloadDelivery
time.ticktime{ epochMs }second-aligned wall clock
settings.changednothingeffective settings objecton every edit, including live previews from the settings dialog
theme.tokensnothingfull --dd-* token mapon every theme change, to every widget; the SDK re-applies it to :root
cursor.hovernothing{ inside }enter and leave over the widget-plus-pane footprint
cursor.movenothing{ x, y } viewport fractionsroughly 30 Hz while the cursor is inside
desktop.changeddesktop{} pingdesktop item set changed; re-list
folders.changedfolders{} pinga granted folder changed; only to affected instances; debounced ~400 ms
system.vitalssystemSystemVitals1 Hz sampler snapshot
media.nowPlayingmediaNowPlayingchange-driven, not fixed-rate
audio.spectrumaudio{ bins: number[] }~30 Hz while media plays (20 Hz low-spec)
widgets.changedcontrol{} pingsome approved instance's state changed; re-list
popout.changedmanifest popout{ active, side? }open, reshape, close, including host force-closes; both documents
popout.messagemanifest popoutwhatever the other document sentmain and pane relay, up to 8 KB per message
peers.dragpeers{ active, self }drag lifecycle, to same-type frames
peers.droppeers{ data, x, y }targeted: only the drop target sees the payload
peers.endpeers{ 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.