Add Mute / Bypass Relay and Repeater
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
"""
|
||||
@author: rgthree
|
||||
@title: Comfy Nodes
|
||||
@nickname: rgthree
|
||||
@description: A bunch of nodes I created that I also find useful.
|
||||
"""
|
||||
|
||||
import glob
|
||||
import inspect
|
||||
import os
|
||||
@@ -14,6 +21,7 @@ from .py.seed import RgthreeSeed
|
||||
from .py.sdxl_empty_latent_image import RgthreeSDXLEmptyLatentImage
|
||||
from .py.power_prompt import RgthreePowerPrompt
|
||||
from .py.power_prompt_simple import RgthreePowerPromptSimple
|
||||
from .py.image_inset_crop import RgthreeImageInsetCrop
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
RgthreeContext.NAME: RgthreeContext,
|
||||
@@ -24,6 +32,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
RgthreeSDXLEmptyLatentImage.NAME: RgthreeSDXLEmptyLatentImage,
|
||||
RgthreePowerPrompt.NAME: RgthreePowerPrompt,
|
||||
RgthreePowerPromptSimple.NAME: RgthreePowerPromptSimple,
|
||||
RgthreeImageInsetCrop.NAME: RgthreeImageInsetCrop
|
||||
}
|
||||
|
||||
THIS_DIR=os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
export class RgthreeBaseNode extends LGraphNode {
|
||||
constructor(title = RgthreeBaseNode.title) {
|
||||
super(title);
|
||||
this.isVirtualNode = true;
|
||||
if (title == '__NEED_NAME__') {
|
||||
throw new Error('RgthreeBaseNode needs overrides.');
|
||||
}
|
||||
this.properties = this.properties || {};
|
||||
}
|
||||
onModeChange() {
|
||||
}
|
||||
set mode(mode) {
|
||||
if (this.mode_ != mode) {
|
||||
this.mode_ = mode;
|
||||
this.onModeChange();
|
||||
}
|
||||
}
|
||||
get mode() {
|
||||
return this.mode_;
|
||||
}
|
||||
}
|
||||
RgthreeBaseNode.title = "__NEED_NAME__";
|
||||
RgthreeBaseNode.category = 'rgthree';
|
||||
RgthreeBaseNode._category = 'rgthree';
|
||||
@@ -1,13 +1,9 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
export class BaseCollectorNode extends LGraphNode {
|
||||
constructor(title = BaseCollectorNode.title) {
|
||||
import { RgthreeBaseNode } from "./base_node.js";
|
||||
export class BaseCollectorNode extends RgthreeBaseNode {
|
||||
constructor(title) {
|
||||
super(title);
|
||||
this.isVirtualNode = true;
|
||||
if (title == '__NEED_NAME__') {
|
||||
throw new Error('BaseCollectorNode needs overrides.');
|
||||
}
|
||||
this.properties = this.properties || {};
|
||||
this.connections = [];
|
||||
this.addInput("", "*");
|
||||
this.addOutput("Output", "*");
|
||||
}
|
||||
@@ -55,6 +51,3 @@ export class BaseCollectorNode extends LGraphNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
BaseCollectorNode.title = "__NEED_NAME__";
|
||||
BaseCollectorNode.category = 'rgthree';
|
||||
BaseCollectorNode._category = 'rgthree';
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { addConnectionLayoutSupport, addMenuItem, doChainLookup, wait } from "./utils.js";
|
||||
export class BaseNodeModeChanger extends LGraphNode {
|
||||
constructor(title = BaseNodeModeChanger.title) {
|
||||
import { RgthreeBaseNode } from "./base_node.js";
|
||||
import { addConnectionLayoutSupport, addMenuItem, getConnectedInputNodes, wait } from "./utils.js";
|
||||
export class BaseNodeModeChanger extends RgthreeBaseNode {
|
||||
constructor(title) {
|
||||
super(title);
|
||||
this.isVirtualNode = true;
|
||||
this.debouncer = 0;
|
||||
this.schedulePromise = null;
|
||||
this.isVirtualNode = true;
|
||||
this.modeOn = -1;
|
||||
this.modeOff = -1;
|
||||
if (title == '__NEED_NAME__') {
|
||||
throw new Error('BaseNodeModeChanger needs overrides.');
|
||||
}
|
||||
wait(10).then(() => {
|
||||
if (this.modeOn < 0 || this.modeOff < 0) {
|
||||
throw new Error('modeOn and modeOff must be overridden.');
|
||||
}
|
||||
});
|
||||
this.properties = this.properties || {};
|
||||
this.connections = [];
|
||||
this.addInput("", "*");
|
||||
}
|
||||
scheduleRefreshWidgets() {
|
||||
scheduleStabilizeWidgets() {
|
||||
if (!this.schedulePromise) {
|
||||
this.schedulePromise = new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(this.refreshWidgets());
|
||||
resolve(this.stabilizeWidgets());
|
||||
this.schedulePromise = null;
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
return this.schedulePromise;
|
||||
}
|
||||
refreshWidgets() {
|
||||
const linkedNodes = doChainLookup(app, this, this);
|
||||
stabilizeWidgets() {
|
||||
if (!this.graph) {
|
||||
return;
|
||||
}
|
||||
const linkedNodes = getConnectedInputNodes(app, this);
|
||||
this.stabilizeInputsOutputs();
|
||||
for (const [index, node] of linkedNodes.entries()) {
|
||||
let widget = this.widgets && this.widgets[index];
|
||||
@@ -47,6 +46,7 @@ export class BaseNodeModeChanger extends LGraphNode {
|
||||
this.widgets.length = linkedNodes.length;
|
||||
}
|
||||
app.graph.setDirtyCanvas(true, true);
|
||||
setTimeout(() => { this.stabilizeWidgets(); }, 500);
|
||||
}
|
||||
setWidget(widget, linkedNode) {
|
||||
const off = linkedNode.mode === this.modeOff;
|
||||
@@ -60,10 +60,10 @@ export class BaseNodeModeChanger extends LGraphNode {
|
||||
};
|
||||
}
|
||||
onConnectionsChainChange() {
|
||||
this.scheduleRefreshWidgets();
|
||||
this.scheduleStabilizeWidgets();
|
||||
}
|
||||
onConnectionsChange(_type, _index, _connected, _linkInfo, _ioSlot) {
|
||||
this.scheduleRefreshWidgets();
|
||||
this.scheduleStabilizeWidgets();
|
||||
}
|
||||
removeInput(slot) {
|
||||
this._tempWidth = this.size[0];
|
||||
@@ -107,7 +107,7 @@ export class BaseNodeModeChanger extends LGraphNode {
|
||||
static setUp(clazz) {
|
||||
addMenuItem(clazz, app, {
|
||||
name: 'Refresh',
|
||||
callback: (node) => { node.scheduleRefreshWidgets(); }
|
||||
callback: (node) => { node.scheduleStabilizeWidgets(); }
|
||||
});
|
||||
addMenuItem(clazz, app, {
|
||||
name: (node) => { var _a; return (`${((_a = node.properties) === null || _a === void 0 ? void 0 : _a['collapse_connections']) ? 'Show' : 'Collapse'} Connections`); },
|
||||
@@ -116,11 +116,8 @@ export class BaseNodeModeChanger extends LGraphNode {
|
||||
callback: (_node) => { app.graph.setDirtyCanvas(true, true); }
|
||||
});
|
||||
addConnectionLayoutSupport(clazz, app, [['Left'], ['Right']]);
|
||||
LiteGraph.registerNodeType(clazz.title, clazz);
|
||||
LiteGraph.registerNodeType(clazz.type, clazz);
|
||||
clazz.category = clazz._category;
|
||||
}
|
||||
}
|
||||
BaseNodeModeChanger.title = "__NEED_NAME__";
|
||||
BaseNodeModeChanger.category = 'rgthree';
|
||||
BaseNodeModeChanger._category = 'rgthree';
|
||||
BaseNodeModeChanger.collapsible = false;
|
||||
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { BaseNodeModeChanger } from "./base_node_mode_changer.js";
|
||||
import { NodeTypesString } from "./constants.js";
|
||||
const MODE_BYPASS = 4;
|
||||
const MODE_ALWAYS = 0;
|
||||
class BypasserNode extends BaseNodeModeChanger {
|
||||
@@ -9,7 +10,8 @@ class BypasserNode extends BaseNodeModeChanger {
|
||||
this.modeOff = MODE_BYPASS;
|
||||
}
|
||||
}
|
||||
BypasserNode.title = "Fast Bypasser (rgthree)";
|
||||
BypasserNode.type = NodeTypesString.FAST_BYPASSER;
|
||||
BypasserNode.title = NodeTypesString.FAST_BYPASSER;
|
||||
app.registerExtension({
|
||||
name: "rgthree.Bypasser",
|
||||
registerCustomNodes() {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
export function addRgthree(str) {
|
||||
return str + ' (rgthree)';
|
||||
}
|
||||
export function stripRgthree(str) {
|
||||
return str.replace(/\s*\(rgthree\)$/, '');
|
||||
}
|
||||
export const NodeTypesString = {
|
||||
NODE_MODE_RELAY: addRgthree('Mute / Bypass Relay'),
|
||||
NODE_MODE_REPEATER: addRgthree('Mute / Bypass Repeater'),
|
||||
FAST_MUTER: addRgthree('Fast Muter'),
|
||||
FAST_BYPASSER: addRgthree('Fast Bypasser'),
|
||||
};
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { BaseNodeModeChanger } from "./base_node_mode_changer.js";
|
||||
import { NodeTypesString } from "./constants.js";
|
||||
const MODE_MUTE = 2;
|
||||
const MODE_ALWAYS = 0;
|
||||
class MuterNode extends BaseNodeModeChanger {
|
||||
@@ -9,7 +10,8 @@ class MuterNode extends BaseNodeModeChanger {
|
||||
this.modeOff = MODE_MUTE;
|
||||
}
|
||||
}
|
||||
MuterNode.title = "Fast Muter (rgthree)";
|
||||
MuterNode.type = NodeTypesString.FAST_MUTER;
|
||||
MuterNode.title = NodeTypesString.FAST_MUTER;
|
||||
app.registerExtension({
|
||||
name: "rgthree.Muter",
|
||||
registerCustomNodes() {
|
||||
|
||||
@@ -43,7 +43,7 @@ async function updateCombinerToCollector(node) {
|
||||
}
|
||||
newNode.pos = [...node.pos];
|
||||
newNode.size = [...node.size];
|
||||
newNode.properties = Object.assign({}, node.properties);
|
||||
newNode.properties = { ...node.properties };
|
||||
const links = [];
|
||||
for (const [index, output] of node.outputs.entries()) {
|
||||
for (const linkId of (output.links || [])) {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { addConnectionLayoutSupport, addHelp, getConnectedInputNodes, getConnectedOutputNodes, wait } from "./utils.js";
|
||||
import { BaseCollectorNode } from './base_node_collector.js';
|
||||
import { NodeTypesString, stripRgthree } from "./constants.js";
|
||||
const MODE_ALWAYS = 0;
|
||||
const MODE_MUTE = 2;
|
||||
const MODE_BYPASS = 4;
|
||||
const MODE_REPEATS = [MODE_MUTE, MODE_BYPASS];
|
||||
class NodeModeRelay extends BaseCollectorNode {
|
||||
constructor(title) {
|
||||
super(title);
|
||||
setTimeout(() => { this.stabilize(); }, 500);
|
||||
this.removeOutput(0);
|
||||
this.addOutput('REPEATER', '_NODE_REPEATER_', {
|
||||
color_on: '#Fc0',
|
||||
color_off: '#a80',
|
||||
shape: LiteGraph.ARROW_SHAPE,
|
||||
});
|
||||
}
|
||||
onConnectOutput(outputIndex, inputType, inputSlot, inputNode, inputIndex) {
|
||||
var _a, _b;
|
||||
let canConnect = true;
|
||||
if (super.onConnectOutput) {
|
||||
canConnect = (_a = super.onConnectOutput) === null || _a === void 0 ? void 0 : _a.call(this, outputIndex, inputType, inputSlot, inputNode, inputIndex);
|
||||
}
|
||||
let nextNode = (_b = getConnectedOutputNodes(app, this, inputNode)[0]) !== null && _b !== void 0 ? _b : inputNode;
|
||||
return canConnect && nextNode.type === NodeTypesString.NODE_MODE_REPEATER;
|
||||
}
|
||||
onConnectionsChange(type, slotIndex, isConnected, link_info, ioSlot) {
|
||||
super.onConnectionsChange(type, slotIndex, isConnected, link_info, ioSlot);
|
||||
setTimeout(() => { this.stabilize(); }, 500);
|
||||
}
|
||||
stabilize() {
|
||||
var _a;
|
||||
if (!this.graph || !this.isAnyOutputConnected() || !this.isInputConnected(0)) {
|
||||
return;
|
||||
}
|
||||
const inputNodes = getConnectedInputNodes(app, this);
|
||||
let mode = undefined;
|
||||
for (const inputNode of inputNodes) {
|
||||
if (mode === undefined) {
|
||||
mode = inputNode.mode;
|
||||
}
|
||||
else if (mode === inputNode.mode && MODE_REPEATS.includes(mode)) {
|
||||
continue;
|
||||
}
|
||||
else if (inputNode.mode === MODE_ALWAYS || mode === MODE_ALWAYS) {
|
||||
mode = MODE_ALWAYS;
|
||||
}
|
||||
else {
|
||||
mode = null;
|
||||
}
|
||||
}
|
||||
if (mode != null) {
|
||||
if ((_a = this.outputs) === null || _a === void 0 ? void 0 : _a.length) {
|
||||
const outputNodes = getConnectedOutputNodes(app, this);
|
||||
for (const outputNode of outputNodes) {
|
||||
outputNode.mode = mode;
|
||||
wait(16).then(() => {
|
||||
outputNode.setDirtyCanvas(true, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(() => { this.stabilize(); }, 500);
|
||||
}
|
||||
}
|
||||
NodeModeRelay.type = NodeTypesString.NODE_MODE_RELAY;
|
||||
NodeModeRelay.title = NodeTypesString.NODE_MODE_RELAY;
|
||||
NodeModeRelay.help = [
|
||||
`This node will relay its input nodes' modes (Mute, Bypass, or Active) to a connected`,
|
||||
`${stripRgthree(NodeTypesString.NODE_MODE_REPEATER)} (which would then repeat that mode change to all of its inputs).`,
|
||||
`\n`,
|
||||
`\n- When all connected input nodes are muted, the relay will set a connected repeater to mute.`,
|
||||
`\n- When all connected input nodes are bypassed, the relay will set a connected repeater to bypass.`,
|
||||
`\n- When any connected input nodes are active, the relay will set a connected repeater to active.`,
|
||||
].join(' ');
|
||||
app.registerExtension({
|
||||
name: "rgthree.NodeModeRepeaterHelper",
|
||||
registerCustomNodes() {
|
||||
addHelp(NodeModeRelay, app);
|
||||
addConnectionLayoutSupport(NodeModeRelay, app, [['Left', 'Right'], ['Right', 'Left']]);
|
||||
LiteGraph.registerNodeType(NodeModeRelay.type, NodeModeRelay);
|
||||
NodeModeRelay.category = NodeModeRelay._category;
|
||||
},
|
||||
});
|
||||
+49
-18
@@ -1,30 +1,61 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { addConnectionLayoutSupport, addHelp, doChainLookup } from "./utils.js";
|
||||
import { BaseCollectorNode } from './base_node_collector.js';
|
||||
import { NodeTypesString, stripRgthree } from "./constants.js";
|
||||
import { addConnectionLayoutSupport, addHelp, getConnectedInputNodes, getConnectedOutputNodes } from "./utils.js";
|
||||
class NodeModeRepeater extends BaseCollectorNode {
|
||||
constructor(title) {
|
||||
super(title);
|
||||
this.removeOutput(0);
|
||||
this.addOutput('FAST_TOGGLER', '_FAST_TOGGLER_', {
|
||||
color_on: '#Fc0',
|
||||
color_off: '#a80',
|
||||
shape: LiteGraph.ARROW_SHAPE,
|
||||
});
|
||||
}
|
||||
onConnectOutput(outputIndex, inputType, inputSlot, inputNode, inputIndex) {
|
||||
var _a, _b;
|
||||
let canConnect = true;
|
||||
if (super.onConnectOutput) {
|
||||
canConnect = (_a = super.onConnectOutput) === null || _a === void 0 ? void 0 : _a.call(this, outputIndex, inputType, inputSlot, inputNode, inputIndex);
|
||||
}
|
||||
let nextNode = (_b = getConnectedOutputNodes(app, this, inputNode)[0]) !== null && _b !== void 0 ? _b : inputNode;
|
||||
return canConnect && (nextNode.type === NodeTypesString.FAST_MUTER || nextNode.type === NodeTypesString.FAST_BYPASSER);
|
||||
}
|
||||
onConnectionsChange(type, slotIndex, isConnected, linkInfo, ioSlot) {
|
||||
super.onConnectionsChange(type, slotIndex, isConnected, linkInfo, ioSlot);
|
||||
if (type === LiteGraph.INPUT && isConnected) {
|
||||
const connectedNode = this.getInputNode(slotIndex);
|
||||
if ((connectedNode === null || connectedNode === void 0 ? void 0 : connectedNode.type) === NodeTypesString.NODE_MODE_RELAY) {
|
||||
const input = this.inputs[slotIndex];
|
||||
if (input) {
|
||||
input.color_on = '#FC0';
|
||||
input.color_off = '#a80';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
onModeChange() {
|
||||
const linkedNodes = doChainLookup(app, this, this);
|
||||
super.onModeChange();
|
||||
const linkedNodes = getConnectedInputNodes(app, this);
|
||||
for (const node of linkedNodes) {
|
||||
node.mode = this.mode;
|
||||
if (node.type !== NodeTypesString.NODE_MODE_RELAY) {
|
||||
node.mode = this.mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
set mode(mode) {
|
||||
if (this.mode_ != mode) {
|
||||
this.mode_ = mode;
|
||||
this.onModeChange();
|
||||
}
|
||||
}
|
||||
get mode() {
|
||||
return this.mode_;
|
||||
}
|
||||
}
|
||||
NodeModeRepeater.type = "Node Mode Repeater (rgthree)";
|
||||
NodeModeRepeater.title = "Node Mode Repeater (rgthree)";
|
||||
NodeModeRepeater.type = NodeTypesString.NODE_MODE_REPEATER;
|
||||
NodeModeRepeater.title = NodeTypesString.NODE_MODE_REPEATER;
|
||||
NodeModeRepeater.help = [
|
||||
`Connect other nodes\' outputs to this Node Mode Repeater and all connected nodes`,
|
||||
`will update their mode (mute/bypass/active) when this node's mode changes.`,
|
||||
`\n\nOptionally, connect this mode's output to a Fast Muter or Fast Bypasser for a single toggle`,
|
||||
`to then quickly mute or bypass this node and all its connected nodes.`
|
||||
`When this node's mode (Mute, Bypass, Active) changes, it will "repeat" that mode to all`,
|
||||
`connected input nodes.`,
|
||||
`\n`,
|
||||
`\n- Optionally, connect this mode's output to a ${stripRgthree(NodeTypesString.FAST_MUTER)}`,
|
||||
`or ${stripRgthree(NodeTypesString.FAST_BYPASSER)} for a single toggle to quickly`,
|
||||
`mute/bypass all its connected nodes.`,
|
||||
`\n- Optionally, connect a ${stripRgthree(NodeTypesString.NODE_MODE_RELAY)} to this nodes'`,
|
||||
`inputs to have it automatically toggle its mode. If connected, this will always take`,
|
||||
`precedence`,
|
||||
].join(' ');
|
||||
app.registerExtension({
|
||||
name: "rgthree.NodeModeRepeater",
|
||||
|
||||
+36
-9
@@ -5,6 +5,11 @@ api.getNodeDefs = async function () {
|
||||
this.dispatchEvent(new CustomEvent('fresh-node-defs', { detail: defs }));
|
||||
return defs;
|
||||
};
|
||||
var IoDirection;
|
||||
(function (IoDirection) {
|
||||
IoDirection[IoDirection["INPUT"] = 0] = "INPUT";
|
||||
IoDirection[IoDirection["OUTPUT"] = 1] = "OUTPUT";
|
||||
})(IoDirection || (IoDirection = {}));
|
||||
const PADDING = 0;
|
||||
export const LAYOUT_LABEL_TO_DATA = {
|
||||
'Left': [LiteGraph.LEFT, [0, 0.5], [PADDING, 0]],
|
||||
@@ -226,7 +231,7 @@ export function addHelp(node, app) {
|
||||
const help = node.help;
|
||||
if (help) {
|
||||
addMenuItem(node, app, {
|
||||
name: 'Node Help',
|
||||
name: '🛟 Node Help',
|
||||
property: 'help',
|
||||
callback: (_node) => { alert(help); }
|
||||
});
|
||||
@@ -239,34 +244,56 @@ export function isPassThroughType(node) {
|
||||
|| (type === null || type === void 0 ? void 0 : type.includes('Node Combiner'))
|
||||
|| (type === null || type === void 0 ? void 0 : type.includes('Node Collector'));
|
||||
}
|
||||
export function doChainLookup(app, startNode, currentNode) {
|
||||
export function getConnectedInputNodes(app, startNode, currentNode) {
|
||||
return getConnectedNodes(app, startNode, IoDirection.INPUT, currentNode);
|
||||
}
|
||||
export function getConnectedOutputNodes(app, startNode, currentNode) {
|
||||
return getConnectedNodes(app, startNode, IoDirection.OUTPUT, currentNode);
|
||||
}
|
||||
function getConnectedNodes(app, startNode, dir = IoDirection.INPUT, currentNode) {
|
||||
var _a, _b;
|
||||
currentNode = currentNode || startNode;
|
||||
let rootNodes = [];
|
||||
const slotsToRemove = [];
|
||||
if (startNode === currentNode || isPassThroughType(currentNode)) {
|
||||
const removeDups = startNode === currentNode;
|
||||
for (const input of currentNode.inputs) {
|
||||
const linkId = input.link;
|
||||
let linkIds;
|
||||
if (dir == IoDirection.OUTPUT) {
|
||||
linkIds = (_a = currentNode.outputs) === null || _a === void 0 ? void 0 : _a.flatMap(i => i.links);
|
||||
}
|
||||
else {
|
||||
linkIds = (_b = currentNode.inputs) === null || _b === void 0 ? void 0 : _b.map(i => i.link);
|
||||
}
|
||||
let graph = app.graph;
|
||||
for (const linkId of linkIds) {
|
||||
if (!linkId) {
|
||||
continue;
|
||||
}
|
||||
const link = app.graph.links[linkId];
|
||||
const originNode = app.graph.getNodeById(link.origin_id);
|
||||
const link = graph.links[linkId];
|
||||
const connectedId = dir == IoDirection.OUTPUT ? link.target_id : link.origin_id;
|
||||
const originNode = graph.getNodeById(connectedId);
|
||||
if (isPassThroughType(originNode)) {
|
||||
for (const foundNode of doChainLookup(app, startNode, originNode)) {
|
||||
for (const foundNode of getConnectedNodes(app, startNode, dir, originNode)) {
|
||||
if (!rootNodes.includes(foundNode)) {
|
||||
rootNodes.push(foundNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rootNodes.includes(originNode)) {
|
||||
removeDups && (slotsToRemove.push(link.target_slot));
|
||||
const connectedSlot = dir == IoDirection.OUTPUT ? link.origin_slot : link.target_slot;
|
||||
removeDups && (slotsToRemove.push(connectedSlot));
|
||||
}
|
||||
else {
|
||||
rootNodes.push(originNode);
|
||||
}
|
||||
}
|
||||
for (const slot of slotsToRemove) {
|
||||
startNode.disconnectInput(slot);
|
||||
if (dir == IoDirection.OUTPUT) {
|
||||
startNode.disconnectOutput(slot);
|
||||
}
|
||||
else {
|
||||
startNode.disconnectInput(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rootNodes;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// / <reference path="../node_modules/litegraph.js/src/litegraph.d.ts" />
|
||||
// @ts-ignore
|
||||
import {app} from "../../scripts/app.js";
|
||||
import { NodeMode } from "./typings/comfy.js";
|
||||
import type {LGraphNode as TLGraphNode, LiteGraph as TLiteGraph} from './typings/litegraph.js';
|
||||
|
||||
declare const LiteGraph: typeof TLiteGraph;
|
||||
declare const LGraphNode: typeof TLGraphNode;
|
||||
|
||||
|
||||
export class RgthreeBaseNode extends LGraphNode {
|
||||
|
||||
static override title = "__NEED_NAME__";
|
||||
// `category` seems to get reset at register, so we'll
|
||||
// re-reset it after the register call. ¯\_(ツ)_/¯
|
||||
static category = 'rgthree';
|
||||
static _category = 'rgthree';
|
||||
|
||||
isVirtualNode = true;
|
||||
|
||||
constructor(title = RgthreeBaseNode.title) {
|
||||
super(title);
|
||||
if (title == '__NEED_NAME__') {
|
||||
throw new Error('RgthreeBaseNode needs overrides.');
|
||||
}
|
||||
this.properties = this.properties || {};
|
||||
}
|
||||
|
||||
mode_: NodeMode;
|
||||
|
||||
/** When a mode change, we want all connected nodes to match. */
|
||||
onModeChange() {
|
||||
// Override
|
||||
}
|
||||
|
||||
// @ts-ignore - Changing the property to an accessor here seems to work, but ts compiler complains.
|
||||
override set mode(mode: NodeMode) {
|
||||
if (this.mode_ != mode) {
|
||||
this.mode_ = mode;
|
||||
this.onModeChange();
|
||||
}
|
||||
|
||||
}
|
||||
override get mode() {
|
||||
return this.mode_;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +1,20 @@
|
||||
// / <reference path="../node_modules/litegraph.js/src/litegraph.d.ts" />
|
||||
// @ts-ignore
|
||||
import { app } from "../../scripts/app.js";
|
||||
import type {LLink, LGraph, INodeInputSlot, INodeOutputSlot, LGraphNode as TLGraphNode} from './typings/litegraph.js';
|
||||
// @ts-ignore
|
||||
import { ComfyWidgets } from "../../scripts/widgets.js";
|
||||
|
||||
import type {LLink, LGraph, INodeInputSlot, INodeOutputSlot, LGraphNode as TLGraphNode} from './typings/litegraph.js';
|
||||
import { RgthreeBaseNode } from "./base_node.js";
|
||||
|
||||
declare const LGraphNode: typeof TLGraphNode;
|
||||
|
||||
export class BaseCollectorNode extends LGraphNode {
|
||||
export class BaseCollectorNode extends RgthreeBaseNode {
|
||||
|
||||
static override title = "__NEED_NAME__";
|
||||
// `category` seems to get reset at register, so we'll
|
||||
// re-reset it after the register call. ¯\_(ツ)_/¯
|
||||
static category = 'rgthree';
|
||||
static _category = 'rgthree';
|
||||
override isVirtualNode = true;
|
||||
|
||||
isVirtualNode = true;
|
||||
|
||||
constructor(title = BaseCollectorNode.title) {
|
||||
constructor(title?: string) {
|
||||
super(title);
|
||||
if (title == '__NEED_NAME__') {
|
||||
throw new Error('BaseCollectorNode needs overrides.');
|
||||
}
|
||||
this.properties = this.properties || {};
|
||||
this.connections = [];
|
||||
this.addInput("", "*");
|
||||
this.addOutput("Output", "*");
|
||||
}
|
||||
|
||||
@@ -1,47 +1,41 @@
|
||||
// / <reference path="../node_modules/litegraph.js/src/litegraph.d.ts" />
|
||||
// @ts-ignore
|
||||
import {app} from "../../scripts/app.js";
|
||||
import { RgthreeBaseNode } from "./base_node.js";
|
||||
import type {Vector2, LLink, INodeInputSlot, INodeOutputSlot, LGraphNode as TLGraphNode, LiteGraph as TLiteGraph, IWidget} from './typings/litegraph.js';
|
||||
import { addConnectionLayoutSupport, addMenuItem, doChainLookup, wait } from "./utils.js";
|
||||
import { addConnectionLayoutSupport, addMenuItem, getConnectedInputNodes, wait } from "./utils.js";
|
||||
|
||||
declare const LiteGraph: typeof TLiteGraph;
|
||||
declare const LGraphNode: typeof TLGraphNode;
|
||||
|
||||
export class BaseNodeModeChanger extends LGraphNode {
|
||||
export class BaseNodeModeChanger extends RgthreeBaseNode {
|
||||
|
||||
static override title = "__NEED_NAME__";
|
||||
// `category` seems to get reset at register, so we'll
|
||||
// re-reset it after the register call. ¯\_(ツ)_/¯
|
||||
static category = 'rgthree';
|
||||
static _category = 'rgthree';
|
||||
static collapsible = false;
|
||||
|
||||
override isVirtualNode = true;
|
||||
debouncer: number = 0;
|
||||
schedulePromise: Promise<void> | null = null;
|
||||
isVirtualNode = true;
|
||||
|
||||
// These Must be overriden
|
||||
readonly modeOn: number = -1;
|
||||
readonly modeOff: number = -1;
|
||||
|
||||
constructor(title = BaseNodeModeChanger.title) {
|
||||
constructor(title?: string) {
|
||||
super(title);
|
||||
if (title == '__NEED_NAME__') {
|
||||
throw new Error('BaseNodeModeChanger needs overrides.');
|
||||
}
|
||||
|
||||
wait(10).then(() => {
|
||||
if (this.modeOn < 0 || this.modeOff < 0) {
|
||||
throw new Error('modeOn and modeOff must be overridden.');
|
||||
}
|
||||
});
|
||||
this.properties = this.properties || {};
|
||||
this.connections = [];
|
||||
this.addInput("", "*");
|
||||
}
|
||||
|
||||
scheduleRefreshWidgets() {
|
||||
scheduleStabilizeWidgets() {
|
||||
if (!this.schedulePromise) {
|
||||
this.schedulePromise = new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(this.refreshWidgets());
|
||||
resolve(this.stabilizeWidgets());
|
||||
this.schedulePromise = null;
|
||||
}, 100);
|
||||
});
|
||||
@@ -49,8 +43,11 @@ export class BaseNodeModeChanger extends LGraphNode {
|
||||
return this.schedulePromise;
|
||||
}
|
||||
|
||||
refreshWidgets() {
|
||||
const linkedNodes = doChainLookup(app, this, this);
|
||||
stabilizeWidgets() {
|
||||
if (!this.graph) {
|
||||
return;
|
||||
}
|
||||
const linkedNodes = getConnectedInputNodes(app, this);
|
||||
this.stabilizeInputsOutputs();
|
||||
for (const [index, node] of linkedNodes.entries()) {
|
||||
let widget = this.widgets && this.widgets[index];
|
||||
@@ -69,6 +66,7 @@ export class BaseNodeModeChanger extends LGraphNode {
|
||||
this.widgets.length = linkedNodes.length
|
||||
}
|
||||
app.graph.setDirtyCanvas(true, true);
|
||||
setTimeout(() => { this.stabilizeWidgets(); }, 500);
|
||||
}
|
||||
|
||||
setWidget(widget: IWidget, linkedNode: TLGraphNode) {
|
||||
@@ -84,13 +82,12 @@ export class BaseNodeModeChanger extends LGraphNode {
|
||||
}
|
||||
|
||||
|
||||
|
||||
onConnectionsChainChange() {
|
||||
this.scheduleRefreshWidgets();
|
||||
this.scheduleStabilizeWidgets();
|
||||
}
|
||||
|
||||
override onConnectionsChange(_type: number, _index: number, _connected: boolean, _linkInfo: LLink, _ioSlot: (INodeOutputSlot | INodeInputSlot)) {
|
||||
this.scheduleRefreshWidgets();
|
||||
this.scheduleStabilizeWidgets();
|
||||
}
|
||||
|
||||
override removeInput(slot: number) {
|
||||
@@ -138,7 +135,7 @@ export class BaseNodeModeChanger extends LGraphNode {
|
||||
// @ts-ignore: Fix incorrect litegraph typings.
|
||||
addMenuItem(clazz, app, {
|
||||
name: 'Refresh',
|
||||
callback: (node) => {(node as T).scheduleRefreshWidgets()}
|
||||
callback: (node) => {(node as T).scheduleStabilizeWidgets()}
|
||||
});
|
||||
|
||||
// @ts-ignore: Fix incorrect litegraph typings.
|
||||
@@ -152,7 +149,7 @@ export class BaseNodeModeChanger extends LGraphNode {
|
||||
// @ts-ignore: Fix incorrect litegraph typings.
|
||||
addConnectionLayoutSupport(clazz, app, [['Left'],['Right']]);
|
||||
|
||||
LiteGraph.registerNodeType((clazz as any).title, clazz);
|
||||
LiteGraph.registerNodeType((clazz as any).type, clazz);
|
||||
(clazz as any).category = (clazz as any)._category;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -2,6 +2,7 @@
|
||||
// @ts-ignore
|
||||
import {app} from "../../scripts/app.js";
|
||||
import { BaseNodeModeChanger } from "./base_node_mode_changer.js";
|
||||
import { NodeTypesString } from "./constants.js";
|
||||
import type {LGraphNode} from './typings/litegraph.js';
|
||||
|
||||
const MODE_BYPASS = 4;
|
||||
@@ -9,7 +10,8 @@ const MODE_ALWAYS = 0;
|
||||
|
||||
class BypasserNode extends BaseNodeModeChanger {
|
||||
|
||||
static override title = "Fast Bypasser (rgthree)";
|
||||
static override type = NodeTypesString.FAST_BYPASSER;
|
||||
static override title = NodeTypesString.FAST_BYPASSER;
|
||||
override readonly modeOn = MODE_ALWAYS;
|
||||
override readonly modeOff = MODE_BYPASS;
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
export function addRgthree(str: string) {
|
||||
return str + ' (rgthree)';
|
||||
}
|
||||
|
||||
export function stripRgthree(str: string) {
|
||||
return str.replace(/\s*\(rgthree\)$/, '');
|
||||
}
|
||||
|
||||
export const NodeTypesString = {
|
||||
NODE_MODE_RELAY: addRgthree('Mute / Bypass Relay'),
|
||||
NODE_MODE_REPEATER: addRgthree('Mute / Bypass Repeater'),
|
||||
FAST_MUTER: addRgthree('Fast Muter'),
|
||||
FAST_BYPASSER: addRgthree('Fast Bypasser'),
|
||||
}
|
||||
+3
-1
@@ -2,6 +2,7 @@
|
||||
// @ts-ignore
|
||||
import {app} from "../../scripts/app.js";
|
||||
import { BaseNodeModeChanger } from "./base_node_mode_changer.js";
|
||||
import { NodeTypesString } from "./constants.js";
|
||||
import type {LGraphNode} from './typings/litegraph.js';
|
||||
|
||||
const MODE_MUTE = 2;
|
||||
@@ -9,7 +10,8 @@ const MODE_ALWAYS = 0;
|
||||
|
||||
class MuterNode extends BaseNodeModeChanger {
|
||||
|
||||
static override title = "Fast Muter (rgthree)";
|
||||
static override type = NodeTypesString.FAST_MUTER;
|
||||
static override title = NodeTypesString.FAST_MUTER;
|
||||
override readonly modeOn = MODE_ALWAYS;
|
||||
override readonly modeOff = MODE_MUTE;
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
// / <reference path="../node_modules/litegraph.js/src/litegraph.d.ts" />
|
||||
// @ts-ignore
|
||||
import { app } from "../../scripts/app.js";
|
||||
import type {INodeInputSlot, INodeOutputSlot, LGraphNode, LLink, LiteGraph as TLiteGraph,} from './typings/litegraph.js';
|
||||
import { addConnectionLayoutSupport, addHelp, getConnectedInputNodes, getConnectedOutputNodes, wait} from "./utils.js";
|
||||
// @ts-ignore
|
||||
import { ComfyWidgets } from "../../scripts/widgets.js";
|
||||
// @ts-ignore
|
||||
import { BaseCollectorNode } from './base_node_collector.js';
|
||||
import { NodeMode } from "./typings/comfy.js";
|
||||
import { NodeTypesString, stripRgthree } from "./constants.js";
|
||||
|
||||
declare const LiteGraph: typeof TLiteGraph;
|
||||
|
||||
const MODE_ALWAYS = 0;
|
||||
const MODE_MUTE = 2;
|
||||
const MODE_BYPASS = 4;
|
||||
const MODE_REPEATS = [MODE_MUTE, MODE_BYPASS];
|
||||
|
||||
|
||||
class NodeModeRelay extends BaseCollectorNode {
|
||||
|
||||
static override type = NodeTypesString.NODE_MODE_RELAY;
|
||||
static override title = NodeTypesString.NODE_MODE_RELAY;
|
||||
|
||||
static help = [
|
||||
`This node will relay its input nodes' modes (Mute, Bypass, or Active) to a connected`,
|
||||
`${stripRgthree(NodeTypesString.NODE_MODE_REPEATER)} (which would then repeat that mode change to all of its inputs).`,
|
||||
`\n`,
|
||||
`\n- When all connected input nodes are muted, the relay will set a connected repeater to mute.`,
|
||||
`\n- When all connected input nodes are bypassed, the relay will set a connected repeater to bypass.`,
|
||||
`\n- When any connected input nodes are active, the relay will set a connected repeater to active.`,
|
||||
].join(' ');
|
||||
|
||||
constructor(title?: string) {
|
||||
super(title);
|
||||
|
||||
setTimeout(() => { this.stabilize(); }, 500);
|
||||
this.removeOutput(0);
|
||||
this.addOutput('REPEATER', '_NODE_REPEATER_', {
|
||||
color_on: '#Fc0',
|
||||
color_off: '#a80',
|
||||
shape: LiteGraph.ARROW_SHAPE,
|
||||
});
|
||||
}
|
||||
|
||||
override onConnectOutput(outputIndex: number, inputType: string | -1, inputSlot: INodeInputSlot, inputNode: LGraphNode, inputIndex: number): boolean {
|
||||
let canConnect = true;
|
||||
if (super.onConnectOutput) {
|
||||
canConnect = super.onConnectOutput?.(outputIndex, inputType, inputSlot, inputNode, inputIndex);
|
||||
}
|
||||
let nextNode = getConnectedOutputNodes(app, this, inputNode)[0] ?? inputNode;
|
||||
return canConnect && nextNode.type === NodeTypesString.NODE_MODE_REPEATER;
|
||||
}
|
||||
|
||||
override onConnectionsChange(type: number, slotIndex: number, isConnected: boolean, link_info: LLink, ioSlot: INodeOutputSlot | INodeInputSlot): void {
|
||||
super.onConnectionsChange(type, slotIndex, isConnected, link_info, ioSlot);
|
||||
setTimeout(() => { this.stabilize(); }, 500);
|
||||
}
|
||||
|
||||
stabilize() {
|
||||
// If we aren't connected to a repeater, then theres no sense in checking. And if we are, but
|
||||
// have no inputs, then we're also not ready.
|
||||
if (!this.graph || !this.isAnyOutputConnected() || !this.isInputConnected(0)) {
|
||||
return;
|
||||
}
|
||||
const inputNodes = getConnectedInputNodes(app, this);
|
||||
let mode: NodeMode|null = undefined;
|
||||
for (const inputNode of inputNodes) {
|
||||
// If we haven't set our mode to be, then let's set it. Otherwise, mode will stick if it
|
||||
// remains constant, otherwise, if we hit an ALWAYS, then we'll unmute all repeaters and
|
||||
// if not then we won't do anything.
|
||||
if (mode === undefined) {
|
||||
mode = inputNode.mode;
|
||||
} else if (mode === inputNode.mode && MODE_REPEATS.includes(mode)) {
|
||||
continue;
|
||||
} else if (inputNode.mode === MODE_ALWAYS || mode === MODE_ALWAYS) {
|
||||
mode = MODE_ALWAYS;
|
||||
} else {
|
||||
mode = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != null) {
|
||||
if (this.outputs?.length) {
|
||||
const outputNodes = getConnectedOutputNodes(app, this);
|
||||
for (const outputNode of outputNodes) {
|
||||
outputNode.mode = mode
|
||||
wait(16).then(() => {
|
||||
outputNode.setDirtyCanvas(true, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(() => { this.stabilize(); }, 500);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
app.registerExtension({
|
||||
name: "rgthree.NodeModeRepeaterHelper",
|
||||
registerCustomNodes() {
|
||||
|
||||
addHelp(NodeModeRelay, app);
|
||||
addConnectionLayoutSupport(NodeModeRelay, app, [['Left','Right'],['Right','Left']]);
|
||||
|
||||
LiteGraph.registerNodeType(NodeModeRelay.type, NodeModeRelay);
|
||||
NodeModeRelay.category = NodeModeRelay._category;
|
||||
},
|
||||
});
|
||||
+57
-27
@@ -1,50 +1,80 @@
|
||||
// / <reference path="../node_modules/litegraph.js/src/litegraph.d.ts" />
|
||||
// @ts-ignore
|
||||
import { app } from "../../scripts/app.js";
|
||||
import type {LiteGraph as TLiteGraph,} from './typings/litegraph.js';
|
||||
import { addConnectionLayoutSupport, addHelp, doChainLookup} from "./utils.js";
|
||||
// @ts-ignore
|
||||
import { ComfyWidgets } from "../../scripts/widgets.js";
|
||||
// @ts-ignore
|
||||
import { BaseCollectorNode } from './base_node_collector.js';
|
||||
import { NodeMode } from "./typings/comfy.js";
|
||||
import { NodeTypesString, stripRgthree } from "./constants.js";
|
||||
|
||||
import type {INodeInputSlot, INodeOutputSlot, LGraphNode, LLink, LiteGraph as TLiteGraph,} from './typings/litegraph.js';
|
||||
import { addConnectionLayoutSupport, addHelp, getConnectedInputNodes, getConnectedOutputNodes} from "./utils.js";
|
||||
|
||||
declare const LiteGraph: typeof TLiteGraph;
|
||||
|
||||
/** Legacy "Combiner" */
|
||||
|
||||
class NodeModeRepeater extends BaseCollectorNode {
|
||||
|
||||
static override type = "Node Mode Repeater (rgthree)";
|
||||
static override title = "Node Mode Repeater (rgthree)";
|
||||
static override type = NodeTypesString.NODE_MODE_REPEATER;
|
||||
static override title = NodeTypesString.NODE_MODE_REPEATER;
|
||||
|
||||
static help = [
|
||||
`Connect other nodes\' outputs to this Node Mode Repeater and all connected nodes`,
|
||||
`will update their mode (mute/bypass/active) when this node's mode changes.`,
|
||||
`\n\nOptionally, connect this mode's output to a Fast Muter or Fast Bypasser for a single toggle`,
|
||||
`to then quickly mute or bypass this node and all its connected nodes.`
|
||||
`When this node's mode (Mute, Bypass, Active) changes, it will "repeat" that mode to all`,
|
||||
`connected input nodes.`,
|
||||
`\n`,
|
||||
`\n- Optionally, connect this mode's output to a ${stripRgthree(NodeTypesString.FAST_MUTER)}`,
|
||||
`or ${stripRgthree(NodeTypesString.FAST_BYPASSER)} for a single toggle to quickly`,
|
||||
`mute/bypass all its connected nodes.`,
|
||||
`\n- Optionally, connect a ${stripRgthree(NodeTypesString.NODE_MODE_RELAY)} to this nodes'`,
|
||||
`inputs to have it automatically toggle its mode. If connected, this will always take`,
|
||||
`precedence`,
|
||||
].join(' ');
|
||||
|
||||
mode_: NodeMode;
|
||||
constructor(title?: string) {
|
||||
super(title);
|
||||
this.removeOutput(0);
|
||||
this.addOutput('FAST_TOGGLER', '_FAST_TOGGLER_', {
|
||||
color_on: '#Fc0',
|
||||
color_off: '#a80',
|
||||
shape: LiteGraph.ARROW_SHAPE,
|
||||
});
|
||||
}
|
||||
|
||||
/** When a mode change, we want all connected nodes to match. */
|
||||
onModeChange() {
|
||||
const linkedNodes = doChainLookup(app, this, this);
|
||||
override onConnectOutput(outputIndex: number, inputType: string | -1, inputSlot: INodeInputSlot, inputNode: LGraphNode, inputIndex: number): boolean {
|
||||
let canConnect = true;
|
||||
if (super.onConnectOutput) {
|
||||
canConnect = super.onConnectOutput?.(outputIndex, inputType, inputSlot, inputNode, inputIndex);
|
||||
}
|
||||
// Output can only connect to a FAST MUTER or FAST BYPASSER
|
||||
let nextNode = getConnectedOutputNodes(app, this, inputNode)[0] ?? inputNode;
|
||||
return canConnect && (nextNode.type === NodeTypesString.FAST_MUTER || nextNode.type === NodeTypesString.FAST_BYPASSER);
|
||||
}
|
||||
|
||||
override onConnectionsChange(type: number, slotIndex: number, isConnected: boolean, linkInfo: LLink, ioSlot: INodeOutputSlot | INodeInputSlot): void {
|
||||
super.onConnectionsChange(type, slotIndex, isConnected, linkInfo, ioSlot);
|
||||
// If we've added an input, let's see if it's a relay and change our shape and color.
|
||||
if (type === LiteGraph.INPUT && isConnected) {
|
||||
const connectedNode = this.getInputNode(slotIndex);
|
||||
if (connectedNode?.type === NodeTypesString.NODE_MODE_RELAY) {
|
||||
const input = this.inputs[slotIndex]
|
||||
if (input) {
|
||||
input.color_on = '#FC0';
|
||||
input.color_off = '#a80';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** When a mode change, we want all connected nodes to match except for connected relays. */
|
||||
override onModeChange() {
|
||||
super.onModeChange();
|
||||
const linkedNodes = getConnectedInputNodes(app, this);
|
||||
for (const node of linkedNodes) {
|
||||
node.mode = this.mode;
|
||||
if (node.type !== NodeTypesString.NODE_MODE_RELAY) {
|
||||
node.mode = this.mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore - Changing the property to an accessor here seems to work, but ts compiler complains.
|
||||
override set mode(mode: NodeMode) {
|
||||
if (this.mode_ != mode) {
|
||||
this.mode_ = mode;
|
||||
this.onModeChange();
|
||||
}
|
||||
|
||||
}
|
||||
override get mode() {
|
||||
return this.mode_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+37
-10
@@ -18,6 +18,11 @@ api.getNodeDefs = async function() {
|
||||
declare const LGraphNode: typeof TLGraphNode;
|
||||
declare const LiteGraph: typeof TLiteGraph;
|
||||
|
||||
enum IoDirection {
|
||||
INPUT,
|
||||
OUTPUT,
|
||||
}
|
||||
|
||||
const PADDING = 0;
|
||||
|
||||
type LiteGraphDir = typeof LiteGraph.LEFT | typeof LiteGraph.RIGHT | typeof LiteGraph.UP | typeof LiteGraph.DOWN;
|
||||
@@ -279,7 +284,7 @@ export function addHelp(node: typeof LGraphNode, app: ComfyApp) {
|
||||
const help = (node as any).help as string;
|
||||
if (help) {
|
||||
addMenuItem(node, app, {
|
||||
name: 'Node Help',
|
||||
name: '🛟 Node Help',
|
||||
property: 'help',
|
||||
callback: (_node) => { alert(help); }
|
||||
});
|
||||
@@ -302,33 +307,55 @@ export function isPassThroughType(node: TLGraphNode|null) {
|
||||
* Looks through the immediate chain of a node to collect all connected nodes, passing through nodes
|
||||
* like reroute, etc. Will also disconnect duplicate nodes from a provided node
|
||||
*/
|
||||
export function doChainLookup(app: ComfyApp, startNode: TLGraphNode, currentNode: TLGraphNode) {
|
||||
export function getConnectedInputNodes(app: ComfyApp, startNode: TLGraphNode, currentNode?: TLGraphNode) {
|
||||
return getConnectedNodes(app, startNode, IoDirection.INPUT, currentNode);
|
||||
}
|
||||
|
||||
export function getConnectedOutputNodes(app: ComfyApp, startNode: TLGraphNode, currentNode?: TLGraphNode) {
|
||||
return getConnectedNodes(app, startNode, IoDirection.OUTPUT, currentNode);
|
||||
}
|
||||
|
||||
|
||||
function getConnectedNodes(app: ComfyApp, startNode: TLGraphNode, dir = IoDirection.INPUT, currentNode?: TLGraphNode) {
|
||||
currentNode = currentNode || startNode;
|
||||
let rootNodes: TLGraphNode[] = [];
|
||||
const slotsToRemove = [];
|
||||
if (startNode === currentNode || isPassThroughType(currentNode)) {
|
||||
const removeDups = startNode === currentNode;
|
||||
for (const input of currentNode.inputs) {
|
||||
const linkId: number | null = input!.link;
|
||||
let linkIds: Array<number|null>;
|
||||
if (dir == IoDirection.OUTPUT) {
|
||||
linkIds = currentNode.outputs?.flatMap(i => i.links);
|
||||
} else {
|
||||
linkIds = currentNode.inputs?.map(i => i.link);
|
||||
}
|
||||
let graph = app.graph as LGraph;
|
||||
for (const linkId of linkIds) {
|
||||
if (!linkId) {
|
||||
continue;
|
||||
}
|
||||
const link: LLink = (app.graph as LGraph).links[linkId]!;
|
||||
const originNode: TLGraphNode = (app.graph as LGraph).getNodeById(link.origin_id)!;
|
||||
const link: LLink = graph.links[linkId]!;
|
||||
const connectedId = dir == IoDirection.OUTPUT ? link.target_id : link.origin_id;
|
||||
const originNode: TLGraphNode = graph.getNodeById(connectedId)!;
|
||||
if (isPassThroughType(originNode)) {
|
||||
for (const foundNode of doChainLookup(app, startNode, originNode)) {
|
||||
for (const foundNode of getConnectedNodes(app, startNode, dir, originNode)) {
|
||||
if (!rootNodes.includes(foundNode)) {
|
||||
rootNodes.push(foundNode);
|
||||
}
|
||||
}
|
||||
} else if (rootNodes.includes(originNode)) {
|
||||
removeDups && (slotsToRemove.push(link.target_slot))
|
||||
const connectedSlot = dir == IoDirection.OUTPUT ? link.origin_slot : link.target_slot;
|
||||
removeDups && (slotsToRemove.push(connectedSlot))
|
||||
} else {
|
||||
rootNodes.push(originNode);
|
||||
}
|
||||
}
|
||||
for (const slot of slotsToRemove) {
|
||||
startNode.disconnectInput(slot);
|
||||
if (dir == IoDirection.OUTPUT) {
|
||||
startNode.disconnectOutput(slot);
|
||||
} else {
|
||||
startNode.disconnectInput(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rootNodes;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"target": "es2019",
|
||||
"module": "ESNext",
|
||||
"typeRoots": [
|
||||
"./ts/typings",
|
||||
|
||||
Reference in New Issue
Block a user