Prompt serializer and test fixes
This commit is contained in:
Submodule litegraph updated: cd4f68ef42...6e4c8301cd
@@ -1,6 +1,11 @@
|
|||||||
{
|
{
|
||||||
"createdBy": "ComfyBox",
|
"createdBy": "ComfyBox",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
|
"attrs": {
|
||||||
|
"title": "Default",
|
||||||
|
"queuePromptButtonName": "Queue txt2img",
|
||||||
|
"queuePromptButtonRunWorkflow": false
|
||||||
|
},
|
||||||
"workflow": {
|
"workflow": {
|
||||||
"last_node_id": 0,
|
"last_node_id": 0,
|
||||||
"last_link_id": 0,
|
"last_link_id": 0,
|
||||||
@@ -25709,10 +25714,6 @@
|
|||||||
],
|
],
|
||||||
"parent": "eae32e42-1ccc-4a4a-923f-7ab4ccdac97a"
|
"parent": "eae32e42-1ccc-4a4a-923f-7ab4ccdac97a"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"attrs": {
|
|
||||||
"queuePromptButtonName": "Queue txt2img",
|
|
||||||
"queuePromptButtonRunWorkflow": false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"canvas": {
|
"canvas": {
|
||||||
|
|||||||
@@ -118,7 +118,6 @@ export default class ComfyGraph extends LGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (get(uiState).autoAddUI) {
|
if (get(uiState).autoAddUI) {
|
||||||
console.warn("ADD", node.type, options)
|
|
||||||
if (!("svelteComponentType" in node) && options.addedBy == null) {
|
if (!("svelteComponentType" in node) && options.addedBy == null) {
|
||||||
console.debug("[ComfyGraph] AutoAdd UI")
|
console.debug("[ComfyGraph] AutoAdd UI")
|
||||||
const comfyNode = node as ComfyGraphNode;
|
const comfyNode = node as ComfyGraphNode;
|
||||||
@@ -167,10 +166,10 @@ export default class ComfyGraph extends LGraph {
|
|||||||
override onNodeRemoved(node: LGraphNode, options: LGraphRemoveNodeOptions) {
|
override onNodeRemoved(node: LGraphNode, options: LGraphRemoveNodeOptions) {
|
||||||
selectionState.clear(); // safest option
|
selectionState.clear(); // safest option
|
||||||
|
|
||||||
if (node.getRootGraph() != null && !this._is_subgraph && this.workflowID != null) {
|
if (!this._is_subgraph && this.workflowID != null) {
|
||||||
const layoutState = get(layoutStates).all[this.workflowID]
|
const layoutState = get(layoutStates).all[this.workflowID]
|
||||||
if (layoutState === null) {
|
if (layoutState === null) {
|
||||||
throw new Error(`LGraph with workflow missing layout! ${this.workflowID}`)
|
throw new Error(`ComfyGraph with workflow missing layout! ${this.workflowID}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutState.nodeRemoved(node, options);
|
layoutState.nodeRemoved(node, options);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import type { ComfyBoxStdPrompt } from "$lib/ComfyBoxStdPrompt";
|
|||||||
import ComfyBoxStdPromptSerializer from "$lib/ComfyBoxStdPromptSerializer";
|
import ComfyBoxStdPromptSerializer from "$lib/ComfyBoxStdPromptSerializer";
|
||||||
import selectionState from "$lib/stores/selectionState";
|
import selectionState from "$lib/stores/selectionState";
|
||||||
import layoutStates from "$lib/stores/layoutStates";
|
import layoutStates from "$lib/stores/layoutStates";
|
||||||
import { ComfyWorkflow } from "$lib/stores/workflowState";
|
import { ComfyWorkflow, type WorkflowAttributes } from "$lib/stores/workflowState";
|
||||||
import workflowState from "$lib/stores/workflowState";
|
import workflowState from "$lib/stores/workflowState";
|
||||||
|
|
||||||
export const COMFYBOX_SERIAL_VERSION = 1;
|
export const COMFYBOX_SERIAL_VERSION = 1;
|
||||||
@@ -80,8 +80,8 @@ export type SerializedAppState = {
|
|||||||
commitHash?: string,
|
commitHash?: string,
|
||||||
/** Graph state */
|
/** Graph state */
|
||||||
workflow: SerializedLGraph,
|
workflow: SerializedLGraph,
|
||||||
/** Workflow name */
|
/** Workflow attributes */
|
||||||
workflowName: string,
|
attrs: WorkflowAttributes,
|
||||||
/** UI state */
|
/** UI state */
|
||||||
layout: SerializedLayoutState,
|
layout: SerializedLayoutState,
|
||||||
/** Position/offset of the canvas at the time of saving */
|
/** Position/offset of the canvas at the time of saving */
|
||||||
@@ -165,7 +165,7 @@ export default class ComfyApp {
|
|||||||
|
|
||||||
async setup(): Promise<void> {
|
async setup(): Promise<void> {
|
||||||
if (get(this.alreadySetup)) {
|
if (get(this.alreadySetup)) {
|
||||||
console.error("Already setup")
|
console.log("Already setup")
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ export default class ComfyApp {
|
|||||||
if (layoutState == null)
|
if (layoutState == null)
|
||||||
throw new Error("Workflow has no layout!")
|
throw new Error("Workflow has no layout!")
|
||||||
|
|
||||||
const { graph, layout } = workflow.serialize(layoutState);
|
const { graph, layout, attrs } = workflow.serialize(layoutState);
|
||||||
const canvas = this.lCanvas.serialize();
|
const canvas = this.lCanvas.serialize();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -249,6 +249,7 @@ export default class ComfyApp {
|
|||||||
version: COMFYBOX_SERIAL_VERSION,
|
version: COMFYBOX_SERIAL_VERSION,
|
||||||
commitHash: __GIT_COMMIT_HASH__,
|
commitHash: __GIT_COMMIT_HASH__,
|
||||||
workflow: graph,
|
workflow: graph,
|
||||||
|
attrs,
|
||||||
layout,
|
layout,
|
||||||
canvas
|
canvas
|
||||||
}
|
}
|
||||||
@@ -566,7 +567,7 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get(workflow.layout).attrs.queuePromptButtonRunWorkflow) {
|
if (workflow.attrs.queuePromptButtonRunWorkflow) {
|
||||||
// Hold control to queue at the front
|
// Hold control to queue at the front
|
||||||
const num = this.ctrlDown ? -1 : 0;
|
const num = this.ctrlDown ? -1 : 0;
|
||||||
this.queuePrompt(num, 1);
|
this.queuePrompt(num, 1);
|
||||||
|
|||||||
@@ -35,7 +35,15 @@ export function isActiveBackendNode(node: LGraphNode, tag: string | null = null)
|
|||||||
if (!(node as any).isBackendNode)
|
if (!(node as any).isBackendNode)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return isActiveNode(node, tag);
|
if (!isActiveNode(node, tag))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Make sure this node is not contained in an inactive subgraph, even if the
|
||||||
|
// node itself is active
|
||||||
|
if (node.is(Subgraph) && !Array.from(node.iterateParentNodes()).every(n => isActiveNode(n, tag)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpstreamNodeLocator {
|
export class UpstreamNodeLocator {
|
||||||
@@ -166,7 +174,7 @@ export default class ComfyPromptSerializer {
|
|||||||
// We don't check tags for non-backend nodes.
|
// We don't check tags for non-backend nodes.
|
||||||
// Just check for node inactivity (so you can toggle groups of
|
// Just check for node inactivity (so you can toggle groups of
|
||||||
// tagged frontend nodes on/off)
|
// tagged frontend nodes on/off)
|
||||||
if (inputNode && inputNode.mode === NodeMode.NEVER) {
|
if (inputNode && inputNode.mode !== NodeMode.ALWAYS) {
|
||||||
console.debug("Skipping inactive node", inputNode)
|
console.debug("Skipping inactive node", inputNode)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -248,6 +256,8 @@ export default class ComfyPromptSerializer {
|
|||||||
const inputs = this.serializeInputValues(node);
|
const inputs = this.serializeInputValues(node);
|
||||||
const links = this.serializeBackendLinks(node, tag);
|
const links = this.serializeBackendLinks(node, tag);
|
||||||
|
|
||||||
|
console.warn("OUTPUT", node.id, node.comfyClass, node.mode)
|
||||||
|
|
||||||
output[String(node.id)] = {
|
output[String(node.id)] = {
|
||||||
inputs: { ...inputs, ...links },
|
inputs: { ...inputs, ...links },
|
||||||
class_type: node.comfyClass,
|
class_type: node.comfyClass,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { LGraphNode } from "@litegraph-ts/core"
|
import { LGraphNode } from "@litegraph-ts/core"
|
||||||
import { type IDragItem, type WidgetLayout, ALL_ATTRIBUTES, type AttributesSpec, type WritableLayoutStateStore } from "$lib/stores/layoutStates"
|
import { type IDragItem, type WidgetLayout, ALL_ATTRIBUTES, type AttributesSpec, type WritableLayoutStateStore } from "$lib/stores/layoutStates"
|
||||||
import uiState from "$lib/stores/uiState"
|
import uiState from "$lib/stores/uiState"
|
||||||
|
import workflowState from "$lib/stores/workflowState"
|
||||||
import layoutStates from "$lib/stores/layoutStates"
|
import layoutStates from "$lib/stores/layoutStates"
|
||||||
import selectionState from "$lib/stores/selectionState"
|
import selectionState from "$lib/stores/selectionState"
|
||||||
import { get, type Writable, writable } from "svelte/store"
|
import { get, type Writable, writable } from "svelte/store"
|
||||||
@@ -127,7 +128,10 @@
|
|||||||
if (spec.location !== "workflow")
|
if (spec.location !== "workflow")
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return spec.name in $layoutState.attrs
|
if (workflow == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return spec.name in workflow.attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAttribute(target: IDragItem, spec: AttributesSpec): any {
|
function getAttribute(target: IDragItem, spec: AttributesSpec): any {
|
||||||
@@ -240,7 +244,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getWorkflowAttribute(spec: AttributesSpec): any {
|
function getWorkflowAttribute(spec: AttributesSpec): any {
|
||||||
let value = $layoutState.attrs[spec.name]
|
if (workflow == null)
|
||||||
|
throw new Error("Active workflow is null!");
|
||||||
|
|
||||||
|
let value = workflow.attrs[spec.name]
|
||||||
if (value == null)
|
if (value == null)
|
||||||
value = spec.defaultValue
|
value = spec.defaultValue
|
||||||
else if (spec.serialize)
|
else if (spec.serialize)
|
||||||
@@ -253,17 +260,20 @@
|
|||||||
if (!spec.editable)
|
if (!spec.editable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (workflow == null)
|
||||||
|
throw new Error("Active workflow is null!");
|
||||||
|
|
||||||
const name = spec.name
|
const name = spec.name
|
||||||
// console.warn("[ComfyProperties] updateWorkflowAttribute", name, value)
|
// console.warn("[ComfyProperties] updateWorkflowAttribute", name, value)
|
||||||
|
|
||||||
const prevValue = value
|
const prevValue = value
|
||||||
$layoutState.attrs[name] = value
|
workflow.attrs[name] = value
|
||||||
$layoutState = $layoutState
|
$workflowState = $workflowState;
|
||||||
|
|
||||||
if (spec.onChanged)
|
if (spec.onChanged)
|
||||||
spec.onChanged($layoutState, value, prevValue)
|
spec.onChanged($layoutState, value, prevValue)
|
||||||
|
|
||||||
if (spec.refreshPanelOnChange)
|
// if (spec.refreshPanelOnChange)
|
||||||
doRefreshPanel()
|
doRefreshPanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
import { tick } from "svelte";
|
import { tick } from "svelte";
|
||||||
import Modal from "./Modal.svelte";
|
import Modal from "./Modal.svelte";
|
||||||
import DropZone from "./DropZone.svelte";
|
import DropZone from "./DropZone.svelte";
|
||||||
|
import workflowState from "$lib/stores/workflowState";
|
||||||
|
|
||||||
export let app: ComfyApp;
|
export let app: ComfyApp;
|
||||||
|
|
||||||
@@ -71,10 +72,17 @@
|
|||||||
const subgraphs: string[] | null = entry.extraData?.extra_pnginfo?.comfyBoxSubgraphs;
|
const subgraphs: string[] | null = entry.extraData?.extra_pnginfo?.comfyBoxSubgraphs;
|
||||||
|
|
||||||
let message = "Prompt";
|
let message = "Prompt";
|
||||||
|
if (entry.workflowID != null) {
|
||||||
|
const workflow = workflowState.getWorkflow(entry.workflowID);
|
||||||
|
if (workflow != null && workflow.attrs.title) {
|
||||||
|
message = `Workflow: ${workflow.attrs.title}`
|
||||||
|
}
|
||||||
if (subgraphs?.length > 0)
|
if (subgraphs?.length > 0)
|
||||||
message = `Prompt: ${subgraphs.join(', ')}`
|
message += ` (${subgraphs.join(', ')})`
|
||||||
|
}
|
||||||
|
|
||||||
let submessage = `Nodes: ${Object.keys(entry.prompt).length}`
|
let submessage = `Nodes: ${Object.keys(entry.prompt).length}`
|
||||||
|
|
||||||
if (Object.keys(entry.outputs).length > 0) {
|
if (Object.keys(entry.outputs).length > 0) {
|
||||||
const imageCount = Object.values(entry.outputs).flatMap(o => o.images).length
|
const imageCount = Object.values(entry.outputs).flatMap(o => o.images).length
|
||||||
submessage = `Images: ${imageCount}`
|
submessage = `Images: ${imageCount}`
|
||||||
@@ -84,7 +92,7 @@
|
|||||||
entry,
|
entry,
|
||||||
message,
|
message,
|
||||||
submessage,
|
submessage,
|
||||||
dateStr,
|
date: dateStr,
|
||||||
status: "pending",
|
status: "pending",
|
||||||
images: []
|
images: []
|
||||||
}
|
}
|
||||||
@@ -387,7 +395,7 @@
|
|||||||
|
|
||||||
&.all_cached, &.interrupted {
|
&.all_cached, &.interrupted {
|
||||||
filter: brightness(80%);
|
filter: brightness(80%);
|
||||||
color: var(--neutral-300);
|
color: var(--comfy-accent-soft);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
import { get, writable, type Writable } from "svelte/store";
|
import { get, writable, type Writable } from "svelte/store";
|
||||||
import ComfyProperties from "./ComfyProperties.svelte";
|
import ComfyProperties from "./ComfyProperties.svelte";
|
||||||
import uiState from "$lib/stores/uiState";
|
import uiState from "$lib/stores/uiState";
|
||||||
import workflowState from "$lib/stores/workflowState";
|
import workflowState, { ComfyWorkflow } from "$lib/stores/workflowState";
|
||||||
import selectionState from "$lib/stores/selectionState";
|
import selectionState from "$lib/stores/selectionState";
|
||||||
import type ComfyApp from './ComfyApp';
|
import type ComfyApp from './ComfyApp';
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
export let app: ComfyApp;
|
export let app: ComfyApp;
|
||||||
export let uiTheme: string = "gradio-dark" // TODO config
|
export let uiTheme: string = "gradio-dark" // TODO config
|
||||||
|
|
||||||
let layoutState: WritableLayoutStateStore | null = null;
|
let workflow: ComfyWorkflow | null = null;
|
||||||
|
|
||||||
let containerElem: HTMLDivElement;
|
let containerElem: HTMLDivElement;
|
||||||
let resizeTimeout: NodeJS.Timeout | null;
|
let resizeTimeout: NodeJS.Timeout | null;
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
let appSetupPromise: Promise<void> = null;
|
let appSetupPromise: Promise<void> = null;
|
||||||
|
|
||||||
$: layoutState = $workflowState.activeWorkflow?.layout;
|
$: workflow = $workflowState.activeWorkflow;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
appSetupPromise = app.setup().then(() => {
|
appSetupPromise = app.setup().then(() => {
|
||||||
@@ -189,10 +189,10 @@
|
|||||||
<button class="workflow-tab"
|
<button class="workflow-tab"
|
||||||
class:selected={index === $workflowState.activeWorkflowIdx}
|
class:selected={index === $workflowState.activeWorkflowIdx}
|
||||||
on:click={() => app.setActiveWorkflow(index)}>
|
on:click={() => app.setActiveWorkflow(index)}>
|
||||||
<span class="workflow-tab-title">{workflow.title}</span>
|
<span class="workflow-tab-title">{workflow.attrs.title}</span>
|
||||||
<button class="workflow-close-button"
|
<button class="workflow-close-button"
|
||||||
on:click={(e) => closeWorkflow(e, index)}>
|
on:click={(e) => closeWorkflow(e, index)}>
|
||||||
X
|
✕
|
||||||
</button>
|
</button>
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -200,9 +200,9 @@
|
|||||||
<div id="bottombar">
|
<div id="bottombar">
|
||||||
<div class="bottombar-content">
|
<div class="bottombar-content">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
{#if layoutState != null && $layoutState.attrs.queuePromptButtonName != ""}
|
{#if workflow != null && workflow.attrs.queuePromptButtonName != ""}
|
||||||
<Button variant="primary" disabled={!$alreadySetup} on:click={queuePrompt}>
|
<Button variant="primary" disabled={!$alreadySetup} on:click={queuePrompt}>
|
||||||
{$layoutState.attrs.queuePromptButtonName}
|
{workflow.attrs.queuePromptButtonName}
|
||||||
</Button>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
<Button variant="secondary" disabled={!$alreadySetup} on:click={toggleGraph}>
|
<Button variant="secondary" disabled={!$alreadySetup} on:click={toggleGraph}>
|
||||||
@@ -395,11 +395,12 @@
|
|||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
opacity: 50%;
|
||||||
background: var(--neutral-500);
|
background: var(--neutral-500);
|
||||||
color: var(--neutral-300);
|
color: var(--neutral-300);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--neutral-400);
|
opacity: 100%;
|
||||||
color: var(--neutral-100);
|
color: var(--neutral-100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import type { SerializedAppState } from "./components/ComfyApp"
|
|||||||
const blankGraph: SerializedAppState = {
|
const blankGraph: SerializedAppState = {
|
||||||
createdBy: "ComfyBox",
|
createdBy: "ComfyBox",
|
||||||
version: 1,
|
version: 1,
|
||||||
workflowName: "New Workflow",
|
attrs: {
|
||||||
|
title: "New Workflow",
|
||||||
|
queuePromptButtonName: "Queue Prompt",
|
||||||
|
queuePromptButtonRunWorkflow: true
|
||||||
|
},
|
||||||
workflow: {
|
workflow: {
|
||||||
last_node_id: 0,
|
last_node_id: 0,
|
||||||
last_link_id: 0,
|
last_link_id: 0,
|
||||||
@@ -17,10 +21,10 @@ const blankGraph: SerializedAppState = {
|
|||||||
layout: {
|
layout: {
|
||||||
root: null,
|
root: null,
|
||||||
allItems: {},
|
allItems: {},
|
||||||
attrs: {
|
},
|
||||||
queuePromptButtonName: "Queue Prompt",
|
canvas: {
|
||||||
queuePromptButtonRunWorkflow: true
|
offset: [0, 0],
|
||||||
}
|
scale: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,13 +29,10 @@ export default class ComfyConfigureQueuePromptButton extends ComfyGraphNode {
|
|||||||
throw new Error(`Could not find layout attached to this node! ${this.id}`)
|
throw new Error(`Could not find layout attached to this node! ${this.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.layoutState.update(state => {
|
|
||||||
if (typeof param === "string")
|
if (typeof param === "string")
|
||||||
state.attrs.queuePromptButtonName = param || ""
|
this.workflow.attrs.queuePromptButtonName = param || ""
|
||||||
else if (typeof param === "object" && "buttonName" in param)
|
else if (typeof param === "object" && "buttonName" in param)
|
||||||
state.attrs.queuePromptButtonName = param.buttonName || ""
|
this.workflow.attrs.queuePromptButtonName = param.buttonName || ""
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { get } from "svelte/store";
|
|||||||
import configState from "$lib/stores/configState";
|
import configState from "$lib/stores/configState";
|
||||||
import type { WritableLayoutStateStore } from "$lib/stores/layoutStates";
|
import type { WritableLayoutStateStore } from "$lib/stores/layoutStates";
|
||||||
import layoutStates from "$lib/stores/layoutStates";
|
import layoutStates from "$lib/stores/layoutStates";
|
||||||
|
import workflowStateStore, { ComfyWorkflow } from "$lib/stores/workflowState";
|
||||||
|
|
||||||
export type DefaultWidgetSpec = {
|
export type DefaultWidgetSpec = {
|
||||||
defaultWidgetNode: new (name?: string) => ComfyWidgetNode,
|
defaultWidgetNode: new (name?: string) => ComfyWidgetNode,
|
||||||
@@ -106,6 +107,10 @@ export default class ComfyGraphNode extends LGraphNode {
|
|||||||
return layoutStates.getLayoutByNode(this);
|
return layoutStates.getLayoutByNode(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get workflow(): ComfyWorkflow | null {
|
||||||
|
return workflowStateStore.getWorkflowByNode(this);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(title?: string) {
|
constructor(title?: string) {
|
||||||
super(title)
|
super(title)
|
||||||
this.addProperty("tags", [], "array")
|
this.addProperty("tags", [], "array")
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { v4 as uuidv4 } from "uuid";
|
|||||||
import type { ComfyWidgetNode } from '$lib/nodes/widgets';
|
import type { ComfyWidgetNode } from '$lib/nodes/widgets';
|
||||||
import type { ComfyWorkflow, WorkflowInstID } from '$lib/components/ComfyApp';
|
import type { ComfyWorkflow, WorkflowInstID } from '$lib/components/ComfyApp';
|
||||||
import type ComfyGraph from '$lib/ComfyGraph';
|
import type ComfyGraph from '$lib/ComfyGraph';
|
||||||
|
import type { WorkflowAttributes } from './workflowState';
|
||||||
|
|
||||||
function isComfyWidgetNode(node: LGraphNode): node is ComfyWidgetNode {
|
function isComfyWidgetNode(node: LGraphNode): node is ComfyWidgetNode {
|
||||||
return "svelteComponentType" in node
|
return "svelteComponentType" in node
|
||||||
@@ -31,24 +32,6 @@ type DragItemEntry = {
|
|||||||
parent: IDragItem | null
|
parent: IDragItem | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Global workflow attributes
|
|
||||||
*/
|
|
||||||
export type LayoutAttributes = {
|
|
||||||
/*
|
|
||||||
* Name of the "Queue Prompt" button. Set to blank to hide the button.
|
|
||||||
*/
|
|
||||||
queuePromptButtonName: string,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If true, clicking the "Queue Prompt" button will run the default
|
|
||||||
* subgraph. Set this to false if you need special behavior before running
|
|
||||||
* any subgraphs, and instead use the `onDefaultQueueAction` event of the
|
|
||||||
* Comfy.QueueEvents node.
|
|
||||||
*/
|
|
||||||
queuePromptButtonRunWorkflow: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keeps track of the tree of UI components - widgets and the containers that
|
* Keeps track of the tree of UI components - widgets and the containers that
|
||||||
* group them together.
|
* group them together.
|
||||||
@@ -82,11 +65,6 @@ export type LayoutState = {
|
|||||||
* If true, the right-click context menu is open
|
* If true, the right-click context menu is open
|
||||||
*/
|
*/
|
||||||
isMenuOpen: boolean,
|
isMenuOpen: boolean,
|
||||||
|
|
||||||
/*
|
|
||||||
* Global workflow attributes
|
|
||||||
*/
|
|
||||||
attrs: LayoutAttributes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,7 +163,7 @@ export type AttributesSpec = {
|
|||||||
* - "widget": inside IDragNode.attrs
|
* - "widget": inside IDragNode.attrs
|
||||||
* - "nodeProps": inside LGraphNode.properties
|
* - "nodeProps": inside LGraphNode.properties
|
||||||
* - "nodeVars": an instance variable directly on an LGraphNode
|
* - "nodeVars": an instance variable directly on an LGraphNode
|
||||||
* - "workflow": inside $layoutState.attrs
|
* - "workflow": inside $workflowState.activeWorkflow.attrs
|
||||||
*/
|
*/
|
||||||
location: "widget" | "nodeProps" | "nodeVars" | "workflow"
|
location: "widget" | "nodeProps" | "nodeVars" | "workflow"
|
||||||
|
|
||||||
@@ -601,7 +579,7 @@ export { ALL_ATTRIBUTES };
|
|||||||
|
|
||||||
// TODO Should be nested by category for name uniqueness?
|
// TODO Should be nested by category for name uniqueness?
|
||||||
const defaultWidgetAttributes: Attributes = {} as any
|
const defaultWidgetAttributes: Attributes = {} as any
|
||||||
const defaultWorkflowAttributes: LayoutAttributes = {} as any
|
export const defaultWorkflowAttributes: WorkflowAttributes = {} as any
|
||||||
for (const cat of Object.values(ALL_ATTRIBUTES)) {
|
for (const cat of Object.values(ALL_ATTRIBUTES)) {
|
||||||
for (const spec of Object.values(cat.specs)) {
|
for (const spec of Object.values(cat.specs)) {
|
||||||
if (spec.defaultValue != null) {
|
if (spec.defaultValue != null) {
|
||||||
@@ -697,7 +675,6 @@ type LayoutStateOps = {
|
|||||||
export type SerializedLayoutState = {
|
export type SerializedLayoutState = {
|
||||||
root: DragItemID | null,
|
root: DragItemID | null,
|
||||||
allItems: Record<DragItemID, SerializedDragEntry>,
|
allItems: Record<DragItemID, SerializedDragEntry>,
|
||||||
attrs: LayoutAttributes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SerializedDragEntry = {
|
export type SerializedDragEntry = {
|
||||||
@@ -726,9 +703,6 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
allItemsByNode: {},
|
allItemsByNode: {},
|
||||||
isMenuOpen: false,
|
isMenuOpen: false,
|
||||||
isConfiguring: true,
|
isConfiguring: true,
|
||||||
attrs: {
|
|
||||||
...defaultWorkflowAttributes
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
@@ -738,9 +712,6 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
allItemsByNode: {},
|
allItemsByNode: {},
|
||||||
isMenuOpen: false,
|
isMenuOpen: false,
|
||||||
isConfiguring: true,
|
isConfiguring: true,
|
||||||
attrs: {
|
|
||||||
...defaultWorkflowAttributes
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1063,9 +1034,6 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
allItemsByNode: {},
|
allItemsByNode: {},
|
||||||
isMenuOpen: false,
|
isMenuOpen: false,
|
||||||
isConfiguring: false,
|
isConfiguring: false,
|
||||||
attrs: {
|
|
||||||
...defaultWorkflowAttributes
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const root = addContainer(null, { direction: "horizontal", title: "" });
|
const root = addContainer(null, { direction: "horizontal", title: "" });
|
||||||
@@ -1100,7 +1068,6 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
return {
|
return {
|
||||||
root: state.root?.id,
|
root: state.root?.id,
|
||||||
allItems,
|
allItems,
|
||||||
attrs: state.attrs
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1156,7 +1123,6 @@ function create(workflow: ComfyWorkflow): WritableLayoutStateStore {
|
|||||||
allItemsByNode,
|
allItemsByNode,
|
||||||
isMenuOpen: false,
|
isMenuOpen: false,
|
||||||
isConfiguring: false,
|
isConfiguring: false,
|
||||||
attrs: { ...defaultWorkflowAttributes, ...data.attrs }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debug("[layoutState] deserialize", data, state, defaultWorkflowAttributes)
|
console.debug("[layoutState] deserialize", data, state, defaultWorkflowAttributes)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { SerializedGraphCanvasState } from '$lib/ComfyGraphCanvas';
|
import type { SerializedGraphCanvasState } from '$lib/ComfyGraphCanvas';
|
||||||
import { clamp, type LGraphCanvas, type NodeID, type SerializedLGraph, type UUID } from '@litegraph-ts/core';
|
import { clamp, LGraphNode, type LGraphCanvas, type NodeID, type SerializedLGraph, type UUID, LGraph } from '@litegraph-ts/core';
|
||||||
import { get, writable } from 'svelte/store';
|
import { get, writable } from 'svelte/store';
|
||||||
import type { Readable, Writable } from 'svelte/store';
|
import type { Readable, Writable } from 'svelte/store';
|
||||||
import type { SerializedLayoutState, WritableLayoutStateStore } from './layoutStates';
|
import { defaultWorkflowAttributes, type SerializedLayoutState, type WritableLayoutStateStore } from './layoutStates';
|
||||||
import ComfyGraph from '$lib/ComfyGraph';
|
import ComfyGraph from '$lib/ComfyGraph';
|
||||||
import layoutStates from './layoutStates';
|
import layoutStates from './layoutStates';
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
@@ -18,7 +18,8 @@ type ActiveCanvas = {
|
|||||||
|
|
||||||
export type SerializedWorkflowState = {
|
export type SerializedWorkflowState = {
|
||||||
graph: SerializedLGraph,
|
graph: SerializedLGraph,
|
||||||
layout: SerializedLayoutState
|
layout: SerializedLayoutState,
|
||||||
|
attrs: WorkflowAttributes
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -31,22 +32,45 @@ export type SerializedWorkflowState = {
|
|||||||
*/
|
*/
|
||||||
export type WorkflowInstID = UUID;
|
export type WorkflowInstID = UUID;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global workflow attributes
|
||||||
|
*/
|
||||||
|
export type WorkflowAttributes = {
|
||||||
|
/*
|
||||||
|
* Title of the workflow.
|
||||||
|
*/
|
||||||
|
title: string,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Name of the "Queue Prompt" button. Set to blank to hide the button.
|
||||||
|
*/
|
||||||
|
queuePromptButtonName: string,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If true, clicking the "Queue Prompt" button will run the default
|
||||||
|
* subgraph. Set this to false if you need special behavior before running
|
||||||
|
* any subgraphs, and instead use the `onDefaultQueueAction` event of the
|
||||||
|
* Comfy.QueueEvents node.
|
||||||
|
*/
|
||||||
|
queuePromptButtonRunWorkflow: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
export class ComfyWorkflow {
|
export class ComfyWorkflow {
|
||||||
/*
|
/*
|
||||||
* Used for uniquely identifying the instance of the opened workflow in the frontend.
|
* Used for uniquely identifying the instance of the opened workflow in the frontend.
|
||||||
*/
|
*/
|
||||||
id: WorkflowInstID;
|
id: WorkflowInstID;
|
||||||
|
|
||||||
/*
|
|
||||||
* Human-readable name on the tab
|
|
||||||
*/
|
|
||||||
title: string;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Graph of this workflow, whose nodes are bound to the UI layout
|
* Graph of this workflow, whose nodes are bound to the UI layout
|
||||||
*/
|
*/
|
||||||
graph: ComfyGraph;
|
graph: ComfyGraph;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global workflow attributes
|
||||||
|
*/
|
||||||
|
attrs: WorkflowAttributes
|
||||||
|
|
||||||
get layout(): WritableLayoutStateStore | null {
|
get layout(): WritableLayoutStateStore | null {
|
||||||
return layoutStates.getLayout(this.id)
|
return layoutStates.getLayout(this.id)
|
||||||
}
|
}
|
||||||
@@ -58,7 +82,10 @@ export class ComfyWorkflow {
|
|||||||
|
|
||||||
constructor(title: string) {
|
constructor(title: string) {
|
||||||
this.id = uuidv4();
|
this.id = uuidv4();
|
||||||
this.title = title;
|
this.attrs = {
|
||||||
|
...defaultWorkflowAttributes,
|
||||||
|
title,
|
||||||
|
}
|
||||||
this.graph = new ComfyGraph(this.id);
|
this.graph = new ComfyGraph(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +142,8 @@ export class ComfyWorkflow {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
graph: serializedGraph,
|
graph: serializedGraph,
|
||||||
layout: serializedLayout
|
layout: serializedLayout,
|
||||||
|
attrs: this.attrs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,6 +175,8 @@ export class ComfyWorkflow {
|
|||||||
// this.#invokeExtensions("loadedGraphNode", node);
|
// this.#invokeExtensions("loadedGraphNode", node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.attrs = data.attrs;
|
||||||
|
|
||||||
// Now restore the layout
|
// Now restore the layout
|
||||||
// Subsequent added nodes will add the UI data to layoutState
|
// Subsequent added nodes will add the UI data to layoutState
|
||||||
// TODO
|
// TODO
|
||||||
@@ -163,6 +193,8 @@ export type WorkflowState = {
|
|||||||
|
|
||||||
type WorkflowStateOps = {
|
type WorkflowStateOps = {
|
||||||
getWorkflow: (id: WorkflowInstID) => ComfyWorkflow | null
|
getWorkflow: (id: WorkflowInstID) => ComfyWorkflow | null
|
||||||
|
getWorkflowByGraph: (graph: LGraph) => ComfyWorkflow | null
|
||||||
|
getWorkflowByNode: (node: LGraphNode) => ComfyWorkflow | null
|
||||||
getWorkflowByNodeID: (id: NodeID) => ComfyWorkflow | null
|
getWorkflowByNodeID: (id: NodeID) => ComfyWorkflow | null
|
||||||
getActiveWorkflow: () => ComfyWorkflow | null
|
getActiveWorkflow: () => ComfyWorkflow | null
|
||||||
createNewWorkflow: (canvas: ComfyGraphCanvas, title?: string, setActive?: boolean) => ComfyWorkflow,
|
createNewWorkflow: (canvas: ComfyGraphCanvas, title?: string, setActive?: boolean) => ComfyWorkflow,
|
||||||
@@ -185,6 +217,16 @@ function getWorkflow(id: WorkflowInstID): ComfyWorkflow | null {
|
|||||||
return get(store).openedWorkflowsByID[id];
|
return get(store).openedWorkflowsByID[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWorkflowByGraph(graph: LGraph): ComfyWorkflow | null {
|
||||||
|
if ("workflowID" in graph && graph.workflowID != null)
|
||||||
|
return getWorkflow((graph as ComfyGraph).workflowID);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWorkflowByNode(node: LGraphNode): ComfyWorkflow | null {
|
||||||
|
return getWorkflowByGraph(node.graph);
|
||||||
|
}
|
||||||
|
|
||||||
function getWorkflowByNodeID(id: NodeID): ComfyWorkflow | null {
|
function getWorkflowByNodeID(id: NodeID): ComfyWorkflow | null {
|
||||||
return Object.values(get(store).openedWorkflows).find(w => {
|
return Object.values(get(store).openedWorkflows).find(w => {
|
||||||
return w.graph.getNodeByIdRecursive(id) != null
|
return w.graph.getNodeByIdRecursive(id) != null
|
||||||
@@ -216,8 +258,8 @@ function createNewWorkflow(canvas: ComfyGraphCanvas, title: string = "New Workfl
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openWorkflow(canvas: ComfyGraphCanvas, data: SerializedAppState): ComfyWorkflow {
|
function openWorkflow(canvas: ComfyGraphCanvas, data: SerializedAppState): ComfyWorkflow {
|
||||||
const [workflow, layoutState] = ComfyWorkflow.create(data.workflowName || "Workflow")
|
const [workflow, layoutState] = ComfyWorkflow.create("Workflow")
|
||||||
workflow.deserialize(layoutState, { graph: data.workflow, layout: data.layout })
|
workflow.deserialize(layoutState, { graph: data.workflow, layout: data.layout, attrs: data.attrs })
|
||||||
|
|
||||||
const state = get(store);
|
const state = get(store);
|
||||||
state.openedWorkflows.push(workflow);
|
state.openedWorkflows.push(workflow);
|
||||||
@@ -285,6 +327,8 @@ const workflowStateStore: WritableWorkflowStateStore =
|
|||||||
{
|
{
|
||||||
...store,
|
...store,
|
||||||
getWorkflow,
|
getWorkflow,
|
||||||
|
getWorkflowByGraph,
|
||||||
|
getWorkflowByNode,
|
||||||
getWorkflowByNodeID,
|
getWorkflowByNodeID,
|
||||||
getActiveWorkflow,
|
getActiveWorkflow,
|
||||||
createNewWorkflow,
|
createNewWorkflow,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { type WidgetLayout, type WritableLayoutStateStore } from "$lib/stores/layoutStates";
|
import { type WidgetLayout, type WritableLayoutStateStore } from "$lib/stores/layoutStates";
|
||||||
import selectionState from "$lib/stores/selectionState";
|
import selectionState from "$lib/stores/selectionState";
|
||||||
import type { FileData as GradioFileData } from "@gradio/upload";
|
import type { FileData as GradioFileData } from "@gradio/upload";
|
||||||
import { Subgraph, type LGraph, type LGraphNode, type LLink, type SerializedLGraph, type UUID } from "@litegraph-ts/core";
|
import { Subgraph, type LGraph, type LGraphNode, type LLink, type SerializedLGraph, type UUID, type NodeID } from "@litegraph-ts/core";
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
import type { ComfyNodeID } from "./api";
|
import type { ComfyNodeID } from "./api";
|
||||||
import { type SerializedPrompt } from "./components/ComfyApp";
|
import { type SerializedPrompt } from "./components/ComfyApp";
|
||||||
@@ -176,23 +176,33 @@ export function workflowToGraphVis(workflow: SerializedLGraph): string {
|
|||||||
export function promptToGraphVis(prompt: SerializedPrompt): string {
|
export function promptToGraphVis(prompt: SerializedPrompt): string {
|
||||||
let out = "digraph {\n"
|
let out = "digraph {\n"
|
||||||
|
|
||||||
|
const ids: Record<NodeID, number> = {}
|
||||||
|
let nextID = 0;
|
||||||
|
|
||||||
for (const pair of Object.entries(prompt.output)) {
|
for (const pair of Object.entries(prompt.output)) {
|
||||||
const [id, o] = pair;
|
const [id, o] = pair;
|
||||||
const outNode = prompt.workflow.nodes.find(n => n.id == id)
|
if (ids[id] == null)
|
||||||
if (outNode) {
|
ids[id] = nextID++;
|
||||||
|
|
||||||
|
if ("class_type" in o) {
|
||||||
for (const pair2 of Object.entries(o.inputs)) {
|
for (const pair2 of Object.entries(o.inputs)) {
|
||||||
const [inpName, i] = pair2;
|
const [inpName, i] = pair2;
|
||||||
|
|
||||||
if (Array.isArray(i) && i.length === 2 && typeof i[0] === "string" && typeof i[1] === "number") {
|
if (Array.isArray(i) && i.length === 2 && typeof i[0] === "string" && typeof i[1] === "number") {
|
||||||
// Link
|
// Link
|
||||||
const inpNode = prompt.workflow.nodes.find(n => n.id == i[0])
|
const [inpID, inpSlot] = i;
|
||||||
|
if (ids[inpID] == null)
|
||||||
|
ids[inpID] = nextID++;
|
||||||
|
|
||||||
|
const inpNode = prompt.output[inpID]
|
||||||
if (inpNode) {
|
if (inpNode) {
|
||||||
out += `"${inpNode.title}" -> "${outNode.title}"\n`
|
out += `"${ids[inpID]}_${inpNode.class_type}" -> "${ids[id]}_${o.class_type}"\n`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
const value = String(i).substring(0, 20)
|
||||||
// Value
|
// Value
|
||||||
out += `"${id}-${inpName}-${i}" -> "${outNode.title}"\n`
|
out += `"${ids[id]}-${inpName}-${value}" -> "${ids[id]}_${o.class_type}"\n`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
||||||
import queueState from "$lib/stores/queueState";
|
import queueState from "$lib/stores/queueState";
|
||||||
import workflowState from "$lib/stores/workflowState";
|
import workflowState, { ComfyWorkflow } from "$lib/stores/workflowState";
|
||||||
import { getNodeInfo } from "$lib/utils"
|
import { getNodeInfo } from "$lib/utils"
|
||||||
|
|
||||||
import { Link, Toolbar } from "framework7-svelte"
|
import { Link, Toolbar } from "framework7-svelte"
|
||||||
@@ -14,8 +14,9 @@
|
|||||||
export let app: ComfyApp = undefined;
|
export let app: ComfyApp = undefined;
|
||||||
let layoutState: WritableLayoutStateStore = null;
|
let layoutState: WritableLayoutStateStore = null;
|
||||||
let fileInput: HTMLInputElement = undefined;
|
let fileInput: HTMLInputElement = undefined;
|
||||||
|
let workflow: ComfyWorkflow | null = null;
|
||||||
|
|
||||||
$: layoutState = $workflowState.activeWorkflow?.layout;
|
$: workflow = $workflowState.activeWorkflow;
|
||||||
|
|
||||||
function queuePrompt() {
|
function queuePrompt() {
|
||||||
navigator.vibrate(20)
|
navigator.vibrate(20)
|
||||||
@@ -72,9 +73,9 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<Toolbar bottom>
|
<Toolbar bottom>
|
||||||
{#if $layoutState.attrs.queuePromptButtonName != ""}
|
{#if workflow != null && workflow.attrs.queuePromptButtonName != ""}
|
||||||
<Link on:click={queuePrompt}>
|
<Link on:click={queuePrompt}>
|
||||||
{$layoutState.attrs.queuePromptButtonName}
|
{workflow.attrs.queuePromptButtonName}
|
||||||
</Link>
|
</Link>
|
||||||
{/if}
|
{/if}
|
||||||
<Link on:click={refreshCombos}>🔄</Link>
|
<Link on:click={refreshCombos}>🔄</Link>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { graphToGraphVis } from "$lib/utils";
|
|||||||
import { ComfyNumberNode } from "$lib/nodes/widgets";
|
import { ComfyNumberNode } from "$lib/nodes/widgets";
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
import layoutStates from "$lib/stores/layoutStates";
|
import layoutStates from "$lib/stores/layoutStates";
|
||||||
import { ComfyWorkflow } from "$lib/components/ComfyApp";
|
import { ComfyWorkflow } from "$lib/stores/workflowState";
|
||||||
|
|
||||||
export default class ComfyGraphTests extends UnitTest {
|
export default class ComfyGraphTests extends UnitTest {
|
||||||
test__onNodeAdded__updatesLayoutState() {
|
test__onNodeAdded__updatesLayoutState() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { LGraph, LiteGraph, Subgraph, type SlotLayout } from "@litegraph-ts/core"
|
import { LGraph, LiteGraph, Subgraph, type SlotLayout, NodeMode } from "@litegraph-ts/core"
|
||||||
import { Watch } from "@litegraph-ts/nodes-basic"
|
import { Watch } from "@litegraph-ts/nodes-basic"
|
||||||
import { expect } from 'vitest'
|
import { expect } from 'vitest'
|
||||||
import UnitTest from "./UnitTest"
|
import UnitTest from "./UnitTest"
|
||||||
@@ -139,6 +139,39 @@ export default class ComfyPromptSerializerTests extends UnitTest {
|
|||||||
expect(result.output[output.id].inputs).toEqual({})
|
expect(result.output[output.id].inputs).toEqual({})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test__serialize__shouldIgnoreInactiveSubgraphs() {
|
||||||
|
const ser = new ComfyPromptSerializer();
|
||||||
|
const graph = new ComfyGraph();
|
||||||
|
|
||||||
|
const output = LiteGraph.createNode(MockBackendOutput)
|
||||||
|
const link = LiteGraph.createNode(MockBackendLink)
|
||||||
|
const input = LiteGraph.createNode(MockBackendInput)
|
||||||
|
|
||||||
|
const subgraph = LiteGraph.createNode(Subgraph)
|
||||||
|
const graphInput = subgraph.addGraphInput("testIn", "number")
|
||||||
|
const graphOutput = subgraph.addGraphOutput("testOut", "number")
|
||||||
|
|
||||||
|
graph.add(subgraph)
|
||||||
|
graph.add(output)
|
||||||
|
subgraph.subgraph.add(link)
|
||||||
|
graph.add(input)
|
||||||
|
|
||||||
|
output.connect(0, subgraph, 0)
|
||||||
|
graphInput.innerNode.connect(0, link, 0)
|
||||||
|
link.connect(0, graphOutput.innerNode, 0)
|
||||||
|
subgraph.connect(0, input, 0)
|
||||||
|
|
||||||
|
subgraph.mode = NodeMode.NEVER;
|
||||||
|
|
||||||
|
const result = ser.serialize(graph)
|
||||||
|
|
||||||
|
expect(Object.keys(result.output)).toHaveLength(3);
|
||||||
|
expect(result.output[input.id].inputs["in"]).toBeUndefined();
|
||||||
|
expect(result.output[link.id].inputs["in"]).toBeInstanceOf(Array)
|
||||||
|
expect(result.output[link.id].inputs["in"][0]).toEqual(output.id)
|
||||||
|
expect(result.output[output.id].inputs).toEqual({})
|
||||||
|
}
|
||||||
|
|
||||||
test__serialize__shouldFollowSubgraphsRecursively() {
|
test__serialize__shouldFollowSubgraphsRecursively() {
|
||||||
const ser = new ComfyPromptSerializer();
|
const ser = new ComfyPromptSerializer();
|
||||||
const graph = new ComfyGraph();
|
const graph = new ComfyGraph();
|
||||||
|
|||||||
Reference in New Issue
Block a user