From 882f4074463b88e0c19bc4fcbb8242cfded482cc Mon Sep 17 00:00:00 2001 From: rgthree Date: Sun, 1 Oct 2023 16:52:50 -0400 Subject: [PATCH] Adds a new node: Random Unmuter and addresses #31. Also adds ability to hook into prompting from rgthree global. --- ts/base_any_input_connected_node.ts | 10 +++ ts/constants.ts | 1 + ts/node_mode_repeater.ts | 1 + ts/random_unmuter.ts | 107 +++++++++++++++++++++++++++ ts/rgthree.ts | 43 +++++++++++ web/base_any_input_connected_node.js | 10 +++ web/constants.js | 1 + web/node_mode_repeater.js | 1 + web/random_unmuter.js | 80 ++++++++++++++++++++ web/rgthree.js | 35 +++++++++ 10 files changed, 289 insertions(+) create mode 100644 ts/random_unmuter.ts create mode 100644 web/random_unmuter.js 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; }