Try to make it work as intended

This commit is contained in:
StableLlama
2025-06-02 22:57:05 +02:00
parent f9b9a52010
commit 90684e9e57
+440 -174
View File
@@ -55,6 +55,7 @@ app.registerExtension({
/** Array of groups of (generic) dynamic inputs. */
const dynamicInputGroups = [];
for (const name of combinedInputDataOrder) {
const widgetType = combinedInputData[name][1]?.widgetType;
const dynamic = combinedInputData[name][1]?._dynamic;
const dynamicGroup = combinedInputData[name][1]?._dynamicGroup ?? 0;
@@ -81,7 +82,7 @@ app.registerExtension({
dynamicInputGroups[dynamicGroup].push(
dynamicInputs.length
)
dynamicInputs.push({name, baseName, matcher, dynamic, dynamicType, dynamicGroup});
dynamicInputs.push({name, baseName, matcher, dynamic, dynamicType, dynamicGroup, widgetType});
}
}
if (dynamicInputs.length === 0) {
@@ -89,16 +90,13 @@ app.registerExtension({
}
/**
* Utility: Check if an input is dynamic.
* @param {string} inputName - Name of the input to check.
* Utility functions for dynamic input operations
*/
// Check if an input is dynamic
const isDynamicInput = (inputName) =>
dynamicInputs.some((di) => di.matcher.test(inputName));
dynamicInputs.some((di) => di.matcher.test(inputName));
/**
* Utility: Update inputs' slot indices after reordering.
* @param {ComfyNode} node - The node to update.
*/
// Update inputs' slot indices after reordering
const updateSlotIndices = (node) => {
node.inputs.forEach((input, index) => {
input.slot_index = index;
@@ -113,21 +111,88 @@ app.registerExtension({
});
};
// Get default value for widget based on its type
const getWidgetDefaultValue = (widget) => {
switch (widget.type) {
case 'number':
return 0;
case 'combo':
return widget.options?.[0] || '';
case 'text':
case 'string':
default:
return '';
}
};
// Check if a dynamic input is empty (not connected and widget has default value)
const isDynamicInputEmpty = (node, inputIndex) => {
const input = node.inputs[inputIndex];
if (input.isConnected) return false;
if (input.widget) {
const widget = node.widgets.find(w => w.name === input.widget.name);
return widget?.value === getWidgetDefaultValue(widget);
}
return true;
};
// Find if input is the last of its base name group
const isLastDynamicInput = (node, idx, baseName) => {
let isLast = true;
for (let i = idx + 1; i < node.inputs.length; i++) {
isLast &&= !node.inputs[i].name.startsWith(baseName);
}
return isLast;
};
// Remove widget associated with an input
const removeWidgetForInput = (node, inputIdx) => {
if (node.inputs[inputIdx].widget !== undefined) {
const widgetIdx = node.widgets.findIndex((w) => w.name === node.inputs[inputIdx].widget.name);
node.widgets.splice(widgetIdx, 1);
node.widgets_values?.splice(widgetIdx, 1);
}
};
// Add helper method to get dynamic group for an input name
nodeType.prototype.getDynamicGroup = function(inputName) {
// Find the dynamicGroup by matching the baseName with input name
for (const di of dynamicInputs) {
if (inputName.startsWith(di.baseName)) {
return di.dynamicGroup;
}
}
return undefined;
};
/**
* Add a widget with standard configuration.
* @param {string} name - The name of the widget.
* @param {string} widget_type - The type of widget.
* @returns {object} The created widget.
*/
const addStandardWidget = function(name, widget_type) {
return this.addWidget(widget_type, name, '', () => {}, {});
};
// Add helper method to insert input at a specific position
nodeType.prototype.addInputAtPosition = function (name, type, position, isWidget, shape) {
nodeType.prototype.addInputAtPosition = function (name, input_type, widget_type, position, isWidget, shape) {
// Add widget if needed
if (isWidget) {
this.addWidget(type, name, '', ()=>{}, {});
addStandardWidget.call(this, name, widget_type);
const GET_CONFIG = Symbol();
const input = this.addInput(name, type, {
this.addInput(name, input_type, {
shape,
widget: {name, [GET_CONFIG]: () =>{}}
})
});
} else {
this.addInput(name, type, {shape}); // Add new input
this.addInput(name, input_type, {shape}); // Add new input without widget
}
const newInput = this.inputs.pop(); // Fetch the newly added input (last item)
// Position the input at the desired location
const newInput = this.inputs.pop(); // Get the newly added input (last item)
this.inputs.splice(position, 0, newInput); // Place it at the desired position
updateSlotIndices(this); // Update indices
return newInput;
@@ -137,155 +202,141 @@ app.registerExtension({
// running in parallel.
let isProcessingConnection = false;
/**
* Utility: Handle when a dynamic input becomes empty (disconnected or empty widget value).
* @param {ComfyNode} node - The node with the empty input.
*/
const handleEmptyDynamicInput = function() {
// Process each input to check for empty dynamic inputs
for (let idx = 0; idx < this.inputs.length; idx++) {
const input = this.inputs[idx];
// Skip if not a dynamic input
if (!isDynamicInput(input.name)) {
continue;
}
// Check if this input is empty
if (!isDynamicInputEmpty(this, idx)) {
continue;
}
// Get information about this dynamic input group
const dynamicGroup = this.getDynamicGroup(input.name);
const baseName = dynamicInputs[dynamicInputGroups[dynamicGroup][0]].baseName;
// Don't remove if it's the last dynamic input of its type
if (isLastDynamicInput(this, idx, baseName)) {
continue;
}
// Move the empty input to the end
for (let i = idx + 1; i < this.inputs.length; i++) {
this.swapInputs(i - 1, i);
}
// Remove the input that's now at the end
const lastIdx = this.inputs.length - 1;
removeWidgetForInput(this, lastIdx);
this.removeInput(lastIdx);
// Adjust idx to check the current position again (which now has a new input)
idx--;
}
// Renumber all dynamic inputs to ensure proper ordering
for (const groupIdx in dynamicInputGroups) {
for (const memberIdx of dynamicInputGroups[groupIdx]) {
const baseName = dynamicInputs[memberIdx].baseName;
const dynamic = dynamicInputs[memberIdx].dynamic;
this.renumberDynamicInputs(baseName, dynamicInputs, dynamic);
}
}
};
/**
* Utility: Handle when a dynamic input becomes active (connected or non-empty widget value).
* @param {ComfyNode} node - The node with the activated input.
* @param {number} dynamicGroup - The dynamic group to handle.
*/
const handleDynamicInputActivation = function(dynamicGroup) {
// Get information about dynamic inputs
const {
slots: dynamicSlots,
groupConnected: dynamicGroupConnected
} = this.getDynamicSlots();
// Ensure all widget-based inputs have actual widgets
// This is important when loading workflows
for (const slot of dynamicSlots) {
if (slot.isWidget && this.widgets &&
!this.widgets.some((w) => w.name === slot.name)) {
this.addWidget(
this.inputs[slot.index].type,
slot.name,
'',
()=>{},
{}
);
}
}
// If all inputs in this group are active, we need to add a new empty one
const hasEmptyInput = dynamicGroupConnected[dynamicGroup]?.some(isActive => !isActive);
if (!hasEmptyInput) {
// Find position for the new input (after the last one in this group)
const groupSlots = dynamicSlots.filter(slot => slot.dynamicGroup === dynamicGroup);
const lastDynamicIdx = groupSlots.length > 0
? Math.max(...groupSlots.map(slot => slot.index))
: -1;
// Add a new empty input for this group
this.addNewDynamicInputForGroup(dynamicGroup, lastDynamicIdx);
}
// Ensure the canvas is updated
this.setDirtyCanvas(true, true);
};
// Override onConnectionsChange: Handle connections for dynamic inputs
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
nodeType.prototype.onConnectionsChange = function (type, slotIndex, isConnected, link, ioSlot) {
// Call the original method first
const result = onConnectionsChange?.apply(this, arguments);
if (type !== TypeSlot.Input || isProcessingConnection || !isDynamicInput(this.inputs[slotIndex].name)) {
// Only process input connections for dynamic inputs
const isInput = type === TypeSlot.Input;
const isDynamic = isInput && isDynamicInput(this.inputs[slotIndex].name);
// Skip if not an input, already processing, or not a dynamic input
if (!isInput || isProcessingConnection || !isDynamic) {
return result;
}
function getDynamicGroup(inputName) {
// Find the dynamicGroup by matching the baseName with input name
for (const di of dynamicInputs) {
if (inputName.startsWith(di.baseName)) {
return di.dynamicGroup;
}
}
return undefined;
}
// Prevent recursive processing
isProcessingConnection = true;
try {
// Get dynamic input slots
const dynamicSlots = [];
const dynamicGroupCount = [];
const dynamicGroupConnected = [];
for (const [index, input] of this.inputs.entries()) {
const isDynamic = isDynamicInput(input.name);
if (isDynamic) {
const connected = input.isConnected;
const dynamicGroup = getDynamicGroup(input.name);
if (dynamicGroup in dynamicGroupCount) {
if (input.name.startsWith(dynamicInputs[dynamicInputGroups[dynamicGroup][0]].baseName)) {
dynamicGroupConnected[dynamicGroup][dynamicGroupCount[dynamicGroup]] ||= connected;
dynamicGroupCount[dynamicGroup]++;
}
} else {
dynamicGroupCount[dynamicGroup] = 1;
dynamicGroupConnected[dynamicGroup] = [connected];
}
dynamicSlots.push({
index,
name: input.name,
isWidget: input.widget !== undefined,
shape: input.shape,
connected,
isDynamic,
dynamicGroup,
dynamicGroupCount: dynamicGroupCount[dynamicGroup]
});
// Get the dynamic group for this input
const dynamicGroup = this.getDynamicGroup(this.inputs[slotIndex].name);
// sanity check to make sure every widget is in reality a widget. When loading a workflow this
// isn't the case so we must fix it ourselves.
if (this.widgets && !this.widgets.some((w) => w.name === input.name)) {
this.addWidget(input.type, input.name, '', ()=>{}, {});
}
}
}
// Handle connection event
if (isConnected === TypeSlotEvent.Connect) {
const hasEmptyDynamic = dynamicGroupConnected[0].some(dgc => !dgc);
if (!hasEmptyDynamic) {
// No empty slot - add a new one after the last dynamic input
const lastDynamicIdx = Math.max(...dynamicSlots.map((slot) => slot.index), -1);
let insertPosition = lastDynamicIdx + 1;
let inputInRange = true;
for (const groupMember of dynamicInputGroups[dynamicInputs[0].dynamicGroup]) {
const baseName = dynamicInputs[groupMember].baseName;
const dynamicType = dynamicInputs[groupMember].dynamicType;
let newName;
if (dynamicInputs[0].dynamic === 'letter') {
if (dynamicSlots.length >= 26) {
inputInRange = false;
}
// For letter type, use the next letter in sequence
newName = String.fromCharCode(97 + dynamicSlots.length); // 97 is ASCII for 'a'
} else {
// For number type, use baseName + index as before
newName = `${baseName}${dynamicSlots.length}`;
}
if (inputInRange) {
// Insert the new empty input at the correct position
this.addInputAtPosition(newName, dynamicType, insertPosition++, dynamicSlots[groupMember].isWidget, dynamicSlots[groupMember].shape);
// Renumber inputs after addition
this.renumberDynamicInputs(baseName, dynamicInputs, dynamicInputs[0].dynamic);
}
}
}
} else if (isConnected === TypeSlotEvent.Disconnect) {
let foundEmptyIndex = -1;
for (let idx = 0; idx < this.inputs.length; idx++) {
const input = this.inputs[idx];
if (!isDynamicInput(input.name)) {
continue;
}
if (!input.isConnected) { // Check if the input is empty
// remove empty input - but only when it's not the last one
const dynamicGroup = getDynamicGroup(input.name);
let isLast = true;
for (let i = idx + 1; i < this.inputs.length; i++) {
isLast &&= !this.inputs[i].name.startsWith(dynamicInputs[dynamicInputGroups[dynamicGroup][0]].baseName);
}
if (isLast) {
continue;
}
for (let i = idx + 1; i < this.inputs.length; i++) {
this.swapInputs(i - 1, i);
}
const lastIdx = this.inputs.length - 1;
if (this.inputs[lastIdx].widget !== undefined) {
const widgetIdx = this.widgets.findIndex((w) => w.name === this.inputs[lastIdx].widget.name)
this.widgets.splice(widgetIdx, 1);
this.widgets_values?.splice(widgetIdx, 1);
}
this.removeInput(lastIdx);
}
}
// Renumber dynamic inputs to ensure proper ordering
for (const groupMember of dynamicInputGroups[dynamicInputs[0].dynamicGroup]) {
const baseName = dynamicInputs[groupMember].baseName;
this.renumberDynamicInputs(baseName, dynamicInputs, dynamicInputs[0].dynamic);
}
}
this.setDirtyCanvas(true, true);
} catch (e) {
console.error(e);
debugger;
alert(e);
} finally {
isProcessingConnection = false;
// Handle connection or disconnection event
if (isConnected === TypeSlotEvent.Connect) {
// Input was connected
handleDynamicInputActivation.call(this, dynamicGroup);
} else if (isConnected === TypeSlotEvent.Disconnect) {
// Input was disconnected
handleEmptyDynamicInput.call(this);
}
isProcessingConnection = false;
return result;
};
const onConnectInput = nodeType.prototype.onConnectInput;
nodeType.prototype.onConnectInput = function(inputIndex, outputType, outputSlot, outputNode, outputIndex) {
const result = onRemoved?.apply(this, arguments) ?? true;
const result = onConnectInput?.apply(this, arguments) ?? true;
if (this.inputs[inputIndex].isConnected) {
const pre_isProcessingConnection = isProcessingConnection;
@@ -312,6 +363,67 @@ app.registerExtension({
return result;
}
/**
* Utility: Find input index for a widget by name.
* @param {ComfyNode} node - The node containing the widget.
* @param {string} widgetName - Name of the widget to find.
* @returns {number} Index of the input associated with the widget, or -1 if not found.
*/
const findInputIndexForWidget = (node, widgetName) => {
for (let i = 0; i < node.inputs.length; i++) {
if (node.inputs[i].widget && node.inputs[i].widget.name === widgetName) {
return i;
}
}
return -1;
};
const onWidgetChanged = nodeType.prototype.onWidgetChanged;
nodeType.prototype.onWidgetChanged = function () {
const result = onWidgetChanged?.apply(this, arguments);
// Extract arguments
const widget_name = arguments[0];
const new_val = arguments[1];
const old_val = arguments[2];
const widget = arguments[3];
// Skip if not a dynamic input widget or already processing connections
if (!isDynamicInput(widget_name) || isProcessingConnection) {
return result;
}
// Find the dynamic group for this widget
const dynamicGroup = this.getDynamicGroup(widget_name);
if (dynamicGroup === undefined) {
return result;
}
// Check if widget value changed between default and non-default
const default_val = getWidgetDefaultValue(widget);
const wasEmpty = old_val === default_val;
const isNowEmpty = new_val === default_val;
// Only process if the empty state changed
if (wasEmpty === isNowEmpty) {
return result;
}
// Prevent recursive processing
isProcessingConnection = true;
if (wasEmpty && !isNowEmpty) {
// Widget changed from empty to non-empty (like connecting an input)
handleDynamicInputActivation.call(this, dynamicGroup);
} else if (!wasEmpty && isNowEmpty) {
// Widget changed to empty (like disconnecting an input)
handleEmptyDynamicInput.call(this);
}
isProcessingConnection = false;
return result;
}
// Method to swap two inputs in the "this.inputs" array by their indices
nodeType.prototype.swapInputs = function(indexA, indexB) {
// Validate indices
@@ -325,64 +437,218 @@ app.registerExtension({
return;
}
// reflect the swap with the widgets
if (this.inputs[indexA].widget !== undefined) {
if (this.inputs[indexB].widget === undefined) {
console.error("Bad swap: input A is a widget but input B is not", indexA, indexB);
}
const widgetIdxA = this.widgets.findIndex((w) => w.name === this.inputs[indexA].widget.name);
const widgetIdxB = this.widgets.findIndex((w) => w.name === this.inputs[indexB].widget.name);
[this.widgets[widgetIdxA].y, this.widgets[widgetIdxB].y] = [this.widgets[widgetIdxB].y, this.widgets[widgetIdxA].y];
[this.widgets[widgetIdxA].last_y, this.widgets[widgetIdxB].last_y] = [this.widgets[widgetIdxB].last_y, this.widgets[widgetIdxA].last_y];
[this.widgets[widgetIdxA], this.widgets[widgetIdxB]] = [this.widgets[widgetIdxB], this.widgets[widgetIdxA]];
// Handle widgets if both inputs have them
const hasWidgetA = this.inputs[indexA].widget !== undefined;
const hasWidgetB = this.inputs[indexB].widget !== undefined;
if (hasWidgetA && hasWidgetB) {
// Find widget indices
const widgetIdxA = this.widgets.findIndex(
(w) => w.name === this.inputs[indexA].widget.name
);
const widgetIdxB = this.widgets.findIndex(
(w) => w.name === this.inputs[indexB].widget.name
);
// Swap widget positions
[this.widgets[widgetIdxA].y, this.widgets[widgetIdxB].y] =
[this.widgets[widgetIdxB].y, this.widgets[widgetIdxA].y];
[this.widgets[widgetIdxA].last_y, this.widgets[widgetIdxB].last_y] =
[this.widgets[widgetIdxB].last_y, this.widgets[widgetIdxA].last_y];
// Swap the widgets themselves
[this.widgets[widgetIdxA], this.widgets[widgetIdxB]] =
[this.widgets[widgetIdxB], this.widgets[widgetIdxA]];
// Swap widget values if they exist
if (this.widgets_values) {
[this.widgets_values[widgetIdxA], this.widgets_values[widgetIdxB]] = [this.widgets_values[widgetIdxB], this.widgets_values[widgetIdxA]];
[this.widgets_values[widgetIdxA], this.widgets_values[widgetIdxB]] =
[this.widgets_values[widgetIdxB], this.widgets_values[widgetIdxA]];
}
} else if (hasWidgetA || hasWidgetB) {
console.error("Bad swap: one input has a widget but the other doesn't", indexA, indexB);
}
// Swap the inputs in the array
[this.inputs[indexA].boundingRect, this.inputs[indexB].boundingRect] = [this.inputs[indexB].boundingRect, this.inputs[indexA].boundingRect];
[this.inputs[indexA].pos, this.inputs[indexB].pos] = [this.inputs[indexB].pos, this.inputs[indexA].pos];
[this.inputs[indexA], this.inputs[indexB]] = [this.inputs[indexB], this.inputs[indexA]];
updateSlotIndices(this); // Refresh indices
// Swap input properties
[this.inputs[indexA].boundingRect, this.inputs[indexB].boundingRect] =
[this.inputs[indexB].boundingRect, this.inputs[indexA].boundingRect];
[this.inputs[indexA].pos, this.inputs[indexB].pos] =
[this.inputs[indexB].pos, this.inputs[indexA].pos];
// Redraw the node to ensure the graph updates properly
// -> not needed as the calling method must do it!
this.setDirtyCanvas(true, true);
// Swap the inputs themselves
[this.inputs[indexA], this.inputs[indexB]] =
[this.inputs[indexB], this.inputs[indexA]];
// Update indices to maintain connections
updateSlotIndices(this);
// The calling method is responsible for redrawing the canvas if needed
};
// Add helper method to get dynamic slots info
nodeType.prototype.getDynamicSlots = function(dynamicGroup = null) {
const dynamicSlots = [];
const dynamicGroupCount = {};
const dynamicGroupConnected = {};
// Process each input to gather information about dynamic inputs
for (const [index, input] of this.inputs.entries()) {
// Skip non-dynamic inputs
if (!isDynamicInput(input.name)) {
continue;
}
// Get the dynamic group for this input
const currentDynamicGroup = this.getDynamicGroup(input.name);
// Skip if filtering by group and this doesn't match
if (dynamicGroup !== null && currentDynamicGroup !== dynamicGroup) {
continue;
}
// Determine if this input is active (connected or has non-default widget value)
const isActive = !isDynamicInputEmpty(this, index);
// Initialize group tracking if this is the first input for this group
if (!(currentDynamicGroup in dynamicGroupCount)) {
dynamicGroupCount[currentDynamicGroup] = 0;
dynamicGroupConnected[currentDynamicGroup] = [];
}
// Get the base name for this dynamic input
const baseNameInfo = dynamicInputs[dynamicInputGroups[currentDynamicGroup][0]];
// Track connection status for this input in its group
if (input.name.startsWith(baseNameInfo.baseName)) {
const groupIndex = dynamicGroupCount[currentDynamicGroup];
// Use OR assignment to preserve 'true' values
dynamicGroupConnected[currentDynamicGroup][groupIndex] =
dynamicGroupConnected[currentDynamicGroup][groupIndex] || isActive;
dynamicGroupCount[currentDynamicGroup]++;
}
// Store detailed information about this dynamic input
dynamicSlots.push({
index,
name: input.name,
isWidget: input.widget !== undefined,
shape: input.shape,
connected: isActive,
isDynamic: true,
dynamicGroup: currentDynamicGroup,
dynamicGroupCount: dynamicGroupCount[currentDynamicGroup]
});
}
return {
slots: dynamicSlots,
groupCount: dynamicGroupCount,
groupConnected: dynamicGroupConnected
};
};
/**
* Generate a new dynamic input name based on type and count.
* @param {string} dynamic - The dynamic type ('number' or 'letter').
* @param {string} baseName - The base name for the input.
* @param {number} count - The count/position for the new input.
* @returns {string} The generated input name.
*/
const generateDynamicInputName = (dynamic, baseName, count) => {
if (dynamic === 'letter') {
// For letter type, use the next letter in sequence
return String.fromCharCode(97 + count); // 97 is ASCII for 'a'
} else {
// For number type, use baseName + index
return `${baseName}${count}`;
}
};
// Add helper method to add new dynamic input for a group
nodeType.prototype.addNewDynamicInputForGroup = function(dynamicGroup, lastDynamicIdx) {
let insertPosition = lastDynamicIdx + 1;
let inputInRange = true;
// Add new inputs for each member of the dynamic group
for (const groupMember of dynamicInputGroups[dynamicGroup]) {
const dynamicInput = dynamicInputs[groupMember];
const baseName = dynamicInput.baseName;
const dynamicType = dynamicInput.dynamicType;
const widgetType = dynamicInput.widgetType ?? dynamicType;
const dynamic = dynamicInput.dynamic;
// Get current slots for this group
const { slots } = this.getDynamicSlots(dynamicGroup);
const groupSlots = slots.filter(s => s.name.startsWith(baseName));
// Check if we've reached the limit for letter inputs (a-z)
if (dynamic === 'letter' && groupSlots.length >= 26) {
inputInRange = false;
continue;
}
// Generate the new input name based on current count
const newName = generateDynamicInputName(dynamic, baseName, groupSlots.length);
// Find a reference slot to copy properties from
const referenceSlot = groupSlots[0] || slots.find(s =>
s.name.startsWith(dynamicInput.baseName)
);
// Create the new input at the correct position
this.addInputAtPosition(
newName,
dynamicType,
widgetType,
insertPosition++,
referenceSlot?.isWidget ?? false,
referenceSlot?.shape
);
// Ensure inputs are numbered correctly
this.renumberDynamicInputs(baseName, dynamicInputs, dynamic);
}
return inputInRange;
};
// Add method to safely renumber dynamic inputs without breaking connections
nodeType.prototype.renumberDynamicInputs = function(baseName, dynamicInputs, dynamic) {
// Get current dynamic inputs info
// Collect information about dynamic inputs with this base name
const dynamicInputInfo = [];
// Find all inputs that match this base name
for (let i = 0; i < this.inputs.length; i++) {
const input = this.inputs[i];
const isDynamic = isDynamicInput(input.name);
if (isDynamic && input.name.startsWith(baseName)) {
if (isDynamicInput(input.name) && input.name.startsWith(baseName)) {
// Store info about this input
dynamicInputInfo.push({
index: i,
widgetIdx: input.widget !== undefined ? this.widgets.findIndex((w) => w.name === input.widget.name) : undefined,
widgetIdx: input.widget !== undefined
? this.widgets.findIndex((w) => w.name === input.widget.name)
: undefined,
name: input.name,
connected: input.isConnected
});
}
}
// Just rename the inputs in place - don't remove/add to keep connections intact
// Rename inputs in place to maintain connections
for (let i = 0; i < dynamicInputInfo.length; i++) {
const info = dynamicInputInfo[i];
const input = this.inputs[info.index];
const newName = dynamic === "number" ? `${baseName}${i}` : String.fromCharCode(97 + i); // 97 is ASCII for 'a'
const newName = generateDynamicInputName(dynamic, baseName, i);
if (input.widget !== undefined) {
const widgetIdx = info.widgetIdx;
// Update widget name if this input has a widget
if (input.widget !== undefined && info.widgetIdx !== undefined) {
const widget = this.widgets[info.widgetIdx];
widget.name = newName;
widget.label = newName;
input.widget.name = newName;
this.widgets[widgetIdx].name = newName;
this.widgets[widgetIdx].label = newName;
}
// Update the input name if it's different
if (input.name !== newName) {
input.name = newName;
input.localized_name = newName;