diff --git a/src/lib/components/ComfyGraphErrorList.svelte b/src/lib/components/ComfyGraphErrorList.svelte index 7680169..f8a669b 100644 --- a/src/lib/components/ComfyGraphErrorList.svelte +++ b/src/lib/components/ComfyGraphErrorList.svelte @@ -4,8 +4,8 @@ import Accordion from "./gradio/app/Accordion.svelte"; import uiState from '$lib/stores/uiState'; import type { ComfyNodeDefInputType } from "$lib/ComfyNodeDef"; - import type { INodeInputSlot, LGraphNode, Subgraph } from "@litegraph-ts/core"; - import { UpstreamNodeLocator } from "./ComfyPromptSerializer"; + import type { INodeInputSlot, LGraphNode, LLink, Subgraph } from "@litegraph-ts/core"; + import { UpstreamNodeLocator, getUpstreamLink } from "./ComfyPromptSerializer"; import JsonView from "./JsonView.svelte"; export let app: ComfyApp; @@ -35,7 +35,7 @@ const node = app.lCanvas.graph.getNodeByIdRecursive(error.nodeID); - const inputIndex =node.findInputSlotIndexByName(error.input.name); + const inputIndex = node.findInputSlotIndexByName(error.input.name); if (inputIndex === -1) { return } @@ -43,7 +43,10 @@ // TODO multiple tags? const tag: string | null = error.queueEntry.extraData.extra_pnginfo.comfyBoxPrompt.subgraphs[0]; - const test = (node: LGraphNode) => (node as any).isBackendNode + const test = (node: LGraphNode, currentLink: LLink) => { + const [nextGraph, nextLink, nextInputSlot, nextNode] = getUpstreamLink(node, currentLink) + return nextLink == null; + }; const nodeLocator = new UpstreamNodeLocator(test) const [_, foundLink, foundInputSlot, foundPrevNode] = nodeLocator.locateUpstream(node, inputIndex, tag); diff --git a/src/lib/components/ComfyPromptSerializer.ts b/src/lib/components/ComfyPromptSerializer.ts index 51808d4..cfb5786 100644 --- a/src/lib/components/ComfyPromptSerializer.ts +++ b/src/lib/components/ComfyPromptSerializer.ts @@ -71,62 +71,62 @@ export function isActiveBackendNode(node: LGraphNode, tag: string | null = null) return true; } -type UpstreamResult = [LGraph | null, LLink | null, number | null, LGraphNode | null]; +export type UpstreamResult = [LGraph | null, LLink | null, number | null, LGraphNode | null]; + +function followSubgraph(subgraph: Subgraph, link: LLink): UpstreamResult { + if (link.origin_id != subgraph.id) + throw new Error("Invalid link and graph output!") + + const innerGraphOutput = subgraph.getInnerGraphOutputByIndex(link.origin_slot) + if (innerGraphOutput == null) + throw new Error("No inner graph input!") + + const nextLink = innerGraphOutput.getInputLink(0) + return [innerGraphOutput.graph, nextLink, 0, innerGraphOutput]; +} + +function followGraphInput(graphInput: GraphInput, link: LLink): UpstreamResult { + if (link.origin_id != graphInput.id) + throw new Error("Invalid link and graph input!") + + const outerSubgraph = graphInput.getParentSubgraph(); + if (outerSubgraph == null) + throw new Error("No outer subgraph!") + + const outerInputIndex = outerSubgraph.inputs.findIndex(i => i.name === graphInput.nameInGraph) + if (outerInputIndex == null) + throw new Error("No outer input slot!") + + const nextLink = outerSubgraph.getInputLink(outerInputIndex) + return [outerSubgraph.graph, nextLink, outerInputIndex, outerSubgraph]; +} + +export function getUpstreamLink(parent: LGraphNode, currentLink: LLink): UpstreamResult { + if (parent.is(Subgraph)) { + console.debug("FollowSubgraph") + return followSubgraph(parent, currentLink); + } + else if (parent.is(GraphInput)) { + console.debug("FollowGraphInput") + return followGraphInput(parent, currentLink); + } + else if ("getUpstreamLink" in parent) { + const link = (parent as ComfyGraphNode).getUpstreamLink(); + return [parent.graph, link, link?.target_slot, parent]; + } + else if (parent.inputs.length === 1) { + // Only one input, so assume we can follow it backwards. + const link = parent.getInputLink(0); + if (link) { + return [parent.graph, link, 0, parent] + } + } + console.warn("[graphToPrompt] Frontend node does not support getUpstreamLink", parent.type) + return [null, null, null, null]; +} export class UpstreamNodeLocator { - constructor(private isTheTargetNode: (node: LGraphNode) => boolean) { - } - - private followSubgraph(subgraph: Subgraph, link: LLink): UpstreamResult { - if (link.origin_id != subgraph.id) - throw new Error("Invalid link and graph output!") - - const innerGraphOutput = subgraph.getInnerGraphOutputByIndex(link.origin_slot) - if (innerGraphOutput == null) - throw new Error("No inner graph input!") - - const nextLink = innerGraphOutput.getInputLink(0) - return [innerGraphOutput.graph, nextLink, 0, innerGraphOutput]; - } - - private followGraphInput(graphInput: GraphInput, link: LLink): UpstreamResult { - if (link.origin_id != graphInput.id) - throw new Error("Invalid link and graph input!") - - const outerSubgraph = graphInput.getParentSubgraph(); - if (outerSubgraph == null) - throw new Error("No outer subgraph!") - - const outerInputIndex = outerSubgraph.inputs.findIndex(i => i.name === graphInput.nameInGraph) - if (outerInputIndex == null) - throw new Error("No outer input slot!") - - const nextLink = outerSubgraph.getInputLink(outerInputIndex) - return [outerSubgraph.graph, nextLink, outerInputIndex, outerSubgraph]; - } - - private getUpstreamLink(parent: LGraphNode, currentLink: LLink): UpstreamResult { - if (parent.is(Subgraph)) { - console.debug("FollowSubgraph") - return this.followSubgraph(parent, currentLink); - } - else if (parent.is(GraphInput)) { - console.debug("FollowGraphInput") - return this.followGraphInput(parent, currentLink); - } - else if ("getUpstreamLink" in parent) { - const link = (parent as ComfyGraphNode).getUpstreamLink(); - return [parent.graph, link, link?.target_slot, parent]; - } - else if (parent.inputs.length === 1) { - // Only one input, so assume we can follow it backwards. - const link = parent.getInputLink(0); - if (link) { - return [parent.graph, link, 0, parent] - } - } - console.warn("[graphToPrompt] Frontend node does not support getUpstreamLink", parent.type) - return [null, null, null, null]; + constructor(private isTheTargetNode: (node: LGraphNode, currentLink: LLink) => boolean) { } /* @@ -146,8 +146,8 @@ export class UpstreamNodeLocator { let currentInputSlot = inputIndex; let currentNode = fromNode; - const shouldFollowParent = (parent: LGraphNode) => { - return isActiveNode(parent, tag) && !this.isTheTargetNode(parent); + const shouldFollowParent = (parent: LGraphNode, currentLink: LLink) => { + return isActiveNode(parent, tag) && !this.isTheTargetNode(parent, currentLink); } // If there are non-target nodes between us and another @@ -156,8 +156,8 @@ export class UpstreamNodeLocator { // will simply follow their single input, while branching // nodes have conditional logic that determines which link // to follow backwards. - while (shouldFollowParent(parent)) { - const [nextGraph, nextLink, nextInputSlot, nextNode] = this.getUpstreamLink(parent, currentLink); + while (shouldFollowParent(parent, currentLink)) { + const [nextGraph, nextLink, nextInputSlot, nextNode] = getUpstreamLink(parent, currentLink); currentInputSlot = nextInputSlot; currentNode = nextNode; @@ -183,7 +183,7 @@ export class UpstreamNodeLocator { } } - if (!isActiveNode(parent, tag) || !this.isTheTargetNode(parent) || currentLink == null) + if (!isActiveNode(parent, tag) || !this.isTheTargetNode(parent, currentLink) || currentLink == null) return [null, currentLink, currentInputSlot, currentNode]; return [parent, currentLink, currentInputSlot, currentNode] diff --git a/src/lib/nodes/ComfyPickFirstNode.ts b/src/lib/nodes/ComfyPickFirstNode.ts index 1669dc8..50d5875 100644 --- a/src/lib/nodes/ComfyPickFirstNode.ts +++ b/src/lib/nodes/ComfyPickFirstNode.ts @@ -3,7 +3,7 @@ import ComfyGraphNode, { type ComfyGraphNodeProperties } from "./ComfyGraphNode" import { Watch } from "@litegraph-ts/nodes-basic"; import { nextLetter } from "$lib/utils"; -export type PickFirstMode = "anyActiveLink" | "truthy" | "dataNonNull" +export type PickFirstMode = "anyActiveLink" | "dataTruthy" | "dataNonNull" export interface ComfyPickFirstNodeProperties extends ComfyGraphNodeProperties { mode: PickFirstMode @@ -12,7 +12,7 @@ export interface ComfyPickFirstNodeProperties extends ComfyGraphNodeProperties { export default class ComfyPickFirstNode extends ComfyGraphNode { override properties: ComfyPickFirstNodeProperties = { tags: [], - mode: "dataNonNull" + mode: "anyActiveLink" } static slotLayout: SlotLayout = { @@ -36,7 +36,7 @@ export default class ComfyPickFirstNode extends ComfyGraphNode { super(title); this.displayWidget = this.addWidget("text", "Value", "") this.displayWidget.disabled = true; - this.modeWidget = this.addWidget("combo", "Mode", this.properties.mode, null, { property: "mode", values: ["anyActiveLink", "truthy", "dataNonNull"] }) + this.modeWidget = this.addWidget("combo", "Mode", this.properties.mode, null, { property: "mode", values: ["anyActiveLink", "dataTruthy", "dataNonNull"] }) } override onDrawBackground(ctx: CanvasRenderingContext2D) { @@ -45,7 +45,7 @@ export default class ComfyPickFirstNode extends ComfyGraphNode { } if (this.selected === -1) { - // Draw an X + // Draw an X indicating nothing matched the selection criteria const y = LiteGraph.NODE_SLOT_HEIGHT + 6; ctx.lineWidth = 5; ctx.strokeStyle = "red"; @@ -60,6 +60,7 @@ export default class ComfyPickFirstNode extends ComfyGraphNode { ctx.stroke(); } else { + // Draw an arrow pointing to the selected input ctx.fillStyle = "#AFB"; const y = (this.selected) * LiteGraph.NODE_SLOT_HEIGHT + 6; ctx.beginPath(); @@ -130,7 +131,7 @@ export default class ComfyPickFirstNode extends ComfyGraphNode { else { if (this.properties.mode === "dataNonNull") return link.data != null; - else if (this.properties.mode === "truthy") + else if (this.properties.mode === "dataTruthy") return Boolean(link.data) else // anyActiveLink return true;