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:
- App start - App launches with JS plugins enabled
- Plugin activation - JS plugins toggle turned on in settings
- Plugin add/modify - Plugin list changes
- Reload - Reload button clicked in settings
Cleanup Execution Timing
Registered cleanup functions are automatically called in these situations:
- Plugin deactivation - JS plugins toggle turned off
- Plugin removal - Deleted from plugin list
- Reload - Cleanup before new code execution
- App exit - Window closed
Registering Cleanup (registerCleanup)
Register cleanup tasks with dmn.plugin.registerCleanup().
Basic Usage (Recommended)
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
| Resource | Cleanup Method |
|---|---|
addEventListener | removeEventListener |
setInterval | clearInterval |
setTimeout | clearTimeout |
| DOM element creation | element.remove() |
| Global variables | delete window.varName |
| Event subscriptions | Call unsubscribe function |
| WebSocket | connection.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());
// ...
}