Add util method to change mode of nodes so we can manually update subgraphs too. Fixes #556

This commit is contained in:
rgthree
2025-08-16 01:32:58 -04:00
parent 20f3c0e4ef
commit fbbc2b8536
19 changed files with 104 additions and 51 deletions
+3 -3
View File
@@ -1,7 +1,7 @@
import type {LGraphNode, IWidget} from "@comfyorg/frontend";
import {BaseAnyInputConnectedNode} from "./base_any_input_connected_node.js";
import {PassThroughFollowing} from "./utils.js";
import {changeModeOfNodes, PassThroughFollowing} from "./utils.js";
import {wait} from "rgthree/common/shared_utils.js";
export class BaseNodeModeChanger extends BaseAnyInputConnectedNode {
@@ -76,7 +76,7 @@ export class BaseNodeModeChanger extends BaseAnyInputConnectedNode {
newValue = this.widgets.every((w) => !w.value || w === widget);
}
}
linkedNode.mode = (newValue ? this.modeOn : this.modeOff) as 1 | 2 | 3 | 4;
changeModeOfNodes(linkedNode, (newValue ? this.modeOn : this.modeOff))
widget.value = newValue;
};
widget.callback = () => {
@@ -87,7 +87,7 @@ export class BaseNodeModeChanger extends BaseAnyInputConnectedNode {
if (forceValue != null) {
const newMode = (forceValue ? this.modeOn : this.modeOff) as 1 | 2 | 3 | 4;
if (linkedNode.mode !== newMode) {
linkedNode.mode = newMode;
changeModeOfNodes(linkedNode, newMode);
changed = true;
}
}
+4 -4
View File
@@ -13,7 +13,7 @@ import type {RgthreeBaseVirtualNode} from "./base_node.js";
import {app} from "scripts/app.js";
import {BaseAnyInputConnectedNode} from "./base_any_input_connected_node.js";
import {NodeTypesString} from "./constants.js";
import {addMenuItem} from "./utils.js";
import {addMenuItem, changeModeOfNodes} from "./utils.js";
import {rgthree} from "./rgthree.js";
const MODE_ALWAYS = 0;
@@ -272,11 +272,11 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
}
if (node) {
if (action === "Mute") {
node.mode = MODE_MUTE;
changeModeOfNodes(node, MODE_MUTE);
} else if (action === "Bypass") {
node.mode = MODE_BYPASS;
changeModeOfNodes(node, MODE_BYPASS);
} else if (action === "Enable") {
node.mode = MODE_ALWAYS;
changeModeOfNodes(node, MODE_ALWAYS);
}
// If there's a handleAction, always call it.
if ((node as RgthreeBaseVirtualNode).handleAction) {
+2 -3
View File
@@ -15,6 +15,7 @@ import {NodeTypesString} from "./constants.js";
import {SERVICE as FAST_GROUPS_SERVICE} from "./services/fast_groups_service.js";
import {drawNodeWidget, fitString} from "./utils_canvas.js";
import {RgthreeBaseWidget} from "./utils_widgets.js";
import { changeModeOfNodes } from "./utils.js";
const PROPERTY_SORT = "sort";
const PROPERTY_SORT_CUSTOM_ALPHA = "customSortAlphabet";
@@ -396,9 +397,7 @@ class FastGroupsToggleRowWidget extends RgthreeBaseWidget<{toggled: boolean}> {
newValue = this.node.widgets.every((w) => !w.value || w === this);
}
}
for (const node of this.group._nodes) {
node.mode = (newValue ? this.node.modeOn : this.node.modeOff) as 1 | 2 | 3 | 4;
}
changeModeOfNodes(this.group._nodes, (newValue ? this.node.modeOn : this.node.modeOff));
this.group.rgthree_hasAnyActiveNode = newValue;
this.toggled = newValue;
app.graph.setDirtyCanvas(true, false);
+2 -4
View File
@@ -9,7 +9,7 @@ import type {AdjustedMouseCustomEvent} from "typings/rgthree.js";
import {app} from "scripts/app.js";
import {rgthree} from "./rgthree.js";
import {getOutputNodes} from "./utils.js";
import {changeModeOfNodes, getOutputNodes} from "./utils.js";
import {SERVICE as CONFIG_SERVICE} from "./services/config_service.js";
const BTN_SIZE = 20;
@@ -113,9 +113,7 @@ app.registerExtension({
} else {
newMode = isAllBypassed ? LiteGraph.ALWAYS : 4;
}
for (const node of group._nodes) {
node.mode = newMode;
}
changeModeOfNodes(group._nodes, newMode);
}
}
// Make it such that we're not then moving the group on drag.
+2 -1
View File
@@ -13,6 +13,7 @@ import {app} from "scripts/app.js";
import {
PassThroughFollowing,
addConnectionLayoutSupport,
changeModeOfNodes,
getConnectedInputNodesAndFilterPassThroughs,
getConnectedOutputNodesAndFilterPassThroughs,
} from "./utils.js";
@@ -219,7 +220,7 @@ class NodeModeRelay extends BaseCollectorNode {
if (this.outputs?.length) {
const outputNodes = getConnectedOutputNodesAndFilterPassThroughs(this);
for (const outputNode of outputNodes) {
outputNode.mode = mode;
changeModeOfNodes(outputNode, mode);
wait(16).then(() => {
outputNode.setDirtyCanvas(true, true);
});
+4 -3
View File
@@ -13,6 +13,7 @@ import {NodeTypesString, stripRgthree} from "./constants.js";
import {
PassThroughFollowing,
addConnectionLayoutSupport,
changeModeOfNodes,
getConnectedInputNodesAndFilterPassThroughs,
getConnectedOutputNodesAndFilterPassThroughs,
} from "./utils.js";
@@ -126,7 +127,7 @@ class NodeModeRepeater extends BaseCollectorNode {
}
}
} else {
inputNode.mode = this.mode;
changeModeOfNodes(inputNode, this.mode);
}
}
@@ -157,7 +158,7 @@ class NodeModeRepeater extends BaseCollectorNode {
for (const node of linkedNodes) {
if (node.type !== NodeTypesString.NODE_MODE_RELAY) {
// Use "to" as there may be other getters in the way to access this.mode directly.
node.mode = to;
changeModeOfNodes(node, to);
}
}
} else if (app.graph._groups?.length) {
@@ -168,7 +169,7 @@ class NodeModeRepeater extends BaseCollectorNode {
for (const node of group._nodes) {
if (node !== this) {
// Use "to" as there may be other getters in the way to access this.mode directly.
node.mode = to;
changeModeOfNodes(node, to);
}
}
}
+4 -3
View File
@@ -8,6 +8,7 @@ import {RgthreeBetterButtonWidget} from "./utils_widgets.js";
import {NodeTypesString} from "./constants.js";
import {ComfyWidgets} from "scripts/widgets.js";
import {SERVICE as CONFIG_SERVICE} from "./services/config_service.js";
import { changeModeOfNodes } from "./utils.js";
const BUILT_INS = {
node: {
@@ -85,15 +86,15 @@ class ComfyNodeWrapper {
}
@Exposed mute() {
this.getNode().mode = 2;
changeModeOfNodes(this.getNode(), 2);
}
@Exposed bypass() {
this.getNode().mode = 4;
changeModeOfNodes(this.getNode(), 4);
}
@Exposed enable() {
this.getNode().mode = 0;
changeModeOfNodes(this.getNode(), 0);
}
}
+3 -3
View File
@@ -4,7 +4,7 @@ import {app} from "scripts/app.js";
import {BaseAnyInputConnectedNode} from "./base_any_input_connected_node.js";
import {NodeTypesString} from "./constants.js";
import {rgthree} from "./rgthree.js";
import {getConnectedInputNodesAndFilterPassThroughs} from "./utils.js";
import {changeModeOfNodes, getConnectedInputNodesAndFilterPassThroughs} from "./utils.js";
const MODE_MUTE = 2;
const MODE_ALWAYS = 0;
@@ -67,14 +67,14 @@ class RandomUnmuterNode extends BaseAnyInputConnectedNode {
if (allMuted) {
this.tempEnabledNode = linkedNodes[Math.floor(Math.random() * linkedNodes.length)] || null;
if (this.tempEnabledNode) {
this.tempEnabledNode.mode = this.modeOn;
changeModeOfNodes(this.tempEnabledNode, this.modeOn);
}
}
}
}
onGraphtoPromptEnd(event: Event) {
if (this.tempEnabledNode) {
this.tempEnabledNode.mode = this.modeOff;
changeModeOfNodes(this.tempEnabledNode, this.modeOff);
this.tempEnabledNode = null;
}
}
+35
View File
@@ -963,6 +963,41 @@ export function getOutputNodes(nodes: LGraphNode[]) {
);
}
/**
* Changes the mode of a node. We must go through this to change a node's mode after the
* introduction of subgraphs, as ComfyUI doesn't update the mode of a node in a subgraph on its own.
*/
export function changeModeOfNodes(nodeOrNodes: LGraphNode | LGraphNode[], mode: LGraphEventMode) {
const nodes = Array.isArray(nodeOrNodes) ? nodeOrNodes : [nodeOrNodes];
traverseNodesDepthFirst(nodes, (n) => {
n.mode = mode;
});
}
/**
* Performs depth-first traversal of nodes and their subgraphs.
* Adapted from ComfyUI Frontend's method.
*/
function traverseNodesDepthFirst(nodes: LGraphNode[], visitor: (n: LGraphNode) => void) {
const stack: Array<{node: LGraphNode}> = nodes.map((node) => ({node}));
// Process stack iteratively (DFS)
while (stack.length > 0) {
const {node} = stack.pop()!;
visitor(node);
// If it's a subgraph and we should expand, add children to stack
if (node.isSubgraphNode?.() && node.subgraph) {
// Process children in reverse order to maintain left-to-right DFS processing
// when popping from stack (LIFO). Iterate backwards to avoid array reversal.
const children = node.subgraph.nodes;
for (let i = children.length - 1; i >= 0; i--) {
stack.push({node: children[i]!});
}
}
}
}
/**
* Gets a full color string, including parsing from the LGraphCanvas data.
*/