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.image_inset_crop import RgthreeImageInsetCrop
|
||||
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.sdxl_power_prompt_postive import RgthreeSDXLPowerPromptPositive
|
||||
from .py.sdxl_power_prompt_simple import RgthreeSDXLPowerPromptSimple
|
||||
from .py.any_switch import RgthreeAnySwitch
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
RgthreeDynamicContext.NAME: RgthreeDynamicContext,
|
||||
RgthreeContextDynamicSwitch.NAME: RgthreeContextDynamicSwitch,
|
||||
RgthreeBigContext.NAME: RgthreeBigContext,
|
||||
RgthreeContext.NAME: RgthreeContext,
|
||||
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_info(NODE_NAME, f'{len(loras)} Loras processed; stripping tags for TEXT output.')
|
||||
elif '<lora:' in prompt:
|
||||
_prompt_stripped, loras = get_and_strip_loras(prompt, log_node=NODE_NAME, silent=True)
|
||||
if len(loras):
|
||||
_prompt_stripped, loras, skipped, unfound = get_and_strip_loras(prompt, log_node=NODE_NAME, silent=True)
|
||||
total_loras = len(loras) + len(skipped) + len(unfound)
|
||||
if total_loras:
|
||||
log_node_warn(
|
||||
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.')
|
||||
|
||||
@@ -16,14 +16,18 @@ def get_and_strip_loras(prompt, silent=False, log_node="Power Prompt"):
|
||||
matches = re.findall(pattern, prompt)
|
||||
|
||||
loras = []
|
||||
unfound_loras = []
|
||||
skipped_loras = []
|
||||
for match in matches:
|
||||
tag_path = match[0]
|
||||
tag_path_no_ext = os.path.splitext(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)
|
||||
if strength == 0 and not silent:
|
||||
log_node_info(log_node, f'Skipping "{tag_path}" with strength of zero')
|
||||
if strength == 0:
|
||||
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
|
||||
|
||||
if tag_path not in lora_paths:
|
||||
@@ -49,8 +53,9 @@ def get_and_strip_loras(prompt, silent=False, log_node="Power Prompt"):
|
||||
else:
|
||||
if not silent:
|
||||
log_node_warn(log_node, f'Lora "{tag_path}" not found, skipping.')
|
||||
unfound_loras.append({'lora': tag_path, 'strength': strength})
|
||||
continue
|
||||
|
||||
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):
|
||||
|
||||
if insert_lora == 'DISABLE LORAS':
|
||||
prompt_g, loras_g = get_and_strip_loras(prompt_g, True)
|
||||
prompt_l, loras_l = get_and_strip_loras(prompt_l, True)
|
||||
prompt_g, loras_g, skipped_g, unfound_g = get_and_strip_loras(prompt_g, True)
|
||||
prompt_l, loras_l, skipped_l, unfound_l = get_and_strip_loras(prompt_l, True)
|
||||
loras = loras_g + loras_l
|
||||
log_node_info(
|
||||
NODE_NAME,
|
||||
f'Disabling all found loras ({len(loras)}) and stripping lora tags for TEXT output.')
|
||||
elif opt_model != None and opt_clip != None:
|
||||
prompt_g, loras_g = get_and_strip_loras(prompt_g)
|
||||
prompt_l, loras_l = get_and_strip_loras(prompt_l)
|
||||
prompt_g, loras_g, skipped_g, unfound_g = get_and_strip_loras(prompt_g)
|
||||
prompt_l, loras_l, skipped_l, unfound_l = get_and_strip_loras(prompt_l)
|
||||
loras = loras_g + loras_l
|
||||
if len(loras):
|
||||
for lora in loras:
|
||||
@@ -122,13 +122,16 @@ class RgthreeSDXLPowerPromptPositive:
|
||||
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.')
|
||||
elif '<lora:' in prompt_g or '<lora:' in prompt_l:
|
||||
_prompt_stripped_g, loras_g = get_and_strip_loras(prompt_g, True)
|
||||
_prompt_stripped_l, loras_l = get_and_strip_loras(prompt_l, True)
|
||||
loras = loras_g + loras_l
|
||||
if len(loras):
|
||||
log_node_warn(
|
||||
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.')
|
||||
_prompt_stripped_g, loras_g, skipped_g, unfound_g = get_and_strip_loras(prompt_g, True)
|
||||
_prompt_stripped_l, loras_l, skipped_l, unfound_l = get_and_strip_loras(prompt_l, True)
|
||||
loras = len(loras_g) + len(loras_l)
|
||||
unfound_loras = len(unfound_g) + len(unfound_l)
|
||||
total_loras = loras + unfound_loras + len(skipped_g) + len(skipped_l)
|
||||
if total_loras:
|
||||
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,
|
||||
opt_clip_height, target_width, target_height, crop_width,
|
||||
|
||||
+11
-1
@@ -1,4 +1,3 @@
|
||||
|
||||
class AnyType(str):
|
||||
"""A special class that is always equal in not equal comparisons. Credit to pythongosssss"""
|
||||
|
||||
@@ -7,3 +6,14 @@ class AnyType(str):
|
||||
|
||||
|
||||
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.type === 'RGTHREE_CONTEXT' ? 'CONTEXT' :
|
||||
Array.isArray(this.nodeType) || this.nodeType.includes(",")
|
||||
? connectedType?.label || String(this.nodeType)
|
||||
? connectedType?.label || connectedType?.name || 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_: NodeMode;
|
||||
|
||||
configuring = false;
|
||||
|
||||
isVirtualNode = false;
|
||||
|
||||
constructor(title = RgthreeBaseNode.title) {
|
||||
@@ -45,12 +47,14 @@ export class RgthreeBaseNode extends LGraphNode {
|
||||
}
|
||||
|
||||
override configure(info: SerializedLGraphNode<TLGraphNode>): void {
|
||||
this.configuring = true;
|
||||
super.configure(info);
|
||||
// Fix https://github.com/comfyanonymous/ComfyUI/issues/1448 locally.
|
||||
// Can removed when fixed and adopted.
|
||||
for (const w of (this.widgets || [])) {
|
||||
w.last_y = w.last_y || 0;
|
||||
}
|
||||
this.configuring = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+34
-12
@@ -2,8 +2,6 @@
|
||||
import type {
|
||||
INodeInputSlot,
|
||||
INodeOutputSlot,
|
||||
LGraph,
|
||||
LLink,
|
||||
LiteGraph as TLiteGraph,
|
||||
LGraphNode as TLGraphNode,
|
||||
} from "./typings/litegraph.js";
|
||||
@@ -14,7 +12,6 @@ import {
|
||||
IoDirection,
|
||||
addConnectionLayoutSupport,
|
||||
addMenuItem,
|
||||
applyMixins,
|
||||
matchLocalSlotsToServer,
|
||||
replaceNode,
|
||||
} from "./utils.js";
|
||||
@@ -36,10 +33,12 @@ function findMatchingIndexByTypeOrName(otherNode: TLGraphNode, otherSlot: INodeI
|
||||
otherSlotType = 'COMBO';
|
||||
}
|
||||
const otherSlotName = otherSlot.name.toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||
const otherSlotLabel = (otherSlot.label || otherSlotName).toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||
let ctxSlotIndex = -1;
|
||||
if (["CONDITIONING", "INT", "STRING", "FLOAT", "COMBO"].includes(otherSlotType)) {
|
||||
ctxSlotIndex = ctxSlots.findIndex((ctxSlot) => {
|
||||
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;
|
||||
if (Array.isArray(ctxSlotType) || ctxSlotType.includes(',')) {
|
||||
ctxSlotType = 'COMBO';
|
||||
@@ -49,6 +48,7 @@ function findMatchingIndexByTypeOrName(otherNode: TLGraphNode, otherSlot: INodeI
|
||||
}
|
||||
// Straightforward matches.
|
||||
if(ctxSlotName === otherSlotName
|
||||
|| (ctxSlotLabel && otherSlotLabel && ctxSlotLabel == otherSlotLabel)
|
||||
|| (ctxSlotName === "SEED" && otherSlotName.includes("SEED"))
|
||||
|| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("AT_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.
|
||||
*/
|
||||
class BaseContextNode extends RgthreeBaseServerNode {
|
||||
export class BaseContextNode extends RgthreeBaseServerNode {
|
||||
constructor(title: string) {
|
||||
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 contextTypeToServerDef: { [type: string]: ComfyObjectInfo } = {};
|
||||
|
||||
@@ -296,10 +314,12 @@ app.registerExtension({
|
||||
const serverDef = type && contextTypeToServerDef[type];
|
||||
if (serverDef) {
|
||||
fixBadConfigs(node as ContextNode);
|
||||
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
||||
// Switches don't need to change inputs, only context outputs
|
||||
if (!type!.includes("Switch")) {
|
||||
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
||||
if (!type!.includes('Dynamic')) {
|
||||
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
||||
// Switches don't need to change inputs, only context outputs
|
||||
if (!type!.includes("Switch")) {
|
||||
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
||||
}
|
||||
}
|
||||
// }, 100);
|
||||
}
|
||||
@@ -314,10 +334,12 @@ app.registerExtension({
|
||||
const serverDef = type && contextTypeToServerDef[type];
|
||||
if (serverDef) {
|
||||
fixBadConfigs(node as ContextNode);
|
||||
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
||||
// Switches don't need to change inputs, only context outputs
|
||||
if (!type!.includes("Switch")) {
|
||||
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
||||
if (!type!.includes('Dynamic')) {
|
||||
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
||||
// Switches don't need to change inputs, only context outputs
|
||||
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,
|
||||
addConnectionLayoutSupport,
|
||||
addMenuItem,
|
||||
getConnectedOutputNodesAndFilterPassThroughs,
|
||||
getSlotLinks,
|
||||
isValidConnection,
|
||||
} from "./utils.js";
|
||||
@@ -71,7 +72,6 @@ app.registerExtension({
|
||||
readonly hideSlotLabels: boolean;
|
||||
|
||||
private configuring = true;
|
||||
private schedulePromise: Promise<void> | null = null;
|
||||
|
||||
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 {
|
||||
@@ -161,20 +180,6 @@ app.registerExtension({
|
||||
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() {
|
||||
// If we are currently "configuring" then skip this stabilization. The connected nodes may
|
||||
// not yet be configured.
|
||||
@@ -229,7 +234,7 @@ app.registerExtension({
|
||||
|
||||
// Find all outputs
|
||||
const nodes: TLGraphNode[] = [this];
|
||||
let outputNode = null;
|
||||
let outputNodes: TLGraphNode[] = [];
|
||||
let outputType = null;
|
||||
while (nodes.length) {
|
||||
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)})`);
|
||||
node.disconnectInput(link.target_slot);
|
||||
} else {
|
||||
if (outputType != null && outputType !== nodeOutType) {
|
||||
console.warn(`[rgthree] Reroute - Mismatching output types..`);
|
||||
}
|
||||
outputType = nodeOutType;
|
||||
outputNode = node;
|
||||
outputNodes.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,8 +314,10 @@ app.registerExtension({
|
||||
}
|
||||
}
|
||||
}
|
||||
(inputNode as any)?.onConnectionsChainChange?.();
|
||||
(outputNode as any)?.onConnectionsChainChange?.();
|
||||
// (inputNode as any)?.onConnectionsChainChange?.(this);
|
||||
// for (const outputNode of outputNodes) {
|
||||
// (outputNode as any)?.onConnectionsChainChange?.(this);
|
||||
// }
|
||||
app.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -77,7 +77,7 @@ class LogSession {
|
||||
export type RgthreeUiMessage = {
|
||||
id: string;
|
||||
message: string;
|
||||
type?: 'warn'|null;
|
||||
type?: 'warn'|'info'|null;
|
||||
timeout?: number;
|
||||
// closeable?: boolean; // TODO
|
||||
actions?: Array<{
|
||||
@@ -305,8 +305,8 @@ class Rgthree {
|
||||
fireEvent(event: string, data: any) {
|
||||
if (this.eventsToFns.has(event)) {
|
||||
for (let fn of this.eventsToFns.get(event)!) {
|
||||
const event = new Event(data);
|
||||
fn(event);
|
||||
const e = new CustomEvent(event, {detail:data});
|
||||
fn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+4
-1
@@ -38,6 +38,8 @@ export interface INodeSlot {
|
||||
hidden?: boolean;
|
||||
// @rgthree
|
||||
disabled?: boolean;
|
||||
// @rgthree
|
||||
removable?: boolean;
|
||||
}
|
||||
|
||||
export interface INodeInputSlot extends INodeSlot {
|
||||
@@ -1087,7 +1089,8 @@ export declare class LGraphNode {
|
||||
|
||||
/** Called by `LGraphCanvas.processContextMenu` */
|
||||
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;
|
||||
}
|
||||
|
||||
+34
-22
@@ -400,7 +400,7 @@ export function getConnectedInputNodes(
|
||||
slot?: number,
|
||||
passThroughFollowing = PassThroughFollowing.ALL,
|
||||
) : 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(
|
||||
startNode: TLGraphNode,
|
||||
@@ -413,14 +413,16 @@ export function getConnectedInputNodesAndFilterPassThroughs(
|
||||
passThroughFollowing,
|
||||
);
|
||||
}
|
||||
|
||||
export function getConnectedOutputNodes(
|
||||
startNode: TLGraphNode,
|
||||
currentNode?: TLGraphNode,
|
||||
slot?: number,
|
||||
passThroughFollowing = PassThroughFollowing.ALL,
|
||||
) : 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(
|
||||
startNode: 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,
|
||||
dir = IoDirection.INPUT,
|
||||
currentNode?: TLGraphNode,
|
||||
slot?: number,
|
||||
passThroughFollowing = PassThroughFollowing.ALL,
|
||||
) : {node:TLGraphNode, slot: number}[] {
|
||||
originTravelFromSlot?: number
|
||||
) : ConnectedNodeInfo[] {
|
||||
currentNode = currentNode || startNode;
|
||||
let rootNodes: {node:TLGraphNode, slot: number}[] = [];
|
||||
let rootNodes: ConnectedNodeInfo[] = [];
|
||||
const slotsToRemove = [];
|
||||
if (startNode === currentNode || shouldPassThrough(currentNode, passThroughFollowing)) {
|
||||
// const removeDups = startNode === currentNode;
|
||||
let linkIds: Array<number | null>;
|
||||
|
||||
if (dir == IoDirection.OUTPUT) {
|
||||
linkIds = currentNode.outputs?.flatMap((i) => i.links) || [];
|
||||
} else {
|
||||
linkIds = currentNode.inputs?.map((i) => i.link) || [];
|
||||
}
|
||||
if (typeof slot == "number" && slot > -1) {
|
||||
if (linkIds[slot]) {
|
||||
linkIds = [linkIds[slot]!];
|
||||
if (slot != null && slot > -1) {
|
||||
linkIds = currentNode.outputs[slot]?.links ?? [];
|
||||
} 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;
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
const travelFromSlot = dir == IoDirection.OUTPUT ? link.origin_slot : link.target_slot;
|
||||
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)!;
|
||||
if (!link) {
|
||||
console.error("No connected node found... weird");
|
||||
@@ -480,10 +491,10 @@ export function getConnectedNodes(
|
||||
);
|
||||
} else {
|
||||
// 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)) {
|
||||
for (const foundNode of getConnectedNodes(startNode, dir, originNode)) {
|
||||
if (!rootNodes.includes(foundNode)) {
|
||||
for (const foundNode of getConnectedNodesInfo(startNode, dir, originNode, undefined, undefined, originTravelFromSlot)) {
|
||||
if (!rootNodes.map(n => n.node).includes(foundNode.node)) {
|
||||
rootNodes.push(foundNode);
|
||||
}
|
||||
}
|
||||
@@ -494,7 +505,7 @@ export function getConnectedNodes(
|
||||
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.
|
||||
@@ -539,7 +550,7 @@ function getTypeFromSlot(
|
||||
let graph = app.graph as LGraph;
|
||||
let type = slot?.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);
|
||||
for (const link of links) {
|
||||
@@ -554,7 +565,8 @@ function getTypeFromSlot(
|
||||
if (connectedSlot?.type != null && connectedSlot?.type != "*") {
|
||||
return {
|
||||
type: connectedSlot.type as string,
|
||||
label: connectedSlot?.label || connectedSlot?.name,
|
||||
label: connectedSlot?.label,
|
||||
name: connectedSlot?.name
|
||||
};
|
||||
} else if (connectedSlot?.type == "*") {
|
||||
return followConnectionUntilType(connectedNode, dir);
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ class AnySwitchforMixin extends RgthreeBaseNode {
|
||||
output.label =
|
||||
output.type === 'RGTHREE_CONTEXT' ? 'CONTEXT' :
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ export class RgthreeBaseNode extends LGraphNode {
|
||||
constructor(title = RgthreeBaseNode.title) {
|
||||
super(title);
|
||||
this._tempWidth = 0;
|
||||
this.configuring = false;
|
||||
this.isVirtualNode = false;
|
||||
if (title == '__NEED_NAME__') {
|
||||
throw new Error('RgthreeBaseNode needs overrides.');
|
||||
@@ -12,10 +13,12 @@ export class RgthreeBaseNode extends LGraphNode {
|
||||
this.properties = this.properties || {};
|
||||
}
|
||||
configure(info) {
|
||||
this.configuring = true;
|
||||
super.configure(info);
|
||||
for (const w of (this.widgets || [])) {
|
||||
w.last_y = w.last_y || 0;
|
||||
}
|
||||
this.configuring = false;
|
||||
}
|
||||
set mode(mode) {
|
||||
if (this.mode_ != mode) {
|
||||
|
||||
+30
-7
@@ -10,10 +10,12 @@ function findMatchingIndexByTypeOrName(otherNode, otherSlot, ctxSlots) {
|
||||
otherSlotType = 'COMBO';
|
||||
}
|
||||
const otherSlotName = otherSlot.name.toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||
const otherSlotLabel = (otherSlot.label || otherSlotName).toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||
let ctxSlotIndex = -1;
|
||||
if (["CONDITIONING", "INT", "STRING", "FLOAT", "COMBO"].includes(otherSlotType)) {
|
||||
ctxSlotIndex = ctxSlots.findIndex((ctxSlot) => {
|
||||
const ctxSlotName = ctxSlot.name.toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||
const ctxSlotLabel = (ctxSlot.label || ctxSlotName).toUpperCase().replace('OPT_', '').replace('_NAME', '');
|
||||
let ctxSlotType = ctxSlot.type;
|
||||
if (Array.isArray(ctxSlotType) || ctxSlotType.includes(',')) {
|
||||
ctxSlotType = 'COMBO';
|
||||
@@ -22,6 +24,7 @@ function findMatchingIndexByTypeOrName(otherNode, otherSlot, ctxSlots) {
|
||||
return false;
|
||||
}
|
||||
if (ctxSlotName === otherSlotName
|
||||
|| (ctxSlotLabel && otherSlotLabel && ctxSlotLabel == otherSlotLabel)
|
||||
|| (ctxSlotName === "SEED" && otherSlotName.includes("SEED"))
|
||||
|| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("AT_STEP"))
|
||||
|| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("REFINER_STEP"))) {
|
||||
@@ -47,7 +50,7 @@ function findMatchingIndexByTypeOrName(otherNode, otherSlot, ctxSlots) {
|
||||
}
|
||||
return ctxSlotIndex;
|
||||
}
|
||||
class BaseContextNode extends RgthreeBaseServerNode {
|
||||
export class BaseContextNode extends RgthreeBaseServerNode {
|
||||
constructor(title) {
|
||||
super(title);
|
||||
}
|
||||
@@ -171,6 +174,22 @@ class ContextSwitchBigNode extends BaseContextNode {
|
||||
ContextSwitchBigNode.title = "Context Switch Big (rgthree)";
|
||||
ContextSwitchBigNode.type = "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 contextTypeToServerDef = {};
|
||||
function fixBadConfigs(node) {
|
||||
@@ -199,9 +218,11 @@ app.registerExtension({
|
||||
const serverDef = type && contextTypeToServerDef[type];
|
||||
if (serverDef) {
|
||||
fixBadConfigs(node);
|
||||
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
||||
if (!type.includes("Switch")) {
|
||||
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
||||
if (!type.includes('Dynamic')) {
|
||||
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
||||
if (!type.includes("Switch")) {
|
||||
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -210,9 +231,11 @@ app.registerExtension({
|
||||
const serverDef = type && contextTypeToServerDef[type];
|
||||
if (serverDef) {
|
||||
fixBadConfigs(node);
|
||||
matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef);
|
||||
if (!type.includes("Switch")) {
|
||||
matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef);
|
||||
if (!type.includes('Dynamic')) {
|
||||
matchLocalSlotsToServer(node, IoDirection.OUTPUT, 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 { rgthreeConfig } from "./rgthree_config.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";
|
||||
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);
|
||||
@@ -31,7 +31,6 @@ app.registerExtension({
|
||||
var _a;
|
||||
super(title);
|
||||
this.configuring = true;
|
||||
this.schedulePromise = null;
|
||||
this.defaultConnectionsLayout = configLayout;
|
||||
this.isVirtualNode = 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) {
|
||||
var _a, _b, _c;
|
||||
@@ -97,20 +109,8 @@ app.registerExtension({
|
||||
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() {
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h;
|
||||
var _a, _b, _c, _d, _e, _f;
|
||||
if (this.configuring) {
|
||||
return;
|
||||
}
|
||||
@@ -155,7 +155,7 @@ app.registerExtension({
|
||||
}
|
||||
}
|
||||
const nodes = [this];
|
||||
let outputNode = null;
|
||||
let outputNodes = [];
|
||||
let outputType = null;
|
||||
while (nodes.length) {
|
||||
currentNode = nodes.pop();
|
||||
@@ -187,8 +187,11 @@ app.registerExtension({
|
||||
node.disconnectInput(link.target_slot);
|
||||
}
|
||||
else {
|
||||
if (outputType != null && outputType !== nodeOutType) {
|
||||
console.warn(`[rgthree] Reroute - Mismatching output types..`);
|
||||
}
|
||||
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);
|
||||
}
|
||||
computeSize(out) {
|
||||
|
||||
+2
-2
@@ -245,8 +245,8 @@ class Rgthree {
|
||||
fireEvent(event, data) {
|
||||
if (this.eventsToFns.has(event)) {
|
||||
for (let fn of this.eventsToFns.get(event)) {
|
||||
const event = new Event(data);
|
||||
fn(event);
|
||||
const e = new CustomEvent(event, { detail: data });
|
||||
fn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-20
@@ -275,46 +275,53 @@ export function filterOutPassthroughNodes(nodes, passThroughFollowing = PassThro
|
||||
return nodes.filter((n) => !shouldPassThrough(n, passThroughFollowing));
|
||||
}
|
||||
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) {
|
||||
return filterOutPassthroughNodes(getConnectedInputNodes(startNode, currentNode, slot, passThroughFollowing), passThroughFollowing);
|
||||
}
|
||||
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) {
|
||||
return filterOutPassthroughNodes(getConnectedOutputNodes(startNode, currentNode, slot, passThroughFollowing), passThroughFollowing);
|
||||
}
|
||||
export function getConnectedNodes(startNode, dir = IoDirection.INPUT, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL) {
|
||||
var _a, _b;
|
||||
export function getConnectedNodesInfo(startNode, dir = IoDirection.INPUT, currentNode, slot, passThroughFollowing = PassThroughFollowing.ALL, originTravelFromSlot) {
|
||||
var _a, _b, _c, _d, _e, _f;
|
||||
currentNode = currentNode || startNode;
|
||||
let rootNodes = [];
|
||||
const slotsToRemove = [];
|
||||
if (startNode === currentNode || shouldPassThrough(currentNode, passThroughFollowing)) {
|
||||
let linkIds;
|
||||
if (dir == IoDirection.OUTPUT) {
|
||||
linkIds = ((_a = currentNode.outputs) === null || _a === void 0 ? void 0 : _a.flatMap((i) => i.links)) || [];
|
||||
}
|
||||
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]];
|
||||
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 {
|
||||
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;
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
const travelFromSlot = dir == IoDirection.OUTPUT ? link.origin_slot : link.target_slot;
|
||||
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);
|
||||
if (!link) {
|
||||
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(", ")}`);
|
||||
}
|
||||
else {
|
||||
rootNodes.push({ node: originNode, slot: originSlot });
|
||||
rootNodes.push({ node: originNode, travelFromSlot, travelToSlot, originTravelFromSlot });
|
||||
if (shouldPassThrough(originNode, passThroughFollowing)) {
|
||||
for (const foundNode of getConnectedNodes(startNode, dir, originNode)) {
|
||||
if (!rootNodes.includes(foundNode)) {
|
||||
for (const foundNode of getConnectedNodesInfo(startNode, dir, originNode, undefined, undefined, originTravelFromSlot)) {
|
||||
if (!rootNodes.map(n => n.node).includes(foundNode.node)) {
|
||||
rootNodes.push(foundNode);
|
||||
}
|
||||
}
|
||||
@@ -363,7 +370,7 @@ function getTypeFromSlot(slot, dir, skipSelf = false) {
|
||||
let graph = app.graph;
|
||||
let type = slot === null || slot === void 0 ? void 0 : slot.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);
|
||||
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) != "*") {
|
||||
return {
|
||||
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) == "*") {
|
||||
|
||||
Reference in New Issue
Block a user