Fix i18n workaround breaks node configure & subgraphs

- Copy *only* the i18n values from the node into the configure data
- Do not replace the inputs / outputs properties of the configure data
- Do not pass existing input / output objects to the parent `configure` method
- Removes need for extensions to workaround this peculiar behaviour
- Drastically simplifies code
This commit is contained in:
filtered
2025-07-20 18:01:12 +10:00
parent ed1d944e0e
commit 2c72f5d871

View File

@@ -13,11 +13,8 @@ import {
} from '@comfyorg/litegraph' } from '@comfyorg/litegraph'
import type { import type {
ExportedSubgraphInstance, ExportedSubgraphInstance,
ISerialisableNodeInput,
ISerialisableNodeOutput,
ISerialisedNode ISerialisedNode
} from '@comfyorg/litegraph/dist/types/serialisation' } from '@comfyorg/litegraph/dist/types/serialisation'
import _ from 'lodash'
import { useNodeAnimatedImage } from '@/composables/node/useNodeAnimatedImage' import { useNodeAnimatedImage } from '@/composables/node/useNodeAnimatedImage'
import { useNodeCanvasImagePreview } from '@/composables/node/useNodeCanvasImagePreview' import { useNodeCanvasImagePreview } from '@/composables/node/useNodeCanvasImagePreview'
@@ -249,51 +246,35 @@ export const useLitegraphService = () => {
* and 'localized_name' information from the original node definition. * and 'localized_name' information from the original node definition.
*/ */
override configure(data: ISerialisedNode): void { override configure(data: ISerialisedNode): void {
const RESERVED_KEYS = ['name', 'type', 'shape', 'localized_name']
// Note: input name is unique in a node definition, so we can lookup // Note: input name is unique in a node definition, so we can lookup
// input by name. // input by name.
const inputByName = new Map<string, ISerialisableNodeInput>( const thisInputsByName = new Map(this.inputs?.map((x) => [x.name, x]))
data.inputs?.map((input) => [input.name, input]) ?? []
) for (const input of data.inputs ?? []) {
// Inputs defined by the node definition. const currentInput = thisInputsByName.get(input.name)
const definedInputNames = new Set( if (!currentInput) continue
this.inputs.map((input) => input.name)
) input.name = currentInput.name
const definedInputs = this.inputs.map((input) => { input.type = currentInput.type
const inputData = inputByName.get(input.name) input.shape = currentInput.shape
return inputData input.localized_name = currentInput.localized_name
? {
...inputData, // Whether the input has associated widget follows the original node
// Whether the input has associated widget follows the // definition.
// original node definition. input.widget = currentInput.widget
..._.pick(input, RESERVED_KEYS.concat('widget')) }
}
: input
})
// Extra inputs that potentially dynamically added by custom js logic.
const extraInputs = data.inputs?.filter(
(input) => !definedInputNames.has(input.name)
)
data.inputs = [...definedInputs, ...(extraInputs ?? [])]
// Note: output name is not unique, so we cannot lookup output by name. // Note: output name is not unique, so we cannot lookup output by name.
// Use index instead. // Use index instead.
data.outputs = _.zip(this.outputs, data.outputs).map( for (const [index, output] of data.outputs?.entries() ?? []) {
([output, outputData]) => { const currentOutput = this.outputs.at(index)
// If there are extra outputs in the serialised node, use them directly. if (!currentOutput) continue
// There are currently custom nodes that dynamically add outputs via
// js logic.
if (!output) return outputData as ISerialisableNodeOutput
return outputData output.name = currentOutput.name
? { output.type = currentOutput.type
...outputData, output.shape = currentOutput.shape
..._.pick(output, RESERVED_KEYS) output.localized_name = currentOutput.localized_name
} }
: output
}
)
data.widgets_values = migrateWidgetsValues( data.widgets_values = migrateWidgetsValues(
ComfyNode.nodeData.inputs, ComfyNode.nodeData.inputs,
@@ -504,57 +485,35 @@ export const useLitegraphService = () => {
* and 'localized_name' information from the original node definition. * and 'localized_name' information from the original node definition.
*/ */
override configure(data: ISerialisedNode): void { override configure(data: ISerialisedNode): void {
const RESERVED_KEYS = ['name', 'type', 'shape', 'localized_name']
// Note: input name is unique in a node definition, so we can lookup // Note: input name is unique in a node definition, so we can lookup
// input by name. // input by name.
const inputByName = new Map<string, ISerialisableNodeInput>( const thisInputsByName = new Map(this.inputs?.map((x) => [x.name, x]))
data.inputs?.map((input) => [input.name, input]) ?? []
) for (const input of data.inputs ?? []) {
// Inputs defined by the node definition. const currentInput = thisInputsByName.get(input.name)
const definedInputNames = new Set( if (!currentInput) continue
this.inputs.map((input) => input.name)
) input.name = currentInput.name
const definedInputs = this.inputs.map((input) => { input.type = currentInput.type
const inputData = inputByName.get(input.name) input.shape = currentInput.shape
return inputData input.localized_name = currentInput.localized_name
? {
...inputData, // Whether the input has associated widget follows the original node
// Whether the input has associated widget follows the // definition.
// original node definition. input.widget = currentInput.widget
..._.pick(input, RESERVED_KEYS.concat('widget')) }
}
: input
})
// Extra inputs that potentially dynamically added by custom js logic.
const extraInputs = data.inputs?.filter(
(input) => !definedInputNames.has(input.name)
)
data.inputs = [...definedInputs, ...(extraInputs ?? [])]
// Note: output name is not unique, so we cannot lookup output by name. // Note: output name is not unique, so we cannot lookup output by name.
// Use index instead. // Use index instead.
data.outputs = _.zip(this.outputs, data.outputs).map( for (const [index, output] of data.outputs?.entries() ?? []) {
([output, outputData]) => { const currentOutput = this.outputs.at(index)
// If there are extra outputs in the serialised node, use them directly. if (!currentOutput) continue
// There are currently custom nodes that dynamically add outputs via
// js logic.
if (!output) return outputData as ISerialisableNodeOutput
return outputData output.name = currentOutput.name
? { output.type = currentOutput.type
...outputData, output.shape = currentOutput.shape
..._.pick(output, RESERVED_KEYS) output.localized_name = currentOutput.localized_name
} }
: output
}
)
data.widgets_values = migrateWidgetsValues(
ComfyNode.nodeData.inputs,
this.widgets ?? [],
data.widgets_values ?? []
)
super.configure(data) super.configure(data)
} }