Skip to Content
UI API

UI API

Extend the app’s UI through dmn.ui.

UI API is only available in main window. Calling from overlay shows a warning and does nothing.

Context Menu

Key Menu (addKeyMenuItem)

Add items to the menu shown when right-clicking a key.

const menuId = dmn.ui.contextMenu.addKeyMenuItem({ id: 'copy-keycode', label: 'Copy Key Code', onClick: (context) => { navigator.clipboard.writeText(context.keyCode); }, });

Context Object:

PropertyTypeDescription
keyCodestringCanonical slot identifier (e.g. “D”, “LEFT CTRL+Z”, “F|NUMPAD 4”)
idstringStable element ID (UUID). Keeps identifying the same key across reorders and mode switches
indexnumberKey index. Deprecated: only valid for the current snapshot, use id for identity
positionKeyPositionKey position info
modestringCurrent key mode

Grid Menu (addGridMenuItem)

Add items to the menu shown when right-clicking empty grid space. So the menu stays short no matter how many plugins are installed, items added here are grouped under a single Plugins submenu. They keep their registration order and position does not apply.

dmn.ui.contextMenu.addGridMenuItem({ id: 'add-timer', label: 'Add Timer', onClick: (context) => { createTimer(context.position); }, });

Context Object:

PropertyTypeDescription
position{ dx, dy }Click position (grid coordinates)
modestringCurrent key mode
{ id: "my-menu", // Unique ID within plugin label: "Menu Item", // Display text position: "bottom", // "top" | "bottom" (default: bottom, key menu only) // Conditional visibility visible: (context) => context.mode === "4key", // Conditional disable disabled: (context) => context.position.count === 0, // Click handler onClick: async (context) => { // ... }, }

visible and disabled accept a boolean or a function of the context. A function that throws is treated as hidden / disabled (the rest of the menu still renders), and non-function values are coerced by truthiness. 0, "", and null hide an item when passed to visible, and leave it enabled when passed to disabled.

// Update menu dmn.ui.contextMenu.updateMenuItem(menuId, { label: 'New Label', disabled: true, }); // Remove specific menu dmn.ui.contextMenu.removeMenuItem(menuId); // Remove all menus from this plugin dmn.ui.contextMenu.clearMyMenuItems();

Display Element

displayElement is a low-level (primitive) API, not recommended for direct use. For adding custom UI elements to grid and overlay, strongly recommend using the declarative defineElement approach.

Add custom UI elements to grid and overlay.

Basic Usage

const panel = dmn.ui.displayElement.add({ html: `<div style="background: #000; color: #fff; padding: 16px;"> Hello! </div>`, position: { x: 100, y: 100 }, draggable: true, }); // Remove panel.remove();

State-based Template

const panel = dmn.ui.displayElement.add({ position: { x: 100, y: 100 }, state: { value: 0, history: [] }, template: (state, { html }) => html` <div style="background: #000; color: #fff; padding: 16px;"> <strong>${state.value}</strong> <div style="display: flex; gap: 2px;"> ${state.history.map( (v) => html` <span style="width: 4px; height: ${v}px; background: #86EFAC;" ></span> `, )} </div> </div> `, }); // State update → auto re-render panel.setState({ value: 42, history: [10, 20, 30] });

Event Handlers

dmn.ui.displayElement.add({ html: `<div>Click me</div>`, position: { x: 100, y: 100 }, // Direct function (recommended) onClick: async () => { console.log('Clicked!'); }, onPositionChange: async (pos) => { await dmn.plugin.storage.set('position', pos); }, onDelete: async () => { console.log('Deleted!'); }, });

Instance Methods

MethodDescription
setState(updates)Update state (re-renders template)
setData(updates)Update state (setState alias)
getState()Get current state
setText(selector, text)Set element text
setHTML(selector, html)Set element HTML
setStyle(selector, styles)Apply styles
addClass(selector, ...classes)Add classes
removeClass(selector, ...classes)Remove classes
toggleClass(selector, className)Toggle class
query(selector)Find element (including Shadow DOM)
update(config)Update metadata
remove()Remove element

Anchor

Pins an element to a specific key:

dmn.ui.displayElement.add({ html: `<div>→</div>`, position: { x: 0, y: 0 }, anchor: { keyCode: 'D', offset: { x: 70, y: 0 }, }, });

anchor.keyCode is a canonical slot identifier. For a multi-key slot, use its canonical string (e.g. "LEFT CTRL+Z" for match: 'all', "F|NUMPAD 4" for match: 'any'). See Canonical slot identifiers.

Cleanup

Remove every display element owned by the current plugin during cleanup.

dmn.plugin.registerCleanup(() => { dmn.ui.displayElement.clearMyElements(); });

Dialog

Modal dialogs for user interaction.

alert

Show simple alert. The returned Promise resolves when the user presses the confirm button or the dialog is dismissed.

await dmn.ui.dialog.alert('Saved!'); // Runs after the user confirms await dmn.ui.dialog.alert('Task complete', { confirmText: 'OK' });

confirm

Show confirm/cancel dialog.

Options: confirmText (confirm button label), cancelText (cancel button label), danger (renders destructive confirmations in danger colors)

const ok = await dmn.ui.dialog.confirm('Proceed?'); if (ok) { // Confirm clicked } // Destructive action const confirmed = await dmn.ui.dialog.confirm('All data will be deleted.', { danger: true, confirmText: 'Delete', cancelText: 'Keep', });

custom

Shows a custom HTML dialog. Use the Components API to build controls that match the app UI.

const formHtml = ` <div class="flex flex-col gap-[12px]"> ${dmn.ui.components.formRow( 'Name', dmn.ui.components.input({ id: 'name' }), )} ${dmn.ui.components.formRow( 'Theme', dmn.ui.components.dropdown({ id: 'theme', options: [ { label: 'Dark', value: 'dark' }, { label: 'Light', value: 'light' }, ], }), )} </div> `; const confirmed = await dmn.ui.dialog.custom(formHtml, { confirmText: 'Save', cancelText: 'Cancel', showCancel: true, });

Components

These helpers return HTML strings for dialogs and display elements.

button

const saveButton = dmn.ui.components.button('Save', { variant: 'primary', onClick: () => console.log('Save clicked'), });

checkbox

const enabledCheckbox = dmn.ui.components.checkbox({ checked: true, id: 'enabled', onChange: (checked) => console.log('Enabled:', checked), });

input

const volumeInput = dmn.ui.components.input({ type: 'number', value: 50, min: 0, max: 100, width: 100, id: 'volume', onChange: (value) => console.log('Volume:', value), });
const themeDropdown = dmn.ui.components.dropdown({ options: [ { label: 'Dark', value: 'dark' }, { label: 'Light', value: 'light' }, ], selected: 'dark', id: 'theme', onChange: (value) => console.log('Theme:', value), });

formRow

Pairs a label with a component.

const row = dmn.ui.components.formRow('Volume', volumeInput);

panel

Wraps content for use inside a display element. Do not use this wrapper inside a dialog.

const panelHtml = dmn.ui.components.panel(row, { title: 'Settings', width: 400, }); dmn.ui.displayElement.add({ html: panelHtml, position: { x: 100, y: 100 }, });

Color Picker

pickColor(options)

Opens the app color picker.

dmn.ui.pickColor({ initialColor: '#ff0000', onColorChange: (color) => { console.log('Preview color:', color); }, onColorChangeComplete: (color) => { console.log('Committed color:', color); }, referenceElement: document.getElementById('color-button'), onClose: () => console.log('Picker closed'), });

Use position: { x, y } when there is no reference element. The optional id prevents duplicate pickers for the same owner.