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
| 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 and every per-widget style edit (previews and the revert included), 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 |
launchers.drag | launchers | { inside, count, kinds } | targeted: an OS drag entered or left this instance's drop rect; emitted on target change only |
launchers.drop | launchers | { items } | targeted: the drop landed on this instance; the items are already in the widget's store |
launchers.changed | launchers | {} ping | the widget's store changed (a drop, a forget, an uninstall); every frame of the widget type |
clipboard.changed | clipboard | {} ping | the clipboard history or the pause changed; re-list |
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 |
windows.changed | taskbar + kind bar | { windows }, the full dd.windows.list snapshot | change-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.changed | taskbar + kind bar | { pins }, the full dd.pins.list snapshot | change-driven: a pin made or removed, or the pinned order moved |
bar.strip.changed | taskbar + kind bar | { items }, the full dd.bar.strip.list snapshot | change-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.changed | taskbar + kind bar | { open, key } | the preview flyout's real state, on every open, switch and close, whoever caused it; the bar document only |
systray.changed | taskbar + 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.changed | taskbar + kind bar | { count, open, dnd, access }, never the notifications | every 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.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.
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.