diff --git a/README.md b/README.md index 1a7941a..9768ea9 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,12 @@ A set of four nodes designed to pause execution of the workflow to allow you to make selections and/or edits before continuing. There's an example workflow that illustrates all of them at the end. -- 'Image Filter' - pause the flow and pick which images from a set you want to proceed with -- 'Mask Image Filter' - launch the mask editor for the image, and return the image and mask -- 'Text Image Filter' - show the image with some editable text which is returned -- 'Text Image Filter witrh Extras' - as 'Text Image Filter' but with three extra single line texts fields that are also returned +- ['Image Filter'](#image-filter) - pause the flow and pick which images from a set you want to proceed with +- ['Mask Image Filter'](#mask-image-filter) - launch the mask editor for the image, and return the image and mask +- ['Text Image Filter'](#text-image-filter) - show the image with some editable text which is returned +- ['Text Image Filter with Extras'](#text-image-filter-with-extras) - as 'Text Image Filter' but with three extra single line texts fields that are also returned + +There's also a helper node, 'Split String by Commas' that can be used if you want more extra values. ## Examples of what you might do with them @@ -83,11 +85,15 @@ generated. Here's a trivial workflow: ![text workflow](images/text%20workflow.png) + ## Text Image Filter with Extras Just like the Text Image Filter, but with three extra text fields, intended for short form - like specifying the denoising you want on the next step. -They are all strings, so you'll probably need to run the output through some sort of conversion node. +They are all strings, so you'll probably need to run the output through some sort of conversion node. +If you need more than three, consider using the 'Split Text by Commas' helper node. + +If you use the optional 'tip' input, the contents will be displayed under the extras input fields, so you can remind yourself what they are for! --- diff --git a/__init__.py b/__init__.py index db821c1..88ac668 100644 --- a/__init__.py +++ b/__init__.py @@ -6,15 +6,17 @@ """ from .image_filter import ImageFilter, TextImageFilter, MaskImageFilter, TextImageFilterWithExtras +from .split_by_commas import SplitByCommas -VERSION = "1.1.2" +VERSION = "1.1.3" WEB_DIRECTORY = "./js" NODE_CLASS_MAPPINGS= { "Image Filter": ImageFilter, "Text Image Filter": TextImageFilter, "Text Image Filter with Extras": TextImageFilterWithExtras, - "Mask Image Filter": MaskImageFilter + "Mask Image Filter": MaskImageFilter, + "Split String by Commas": SplitByCommas } __all__ = ["NODE_CLASS_MAPPINGS", "WEB_DIRECTORY"] diff --git a/image_filter.py b/image_filter.py index ba7bae3..ead674e 100644 --- a/image_filter.py +++ b/image_filter.py @@ -158,6 +158,7 @@ class TextImageFilterWithExtras(PreviewImage): }, "optional": { "mask" : ("MASK", {"tooltip": "Optional - if provided, will be overlaid on image"}), + "tip" : ("STRING", {"default":"", "tooltip": "Optional - if provided, will be displayed in popup window"}) }, "hidden": HIDDEN, } @@ -166,9 +167,9 @@ class TextImageFilterWithExtras(PreviewImage): def IS_CHANGED(cls, **kwargs): return float("NaN") - def func(self, image, text, extra1, extra2, extra3, timeout, uid, mask=None, **kwargs): + def func(self, image, text, extra1, extra2, extra3, timeout, uid, mask=None, tip="", **kwargs): urls:list[str] = self.save_images(images=image, **kwargs)['ui']['images'] - payload = {"uid": uid, "urls":urls, "text":text, "extras":[extra1, extra2, extra3]} + payload = {"uid": uid, "urls":urls, "text":text, "extras":[extra1, extra2, extra3], "tip":tip} if mask is not None: payload['mask_urls'] = self.save_images(images=mask_to_image(mask), **kwargs)['ui']['images'] response = send_with_resend(payload, timeout, uid) diff --git a/js/filter.css b/js/filter.css index d52df3a..b1fc857 100644 --- a/js/filter.css +++ b/js/filter.css @@ -41,6 +41,12 @@ font-size: larger; } +.cg_popup .tip { + display: block; + padding-top: 10px; + line-height: 20px; +} + .cg_popup .buttons { position: absolute; width:14%; diff --git a/js/popup.js b/js/popup.js index a4eed9c..4d44c1d 100644 --- a/js/popup.js +++ b/js/popup.js @@ -36,6 +36,7 @@ class Popup extends HTMLSpanElement { this.send_button = create('button', 'control', this.buttons, {innerText:"Send (S)"} ) this.cancel_button = create('button', 'control', this.buttons, {innerText:"Cancel (X)"} ) this.extras = create('span', 'extras', this.buttons) + this.tip = create('span', 'tip', this.buttons) this.grid.addEventListener('click', this.on_click.bind(this)) this.send_button.addEventListener( 'click', this.send_current_state.bind(this) ) @@ -43,16 +44,19 @@ class Popup extends HTMLSpanElement { document.addEventListener('keypress', this.on_keypress.bind(this) ) document.body.appendChild(this) + this.last_response_sent = 0 this.close() } _send_response(msg, no_extras) { + if (Date.now()-this.last_response_sent < 1000) return const body = new FormData(); var response = msg if (!no_extras) Array.from(this.extras.children).forEach((e)=>{ response = response + "|||" + e.value }) body.append('response', response); api.fetchApi("/cg-image-filter-message", { method: "POST", body, }); Log.log(`"Sent ${JSON.stringify(response)}`) + if (!no_extras) this.last_response_sent = Date.now() this.close() } @@ -70,13 +74,19 @@ class Popup extends HTMLSpanElement { close() { this.classList.add('hidden') this.counter.classList.add('hidden') + this.tip.innerHTML = "" this.active = false if (document.getElementById('maskEditor')) document.getElementById('maskEditor').style.display = 'none' this.n_images = 0 } handle_message(message) { + if (!message.detail?.tick) Log.log(`Got ${JSON.stringify(message.detail)}`) + if (Date.now()-this.last_response_sent < 1000) { + Log.log(`Ignoring message because we just responded`) + return + } if (this.handling_message && !detail.timeout) { Log.log(`Already handling a message, so dropped message`) return @@ -98,6 +108,9 @@ class Popup extends HTMLSpanElement { else if (detail.tick) this.handle_tick(detail) else if (detail.maskedit) this.handle_maskedit(detail) else if (detail.urls) this.handle_urls(detail) + + if (detail.tip) this.handle_tip(detail) + } finally { this.handling_message = false } } @@ -108,6 +121,8 @@ class Popup extends HTMLSpanElement { handle_timeout(detail) { this.close() } + handle_tip(detail) { this.tip.innerHTML = detail.tip.replace(/(?:\r\n|\r|\n)/g, '
') } + window_not_showing(uid) { const node = app.graph._nodes_by_id[uid] return ( diff --git a/pyproject.toml b/pyproject.toml index 438b6fe..73e77be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "cg-image-filter" description = "A set of custom nodes that pause a workflow while you select images, add masks, or edit text." -version = "1.1.2" +version = "1.1.3" license = { file = "LICENSE" } [project.urls] diff --git a/split_by_commas.py b/split_by_commas.py new file mode 100644 index 0000000..8297d82 --- /dev/null +++ b/split_by_commas.py @@ -0,0 +1,15 @@ +class SplitByCommas: + RETURN_TYPES = ("STRING","STRING","STRING","STRING","STRING") + FUNCTION = "func" + CATEGORY = "image_filter" + OUTPUT_NODE = False + + DESCRIPTION = "Split the input string into up to five pieces. Splits on commas and then strips whitespace from front and end." + + @classmethod + def INPUT_TYPES(s): + return {"required": {"string" : ("STRING", {"default":""})}} + + def func(self, string:str): + bits = [r.strip() for r in string.split(",",5)] + [""]*5 + return tuple(bits[:5]) \ No newline at end of file