A growing collection of fragments of example code…
Comfy UI preference settings
Add and read a setting
import { app } from "../../scripts/app.js";
app.ui.settings.addSetting({
id: "unique.setting.name",
name: "Switch my cool extension on?",
type: "boolean",
defaultValue: false,
onChange: (newVal, oldVal) => { console.log("Setting got changed!") },
*/
});
if (app.ui.settings.getSettingValue("unique.setting.name", false)) {
}
Sliders for numbers
The type slider
lets the user enter a value directly or via a slider:
app.ui.settings.addSetting({
id: "unique.setting.slider",
name: "Move me around",
type: "slider",
attrs: { min: -1, max: 500, step: 1, },
defaultValue: 0,
onChange: (newVal, oldVal) => { console.log(`Setting got changed to ${newVal}`) },
});
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:
const original_getCanvasMenuOptions = LGraphCanvas.prototype.getCanvasMenuOptions;
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
const options = original_getCanvasMenuOptions.apply(this, arguments);
options.push(null);
options.push({
content: "The text for the menu",
callback: async () => {
}
})
return options;
}
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…
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 () => {
}
})
}
}
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) {
},
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";
function on_execution_start() {
}
api.addEventListener("execution_start", on_execution_start);
Detect an interrupted workflow
A simple example of hijacking the api:
import { api } from "../../scripts/api.js";
const original_api_interrupt = api.interrupt;
api.interrupt = function () {
original_api_interrupt.apply(this, arguments);
}
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);
}
}
}