Workflow Management Reworked (#1406)

* Merge temp userfile

Basic migration

Remove deprecated isFavourite

Rename

nit

nit

Rework open/load

Refactor save

Refactor delete

Remove workflow dep on manager

WIP

Change map to record

Fix directory

nit

isActive

Move

nit

Add unload

Add close workflow

Remove workflowManager.closeWorkflow

nit

Remove workflowManager.storePrompt

move from commandStore

move more from commandStore

nit

Use workflowservice

nit

nit

implement setWorkflow

nit

Remove workflows.ts

Fix strict errors

nit

nit

Resolves circular dep

nit

nit

Fix workflow switching

Add openworkflowPaths

Fix store

Fix key

Serialize by default

Fix proxy

nit

Update path

Proper sync

Fix tabs

WIP

nit

Resolve merge conflict

Fix userfile store tests

Update jest test

Update tabs

patch tests

Fix changeTracker init

Move insert to service

nit

Fix insert

nit

Handle bookmark rename

Refactor tests

Add delete workflow

Add test on deleting workflow

Add closeWorkflow tests

nit

* Fix path

* Move load next/previous

* Move logic from store to service

* nit

* nit

* nit

* nit

* nit

* Add ChangeTracker.initialState

* ChangeTracker load/unload

* Remove app.changeWorkflow

* Hook to app.ts

* Changetracker restore

* nit

* nit

* nit

* Add debug logs

* Remove unnecessary checkState on graphLoad

* nit

* Fix strict

* Fix temp workflow name

* Track ismodified

* Fix reactivity

* nit

* Fix graph equal

* nit

* update test

* nit

* nit

* Fix modified state

* nit

* Fix modified state

* Sidebar force close

* tabs force close

* Fix save

* Add load remote workflow test

* Force save

* Add save test

* nit

* Correctly handle delete last opened workflow

* nit

* Fix workflow rename

* Fix save

* Fix tests

* Fix strict

* Update playwright tests

* Fix filename conflict handling

* nit

* Merge temporary and persisted ref

* Update playwright expectations

* nit

* nit

* Fix saveAs

* Add playwright test

* nit
This commit is contained in:
Chenlei Hu
2024-11-05 11:03:27 -05:00
committed by GitHub
parent 1387d7e627
commit c56533bb23
28 changed files with 1409 additions and 784 deletions

View File

@@ -25,7 +25,7 @@ import { ComfyNodeDef, StatusWsMessageStatus } from '@/types/apiTypes'
import { adjustColor, ColorAdjustOptions } from '@/utils/colorUtil'
import { ComfyAppMenu } from './ui/menu/index'
import { getStorageValue } from './utils'
import { ComfyWorkflowManager, ComfyWorkflow } from './workflows'
import { ComfyWorkflow, useWorkflowStore } from '@/stores/workflowStore'
import {
LGraphCanvas,
LGraph,
@@ -58,6 +58,7 @@ import { KeyComboImpl, useKeybindingStore } from '@/stores/keybindingStore'
import { useCommandStore } from '@/stores/commandStore'
import { shallowReactive } from 'vue'
import { type IBaseWidget } from '@comfyorg/litegraph/dist/types/widgets'
import { workflowService } from '@/services/workflowService'
export const ANIM_PREVIEW_WIDGET = '$$comfy_animation_preview'
@@ -141,7 +142,6 @@ export class ComfyApp {
multiUserServer: boolean
ctx: CanvasRenderingContext2D
widgets: Record<string, ComfyWidgetConstructor>
workflowManager: ComfyWorkflowManager
bodyTop: HTMLElement
bodyLeft: HTMLElement
bodyRight: HTMLElement
@@ -170,7 +170,6 @@ export class ComfyApp {
this.vueAppReady = false
this.ui = new ComfyUI(this)
this.logging = new ComfyLogging(this)
this.workflowManager = new ComfyWorkflowManager(this)
this.bodyTop = $el('div.comfyui-body-top', { parent: document.body })
this.bodyLeft = $el('div.comfyui-body-left', { parent: document.body })
this.bodyRight = $el('div.comfyui-body-right', { parent: document.body })
@@ -1789,7 +1788,7 @@ export class ComfyApp {
this.resizeCanvas()
await Promise.all([
this.workflowManager.loadWorkflows(),
useWorkspaceStore().workflow.syncWorkflows(),
this.ui.settings.load()
])
await this.#loadExtensions()
@@ -2160,21 +2159,6 @@ export class ComfyApp {
})
}
async changeWorkflow(callback, workflow = null) {
try {
this.workflowManager.activeWorkflow?.changeTracker?.store()
} catch (error) {
console.error(error)
}
await callback()
try {
this.workflowManager.setWorkflow(workflow)
this.workflowManager.activeWorkflow?.track()
} catch (error) {
console.error(error)
}
}
async loadGraphData(
graphData?: ComfyWorkflowJSON,
clean: boolean = true,
@@ -2198,12 +2182,6 @@ export class ComfyApp {
graphData = structuredClone(graphData)
}
try {
this.workflowManager.setWorkflow(workflow)
} catch (error) {
console.error(error)
}
if (useSettingStore().get('Comfy.Validation.Workflows')) {
// TODO: Show validation error in a dialog.
const validatedGraphData = await validateComfyWorkflow(
@@ -2217,6 +2195,8 @@ export class ComfyApp {
graphData = validatedGraphData ?? graphData
}
workflowService.beforeLoadNewGraph()
const missingNodeTypes: MissingNodeType[] = []
const missingModels = []
await this.#invokeExtensionsAsync(
@@ -2270,12 +2250,6 @@ export class ComfyApp {
this.canvas.ds.offset = graphData.extra.ds.offset
this.canvas.ds.scale = graphData.extra.ds.scale
}
try {
this.workflowManager.activeWorkflow?.track()
} catch (error) {
// TODO: Do we want silently fail here?
}
} catch (error) {
let errorHint = []
// Try extracting filename to see if it was caused by an extension script
@@ -2384,6 +2358,8 @@ export class ComfyApp {
this.#showMissingModelsError(missingModels, paths)
}
await this.#invokeExtensionsAsync('afterConfigureGraph', missingNodeTypes)
// @ts-expect-error zod types issue. Will be fixed after we enable ts-strict
workflowService.afterLoadNewGraph(workflow, this.graph.serialize())
requestAnimationFrame(() => {
this.graph.setDirtyCanvas(true, true)
})
@@ -2602,9 +2578,11 @@ export class ComfyApp {
this.canvas.draw(true, true)
} else {
try {
this.workflowManager.storePrompt({
useExecutionStore().storePrompt({
id: res.prompt_id,
nodes: Object.keys(p.output)
nodes: Object.keys(p.output),
workflow: useWorkspaceStore().workflow
.activeWorkflow as ComfyWorkflow
})
} catch (error) {}
}
@@ -2678,9 +2656,12 @@ export class ComfyApp {
} else if (pngInfo?.prompt) {
this.loadApiJson(JSON.parse(pngInfo.prompt), fileName)
} else if (pngInfo?.parameters) {
this.changeWorkflow(() => {
importA1111(this.graph, pngInfo.parameters)
}, fileName)
// Note: Not putting this in `importA1111` as it is mostly not used
// by external callers, and `importA1111` has no access to `app`.
workflowService.beforeLoadNewGraph()
importA1111(this.graph, pngInfo.parameters)
// @ts-expect-error zod type issue on ComfyWorkflowJSON. Should be resolved after enabling ts-strict globally.
workflowService.afterLoadNewGraph(fileName, this.serializeGraph())
} else {
this.showErrorOnFileLoad(file)
}
@@ -2764,6 +2745,8 @@ export class ComfyApp {
}
loadApiJson(apiData, fileName: string) {
workflowService.beforeLoadNewGraph()
const missingNodeTypes = Object.values(apiData).filter(
// @ts-expect-error
(n) => !LiteGraph.registered_node_types[n.class_type]
@@ -2786,40 +2769,38 @@ export class ComfyApp {
app.graph.add(node)
}
this.changeWorkflow(() => {
for (const id of ids) {
const data = apiData[id]
const node = app.graph.getNodeById(id)
for (const input in data.inputs ?? {}) {
const value = data.inputs[input]
if (value instanceof Array) {
const [fromId, fromSlot] = value
const fromNode = app.graph.getNodeById(fromId)
let toSlot = node.inputs?.findIndex((inp) => inp.name === input)
if (toSlot == null || toSlot === -1) {
try {
// Target has no matching input, most likely a converted widget
const widget = node.widgets?.find((w) => w.name === input)
// @ts-expect-error
if (widget && node.convertWidgetToInput?.(widget)) {
toSlot = node.inputs?.length - 1
}
} catch (error) {}
}
if (toSlot != null || toSlot !== -1) {
fromNode.connect(fromSlot, node, toSlot)
}
} else {
const widget = node.widgets?.find((w) => w.name === input)
if (widget) {
widget.value = value
widget.callback?.(value)
}
for (const id of ids) {
const data = apiData[id]
const node = app.graph.getNodeById(id)
for (const input in data.inputs ?? {}) {
const value = data.inputs[input]
if (value instanceof Array) {
const [fromId, fromSlot] = value
const fromNode = app.graph.getNodeById(fromId)
let toSlot = node.inputs?.findIndex((inp) => inp.name === input)
if (toSlot == null || toSlot === -1) {
try {
// Target has no matching input, most likely a converted widget
const widget = node.widgets?.find((w) => w.name === input)
// @ts-expect-error
if (widget && node.convertWidgetToInput?.(widget)) {
toSlot = node.inputs?.length - 1
}
} catch (error) {}
}
if (toSlot != null || toSlot !== -1) {
fromNode.connect(fromSlot, node, toSlot)
}
} else {
const widget = node.widgets?.find((w) => w.name === input)
if (widget) {
widget.value = value
widget.callback?.(value)
}
}
}
app.graph.arrange()
}, fileName)
}
app.graph.arrange()
for (const id of ids) {
const data = apiData[id]
@@ -2854,6 +2835,9 @@ export class ComfyApp {
}
app.graph.arrange()
// @ts-expect-error zod type issue on ComfyWorkflowJSON. Should be resolved after enabling ts-strict globally.
workflowService.afterLoadNewGraph(fileName, this.serializeGraph())
}
/**