diff --git a/nodes.py b/nodes.py index 92aeada..16f715b 100644 --- a/nodes.py +++ b/nodes.py @@ -48,7 +48,7 @@ def INPUT_TYPES(s): "text_l": ("STRING", {"multiline": True, "placeholder": "CLIP_L"}), }, "optional": { - "steps": ("INT", {"default": 1, "min": 1, "max": 0xffffffffffffffff}), + "smZ_steps": ("INT", {"default": 1, "min": 1, "max": 0xffffffffffffffff}), }, } RETURN_TYPES = ("CONDITIONING",) @@ -58,8 +58,9 @@ def INPUT_TYPES(s): def encode(self, clip: comfy.sd.CLIP, text, parser, mean_normalization, multi_conditioning, use_old_emphasis_implementation, with_SDXL, ascore, width, height, crop_w, - crop_h, target_width, target_height, text_g, text_l, steps=1): + crop_h, target_width, target_height, text_g, text_l, smZ_steps=1): params = locals() + params['steps'] = params.pop('smZ_steps', smZ_steps) from .modules.shared import opts is_sdxl = "SDXL" in type(clip.cond_stage_model).__name__ diff --git a/smZNodes.py b/smZNodes.py index e98e02a..7105955 100644 --- a/smZNodes.py +++ b/smZNodes.py @@ -69,8 +69,6 @@ def set_dtype_compat(dtype): newv = False if newv: dtype = devices.dtype if dtype != devices.dtype else dtype - token_embedding_dtype = position_embedding_dtype = torch.float32 - if newv: # self.transformer.text_model.embeddings.position_embedding.to(dtype) # self.transformer.text_model.embeddings.token_embedding.to(dtype) inner_model = getattr(self.transformer, self.inner_name, None) @@ -80,6 +78,7 @@ def set_dtype_compat(dtype): self.transformer.set_input_embeddings(self.transformer.get_input_embeddings().to(dtype)) def reset_dtype_compat(newv): if newv: + # token_embedding_dtype = position_embedding_dtype = torch.float32 # self.transformer.text_model.embeddings.token_embedding.to(token_embedding_dtype) # self.transformer.text_model.embeddings.position_embedding.to(position_embedding_dtype) inner_model = getattr(self.transformer, self.inner_name, None) @@ -87,7 +86,7 @@ def reset_dtype_compat(newv): inner_model.embeddings.to(torch.float32) else: self.transformer.set_input_embeddings(self.transformer.get_input_embeddings().to(torch.float32)) - # set_dtype_compat() + # set_dtype_compat(torch.float16) backup_embeds = self.transformer.get_input_embeddings() device = backup_embeds.weight.device @@ -818,7 +817,7 @@ def check_link_to_clip(node_id, clip_id, visited=None): current_clip_id = clip_id steps = find_nearest_ksampler(clip_id) if steps is not None: - node["inputs"]["steps"] = steps + node["inputs"]["smZ_steps"] = steps if opts.debug: print(f'[smZNodes] id: {current_clip_id} | steps: {steps}') tmp() diff --git a/web/js/smZdynamicWidgets.js b/web/js/smZdynamicWidgets.js index 7479fc1..dcba095 100644 --- a/web/js/smZdynamicWidgets.js +++ b/web/js/smZdynamicWidgets.js @@ -2,14 +2,17 @@ import { app } from "/scripts/app.js"; // import { app } from "../../../scripts/app.js"; // import { ComfyWidgets } from "../../../scripts/widgets.js"; -const ids = ["CLIP Text Encode++", "Settings (smZ)"] -const widgets = ['parser', 'mean_normalization', 'multi_conditioning', 'use_old_emphasis_implementation', 'with_SDXL'] -const getSetWidgets = ['parser', 'with_SDXL'] +const ids1 = new Set(["smZ CLIPTextEncode"]) +const ids2 = new Set(["smZ Settings"]) +const widgets = ['mean_normalization', 'multi_conditioning', 'use_old_emphasis_implementation', 'with_SDXL'] +const widgets_sdxl = ['ascore', 'width', 'height', 'crop_w', 'crop_h', 'target_width', 'target_height', 'text_g', 'text_l'] +const getSetWidgets = new Set(['parser', 'with_SDXL']) let origProps = {}; const HIDDEN_TAG = "smZhidden" -const findWidgetByName = (node, name) => node.widgets.find((w) => (w._name !== undefined && w._name ? w._name : w.name) === name); +const findWidgetByName = (node, name) => node.widgets.find((w) => w.name === name); +const findWidgetsByName = (node, name) => node.widgets.filter((w) => w.name.endsWith(name)); const doesInputWithNameExist = (node, name) => node.inputs ? node.inputs.some((input) => input.name === name) : false; @@ -34,16 +37,16 @@ function toggleWidget(node, widget, show = false, suffix = "") { } function widgetLogic(node, widget) { - if (widget.name === 'parser') { + const wname = widget.name + if (wname.endsWith("parser")) { const in_comfy = widget.value.includes("comfy") - toggleMenuOption(node, 'multi_conditioning', !in_comfy) - toggleMenuOption(node, 'mean_normalization', widget.value !== "comfy") + toggleMenuOption(node, ['multi_conditioning', wname], !in_comfy) + toggleMenuOption(node, ['mean_normalization', wname], widget.value !== "comfy") if (in_comfy) { - toggleMenuOption(node, 'use_old_emphasis_implementation', false) + toggleMenuOption(node, ['use_old_emphasis_implementation', wname], false) } - } else if (widget.name === 'with_SDXL') { - const ws = ['ascore', 'width', 'height', 'crop_w', 'crop_h', 'target_width', 'target_height', 'text_g', 'text_l'] - toggleMenuOption(node, 'text', !widget.value) + } else if (wname.endsWith("with_SDXL")) { + toggleMenuOption(node, ['text', wname], !widget.value) // Resize node when widget is set to false if (!widget.value) { @@ -58,51 +61,88 @@ function widgetLogic(node, widget) { } // Toggle sdxl widgets if sdxl widget value is true/false - for (const w of ws) { - toggleMenuOption(node, w, widget.value) + for (const w of widgets_sdxl) { + toggleMenuOption(node, [w, wname], widget.value) } // Keep showing the widget if it's enabled if (widget.value && widget.type === HIDDEN_TAG) { - toggleMenuOption(node, widget.name, true) + toggleMenuOption(node, [widget.name, wname], true) } } } +function getGroupNodeConfig(node) { + let ls = [] + let nodeData = node.constructor?.nodeData + if (nodeData) { + for(let sym of Object.getOwnPropertySymbols(nodeData) ) { + const o = nodeData[sym]; + if (!o) continue; + ls.push(o) + } + } + return ls +} function getSetters(node) { - if (node.widgets) + if (node.widgets) { + let gncl = getGroupNodeConfig(node) for (const w of node.widgets) { - if (getSetWidgets.includes(w.name)) { + for (const gsw of [...getSetWidgets]) { + if (!w.name.endsWith(gsw)) continue; + let shouldBreak = false + for (const gnc of gncl) { + const nwmap = gnc.newToOldWidgetMap[w.name] + if (nwmap && !(nwmap.node.type === [...ids1][0] && nwmap.inputName === gsw)) + shouldBreak = true + } + if (shouldBreak) break; widgetLogic(node, w); w._value = w.value - Object.defineProperty(w, 'value', { - get() { - return w._value; - }, - set(newVal) { + Object.defineProperty(w, 'value', { + get() { + return w._value; + }, + set(newVal) { w._value = newVal - widgetLogic(node, w); + widgetLogic(node, w); } }); // Hide SDXL widget on init // Doing it in nodeCreated fixes its toggling for some reason - if (w.name === 'with_SDXL' && w) { - toggleMenuOption(node, 'with_SDXL') + if (w.name.endsWith('with_SDXL')) { + toggleMenuOption(node, ['with_SDXL', w.name]) w.init = true + + // Hide steps + toggleMenuOption(node, ['smZ_steps', w.name] , false) } } } + } +} + +function toggleMenuOption(node, widget_arr, _show = null, perform_action = true) { + const gncl = getGroupNodeConfig(node) + const [widget_name, companion_widget_name] = Array.isArray(widget_arr) ? widget_arr : [widget_arr] + let nwname = widget_name + // Use companion_widget_name to get the correct widget with the new name + if (companion_widget_name) { + for (const gnc of gncl) { + const omap = Object.values(gnc.oldToNewWidgetMap).find(x => Object.values(x).find(z => z === companion_widget_name)) + const tmp2 = omap[widget_name] + if (tmp2) nwname = tmp2; + } + } + const widgets = companion_widget_name ? [findWidgetByName(node, nwname)] : findWidgetsByName(node, nwname) + for (const widget of widgets) + toggleMenuOption0(node, widget, _show, perform_action) } -function toggleMenuOption(node, widget_name, _show = null, perform_action = true) { - let widget = findWidgetByName(node, widget_name) - const name_backup = widget.name - // Empty names result in clean MenuOptions - // Our real name could be in _name - widget.name = widget_name ? widget_name : (widget._name ? widget._name : widget.name) +function toggleMenuOption0(node, widget, _show = null, perform_action = true) { if (!widget || doesInputWithNameExist(node, widget.name)) return; if (!origProps[widget.name]) { origProps[widget.name] = { origType: widget.type, origComputeSize: widget.computeSize}; @@ -112,23 +152,19 @@ function toggleMenuOption(node, widget_name, _show = null, perform_action = true toggleWidget(node, widget, _show !== null ? _show : !show) node.setDirtyCanvas(true); } - widget.name = name_backup } function toggle_all_settings_desc_widgets(node, widget_name = '', _show = null) { - let found_widgets = node.widgets.filter((w) => (w._name ? w._name : w.name).includes('info')); + let found_widgets = node.widgets.filter((w) => (widget_name ? widget_name : w.name).includes('info')); let is_showing = true found_widgets.forEach(w => { if (w) { - const name_backup = w.name - w.name = w._name ? w._name : w.name - toggleMenuOption(node, w.name, _show) + toggleMenuOption(node, [w.name, w.name], _show) is_showing = (w.type === origProps[w.name].origType) - w.name = name_backup } }); - let w = node.widgets.find((w) => (w._name ? w._name : w.name) === 'extra'); + let w = node.widgets.find((w) => w.name === 'extra'); if (w) { let value = null; try { @@ -147,123 +183,150 @@ function toggle_all_settings_desc_widgets(node, widget_name = '', _show = null) } } -function handleSettings(node) { - if (!node.widgets) return; - const w = node.widgets.find(w => (w._name ? w._name : w.name) === 'extra') - w._name = (w._name ? w._name : w.name) - w.name = '' - // Hide `extra` widget - toggleMenuOption(node, 'extra', false) -} +function create_custom_option(content, _callback) { + return { + content: content, + callback: () => _callback(), + } +}; app.registerExtension({ name: "comfy.smZ.dynamicWidgets", /** - * Called after inputs, outputs, widgets, menus are added to the node given the node definition. - * Used to add methods to nested nodes. - * @param nodeType The ComfyNode object to be registered with LiteGraph. - * @param nodeData The node definition. + * Called when a node is created. Used to add menu options to nodes. + * @param node The node that was created. * @param app The app. - * @returns {Promise} */ - beforeRegisterNodeDef(nodeType, nodeData, app) { - /** - * Called after the node has been configured. - * On initial configure of nodes hide all converted widgets - * See web/extensions/core/widgetInputs.js - * See litegraph.core.js - * @param node The node that was created. - */ - if (nodeType.title === ids[1]) { - const origOnConfigure = nodeType.prototype.onConfigure; - nodeType.prototype.onConfigure = function() { - const r = origOnConfigure ? origOnConfigure.apply(this, arguments) : undefined; - const w = this.widgets.find(w => (w._name ? w._name : w.name) === 'extra') - let value = null - try { - value =JSON.parse(w.value); - } catch (error) { - // when node definitions change due to an update or some other error - value = {"show":true} - } - toggle_all_settings_desc_widgets(this, null, value.show) - const ww = this.widgets.find(w => (w._name ? w._name : w.name) === 'extra') - return r; - } + nodeCreated(node) { + const nodeType = node.type || node.constructor?.type + let inGroupNode = false + let inGroupNode2 = false + let innerNodes = node.getInnerNodes?.() + if (innerNodes) { + for (const inode of innerNodes) { + const _nodeType = inode.type || inode.constructor?.type + if (ids1.has(_nodeType)) + inGroupNode = ids1.has(_nodeType) + if (inGroupNode) + ids1.add(nodeType) // GroupNode's type + if (ids2.has(_nodeType)) + inGroupNode2 = ids2.has(_nodeType) + if (inGroupNode2) + ids2.add(nodeType) // GroupNode's type + } } - if (nodeType.title === ids[0] || nodeType.title === ids[1]) { - const onNodeCreated = nodeType.prototype.onNodeCreated; - nodeType.prototype.onNodeCreated = function() { - const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined; - let that = this; + // let nodeData = node.constructor?.nodeData + // if (nodeData) { + // for(let sym of Object.getOwnPropertySymbols(nodeData) ) { + // const nds = nodeData[sym]; + // if (nds) { + // inGroupNode=true + // inGroupNode2=true + // break + // } + // } + // } + // ClipTextEncode++ node + if (ids1.has(nodeType) || inGroupNode) { + node.widgets.forEach(w => w._name = w.name) - let customOptions = [] - function create_custom_option(content, _callback) { - return { - content: content, - callback: () => _callback(), - } - }; - - if (nodeType.title === ids[1]) { - // Styling. Setting `name` cleans up the MenuOption - that.widgets.forEach(function(w, i) { - if (w.name.includes('ㅤ')) { - w._name = w.name - w.name = '' - w.heading = true - } else if (w.name.includes('info')) { - w._name = w.name - w.name = 'text' - w.info = true - w.inputEl.disabled = true; - w.inputEl.readOnly = true; - w.inputEl.style.opacity = 0.75; - w.inputEl.style.alignContent = 'center'; - w.inputEl.style.textAlign = 'center'; - } - }) - const content_hide_show = "Hide/show all descriptions"; - customOptions.length = 0 - customOptions.push(create_custom_option(content_hide_show, toggle_all_settings_desc_widgets.bind(this, that))) - customOptions.push(null) // seperator + getSetters(node) - } else if (nodeType.title === ids[0]) { - // Hide steps - toggleMenuOption(that, 'steps', false) + // Reduce initial node size cause of SDXL widgets + // node.setSize([node.size[0], Math.max(node.size[1]/1.5, 220)]) + node.setSize([node.size[0], 220]) + } + // Settings node + if (ids2.has(nodeType) || inGroupNode2) { + node.serialize_widgets = true - // Reduce initial node size cause of SDXL widgets - that.setSize([that.size[0], 220]) - customOptions.length = 0 - const content_hide_show = "Hide/show "; - customOptions.push(...widgets.map(widget_name => create_custom_option(content_hide_show + widget_name, toggleMenuOption.bind(this, that, widget_name)))) - customOptions.push(null) // seperator - }; - // Save the original options - const getBaseMenuOptions = that.getExtraMenuOptions; - // Add new options - that.getExtraMenuOptions = function (_, options) { - // Call the original function for the default menu options - getBaseMenuOptions.call(this, _, options); - options.unshift(...customOptions); + const onConfigure = node.onConfigure; + node.onConfigure = function(o) { + const r = onConfigure ? onConfigure.apply(this, arguments) : undefined; + const w = this.widgets.find(w => w.name === 'extra') + let value = null + try { + value = JSON.parse(w.value); + } catch (error) { + // when node definitions change due to an update or some other error + value = {"show":true} } + toggle_all_settings_desc_widgets(this, null, value.show) return r; - }; + } + + // Styling. + node.widgets.forEach(function(w) { + w._name = w.name + if (w.name.includes('ㅤ')) { + w.heading = true + } else if (w.name.includes('info')) { + w.info = true + w.inputEl.disabled = true; + w.inputEl.readOnly = true; + w.inputEl.style.opacity = 0.75; + w.inputEl.style.alignContent = 'center'; + w.inputEl.style.textAlign = 'center'; + } + }) + // Hide `extra` widget + toggleMenuOption(node, 'extra', false) } - }, + // Add extra MenuOptions for + // ClipTextEncode++ and Settings node + if (ids1.has(nodeType) || inGroupNode || ids2.has(nodeType) || inGroupNode2) { + // Save the original options + const getExtraMenuOptions = node.getExtraMenuOptions; - /** - * Called when a node is created. Used to add menu options to nodes. - * @param node The node that was created. - * @param app The app. - */ - nodeCreated(node) { - if (node.getTitle() == ids[0]) { - getSetters(node) - } - else if (node.getTitle() == ids[1]) { - handleSettings(node) + node.getExtraMenuOptions = function (_, options) { + // Call the original function for the default menu options + const r = getExtraMenuOptions ? getExtraMenuOptions.apply(this, arguments) : undefined; + let customOptions = [] + node.setDirtyCanvas(true, true); + // if (!r) return r; + + if (ids2.has(nodeType) || inGroupNode2) { + const content_hide_show = "Hide/show all descriptions"; + // customOptions.push(null) // seperator + customOptions.push(create_custom_option(content_hide_show, toggle_all_settings_desc_widgets.bind(this, node))) + + // Alternate way to cleanup MenuOption + const toHideWidgets = node.widgets.filter(w => w.name.includes('ㅤ') || w.name.includes('info') || w.name.includes('extra')) + const wo = options.filter(o => o === null || (o && !toHideWidgets.some(w => o.content.includes(`Convert ${w.name} to input`)))) + options.splice(0, options.length, ...wo); + } + + if (ids1.has(nodeType) || inGroupNode) { + // Dynamic MenuOption depending on the widgets + const content_hide_show = "Hide/show "; + // const whWidgets = node.widgets.filter(w => w.name === 'width' || w.name === 'height') + const hiddenWidgets = node.widgets.filter(w => w.type === HIDDEN_TAG) + // doesn't take GroupNode into account + const with_SDXL = node.widgets.find(w => w.name === 'with_SDXL') + const parser = node.widgets.find(w => w.name === 'parser') + const in_comfy = parser.value.includes("comfy") + let ws = widgets.map(widget_name => create_custom_option(content_hide_show + widget_name, toggleMenuOption.bind(this, node, widget_name))) + ws = ws.filter((w) => (in_comfy && parser.value !== 'comfy' && w.content.includes('mean_normalization')) || (in_comfy && w.content.includes('with_SDXL')) || !in_comfy ) + // customOptions.push(null) // seperator + customOptions.push(...ws) + + let wo = options.filter(o => o === null || (o && !hiddenWidgets.some(w => o.content.includes(`Convert ${w.name} to input`)))) + const width = node.widgets.find(w => w.name === 'width') + const height = node.widgets.find(w => w.name === 'height') + if (width && height) { + const width_type = width.type.toLowerCase() + const height_type = height.type.toLowerCase() + if (!(width_type.includes('number') || width_type.includes('int') || width_type.includes('float') || + height_type.includes('number') || height_type.includes('int') || height_type.includes('float'))) + wo = wo.filter(o => o === null || (o && !o.content.includes('Swap width/height'))) + } + options.splice(0, options.length, ...wo); + } + // options.unshift(...customOptions); // top + options.splice(options.length - 1, 0, ...customOptions) + // return r; + } } } -}); \ No newline at end of file +});