updated to v2.0.0
added new features such as dynamic sum adjustment and sum-lock dynamics
This commit is contained in:
+1043
-53
File diff suppressed because it is too large
Load Diff
+5
-3
@@ -13,7 +13,7 @@ const COLOR_THEMES = {
|
||||
|
||||
// Updated NODE_COLORS map
|
||||
const NODE_COLORS = {
|
||||
"Slider Stacker": "purple",
|
||||
"Slider Stacker": "purple",
|
||||
"Slider Receiver": "purple", // Added receiver, same color
|
||||
// Add other nodes here if needed
|
||||
};
|
||||
@@ -30,7 +30,6 @@ shuffleArray(colorKeys); // Shuffle the color themes initially
|
||||
|
||||
function setNodeColors(node, theme) {
|
||||
if (!theme) {return;}
|
||||
node.shape = "box"; // Apply box shape if desired
|
||||
if(theme.nodeColor && theme.nodeBgColor) {
|
||||
node.color = theme.nodeColor;
|
||||
node.bgcolor = theme.nodeBgColor;
|
||||
@@ -62,4 +61,7 @@ const ext = {
|
||||
}
|
||||
};
|
||||
|
||||
app.registerExtension(ext);
|
||||
// Add a console log for loading confirmation
|
||||
// console.log("Loaded Dynamic Sliders Stack Appearance Script");
|
||||
|
||||
app.registerExtension(ext);
|
||||
|
||||
+73
-48
@@ -1,6 +1,6 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
console.log("Loading context.js for Slider Stacker");
|
||||
// Removed console log
|
||||
|
||||
// --- Helper Functions (Copied from Dynamic.js for self-containment) ---
|
||||
|
||||
@@ -14,9 +14,28 @@ const getWidgetValue = (widget) => {
|
||||
return isNaN(num) ? 0 : num;
|
||||
};
|
||||
|
||||
// Function to check if node is our Slider Stacker using multiple identification methods
|
||||
function isSliderStackerNode(node) {
|
||||
// Check the display title
|
||||
const nodeTitle = node.getTitle ? node.getTitle() : node.title;
|
||||
if (nodeTitle === "Slider Stacker") return true;
|
||||
|
||||
// Check internal type name
|
||||
if (node.type === "Slider Stacker (DSS)") return true;
|
||||
|
||||
// Check properties that are unique to our node
|
||||
if (findWidgetByName(node, "sliders_count") &&
|
||||
findWidgetByName(node, "sliders_max") &&
|
||||
findWidgetByName(node, "slider_name_1")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gets the actual max strength based on current slider values and activity
|
||||
function recalculateCurrentMaxStrength(node) {
|
||||
const sliderCountWidget = findWidgetByName(node, "slider_count");
|
||||
const sliderCountWidget = findWidgetByName(node, "sliders_count");
|
||||
const count = getWidgetValue(sliderCountWidget);
|
||||
let currentMaxValue = 0;
|
||||
for (let i = 1; i <= count; i++) {
|
||||
@@ -25,7 +44,7 @@ function recalculateCurrentMaxStrength(node) {
|
||||
currentMaxValue = Math.max(currentMaxValue, getWidgetValue(wtWidget));
|
||||
}
|
||||
}
|
||||
const maxStrengthWidget = findWidgetByName(node, "slider_max_strength");
|
||||
const maxStrengthWidget = findWidgetByName(node, "sliders_max");
|
||||
const overallMax = maxStrengthWidget?.options?.max ?? 2.0;
|
||||
const overallMin = maxStrengthWidget?.options?.min ?? 0.0;
|
||||
return Math.max(overallMin, Math.min(currentMaxValue, overallMax));
|
||||
@@ -34,7 +53,7 @@ function recalculateCurrentMaxStrength(node) {
|
||||
// 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 sliderCountWidget = findWidgetByName(node, "sliders_count");
|
||||
const maxSliders = sliderCountWidget?.options?.max || 50;
|
||||
for (let i = 1; i <= maxSliders; i++) {
|
||||
const wtWidget = findWidgetByName(node, `(${i})`);
|
||||
@@ -50,9 +69,9 @@ function updateNormalization(node, currentMaxStrength) {
|
||||
// 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;
|
||||
if (!node.normalizedSliderWeights) return;
|
||||
node._isUpdatingInternally = true; // Set flag for this operation
|
||||
const count = getWidgetValue(findWidgetByName(node, "slider_count"));
|
||||
const count = getWidgetValue(findWidgetByName(node, "sliders_count"));
|
||||
for (let i = 1; i <= count; i++) {
|
||||
const wtWidget = findWidgetByName(node, `(${i})`);
|
||||
if (wtWidget) {
|
||||
@@ -60,17 +79,19 @@ function updateAllSliderDisplaysFromContext(node, newMaxStrength) {
|
||||
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));
|
||||
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);
|
||||
if (node.graph) {
|
||||
node.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
node._isUpdatingInternally = false;
|
||||
}
|
||||
|
||||
// --- Function to add menu options ---
|
||||
// --- Function to add menu options ---
|
||||
|
||||
function addContextMenuOptions(node) {
|
||||
const originalGetExtraMenuOptions = node.getExtraMenuOptions;
|
||||
@@ -83,7 +104,7 @@ function addContextMenuOptions(node) {
|
||||
options.push({
|
||||
content: "Average Slider Values",
|
||||
callback: () => {
|
||||
const count = getWidgetValue(findWidgetByName(this, "slider_count"));
|
||||
const count = getWidgetValue(findWidgetByName(this, "sliders_count"));
|
||||
let sum = 0;
|
||||
let activeCount = 0;
|
||||
for (let i = 1; i <= count; i++) {
|
||||
@@ -94,27 +115,29 @@ function addContextMenuOptions(node) {
|
||||
}
|
||||
}
|
||||
const average = activeCount > 0 ? sum / activeCount : 0;
|
||||
|
||||
this._isUpdatingInternally = true;
|
||||
|
||||
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");
|
||||
const masterWidget = findWidgetByName(this, "sliders_max");
|
||||
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);
|
||||
if (this.graph) {
|
||||
this.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -122,25 +145,27 @@ function addContextMenuOptions(node) {
|
||||
options.push({
|
||||
content: "Reset Slider Values",
|
||||
callback: () => {
|
||||
const count = getWidgetValue(findWidgetByName(this, "slider_count"));
|
||||
const defaultValue = 1.0;
|
||||
|
||||
const count = getWidgetValue(findWidgetByName(this, "sliders_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");
|
||||
updateNormalization(this, newMax);
|
||||
const masterWidget = findWidgetByName(this, "sliders_max");
|
||||
if (masterWidget && Math.abs(getWidgetValue(masterWidget) - newMax) > 0.001) {
|
||||
masterWidget.value = newMax;
|
||||
} else {
|
||||
updateAllSliderDisplaysFromContext(this, newMax);
|
||||
}
|
||||
this.graph.setDirtyCanvas(true, true);
|
||||
if (this.graph) {
|
||||
this.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -148,7 +173,7 @@ function addContextMenuOptions(node) {
|
||||
options.push({
|
||||
content: "Set All to Max Value",
|
||||
callback: () => {
|
||||
const count = getWidgetValue(findWidgetByName(this, "slider_count"));
|
||||
const count = getWidgetValue(findWidgetByName(this, "sliders_count"));
|
||||
let maxValue = -Infinity; // Start low
|
||||
|
||||
// First pass: Find the maximum value among visible sliders
|
||||
@@ -158,37 +183,36 @@ function addContextMenuOptions(node) {
|
||||
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;
|
||||
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");
|
||||
const masterWidget = findWidgetByName(this, "sliders_max");
|
||||
// Update master slider directly - its setter handles updating displays
|
||||
if (masterWidget && Math.abs(getWidgetValue(masterWidget) - newMax) > 0.001) {
|
||||
masterWidget.value = newMax;
|
||||
masterWidget.value = newMax;
|
||||
}
|
||||
if (this.graph) {
|
||||
this.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
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"));
|
||||
const count = getWidgetValue(findWidgetByName(this, "sliders_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})`);
|
||||
@@ -196,40 +220,41 @@ function addContextMenuOptions(node) {
|
||||
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;
|
||||
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");
|
||||
updateNormalization(this, newMax);
|
||||
const masterWidget = findWidgetByName(this, "sliders_max");
|
||||
// Update master slider directly
|
||||
if (masterWidget && Math.abs(getWidgetValue(masterWidget) - newMax) > 0.001) {
|
||||
masterWidget.value = newMax;
|
||||
}
|
||||
this.graph.setDirtyCanvas(true, true);
|
||||
if (this.graph) {
|
||||
this.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// --- ComfyUI Extension ---
|
||||
// --- ComfyUI Extension ---
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.DynamicSlidersStack.ContextMenu",
|
||||
nodeCreated(node) {
|
||||
const nodeTitle = node.getTitle ? node.getTitle() : node.title;
|
||||
if (nodeTitle === "Slider Stacker") {
|
||||
// Use our robust identification instead of title
|
||||
if (isSliderStackerNode(node)) {
|
||||
addContextMenuOptions(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// console.log("Loaded Dynamic Sliders Stack Context Menu Script");
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* Dynamic Sliders Stack - Modern Widget Hider
|
||||
* Manages widget visibility based on slider count
|
||||
*/
|
||||
|
||||
// REMOVE top-level import attempts for app
|
||||
/*
|
||||
// Import app with fallbacks
|
||||
let app;
|
||||
try { app = await import('/scripts/app.js'); } catch (e) {
|
||||
try { app = await import('../../../scripts/app.js'); } catch (e) {
|
||||
app = window.app;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// REMOVE top-level check
|
||||
// const isModernFrontend = app && app.extensionManager;
|
||||
|
||||
// Config
|
||||
const NODE_CONFIG = {
|
||||
controlWidgets: 3, // Number of control widgets
|
||||
maxSliders: 50, // Maximum number of sliders
|
||||
// nodeRadius removed as ComfyUI default is round
|
||||
};
|
||||
|
||||
let domObserver = null; // DOM Mutation observer
|
||||
|
||||
// Helper to find node element
|
||||
const findNodeElement = (nodeId) => document?.getElementById(nodeId) ||
|
||||
document?.querySelector(`.litegraph-node[data-node-id="${nodeId}"]`) ||
|
||||
document?.querySelector(`.comfy-node[data-node-id="${nodeId}"]`) ||
|
||||
document?.querySelector(`.node_box[data-litegraph-id="${nodeId}"]`) ||
|
||||
document?.querySelector(`.litegraph.litecontextualmenu .node_box[data-litegraph-id='${nodeId}']`);
|
||||
|
||||
// Extension definition
|
||||
const extension = {
|
||||
app: null, // Add property to store app instance
|
||||
|
||||
/** Set up the extension */
|
||||
setup(app) { // Receive app as argument
|
||||
// console.log("[DSS Modern Hider] setup() called.");
|
||||
this.app = app; // Store app instance
|
||||
|
||||
// No need to check isModernFrontend here, this script should only load in modern
|
||||
this.createGlobalStyles();
|
||||
this.setupNodeTracking();
|
||||
// console.log("[DSS Modern Hider] setup() completed.");
|
||||
return true; // Indicate setup was successful (or based on actual success)
|
||||
},
|
||||
|
||||
/** Update widget visibility (internal & DOM) */
|
||||
updateWidgetVisibility(node, sliderCount) {
|
||||
// console.log(`[DSS Modern Hider] updateWidgetVisibility called for node ${node.id}, sliderCount: ${sliderCount}`); // Log entry
|
||||
if (!node?.widgets) {
|
||||
// console.warn(`[DSS Modern Hider] No widgets found for node ${node.id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanitize slider count
|
||||
const originalSliderCount = sliderCount;
|
||||
sliderCount = Math.max(1, Math.min(Math.floor(sliderCount), NODE_CONFIG.maxSliders));
|
||||
if (originalSliderCount !== sliderCount) {
|
||||
// console.log(`[DSS Modern Hider] Sanitized sliderCount from ${originalSliderCount} to ${sliderCount}`);
|
||||
}
|
||||
|
||||
// Update internal widget visibility
|
||||
let hiddenChangedCount = 0;
|
||||
node.widgets.slice(NODE_CONFIG.controlWidgets).forEach((widget, i) => {
|
||||
const shouldBeHidden = Math.floor(i / 2) + 1 > sliderCount;
|
||||
if (widget.hidden !== shouldBeHidden) {
|
||||
widget.hidden = shouldBeHidden;
|
||||
hiddenChangedCount++;
|
||||
// console.log(`[DSS Modern Hider] Set widget ${widget.name} (index ${i}) hidden: ${shouldBeHidden}`); // Optional detailed log
|
||||
}
|
||||
});
|
||||
// console.log(`[DSS Modern Hider] Updated 'hidden' property for ${hiddenChangedCount} widgets.`);
|
||||
|
||||
// Remove the DOM manipulation block entirely
|
||||
/*
|
||||
// Update DOM widget visibility <-- REMOVED
|
||||
const nodeElement = findNodeElement(node.id);
|
||||
if (nodeElement) {
|
||||
const widgetContainers = nodeElement.querySelectorAll('.slotcontainer .for_widget');
|
||||
if (widgetContainers?.length > NODE_CONFIG.controlWidgets) {
|
||||
widgetContainers.forEach((container, i) => {
|
||||
if (i >= NODE_CONFIG.controlWidgets) {
|
||||
container.style.display = Math.floor((i - NODE_CONFIG.controlWidgets) / 2) + 1 > sliderCount ? 'none' : '';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
*/
|
||||
},
|
||||
|
||||
/** Create global CSS */
|
||||
createGlobalStyles() {
|
||||
if (document.getElementById('dynamic-slider-stack-styles')) return;
|
||||
const style = document.createElement('style');
|
||||
style.id = 'dynamic-slider-stack-styles';
|
||||
style.textContent = `
|
||||
/* Hide disabled widgets */
|
||||
.widget.disabled { display: none !important; }
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
},
|
||||
|
||||
/** Setup node tracking */
|
||||
setupNodeTracking() {
|
||||
// console.log("[DSS Modern Hider] setupNodeTracking() called.");
|
||||
// Use this.app instead of global app
|
||||
if (!this.app?.graph) {
|
||||
// console.warn("[DSS Modern Hider] this.app.graph not available during setupNodeTracking.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Process existing nodes
|
||||
// console.log(`[DSS Modern Hider] Processing ${this.app.graph.nodes?.length || 0} existing nodes.`);
|
||||
this.app.graph.nodes?.forEach(node => {
|
||||
// Add more comprehensive check including the suffixed name
|
||||
if (node.type === "Slider Stacker (DSS)" || node.type === "SliderStacker" || node.title === "Slider Stacker") {
|
||||
// console.log(`[DSS Modern Hider] Found existing Slider Stacker node (ID: ${node.id}, Type: ${node.type}, Title: ${node.title}). Processing...`);
|
||||
this.processNewNode(node);
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for new nodes
|
||||
// console.log("[DSS Modern Hider] Adding nodeCreated listener.");
|
||||
// Use this.app instead of global app
|
||||
this.app.graph.addEventListener("nodeCreated", (node) => {
|
||||
// console.log(`[DSS Modern Hider] nodeCreated event fired for node (ID: ${node.id}, Type: ${node.type}, Title: ${node.title})`);
|
||||
// Add more comprehensive check including the suffixed name
|
||||
if (node.type === "Slider Stacker (DSS)" || node.type === "SliderStacker" || node.title === "Slider Stacker") {
|
||||
// console.log(`[DSS Modern Hider] nodeCreated is a Slider Stacker. Scheduling processNewNode...`);
|
||||
// Pass 'this' context if processNewNode uses it, although it doesn't currently
|
||||
setTimeout(() => this.processNewNode(node), 0);
|
||||
} else if (node.title === "Slider Receiver") { // Also log receiver for context
|
||||
// console.log(`[DSS Modern Hider] nodeCreated is a Slider Receiver.`);
|
||||
// Potentially call processNewNode if receiver needs processing later
|
||||
}
|
||||
});
|
||||
|
||||
// Setup DOM observer
|
||||
// Pass 'this' context if setupDOMObserver uses it (it doesn't currently)
|
||||
this.setupDOMObserver();
|
||||
// console.log("[DSS Modern Hider] setupNodeTracking() completed.");
|
||||
},
|
||||
|
||||
/** Setup DOM observer */
|
||||
setupDOMObserver() {
|
||||
if (domObserver) domObserver.disconnect();
|
||||
|
||||
domObserver = new MutationObserver(mutations => {
|
||||
mutations.forEach(mutation => {
|
||||
mutation.addedNodes?.forEach(domNode => { // Rename to domNode to avoid confusion with LiteGraph node
|
||||
if (domNode.classList?.contains('comfy-node') && // Check for modern class name
|
||||
domNode.dataset?.nodeType?.includes('Slider Stacker')) { // Check data attribute if available
|
||||
|
||||
// console.log(`[DSS Modern Hider] DOM Observer detected node addition (ID: ${domNode.dataset?.nodeId}, Type: ${domNode.dataset?.nodeType})`);
|
||||
// Attempt to find the LiteGraph node and re-process it
|
||||
// Use this.app
|
||||
const lgNode = this.app?.graph?.getNodeById(parseInt(domNode.dataset?.nodeId));
|
||||
if (lgNode) {
|
||||
// console.log(`[DSS Modern Hider] Found corresponding LiteGraph node ${lgNode.id}. Processing...`);
|
||||
this.processNewNode(lgNode);
|
||||
} else {
|
||||
// console.warn(`[DSS Modern Hider] Could not find LiteGraph node for DOM node ID ${domNode.dataset?.nodeId}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Assuming the main graph container might have a different ID or structure in modern
|
||||
// Let's try a more generic approach or rely on setupNodeTracking initially
|
||||
// const graphCanvas = document.getElementById('graph-canvas'); // This might be legacy
|
||||
// For now, let's observe the body, which is less efficient but might catch additions
|
||||
// Consider refining this later if needed
|
||||
// console.log("[DSS Modern Hider] Setting up DOM Observer on document.body.");
|
||||
domObserver.observe(document.body, { childList: true, subtree: true });
|
||||
},
|
||||
|
||||
/** Process a new node */
|
||||
processNewNode(node) {
|
||||
// console.log(`[DSS Modern Hider] processNewNode called for node (ID: ${node.id}, Type: ${node.type}, Title: ${node.title})`); // Log process entry
|
||||
if (!node) {
|
||||
// console.warn("[DSS Modern Hider] processNewNode called with null node.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Intercept widget and update visibility (only for Stacker)
|
||||
// Use more robust check
|
||||
if (node.type === "Slider Stacker (DSS)" || node.type === "SliderStacker" || node.title === "Slider Stacker") {
|
||||
// console.log(`[DSS Modern Hider] Node ${node.id} confirmed as Slider Stacker, calling interceptAndUpdate.`);
|
||||
this.interceptAndUpdate(node);
|
||||
}
|
||||
// No styling needed here, assuming ComfyUI default round shape
|
||||
},
|
||||
|
||||
/** Intercept sliders_count widget setter and update visibility */
|
||||
interceptAndUpdate(node) {
|
||||
const widget = node.widgets?.find(w => w.name === "sliders_count");
|
||||
if (!widget) {
|
||||
// console.warn(`[DSS Modern Hider] 'sliders_count' widget not found for node ${node.id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (widget._valueSetterOverridden) {
|
||||
// console.log(`[DSS Modern Hider] Setter already overridden for node ${node.id}. Triggering initial update.`);
|
||||
this.updateWidgetVisibility(node, widget.value);
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log(`[DSS Modern Hider] Intercepting 'sliders_count' setter for node ${node.id}`);
|
||||
const originalSetter = Object.getOwnPropertyDescriptor(widget, "value")?.set;
|
||||
widget._valueSetterOverridden = true;
|
||||
|
||||
Object.defineProperty(widget, "value", {
|
||||
get: function() { return this._value; },
|
||||
set: function(v) {
|
||||
// console.log(`[DSS Modern Hider] 'sliders_count' setter triggered for node ${node.id}. New value: ${v}, Old value: ${this._value}`);
|
||||
const oldValue = this._value;
|
||||
this._value = v;
|
||||
if (originalSetter) originalSetter.call(this, v);
|
||||
if (oldValue !== v) {
|
||||
// console.log(`[DSS Modern Hider] Value changed, calling updateWidgetVisibility.`);
|
||||
extension.updateWidgetVisibility(node, v);
|
||||
} else {
|
||||
// console.log(`[DSS Modern Hider] Value did not change, skipping update.`);
|
||||
}
|
||||
},
|
||||
enumerable: true, configurable: true
|
||||
});
|
||||
|
||||
// console.log(`[DSS Modern Hider] Triggering initial update for node ${node.id} with value ${widget.value}`);
|
||||
this.updateWidgetVisibility(node, widget.value); // Initial update
|
||||
},
|
||||
|
||||
/** Clean up resources */
|
||||
cleanup() {
|
||||
if (domObserver) { domObserver.disconnect(); domObserver = null; }
|
||||
const style = document.getElementById('dynamic-slider-stack-styles');
|
||||
if (style) style.remove();
|
||||
}
|
||||
};
|
||||
|
||||
export default extension;
|
||||
+195
-136
@@ -1,150 +1,209 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
// --- Start: Simplified code snippets ---
|
||||
|
||||
let origProps = {};
|
||||
let initialized = false;
|
||||
|
||||
// Helper function to find a widget by name
|
||||
const findWidgetByName = (node, name) => {
|
||||
return node.widgets ? node.widgets.find((w) => w.name === name) : null;
|
||||
};
|
||||
|
||||
const doesInputWithNameExist = (node, name) => {
|
||||
return node.inputs ? node.inputs.some((input) => input.name === name) : false;
|
||||
};
|
||||
|
||||
const HIDDEN_TAG = "tschide";
|
||||
function toggleWidget(node, widget, show = false, suffix = "") {
|
||||
// ... (toggleWidget function remains the same) ...
|
||||
if (!widget || doesInputWithNameExist(node, widget.name)) return;
|
||||
if (!origProps[widget.name]) {
|
||||
origProps[widget.name] = { origType: widget.type, origComputeSize: widget.computeSize };
|
||||
}
|
||||
const origSize = node.size;
|
||||
widget.type = show ? origProps[widget.name].origType : HIDDEN_TAG + suffix;
|
||||
widget.computeSize = show ? origProps[widget.name].origComputeSize : () => [0, -4];
|
||||
widget.linkedWidgets?.forEach(w => toggleWidget(node, w, ":" + widget.name, show));
|
||||
let newHeight = node.size[1];
|
||||
try {
|
||||
if (node.computeSize) {
|
||||
newHeight = node.computeSize()[1];
|
||||
} else if (node.onResize) {
|
||||
node.onResize(node.size);
|
||||
newHeight = node.size[1];
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error computing node size for", node.getTitle(), e);
|
||||
}
|
||||
node.setSize([node.size[0], newHeight]);
|
||||
}
|
||||
|
||||
function generateWidgetNames(baseName, count) {
|
||||
return Array.from({ length: count }, (_, i) => `${baseName}_${i + 1}`);
|
||||
}
|
||||
|
||||
function handleSliderVisibility(node, countValue) {
|
||||
const node_type = "Slider Stacker";
|
||||
const baseNamesMap = {
|
||||
"Slider Stacker": ["slider_name", ""]
|
||||
};
|
||||
const baseNames = baseNamesMap[node_type];
|
||||
if (!baseNames) return;
|
||||
const sliderCountWidget = findWidgetByName(node, "slider_count");
|
||||
const maxSliders = sliderCountWidget?.options?.max || 50;
|
||||
for (let i = 1; i <= maxSliders; i++) {
|
||||
const nameWidget = findWidgetByName(node, `${baseNames[0]}_${i}`);
|
||||
const weightWidget = findWidgetByName(node, `(${i})`);
|
||||
const currentCount = Number(countValue) || 0;
|
||||
const show = i <= currentCount;
|
||||
toggleWidget(node, nameWidget, show);
|
||||
toggleWidget(node, weightWidget, show);
|
||||
}
|
||||
node.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
|
||||
function handleSliderStackerSliderCount(node, widget) {
|
||||
handleSliderVisibility(node, widget.value);
|
||||
}
|
||||
|
||||
// Updated handler map key to match new display name
|
||||
const nodeWidgetHandlers = {
|
||||
"Slider Stacker": { // Updated node title check
|
||||
'slider_count': handleSliderStackerSliderCount
|
||||
}
|
||||
};
|
||||
|
||||
function widgetLogic(node, widget) {
|
||||
// Function to check if node is our Slider Stacker
|
||||
function isSliderStackerNode(node) {
|
||||
const nodeTitle = node.getTitle ? node.getTitle() : node.title;
|
||||
// Use the potentially updated title for the handler check
|
||||
const handler = nodeWidgetHandlers[nodeTitle]?.[widget.name];
|
||||
if (handler) {
|
||||
handler(node, widget);
|
||||
}
|
||||
if (nodeTitle === "Slider Stacker") return true;
|
||||
if (node.type === "Slider Stacker (DSS)") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- End: Simplified code snippets ---
|
||||
// Constants for hiding
|
||||
const HIDDEN_TAG = "_hidden_"; // Needs a unique suffix if other nodes use similar tags
|
||||
|
||||
app.registerExtension({
|
||||
// Updated extension name
|
||||
name: "Comfy.DynamicSlidersStack.WidgetHider",
|
||||
nodeCreated(node) {
|
||||
const nodeTitle = node.getTitle ? node.getTitle() : node.title;
|
||||
// Check for the new display name
|
||||
if (nodeTitle === "Slider Stacker") {
|
||||
let hasSliderStackerHandler = false;
|
||||
// Cache for original widget properties
|
||||
let originalWidgetProps = {};
|
||||
|
||||
for (const w of node.widgets || []) {
|
||||
// Use updated title for handler check
|
||||
if (nodeWidgetHandlers[nodeTitle]?.[w.name]) {
|
||||
hasSliderStackerHandler = true;
|
||||
}
|
||||
}
|
||||
// Function to toggle widget visibility using standard JS properties
|
||||
function toggleWidgetVisibility(node, widget, show) {
|
||||
if (!widget) return false; // Return false if no widget
|
||||
|
||||
if(hasSliderStackerHandler) {
|
||||
for (const w of node.widgets) {
|
||||
// Use updated title for handler check
|
||||
if (nodeWidgetHandlers[nodeTitle]?.[w.name]) {
|
||||
// ... (value interception logic remains the same) ...
|
||||
let widgetValue = w.value;
|
||||
let originalDescriptor = Object.getOwnPropertyDescriptor(w, 'value');
|
||||
if (!originalDescriptor) {
|
||||
let proto = Object.getPrototypeOf(w);
|
||||
while (proto && !originalDescriptor && proto !== Object.prototype) {
|
||||
originalDescriptor = Object.getOwnPropertyDescriptor(proto, 'value');
|
||||
proto = Object.getPrototypeOf(proto);
|
||||
}
|
||||
}
|
||||
Object.defineProperty(w, 'value', {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
let valueToReturn = originalDescriptor?.get
|
||||
? originalDescriptor.get.call(w)
|
||||
: widgetValue;
|
||||
return valueToReturn;
|
||||
},
|
||||
set(newVal) {
|
||||
if (originalDescriptor?.set) {
|
||||
originalDescriptor.set.call(w, newVal);
|
||||
} else {
|
||||
widgetValue = newVal;
|
||||
}
|
||||
widgetLogic(node, w);
|
||||
}
|
||||
});
|
||||
widgetLogic(node, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Trigger full node redraw after setup
|
||||
setTimeout(() => {
|
||||
const sliderCountWidget = findWidgetByName(node, "slider_count");
|
||||
if (sliderCountWidget) {
|
||||
handleSliderVisibility(node, sliderCountWidget.value);
|
||||
} else {
|
||||
node.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
}, 50);
|
||||
// Store original properties if not already saved
|
||||
const widgetKey = `${node.id}_${widget.name}`;
|
||||
if (!originalWidgetProps[widgetKey]) {
|
||||
originalWidgetProps[widgetKey] = {
|
||||
origType: widget.type,
|
||||
origComputeSize: widget.computeSize,
|
||||
origHidden: widget.hidden // Store original hidden state if available
|
||||
};
|
||||
}
|
||||
|
||||
const origProps = originalWidgetProps[widgetKey];
|
||||
// Check both hidden property and our custom type tag
|
||||
const isCurrentlyHidden = widget.hidden || widget.type === HIDDEN_TAG;
|
||||
|
||||
// Only change if the state needs toggling
|
||||
if (show && !isCurrentlyHidden) return false; // Return false: no change needed
|
||||
if (!show && isCurrentlyHidden) return false; // Return false: no change needed
|
||||
|
||||
if (show) {
|
||||
// Restore original properties
|
||||
widget.type = origProps.origType;
|
||||
widget.hidden = origProps.origHidden || false;
|
||||
widget.computeSize = origProps.origComputeSize || null; // Restore original or set to null
|
||||
} else {
|
||||
// Hide the widget
|
||||
widget.type = HIDDEN_TAG;
|
||||
widget.hidden = true;
|
||||
widget.computeSize = () => [0, -4]; // Standard way to hide widget space
|
||||
}
|
||||
|
||||
// Mark the node as needing redraw
|
||||
node.setDirtyCanvas(true, false); // Only redraw foreground (nodes)
|
||||
return true; // Return true: change was made
|
||||
}
|
||||
|
||||
// Function to handle the actual update of slider visibility
|
||||
function updateSliderWidgetsVisibility(node) {
|
||||
if (!isSliderStackerNode(node) || !node.widgets) return;
|
||||
|
||||
const countWidget = findWidgetByName(node, "sliders_count");
|
||||
if (!countWidget) {
|
||||
// Don't log error repeatedly if widget is just missing temporarily during load
|
||||
return;
|
||||
}
|
||||
|
||||
let currentCount = 1;
|
||||
try {
|
||||
currentCount = parseInt(countWidget.value) || 1;
|
||||
currentCount = Math.max(1, Math.min(currentCount, 50)); // Clamp value (min 1)
|
||||
} catch (e) {
|
||||
console.error("Slider Stacker: Error parsing sliders_count value", e); // Keep error log
|
||||
currentCount = 1; // Default to 1 on error
|
||||
}
|
||||
|
||||
const maxSliders = 50; // Assuming max 50 sliders based on Python code
|
||||
let visibilityChanged = false;
|
||||
// Store current width to preserve it
|
||||
const currentWidth = node.size[0];
|
||||
|
||||
for (let i = 1; i <= maxSliders; i++) {
|
||||
const nameWidget = findWidgetByName(node, `slider_name_${i}`);
|
||||
const sliderWidget = findWidgetByName(node, `(${i})`);
|
||||
const shouldShow = i <= currentCount;
|
||||
|
||||
let nameChanged = false;
|
||||
let sliderChanged = false;
|
||||
|
||||
if (nameWidget) {
|
||||
nameChanged = toggleWidgetVisibility(node, nameWidget, shouldShow);
|
||||
}
|
||||
if (sliderWidget) {
|
||||
sliderChanged = toggleWidgetVisibility(node, sliderWidget, shouldShow);
|
||||
}
|
||||
|
||||
if (nameChanged || sliderChanged) {
|
||||
visibilityChanged = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Trigger a resize and redraw if visibility actually changed
|
||||
if (visibilityChanged && node.graph) {
|
||||
// Recalculate the required size based on visible widgets
|
||||
let newHeight = node.size[1]; // Default to current height
|
||||
if (node.computeSize) {
|
||||
try {
|
||||
const computedSize = node.computeSize();
|
||||
if (computedSize && computedSize.length === 2) {
|
||||
newHeight = computedSize[1]; // Get the computed height
|
||||
}
|
||||
} catch (e) {
|
||||
// Keep error log
|
||||
console.error("Slider Stacker: Error computing node size after visibility change", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the size, preserving width and using the new height
|
||||
node.setSize([currentWidth, newHeight]);
|
||||
|
||||
// Mark the whole graph canvas as dirty to ensure redraw and layout adjustment
|
||||
node.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Main App Extension
|
||||
app.registerExtension({
|
||||
name: "Comfy.DynamicSlidersStack.WidgetHider.JS",
|
||||
|
||||
// Called once per node type before registration
|
||||
beforeRegisterNodeDef(nodeType, nodeData, appInstance) {
|
||||
if (nodeData.name === "Slider Stacker (DSS)") {
|
||||
// Store original onConfigure if it exists
|
||||
const originalOnConfigure = nodeType.prototype.onConfigure;
|
||||
|
||||
// Override onConfigure to apply initial visibility
|
||||
nodeType.prototype.onConfigure = function(info) {
|
||||
if (originalOnConfigure) {
|
||||
originalOnConfigure.apply(this, arguments);
|
||||
}
|
||||
requestAnimationFrame(() => updateSliderWidgetsVisibility(this));
|
||||
};
|
||||
|
||||
// Store original onConnectionsChange if it exists
|
||||
const originalOnConnectionsChange = nodeType.prototype.onConnectionsChange;
|
||||
|
||||
// Override onConnectionsChange to potentially update visibility if needed
|
||||
nodeType.prototype.onConnectionsChange = function(side, slot, connect, link_info, output) {
|
||||
if (originalOnConnectionsChange) {
|
||||
originalOnConnectionsChange.apply(this, arguments);
|
||||
}
|
||||
requestAnimationFrame(() => updateSliderWidgetsVisibility(this));
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// Called every time a node instance is created
|
||||
nodeCreated(node) {
|
||||
if (!isSliderStackerNode(node)) return;
|
||||
|
||||
const countWidget = findWidgetByName(node, "sliders_count");
|
||||
if (!countWidget) {
|
||||
// Don't log error repeatedly
|
||||
return;
|
||||
}
|
||||
|
||||
// Intercept the 'value' property of the sliders_count widget
|
||||
let widgetValue = countWidget.value;
|
||||
const descriptor = Object.getOwnPropertyDescriptor(countWidget, 'value');
|
||||
|
||||
Object.defineProperty(countWidget, 'value', {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
return descriptor?.get ? descriptor.get.call(countWidget) : widgetValue;
|
||||
},
|
||||
set(newVal) {
|
||||
let changedValue = newVal;
|
||||
// Ensure value is at least 1
|
||||
changedValue = Math.max(1, parseInt(changedValue) || 1);
|
||||
|
||||
if (descriptor?.set) {
|
||||
descriptor.set.call(countWidget, changedValue);
|
||||
} else {
|
||||
widgetValue = changedValue;
|
||||
}
|
||||
// Update input element visually if it exists
|
||||
if(countWidget.inputEl) {
|
||||
countWidget.inputEl.value = changedValue;
|
||||
}
|
||||
requestAnimationFrame(() => updateSliderWidgetsVisibility(node));
|
||||
}
|
||||
});
|
||||
|
||||
// Initial setup - apply visibility based on the default/loaded value
|
||||
requestAnimationFrame(() => updateSliderWidgetsVisibility(node));
|
||||
},
|
||||
|
||||
// Called when node is added to the graph (might be redundant with nodeCreated)
|
||||
nodeAdded(node) {
|
||||
if (!isSliderStackerNode(node)) return;
|
||||
requestAnimationFrame(() => updateSliderWidgetsVisibility(node));
|
||||
}
|
||||
});
|
||||
|
||||
// console.log("Loaded Dynamic Sliders Stack Widget Hider Script");
|
||||
|
||||
Reference in New Issue
Block a user