Skip to Content
Settings System

Settings System (defineSettings)

dmn.plugin.defineSettings is a versatile settings management API for defining settings independent of panels.

When to Use?

Use CaseDescription
Global settings for multiple panelsCommon settings shared by multiple defineElement panels
Independent feature settingsSettings for features without panels like notifications, shortcuts, API integrations
Plugin environment settingsOptions affecting the entire plugin

Basic Usage

// @id my-plugin const pluginSettings = dmn.plugin.defineSettings({ settings: { connectionSection: { type: "section", label: "Connection", }, apiKey: { type: "string", default: "", label: "API Key", }, theme: { type: "select", options: [ { value: "dark", label: "Dark" }, { value: "light", label: "Light" }, ], default: "dark", label: "Theme", }, behaviorSection: { type: "section", label: "Behavior", }, enabled: { type: "boolean", default: true, label: "Enabled", }, // Conditional visibility: static boolean or function, shown only while enabled is true advancedOption: { type: "number", default: 5, label: "Advanced Option", visible: (settings) => settings.enabled, }, }, }); // section starts a new card. (The legacy divider type was removed, so existing divider entries are ignored.) // Get settings values const current = pluginSettings.get(); console.log("API Key:", current.apiKey); // Change settings values try { await pluginSettings.set({ theme: "light" }); } catch (error) { console.error("Failed to save settings:", error); } // Open settings panel (default: property panel) const confirmed = await pluginSettings.open(); if (confirmed) { console.log("Settings saved!"); }

defineSettings uses the same section contract as defineElement: a section starts a new card, its optional label appears above the card, and its key is excluded from stored/default values and callbacks. section.visible controls the entire group. Sections with no renderable value settings are hidden entirely; only when no value setting is renderable anywhere does the settings UI show its standard empty state. Panel and modal modes have identical semantics, including fail-closed visibility evaluation.

API Reference

defineSettings(definition)

