Rewritten a lot of the code to improve performance and readability, and slightly adjusted the UI to ensure compatibility with future updates.
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!
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
|
# Config file
|
||||||
|
app/settings.json
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|||||||
@@ -11,6 +11,18 @@ For what i know nobody did it, so i did it.
|
|||||||
Maybe you don't need it. I need it >.<
|
Maybe you don't need it. I need it >.<
|
||||||
|
|
||||||
# Updates
|
# Updates
|
||||||
|
- 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.
|
||||||
|
- 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!
|
||||||
|
|
||||||
- 31-03-2024
|
- 31-03-2024
|
||||||
- Added fuzzy search (work in reverse too!) (enabled by default) - You can disable it in the settings
|
- Added fuzzy search (work in reverse too!) (enabled by default) - You can disable it in the settings
|
||||||
@@ -62,6 +74,11 @@ to your ComfyUI `custom_nodes` directory
|
|||||||

|

|
||||||
This feature is enabled by default, you can disable it in the settings. I've used fts_fuzzy_match.js by [Forrest Smith](https://github.com/forrestthewoods/lib_fts)
|
This feature is enabled by default, you can disable it in the settings. I've used fts_fuzzy_match.js by [Forrest Smith](https://github.com/forrestthewoods/lib_fts)
|
||||||
|
|
||||||
|
### 🎨 Custom Categories 📂
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### ➕Expand/Collapse Categories/Sidebar➖
|
### ➕Expand/Collapse Categories/Sidebar➖
|
||||||

|

|
||||||
|
|
||||||
@@ -76,7 +93,7 @@ This feature is enabled by default, you can disable it in the settings. I've use
|
|||||||
### 🎨 ComfyUI Themes Support 🎨
|
### 🎨 ComfyUI Themes Support 🎨
|
||||||

|

|
||||||
|
|
||||||
### 🎨 Custom Values From ComfyUI Settings 🎨
|
### 🎨 New Settings Panel 🎨
|
||||||

|

