235 lines
11 KiB
JavaScript
235 lines
11 KiB
JavaScript
import { app } from "../../scripts/app.js";
|
|
|
|
console.log("Loading context.js for Slider Stacker");
|
|
|
|
// --- Helper Functions (Copied from Dynamic.js for self-containment) ---
|
|
|
|
const findWidgetByName = (node, name) => {
|
|
return node.widgets ? node.widgets.find((w) => w.name === name) : null;
|
|
};
|
|
|
|
const getWidgetValue = (widget) => {
|
|
const val = widget?.value;
|
|
const num = Number(val);
|
|
return isNaN(num) ? 0 : num;
|
|
};
|
|
|
|
// Gets the actual max strength based on current slider values and activity
|
|
function recalculateCurrentMaxStrength(node) {
|
|
const sliderCountWidget = findWidgetByName(node, "slider_count");
|
|
const count = getWidgetValue(sliderCountWidget);
|
|
let currentMaxValue = 0;
|
|
for (let i = 1; i <= count; i++) {
|
|
const wtWidget = findWidgetByName(node, `(${i})`);
|
|
if (wtWidget) {
|
|
currentMaxValue = Math.max(currentMaxValue, getWidgetValue(wtWidget));
|
|
}
|
|
}
|
|
const maxStrengthWidget = findWidgetByName(node, "slider_max_strength");
|
|
const overallMax = maxStrengthWidget?.options?.max ?? 2.0;
|
|
const overallMin = maxStrengthWidget?.options?.min ?? 0.0;
|
|
return Math.max(overallMin, Math.min(currentMaxValue, overallMax));
|
|
}
|
|
|
|
// Updates the stored normalized weights based on current displayed values and max strength
|
|
function updateNormalization(node, currentMaxStrength) {
|
|
if (!node.normalizedSliderWeights) node.normalizedSliderWeights = {}; // Ensure it exists
|
|
const sliderCountWidget = findWidgetByName(node, "slider_count");
|
|
const maxSliders = sliderCountWidget?.options?.max || 50;
|
|
for (let i = 1; i <= maxSliders; i++) {
|
|
const wtWidget = findWidgetByName(node, `(${i})`);
|
|
if (wtWidget) {
|
|
const currentWt = getWidgetValue(wtWidget);
|
|
node.normalizedSliderWeights[i] = (currentMaxStrength > 0)
|
|
? (currentWt / currentMaxStrength)
|
|
: (node.normalizedSliderWeights[i] !== undefined ? node.normalizedSliderWeights[i] : 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Updates the displayed values of individual sliders based on normalized weights and new max strength
|
|
// Note: Needs access to node._isUpdatingInternally if called directly, but here it's used post-operation
|
|
function updateAllSliderDisplaysFromContext(node, newMaxStrength) {
|
|
if (!node.normalizedSliderWeights) return;
|
|
node._isUpdatingInternally = true; // Set flag for this operation
|
|
const count = getWidgetValue(findWidgetByName(node, "slider_count"));
|
|
for (let i = 1; i <= count; i++) {
|
|
const wtWidget = findWidgetByName(node, `(${i})`);
|
|
if (wtWidget) {
|
|
const normalizedValue = node.normalizedSliderWeights[i] || 0;
|
|
let newDisplayValue = normalizedValue * newMaxStrength;
|
|
const widgetMax = wtWidget.options?.max ?? 2.0;
|
|
const widgetMin = wtWidget.options?.min ?? 0.0;
|
|
newDisplayValue = Math.max(widgetMin, Math.min(newDisplayValue, widgetMax, newMaxStrength));
|
|
if (Math.abs(getWidgetValue(wtWidget) - newDisplayValue) > 0.001) {
|
|
wtWidget.value = newDisplayValue; // This should use the intercepted setter
|
|
}
|
|
}
|
|
}
|
|
node.graph.setDirtyCanvas(true, true);
|
|
node._isUpdatingInternally = false;
|
|
}
|
|
|
|
// --- Function to add menu options ---
|
|
|
|
function addContextMenuOptions(node) {
|
|
const originalGetExtraMenuOptions = node.getExtraMenuOptions;
|
|
node.getExtraMenuOptions = function(_, options) {
|
|
if (originalGetExtraMenuOptions) {
|
|
originalGetExtraMenuOptions.apply(this, arguments);
|
|
}
|
|
|
|
// Option 1: Average Values
|
|
options.push({
|
|
content: "Average Slider Values",
|
|
callback: () => {
|
|
const count = getWidgetValue(findWidgetByName(this, "slider_count"));
|
|
let sum = 0;
|
|
let activeCount = 0;
|
|
for (let i = 1; i <= count; i++) {
|
|
const wtWidget = findWidgetByName(this, `(${i})`);
|
|
if(wtWidget) {
|
|
sum += getWidgetValue(wtWidget);
|
|
activeCount++;
|
|
}
|
|
}
|
|
const average = activeCount > 0 ? sum / activeCount : 0;
|
|
|
|
this._isUpdatingInternally = true;
|
|
for (let i = 1; i <= count; i++) {
|
|
const wtWidget = findWidgetByName(this, `(${i})`);
|
|
if (wtWidget) { wtWidget.value = average; }
|
|
}
|
|
this._isUpdatingInternally = false;
|
|
|
|
// Recalculate max and update normalization *after* loop
|
|
const newMax = recalculateCurrentMaxStrength(this);
|
|
updateNormalization(this, newMax);
|
|
|
|
// Trigger update on master slider to sync everything
|
|
const masterWidget = findWidgetByName(this, "slider_max_strength");
|
|
if (masterWidget && Math.abs(getWidgetValue(masterWidget) - newMax) > 0.001) {
|
|
masterWidget.value = newMax; // This uses the setter from Dynamic.js
|
|
} else {
|
|
// If max didn't change, still redraw displays based on new average
|
|
updateAllSliderDisplaysFromContext(this, newMax);
|
|
}
|
|
this.graph.setDirtyCanvas(true, true);
|
|
}
|
|
});
|
|
|
|
// Option 2: Reset Values
|
|
options.push({
|
|
content: "Reset Slider Values",
|
|
callback: () => {
|
|
const count = getWidgetValue(findWidgetByName(this, "slider_count"));
|
|
const defaultValue = 1.0;
|
|
|
|
this._isUpdatingInternally = true;
|
|
for (let i = 1; i <= count; i++) {
|
|
const wtWidget = findWidgetByName(this, `(${i})`);
|
|
if (wtWidget) { wtWidget.value = defaultValue; }
|
|
}
|
|
this._isUpdatingInternally = false;
|
|
|
|
const newMax = recalculateCurrentMaxStrength(this);
|
|
updateNormalization(this, newMax);
|
|
const masterWidget = findWidgetByName(this, "slider_max_strength");
|
|
if (masterWidget && Math.abs(getWidgetValue(masterWidget) - newMax) > 0.001) {
|
|
masterWidget.value = newMax;
|
|
} else {
|
|
updateAllSliderDisplaysFromContext(this, newMax);
|
|
}
|
|
this.graph.setDirtyCanvas(true, true);
|
|
}
|
|
});
|
|
|
|
// Option 3: Set All to Max Value
|
|
options.push({
|
|
content: "Set All to Max Value",
|
|
callback: () => {
|
|
const count = getWidgetValue(findWidgetByName(this, "slider_count"));
|
|
let maxValue = -Infinity; // Start low
|
|
|
|
// First pass: Find the maximum value among visible sliders
|
|
for (let i = 1; i <= count; i++) {
|
|
const wtWidget = findWidgetByName(this, `(${i})`);
|
|
if(wtWidget) {
|
|
maxValue = Math.max(maxValue, getWidgetValue(wtWidget));
|
|
}
|
|
}
|
|
// Ensure maxValue is within widget bounds if needed (though setters handle clamping)
|
|
// const widgetMaxBound = findWidgetByName(this, `(1)`)?.options?.max ?? 2.0;
|
|
// maxValue = Math.min(maxValue, widgetMaxBound);
|
|
|
|
// Second pass: Set all visible sliders to the found maximum value
|
|
this._isUpdatingInternally = true;
|
|
for (let i = 1; i <= count; i++) {
|
|
const wtWidget = findWidgetByName(this, `(${i})`);
|
|
if (wtWidget) { wtWidget.value = maxValue; } // Use setter
|
|
}
|
|
this._isUpdatingInternally = false;
|
|
|
|
// Recalculate max (should be == maxValue), normalize, and update master slider
|
|
const newMax = recalculateCurrentMaxStrength(this); // Will be maxValue
|
|
updateNormalization(this, newMax);
|
|
const masterWidget = findWidgetByName(this, "slider_max_strength");
|
|
// Update master slider directly - its setter handles updating displays
|
|
if (masterWidget && Math.abs(getWidgetValue(masterWidget) - newMax) > 0.001) {
|
|
masterWidget.value = newMax;
|
|
}
|
|
this.graph.setDirtyCanvas(true, true);
|
|
}
|
|
});
|
|
|
|
// Option 4: Set All to Min Value
|
|
options.push({
|
|
content: "Set All to Min Value",
|
|
callback: () => {
|
|
const count = getWidgetValue(findWidgetByName(this, "slider_count"));
|
|
let minValue = Infinity; // Start high
|
|
|
|
// First pass: Find the minimum value among visible sliders
|
|
for (let i = 1; i <= count; i++) {
|
|
const wtWidget = findWidgetByName(this, `(${i})`);
|
|
if(wtWidget) {
|
|
minValue = Math.min(minValue, getWidgetValue(wtWidget));
|
|
}
|
|
}
|
|
// Ensure minValue isn't below widget bounds
|
|
// const widgetMinBound = findWidgetByName(this, `(1)`)?.options?.min ?? 0.0;
|
|
// minValue = Math.max(minValue, widgetMinBound);
|
|
|
|
// Second pass: Set all visible sliders to the found minimum value
|
|
this._isUpdatingInternally = true;
|
|
for (let i = 1; i <= count; i++) {
|
|
const wtWidget = findWidgetByName(this, `(${i})`);
|
|
if (wtWidget) { wtWidget.value = minValue; } // Use setter
|
|
}
|
|
this._isUpdatingInternally = false;
|
|
|
|
// Recalculate max (will be == minValue), normalize, and update master slider
|
|
const newMax = recalculateCurrentMaxStrength(this); // Will be minValue
|
|
updateNormalization(this, newMax);
|
|
const masterWidget = findWidgetByName(this, "slider_max_strength");
|
|
// Update master slider directly
|
|
if (masterWidget && Math.abs(getWidgetValue(masterWidget) - newMax) > 0.001) {
|
|
masterWidget.value = newMax;
|
|
}
|
|
this.graph.setDirtyCanvas(true, true);
|
|
}
|
|
});
|
|
};
|
|
}
|
|
|
|
// --- ComfyUI Extension ---
|
|
|
|
app.registerExtension({
|
|
name: "Comfy.DynamicSlidersStack.ContextMenu",
|
|
nodeCreated(node) {
|
|
const nodeTitle = node.getTitle ? node.getTitle() : node.title;
|
|
if (nodeTitle === "Slider Stacker") {
|
|
addContextMenuOptions(node);
|
|
}
|
|
}
|
|
});
|