4.8 KiB
JavaScript Annotated Examples
Summary
A growing collection of fragments of example code for ComfyUI custom nodes JavaScript development.
Right Click Menus
Background Menu
The main background menu (right-click on the canvas) is generated by a call to LGraph.getCanvasMenuOptions. One way to add your own menu options is to hijack this call:
/* in setup() */
const original_getCanvasMenuOptions = LGraphCanvas.prototype.getCanvasMenuOptions;
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
// get the basic options
const options = original_getCanvasMenuOptions.apply(this, arguments);
options.push(null); // inserts a divider
options.push({
content: "The text for the menu",
callback: async () => {
// do whatever
}
})
return options;
}
Node Menu
When you right click on a node, the menu is similarly generated by node.getExtraMenuOptions. But instead of returning an options object, this one gets it passed in…
/* in beforeRegisterNodeDef() */
if (nodeType?.comfyClass=="MyNodeClass") {
const original_getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
nodeType.prototype.getExtraMenuOptions = function(_, options) {
original_getExtraMenuOptions?.apply(this, arguments);
options.push({
content: "Do something fun",
callback: async () => {
// fun thing
}
})
}
}
Submenus
If you want a submenu, provide a callback which uses LiteGraph.ContextMenu to create it:
function make_submenu(value, options, e, menu, node) {
const submenu = new LiteGraph.ContextMenu(
["option 1", "option 2", "option 3"],
{
event: e,
callback: function (v) {
// do something with v (=="option x")
},
parentMenu: menu,
node:node
}
)
}
/* ... */
options.push(
{
content: "Menu with options",
has_submenu: true,
callback: make_submenu,
}
)
Capture UI Events
This works just like you'd expect - find the UI element in the DOM and add an eventListener. setup() is a good place to do this, since the page has fully loaded. For instance, to detect a click on the 'Queue' button:
function queue_button_pressed() { console.log("Queue button was pressed!") }
document.getElementById("queue-button").addEventListener("click", queue_button_pressed);
Detect When a Workflow Starts
This is one of many api events:
import { api } from "../../scripts/api.js";
/* in setup() */
function on_execution_start() {
/* do whatever */
}
api.addEventListener("execution_start", on_execution_start);
Detect an Interrupted Workflow
A simple example of hijacking the api:
import { api } from "../../scripts/api.js";
/* in setup() */
const original_api_interrupt = api.interrupt;
api.interrupt = function () {
/* Do something before the original method is called */
original_api_interrupt.apply(this, arguments);
/* Or after */
}
Catch Clicks on Your Node
node has a mouseDown method you can hijack. This time we're careful to pass on any return value.
async nodeCreated(node) {
if (node?.comfyClass === "My Node Name") {
const original_onMouseDown = node.onMouseDown;
node.onMouseDown = function( e, pos, canvas ) {
alert("ouch!");
return original_onMouseDown?.apply(this, arguments);
}
}
}
Key Concepts
Method Hijacking
Many of these examples use a common pattern of "hijacking" existing methods:
- Save a reference to the original method
- Replace the method with your own implementation
- Call the original method from within your implementation
- Add your custom logic before or after the original call
Event Handling
ComfyUI provides several event systems:
- DOM Events: Standard browser events on UI elements
- API Events: Custom events fired by the ComfyUI API
- Node Events: Events specific to node interactions
- Graph Events: Events related to workflow changes
Common Patterns
- Menu Extension: Adding custom options to context menus
- Event Interception: Capturing and responding to user actions
- UI Enhancement: Adding functionality to existing interface elements
- Workflow Monitoring: Tracking workflow execution state
Usage Notes
- These examples assume you're working within the ComfyUI extension system
- Most hijacking should be done in the
setup()method after the page loads - Always preserve the original method's behavior unless you specifically need to change it
- Be careful with event listeners to avoid memory leaks
- Test thoroughly as hijacking can affect other extensions