diff --git a/nodes.py b/nodes.py index 02cb7e8e..dd9f06d4 100644 --- a/nodes.py +++ b/nodes.py @@ -51,7 +51,7 @@ def interrupt_processing(value=True): class CLIPTextEncode: @classmethod def INPUT_TYPES(s): - return {"required": {"text": ("STRING", {"multiline": True, "dynamic_prompt": True}), "clip": ("CLIP", )}} + return {"required": {"text": ("STRING", {"multiline": True}), "clip": ("CLIP", )}} RETURN_TYPES = ("CONDITIONING",) FUNCTION = "encode" diff --git a/web/extensions/core/dynamicPrompts.js b/web/extensions/core/dynamicPrompts.js new file mode 100644 index 00000000..3729cf96 --- /dev/null +++ b/web/extensions/core/dynamicPrompts.js @@ -0,0 +1,40 @@ +import { app } from "../../scripts/app.js"; + +// Allows for simple dynamic prompt replacement +// Inputs in the format {a|b} will have a random value of a or b chosen when the prompt is queued. + +app.registerExtension({ + name: "Comfy.DynamicPrompts", + nodeCreated(node) { + // TODO: Change this to replace the value and restore it after posting + + + if (node.widgets) { + // Locate dynamic prompt text widgets + // Include any widgets with dynamicPrompts set to true, and customtext + const widgets = node.widgets.filter( + (n) => (n.type === "customtext" && n.dynamicPrompts !== false) || n.dynamicPrompts + ); + for (const widget of widgets) { + // Override the serialization of the value to resolve dynamic prompts for all widgets supporting it in this node + widget.serializeValue = () => { + let prompt = widget.value; + while (prompt.replace("\\{", "").includes("{") && prompt.replace("\\}", "").includes("}")) { + const startIndex = prompt.replace("\\{", "00").indexOf("{"); + const endIndex = prompt.replace("\\}", "00").indexOf("}"); + + const optionsString = prompt.substring(startIndex + 1, endIndex); + const options = optionsString.split("|"); + + const randomIndex = Math.floor(Math.random() * options.length); + const randomOption = options[randomIndex]; + + prompt = prompt.substring(0, startIndex) + randomOption + prompt.substring(endIndex + 1); + } + + return prompt; + }; + } + } + }, +}); diff --git a/web/extensions/core/rerouteNode.js b/web/extensions/core/rerouteNode.js new file mode 100644 index 00000000..45b63d87 --- /dev/null +++ b/web/extensions/core/rerouteNode.js @@ -0,0 +1,92 @@ +import { app } from "../../scripts/app.js"; + +// Node that allows you to redirect connections for cleaner graphs + +app.registerExtension({ + name: "Comfy.RerouteNode", + registerCustomNodes() { + class RerouteNode { + constructor() { + if (!this.properties) { + this.properties = {}; + } + this.properties.showOutputText = RerouteNode.defaultVisibility; + + this.addInput("", "*"); + this.addOutput(this.properties.showOutputText ? "*" : "", "*"); + this.onConnectInput = function (_, type) { + if (type !== this.outputs[0].type) { + this.removeOutput(0); + this.addOutput(this.properties.showOutputText ? type : "", type); + this.size = this.computeSize(); + } + }; + + this.clone = function () { + const cloned = RerouteNode.prototype.clone.apply(this); + cloned.removeOutput(0); + cloned.addOutput(this.properties.showOutputText ? "*" : "", "*"); + cloned.size = cloned.computeSize(); + return cloned; + }; + + // This node is purely frontend and does not impact the resulting prompt so should not be serialized + this.isVirtualNode = true; + } + + getExtraMenuOptions(_, options) { + options.unshift( + { + content: (this.properties.showOutputText ? "Hide" : "Show") + " Type", + callback: () => { + this.properties.showOutputText = !this.properties.showOutputText; + if (this.properties.showOutputText) { + this.outputs[0].name = this.outputs[0].type; + } else { + this.outputs[0].name = ""; + } + this.size = this.computeSize(); + }, + }, + { + content: (RerouteNode.defaultVisibility ? "Hide" : "Show") + " Type By Default", + callback: () => { + RerouteNode.setDefaultTextVisibility(!RerouteNode.defaultVisibility); + }, + } + ); + } + + computeSize() { + return [ + this.properties.showOutputText && this.outputs && this.outputs.length + ? Math.max(55, LiteGraph.NODE_TEXT_SIZE * this.outputs[0].name.length * 0.6 + 40) + : 55, + 26, + ]; + } + + static setDefaultTextVisibility(visible) { + RerouteNode.defaultVisibility = visible; + if (visible) { + localStorage["Comfy.RerouteNode.DefaultVisibility"] = "true"; + } else { + delete localStorage["Comfy.RerouteNode.DefaultVisibility"]; + } + } + } + + // Load default visibility + RerouteNode.setDefaultTextVisibility(!!localStorage["Comfy.RerouteNode.DefaultVisibility"]); + + LiteGraph.registerNodeType( + "Reroute", + Object.assign(RerouteNode, { + title_mode: LiteGraph.NO_TITLE, + title: "Reroute", + }) + ); + + RerouteNode.category = "utils"; + }, +}); diff --git a/web/extensions/logging.js.example b/web/extensions/logging.js.example new file mode 100644 index 00000000..ce0b346e --- /dev/null +++ b/web/extensions/logging.js.example @@ -0,0 +1,54 @@ +import { app } from "../scripts/app.js"; + +const ext = { + name: "Example.LoggingExtension", + async init(app) { + // Any initial setup to run as soon as the page loads + console.log("[logging]", "extension init"); + }, + async setup(app) { + // Any setup to run after the app is created + console.log("[logging]", "extension setup"); + }, + async addCustomNodeDefs(defs, app) { + // Add custom node definitions + // These definitions will be configured and registered automatically + // defs is a lookup core nodes, add yours into this + console.log("[logging]", "add custom node definitions", "current nodes:", Object.keys(defs)); + }, + async getCustomWidgets(app) { + // Return custom widget types + // See ComfyWidgets for widget examples + console.log("[logging]", "provide custom widgets"); + }, + async beforeRegisterNodeDef(nodeType, nodeData, app) { + // Run custom logic before a node definition is registered with the graph + console.log("[logging]", "before register node: ", nodeType, nodeData); + + // This fires for every node definition so only log once + delete ext.beforeRegisterNodeDef; + }, + async registerCustomNodes(app) { + // Register any custom node implementations here allowing for more flexability than a custom node def + console.log("[logging]", "register custom nodes"); + }, + loadedGraphNode(node, app) { + // Fires for each node when loading/dragging/etc a workflow json or png + // If you break something in the backend and want to patch workflows in the frontend + // This is the place to do this + console.log("[logging]", "loaded graph node: ", node); + + // This fires for every node on each load so only log once + delete ext.loadedGraphNode; + }, + nodeCreated(node, app) { + // Fires every time a node is constructed + // You can modify widgets/add handlers/etc here + console.log("[logging]", "node created: ", node); + + // This fires for every node so only log once + delete ext.nodeCreated; + } +}; + +app.registerExtension(ext); diff --git a/web/index.html b/web/index.html index cd95594f..67c7d1e8 100644 --- a/web/index.html +++ b/web/index.html @@ -6,6 +6,8 @@