diff --git a/ts/base_any_input_connected_node.ts b/ts/base_any_input_connected_node.ts
index c4cd873..219e478 100644
--- a/ts/base_any_input_connected_node.ts
+++ b/ts/base_any_input_connected_node.ts
@@ -44,6 +44,16 @@ export class BaseAnyInputConnectedNode extends RgthreeBaseNode {
return this.schedulePromise;
}
+ override clone() {
+ const cloned = super.clone();
+ while (cloned.inputs.length > 1) {
+ cloned.removeInput(cloned.inputs.length - 1);
+ }
+ if (cloned.inputs[0]) {
+ cloned.inputs[0].label = '';
+ }
+ return cloned;
+ }
/**
* Ensures we have at least one empty input at the end.
*/
diff --git a/ts/constants.ts b/ts/constants.ts
index fd44746..16843a7 100644
--- a/ts/constants.ts
+++ b/ts/constants.ts
@@ -15,4 +15,5 @@ export const NodeTypesString = {
FAST_ACTIONS_BUTTON: addRgthree('Fast Actions Button'),
NODE_COLLECTOR: addRgthree('Node Collector'),
REROUTE: addRgthree('Reroute'),
+ RANDOM_UNMUTER: addRgthree('Random Unmuter'),
}
\ No newline at end of file
diff --git a/ts/node_mode_repeater.ts b/ts/node_mode_repeater.ts
index 5d35f10..64850f9 100644
--- a/ts/node_mode_repeater.ts
+++ b/ts/node_mode_repeater.ts
@@ -87,6 +87,7 @@ class NodeModeRepeater extends BaseCollectorNode {
NodeTypesString.NODE_COLLECTOR,
NodeTypesString.FAST_ACTIONS_BUTTON,
NodeTypesString.REROUTE,
+ NodeTypesString.RANDOM_UNMUTER,
].includes(nextNode.type || "")
);
}
diff --git a/ts/random_unmuter.ts b/ts/random_unmuter.ts
new file mode 100644
index 0000000..12677c5
--- /dev/null
+++ b/ts/random_unmuter.ts
@@ -0,0 +1,107 @@
+// /
+// @ts-ignore
+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 type {LGraphNode} from './typings/litegraph.js';
+import { getConnectedInputNodesAndFilterPassThroughs } from "./utils.js";
+
+const MODE_MUTE = 2;
+const MODE_ALWAYS = 0;
+
+class RandomUnmuterNode extends BaseAnyInputConnectedNode {
+
+ static override exposedActions = ['Mute all', 'Enable all'];
+
+ static override type = NodeTypesString.RANDOM_UNMUTER;
+ static override title = RandomUnmuterNode.type;
+ readonly modeOn = MODE_ALWAYS;
+ readonly modeOff = MODE_MUTE;
+
+ tempEnabledNode: LGraphNode | null = null;
+ processingQueue: boolean = false;
+
+ onQueueBound = this.onQueue.bind(this);
+ onQueueEndBound = this.onQueueEnd.bind(this);
+ onGraphtoPromptBound = this.onGraphtoPrompt.bind(this);
+ onGraphtoPromptEndBound = this.onGraphtoPromptEnd.bind(this);
+
+ constructor(title = RandomUnmuterNode.title) {
+ super(title);
+
+ rgthree.addEventListener('queue', this.onQueueBound);
+ rgthree.addEventListener('queue-end', this.onQueueEndBound);
+ rgthree.addEventListener('graph-to-prompt', this.onGraphtoPromptBound);
+ rgthree.addEventListener('graph-to-prompt-end', this.onGraphtoPromptEndBound);
+ }
+
+ override onRemoved() {
+ rgthree.removeEventListener('queue', this.onQueueBound);
+ rgthree.removeEventListener('queue-end', this.onQueueEndBound);
+ rgthree.removeEventListener('graph-to-prompt', this.onGraphtoPromptBound);
+ rgthree.removeEventListener('graph-to-prompt-end', this.onGraphtoPromptEndBound);
+ }
+
+ onQueue(event: Event) {
+ this.processingQueue = true;
+ }
+ onQueueEnd(event: Event) {
+ this.processingQueue = false;
+ }
+ onGraphtoPrompt(event: Event) {
+ console.log('onQueue', event);
+ if (!this.processingQueue) {
+ return;
+ }
+ this.tempEnabledNode = null;
+ // Check that all are muted and, if so, choose one to unmute.
+ const linkedNodes = getConnectedInputNodesAndFilterPassThroughs(this);
+ let allMuted = true;
+ if (linkedNodes.length) {
+ for (const node of linkedNodes) {
+ if (node.mode !== this.modeOff) {
+ allMuted = false;
+ break;
+ }
+ }
+ if (allMuted) {
+ this.tempEnabledNode = linkedNodes[Math.floor(Math.random() * linkedNodes.length)] || null;
+ if (this.tempEnabledNode) {
+ this.tempEnabledNode.mode = this.modeOn;
+ }
+ }
+ }
+ }
+ onGraphtoPromptEnd(event: Event) {
+ console.log('onQueueEnd', event);
+ if (this.tempEnabledNode) {
+ this.tempEnabledNode.mode = this.modeOff;
+ this.tempEnabledNode = null;
+ }
+ }
+
+ // override async handleAction(action: string) {
+ // if (action === 'Mute all') {
+ // for (const widget of this.widgets) {
+ // this.forceWidgetOff(widget);
+ // }
+ // } else if (action === 'Enable all') {
+ // for (const widget of this.widgets) {
+ // this.forceWidgetOn(widget);
+ // }
+ // }
+ // }
+}
+
+app.registerExtension({
+ name: "rgthree.RandomUnmuter",
+ registerCustomNodes() {
+ RandomUnmuterNode.setUp(RandomUnmuterNode);
+ },
+ loadedGraphNode(node: LGraphNode) {
+ if (node.type == RandomUnmuterNode.title) {
+ (node as any)._tempWidth = node.size[0];
+ }
+ }
+});
\ No newline at end of file
diff --git a/ts/rgthree.ts b/ts/rgthree.ts
index 2632e35..5ef5670 100644
--- a/ts/rgthree.ts
+++ b/ts/rgthree.ts
@@ -107,11 +107,30 @@ class Rgthree {
// Override the loadGraphData so we can check for bad links and ask the user to fix them.
const that = this;
+
+ const queuePrompt = app.queuePrompt as Function;
+ app.queuePrompt = async function() {
+ that.fireEvent('queue', {});
+ let promise = queuePrompt.apply(app, [...arguments]);
+ that.fireEvent('queue-end', {});
+ return promise;
+ }
+
+ const graphToPrompt = app.graphToPrompt as Function;
+ app.graphToPrompt = async function() {
+ that.fireEvent('graph-to-prompt', {});
+ let promise = graphToPrompt.apply(app, [...arguments]);
+ await promise;
+ that.fireEvent('graph-to-prompt-end', {});
+ return promise;
+ }
+
const clean = app.clean;
app.clean = function() {
document.querySelector('.rgthree-bad-links-alerts-container')?.remove();
clean && clean.call(app, ...arguments);
};
+
const loadGraphData = app.loadGraphData;
app.loadGraphData = function(graph: serializedLGraph) {
if (this.monitorLinkTimeout) {
@@ -204,7 +223,30 @@ class Rgthree {
}, 100);
loadGraphData && loadGraphData.call(app, ...arguments);
}
+ }
+ private readonly eventsToFns = new Map void>>();
+
+ addEventListener(event: string, fn: (ev: Event) => void) {
+ if (!this.eventsToFns.has(event)) {
+ this.eventsToFns.set(event, new Set());
+ }
+ this.eventsToFns.get(event)!.add(fn);
+ }
+
+ removeEventListener(event: string, fn: (ev: Event) => void) {
+ if (this.eventsToFns.has(event)) {
+ this.eventsToFns.get(event)!.delete(fn);
+ }
+ }
+
+ fireEvent(event: string, data: any) {
+ if (this.eventsToFns.has(event)) {
+ for (let fn of this.eventsToFns.get(event)!) {
+ const event = new Event(data);
+ fn(event);
+ }
+ }
}
setLogLevel(level: LogLevel) {
@@ -237,3 +279,4 @@ class Rgthree {
export const rgthree = new Rgthree();
// @ts-ignore. Expose it on window because, why not.
window.rgthree = rgthree;
+
diff --git a/web/base_any_input_connected_node.js b/web/base_any_input_connected_node.js
index 1ea490f..28d3323 100644
--- a/web/base_any_input_connected_node.js
+++ b/web/base_any_input_connected_node.js
@@ -22,6 +22,16 @@ export class BaseAnyInputConnectedNode extends RgthreeBaseNode {
}
return this.schedulePromise;
}
+ clone() {
+ const cloned = super.clone();
+ while (cloned.inputs.length > 1) {
+ cloned.removeInput(cloned.inputs.length - 1);
+ }
+ if (cloned.inputs[0]) {
+ cloned.inputs[0].label = '';
+ }
+ return cloned;
+ }
stabilizeInputsOutputs() {
var _a;
const hasEmptyInput = !((_a = this.inputs[this.inputs.length - 1]) === null || _a === void 0 ? void 0 : _a.link);
diff --git a/web/constants.js b/web/constants.js
index 4d54da6..1fbdf36 100644
--- a/web/constants.js
+++ b/web/constants.js
@@ -12,4 +12,5 @@ export const NodeTypesString = {
FAST_ACTIONS_BUTTON: addRgthree('Fast Actions Button'),
NODE_COLLECTOR: addRgthree('Node Collector'),
REROUTE: addRgthree('Reroute'),
+ RANDOM_UNMUTER: addRgthree('Random Unmuter'),
};
diff --git a/web/node_mode_repeater.js b/web/node_mode_repeater.js
index 2001e39..b69e7eb 100644
--- a/web/node_mode_repeater.js
+++ b/web/node_mode_repeater.js
@@ -25,6 +25,7 @@ class NodeModeRepeater extends BaseCollectorNode {
NodeTypesString.NODE_COLLECTOR,
NodeTypesString.FAST_ACTIONS_BUTTON,
NodeTypesString.REROUTE,
+ NodeTypesString.RANDOM_UNMUTER,
].includes(nextNode.type || ""));
}
onConnectInput(inputIndex, outputType, outputSlot, outputNode, outputIndex) {
diff --git a/web/random_unmuter.js b/web/random_unmuter.js
new file mode 100644
index 0000000..b7c22b6
--- /dev/null
+++ b/web/random_unmuter.js
@@ -0,0 +1,80 @@
+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";
+const MODE_MUTE = 2;
+const MODE_ALWAYS = 0;
+class RandomUnmuterNode extends BaseAnyInputConnectedNode {
+ constructor(title = RandomUnmuterNode.title) {
+ super(title);
+ this.modeOn = MODE_ALWAYS;
+ this.modeOff = MODE_MUTE;
+ this.tempEnabledNode = null;
+ this.processingQueue = false;
+ this.onQueueBound = this.onQueue.bind(this);
+ this.onQueueEndBound = this.onQueueEnd.bind(this);
+ this.onGraphtoPromptBound = this.onGraphtoPrompt.bind(this);
+ this.onGraphtoPromptEndBound = this.onGraphtoPromptEnd.bind(this);
+ rgthree.addEventListener('queue', this.onQueueBound);
+ rgthree.addEventListener('queue-end', this.onQueueEndBound);
+ rgthree.addEventListener('graph-to-prompt', this.onGraphtoPromptBound);
+ rgthree.addEventListener('graph-to-prompt-end', this.onGraphtoPromptEndBound);
+ }
+ onRemoved() {
+ rgthree.removeEventListener('queue', this.onQueueBound);
+ rgthree.removeEventListener('queue-end', this.onQueueEndBound);
+ rgthree.removeEventListener('graph-to-prompt', this.onGraphtoPromptBound);
+ rgthree.removeEventListener('graph-to-prompt-end', this.onGraphtoPromptEndBound);
+ }
+ onQueue(event) {
+ this.processingQueue = true;
+ }
+ onQueueEnd(event) {
+ this.processingQueue = false;
+ }
+ onGraphtoPrompt(event) {
+ console.log('onQueue', event);
+ if (!this.processingQueue) {
+ return;
+ }
+ this.tempEnabledNode = null;
+ const linkedNodes = getConnectedInputNodesAndFilterPassThroughs(this);
+ let allMuted = true;
+ if (linkedNodes.length) {
+ for (const node of linkedNodes) {
+ if (node.mode !== this.modeOff) {
+ allMuted = false;
+ break;
+ }
+ }
+ if (allMuted) {
+ this.tempEnabledNode = linkedNodes[Math.floor(Math.random() * linkedNodes.length)] || null;
+ if (this.tempEnabledNode) {
+ this.tempEnabledNode.mode = this.modeOn;
+ }
+ }
+ }
+ }
+ onGraphtoPromptEnd(event) {
+ console.log('onQueueEnd', event);
+ if (this.tempEnabledNode) {
+ this.tempEnabledNode.mode = this.modeOff;
+ this.tempEnabledNode = null;
+ }
+ }
+}
+RandomUnmuterNode.exposedActions = ['Mute all', 'Enable all'];
+RandomUnmuterNode.type = NodeTypesString.RANDOM_UNMUTER;
+RandomUnmuterNode.title = RandomUnmuterNode.type;
+app.registerExtension({
+ name: "rgthree.RandomUnmuter",
+ registerCustomNodes() {
+ RandomUnmuterNode.setUp(RandomUnmuterNode);
+ },
+ loadedGraphNode(node) {
+ if (node.type == RandomUnmuterNode.title) {
+ node._tempWidth = node.size[0];
+ }
+ }
+});
diff --git a/web/rgthree.js b/web/rgthree.js
index fe64df6..ef0cd05 100644
--- a/web/rgthree.js
+++ b/web/rgthree.js
@@ -66,6 +66,7 @@ class Rgthree {
this.logger = new LogSession("[rgthree]");
this.monitorBadLinksAlerted = false;
this.monitorLinkTimeout = null;
+ this.eventsToFns = new Map();
window.addEventListener("keydown", (e) => {
this.ctrlKey = !!e.ctrlKey;
this.altKey = !!e.altKey;
@@ -79,6 +80,21 @@ class Rgthree {
this.shiftKey = !!e.shiftKey;
});
const that = this;
+ const queuePrompt = app.queuePrompt;
+ app.queuePrompt = async function () {
+ that.fireEvent('queue', {});
+ let promise = queuePrompt.apply(app, [...arguments]);
+ that.fireEvent('queue-end', {});
+ return promise;
+ };
+ const graphToPrompt = app.graphToPrompt;
+ app.graphToPrompt = async function () {
+ that.fireEvent('graph-to-prompt', {});
+ let promise = graphToPrompt.apply(app, [...arguments]);
+ await promise;
+ that.fireEvent('graph-to-prompt-end', {});
+ return promise;
+ };
const clean = app.clean;
app.clean = function () {
var _a;
@@ -178,6 +194,25 @@ class Rgthree {
loadGraphData && loadGraphData.call(app, ...arguments);
};
}
+ addEventListener(event, fn) {
+ if (!this.eventsToFns.has(event)) {
+ this.eventsToFns.set(event, new Set());
+ }
+ this.eventsToFns.get(event).add(fn);
+ }
+ removeEventListener(event, fn) {
+ if (this.eventsToFns.has(event)) {
+ this.eventsToFns.get(event).delete(fn);
+ }
+ }
+ fireEvent(event, data) {
+ if (this.eventsToFns.has(event)) {
+ for (let fn of this.eventsToFns.get(event)) {
+ const event = new Event(data);
+ fn(event);
+ }
+ }
+ }
setLogLevel(level) {
GLOBAL_LOG_LEVEL = level;
}