Skip to Content
DocumentationLifecycle

Lifecycle

Plugins have a lifecycle of load, execute, and unload. Proper cleanup implementation is essential to prevent resource leaks.

Plugin Execution Timing

Plugin code executes in these situations:

  1. App start - App launches with JS plugins enabled
  2. Plugin activation - JS plugins toggle turned on in settings
  3. Plugin add/modify - Plugin list changes
  4. Reload - Reload button clicked in settings

Cleanup Execution Timing

Registered cleanup functions are automatically called in these situations:

  1. Plugin deactivation - JS plugins toggle turned off
  2. Plugin removal - Deleted from plugin list
  3. Reload - Cleanup before new code execution
  4. App exit - Window closed

Registering Cleanup (registerCleanup)

Register cleanup tasks with dmn.plugin.registerCleanup().

Register all cleanup at once:

// @id my-plugin const panel = document.createElement("div"); document.body.appendChild(panel); const timer = setInterval(() => console.log("tick"), 1000); const unsubKeys = dmn.keys.onKeyState(() => {}); const unsubSettings = dmn.settings.onChanged(() => {}); // Register all cleanup at once dmn.plugin.registerCleanup(() => { clearInterval(timer); unsubKeys(); unsubSettings(); panel.remove(); });

Advanced Usage

Register separately by resource in complex plugins:

// DOM element const panel = document.createElement("div"); document.body.appendChild(panel); dmn.plugin.registerCleanup(() => panel.remove()); // Timer const timer = setInterval(() => {}, 1000); dmn.plugin.registerCleanup(() => clearInterval(timer)); // Event subscription const unsub = dmn.keys.onKeyState(() => {}); dmn.plugin.registerCleanup(() => unsub());

All registered cleanup functions execute in order.

Resources Requiring Cleanup

ResourceCleanup Method
addEventListenerremoveEventListener
setIntervalclearInterval
setTimeoutclearTimeout
DOM element creationelement.remove()
Global variablesdelete window.varName
Event subscriptionsCall unsubscribe function
WebSocketconnection.close()

Practical Examples

Complete Cleanup Example

// @id complete-cleanup-example if (dmn.window.type !== "overlay") return; // 1. DOM elements const style = document.createElement("style"); style.textContent = `.my-panel { background: #000; color: #fff; }`; document.head.appendChild(style); const panel = document.createElement("div"); panel.className = "my-panel"; document.body.appendChild(panel); // 2. Timer const intervalId = setInterval(() => { panel.textContent = new Date().toLocaleTimeString(); }, 1000); // 3. Event listener const handleKeyDown = (e) => console.log("Key:", e.key); window.addEventListener("keydown", handleKeyDown); // 4. API subscriptions const unsubKeyState = dmn.keys.onKeyState(({ key, state }) => { console.log(`${key}: ${state}`); }); const unsubSettings = dmn.settings.onChanged(({ changed }) => { console.log("Settings changed:", changed); }); // 5. Global handler window.myPluginHandler = () => { console.log("Handler called"); }; // Cleanup all resources dmn.plugin.registerCleanup(() => { // Timer clearInterval(intervalId); // Event listener window.removeEventListener("keydown", handleKeyDown); // API subscriptions unsubKeyState(); unsubSettings(); // DOM elements panel.remove(); style.remove(); // Global variable delete window.myPluginHandler; console.log("[my-plugin] Cleanup completed"); });

defineElement Cleanup

When using defineElement, return cleanup function from onMount:

dmn.plugin.defineElement({ name: "My Panel", onMount: ({ setState, onHook }) => { const timestamps = []; // Events registered with onHook are automatically cleaned up onHook("key", ({ state }) => { if (state === "DOWN") { timestamps.push(Date.now()); } }); // Manually created resources need cleanup in the cleanup function const interval = setInterval(() => { const now = Date.now(); const recent = timestamps.filter((t) => t > now - 1000); timestamps.length = 0; timestamps.push(...recent); setState({ kps: timestamps.length }); }, 100); // Return cleanup function return () => { clearInterval(interval); console.log("Panel cleanup"); }; }, });

Async Context Preservation

Plugin context is automatically preserved even when using async/await:

// @id async-example async function initialize() { // Plugin context preserved after await const settings = await dmn.plugin.storage.get("settings"); const data = await fetch("/api/data").then((r) => r.json()); // ... }