Advanced Guide
Imperative Style Plugins
While defineElement is recommended, direct DOM manipulation is still supported.
Direct DOM Manipulation
// @id my-legacy-plugin
if (dmn.window.type !== "overlay") return;
// Create and append element
const panel = document.createElement("div");
panel.className = "my-panel";
panel.innerHTML = `<h1>Hello</h1>`;
document.body.appendChild(panel);
// Register cleanup
dmn.plugin.registerCleanup(() => {
panel.remove();
});When directly manipulating DOM, you must clean up elements with
dmn.plugin.registerCleanup.
Direct Style Sheet Injection
// @id style-injection
if (dmn.window.type !== "overlay") return;
const style = document.createElement("style");
style.textContent = `
.my-panel {
background: rgba(0, 0, 0, 0.8);
padding: 16px;
border-radius: 8px;
}
`;
document.head.appendChild(style);
dmn.plugin.registerCleanup(() => {
style.remove();
});Event Bus Usage
For low-level event handling that reacts to key inputs:
// @id event-bus-example
// onKeyState - Only registered keys
const unsub1 = dmn.keys.onKeyState(({ key, state }) => {
if (state === "DOWN") {
console.log(`Key ${key} pressed`);
}
});
// onRawInput - All inputs (keyboard + mouse)
const unsub2 = dmn.keys.onRawInput(({ device, label, state }) => {
console.log(`[${device}] ${label} ${state}`);
});
dmn.plugin.registerCleanup(() => {
unsub1();
unsub2();
});Debugging Tips
Opening DevTools
// Open DevTools for all windows
dmn.window.openDevtoolsAll();State Inspection
// Current window type
console.log("Window type:", dmn.window.type);
// Current settings
dmn.settings.get().then((s) => console.log("Settings:", s));
// Key mapping
dmn.keys.get().then((k) => console.log("Keys:", k));
// Storage key list
dmn.plugin.storage.keys().then((keys) => console.log("Stored keys:", keys));Error Tracking
// Global error handler
window.addEventListener("error", (e) => {
console.error("Plugin error:", e.error);
});
window.addEventListener("unhandledrejection", (e) => {
console.error("Unhandled promise:", e.reason);
});Security Considerations
No Sandboxing
Plugins run with the same privileges as the app:
- Full access to all dmn.* APIs
- Full access to DOM
- Can make external network requests
Never run untrusted code. Always review plugin code before use.
External Requests
// Regular fetch example (may fail depending on server CORS policy)
const response = await fetch("https://api.example.com/data", {
headers: { "Content-Type": "application/json" },
});
const data = await response.json();Performance Optimization
State Update Optimization
Use setState for state updates in defineElement:
dmn.plugin.defineElement({
name: "Optimized Panel",
template: (state, settings, { html }) => html`
<div>Count: ${state.count ?? 0}</div>
`,
onMount: ({ setState }) => {
// Update multiple values at once
setState({
count: 0,
history: [],
lastUpdate: Date.now(),
});
},
});Avoiding Unnecessary Renders
Only call setState when state actually changes:
onMount: ({ setState, getSettings }) => {
let lastKps = 0;
const interval = setInterval(() => {
const newKps = calculateKps();
// Only update when value changes
if (newKps !== lastKps) {
lastKps = newKps;
setState({ kps: newKps });
}
}, 100);
return () => clearInterval(interval);
};Multi-Window Patterns
Control Overlay from Main
// In main window
if (dmn.window.type === "main") {
dmn.bridge.sendTo("overlay", "UPDATE_OVERLAY_VALUE", { value: 42 });
}