Files
Endless-Nodes/web/endless_ui_helpers.js
tusharbhutt 8da70aa94a Added minimap and node spawner
Added minimap and node spawner
2025-07-25 15:58:03 -06:00

277 lines
9.8 KiB
JavaScript

// === Endless 🌊✨ Tools UI Helper ===
const endlessToolsRegistry = [];
export function registerEndlessTool(name, callback) {
endlessToolsRegistry.push({ name, callback });
}
export function injectEndlessToolsButton() {
const toolbar = findToolbar();
if (!toolbar || document.getElementById("endless-tools-button")) return;
const btn = document.createElement("button");
btn.id = "endless-tools-button";
btn.textContent = "Endless 🌊✨ Tools";
btn.className = "comfyui-button";
btn.style.marginLeft = "8px";
btn.onclick = showEndlessToolMenu;
toolbar.appendChild(btn);
}
export function showEndlessToolMenu() {
document.getElementById("endless-tools-float")?.remove();
const colors = getComfyUIColors();
const menu = document.createElement("div");
menu.id = "endless-tools-float";
menu.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: ${colors.menu};
color: ${colors.inputText};
padding: 12px;
border: 1px solid ${colors.accent};
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
z-index: 99999;
opacity: 1;
width: fit-content;
transition: opacity 0.2s ease;
`;
const dragBar = document.createElement("div");
dragBar.textContent = "Endless 🌊✨ Tools Drag Bar";
dragBar.style.cssText = `
padding: 4px;
background: ${toRGBA(colors.inputText, 0.05)};
cursor: move;
font-size: 12px;
text-align: center;
user-select: none;
border-bottom: 1px solid ${colors.border};
`;
menu.appendChild(dragBar);
endlessToolsRegistry.sort((a, b) => a.name.localeCompare(b.name)).forEach(tool => {
const btn = document.createElement("div");
btn.textContent = `🌊✨ ${tool.name}`;
btn.style.cssText = `
padding: 6px 10px;
cursor: pointer;
border-radius: 4px;
transition: background 0.2s ease;
`;
btn.onmouseover = () => btn.style.background = toRGBA(colors.inputText, 0.1);
btn.onmouseout = () => btn.style.background = "transparent";
btn.onclick = () => {
tool.callback();
menu.remove();
};
menu.appendChild(btn);
});
makeDraggable(menu, dragBar);
// Live theme updater
function updateMenuTheme(newColors = getComfyUIColors()) {
menu.style.background = newColors.menu;
menu.style.color = newColors.inputText;
menu.style.borderColor = newColors.accent;
menu.style.boxShadow = newColors.shadow;
dragBar.style.background = toRGBA(newColors.inputText, 0.05);
dragBar.style.borderBottomColor = newColors.border;
}
const unregister = onThemeChange(updateMenuTheme);
menu.remove = ((orig => function () {
unregister();
orig.call(this);
})(menu.remove));
document.body.appendChild(menu);
}
// === Hotkeys ===
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.altKey && e.key.toLowerCase() === 'e') {
showEndlessToolMenu();
e.preventDefault();
}
if (e.key === "Escape") {
document.getElementById("endless-tools-float")?.remove();
}
});
console.log("Endless 🌊✨ Tools menu: press Ctrl+Alt+E if toolbar button is missing.");
function waitForToolbarAndInject() {
if (document.querySelector('.comfyui-menu')) {
injectEndlessToolsButton();
return;
}
const observer = new MutationObserver(() => {
if (document.querySelector('.comfyui-menu')) {
injectEndlessToolsButton();
observer.disconnect();
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
waitForToolbarAndInject();
function findToolbar() {
return (
document.querySelector('.comfyui-menu, .comfy-menu, [class*="menu"], [class*="toolbar"]') ||
Array.from(document.querySelectorAll('[class*="button-group"], [class*="btn-group"], .comfyui-button-group'))
.find(g => g.querySelectorAll('button').length > 0) ||
Array.from(document.querySelectorAll('*'))
.find(el => {
const buttons = el.querySelectorAll('button');
return buttons.length >= 2 && buttons.length <= 10;
}) ||
Array.from(document.querySelectorAll(".comfyui-button-group"))
.find(div => Array.from(div.querySelectorAll("button")).some(btn => btn.title === "Share"))
);
}
// === Live Theme Monitoring ===
let themeObserver = null;
const themeCallbacks = new Set();
export function onThemeChange(callback) {
themeCallbacks.add(callback);
if (themeCallbacks.size === 1) startThemeObserver();
return () => {
themeCallbacks.delete(callback);
if (themeCallbacks.size === 0) stopThemeObserver();
};
}
function startThemeObserver() {
if (themeObserver) return;
themeObserver = new MutationObserver(() => {
clearTimeout(window.themeChangeTimeout);
window.themeChangeTimeout = setTimeout(() => {
const newColors = getComfyUIColors();
themeCallbacks.forEach(cb => cb(newColors));
}, 100);
});
themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class', 'style', 'data-theme'] });
themeObserver.observe(document.body, { attributes: true, attributeFilter: ['class', 'style', 'data-theme'] });
}
function stopThemeObserver() {
if (themeObserver) {
themeObserver.disconnect();
themeObserver = null;
}
}
export function getComfyUIColors() {
const computed = getComputedStyle(document.documentElement);
const getVar = name => computed.getPropertyValue(name).trim() || null;
return {
fg: getVar("--fg-color") || "#ddd",
bg: getVar("--bg-color") || "#353535",
menu: getVar("--comfy-menu-bg") || "#353535",
menuSecondary: getVar("--comfy-menu-secondary-bg") || "#222",
inputBg: getVar("--comfy-input-bg") || "#222",
inputText: getVar("--input-text") || "#ddd",
descriptionText: getVar("--descrip-text") || "#999",
dragText: getVar("--drag-text") || "#ddd",
errorText: getVar("--error-text") || "#f44336",
border: getVar("--border-color") || "#999",
accent: getVar("--comfy-accent") || getVar("--comfy-accent-color") || "#4a90e2",
hoverBg: getVar("--content-hover-bg") || "rgba(255,255,255,0.1)",
hoverFg: getVar("--content-hover-fg") || "#fff",
shadow: getVar("--bar-shadow") || "0 2px 10px rgba(0,0,0,0.3)",
dialogBg: getVar("--comfy-menu-bg") || getVar("--bg-color") || "#353535",
buttonHoverBg: getVar("--content-hover-bg") || "rgba(255,255,255,0.1)"
};
}
export function toRGBA(color, alpha = 0.2) {
if (!color) return `rgba(128,128,128,${alpha})`;
color = color.trim();
if (color.startsWith('#')) {
const hex = color.slice(1);
const fullHex = hex.length === 3 ? hex.split('').map(c => c + c).join('') : hex;
const bigint = parseInt(fullHex, 16);
const r = (bigint >> 16) & 255, g = (bigint >> 8) & 255, b = bigint & 255;
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
if (color.startsWith('rgb')) {
const rgb = color.match(/\d+/g);
if (rgb?.length >= 3) return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${alpha})`;
}
return `rgba(128,128,128,${alpha})`;
}
export function blendColors(color1, color2, ratio) {
const c1 = toRGBA(color1, 1).match(/\d+/g);
const c2 = toRGBA(color2, 1).match(/\d+/g);
if (!c1 || !c2) return color1;
const r = Math.round(c1[0] * (1 - ratio) + c2[0] * ratio);
const g = Math.round(c1[1] * (1 - ratio) + c2[1] * ratio);
const b = Math.round(c1[2] * (1 - ratio) + c2[2] * ratio);
return `rgb(${r}, ${g}, ${b})`;
}
export function addButtonHoverEffects(container) {
container?.querySelectorAll('button').forEach(button => {
button.addEventListener('mouseenter', () => {
button.style.boxShadow = '0 0 0 1px currentColor';
button.style.filter = 'brightness(1.1)';
button.style.transform = 'translateY(-1px)';
});
button.addEventListener('mouseleave', () => {
button.style.boxShadow = 'none';
button.style.filter = 'brightness(1)';
button.style.transform = 'translateY(0px)';
});
});
}
export function makeDraggable(element, handle = element) {
let offsetX = 0, offsetY = 0, isDown = false;
handle.onmousedown = (e) => {
isDown = true;
if (element.style.position !== 'fixed') {
element.style.position = 'fixed';
element.style.right = 'auto';
}
const rect = element.getBoundingClientRect();
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
element.style.cursor = 'move';
document.onmousemove = (e) => {
if (!isDown) return;
element.style.left = `${e.clientX - offsetX}px`;
element.style.top = `${e.clientY - offsetY}px`;
element.style.transform = 'none';
};
document.onmouseup = () => {
isDown = false;
element.style.cursor = 'default';
document.onmousemove = null;
document.onmouseup = null;
};
};
}
// === Global exposure for F12 ===
window.EndlessHelpers = {
registerEndlessTool,
injectEndlessToolsButton,
showEndlessToolMenu,
onThemeChange,
getComfyUIColors,
toRGBA,
blendColors,
addButtonHoverEffects,
makeDraggable
};