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 { rgthree } from "./rgthree.js"; const MODE_ALWAYS = 0; const MODE_MUTE = 2; const MODE_BYPASS = 4; class FastActionsButton extends BaseAnyInputConnectedNode { constructor(title) { super(title); this.comfyClass = NodeTypesString.FAST_ACTIONS_BUTTON; this.logger = rgthree.newLogSession("[FastActionsButton]"); this.isVirtualNode = true; this.serialize_widgets = true; this.widgetToData = new Map(); this.nodeIdtoFunctionCache = new Map(); this.executingFromShortcut = false; this.properties["buttonText"] = "🎬 Action!"; this.properties["shortcutModifier"] = "alt"; this.properties["shortcutKey"] = ""; this.buttonWidget = this.addWidget("button", this.properties["buttonText"], null, () => { this.executeConnectedNodes(); }, { serialize: false }); this.keypressBound = this.onKeypress.bind(this); this.keyupBound = this.onKeyup.bind(this); this.onConstructed(); } configure(info) { super.configure(info); setTimeout(() => { if (info.widgets_values) { for (let [index, value] of info.widgets_values.entries()) { if (index > 0) { if (value.startsWith("comfy_action:")) { value = value.replace("comfy_action:", ""); this.addComfyActionWidget(index, value); } if (this.widgets[index]) { this.widgets[index].value = value; } } } } }, 100); } clone() { const cloned = super.clone(); cloned.properties["buttonText"] = "🎬 Action!"; cloned.properties["shortcutKey"] = ""; return cloned; } onAdded(graph) { window.addEventListener("keydown", this.keypressBound); window.addEventListener("keyup", this.keyupBound); } onRemoved() { window.removeEventListener("keydown", this.keypressBound); window.removeEventListener("keyup", this.keyupBound); } async onKeypress(event) { const target = event.target; if (this.executingFromShortcut || target.localName == "input" || target.localName == "textarea") { return; } if (this.properties["shortcutKey"].trim() && this.properties["shortcutKey"].toLowerCase() === event.key.toLowerCase()) { const shortcutModifier = this.properties["shortcutModifier"]; let good = shortcutModifier === "ctrl" && event.ctrlKey; good = good || (shortcutModifier === "alt" && event.altKey); good = good || (shortcutModifier === "shift" && event.shiftKey); good = good || (shortcutModifier === "meta" && event.metaKey); if (good) { setTimeout(() => { this.executeConnectedNodes(); }, 20); this.executingFromShortcut = true; event.preventDefault(); event.stopImmediatePropagation(); app.canvas.dirty_canvas = true; return false; } } return; } onKeyup(event) { const target = event.target; if (target.localName == "input" || target.localName == "textarea") { return; } this.executingFromShortcut = false; } onPropertyChanged(property, value, _prevValue) { if (property == "buttonText") { this.buttonWidget.name = value; } if (property == "shortcutKey") { value = value.trim(); this.properties["shortcutKey"] = (value && value[0].toLowerCase()) || ""; } } handleLinkedNodesStabilization(linkedNodes) { var _a, _b, _c, _d, _e, _f, _g, _h; for (const [widget, data] of this.widgetToData.entries()) { if (!data.node) { continue; } if (!linkedNodes.includes(data.node)) { const index = this.widgets.indexOf(widget); if (index > -1) { this.widgetToData.delete(widget); this.removeWidget(widget); } else { const [m, a] = this.logger.debugParts("Connected widget is not in widgets... weird."); (_a = console[m]) === null || _a === void 0 ? void 0 : _a.call(console, ...a); } } } const badNodes = []; let indexOffset = 1; for (const [index, node] of linkedNodes.entries()) { if (!node) { const [m, a] = this.logger.debugParts("linkedNode provided that does not exist. "); (_b = console[m]) === null || _b === void 0 ? void 0 : _b.call(console, ...a); badNodes.push(node); continue; } let widgetAtSlot = this.widgets[index + indexOffset]; if (widgetAtSlot && ((_c = this.widgetToData.get(widgetAtSlot)) === null || _c === void 0 ? void 0 : _c.comfy)) { indexOffset++; widgetAtSlot = this.widgets[index + indexOffset]; } if (!widgetAtSlot || ((_e = (_d = this.widgetToData.get(widgetAtSlot)) === null || _d === void 0 ? void 0 : _d.node) === null || _e === void 0 ? void 0 : _e.id) !== node.id) { let widget = null; for (let i = index + indexOffset; i < this.widgets.length; i++) { if (((_g = (_f = this.widgetToData.get(this.widgets[i])) === null || _f === void 0 ? void 0 : _f.node) === null || _g === void 0 ? void 0 : _g.id) === node.id) { widget = this.widgets.splice(i, 1)[0]; this.widgets.splice(index + indexOffset, 0, widget); break; } } if (!widget) { const exposedActions = node.constructor.exposedActions || []; widget = this.addWidget("combo", node.title, "None", "", { values: ["None", "Mute", "Bypass", "Enable", ...exposedActions], }); widget.serializeValue = async (_node, _index) => { return widget === null || widget === void 0 ? void 0 : widget.value; }; this.widgetToData.set(widget, { node }); } } } for (let i = this.widgets.length - 1; i > linkedNodes.length + indexOffset - 1; i--) { const widgetAtSlot = this.widgets[i]; if (widgetAtSlot && ((_h = this.widgetToData.get(widgetAtSlot)) === null || _h === void 0 ? void 0 : _h.comfy)) { continue; } this.removeWidget(widgetAtSlot); } } removeWidget(widgetOrSlot) { const widget = typeof widgetOrSlot === "number" ? this.widgets[widgetOrSlot] : widgetOrSlot; if (widget && this.widgetToData.has(widget)) { this.widgetToData.delete(widget); } super.removeWidget(widgetOrSlot); } async executeConnectedNodes() { var _a; for (const widget of this.widgets) { if (widget == this.buttonWidget) { continue; } const action = widget.value; const { comfy, node } = (_a = this.widgetToData.get(widget)) !== null && _a !== void 0 ? _a : {}; if (comfy) { if (action === "Queue Prompt") { await comfy.queuePrompt(0); } continue; } if (node) { if (action === "Mute") { node.mode = MODE_MUTE; } else if (action === "Bypass") { node.mode = MODE_BYPASS; } else if (action === "Enable") { node.mode = MODE_ALWAYS; } if (node.handleAction) { await node.handleAction(action); } app.graph.change(); continue; } console.warn("Fast Actions Button has a widget without correct data."); } } addComfyActionWidget(slot, value) { let widget = this.addWidget("combo", "Comfy Action", "None", () => { if (widget.value.startsWith("MOVE ")) { this.widgets.push(this.widgets.splice(this.widgets.indexOf(widget), 1)[0]); widget.value = widget["lastValue_"]; } else if (widget.value.startsWith("REMOVE ")) { this.removeWidget(widget); } widget["lastValue_"] = widget.value; }, { values: ["None", "Queue Prompt", "REMOVE Comfy Action", "MOVE to end"], }); widget["lastValue_"] = value; widget.serializeValue = async (_node, _index) => { return `comfy_app:${widget === null || widget === void 0 ? void 0 : widget.value}`; }; this.widgetToData.set(widget, { comfy: app }); if (slot != null) { this.widgets.splice(slot, 0, this.widgets.splice(this.widgets.indexOf(widget), 1)[0]); } return widget; } onSerialize(o) { var _a; super.onSerialize && super.onSerialize(o); for (let [index, value] of (o.widgets_values || []).entries()) { if (((_a = this.widgets[index]) === null || _a === void 0 ? void 0 : _a.name) === "Comfy Action") { o.widgets_values[index] = `comfy_action:${value}`; } } } static setUp() { super.setUp(); addMenuItem(this, app, { name: "➕ Append a Comfy Action", callback: (nodeArg) => { nodeArg.addComfyActionWidget(); }, }); } } FastActionsButton.type = NodeTypesString.FAST_ACTIONS_BUTTON; FastActionsButton.title = NodeTypesString.FAST_ACTIONS_BUTTON; FastActionsButton["@buttonText"] = { type: "string" }; FastActionsButton["@shortcutModifier"] = { type: "combo", values: ["ctrl", "alt", "shift"], }; FastActionsButton["@shortcutKey"] = { type: "string" }; FastActionsButton.collapsible = false; app.registerExtension({ name: "rgthree.FastActionsButton", registerCustomNodes() { FastActionsButton.setUp(); }, loadedGraphNode(node) { if (node.type == FastActionsButton.title) { node._tempWidth = node.size[0]; } }, });