interface PluginSettingsDefinition { // Settings schema (same format as defineElement) settings: Record<string, PluginSettingSchema>; // i18n messages (optional) messages?: Record<string, Record<string, string>>; // Settings UI mode (optional, default: "panel") settingsUI?: "panel" | "modal"; // Callback on settings change (optional) onChange?: (newSettings, oldSettings) => void; }

Return Value (PluginSettingsInstance)

MethodDescription
get()Get current settings
set(updates)Change settings (auto-save)
open()Open settings panel (default: property panel)
reset()Reset to defaults
subscribe(listener)Subscribe to settings changes

set() and reset() reject their promises if saving fails. The failed edit is restored from saved settings without overwriting a newer edit started during recovery. Change callbacks and subscriber notifications run only after a successful save. A property panel save failure shows an error and resolves open() to false; a modal save failure rejects open().

onChange vs subscribe()

Both detect settings changes but have different purposes:

onChangesubscribe()
Declaration locationInside defineSettings() definitionAnywhere
Can unsubscribe❌ No✅ Yes
Count1 onlyMultiple
PurposeCore/essential logicConditional/temporary logic
const settings = dmn.plugin.defineSettings({ settings: { apiKey: { type: "string", default: "", label: "API Key" } }, // onChange: Always executes (cannot unsubscribe) onChange: (newSettings, oldSettings) => { if (newSettings.apiKey !== oldSettings.apiKey) { reconnectAPI(newSettings.apiKey); } }, }); // subscribe: Subscribe only when needed, unsubscribe later function openPreviewPanel() { const panel = createPanel(); const unsubscribe = settings.subscribe((newSettings) => { panel.update(newSettings); }); panel.onClose = () => unsubscribe(); }

i18n Support

const pluginSettings = dmn.plugin.defineSettings({ settings: { volume: { type: "number", default: 50, min: 0, max: 100, label: "settings.volume", // Message key }, }, messages: { ko: { "settings.volume": "볼륨", }, en: { "settings.volume": "Volume", }, }, });

Practical Examples

Global Settings + defineElement Integration

// @id kps-plugin // Define global settings const globalSettings = dmn.plugin.defineSettings({ settings: { defaultColor: { type: "color", default: "#86EFAC", label: "Default Color", }, refreshRate: { type: "number", default: 50, min: 10, max: 200, label: "Refresh Rate (ms)", }, }, }); // Define panel dmn.plugin.defineElement({ name: "KPS Panel", contextMenu: { create: "Create KPS Panel", delete: "Delete KPS Panel", items: [ { label: "Global Settings", onClick: () => globalSettings.open(), }, ], }, // Instance-specific settings settings: { showGraph: { type: "boolean", default: true, label: "Show Graph" }, graphColor: { type: "color", default: "#86EFAC", label: "Graph Color", visible: (s) => s.showGraph, }, }, template: (state, instanceSettings, { html }) => { const global = globalSettings.get(); return html` <div style="color: ${global.defaultColor};">KPS: ${state.kps ?? 0}</div> `; }, onMount: ({ setState, onHook }) => { const global = globalSettings.get(); let count = 0; onHook("key", ({ state }) => { if (state === "DOWN") count++; }); const interval = setInterval(() => { setState({ kps: count }); count = 0; }, global.refreshRate); return () => clearInterval(interval); }, });

Adding Settings to the Grid Menu

You can also add a settings menu without any panel:

// @id settings-only-plugin const pluginSettings = dmn.plugin.defineSettings({ settings: { volume: { type: "number", default: 50, min: 0, max: 100, label: "Volume" }, notifications: { type: "boolean", default: true, label: "Notifications" }, }, }); // Add to the right-click menu on empty grid space dmn.ui.contextMenu.addGridMenuItem({ id: "my-plugin-settings", label: "Plugin Settings", onClick: () => pluginSettings.open(), }); dmn.plugin.registerCleanup(() => { dmn.ui.contextMenu.clearMyMenuItems(); });

Reacting to Settings Changes

// @id data-fetcher const fetcherSettings = dmn.plugin.defineSettings({ settings: { apiEndpoint: { type: "string", default: "https://api.example.com", label: "API Endpoint", }, refreshInterval: { type: "number", default: 5000, min: 1000, max: 60000, label: "Refresh Interval (ms)", }, autoRefresh: { type: "boolean", default: true, label: "Auto Refresh", }, }, }); let fetchInterval = null; function startFetching() { const { refreshInterval, autoRefresh, apiEndpoint } = fetcherSettings.get(); if (fetchInterval) { clearInterval(fetchInterval); fetchInterval = null; } if (!autoRefresh) return; fetchInterval = setInterval(async () => { const response = await fetch(apiEndpoint); const data = await response.json(); console.log("Data:", data); }, refreshInterval); } // Restart the interval when settings change fetcherSettings.subscribe((newSettings, oldSettings) => { if ( newSettings.apiEndpoint !== oldSettings.apiEndpoint || newSettings.refreshInterval !== oldSettings.refreshInterval || newSettings.autoRefresh !== oldSettings.autoRefresh ) { startFetching(); } }); startFetching(); dmn.plugin.registerCleanup(() => { if (fetchInterval) clearInterval(fetchInterval); });

Comparison with defineElement’s onSettingsChange

FeaturedefineElementdefineSettings
Settings change handlingonSettingsChange(callback)onChange + subscribe()
UnsubscribingAutomatic (on unmount)Manual, via subscribe() return value
Where to useInside onMount onlyAnywhere
TargetInstance-specific settingsGlobal/standalone settings

Automatic Behavior

FeatureDescription
Automatic UI generationProperty panel/modal generated from the settings schema
Automatic storage handlingSaved to and restored from plugin.storage
i18n supportIntegrates with messages
Per-type componentsboolean→checkbox, color→color picker, and so on
Automatic panel syncAll panels of the same plugin re-render when settings change