diff --git a/src/lib/components/ComfyApp.ts b/src/lib/components/ComfyApp.ts index 904501b..e2a2daf 100644 --- a/src/lib/components/ComfyApp.ts +++ b/src/lib/components/ComfyApp.ts @@ -200,6 +200,10 @@ export default class ComfyApp { await this.initDefaultGraph(); } + workflowState.createNewWorkflow(this.lCanvas); + workflowState.createNewWorkflow(this.lCanvas); + workflowState.createNewWorkflow(this.lCanvas); + // Save current workflow automatically // setInterval(this.saveStateToLocalStorage.bind(this), 1000); @@ -503,7 +507,7 @@ export default class ComfyApp { } this.clean(); - const workflow = workflowState.openWorkflow(data); + const workflow = workflowState.openWorkflow(this.lCanvas, data); // Restore canvas offset/zoom this.lCanvas.deserialize(data.canvas) @@ -514,13 +518,7 @@ export default class ComfyApp { } setActiveWorkflow(index: number) { - const workflow = workflowState.setActiveWorkflow(index); - - if (workflow != null) { - workflow.start("app", this.lCanvas); - this.lCanvas.deserialize(workflow.canvases["app"].state) - } - + workflowState.setActiveWorkflow(this.lCanvas, index); selectionState.clear(); } @@ -542,7 +540,7 @@ export default class ComfyApp { this.clean(); this.lCanvas.closeAllSubgraphs(); - workflowState.closeAllWorkflows(); + workflowState.closeAllWorkflows(this.lCanvas); uiState.update(s => { s.uiUnlocked = true; s.uiEditMode = "widgets"; diff --git a/src/lib/components/ComfyWorkflowsView.svelte b/src/lib/components/ComfyWorkflowsView.svelte index 186cd85..8949dea 100644 --- a/src/lib/components/ComfyWorkflowsView.svelte +++ b/src/lib/components/ComfyWorkflowsView.svelte @@ -187,7 +187,9 @@
{#each $workflowState.openedWorkflows as workflow, index} - {/each} diff --git a/src/lib/init.ts b/src/lib/init.ts index fb22eb5..9a4513d 100644 --- a/src/lib/init.ts +++ b/src/lib/init.ts @@ -2,6 +2,7 @@ import ComfyGraph from '$lib/ComfyGraph'; import { LGraphCanvas, LiteGraph, Subgraph } from '@litegraph-ts/core'; import layoutStates from './stores/layoutStates'; import { get } from 'svelte/store'; +import workflowState from './stores/workflowState'; export function configureLitegraph(isMobile: boolean = false) { LiteGraph.catch_exceptions = false; @@ -28,5 +29,7 @@ export function configureLitegraph(isMobile: boolean = false) { (window as any).LiteGraph = LiteGraph; (window as any).LGraphCanvas = LGraphCanvas; - (window as any).layoutStates = get(layoutStates) + (window as any).layoutStates = layoutStates; + (window as any).workflowState = workflowState; + (window as any).svelteGet = get; } diff --git a/src/lib/nodes/ComfyActionNodes.ts b/src/lib/nodes/ComfyActionNodes.ts index 5dcac65..1d286a7 100644 --- a/src/lib/nodes/ComfyActionNodes.ts +++ b/src/lib/nodes/ComfyActionNodes.ts @@ -488,7 +488,7 @@ export class ComfySetNodeModeAdvancedAction extends ComfyGraphNode { } } - for (const entry of Object.values(get(layoutState).allItems)) { + for (const entry of Object.values(get(this.layoutState).allItems)) { if (entry.dragItem.type === "container") { const container = entry.dragItem; const hasTag = container.attrs.tags.indexOf(action.tag) != -1; @@ -532,7 +532,7 @@ export class ComfySetNodeModeAdvancedAction extends ComfyGraphNode { this.graph.getNodeByIdRecursive(nodeId).changeMode(newMode); } - const layout = get(layoutState); + const layout = get(this.layoutState); for (const [dragItemID, isHidden] of Object.entries(widgetChanges)) { const container = layout.allItems[dragItemID].dragItem container.attrs.hidden = isHidden; diff --git a/src/lib/stores/layoutStates.ts b/src/lib/stores/layoutStates.ts index 8ab102d..07358d0 100644 --- a/src/lib/stores/layoutStates.ts +++ b/src/lib/stores/layoutStates.ts @@ -1233,7 +1233,14 @@ function getLayoutByNode(node: LGraphNode): WritableLayoutStateStore | null { } export type LayoutStateStores = { + /* + * Layouts associated with opened workflows + */ all: Record, + + /* + * Increment to force Svelte to re-render the props panel + */ refreshPropsPanel: number } @@ -1249,10 +1256,6 @@ export type WritableLayoutStateStores = Writable & LayoutStat const store = writable({ all: {}, - - /* - * Increment to force Svelte to re-render the props panel - */ refreshPropsPanel: 0 }) diff --git a/src/lib/stores/queueState.ts b/src/lib/stores/queueState.ts index 6c085f2..4f563c2 100644 --- a/src/lib/stores/queueState.ts +++ b/src/lib/stores/queueState.ts @@ -344,7 +344,7 @@ function onExecuted(promptID: PromptID, nodeID: ComfyNodeID, outputs: ComfyExecu entry_ = entry; return s }) - return entry; + return entry_; } const queueStateStore: WritableQueueStateStore = diff --git a/src/lib/stores/workflowState.ts b/src/lib/stores/workflowState.ts index 55850c3..675b1f8 100644 --- a/src/lib/stores/workflowState.ts +++ b/src/lib/stores/workflowState.ts @@ -1,5 +1,5 @@ import type { SerializedGraphCanvasState } from '$lib/ComfyGraphCanvas'; -import type { LGraphCanvas, SerializedLGraph, UUID } from '@litegraph-ts/core'; +import type { LGraphCanvas, NodeID, SerializedLGraph, UUID } from '@litegraph-ts/core'; import { get, writable } from 'svelte/store'; import type { Readable, Writable } from 'svelte/store'; import type { SerializedLayoutState, WritableLayoutStateStore } from './layoutStates'; @@ -36,13 +36,24 @@ export class ComfyWorkflow { * Used for uniquely identifying the instance of the opened workflow in the frontend. */ id: WorkflowInstID; + + /* + * Human-readable name on the tab + */ title: string; + + /* + * Graph of this workflow, whose nodes are bound to the UI layout + */ graph: ComfyGraph; get layout(): WritableLayoutStateStore | null { return layoutStates.getLayout(this.id) } + /* + * Graph canvases attached to the graph of this workflow + */ canvases: Record = {}; constructor(title: string) { @@ -150,12 +161,13 @@ export type WorkflowState = { type WorkflowStateOps = { getWorkflow: (id: WorkflowInstID) => ComfyWorkflow | null + getWorkflowByNodeID: (id: NodeID) => ComfyWorkflow | null getActiveWorkflow: () => ComfyWorkflow | null - createNewWorkflow: () => ComfyWorkflow, - openWorkflow: (data: SerializedAppState) => ComfyWorkflow, - closeWorkflow: (index: number) => void, - closeAllWorkflows: () => void, - setActiveWorkflow: (index: number) => ComfyWorkflow | null + createNewWorkflow: (canvas: ComfyGraphCanvas, setActive?: boolean) => ComfyWorkflow, + openWorkflow: (canvas: ComfyGraphCanvas, data: SerializedAppState) => ComfyWorkflow, + closeWorkflow: (canvas: ComfyGraphCanvas, index: number) => void, + closeAllWorkflows: (canvas: ComfyGraphCanvas) => void, + setActiveWorkflow: (canvas: ComfyGraphCanvas, index: number) => ComfyWorkflow | null } export type WritableWorkflowStateStore = Writable & WorkflowStateOps; @@ -163,13 +175,20 @@ const store: Writable = writable( { openedWorkflows: [], openedWorkflowsByID: {}, - activeWorkflowIdx: -1 + activeWorkflowIdx: -1, + activeWorkflow: null }) function getWorkflow(id: WorkflowInstID): ComfyWorkflow | null { return get(store).openedWorkflowsByID[id]; } +function getWorkflowByNodeID(id: NodeID): ComfyWorkflow | null { + return Object.values(get(store).openedWorkflows).find(w => { + return w.graph.getNodeByIdRecursive(id) != null + }) +} + function getActiveWorkflow(): ComfyWorkflow | null { const state = get(store); if (state.activeWorkflowIdx === -1) @@ -177,34 +196,36 @@ function getActiveWorkflow(): ComfyWorkflow | null { return state.openedWorkflows[state.activeWorkflowIdx]; } -function createNewWorkflow(): ComfyWorkflow { +function createNewWorkflow(canvas: ComfyGraphCanvas, setActive: boolean = false): ComfyWorkflow { const workflow = new ComfyWorkflow("Workflow X"); const layoutState = layoutStates.create(workflow); - workflow.deserialize(layoutState, { graph: blankGraph.workflow, layout: blankGraph.layout }) + layoutState.initDefaultLayout(); const state = get(store); - this.openedWorkflows.push(workflow); - setActiveWorkflow(state.openedWorkflows.length - 1) + state.openedWorkflows.push(workflow); + + if (setActive) + setActiveWorkflow(canvas, state.openedWorkflows.length - 1) store.set(state) return workflow; } -function openWorkflow(data: SerializedAppState): ComfyWorkflow { +function openWorkflow(canvas: ComfyGraphCanvas, data: SerializedAppState): ComfyWorkflow { const [workflow, layoutState] = ComfyWorkflow.create("Workflow X") workflow.deserialize(layoutState, { graph: data.workflow, layout: data.layout }) const state = get(store); state.openedWorkflows.push(workflow); - setActiveWorkflow(state.openedWorkflows.length - 1) + setActiveWorkflow(canvas, state.openedWorkflows.length - 1) store.set(state) return workflow; } -function closeWorkflow(index: number) { +function closeWorkflow(canvas: ComfyGraphCanvas, index: number) { const state = get(store); if (index < 0 || index >= state.openedWorkflows.length) @@ -213,19 +234,21 @@ function closeWorkflow(index: number) { const workflow = state.openedWorkflows[index]; workflow.stopAll(); + layoutStates.remove(workflow.id) + state.openedWorkflows.splice(index, 1) - setActiveWorkflow(0); + setActiveWorkflow(canvas, 0); store.set(state); } -function closeAllWorkflows() { +function closeAllWorkflows(canvas: ComfyGraphCanvas) { const state = get(store) while (state.openedWorkflows.length > 0) - closeWorkflow(0) + closeWorkflow(canvas, 0) } -function setActiveWorkflow(index: number): ComfyWorkflow | null { +function setActiveWorkflow(canvas: ComfyGraphCanvas, index: number): ComfyWorkflow | null { const state = get(store); if (state.openedWorkflows.length === 0) { @@ -244,6 +267,11 @@ function setActiveWorkflow(index: number): ComfyWorkflow | null { state.activeWorkflowIdx = index; state.activeWorkflow = workflow; + workflow.start("app", canvas); + canvas.deserialize(workflow.canvases["app"].state) + + store.set(state) + return workflow; } @@ -251,6 +279,7 @@ const workflowStateStore: WritableWorkflowStateStore = { ...store, getWorkflow, + getWorkflowByNodeID, getActiveWorkflow, createNewWorkflow, openWorkflow, diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 21d349d..da2147d 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -203,14 +203,15 @@ export function promptToGraphVis(prompt: SerializedPrompt): string { } export function getNodeInfo(nodeId: ComfyNodeID): string { - let app = (window as any).app; - if (!app?.activeGraph) - return String(nodeId); + const workflow = workflowState.getWorkflowByNodeID(nodeId); + if (workflow == null) + return nodeId; + + const title = workflow.graph?.getNodeByIdRecursive(nodeId)?.title; + if (title == null) + return nodeId; const displayNodeID = nodeId ? (nodeId.split("-")[0]) : String(nodeId); - - const workflow = workflowState.getActiveWorkflow(); - const title = workflow?.graph?.getNodeByIdRecursive(nodeId)?.title || String(nodeId); return title + " (" + displayNodeID + ")" }