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
launchers.draglaunchers{ inside, count, kinds }targeted: an OS drag entered or left this instance's drop rect; emitted on target change only
launchers.droplaunchers{ items }targeted: the drop landed on this instance; the items are already in the widget's store
launchers.changedlaunchers{} pingthe widget's store changed (a drop, a forget, an uninstall); every frame of the widget type
clipboard.changedclipboard{} pingthe clipboard history or the pause changed; re-list
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. From app 0.4.2, open is true only on the bar whose caret opened it
notificationCenter.changedtaskbar + kind bar{ count, open, dnd, access }, never the notificationsevery change of the count, the access or the Do not disturb mode, and every open or close of the host's notification center; open is true only on the bar whose bell opened it. App 0.4.2+
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. Since app 0.4.0 theme colors fade on screen, and the handlers run once the fade has settled (at once when there is no fade).

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, launchers.changed, clipboard.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.