From 3637b748cc9760f1c73bb08b861d4ed69f71d550 Mon Sep 17 00:00:00 2001 From: Trung0246 Date: Tue, 16 Jan 2024 15:11:37 -0800 Subject: [PATCH] Disable addDOMWidget replacement for now It looks like it kind of break different dom-related widgets. The final goal should hopefully is somewhat achieve desirable backward-compatible with native ComfyuUI dom widget implementation. --- nodes.py | 462 ++++++++++++++++++++++++++++++++++++++++------ requirements.txt | 3 +- utils.py | 1 + web/js/nodes.js | 7 +- web/js/widgets.js | 323 +++++++++++++++----------------- 5 files changed, 553 insertions(+), 243 deletions(-) diff --git a/nodes.py b/nodes.py index 384e8b0..2ae1666 100644 --- a/nodes.py +++ b/nodes.py @@ -13,8 +13,10 @@ import builtins import json import copy import functools +import itertools import copy import re +import uuid # Self Code from . import utils as lib0246 @@ -22,6 +24,7 @@ from . import utils as lib0246 # 3rd Party import aiohttp.web import natsort +import toposort # ComfyUI from server import PromptServer @@ -327,9 +330,11 @@ def junction_unpack_raw( return base_dict def update_hold_db(key, data): - if data is not None: - Hold.HOLD_DB[key]["data"].extend(data) - return [None] if not Hold.HOLD_DB[key]["data"] else Hold.HOLD_DB[key]["data"] + if key in Hold.HOLD_DB: + if data is not None: + Hold.HOLD_DB[key]["data"].extend(data) + return [None] if not Hold.HOLD_DB[key]["data"] else Hold.HOLD_DB[key]["data"] + return [None] def junction_pack_loop(_junc_in, name, value): _junc_in[("type", name)] = type(value).__name__ @@ -911,46 +916,72 @@ class Hold: CATEGORY = "0246" def execute(self, _data_in = None, _id = None, _hold = None, _mode = None, _key_id = None, **kwargs): - _data_in = kwargs.get(next(filter(lambda x: x.startswith("_data_in"), kwargs.keys())), _data_in) - mode = _mode[0] if _mode else None + for key in kwargs: + if key.startswith("_data_in"): + _data_in = kwargs[key] + break - if _id[0] not in Hold.HOLD_DB: - Hold.HOLD_DB[_id[0]] = { + if isinstance(_id, list): + _id = str(_id[0]) if len(_id) > 0 else None + if isinstance(_hold, list): + _hold = _hold[0] if len(_hold) > 0 else None + if isinstance(_mode, list): + _mode = _mode[0] if len(_mode) > 0 else None + if isinstance(_key_id, list): + _key_id = str(_key_id[0]) if len(_key_id) > 0 else None + + if _id not in Hold.HOLD_DB: + Hold.HOLD_DB[_id] = { "track": PROMPT_ID, "data": [] } - if Hold.HOLD_DB[_id[0]]["track"] != PROMPT_ID: - Hold.HOLD_DB[_id[0]]["track"] = PROMPT_ID + ui_text = f"Id: {_id}, " - if mode != "save": - Hold.HOLD_DB[_id[0]]["data"] = [] + ext_db = _key_id in Hold.HOLD_DB - ui_text = f"Id: {_id[0]}, " - - # Check if _key_id is specified and process accordingly - if _key_id and len(_key_id[0]) > 0: - if mode == "clear": - Hold.HOLD_DB[_key_id[0]]["data"] = [] - result = update_hold_db(_key_id[0], _data_in) - ui_text += f"Size: {lib0246.len_zero_arr(result)}, Key: {_key_id[0]}, " - # Check if _hold is specified and process accordingly - elif _hold and _hold[0]: - result = _data_in if _data_in is not None else [None] - ui_text += f"Size: {lib0246.len_zero_arr(result)}, Passed, " - else: - # Update the outputs and HOLD_DB for _id if specified - if _id: - result = update_hold_db(_id[0], None if (mode == "ignore" and len(Hold.HOLD_DB[_id[0]]["data"]) > 0) else _data_in) - BASE_EXECUTOR.outputs[_id[0]] = Hold.HOLD_DB[_id[0]]["data"] - ui_text += f"Size: {len(result)}, " - if mode == "clear": - Hold.HOLD_DB[_id[0]]["data"] = [] + result = None + if _hold: + result = Hold.HOLD_DB[_id]["data"] + if ext_db: + if _mode == "clear": + Hold.HOLD_DB[_key_id]["data"].clear() + elif _mode == "ignore": + result = Hold.HOLD_DB[_key_id]["data"] + elif _mode == "save": + Hold.HOLD_DB[_id]["data"].extend(Hold.HOLD_DB[_key_id]["data"]) + if _mode != "save": + Hold.HOLD_DB[_id]["data"].clear() + Hold.HOLD_DB[_id]["data"].extend(_data_in) + ui_text += f"Passed, Size: {len(result)}, " + elif ext_db: + result = _data_in + if _data_in is None or ( + Hold.HOLD_DB[_key_id]["track"] == PROMPT_ID and + len(Hold.HOLD_DB[_key_id]["data"]) > 0 + ): + if _mode == "ignore": + result = Hold.HOLD_DB[_key_id]["data"] + else: + result = update_hold_db(_key_id, _data_in) + ui_text += f"Key: {_key_id}, Size: {len(result)}, " + elif _data_in and len(_data_in) > 0: + if Hold.HOLD_DB[_id]["track"] != PROMPT_ID: + Hold.HOLD_DB[_id]["track"] = PROMPT_ID + if _mode != "save": + Hold.HOLD_DB[_id]["data"].clear() + result = [] + elif _mode == "clear": + result = _data_in + Hold.HOLD_DB[_id]["data"] = _data_in else: - ui_text += f"None, " - result = [None] + result = update_hold_db(_id, _data_in) + ui_text += f"Size: {len(result)}, " - ui_text += f"Track: {Hold.HOLD_DB[_id[0]]['track']}" + if not result or len(result) == 0: + result = [None] + + ui_text += f"Track: {Hold.HOLD_DB[_id]['track']}" return { "ui": { @@ -1127,7 +1158,10 @@ class Beautify: CATEGORY = "0246" def execute(self, data = None, mode = None, **kwargs): - data = kwargs.get(next(filter(lambda x: x.startswith("data"), kwargs.keys())), data) + for key in kwargs: + if key.startswith("data"): + data = kwargs[key] + break raw_mode = 0 res_str = None @@ -1967,7 +2001,11 @@ class Hub: name_data = {} curr_nodes = _workflow[0]["workflow"]["nodes"] - self_index = next(i for i, _ in enumerate(curr_nodes) if _["id"] == int(_id[0])) + self_index = None + for i, _ in enumerate(curr_nodes): + if _["id"] == int(_id[0]): + self_index = i + break curr_extra = _workflow[0]["workflow"]["extra"] for i, pin in enumerate(curr_nodes[self_index]["outputs"]): @@ -2036,45 +2074,351 @@ class Hub: ###################################################################################### CLOUD_METHOD = { - "text": { - "func": False, # Can be used as function + "text": None, # Cannot be used as function if None + "pin": None, + "weight": { "bind": False, # Can affect other clouds if itself is affected "many": False, # Can output multiple clouds - "sole": False # Can only exist once for same kind within a group - }, - "weight": { - "func": True, - "bind": False, - "many": False, - "sole": True, + "sole": True, # Can only exist once for same kind within a group }, "rand": { - "func": True, "bind": True, "many": True, "sole": True, }, "cycle": { - "func": True, "bind": True, "many": True, "sole": True, }, "merge": { - "func": True, "bind": True, "many": False, "sole": True, }, } +def group_query_inst(group_dict, group_id, group_list = None, inst_curr = None): + inst_curr = set() if inst_curr is None else inst_curr + group_list = list(group_dict.keys()) if group_list is None else group_list + stack = [group_id] + seen = set() + while len(stack) > 0: + track_group = stack.pop() + if track_group in seen: + continue + seen.add(track_group) + if "group" in group_dict[track_group]: + stack.extend(group_dict[track_group]["group"]) + if "inst" in group_dict[track_group]: + for track_inst in group_dict[track_group]["inst"]: + inst_curr.add(track_inst) + return inst_curr + +class CloudFunc: + def __init__(self, kind): + self.func = getattr(CloudFunc, f"func_{kind}") + + """ + { + eval # Evaluated data + data # Persistent data + order # Current isntance order + index # Current instance result index + change # Whether whole cloud changed + } + """ + + @classmethod + def func_text(cls, obj, inst_id, pass_state, state): + return obj.inst[inst_id]["widgets_values"][0] + + @classmethod + def func_weight(cls, obj, inst_id, pass_state, state): + # [TODO] Escape special characters for _[0] + return list(map(lambda _: f"({_[0]}: {_[1]})", itertools.product(pass_state["data"], obj.inst[inst_id]["widgets_values"][0]))) + + @classmethod + def func_rand(cls, obj, inst_id, pass_state, state): + res = [] + if inst_id not in state["data"]: + state["data"][inst_id] = { + "rand": random.Random(), + "seed_mode": [], + "seed_data": [], + "seed_count": 0 + } + + curr_state = state["data"][inst_id] + + curr_state["seed_count"] = len(obj.inst[inst_id]["widgets_values"][0]) + curr_seed_len = len(curr_state["seed_data"]) + if curr_state["seed_count"] > curr_seed_len: + curr_state["seed_data"].extend([None] * (curr_state["seed_count"] - curr_seed_len)) + curr_state["seed_mode"].extend([None] * (curr_state["seed_count"] - curr_seed_len)) + elif curr_state["seed_count"] < curr_seed_len: + curr_state["seed_data"] = curr_state["seed_data"][:curr_state["seed_count"]] + curr_state["seed_mode"] = curr_state["seed_mode"][:curr_state["seed_count"]] + + for i, curr_count, curr_seed, curr_mode in zip(itertools.count(start=0, step=1), *obj.inst[inst_id]["widgets_values"]): + if curr_state["seed_data"][i] is None or \ + curr_state["seed_mode"][i] != curr_mode or \ + state["change"]: + curr_state["seed_data"][i] = curr_seed + curr_state["seed_mode"][i] = curr_mode + curr_state["rand"].seed(curr_seed) + else: + match curr_mode: + case "fix": + curr_state["rand"].seed(curr_seed) + case "add": + curr_state["seed_data"][i] += 1 + curr_state["rand"].seed(curr_state["seed_data"][i]) + case "sub": + curr_state["seed_data"][i] -= 1 + curr_state["rand"].seed(curr_state["seed_data"][i]) + case _: + # Default to "rand" mode + pass + + choice_list: list = copy.copy(pass_state["data"]) + for i in range(curr_count): + curr_choice = curr_state["rand_inst"].choice(choice_list) + res.append(curr_choice) + choice_list.remove(curr_choice) + + for i in pass_state["index"]: + pass_state["index"][i] = None + + return res + + @classmethod + def func_cycle(cls, obj, inst_id, pass_state, state): + res = [] + if inst_id not in state["data"]: + state["data"][inst_id] = { + "track_step": [], + "track_data": [], + "track_count": 0 + } + + curr_state = state["data"][inst_id] + + curr_state["track_count"] = len(obj.inst[inst_id]["widgets_values"][0]) + curr_track_len = len(curr_state["track_data"]) + if curr_state["track_count"] > curr_track_len: + curr_state["track_data"].extend([None] * (curr_state["track_count"] - curr_track_len)) + curr_state["track_step"].extend([None] * (curr_state["track_count"] - curr_track_len)) + elif curr_state["track_count"] < curr_track_len: + curr_state["track_data"] = curr_state["track_data"][:curr_state["track_count"]] + curr_state["track_step"] = curr_state["track_step"][:curr_state["track_count"]] + + for i, curr_offset, curr_step, curr_space, curr_count in zip(itertools.count(start=0, step=1), *obj.inst[inst_id]["widgets_values"]): + if curr_state["track_data"][i] is None or \ + curr_state["track_step"][i] != curr_step or \ + state["change"]: + curr_state["track_data"][i] = curr_offset + curr_state["track_step"][i] = curr_step + else: + curr_state["track_data"][i] += curr_step + + for i in range(curr_state["track_data"][i], curr_count * curr_space, curr_space): + res.append(pass_state["data"][i % len(pass_state["data"])]) + + for i in pass_state["index"]: + # result append to res should be already in order so no need special handling + pass_state["index"][i] = None + + return res + + @classmethod + def func_merge(cls, obj, inst_id, pass_state, state): + res = [] + + for i, curr_delim in zip(itertools.count(start=0, step=1), *obj.inst[inst_id]["widgets_values"]): + res.append(curr_delim.join(pass_state["data"])) + + for i in pass_state["index"]: + pass_state["index"][i] = None + + return res + +class CloudData: + def __init__(self): + self.inst = [] + self.group = {} + self.db = {} + + def dict_to_data(self, curr_id, inst_list, group_dict, db_dict = None, kwargs = None): + if kwargs is not None: + self.id = curr_id + if db_dict is not None: + self.db.update(db_dict) + self.db[curr_id] = self.db.get(curr_id, {}) + for inst in inst_list: + old_id = inst["id"] + new_id = str(uuid.uuid4()) + match inst["kind"]: + case "pin" if kwargs is not None: + curr_value = None + for key in kwargs: + curr_int = key.split(":")[0] + if curr_int.isnumeric() and int(curr_int) == inst["widgets_values"][0]: + curr_value = kwargs[key][0] + break + match curr_value: + case CloudData(): + self.dict_to_data(curr_value.id, curr_value.inst, curr_value.group, curr_value.db, None) + for group_id in group_dict: + if "inst" in group_dict[group_id]: + try: + curr_index = group_dict[group_id]["inst"].index(old_id) + group_dict[group_id]["inst"][curr_index:curr_index + 1] = map(lambda _: _["id"], curr_value.inst) + except ValueError: + pass + continue + case str() | int() | float(): + self.inst.append({ + "id": new_id, + "kind": "text", + "widgets_values": [str(curr_value)], + "widgets_names": [f"cloud:_:{new_id}:text:text_input"] + }) + case _: + self.inst.append(inst) + if old_id.isnumeric(): + inst["id"] = new_id + self.db[curr_id][new_id] = old_id + + for param in kwargs: + if param.startswith("cloud:"): + for inst in self.inst: + for i in range(self.inst["widgets_values"]): + if self.inst["widgets_names"][i] == param: + self.inst["widgets_values"][i] = kwargs[param] + break + + for old_id, group_data in group_dict.items(): + if "inst" in group_data: + new_list = [] + for i, inst_id in enumerate(group_data["inst"]): + if not inst_id.isnumeric(): + new_list.append(inst_id) + else: + for curr_inst_id in self.db[curr_id].keys(): + if self.db[curr_id][curr_inst_id] == inst_id: + new_list.append(curr_inst_id) + break + if len(new_list) > 0: + group_data["inst"] = new_list + else: + del group_data["inst"] + if "group" in group_data: + for i, inner_old_id in enumerate(group_data["group"]): + new_id = str(uuid.uuid4()) + if not inner_old_id.split(":")[-1].isnumeric(): + group_data["group"][i] = inner_old_id + else: + for curr_group_id in self.db[curr_id].keys(): + if self.db[curr_id][curr_group_id] == inner_old_id: + group_data["group"][i] = curr_group_id + break + new_id = str(uuid.uuid4()) + if not old_id.split(":")[-1].isnumeric(): + self.group[old_id] = group_data + else: + for curr_group_id in self.db[curr_id].keys(): + if self.db[curr_id][curr_group_id] == old_id: + self.group[curr_group_id] = group_data + break + else: + self.group[new_id] = group_data + for curr_group_id in self.db[curr_id].keys(): + if self.db[curr_id][curr_group_id] == old_id: + break + else: + self.db[curr_id][new_id] = old_id + + @classmethod + def text_to_dict(cls, text): + pass + + def text_to_data(self, text): + pass + + def data_eval(self): + sort_data = self.sort() + state_data = [] + func_data = {} + + for inst_id in self.inst: + func_data[inst_id["id"]] = CloudData(inst_id["kind"]) if \ + isinstance(inst_id["kind"], str) else \ + inst_id["kind"] + + def sort(self): + # inst = kwargs["cloud:cloud"][0]["inst"] + # group = kwargs["cloud:cloud"][0]["group"] + inst = self.inst + group = self.group + dep = {} + + for group_id in group: + inst_list = group_query_inst(group, group_id) + inst_dep_list_prim = [] + inst_dep_list_func = [] + inst_dep_list_bind = [] + inst_kind_set = set() + + for inst_id in inst_list: + if inst_id not in dep: + dep[inst_id] = [] + inst_curr_kind = next(filter(lambda _: _["id"] == inst_id, inst))["kind"] + if CLOUD_METHOD[inst_curr_kind] is None: + inst_dep_list_prim.append(inst_id) + else: + if inst_curr_kind in inst_kind_set and CLOUD_METHOD[inst_curr_kind]["sole"]: + raise Exception(f"Cannot have multiple {inst_curr_kind} cloud to same group {group_id}.") + (inst_dep_list_bind if CLOUD_METHOD[inst_curr_kind]["bind"] else inst_dep_list_func).append(inst_id) + if isinstance(inst_curr_kind, str): + inst_kind_set.add(inst_curr_kind) + + for inst_id in inst_dep_list_func: + dep[inst_id].extend(inst_dep_list_prim) + + idx_db = {} + for inst_id in inst_dep_list_bind: + dep[inst_id].extend(inst_dep_list_prim) + dep[inst_id].extend(inst_dep_list_func) + idx_db[inst_id] = inst.index(next(filter(lambda _: _["id"] == inst_id, inst))) + + for a_inst_id in inst_dep_list_bind: + for b_inst_id in inst_dep_list_bind: + if idx_db[a_inst_id] > idx_db[b_inst_id] and idx_db[a_inst_id] != idx_db[b_inst_id]: + dep[a_inst_id].append(b_inst_id) + + for inst_id in inst: + if inst_id["id"] not in dep: + dep[inst_id["id"]] = [] + + return { + "idx": toposort.toposort_flatten(dep), + "dep": dep + } + + def __str__(self): + return f"CloudData({self.inst}, {self.group}, {self.db})" + + def __repr__(self): + return f"CloudData({json.dumps(self.inst, indent=2)}, {json.dumps(self.group, indent=2)}, {json.dumps(self.db, indent=2)})" + class Cloud: @classmethod def INPUT_TYPES(s): return { "required": lib0246.WildDict(), "optional": { - "_cloud_in": ("CLOUD_PIPE", ), + # "_cloud_in": ("CLOUD_PIPE", ), "cloud": ("CLOUD_DATA", ) }, "hidden": { @@ -2091,18 +2435,18 @@ class Cloud: FUNCTION = "execute" CATEGORY = "0246" - def execute(self, _id = None, _prompt = None, _workflow = None, _cloud_in = None, **kwargs): - print(kwargs) - return ([None], ) - - def text_to_cloud(self): - pass - - def cloud_to_text(self): - pass + def execute(self, _id = None, _prompt = None, _workflow = None, **kwargs): + res = CloudData() + res.dict_to_data(_id[0], kwargs["cloud:cloud"][0]["inst"], kwargs["cloud:cloud"][0]["group"], None, kwargs) + return { + "ui": { + "text": [[""]] + }, + "result": [[res]] + } ###################################################################################### - + class Meta: @classmethod def INPUT_TYPES(s): diff --git a/requirements.txt b/requirements.txt index 7b25913..5fc4cea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ wrapt -natsort \ No newline at end of file +natsort +toposort \ No newline at end of file diff --git a/utils.py b/utils.py index c10adb5..ef3636d 100644 --- a/utils.py +++ b/utils.py @@ -20,6 +20,7 @@ try: except: subprocess.Popen([sys.executable, "-m", "pip", "install", "wrapt"]).wait() subprocess.Popen([sys.executable, "-m", "pip", "install", "natsort"]).wait() + subprocess.Popen([sys.executable, "-m", "pip", "install", "toposort"]).wait() wrapt = __import__("wrapt") natsort = __import__("natsort") diff --git a/web/js/nodes.js b/web/js/nodes.js index 6639cdc..9e0d85c 100644 --- a/web/js/nodes.js +++ b/web/js/nodes.js @@ -304,9 +304,6 @@ app.registerExtension({ serialize: false }); } - }, function (mode) { - if (mode === 0b100000) - this.self.setSize(this.self.computeSize()); }); wg0246.setup_log(nodeType.prototype, true); } break; @@ -543,9 +540,10 @@ app.registerExtension({ } }); - Hub.prototype.hubSize = function () { + Hub.prototype.hubSize = function (extra = 0) { const curr_size = this.computeSize(); curr_size[0] = this.size[0] < 350 ? 350 : this.size[0]; + // curr_size[1] += extra - 32; this.setSize(curr_size); }; @@ -780,6 +778,7 @@ app.registerExtension({ } delete this.hub.node_widget[curr_id]; } + // [TODO] For some reason there's trailing space_widget const space_widget_index = this.widgets.indexOf(this.hub.space_widget[curr_id]); if (space_widget_index > -1) this.widgets.splice(space_widget_index, 1); diff --git a/web/js/widgets.js b/web/js/widgets.js index a54f408..8443058 100644 --- a/web/js/widgets.js +++ b/web/js/widgets.js @@ -1257,7 +1257,7 @@ function node_mouse_pos(node) { ]; } -function calc_flex(node, widget, width) { +function calc_flex(node, width) { node.flex_data = node.flex_data ?? {}; node.flex_data.share_count = 0; node.flex_data.share_weight = []; @@ -1275,11 +1275,17 @@ function calc_flex(node, widget, width) { node.flex_data.share_min_h.push(Number.isFinite(node.widgets[i]?.flex?.real_min_h) && node.widgets[i]?.flex?.real_min_h > 0 ? node.widgets[i]?.flex?.real_min_h : null); node.flex_data.share_max_h.push(Number.isFinite(node.widgets[i]?.flex?.real_max_h) && node.widgets[i]?.flex?.real_max_h > 0 ? node.widgets[i]?.flex?.real_max_h : null); ++ node.flex_data.share_count; - if (node.widgets[i] === widget) - node.widgets[i].flex.index = node.flex_data.share_count - 1; + node.widgets[i].flex.index = node.flex_data.share_count - 1; node.flex_data.left_h += 4; } else node.flex_data.off_h += (node.widgets[i]?.computedHeight ?? node.widgets[i]?.computeSize?.(width ?? LiteGraph.NODE_WIDGET_WIDTH)?.[1] ?? LiteGraph.NODE_WIDGET_HEIGHT) + 4; + node.flex_data.slot_c = Math.max(node.inputs?.length ?? 0, node.outputs?.length ?? 0); + node.flex_data.slot_h = (node.flex_data.slot_c + 0.2) * LiteGraph.NODE_SLOT_HEIGHT + (node.flex_data.slot_c === 0 ? 32 : 10); + node.flex_data.take_h = node.flex_data.off_h + node.flex_data.left_h + node.flex_data.slot_h; + node.size[1] = Math.max( + node.flex_data.share_min_h.reduce((a, b) => a + b, node.flex_data.take_h + node.flex_data.img_h), + (app.canvas.resizing_node === node) ? app.canvas.graph_mouse[1] - node.pos[1] : node.size[1] + ); } let PROCESS_WIDGET_NODE; @@ -1329,12 +1335,7 @@ export function widget_flex(node, widget, options = {}) { }); lib0246.hijack(widget, "computeSize", function (width) { - // if (window.test_func) - // return window.test_func.call(this, width, node, calc_flex, lib0246, PROCESS_WIDGET_NODE); if (!this.mark) { - const slot_count = Math.max(node.inputs?.length ?? 0, node.outputs?.length ?? 0), - slot_h = (slot_count + 0.2) * LiteGraph.NODE_SLOT_HEIGHT; - this.self.flex.temp_h = 0; if (PROCESS_WIDGET_NODE && PROCESS_WIDGET_NODE.isPointInside(app.canvas.graph_mouse[0], app.canvas.graph_mouse[1])) { // Intentional double-ifs @@ -1348,32 +1349,22 @@ export function widget_flex(node, widget, options = {}) { } // Don't ask why how I came up with this. This took a week of brain power. - this.self.flex.real_y = this.self.flex.real_y ?? 0; this.self.flex.margin_tail_real_y = this.self.flex.margin_tail_y; - this.self.flex.real_max_h = Infinity; - this.self.flex.real_min_h = this.self.flex.min_h ?? 0; - - let raw_size = node.size?.[1] ?? this.self.flex.real_min_h; // dom_flag = width === undefined - calc_flex(node, this.self, width); + if (!node.flex_data) + calc_flex(node, width); + if (!node.imgs || node.widgets.find(_ => _.name === ANIM_PREVIEW_WIDGET)) node.flex_data.img_h = 0; - if (app.canvas.resizing_node === node) { - raw_size = app.canvas.graph_mouse[1] - node.pos[1]; - const min_h = node.flex_data.share_min_h.reduce((a, b) => a + b, slot_h + 10 + node.flex_data.off_h + node.flex_data.img_h); - if (raw_size < min_h) - raw_size = min_h; - node.size[1] = raw_size; - } - + // [TODO] Caching this? this.self.flex.real_h = lib0246.calc_spread( node.flex_data.share_count, - raw_size - node.flex_data.off_h - slot_h - 10 - node.flex_data.left_h - node.flex_data.img_h, + node.size[1] - node.flex_data.take_h - node.flex_data.img_h, node.flex_data.share_weight, node.flex_data.share_min_h, node.flex_data.share_max_h @@ -1387,29 +1378,24 @@ export function widget_flex(node, widget, options = {}) { } }); - if (node.configure && !node.configure[lib0246.HIJACK_MARK]) { - let temp_compute_size, temp_compute_flag = false; - lib0246.hijack(node, "configure", function (data) { + if (!node.computeSize[lib0246.HIJACK_MARK]) { + lib0246.hijack(node, "computeSize", function () { + const node = this.self; if (!this.mark) { - temp_compute_size = this.self.computeSize; - temp_compute_flag = true; - const hold_w = data.size[0], hold_h = data.size[1]; - this.self.computeSize = function () { - return [hold_w, hold_h]; - }; - } - }, function (mode) { - if (mode === 0b100000 && temp_compute_flag) { - temp_compute_flag = false; - this.self.size = this.self.computeSize(); - this.self.computeSize = temp_compute_size; + if (!node.size) { + this.stop = true; + this.res = [LiteGraph.NODE_MIN_WIDTH, 100]; + return; + } + calc_flex(node, node.size[0]); } }); - lib0246.hijack(node, "onResize", function (size) { - if (!this.mark && Number.isFinite(this.self.flex_data.force_h)) { - this.self.size[1] = this.self.flex_data.force_h; - this.self.flex_data.force_h = null; + lib0246.hijack(node, "setSize", function (size) { + const node = this.self; + if (!this.mark && Number.isFinite(node?.flex_data?.force_h)) { + size[1] = node.flex_data.force_h; + node.flex_data.force_h = null; } }); @@ -1466,7 +1452,7 @@ export function DOM_WIDGET(data_type, data_name, element, options = {}) { widget.flex.hold_draw[3] <= 0 || widget.type === "converted-widget"; this.element.hidden = hidden; - this.element.style.display = hidden ? "none" : null; + this.element.style.display = hidden ? "none" : "inherit"; if (hidden) { widget.options.onHide?.(widget); return; @@ -1556,7 +1542,7 @@ export function DOM_WIDGET(data_type, data_name, element, options = {}) { return widget; } -//* +/* Object.defineProperty(LGraphNode.prototype, "addDOMWidget", { // Secret A/B testing ;) // If no one complaining then it works I guess @@ -1584,7 +1570,6 @@ Object.defineProperty(LGraphNode.prototype, "addDOMWidget", { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function eval_state(db, state, node, widget, event, pos) { for (let i = 0; i < db.length; i += 2) { if (Array.isArray(db[i])) { @@ -3135,7 +3120,7 @@ export function hub_setup_widget(node, data, id) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -function cloud_group_query_inst(group_curr, group_dict, group_list, inst_id) { +function cloud_inst_query_group(group_curr, group_dict, group_list, inst_id) { let stack = []; for (let ii = 0; ii < group_list.length; ++ ii) if (group_dict[group_list[ii]]?.inst?.includes?.(inst_id)) { @@ -3143,6 +3128,19 @@ function cloud_group_query_inst(group_curr, group_dict, group_list, inst_id) { stack.push(group_list[ii]); } cloud_group_query_group(group_curr, group_dict, group_list, stack); + return group_curr; +} + +function cloud_group_query_inst(inst_curr, group_dict, group_id) { + let stack = [group_id]; + while (stack.length > 0) { + const temp = group_dict[stack.pop()]; + if (temp?.inst) + inst_curr.push(...temp.inst); + if (temp?.group) + stack.push(...temp.group); + } + return inst_curr; } function cloud_group_query_group(group_curr, group_dict, group_list, group_stack) { @@ -3158,6 +3156,7 @@ function cloud_group_query_group(group_curr, group_dict, group_list, group_stack seen.add(group_list[ii]); } } + return group_curr; } function shelf_layout( @@ -3205,7 +3204,7 @@ function shelf_layout( group_y = 0; for (; i < key_list.length; ++ i) { let group_curr = []; - cloud_group_query_inst(group_curr, group_dict, group_list, key_list[i]); + cloud_inst_query_group(group_curr, group_dict, group_list, key_list[i]); const text_data = ctx.measureText(view_dict[key_list[i]]); @@ -3509,7 +3508,7 @@ function cloud_widget_build_basic(node, widget, inst_id, name, input, data) { } if (data !== null) for (let i = 0; i < widget.cloud.widgets[inst_id].length; ++ i) - widget.cloud.widgets[inst_id][i].value = data.widgets_values[i]; + widget.cloud.widgets[inst_id][i].value = data.widgets_values[i][0]; } function cloud_widget_select_basic(node, inst_id, name, flag) { @@ -3562,7 +3561,7 @@ function cloud_build(node, kind, data = null, input = null, full = null) { node.widgets.splice(node.widgets.length - 1, 1); if (data !== null) - this.cloud.widgets[inst_id][0].options.setValue(data.widgets_values[0]); + this.cloud.widgets[inst_id][0].options.setValue(data.widgets_values[0][0]); } break; case "text_list": { } break; @@ -3593,16 +3592,6 @@ function cloud_build(node, kind, data = null, input = null, full = null) { case "rand": { this.cloud.widgets[inst_id] = [ { - name: `cloud:${name}:${inst_id}:rand:count`, - type: "number", - value: 1, - options: { - min: 1, - max: Number.MAX_SAFE_INTEGER, - step: 10, - precision: 0 - }, - }, { name: `cloud:${name}:${inst_id}:rand:seed`, type: "number", value: 1, @@ -3612,6 +3601,16 @@ function cloud_build(node, kind, data = null, input = null, full = null) { step: 10, precision: 0 }, + }, { + name: `cloud:${name}:${inst_id}:rand:count`, + type: "number", + value: 1, + options: { + min: 1, + max: Number.MAX_SAFE_INTEGER, + step: 10, + precision: 0 + }, }, { name: `cloud:${name}:${inst_id}:rand:mode`, type: "combo", @@ -3624,7 +3623,7 @@ function cloud_build(node, kind, data = null, input = null, full = null) { }, } ]; - cloud_widget_build_basic(node, this, inst_id, `cloud:${name}:${inst_id}:rand:count`, input, data); + cloud_widget_build_basic(node, this, inst_id, `cloud:${name}:${inst_id}:rand:seed`, input, data); } break; case "cycle": { this.cloud.widgets[inst_id] = [ @@ -3648,6 +3647,26 @@ function cloud_build(node, kind, data = null, input = null, full = null) { step: 10, precision: 0 }, + }, { + name: `cloud:${name}:${inst_id}:cycle:space`, + type: "number", + value: 0, + options: { + min: Number.MIN_SAFE_INTEGER, + max: Number.MAX_SAFE_INTEGER, + step: 10, + precision: 0 + }, + }, { + name: `cloud:${name}:${inst_id}:rand:count`, + type: "number", + value: 1, + options: { + min: 1, + max: Number.MAX_SAFE_INTEGER, + step: 10, + precision: 0 + }, }, { name: `cloud:${name}:${inst_id}:cycle:mode`, type: "combo", @@ -3658,7 +3677,7 @@ function cloud_build(node, kind, data = null, input = null, full = null) { }, } ]; - this.cloud.widgets[inst_id][2].afterQueued = cloud_after_func.bind(this, node, inst_id, this.cloud.widgets[inst_id][1]); + this.cloud.widgets[inst_id][3].afterQueued = cloud_after_func.bind(this, node, inst_id, this.cloud.widgets[inst_id][1]); cloud_widget_build_basic(node, this, inst_id, `cloud:${name}:${inst_id}:cycle:offset`, input, data); } break; case "pin": { @@ -3706,7 +3725,7 @@ function cloud_select(node, inst_id, flag) { cloud_widget_select_basic.call(this, node, inst_id, `cloud:${name}:${inst_id}:weight:weight_input`, flag); } break; case "rand": { - cloud_widget_select_basic.call(this, node, inst_id, `cloud:${name}:${inst_id}:rand:count`, flag); + cloud_widget_select_basic.call(this, node, inst_id, `cloud:${name}:${inst_id}:rand:seed`, flag); } break; case "cycle": { cloud_widget_select_basic.call(this, node, inst_id, `cloud:${name}:${inst_id}:cycle:offset`, flag); @@ -3718,9 +3737,7 @@ function cloud_select(node, inst_id, flag) { this.options?.select?.(node, inst_id, flag); } break; } - if (this.cloud.select.size === 1) { - // node.widgets.find(_ => _.name === "base:inst_group_order").value = - } + node.computeSize(); } const CLOUD_KIND = ["text", "text_list", "text_file_list", "text_file_json", "weight", "rand", "cycle", "merge"]; @@ -3849,8 +3866,8 @@ function cloud_build_group(node, mode, second = null, color = undefined) { } } break; } - cloud_clean_group(this.cloud.data.group); } + cloud_clean_group(this.cloud.data.group); } function cloud_remove_group(node, group_id, mode) { @@ -3922,6 +3939,10 @@ function cloud_clean_group(group_dict) { } else if (!valid_group.has(key)) delete group_dict[key]; } + + // for (let group_id in group_dict) + // if (cloud_group_query_inst([], group_dict, group_id).length < 2) + // delete group_dict[group_id]; } function cloud_clean_dupe(group_dict) { @@ -3993,7 +4014,7 @@ export function CLOUD_WIDGET(data_type, data_name, options = {}) { for (let i = 0; i < this.cloud.data.inst.length; ++ i) if (this.cloud.widgets[this.cloud.data.inst[i].id]) for (let j = 0; j < this.cloud.widgets[this.cloud.data.inst[i].id].length; ++ j) { - this.cloud.data.inst[i].widgets_values[j] = this.cloud.widgets[this.cloud.data.inst[i].id][j].value; + this.cloud.data.inst[i].widgets_values[j] = [this.cloud.widgets[this.cloud.data.inst[i].id][j].value]; this.cloud.data.inst[i].widgets_names[j] = this.cloud.widgets[this.cloud.data.inst[i].id][j].name; } return { @@ -4248,7 +4269,7 @@ export function cloud_menu(name, options) { if (cloud_widget.cloud.data.inst.find(_ => _.id === inst_id).kind !== "pin") cloud_widget.remove(node, inst_id); cloud_widget.cloud.select.clear(); - node.setSize([node.size[0], Math.max(node.computeSize()[1], node.size[1])]); + // node.setSize([node.size[0], Math.max(node.computeSize()[1], node.size[1])]); app.canvas.setDirty(true); } }, { @@ -4260,7 +4281,7 @@ export function cloud_menu(name, options) { cloud_widget.cloud.select.add(cloud_widget.cloud.data.inst[i].id); cloud_widget.select(node, cloud_widget.cloud.data.inst[i].id, true); } - node.setSize([node.size[0], Math.max(node.computeSize()[1], node.size[1])]); + // node.setSize([node.size[0], Math.max(node.computeSize()[1], node.size[1])]); app.canvas.setDirty(true); } }, { @@ -4272,45 +4293,45 @@ export function cloud_menu(name, options) { cloud_widget.select(node, cloud_widget.cloud.data.inst[i].id, false); cloud_widget.cloud.select.delete(cloud_widget.cloud.data.inst[i].id); } - node.setSize([node.size[0], Math.max(node.computeSize()[1], node.size[1])]); + // node.setSize([node.size[0], Math.max(node.computeSize()[1], node.size[1])]); app.canvas.setDirty(true); } }, null, { content: "[0246.Cloud] ✔️ first_group_id ➡️ picked clouds", callback: (value, options, evt, menu, node) => { - node.widgets.find(w => w.name === `cloud:${name}`) - .build_group(node, node.widgets.find(w => w.name === `cloud:${name}:first_group_id`).value, null); + node.widgets.find(w => w.name === `cloud:${name}`).build_group( + node, + node.widgets.find(w => w.name === `cloud:${name}:first_group_id`).value, + null + ); } }, { content: "[0246.Cloud] 🗑️ second_group_id ➡️ picked clouds", callback: (value, options, evt, menu, node) => { - node.widgets.find(w => w.name === `cloud:${name}`) - .remove_group( - node, - node.widgets.find(w => w.name === `cloud:${name}:second_group_id`).value, - null - ); + node.widgets.find(w => w.name === `cloud:${name}`).remove_group( + node, + node.widgets.find(w => w.name === `cloud:${name}:second_group_id`).value, + null + ); } }, { content: "[0246.Cloud] ✔️ first_group_id ➡️ second_group_id", callback: (value, options, evt, menu, node) => { - node.widgets.find(w => w.name === `cloud:${name}`) - .build_group( - node, - node.widgets.find(w => w.name === `cloud:${name}:first_group_id`).value, - node.widgets.find(w => w.name === `cloud:${name}:second_group_id`).value - ); + node.widgets.find(w => w.name === `cloud:${name}`).build_group( + node, + node.widgets.find(w => w.name === `cloud:${name}:first_group_id`).value, + node.widgets.find(w => w.name === `cloud:${name}:second_group_id`).value + ); } }, { content: "[0246.Cloud] 🗑️ second_group_id ➡️ first_group_id", callback: (value, options, evt, menu, node) => { - node.widgets.find(w => w.name === `cloud:${name}`) - .remove_group( - node, - node.widgets.find(w => w.name === `cloud:${name}:second_group_id`).value, - node.widgets.find(w => w.name === `cloud:${name}:first_group_id`).value - ); + node.widgets.find(w => w.name === `cloud:${name}`).remove_group( + node, + node.widgets.find(w => w.name === `cloud:${name}:second_group_id`).value, + node.widgets.find(w => w.name === `cloud:${name}:first_group_id`).value + ); } }, null, { @@ -4371,7 +4392,7 @@ function reset_hijack_widget(node) { } } -const NODE_PARENT = Symbol("node_parent"); +export const NODE_PARENT = Symbol("node_parent"); function hijack_widget_name(node, widget) { if (node.comfyClass === "0246.Hub" && widget[NODE_PARENT]) @@ -4382,7 +4403,7 @@ function hijack_widget_name(node, widget) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - +window.test = 0; app.registerExtension({ name: "0246.Widget", async init() { @@ -4405,82 +4426,24 @@ app.registerExtension({ if (!this.mark) { const node = arguments[0]; if (node.comfyClass === "0246.Hub") { - node.hub.temp_y = node.hub.temp_y ?? {}; - const io_height = Math.max(node?.inputs?.length ?? 0, node?.outputs?.length ?? 0) * 24; - node.hub.curr_y = io_height; - let compute_height = node.size[1], - widget_count = 0; - - for (; widget_count < node.widgets.length; ++ widget_count) { - if ( - node.widgets[widget_count] === app.graph.getNodeById(node.hub?.data?.node_list?.[0])?.widgets?.[0] || - node.widgets[widget_count].type === "space_title" - ) - break; - node.hub.curr_y += LiteGraph.NODE_WIDGET_HEIGHT + 4; - } - - if (node.hub.sole_space) { - node.hub.curr_y += LiteGraph.NODE_WIDGET_HEIGHT + 8 - node.hub.sole_widget.length * 4; - ++ widget_count; - } - - for (let i = 0; i < node.hub.sole_widget.length; ++ i, ++ widget_count) { - const curr_widget = node.hub.sole_widget[i]; - if (curr_widget.y !== undefined) - node.hub.temp_y[widget_count] = curr_widget.y; - curr_widget.y = node.hub.curr_y; - node.hub.curr_y += LiteGraph.NODE_WIDGET_HEIGHT + 4; // [TODO] Maybe add checks like below - } - - if (node.hub.data) - for (let i = 0; i < node.hub.data.node_list.length; ++ i) { - const curr_node = app.graph.getNodeById(node.hub.data.node_list[i]); - if (node.widgets.length <= widget_count || !curr_node) - break; - node.widgets[widget_count ++].y = node.hub.curr_y; - node.hub.curr_y += LiteGraph.NODE_WIDGET_HEIGHT + 4; - for (let j = 0; j < curr_node.widgets.length; ++ j, ++ widget_count) { - const curr_widget = curr_node.widgets[j]; - if (curr_widget.y !== undefined) - node.hub.temp_y[widget_count] = curr_widget.y; - curr_widget.y = node.hub.curr_y; - if (curr_widget.flex) { - curr_widget.flex.real_w = node.size[0]; - curr_widget.flex.real_y = node.hub.curr_y; - node.hub.curr_y += curr_widget.flex.temp_h; - } else if (curr_widget.computedHeight) { - node.hub.curr_y += curr_widget.computedHeight + 4; - compute_height += curr_widget.computedHeight; - } else if (curr_widget.openpose) { - node.hub.curr_y += node.size[0]; - } else if (curr_widget.painter_wrap) { - node.hub.curr_y += Math.max(( - curr_widget.painter_toolbox = curr_widget.painter_toolbox ?? document.querySelector("div.painter_drawning_box") - ).clientHeight / app.canvas.ds.scale + 4, node.size[0]); - app.canvas.setDirty(true); - } else if (curr_widget.computeSize) { - const curr_height = curr_widget.computeSize(node.size[0], node)[1]; // compute_height, raw_height - io_height + flex_height - node.hub.curr_y += curr_height + 4; - } else - node.hub.curr_y += LiteGraph.NODE_WIDGET_HEIGHT + 4; - } - } - } - } else { - const node = arguments[0]; - if (node.comfyClass === "0246.Hub") { - let curr_size = node.computeSize(); - for (let i = 0; i < node.widgets.length; ++ i) { - if (node.hub.temp_y[i] !== undefined) - node.widgets[i].y = node.hub.temp_y[i]; - else - node.widgets[i].y = undefined; - delete node.hub.temp_y[i]; - } - curr_size[0] = node.size[0]; - curr_size[1] = node.hub.curr_y + 2; - node.setSize(curr_size); + // [TODO] Temporary. Probably we wants to rebuild widget list dynamically + // for (let i = 7; i < node.widgets.length; ++ i) { + // let found = false; + // for (let id in node.hub.node_widget) + // if ( + // node.hub.node_widget[id].indexOf(node.widgets[i]) > -1 || + // this.hub.sole_widget.includes(node.widgets[i]) + // ) { + // found = true; + // break; + // } + // if (!found && node.widgets[i].type !== "space_title") + // node.widgets.splice(i --, 1); + // } + // calc_flex(node, node.size[0]); + // console.log(node.size); + node.hubSize(); + // app.canvas.setDirty(true); } } }); @@ -4561,20 +4524,22 @@ app.registerExtension({ const first_group_widget = node.addWidget("combo", `cloud:${inputName}:first_group_id`, "_", () => {}, { serialize: false, values: function (widget) { - let res = ["_", "global", `local:${node.id}`]; - for (let key in cloud_widget.cloud.data.group) { - if (key === "global" || key === `local:${node.id}`) - continue; - else - res.push(key); - } - return res; + // let res = ["_", "global", `local:${node.id}`]; + // for (let key in cloud_widget.cloud.data.group) { + // if (key === "global" || key === `local:${node.id}`) + // continue; + // else + // res.push(key); + // } + // return res; + return ["_", ...Object.keys(cloud_widget.cloud.data.group ?? {})]; } }); - const second_group_widget = node.addWidget("combo", `cloud:${inputName}:second_group_id`, "_", () => {}, { + const second_group_widget = node.addWidget("combo", `cloud:${inputName}:second_group_id`, "", () => {}, { serialize: false, values: function (widget) { - return ["_", ...Object.keys(cloud_widget.cloud.data.group ?? {})]; + // return ["_", ...Object.keys(cloud_widget.cloud.data.group ?? {})]; + return Object.keys(cloud_widget.cloud.data.group ?? {}); } }); @@ -4627,7 +4592,7 @@ app.registerExtension({ }); lib0246.hijack(node, "clone", function () { - const node = this.self; + const node = this.res; if (this.mark) for (let j = 0; j < node.widgets.length; ++ j) if (node.widgets[j].type === "CLOUD_DATA")