Add custom nodes, Civitai loras (LFS), and vast.ai setup script
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Includes 30 custom nodes committed directly, 7 Civitai-exclusive loras stored via Git LFS, and a setup script that installs all dependencies and downloads HuggingFace-hosted models on vast.ai. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
281
custom_nodes/ComfyUI-Impact-Pack/js/common.js
Normal file
281
custom_nodes/ComfyUI-Impact-Pack/js/common.js
Normal file
@@ -0,0 +1,281 @@
|
||||
import { api } from "../../scripts/api.js";
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
let original_show = app.ui.dialog.show;
|
||||
|
||||
export function customAlert(message) {
|
||||
try {
|
||||
app.extensionManager.toast.addAlert(message);
|
||||
}
|
||||
catch {
|
||||
alert(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function isBeforeFrontendVersion(compareVersion) {
|
||||
try {
|
||||
const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__'];
|
||||
if (typeof frontendVersion !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
function parseVersion(versionString) {
|
||||
const parts = versionString.split('.').map(Number);
|
||||
return parts.length === 3 && parts.every(part => !isNaN(part)) ? parts : null;
|
||||
}
|
||||
|
||||
const currentVersion = parseVersion(frontendVersion);
|
||||
const comparisonVersion = parseVersion(compareVersion);
|
||||
|
||||
if (!currentVersion || !comparisonVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (currentVersion[i] > comparisonVersion[i]) {
|
||||
return false;
|
||||
} else if (currentVersion[i] < comparisonVersion[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function dialog_show_wrapper(html) {
|
||||
if (typeof html === "string") {
|
||||
if(html.includes("IMPACT-PACK-SIGNAL: STOP CONTROL BRIDGE")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.textElement.innerHTML = html;
|
||||
} else {
|
||||
this.textElement.replaceChildren(html);
|
||||
}
|
||||
this.element.style.display = "flex";
|
||||
}
|
||||
|
||||
app.ui.dialog.show = dialog_show_wrapper;
|
||||
|
||||
|
||||
function nodeFeedbackHandler(event) {
|
||||
let nodes = app.graph._nodes_by_id;
|
||||
let node = nodes[event.detail.node_id];
|
||||
if(node) {
|
||||
const w = node.widgets.find((w) => event.detail.widget_name === w.name);
|
||||
if(w) {
|
||||
w.value = event.detail.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
api.addEventListener("impact-node-feedback", nodeFeedbackHandler);
|
||||
|
||||
|
||||
function setMuteState(event) {
|
||||
let nodes = app.graph._nodes_by_id;
|
||||
let node = nodes[event.detail.node_id];
|
||||
if(node) {
|
||||
if(event.detail.is_active)
|
||||
node.mode = 0;
|
||||
else
|
||||
node.mode = 2;
|
||||
}
|
||||
}
|
||||
|
||||
api.addEventListener("impact-node-mute-state", setMuteState);
|
||||
|
||||
|
||||
async function bridgeContinue(event) {
|
||||
let nodes = app.graph._nodes_by_id;
|
||||
let node = nodes[event.detail.node_id];
|
||||
if(node) {
|
||||
const mutes = new Set(event.detail.mutes);
|
||||
const actives = new Set(event.detail.actives);
|
||||
const bypasses = new Set(event.detail.bypasses);
|
||||
|
||||
for(let i in app.graph._nodes_by_id) {
|
||||
let this_node = app.graph._nodes_by_id[i];
|
||||
if(mutes.has(i)) {
|
||||
this_node.mode = 2;
|
||||
}
|
||||
else if(actives.has(i)) {
|
||||
this_node.mode = 0;
|
||||
}
|
||||
else if(bypasses.has(i)) {
|
||||
this_node.mode = 4;
|
||||
}
|
||||
}
|
||||
|
||||
await app.queuePrompt(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
api.addEventListener("impact-bridge-continue", bridgeContinue);
|
||||
|
||||
|
||||
function addQueue(event) {
|
||||
app.queuePrompt(0, 1);
|
||||
}
|
||||
|
||||
api.addEventListener("impact-add-queue", addQueue);
|
||||
|
||||
|
||||
function refreshPreview(event) {
|
||||
let node_id = event.detail.node_id;
|
||||
let item = event.detail.item;
|
||||
let img = new Image();
|
||||
img.src = `/view?filename=${item.filename}&subfolder=${item.subfolder}&type=${item.type}&no-cache=${Date.now()}`;
|
||||
let node = app.graph._nodes_by_id[node_id];
|
||||
if(node)
|
||||
node.imgs = [img];
|
||||
}
|
||||
|
||||
api.addEventListener("impact-preview", refreshPreview);
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// MaskRectArea Shared Utilities
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Reads a numeric value from a connected link by inspecting the origin node widget.
|
||||
* More reliable than getInputData() in ComfyUI's frontend execution model.
|
||||
*
|
||||
* @param {LGraphNode} node - LiteGraph node instance
|
||||
* @param {string} inputName - Name of the input to read
|
||||
* @returns {number|null} The numeric value or null if not available
|
||||
*/
|
||||
export function readLinkedNumber(node, inputName) {
|
||||
try {
|
||||
if (!node || !node.graph || !Array.isArray(node.inputs)) {
|
||||
return null;
|
||||
}
|
||||
const inp = node.inputs.find(i => i && i.name === inputName);
|
||||
if (!inp || inp.link == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const link = node.graph.links && node.graph.links[inp.link];
|
||||
if (!link) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const originNode = node.graph.getNodeById
|
||||
? node.graph.getNodeById(link.origin_id)
|
||||
: null;
|
||||
if (!originNode || !Array.isArray(originNode.widgets) || originNode.widgets.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const w = originNode.widgets.find(ww => ww && ww.name === "value")
|
||||
|| originNode.widgets[0];
|
||||
const v = w ? w.value : null;
|
||||
|
||||
return (typeof v === "number") ? v : null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a color based on percentage using HSL color space.
|
||||
*
|
||||
* @param {number} percent - Value between 0 and 1
|
||||
* @param {string} alpha - Hex alpha value (e.g., "ff", "80")
|
||||
* @returns {string} Hex color string with alpha (e.g., "#ff8040ff")
|
||||
*/
|
||||
export function getDrawColor(percent, alpha) {
|
||||
let h = 360 * percent;
|
||||
let s = 50;
|
||||
let l = 50;
|
||||
l /= 100;
|
||||
const a = s * Math.min(l, 1 - l) / 100;
|
||||
const f = n => {
|
||||
const k = (n + h / 30) % 12;
|
||||
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
||||
return Math.round(255 * color).toString(16).padStart(2, '0');
|
||||
};
|
||||
return `#${f(0)}${f(8)}${f(4)}${alpha}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and adjusts canvas size for preview widgets.
|
||||
*
|
||||
* @param {LGraphNode} node - LiteGraph node instance
|
||||
* @param {[number, number]} size - [width, height] array
|
||||
* @param {number} minHeight - Minimum canvas height (REQUIRED)
|
||||
* @param {number} minWidth - Minimum canvas width (REQUIRED)
|
||||
* @returns {void}
|
||||
*/
|
||||
export function computeCanvasSize(node, size, minHeight, minWidth) {
|
||||
// Validate required parameters
|
||||
if (typeof minHeight !== 'number' || typeof minWidth !== 'number') {
|
||||
console.warn('[computeCanvasSize] minHeight and minWidth are required parameters');
|
||||
return;
|
||||
}
|
||||
|
||||
// Null safety check for widgets array
|
||||
if (!node.widgets?.length || node.widgets[0].last_y == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// LiteGraph global availability check
|
||||
const NODE_WIDGET_HEIGHT = (typeof LiteGraph !== 'undefined' && LiteGraph.NODE_WIDGET_HEIGHT)
|
||||
? LiteGraph.NODE_WIDGET_HEIGHT
|
||||
: 20;
|
||||
|
||||
let y = node.widgets[0].last_y + 5;
|
||||
let freeSpace = size[1] - y;
|
||||
|
||||
// Compute the height of all non-customCanvas widgets
|
||||
let widgetHeight = 0;
|
||||
for (let i = 0; i < node.widgets.length; i++) {
|
||||
const w = node.widgets[i];
|
||||
if (w.type !== "customCanvas") {
|
||||
if (w.computeSize) {
|
||||
widgetHeight += w.computeSize()[1] + 4;
|
||||
} else {
|
||||
widgetHeight += NODE_WIDGET_HEIGHT + 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure there is enough vertical space
|
||||
freeSpace -= widgetHeight;
|
||||
|
||||
// Clamp minimum canvas height
|
||||
if (freeSpace < minHeight) {
|
||||
freeSpace = minHeight;
|
||||
}
|
||||
|
||||
// Allow both grow and shrink to fit content
|
||||
const targetHeight = y + widgetHeight + freeSpace;
|
||||
if (node.size[1] !== targetHeight) {
|
||||
node.size[1] = targetHeight;
|
||||
node.graph.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
// Ensure the node width meets the minimum width requirement
|
||||
if (node.size[0] < minWidth) {
|
||||
node.size[0] = minWidth;
|
||||
node.graph.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
// Position each of the widgets
|
||||
for (const w of node.widgets) {
|
||||
w.y = y;
|
||||
if (w.type === "customCanvas") {
|
||||
y += freeSpace;
|
||||
} else if (w.computeSize) {
|
||||
y += w.computeSize()[1] + 4;
|
||||
} else {
|
||||
y += NODE_WIDGET_HEIGHT + 4;
|
||||
}
|
||||
}
|
||||
|
||||
node.canvasHeight = freeSpace;
|
||||
}
|
||||
Reference in New Issue
Block a user