不断增长的示例代码片段集合……

右键菜单

背景菜单

主背景菜单(在画布上右键)是通过调用 LGraph.getCanvasMenuOptions 生成的。添加自定义菜单选项的一种方式是劫持这个调用:

/* 在 setup() 中 */
    const original_getCanvasMenuOptions = LGraphCanvas.prototype.getCanvasMenuOptions;
    LGraphCanvas.prototype.getCanvasMenuOptions = function () {
        // 获取基础选项
        const options = original_getCanvasMenuOptions.apply(this, arguments);
        options.push(null); // 插入分隔线
        options.push({
            content: "菜单的文本",
            callback: async () => {
                // 执行任意操作
            }
        })
        return options;
    }

节点菜单

当你在节点上右键时,菜单同样是通过 node.getExtraMenuOptions 生成的。但这次不是返回一个 options 对象,而是将其作为参数传入……

/* 在 beforeRegisterNodeDef() 中 */
if (nodeType?.comfyClass=="MyNodeClass") { 
    const original_getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
    nodeType.prototype.getExtraMenuOptions = function(_, options) {
        original_getExtraMenuOptions?.apply(this, arguments);
        options.push({
            content: "做点有趣的事",
            callback: async () => {
                // 有趣的操作
            }
        })
    }   
}

子菜单

如果你想要子菜单,可以提供一个回调,使用 LiteGraph.ContextMenu 创建它:

function make_submenu(value, options, e, menu, node) {
    const submenu = new LiteGraph.ContextMenu(
        ["选项 1", "选项 2", "选项 3"],
        { 
            event: e, 
            callback: function (v) { 
                // 用 v (=="选项 x") 做点什么
            }, 
            parentMenu: menu, 
            node:node
        }
    )
}

/* ... */
    options.push(
        {
            content: "带选项的菜单",
            has_submenu: true,
            callback: make_submenu,
        }
    )

捕获 UI 事件

这和你预期的一样——在 DOM 中找到 UI 元素并添加 eventListener。setup() 是做这件事的好地方,因为此时页面已完全加载。例如,检测”队列”按钮的点击:

function queue_button_pressed() { console.log("队列按钮被按下!") }
document.getElementById("queue-button").addEventListener("click", queue_button_pressed);

检测工作流开始

这是众多 api 事件之一:

import { api } from "../../scripts/api.js";
/* 在 setup() 中 */
    function on_execution_start() { 
        /* 执行任意操作 */
    }
    api.addEventListener("execution_start", on_execution_start);

检测工作流被中断

这是一个劫持 api 的简单例子:

import { api } from "../../scripts/api.js";
/* 在 setup() 中 */
    const original_api_interrupt = api.interrupt;
    api.interrupt = function () {
        /* 在调用原方法前做点什么 */
        original_api_interrupt.apply(this, arguments);
        /* 或者在之后 */
    }

捕获节点点击

node 有一个 mouseDown 方法可以被劫持。 这次我们注意传递任何返回值。

async nodeCreated(node) {
    if (node?.comfyClass === "My Node Name") {
        const original_onMouseDown = node.onMouseDown;
        node.onMouseDown = function( e, pos, canvas ) {
            alert("哎呦!");
            return original_onMouseDown?.apply(this, arguments);
        }        
    }
}