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.
352 lines
19 KiB
JavaScript
352 lines
19 KiB
JavaScript
(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
|
|
})();
|