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. A peek (every widget concealed by the user) keeps widgets mounted, so events keep flowing.

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 and every per-widget style edit (previews and the revert included), 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
windows.changedtaskbar + kind bar{ windows }, the full dd.windows.list snapshotchange-driven: a window opened or closed, focus moved, a title changed, minimize state flipped, a window changed monitors; the driver for a taskbar's focus indicator
pins.changedtaskbar + kind bar{ pins }, the full dd.pins.list snapshotchange-driven: a pin made or removed, or the pinned order moved
bar.strip.changedtaskbar + kind bar{ items }, the full dd.bar.strip.list snapshotchange-driven: a row added or dropped, a pin made or removed, a move on any bar; the render order for a taskbar, to the bar document and its pane
bar.flyout.changedtaskbar + kind bar{ open, key }the preview flyout's real state, on every open, switch and close, whoever caused it; the bar document only
systray.changedtaskbar + kind bar{ count, open }the notification area's state, on every tray change and every open or close of the host list; the driver for a caret's pressed look
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. Per-widget style edits (the Style tab of your widget's settings window) arrive the same way, live previews included. 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.

The taskbar streams (windows.changed, pins.changed, bar.strip.changed, bar.flyout.changed, systray.changed) are the whole truth for a bar: never move a focus indicator, a pressed caret or a row from your own click, follow the stream. dd.bar.store() does that for you; the shapes and the seed calls are on the bar widgets page.