Make optional node input's slot hollow circle (#912)

* Use hollow circle for optional input

* nit

* Show hollow shape for optional input

* Add playwright tests

* Update litegraph

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Chenlei Hu
2024-09-22 12:12:48 +09:00
committed by GitHub
parent a15c4d1612
commit f749734863
10 changed files with 232 additions and 13 deletions

View File

@@ -0,0 +1,57 @@
{
"last_node_id": 5,
"last_link_id": 0,
"nodes": [
{
"id": 5,
"type": "DevToolsNodeWithOptionalInput",
"pos": {
"0": 19,
"1": 46
},
"size": {
"0": 302.4000244140625,
"1": 46
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "required_input",
"type": "IMAGE",
"link": null
},
{
"name": "optional_input",
"type": "IMAGE",
"link": null,
"shape": 7
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": null
}
],
"properties": {
"Node name for S&R": "DevToolsNodeWithOptionalInput"
}
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,56 @@
{
"last_node_id": 5,
"last_link_id": 0,
"nodes": [
{
"id": 5,
"type": "DevToolsNodeWithOptionalInput",
"pos": {
"0": 19,
"1": 46
},
"size": {
"0": 302.4000244140625,
"1": 46
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "required_input",
"type": "IMAGE",
"link": null
},
{
"name": "optional_input",
"type": "IMAGE",
"link": null
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": null
}
],
"properties": {
"Node name for S&R": "DevToolsNodeWithOptionalInput"
}
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,57 @@
{
"last_node_id": 5,
"last_link_id": 0,
"nodes": [
{
"id": 5,
"type": "DevToolsNodeWithOptionalInput",
"pos": {
"0": 19,
"1": 46
},
"size": {
"0": 302.4000244140625,
"1": 46
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "required_input",
"type": "IMAGE",
"link": null
},
{
"name": "optional_input",
"type": "IMAGE",
"link": null,
"shape": 6
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": null
}
],
"properties": {
"Node name for S&R": "DevToolsNodeWithOptionalInput"
}
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -9,7 +9,6 @@ test.describe('Node Badge', () => {
const LGraphBadge = window['LGraphBadge']
const app = window['app'] as ComfyApp
const graph = app.graph
// @ts-expect-error - accessing private property
const nodes = graph.nodes
for (const node of nodes) {
@@ -27,7 +26,6 @@ test.describe('Node Badge', () => {
const LGraphBadge = window['LGraphBadge']
const app = window['app'] as ComfyApp
const graph = app.graph
// @ts-expect-error - accessing private property
const nodes = graph.nodes
for (const node of nodes) {
@@ -48,7 +46,6 @@ test.describe('Node Badge', () => {
const LGraphBadge = window['LGraphBadge']
const app = window['app'] as ComfyApp
const graph = app.graph
// @ts-expect-error - accessing private property
const nodes = graph.nodes
for (const node of nodes) {

View File

@@ -0,0 +1,21 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from './ComfyPage'
// If an input is optional by node definition, it should be shown as
// a hollow circle no matter what shape it was defined in the workflow JSON.
test.describe('Optional input', () => {
test('No shape specified', async ({ comfyPage }) => {
await comfyPage.loadWorkflow('optional_input_no_shape')
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
})
test('Wrong shape specified', async ({ comfyPage }) => {
await comfyPage.loadWorkflow('optional_input_wrong_shape')
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
})
test('Correct shape specified', async ({ comfyPage }) => {
await comfyPage.loadWorkflow('optional_input_correct_shape')
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

8
package-lock.json generated
View File

@@ -9,7 +9,7 @@
"version": "1.2.60",
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.2.1",
"@comfyorg/litegraph": "^0.7.75",
"@comfyorg/litegraph": "^0.7.76",
"@primevue/themes": "^4.0.5",
"@vitejs/plugin-vue": "^5.0.5",
"@vueuse/core": "^11.0.0",
@@ -1910,9 +1910,9 @@
"dev": true
},
"node_modules/@comfyorg/litegraph": {
"version": "0.7.75",
"resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.7.75.tgz",
"integrity": "sha512-RNYZVMoJ/a5btwP+S124FnrIVlwOdv6uNsTdYfwv7L8teDpwvf/TQa66QfCePqUlypBKEhKw+avTncLAu2FYUw==",
"version": "0.7.76",
"resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.7.76.tgz",
"integrity": "sha512-SwlBq7WsolRyxqHsm+aXi8pSQDf3+Kt9ORBsznIvL42X+1RoAwetI0GDqML/zqKBI3zr9MbpjOboaeyVx3il1w==",
"license": "MIT"
},
"node_modules/@cspotcode/source-map-support": {

View File

@@ -63,7 +63,7 @@
},
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.2.1",
"@comfyorg/litegraph": "^0.7.75",
"@comfyorg/litegraph": "^0.7.76",
"@primevue/themes": "^4.0.5",
"@vitejs/plugin-vue": "^5.0.5",
"@vueuse/core": "^11.0.0",

View File

@@ -1996,6 +1996,8 @@ export class ComfyApp {
constructor(title?: string) {
super(title)
const requiredInputs = nodeData.input.required
var inputs = nodeData['input']['required']
if (nodeData['input']['optional'] != undefined) {
inputs = Object.assign(
@@ -2025,7 +2027,12 @@ export class ComfyApp {
}
} else {
// Node connection inputs
this.addInput(inputName, type)
const inputIsRequired = inputName in requiredInputs
const inputOptions = inputIsRequired
? {}
: // @ts-expect-error LiteGraph.SlotShape is not typed.
{ shape: LiteGraph.SlotShape.HollowCircle }
this.addInput(inputName, type, inputOptions)
widgetCreated = false
}
// @ts-expect-error
@@ -2048,10 +2055,11 @@ export class ComfyApp {
let output = nodeData['output'][o]
if (output instanceof Array) output = 'COMBO'
const outputName = nodeData['output_name'][o] || output
const outputShape = nodeData['output_is_list'][o]
? LiteGraph.GRID_SHAPE
: LiteGraph.CIRCLE_SHAPE
this.addOutput(outputName, output, { shape: outputShape })
const outputIsList = nodeData['output_is_list'][o]
const outputOptions = outputIsList
? { shape: LiteGraph.GRID_SHAPE }
: {}
this.addOutput(outputName, output, outputOptions)
}
const s = this.computeSize()
@@ -2062,6 +2070,29 @@ export class ComfyApp {
app.#invokeExtensionsAsync('nodeCreated', this)
}
configure(data: any) {
// Keep 'name', 'type', and 'shape' information from the original node definition.
const merge = (
current: Record<string, any>,
incoming: Record<string, any>
) => {
const result = { ...incoming }
for (const key of ['name', 'type', 'shape']) {
if (current[key] !== undefined) {
result[key] = current[key]
}
}
return result
}
for (const field of ['inputs', 'outputs']) {
const slots = data[field] ?? []
data[field] = slots.map((slot, i) =>
merge(this[field][i] ?? {}, slot)
)
}
super.configure(data)
}
}
node.prototype.comfyClass = nodeData.name