ADDEDD CUSTOM CATEGORIES Added custom setting panel Re-Implemented the 'original' search function to support translated versions of ComfyUI (requires testing) Added LEFT and RIGHT positioning options Added "Auto Hide" feature Now you have the option to use the default node ordering in ComfyUI instead of the alphabetical one Added the possibility to perform a Soft or Factory Reset of the configuration Now most configuration settings are stored in the settings.json file. Fixed some bugs Added some other bugs for sure!
497 lines
14 KiB
JavaScript
497 lines
14 KiB
JavaScript
|
|
let new_menu_options = [];
|
|
let new_submenu_options = [];
|
|
let new_menu_options_callback = [];
|
|
let new_submenu_options_callback = [];
|
|
const settingsStyle = `
|
|
|
|
#comfy-settings-dialog input{
|
|
background: var(--comfy-input-bg);
|
|
color: var(--input-text);
|
|
border: 1px solid var(--border-color);
|
|
padding: 5px;
|
|
}
|
|
#comfy-settings-dialog tr > td:first-child {
|
|
text-align: left;
|
|
}
|
|
#comfy-settings-dialog select{
|
|
background: var(--bg-color);
|
|
color: var(--drag-text);
|
|
padding: 5px;
|
|
}
|
|
|
|
#comfy-settings-dialog {
|
|
|
|
background: var(--bg-color);
|
|
}
|
|
|
|
#comfy-settings-dialog::-webkit-scrollbar {
|
|
margin-top: 0.5rem;
|
|
height: 1rem;
|
|
width: .6rem;
|
|
top: 10px;
|
|
background-color: transparent;
|
|
}
|
|
|
|
#comfy-settings-dialog::-webkit-scrollbar:horizontal {
|
|
height: .5rem;
|
|
width: 2.5rem
|
|
}
|
|
|
|
#comfy-settings-dialog::-webkit-scrollbar-track {
|
|
background-color: var(--comfy-input-bg);
|
|
border-radius: 9999px
|
|
|
|
}
|
|
|
|
#comfy-settings-dialog::-webkit-scrollbar-thumb {
|
|
--tw-border-opacity: 1;
|
|
background-color: var(--border-color);
|
|
border: 0;
|
|
border-radius: 9999px;
|
|
|
|
}
|
|
|
|
#comfy-settings-dialog::-webkit-scrollbar-thumb:hover {
|
|
--tw-bg-opacity: 1;
|
|
background-color: var(--border-color);
|
|
|
|
}
|
|
|
|
.comfy-table td, .comfy-table th {
|
|
border: 0px solid var(--border-color);
|
|
padding: 8px;
|
|
}
|
|
|
|
|
|
`
|
|
async function api_get(url) {
|
|
var response = await api.fetchApi(url, { cache: "no-store" })
|
|
return await response.json()
|
|
}
|
|
|
|
function addDynamicCSSRule(selector, property, value) {
|
|
const styleId = 'dynamic-styles';
|
|
let customStyle = document.getElementById(styleId);
|
|
|
|
if (!customStyle) {
|
|
customStyle = document.createElement('style');
|
|
customStyle.type = 'text/css';
|
|
customStyle.id = styleId;
|
|
document.head.appendChild(customStyle);
|
|
}
|
|
|
|
const existingRule = Array.from(customStyle.sheet.cssRules).find(rule => rule.selectorText === selector);
|
|
if (existingRule) {
|
|
existingRule.style[property] = value;
|
|
} else {
|
|
|
|
customStyle.sheet.insertRule(`${selector} { ${property}: ${value} !important; }`, customStyle.sheet.cssRules.length);
|
|
}
|
|
}
|
|
|
|
function addJavascriptVar(name, value) {
|
|
const scripta = document.createElement('script');
|
|
scripta.type = 'text/javascript';
|
|
console.log("var "+name+" = "+value)
|
|
scripta.innerHTML = "var "+name+" = "+value;
|
|
|
|
document.head.appendChild(scripta);
|
|
|
|
}
|
|
function getVar(name) {
|
|
var varValue = localStorage.getItem(name);
|
|
if (varValue === null) {
|
|
if (name=="pinnedItems") {
|
|
//get cookie pinnedItems
|
|
var cookies = document.cookie.split('; ');
|
|
cookies.forEach(cookie => {
|
|
const [cookieName, cookieValue] = cookie.split('=');
|
|
if (cookieName === name) {
|
|
|
|
setVar(name, cookieValue)
|
|
|
|
}
|
|
})
|
|
//delete cookie pinnedItems
|
|
document.cookie = name + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
|
|
}else {
|
|
return varValue;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function setVar(name, value) {
|
|
/*migration to localstorage*/
|
|
localStorage.setItem(name, value);
|
|
|
|
}
|
|
function removeVar(name) {
|
|
localStorage.removeItem(name);
|
|
}
|
|
|
|
function renameVar(oldName, newName) {
|
|
const oldValue = getVar(oldName);
|
|
setVar(newName, oldValue);
|
|
removeVar(oldName);
|
|
|
|
}
|
|
|
|
function getElementPosition(element) {
|
|
const rect = element.getBoundingClientRect();
|
|
return {
|
|
top: rect.top,
|
|
left: rect.left
|
|
};
|
|
}
|
|
|
|
function reverseAndAppend(originalName) {
|
|
let words;
|
|
let lastWord;
|
|
let reversedWords;
|
|
let reversedString;
|
|
let finalName;
|
|
if (originalName.includes(' ')) {
|
|
const words = originalName.split(' ');
|
|
|
|
lastWord = words.pop();
|
|
|
|
|
|
reversedWords = words.reverse();
|
|
reversedString = reversedWords.join(' ');
|
|
|
|
finalName = originalName + ' ' + reversedString;
|
|
|
|
return finalName;
|
|
|
|
} else {
|
|
|
|
|
|
words = originalName.split(/(?=[A-Z])/);
|
|
lastWord = words.pop();
|
|
reversedWords = words.reverse();
|
|
reversedString = reversedWords.join('');
|
|
finalName = originalName + reversedString;
|
|
|
|
return finalName;
|
|
|
|
}
|
|
}
|
|
function cleanText(text) {
|
|
let cleanedText = text.replace(/[^a-zA-Z0-9\s\n\-]/g, '');
|
|
cleanedText = cleanedText.replace(/\s+/g, ' ');
|
|
cleanedText = cleanedText.replace(/\n/g, '');
|
|
return cleanedText;
|
|
}
|
|
|
|
|
|
|
|
//CONTEXT MENU
|
|
function createContextMenu(event, subMenus,settingsData) {
|
|
event.preventDefault();
|
|
hideAllSubMenus();
|
|
|
|
const sbcontextMenu = document.getElementById('customMenu');
|
|
const menuOptions = document.getElementById('menu-options');
|
|
|
|
// Clear existing menu items
|
|
menuOptions.innerHTML = '';
|
|
|
|
// Populate main options
|
|
settingsData.menuctx_options.forEach((option, index) => {
|
|
const li = document.createElement('li');
|
|
li.textContent = option;
|
|
li.addEventListener('mouseenter', () => {
|
|
hideAllSubMenus();
|
|
showSubMenu(subMenus[index], li);
|
|
});
|
|
try {
|
|
if (typeof (window[settingsData.menuctx_opt_callback[index]]) === 'function') {
|
|
li.addEventListener('click', () => {
|
|
// Handle main option click
|
|
//alert(`Selected option: ${option}`);
|
|
|
|
|
|
window[settingsData.menuctx_opt_callback[index]](event);
|
|
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
menuOptions.appendChild(li);
|
|
});
|
|
|
|
// Show context menu
|
|
sbcontextMenu.style.display = 'block';
|
|
sbcontextMenu.style.left = event.clientX + 'px';
|
|
sbcontextMenu.style.top = event.clientY + 'px';
|
|
|
|
// Hide context menu on body click
|
|
document.body.addEventListener('click', hideContextMenu);
|
|
}
|
|
|
|
function showSubMenu(subMenu, element) {
|
|
try{
|
|
if (subMenu) {
|
|
subMenu.style.display = 'block';
|
|
|
|
const rect = element.getBoundingClientRect();
|
|
subMenu.style.top = element.offsetTop + 'px';
|
|
|
|
subMenuVisible = true;
|
|
}
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
function hideAllSubMenus() {
|
|
const subMenus = document.querySelectorAll('.sub-menu');
|
|
subMenus.forEach(subMenu => {
|
|
subMenu.style.display = 'none';
|
|
});
|
|
subMenuVisible = false;
|
|
}
|
|
|
|
function hideContextMenu() {
|
|
const sbcontextMenu = document.getElementById('customMenu');
|
|
sbcontextMenu.style.display = 'none';
|
|
document.body.removeEventListener('click', hideContextMenu);
|
|
}
|
|
|
|
let subMenuVisible = false;
|
|
let lastTargetClicked = null;
|
|
|
|
async function setContextMenu(settingsData,class_menu_item,main=0,exluded_class=null,included_class = null) {
|
|
|
|
|
|
if (main==1){
|
|
settingsData.menuctx_options = settingsData.menuctx_options.concat(new_menu_options);
|
|
settingsData.menuctx_subOptions = settingsData.menuctx_subOptions.concat(new_submenu_options);
|
|
settingsData.menuctx_opt_callback = settingsData.menuctx_opt_callback.concat(new_menu_options_callback);
|
|
settingsData.menuctx_sub_opt_callback = settingsData.menuctx_sub_opt_callback.concat(new_submenu_options_callback);
|
|
}
|
|
const sbcontextMenu = document.getElementById('customMenu');
|
|
const menuOptions = document.createElement("ul");
|
|
menuOptions.id = "menu-options";
|
|
sbcontextMenu.appendChild(menuOptions);
|
|
|
|
const subMenus = [];
|
|
for (let i = 0; i < settingsData.menuctx_subOptions.length; i++) {
|
|
|
|
|
|
|
|
const subMenu = document.createElement("ul");
|
|
subMenu.id = `sub-menu-op${i + 1}`;
|
|
subMenu.classList.add("sub-menu");
|
|
if (settingsData.menuctx_subOptions[i].length == 0) {
|
|
subMenu.classList.add("cat_empty");
|
|
}
|
|
|
|
sbcontextMenu.appendChild(subMenu);
|
|
subMenus.push(subMenu);
|
|
|
|
for (let j = 0; j < settingsData.menuctx_subOptions[i].length; j++) {
|
|
const li = document.createElement("li");
|
|
li.textContent = settingsData.menuctx_subOptions[i][j];
|
|
subMenu.appendChild(li);
|
|
try {
|
|
|
|
if (typeof (window[settingsData.menuctx_sub_opt_callback[i][j]]) === 'function') {
|
|
li.addEventListener('click', () => {
|
|
// Handle main option click
|
|
|
|
//alert(`Selected option: ${settingsData.menuctx_subOptions[i][j]}`);
|
|
|
|
|
|
window[settingsData.menuctx_sub_opt_callback[i][j]](lastTargetClicked,settingsData.menuctx_subOptions[i][j]);
|
|
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
const sidebarItems = document.querySelectorAll(class_menu_item);
|
|
//const sb =document.getElementById('content_sidebar_home');
|
|
|
|
|
|
for (const sidebarItem of sidebarItems) {
|
|
sidebarItem.addEventListener('contextmenu', (event) => {
|
|
|
|
if(!event.target.classList.contains(exluded_class)){
|
|
|
|
if (included_class != null && !event.target.classList.contains(included_class)){
|
|
return;
|
|
}
|
|
createContextMenu (event, subMenus,settingsData);
|
|
lastTargetClicked = event;
|
|
}
|
|
});
|
|
}
|
|
|
|
//ContextMenu
|
|
//sb.addEventListener("contextmenu", function(event) {
|
|
// event.preventDefault();
|
|
//
|
|
// const customMenu = document.getElementById("customMenu");
|
|
// customMenu.style.display = "block";
|
|
// customMenu.style.left = event.pageX + "px";
|
|
// customMenu.style.top = event.pageY + "px";
|
|
//});
|
|
//
|
|
window.addEventListener("click", function(event) {
|
|
const customMenu = document.getElementById("customMenu");
|
|
customMenu.style.display = "none";
|
|
});
|
|
}
|
|
|
|
|
|
// Function to check for special characters
|
|
function containsSpecialCharacters(name) {
|
|
// Define a regular expression pattern to match special characters
|
|
var specialCharacters = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/;
|
|
// Return true if the name contains any special characters, false otherwise
|
|
return specialCharacters.test(name);
|
|
}
|
|
|
|
async function reloadCtxMenu() {
|
|
const cnPath="../extensions/ComfyUI-N-Sidebar/"
|
|
const response3 = await fetch(cnPath +'settings.json');
|
|
const settingsData = await response3.json();
|
|
|
|
setContextMenu(settingsData,"#content_sidebar_home .sidebarItem",1);
|
|
|
|
}
|
|
|
|
function setNodeStatus(node,status) {
|
|
try{
|
|
const categoryNodeStatus = JSON.parse(localStorage.getItem('sb_categoryNodeStatus')) || {};
|
|
categoryNodeStatus[node] = [status];
|
|
localStorage.setItem('sb_categoryNodeStatus', JSON.stringify(categoryNodeStatus));
|
|
}catch(err){ }
|
|
}
|
|
|
|
function getNodeStatus(node) {
|
|
try{
|
|
const categoryNodeStatus = JSON.parse(localStorage.getItem('sb_categoryNodeStatus')) || {};
|
|
return categoryNodeStatus[node] || "none";
|
|
}catch(err){ }
|
|
return "none";
|
|
}
|
|
|
|
function getNodesStatus() {
|
|
let categoryNodeStatus = JSON.parse(localStorage.getItem('sb_categoryNodeStatus')) || {};
|
|
return categoryNodeStatus;
|
|
}
|
|
|
|
|
|
// MODAL
|
|
|
|
function setModalContext(addon=null) {
|
|
if (addon==null) {
|
|
return `
|
|
<h2 class="sb-modal-title">Add New Category</h2>
|
|
<form id="sb-category-form" action="javascript:void(0);">
|
|
<input type="text" autocomplete="off" class="sb-input" id="sb-categoryName" name="sb-categoryName" placeholder="Category Name" required>
|
|
<button type="submit" class="sb-button" onclick="addCategory()">Add Category</button>
|
|
</form>
|
|
<button id="closeModalButton" class="sb-button">X</button>
|
|
`;
|
|
}
|
|
else {
|
|
return addon
|
|
}
|
|
}
|
|
|
|
function openModal(addon=null) {
|
|
// Create HTML elements for the modal
|
|
const modalBackdrop = document.createElement('div');
|
|
modalBackdrop.classList.add('sb-modal-backdrop');
|
|
|
|
const modal = document.createElement('div');
|
|
modal.classList.add('sb-category-dialog');
|
|
modal.innerHTML = setModalContext(addon)
|
|
|
|
// Add the modal to the DOM
|
|
document.body.appendChild(modalBackdrop);
|
|
document.body.appendChild(modal);
|
|
|
|
// Add an event listener for the close button
|
|
const closeModalButton = modal.querySelector('#closeModalButton');
|
|
closeModalButton.addEventListener('click', closeModal);
|
|
try{
|
|
// Focus the input field
|
|
document.getElementById('sb-categoryName').focus();
|
|
}catch(err){
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Function to close the modal
|
|
function closeModal() {
|
|
const modalBackdrop = document.querySelector('.sb-modal-backdrop');
|
|
const modal = document.querySelector('.sb-category-dialog');
|
|
|
|
// Remove the modal from the DOM
|
|
modalBackdrop.parentNode.removeChild(modalBackdrop);
|
|
modal.parentNode.removeChild(modal);
|
|
}
|
|
|
|
function rgbToHex(rgb) {
|
|
// Verifica se il colore è nel formato "rgb()"
|
|
if (rgb.indexOf('rgb') !== -1) {
|
|
// Esegue il parsing dei valori di rosso, verde e blu
|
|
const [r, g, b] = rgb.match(/\d+/g);
|
|
|
|
// Converte i valori in esadecimale e li concatena con "#"
|
|
return `#${parseInt(r, 10).toString(16).padStart(2, '0')}${parseInt(g, 10).toString(16).padStart(2, '0')}${parseInt(b, 10).toString(16).padStart(2, '0')}`;
|
|
}
|
|
return rgb; // Se il colore è già nel formato con "#", lo restituisce direttamente
|
|
}
|
|
|
|
|
|
function hexToRgb(hex) {
|
|
try{
|
|
// Rimuovi il carattere # se presente
|
|
hex = hex.replace(/^#/, '');
|
|
|
|
// Estrai i valori R, G e B
|
|
let r = parseInt(hex.substring(0, 2), 16);
|
|
let g = parseInt(hex.substring(2, 4), 16);
|
|
let b = parseInt(hex.substring(4, 6), 16);
|
|
|
|
// Restituisci il colore in formato RGB
|
|
return `${r}, ${g}, ${b}`;
|
|
}catch(err){
|
|
console.log(err)
|
|
return "#000000";
|
|
}
|
|
}
|
|
|
|
//click anywhere outside the modal to close it
|
|
document.body.addEventListener('click', function(event) {
|
|
const modalBackdrop = document.querySelector('.sb-modal-backdrop');
|
|
const modal = document.querySelector('.sb-category-dialog');
|
|
if (event.target === modalBackdrop) {
|
|
closeModal();
|
|
}
|
|
})
|
|
|
|
|
|
|
|
|
|
|