diff --git a/nodes.py b/nodes.py index f53531cc..b35c09de 100644 --- a/nodes.py +++ b/nodes.py @@ -44,7 +44,7 @@ def filter_files_extensions(files, extensions): class CLIPTextEncode: @classmethod def INPUT_TYPES(s): - return {"required": {"text": ("STRING", {"multiline": True}), "clip": ("CLIP", )}} + return {"required": {"text": ("STRING", {"multiline": True, "dynamic_prompt": True}), "clip": ("CLIP", )}} RETURN_TYPES = ("CONDITIONING",) FUNCTION = "encode" diff --git a/webshit/index.html b/webshit/index.html index 3e2645a2..e09c69ad 100644 --- a/webshit/index.html +++ b/webshit/index.html @@ -133,13 +133,14 @@ function onObjectInfo(json) { min_height = 1; min_width = 1; for (let x in inp) { - let default_val = min_val = max_val = step_val = multiline = undefined; + let default_val = min_val = max_val = step_val = multiline = dynamic_prompt = undefined; if (inp[x].length > 1) { default_val = inp[x][1]['default']; min_val = inp[x][1]['min']; max_val = inp[x][1]['max']; step_val = inp[x][1]['step']; multiline = inp[x][1]['multiline']; + dynamic_prompt = inp[x][1]['dynamic_prompt']; } let type = inp[x][0]; @@ -173,11 +174,21 @@ function onObjectInfo(json) { } else if (type == "STRING") { if (default_val == undefined) default_val = ""; if (multiline == undefined) multiline = false; + if (dynamic_prompt == undefined) dynamic_prompt = false; + + if (dynamic_prompt) + { + w1 = this.addWidget("number", "seed", 0, function(v){let s = this.options.step / 10;this.value = Math.round( v / s ) * s;}, { min: min_val, max: max_val, step: 10.0 * step_val} ); + w2 = this.addWidget("toggle", "Random seed after every gen", true, function(v){}, { on: "enabled", off:"disabled"} ); + w2.to_randomize = w1; + this._widgets += [w1, w2]; + } if (multiline) { var w = { type: "customtext", name: x, + dynamic_prompt: dynamic_prompt, get value() { return this.input_div.innerText;}, set value(x) { this.input_div.innerText = x;}, callback: function(v){console.log(v);}, @@ -252,6 +263,7 @@ function onObjectInfo(json) { } } else { w = this.addWidget("text", x, default_val, function(v){}, { multiline:false } ); + w.dynamic_prompt = dynamic_prompt; this._widgets += [w]; } } else { @@ -300,6 +312,16 @@ graph.onNodeRemoved = function(n) { } } +// from https://stackoverflow.com/a/47593316 +function rng_mulberry32(a) { + return function() { + var t = a += 0x6D2B79F5; + t = Math.imul(t ^ t >>> 15, t | 1); + t ^= t + Math.imul(t ^ t >>> 7, t | 61); + return ((t ^ t >>> 14) >>> 0) / 4294967296; + } +} + function graphToPrompt() { let s = graph.serialize(); let output = {}; @@ -309,6 +331,45 @@ function graphToPrompt() { for (let x in nodes) { let n = graph.getNodeById(nodes[x].id); let input_ = {}; + + // dynamic prompts handling + if (n.widgets && n.widgets.length > 0) { + // find widgets declared as supporting dynamic prompting + var supportedWidgets = n.widgets.filter(e => e.dynamic_prompt === true); + if (supportedWidgets.length > 0) { + // find the seed widget to use for this node + var seed = n.widgets.filter(e => e.name === "seed")[0].value; + var rng = rng_mulberry32(seed); + + // resolve dynamic prompts for all widgets supporting it in this node + for (let i in supportedWidgets) + { + var widget = supportedWidgets[i]; + + // store the unresolved prompt to restore it after sending the resolved prompt to the backend + // use of .innerText which keep \n symbols in order to not break multilines support + widget.value_initial = widget.input_div.innerText; + + // resolve the string + var prompt = widget.input_div.textContent; + while (prompt.includes('{') && prompt.includes('}')) { + const startIndex = prompt.indexOf('{'); + const endIndex = prompt.indexOf('}'); + + const optionsString = prompt.substring(startIndex + 1, endIndex); + const options = optionsString.split('|'); + + const randomIndex = Math.floor(rng() * (options.length)); + const randomOption = options[randomIndex]; + + prompt = prompt.substring(0, startIndex) + randomOption + prompt.substring(endIndex + 1); + } + + widget.value = prompt; + } + } + } + for (let y in n.widgets) { input_[n.widgets[y].name] = n.widgets[y].value; } @@ -378,6 +439,10 @@ function promptPosted(data) wid.to_randomize.value = Math.floor(Math.random() * 1125899906842624); } } + + // restore initial values replaced by dynamic prompting + if (wid.dynamic_prompt && wid.dynamic_prompt === true) + wid.value = wid.value_initial; } }