|
||||||
|
|
||||||
### Additional Features
|
### Additional Features
|
||||||
@@ -87,7 +104,9 @@ This feature is enabled by default, you can disable it in the settings. I've use
|
|||||||
- [x] Reordering pinned nodes
|
- [x] Reordering pinned nodes
|
||||||
- [x] Node preview (i don't think it will be an image)
|
- [x] Node preview (i don't think it will be an image)
|
||||||
- [x] Color integration with Jovimetrix
|
- [x] Color integration with Jovimetrix
|
||||||
- [ ] Better search
|
- [x] Better search
|
||||||
|
- [x] Custom Categories!!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
@@ -99,6 +118,7 @@ Feel free to contribute to this project by reporting issues or suggesting improv
|
|||||||
If you'd like to support the project, consider making a donation ❤️
|
If you'd like to support the project, consider making a donation ❤️
|
||||||
<br>
|
<br>
|
||||||
[](https://ko-fi.com/C0C0AJECJ)
|
[](https://ko-fi.com/C0C0AJECJ)
|
||||||
|
<a href="https://patreon.com/DreamingAIChannel"><img src="./images/patreon_badge.png" alt="Support me on Patreon" height="27" /></a>
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
+121
-1
@@ -1,6 +1,126 @@
|
|||||||
|
import server
|
||||||
|
import folder_paths
|
||||||
|
from aiohttp import web
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class JSONConfigManager:
|
||||||
|
def __init__(self, file_name):
|
||||||
|
self.file_name = file_name
|
||||||
|
self.initial_config = """
|
||||||
|
{"menuctx_category": "Context Menu",
|
||||||
|
"menuctx_options": [],
|
||||||
|
"menuctx_subOptions": [],
|
||||||
|
"menuctx_opt_callback": [],
|
||||||
|
"menuctx_sub_opt_callback": []
|
||||||
|
}"""
|
||||||
|
if os.path.isfile(file_name):
|
||||||
|
self.data = self.read_json_file()
|
||||||
|
else:
|
||||||
|
|
||||||
|
self.data = json.loads(self.initial_config)
|
||||||
|
self.write_json_file()
|
||||||
|
|
||||||
|
def read_json_file(self):
|
||||||
|
try:
|
||||||
|
with open(self.file_name, 'r') as file:
|
||||||
|
return json.load(file)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def write_json_file(self):
|
||||||
|
with open(self.file_name, 'w') as file:
|
||||||
|
json.dump(self.data, file, indent=4)
|
||||||
|
|
||||||
|
def read_item(self, parameter_name):
|
||||||
|
return self.data.get(parameter_name)
|
||||||
|
|
||||||
|
def update_item(self, parameter_name, new_value):
|
||||||
|
self.data[parameter_name] = new_value
|
||||||
|
self.write_json_file()
|
||||||
|
|
||||||
|
def delete_item(self, parameter_name):
|
||||||
|
if parameter_name in self.data:
|
||||||
|
del self.data[parameter_name]
|
||||||
|
self.write_json_file()
|
||||||
|
|
||||||
|
def add_item(self, parameter_name, value):
|
||||||
|
self.data[parameter_name] = value
|
||||||
|
self.write_json_file()
|
||||||
|
|
||||||
|
file_path = os.path.join(folder_paths.get_folder_paths("custom_nodes")[0],"ComfyUI-N-Sidebar","app","settings.json")
|
||||||
|
|
||||||
|
|
||||||
|
config_manager = JSONConfigManager(file_path)
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {}
|
NODE_CLASS_MAPPINGS = {}
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||||
|
print(folder_paths.get_folder_paths("custom_nodes"))
|
||||||
|
list_panels = os.path.join(folder_paths.get_folder_paths("custom_nodes")[0],"ComfyUI-N-Sidebar","app","panels")
|
||||||
|
|
||||||
|
list_panels_array = []
|
||||||
|
#list folders
|
||||||
|
folders = os.listdir(list_panels)
|
||||||
|
for folder in folders:
|
||||||
|
if os.path.isdir(os.path.join(list_panels, folder)):
|
||||||
|
list_panels_array.append(folder)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
WEB_DIRECTORY = "./js"
|
@server.PromptServer.instance.routes.get("/sidebar/panels" )
|
||||||
|
async def s_get(request):
|
||||||
|
result = {"panels": list_panels_array}
|
||||||
|
return web.json_response(result, content_type='application/json')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@server.PromptServer.instance.routes.post("/sidebar/settings" )
|
||||||
|
async def s_get(request):
|
||||||
|
data = await request.json()
|
||||||
|
action = data["action"]
|
||||||
|
|
||||||
|
if action == "add":
|
||||||
|
parameter = data["parameter"]
|
||||||
|
value = data["value"]
|
||||||
|
config_manager.add_item(parameter, value)
|
||||||
|
result = {"result": "OK"}
|
||||||
|
return web.json_response(result, content_type='application/json')
|
||||||
|
|
||||||
|
elif action == "update":
|
||||||
|
parameter = data["parameter"]
|
||||||
|
value = data["value"]
|
||||||
|
config_manager.update_item(parameter, value)
|
||||||
|
result = {"result": "OK"}
|
||||||
|
return web.json_response(result, content_type='application/json')
|
||||||
|
|
||||||
|
|
||||||
|
elif action == "delete":
|
||||||
|
parameter = data["parameter"]
|
||||||
|
config_manager.delete_item(parameter)
|
||||||
|
result = {"result": "OK"}
|
||||||
|
return web.json_response(result, content_type='application/json')
|
||||||
|
|
||||||
|
elif action == "read":
|
||||||
|
parameter = data["parameter"]
|
||||||
|
read_language = config_manager.read_item(parameter)
|
||||||
|
result = {"value": read_language}
|
||||||
|
return web.json_response(result, content_type='application/json')
|
||||||
|
elif action == "factory_reset":
|
||||||
|
config_manager.data = json.loads(config_manager.initial_config)
|
||||||
|
config_manager.write_json_file()
|
||||||
|
result = {"result": "OK"}
|
||||||
|
return web.json_response(result, content_type='application/json')
|
||||||
|
else:
|
||||||
|
result = {"result": "ERROR"}
|
||||||
|
return web.json_response(result, content_type='application/json')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
WEB_DIRECTORY = "./app"
|
||||||
@@ -0,0 +1,756 @@
|
|||||||
|
.litegraph .dialog {
|
||||||
|
z-index: 100 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -250px;
|
||||||
|
left: -250px;
|
||||||
|
width: fit-content;
|
||||||
|
height: calc(100% - 19px);
|
||||||
|
color: var(--drag-text);
|
||||||
|
transition: left 0.3s ease;
|
||||||
|
z-index: 2;
|
||||||
|
padding-top: 19px;
|
||||||
|
left: 0;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar ul {
|
||||||
|
list-style-type: none;
|
||||||
|
/* border-bottom: 6px solid rgb(from var(--comfy-input-bg) r g b / 60%);*/
|
||||||
|
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
margin-top: 0;
|
||||||
|
padding-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li {
|
||||||
|
padding: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
color: var(--input-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content_sidebar {
|
||||||
|
|
||||||
|
background: rgb(from var(--comfy-menu-bg) r g b / 100%);
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
height: 100%;
|
||||||
|
float: left;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
width: calc(100% - 45px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragHandle {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 5px;
|
||||||
|
cursor: ew-resize;
|
||||||
|
background: rgb(62, 62, 62);
|
||||||
|
background: linear-gradient(90deg, rgb(62 62 62 / 46%) 0%, rgb(39 39 39 / 47%) 50%, rgb(28 28 28 / 31%) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchInput {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header {
|
||||||
|
position: absolute;
|
||||||
|
width: calc(100% - 75px);
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-left: 10px;
|
||||||
|
|
||||||
|
z-index: 400;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearIcon, .searchCategoryIcon, .clearIconCustomCategory, .searchCategoryIconCustomCategory {
|
||||||
|
position: absolute;
|
||||||
|
padding: 5px;
|
||||||
|
right: 30px;
|
||||||
|
color: var(--input-text);
|
||||||
|
font-size: x-large;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.5;
|
||||||
|
user-select: none;
|
||||||
|
line-height: 1.4;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.clearIcon:hover, .clearIconCustomCategory:hover {
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchCategoryIcon, .searchCategoryIconCustomCategory {
|
||||||
|
right: 0px;
|
||||||
|
padding: 3px;
|
||||||
|
font-size: larger;
|
||||||
|
background: var(--border-color);
|
||||||
|
color: var(--drag-text);
|
||||||
|
margin: 4px;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 18px;
|
||||||
|
text-align: center;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchCategoryIcon:hover, .searchCategoryIconCustomCategory:hover {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarCategory, #sidebarBookmarks {
|
||||||
|
list-style-type: none;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
text-transform: capitalize;
|
||||||
|
margin: 2px;
|
||||||
|
background-color: var(--comfy-input-bg);
|
||||||
|
opacity: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding-top: 11px;
|
||||||
|
font-size: 15px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebarCustomNodes {
|
||||||
|
list-style-type: none;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
text-transform: capitalize;
|
||||||
|
margin: 0px;
|
||||||
|
background-color: transparent;
|
||||||
|
opacity: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 15px;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0px;
|
||||||
|
padding-top: 0px;
|
||||||
|
padding-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarItem:hover {
|
||||||
|
background: color-mix(in srgb, currentColor 15%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarItem {
|
||||||
|
list-style-type: none;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
text-transform: capitalize;
|
||||||
|
margin: 2px;
|
||||||
|
background: color-mix(in srgb, var(--border-color) 35%, transparent);
|
||||||
|
opacity: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: calc(100% - 39px);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.content_sidebar::-webkit-scrollbar {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
height: 1rem;
|
||||||
|
width: .5rem;
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content_sidebar::-webkit-scrollbar:horizontal {
|
||||||
|
height: .5rem;
|
||||||
|
width: 1rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.content_sidebar::-webkit-scrollbar-track {
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 9999px
|
||||||
|
}
|
||||||
|
|
||||||
|
.content_sidebar::-webkit-scrollbar-thumb {
|
||||||
|
--tw-border-opacity: 1;
|
||||||
|
background-color: hsla(0, 0%, 50%, .8);
|
||||||
|
border-color: rgba(255, 255, 255, 0, 0, 0);
|
||||||
|
border-radius: 9999px;
|
||||||
|
border-width: 1px
|
||||||
|
}
|
||||||
|
|
||||||
|
.content_sidebar::-webkit-scrollbar-thumb:hover {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
|
||||||
|
background-color: rgba(150, 150, 150, var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
#spacer {
|
||||||
|
height: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.view_button {
|
||||||
|
position: relative;
|
||||||
|
float: unset;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 20px;
|
||||||
|
background-color: var(--comfy-input-bg);
|
||||||
|
opacity: 0.7;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
margin-top:2px;
|
||||||
|
}
|
||||||
|
.view_button:hover {
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar_views {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
padding: 0px 2px 2px 2px;
|
||||||
|
font-size: 20px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
height: 100%;
|
||||||
|
width: 30px;
|
||||||
|
border: 0;
|
||||||
|
border-left: 2px rgb(from var(--comfy-menu-bg) r g b / 100%);
|
||||||
|
border-style: solid;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
backdrop-filter: blur(16px) !important;
|
||||||
|
background: rgb(from var(--comfy-menu-bg) r g b / 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#searchInput.closed {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content_sidebar.closed {
|
||||||
|
width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchCategoryIcon.closed {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearIcon.closed {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarCategory .pinButton {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#sidebarBookmarks .pinButton {
|
||||||
|
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewButton {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pinned {
|
||||||
|
fill: var(--descrip-text) !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg_class {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
fill: var(--border-color);
|
||||||
|
cursor: pointer;
|
||||||
|
/* hacky fix */
|
||||||
|
margin-top: -5px;
|
||||||
|
margin-left: -25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pin_normal {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pin_normal:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg_class:hover {
|
||||||
|
fill: var(--border-color);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#sidebarBookmarks .sidebarItem {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 29px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.sb_label {
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
position: relative;
|
||||||
|
margin: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
background: var(--comfy-input-bg);
|
||||||
|
border-radius: 3px;
|
||||||
|
padding-left: 6px;
|
||||||
|
padding-right: 6px;
|
||||||
|
display: block;
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
text-align: center;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand_node, .pin_node {
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand_node svg, .pin_node svg {
|
||||||
|
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background: transparent;
|
||||||
|
fill: var(--input-text);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sb_dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node_header {
|
||||||
|
line-height: 1;
|
||||||
|
padding: 8px 13px 7px;
|
||||||
|
background: var(--comfy-input-bg);
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-size: 15px;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headdot {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
float: inline-start;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sb_hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.IMAGE {
|
||||||
|
background-color: #64b5f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VAE {
|
||||||
|
background-color: #ff6e6e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.LATENT {
|
||||||
|
background-color: #ff9cf9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MASK {
|
||||||
|
background-color: #81c784;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CONDITIONING {
|
||||||
|
background-color: #ffa931;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CLIP {
|
||||||
|
background-color: #ffd500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MODEL {
|
||||||
|
background-color: #b39ddb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CONTROL_NET {
|
||||||
|
background-color: #a5d6a7;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#previewDiv {
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--comfy-menu-bg);
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
font-size: small;
|
||||||
|
color: var(--descrip-text);
|
||||||
|
border: 1px solid var(--descrip-text);
|
||||||
|
display: none;
|
||||||
|
min-width: 300px;
|
||||||
|
width: min-content;
|
||||||
|
height: fit-content;
|
||||||
|
z-index: 9999;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 12px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#previewDiv .sb_description {
|
||||||
|
margin: 10px;
|
||||||
|
padding: 6px;
|
||||||
|
background: var(--border-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sb_table {
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
grid-column-gap: 10px;
|
||||||
|
/* Spazio tra le colonne */
|
||||||
|
width: 100%;
|
||||||
|
/* Imposta la larghezza della tabella al 100% del contenitore */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sb_row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 10px 1fr 1fr 1fr 10px;
|
||||||
|
grid-column-gap: 10px;
|
||||||
|
align-items: baseline;
|
||||||
|
padding-left: 9px;
|
||||||
|
padding-right: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sb_row_string {
|
||||||
|
grid-template-columns: 10px 1fr 1fr 10fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sb_col {
|
||||||
|
border: 0px solid #000;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-content: flex-start;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sb_inherit {
|
||||||
|
display: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.long_field {
|
||||||
|
background: var(--bg-color);
|
||||||
|
border: 2px solid var(--border-color);
|
||||||
|
margin: 5px 5px 0 5px;
|
||||||
|
border-radius: 10px;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sb_arrow {
|
||||||
|
color: var(--fg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sb_preview_badge {
|
||||||
|
text-align: center;
|
||||||
|
background: var(--comfy-input-bg);
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--error-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sb_scrollToTopButton {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
right: 56px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
border-radius: 20%;
|
||||||
|
background: var(--comfy-menu-bg);
|
||||||
|
padding: 4px;
|
||||||
|
border: 1px solid #4c4c4c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sb_scrollToTopButton svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
fill: var(--descrip-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.panel_sidebar{
|
||||||
|
min-height: 200px;
|
||||||
|
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel_sidebar label{
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.custom-menu {
|
||||||
|
box-shadow: inset 0px 0px 0px rgb(44 44 44 / 5%), inset 1px -1px 0px 0px rgb(22 22 22 / 50%), 0px 0px 9px 0px rgb(0, 0, 0);
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
border: 0px solid #313131;
|
||||||
|
color: var(--input-text);
|
||||||
|
padding: 5px 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
z-index: 9999;
|
||||||
|
/* backdrop-filter: blur(5px);
|
||||||
|
background: rgb(from #353535 r g b / 50%);*/
|
||||||
|
}
|
||||||
|
.custom-menu ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.custom-menu li {
|
||||||
|
padding: 8px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
}
|
||||||
|
#menu-options li {
|
||||||
|
|
||||||
|
min-width: 134px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.custom-menu .sub-menu li:first-child {
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.custom-menu .sub-menu li:last-child {
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
}
|
||||||
|
.custom-menu li:hover {
|
||||||
|
background-color: var(--border-color);
|
||||||
|
}
|
||||||
|
.sub-menu {
|
||||||
|
|
||||||
|
box-shadow: inset 0px 0px 0px rgb(44 44 44 / 5%), inset 1px -1px 0px 0px rgb(22 22 22 / 50%), 0px 0px 9px 0px rgb(0, 0, 0);
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 100%;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
border: 0px solid #313131;
|
||||||
|
border-radius: 5px;
|
||||||
|
border-top-left-radius: 0px;
|
||||||
|
|
||||||
|
z-index: 1001; /* Ensure sub-menu appears above main menu */
|
||||||
|
/*
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
background: rgb(from #353535 r g b / 50%);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.cat_empty {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Settings */
|
||||||
|
#switch_settings{
|
||||||
|
bottom: 5px;
|
||||||
|
position: absolute;
|
||||||
|
width: 19px;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sb_settingsDiv{
|
||||||
|
outline: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--bg-color);
|
||||||
|
color: var(--input-text);
|
||||||
|
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;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 5px 20px 20px 20px;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 1001;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
#sb_settingsDiv .setting{
|
||||||
|
height: 28px;
|
||||||
|
width: 450px;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
}
|
||||||
|
#sb_settingsDiv .setting input[type="range"]{
|
||||||
|
height: 28px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#sb_settingsDiv .setting input[type="checkbox"]{
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
#sb_settingsDiv .setting input{
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
#sb_settingsDiv .setting label{
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
#sb_settingsDiv .slider-container{
|
||||||
|
float: right;
|
||||||
|
width: 36px;
|
||||||
|
background: var(--comfy-menu-bg);
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 5px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sb_settingsDiv h1{
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
padding: 0 0 5px 0;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 5px solid var(--border-color);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sb_settingsDiv button{
|
||||||
|
|
||||||
|
padding: 9px;
|
||||||
|
/* position: absolute; */
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
background: var(--comfy-menu-bg);
|
||||||
|
color: var(--input-text);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
#sb_settingsDiv button:hover{
|
||||||
|
filter: brightness(120%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sb_settingsDiv i{
|
||||||
|
margin-left: 4px;
|
||||||
|
color: #0075ff;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: larger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MODALS */
|
||||||
|
.sb-modal-title{
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
margin-top: 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sb-modal-backdrop, .sb-modal-backdrop-settings {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.2); /* Add desired opacity */
|
||||||
|
z-index: 1000; /* Make sure the backdrop is behind the modal */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#sb_settingsDiv select{
|
||||||
|
background: var(--comfy-menu-bg);
|
||||||
|
color: var(--drag-text);
|
||||||
|
padding: 5px;
|
||||||
|
float: right;
|
||||||
|
width: 129px;
|
||||||
|
margin-right: 47px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorBlob {
|
||||||
|
float: right;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 34px;
|
||||||
|
width: 100%;
|
||||||
|
height: 125px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorBlob input[type='color'] {
|
||||||
|
width: 106%;
|
||||||
|
height: 112%;
|
||||||
|
margin: -6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#switch_home,#switch_custom_view{
|
||||||
|
width: 1.3rem;
|
||||||
|
height: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#switch_sidebar{
|
||||||
|
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<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">×</span>
|
||||||
|
<span class="searchCategoryIcon">C</span>
|
||||||
|
</div>
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
<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"/>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<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
|
||||||
|
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>
|
||||||
|
<div id="customMenu" class="custom-menu">
|
||||||
|
<ul> </ul></div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Context Menu</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: rgb(26, 26, 26);
|
||||||
|
}
|
||||||
|
.context-menu {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: #3b3b3b;
|
||||||
|
border: 1px solid #313131;
|
||||||
|
color: #d4d4d4;
|
||||||
|
padding: 5px 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
.context-menu ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.context-menu li {
|
||||||
|
padding: 8px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.context-menu li:hover {
|
||||||
|
background-color: #6d6d6d;
|
||||||
|
}
|
||||||
|
.sub-menu {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 100%;
|
||||||
|
background-color: #3b3b3b;
|
||||||
|
border: 1px solid #313131;
|
||||||
|
border-radius: 5px;
|
||||||
|
z-index: 1001; /* Ensure sub-menu appears above main menu */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body oncontextmenu="return false;">
|
||||||
|
|
||||||
|
<div class="context-menu" id="context-menu">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const options = ["op1", "op2", "op3"];
|
||||||
|
const subOptions = [
|
||||||
|
["sub1", "sub2"],
|
||||||
|
["sub3", "sub4"],
|
||||||
|
["sub5", "sub6", "sub44", "sub26"]
|
||||||
|
];
|
||||||
|
let subMenuVisible = false;
|
||||||
|
|
||||||
|
contextMenu = document.getElementById('context-menu');
|
||||||
|
const menuOptions = document.createElement("ul");
|
||||||
|
menuOptions.id = "menu-options";
|
||||||
|
contextMenu.appendChild(menuOptions);
|
||||||
|
|
||||||
|
const subMenus = [];
|
||||||
|
for (let i = 0; i < subOptions.length; i++) {
|
||||||
|
const subMenu = document.createElement("ul");
|
||||||
|
subMenu.id = `sub-menu-op${i + 1}`;
|
||||||
|
subMenu.classList.add("sub-menu");
|
||||||
|
contextMenu.appendChild(subMenu);
|
||||||
|
subMenus.push(subMenu);
|
||||||
|
|
||||||
|
for (let j = 0; j < subOptions[i].length; j++) {
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.textContent = subOptions[i][j];
|
||||||
|
subMenu.appendChild(li);
|
||||||
|
li.addEventListener('click', () => {
|
||||||
|
alert(`Selected option: ${subOptions[i][j]}`);
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createContextMenu(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
hideAllSubMenus();
|
||||||
|
const contextMenu = document.getElementById('context-menu');
|
||||||
|
const menuOptions = document.getElementById('menu-options');
|
||||||
|
|
||||||
|
// Clear existing menu items
|
||||||
|
menuOptions.innerHTML = '';
|
||||||
|
|
||||||
|
// Populate main options
|
||||||
|
options.forEach((option, index) => {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = option;
|
||||||
|
li.addEventListener('mouseenter', () => {
|
||||||
|
hideAllSubMenus();
|
||||||
|
showSubMenu(subMenus[index], li, index);
|
||||||
|
});
|
||||||
|
li.addEventListener('click', () => {
|
||||||
|
// Handle main option click
|
||||||
|
alert(`Selected option: ${option}`);
|
||||||
|
});
|
||||||
|
menuOptions.appendChild(li);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show context menu
|
||||||
|
contextMenu.style.display = 'block';
|
||||||
|
contextMenu.style.left = event.clientX + 'px';
|
||||||
|
contextMenu.style.top = event.clientY + 'px';
|
||||||
|
|
||||||
|
// Hide context menu on body click
|
||||||
|
document.body.addEventListener('click', hideContextMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSubMenu(subMenu, element, index) {
|
||||||
|
subMenu.style.display = 'block';
|
||||||
|
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
subMenu.style.top = element.offsetTop + 'px';
|
||||||
|
|
||||||
|
subMenuVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideAllSubMenus() {
|
||||||
|
const subMenus = document.querySelectorAll('.sub-menu');
|
||||||
|
subMenus.forEach(subMenu => {
|
||||||
|
subMenu.style.display = 'none';
|
||||||
|
});
|
||||||
|
subMenuVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideContextMenu() {
|
||||||
|
const contextMenu = document.getElementById('context-menu');
|
||||||
|
contextMenu.style.display = 'none';
|
||||||
|
document.body.removeEventListener('click', hideContextMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
document.addEventListener('contextmenu', createContextMenu);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('click', hideContextMenu);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<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>
|
||||||
|
After Width: | Height: | Size: 439 B |
@@ -0,0 +1,404 @@
|
|||||||
|
const cnPath="../extensions/ComfyUI-N-Sidebar/"
|
||||||
|
function addSidebarStyles(cssPath) {
|
||||||
|
const linkElement = document.createElement("link");
|
||||||
|
linkElement.rel = "stylesheet";
|
||||||
|
linkElement.type = "text/css";
|
||||||
|
linkElement.href = cnPath+cssPath
|
||||||
|
document.head.appendChild(linkElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function sidebarAddNode(name, text, x, y) {
|
||||||
|
const node = LiteGraph.createNode(name, text)
|
||||||
|
if (node) {
|
||||||
|
const pos =
|
||||||
|
[
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
]
|
||||||
|
node.pos = pos;
|
||||||
|
app.graph.add(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function sdExpandAll(forceExpand=0) {
|
||||||
|
|
||||||
|
const categoryItems = document.querySelectorAll(".content_sidebar .sidebarCategory");
|
||||||
|
const side_bar_status = document.querySelector(".content_sidebar").dataset.expanded;
|
||||||
|
const expand_node = document.getElementsByClassName("expand_node")[0];
|
||||||
|
|
||||||
|
let display_value = "true";
|
||||||
|
|
||||||
|
if (side_bar_status === "true" && forceExpand === 0) {
|
||||||
|
|
||||||
|
display_value = "none";
|
||||||
|
expand_node.innerHTML = `<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"/>
|
||||||
|
<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>`;
|
||||||
|
document.querySelector(".content_sidebar").dataset.expanded = "false";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
display_value = "block";
|
||||||
|
expand_node.innerHTML = `<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>
|
||||||
|
<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"></path>
|
||||||
|
<path d="M34.5,29c0.8,0,1.5-0.7,1.5-1.5v-3c0-0.8-0.7-1.5-1.5-1.5h-17c-0.8,0-1.5,0.7-1.5,1.5v3
|
||||||
|
c0,0.8,0.7,1.5,1.5,1.5H34.5z"></path>
|
||||||
|
|
||||||
|
<rect class="expand_node" style="opacity:0" x="0" y="0" width="52" height="52" />
|
||||||
|
</svg>`;
|
||||||
|
document.querySelector(".content_sidebar").dataset.expanded = "true";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
categoryItems.forEach(function (categoryItem) {
|
||||||
|
const displayNamesList = categoryItem.querySelector("ul");
|
||||||
|
|
||||||
|
if (expand_node) {
|
||||||
|
|
||||||
|
|
||||||
|
displayNamesList.style.display = display_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleSearch(categorySearchToggle,searchSection,searchIdInput) {
|
||||||
|
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const options = {
|
||||||
|
keys: ['textContent','category'],
|
||||||
|
threshold: 0.5,
|
||||||
|
useExtendedSearch: true
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const searchTerm = document.getElementById(searchIdInput).value.toLowerCase();
|
||||||
|
const categoryItems = document.querySelectorAll(".sidebarCategory li");
|
||||||
|
const categories = document.querySelectorAll(searchSection+" .sidebarCategory");
|
||||||
|
const listItems = document.querySelectorAll(searchSection+" .sidebarItem");
|
||||||
|
|
||||||
|
|
||||||
|
/*reset*/
|
||||||
|
categoryItems.forEach(category => {
|
||||||
|
const subItems = category.querySelectorAll("li");
|
||||||
|
category.style.display = "block";
|
||||||
|
|
||||||
|
subItems.forEach(sub => {
|
||||||
|
sub.style.display = "block";
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const sidebarItems = categorySearchToggle ? categories : listItems;
|
||||||
|
|
||||||
|
sidebarItems.forEach(item => {
|
||||||
|
let itemText = item.textContent.toLowerCase();
|
||||||
|
if (categorySearchToggle) {
|
||||||
|
|
||||||
|
itemText = Array.from(item.childNodes)
|
||||||
|
.filter(node => node.nodeType === Node.TEXT_NODE)
|
||||||
|
.map(node => node.textContent.trim())
|
||||||
|
.join(' ').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
//fts_fuzzy_match(searchTerm, cleanText(itemText))
|
||||||
|
const isInSearchTerm = itemText.includes(searchTerm);
|
||||||
|
item.style.display = isInSearchTerm ? "block" : "none";
|
||||||
|
});
|
||||||
|
|
||||||
|
/* hide empty categories */
|
||||||
|
categories.forEach(category => {
|
||||||
|
const subItems = category.querySelectorAll("li");
|
||||||
|
const areAllHidden = Array.from(subItems).every(subItem => subItem.style.display === "none");
|
||||||
|
category.style.display = areAllHidden ? "none" : category.style.display;
|
||||||
|
});
|
||||||
|
|
||||||
|
sdExpandAll(1);
|
||||||
|
resolve(searchTerm);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//PREVIEW
|
||||||
|
function safeObjectKeys(obj) {
|
||||||
|
try {
|
||||||
|
return Object.keys(obj);
|
||||||
|
} catch (error) {
|
||||||
|
//console.error('Error while trying to get object keys:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNodePreview(nodeID) {
|
||||||
|
let description = "";
|
||||||
|
let rows = "";
|
||||||
|
let last_rows = "";
|
||||||
|
const data = LiteGraph.registered_node_types;
|
||||||
|
|
||||||
|
|
||||||
|
try{
|
||||||
|
description = data[nodeID].nodeData.description;
|
||||||
|
}catch(err){
|
||||||
|
description = "";
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
let inputs = data[nodeID].nodeData.input; //divided between optional and required
|
||||||
|
let outputs_name = data[nodeID].nodeData.output_name;
|
||||||
|
let outputs = data[nodeID].nodeData.output;
|
||||||
|
//console.log(category, inputs, outputs);
|
||||||
|
//create array with objectkeys from input and output
|
||||||
|
const inputArray = [];
|
||||||
|
const outputArray = [];
|
||||||
|
|
||||||
|
const inputKeysRequired = safeObjectKeys(inputs.required)
|
||||||
|
const inputKeysOptional = safeObjectKeys(inputs.optional)
|
||||||
|
|
||||||
|
|
||||||
|
for (let i = 0; i < inputKeysRequired.length; i++) {
|
||||||
|
try{
|
||||||
|
let thirdV = null;
|
||||||
|
let secondV = inputs.required[inputKeysRequired[i]][0];
|
||||||
|
|
||||||
|
if (Array.isArray(secondV)){
|
||||||
|
//array
|
||||||
|
secondV = inputs.required[inputKeysRequired[i]][0][0];
|
||||||
|
thirdV = inputs.required[inputKeysRequired[i]][0][0];
|
||||||
|
}
|
||||||
|
if (inputs.required[inputKeysRequired[i]][1]!=undefined){
|
||||||
|
|
||||||
|
//object
|
||||||
|
thirdV = inputs.required[inputKeysRequired[i]][1].default;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inputArray.push([inputKeysRequired[i],secondV,thirdV]);
|
||||||
|
}catch(err){
|
||||||
|
console.log("error",err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
for (let i = 0; i < inputKeysOptional.length; i++) {
|
||||||
|
let thirdV = null;
|
||||||
|
let secondV = inputs.optional[inputKeysOptional[i]][0];
|
||||||
|
if (Array.isArray(secondV)){
|
||||||
|
//array
|
||||||
|
secondV = inputs.optional[inputKeysOptional[i]][0][0];
|
||||||
|
thirdV = inputs.optional[inputKeysOptional[i]][0][0];
|
||||||
|
}
|
||||||
|
if (inputs.optional[inputKeysOptional[i]][1]!=undefined){
|
||||||
|
|
||||||
|
//object
|
||||||
|
thirdV = inputs.optional[inputKeysOptional[i]][1].default;
|
||||||
|
}
|
||||||
|
inputArray.push([inputKeysOptional[i],secondV,thirdV]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}catch(err){
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (let i = 0; i < outputs.length; i++) {
|
||||||
|
if (outputs_name[i] != undefined) {
|
||||||
|
outputArray.push([outputs[i],outputs_name[i]]);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
outputArray.push(outputs[i],outputs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let length_loop = outputArray.length;
|
||||||
|
if (inputArray.length> outputArray.length){
|
||||||
|
length_loop = inputArray.length;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < length_loop; i++) {
|
||||||
|
|
||||||
|
const inputList= inputArray[i] ? inputArray[i] : null;
|
||||||
|
const outputList= outputArray[i] ? outputArray[i] : null;
|
||||||
|
let inputName = "";
|
||||||
|
let inputType = "";
|
||||||
|
let inputValue = null;
|
||||||
|
let outputType = "";
|
||||||
|
let outputName = "";
|
||||||
|
|
||||||
|
if (inputList != null){
|
||||||
|
inputName = inputList[0];
|
||||||
|
inputType = inputList[1];
|
||||||
|
inputValue = inputList[2];
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
inputName = "";
|
||||||
|
inputType = "sb_hidden";
|
||||||
|
inputValue = null;
|
||||||
|
}
|
||||||
|
if (outputList != null){
|
||||||
|
outputType = outputList[0];
|
||||||
|
outputName = outputList[1];
|
||||||
|
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
outputType = "sb_hidden";
|
||||||
|
outputName = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (inputValue != null){
|
||||||
|
if (inputType == "STRING"){
|
||||||
|
last_rows += `<div class="sb_row sb_row_string nodepreview long_field">
|
||||||
|
<div class="sb_col sb_arrow"></div>
|
||||||
|
<div class="sb_col">${inputName}</div>
|
||||||
|
<div class="sb_col middle-column"></div>
|
||||||
|
<div class="sb_col sb_inherit">${inputValue}</div>
|
||||||
|
<div class="sb_col sb_arrow"></div>
|
||||||
|
</div>`;
|
||||||
|
}else{
|
||||||
|
last_rows += `<div class="sb_row nodepreview long_field">
|
||||||
|
<div class="sb_col sb_arrow">◀</div>
|
||||||
|
<div class="sb_col ">${inputName}</div>
|
||||||
|
<div class="sb_col middle-column"></div>
|
||||||
|
<div class="sb_col sb_inherit">${inputValue}</div>
|
||||||
|
<div class="sb_col sb_arrow">▶</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
inputType = "sb_hidden";
|
||||||
|
inputName = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rows += `<div class="sb_row nodepreview">
|
||||||
|
<div class="sb_col"><div class="sb_dot ${inputType}"></div></div>
|
||||||
|
<div class="sb_col">${inputName}</div>
|
||||||
|
<div class="sb_col middle-column"> </div>
|
||||||
|
<div class="sb_col sb_inherit">${outputName}</div>
|
||||||
|
<div class="sb_col "><div class="sb_dot ${outputType}"></div></div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
//console.log( inputArray,outputArray);
|
||||||
|
|
||||||
|
////create preview
|
||||||
|
//const nodePreview = document.createElement("div");
|
||||||
|
//nodePreview.classList.add("node_preview");
|
||||||
|
//nodePreview.id = nodeID;
|
||||||
|
|
||||||
|
}catch(err){
|
||||||
|
rows = `<div class="sb_row nodepreview">
|
||||||
|
<div class="sb_col"></div>
|
||||||
|
<div class="sb_col"></div>
|
||||||
|
<div class="sb_col middle-column sb_preview_badge ">NOT AVAILABLE</div>
|
||||||
|
<div class="sb_col sb_inherit"></div>
|
||||||
|
<div class="sb_col "></div>
|
||||||
|
</div>`
|
||||||
|
last_rows = ``
|
||||||
|
}
|
||||||
|
return [`<div class="sb_table">
|
||||||
|
<div class="node_header"><div class="sb_dot headdot"></div>${nodeID}</div>
|
||||||
|
<div class="sb_preview_badge">PREVIEW</div>
|
||||||
|
${rows}
|
||||||
|
${last_rows}
|
||||||
|
</div>`,description];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//change view
|
||||||
|
function switchTab(tabId) {
|
||||||
|
if (tabId != null) {
|
||||||
|
const tabContents = document.querySelectorAll('.content_sidebar');
|
||||||
|
const tabHead = document.querySelectorAll('.sidebar-header');
|
||||||
|
|
||||||
|
if (tabId=="content_sidebar_home"){
|
||||||
|
const tabIdHead = document.getElementById("sidebar-header");
|
||||||
|
tabIdHead.classList.remove('sb_hidden');
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
tabHead.forEach(content => {
|
||||||
|
content.classList.add('sb_hidden');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Hide all tabs
|
||||||
|
tabContents.forEach(content => {
|
||||||
|
content.classList.add('sb_hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Show the content of the clicked tab
|
||||||
|
document.getElementById(tabId).classList.remove('sb_hidden');
|
||||||
|
|
||||||
|
setVar('sb_current_tab', tabId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// PINNED ITEMS
|
||||||
|
function savePinnedItems(pinnedItems) {
|
||||||
|
const pinnedItemsString = JSON.stringify(pinnedItems);
|
||||||
|
//setVar('pinnedItems', pinnedItemsString, 9999);
|
||||||
|
updateConfiguration('sb_pinnedItems',pinnedItemsString)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function loadPinnedItems() {
|
||||||
|
|
||||||
|
const pinnedItemsString = await getConfiguration('sb_pinnedItems');//getVar('pinnedItems');
|
||||||
|
|
||||||
|
if (pinnedItemsString) {
|
||||||
|
return JSON.parse(pinnedItemsString);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function migrationSettings() {
|
||||||
|
const oldSettingsList = ["pinnedItems","Comfy.Settings.sidebar_noderadius_settings","Comfy.Settings.sidebar_barbottom","Comfy.Settings.sidebar_bartop","Comfy.Settings.sidebar_blur_settings","Comfy.Settings.sidebar_opacity_settings","Comfy.Settings.sidebar_font_settings","Comfy.Settings.sidebar_node_settings"];
|
||||||
|
const newSettingsList = ["sb_pinnedItems","sb_noderadius","sb_barbottom","sb_bartop","sb_blur","sb_opacity","sb_font","sb_node"];
|
||||||
|
|
||||||
|
oldSettingsList.forEach((setting, index) => {
|
||||||
|
try{
|
||||||
|
const newSetting = newSettingsList[index];
|
||||||
|
const oldSettingValue = getVar(setting);
|
||||||
|
if (oldSettingValue) {
|
||||||
|
if (setting != "pinnedItems") {
|
||||||
|
updateConfiguration(newSetting, parseFloat(oldSettingValue.replace('"',"").replace('"',"")));
|
||||||
|
} else {
|
||||||
|
updateConfiguration(newSetting, oldSettingValue);
|
||||||
|
}
|
||||||
|
renameVar(setting, "bk_"+setting)
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// updateConfiguration('sb_pinnedItems',pinnedItemsString) ;
|
||||||
|
}
|
||||||
@@ -0,0 +1,481 @@
|
|||||||
|
|
||||||
|
|
||||||
|
let sbPosition = "left";
|
||||||
|
let defaultSearchToggle = "fuzzy";
|
||||||
|
let defaultSearchOrder = "comfyui";
|
||||||
|
|
||||||
|
async function getConfiguration(name) {
|
||||||
|
//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();
|
||||||
|
|
||||||
|
return data["value"];
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addConfiguration(name,value) {
|
||||||
|
return fetch('/sidebar/settings', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
action : "add",
|
||||||
|
parameter: name,
|
||||||
|
value: value
|
||||||
|
}),
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateConfiguration(name,value) {
|
||||||
|
return fetch('/sidebar/settings', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
action : "update",
|
||||||
|
parameter: name,
|
||||||
|
value: value
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeConfiguration(name) {
|
||||||
|
|
||||||
|
return fetch('/sidebar/settings', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
action : "delete",
|
||||||
|
parameter: name
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function factoryResetSettings() {
|
||||||
|
return fetch('/sidebar/settings', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
action : "factory_reset"
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function settingsSetup(app,$el) {
|
||||||
|
|
||||||
|
|
||||||
|
app.ui.settings.addSetting({
|
||||||
|
id: "0_sidebar",
|
||||||
|
name: "[Sidebar] Better ComfyUI Settings Style",
|
||||||
|
type: "boolean",
|
||||||
|
defaultValue: false,
|
||||||
|
onChange(value) {
|
||||||
|
let styleElement;
|
||||||
|
if (value) {
|
||||||
|
styleElement = $el("style", {
|
||||||
|
id: "sb-settings-style",
|
||||||
|
parent: document.head,
|
||||||
|
textContent: settingsStyle
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
try{
|
||||||
|
document.getElementById("sb-settings-style").remove();
|
||||||
|
} catch(e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
addSBSetting( "sb_settingsDiv",{
|
||||||
|
id: "font",
|
||||||
|
name: "Font Size",
|
||||||
|
defaultValue: "13",
|
||||||
|
type: "slider",
|
||||||
|
attrs: {
|
||||||
|
min: 5,
|
||||||
|
max: 30,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
|
onChange(value) {
|
||||||
|
addDynamicCSSRule('.sidebarCategory, #sidebarBookmarks', 'font-size', value + 'px');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
addSBSetting( "sb_settingsDiv",{
|
||||||
|
id: "node",
|
||||||
|
name: "Node Size",
|
||||||
|
defaultValue: "10",
|
||||||
|
type: "slider",
|
||||||
|
attrs: {
|
||||||
|
min: 2,
|
||||||
|
max: 40,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
|
onChange(value) {
|
||||||
|
addDynamicCSSRule('.sidebar li', 'padding', value + 'px');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
addSBSetting( "sb_settingsDiv", {
|
||||||
|
id: "bartop",
|
||||||
|
name: "Space Top",
|
||||||
|
defaultValue: "19",
|
||||||
|
type: "slider",
|
||||||
|
attrs: {
|
||||||
|
min: 0,
|
||||||
|
max: 300,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
|
onChange(value) {
|
||||||
|
addDynamicCSSRule('.sidebar', 'padding-top', value + 'px');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addSBSetting( "sb_settingsDiv", {
|
||||||
|
id: "barbottom",
|
||||||
|
name: "Space Bottom",
|
||||||
|
defaultValue: "19",
|
||||||
|
type: "slider",
|
||||||
|
attrs: {
|
||||||
|
min: 0,
|
||||||
|
max: 300,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
|
onChange(value) {
|
||||||
|
addDynamicCSSRule('.sidebar', 'height', 'calc(100% - ' + value + 'px');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addSBSetting( "sb_settingsDiv",{
|
||||||
|
id: "noderadius",
|
||||||
|
name: "Node Radius Border",
|
||||||
|
defaultValue: "9",
|
||||||
|
type: "slider",
|
||||||
|
attrs: {
|
||||||
|
min: 0,
|
||||||
|
max: 20,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
|
onChange(value) {
|
||||||
|
addDynamicCSSRule('.sidebarCategory, #sidebarBookmarks', 'border-radius', value + 'px');
|
||||||
|
addDynamicCSSRule('.sidebarItem', 'border-radius', value + 'px');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
addSBSetting( "sb_settingsDiv",{
|
||||||
|
id: "blur",
|
||||||
|
name: "Blur Intesity",
|
||||||
|
defaultValue: "5",
|
||||||
|
type: "slider",
|
||||||
|
attrs: {
|
||||||
|
min: 0,
|
||||||
|
max: 30,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
|
onChange(value) {
|
||||||
|
addDynamicCSSRule('.content_sidebar', 'backdrop-filter', 'blur(' + value + 'px)');
|
||||||
|
addDynamicCSSRule('#sidebar_views', 'backdrop-filter', 'blur(' + value + 'px)');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
addSBSetting( "sb_settingsDiv", {
|
||||||
|
id: "opacity",
|
||||||
|
name: "Opacity",
|
||||||
|
defaultValue: "1.0",
|
||||||
|
type: "slider",
|
||||||
|
info: "Note: The opacity of custom-colored nodes is not applied in real-time and will take effect after the page is refreshed.",
|
||||||
|
attrs: {
|
||||||
|
min: 0.1,
|
||||||
|
max: 1,
|
||||||
|
step: 0.1,
|
||||||
|
},
|
||||||
|
onChange(value) {
|
||||||
|
let value_perc = (value * 100).toFixed(0);
|
||||||
|
addDynamicCSSRule('.content_sidebar', 'background', 'rgb(from var(--comfy-menu-bg) r g b / ' + value_perc + '%)');
|
||||||
|
addDynamicCSSRule('#sidebar_views', 'background', 'rgb(from var(--comfy-menu-bg) r g b / ' + value_perc + '%)');
|
||||||
|
addDynamicCSSRule('#sidebar_views', 'border-left', '2px rgb(from var(--comfy-menu-bg) r g b / ' + (parseFloat(value) + 10) + '%)');
|
||||||
|
addDynamicCSSRule('.sidebarCategory, #sidebarBookmarks', 'background-color', 'rgb(from var(--comfy-input-bg) r g b / ' + value_perc + '%)');
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
addSBSetting( "sb_settingsDiv", {
|
||||||
|
id: "search_type",
|
||||||
|
name: "Search Type",
|
||||||
|
defaultValue: "fuzzy",
|
||||||
|
type: "dropdown",
|
||||||
|
info: "Select the search type:\nORIGINAL: People who use custom nodes to translate ComfyUI (e.g. Chinese, Japanese...) should use this!\nNORMAL: Search based on original node name but without fuzzy matching\nFUZZY: Search based on the original node name with fuzzy matching (Default).",
|
||||||
|
options: [
|
||||||
|
{ value: "original", label: "ORIGINAL" },
|
||||||
|
{ value: "normal", label: "NORMAL" },
|
||||||
|
{ value: "fuzzy", label: "FUZZY" },
|
||||||
|
|
||||||
|
|
||||||
|
],
|
||||||
|
onChange(value) {
|
||||||
|
defaultSearchToggle = value;
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
addSBSetting( "sb_settingsDiv", {
|
||||||
|
id: "position",
|
||||||
|
name: "Position",
|
||||||
|
defaultValue: "left",
|
||||||
|
type: "dropdown",
|
||||||
|
options: [
|
||||||
|
{ value: "left", label: "LEFT" },
|
||||||
|
{ value: "right", label: "RIGHT" }
|
||||||
|
|
||||||
|
|
||||||
|
],
|
||||||
|
onChange(value) {
|
||||||
|
if (value == "right") {
|
||||||
|
addDynamicCSSRule('.content_sidebar', 'float', 'right');
|
||||||
|
addDynamicCSSRule('#sidebar_views', 'float', 'right');
|
||||||
|
addDynamicCSSRule('.dragHandle', 'float', 'right');
|
||||||
|
addDynamicCSSRule('.sidebar', 'left', 'unset');
|
||||||
|
addDynamicCSSRule('.sidebar', 'right', '0');
|
||||||
|
addDynamicCSSRule('.sidebar-header', 'right', '16px');
|
||||||
|
addDynamicCSSRule('#sb_scrollToTopButton', 'right', '12px');
|
||||||
|
|
||||||
|
}
|
||||||
|
if (value == "left") {
|
||||||
|
addDynamicCSSRule('.content_sidebar', 'float', 'left');
|
||||||
|
addDynamicCSSRule('#sidebar_views', 'float', 'left');
|
||||||
|
addDynamicCSSRule('.dragHandle', 'float', 'left');
|
||||||
|
addDynamicCSSRule('.sidebar', 'left', '0');
|
||||||
|
addDynamicCSSRule('.sidebar', 'right', 'unset');
|
||||||
|
addDynamicCSSRule('.sidebar-header', 'right', 'unset');
|
||||||
|
addDynamicCSSRule('#sb_scrollToTopButton', 'right', '56px');
|
||||||
|
}
|
||||||
|
sbPosition = value;
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
addSBSetting( "sb_settingsDiv", {
|
||||||
|
id: "order_type",
|
||||||
|
name: "Nodes Order Type",
|
||||||
|
defaultValue: "comfyui",
|
||||||
|
type: "dropdown",
|
||||||
|
info: "Select the order type:\nCOMFYUI: The order of nodes will be the same as in the original ComfyUI\nALPHABETICAL: The order of nodes will be alphabetical\nNOTE: The new order will be applied after a page refresh!",
|
||||||
|
options: [
|
||||||
|
{ value: "comfyui", label: "COMFYUI" },
|
||||||
|
{ value: "alphabetical", label: "ALPHABETICAL" }
|
||||||
|
|
||||||
|
|
||||||
|
],
|
||||||
|
onChange(value) {
|
||||||
|
|
||||||
|
defaultSearchOrder = value;
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const switch_settings = document.getElementById("switch_settings");
|
||||||
|
const sb_modal_backdrop = document.getElementById("sb-modal-backdrop-settings");
|
||||||
|
switch_settings.addEventListener('click', function() {
|
||||||
|
|
||||||
|
//set sb-settingsDiv visible
|
||||||
|
|
||||||
|
const sb_settingsDiv = document.getElementById("sb_settingsDiv");
|
||||||
|
|
||||||
|
sb_settingsDiv.classList.remove('sb_hidden');
|
||||||
|
sb_modal_backdrop.classList.remove('sb_hidden');
|
||||||
|
|
||||||
|
})
|
||||||
|
document.body.addEventListener('click', function(event) {
|
||||||
|
const modalBackdrop = document.querySelector('.sb-modal-backdrop-settings');
|
||||||
|
|
||||||
|
if (event.target === modalBackdrop) {
|
||||||
|
sb_settingsDiv.classList.add('sb_hidden');
|
||||||
|
sb_modal_backdrop.classList.add('sb_hidden');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
sb_resetButton.addEventListener('click', function() {
|
||||||
|
|
||||||
|
resetSettings();
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
sb_factoryResetButton.addEventListener('click', function() {
|
||||||
|
|
||||||
|
factoryReset();
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function addSBSetting(fieldLocation, setting) {
|
||||||
|
setting.id = "sb_" + setting.id; // Aggiunge il prefisso del campo
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
if (setting.info) {
|
||||||
|
const infoIcon = document.createElement("i");
|
||||||
|
infoIcon.classList.add("info-icon");
|
||||||
|
infoIcon.innerHTML ="🛈";
|
||||||
|
infoIcon.title = setting.info;
|
||||||
|
|
||||||
|
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;
|
||||||
|
setting.onChange(inputElement.checked);
|
||||||
|
inputElement.addEventListener("change", function() {
|
||||||
|
setting.onChange(inputElement.checked);
|
||||||
|
updateConfiguration(setting.id,inputElement.checked)
|
||||||
|
|
||||||
|
});
|
||||||
|
} else if (setting.type === "slider") {
|
||||||
|
// Crea l'elemento input per lo slider
|
||||||
|
const sliderContainer = document.createElement("div");
|
||||||
|
sliderContainer.classList.add("slider-container");
|
||||||
|
|
||||||
|
inputElement = document.createElement("input");
|
||||||
|
inputElement.type = "range";
|
||||||
|
inputElement.min = setting.attrs.min;
|
||||||
|
inputElement.max = setting.attrs.max;
|
||||||
|
inputElement.step = setting.attrs.step;
|
||||||
|
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;
|
||||||
|
sliderContainer.appendChild(inputElement);
|
||||||
|
sliderContainer.appendChild(valueText);
|
||||||
|
settingElement.appendChild(sliderContainer);
|
||||||
|
} else if (setting.type === "text") {
|
||||||
|
inputElement = document.createElement("input");
|
||||||
|
inputElement.type = "text";
|
||||||
|
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) => {
|
||||||
|
const optionElement = document.createElement("option");
|
||||||
|
optionElement.value = option.value;
|
||||||
|
optionElement.textContent = option.label;
|
||||||
|
|
||||||
|
if (option.value === defaultValue) {
|
||||||
|
optionElement.selected = true;
|
||||||
|
}
|
||||||
|
inputElement.appendChild(optionElement);
|
||||||
|
});
|
||||||
|
setting.onChange(defaultValue);
|
||||||
|
inputElement.addEventListener("change", function() {
|
||||||
|
setting.onChange(inputElement.value);
|
||||||
|
updateConfiguration(setting.id,inputElement.value)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
settingElement.appendChild(inputElement);
|
||||||
|
|
||||||
|
// Aggiungi il campo di impostazione al luogo specificato
|
||||||
|
const targetElement = document.getElementById(fieldLocation);
|
||||||
|
if (targetElement) {
|
||||||
|
targetElement.appendChild(settingElement);
|
||||||
|
} else {
|
||||||
|
console.error("Target element not found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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?");
|
||||||
|
if (!ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const actualSettings = ["sb_noderadius","sb_barbottom","sb_bartop","sb_blur","sb_opacity","sb_font","sb_node"];
|
||||||
|
actualSettings.forEach(setting => {
|
||||||
|
removeConfiguration(setting);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function factoryReset() {
|
||||||
|
let ok = confirm("Are you sure you want to do a factory reset?");
|
||||||
|
if (!ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
factoryResetSettings();
|
||||||
|
|
||||||
|
const localSettings = ["sidebarWidth","sb_pinned_collapsed","sb_minimized","sb_current_tab","sb_current_tab"];
|
||||||
|
|
||||||
|
localSettings.forEach(setting => {
|
||||||
|
try{
|
||||||
|
removeVar(setting);
|
||||||
|
}catch(err){
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
window.location.reload();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,496 @@
|
|||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
<style>
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id ="custom_categories_header">
|
||||||
|
|
||||||
|
<div class="h-header" id="h_search" data-content="panel_head_home">
|
||||||
|
<input type="text" id="searchInputCn" oninput="searchT(this)" placeholder="Search..." autocomplete="off" class="">
|
||||||
|
<span class="clearIconCustomCategory">×</span>
|
||||||
|
<span class="searchCategoryIconCustomCategory">C</span> <button id="openModalButton" onclick="openModal()">+</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="custom_categories_main" >
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,829 @@
|
|||||||
|
//console.log(LiteGraph.registered_node_types);
|
||||||
|
|
||||||
|
let currentElement = null;
|
||||||
|
|
||||||
|
function createCustomHtml(type, name, elem = null) {
|
||||||
|
if (type == "rename") {
|
||||||
|
return `<h2 class="sb-modal-title">Rename Category</h2>
|
||||||
|
<form id="sb-category-form" action="javascript:void(0);">
|
||||||
|
<input type="text" autocomplete="off" class="sb-input" id="sb-categoryName" value="${name}" name="sb-categoryName" required>
|
||||||
|
<button type="submit" class="sb-button" onclick="renameCategory('${name}',document.getElementById('sb-categoryName').value)">Rename Category</button>
|
||||||
|
</form>
|
||||||
|
<button id="closeModalButton" class="sb-button">X</button>
|
||||||
|
|
||||||
|
`
|
||||||
|
} else if (type == "color") {
|
||||||
|
currentElement = elem;
|
||||||
|
return `<h2 class="sb-modal-title">Color ${name}</h2>
|
||||||
|
<div class="colorBlob">
|
||||||
|
<input class="colorBlobPicker" type="color" oninput="colorCategory('${name}',this.value)" value="${rgbToHex(elem.style.backgroundColor)}" id="colorPicker">
|
||||||
|
</div>
|
||||||
|
<button type="button" class="sb-button" onclick="resetColorCategory('${name}')">Reset Color</button>
|
||||||
|
<button type="button" class="sb-button" onclick="removeKeyConfig('sb_ColorCustomCategories') ">Reset All Colors</button>
|
||||||
|
|
||||||
|
<button id="closeModalButton" class="sb-button">X</button>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Function to open the modal
|
||||||
|
|
||||||
|
//insert button in label with id panel_title_custom_categories
|
||||||
|
let categorySearchToggleCustom = false;
|
||||||
|
const panel_title_custom_categories = document.getElementById("panel_title_custom_categories");
|
||||||
|
|
||||||
|
const button_custom_categories = document.createElement("button");
|
||||||
|
button_custom_categories.classList = "expand_node";
|
||||||
|
button_custom_categories.innerHTML = `<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>
|
||||||
|
<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"></path>
|
||||||
|
<path d="M34.5,29c0.8,0,1.5-0.7,1.5-1.5v-3c0-0.8-0.7-1.5-1.5-1.5h-17c-0.8,0-1.5,0.7-1.5,1.5v3
|
||||||
|
c0,0.8,0.7,1.5,1.5,1.5H34.5z"></path>
|
||||||
|
|
||||||
|
<rect class="expand_node" style="opacity:0" x="0" y="0" width="52" height="52"></rect>
|
||||||
|
</svg>`;
|
||||||
|
|
||||||
|
panel_title_custom_categories.appendChild(button_custom_categories);
|
||||||
|
|
||||||
|
button_custom_categories.addEventListener("click", (event) => {
|
||||||
|
const clickedElement = event.target;
|
||||||
|
const pinButton = clickedElement.tagName;
|
||||||
|
if ((pinButton == "rect") && clickedElement.classList.contains("expand_node")) {
|
||||||
|
|
||||||
|
sdExpandAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
const clearIconCustomCategory = document.querySelector(".clearIconCustomCategory");
|
||||||
|
const itemSearchInput = document.getElementById("searchInputCn");
|
||||||
|
clearIconCustomCategory.addEventListener("click", async function () {
|
||||||
|
try {
|
||||||
|
|
||||||
|
itemSearchInput.value = "";
|
||||||
|
renderList("custom_categories_main");
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error occurred during search:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function afterRender() {
|
||||||
|
|
||||||
|
customMenu = `
|
||||||
|
{
|
||||||
|
"menuctx_category": "Context Menu",
|
||||||
|
"menuctx_options": [
|
||||||
|
"Rename Category",
|
||||||
|
"Delete Category",
|
||||||
|
"Change Color"
|
||||||
|
],
|
||||||
|
"menuctx_subOptions": [
|
||||||
|
[
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"menuctx_opt_callback": [
|
||||||
|
"renameCategoryCallback",
|
||||||
|
"deleteCategory",
|
||||||
|
"colorCategoryCallback"
|
||||||
|
|
||||||
|
],
|
||||||
|
"menuctx_sub_opt_callback": [
|
||||||
|
[
|
||||||
|
],
|
||||||
|
[
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
|
||||||
|
customMenuItem = `
|
||||||
|
{
|
||||||
|
"menuctx_category": "Context Menu",
|
||||||
|
"menuctx_options": [
|
||||||
|
"Delete from Category"
|
||||||
|
],
|
||||||
|
"menuctx_subOptions": [
|
||||||
|
[
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"menuctx_opt_callback": [
|
||||||
|
"removeNodeFromCategoryCallback"
|
||||||
|
|
||||||
|
],
|
||||||
|
"menuctx_sub_opt_callback": [
|
||||||
|
[
|
||||||
|
],
|
||||||
|
[
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
customMenuJSON = JSON.parse(customMenu);
|
||||||
|
customMenuItemJSON = JSON.parse(customMenuItem);
|
||||||
|
|
||||||
|
setContextMenu(customMenuJSON, "#custom_categories_main .sidebarCategory", 0, "sidebarItem", "sidebarCategory");
|
||||||
|
|
||||||
|
setContextMenu(customMenuItemJSON, "#custom_categories_main .sidebarItem");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ORDER CUSTOM CATEGORIES
|
||||||
|
|
||||||
|
|
||||||
|
const custom_categories_main = document.getElementById("custom_categories_main");
|
||||||
|
custom_categories_main.querySelectorAll(".sidebarCategory").forEach(function (item) {
|
||||||
|
item.addEventListener("dragstart", function (event) {
|
||||||
|
dragItem = event.target;
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
custom_categories_main.addEventListener("dragover", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
custom_categories_main.addEventListener("drop", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (dragItem) {
|
||||||
|
|
||||||
|
|
||||||
|
if (dragItem.parentElement === event.target.parentElement) {
|
||||||
|
custom_categories_main.insertBefore(dragItem, event.target);
|
||||||
|
reorderCategories(getCustomCategoryOrder());
|
||||||
|
}
|
||||||
|
dragItem = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ORDER CUSTOM NODES
|
||||||
|
|
||||||
|
|
||||||
|
custom_categories_main.querySelectorAll(".sidebarItem").forEach(function (item) {
|
||||||
|
item.addEventListener("dragstart", function (event) {
|
||||||
|
dragItem = event.target;
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCustomCategoryOrder() {
|
||||||
|
let cat = [];
|
||||||
|
const custom_categories_main = document.getElementById("custom_categories_main");
|
||||||
|
custom_categories_main.querySelectorAll(".sidebarCategory").forEach(function (item) {
|
||||||
|
cat.push(item.dataset.namecategory)
|
||||||
|
|
||||||
|
});
|
||||||
|
return cat
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getCustomNodeOrder(idcat) {
|
||||||
|
let node = [];
|
||||||
|
const objMain = document.getElementById(idcat);
|
||||||
|
objMain.querySelectorAll(".sidebarItem").forEach(function (item) {
|
||||||
|
node.push(item.dataset.type)
|
||||||
|
|
||||||
|
});
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function renderList(elementID) {
|
||||||
|
const data = LiteGraph.registered_node_types;
|
||||||
|
|
||||||
|
|
||||||
|
const myPromise = new Promise(async (resolve, reject) => {
|
||||||
|
const categories = await readCategories();
|
||||||
|
//const pinnedItems = loadPinnedItems();
|
||||||
|
|
||||||
|
//console.log(categories);
|
||||||
|
const sidebarCustomNodes = document.getElementById(elementID);
|
||||||
|
//clear elementid
|
||||||
|
sidebarCustomNodes.innerHTML = "";
|
||||||
|
|
||||||
|
|
||||||
|
for (const category in categories) {
|
||||||
|
//data[objKey].title.toLowerCase();
|
||||||
|
let categoryName = categories[category]
|
||||||
|
const categoryItem = document.createElement("li");
|
||||||
|
categoryItem.classList.add("sidebarCategory");
|
||||||
|
categoryItem.dataset.namecategory = categoryName;
|
||||||
|
categoryItem.textContent = categoryName;
|
||||||
|
categoryItem.draggable = true;
|
||||||
|
|
||||||
|
const currentColor = await getValueFromConfig("sb_ColorCustomCategories", categoryName);
|
||||||
|
if (currentColor) {
|
||||||
|
const value_perc = await getConfiguration("sb_opacity") || "1.0";
|
||||||
|
|
||||||
|
const rgbColor = hexToRgb(currentColor);
|
||||||
|
|
||||||
|
categoryItem.style.backgroundColor = `rgba(${rgbColor}, ${value_perc})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const displayNamesList = document.createElement("ul");
|
||||||
|
displayNamesList.id = categoryName + "_ul";
|
||||||
|
displayNamesList.style.display = getNodeStatus(categoryName);
|
||||||
|
categoryItem.appendChild(displayNamesList);
|
||||||
|
|
||||||
|
let displayName = category;
|
||||||
|
|
||||||
|
|
||||||
|
const listNodeForCategory = await getNodesForCategory(categoryName);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
listNodeForCategory.forEach(displayName => {
|
||||||
|
try {
|
||||||
|
const displayNameItem = document.createElement("li");
|
||||||
|
displayNameItem.classList.add("sidebarItem");
|
||||||
|
displayNameItem.textContent = data[displayName].title;
|
||||||
|
displayNameItem.title = data[displayName].title;
|
||||||
|
displayNameItem.draggable = true;
|
||||||
|
displayNameItem.dataset.type = data[displayName].type;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
displayNameItem.id = displayName.replace(" ", "_");
|
||||||
|
|
||||||
|
/* Create Pin Button
|
||||||
|
const pinButton = document.createElement("button");
|
||||||
|
pinButton.classList.add("pinButton");
|
||||||
|
|
||||||
|
let add_class = "";
|
||||||
|
if (pinnedItems.includes(displayNameItem.dataset.type)) {
|
||||||
|
add_class = "pinned";
|
||||||
|
}
|
||||||
|
|
||||||
|
pinButton.innerHTML = `<svg class="svg_class" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path class="pin_normal ${add_class}" d="M19.1835 7.80516L16.2188 4.83755C14.1921 2.8089 13.1788 1.79457 12.0904 2.03468C11.0021 2.2748 10.5086 3.62155 9.5217 6.31506L8.85373 8.1381C8.59063 8.85617 8.45908 9.2152 8.22239 9.49292C8.11619 9.61754 7.99536 9.72887 7.86251 9.82451C7.56644 10.0377 7.19811 10.1392 6.46145 10.3423C4.80107 10.8 3.97088 11.0289 3.65804 11.5721C3.5228 11.8069 3.45242 12.0735 3.45413 12.3446C3.45809 12.9715 4.06698 13.581 5.28476 14.8L6.69935 16.2163L2.22345 20.6964C1.92552 20.9946 1.92552 21.4782 2.22345 21.7764C2.52138 22.0746 3.00443 22.0746 3.30236 21.7764L7.77841 17.2961L9.24441 18.7635C10.4699 19.9902 11.0827 20.6036 11.7134 20.6045C11.9792 20.6049 12.2404 20.5358 12.4713 20.4041C13.0192 20.0914 13.2493 19.2551 13.7095 17.5825C13.9119 16.8472 14.013 16.4795 14.2254 16.1835C14.3184 16.054 14.4262 15.9358 14.5468 15.8314C14.8221 15.593 15.1788 15.459 15.8922 15.191L17.7362 14.4981C20.4 13.4973 21.7319 12.9969 21.9667 11.9115C22.2014 10.826 21.1954 9.81905 19.1835 7.80516Z" />
|
||||||
|
<rect class="pin_node" style="opacity:0" x="0" y="0" width="24" height="24" />
|
||||||
|
</svg>`;
|
||||||
|
|
||||||
|
|
||||||
|
displayNameItem.appendChild(pinButton);
|
||||||
|
/* End Pin Button */
|
||||||
|
|
||||||
|
|
||||||
|
displayNamesList.appendChild(displayNameItem);
|
||||||
|
|
||||||
|
|
||||||
|
displayNamesList.addEventListener("drop", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (dragItem) {
|
||||||
|
|
||||||
|
if (dragItem.parentElement == event.target.parentElement) {
|
||||||
|
|
||||||
|
displayNamesList.insertBefore(dragItem, event.target);
|
||||||
|
|
||||||
|
reorderNodeInCategory(categoryItem.dataset.namecategory, getCustomNodeOrder(dragItem.parentElement.id))
|
||||||
|
|
||||||
|
}
|
||||||
|
dragItem = null;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
sidebarCustomNodes.appendChild(categoryItem);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Preview
|
||||||
|
const sidebarItems_cat = document.querySelectorAll('#' + elementID + ' .sidebarItem');
|
||||||
|
const previewDiv = document.getElementById('previewDiv');
|
||||||
|
|
||||||
|
sidebarItems_cat.forEach(item => {
|
||||||
|
|
||||||
|
item.addEventListener('mouseover', function () {
|
||||||
|
|
||||||
|
if (this.classList.contains('sidebarItem') && this.tagName === 'LI') {
|
||||||
|
let descriptionDiv = "";
|
||||||
|
const itemPosition = getElementPosition(this);
|
||||||
|
let previewDivTop = 0;
|
||||||
|
const [previewContent, node_description] = createNodePreview(item.dataset.type);
|
||||||
|
if (node_description) {
|
||||||
|
descriptionDiv = "<div class='sb_description'>" + node_description + "</div>";
|
||||||
|
}
|
||||||
|
previewDiv.innerHTML = previewContent + descriptionDiv;
|
||||||
|
previewDiv.style.display = 'block';
|
||||||
|
const correction_offset = 45;
|
||||||
|
|
||||||
|
if (itemPosition.top - this.offsetHeight >= 0 && itemPosition.top + previewDiv.offsetHeight < document.body.offsetHeight) {
|
||||||
|
previewDivTop = itemPosition.top - this.offsetHeight
|
||||||
|
|
||||||
|
} else if (itemPosition.top - this.offsetHeight - previewDiv.offsetHeight <= 0) {
|
||||||
|
previewDivTop = 0 + correction_offset;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
previewDivTop = (itemPosition.top + this.offsetHeight) - previewDiv.offsetHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
const previewDivLeft = itemPosition.left + this.offsetWidth + correction_offset;
|
||||||
|
|
||||||
|
previewDiv.style.top = `${previewDivTop}px`;
|
||||||
|
previewDiv.style.left = `${previewDivLeft}px`;
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
item.addEventListener('mouseout', function () {
|
||||||
|
previewDiv.style.display = 'none';
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const categoriesList = document.getElementsByClassName("content_sidebar")[1];
|
||||||
|
categoriesList.addEventListener('scroll', function () {
|
||||||
|
previewDiv.style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('click', function (event) {
|
||||||
|
|
||||||
|
if (!event.target.classList.contains('sidebarItem')) {
|
||||||
|
previewDiv.style.display = 'none';
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const categoryItems = document.querySelectorAll("#custom_categories_main .sidebarCategory");
|
||||||
|
categoryItems.forEach(function (categoryItem) {
|
||||||
|
categoryItem.addEventListener("click", function (event) {
|
||||||
|
|
||||||
|
if (event.target === event.currentTarget) {
|
||||||
|
const displayNamesList = event.target.querySelector("ul");
|
||||||
|
|
||||||
|
displayNamesList.style.display = displayNamesList.style.display === "none" ? "block" : "none";
|
||||||
|
|
||||||
|
setNodeStatus(displayNamesList.parentElement.dataset.namecategory, displayNamesList.style.display)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
resolve(afterRender);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
myPromise.then((callback) => {
|
||||||
|
|
||||||
|
callback();
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error("An error occurred:", error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function searchT(e) {
|
||||||
|
|
||||||
|
if (e.value != "") {
|
||||||
|
//search in all .sidebarItem
|
||||||
|
handleSearch(categorySearchToggleCustom, "#custom_categories_main", "searchInputCn")
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
renderList("custom_categories_main");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function getNodeMap() {
|
||||||
|
let categoryNodeMap = JSON.parse(await getConfiguration('sb_categoryNodeMap')) || {};
|
||||||
|
return categoryNodeMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNodesStatus() {
|
||||||
|
let categoryNodeStatus = JSON.parse(localStorage.getItem('sb_categoryNodeStatus')) || {};
|
||||||
|
return categoryNodeStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function addCategory() {
|
||||||
|
|
||||||
|
|
||||||
|
const newCategoryName = document.getElementById('sb-categoryName').value;
|
||||||
|
// Check if the category name contains special characters
|
||||||
|
if (containsSpecialCharacters(newCategoryName)) {
|
||||||
|
// Alert the user and return if special characters are found
|
||||||
|
alert("Category name cannot contain special characters.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let categoryNodeMap = await getNodeMap();
|
||||||
|
|
||||||
|
|
||||||
|
if (!categoryNodeMap[newCategoryName]) {
|
||||||
|
|
||||||
|
categoryNodeMap[newCategoryName] = [];
|
||||||
|
updateConfiguration('sb_categoryNodeMap', JSON.stringify(categoryNodeMap));
|
||||||
|
createContextualMenu();
|
||||||
|
reloadCtxMenu()
|
||||||
|
renderList("custom_categories_main");
|
||||||
|
closeModal()
|
||||||
|
} else {
|
||||||
|
alert("Category already exists!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Function to rename a category
|
||||||
|
|
||||||
|
async function renameCategory(oldCategoryName, newCategoryName) {
|
||||||
|
|
||||||
|
|
||||||
|
if (containsSpecialCharacters(newCategoryName)) {
|
||||||
|
// Alert the user and return if special characters are found
|
||||||
|
alert("Category name cannot contain special characters.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (oldCategoryName === newCategoryName) {
|
||||||
|
closeModal()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let categoryNodeMap = await getNodeMap();
|
||||||
|
let categoryNodeStatus = getNodesStatus();
|
||||||
|
if (categoryNodeMap[oldCategoryName]) {
|
||||||
|
// Categoty Current Order
|
||||||
|
let keys = Object.keys(categoryNodeMap);
|
||||||
|
|
||||||
|
categoryNodeMap[newCategoryName] = categoryNodeMap[oldCategoryName];
|
||||||
|
delete categoryNodeMap[oldCategoryName];
|
||||||
|
|
||||||
|
// Rebuild Order
|
||||||
|
let updatedMap = {};
|
||||||
|
keys.forEach(key => {
|
||||||
|
if (key === oldCategoryName) {
|
||||||
|
updatedMap[newCategoryName] = categoryNodeMap[newCategoryName];
|
||||||
|
} else {
|
||||||
|
updatedMap[key] = categoryNodeMap[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
updateConfiguration('sb_categoryNodeMap', JSON.stringify(updatedMap));
|
||||||
|
//rename status
|
||||||
|
if (categoryNodeStatus[oldCategoryName]) {
|
||||||
|
|
||||||
|
categoryNodeStatus[newCategoryName] = categoryNodeStatus[oldCategoryName];
|
||||||
|
delete categoryNodeStatus[oldCategoryName];
|
||||||
|
localStorage.setItem('sb_categoryNodeStatus', JSON.stringify(categoryNodeStatus));
|
||||||
|
}
|
||||||
|
//rename color
|
||||||
|
renameKeyConfig("sb_ColorCustomCategories", oldCategoryName, newCategoryName)
|
||||||
|
|
||||||
|
|
||||||
|
createContextualMenu();
|
||||||
|
reloadCtxMenu()
|
||||||
|
renderList("custom_categories_main");
|
||||||
|
closeModal()
|
||||||
|
} else {
|
||||||
|
alert("Category not found!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function readCategories() {
|
||||||
|
let categoryNodeMap = await getNodeMap();
|
||||||
|
if (categoryNodeMap) {
|
||||||
|
return Object.keys(categoryNodeMap);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Function to delete a category
|
||||||
|
async function deleteCategory(e) {
|
||||||
|
|
||||||
|
const name = e.target.dataset.namecategory;
|
||||||
|
confirmation = confirm("Are you sure you want to delete the category " + name + "?");
|
||||||
|
if (!confirmation) { return; }
|
||||||
|
|
||||||
|
let categoryNodeMap = await getNodeMap();
|
||||||
|
// If the category name is found, remove it from the array
|
||||||
|
if (categoryNodeMap[name]) {
|
||||||
|
removeNodeMapCategory(name);
|
||||||
|
createContextualMenu();
|
||||||
|
removeKeyFromConfig("sb_ColorCustomCategories", name)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
|
||||||
|
renderList("custom_categories_main");
|
||||||
|
},200)
|
||||||
|
reloadCtxMenu()
|
||||||
|
} else {
|
||||||
|
alert("Category not found!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
|
||||||
|
async function getConfig(configName) {
|
||||||
|
let categoryNodeMap = JSON.parse(await getConfiguration(configName)) || {};
|
||||||
|
return categoryNodeMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function getValueFromConfig(configName, key) {
|
||||||
|
let config = await getConfig(configName);
|
||||||
|
return config[key] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assignValueToConfig(configName, key, value) {
|
||||||
|
let config = await getConfig(configName);
|
||||||
|
config[key] = value;
|
||||||
|
updateConfiguration(configName, JSON.stringify(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeKeyFromConfig(configName, key) {
|
||||||
|
let config = await getConfig(configName);
|
||||||
|
delete config[key];
|
||||||
|
updateConfiguration(configName, JSON.stringify(config));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
async function renameKeyConfig(configName, oldKey, newKey) {
|
||||||
|
let config = await getConfig(configName);
|
||||||
|
let value = config[oldKey];
|
||||||
|
delete config[oldKey];
|
||||||
|
config[newKey] = value;
|
||||||
|
updateConfiguration(configName, JSON.stringify(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeKeyConfig(configName) {
|
||||||
|
removeConfiguration(configName)
|
||||||
|
renderList("custom_categories_main");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
|
||||||
|
async function getCategoriesForNode(nodeName) {
|
||||||
|
let categoryNodeMap = await getNodeMap();
|
||||||
|
|
||||||
|
let categories = [];
|
||||||
|
for (let category in categoryNodeMap) {
|
||||||
|
if (categoryNodeMap.hasOwnProperty(category)) {
|
||||||
|
if (categoryNodeMap[category].includes(nodeName)) {
|
||||||
|
categories.push(category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNodesForCategory(categoryName) {
|
||||||
|
let categoryNodeMap = await getNodeMap();
|
||||||
|
|
||||||
|
return categoryNodeMap[categoryName] || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function assignNodeToCategory(nodeName, categoryNames) {
|
||||||
|
let categoryNodeMap = await getNodeMap();
|
||||||
|
|
||||||
|
let assignedCategories = await getCategoriesForNode(nodeName);
|
||||||
|
categoryNames = categoryNames.filter(category => !assignedCategories.includes(category));
|
||||||
|
|
||||||
|
categoryNames.forEach(categoryName => {
|
||||||
|
if (!categoryNodeMap[categoryName]) {
|
||||||
|
categoryNodeMap[categoryName] = [];
|
||||||
|
}
|
||||||
|
categoryNodeMap[categoryName].push(nodeName);
|
||||||
|
});
|
||||||
|
updateConfiguration('sb_categoryNodeMap', JSON.stringify(categoryNodeMap));
|
||||||
|
renderList("custom_categories_main");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function removeNodeFromCategory(nodeName, categoryName) {
|
||||||
|
let categoryNodeMap = await getNodeMap();
|
||||||
|
|
||||||
|
if (categoryNodeMap[categoryName]) {
|
||||||
|
categoryNodeMap[categoryName] = categoryNodeMap[categoryName].filter(node => node !== nodeName);
|
||||||
|
updateConfiguration('sb_categoryNodeMap', JSON.stringify(categoryNodeMap));
|
||||||
|
renderList("custom_categories_main");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function removeNodeMapCategory(categoryName) {
|
||||||
|
let categoryNodeMap = await getNodeMap();
|
||||||
|
let categoryNodeStatus = getNodesStatus();
|
||||||
|
delete categoryNodeMap[categoryName];
|
||||||
|
updateConfiguration('sb_categoryNodeMap', JSON.stringify(categoryNodeMap));
|
||||||
|
|
||||||
|
if (categoryNodeStatus[categoryName]) {
|
||||||
|
delete categoryNodeStatus[categoryName];
|
||||||
|
localStorage.setItem('sb_categoryNodeStatus', JSON.stringify(categoryNodeStatus));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function editNodeCategory(oldCategoryName, newCategoryName, nodeName) {
|
||||||
|
|
||||||
|
removeNodeFromCategory(nodeName, oldCategoryName);
|
||||||
|
|
||||||
|
assignNodeToCategory(nodeName, [newCategoryName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function reorderCategories(orderedKeys) {
|
||||||
|
let categoryNodeMap = await getNodeMap();
|
||||||
|
let reorderedCategoryNodeMap = {};
|
||||||
|
|
||||||
|
|
||||||
|
orderedKeys.forEach(categoryKey => {
|
||||||
|
if (categoryNodeMap.hasOwnProperty(categoryKey)) {
|
||||||
|
reorderedCategoryNodeMap[categoryKey] = categoryNodeMap[categoryKey];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateConfiguration('sb_categoryNodeMap', JSON.stringify(reorderedCategoryNodeMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function reorderNodeInCategory(categoryName, orderedKeys) {
|
||||||
|
let categoryNodeMap = await getNodeMap();
|
||||||
|
|
||||||
|
if (categoryNodeMap[categoryName]) {
|
||||||
|
categoryNodeMap[categoryName] = orderedKeys;
|
||||||
|
updateConfiguration('sb_categoryNodeMap', JSON.stringify(categoryNodeMap));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
|
function removeNodeFromCategoryCallback(e, trge) {
|
||||||
|
|
||||||
|
removeNodeFromCategory(e.target.dataset.type, e.target.parentElement.parentElement.dataset.namecategory)
|
||||||
|
}
|
||||||
|
|
||||||
|
function assignNodeToCategoryCallback(e, trge) {
|
||||||
|
assignNodeToCategory(e.target.id, [trge]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renameCategoryCallback(e, trge) {
|
||||||
|
const name = e.target.dataset.namecategory;
|
||||||
|
openModal(createCustomHtml("rename", name))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function colorCategoryCallback(e) {
|
||||||
|
|
||||||
|
const name = e.target.dataset.namecategory;
|
||||||
|
openModal(createCustomHtml("color", name, e.target))
|
||||||
|
//colorCategory(e.target.id, e.target.parentElement.parentElement.dataset.namecategory)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function colorCategory(name, value) {
|
||||||
|
|
||||||
|
|
||||||
|
const value_perc = await getConfiguration("sb_opacity") || "1.0";
|
||||||
|
const rgbColor = hexToRgb(value);
|
||||||
|
|
||||||
|
currentElement.style.backgroundColor = `rgba(${rgbColor}, ${value_perc})`;
|
||||||
|
|
||||||
|
|
||||||
|
assignValueToConfig("sb_ColorCustomCategories", name, value)
|
||||||
|
}
|
||||||
|
// Add an event listener to the button to open the modal
|
||||||
|
function resetColorCategory(name) {
|
||||||
|
|
||||||
|
removeKeyFromConfig("sb_ColorCustomCategories", name)
|
||||||
|
renderList("custom_categories_main");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function createContextualMenu() {
|
||||||
|
|
||||||
|
//const menuOptions = document.getElementById('menu-options');
|
||||||
|
new_menu_options = [];
|
||||||
|
new_submenu_options = [];
|
||||||
|
new_menu_options_callback = [];
|
||||||
|
new_submenu_options_callback = [];
|
||||||
|
new_menu_options.push("Add to category");
|
||||||
|
|
||||||
|
//for each category
|
||||||
|
category_menu = await readCategories();
|
||||||
|
callback_menu = [];
|
||||||
|
for (let i = 0; i < category_menu.length; i++) {
|
||||||
|
callback_menu.push("assignNodeToCategoryCallback");
|
||||||
|
|
||||||
|
}
|
||||||
|
new_submenu_options.push(category_menu);
|
||||||
|
new_submenu_options_callback.push(callback_menu);
|
||||||
|
|
||||||
|
|
||||||
|
console.log("custom category panel loaded")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function test_altert(e, trge) {
|
||||||
|
console.log(trge);
|
||||||
|
console.log(e.target.id);
|
||||||
|
console.log(e.target.parentElement.parentElement.dataset.namecategory);
|
||||||
|
|
||||||
|
assignNodeToCategory(e.target.id, [trge]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const searchCategoryIconCustomCategory = document.querySelector(".searchCategoryIconCustomCategory");
|
||||||
|
|
||||||
|
|
||||||
|
searchCategoryIconCustomCategory.addEventListener("click", async function () {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
categorySearchToggleCustom = !categorySearchToggleCustom;
|
||||||
|
searchCategoryIconCustomCategory.style.opacity = categorySearchToggleCustom ? "1" : "0.5";
|
||||||
|
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
console.error("Error occurred during search:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addSidebarStyles("panels/custom_categories/style.css");
|
||||||
|
createContextualMenu()
|
||||||
|
renderList("custom_categories_main");
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
#custom_categories_main,#custom_categories_header {
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#custom_categories_main{
|
||||||
|
|
||||||
|
padding-top: 44px;
|
||||||
|
}
|
||||||
|
#custom_categories_header {
|
||||||
|
margin: 10px 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
}
|
||||||
|
#custom_categories_header #h_search {
|
||||||
|
position: absolute;
|
||||||
|
width: calc(100% - 74px);
|
||||||
|
margin-top: 0px;
|
||||||
|
|
||||||
|
z-index: 400;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom_categories_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_categories_header button:hover {border: 2px solid var(--descrip-text);
|
||||||
|
|
||||||
|
}
|
||||||
|
.sb-category-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-category-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%);
|
||||||
|
|
||||||
|
}
|
||||||
|
#searchInputCn {
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "custom_view",
|
||||||
|
"icon": "⧉",
|
||||||
|
"description": "Custom Node Categories",
|
||||||
|
"panels": [
|
||||||
|
"custom_categories",
|
||||||
|
"custom_test"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<style>
|
||||||
|
#custom_categories {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom_categories button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="custom_categories" >
|
||||||
|
<button onclick="showAlert()">Cliccami</button>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
//console.log(LiteGraph.registered_node_types);
|
||||||
|
|
||||||
|
//const menuOptions = document.getElementById('menu-options');
|
||||||
|
|
||||||
|
new_menu_options.push("Add to category");
|
||||||
|
new_submenu_options.push(["Category 1", "Category 2"]);
|
||||||
|
|
||||||
|
console.log("custom loaded")
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 31 KiB |
-2016
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user