Add files via upload

few issues still persists for the dynamic sliders stack node, the bottom padding issue when adding/removing sliders, and the persistence issue where the dynamic functionality is lost after ComfyUi restarts.
This commit is contained in:
FunnyFinger
2025-04-14 18:33:01 +03:00
committed by GitHub
parent b0d310fa49
commit 119d6f60c7
5 changed files with 466 additions and 0 deletions
+351
View File
@@ -0,0 +1,351 @@
(function() {
// Wait for app to be defined
const checkExist = setInterval(function() {
if (typeof app !== 'undefined') {
clearInterval(checkExist);
// Log to console to verify the script is loaded
console.log("[Dynamic_Slider_Pack] ✅ JavaScript loaded successfully.");
// Register the extension
app.registerExtension({
name: "Dynamic_Slider_Pack",
nodeCreated(node) {
// Only apply to our specific node
if (node.constructor.type !== "DynamicSlidersStack") {
return;
}
console.log("[Dynamic_Slider_Pack] Node created, initializing");
// Constants for node sizing
const HEADER_HEIGHT = 30;
const CONTROLS_HEIGHT = 60;
const PAIR_HEIGHT = 60;
const FOOTER_PADDING = 4;
const DEFAULT_WIDTH = 340;
const MIN_WIDTH = 200;
const MAX_SLIDER_VALUE = 2.0; // Hard cap on max value
// Lock to prevent recursive updates
node._updatingSliders = false;
// Override the onResize method to handle width constraints properly
const origOnResize = node.onResize;
node.onResize = function(size) {
if (origOnResize) {
origOnResize.call(this, size);
}
// Allow width to be changed freely but maintain minimum
if (size[0] < MIN_WIDTH) {
size[0] = MIN_WIDTH;
}
// Calculate minimum height based on visible widgets
const countWidget = this.widgets.find(w => w.name === "sliders_count");
const count = countWidget ? countWidget.value : 5;
const minHeight = HEADER_HEIGHT + CONTROLS_HEIGHT + (count * PAIR_HEIGHT) + FOOTER_PADDING;
// Set to exact calculated height to prevent extra space
size[1] = minHeight;
this.size = size;
};
// Function to find the maximum slider value and index
function findMaxSliderValue() {
const countWidget = node.widgets.find(w => w.name === "sliders_count");
if (!countWidget) return { maxValue: 1.0, maxIndex: 0 };
const count = countWidget.value;
let maxValue = 0;
let maxIndex = -1;
for (let i = 1; i <= count; i++) {
const sliderWidget = node.widgets.find(w => w.name === `slider_${i}`);
if (sliderWidget && !sliderWidget.hidden) {
if (sliderWidget.value > maxValue) {
maxValue = sliderWidget.value;
maxIndex = i - 1; // Zero-based index
}
}
}
// If no max found, use defaults
if (maxIndex < 0) {
maxValue = 1.0;
maxIndex = 0;
}
return { maxValue, maxIndex };
}
// Update the MAX widget based on current slider values
function updateMaxFromSliders() {
if (node._updatingSliders) return;
const maxWidget = node.widgets.find(w => w.name === "sliders_Max");
if (!maxWidget) return;
const { maxValue } = findMaxSliderValue();
// Cap at the hard maximum of 2.0
const capped = Math.min(maxValue, MAX_SLIDER_VALUE);
// Only update if significantly different
if (Math.abs(maxWidget.value - capped) > 0.0001) {
console.log(`[Dynamic_Slider_Pack] Updating MAX to ${capped.toFixed(4)} from sliders`);
maxWidget.value = capped;
}
}
// Function to update widget visibility based on sliders_count
function updateWidgetVisibility() {
if (node._updatingSliders) return;
node._updatingSliders = true;
try {
// Find the control widgets
const countWidget = node.widgets.find(w => w.name === "sliders_count");
const maxWidget = node.widgets.find(w => w.name === "sliders_Max");
if (!countWidget || !maxWidget) {
console.error("[Dynamic_Slider_Pack] Could not find control widgets");
return;
}
const count = countWidget.value;
const maxVal = Math.max(0.01, maxWidget.value);
console.log(`[Dynamic_Slider_Pack] Updating visibility: showing ${count} sliders with max=${maxVal}`);
// Loop through all potential title/slider widgets
let visibleSliders = 0;
for (let i = 1; i <= 10; i++) {
const titleWidget = node.widgets.find(w => w.name === `title_${i}`);
const sliderWidget = node.widgets.find(w => w.name === `slider_${i}`);
if (titleWidget && sliderWidget) {
// Show only up to 'count' widgets
const shouldShow = i <= count;
titleWidget.hidden = !shouldShow;
sliderWidget.hidden = !shouldShow;
if (shouldShow) {
visibleSliders++;
// Update slider's max value
if (sliderWidget.options) {
sliderWidget.options.max = MAX_SLIDER_VALUE; // Always use the hard max
sliderWidget.options.step = 0.0001;
// Cap value at current Max if needed
if (sliderWidget.value > maxVal) {
sliderWidget.value = maxVal;
}
}
}
}
}
// Calculate the new height based on visible widgets
const newHeight = HEADER_HEIGHT + CONTROLS_HEIGHT + (visibleSliders * PAIR_HEIGHT) + FOOTER_PADDING;
// Update node size
const currentWidth = node.size[0] || DEFAULT_WIDTH;
node.size = [currentWidth, newHeight];
// Use the graph's setDirty for better rendering
if (node.graph) {
node.graph.setDirty(true, true);
} else {
node.setDirtyCanvas(true, true);
}
} finally {
node._updatingSliders = false;
}
}
// Set up callbacks for each slider to update MAX value
function setupSliderCallbacks() {
for (let i = 1; i <= 10; i++) {
const sliderWidget = node.widgets.find(w => w.name === `slider_${i}`);
if (sliderWidget) {
// Store original callback if any
const origCallback = sliderWidget.callback;
// Replace with our callback
sliderWidget.callback = function(v) {
if (origCallback) origCallback.call(this, v);
// Skip if we're currently updating sliders programmatically
if (node._updatingSliders) return;
// Update MAX widget if this slider is now the maximum
updateMaxFromSliders();
};
}
}
}
// Set up MAX widget callback to scale slider values
function setupMaxCallback() {
const maxWidget = node.widgets.find(w => w.name === "sliders_Max");
if (!maxWidget) return;
// Store original callback if any
const origCallback = maxWidget.callback;
// Replace with our callback to handle slider scaling
maxWidget.callback = function(newMax) {
if (origCallback) origCallback.call(this, newMax);
// Skip if we're in the middle of updating
if (node._updatingSliders) return;
node._updatingSliders = true;
try {
// Ensure minimum max value
newMax = Math.max(0.01, newMax);
console.log(`[Dynamic_Slider_Pack] MAX changed to ${newMax.toFixed(4)}`);
// Find the current maximum slider
const { maxValue, maxIndex } = findMaxSliderValue();
// Only proceed if we have a valid max index
if (maxIndex >= 0) {
const countWidget = node.widgets.find(w => w.name === "sliders_count");
const count = countWidget ? countWidget.value : 5;
// Calculate scaling ratio
const ratio = maxValue > 0 ? newMax / maxValue : 1;
console.log(`[Dynamic_Slider_Pack] Scaling ratio: ${ratio.toFixed(4)}`);
// Scale all slider values
for (let i = 1; i <= count; i++) {
const sliderWidget = node.widgets.find(w => w.name === `slider_${i}`);
if (sliderWidget && !sliderWidget.hidden) {
// Scale value
const scaledValue = sliderWidget.value * ratio;
sliderWidget.value = scaledValue;
}
}
}
updateWidgetVisibility();
} finally {
node._updatingSliders = false;
}
};
}
// Set up sliders_count callback
function setupCountCallback() {
const countWidget = node.widgets.find(w => w.name === "sliders_count");
if (!countWidget) return;
const origCallback = countWidget.callback;
countWidget.callback = function(v) {
if (origCallback) origCallback.call(this, v);
// Update slider visibility
updateWidgetVisibility();
// After changing count, update MAX value
updateMaxFromSliders();
};
}
// Add right-click menu options
function setupContextMenu() {
const getExtraMenuOptions = node.getExtraMenuOptions;
node.getExtraMenuOptions = function(_, options) {
if (getExtraMenuOptions) getExtraMenuOptions.apply(this, arguments);
options.push({
content: "Refresh Sliders",
callback: () => {
updateWidgetVisibility();
updateMaxFromSliders();
}
});
// Add option to evenly distribute slider values
options.push({
content: "Balance Slider Values",
callback: () => {
const countWidget = node.widgets.find(w => w.name === "sliders_count");
const maxWidget = node.widgets.find(w => w.name === "sliders_Max");
if (countWidget && maxWidget) {
const count = countWidget.value;
const maxVal = Math.max(0.01, maxWidget.value);
// Prevent callbacks during this operation
node._updatingSliders = true;
try {
// Calculate step size for even distribution
const step = count > 1 ? maxVal / (count - 1) : maxVal / 2;
// Distribute values evenly, making sure highest = MAX
for (let i = 0; i < count; i++) {
const sliderWidget = node.widgets.find(w => w.name === `slider_${i+1}`);
if (sliderWidget) {
const value = count > 1 ? step * i : maxVal / 2;
sliderWidget.value = value;
}
}
// Set the last slider to exactly MAX
if (count > 0) {
const lastSlider = node.widgets.find(w => w.name === `slider_${count}`);
if (lastSlider) {
lastSlider.value = maxVal;
}
}
} finally {
node._updatingSliders = false;
}
node.setDirtyCanvas(true, true);
}
}
});
// Add option to reset node size
options.push({
content: "Reset Node Size",
callback: () => {
node.size[0] = DEFAULT_WIDTH;
updateWidgetVisibility();
}
});
};
}
// Initialize the node
function initialize() {
// Set up callbacks in this specific order
setupSliderCallbacks();
setupMaxCallback();
setupCountCallback();
setupContextMenu();
updateWidgetVisibility();
// Add a short delay before updating Max based on sliders
setTimeout(() => {
updateMaxFromSliders();
console.log("[Dynamic_Slider_Pack] Initialization complete");
}, 200);
}
// Initialize with a delay to ensure widgets are created
setTimeout(initialize, 500);
}
});
}
}, 100); // Check every 100ms
})();