- Fixed some bugs

- Added Templates support
    - Added ability categorize custom categories
    - Added preview (list of used nodes)

  - Added Workflow support!
    - Added ability to rename workflows
    - Added ability to remove workflows
    - Added ability to categorize workflows
    - Added preview (list of used nodes), (disabled by default)
This commit is contained in:
Nuked
2024-05-25 16:24:19 +02:00
parent 5571833337
commit a1eefbb87a
19 changed files with 3155 additions and 108 deletions
+24 -3
View File
@@ -11,6 +11,21 @@ For what i know nobody did it, so i did it.
Maybe you don't need it. I need it >.<
# Updates
- 25-05-2024
- Fixed some bugs
- Added Templates support**
- Added ability categorize custom categories
- Added preview (list of used nodes)
- Added Workflow support!
- Added ability to rename workflows
- Added ability to remove workflows
- Added ability to categorize workflows
- Added preview (list of used nodes), (disabled by default)
** i did not enabled the **rename** and **delete** because it's conflicing with the TemplateManager
- 21-04-2024
- Rewritten a lot of the code to improve performance and readability, and slightly adjusted the UI to ensure compatibility with future updates.
- ADDED CUSTOM CATEGORIES
@@ -80,7 +95,6 @@ This feature is enabled by default, you can disable it in the settings. I've use
![Custom Categories](./images/custom_categories.gif)
### ➕Expand/Collapse Categories/Sidebar➖
![Expand/Collapse Categories and Sidebar](./images/expand_collapse.gif)
@@ -92,14 +106,21 @@ This feature is enabled by default, you can disable it in the settings. I've use
### 👁 Preview Node 👁
![Preview Node](./images/preview.gif)
### 📄 Templates Support 📄
![Templates Support](./images/templates.gif)
#### 📄 Workflow Support 📄
![Workflow Support](./images/workflows.gif)
### 🎨 ComfyUI Themes Support 🎨
![Themes Support ](./images/theme.gif)
### 🎨 New Settings Panel 🎨
![ComfyUI Settings](./images/settings.png)
### Additional Features
- Width and Minimized State of the sidebar are saved in cookies
PS: Workflow Path
### Todo:
+97 -3
View File
@@ -3,6 +3,8 @@ import folder_paths
from aiohttp import web
import os
import json
from collections import OrderedDict
from pathlib import Path
@@ -14,7 +16,8 @@ class JSONConfigManager:
"menuctx_options": [],
"menuctx_subOptions": [],
"menuctx_opt_callback": [],
"menuctx_sub_opt_callback": []
"menuctx_sub_opt_callback": [],
"sb_wf_path": ""
}"""
if os.path.isfile(file_name):
self.data = self.read_json_file()
@@ -67,7 +70,7 @@ else:
file_path = os.path.join(folder_paths.get_folder_paths("custom_nodes")[0],"ComfyUI-N-Sidebar","app","settings.json")
file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"app","settings.json")
config_manager = JSONConfigManager(file_path)
@@ -75,8 +78,27 @@ config_manager = JSONConfigManager(file_path)
NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
list_panels = os.path.join(folder_paths.get_folder_paths("custom_nodes")[0],"ComfyUI-N-Sidebar","app","panels")
default_path = os.path.join(folder_paths.base_path, "workflows")
try:
list_workflows_dirs = config_manager.read_item("sb_wf_path").replace("\n\r", "\n").split('\n')
except:
print("No 'sb_wf_path' in settings.json. Using default path.")
list_workflows_dirs = []
workflow_dirs = []
for list_workflows in list_workflows_dirs:
if os.path.isdir(list_workflows.strip()):
workflow_dirs.append(list_workflows.strip())
workflow_dirs.insert(0, default_path)
list_panels = os.path.join(os.path.dirname(os.path.realpath(__file__)),"app","panels")
list_workflows_array = []
list_panels_array = []
#list folders
folders = os.listdir(list_panels)
@@ -86,6 +108,78 @@ for folder in folders:
@server.PromptServer.instance.routes.get("/sidebar/workflows" )
async def s_get(request):
workflows_dict = {}
existing_names = set()
for list_workflows in workflow_dirs:
for entry in os.scandir(list_workflows):
if entry.is_file() and entry.name.endswith('.json'):
base_name = entry.name[:-5] # Remove the '.json' extension
unique_name = base_name
if unique_name in workflows_dict:
folder_name = os.path.basename(list_workflows)
unique_name = f"{base_name} (from {folder_name})"
workflows_dict[unique_name] = entry.path
existing_names.add(unique_name)
sorted_workflows_dict = dict(sorted(workflows_dict.items()))
return web.json_response(sorted_workflows_dict, content_type='application/json')
@server.PromptServer.instance.routes.post("/sidebar/workflow")
async def s_get(request):
data = await request.json()
action = data["action"]
if action == "delete":
path = Path(data["path"])
os.remove(path)
result = {"result": "OK"}
return web.json_response(result, content_type='application/json')
elif action == "rename":
path = Path(data["path"])
newName = data["newName"]
if not path.exists():
raise FileNotFoundError(f"The system cannot find the path specified: {path}")
# Ottieni la directory e il nuovo percorso
new_path = path.with_name(newName).with_suffix('.json')
if new_path.exists():
result = {"result": "File already exists!"}
else:
#rename
os.rename(path, new_path)
result = {"result": "OK"}
return web.json_response(result, content_type='application/json')
else:
file = data["path"]
# if file exists
if os.path.isfile(file):
return web.FileResponse(file)
else:
return web.Response(status=403)
@server.PromptServer.instance.routes.get("/sidebar/panels" )
async def s_get(request):
result = {"panels": list_panels_array}
+31 -4
View File
@@ -84,7 +84,7 @@
}
.clearIcon, .searchCategoryIcon, .clearIconCustomCategory, .searchCategoryIconCustomCategory {
.clearIcon, .searchCategoryIcon, .clearIconCustomCategory, .searchCategoryIconCustomCategory, .clearIconCustomTemplate, .searchTemplateIconCustomTemplate, .clearIconCustomWorkflow, .searchWorkflowIconCustomWorkflow {
position: absolute;
padding: 5px;
right: 30px;
@@ -98,11 +98,11 @@
}
.clearIcon:hover, .clearIconCustomCategory:hover {
.clearIcon:hover, .clearIconCustomCategory:hover, .clearIconCustomTemplate:hover, .clearIconCustomWorkflow:hover {
opacity: 1.0;
}
.searchCategoryIcon, .searchCategoryIconCustomCategory {
.searchCategoryIcon, .searchCategoryIconCustomCategory, .searchTemplateIconCustomTemplate, .searchWorkflowIconCustomWorkflow {
right: 0px;
padding: 3px;
font-size: larger;
@@ -117,7 +117,7 @@
}
.searchCategoryIcon:hover, .searchCategoryIconCustomCategory:hover {
.searchCategoryIcon:hover, .searchCategoryIconCustomCategory:hover, .searchTemplateIconCustomTemplate:hover, .searchWorkflowIconCustomWorkflow:hover {
opacity: 0.5;
}
@@ -528,6 +528,7 @@
border-radius: 5px;
margin-left: 7px;
}
.panel_sidebar label{
@@ -648,6 +649,27 @@
}
#sb_settingsDiv .setting input[type="text"]{
background: var(--comfy-menu-bg);
color: var(--drag-text);
padding: 5px;
float: right;
width: 129px;
margin-right: 47px;
border-radius: 5px;
border: solid 1px var(--descrip-text);
}
#sb_settingsDiv .setting textarea{
background: var(--comfy-menu-bg);
color: var(--drag-text);
padding: 5px;
float: right;
width: calc(100% - 60px);
resize: vertical;
margin-right: 47px;
border-radius: 5px;
border: solid 1px var(--descrip-text);
}
#sb_settingsDiv .setting input[type="checkbox"]{
width: 18px;
height: 18px;
@@ -756,4 +778,9 @@
#switch_sidebar{
margin-top: 5px;
}
#sbModal{
display: none;
z-index: 9999;
}
+44 -32
View File
@@ -1,49 +1,61 @@
<div class="sidebar" id="sidebar" style="width:{{sidebar_width}};">
<div class="sidebar-header" id="sidebar-header" data-content="panel_head_home">
<input type="text" id="searchInput" placeholder="Search..." autocomplete="off">
<span class="clearIcon">&times;</span>
<span class="searchCategoryIcon">C</span>
<input type="text" id="searchInput" placeholder="Search..." autocomplete="off">
<span class="clearIcon">&times;</span>
<span class="searchCategoryIcon">C</span>
</div>
<div class="content_sidebar" id ="content_sidebar_home" data-expanded="false" data-content="panel_content_home" >
<div class="content_sidebar" id="content_sidebar_home" data-expanded="false" data-content="panel_content_home">
<div id="spacer"></div>
<label class="sb_label" id="sb_label_pinned">PINNED</label>
<ul id="sidebarBookmarks"></ul>
<label class="sb_label" id="sb_label_custom">CUSTOM NODES<button class="expand_node" >
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 52 52" enable-background="new 0 0 52 52" xml:space="preserve">
<path d="M48,9.5C48,8.7,47.3,8,46.5,8h-41C4.7,8,4,8.7,4,9.5v3C4,13.3,4.7,14,5.5,14h41c0.8,0,1.5-0.7,1.5-1.5V9.5z"
/>
<div id="spacer"></div>
<label class="sb_label" id="sb_label_pinned">PINNED</label>
<ul id="sidebarBookmarks"></ul>
<label class="sb_label" id="sb_label_custom">CUSTOM NODES<button class="expand_node">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52" enable-background="new 0 0 52 52"
xml:space="preserve">
<path
d="M48,9.5C48,8.7,47.3,8,46.5,8h-41C4.7,8,4,8.7,4,9.5v3C4,13.3,4.7,14,5.5,14h41c0.8,0,1.5-0.7,1.5-1.5V9.5z" />
<path d="M48,39.5c0-0.8-0.7-1.5-1.5-1.5h-41C4.7,38,4,38.7,4,39.5v3C4,43.3,4.7,44,5.5,44h41c0.8,0,1.5-0.7,1.5-1.5
V39.5z"/>
V39.5z" />
<path d="M30,29h4.5c0.8,0,1.5-0.7,1.5-1.5v-3c0-0.8-0.7-1.5-1.5-1.5H30c-0.6,0-1-0.4-1-1v-4.5c0-0.8-0.7-1.5-1.5-1.5
h-3c-0.8,0-1.5,0.7-1.5,1.5V22c0,0.6-0.4,1-1,1h-4.5c-0.8,0-1.5,0.7-1.5,1.5v3c0,0.8,0.7,1.5,1.5,1.5H22c0.6,0,1,0.4,1,1v4.5
c0,0.8,0.7,1.5,1.5,1.5h3c0.8,0,1.5-0.7,1.5-1.5V30C29,29.4,29.4,29,30,29z"/>
<rect class="expand_node" style="opacity:0" x="0" y="0" width="52" height="52" />
</svg>
</button></label>
<ul id="sidebarCustomNodes"></ul>
</div>
c0,0.8,0.7,1.5,1.5,1.5h3c0.8,0,1.5-0.7,1.5-1.5V30C29,29.4,29.4,29,30,29z" />
<rect class="expand_node" style="opacity:0" x="0" y="0" width="52" height="52" />
</svg>
</button></label>
<ul id="sidebarCustomNodes"></ul>
</div>
<div id="sidebar_views">
<div class="view_button" id ="switch_sidebar">☰</div>
<div class="view_button" id ="switch_home" data-content="home"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" style="enable-background:new 0 0 100 100; fill:var(--input-text);" xml:space="preserve"><g><path d="M29,82h13V63.1c0-1.7,1.3-3,3-3h10.3c1.7,0,3,1.3,3,3V82H71c1.7,0,3-1.3,3-3V43.2c0-0.8-0.3-1.6-0.9-2.1l-21-21 c-1.2-1.2-3.1-1.2-4.2,0l-21,21c-0.6,0.6-0.9,1.3-0.9,2.1V79C26,80.7,27.3,82,29,82z"/></g></svg></div>
<div class="view_button" id ="switch_settings">⚙</div>
<div id="sidebar_views">
<div class="view_button" id="switch_sidebar">☰</div>
<div class="view_button" id="switch_home" data-content="home"><svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100"
style="enable-background:new 0 0 100 100; fill:var(--input-text);" xml:space="preserve">
<g>
<path
d="M29,82h13V63.1c0-1.7,1.3-3,3-3h10.3c1.7,0,3,1.3,3,3V82H71c1.7,0,3-1.3,3-3V43.2c0-0.8-0.3-1.6-0.9-2.1l-21-21 c-1.2-1.2-3.1-1.2-4.2,0l-21,21c-0.6,0.6-0.9,1.3-0.9,2.1V79C26,80.7,27.3,82,29,82z" />
</g>
</svg></div>
<div class="view_button" id="switch_settings">⚙</div>
</div>
<div class="dragHandle" id="dragHandle"></div>
<div id="previewDiv"></div>
<div id="sb-modal-backdrop-settings" class="sb-modal-backdrop-settings sb_hidden"></div>
<div id="sb_settingsDiv" class="sb_hidden"><h1>SideBar Settings</h1>
<button id="sb_resetButton">Soft Reset</button> <button id="sb_factoryResetButton">Factory Reset</button></div>
<div id="sb_settingsDiv" class="sb_hidden">
<h1>SideBar Settings</h1>
<button id="sb_resetButton">Soft Reset</button>
<button id="sb_factoryResetButton">Factory Reset</button>
</div>
<a href="#" id="sb_scrollToTopButton">
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 330 330" xml:space="preserve">
<path id="XMLID_224_" d="M325.606,229.393l-150.004-150C172.79,76.58,168.974,75,164.996,75c-3.979,0-7.794,1.581-10.607,4.394
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 330 330" xml:space="preserve">
<path id="XMLID_224_"
d="M325.606,229.393l-150.004-150C172.79,76.58,168.974,75,164.996,75c-3.979,0-7.794,1.581-10.607,4.394
l-149.996,150c-5.858,5.858-5.858,15.355,0,21.213c5.857,5.857,15.355,5.858,21.213,0l139.39-139.393l139.397,139.393
C307.322,253.536,311.161,255,315,255c3.839,0,7.678-1.464,10.607-4.394C331.464,244.748,331.464,235.251,325.606,229.393z"/>
</svg>
</a>
C307.322,253.536,311.161,255,315,255c3.839,0,7.678-1.464,10.607-4.394C331.464,244.748,331.464,235.251,325.606,229.393z" />
</svg>
</a>
<div id="customMenu" class="custom-menu">
<ul> </ul></div>
<ul> </ul>
</div>
</div>
+128 -42
View File
@@ -4,30 +4,36 @@ let sbPosition = "left";
let defaultSearchToggle = "fuzzy";
let defaultSearchOrder = "comfyui";
async function getConfiguration(name) {
async function getConfiguration(name,local = false) {
//console.log("getStyles called " + name);
try {
const response = await fetch('/sidebar/settings', {
cache: "no-store",
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action : "read",
parameter: name
}),
});
const data = await response.json();
if (local) {
return localStorage.getItem(name);
} else {
const response = await fetch('/sidebar/settings', {
cache: "no-store",
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action : "read",
parameter: name
}),
});
const data = await response.json();
return data["value"];
} catch (error) {
console.log(error);
return null;
}
return data["value"];
}
} catch (error) {
console.log(error);
return null;
}
}
function addConfiguration(name,value) {
@@ -45,7 +51,11 @@ async function getConfiguration(name) {
})
}
function updateConfiguration(name,value) {
function updateConfiguration(name,value,local = false) {
if (local) {
return localStorage.setItem(name,value)
} else {
return fetch('/sidebar/settings', {
method: 'POST',
headers: {
@@ -57,6 +67,8 @@ async function getConfiguration(name) {
value: value
}),
})
}
}
function removeConfiguration(name) {
@@ -91,9 +103,9 @@ function factoryResetSettings() {
function settingsSetup(app,$el) {
async function settingsSetup(app,$el) {
app.ui.settings.addSetting({
id: "0_sidebar",
name: "[Sidebar] Better ComfyUI Settings Style",
@@ -118,6 +130,33 @@ function settingsSetup(app,$el) {
});
addSBSetting( "sb_settingsDiv",{
id: "workflow_preview",
name: "Enable Workflow Node List",
defaultValue: "false",
type: "boolean",
local: true,
info: "This is disabled by default since workflows preview can be slow.",
onChange(value) {
},
});
addSBSetting( "sb_settingsDiv",{
id: "workflow_replace",
name: "Workflow Replace Popup",
defaultValue: "true",
type: "boolean",
local: true,
onChange(value) {
},
});
addSBSetting( "sb_settingsDiv",{
id: "font",
name: "Font Size",
@@ -147,7 +186,7 @@ function settingsSetup(app,$el) {
addDynamicCSSRule('.sidebar li', 'padding', value + 'px');
},
});
addSBSetting( "sb_settingsDiv", {
id: "bartop",
name: "Space Top",
@@ -395,7 +434,7 @@ function settingsSetup(app,$el) {
},
});
addSBSetting( "sb_settingsDiv", {
await addSBSetting( "sb_settingsDiv", {
id: "order_type",
name: "Nodes Order Type",
defaultValue: "comfyui",
@@ -416,6 +455,23 @@ function settingsSetup(app,$el) {
});
addSBSetting( "sb_settingsDiv", {
id: "wf_path",
name: "Workflow Paths",
defaultValue: "",
type: "textarea",
info: "Paths must be row separated (only one per row).\nThe default ComfyUI workflows path has already been set.\nReboot ComfyUI to apply changes!",
onChange(value) {
NaN
},
});
@@ -446,18 +502,22 @@ function settingsSetup(app,$el) {
factoryReset();
})
}
async function addSBSetting(fieldLocation, setting) {
setting.id = "sb_" + setting.id; // Aggiunge il prefisso del campo
setting.id = "sb_" + setting.id;
// Crea l'elemento HTML per il campo di impostazione
const settingElement = document.createElement("div");
settingElement.classList.add("setting");
// Aggiungi il nome del campo
const nameLabel = document.createElement("label");
nameLabel.textContent = setting.name;
settingElement.appendChild(nameLabel);
@@ -471,20 +531,27 @@ async function addSBSetting(fieldLocation, setting) {
nameLabel.appendChild(infoIcon);
}
// Aggiungi il tipo appropriato di input per il campo
let inputElement;
if (setting.type === "boolean") {
inputElement = document.createElement("input");
inputElement.type = "checkbox";
inputElement.checked = await getConfiguration(setting.id) || setting.defaultValue;
let configValue = await getConfiguration(setting.id, setting.local) || setting.defaultValue;
if (configValue == "true") {
inputElement.checked = true;
} else if (configValue == "false") {
inputElement.checked = false;
}
setting.onChange(inputElement.checked);
inputElement.addEventListener("change", function() {
setting.onChange(inputElement.checked);
updateConfiguration(setting.id,inputElement.checked)
updateConfiguration(setting.id,inputElement.checked, setting.local)
});
} else if (setting.type === "slider") {
// Crea l'elemento input per lo slider
const sliderContainer = document.createElement("div");
sliderContainer.classList.add("slider-container");
@@ -497,19 +564,20 @@ async function addSBSetting(fieldLocation, setting) {
inputElement.value = await getConfiguration(setting.id) || setting.defaultValue || setting.defaultValue;
setting.onChange(inputElement.value);
inputElement.addEventListener("input", function() {
// Aggiorna il valore del campo testuale quando lo slider viene spostato
valueText.textContent = inputElement.value;
setting.onChange(inputElement.value);
updateConfiguration(setting.id,inputElement.value)
});
// Crea l'elemento per il valore testuale dello slider
const valueText = document.createElement("span");
valueText.textContent = await getConfiguration(setting.id) || setting.defaultValue;
valueText.id = setting.id+"_span";
sliderContainer.appendChild(inputElement);
sliderContainer.appendChild(valueText);
settingElement.appendChild(sliderContainer);
} else if (setting.type === "text") {
inputElement = document.createElement("input");
inputElement.type = "text";
@@ -519,7 +587,18 @@ async function addSBSetting(fieldLocation, setting) {
setting.onChange(inputElement.value);
updateConfiguration(setting.id,inputElement.value)
});
} else if (setting.type === "dropdown") {
}
else if (setting.type === "textarea") {
inputElement = document.createElement("textarea");
inputElement.rows = 3;
inputElement.value = await getConfiguration(setting.id) || setting.defaultValue;
setting.onChange(inputElement.value);
inputElement.addEventListener("input", function() {
setting.onChange(inputElement.value);
updateConfiguration(setting.id,inputElement.value)
});
}
else if (setting.type === "dropdown") {
inputElement = document.createElement("select");
const defaultValue = await getConfiguration(setting.id) || setting.defaultValue;
setting.options.forEach(async (option) => {
@@ -541,7 +620,7 @@ async function addSBSetting(fieldLocation, setting) {
settingElement.appendChild(inputElement);
// Aggiungi il campo di impostazione al luogo specificato
const targetElement = document.getElementById(fieldLocation);
if (targetElement) {
targetElement.appendChild(settingElement);
@@ -552,12 +631,9 @@ async function addSBSetting(fieldLocation, setting) {
}
function resetSettings() {
let ok = confirm("This option will reset the following settings: \n-Search Type\n-Position\n-Nodes Order Type\n-Font Size\n-Node Size\n-Space Top\n-Space Bottom\n-Node Radius Border\n-Blur Intesity\n-Opacity\nAre you sure you want to reset these settings?");
let ok = confirm("This option will reset the following settings: \n-Search Type\n-Position\n-Nodes Order Type\n-Font Size\n-Node Size\n-Space Top\n-Space Bottom\n-Node Radius Border\n-Blur Intesity\n-Opacity\nsidebarWidth\nsb_pinned_collapsed\nsb_minimized\nsb_current_tab\nsb_workflow_preview\nsb_workflow_replace\nAre you sure you want to reset these settings?");
if (!ok) {
return;
}
@@ -566,6 +642,16 @@ function resetSettings() {
removeConfiguration(setting);
});
const localSettings = ["sidebarWidth","sb_pinned_collapsed","sb_minimized","sb_current_tab","sb_workflow_preview","sb_workflow_replace"];
localSettings.forEach(setting => {
try{
removeVar(setting);
}catch(err){
console.log(err)
}
})
window.location.reload();
}
@@ -577,7 +663,7 @@ function factoryReset() {
}
factoryResetSettings();
const localSettings = ["sidebarWidth","sb_pinned_collapsed","sb_minimized","sb_current_tab","sb_current_tab"];
const localSettings = ["sidebarWidth","sb_pinned_collapsed","sb_minimized","sb_current_tab","sb_workflow_preview","sb_workflow_replace"];
localSettings.forEach(setting => {
try{
+49 -6
View File
@@ -93,7 +93,6 @@
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);
@@ -212,16 +211,37 @@ function createContextMenu(event, subMenus,settingsData) {
showSubMenu(subMenus[index], li);
});
try {
if (settingsData.menuctx_opt_callback[index].indexOf(".") !== -1) {
// Handle sub menu option
var mainfunc = settingsData.menuctx_opt_callback[index].split(".")[0];
var subfunc = settingsData.menuctx_opt_callback[index].split(".")[1];
if (typeof (window[mainfunc][subfunc]) === 'function') {
li.addEventListener('click', () => {
// Handle sub menu click
window[mainfunc][subfunc](event);
});
}
}
else{
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);
}
@@ -368,9 +388,16 @@ window.addEventListener("click", function(event) {
// Function to check for special characters
function containsSpecialCharacters(name) {
function containsSpecialCharacters(name, level=0) {
// Define a regular expression pattern to match special characters
var specialCharacters = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/;
if (level == 0) {
var specialCharacters = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/;
} else if (level == 1) {
var specialCharacters = /[\*':"\\|,<>\/?]/g;
} else{
var specialCharacters = /['"]/gi;
}
// Return true if the name contains any special characters, false otherwise
return specialCharacters.test(name);
}
@@ -442,7 +469,11 @@ function openModal(addon=null) {
closeModalButton.addEventListener('click', closeModal);
try{
// Focus the input field
document.getElementById('sb-categoryName').focus();
inputElement = document.getElementById('sb-categoryName');
inputElement.focus();
const length = inputElement.value.length;
inputElement.setSelectionRange(length, length);
}catch(err){
}
@@ -454,12 +485,17 @@ try{
function closeModal() {
const modalBackdrop = document.querySelector('.sb-modal-backdrop');
const modal = document.querySelector('.sb-category-dialog');
try{
// Remove the modal from the DOM
modalBackdrop.parentNode.removeChild(modalBackdrop);
modal.parentNode.removeChild(modal);
}
catch(err){
console.log(err);
}
}
function rgbToHex(rgb) {
// Verifica se il colore è nel formato "rgb()"
if (rgb.indexOf('rgb') !== -1) {
@@ -522,3 +558,10 @@ function isBottomEdgeVisible(el) {
return false;
}
function convertCanvasToOffset(canvas, pos, out) {
out = out || [0, 0];
out[0] = pos[0] / canvas.scale - canvas.offset[0];
out[1] = pos[1] / canvas.scale - canvas.offset[1];
return out;
};
+10 -16
View File
@@ -3,7 +3,9 @@ import { api } from "../../../../scripts/api.js"
import { app } from "../../../../scripts/app.js";
import { $el } from "../../../../scripts/ui.js";
import { fuzzyMatch } from "./fts_fuzzy_match.js";
import { GroupNodeConfig, GroupNodeHandler } from "../../../../extensions/core/groupNode.js";
const gnc = GroupNodeConfig;
//import {settingsSetup} from './functions/settings.jsb';
console.time('execution time');
const cnPath = "../extensions/ComfyUI-N-Sidebar/"
@@ -93,7 +95,7 @@ function postPinned() {
previewDivTop = (itemPosition.top + this.offsetHeight) - previewDiv.offsetHeight;
}
let sidebar_width = parseInt(getVar("sidebarWidth")) || 300;
let sidebar_width = parseInt(getVar("sidebarWidth")) || 500;
previewDiv.style.top = `${previewDivTop}px`;
const previewDivLeft = sidebar_width - sidebad_view_width;
@@ -112,14 +114,8 @@ function postPinned() {
previewDiv.style.display = 'none';
})
reloadCtxMenu()
});
pinnedElement.addEventListener("dragover", function (event) {
@@ -228,8 +224,11 @@ async function loadPinnedItemsAndAddToBookmarks() {
if (item) {
try{
let addedItem = sidebarBookmarks.appendChild(item.cloneNode(true));
addedItem.lastChild.lastChild.classList.add("pinned");
}catch(err){ }
}
});
@@ -579,7 +578,7 @@ async function createCategoryList() {
previewDivTop = (itemPosition.top + this.offsetHeight) - previewDiv.offsetHeight;
}
let sidebar_width = parseInt(getVar("sidebarWidth")) || 300;
let sidebar_width = parseInt(getVar("sidebarWidth")) || 500;
previewDiv.style.top = `${previewDivTop}px`;
@@ -755,13 +754,7 @@ async function addSidebar() {
});
function convertCanvasToOffset(canvas, pos, out) {
out = out || [0, 0];
out[0] = pos[0] / canvas.scale - canvas.offset[0];
out[1] = pos[1] / canvas.scale - canvas.offset[1];
return out;
};
function handleDrop(event) {
event.preventDefault();
@@ -973,7 +966,7 @@ function processViews(viewsData, settingsData) {
div_panel_title.classList = "panel_sidebar_title sb_label";
div_panel_title.id = "panel_title_" + panel;
div_panel_title.innerHTML = panel.replace("_", " ");
div_panel_title.innerHTML = view.id.replace("_", " ");
@@ -987,6 +980,7 @@ function processViews(viewsData, settingsData) {
div_view.insertAdjacentElement('beforeend', div_panel);
jsloader(`${cnPath}panels/${panel}/${panel}.jsb`);
//window.custom_templates();
console.log("LOADING " + cnPath + `panels/${panel}/${panel}.js`)
}
} catch (error) {
@@ -512,6 +512,13 @@ async function renameCategory(oldCategoryName, newCategoryName) {
}
let categoryNodeMap = await getNodeMap();
if (categoryNodeMap[newCategoryName]) {
alert("Category already exists!");
return;
}
let categoryNodeStatus = getNodesStatus();
if (categoryNodeMap[oldCategoryName]) {
// Categoty Current Order
@@ -0,0 +1,12 @@
<div id="custom_templates_header">
<div class="h-header" id="h_search" data-content="panel_head_home">
<input type="text" id="searchTemplateInput" oninput="custom_templates.searchT(this)" placeholder="Search..."
autocomplete="off" class="">
<span class="clearIconCustomTemplate">×</span>
<span class="searchTemplateIconCustomTemplate">C</span>
<button id="openModalButton" onclick="custom_templates.openModal()">+</button>
<button id="refreshlButton" onclick="custom_templates.renderList('custom_templates_main')">⭮</button>
</div>
</div>
<div id="custom_templates_main">
</div>
File diff suppressed because it is too large Load Diff
+141
View File
@@ -0,0 +1,141 @@
#custom_templates_main,#custom_templates_header {
justify-content: center;
align-items: center;
height: 100%;
margin: 0;
}
#custom_templates_main{
padding-top: 44px;
}
#custom_templates_header {
margin: 10px 20px;
font-size: 16px;
}
#custom_templates_header #h_search {
position: absolute;
width: calc(100% - 115px);
margin-top: 0px;
z-index: 400;
}
#custom_templates_header button {
/* padding: 10px 20px; */
height: 35px;
width: 35px;
font-size: 16px;
background: var(--comfy-input-bg);
border: 2px solid var(--border-color);
color: var(--input-text);
border-radius: 5px;
float: right;
position: absolute;
margin: 2px 2px 2px 8px;
cursor: pointer;
}
#custom_templates_header button:hover {
border: 2px solid var(--descrip-text);
}
.sb-template-dialog {
outline: 0;
border: 0;
border-radius: 6px;
background: var(--bg-color);
color: #fff;
box-shadow: inset 1px 1px 0px rgba(255, 255, 255, 0.05), inset -1px -1px 0px rgba(0, 0, 0, 0.5), 2px 2px 20px rgb(0, 0, 0);
max-width: 800px;
min-width: 30rem;
box-sizing: border-box;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
font-size: 1rem;
padding: 20px;
position: fixed;
top: 50%;
left: 50%;
z-index: 1001;
}
#sb-template-form {
margin-top: 10px;
text-align: center;
}
.sb-input {
width: 100%;
padding: 8px;
margin-bottom: 10px;
box-sizing: border-box;
border: 2px solid var(--border-color);
border-radius: 4px;
margin-top: 16px;
background: var(--bg-color);
color: var(--input-text);
}
.sb-input:focus {
outline: none;
border: 2px solid var(--descrip-text);
}
.sb-button {
background-color: var(--comfy-menu-bg);
color: var(--input-text);
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
text-transform: uppercase;
font-weight: bold;
}
.sb-button:hover {
filter: brightness(120%);
}
#closeModalButton {
background-color: var(--comfy-menu-bg);
position: absolute;
top: 0;
right: 0;
margin: 0;
}
#closeModalButton:hover {
filter: brightness(120%);
}
#searchTemplateInput {
box-sizing: border-box;
width: 100%;
border-radius: 5px;
padding: 10px;
border: none;
user-select: none;
background: var(--comfy-input-bg);
color: var(--input-text);
line-height: 1.4;
border: 1px solid var(--border-color);
}
#previewDiv .sb_table {
text-align: center;
}
.sidebar #previewDiv .sidebarItem, #custom_templates_main .sidebarItem {
max-width: 100% !important;
}
#refreshlButton{
margin-left: 48px !important;
}
@@ -0,0 +1,12 @@
<div id="custom_workflows_header">
<div class="h-header" id="h_search" data-content="panel_head_home">
<input type="text" id="searchWorkflowInput" oninput="custom_workflows.searchT(this)" placeholder="Search..."
autocomplete="off" class="">
<span class="clearIconCustomWorkflow">×</span>
<span class="searchWorkflowIconCustomWorkflow">C</span>
<button id="openModalButton" onclick="custom_workflows.openModal()">+</button>
<button id="refreshlButton" onclick="custom_workflows.renderList('custom_workflows_main')">⭮</button>
</div>
</div>
<div id="custom_workflows_main">
</div>
File diff suppressed because it is too large Load Diff
+139
View File
@@ -0,0 +1,139 @@
#custom_workflows_main,#custom_workflows_header {
justify-content: center;
align-items: center;
height: 100%;
margin: 0;
}
#custom_workflows_main{
padding-top: 44px;
}
#custom_workflows_header {
margin: 10px 20px;
font-size: 16px;
}
#custom_workflows_header #h_search {
position: absolute;
width: calc(100% - 115px);
margin-top: 0px;
z-index: 400;
}
#custom_workflows_header button {
/* padding: 10px 20px; */
height: 35px;
width: 35px;
font-size: 16px;
background: var(--comfy-input-bg);
border: 2px solid var(--border-color);
color: var(--input-text);
border-radius: 5px;
float: right;
position: absolute;
margin: 2px 2px 2px 8px;
cursor: pointer;
}
#custom_workflows_header button:hover {
border: 2px solid var(--descrip-text);
}
.sb-workflow-dialog {
outline: 0;
border: 0;
border-radius: 6px;
background: var(--bg-color);
color: #fff;
box-shadow: inset 1px 1px 0px rgba(255, 255, 255, 0.05), inset -1px -1px 0px rgba(0, 0, 0, 0.5), 2px 2px 20px rgb(0, 0, 0);
max-width: 800px;
min-width: 30rem;
box-sizing: border-box;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
font-size: 1rem;
padding: 20px;
position: fixed;
top: 50%;
left: 50%;
z-index: 1001;
}
#sb-workflow-form {
margin-top: 10px;
text-align: center;
}
.sb-input {
width: 100%;
padding: 8px;
margin-bottom: 10px;
box-sizing: border-box;
border: 2px solid var(--border-color);
border-radius: 4px;
margin-top: 16px;
background: var(--bg-color);
color: var(--input-text);
}
.sb-input:focus {
outline: none;
border: 2px solid var(--descrip-text);
}
.sb-button {
background-color: var(--comfy-menu-bg);
color: var(--input-text);
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
text-transform: uppercase;
font-weight: bold;
}
.sb-button:hover {
filter: brightness(120%);
}
#closeModalButton {
background-color: var(--comfy-menu-bg);
position: absolute;
top: 0;
right: 0;
margin: 0;
}
#closeModalButton:hover {
filter: brightness(120%);
}
#searchWorkflowInput {
box-sizing: border-box;
width: 100%;
border-radius: 5px;
padding: 10px;
border: none;
user-select: none;
background: var(--comfy-input-bg);
color: var(--input-text);
line-height: 1.4;
border: 1px solid var(--border-color);
}
#previewDiv .sb_table {
text-align: center;
}
.sidebar #previewDiv .sidebarItem, #custom_workflows_main .sidebarItem {
max-width: 100% !important;
}
#refreshlButton{
margin-left: 48px !important;
}
+21 -1
View File
@@ -1,11 +1,31 @@
[
{
"id": "custom_view",
"id": "custom_categories",
"icon": "⧉",
"description": "Custom Node Categories",
"panels": [
"custom_categories"
]
},
{
"id": "templates",
"icon": "Τ",
"description": "Custom Node templates",
"panels": [
"custom_templates"
]
},
{
"id": "workflows",
"icon": "W",
"description": "Custom Node workflows",
"panels": [
"custom_workflows"
]
}
]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui-n-sidebar"
description = "A simple sidebar for ComfyUI."
version = "1.0.1"
version = "1.1.0"
license = "LICENSE"
[project.urls]