166 lines
4.8 KiB
Markdown
166 lines
4.8 KiB
Markdown
# 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:
|
|
|
|
```javascript
|
|
/* 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…
|
|
|
|
```javascript
|
|
/* 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:
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
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:
|
|
|
|
1. Save a reference to the original method
|
|
2. Replace the method with your own implementation
|
|
3. Call the original method from within your implementation
|
|
4. 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
|