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:
@@ -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
|
||||
})();
|
||||
Reference in New Issue
Block a user