Add some branching nodes

This commit is contained in:
space-nuko
2023-05-04 19:02:54 -05:00
parent e663f4db88
commit 2ae41e26e6
12 changed files with 433 additions and 87 deletions

View File

@@ -44,6 +44,7 @@
"@litegraph-ts/core": "workspace:*", "@litegraph-ts/core": "workspace:*",
"@litegraph-ts/nodes-basic": "workspace:*", "@litegraph-ts/nodes-basic": "workspace:*",
"@litegraph-ts/nodes-events": "workspace:*", "@litegraph-ts/nodes-events": "workspace:*",
"@litegraph-ts/nodes-math": "workspace:*",
"@litegraph-ts/tsconfig": "workspace:*", "@litegraph-ts/tsconfig": "workspace:*",
"@sveltejs/vite-plugin-svelte": "^2.1.1", "@sveltejs/vite-plugin-svelte": "^2.1.1",
"@tsconfig/svelte": "^4.0.1", "@tsconfig/svelte": "^4.0.1",

19
pnpm-lock.yaml generated
View File

@@ -40,6 +40,9 @@ importers:
'@litegraph-ts/nodes-events': '@litegraph-ts/nodes-events':
specifier: workspace:* specifier: workspace:*
version: link:litegraph/packages/nodes-events version: link:litegraph/packages/nodes-events
'@litegraph-ts/nodes-math':
specifier: workspace:*
version: link:litegraph/packages/nodes-math
'@litegraph-ts/tsconfig': '@litegraph-ts/tsconfig':
specifier: workspace:* specifier: workspace:*
version: link:litegraph/packages/tsconfig version: link:litegraph/packages/tsconfig
@@ -786,6 +789,22 @@ importers:
specifier: ^4.2.1 specifier: ^4.2.1
version: 4.3.1 version: 4.3.1
litegraph/packages/nodes-math:
dependencies:
'@litegraph-ts/core':
specifier: workspace:*
version: link:../core
devDependencies:
'@litegraph-ts/tsconfig':
specifier: workspace:*
version: link:../tsconfig
typescript:
specifier: ^5.0.3
version: 5.0.3
vite:
specifier: ^4.2.1
version: 4.3.1
litegraph/packages/tsconfig: {} litegraph/packages/tsconfig: {}
packages: packages:

View File

