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 ![Fuzzy Search](./images/fuzzysearch.gif) 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 📂 +![Custom Categories](./images/custom_categories.gif) + + + ### ➕Expand/Collapse Categories/Sidebar➖ ![Expand/Collapse Categories and Sidebar](./images/expand_collapse.gif) @@ -76,7 +93,7 @@ This feature is enabled by default, you can disable it in the settings. I've use ### 🎨 ComfyUI Themes Support 🎨 ![Themes Support ](./images/theme.gif) -### 🎨 Custom Values From ComfyUI Settings 🎨 +### 🎨 New Settings Panel 🎨 ![ComfyUI Settings](./images/settings.png) ### 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 ❤️
-[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/C0C0AJECJ) +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/C0C0AJECJ) +Support me on Patreon ## 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 [`
+
${nodeID}
+
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 [`
-
${nodeID}
-
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