Switch between workflows

This commit is contained in:
space-nuko
2023-05-20 20:27:43 -05:00
parent 61d9803e17
commit 1ca069b35f
8 changed files with 78 additions and 42 deletions

View File

@@ -200,6 +200,10 @@ export default class ComfyApp {
await this.initDefaultGraph(); await this.initDefaultGraph();
} }
workflowState.createNewWorkflow(this.lCanvas);
workflowState.createNewWorkflow(this.lCanvas);
workflowState.createNewWorkflow(this.lCanvas);
// Save current workflow automatically // Save current workflow automatically
// setInterval(this.saveStateToLocalStorage.bind(this), 1000); // setInterval(this.saveStateToLocalStorage.bind(this), 1000);
@@ -503,7 +507,7 @@ export default class ComfyApp {
} }
this.clean(); this.clean();
const workflow = workflowState.openWorkflow(data); const workflow = workflowState.openWorkflow(this.lCanvas, data);
// Restore canvas offset/zoom // Restore canvas offset/zoom
this.lCanvas.deserialize(data.canvas) this.lCanvas.deserialize(data.canvas)
@@ -514,13 +518,7 @@ export default class ComfyApp {
} }
setActiveWorkflow(index: number) { setActiveWorkflow(index: number) {
const workflow = workflowState.setActiveWorkflow(index); workflowState.setActiveWorkflow(this.lCanvas, index);
if (workflow != null) {
workflow.start("app", this.lCanvas);
this.lCanvas.deserialize(workflow.canvases["app"].state)
}
selectionState.clear(); selectionState.clear();
} }
@@ -542,7 +540,7 @@ export default class ComfyApp {
this.clean(); this.clean();
this.lCanvas.closeAllSubgraphs(); this.lCanvas.closeAllSubgraphs();
workflowState.closeAllWorkflows(); workflowState.closeAllWorkflows(this.lCanvas);
uiState.update(s => { uiState.update(s => {
s.uiUnlocked = true; s.uiUnlocked = true;
s.uiEditMode = "widgets"; s.uiEditMode = "widgets";

View File

@@ -187,7 +187,9 @@
</Splitpanes> </Splitpanes>
<div id="workflow-tabs"> <div id="workflow-tabs">
{#each $workflowState.openedWorkflows as workflow, index} {#each $workflowState.openedWorkflows as workflow, index}
<button class="workflow-tab" class:selected={index === $workflowState.activeWorkflowIdx}> <button class="workflow-tab"
class:selected={index === $workflowState.activeWorkflowIdx}
on:click={() => app.setActiveWorkflow(index)}>
{workflow.title} {workflow.title}
</button> </button>
{/each} {/each}

View File

@@ -2,6 +2,7 @@ import ComfyGraph from '$lib/ComfyGraph';
import { LGraphCanvas, LiteGraph, Subgraph } from '@litegraph-ts/core'; import { LGraphCanvas, LiteGraph, Subgraph } from '@litegraph-ts/core';
import layoutStates from './stores/layoutStates'; import layoutStates from './stores/layoutStates';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import workflowState from './stores/workflowState';
export function configureLitegraph(isMobile: boolean = false) { export function configureLitegraph(isMobile: boolean = false) {
LiteGraph.catch_exceptions = false; LiteGraph.catch_exceptions = false;
@@ -28,5 +29,7 @@ export function configureLitegraph(isMobile: boolean = false) {
(window as any).LiteGraph = LiteGraph; (window as any).LiteGraph = LiteGraph;
(window as any).LGraphCanvas = LGraphCanvas; (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;
} }

View File

@@ -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") { if (entry.dragItem.type === "container") {
const container = entry.dragItem; const container = entry.dragItem;
const hasTag = container.attrs.tags.indexOf(action.tag) != -1; const hasTag = container.attrs.tags.indexOf(action.tag) != -1;
@@ -532,7 +532,7 @@ export class ComfySetNodeModeAdvancedAction extends ComfyGraphNode {
this.graph.getNodeByIdRecursive(nodeId).changeMode(newMode); this.graph.getNodeByIdRecursive(nodeId).changeMode(newMode);
} }
const layout = get(layoutState); const layout = get(this.layoutState);
for (const [dragItemID, isHidden] of Object.entries(widgetChanges)) { for (const [dragItemID, isHidden] of Object.entries(widgetChanges)) {
const container = layout.allItems[dragItemID].dragItem const container = layout.allItems[dragItemID].dragItem
container.attrs.hidden = isHidden; container.attrs.hidden = isHidden;

View File

@@ -1233,7 +1233,14 @@ function getLayoutByNode(node: LGraphNode): WritableLayoutStateStore | null {
} }
export type LayoutStateStores = { export type LayoutStateStores = {
/*
* Layouts associated with opened workflows
*/
all: Record<WorkflowInstID, WritableLayoutStateStore>, all: Record<WorkflowInstID, WritableLayoutStateStore>,
/*
* Increment to force Svelte to re-render the props panel
*/
refreshPropsPanel: number refreshPropsPanel: number
} }
@@ -1249,10 +1256,6 @@ export type WritableLayoutStateStores = Writable<LayoutStateStores> & LayoutStat
const store = writable({ const store = writable({
all: {}, all: {},
/*
* Increment to force Svelte to re-render the props panel
*/
refreshPropsPanel: 0 refreshPropsPanel: 0
}) })

View File

@@ -344,7 +344,7 @@ function onExecuted(promptID: PromptID, nodeID: ComfyNodeID, outputs: ComfyExecu
entry_ = entry; entry_ = entry;
return s return s
}) })
return entry; return entry_;
} }
const queueStateStore: WritableQueueStateStore = const queueStateStore: WritableQueueStateStore =

View File

@@ -1,5 +1,5 @@
import type { SerializedGraphCanvasState } from '$lib/ComfyGraphCanvas'; 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 { 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 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. * 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; title: string;
/*
* Graph of this workflow, whose nodes are bound to the UI layout
*/
graph: ComfyGraph; graph: ComfyGraph;
get layout(): WritableLayoutStateStore | null { get layout(): WritableLayoutStateStore | null {
return layoutStates.getLayout(this.id) return layoutStates.getLayout(this.id)
} }
/*
* Graph canvases attached to the graph of this workflow
*/
canvases: Record<string, ActiveCanvas> = {}; canvases: Record<string, ActiveCanvas> = {};
constructor(title: string) { constructor(title: string) {
@@ -150,12 +161,13 @@ export type WorkflowState = {
type WorkflowStateOps = { type WorkflowStateOps = {
getWorkflow: (id: WorkflowInstID) => ComfyWorkflow | null getWorkflow: (id: WorkflowInstID) => ComfyWorkflow | null
getWorkflowByNodeID: (id: NodeID) => ComfyWorkflow | null
getActiveWorkflow: () => ComfyWorkflow | null getActiveWorkflow: () => ComfyWorkflow | null
createNewWorkflow: () => ComfyWorkflow, createNewWorkflow: (canvas: ComfyGraphCanvas, setActive?: boolean) => ComfyWorkflow,
openWorkflow: (data: SerializedAppState) => ComfyWorkflow, openWorkflow: (canvas: ComfyGraphCanvas, data: SerializedAppState) => ComfyWorkflow,
closeWorkflow: (index: number) => void, closeWorkflow: (canvas: ComfyGraphCanvas, index: number) => void,
closeAllWorkflows: () => void, closeAllWorkflows: (canvas: ComfyGraphCanvas) => void,
setActiveWorkflow: (index: number) => ComfyWorkflow | null setActiveWorkflow: (canvas: ComfyGraphCanvas, index: number) => ComfyWorkflow | null
} }
export type WritableWorkflowStateStore = Writable<WorkflowState> & WorkflowStateOps; export type WritableWorkflowStateStore = Writable<WorkflowState> & WorkflowStateOps;
@@ -163,13 +175,20 @@ const store: Writable<WorkflowState> = writable(
{ {
openedWorkflows: [], openedWorkflows: [],
openedWorkflowsByID: {}, openedWorkflowsByID: {},
activeWorkflowIdx: -1 activeWorkflowIdx: -1,
activeWorkflow: null
}) })
function getWorkflow(id: WorkflowInstID): ComfyWorkflow | null { function getWorkflow(id: WorkflowInstID): ComfyWorkflow | null {
return get(store).openedWorkflowsByID[id]; 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 { function getActiveWorkflow(): ComfyWorkflow | null {
const state = get(store); const state = get(store);
if (state.activeWorkflowIdx === -1) if (state.activeWorkflowIdx === -1)
@@ -177,34 +196,36 @@ function getActiveWorkflow(): ComfyWorkflow | null {
return state.openedWorkflows[state.activeWorkflowIdx]; return state.openedWorkflows[state.activeWorkflowIdx];
} }
function createNewWorkflow(): ComfyWorkflow { function createNewWorkflow(canvas: ComfyGraphCanvas, setActive: boolean = false): ComfyWorkflow {
const workflow = new ComfyWorkflow("Workflow X"); const workflow = new ComfyWorkflow("Workflow X");
const layoutState = layoutStates.create(workflow); const layoutState = layoutStates.create(workflow);
workflow.deserialize(layoutState, { graph: blankGraph.workflow, layout: blankGraph.layout }) layoutState.initDefaultLayout();
const state = get(store); const state = get(store);
this.openedWorkflows.push(workflow); state.openedWorkflows.push(workflow);
setActiveWorkflow(state.openedWorkflows.length - 1)
if (setActive)
setActiveWorkflow(canvas, state.openedWorkflows.length - 1)
store.set(state) store.set(state)
return workflow; return workflow;
} }
function openWorkflow(data: SerializedAppState): ComfyWorkflow { function openWorkflow(canvas: ComfyGraphCanvas, data: SerializedAppState): ComfyWorkflow {
const [workflow, layoutState] = ComfyWorkflow.create("Workflow X") const [workflow, layoutState] = ComfyWorkflow.create("Workflow X")
workflow.deserialize(layoutState, { graph: data.workflow, layout: data.layout }) workflow.deserialize(layoutState, { graph: data.workflow, layout: data.layout })
const state = get(store); const state = get(store);
state.openedWorkflows.push(workflow); state.openedWorkflows.push(workflow);
setActiveWorkflow(state.openedWorkflows.length - 1) setActiveWorkflow(canvas, state.openedWorkflows.length - 1)
store.set(state) store.set(state)
return workflow; return workflow;
} }
function closeWorkflow(index: number) { function closeWorkflow(canvas: ComfyGraphCanvas, index: number) {
const state = get(store); const state = get(store);
if (index < 0 || index >= state.openedWorkflows.length) if (index < 0 || index >= state.openedWorkflows.length)
@@ -213,19 +234,21 @@ function closeWorkflow(index: number) {
const workflow = state.openedWorkflows[index]; const workflow = state.openedWorkflows[index];
workflow.stopAll(); workflow.stopAll();
layoutStates.remove(workflow.id)
state.openedWorkflows.splice(index, 1) state.openedWorkflows.splice(index, 1)
setActiveWorkflow(0); setActiveWorkflow(canvas, 0);
store.set(state); store.set(state);
} }
function closeAllWorkflows() { function closeAllWorkflows(canvas: ComfyGraphCanvas) {
const state = get(store) const state = get(store)
while (state.openedWorkflows.length > 0) 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); const state = get(store);
if (state.openedWorkflows.length === 0) { if (state.openedWorkflows.length === 0) {
@@ -244,6 +267,11 @@ function setActiveWorkflow(index: number): ComfyWorkflow | null {
state.activeWorkflowIdx = index; state.activeWorkflowIdx = index;
state.activeWorkflow = workflow; state.activeWorkflow = workflow;
workflow.start("app", canvas);
canvas.deserialize(workflow.canvases["app"].state)
store.set(state)
return workflow; return workflow;
} }
@@ -251,6 +279,7 @@ const workflowStateStore: WritableWorkflowStateStore =
{ {
...store, ...store,
getWorkflow, getWorkflow,
getWorkflowByNodeID,
getActiveWorkflow, getActiveWorkflow,
createNewWorkflow, createNewWorkflow,
openWorkflow, openWorkflow,

View File

@@ -203,14 +203,15 @@ export function promptToGraphVis(prompt: SerializedPrompt): string {
} }
export function getNodeInfo(nodeId: ComfyNodeID): string { export function getNodeInfo(nodeId: ComfyNodeID): string {
let app = (window as any).app; const workflow = workflowState.getWorkflowByNodeID(nodeId);
if (!app?.activeGraph) if (workflow == null)
return String(nodeId); return nodeId;
const title = workflow.graph?.getNodeByIdRecursive(nodeId)?.title;
if (title == null)
return nodeId;
const displayNodeID = nodeId ? (nodeId.split("-")[0]) : String(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 + ")" return title + " (" + displayNodeID + ")"
} }