Fix group node naming compatibility (#969)

* Convery legacy group node names in workflow

* Add Playwright test

* Remove hardcoded strings
This commit is contained in:
bymyself
2024-09-24 23:25:08 -07:00
committed by GitHub
parent 941f71faea
commit 84fc0e9205
4 changed files with 302 additions and 16 deletions

View File

@@ -0,0 +1,252 @@
{
"last_node_id": 15,
"last_link_id": 9,
"nodes": [
{
"id": 15,
"type": "workflow/hello",
"pos": {
"0": 566,
"1": 316
},
"size": {
"0": 468.5999755859375,
"1": 582
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null,
"label": "model"
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null,
"label": "positive"
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null,
"label": "negative"
},
{
"name": "latent_image",
"type": "LATENT",
"link": null,
"label": "latent_image"
},
{
"name": "KSampler model",
"type": "MODEL",
"link": null,
"label": "KSampler model"
},
{
"name": "KSampler positive",
"type": "CONDITIONING",
"link": null,
"label": "KSampler positive"
},
{
"name": "KSampler negative",
"type": "CONDITIONING",
"link": null,
"label": "KSampler negative"
},
{
"name": "KSampler latent_image",
"type": "LATENT",
"link": null,
"label": "KSampler latent_image"
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null,
"shape": 3,
"label": "LATENT"
},
{
"name": "KSampler LATENT",
"type": "LATENT",
"links": null,
"shape": 3,
"label": "KSampler LATENT"
}
],
"properties": {
"Node name for S&R": "workflow/hello"
},
"widgets_values": [
"enable",
0,
"randomize",
20,
8,
"euler",
"normal",
0,
10000,
"disable",
0,
"randomize",
20,
8,
"euler",
"normal",
1
]
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"groupNodes": {
"hello": {
"nodes": [
{
"id": -1,
"type": "KSamplerAdvanced",
"pos": {
"0": 351.3332824707031,
"1": 577.3333129882812
},
"size": {
"0": 315,
"1": 334
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null,
"label": "model"
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null,
"label": "positive"
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null,
"label": "negative"
},
{
"name": "latent_image",
"type": "LATENT",
"link": null,
"label": "latent_image"
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null,
"shape": 3,
"label": "LATENT"
}
],
"properties": {
"Node name for S&R": "KSamplerAdvanced"
},
"widgets_values": [
"enable",
0,
"randomize",
20,
8,
"euler",
"normal",
0,
10000,
"disable"
],
"index": 0
},
{
"id": -1,
"type": "KSampler",
"pos": {
"0": 636,
"1": 427
},
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null,
"label": "model"
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null,
"label": "positive"
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null,
"label": "negative"
},
{
"name": "latent_image",
"type": "LATENT",
"link": null,
"label": "latent_image"
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null,
"shape": 3,
"label": "LATENT"
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
0,
"randomize",
20,
8,
"euler",
"normal",
1
],
"index": 1
}
],
"links": [],
"external": []
}
}
},
"version": 0.4
}

View File

@@ -140,4 +140,12 @@ test.describe('Group Node', () => {
// Ensure the link is still present
expect(await input.getLinkCount()).toBe(1)
})
test('Loads from a workflow using the legacy path separator ("/")', async ({
comfyPage
}) => {
await comfyPage.loadWorkflow('legacy_group_node')
expect(await comfyPage.getGraphNodesCount()).toBe(1)
expect(comfyPage.page.locator('.comfy-missing-nodes')).not.toBeVisible()
})
})

View File

