Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f940ddfbb | ||
|
|
47d0836ca7 | ||
|
|
69254f964e | ||
|
|
4f6cece74b | ||
|
|
507571302d | ||
|
|
00f35ebd71 | ||
|
|
fcea21270b | ||
|
|
bbb5c5ed4d | ||
|
|
633d6ba8fb |
+3
-1
@@ -1,3 +1,5 @@
|
|||||||
{
|
{
|
||||||
"printWidth": 100
|
"printWidth": 100,
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"bracketSameLine": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,16 @@ from .py.power_prompt import RgthreePowerPrompt
|
|||||||
from .py.power_prompt_simple import RgthreePowerPromptSimple
|
from .py.power_prompt_simple import RgthreePowerPromptSimple
|
||||||
from .py.image_inset_crop import RgthreeImageInsetCrop
|
from .py.image_inset_crop import RgthreeImageInsetCrop
|
||||||
from .py.context_big import RgthreeBigContext
|
from .py.context_big import RgthreeBigContext
|
||||||
|
from .py.context_dynamic import RgthreeDynamicContext
|
||||||
|
from .py.context_dynamic_switch import RgthreeContextDynamicSwitch
|
||||||
from .py.ksampler_config import RgthreeKSamplerConfig
|
from .py.ksampler_config import RgthreeKSamplerConfig
|
||||||
from .py.sdxl_power_prompt_postive import RgthreeSDXLPowerPromptPositive
|
from .py.sdxl_power_prompt_postive import RgthreeSDXLPowerPromptPositive
|
||||||
from .py.sdxl_power_prompt_simple import RgthreeSDXLPowerPromptSimple
|
from .py.sdxl_power_prompt_simple import RgthreeSDXLPowerPromptSimple
|
||||||
from .py.any_switch import RgthreeAnySwitch
|
from .py.any_switch import RgthreeAnySwitch
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
RgthreeDynamicContext.NAME: RgthreeDynamicContext,
|
||||||
|
RgthreeContextDynamicSwitch.NAME: RgthreeContextDynamicSwitch,
|
||||||
RgthreeBigContext.NAME: RgthreeBigContext,
|
RgthreeBigContext.NAME: RgthreeBigContext,
|
||||||
RgthreeContext.NAME: RgthreeContext,
|
RgthreeContext.NAME: RgthreeContext,
|
||||||
RgthreeContextSwitch.NAME: RgthreeContextSwitch,
|
RgthreeContextSwitch.NAME: RgthreeContextSwitch,
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
"""The Dynamic Context node."""
|
||||||
|
from .constants import get_category, get_name
|
||||||
|
from .utils import ByPassTypeTuple
|
||||||
|
|
||||||
|
|
||||||
|
class RgthreeDynamicContext:
|
||||||
|
"""The Dynamic Context node.
|
||||||
|
|
||||||
|
Similar to the static Context and Context Big nodes, this allows users to add any number and
|
||||||
|
variety of inputs to a Dynamic Context node, and return the outputs by key name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
NAME = get_name("Dynamic Context")
|
||||||
|
CATEGORY = get_category()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls): # pylint: disable = invalid-name,missing-function-docstring
|
||||||
|
return {
|
||||||
|
"required": {},
|
||||||
|
"optional": {
|
||||||
|
"base_ctx": ("DYNAMIC_CONTEXT",),
|
||||||
|
"output_keys": ("DYNAMIC_CONTEXT_OUTPUTS",), # This is a hidden widget of the output keys
|
||||||
|
},
|
||||||
|
"hidden": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ByPassTypeTuple(("DYNAMIC_CONTEXT",))
|
||||||
|
RETURN_NAMES = ByPassTypeTuple(("CONTEXT",))
|
||||||
|
FUNCTION = "main"
|
||||||
|
|
||||||
|
def main(self, base_ctx=None, output_keys=None, **kwargs):
|
||||||
|
"""Creates a new context from the provided data, with an optional base ctx to start."""
|
||||||
|
new_ctx = base_ctx.copy() if base_ctx is not None else {}
|
||||||
|
|
||||||
|
for key_raw, value in kwargs.items():
|
||||||
|
key = key_raw.upper()
|
||||||
|
if key.startswith('+ '):
|
||||||
|
key = key[2:]
|
||||||
|
if key == "base_ctx":
|
||||||
|
continue
|
||||||
|
new_ctx[key] = value
|
||||||
|
|
||||||
|
res = [new_ctx]
|
||||||
|
output_keys = output_keys.split(',') if output_keys is not None else []
|
||||||
|
for key in output_keys:
|
||||||
|
res.append(new_ctx[key] if key in new_ctx else None)
|
||||||
|
return tuple(res)
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
"""The original Context Switch."""
|
||||||
|
from .constants import get_category, get_name
|
||||||
|
from .context_utils import is_context_empty
|
||||||
|
from .utils import ByPassTypeTuple
|
||||||
|
|
||||||
|
|
||||||
|
class RgthreeContextDynamicSwitch:
|
||||||
|
"""The initial Context Switch node.
|
||||||
|
|
||||||
|
For now, this will remain as-is but is otherwise backwards compatible with other Context nodes
|
||||||
|
outputs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
NAME = get_name("Dynamic Context Switch")
|
||||||
|
CATEGORY = get_category()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls): # pylint: disable = invalid-name, missing-function-docstring
|
||||||
|
return {
|
||||||
|
"required": {},
|
||||||
|
"optional": {
|
||||||
|
"output_keys": ("DYNAMIC_CONTEXT_OUTPUTS",), # This is a hidden widget of the output keys
|
||||||
|
"ctx_01": ("DYNAMIC_CONTEXT",),
|
||||||
|
"ctx_02": ("DYNAMIC_CONTEXT",),
|
||||||
|
"ctx_03": ("DYNAMIC_CONTEXT",),
|
||||||
|
"ctx_04": ("DYNAMIC_CONTEXT",),
|
||||||
|
"ctx_05": ("DYNAMIC_CONTEXT",),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ByPassTypeTuple(("DYNAMIC_CONTEXT", ))
|
||||||
|
RETURN_NAMES = ByPassTypeTuple(("CONTEXT", ))
|
||||||
|
FUNCTION = "switch"
|
||||||
|
|
||||||
|
def switch(self, output_keys=None, ctx_01=None, ctx_02=None, ctx_03=None, ctx_04=None, ctx_05=None):
|
||||||
|
"""Chooses the first non-empty Context to output.
|
||||||
|
"""
|
||||||
|
base_ctx = None
|
||||||
|
if not is_context_empty(ctx_01):
|
||||||
|
base_ctx = ctx_01
|
||||||
|
elif not is_context_empty(ctx_02):
|
||||||
|
base_ctx = ctx_02
|
||||||
|
elif not is_context_empty(ctx_03):
|
||||||
|
base_ctx = ctx_03
|
||||||
|
elif not is_context_empty(ctx_04):
|
||||||
|
base_ctx = ctx_04
|
||||||
|
elif not is_context_empty(ctx_05):
|
||||||
|
base_ctx = ctx_05
|
||||||
|
|
||||||
|
new_ctx = base_ctx.copy() if not is_context_empty(base_ctx) else None
|
||||||
|
res = [new_ctx]
|
||||||
|
output_keys = output_keys.split(',') if output_keys is not None else []
|
||||||
|
for key in output_keys:
|
||||||
|
if new_ctx is None:
|
||||||
|
res.append(None)
|
||||||
|
else:
|
||||||
|
res.append(new_ctx[key] if key in new_ctx else None)
|
||||||
|
|
||||||
|
return tuple(res)
|
||||||
+3
-2
@@ -81,8 +81,9 @@ class RgthreePowerPrompt:
|
|||||||
log_node_success(NODE_NAME, f'Loaded "{lora["lora"]}" from prompt')
|
log_node_success(NODE_NAME, f'Loaded "{lora["lora"]}" from prompt')
|
||||||
log_node_info(NODE_NAME, f'{len(loras)} Loras processed; stripping tags for TEXT output.')
|
log_node_info(NODE_NAME, f'{len(loras)} Loras processed; stripping tags for TEXT output.')
|
||||||
elif '<lora:' in prompt:
|
elif '<lora:' in prompt:
|
||||||
_prompt_stripped, loras = get_and_strip_loras(prompt, log_node=NODE_NAME, silent=True)
|
_prompt_stripped, loras, skipped, unfound = get_and_strip_loras(prompt, log_node=NODE_NAME, silent=True)
|
||||||
if len(loras):
|
total_loras = len(loras) + len(skipped) + len(unfound)
|
||||||
|
if total_loras:
|
||||||
log_node_warn(
|
log_node_warn(
|
||||||
NODE_NAME, f'Found {len(loras)} lora tags in prompt but model & clip were not supplied!')
|
NODE_NAME, f'Found {len(loras)} lora tags in prompt but model & clip were not supplied!')
|
||||||
log_node_info(NODE_NAME, 'Loras not processed, keeping for TEXT output.')
|
log_node_info(NODE_NAME, 'Loras not processed, keeping for TEXT output.')
|
||||||
|
|||||||
@@ -16,14 +16,18 @@ def get_and_strip_loras(prompt, silent=False, log_node="Power Prompt"):
|
|||||||
matches = re.findall(pattern, prompt)
|
matches = re.findall(pattern, prompt)
|
||||||
|
|
||||||
loras = []
|
loras = []
|
||||||
|
unfound_loras = []
|
||||||
|
skipped_loras = []
|
||||||
for match in matches:
|
for match in matches:
|
||||||
tag_path = match[0]
|
tag_path = match[0]
|
||||||
tag_path_no_ext = os.path.splitext(match[0])[0]
|
tag_path_no_ext = os.path.splitext(match[0])[0]
|
||||||
tag_filename_no_ext = os.path.splitext(os.path.basename(match[0]))[0]
|
tag_filename_no_ext = os.path.splitext(os.path.basename(match[0]))[0]
|
||||||
|
|
||||||
strength = float(match[1] if len(match) > 1 and len(match[1]) else 1.0)
|
strength = float(match[1] if len(match) > 1 and len(match[1]) else 1.0)
|
||||||
if strength == 0 and not silent:
|
if strength == 0:
|
||||||
log_node_info(log_node, f'Skipping "{tag_path}" with strength of zero')
|
if not silent:
|
||||||
|
log_node_info(log_node, f'Skipping "{tag_path}" with strength of zero')
|
||||||
|
skipped_loras.append({'lora': tag_path, 'strength': strength})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if tag_path not in lora_paths:
|
if tag_path not in lora_paths:
|
||||||
@@ -49,8 +53,9 @@ def get_and_strip_loras(prompt, silent=False, log_node="Power Prompt"):
|
|||||||
else:
|
else:
|
||||||
if not silent:
|
if not silent:
|
||||||
log_node_warn(log_node, f'Lora "{tag_path}" not found, skipping.')
|
log_node_warn(log_node, f'Lora "{tag_path}" not found, skipping.')
|
||||||
|
unfound_loras.append({'lora': tag_path, 'strength': strength})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
loras.append({'lora': tag_path, 'strength': strength})
|
loras.append({'lora': tag_path, 'strength': strength})
|
||||||
|
|
||||||
return (re.sub(pattern, '', prompt), loras)
|
return (re.sub(pattern, '', prompt), loras, skipped_loras, unfound_loras)
|
||||||
|
|||||||
@@ -105,15 +105,15 @@ class RgthreeSDXLPowerPromptPositive:
|
|||||||
values_insert_saved=None):
|
values_insert_saved=None):
|
||||||
|
|
||||||
if insert_lora == 'DISABLE LORAS':
|
if insert_lora == 'DISABLE LORAS':
|
||||||
prompt_g, loras_g = get_and_strip_loras(prompt_g, True)
|
prompt_g, loras_g, skipped_g, unfound_g = get_and_strip_loras(prompt_g, True)
|
||||||
prompt_l, loras_l = get_and_strip_loras(prompt_l, True)
|
prompt_l, loras_l, skipped_l, unfound_l = get_and_strip_loras(prompt_l, True)
|
||||||
loras = loras_g + loras_l
|
loras = loras_g + loras_l
|
||||||
log_node_info(
|
log_node_info(
|
||||||
NODE_NAME,
|
NODE_NAME,
|
||||||
f'Disabling all found loras ({len(loras)}) and stripping lora tags for TEXT output.')
|
f'Disabling all found loras ({len(loras)}) and stripping lora tags for TEXT output.')
|
||||||
elif opt_model != None and opt_clip != None:
|
elif opt_model != None and opt_clip != None:
|
||||||
prompt_g, loras_g = get_and_strip_loras(prompt_g)
|
prompt_g, loras_g, skipped_g, unfound_g = get_and_strip_loras(prompt_g)
|
||||||
prompt_l, loras_l = get_and_strip_loras(prompt_l)
|
prompt_l, loras_l, skipped_l, unfound_l = get_and_strip_loras(prompt_l)
|
||||||
loras = loras_g + loras_l
|
loras = loras_g + loras_l
|
||||||
if len(loras):
|
if len(loras):
|
||||||
for lora in loras:
|
for lora in loras:
|
||||||
@@ -122,13 +122,16 @@ class RgthreeSDXLPowerPromptPositive:
|
|||||||
log_node_success(NODE_NAME, f'Loaded "{lora["lora"]}" from prompt')
|
log_node_success(NODE_NAME, f'Loaded "{lora["lora"]}" from prompt')
|
||||||
log_node_info(NODE_NAME, f'{len(loras)} Loras processed; stripping tags for TEXT output.')
|
log_node_info(NODE_NAME, f'{len(loras)} Loras processed; stripping tags for TEXT output.')
|
||||||
elif '<lora:' in prompt_g or '<lora:' in prompt_l:
|
elif '<lora:' in prompt_g or '<lora:' in prompt_l:
|
||||||
_prompt_stripped_g, loras_g = get_and_strip_loras(prompt_g, True)
|
_prompt_stripped_g, loras_g, skipped_g, unfound_g = get_and_strip_loras(prompt_g, True)
|
||||||
_prompt_stripped_l, loras_l = get_and_strip_loras(prompt_l, True)
|
_prompt_stripped_l, loras_l, skipped_l, unfound_l = get_and_strip_loras(prompt_l, True)
|
||||||
loras = loras_g + loras_l
|
loras = len(loras_g) + len(loras_l)
|
||||||
if len(loras):
|
unfound_loras = len(unfound_g) + len(unfound_l)
|
||||||
log_node_warn(
|
total_loras = loras + unfound_loras + len(skipped_g) + len(skipped_l)
|
||||||
NODE_NAME, f'Found {len(loras)} lora tags in prompt but model & clip were not supplied!')
|
if total_loras:
|
||||||
log_node_info(NODE_NAME, 'Loras not processed, keeping for TEXT output.')
|
log_node_warn(NODE_NAME, f'Found {total_loras} lora tags in prompt but model & clip were not supplied!')
|
||||||
|
if unfound_loras:
|
||||||
|
log_node_warn(NODE_NAME, f'Note: {unfound_loras} lora tags were not found!')
|
||||||
|
log_node_info(NODE_NAME, 'Loras not processed but NOT stripped. Keeping in case used for TEXT output.')
|
||||||
|
|
||||||
conditioning = self.get_conditioning(prompt_g, prompt_l, opt_clip, opt_clip_width,
|
conditioning = self.get_conditioning(prompt_g, prompt_l, opt_clip, opt_clip_width,
|
||||||
opt_clip_height, target_width, target_height, crop_width,
|
opt_clip_height, target_width, target_height, crop_width,
|
||||||
|
|||||||
+11
-1
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
class AnyType(str):
|
class AnyType(str):
|
||||||
"""A special class that is always equal in not equal comparisons. Credit to pythongosssss"""
|
"""A special class that is always equal in not equal comparisons. Credit to pythongosssss"""
|
||||||
|
|
||||||
@@ -7,3 +6,14 @@ class AnyType(str):
|
|||||||
|
|
||||||
|
|
||||||
any_type = AnyType("*")
|
any_type = AnyType("*")
|
||||||
|
|
||||||
|
|
||||||
|
class ByPassTypeTuple(tuple):
|
||||||
|
"""A special class that will return additional "AnyType" strings beyond defined values.
|
||||||
|
Credit to Trung0246
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
if index > len(self) - 1:
|
||||||
|
return AnyType("*")
|
||||||
|
return super().__getitem__(index)
|
||||||
|
|||||||
+1
-1
@@ -79,7 +79,7 @@ class AnySwitchforMixin extends RgthreeBaseNode {
|
|||||||
output.label =
|
output.label =
|
||||||
output.type === 'RGTHREE_CONTEXT' ? 'CONTEXT' :
|
output.type === 'RGTHREE_CONTEXT' ? 'CONTEXT' :
|
||||||
Array.isArray(this.nodeType) || this.nodeType.includes(",")
|
Array.isArray(this.nodeType) || this.nodeType.includes(",")
|
||||||
? connectedType?.label || String(this.nodeType)
|
? connectedType?.label || connectedType?.name || String(this.nodeType)
|
||||||
: String(this.nodeType);
|
: String(this.nodeType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export class RgthreeBaseNode extends LGraphNode {
|
|||||||
/** Private Mode member so we can override the setter/getter and call an `onModeChange`. */
|
/** Private Mode member so we can override the setter/getter and call an `onModeChange`. */
|
||||||
private mode_: NodeMode;
|
private mode_: NodeMode;
|
||||||
|
|
||||||
|
configuring = false;
|
||||||
|
|
||||||
isVirtualNode = false;
|
isVirtualNode = false;
|
||||||
|
|
||||||
constructor(title = RgthreeBaseNode.title) {
|
constructor(title = RgthreeBaseNode.title) {
|
||||||
@@ -45,12 +47,14 @@ export class RgthreeBaseNode extends LGraphNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override configure(info: SerializedLGraphNode<TLGraphNode>): void {
|
override configure(info: SerializedLGraphNode<TLGraphNode>): void {
|
||||||
|
this.configuring = true;
|
||||||
super.configure(info);
|
super.configure(info);
|
||||||
// Fix https://github.com/comfyanonymous/ComfyUI/issues/1448 locally.
|
// Fix https://github.com/comfyanonymous/ComfyUI/issues/1448 locally.
|
||||||
// Can removed when fixed and adopted.
|
// Can removed when fixed and adopted.
|
||||||
for (const w of (this.widgets || [])) {
|
for (const w of (this.widgets || [])) {
|
||||||
w.last_y = w.last_y || 0;
|
w.last_y = w.last_y || 0;
|
||||||
}
|
}
|
||||||
|
this.configuring = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+34
-12
@@ -2,8 +2,6 @@
|
|||||||
import type {
|
import type {
|
||||||
INodeInputSlot,
|
INodeInputSlot,
|
||||||
INodeOutputSlot,
|
INodeOutputSlot,
|
||||||
LGraph,
|
|
||||||
LLink,
|
|
||||||
LiteGraph as TLiteGraph,
|
LiteGraph as TLiteGraph,
|
||||||
LGraphNode as TLGraphNode,
|
LGraphNode as TLGraphNode,
|
||||||
} from "./typings/litegraph.js";
|
} from "./typings/litegraph.js";
|
||||||
@@ -14,7 +12,6 @@ import {
|
|||||||
IoDirection,
|
IoDirection,
|
||||||
addConnectionLayoutSupport,
|
addConnectionLayoutSupport,
|
||||||
addMenuItem,
|
addMenuItem,
|
||||||
applyMixins,
|
|
||||||
matchLocalSlotsToServer,
|
matchLocalSlotsToServer,
|
||||||
replaceNode,
|
replaceNode,
|
||||||
} from "./utils.js";
|
} from "./utils.js";
|
||||||
@@ -36,10 +33,12 @@ function findMatchingIndexByTypeOrName(otherNode: TLGraphNode, otherSlot: INodeI
|
|||||||
otherSlotType = 'COMBO';
|
otherSlotType = 'COMBO';
|
||||||
}
|
}
|
||||||
const otherSlotName = otherSlot.name.toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
const otherSlotName = otherSlot.name.toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||||
|
const otherSlotLabel = (otherSlot.label || otherSlotName).toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||||
let ctxSlotIndex = -1;
|
let ctxSlotIndex = -1;
|
||||||
if (["CONDITIONING", "INT", "STRING", "FLOAT", "COMBO"].includes(otherSlotType)) {
|
if (["CONDITIONING", "INT", "STRING", "FLOAT", "COMBO"].includes(otherSlotType)) {
|
||||||
ctxSlotIndex = ctxSlots.findIndex((ctxSlot) => {
|
ctxSlotIndex = ctxSlots.findIndex((ctxSlot) => {
|
||||||
const ctxSlotName = ctxSlot.name.toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
const ctxSlotName = ctxSlot.name.toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||||
|
const ctxSlotLabel = (ctxSlot.label || ctxSlotName).toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||||
let ctxSlotType = ctxSlot.type as string;
|
let ctxSlotType = ctxSlot.type as string;
|
||||||
if (Array.isArray(ctxSlotType) || ctxSlotType.includes(',')) {
|
if (Array.isArray(ctxSlotType) || ctxSlotType.includes(',')) {
|
||||||
ctxSlotType = 'COMBO';
|
ctxSlotType = 'COMBO';
|
||||||
@@ -49,6 +48,7 @@ function findMatchingIndexByTypeOrName(otherNode: TLGraphNode, otherSlot: INodeI
|
|||||||
}
|
}
|
||||||
// Straightforward matches.
|
// Straightforward matches.
|
||||||
if(ctxSlotName === otherSlotName
|
if(ctxSlotName === otherSlotName
|
||||||
|
|| (ctxSlotLabel && otherSlotLabel && ctxSlotLabel == otherSlotLabel)
|
||||||
|| (ctxSlotName === "SEED" && otherSlotName.includes("SEED"))
|
|| (ctxSlotName === "SEED" && otherSlotName.includes("SEED"))
|
||||||
|| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("AT_STEP"))
|
|| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("AT_STEP"))
|
||||||
|| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("REFINER_STEP"))) {
|
|| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("REFINER_STEP"))) {
|
||||||
@@ -85,7 +85,7 @@ function findMatchingIndexByTypeOrName(otherNode: TLGraphNode, otherSlot: INodeI
|
|||||||
/**
|
/**
|
||||||
* A Base Context node for other context based nodes to extend.
|
* A Base Context node for other context based nodes to extend.
|
||||||
*/
|
*/
|
||||||
class BaseContextNode extends RgthreeBaseServerNode {
|
export class BaseContextNode extends RgthreeBaseServerNode {
|
||||||
constructor(title: string) {
|
constructor(title: string) {
|
||||||
super(title);
|
super(title);
|
||||||
}
|
}
|
||||||
@@ -261,6 +261,24 @@ class ContextSwitchBigNode extends BaseContextNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function addWidgetForDynamicContextOutputs(node: TLGraphNode, inputName: string) {
|
||||||
|
node.addCustomWidget({
|
||||||
|
name: inputName,
|
||||||
|
value: '',
|
||||||
|
draw(ctx, node, width, posY, height) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
computeSize(width) {
|
||||||
|
return [0,0];
|
||||||
|
},
|
||||||
|
serializeValue() {
|
||||||
|
const value = (node.outputs || []).map((o, i) => i > 0 && o.name).filter(n => n !== false).join(',');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const contextNodes = [ContextNode, ContextBigNode, ContextSwitchNode, ContextSwitchBigNode];
|
const contextNodes = [ContextNode, ContextBigNode, ContextSwitchNode, ContextSwitchBigNode];
|
||||||
const contextTypeToServerDef: { [type: string]: ComfyObjectInfo } = {};
|
const contextTypeToServerDef: { [type: string]: ComfyObjectInfo } = {};
|
||||||
|
|
||||||
@@ -296,10 +314,12 @@ app.registerExtension({
|
|||||||
const serverDef = type && contextTypeToServerDef[type];
|
const serverDef = type && contextTypeToServerDef[type];
|
||||||
if (serverDef) {
|
if (serverDef) {
|
||||||
fixBadConfigs(node as ContextNode);
|
fixBadConfigs(node as ContextNode);
|
||||||
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
if (!type!.includes('Dynamic')) {
|
||||||
// Switches don't need to change inputs, only context outputs
|
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
||||||
if (!type!.includes("Switch")) {
|
// Switches don't need to change inputs, only context outputs
|
||||||
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
if (!type!.includes("Switch")) {
|
||||||
|
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// }, 100);
|
// }, 100);
|
||||||
}
|
}
|
||||||
@@ -314,10 +334,12 @@ app.registerExtension({
|
|||||||
const serverDef = type && contextTypeToServerDef[type];
|
const serverDef = type && contextTypeToServerDef[type];
|
||||||
if (serverDef) {
|
if (serverDef) {
|
||||||
fixBadConfigs(node as ContextNode);
|
fixBadConfigs(node as ContextNode);
|
||||||
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
if (!type!.includes('Dynamic')) {
|
||||||
// Switches don't need to change inputs, only context outputs
|
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
||||||
if (!type!.includes("Switch")) {
|
// Switches don't need to change inputs, only context outputs
|
||||||
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
if (!type!.includes("Switch")) {
|
||||||
|
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+30
-20
@@ -21,6 +21,7 @@ import {
|
|||||||
LAYOUT_LABEL_TO_DATA,
|
LAYOUT_LABEL_TO_DATA,
|
||||||
addConnectionLayoutSupport,
|
addConnectionLayoutSupport,
|
||||||
addMenuItem,
|
addMenuItem,
|
||||||
|
getConnectedOutputNodesAndFilterPassThroughs,
|
||||||
getSlotLinks,
|
getSlotLinks,
|
||||||
isValidConnection,
|
isValidConnection,
|
||||||
} from "./utils.js";
|
} from "./utils.js";
|
||||||
@@ -71,7 +72,6 @@ app.registerExtension({
|
|||||||
readonly hideSlotLabels: boolean;
|
readonly hideSlotLabels: boolean;
|
||||||
|
|
||||||
private configuring = true;
|
private configuring = true;
|
||||||
private schedulePromise: Promise<void> | null = null;
|
|
||||||
|
|
||||||
defaultConnectionsLayout = configLayout;
|
defaultConnectionsLayout = configLayout;
|
||||||
|
|
||||||
@@ -137,7 +137,26 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.scheduleStabilize();
|
if (this.configuring) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.stabilize();
|
||||||
|
if (type === LiteGraph.INPUT) {
|
||||||
|
this.updateDownstream(connected ? 'connect' : 'disconnect', {index: 0, name: this.inputs[0]!.name});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates connected nodes of a change (checking and calling updateFromUpstream). Used for Dynamic Context.
|
||||||
|
*/
|
||||||
|
private updateDownstream(
|
||||||
|
update: "connect" | "disconnect" | "move" | "update",
|
||||||
|
updatedIndexes: {index: number; name: string; from?: number},
|
||||||
|
) {
|
||||||
|
const nodes = getConnectedOutputNodesAndFilterPassThroughs(this, this, 0);
|
||||||
|
for (const node of nodes) {
|
||||||
|
(node as any)?.updateFromUpstream?.(update, this, updatedIndexes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override onDrawForeground(ctx: CanvasRenderingContext2D, canvas: TLGraphCanvas): void {
|
override onDrawForeground(ctx: CanvasRenderingContext2D, canvas: TLGraphCanvas): void {
|
||||||
@@ -161,20 +180,6 @@ app.registerExtension({
|
|||||||
return super.disconnectOutput(slot, targetNode);
|
return super.disconnectOutput(slot, targetNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
scheduleStabilize(ms = 64) {
|
|
||||||
if (!this.schedulePromise) {
|
|
||||||
this.schedulePromise = new Promise((resolve) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.schedulePromise = null
|
|
||||||
this.stabilize();
|
|
||||||
resolve();
|
|
||||||
}, ms);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return this.schedulePromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
stabilize() {
|
stabilize() {
|
||||||
// If we are currently "configuring" then skip this stabilization. The connected nodes may
|
// If we are currently "configuring" then skip this stabilization. The connected nodes may
|
||||||
// not yet be configured.
|
// not yet be configured.
|
||||||
@@ -229,7 +234,7 @@ app.registerExtension({
|
|||||||
|
|
||||||
// Find all outputs
|
// Find all outputs
|
||||||
const nodes: TLGraphNode[] = [this];
|
const nodes: TLGraphNode[] = [this];
|
||||||
let outputNode = null;
|
let outputNodes: TLGraphNode[] = [];
|
||||||
let outputType = null;
|
let outputType = null;
|
||||||
while (nodes.length) {
|
while (nodes.length) {
|
||||||
currentNode = nodes.pop()!;
|
currentNode = nodes.pop()!;
|
||||||
@@ -266,8 +271,11 @@ app.registerExtension({
|
|||||||
console.warn(`[rgthree] Reroute - Disconnecting connected node's input (${node.id}.${link.target_slot}) (${node.type}) because its type (${String(nodeOutType)}) does not match the reroute type (${String(inputType)})`);
|
console.warn(`[rgthree] Reroute - Disconnecting connected node's input (${node.id}.${link.target_slot}) (${node.type}) because its type (${String(nodeOutType)}) does not match the reroute type (${String(inputType)})`);
|
||||||
node.disconnectInput(link.target_slot);
|
node.disconnectInput(link.target_slot);
|
||||||
} else {
|
} else {
|
||||||
|
if (outputType != null && outputType !== nodeOutType) {
|
||||||
|
console.warn(`[rgthree] Reroute - Mismatching output types..`);
|
||||||
|
}
|
||||||
outputType = nodeOutType;
|
outputType = nodeOutType;
|
||||||
outputNode = node;
|
outputNodes.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,8 +314,10 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(inputNode as any)?.onConnectionsChainChange?.();
|
// (inputNode as any)?.onConnectionsChainChange?.(this);
|
||||||
(outputNode as any)?.onConnectionsChainChange?.();
|
// for (const outputNode of outputNodes) {
|
||||||
|
// (outputNode as any)?.onConnectionsChainChange?.(this);
|
||||||
|
// }
|
||||||
app.graph.setDirtyCanvas(true, true);
|
app.graph.setDirtyCanvas(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -77,7 +77,7 @@ class LogSession {
|
|||||||
export type RgthreeUiMessage = {
|
export type RgthreeUiMessage = {
|
||||||
id: string;
|
id: string;
|
||||||
message: string;
|
message: string;
|
||||||
type?: 'warn'|null;
|
type?: 'warn'|'info'|null;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
// closeable?: boolean; // TODO
|
// closeable?: boolean; // TODO
|
||||||
actions?: Array<{
|
actions?: Array<{
|
||||||
@@ -305,8 +305,8 @@ class Rgthree {
|
|||||||
fireEvent(event: string, data: any) {
|
fireEvent(event: string, data: any) {
|
||||||
if (this.eventsToFns.has(event)) {
|
if (this.eventsToFns.has(event)) {
|
||||||
for (let fn of this.eventsToFns.get(event)!) {
|
for (let fn of this.eventsToFns.get(event)!) {
|
||||||
const event = new Event(data);
|
const e = new CustomEvent(event, {detail:data});
|
||||||
fn(event);
|
fn(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+4
-1
@@ -38,6 +38,8 @@ export interface INodeSlot {
|
|||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
// @rgthree
|
// @rgthree
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
// @rgthree
|
||||||
|
removable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INodeInputSlot extends INodeSlot {
|
export interface INodeInputSlot extends INodeSlot {
|
||||||
@@ -1087,7 +1089,8 @@ export declare class LGraphNode {
|
|||||||
|
|
||||||
/** Called by `LGraphCanvas.processContextMenu` */
|
/** Called by `LGraphCanvas.processContextMenu` */
|
||||||
getMenuOptions?(graphCanvas: LGraphCanvas): ContextMenuItem[];
|
getMenuOptions?(graphCanvas: LGraphCanvas): ContextMenuItem[];
|
||||||
getSlotMenuOptions?(slot: INodeSlot): ContextMenuItem[];
|
// rgthree
|
||||||
|
getSlotMenuOptions?(slotItem: {slot: number, input?: INodeInputSlot, output?: INodeOutputSlot, link_pos: Vector2}): ContextMenuItem[];
|
||||||
|
|
||||||
getExtraMenuOptions?(canvas: LGraphCanvas, options: ContextMenuItem[]): void;
|
getExtraMenuOptions?(canvas: LGraphCanvas, options: ContextMenuItem[]): void;
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-22
@@ -400,7 +400,7 @@ export function getConnectedInputNodes(
|
|||||||
slot?: number,
|
slot?: number,
|
||||||
passThroughFollowing = PassThroughFollowing.ALL,
|
passThroughFollowing = PassThroughFollowing.ALL,
|
||||||
) : TLGraphNode[] {
|
) : TLGraphNode[] {
|
||||||
return getConnectedNodes(startNode, IoDirection.INPUT, currentNode, slot, passThroughFollowing).map(n => n.node);
|
return getConnectedNodesInfo(startNode, IoDirection.INPUT, currentNode, slot, passThroughFollowing).map(n => n.node);
|
||||||
}
|
}
|
||||||
export function getConnectedInputNodesAndFilterPassThroughs(
|
export function getConnectedInputNodesAndFilterPassThroughs(
|
||||||
startNode: TLGraphNode,
|
startNode: TLGraphNode,
|
||||||
@@ -413,14 +413,16 @@ export function getConnectedInputNodesAndFilterPassThroughs(
|
|||||||
passThroughFollowing,
|
passThroughFollowing,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getConnectedOutputNodes(
|
export function getConnectedOutputNodes(
|
||||||
startNode: TLGraphNode,
|
startNode: TLGraphNode,
|
||||||
currentNode?: TLGraphNode,
|
currentNode?: TLGraphNode,
|
||||||
slot?: number,
|
slot?: number,
|
||||||
passThroughFollowing = PassThroughFollowing.ALL,
|
passThroughFollowing = PassThroughFollowing.ALL,
|
||||||
) : TLGraphNode[] {
|
) : TLGraphNode[] {
|
||||||
return getConnectedNodes(startNode, IoDirection.OUTPUT, currentNode, slot, passThroughFollowing).map(n => n.node);
|
return getConnectedNodesInfo(startNode, IoDirection.OUTPUT, currentNode, slot, passThroughFollowing).map(n => n.node);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getConnectedOutputNodesAndFilterPassThroughs(
|
export function getConnectedOutputNodesAndFilterPassThroughs(
|
||||||
startNode: TLGraphNode,
|
startNode: TLGraphNode,
|
||||||
currentNode?: TLGraphNode,
|
currentNode?: TLGraphNode,
|
||||||
@@ -434,39 +436,48 @@ export function getConnectedOutputNodesAndFilterPassThroughs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function getConnectedNodes(
|
export type ConnectedNodeInfo = {node: TLGraphNode, travelFromSlot: number, travelToSlot: number, originTravelFromSlot: number};
|
||||||
|
|
||||||
|
export function getConnectedNodesInfo(
|
||||||
startNode: TLGraphNode,
|
startNode: TLGraphNode,
|
||||||
dir = IoDirection.INPUT,
|
dir = IoDirection.INPUT,
|
||||||
currentNode?: TLGraphNode,
|
currentNode?: TLGraphNode,
|
||||||
slot?: number,
|
slot?: number,
|
||||||
passThroughFollowing = PassThroughFollowing.ALL,
|
passThroughFollowing = PassThroughFollowing.ALL,
|
||||||
) : {node:TLGraphNode, slot: number}[] {
|
originTravelFromSlot?: number
|
||||||
|
) : ConnectedNodeInfo[] {
|
||||||
currentNode = currentNode || startNode;
|
currentNode = currentNode || startNode;
|
||||||
let rootNodes: {node:TLGraphNode, slot: number}[] = [];
|
let rootNodes: ConnectedNodeInfo[] = [];
|
||||||
const slotsToRemove = [];
|
const slotsToRemove = [];
|
||||||
if (startNode === currentNode || shouldPassThrough(currentNode, passThroughFollowing)) {
|
if (startNode === currentNode || shouldPassThrough(currentNode, passThroughFollowing)) {
|
||||||
// const removeDups = startNode === currentNode;
|
|
||||||
let linkIds: Array<number | null>;
|
let linkIds: Array<number | null>;
|
||||||
|
|
||||||
if (dir == IoDirection.OUTPUT) {
|
if (dir == IoDirection.OUTPUT) {
|
||||||
linkIds = currentNode.outputs?.flatMap((i) => i.links) || [];
|
if (slot != null && slot > -1) {
|
||||||
} else {
|
linkIds = currentNode.outputs[slot]?.links ?? [];
|
||||||
linkIds = currentNode.inputs?.map((i) => i.link) || [];
|
|
||||||
}
|
|
||||||
if (typeof slot == "number" && slot > -1) {
|
|
||||||
if (linkIds[slot]) {
|
|
||||||
linkIds = [linkIds[slot]!];
|
|
||||||
} else {
|
} else {
|
||||||
return [];
|
linkIds = currentNode.outputs?.flatMap((i) => i.links) || [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (slot != null && slot > -1) {
|
||||||
|
linkIds = [currentNode.inputs[slot]?.link ?? -1].filter(i => i > -1);
|
||||||
|
} else {
|
||||||
|
linkIds = currentNode.inputs?.map((i) => i.link) || [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let graph = app.graph as LGraph;
|
let graph = app.graph as LGraph;
|
||||||
for (const linkId of linkIds) {
|
for (const linkId of linkIds) {
|
||||||
const link: LLink = (linkId != null && graph.links[linkId]) as LLink;
|
let link: LLink | null = null;
|
||||||
|
if (typeof linkId == "number") {
|
||||||
|
link = graph.links[linkId] as LLink;
|
||||||
|
}
|
||||||
if (!link) {
|
if (!link) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const travelFromSlot = dir == IoDirection.OUTPUT ? link.origin_slot : link.target_slot;
|
||||||
const connectedId = dir == IoDirection.OUTPUT ? link.target_id : link.origin_id;
|
const connectedId = dir == IoDirection.OUTPUT ? link.target_id : link.origin_id;
|
||||||
const originSlot = dir == IoDirection.OUTPUT ? link.target_slot : link.origin_slot;
|
const travelToSlot = dir == IoDirection.OUTPUT ? link.target_slot : link.origin_slot;
|
||||||
|
originTravelFromSlot = originTravelFromSlot != null ? originTravelFromSlot : travelFromSlot;
|
||||||
const originNode: TLGraphNode = graph.getNodeById(connectedId)!;
|
const originNode: TLGraphNode = graph.getNodeById(connectedId)!;
|
||||||
if (!link) {
|
if (!link) {
|
||||||
console.error("No connected node found... weird");
|
console.error("No connected node found... weird");
|
||||||
@@ -480,10 +491,10 @@ export function getConnectedNodes(
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Add the node and, if it's a pass through, let's collect all its nodes as well.
|
// Add the node and, if it's a pass through, let's collect all its nodes as well.
|
||||||
rootNodes.push({node: originNode, slot: originSlot});
|
rootNodes.push({node: originNode, travelFromSlot, travelToSlot, originTravelFromSlot});
|
||||||
if (shouldPassThrough(originNode, passThroughFollowing)) {
|
if (shouldPassThrough(originNode, passThroughFollowing)) {
|
||||||
for (const foundNode of getConnectedNodes(startNode, dir, originNode)) {
|
for (const foundNode of getConnectedNodesInfo(startNode, dir, originNode, undefined, undefined, originTravelFromSlot)) {
|
||||||
if (!rootNodes.includes(foundNode)) {
|
if (!rootNodes.map(n => n.node).includes(foundNode.node)) {
|
||||||
rootNodes.push(foundNode);
|
rootNodes.push(foundNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -494,7 +505,7 @@ export function getConnectedNodes(
|
|||||||
return rootNodes;
|
return rootNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnectionType = { type: string | string[]; label: string | undefined };
|
export type ConnectionType = { type: string | string[]; name: string | undefined; label: string | undefined; };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Follows a connection until we find a type associated with a slot.
|
* Follows a connection until we find a type associated with a slot.
|
||||||
@@ -539,7 +550,7 @@ function getTypeFromSlot(
|
|||||||
let graph = app.graph as LGraph;
|
let graph = app.graph as LGraph;
|
||||||
let type = slot?.type;
|
let type = slot?.type;
|
||||||
if (!skipSelf && type != null && type != "*") {
|
if (!skipSelf && type != null && type != "*") {
|
||||||
return { type: type as string, label: slot?.label || slot?.name };
|
return { type: type as string, label: slot?.label, name: slot?.name};
|
||||||
}
|
}
|
||||||
const links = getSlotLinks(slot);
|
const links = getSlotLinks(slot);
|
||||||
for (const link of links) {
|
for (const link of links) {
|
||||||
@@ -554,7 +565,8 @@ function getTypeFromSlot(
|
|||||||
if (connectedSlot?.type != null && connectedSlot?.type != "*") {
|
if (connectedSlot?.type != null && connectedSlot?.type != "*") {
|
||||||
return {
|
return {
|
||||||
type: connectedSlot.type as string,
|
type: connectedSlot.type as string,
|
||||||
label: connectedSlot?.label || connectedSlot?.name,
|
label: connectedSlot?.label,
|
||||||
|
name: connectedSlot?.name
|
||||||
};
|
};
|
||||||
} else if (connectedSlot?.type == "*") {
|
} else if (connectedSlot?.type == "*") {
|
||||||
return followConnectionUntilType(connectedNode, dir);
|
return followConnectionUntilType(connectedNode, dir);
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@ class AnySwitchforMixin extends RgthreeBaseNode {
|
|||||||
output.label =
|
output.label =
|
||||||
output.type === 'RGTHREE_CONTEXT' ? 'CONTEXT' :
|
output.type === 'RGTHREE_CONTEXT' ? 'CONTEXT' :
|
||||||
Array.isArray(this.nodeType) || this.nodeType.includes(",")
|
Array.isArray(this.nodeType) || this.nodeType.includes(",")
|
||||||
? (connectedType === null || connectedType === void 0 ? void 0 : connectedType.label) || String(this.nodeType)
|
? (connectedType === null || connectedType === void 0 ? void 0 : connectedType.label) || (connectedType === null || connectedType === void 0 ? void 0 : connectedType.name) || String(this.nodeType)
|
||||||
: String(this.nodeType);
|
: String(this.nodeType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export class RgthreeBaseNode extends LGraphNode {
|
|||||||
constructor(title = RgthreeBaseNode.title) {
|
constructor(title = RgthreeBaseNode.title) {
|
||||||
super(title);
|
super(title);
|
||||||
this._tempWidth = 0;
|
this._tempWidth = 0;
|
||||||
|
this.configuring = false;
|
||||||
this.isVirtualNode = false;
|
this.isVirtualNode = false;
|
||||||
if (title == '__NEED_NAME__') {
|
if (title == '__NEED_NAME__') {
|
||||||
throw new Error('RgthreeBaseNode needs overrides.');
|
throw new Error('RgthreeBaseNode needs overrides.');
|
||||||
@@ -12,10 +13,12 @@ export class RgthreeBaseNode extends LGraphNode {
|
|||||||
this.properties = this.properties || {};
|
this.properties = this.properties || {};
|
||||||
}
|
}
|
||||||
configure(info) {
|
configure(info) {
|
||||||
|
this.configuring = true;
|
||||||
super.configure(info);
|
super.configure(info);
|
||||||
for (const w of (this.widgets || [])) {
|
for (const w of (this.widgets || [])) {
|
||||||
w.last_y = w.last_y || 0;
|
w.last_y = w.last_y || 0;
|
||||||
}
|
}
|
||||||
|
this.configuring = false;
|
||||||
}
|
}
|
||||||
set mode(mode) {
|
set mode(mode) {
|
||||||
if (this.mode_ != mode) {
|
if (this.mode_ != mode) {
|
||||||
|
|||||||
+30
-7
@@ -10,10 +10,12 @@ function findMatchingIndexByTypeOrName(otherNode, otherSlot, ctxSlots) {
|
|||||||
otherSlotType = 'COMBO';
|
otherSlotType = 'COMBO';
|
||||||
}
|
}
|
||||||
const otherSlotName = otherSlot.name.toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
const otherSlotName = otherSlot.name.toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||||
|
const otherSlotLabel = (otherSlot.label || otherSlotName).toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||||
let ctxSlotIndex = -1;
|
let ctxSlotIndex = -1;
|
||||||
if (["CONDITIONING", "INT", "STRING", "FLOAT", "COMBO"].includes(otherSlotType)) {
|
if (["CONDITIONING", "INT", "STRING", "FLOAT", "COMBO"].includes(otherSlotType)) {
|
||||||
ctxSlotIndex = ctxSlots.findIndex((ctxSlot) => {
|
ctxSlotIndex = ctxSlots.findIndex((ctxSlot) => {
|
||||||
const ctxSlotName = ctxSlot.name.toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
const ctxSlotName = ctxSlot.name.toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||||
|
const ctxSlotLabel = (ctxSlot.label || ctxSlotName).toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||||
let ctxSlotType = ctxSlot.type;
|
let ctxSlotType = ctxSlot.type;
|
||||||
if (Array.isArray(ctxSlotType) || ctxSlotType.includes(',')) {
|
if (Array.isArray(ctxSlotType) || ctxSlotType.includes(',')) {
|
||||||
ctxSlotType = 'COMBO';
|
ctxSlotType = 'COMBO';
|
||||||
@@ -22,6 +24,7 @@ function findMatchingIndexByTypeOrName(otherNode, otherSlot, ctxSlots) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ctxSlotName === otherSlotName
|
if (ctxSlotName === otherSlotName
|
||||||
|
|| (ctxSlotLabel && otherSlotLabel && ctxSlotLabel == otherSlotLabel)
|
||||||
|| (ctxSlotName === "SEED" && otherSlotName.includes("SEED"))
|
|| (ctxSlotName === "SEED" && otherSlotName.includes("SEED"))
|
||||||
|| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("AT_STEP"))
|
|| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("AT_STEP"))
|
||||||
|| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("REFINER_STEP"))) {
|
|| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("REFINER_STEP"))) {
|
||||||
@@ -47,7 +50,7 @@ function findMatchingIndexByTypeOrName(otherNode, otherSlot, ctxSlots) {
|
|||||||
}
|
}
|
||||||
return ctxSlotIndex;
|
return ctxSlotIndex;
|
||||||
}
|
}
|
||||||
class BaseContextNode extends RgthreeBaseServerNode {
|
export class BaseContextNode extends RgthreeBaseServerNode {
|
||||||
constructor(title) {
|
constructor(title) {
|
||||||
super(title);
|
super(title);
|
||||||
}
|
}
|
||||||
@@ -171,6 +174,22 @@ class ContextSwitchBigNode extends BaseContextNode {
|
|||||||
ContextSwitchBigNode.title = "Context Switch Big (rgthree)";
|
ContextSwitchBigNode.title = "Context Switch Big (rgthree)";
|
||||||
ContextSwitchBigNode.type = "Context Switch Big (rgthree)";
|
ContextSwitchBigNode.type = "Context Switch Big (rgthree)";
|
||||||
ContextSwitchBigNode.comfyClass = "Context Switch Big (rgthree)";
|
ContextSwitchBigNode.comfyClass = "Context Switch Big (rgthree)";
|
||||||
|
function addWidgetForDynamicContextOutputs(node, inputName) {
|
||||||
|
node.addCustomWidget({
|
||||||
|
name: inputName,
|
||||||
|
value: '',
|
||||||
|
draw(ctx, node, width, posY, height) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
computeSize(width) {
|
||||||
|
return [0, 0];
|
||||||
|
},
|
||||||
|
serializeValue() {
|
||||||
|
const value = (node.outputs || []).map((o, i) => i > 0 && o.name).filter(n => n !== false).join(',');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
const contextNodes = [ContextNode, ContextBigNode, ContextSwitchNode, ContextSwitchBigNode];
|
const contextNodes = [ContextNode, ContextBigNode, ContextSwitchNode, ContextSwitchBigNode];
|
||||||
const contextTypeToServerDef = {};
|
const contextTypeToServerDef = {};
|
||||||
function fixBadConfigs(node) {
|
function fixBadConfigs(node) {
|
||||||
@@ -199,9 +218,11 @@ app.registerExtension({
|
|||||||
const serverDef = type && contextTypeToServerDef[type];
|
const serverDef = type && contextTypeToServerDef[type];
|
||||||
if (serverDef) {
|
if (serverDef) {
|
||||||
fixBadConfigs(node);
|
fixBadConfigs(node);
|
||||||
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
if (!type.includes('Dynamic')) {
|
||||||
if (!type.includes("Switch")) {
|
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
||||||
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
if (!type.includes("Switch")) {
|
||||||
|
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -210,9 +231,11 @@ app.registerExtension({
|
|||||||
const serverDef = type && contextTypeToServerDef[type];
|
const serverDef = type && contextTypeToServerDef[type];
|
||||||
if (serverDef) {
|
if (serverDef) {
|
||||||
fixBadConfigs(node);
|
fixBadConfigs(node);
|
||||||
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
if (!type.includes('Dynamic')) {
|
||||||
if (!type.includes("Switch")) {
|
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
||||||
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
if (!type.includes("Switch")) {
|
||||||
|
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,745 @@
|
|||||||
|
import { app } from "../../scripts/app.js";
|
||||||
|
import { IoDirection, PassThroughFollowing, followConnectionUntilType, getConnectedInputNodes, getConnectedInputNodesAndFilterPassThroughs, getConnectedNodesInfo, getConnectedOutputNodesAndFilterPassThroughs, shouldPassThrough, } from "./utils.js";
|
||||||
|
import { rgthree } from "./rgthree.js";
|
||||||
|
import { BaseContextNode } from "./context.js";
|
||||||
|
class ContextDynamicNodeBase extends BaseContextNode {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
this.hasShadowInputs = false;
|
||||||
|
}
|
||||||
|
getContextInputsList() {
|
||||||
|
return this.inputs;
|
||||||
|
}
|
||||||
|
onConnectionsChainChange(arg) {
|
||||||
|
console.log("ContextDynamicNodeBase: onConnectionsChainChange", this.id, arg);
|
||||||
|
}
|
||||||
|
onNodeCreated() {
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
if (inputs[inputs.length - 1].type === "*") {
|
||||||
|
this.removeOutput(inputs.length - 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.addInput("+", "*");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getWidgets() {
|
||||||
|
return Object.assign({}, super.getWidgets(), {
|
||||||
|
DYNAMIC_CONTEXT_OUTPUTS: (node, inputName, inputData, app) => {
|
||||||
|
node.addCustomWidget({
|
||||||
|
name: inputName,
|
||||||
|
value: "",
|
||||||
|
draw(ctx, node, width, posY, height) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
computeSize(width) {
|
||||||
|
return [0, 0];
|
||||||
|
},
|
||||||
|
serializeValue() {
|
||||||
|
const value = (node.outputs || [])
|
||||||
|
.map((o, i) => i > 0 && o.name)
|
||||||
|
.filter((n) => n !== false)
|
||||||
|
.join(",");
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
stripOwnedPrefix(name) {
|
||||||
|
return name.replace(/^\+\s*/, "");
|
||||||
|
}
|
||||||
|
addOwnedPrefix(name) {
|
||||||
|
return `+ ${this.stripOwnedPrefix(name)}`;
|
||||||
|
}
|
||||||
|
isOwnedInput(inputOrName) {
|
||||||
|
const name = typeof inputOrName == "string" ? inputOrName : (inputOrName === null || inputOrName === void 0 ? void 0 : inputOrName.name) || "";
|
||||||
|
return name.startsWith("+ ") || name === "+";
|
||||||
|
}
|
||||||
|
getNextUniqueNameForThisNode(desiredName) {
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
const allExistingKeys = inputs.map((i) => this.stripOwnedPrefix(i.name).toLocaleUpperCase());
|
||||||
|
desiredName = this.stripOwnedPrefix(desiredName);
|
||||||
|
let newName = desiredName;
|
||||||
|
let n = 0;
|
||||||
|
while (allExistingKeys.includes(newName.toLocaleUpperCase())) {
|
||||||
|
newName = `${desiredName}.${++n}`;
|
||||||
|
}
|
||||||
|
return newName;
|
||||||
|
}
|
||||||
|
onConnectionsChange(type, slotIndex, isConnected, linkInfo, ioSlot) {
|
||||||
|
var _a;
|
||||||
|
(_a = super.onConnectionsChange) === null || _a === void 0 ? void 0 : _a.call(this, type, slotIndex, isConnected, linkInfo, ioSlot);
|
||||||
|
if (this.configuring) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type === LiteGraph.INPUT) {
|
||||||
|
if (isConnected) {
|
||||||
|
this.handleInputConnected(slotIndex);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.handleInputDisconnected(slotIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleInputConnected(slotIndex) {
|
||||||
|
}
|
||||||
|
handleInputDisconnected(slotIndex) {
|
||||||
|
}
|
||||||
|
updateFromUpstream(update, node, updatedSlotData) {
|
||||||
|
}
|
||||||
|
provideInputsData() {
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
return inputs
|
||||||
|
.map((input, index) => ({
|
||||||
|
name: this.stripOwnedPrefix(input.name),
|
||||||
|
type: String(input.type),
|
||||||
|
index,
|
||||||
|
}))
|
||||||
|
.filter((i) => i.type !== "*");
|
||||||
|
}
|
||||||
|
updateDownstream(update, updatedSlotData) {
|
||||||
|
var _a;
|
||||||
|
const nodes = getConnectedOutputNodesAndFilterPassThroughs(this, this, 0);
|
||||||
|
for (const node of nodes) {
|
||||||
|
(_a = node === null || node === void 0 ? void 0 : node.updateFromUpstream) === null || _a === void 0 ? void 0 : _a.call(node, update, this, updatedSlotData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addContextInput(name, type, slot = -1) {
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
if (this.hasShadowInputs) {
|
||||||
|
inputs.push({ name, type });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.addInput(name, type);
|
||||||
|
}
|
||||||
|
if (slot > -1) {
|
||||||
|
inputs.splice(slot, 0, inputs.splice(inputs.length - 1, 1)[0]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
slot = inputs.length - 1;
|
||||||
|
}
|
||||||
|
if (type !== "*") {
|
||||||
|
const output = this.addOutput(name.toUpperCase(), type);
|
||||||
|
if (type === "COMBO" || String(type).includes(",") || Array.isArray(type)) {
|
||||||
|
output.widget = true;
|
||||||
|
}
|
||||||
|
if (slot > -1) {
|
||||||
|
this.outputs.splice(slot, 0, this.outputs.splice(this.outputs.length - 1, 1)[0]);
|
||||||
|
}
|
||||||
|
this.fixInputsOutputsLinkSlots();
|
||||||
|
this.updateDownstream("connect", { index: slot, name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeContextInput(slot) {
|
||||||
|
if (this.hasShadowInputs) {
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
const input = inputs.splice(slot, 1)[0];
|
||||||
|
if (this.outputs[slot]) {
|
||||||
|
this.removeOutput(slot);
|
||||||
|
}
|
||||||
|
this.updateDownstream("disconnect", { index: slot, name: input.name });
|
||||||
|
this.fixInputsOutputsLinkSlots();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.removeInput(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
moveContextInput(slotFrom, slotTo) {
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
if (slotTo === 'bottom') {
|
||||||
|
slotTo = inputs.length - 1;
|
||||||
|
}
|
||||||
|
if (slotFrom === slotTo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let newIndex = slotTo + (slotFrom < slotTo ? -1 : 0);
|
||||||
|
const input = inputs.splice(slotFrom, 1)[0];
|
||||||
|
inputs.splice(newIndex, 0, input);
|
||||||
|
this.outputs.splice(newIndex, 0, ...this.outputs.splice(slotFrom, 1));
|
||||||
|
this.fixInputsOutputsLinkSlots();
|
||||||
|
this.updateDownstream("move", { index: slotTo, from: slotFrom, name: input.name });
|
||||||
|
}
|
||||||
|
renameContextInput(index, newName, forceOwnBool = null) {
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
const input = inputs[index];
|
||||||
|
const oldName = input.name;
|
||||||
|
newName = this.stripOwnedPrefix(newName.trim() || this.getSlotDefaultInputLabel(index));
|
||||||
|
if (forceOwnBool === true || (this.isOwnedInput(oldName) && forceOwnBool !== false)) {
|
||||||
|
newName = this.addOwnedPrefix(newName);
|
||||||
|
}
|
||||||
|
input.name = newName;
|
||||||
|
this.outputs[index].name = this.stripOwnedPrefix(inputs[index].name).toUpperCase();
|
||||||
|
this.updateDownstream("update", { index, name: newName });
|
||||||
|
}
|
||||||
|
fixInputsOutputsLinkSlots() {
|
||||||
|
if (!this.hasShadowInputs) {
|
||||||
|
const inputs = this.inputs;
|
||||||
|
for (let index = inputs.length - 1; index > 0; index--) {
|
||||||
|
const input = inputs[index];
|
||||||
|
if ((input === null || input === void 0 ? void 0 : input.link) != null) {
|
||||||
|
app.graph.links[input.link].target_slot = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const outputs = this.outputs;
|
||||||
|
for (let index = outputs.length - 1; index > 0; index--) {
|
||||||
|
const output = outputs[index];
|
||||||
|
for (const link of (output === null || output === void 0 ? void 0 : output.links) || []) {
|
||||||
|
app.graph.links[link].origin_slot = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getSlotDefaultInputLabel(slot) {
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
const input = inputs[slot];
|
||||||
|
let defaultLabel = this.stripOwnedPrefix(input.name).toLowerCase();
|
||||||
|
return defaultLabel.toLocaleLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextDynamicNodeBase.logger = rgthree.newLogSession("[Dynamic Context]");
|
||||||
|
class ContextDynamicNode extends ContextDynamicNodeBase {
|
||||||
|
static setUp(comfyClass) {
|
||||||
|
BaseContextNode.setUp(comfyClass, ContextDynamicNode);
|
||||||
|
}
|
||||||
|
constructor(title = ContextDynamicNode.title) {
|
||||||
|
super(title);
|
||||||
|
}
|
||||||
|
clone() {
|
||||||
|
const cloned = super.clone();
|
||||||
|
while (cloned.inputs.length > 1) {
|
||||||
|
cloned.removeInput(cloned.inputs.length - 1);
|
||||||
|
}
|
||||||
|
cloned.addInput("+", "*");
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
|
removeInput(slot) {
|
||||||
|
const input = this.inputs[slot];
|
||||||
|
super.removeInput(slot);
|
||||||
|
if (this.outputs[slot]) {
|
||||||
|
this.removeOutput(slot);
|
||||||
|
}
|
||||||
|
this.fixInputsOutputsLinkSlots();
|
||||||
|
this.updateDownstream("disconnect", { index: slot, name: input.name });
|
||||||
|
this.stabilizeNames();
|
||||||
|
}
|
||||||
|
handleInputConnected(slotIndex) {
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
const ioSlot = inputs[slotIndex];
|
||||||
|
if (slotIndex === 0) {
|
||||||
|
const baseNodes = getConnectedInputNodesAndFilterPassThroughs(this, this, 0);
|
||||||
|
const baseNodesDynamicCtx = baseNodes[0];
|
||||||
|
if (baseNodesDynamicCtx === null || baseNodesDynamicCtx === void 0 ? void 0 : baseNodesDynamicCtx.provideInputsData) {
|
||||||
|
for (const input of baseNodesDynamicCtx.provideInputsData()) {
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
if (input.name === "base_ctx" || input.name === "+") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const foundIndex = inputs.findIndex(i => this.stripOwnedPrefix(i.name) === input.name);
|
||||||
|
if (foundIndex > -1) {
|
||||||
|
this.moveContextInput(foundIndex, input.index);
|
||||||
|
this.renameContextInput(input.index, input.name, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.addContextInput(input.name, input.type, input.index);
|
||||||
|
this.stabilizeNames();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ioSlot.type === "*") {
|
||||||
|
let cxn = null;
|
||||||
|
if (ioSlot.link) {
|
||||||
|
cxn = followConnectionUntilType(this, IoDirection.INPUT, slotIndex, true);
|
||||||
|
}
|
||||||
|
if (cxn === null || cxn === void 0 ? void 0 : cxn.type) {
|
||||||
|
let name = cxn.name;
|
||||||
|
if (name.match(/^(\+\s*)?[A-Z_]+$/)) {
|
||||||
|
name = name.toLowerCase();
|
||||||
|
}
|
||||||
|
name = this.getNextUniqueNameForThisNode(name);
|
||||||
|
if (!this.outputs[slotIndex]) {
|
||||||
|
this.addOutput("*", "*");
|
||||||
|
}
|
||||||
|
inputs[slotIndex].type = cxn.type;
|
||||||
|
inputs[slotIndex].removable = true;
|
||||||
|
this.outputs[slotIndex].type = cxn.type;
|
||||||
|
this.updateDownstream("connect", { index: slotIndex, name: this.stripOwnedPrefix(name) });
|
||||||
|
this.renameContextInput(slotIndex, name, true);
|
||||||
|
if (cxn.type === "COMBO" || cxn.type.includes(",") || Array.isArray(cxn.type)) {
|
||||||
|
this.outputs[slotIndex].widget = true;
|
||||||
|
}
|
||||||
|
this.addInput("+", "*");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleInputDisconnected(slotIndex) {
|
||||||
|
var _a, _b;
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
if (slotIndex === 0) {
|
||||||
|
for (let index = inputs.length - 1; index > 0; index--) {
|
||||||
|
if (index === 0 || index === inputs.length - 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const input = inputs[index];
|
||||||
|
if (!this.isOwnedInput(input.name)) {
|
||||||
|
if (input.link || ((_b = (_a = this.outputs[index]) === null || _a === void 0 ? void 0 : _a.links) === null || _b === void 0 ? void 0 : _b.length)) {
|
||||||
|
this.renameContextInput(index, input.name, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.removeContextInput(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setSize(this.computeSize());
|
||||||
|
this.setDirtyCanvas(true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateFromUpstream(update, node, updatedSlotData) {
|
||||||
|
var _a, _b;
|
||||||
|
console.log("----- ContextDynamicNode :: updateFromUpstream", arguments);
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
if (update == "connect") {
|
||||||
|
const baseInputsData = node.provideInputsData();
|
||||||
|
const baseIndex = updatedSlotData.index;
|
||||||
|
const baseInputData = baseInputsData[baseIndex];
|
||||||
|
const name = this.getNextUniqueNameForThisNode(baseInputData.name);
|
||||||
|
const foundIndex = inputs.findIndex(i => this.stripOwnedPrefix(i.name) === baseInputData.name);
|
||||||
|
if (foundIndex > -1) {
|
||||||
|
this.moveContextInput(foundIndex, baseIndex);
|
||||||
|
this.renameContextInput(baseIndex, baseInputData.name, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.addContextInput(baseInputData.name, baseInputData.type, baseInputData.index);
|
||||||
|
this.stabilizeNames();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (update == "disconnect") {
|
||||||
|
if ((_b = (_a = this.outputs[updatedSlotData.index]) === null || _a === void 0 ? void 0 : _a.links) === null || _b === void 0 ? void 0 : _b.length) {
|
||||||
|
this.renameContextInput(updatedSlotData.index, updatedSlotData.name, true);
|
||||||
|
this.moveContextInput(updatedSlotData.index, 'bottom');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.removeContextInput(updatedSlotData.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (update === "move") {
|
||||||
|
this.moveContextInput(updatedSlotData.from, updatedSlotData.index);
|
||||||
|
}
|
||||||
|
else if (update == "update") {
|
||||||
|
const baseInputsData = node.provideInputsData();
|
||||||
|
const baseIndex = updatedSlotData.index;
|
||||||
|
const baseInput = baseInputsData[baseIndex];
|
||||||
|
this.renameContextInput(baseIndex, baseInput.name);
|
||||||
|
this.stabilizeNames();
|
||||||
|
}
|
||||||
|
this.setSize(this.computeSize());
|
||||||
|
this.setDirtyCanvas(true, true);
|
||||||
|
}
|
||||||
|
stabilizeNames() {
|
||||||
|
const inputs = this.getContextInputsList();
|
||||||
|
const names = [];
|
||||||
|
for (const [index, input] of inputs.entries()) {
|
||||||
|
if (index === 0 || index === inputs.length - 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
input.label = undefined;
|
||||||
|
this.outputs[index].label = undefined;
|
||||||
|
let origName = this.stripOwnedPrefix(input.name).replace(/\.\d+$/, "");
|
||||||
|
let name = input.name;
|
||||||
|
if (!this.isOwnedInput(name)) {
|
||||||
|
names.push(name.toLocaleUpperCase());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let n = 0;
|
||||||
|
name = this.addOwnedPrefix(origName);
|
||||||
|
while (names.includes(this.stripOwnedPrefix(name).toLocaleUpperCase())) {
|
||||||
|
name = `${this.addOwnedPrefix(origName)}.${++n}`;
|
||||||
|
}
|
||||||
|
names.push(this.stripOwnedPrefix(name).toLocaleUpperCase());
|
||||||
|
if (input.name !== name) {
|
||||||
|
this.renameContextInput(index, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onConnectInput(inputIdx, outputType, outputSlot, outputNode, outputIndex) {
|
||||||
|
let canConnect = true;
|
||||||
|
if (super.onConnectInput) {
|
||||||
|
canConnect = super.onConnectInput(inputIdx, outputType, outputSlot, outputNode, outputIndex);
|
||||||
|
}
|
||||||
|
if (canConnect && outputNode instanceof ContextDynamicNode && outputIndex === 0 && inputIdx !== 0) {
|
||||||
|
ContextDynamicNodeBase.logger.error("Currently, you can only connect a context node in the first slot.");
|
||||||
|
canConnect = false;
|
||||||
|
}
|
||||||
|
return canConnect;
|
||||||
|
}
|
||||||
|
getSlotMenuOptions(info) {
|
||||||
|
const opts = [];
|
||||||
|
if (info.input) {
|
||||||
|
if (this.isOwnedInput(info.input.name) && info.input.type !== '*') {
|
||||||
|
opts.push({
|
||||||
|
content: "✏️ Rename Input",
|
||||||
|
callback: () => {
|
||||||
|
var dialog = app.canvas.createDialog("<span class='name'>Name</span><input autofocus type='text'/><button>OK</button>", {});
|
||||||
|
var dialogInput = dialog.querySelector("input");
|
||||||
|
if (dialogInput) {
|
||||||
|
dialogInput.value = this.stripOwnedPrefix(info.input.name || "");
|
||||||
|
}
|
||||||
|
var inner = () => {
|
||||||
|
app.graph.beforeChange();
|
||||||
|
this.renameContextInput(info.slot, dialogInput.value);
|
||||||
|
this.stabilizeNames();
|
||||||
|
this.setDirtyCanvas(true, true);
|
||||||
|
dialog.close();
|
||||||
|
app.graph.afterChange();
|
||||||
|
};
|
||||||
|
dialog.querySelector("button").addEventListener("click", inner);
|
||||||
|
dialogInput.addEventListener("keydown", (e) => {
|
||||||
|
var _a;
|
||||||
|
dialog.is_modified = true;
|
||||||
|
if (e.keyCode == 27) {
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
else if (e.keyCode == 13) {
|
||||||
|
inner();
|
||||||
|
}
|
||||||
|
else if (e.keyCode != 13 && ((_a = e.target) === null || _a === void 0 ? void 0 : _a.localName) != "textarea") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
dialogInput.focus();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
opts.push({
|
||||||
|
content: "🗑️ Delete Input",
|
||||||
|
callback: () => {
|
||||||
|
this.removeInput(info.slot);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextDynamicNode.title = "Dynamic Context (rgthree)";
|
||||||
|
ContextDynamicNode.type = "Dynamic Context (rgthree)";
|
||||||
|
ContextDynamicNode.comfyClass = "Dynamic Context (rgthree)";
|
||||||
|
class ContextDynamicSwitchNode extends ContextDynamicNodeBase {
|
||||||
|
constructor(title = ContextDynamicSwitchNode.title) {
|
||||||
|
super(title);
|
||||||
|
this.hasShadowInputs = true;
|
||||||
|
this.lastInputsList = [];
|
||||||
|
this.shadowInputs = [
|
||||||
|
{ name: "base_ctx", type: "DYNAMIC_CONTEXT" },
|
||||||
|
{ name: "+", type: "*" },
|
||||||
|
];
|
||||||
|
let alerted = false;
|
||||||
|
setInterval(() => {
|
||||||
|
const plusIndex = this.shadowInputs.findIndex((i) => i.name === "+");
|
||||||
|
if (plusIndex === -1) {
|
||||||
|
!alerted && console.error("ERROR, no plus in shadow inputs", [...this.shadowInputs]);
|
||||||
|
alerted = true;
|
||||||
|
}
|
||||||
|
else if (plusIndex !== this.shadowInputs.length - 1) {
|
||||||
|
!alerted &&
|
||||||
|
console.error("ERROR, plus is not last in shadow inputs", [...this.shadowInputs]);
|
||||||
|
alerted = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alerted && console.error("BACK TO NOREMAL", [...this.shadowInputs]);
|
||||||
|
alerted = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
static setUp(comfyClass) {
|
||||||
|
BaseContextNode.setUp(comfyClass, ContextDynamicSwitchNode);
|
||||||
|
}
|
||||||
|
clone() {
|
||||||
|
const cloned = super.clone();
|
||||||
|
while (cloned.outputs.length > 1) {
|
||||||
|
cloned.removeOutput(cloned.outputs.length - 1);
|
||||||
|
}
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
|
getContextInputsList() {
|
||||||
|
return this.shadowInputs;
|
||||||
|
}
|
||||||
|
onNodeCreated() {
|
||||||
|
}
|
||||||
|
onConnectInput(inputIndex, outputType, outputSlot, outputNode, outputIndex) {
|
||||||
|
let canConnect = true;
|
||||||
|
if (super.onConnectInput) {
|
||||||
|
canConnect = super.onConnectInput(inputIndex, outputType, outputSlot, outputNode, outputIndex);
|
||||||
|
}
|
||||||
|
const allConnectedNodes = getConnectedInputNodes(this);
|
||||||
|
if (canConnect && allConnectedNodes.includes(outputNode)) {
|
||||||
|
rgthree.showMessage({
|
||||||
|
id: "dynamic-context-looped",
|
||||||
|
type: "warn",
|
||||||
|
message: "You may not connect the same context node to a switch.",
|
||||||
|
timeout: 5000,
|
||||||
|
});
|
||||||
|
canConnect = false;
|
||||||
|
}
|
||||||
|
if (canConnect && shouldPassThrough(outputNode, PassThroughFollowing.REROUTE_ONLY)) {
|
||||||
|
const connectedNodes = getConnectedInputNodesAndFilterPassThroughs(outputNode, undefined, undefined, PassThroughFollowing.REROUTE_ONLY);
|
||||||
|
if (connectedNodes.length && allConnectedNodes.find((n) => connectedNodes.includes(n))) {
|
||||||
|
rgthree.showMessage({
|
||||||
|
id: "dynamic-context-looped",
|
||||||
|
type: "warn",
|
||||||
|
message: "You may not connect the same context node to a switch, even through a reroute.",
|
||||||
|
timeout: 5000,
|
||||||
|
});
|
||||||
|
canConnect = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return canConnect;
|
||||||
|
}
|
||||||
|
configure(info) {
|
||||||
|
super.configure(info);
|
||||||
|
setTimeout(() => {
|
||||||
|
this.shadowInputs = this.getAllShadowInputs();
|
||||||
|
this.shadowInputs.push({ name: "+", type: "*" });
|
||||||
|
this.updateLastInputsList();
|
||||||
|
console.log(this.shadowInputs);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
moveContextInput(slotFrom, slotTo) {
|
||||||
|
super.moveContextInput(slotFrom, slotTo);
|
||||||
|
this.updateLastInputsList();
|
||||||
|
}
|
||||||
|
removeContextInput(slot) {
|
||||||
|
super.removeContextInput(slot);
|
||||||
|
this.updateLastInputsList();
|
||||||
|
}
|
||||||
|
addContextInput(name, type, slot = -1) {
|
||||||
|
super.addContextInput(name, type, slot);
|
||||||
|
this.updateLastInputsList();
|
||||||
|
}
|
||||||
|
updateLastInputsList() {
|
||||||
|
this.lastInputsList = this.getAllInputsList();
|
||||||
|
}
|
||||||
|
connectSlotFromUpdateOrInput(data) {
|
||||||
|
console.log(`connectSlotFromUpdateOrInput: ${data.name}`, data);
|
||||||
|
if (data.duplicatesBefore.length) {
|
||||||
|
console.log(`[Do Nothing] It has duplicatesBefore (${data.duplicatesBefore.join(",")}).`);
|
||||||
|
this.updateLastInputsList();
|
||||||
|
}
|
||||||
|
else if (data.duplicatesAfter.length) {
|
||||||
|
const from = this.shadowInputs.findIndex((i) => i.name.toLocaleUpperCase() === data.key);
|
||||||
|
console.log(`[Move] Has duplicates after. ${from} -> ${data.shadowIndex}`);
|
||||||
|
this.moveContextInput(from, data.shadowIndex);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log(`[Add] No dupes, so we can add it at ${data.shadowIndex}.`);
|
||||||
|
this.addContextInput(data.name, data.type, data.shadowIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleInputConnected(slotIndex) {
|
||||||
|
var _a;
|
||||||
|
console.log("--- handleInputConnected", slotIndex);
|
||||||
|
const postInputsList = [...this.getAllInputsList()];
|
||||||
|
const node = (_a = postInputsList.find((i) => i.slot === slotIndex)) === null || _a === void 0 ? void 0 : _a.node;
|
||||||
|
if (!node) {
|
||||||
|
console.error("hmmm... no node foun to handle connect.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const inputsDataLists = postInputsList.filter((d) => d.slot == slotIndex && d.nodeIndex > 0 && d.type !== '*');
|
||||||
|
for (const data of inputsDataLists) {
|
||||||
|
this.connectSlotFromUpdateOrInput(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleInputDisconnected(slotIndex) {
|
||||||
|
var _a;
|
||||||
|
console.log("--- handleInputDisconnected", slotIndex);
|
||||||
|
const preInputsList = [...this.lastInputsList];
|
||||||
|
const node = (_a = preInputsList.find((i) => i.slot === slotIndex)) === null || _a === void 0 ? void 0 : _a.node;
|
||||||
|
if (!node) {
|
||||||
|
console.error("hmmmm... no node found to handle disconnect.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const postInputsList = [...this.getAllInputsList()];
|
||||||
|
const inputs = [...this.shadowInputs];
|
||||||
|
console.log("postInputsList", postInputsList);
|
||||||
|
let lastIndex = 0;
|
||||||
|
for (let [index, data] of postInputsList.entries()) {
|
||||||
|
data = this.getAllInputsList()[index];
|
||||||
|
if (data.shadowIndex === -1 || data.nodeIndex === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
lastIndex++;
|
||||||
|
const foundIndex = this.shadowInputs.findIndex((i) => i.name.toLocaleUpperCase() === data.key);
|
||||||
|
console.log(data.name, foundIndex, data.shadowIndex);
|
||||||
|
if (foundIndex !== data.shadowIndex) {
|
||||||
|
this.moveContextInput(foundIndex, data.shadowIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let index = inputs.length - 1; index > lastIndex; index--) {
|
||||||
|
const input = this.shadowInputs[index];
|
||||||
|
if (input.type !== '*') {
|
||||||
|
this.removeContextInput(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log([...this.shadowInputs]);
|
||||||
|
}
|
||||||
|
updateFromUpstream(update, node, updatedSlotData) {
|
||||||
|
var _a;
|
||||||
|
console.log("----- ContextDynamicSwitchNode :: updateFromUpstream", update, updatedSlotData);
|
||||||
|
const preInputsList = [...this.lastInputsList];
|
||||||
|
const postInputsList = [...this.getAllInputsList()];
|
||||||
|
if (shouldPassThrough(node)) {
|
||||||
|
const connectedNodes = getConnectedNodesInfo(this, IoDirection.INPUT);
|
||||||
|
const foundRerouteInfo = connectedNodes.find((n) => n.node === node);
|
||||||
|
if (update == "connect") {
|
||||||
|
this.handleInputConnected(foundRerouteInfo.originTravelFromSlot);
|
||||||
|
}
|
||||||
|
else if (update == "disconnect") {
|
||||||
|
this.handleInputDisconnected(foundRerouteInfo.originTravelFromSlot);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("Unexpected update type from pass through node: " + update);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (update) {
|
||||||
|
case "connect": {
|
||||||
|
const data = postInputsList.find((d) => {
|
||||||
|
return d.node == node && d.nodeIndex === updatedSlotData.index;
|
||||||
|
});
|
||||||
|
if (!data) {
|
||||||
|
throw new Error("Hmmm.. unfound input slot when connecting upstream.");
|
||||||
|
}
|
||||||
|
this.connectSlotFromUpdateOrInput(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "disconnect":
|
||||||
|
const preInputData = preInputsList.find((i) => {
|
||||||
|
return i.node === node && i.nodeIndex == updatedSlotData.index;
|
||||||
|
});
|
||||||
|
if (!preInputData) {
|
||||||
|
throw new Error("Hmmm... no matching input found in existing input list for disconnect.");
|
||||||
|
}
|
||||||
|
if (preInputData.duplicatesBefore.length) {
|
||||||
|
console.log(`[Do Nothing] It was already duplicated before.`);
|
||||||
|
this.updateLastInputsList();
|
||||||
|
}
|
||||||
|
else if (((_a = preInputData === null || preInputData === void 0 ? void 0 : preInputData.duplicatesAfter) === null || _a === void 0 ? void 0 : _a[0]) != null) {
|
||||||
|
console.log(`[Move after] Not duplicated before, but is after.`);
|
||||||
|
this.moveContextInput(preInputData.shadowIndex, preInputData.duplicatesAfter[0]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log(`[Remove] ${preInputData.shadowIndex}.`, preInputData);
|
||||||
|
this.removeContextInput(preInputData.shadowIndex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "move":
|
||||||
|
break;
|
||||||
|
case "update":
|
||||||
|
const index = postInputsList.findIndex((d) => {
|
||||||
|
return d.node == node && d.nodeIndex === updatedSlotData.index;
|
||||||
|
});
|
||||||
|
const pre = preInputsList[index];
|
||||||
|
const post = postInputsList[index];
|
||||||
|
console.log("preData", { ...pre });
|
||||||
|
console.log("postData", { ...post });
|
||||||
|
if (pre.shadowIndex == -1 && post.shadowIndex !== -1) {
|
||||||
|
console.log(`[Add] Old name wasn't shown, but new is.`, post.name, post.shadowIndex);
|
||||||
|
this.addContextInput(post.name, post.type, post.shadowIndex);
|
||||||
|
}
|
||||||
|
else if (pre.shadowIndex !== -1 && post.shadowIndex === -1) {
|
||||||
|
console.log(`[Remove] Old name was shown, but new isn;t.`, post.name, post.shadowIndex);
|
||||||
|
this.removeContextInput(pre.shadowIndex);
|
||||||
|
}
|
||||||
|
else if (post.shadowIndex > -1) {
|
||||||
|
console.log(`[Rename] It's shown and has a new name.`, post.name, post.shadowIndex);
|
||||||
|
this.renameContextInput(post.shadowIndex, post.name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log(`[Do Nothing] It's shown and has a new name.`, post.name, post.shadowIndex);
|
||||||
|
this.updateLastInputsList();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
console.log(this.shadowInputs);
|
||||||
|
}
|
||||||
|
getAllInputsList(indexToNodeOverride = {}) {
|
||||||
|
var _a, _b, _c;
|
||||||
|
const allConnectedInputsDataByName = {};
|
||||||
|
const allConnectedInputsData = [];
|
||||||
|
let currentShadowIndex = 0;
|
||||||
|
for (const [slot, input] of (this.inputs || []).entries()) {
|
||||||
|
const connectedNode = (_a = indexToNodeOverride[slot]) !== null && _a !== void 0 ? _a : (_b = getConnectedInputNodesAndFilterPassThroughs(this, this, slot)) === null || _b === void 0 ? void 0 : _b[0];
|
||||||
|
if (connectedNode) {
|
||||||
|
for (const inputData of connectedNode.provideInputsData()) {
|
||||||
|
const key = inputData.name.toLocaleUpperCase();
|
||||||
|
allConnectedInputsDataByName[key] = allConnectedInputsDataByName[key] || [];
|
||||||
|
const existings = allConnectedInputsDataByName[key];
|
||||||
|
let data = {
|
||||||
|
node: connectedNode,
|
||||||
|
slot,
|
||||||
|
shadowIndexFull: allConnectedInputsData.length,
|
||||||
|
shadowIndex: !existings.length ? currentShadowIndex : -1,
|
||||||
|
shadowIndexIfShownSingularly: currentShadowIndex,
|
||||||
|
nodeIndex: inputData.index,
|
||||||
|
type: inputData.type,
|
||||||
|
name: inputData.name,
|
||||||
|
key,
|
||||||
|
duplicatesBefore: allConnectedInputsDataByName[key].map((d) => d.shadowIndexFull),
|
||||||
|
duplicatesAfter: [],
|
||||||
|
};
|
||||||
|
if (data.shadowIndex > -1) {
|
||||||
|
currentShadowIndex++;
|
||||||
|
}
|
||||||
|
for (const existing of existings) {
|
||||||
|
existing.duplicatesAfter.push(data.shadowIndexFull);
|
||||||
|
}
|
||||||
|
allConnectedInputsData.push(data);
|
||||||
|
(_c = allConnectedInputsDataByName[key]) === null || _c === void 0 ? void 0 : _c.push(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allConnectedInputsData;
|
||||||
|
}
|
||||||
|
getAllShadowInputs() {
|
||||||
|
const inputsDataMap = {
|
||||||
|
BASE_CTX: { name: "base_ctx", type: "DYNAMIC_CONTEXT", index: 0 },
|
||||||
|
};
|
||||||
|
const baseNodes = getConnectedInputNodesAndFilterPassThroughs(this, this);
|
||||||
|
for (const inputNode of baseNodes) {
|
||||||
|
for (const inputData of inputNode.provideInputsData()) {
|
||||||
|
const dataKey = inputData.name.toLocaleUpperCase();
|
||||||
|
const existingData = inputsDataMap[dataKey];
|
||||||
|
if (!existingData) {
|
||||||
|
inputsDataMap[dataKey] = inputData;
|
||||||
|
}
|
||||||
|
else if (existingData.name !== inputData.name) {
|
||||||
|
throw new Error(`Conflicting data for ${dataKey}. ${existingData.name} !== ${inputData.name}`);
|
||||||
|
}
|
||||||
|
else if (existingData.type !== inputData.type) {
|
||||||
|
throw new Error(`Conflicting data for ${dataKey}. ${existingData.type} !== ${inputData.type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Object.values(inputsDataMap).map((v, index) => Object.assign({ ...v }, { index }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextDynamicSwitchNode.title = "Dynamic Context Switch (rgthree)";
|
||||||
|
ContextDynamicSwitchNode.type = "Dynamic Context Switch (rgthree)";
|
||||||
|
ContextDynamicSwitchNode.comfyClass = "Dynamic Context Switch (rgthree)";
|
||||||
|
const contextDynamicNodes = [ContextDynamicNode, ContextDynamicSwitchNode];
|
||||||
|
app.registerExtension({
|
||||||
|
name: "rgthree.ContextDynamic",
|
||||||
|
async beforeRegisterNodeDef(nodeType, nodeData) {
|
||||||
|
for (const ctxClass of contextDynamicNodes) {
|
||||||
|
if (nodeData.name === ctxClass.type) {
|
||||||
|
console.log(nodeData.name);
|
||||||
|
ctxClass.nodeData = nodeData;
|
||||||
|
ctxClass.nodeType = nodeType;
|
||||||
|
ctxClass.setUp(nodeType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
+21
-20
@@ -2,7 +2,7 @@ var _a;
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
import { rgthreeConfig } from "./rgthree_config.js";
|
import { rgthreeConfig } from "./rgthree_config.js";
|
||||||
import { rgthree } from "./rgthree.js";
|
import { rgthree } from "./rgthree.js";
|
||||||
import { LAYOUT_CLOCKWISE, LAYOUT_LABEL_OPPOSITES, LAYOUT_LABEL_TO_DATA, addConnectionLayoutSupport, addMenuItem, getSlotLinks, isValidConnection, } from "./utils.js";
|
import { LAYOUT_CLOCKWISE, LAYOUT_LABEL_OPPOSITES, LAYOUT_LABEL_TO_DATA, addConnectionLayoutSupport, addMenuItem, getConnectedOutputNodesAndFilterPassThroughs, getSlotLinks, isValidConnection, } from "./utils.js";
|
||||||
import { wait } from "./shared_utils.js";
|
import { wait } from "./shared_utils.js";
|
||||||
const rerouteConfig = ((_a = rgthreeConfig === null || rgthreeConfig === void 0 ? void 0 : rgthreeConfig['nodes']) === null || _a === void 0 ? void 0 : _a['reroute']) || {};
|
const rerouteConfig = ((_a = rgthreeConfig === null || rgthreeConfig === void 0 ? void 0 : rgthreeConfig['nodes']) === null || _a === void 0 ? void 0 : _a['reroute']) || {};
|
||||||
let configWidth = Math.max(Math.round((Number(rerouteConfig['default_width']) || 40) / 10) * 10, 10);
|
let configWidth = Math.max(Math.round((Number(rerouteConfig['default_width']) || 40) / 10) * 10, 10);
|
||||||
@@ -31,7 +31,6 @@ app.registerExtension({
|
|||||||
var _a;
|
var _a;
|
||||||
super(title);
|
super(title);
|
||||||
this.configuring = true;
|
this.configuring = true;
|
||||||
this.schedulePromise = null;
|
|
||||||
this.defaultConnectionsLayout = configLayout;
|
this.defaultConnectionsLayout = configLayout;
|
||||||
this.isVirtualNode = true;
|
this.isVirtualNode = true;
|
||||||
this.hideSlotLabels = true;
|
this.hideSlotLabels = true;
|
||||||
@@ -75,7 +74,20 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.scheduleStabilize();
|
if (this.configuring) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.stabilize();
|
||||||
|
if (type === LiteGraph.INPUT) {
|
||||||
|
this.updateDownstream(connected ? 'connect' : 'disconnect', { index: 0, name: this.inputs[0].name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateDownstream(update, updatedIndexes) {
|
||||||
|
var _a;
|
||||||
|
const nodes = getConnectedOutputNodesAndFilterPassThroughs(this, this, 0);
|
||||||
|
for (const node of nodes) {
|
||||||
|
(_a = node === null || node === void 0 ? void 0 : node.updateFromUpstream) === null || _a === void 0 ? void 0 : _a.call(node, update, this, updatedIndexes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onDrawForeground(ctx, canvas) {
|
onDrawForeground(ctx, canvas) {
|
||||||
var _a, _b, _c;
|
var _a, _b, _c;
|
||||||
@@ -97,20 +109,8 @@ app.registerExtension({
|
|||||||
disconnectOutput(slot, targetNode) {
|
disconnectOutput(slot, targetNode) {
|
||||||
return super.disconnectOutput(slot, targetNode);
|
return super.disconnectOutput(slot, targetNode);
|
||||||
}
|
}
|
||||||
scheduleStabilize(ms = 64) {
|
|
||||||
if (!this.schedulePromise) {
|
|
||||||
this.schedulePromise = new Promise((resolve) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.schedulePromise = null;
|
|
||||||
this.stabilize();
|
|
||||||
resolve();
|
|
||||||
}, ms);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return this.schedulePromise;
|
|
||||||
}
|
|
||||||
stabilize() {
|
stabilize() {
|
||||||
var _a, _b, _c, _d, _e, _f, _g, _h;
|
var _a, _b, _c, _d, _e, _f;
|
||||||
if (this.configuring) {
|
if (this.configuring) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -155,7 +155,7 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const nodes = [this];
|
const nodes = [this];
|
||||||
let outputNode = null;
|
let outputNodes = [];
|
||||||
let outputType = null;
|
let outputType = null;
|
||||||
while (nodes.length) {
|
while (nodes.length) {
|
||||||
currentNode = nodes.pop();
|
currentNode = nodes.pop();
|
||||||
@@ -187,8 +187,11 @@ app.registerExtension({
|
|||||||
node.disconnectInput(link.target_slot);
|
node.disconnectInput(link.target_slot);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (outputType != null && outputType !== nodeOutType) {
|
||||||
|
console.warn(`[rgthree] Reroute - Mismatching output types..`);
|
||||||
|
}
|
||||||
outputType = nodeOutType;
|
outputType = nodeOutType;
|
||||||
outputNode = node;
|
outputNodes.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,8 +223,6 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_g = inputNode === null || inputNode === void 0 ? void 0 : inputNode.onConnectionsChainChange) === null || _g === void 0 ? void 0 : _g.call(inputNode);
|
|
||||||
(_h = outputNode === null || outputNode === void 0 ? void 0 : outputNode.onConnectionsChainChange) === null || _h === void 0 ? void 0 : _h.call(outputNode);
|
|
||||||
app.graph.setDirtyCanvas(true, true);
|
app.graph.setDirtyCanvas(true, true);
|
||||||
}
|
}
|
||||||
computeSize(out) {
|
computeSize(out) {
|
||||||
|
|||||||
+2
-2
@@ -245,8 +245,8 @@ class Rgthree {
|
|||||||
fireEvent(event, data) {
|
fireEvent(event, data) {
|
||||||
if (this.eventsToFns.has(event)) {
|
if (this.eventsToFns.has(event)) {
|
||||||
for (let fn of this.eventsToFns.get(event)) {
|
for (let fn of this.eventsToFns.get(event)) {
|
||||||
const event = new Event(data);
|
const e = new CustomEvent(event, { detail: data });
|
||||||
fn(event);
|
fn(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-20
@@ -275,46 +275,53 @@ export function filterOutPassthroughNodes(nodes, passThroughFollowing = PassThro
|
|||||||
return nodes.filter((n) => !shouldPassThrough(n, passThroughFollowing));
|
return nodes.filter((n) => !shouldPassThrough(n, passThroughFollowing));
|
||||||
}
|
}
|
||||||
export function getConnectedInputNodes(startNode, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) {
|
export function getConnectedInputNodes(startNode, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) {
|
||||||
return getConnectedNodes(startNode, IoDirection.INPUT, currentNode, slot, passThroughFollowing).map(n => n.node);
|
return getConnectedNodesInfo(startNode, IoDirection.INPUT, currentNode, slot, passThroughFollowing).map(n => n.node);
|
||||||
}
|
}
|
||||||
export function getConnectedInputNodesAndFilterPassThroughs(startNode, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) {
|
export function getConnectedInputNodesAndFilterPassThroughs(startNode, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) {
|
||||||
return filterOutPassthroughNodes(getConnectedInputNodes(startNode, currentNode, slot, passThroughFollowing), passThroughFollowing);
|
return filterOutPassthroughNodes(getConnectedInputNodes(startNode, currentNode, slot, passThroughFollowing), passThroughFollowing);
|
||||||
}
|
}
|
||||||
export function getConnectedOutputNodes(startNode, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) {
|
export function getConnectedOutputNodes(startNode, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) {
|
||||||
return getConnectedNodes(startNode, IoDirection.OUTPUT, currentNode, slot, passThroughFollowing).map(n => n.node);
|
return getConnectedNodesInfo(startNode, IoDirection.OUTPUT, currentNode, slot, passThroughFollowing).map(n => n.node);
|
||||||
}
|
}
|
||||||
export function getConnectedOutputNodesAndFilterPassThroughs(startNode, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) {
|
export function getConnectedOutputNodesAndFilterPassThroughs(startNode, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) {
|
||||||
return filterOutPassthroughNodes(getConnectedOutputNodes(startNode, currentNode, slot, passThroughFollowing), passThroughFollowing);
|
return filterOutPassthroughNodes(getConnectedOutputNodes(startNode, currentNode, slot, passThroughFollowing), passThroughFollowing);
|
||||||
}
|
}
|
||||||
export function getConnectedNodes(startNode, dir = IoDirection.INPUT, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) {
|
export function getConnectedNodesInfo(startNode, dir = IoDirection.INPUT, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL, originTravelFromSlot) {
|
||||||
var _a, _b;
|
var _a, _b, _c, _d, _e, _f;
|
||||||
currentNode = currentNode || startNode;
|
currentNode = currentNode || startNode;
|
||||||
let rootNodes = [];
|
let rootNodes = [];
|
||||||
const slotsToRemove = [];
|
const slotsToRemove = [];
|
||||||
if (startNode === currentNode || shouldPassThrough(currentNode, passThroughFollowing)) {
|
if (startNode === currentNode || shouldPassThrough(currentNode, passThroughFollowing)) {
|
||||||
let linkIds;
|
let linkIds;
|
||||||
if (dir == IoDirection.OUTPUT) {
|
if (dir == IoDirection.OUTPUT) {
|
||||||
linkIds = ((_a = currentNode.outputs) === null || _a === void 0 ? void 0 : _a.flatMap((i) => i.links)) || [];
|
if (slot != null && slot > -1) {
|
||||||
}
|
linkIds = (_b = (_a = currentNode.outputs[slot]) === null || _a === void 0 ? void 0 : _a.links) !== null && _b !== void 0 ? _b : [];
|
||||||
else {
|
|
||||||
linkIds = ((_b = currentNode.inputs) === null || _b === void 0 ? void 0 : _b.map((i) => i.link)) || [];
|
|
||||||
}
|
|
||||||
if (typeof slot == "number" && slot > -1) {
|
|
||||||
if (linkIds[slot]) {
|
|
||||||
linkIds = [linkIds[slot]];
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return [];
|
linkIds = ((_c = currentNode.outputs) === null || _c === void 0 ? void 0 : _c.flatMap((i) => i.links)) || [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (slot != null && slot > -1) {
|
||||||
|
linkIds = [(_e = (_d = currentNode.inputs[slot]) === null || _d === void 0 ? void 0 : _d.link) !== null && _e !== void 0 ? _e : -1].filter(i => i > -1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
linkIds = ((_f = currentNode.inputs) === null || _f === void 0 ? void 0 : _f.map((i) => i.link)) || [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let graph = app.graph;
|
let graph = app.graph;
|
||||||
for (const linkId of linkIds) {
|
for (const linkId of linkIds) {
|
||||||
const link = (linkId != null && graph.links[linkId]);
|
let link = null;
|
||||||
|
if (typeof linkId == "number") {
|
||||||
|
link = graph.links[linkId];
|
||||||
|
}
|
||||||
if (!link) {
|
if (!link) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const travelFromSlot = dir == IoDirection.OUTPUT ? link.origin_slot : link.target_slot;
|
||||||
const connectedId = dir == IoDirection.OUTPUT ? link.target_id : link.origin_id;
|
const connectedId = dir == IoDirection.OUTPUT ? link.target_id : link.origin_id;
|
||||||
const originSlot = dir == IoDirection.OUTPUT ? link.target_slot : link.origin_slot;
|
const travelToSlot = dir == IoDirection.OUTPUT ? link.target_slot : link.origin_slot;
|
||||||
|
originTravelFromSlot = originTravelFromSlot != null ? originTravelFromSlot : travelFromSlot;
|
||||||
const originNode = graph.getNodeById(connectedId);
|
const originNode = graph.getNodeById(connectedId);
|
||||||
if (!link) {
|
if (!link) {
|
||||||
console.error("No connected node found... weird");
|
console.error("No connected node found... weird");
|
||||||
@@ -324,10 +331,10 @@ export function getConnectedNodes(startNode, dir = IoDirection.INPUT, currentNod
|
|||||||
console.log(`${startNode.title} (${startNode.id}) seems to have two links to ${originNode.title} (${originNode.id}). One may be stale: ${linkIds.join(", ")}`);
|
console.log(`${startNode.title} (${startNode.id}) seems to have two links to ${originNode.title} (${originNode.id}). One may be stale: ${linkIds.join(", ")}`);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rootNodes.push({ node: originNode, slot: originSlot });
|
rootNodes.push({ node: originNode, travelFromSlot, travelToSlot, originTravelFromSlot });
|
||||||
if (shouldPassThrough(originNode, passThroughFollowing)) {
|
if (shouldPassThrough(originNode, passThroughFollowing)) {
|
||||||
for (const foundNode of getConnectedNodes(startNode, dir, originNode)) {
|
for (const foundNode of getConnectedNodesInfo(startNode, dir, originNode, undefined, undefined, originTravelFromSlot)) {
|
||||||
if (!rootNodes.includes(foundNode)) {
|
if (!rootNodes.map(n => n.node).includes(foundNode.node)) {
|
||||||
rootNodes.push(foundNode);
|
rootNodes.push(foundNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,7 +370,7 @@ function getTypeFromSlot(slot, dir, skipSelf = false) {
|
|||||||
let graph = app.graph;
|
let graph = app.graph;
|
||||||
let type = slot === null || slot === void 0 ? void 0 : slot.type;
|
let type = slot === null || slot === void 0 ? void 0 : slot.type;
|
||||||
if (!skipSelf && type != null && type != "*") {
|
if (!skipSelf && type != null && type != "*") {
|
||||||
return { type: type, label: (slot === null || slot === void 0 ? void 0 : slot.label) || (slot === null || slot === void 0 ? void 0 : slot.name) };
|
return { type: type, label: slot === null || slot === void 0 ? void 0 : slot.label, name: slot === null || slot === void 0 ? void 0 : slot.name };
|
||||||
}
|
}
|
||||||
const links = getSlotLinks(slot);
|
const links = getSlotLinks(slot);
|
||||||
for (const link of links) {
|
for (const link of links) {
|
||||||
@@ -375,7 +382,8 @@ function getTypeFromSlot(slot, dir, skipSelf = false) {
|
|||||||
if ((connectedSlot === null || connectedSlot === void 0 ? void 0 : connectedSlot.type) != null && (connectedSlot === null || connectedSlot === void 0 ? void 0 : connectedSlot.type) != "*") {
|
if ((connectedSlot === null || connectedSlot === void 0 ? void 0 : connectedSlot.type) != null && (connectedSlot === null || connectedSlot === void 0 ? void 0 : connectedSlot.type) != "*") {
|
||||||
return {
|
return {
|
||||||
type: connectedSlot.type,
|
type: connectedSlot.type,
|
||||||
label: (connectedSlot === null || connectedSlot === void 0 ? void 0 : connectedSlot.label) || (connectedSlot === null || connectedSlot === void 0 ? void 0 : connectedSlot.name),
|
label: connectedSlot === null || connectedSlot === void 0 ? void 0 : connectedSlot.label,
|
||||||
|
name: connectedSlot === null || connectedSlot === void 0 ? void 0 : connectedSlot.name
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if ((connectedSlot === null || connectedSlot === void 0 ? void 0 : connectedSlot.type) == "*") {
|
else if ((connectedSlot === null || connectedSlot === void 0 ? void 0 : connectedSlot.type) == "*") {
|
||||||
|
|||||||
Reference in New Issue
Block a user