@@ -8,6 +8,7 @@ import { get } from "svelte/store";
import type ComfyGraphNode from "./nodes/ComfyGraphNode"; import type ComfyGraphNode from "./nodes/ComfyGraphNode";
import type IComfyInputSlot from "./IComfyInputSlot"; import type IComfyInputSlot from "./IComfyInputSlot";
import type { ComfyBackendNode } from "./nodes/ComfyBackendNode"; import type { ComfyBackendNode } from "./nodes/ComfyBackendNode";
import type { ComfyWidgetNode } from "./nodes";
type ComfyGraphEvents = { type ComfyGraphEvents = {
configured: (graph: LGraph) => void configured: (graph: LGraph) => void
@@ -52,12 +53,35 @@ export default class ComfyGraph extends LGraph {
layoutState.nodeAdded(node) layoutState.nodeAdded(node)
this.graphSync.onNodeAdded(node); this.graphSync.onNodeAdded(node);
if ("comfyClass" in node // Is this a comfy node if ("outputProperties" in node) {
&& !("svelteComponentType" in node) // ...and not also a ComfyWidgetNode const widgetNode = node as ComfyWidgetNode;
&& !options.addedByDeserialize // ...and we're not trying to deserialize an existing workflow for (const propName of widgetNode.outputProperties) {
&& get(uiState).autoAddUI) { widgetNode.addPropertyAsOutput(propName.name, propName.type)
}
}
// Check if the class declared a default widget layout
if ("defaultWidgets" in node && !("svelteComponentType" in node)) {
const comfyNode = node as ComfyGraphNode;
const widgets = comfyNode.defaultWidgets;
if (widgets) {
if (widgets.inputs) {
for (const pair of Object.entries(comfyNode.defaultWidgets.inputs)) {
const [index, spec] = pair
const input = comfyNode.inputs[index] as IComfyInputSlot;
input.defaultWidgetNode = spec.defaultWidgetNode;
if (spec.config)
input.config = spec.config
}
}
}
}
if (get(uiState).autoAddUI) {
if (!("svelteComponentType" in node) && !options.addedByDeserialize) {
console.debug("[ComfyGraph] AutoAdd UI") console.debug("[ComfyGraph] AutoAdd UI")
const comfyNode = node as ComfyBackendNode; const comfyNode = node as ComfyGraphNode;
const widgetNodesAdded = [] const widgetNodesAdded = []
for (let index = 0; index < comfyNode.inputs.length; index++) { for (let index = 0; index < comfyNode.inputs.length; index++) {
const input = comfyNode.inputs[index]; const input = comfyNode.inputs[index];
@@ -76,7 +100,9 @@ export default class ComfyGraph extends LGraph {
} }
const dragItems = widgetNodesAdded.map(wn => get(layoutState).allItemsByNode[wn.id]?.dragItem).filter(di => di) const dragItems = widgetNodesAdded.map(wn => get(layoutState).allItemsByNode[wn.id]?.dragItem).filter(di => di)
console.debug("[ComfyGraph] Group new widgets", dragItems) console.debug("[ComfyGraph] Group new widgets", dragItems)
layoutState.groupItems(dragItems, { title: comfyNode.comfyClass })
layoutState.groupItems(dragItems, { title: node.title })
}
} }
console.debug("Added", node); console.debug("Added", node);

View File

@@ -26,7 +26,7 @@
let resizeTimeout: NodeJS.Timeout | null; let resizeTimeout: NodeJS.Timeout | null;
let hasShownUIHelpToast: boolean = false; let hasShownUIHelpToast: boolean = false;
let debugLayout: boolean = true; let debugLayout: boolean = false;
const toastOptions = { const toastOptions = {
intro: { duration: 200 }, intro: { duration: 200 },

View File

@@ -1,7 +1,6 @@
import { LiteGraph, LGraph, LGraphCanvas, LGraphNode, type LGraphNodeConstructor, type LGraphNodeExecutable, type SerializedLGraph, type SerializedLGraphGroup, type SerializedLGraphNode, type SerializedLLink, NodeMode, type Vector2, BuiltInSlotType } from "@litegraph-ts/core"; import { LiteGraph, LGraph, LGraphCanvas, LGraphNode, type LGraphNodeConstructor, type LGraphNodeExecutable, type SerializedLGraph, type SerializedLGraphGroup, type SerializedLGraphNode, type SerializedLLink, NodeMode, type Vector2, BuiltInSlotType } from "@litegraph-ts/core";
import type { LConnectionKind, INodeSlot } from "@litegraph-ts/core"; import type { LConnectionKind, INodeSlot } from "@litegraph-ts/core";
import ComfyAPI from "$lib/api" import ComfyAPI from "$lib/api"
import { ComfyWidgets } from "$lib/widgets"
import defaultGraph from "$lib/defaultGraph" import defaultGraph from "$lib/defaultGraph"
import { getPngMetadata, importA1111 } from "$lib/pnginfo"; import { getPngMetadata, importA1111 } from "$lib/pnginfo";
import EventEmitter from "events"; import EventEmitter from "events";
@@ -10,6 +9,7 @@ import type TypedEmitter from "typed-emitter";
// Import nodes // Import nodes
import "@litegraph-ts/nodes-basic" import "@litegraph-ts/nodes-basic"
import "@litegraph-ts/nodes-events" import "@litegraph-ts/nodes-events"
import "@litegraph-ts/nodes-math"
import * as nodes from "$lib/nodes/index" import * as nodes from "$lib/nodes/index"
import ComfyGraphCanvas, { type SerializedGraphCanvasState } from "$lib/ComfyGraphCanvas"; import ComfyGraphCanvas, { type SerializedGraphCanvasState } from "$lib/ComfyGraphCanvas";
@@ -482,17 +482,19 @@ export default class ComfyApp {
for (let i = 0; i < node.inputs.length; i++) { for (let i = 0; i < node.inputs.length; i++) {
let parent: ComfyGraphNode = node.getInputNode(i) as ComfyGraphNode; let parent: ComfyGraphNode = node.getInputNode(i) as ComfyGraphNode;
if (parent) { if (parent) {
const seen = {}
let link = node.getInputLink(i); let link = node.getInputLink(i);
while (parent && !parent.isBackendNode) { while (parent && !parent.isBackendNode) {
link = parent.getInputLink(link.origin_slot); link = parent.getInputLink(link.origin_slot);
if (link) { if (link && !seen[link.id]) {
seen[link.id] = true
parent = parent.getInputNode(link.origin_slot) as ComfyGraphNode; parent = parent.getInputNode(link.origin_slot) as ComfyGraphNode;
} else { } else {
parent = null; parent = null;
} }
} }
if (link) { if (link && parent && parent.isBackendNode) {
const input = node.inputs[i] const input = node.inputs[i]
// TODO can null be a legitimate value in some cases? // TODO can null be a legitimate value in some cases?
// Nodes like CLIPLoader will never have a value in the frontend, hence "null". // Nodes like CLIPLoader will never have a value in the frontend, hence "null".

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,24 @@
import type { ComfyInputConfig } from "$lib/IComfyInputSlot";
import type { SerializedPrompt } from "$lib/components/ComfyApp"; import type { SerializedPrompt } from "$lib/components/ComfyApp";
import type ComfyWidget from "$lib/components/widgets/ComfyWidget"; import type ComfyWidget from "$lib/components/widgets/ComfyWidget";
import { LGraph, LGraphNode } from "@litegraph-ts/core"; import { LGraph, LGraphNode } from "@litegraph-ts/core";
import type { SvelteComponentDev } from "svelte/internal";
import type { ComfyWidgetNode } from "./ComfyWidgetNodes";
export type DefaultWidgetSpec = {
defaultWidgetNode: new (name?: string) => ComfyWidgetNode,
config?: ComfyInputConfig
}
export type DefaultWidgetLayout = {
inputs?: Record<number, DefaultWidgetSpec>,
}
export default class ComfyGraphNode extends LGraphNode { export default class ComfyGraphNode extends LGraphNode {
isBackendNode?: boolean; isBackendNode?: boolean;
afterQueued?(prompt: SerializedPrompt): void; afterQueued?(prompt: SerializedPrompt): void;
onExecuted?(output: any): void; onExecuted?(output: any): void;
defaultWidgets?: DefaultWidgetLayout
} }

View File

@@ -0,0 +1,120 @@
import { BuiltInSlotType, LiteGraph, type SlotLayout } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode";
export interface ComfySelectorProperties extends Record<any, any> {
value: any
}
export default class ComfySelector extends ComfyGraphNode {
override properties: ComfySelectorProperties = {
value: null
}
static slotLayout: SlotLayout = {
inputs: [
{ name: "select", type: "number" },
{ name: "A", type: "*" },
{ name: "B", type: "*" },
{ name: "C", type: "*" },
{ name: "D", type: "*" },
],
outputs: [
{ name: "out", type: "*" }
],
}
private selected: number = 0;
constructor(title?: string) {
super(title);
}
override onDrawBackground(ctx: CanvasRenderingContext2D) {
if (this.flags.collapsed) {
return;
}
ctx.fillStyle = "#AFB";
var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 6;
ctx.beginPath();
ctx.moveTo(50, y);
ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT);
ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5);
ctx.fill();
};
override onExecute() {
var sel = this.getInputData(0);
if (sel == null || sel.constructor !== Number)
sel = 0;
this.selected = sel = Math.round(sel) % (this.inputs.length - 1);
var v = this.getInputData(sel + 1);
if (v !== undefined) {
this.setOutputData(0, v);
}
}
}
LiteGraph.registerNodeType({
class: ComfySelector,
title: "Comfy.Selector",
desc: "Selects an output from two or more inputs",
type: "utils/selector"
})
export interface ComfySelectorTwoProperties extends Record<any, any> {
value: any
}
export class ComfySelectorTwo extends ComfyGraphNode {
override properties: ComfySelectorTwoProperties = {
value: null
}
static slotLayout: SlotLayout = {
inputs: [
{ name: "select", type: "boolean" },
{ name: "true", type: "*" },
{ name: "false", type: "*" },
],
outputs: [
{ name: "out", type: "*" }
],
}
private selected: number = 0;
constructor(title?: string) {
super(title);
}
override onDrawBackground(ctx: CanvasRenderingContext2D) {
if (this.flags.collapsed) {
return;
}
ctx.fillStyle = "#AFB";
var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 6;
ctx.beginPath();
ctx.moveTo(50, y);
ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT);
ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5);
ctx.fill();
};
override onExecute() {
var sel = this.getInputData(0);
if (sel == null || sel.constructor !== Boolean)
sel = 0;
this.selected = sel ? 0 : 1;
var v = this.getInputData(this.selected + 1);
if (v !== undefined) {
this.setOutputData(0, v);
}
}
}
LiteGraph.registerNodeType({
class: ComfySelectorTwo,
title: "Comfy.Selector2",
desc: "Selects an output from two inputs with a boolean",
type: "utils/selector2"
})

View File

@@ -0,0 +1,107 @@
import { BuiltInSlotType, LiteGraph, type SlotLayout } from "@litegraph-ts/core";
import ComfyGraphNode, { type DefaultWidgetLayout } from "./ComfyGraphNode";
import { clamp } from "$lib/utils";
import ComboWidget from "$lib/widgets/ComboWidget.svelte";
import { ComfyComboNode } from "./ComfyWidgetNodes";
export interface ComfyValueControlProperties extends Record<any, any> {
value: any,
action: "fixed" | "increment" | "decrement" | "randomize",
min: number,
max: number,
step: number
}
const INT_MAX = 1125899906842624;
export default class ComfyValueControl extends ComfyGraphNode {
override properties: ComfyValueControlProperties = {
value: null,
action: "fixed",
min: -INT_MAX,
max: INT_MAX,
step: 1
}
static slotLayout: SlotLayout = {
inputs: [
{ name: "value", type: "number" },
{ name: "trigger", type: BuiltInSlotType.ACTION },
{ name: "action", type: "string" },
{ name: "min", type: "number" },
{ name: "max", type: "number" },
{ name: "step", type: "number" }
],
outputs: [
{ name: "value", type: "*" }
],
}
override defaultWidgets: DefaultWidgetLayout = {
inputs: {
2: {
defaultWidgetNode: ComfyComboNode,
config: {
defaultValue: "randomize",
values: ["fixed", "increment", "decrement", "randomize"]
}
}
}
}
constructor(title?: string) {
super(title);
}
override onExecute() {
this.setProperty("action", this.getInputData(2) || "fixed")
this.setProperty("min", this.getInputData(3))
this.setProperty("max", this.getInputData(4))
this.setProperty("step", this.getInputData(5) || 1)
}
override onAction(action: any, param: any) {
var v = this.getInputData(0)
if (typeof v !== "number")
return
let min = this.properties.min
let max = this.properties.max
if (min == null) min = -INT_MAX
if (max == null) max = INT_MAX
// limit to something that javascript can handle
min = Math.max(-INT_MAX, this.properties.min);
max = Math.min(INT_MAX, this.properties.max);
let range = (max - min) / (this.properties.step);
//adjust values based on valueControl Behaviour
switch (this.properties.action) {
case "fixed":
break;
case "increment":
v += this.properties.step;
break;
case "decrement":
v -= this.properties.step;
break;
case "randomize":
v = Math.floor(Math.random() * range) * (this.properties.step) + min;
default:
break;
}
v = clamp(v, min, max)
this.setProperty("value", v)
this.setOutputData(0, v)
console.debug("ValueControl", v, this.properties)
};
}
LiteGraph.registerNodeType({
class: ComfyValueControl,
title: "Comfy.ValueControl",
desc: "Adjusts an incoming value based on behavior",
type: "utils/value_control"
})

View File

@@ -36,6 +36,15 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
/** If false, user manually set min/max/step, and should not be autoinherited from connected input */ /** If false, user manually set min/max/step, and should not be autoinherited from connected input */
autoConfig: boolean = true; autoConfig: boolean = true;
copyFromInputLink: boolean = true;
/** Names of properties to add as inputs */
// shownInputProperties: string[] = []
/** Names of properties to add as outputs */
private shownOutputProperties: Record<string, { type: string, index: number }> = {}
outputProperties: { name: string, type: string }[] = []
override isBackendNode = false; override isBackendNode = false;
override serialize_widgets = true; override serialize_widgets = true;
@@ -62,6 +71,18 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
this.unsubscribe = this.value.subscribe(this.onValueUpdated.bind(this)) this.unsubscribe = this.value.subscribe(this.onValueUpdated.bind(this))
} }
addPropertyAsOutput(propertyName: string, type: string) {
if (this.shownOutputProperties[propertyName])
return;
if (!(propertyName in this.properties)) {
throw `No property named ${propertyName} found!`
}
this.shownOutputProperties[propertyName] = { type, index: this.outputs.length }
this.addOutput(propertyName, type)
}
formatValue(value: any): string { formatValue(value: any): string {
return Watch.toString(value) return Watch.toString(value)
} }
@@ -84,21 +105,33 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
this.value.set(value) this.value.set(value)
} }
abstract validateValue(value: any): boolean; override onPropertyChanged(property: string, value: any, prevValue?: any) {
const data = this.shownOutputProperties[property]
if (data)
this.setOutputData(data.index, value)
}
/* /*
* Logic to run if this widget can be treated as output (slider, combo, text) * Logic to run if this widget can be treated as output (slider, combo, text)
*/ */
override onExecute() { override onExecute() {
if (this.copyFromInputLink) {
if (this.inputs.length >= this.inputIndex) { if (this.inputs.length >= this.inputIndex) {
const data = this.getInputData(this.inputIndex) const data = this.getInputData(this.inputIndex)
if (data && this.validateValue(data)) { // TODO can "null" be a legitimate value here? if (data) { // TODO can "null" be a legitimate value here?
this.setValue(data) this.setValue(data)
const input = this.getInputLink(this.inputIndex)
input.data = null;
}
} }
} }
if (this.outputs.length >= this.outputIndex) { if (this.outputs.length >= this.outputIndex) {
this.setOutputData(this.outputIndex, get(this.value)) this.setOutputData(this.outputIndex, get(this.value))
} }
for (const propName in this.shownOutputProperties) {
const data = this.shownOutputProperties[propName]
this.setOutputData(data.index, this.properties[propName])
}
} }
/** Called when a backend node sends a ComfyUI output over a link */ /** Called when a backend node sends a ComfyUI output over a link */
@@ -118,7 +151,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
const comfyInput = input as IComfyInputSlot; const comfyInput = input as IComfyInputSlot;
for (const key in comfyInput.config) for (const key in comfyInput.config)
this.setProperty(key, comfyInput.config[key]) this.setProperty(key, comfyInput.config[key])
}
if ("defaultValue" in this.properties) if ("defaultValue" in this.properties)
this.setValue(this.properties.defaultValue) this.setValue(this.properties.defaultValue)
@@ -131,6 +164,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
this.setValue(get(this.value)) this.setValue(get(this.value))
} }
}
return true; return true;
} }
@@ -167,11 +201,13 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
clampOneConfig(input: IComfyInputSlot) { } clampOneConfig(input: IComfyInputSlot) { }
override onSerialize(o: SerializedLGraphNode) { override onSerialize(o: SerializedLGraphNode) {
(o as any).comfyValue = get(this.value) (o as any).comfyValue = get(this.value);
(o as any).shownOutputProperties = this.shownOutputProperties
} }
override onConfigure(o: SerializedLGraphNode) { override onConfigure(o: SerializedLGraphNode) {
this.value.set((o as any).comfyValue) this.value.set((o as any).comfyValue);
this.shownOutputProperties = (o as any).shownOutputProperties;
} }
} }
@@ -199,25 +235,32 @@ export class ComfySliderNode extends ComfyWidgetNode<number> {
], ],
outputs: [ outputs: [
{ name: "value", type: "number" }, { name: "value", type: "number" },
{ name: "changed", type: BuiltInSlotType.EVENT } { name: "changed", type: BuiltInSlotType.EVENT },
] ]
} }
override outputProperties = [
{ name: "min", type: "number" },
{ name: "max", type: "number" },
{ name: "step", type: "number" },
{ name: "precision", type: "number" },
]
constructor(name?: string) { constructor(name?: string) {
super(name, 0) super(name, 0)
} }
override validateValue(value: any): boolean { override setValue(value: any) {
return typeof value === "number" if (typeof value !== "number")
&& value >= this.properties.min return;
&& value <= this.properties.max super.setValue(clamp(value, this.properties.min, this.properties.max))
} }
override clampOneConfig(input: IComfyInputSlot) { override clampOneConfig(input: IComfyInputSlot) {
// this.setProperty("min", clamp(this.properties.min, input.config.min, input.config.max)) // this.setProperty("min", clamp(this.properties.min, input.config.min, input.config.max))
// this.setProperty("max", clamp(this.properties.max, input.config.max, input.config.min)) // this.setProperty("max", clamp(this.properties.max, input.config.max, input.config.min))
// this.setProperty("step", Math.min(this.properties.step, input.config.step)) // this.setProperty("step", Math.min(this.properties.step, input.config.step))
this.setValue(clamp(this.properties.defaultValue, this.properties.min, this.properties.max)) this.setValue(this.properties.defaultValue)
} }
} }
@@ -280,10 +323,10 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
return true; return true;
} }
override validateValue(value: any): boolean { override setValue(value: any) {
if (typeof value !== "string") if (typeof value !== "string" || this.properties.values.indexOf(value) === -1)
return false; return;
return this.properties.values.indexOf(value) !== -1; super.setValue(value)
} }
override clampOneConfig(input: IComfyInputSlot) { override clampOneConfig(input: IComfyInputSlot) {
@@ -291,7 +334,7 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
if (input.config.values.length === 0) if (input.config.values.length === 0)
this.setValue("") this.setValue("")
else else
this.setValue(input.config.values[0]) this.setValue(input.config.defaultValue || input.config.values[0])
} }
} }
} }
@@ -329,8 +372,8 @@ export class ComfyTextNode extends ComfyWidgetNode<string> {
super(name, "") super(name, "")
} }
override validateValue(value: any): boolean { override setValue(value: any) {
return typeof value === "string" super.setValue(`${value}`)
} }
} }
@@ -368,6 +411,7 @@ export class ComfyGalleryNode extends ComfyWidgetNode<GradioFileData[]> {
} }
override svelteComponentType = GalleryWidget override svelteComponentType = GalleryWidget
override copyFromInputLink = false;
constructor(name?: string) { constructor(name?: string) {
super(name, []) super(name, [])
@@ -380,21 +424,12 @@ export class ComfyGalleryNode extends ComfyWidgetNode<GradioFileData[]> {
} }
} }
override formatValue(value: GradioFileData[]): string { override formatValue(value: GradioFileData[] | null): string {
return `Images: ${value.length}` return `Images: ${value?.length || 0}`
} }
override validateValue(value: any): boolean { private convertItems(output: GalleryOutput): GradioFileData[] {
return Array.isArray(value) && value.every(e => "images" in e) return output.images.map(r => {
}
receiveOutput() {
const link = this.getInputLink(0)
if (link.data && "images" in link.data) {
const data = link.data as GalleryOutput
console.debug("[ComfyGalleryNode] Received output!", data)
const galleryItems: GradioFileData[] = data.images.map(r => {
// TODO configure backend URL // TODO configure backend URL
const url = "http://localhost:8188/view?" const url = "http://localhost:8188/view?"
const params = new URLSearchParams(r) const params = new URLSearchParams(r)
@@ -403,6 +438,24 @@ export class ComfyGalleryNode extends ComfyWidgetNode<GradioFileData[]> {
data: url + params data: url + params
} }
}); });
}
override setValue(value: any) {
if (Array.isArray(value)) {
super.setValue(value)
}
else {
super.setValue([])
}
}
receiveOutput() {
const link = this.getInputLink(0)
if (link.data && "images" in link.data) {
const data = link.data as GalleryOutput
console.debug("[ComfyGalleryNode] Received output!", data)
const galleryItems: GradioFileData[] = this.convertItems(link.data)
const currentValue = get(this.value) const currentValue = get(this.value)
this.setValue(currentValue.concat(galleryItems)) this.setValue(currentValue.concat(galleryItems))
@@ -429,7 +482,7 @@ export class ComfyButtonNode extends ComfyWidgetNode<boolean> {
static slotLayout: SlotLayout = { static slotLayout: SlotLayout = {
outputs: [ outputs: [
{ name: "event", type: BuiltInSlotType.EVENT }, { name: "clicked", type: BuiltInSlotType.EVENT },
{ name: "isClicked", type: "boolean" }, { name: "isClicked", type: "boolean" },
] ]
} }
@@ -437,8 +490,8 @@ export class ComfyButtonNode extends ComfyWidgetNode<boolean> {
override outputIndex = 1; override outputIndex = 1;
override svelteComponentType = ButtonWidget; override svelteComponentType = ButtonWidget;
override validateValue(value: any): boolean { override setValue(value: any) {
return typeof value === "boolean" super.setValue(Boolean(value))
} }
onClick() { onClick() {

View File

@@ -1,3 +1,5 @@
export { default as ComfyReroute } from "./ComfyReroute" export { default as ComfyReroute } from "./ComfyReroute"
export { ComfyWidgetNode, ComfySliderNode, ComfyComboNode, ComfyTextNode } from "./ComfyWidgetNodes" export { ComfyWidgetNode, ComfySliderNode, ComfyComboNode, ComfyTextNode } from "./ComfyWidgetNodes"
export { ComfyCopyAction } from "./ComfyActionNodes" export { ComfyCopyAction } from "./ComfyActionNodes"
export { default as ComfyValueControl } from "./ComfyValueControl"
export { default as ComfySelector } from "./ComfySelector"