commit 40be6443bca076f01e30930cd1c27d88d032df89 Author: ryanontheinstide <ryanfosdick87@gmail.com> Date: Wed Jan 29 09:51:43 2025 -0500 similarity filter, lazy condition, remove redundant VAE
204 lines
8.7 KiB
JavaScript
204 lines
8.7 KiB
JavaScript
// Widget validation for RealTime nodes
|
|
import { app } from "../../../scripts/app.js";
|
|
|
|
// TODO: Add validation for step values to ensure they match target widget constraints
|
|
|
|
// List of widget names that need validation
|
|
const VALIDATED_WIDGETS = ["values", "maximum_value", "minimum_value", "starting_value"];
|
|
|
|
// Register validation behavior when nodes are connected
|
|
app.registerExtension({
|
|
name: "RealTime.WidgetValidation",
|
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
|
if (!nodeData.category?.startsWith("real-time/control/")) {
|
|
return;
|
|
}
|
|
|
|
// Check if node has any widgets that need validation
|
|
const hasValidatedWidgets = nodeData.input?.required &&
|
|
Object.entries(nodeData.input.required).some(([name, config]) =>
|
|
VALIDATED_WIDGETS.includes(name) ||
|
|
(typeof config === "object" && config[0] === "FLOAT") ||
|
|
(typeof config === "object" && config[0] === "INT")
|
|
);
|
|
|
|
if (!hasValidatedWidgets) {
|
|
return;
|
|
}
|
|
|
|
// Store constraints from connected widgets
|
|
nodeType.prototype.targetConstraints = null;
|
|
|
|
// Add handler for when node is created (including on page load)
|
|
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
|
nodeType.prototype.onNodeCreated = function() {
|
|
const result = onNodeCreated?.apply(this, arguments);
|
|
|
|
// Wrap the values widget callback to ensure validation
|
|
const valuesWidget = this.widgets.find(w => w.name === "values");
|
|
if (valuesWidget) {
|
|
const originalCallback = valuesWidget.callback;
|
|
valuesWidget.callback = (value) => {
|
|
if (this.targetConstraints) {
|
|
this.validateSequenceValues(valuesWidget);
|
|
value = valuesWidget.value;
|
|
}
|
|
return originalCallback?.call(valuesWidget, value);
|
|
};
|
|
}
|
|
|
|
// Function to check connections
|
|
const checkConnections = () => {
|
|
const outputLinks = this.outputs[0]?.links || [];
|
|
|
|
for (const linkId of outputLinks) {
|
|
const link = app.graph.links[linkId];
|
|
if (!link) continue;
|
|
|
|
const targetNode = app.graph.getNodeById(link.target_id);
|
|
const targetSlot = link.target_slot;
|
|
|
|
if (targetNode?.widgets) {
|
|
const inputName = targetNode.inputs[targetSlot]?.name;
|
|
const targetWidget = targetNode.widgets.find(w => w.name === inputName);
|
|
|
|
if (targetWidget?.options || (targetWidget?.type === "converted-widget" && targetWidget.options)) {
|
|
this.targetConstraints = {
|
|
min: targetWidget.options.min,
|
|
max: targetWidget.options.max,
|
|
step: targetWidget.options.step
|
|
};
|
|
|
|
if (nodeData.category?.includes("/sequence")) {
|
|
const valuesWidget = this.widgets.find(w => w.name === "values");
|
|
if (valuesWidget) {
|
|
this.validateSequenceValues(valuesWidget);
|
|
}
|
|
} else {
|
|
this.updateWidgetConstraints();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Check connections at different intervals to ensure graph is loaded
|
|
checkConnections();
|
|
setTimeout(checkConnections, 100);
|
|
setTimeout(checkConnections, 1000);
|
|
|
|
return result;
|
|
};
|
|
|
|
const originalOnConnectOutput = nodeType.prototype.onConnectOutput;
|
|
nodeType.prototype.onConnectOutput = function (slot, type, input, targetNode, targetSlot) {
|
|
const result = originalOnConnectOutput?.apply(this, arguments);
|
|
|
|
if (targetNode?.widgets) {
|
|
const inputName = targetNode.inputs[targetSlot]?.name;
|
|
const targetWidget = targetNode.widgets.find(w => w.name === inputName);
|
|
|
|
if (targetWidget?.options || (targetWidget?.type === "converted-widget" && targetWidget.options)) {
|
|
const options = targetWidget.options;
|
|
this.targetConstraints = {
|
|
min: options.min,
|
|
max: options.max,
|
|
step: options.step
|
|
};
|
|
|
|
if (nodeData.category?.includes("/sequence")) {
|
|
const valuesWidget = this.widgets.find(w => w.name === "values");
|
|
if (valuesWidget) {
|
|
this.validateSequenceValues(valuesWidget);
|
|
}
|
|
} else {
|
|
this.updateWidgetConstraints();
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
// Add method to update widget constraints
|
|
nodeType.prototype.updateWidgetConstraints = function() {
|
|
if (!this.targetConstraints) return;
|
|
|
|
const widgets = ["maximum_value", "minimum_value", "starting_value"];
|
|
widgets.forEach(name => {
|
|
const widget = this.widgets.find(w => w.name === name);
|
|
if (widget) {
|
|
if (this.targetConstraints.min !== undefined) {
|
|
widget.options.min = this.targetConstraints.min;
|
|
}
|
|
if (this.targetConstraints.max !== undefined) {
|
|
widget.options.max = this.targetConstraints.max;
|
|
}
|
|
}
|
|
});
|
|
|
|
this.clampWidgetValues();
|
|
};
|
|
|
|
// Add method to clamp widget values
|
|
nodeType.prototype.clampWidgetValues = function() {
|
|
const minWidget = this.widgets.find(w => w.name === "minimum_value");
|
|
const maxWidget = this.widgets.find(w => w.name === "maximum_value");
|
|
const startWidget = this.widgets.find(w => w.name === "starting_value");
|
|
|
|
if (minWidget && maxWidget) {
|
|
const targetMin = this.targetConstraints?.min ?? minWidget.options.min;
|
|
const targetMax = this.targetConstraints?.max ?? maxWidget.options.max;
|
|
|
|
minWidget.value = Math.max(targetMin, Math.min(minWidget.value, maxWidget.value));
|
|
maxWidget.value = Math.max(minWidget.value, Math.min(maxWidget.value, targetMax));
|
|
|
|
if (startWidget) {
|
|
startWidget.value = Math.max(minWidget.value, Math.min(startWidget.value, maxWidget.value));
|
|
}
|
|
}
|
|
};
|
|
|
|
// Add method to validate sequence values
|
|
nodeType.prototype.validateSequenceValues = function(widget) {
|
|
if (!this.targetConstraints) return;
|
|
|
|
const separator = widget.options?.multiline ? "\n" : ",";
|
|
const values = widget.value.split(separator)
|
|
.map(v => v.trim())
|
|
.filter(v => v.length > 0);
|
|
|
|
const isFloat = this.type === "FloatSequence";
|
|
const isInt = this.type === "IntSequence";
|
|
|
|
if (isFloat || isInt) {
|
|
const validValues = values.map(v => {
|
|
let num = isFloat ? parseFloat(v) : parseInt(v);
|
|
if (isNaN(num)) num = this.targetConstraints.min;
|
|
const clamped = Math.max(this.targetConstraints.min, Math.min(num, this.targetConstraints.max));
|
|
return isFloat ? clamped.toString() : Math.round(clamped).toString();
|
|
});
|
|
|
|
widget.value = validValues.join(separator);
|
|
}
|
|
};
|
|
|
|
// Override the widget's callback to enforce constraints
|
|
const originalWidgetCallback = nodeType.prototype.onWidgetChanged;
|
|
nodeType.prototype.onWidgetChanged = function(widget, value) {
|
|
if (widget.name === "values" && this.targetConstraints) {
|
|
this.validateSequenceValues(widget);
|
|
value = widget.value;
|
|
|
|
widget.value = value;
|
|
if (widget.callback) {
|
|
widget.callback(value);
|
|
}
|
|
} else if (["maximum_value", "minimum_value", "starting_value"].includes(widget.name)) {
|
|
this.clampWidgetValues();
|
|
}
|
|
|
|
return originalWidgetCallback?.apply(this, [widget, value]);
|
|
};
|
|
}
|
|
});
|