diff --git a/.gitignore b/.gitignore
index 7b59aa8..38931e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+# Config file
+app/settings.json
+
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
diff --git a/README.md b/README.md
index 28c0bec..c51b4be 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,18 @@ For what i know nobody did it, so i did it.
Maybe you don't need it. I need it >.<
# 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
- 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)
+### 🎨 Custom Categories 📂
+
+
+
+
### ➕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 🎨

-### 🎨 Custom Values From ComfyUI Settings 🎨
+### 🎨 New Settings Panel 🎨

### 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] Node preview (i don't think it will be an image)
- [x] Color integration with Jovimetrix
-- [ ] Better search
+- [x] Better search
+- [x] Custom Categories!!
+
## Contributing
@@ -98,7 +117,8 @@ 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 ❤️
-[](https://ko-fi.com/C0C0AJECJ)
+[](https://ko-fi.com/C0C0AJECJ)
+
## License
diff --git a/__init__.py b/__init__.py
index 0df60da..c547a65 100644
--- a/__init__.py
+++ b/__init__.py
@@ -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_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)
+
+
+
+@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')
-WEB_DIRECTORY = "./js"
\ No newline at end of file
+
+@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"
\ No newline at end of file
diff --git a/app/css/base_style.css b/app/css/base_style.css
new file mode 100644
index 0000000..d96f27c
--- /dev/null
+++ b/app/css/base_style.css
@@ -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;
+ }
\ No newline at end of file
diff --git a/app/html/sidebar.html b/app/html/sidebar.html
new file mode 100644
index 0000000..58876d9
--- /dev/null
+++ b/app/html/sidebar.html
@@ -0,0 +1,49 @@
+
\ No newline at end of file
diff --git a/app/html/test.html b/app/html/test.html
new file mode 100644
index 0000000..cae8e8e
--- /dev/null
+++ b/app/html/test.html
@@ -0,0 +1,152 @@
+
+
+
+
+
+Context Menu
+
+
+
+
+
+
+
+
+
+
diff --git a/app/img/home.svg b/app/img/home.svg
new file mode 100644
index 0000000..1b865ca
--- /dev/null
+++ b/app/img/home.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/js/fts_fuzzy_match.js b/app/js/fts_fuzzy_match.js
similarity index 100%
rename from js/fts_fuzzy_match.js
rename to app/js/fts_fuzzy_match.js
diff --git a/app/js/functions/sb_fn.js b/app/js/functions/sb_fn.js
new file mode 100644
index 0000000..4c6a75d
--- /dev/null
+++ b/app/js/functions/sb_fn.js
@@ -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 = ``;
+ document.querySelector(".content_sidebar").dataset.expanded = "false";
+
+ } else {
+
+ display_value = "block";
+ expand_node.innerHTML = ``;
+ 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 += `
+
+
${inputName}
+
+
${inputValue}
+
+
`;
+ }else{
+ last_rows += `
+
◀
+
${inputName}
+
+
${inputValue}
+
▶
+
`;
+ }
+ inputType = "sb_hidden";
+ inputName = "";
+ }
+
+
+ rows += `
+
+
${inputName}
+
+
${outputName}
+
+
`;
+
+
+
+
+
+ }
+ //console.log( inputArray,outputArray);
+
+ ////create preview
+ //const nodePreview = document.createElement("div");
+ //nodePreview.classList.add("node_preview");
+ //nodePreview.id = nodeID;
+
+ }catch(err){
+ rows = `
+
+
+
NOT AVAILABLE
+
+
+
`
+ last_rows = ``
+ }
+return [`
+
+
PREVIEW
+ ${rows}
+ ${last_rows}
+
`,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) ;
+}
\ No newline at end of file
diff --git a/app/js/functions/settings.js b/app/js/functions/settings.js
new file mode 100644
index 0000000..d93c3bf
--- /dev/null
+++ b/app/js/functions/settings.js
@@ -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();
+
+}
diff --git a/app/js/functions/utils.js b/app/js/functions/utils.js
new file mode 100644
index 0000000..542c283
--- /dev/null
+++ b/app/js/functions/utils.js
@@ -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 `
+ Add New Category
+
+
+ `;
+ }
+ 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();
+ }
+})
+
+
+
+
+
diff --git a/app/js/sidebar_node.js b/app/js/sidebar_node.js
new file mode 100644
index 0000000..a278a60
--- /dev/null
+++ b/app/js/sidebar_node.js
@@ -0,0 +1,1133 @@
+
+import { api } from "../../../../scripts/api.js"
+import { app } from "../../../../scripts/app.js";
+import { $el } from "../../../../scripts/ui.js";
+import {fuzzyMatch} from "./fts_fuzzy_match.js";
+
+//import {settingsSetup} from './functions/settings.jsb';
+console.time('execution time');
+const cnPath="../extensions/ComfyUI-N-Sidebar/"
+function jsloader(url) {
+ return new Promise((resolve, reject) => {
+ const script = document.createElement("script");
+ script.src = url;
+ script.type = "text/javascript";
+ script.onload = () => resolve(script);
+ script.onerror = () => reject(new Error(`Failed to load script: ${url}`));
+ document.head.appendChild(script);
+ });
+}
+
+
+
+
+jsloader(cnPath+"js/functions/sb_fn.js");
+jsloader(cnPath+"js/functions/utils.js");
+jsloader(cnPath+"js/functions/settings.js");
+
+
+
+
+let CUSTOM_COLORS;
+try {
+ const CONFIG_CORE = await api_get("/jovimetrix/config")
+ CUSTOM_COLORS = CONFIG_CORE?.user?.default?.color?.theme;
+} catch {}
+
+let categorySearchToggle = false;
+
+
+
+
+
+
+function getSidebarItemIds() {
+ const sidebarItems = document.querySelectorAll("#sidebarBookmarks .sidebarItem");
+ const itemIds = [];
+
+ sidebarItems.forEach(function(item) {
+ itemIds.push(item.id);
+ });
+
+ return itemIds;
+}
+function postPinned() {
+
+
+ var dragItem = null;
+
+ const pinnedElement= document.getElementById("sidebarBookmarks");
+ pinnedElement.querySelectorAll(".sidebarItem").forEach(function(item) {
+
+ item.addEventListener("dragstart", function(event) {
+ dragItem = event.target;
+ });
+
+ 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.id);
+ if (node_description) {
+ descriptionDiv = "" + node_description + "
";
+ }
+ 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';
+ })
+
+
+
+
+ reloadCtxMenu()
+
+
+
+
+ });
+
+ pinnedElement.addEventListener("dragover", function(event) {
+ event.preventDefault();
+ });
+
+ pinnedElement.addEventListener("drop", function(event) {
+ event.preventDefault();
+ if (dragItem) {
+ var dropZone = event.target.closest("ul");
+ dropZone.insertBefore(dragItem, event.target);
+ dragItem = null;
+ //list element in sidebarBookmarks
+
+ savePinnedItems(getSidebarItemIds());
+ }
+ });
+
+
+
+
+}
+
+
+
+function saveSidebarWidth(sideb) {
+ const width = sideb.style.width;
+ document.cookie = setVar("sidebarWidth", width);
+}
+
+
+function restoreSidebarWidth() {
+ let width_sidebar = "auto";
+ let cookieValue = getVar("sidebarWidth");
+ if (cookieValue) {
+ width_sidebar = cookieValue;
+ }
+ return width_sidebar;
+
+}
+
+
+async function pinItem(itemId) {
+
+ const item = document.getElementById(itemId);
+ const sidebarBookmarks = document.getElementById("sidebarBookmarks");
+ const pinnedItems = await loadPinnedItems();
+ if (!pinnedItems.includes(itemId)) {
+ let addedItem = sidebarBookmarks.appendChild(item.cloneNode(true));
+
+ addedItem.lastChild.lastChild.childNodes[1].classList.add("pinned");
+ pinnedItems.push(itemId);
+ savePinnedItems(pinnedItems);
+ postPinned();
+ }
+
+}
+
+
+
+async function unpinItem(itemId) {
+ const item = document.getElementById(itemId);
+ const sidebarBookmarks = document.getElementById("sidebarBookmarks");
+ const pinnedItems = await loadPinnedItems();
+ if (pinnedItems.includes(itemId)) {
+ sidebarBookmarks.removeChild(item);
+ pinnedItems.splice(pinnedItems.indexOf(itemId), 1);
+ savePinnedItems(pinnedItems);
+ }
+}
+
+
+function removePinnedClass(itemId) {
+
+ const sidebarItems = document.querySelectorAll(".sidebarItem");
+
+
+ sidebarItems.forEach(function (item) {
+
+ if (item.id === itemId) {
+
+ const path = item.querySelector("path");
+ if (path) {
+ path.classList.remove("pinned");
+ }
+ }
+ });
+}
+
+
+async function loadPinnedItemsAndAddToBookmarks() {
+ const pinnedItems = await loadPinnedItems();
+
+
+ const sidebarBookmarks = document.getElementById("sidebarBookmarks");
+
+
+ // delete pinned
+ const sidebarCategories = sidebarBookmarks.querySelectorAll(".sidebarItem");
+ sidebarCategories.forEach(category => { category.remove(); });
+
+
+ pinnedItems.forEach(itemId => {
+ pinItem(itemId);
+ const item = document.getElementById(itemId);
+
+
+ if (item) {
+ let addedItem = sidebarBookmarks.appendChild(item.cloneNode(true));
+ addedItem.lastChild.lastChild.classList.add("pinned");
+ }
+ });
+
+
+ postPinned();
+}
+
+
+
+
+
+function toggleCollapsePinned() {
+
+ // expand/collapse pinned
+ const sidebarBookmarks = document.getElementById('sidebarBookmarks');
+
+ if (sidebarBookmarks.style.display === 'none') {
+ sidebarBookmarks.style.display = 'block';
+ setVar('sb_pinned_collapsed', 'block');
+ } else {
+ sidebarBookmarks.style.display = 'none';
+ setVar('sb_pinned_collapsed', 'none');
+ }
+
+}
+
+
+function toggleCollapseCustom() {
+
+ if (sidebarCustomNodes.style.display === 'none') {
+ sidebarCustomNodes.style.display = 'block';
+ setVar('sb_custom_collapsed', 'block');
+ } else {
+ sidebarCustomNodes.style.display = 'none';
+ setVar('sb_custom_collapsed', 'none');
+ }
+
+}
+
+async function createCategoryList() {
+
+ const data = LiteGraph.registered_node_types;
+ let categories = {};
+ // const pinnedItems = loadPinnedItems();
+
+ //search init
+ //get class content_sidebar element
+ const categoriesList = document.getElementsByClassName("content_sidebar")[0];
+
+ const sidebar_main = document.getElementById('sidebar');
+ //const categoriesList = document.getElementById("content_sidebar");
+ const itemSearchInput = document.getElementById("searchInput");
+
+ itemSearchInput.addEventListener("input", async function () {
+ // defaultSearchToggle = await getConfiguration("sb_search_type")
+ if (defaultSearchToggle == "original" && itemSearchInput.value != "")
+ {
+ //search in all .sidebarItem
+ handleSearch(categorySearchToggle,"#content_sidebar_home","searchInput")
+
+
+ }
+ else{
+ updateList();
+ }
+ });
+
+
+ const clearIcon = document.querySelector(".clearIcon");
+ const searchCategoryIcon = document.querySelector(".searchCategoryIcon");
+ clearIcon.addEventListener("click", async function () {
+ try {
+
+ itemSearchInput.value = "";
+ const searchTerm = updateList();
+ } catch (error) {
+ console.error("Error occurred during search:", error);
+ }
+ });
+
+ searchCategoryIcon.addEventListener("click", async function () {
+ try {
+
+ categorySearchToggle = !categorySearchToggle;
+ searchCategoryIcon.style.opacity = categorySearchToggle ? "1" : "0.5";
+ const searchTerm = updateList();
+
+ } catch (error) {
+
+ console.error("Error occurred during search:", error);
+ }
+ });
+ //end search init
+
+
+ async function updateList() {
+
+
+ return new Promise(async (resolve, reject) => {
+ categories = {};
+
+ const itemSearchString = itemSearchInput.value.toLowerCase();
+
+ // delete all element conaining sidebarCategory class
+
+ const sidebarCategories = categoriesList.querySelectorAll(".sidebarCategory");
+ sidebarCategories.forEach(category => {
+ category.remove();
+ });
+
+ let shortedArray = {};
+ for (const objKey in data) {
+ try {
+ let searchable;
+ const category = data[objKey].category.toLowerCase();
+ if (!categorySearchToggle) {
+ searchable = data[objKey].title.toLowerCase();
+ } else{
+ searchable = category;
+ }
+
+
+ if (defaultSearchToggle == "normal") {
+ if (searchable.includes(itemSearchString) || itemSearchString === "") {
+ if (!categories[category]) {
+ categories[category] = [];
+ }
+ categories[category].push(data[objKey]);
+ //remove category if empty
+
+ // if (categories[category].length === 0) {
+ // delete categories[category];
+ // }
+
+ }
+ } else {
+ if (itemSearchString === "") {
+ if (!categories[category]) {
+ categories[category] = [];
+ }
+ categories[category].push(data[objKey]);
+
+ } else {
+
+
+
+ // fuzzy search for eng
+ const [accepted,rank] = fuzzyMatch(itemSearchString, reverseAndAppend(cleanText(searchable)));
+
+
+
+ if (accepted && rank >= 130) {
+
+ if (!shortedArray[rank]) {
+ shortedArray[rank] = [];
+ }
+ shortedArray[rank].push(data[objKey]);
+
+
+
+ }
+
+
+ }
+ }
+
+ } catch (err) {
+ console.log(err);
+ }
+ }
+ if(itemSearchString != ""){
+ if (defaultSearchToggle !== "normal") {
+
+ const ranks = Object.keys(shortedArray);
+ ranks.sort((a, b) => b - a);
+
+ ranks.forEach(rank => {
+
+ const dataArr = shortedArray[rank];
+ dataArr.forEach(item => {
+
+ if (!categories[item.category]) {
+ categories[item.category] = [];
+ }
+ categories[item.category].push(item);
+ });
+ });
+ }
+ } else {
+ defaultSearchOrder = await getConfiguration("sb_order_type")
+ if (defaultSearchOrder == "alphabetical") {
+
+ const sortedCategoriesData = {};
+ const sortedCategories = Object.keys(categories).sort();
+ sortedCategories.forEach(category => {
+
+ sortedCategoriesData[category] = categories[category];
+
+ });
+
+ categories = sortedCategoriesData;
+
+ }
+
+ }
+
+
+
+ renderList();
+
+ resolve(itemSearchString);
+
+});
+}
+
+
+ async function renderList() {
+
+ return new Promise(async (resolve, reject) => {
+ const pinnedItems = await loadPinnedItems();
+ //
+ const sidebarCustomNodes = document.getElementById("sidebarCustomNodes");
+ for (const category in categories) {
+ const categoryItem = document.createElement("li");
+ categoryItem.classList.add("sidebarCategory");
+ categoryItem.textContent = category;
+
+ const displayNamesList = document.createElement("ul");
+ displayNamesList.style.display = "none";
+ categoryItem.appendChild(displayNamesList);
+
+ categories[category].forEach(displayName => {
+ try {
+ const displayNameItem = document.createElement("li");
+ displayNameItem.classList.add("sidebarItem");
+ displayNameItem.textContent = displayName.title;
+ displayNameItem.title = displayName.title;
+ displayNameItem.draggable = true;
+
+ // JOVIMETRIX CUT-OUT FOR CUSTOM COLORED NODES
+ //
+ if (CUSTOM_COLORS) {
+
+ let color = CUSTOM_COLORS[displayName.title];
+ if (color === undefined) {
+ const segments = displayName.nodeData.category.split('/')
+ let k = segments.join('/')
+ while (k) {
+ color = CUSTOM_COLORS[k]
+ if (color) {
+ color = color.title
+ break
+ }
+ const last = k.lastIndexOf('/')
+ k = last !== -1 ? k.substring(0, last) : ''
+ }
+ } else {
+ color = color.title
+ }
+ if (color) {
+ displayNameItem.style = `background: ${color}`;
+ }
+ }
+ //
+
+ displayNameItem.id = displayName.type;
+
+ /* Create Pin Button */
+ const pinButton = document.createElement("button");
+ pinButton.classList.add("pinButton");
+
+ let add_class = "";
+
+ if (pinnedItems.includes(displayName.type)) {
+ add_class = "pinned";
+ }
+ pinButton.innerHTML = ``;
+
+
+ displayNameItem.appendChild(pinButton);
+ /* End Pin Button */
+
+
+ displayNamesList.appendChild(displayNameItem);
+ } catch (err) {
+ console.log(err);
+ }
+ });
+
+ categoryItem.addEventListener("click", function (event) {
+
+ if (event.target === event.currentTarget) {
+
+ displayNamesList.style.display = displayNamesList.style.display === "none" ? "block" : "none";
+ }
+ });
+
+
+
+ sidebarCustomNodes.appendChild(categoryItem);
+
+
+
+ }
+ if (!categorySearchToggle && itemSearchInput.value != "") {
+ sdExpandAll(1);
+
+ }
+
+ loadPinnedItemsAndAddToBookmarks();
+
+
+ /* preview */
+
+ const sidebarItems_cat = document.querySelectorAll('.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.id);
+ if (node_description) {
+ descriptionDiv = "" + node_description + "
";
+ }
+ 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';
+ })
+
+ });
+
+ categoriesList.addEventListener('scroll', function() {
+ previewDiv.style.display = 'none';
+ });
+
+ window.addEventListener('click', function(event) {
+
+ if (!event.target.classList.contains('sidebarItem')) {
+ previewDiv.style.display = 'none';
+
+ }
+ });
+
+
+ reloadCtxMenu()
+ resolve();
+ });
+ }
+ let udata = await updateList();
+
+
+
+
+
+
+ const dragHandle = document.getElementById("dragHandle");
+
+ let isDragging = false;
+ let startX;
+ let startWidth;
+
+
+ dragHandle.addEventListener("mousedown", function (event) {
+ isDragging = true;
+ startX = event.clientX;
+ startWidth = parseInt(window.getComputedStyle(sidebar_main).width);
+
+ });
+
+
+ document.addEventListener("mouseup", function () {
+
+ if (isDragging) {
+ isDragging = false;
+
+ saveSidebarWidth(sidebar_main);
+
+ }
+ });
+
+
+ document.addEventListener("mousemove", function (event) {
+ if (!isDragging) return;
+
+ const delta = event.clientX - startX;
+ if (sbPosition === "left") {
+ sidebar_main.style.width = (startWidth + delta) + "px";
+ } else {
+ sidebar_main.style.width = (startWidth - delta) + "px";
+ }
+ });
+
+
+
+
+
+
+
+}
+
+
+
+
+async function addSidebar() {
+
+ let draggedElementId;
+ const sidebar_width = restoreSidebarWidth();
+ const response = await fetch(cnPath + 'html/sidebar.html');
+ let html = await response.text();
+
+ html = html.replace(/{{sidebar_width}}/g, sidebar_width);
+ html = html.replace(/{{node_path}}/g, cnPath);
+ const canvas = $el("sidebar", {
+ parent: document.body,
+ innerHTML: html
+ });
+
+ document.getElementById("content_sidebar_home").addEventListener("click",async function (event) {
+ const clickedElement = event.target;
+ const pinnedItems = await loadPinnedItems();
+ const pinButton = clickedElement.tagName;
+
+
+ if (pinButton == "rect" && clickedElement.classList.contains("pin_node")) {
+ let itemId;
+ try {
+ itemId = clickedElement.parentElement.parentElement.parentElement.id;
+ } catch (err) {
+ console.log(err);
+ }
+
+ if (!pinnedItems.includes(itemId)) {
+
+ const sidebarItemId = itemId;
+ pinItem(sidebarItemId);
+ clickedElement.parentElement.getElementsByClassName("pin_normal")[0].classList.add("pinned");
+
+ }//remove from sidebar
+ else {
+ if (clickedElement.parentElement.parentElement.parentElement.parentElement.id == "sidebarBookmarks") {
+ clickedElement.parentElement.parentElement.parentElement.remove();
+
+ const itemIndex = pinnedItems.indexOf(itemId);
+
+
+ removePinnedClass(itemId);
+
+ if (itemIndex !== -1) {
+ pinnedItems.splice(itemIndex, 1);
+ savePinnedItems(pinnedItems);
+ }
+ } else {
+
+ unpinItem(itemId);
+ }
+
+ clickedElement.parentElement.getElementsByClassName("pin_normal")[0].classList.remove("pinned");
+ }
+
+
+ }
+ else if ((pinButton == "rect") && clickedElement.classList.contains("expand_node")) {
+
+ sdExpandAll();
+ }
+ else if ((pinButton == "LABEL") && clickedElement.id === "sb_label_custom") {
+
+
+ toggleCollapseCustom();
+
+ }
+
+
+
+
+ });
+
+
+ const sidebarItems = document.querySelectorAll(".sidebar li ul li");
+ sidebarItems.forEach(item => {
+ item.draggable = true;
+ });
+
+
+ function convertCanvasToOffset(canvas, pos, out) {
+ out = out || [0, 0];
+ out[0] = pos[0] / canvas.scale - canvas.offset[0];
+ out[1] = pos[1] / canvas.scale - canvas.offset[1];
+ return out;
+ };
+
+
+ function handleDrop(event) {
+ event.preventDefault();
+
+ if (event.srcElement.tagName.toLowerCase() != "canvas") {
+ return;
+ }
+ const coord = convertCanvasToOffset(app.canvasEl.data.ds, [event.clientX, event.clientY]);
+ const x = coord[0];
+ const y = coord[1];
+ sidebarAddNode(draggedElementId, draggedElementId, x, y);
+ draggedElementId = null;
+ }
+
+
+ function allowDrop(event) {
+ event.preventDefault();
+ }
+
+
+ // const search_bar = document.getElementById('searchInput');
+
+
+ canvas.addEventListener("dragstart", function (event) {
+
+ draggedElementId = event.target.id;
+ previewDiv.style.display = 'none';
+ });
+
+
+
+ document.getElementById('switch_sidebar').addEventListener('click', function () {
+ toggleSHSB();
+
+ });
+
+ document.addEventListener('drop', handleDrop);
+
+
+ canvas.addEventListener('dragover', allowDrop);
+
+
+ // expand/collapse pinned
+ const pinnedLabel = document.getElementById("sb_label_pinned");
+
+
+ pinnedLabel.addEventListener('click', function() {
+ toggleCollapsePinned();
+ });
+
+
+
+ createCategoryList();
+ SidebarPostBoot();
+}
+
+
+function toggleSHSB(force = undefined) {
+ const side_bars = document.querySelectorAll(".content_sidebar");
+ const main_sidebar = document.getElementById('sidebar');
+ const search_bar = document.getElementById('searchInput');
+ const scrollToTopButton = document.getElementById("sb_scrollToTopButton");
+ const clearIcon = document.querySelector(".clearIcon");
+ const searchCategoryIcon = document.querySelector(".searchCategoryIcon");
+ const switch_sidebar = document.getElementById('switch_sidebar');
+ //search_bar.classList.toggle('closed',force);
+
+ side_bars.forEach(side_bar => {
+ //side_bar.classList.toggle('closed',force);
+
+ if (force !== undefined) {
+ if (force) {
+ side_bar.classList.add('closed');
+ clearIcon.classList.add('closed');
+ searchCategoryIcon.classList.add('closed');
+ search_bar.classList.add('closed');
+ scrollToTopButton.classList.add('closed');
+ } else {
+ side_bar.classList.remove('closed');
+ clearIcon.classList.remove('closed');
+ searchCategoryIcon.classList.remove('closed');
+ search_bar.classList.remove('closed');
+ scrollToTopButton.classList.remove('closed');
+
+ }
+ } else {
+
+ if (side_bar.classList.contains('closed')) {
+ side_bar.classList.remove('closed');
+ clearIcon.classList.remove('closed');
+ searchCategoryIcon.classList.remove('closed');
+ search_bar.classList.remove('closed');
+ //fix for keyboard shortcuts
+ if (getVar("sb_minimized")=="true") {
+ switch_sidebar.style.filter = "brightness(0.8)";
+ }
+
+ } else {
+ if (getVar("sb_minimized")=="false") {
+ side_bar.classList.add('closed');
+ clearIcon.classList.add('closed');
+ searchCategoryIcon.classList.add('closed');
+ search_bar.classList.add('closed');
+ scrollToTopButton.classList.add('closed');
+ switch_sidebar.style.filter = "brightness(1.0)";
+ }else {
+ switch_sidebar.style.filter = "brightness(0.8)";
+ }
+ }
+ }
+
+ });
+
+
+ if (getVar("sb_minimized")=="false") {
+
+ if (force==undefined) {
+ setVar("sb_minimized", true);
+ }
+ main_sidebar.style.width = '45px';
+ } else {
+ if (force==undefined) {
+ setVar("sb_minimized", false);
+ }
+ main_sidebar.style.width = getVar("sidebarWidth") || '300px';
+ }
+
+
+
+
+}
+
+//option: disable auto-hide when minimized
+
+//Shortcuts
+
+function handleKeyPress(event) {
+
+ if (event.altKey && event.key.toLowerCase() === "x") {
+ //toggle sidebar if it's closed
+ const side_bar = document.getElementById('content_sidebar_home');
+ if (side_bar.classList.contains('closed')) {
+ toggleSHSB();
+
+ }
+ //focus on searchInput
+ searchInput.focus();
+
+
+ }
+ if (event.altKey && event.key.toLowerCase() === "z") {
+ toggleSHSB()
+ }
+}
+
+
+
+
+
+// Custom Node View
+function processViews(viewsData,settingsData) {
+
+ const sb =document.getElementById('sidebar');
+ const sbv = document.getElementById('sidebar_views');
+
+ const promises = viewsData.map(async (view) => {
+ const icon = view.icon;
+ if (!icon) {
+ return;
+ }
+ const description = view.description;
+
+ const div_view = document.createElement('div');
+ div_view.classList = "content_sidebar sb_hidden";
+ div_view.id = "panel_" + view.id;
+ div_view.dataset.content = view.id;
+ const bsb = document.getElementById('content_sidebar_home');
+ // add button to sidebarviews
+ bsb.insertAdjacentElement('afterend', div_view);
+
+ //console.log('Icon:', icon);
+ //console.log('Descr:', description);
+ //console.log('Panel:', view.panels);
+ //console.log('-------------------');
+
+ await Promise.all(view.panels.map(async (panel) => {
+ try {
+ const response = await fetch(cnPath + `panels/${panel}/${panel}.html`);
+ const html = await response.text();
+
+ if (html !== "404: Not Found") {
+ //inherit width from bsb
+ const div_panel = document.createElement('div');
+ div_panel.classList = "panel_sidebar";
+ div_panel.id = "panel_" + panel;
+
+ const div_panel_title = document.createElement('label');
+ div_panel_title.classList = "panel_sidebar_title sb_label";
+ div_panel_title.id = "panel_title_" + panel;
+
+ div_panel_title.innerHTML = panel.replace("_", " ");
+
+
+
+ div_panel.appendChild(div_panel_title);
+
+ const canvas = $el("div", {
+ parent: div_panel,
+ innerHTML: html,
+ });
+
+
+ div_view.insertAdjacentElement('beforeend', div_panel);
+ jsloader(`${cnPath}panels/${panel}/${panel}.jsb`);
+ console.log("LOADING " + cnPath + `panels/${panel}/${panel}.js`)
+ }
+ } catch (error) {
+ console.error('Errore nel caricamento del file HTML:', error);
+ }
+ }));
+
+
+
+ //sidebar menu
+ const div_button = document.createElement('div');
+
+
+ div_button.classList = "view_button";
+ div_button.id = "switch_" + view.id;
+ //inherit width from bsb
+
+ div_button.dataset.tab = view.id;
+ div_button.alt = view.description;
+ div_button.innerHTML += view.icon;
+
+ div_button.addEventListener('click', function () {
+ const tabId = "panel_" + this.getAttribute('data-tab');
+ switchTab(tabId)
+ });
+ sbv.appendChild(div_button);
+});
+return Promise.all(promises).then(() => {
+
+ setTimeout(() => {
+
+ settingsSetup(app,$el)
+ setContextMenu(settingsData,"#content_sidebar_home .sidebarItem",1);
+ switchTab(getVar('sb_current_tab'));
+ const sidebar_view = document.getElementById('sidebar_views');
+ const sidebar = document.getElementById('sidebar');
+ sidebar_view.addEventListener('mouseover', function() {
+ if (getVar('sb_minimized')=="true") {
+ toggleSHSB(false);
+
+ }
+
+ });
+ document.addEventListener('click', function(event) {
+ //if click is outside sidebar id element
+ if (!sidebar.contains(event.target)) {
+ if (getVar('sb_minimized')=="true") {
+ toggleSHSB(true);
+ }
+ }
+
+ });
+ }, 100);
+
+});
+
+
+
+}
+
+
+
+async function loadData() {
+
+ try {
+
+ const response1 = await fetch(cnPath + 'views/views.json');
+ const viewsData1 = await response1.json();
+ const response2 = await fetch(cnPath +'views/custom_views.json');
+ const viewsData2 = await response2.json();
+ const allViewsData = [...viewsData1, ...viewsData2];
+ const response3 = await fetch(cnPath +'settings.json');
+ const settingsData = await response3.json();
+ processViews(allViewsData,settingsData);
+ } catch (error) {
+ console.error('Error loading views:', error);
+ }
+
+}
+
+
+
+
+async function SidebarPostBoot() {
+ console.log('SidebarPostBoot');
+
+
+
+
+ const switch_home = document.getElementById("switch_home");
+
+ //only for home
+ switch_home.addEventListener('click', function() {
+ switchTab("content_sidebar_home",1)
+
+
+
+ });
+
+
+
+ if (getVar("sb_minimized")=="false") {
+ const switch_sidebar = document.getElementById('switch_sidebar');
+ switch_sidebar.style.filter = "brightness(0.8)";
+ }
+
+
+ document.addEventListener("keydown", handleKeyPress);
+ const sb =document.getElementById('content_sidebar_home');
+ const scrollToTopButton = document.getElementById("sb_scrollToTopButton");
+ scrollToTopButton.addEventListener("click", function() {
+ sb.scrollTo({
+ top: 0,
+ behavior: "smooth"
+ });
+ });
+
+
+
+ sb.addEventListener("scroll", function() {
+
+ if (sb.scrollTop > 0) {
+ scrollToTopButton.style.display = "block";
+ } else {
+ scrollToTopButton.style.display = "none";
+ }
+ });
+
+ const sidebarBookmarks = document.getElementById("sidebarBookmarks");
+ if (getVar("sb_pinned_collapsed")) {
+ sidebarBookmarks.style.display = getVar('sb_pinned_collapsed');
+ }
+
+ if (getVar("sb_custom_collapsed")) {
+ sidebarCustomNodes.style.display = getVar('sb_custom_collapsed');
+ }
+ loadData();
+ /*
+ const sidebar_views = document.getElementById("sidebar_views");
+ const sidebar = document.getElementById("sidebar");
+ //if is hover sidebar_views
+ sidebar_views.addEventListener("mouseover", function() {
+ toggleSHSB();
+ })
+ //if is not hover sidebar_views
+ sidebar.addEventListener("mouseout", function() {
+ toggleSHSB();
+ })
+ */
+}
+
+
+// Function to check if the element is not an empty object
+function SidebarBoot() {
+ if (Object.keys(LiteGraph.registered_node_types).length > 10) {
+ // Execute the function when the element is not an empty object
+ //
+ //MIGRATION
+migrationSettings()
+
+ addSidebarStyles("css/base_style.css");
+
+ addSidebar();
+
+ console.log('SidebarBoot');
+ console.timeEnd('execution time');
+
+
+
+ } else {
+ // Retry after a period of time
+ setTimeout(SidebarBoot, 500); // Check every 100 milliseconds
+ }
+}
+
+// Start checking the element
+SidebarBoot();
diff --git a/app/panels/custom_categories/custom_categories.html b/app/panels/custom_categories/custom_categories.html
new file mode 100644
index 0000000..423ea95
--- /dev/null
+++ b/app/panels/custom_categories/custom_categories.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/panels/custom_categories/custom_categories.jsb b/app/panels/custom_categories/custom_categories.jsb
new file mode 100644
index 0000000..c036250
--- /dev/null
+++ b/app/panels/custom_categories/custom_categories.jsb
@@ -0,0 +1,829 @@
+//console.log(LiteGraph.registered_node_types);
+
+let currentElement = null;
+
+function createCustomHtml(type, name, elem = null) {
+ if (type == "rename") {
+ return `Rename Category
+
+
+
+ `
+ } else if (type == "color") {
+ currentElement = elem;
+ return `Color ${name}
+
+
+
+
+
+
+
+ `
+ }
+}
+// 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 = ``;
+
+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 = ``;
+
+
+ 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 = "" + node_description + "
";
+ }
+ 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");
\ No newline at end of file
diff --git a/app/panels/custom_categories/style.css b/app/panels/custom_categories/style.css
new file mode 100644
index 0000000..707b769
--- /dev/null
+++ b/app/panels/custom_categories/style.css
@@ -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);
+}
\ No newline at end of file
diff --git a/app/views/custom_views.json b/app/views/custom_views.json
new file mode 100644
index 0000000..e24721c
--- /dev/null
+++ b/app/views/custom_views.json
@@ -0,0 +1,6 @@
+[
+ {
+
+ }
+
+]
diff --git a/app/views/views.json b/app/views/views.json
new file mode 100644
index 0000000..c73ef58
--- /dev/null
+++ b/app/views/views.json
@@ -0,0 +1,12 @@
+[
+ {
+ "id": "custom_view",
+ "icon": "⧉",
+ "description": "Custom Node Categories",
+ "panels": [
+ "custom_categories",
+ "custom_test"
+ ]
+ }
+
+]
diff --git a/custom_test/custom_test.html b/custom_test/custom_test.html
new file mode 100644
index 0000000..6095409
--- /dev/null
+++ b/custom_test/custom_test.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/custom_test/custom_test.js b/custom_test/custom_test.js
new file mode 100644
index 0000000..4c9b726
--- /dev/null
+++ b/custom_test/custom_test.js
@@ -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")
\ No newline at end of file
diff --git a/images/custom_categories.gif b/images/custom_categories.gif
new file mode 100644
index 0000000..08ba099
Binary files /dev/null and b/images/custom_categories.gif differ
diff --git a/images/patreon_badge.png b/images/patreon_badge.png
new file mode 100644
index 0000000..e826fee
Binary files /dev/null and b/images/patreon_badge.png differ
diff --git a/images/settings.png b/images/settings.png
index 7a79006..c3b1c38 100644
Binary files a/images/settings.png and b/images/settings.png differ
diff --git a/js/sidebar_node.js b/js/sidebar_node.js
deleted file mode 100644
index 431dfd4..0000000
--- a/js/sidebar_node.js
+++ /dev/null
@@ -1,2016 +0,0 @@
-
-import { api } from "../../../scripts/api.js"
-import { app } from "../../../scripts/app.js";
-import { $el } from "../../../scripts/ui.js";
-import {fuzzyMatch} from "./fts_fuzzy_match.js";
-
-console.time('execution time');
-
-
-
-async function api_get(url) {
- var response = await api.fetchApi(url, { cache: "no-store" })
- return await response.json()
-}
-
-let CUSTOM_COLORS;
-try {
- const CONFIG_CORE = await api_get("/jovimetrix/config")
- CUSTOM_COLORS = CONFIG_CORE?.user?.default?.color?.theme;
-} catch {}
-
-let categorySearchToggle = false;
-let defaultSearchToggle = false;
-
-
-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;
-}
-
-
-`
-
-
-function addSidebarStyles() {
-
-
- const sidebarStyle = `
- .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;
- padding-top: 10px;
- }
- .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);
- }
-
- .dragHandle {
- position: relative;
- float: left;
- right: 0;
- top: 0;
- height: 100%;
- width: 10px;
- 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% - 49px);
- margin-top: 5px;
- margin-bottom: 10px;
- margin-left: 10px;
-
- z-index: 400;
-
- }
- .clearIcon,.searchCategoryIcon{
- 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;
- }
-
-
- .clearIcon:hover{
- opacity: 1.0;
- }
-
- .searchCategoryIcon{
- 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{
- 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;
- }
-
- #switch_sidebar {
- position: relative;
- float: left;
- left: 0;
- margin-left: -0.6rem;
- cursor: pointer;
- user-select: none;
- padding: 2px;
- font-size: 20px;
- background-color: var(--comfy-menu-bg);
- border-bottom-right-radius: 5px;
- border-top-right-radius: 5px;
-
- }
- #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: 2px;
- right: 34px;
- 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);
- }
-
-
- .custom-menu {
- display: none;
- position: absolute;
- background-color: #fff;
- border: 1px solid #ccc;
- padding: 5px 0;
- list-style: none;
- z-index: 1000;
- }
-
- .custom-menu ul {
- margin: 0;
- padding: 0;
- }
-
- .custom-menu li {
- display: block;
- }
-
- .custom-menu li a {
- display: block;
- padding: 8px 12px;
- text-decoration: none;
- color: #333;
- }
-
- .custom-menu li a:hover {
- background-color: #f0f0f0;
- }
-
-
-
-
- `;
-
- const styleElement = $el("style", {
- parent: document.head,
- textContent: sidebarStyle
- });
-}
-
-/* Utils */
-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 getVar(name) {
- var varValue = localStorage.getItem(name);
- if (varValue === null) {
- const cookies = document.cookie.split(';');
- for (const cookie of cookies) {
- const [cookieName, cookieValue] = cookie.trim().split('=');
- if (cookieName === name) {
- /*migration to localstorage*/
- localStorage.setItem(name, cookieValue);
-
- return cookieValue;
-
- }
- }
- }else {
- return varValue;
- }
-
- return null;
-}
-
-function setVar(name, value, days) {
- /*migration to localstorage*/
- localStorage.setItem(name, value);
-
- let expires = '';
- if (days) {
- const date = new Date();
- date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
- expires = '; expires=' + date.toUTCString();
- }
- document.cookie = name + '=' + value + expires + '; path=/';
-}
-
-
-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;
-}
-/* End Utils */
-
-function getSidebarItemIds() {
- const sidebarItems = document.querySelectorAll("#sidebarBookmarks .sidebarItem");
- const itemIds = [];
-
- sidebarItems.forEach(function(item) {
- itemIds.push(item.id);
- });
-
- return itemIds;
-}
-function postPinned() {
-
-
- var dragItem = null;
- const categoriesList = document.getElementById("content_sidebar");
- const pinnedElement= document.getElementById("sidebarBookmarks");
- pinnedElement.querySelectorAll(".sidebarItem").forEach(function(item) {
-
- item.addEventListener("dragstart", function(event) {
- dragItem = event.target;
- });
-
- 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.id);
- if (node_description) {
- descriptionDiv = "" + node_description + "
";
- }
- 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';
- })
-
-
-
-
-
-
-
-
-
- });
-
- pinnedElement.addEventListener("dragover", function(event) {
- event.preventDefault();
- });
-
- pinnedElement.addEventListener("drop", function(event) {
- event.preventDefault();
- if (dragItem) {
- var dropZone = event.target.closest("ul");
- dropZone.insertBefore(dragItem, event.target);
- dragItem = null;
- //list element in sidebarBookmarks
-
- savePinnedItems(getSidebarItemIds());
- }
- });
-
-
-
-
-}
-
-
-
-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 saveSidebarWidth(sideb) {
- const width = sideb.style.width;
- document.cookie = setVar("sidebarWidth", width, 3000);
-}
-
-
-function restoreSidebarWidth() {
- let width_sidebar = "auto";
- let cookieValue = getVar("sidebarWidth");
- if (cookieValue) {
- width_sidebar = cookieValue;
- }
- return width_sidebar;
-
-}
-
-
-function savePinnedItems(pinnedItems) {
- const pinnedItemsString = JSON.stringify(pinnedItems);
- setVar('pinnedItems', pinnedItemsString, 9999);
-}
-
-
-function loadPinnedItems() {
- const pinnedItemsString = getVar('pinnedItems');
- if (pinnedItemsString) {
- return JSON.parse(pinnedItemsString);
- }
- return [];
-}
-
-
-function pinItem(itemId) {
-
- const item = document.getElementById(itemId);
- const sidebarBookmarks = document.getElementById("sidebarBookmarks");
- const pinnedItems = loadPinnedItems();
- if (!pinnedItems.includes(itemId)) {
- let addedItem = sidebarBookmarks.appendChild(item.cloneNode(true));
-
- addedItem.lastChild.lastChild.childNodes[1].classList.add("pinned");
- pinnedItems.push(itemId);
- savePinnedItems(pinnedItems);
- postPinned();
- }
-
-}
-
-
-
-function unpinItem(itemId) {
- const item = document.getElementById(itemId);
- const sidebarBookmarks = document.getElementById("sidebarBookmarks");
- const pinnedItems = loadPinnedItems();
- if (pinnedItems.includes(itemId)) {
- sidebarBookmarks.removeChild(item);
- pinnedItems.splice(pinnedItems.indexOf(itemId), 1);
- savePinnedItems(pinnedItems);
- }
-}
-
-
-function removePinnedClass(itemId) {
-
- const sidebarItems = document.querySelectorAll(".sidebarItem");
-
-
- sidebarItems.forEach(function (item) {
-
- if (item.id === itemId) {
-
- const path = item.querySelector("path");
- if (path) {
- path.classList.remove("pinned");
- }
- }
- });
-}
-
-
-function loadPinnedItemsAndAddToBookmarks() {
- const pinnedItems = loadPinnedItems();
-
-
- const sidebarBookmarks = document.getElementById("sidebarBookmarks");
-
-
- // delete pinned
- const sidebarCategories = sidebarBookmarks.querySelectorAll(".sidebarItem");
- sidebarCategories.forEach(category => { category.remove(); });
-
-
- pinnedItems.forEach(itemId => {
- pinItem(itemId);
- const item = document.getElementById(itemId);
-
-
- if (item) {
- let addedItem = sidebarBookmarks.appendChild(item.cloneNode(true));
- addedItem.lastChild.lastChild.classList.add("pinned");
- }
- });
-
-
- postPinned();
-}
-
-
-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 = ``;
- document.querySelector(".content_sidebar").dataset.expanded = "false";
-
- } else {
-
- display_value = "block";
- expand_node.innerHTML = ``;
- 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 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;
-
- const category = data[nodeID].category;
- 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 += `
-
-
${inputName}
-
-
${inputValue}
-
-
`;
- }else{
- last_rows += `
-
◀
-
${inputName}
-
-
${inputValue}
-
▶
-
`;
- }
- inputType = "sb_hidden";
- inputName = "";
- }
-
-
- rows += `
-
-
${inputName}
-
-
${outputName}
-
-
`;
-
-
-
-
-
- }
- //console.log( inputArray,outputArray);
-
- ////create preview
- //const nodePreview = document.createElement("div");
- //nodePreview.classList.add("node_preview");
- //nodePreview.id = nodeID;
-
- }catch(err){
- rows = `
-
-
-
NOT AVAILABLE
-
-
-
`
- last_rows = ``
- }
-return [`
-
-
PREVIEW
- ${rows}
- ${last_rows}
-
`,description];
-}
-function toggleCollapsePinned() {
-
- // expand/collapse pinned
- const sidebarBookmarks = document.getElementById('sidebarBookmarks');
-
- if (sidebarBookmarks.style.display === 'none') {
- sidebarBookmarks.style.display = 'block';
- setVar('sb_pinned_collapsed', 'block', 3000);
- } else {
- sidebarBookmarks.style.display = 'none';
- setVar('sb_pinned_collapsed', 'none', 3000);
- }
-
-}
-
-
-function toggleCollapseCustom() {
-
- if (sidebarCustomNodes.style.display === 'none') {
- sidebarCustomNodes.style.display = 'block';
- setVar('sb_custom_collapsed', 'block', 3000);
- } else {
- sidebarCustomNodes.style.display = 'none';
- setVar('sb_custom_collapsed', 'none', 3000);
- }
-
-}
-async function createCategoryList() {
- const data = LiteGraph.registered_node_types;
- let categories = {};
- const pinnedItems = loadPinnedItems();
- const categoriesList = document.getElementById("content_sidebar");
- const itemSearchInput = document.getElementById("searchInput");
-
- itemSearchInput.addEventListener("input", updateList);
-
- const clearIcon = document.querySelector(".clearIcon");
- const searchCategoryIcon = document.querySelector(".searchCategoryIcon");
- clearIcon.addEventListener("click", async function () {
- try {
-
- itemSearchInput.value = "";
- const searchTerm = updateList();
- } catch (error) {
- console.error("Error occurred during search:", error);
- }
- });
-
- searchCategoryIcon.addEventListener("click", async function () {
- try {
-
- categorySearchToggle = !categorySearchToggle;
- searchCategoryIcon.style.opacity = categorySearchToggle ? "1" : "0.5";
- const searchTerm = updateList();
-
- } catch (error) {
-
- console.error("Error occurred during search:", error);
- }
- });
-
-
- async function updateList() {
-
-
- return new Promise((resolve, reject) => {
- categories = {};
-
- const itemSearchString = itemSearchInput.value.toLowerCase();
-
- // delete all element conaining sidebarCategory class
- const sidebarCategories = categoriesList.querySelectorAll(".sidebarCategory");
- sidebarCategories.forEach(category => {
- category.remove();
- });
-
- let shortedArray = {};
- for (const objKey in data) {
- try {
- let searchable;
- const category = data[objKey].category.toLowerCase();
- if (!categorySearchToggle) {
- searchable = data[objKey].title.toLowerCase();
- } else{
- searchable = category;
- }
-
-
- if (defaultSearchToggle) {
- if (searchable.includes(itemSearchString) || itemSearchString === "") {
- if (!categories[category]) {
- categories[category] = [];
- }
- categories[category].push(data[objKey]);
- //remove category if empty
-
- // if (categories[category].length === 0) {
- // delete categories[category];
- // }
-
- }
- } else {
- if (itemSearchString === "") {
- if (!categories[category]) {
- categories[category] = [];
- }
- categories[category].push(data[objKey]);
-
- } else {
-
- const [accepted,rank] = fuzzyMatch(itemSearchString, reverseAndAppend(cleanText(searchable)));
-
-
-
- if (accepted && rank >= 130) {
-
- if (!shortedArray[rank]) {
- shortedArray[rank] = [];
- }
- shortedArray[rank].push(data[objKey]);
-
-
-
- }
-
- }
- }
-
- } catch (err) {
- console.log(err);
- }
- }
- if(itemSearchString != ""){
- if (!defaultSearchToggle) {
-
- const ranks = Object.keys(shortedArray);
- ranks.sort((a, b) => b - a);
-
- ranks.forEach(rank => {
-
- const dataArr = shortedArray[rank];
- dataArr.forEach(item => {
-
- if (!categories[item.category]) {
- categories[item.category] = [];
- }
- categories[item.category].push(item);
- });
- });
- }
- } else {
- // order categories alphabetically
- const sortedCategoriesData = {};
- const sortedCategories = Object.keys(categories).sort();
- sortedCategories.forEach(category => {
-
- sortedCategoriesData[category] = categories[category];
-
- });
-
- categories = sortedCategoriesData;
-
-
-
- }
-
-
-
- renderList();
-
- resolve(itemSearchString);
-
-});
-}
-
-
- async function renderList() {
-
- return new Promise((resolve, reject) => {
- const pinnedItems = loadPinnedItems();
- //
- const sidebarCustomNodes = document.getElementById("sidebarCustomNodes");
- for (const category in categories) {
- const categoryItem = document.createElement("li");
- categoryItem.classList.add("sidebarCategory");
- categoryItem.textContent = category;
-
- const displayNamesList = document.createElement("ul");
- displayNamesList.style.display = "none";
- categoryItem.appendChild(displayNamesList);
-
- categories[category].forEach(displayName => {
- try {
- const displayNameItem = document.createElement("li");
- displayNameItem.classList.add("sidebarItem");
- displayNameItem.textContent = displayName.title;
- displayNameItem.title = displayName.title;
- displayNameItem.draggable = true;
-
- // JOVIMETRIX CUT-OUT FOR CUSTOM COLORED NODES
- //
- if (CUSTOM_COLORS) {
-
- let color = CUSTOM_COLORS[displayName.title];
- if (color === undefined) {
- const segments = displayName.nodeData.category.split('/')
- let k = segments.join('/')
- while (k) {
- color = CUSTOM_COLORS[k]
- if (color) {
- color = color.title
- break
- }
- const last = k.lastIndexOf('/')
- k = last !== -1 ? k.substring(0, last) : ''
- }
- } else {
- color = color.title
- }
- if (color) {
- displayNameItem.style = `background: ${color}`;
- }
- }
- //
-
- displayNameItem.id = displayName.type;
-
- /* Create Pin Button */
- const pinButton = document.createElement("button");
- pinButton.classList.add("pinButton");
-
- let add_class = "";
-
- if (pinnedItems.includes(displayName.type)) {
- add_class = "pinned";
- }
- pinButton.innerHTML = ``;
-
-
- displayNameItem.appendChild(pinButton);
- /* End Pin Button */
-
-
- displayNamesList.appendChild(displayNameItem);
- } catch (err) {
- console.log(err);
- }
- });
-
- categoryItem.addEventListener("click", function (event) {
-
- if (event.target === event.currentTarget) {
-
- displayNamesList.style.display = displayNamesList.style.display === "none" ? "block" : "none";
- }
- });
-
-
-
- sidebarCustomNodes.appendChild(categoryItem);
-
-
-
- }
- if (!categorySearchToggle && itemSearchInput.value != "") {
- sdExpandAll(1);
-
- }
-
- loadPinnedItemsAndAddToBookmarks();
-
-
- /* preview */
-
- const sidebarItems_cat = document.querySelectorAll('.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.id);
- if (node_description) {
- descriptionDiv = "" + node_description + "
";
- }
- 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';
- })
-
- });
-
- categoriesList.addEventListener('scroll', function() {
- previewDiv.style.display = 'none';
- });
-
- window.addEventListener('click', function(event) {
-
- if (!event.target.classList.contains('sidebarItem')) {
- previewDiv.style.display = 'none';
-
- }
- });
-
-
-
- resolve();
- });
- }
- let udata = await updateList();
-
- const dragHandle = document.getElementById("dragHandle");
-
- let isDragging = false;
- let startX;
- let startWidth;
-
-
- dragHandle.addEventListener("mousedown", function (event) {
- isDragging = true;
- startX = event.clientX;
- startWidth = parseInt(window.getComputedStyle(categoriesList).width);
-
- });
-
-
- document.addEventListener("mouseup", function () {
-
- if (isDragging) {
- isDragging = false;
-
- saveSidebarWidth(categoriesList);
-
- }
- });
-
-
- document.addEventListener("mousemove", function (event) {
- if (!isDragging) return;
-
- const delta = event.clientX - startX;
- categoriesList.style.width = (startWidth + delta) + "px";
- });
-
-
-
-
-
-
-
-}
-
-
-
-
-
-function addSidebar() {
-
- let draggedElementId;
- const sidebar_width = restoreSidebarWidth();
- const sidebarHtml = `
-
- `;
-
- const canvas = $el("sidebar", {
- parent: document.body,
- innerHTML: sidebarHtml
- });
-
-
-
-
-
- document.getElementById("content_sidebar").addEventListener("click", function (event) {
- const clickedElement = event.target;
- const pinnedItems = loadPinnedItems();
- const pinButton = clickedElement.tagName;
-
-
- if (pinButton == "rect" && clickedElement.classList.contains("pin_node")) {
- let itemId;
- try {
- itemId = clickedElement.parentElement.parentElement.parentElement.id;
- } catch (err) {
- console.log(err);
- }
-
- if (!pinnedItems.includes(itemId)) {
-
- const sidebarItemId = itemId;
- pinItem(sidebarItemId);
- clickedElement.parentElement.getElementsByClassName("pin_normal")[0].classList.add("pinned");
-
- }//remove from sidebar
- else {
- if (clickedElement.parentElement.parentElement.parentElement.parentElement.id == "sidebarBookmarks") {
- clickedElement.parentElement.parentElement.parentElement.remove();
-
- const itemIndex = pinnedItems.indexOf(itemId);
-
-
- removePinnedClass(itemId);
-
- if (itemIndex !== -1) {
- pinnedItems.splice(itemIndex, 1);
- savePinnedItems(pinnedItems);
- }
- } else {
-
- unpinItem(itemId);
- }
-
- clickedElement.parentElement.getElementsByClassName("pin_normal")[0].classList.remove("pinned");
- }
-
-
- }
- else if ((pinButton == "rect") && clickedElement.classList.contains("expand_node")) {
-
- sdExpandAll();
- }
- else if ((pinButton == "LABEL") && clickedElement.id === "sb_label_custom") {
-
-
- toggleCollapseCustom();
-
- }
-
-
-
-
- });
-
-
- const sidebarItems = document.querySelectorAll(".sidebar li ul li");
- sidebarItems.forEach(item => {
- item.draggable = true;
- });
-
-
- function convertCanvasToOffset(canvas, pos, out) {
- out = out || [0, 0];
- out[0] = pos[0] / canvas.scale - canvas.offset[0];
- out[1] = pos[1] / canvas.scale - canvas.offset[1];
- return out;
- };
-
-
- function handleDrop(event) {
- event.preventDefault();
-
- if (event.srcElement.tagName.toLowerCase() != "canvas") {
- return;
- }
- const coord = convertCanvasToOffset(app.canvasEl.data.ds, [event.clientX, event.clientY]);
- const x = coord[0];
- const y = coord[1];
- sidebarAddNode(draggedElementId, draggedElementId, x, y);
- draggedElementId = null;
- }
-
-
- function allowDrop(event) {
- event.preventDefault();
- }
-
-
- // const search_bar = document.getElementById('searchInput');
-
-
- canvas.addEventListener("dragstart", function (event) {
-
- draggedElementId = event.target.id;
- previewDiv.style.display = 'none';
- });
-
-
-
- document.getElementById('switch_sidebar').addEventListener('click', function () {
- toggleSHSB();
-
- });
-
- document.addEventListener('drop', handleDrop);
-
-
- canvas.addEventListener('dragover', allowDrop);
-
-
- // expand/collapse pinned
- const pinnedLabel = document.getElementById("sb_label_pinned");
-
-
- pinnedLabel.addEventListener('click', function() {
- toggleCollapsePinned();
- });
-
-
-
-
-
-}
-
-
-
-
-function toggleSHSB() {
- const side_bar = document.getElementById('content_sidebar');
- const search_bar = document.getElementById('searchInput');
-
- const clearIcon = document.querySelector(".clearIcon");
- const searchCategoryIcon = document.querySelector(".searchCategoryIcon");
- search_bar.classList.toggle('closed');
- side_bar.classList.toggle('closed');
- clearIcon.classList.toggle('closed');
- searchCategoryIcon.classList.toggle('closed');
- if (side_bar.classList.contains('closed')) {
- setVar("sb_minimized", true, 3000);
- }else {
- setVar("sb_minimized", false, 3000);
- }
-
-}
-
-
-//Shortcuts
-
-function handleKeyPress(event) {
-
- if (event.altKey && event.key.toLowerCase() === "x") {
- //toggle sidebar if it's closed
- const side_bar = document.getElementById('content_sidebar');
- if (side_bar.classList.contains('closed')) {
- toggleSHSB();
- }
- //focus on searchInput
- searchInput.focus();
-
-
- }
- if (event.altKey && event.key.toLowerCase() === "z") {
- toggleSHSB()
- }
-}
-
-function setContextMenu() {
-const sb =document.getElementById('content_sidebar');
-
-//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";
-});
-}
-
-//Settings
-function settingsSetup() {
-
- app.ui.settings.addSetting({
- id: "0_sidebar_settings",
- 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) {
-
- }
- }
- },
- });
- app.ui.settings.addSetting({
- id: "0_sidebar_default_search",
- name: "[Sidebar] Use Default Search",
- type: "boolean",
- defaultValue: false,
- onChange(value) {
- defaultSearchToggle = value;
- },
- });
-
- app.ui.settings.addSetting({
- id: "sidebar_font_settings",
- name: "[Sidebar] Font Size",
- defaultValue: "13",
- type: "slider",
- attrs: {
- min: 5,
- max: 30,
- step: 1,
- },
- onChange(value) {
- addDynamicCSSRule('.sidebarCategory, #sidebarBookmarks', 'font-size', value+'px');
- },
- });
-
- app.ui.settings.addSetting({
- id: "sidebar_node_settings",
- name: "[Sidebar] Node Size",
- defaultValue: "10",
- type: "slider",
- attrs: {
- min: 2,
- max: 40,
- step: 1,
- },
- onChange(value) {
- addDynamicCSSRule('.sidebar li', 'padding', value+'px');
- },
- });
-
-
- app.ui.settings.addSetting({
- id: "sidebar_bartop",
- name: "[Sidebar] Space Top",
- defaultValue: "19",
- type: "slider",
- attrs: {
- min: 0,
- max: 300,
- step: 1,
- },
- onChange(value) {
- //addDynamicCSSRule('.sidebar', 'height', 'calc(100% - '+value+'px');
- addDynamicCSSRule('.sidebar', 'padding-top', value+'px');
- }
- });
-
- app.ui.settings.addSetting({
- id: "sidebar_barbottom",
- name: "[Sidebar] Space Bottom",
- defaultValue: "19",
- type: "slider",
- attrs: {
- min: 0,
- max: 300,
- step: 1,
- },
- onChange(value) {
- addDynamicCSSRule('.sidebar', 'height', 'calc(100% - '+value+'px');
-
- }
- });
- app.ui.settings.addSetting({
- id: "sidebar_noderadius_settings",
- name: "[Sidebar] 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');
-
- },
- });
-
-
- app.ui.settings.addSetting({
- id: "sidebar_blur_settings",
- name: "[Sidebar] Blur Intesity",
- defaultValue: "5",
- type: "slider",
- attrs: {
- min: 0,
- max: 30,
- step: 1,
- },
-
- onChange(value) {
- addDynamicCSSRule('.content_sidebar', 'backdrop-filter', 'blur('+value+'px)');
-
-
- },
- });
-
- app.ui.settings.addSetting({
- id: "sidebar_opacity_settings",
- name: "[Sidebar] Opacity",
- defaultValue: "1.0",
- type: "slider",
- 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('.sidebarCategory, #sidebarBookmarks', 'opacity', value);
-
-
- },
- });
-
-
-
-}
-
-
-function SidebarPostBoot() {
- addSidebarStyles();
- addSidebar();
-
- if (getVar("sb_minimized")=="true") {
- toggleSHSB();
- }
-
-
- document.addEventListener("keydown", handleKeyPress);
- const sb =document.getElementById('content_sidebar');
- const scrollToTopButton = document.getElementById("sb_scrollToTopButton");
- scrollToTopButton.addEventListener("click", function() {
- sb.scrollTo({
- top: 0,
- behavior: "smooth"
- });
- });
-
-
-
- sb.addEventListener("scroll", function() {
-
- if (sb.scrollTop > 0) {
- scrollToTopButton.style.display = "block";
- } else {
- scrollToTopButton.style.display = "none";
- }
- });
-
- const sidebarBookmarks = document.getElementById("sidebarBookmarks");
- if (getVar("sb_pinned_collapsed")) {
- sidebarBookmarks.style.display = getVar('sb_pinned_collapsed');
- }
-
- if (getVar("sb_custom_collapsed")) {
- sidebarCustomNodes.style.display = getVar('sb_custom_collapsed');
- }
-
-}
-
-
-// Function to check if the element is not an empty object
-function SidebarBoot() {
- if (Object.keys(LiteGraph.registered_node_types).length > 10) {
- // Execute the function when the element is not an empty object
- settingsSetup();
- createCategoryList();
- //setContextMenu();
- console.timeEnd('execution time');
- } else {
- // Retry after a period of time
- setTimeout(SidebarBoot, 500); // Check every 100 milliseconds
- }
-}
-
-// Start checking the element
-SidebarBoot();
-SidebarPostBoot();
\ No newline at end of file