@@ -5,9 +5,15 @@ import { ManageGroupDialog } from './groupNodeManage'
import type { LGraphNode } from '@comfyorg/litegraph'
import { LGraphCanvas, LiteGraph } from '@comfyorg/litegraph'
import { useNodeDefStore } from '@/stores/nodeDefStore'
import { ComfyNode, ComfyWorkflowJSON } from '@/types/comfyWorkflow'
const GROUP = Symbol()
// v1 Prefix + Separator: workflow/
// v2 Prefix + Separator: workflow> (ComfyUI_frontend v1.2.63)
const PREFIX = 'workflow'
const SEPARATOR = '>'
const Workflow = {
InUse: {
Free: 0,
@@ -15,7 +21,7 @@ const Workflow = {
InWorkflow: 2
},
isInUseGroupNode(name) {
const id = `workflow>${name}`
const id = `${PREFIX}${SEPARATOR}${name}`
// Check if lready registered/in use in this workflow
if (app.graph.extra?.groupNodes?.[name]) {
if (app.graph.nodes.find((n) => n.type === id)) {
@@ -185,15 +191,15 @@ export class GroupNodeConfig {
this.outputVisibility = []
}
async registerType(source = 'workflow') {
async registerType(source = PREFIX) {
this.nodeDef = {
output: [],
output_name: [],
output_is_list: [],
output_is_hidden: [],
name: source + '>' + this.name,
name: source + SEPARATOR + this.name,
display_name: this.name,
category: 'group nodes' + ('>' + source),
category: 'group nodes' + (SEPARATOR + source),
input: { required: {} },
description: `Group node combining ${this.nodeData.nodes
.map((n) => n.type)
@@ -216,7 +222,7 @@ export class GroupNodeConfig {
p()
}
this.#convertedToProcess = null
await app.registerNodeDef('workflow>' + this.name, this.nodeDef)
await app.registerNodeDef(`${PREFIX}${SEPARATOR}` + this.name, this.nodeDef)
useNodeDefStore().addNodeDef(this.nodeDef)
}
@@ -647,7 +653,7 @@ export class GroupNodeConfig {
app.clean = function () {
for (const g in groupNodes) {
try {
LiteGraph.unregisterNodeType('workflow/' + g)
LiteGraph.unregisterNodeType(`${PREFIX}${SEPARATOR}` + g)
} catch (error) {}
}
app.clean = clean
@@ -662,11 +668,11 @@ export class GroupNodeConfig {
if (!(n.type in LiteGraph.registered_node_types)) {
missingNodeTypes.push({
type: n.type,
hint: ` (In group node 'workflow/${g}')`
hint: ` (In group node '${PREFIX}${SEPARATOR}${g}')`
})
missingNodeTypes.push({
type: 'workflow/' + g,
type: `${PREFIX}${SEPARATOR}` + g,
action: {
text: 'Remove from workflow',
callback: (e) => {
@@ -1380,7 +1386,7 @@ export class GroupNodeHandler {
const config = new GroupNodeConfig(name, nodeData)
await config.registerType()
const groupNode = LiteGraph.createNode(`workflow>${name}`)
const groupNode = LiteGraph.createNode(`${PREFIX}${SEPARATOR}${name}`)
// Reuse the existing nodes for this instance
groupNode.setInnerNodes(builder.nodes)
groupNode[GROUP].populateWidgets()
@@ -1444,6 +1450,14 @@ function addConvertToGroupOptions() {
}
}
const replaceLegacySeparators = (nodes: ComfyNode[]): void => {
for (const node of nodes) {
if (typeof node.type === 'string' && node.type.startsWith('workflow/')) {
node.type = node.type.replace(/^workflow\//, `${PREFIX}${SEPARATOR}`)
}
}
}
const id = 'Comfy.GroupNode'
let globalDefs
const ext = {
@@ -1451,9 +1465,13 @@ const ext = {
setup() {
addConvertToGroupOptions()
},
async beforeConfigureGraph(graphData, missingNodeTypes) {
async beforeConfigureGraph(
graphData: ComfyWorkflowJSON,
missingNodeTypes: string[]
) {
const nodes = graphData?.extra?.groupNodes
if (nodes) {
replaceLegacySeparators(graphData.nodes)
await GroupNodeConfig.registerFromWorkflow(nodes, missingNodeTypes)
}
},

View File

@@ -10,6 +10,8 @@ import {
} from '@comfyorg/litegraph'
const ORDER: symbol = Symbol()
const PREFIX = 'workflow'
const SEPARATOR = '>'
function merge(target, source) {
if (typeof target === 'object' && typeof source === 'object') {
@@ -102,7 +104,9 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
getGroupData() {
this.groupNodeType =
LiteGraph.registered_node_types['workflow>' + this.selectedGroup]
LiteGraph.registered_node_types[
`${PREFIX}${SEPARATOR}` + this.selectedGroup
]
this.groupNodeDef = this.groupNodeType.nodeData
this.groupData = GroupNodeHandler.getGroupData(this.groupNodeType)
}
@@ -367,7 +371,7 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
groupNodes.map((g) =>
$el('option', {
textContent: g,
selected: 'workflow>' + g === type,
selected: `${PREFIX}${SEPARATOR}` + g === type,
value: g
})
)
@@ -389,7 +393,7 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
{
onclick: (e) => {
const node = app.graph.nodes.find(
(n) => n.type === 'workflow>' + this.selectedGroup
(n) => n.type === `${PREFIX}${SEPARATOR}` + this.selectedGroup
)
if (node) {
alert(
@@ -403,7 +407,9 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
)
) {
delete app.graph.extra.groupNodes[this.selectedGroup]
LiteGraph.unregisterNodeType('workflow>' + this.selectedGroup)
LiteGraph.unregisterNodeType(
`${PREFIX}${SEPARATOR}` + this.selectedGroup
)
}
this.show()
}
@@ -476,7 +482,7 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
}, {})
}
const nodes = nodesByType['workflow>' + g]
const nodes = nodesByType[`${PREFIX}${SEPARATOR}` + g]
if (nodes) recreateNodes.push(...nodes)
}
@@ -503,7 +509,9 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
this.element.replaceChildren(outer)
this.changeGroup(
type ? groupNodes.find((g) => 'workflow>' + g === type) : groupNodes[0]
type
? groupNodes.find((g) => `${PREFIX}${SEPARATOR}` + g === type)
: groupNodes[0]
)
this.element.showModal()