├── images ├── dd.gif ├── pin.gif ├── header.png ├── search_nodes.gif ├── expand_collapse.gif ├── search_categories.gif └── YouTube.svg ├── __init__.py ├── LICENSE ├── README.md ├── .gitignore └── js └── sidebar_node.js /images/dd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZHO-ZHO-ZHO/ComfyUI-N-Sidebar-ZHO/HEAD/images/dd.gif -------------------------------------------------------------------------------- /images/pin.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZHO-ZHO-ZHO/ComfyUI-N-Sidebar-ZHO/HEAD/images/pin.gif -------------------------------------------------------------------------------- /images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZHO-ZHO-ZHO/ComfyUI-N-Sidebar-ZHO/HEAD/images/header.png -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | NODE_CLASS_MAPPINGS = {} 2 | NODE_DISPLAY_NAME_MAPPINGS = {} 3 | 4 | 5 | 6 | WEB_DIRECTORY = "./js" -------------------------------------------------------------------------------- /images/search_nodes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZHO-ZHO-ZHO/ComfyUI-N-Sidebar-ZHO/HEAD/images/search_nodes.gif -------------------------------------------------------------------------------- /images/expand_collapse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZHO-ZHO-ZHO/ComfyUI-N-Sidebar-ZHO/HEAD/images/expand_collapse.gif -------------------------------------------------------------------------------- /images/search_categories.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZHO-ZHO-ZHO/ComfyUI-N-Sidebar-ZHO/HEAD/images/search_categories.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 pythongosssss 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /images/YouTube.svg: -------------------------------------------------------------------------------- 1 | YOUTUBEYOUTUBE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # 我自己改了一版 ComfyPark 配色(我就喜欢 ALL BLACK ) 5 | 6 | 7 | 8 | ![Dingtalk_20240322020620](https://github.com/ZHO-ZHO-ZHO/ComfyUI-N-Sidebar-ZHO/assets/140084057/f2a71532-04f5-4098-a87f-768b25791f76) 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | # 以下是原作者内容: 20 | 21 | 22 | 23 | 24 | ![Drag and Drop Nodes](./images/header.png) 25 | 26 | 27 | 28 | [![YouTube](./images/YouTube.svg)](https://www.youtube.com/channel/UCnu819ZX2xiusPpbQ4KzSmA) 29 | 30 | # ComfyUI-N-Sidebar 31 | A simple sidebar for ComfyUI. 32 | For what i know nobody did it, so i did it. 33 | Maybe you don't need it. I need it >.< 34 | 35 | # Installation 36 | 37 | 1. Clone the repository: 38 | `git clone https://github.com/Nuked88/ComfyUI-N-Sidebar.git` 39 | to your ComfyUI `custom_nodes` directory 40 | 2. Enjoy! 41 | 42 | 43 | # Uninstall 44 | - Delete the `ComfyUI-N-Sidebar` folder in `custom_nodes` 45 | 46 | 47 | # Update 48 | 1. Navigate to the cloned repo e.g. `custom_nodes/ComfyUI-N-Sidebar` 49 | 2. `git pull` 50 | 51 | # Features 52 | 53 | 54 | ### 🖱️Drag and Drop Nodes🖱️ 55 | ![Drag and Drop Nodes](./images/dd.gif) 56 | 57 | 58 | ### 📌Pin Your Favorite Node📌 59 | ![Pin Your Favorite Node](./images/pin.gif) 60 | 61 | ### 🔍Search within your nodes📄 62 | ![Search within your nodes](./images/search_nodes.gif) 63 | 64 | ### 🔍Search within categories📂 65 | ![Search within categories](./images/search_categories.gif) 66 | 67 | ### ➕Expand/Collapse Categories/Sidebar➖ 68 | ![Expand/Collapse Categories and Sidebar](./images/expand_collapse.gif) 69 | 70 | ## Contributing 71 | 72 | Feel free to contribute to this project by reporting issues or suggesting improvements. Open an issue or submit a pull request on the GitHub repository. 73 | 74 | ## Donations 75 | 76 | If you'd like to support the project, consider making a donation ❤️ 77 |
78 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/C0C0AJECJ) 79 | 80 | ## License 81 | 82 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 83 | 84 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ -------------------------------------------------------------------------------- /js/sidebar_node.js: -------------------------------------------------------------------------------- 1 | import { $el } from "../../../scripts/ui.js"; 2 | 3 | let categorySearchToggle = false; 4 | function addSidebarStyles() { 5 | const sidebarStyle = ` 6 | .litegraph .dialog { 7 | z-index: 100 !important; 8 | } 9 | 10 | .sidebar { 11 | position: absolute; 12 | top: 0; 13 | left: -250px; 14 | width: fit-content; 15 | height: calc(100% - 19px); 16 | 17 | color: white; 18 | transition: left 0.3s ease; 19 | z-index: 2; 20 | overflow: hidden; 21 | 22 | padding-top: 19px; 23 | left: 0; 24 | user-select: none; 25 | 26 | } 27 | 28 | .sidebar ul { 29 | list-style-type: none; 30 | 31 | border-bottom: 2px solid #FF96AC; 32 | background: #000000; 33 | padding-left: 5px; 34 | padding-right: 5px; 35 | } 36 | .sidebar li { 37 | padding: 10px; 38 | cursor: pointer; 39 | user-select: none; 40 | } 41 | 42 | .content_sidebar { 43 | background-color: #000000; 44 | overflow-y: auto; 45 | overflow-x: hidden; 46 | height: 100%; 47 | float:left; 48 | backdrop-filter: blur(5px); 49 | } 50 | 51 | .dragHandle { 52 | position: relative; 53 | float: left; 54 | right: 0; 55 | top: 0; 56 | height: 100%; 57 | width: 10px; 58 | cursor: ew-resize; 59 | background: rgb(62,62,62); 60 | background: linear-gradient(90deg, rgb(62 62 62 / 46%) 0%, rgb(39 39 39 / 47%) 50%, rgb(28 28 28 / 31%) 100%); 61 | } 62 | #searchInput { 63 | box-sizing: border-box; 64 | width: 100%; 65 | border-radius: 5px; 66 | padding: 10px; 67 | border: none; 68 | user-select: none; 69 | background: #000000; 70 | color: #fff; 71 | 72 | } 73 | .sidebar-header { 74 | position: absolute; 75 | width: calc(100% - 49px); 76 | margin-top: 5px; 77 | margin-bottom: 10px; 78 | margin-left: 10px; 79 | 80 | z-index: 400; 81 | 82 | } 83 | .clearIcon,.searchCategoryIcon{ 84 | position: absolute; 85 | padding: 5px; 86 | right: 30px; 87 | color: #fff; 88 | font-size: x-large; 89 | cursor: pointer; 90 | opacity: 0.5; 91 | user-select: none; 92 | } 93 | 94 | 95 | .clearIcon:hover{ 96 | opacity: 1.0; 97 | } 98 | 99 | .searchCategoryIcon{ 100 | right: 0px; 101 | padding: 3px; 102 | font-size: larger; 103 | background: #000000; 104 | color: #fff; 105 | margin: 4px; 106 | border-radius: 5px; 107 | width: 18px; 108 | text-align: center; 109 | top: 0; 110 | 111 | 112 | } 113 | 114 | .searchCategoryIcon:hover{ 115 | opacity: 0.5; 116 | } 117 | 118 | .sidebarCategory, #sidebarBookmarks{ 119 | list-style-type: none; 120 | font-family: 'Open Sans',sans-serif; 121 | text-transform: capitalize; 122 | margin: 2px; 123 | background-color: #000000; 124 | border-radius: 9px; 125 | padding-top: 11px; 126 | font-size: 15px; 127 | } 128 | .sidebarCategory:hover{ 129 | background-color: #232323; 130 | } 131 | .sidebarItem:hover{ 132 | background-color: #3a3a3a; 133 | } 134 | .sidebarItem { 135 | list-style-type: none; 136 | font-family: 'Open Sans',sans-serif; 137 | text-transform: capitalize; 138 | margin: 2px; 139 | background: #000000; 140 | border-radius: 8px; 141 | 142 | white-space: nowrap; 143 | text-overflow: ellipsis; 144 | overflow: hidden; 145 | max-width: calc(100% - 39px); 146 | } 147 | 148 | 149 | .content_sidebar::-webkit-scrollbar { 150 | margin-top: 0.5rem; 151 | height: 1rem; 152 | width: .5rem; 153 | top: 10px; 154 | } 155 | 156 | .content_sidebar::-webkit-scrollbar:horizontal { 157 | height: .5rem; 158 | width: 1rem 159 | } 160 | 161 | .content_sidebar::-webkit-scrollbar-track { 162 | background-color: transparent; 163 | border-radius: 9999px 164 | } 165 | 166 | .content_sidebar::-webkit-scrollbar-thumb { 167 | --tw-border-opacity: 1; 168 | background-color: hsla(0,0%,50%,.8); 169 | border-color: rgba(255,255,255,0,0,0); 170 | border-radius: 9999px; 171 | border-width: 1px 172 | } 173 | 174 | .content_sidebar::-webkit-scrollbar-thumb:hover { 175 | --tw-bg-opacity: 1; 176 | background-color: rgba(150,150,150,var(--tw-bg-opacity)) 177 | } 178 | #spacer { 179 | height: 45px; 180 | } 181 | 182 | #switch_sidebar { 183 | position: relative; 184 | float: left; 185 | left: 0; 186 | margin-left: -0.6rem; 187 | cursor: pointer; 188 | user-select: none; 189 | padding: 2px; 190 | font-size: 20px; 191 | background-color: #000000; 192 | border-bottom-right-radius: 5px; 193 | border-top-right-radius: 5px; 194 | 195 | } 196 | #searchInput.closed { 197 | display: none; 198 | } 199 | #content_sidebar.closed { 200 | width: 0 !important; 201 | } 202 | .searchCategoryIcon.closed { 203 | display: none; 204 | } 205 | .clearIcon.closed { 206 | display: none; 207 | } 208 | 209 | .sidebarCategory .pinButton { 210 | background-color: transparent; 211 | border: 0; 212 | position: absolute; 213 | right: 0; 214 | } 215 | 216 | #sidebarBookmarks .pinButton { 217 | 218 | background-color: transparent; 219 | border: 0; 220 | 221 | position: absolute; 222 | right: 0; 223 | } 224 | .pinned{ 225 | fill: #FF96AC !important; 226 | opacity: 1 !important; 227 | } 228 | .svg_class{ 229 | width: 24px; 230 | height: 24px; 231 | fill: #353535; 232 | cursor: pointer; 233 | /* hacky fix */ 234 | margin-top: -5px; 235 | margin-left: -25px; 236 | } 237 | 238 | .pin_normal{ 239 | opacity: 0.5; 240 | } 241 | .pin_normal:hover{ 242 | opacity: 1; 243 | } 244 | .svg_class:hover{ 245 | fill: #4e4e4e; 246 | 247 | } 248 | 249 | 250 | #sidebarBookmarks .sidebarItem { 251 | margin-left: 10px; 252 | margin-right: 29px; 253 | 254 | } 255 | .sb_label { 256 | font-family: 'Open Sans',sans-serif; 257 | position: relative; 258 | margin: 5px; 259 | font-weight: bold; 260 | 261 | background: #000000; 262 | /* border: 2px solid; */ 263 | border-radius: 3px; 264 | padding-left: 6px; 265 | padding-right: 6px; 266 | display: block; 267 | width: calc(100% - 20px); 268 | text-align: center; 269 | user-select: none; 270 | } 271 | 272 | .expand_node, .pin_node{ 273 | position: absolute; 274 | right: 5px; 275 | background: transparent; 276 | border: 0; 277 | 278 | } 279 | .expand_node svg , .pin_node svg { 280 | 281 | width: 20px; 282 | height: 20px; 283 | background: transparent; 284 | fill: white; 285 | cursor: pointer; 286 | } 287 | 288 | 289 | 290 | 291 | `; 292 | 293 | const styleElement = $el("style", { 294 | parent: document.head, 295 | textContent: sidebarStyle 296 | }); 297 | } 298 | 299 | function getCookie(name) { 300 | const cookies = document.cookie.split(';'); 301 | for (const cookie of cookies) { 302 | const [cookieName, cookieValue] = cookie.trim().split('='); 303 | if (cookieName === name) { 304 | return cookieValue; 305 | } 306 | } 307 | return null; 308 | } 309 | 310 | function setCookie(name, value, days) { 311 | let expires = ''; 312 | if (days) { 313 | const date = new Date(); 314 | date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 315 | expires = '; expires=' + date.toUTCString(); 316 | } 317 | document.cookie = name + '=' + value + expires + '; path=/'; 318 | } 319 | 320 | function sidebarAddNode(name, text, x, y) { 321 | const node = LiteGraph.createNode(name, text) 322 | if (node) { 323 | const pos = 324 | [ 325 | x, 326 | y 327 | ] 328 | node.pos = pos; 329 | app.graph.add(node) 330 | } 331 | } 332 | 333 | function saveSidebarWidth(sideb) { 334 | const width = sideb.style.width; 335 | 336 | 337 | document.cookie = setCookie("sidebarWidth", width, 3000); 338 | } 339 | 340 | 341 | function restoreSidebarWidth() { 342 | let width_sidebar = "auto"; 343 | let cookieValue = getCookie("sidebarWidth"); 344 | if (cookieValue) { 345 | 346 | width_sidebar = cookieValue; 347 | } 348 | return width_sidebar; 349 | } 350 | 351 | 352 | function savePinnedItems(pinnedItems) { 353 | const pinnedItemsString = JSON.stringify(pinnedItems); 354 | setCookie('pinnedItems', pinnedItemsString, 9999); 355 | } 356 | 357 | 358 | function loadPinnedItems() { 359 | const pinnedItemsString = getCookie('pinnedItems'); 360 | if (pinnedItemsString) { 361 | return JSON.parse(pinnedItemsString); 362 | } 363 | return []; 364 | } 365 | 366 | 367 | function pinItem(itemId) { 368 | 369 | const item = document.getElementById(itemId); 370 | const sidebarBookmarks = document.getElementById("sidebarBookmarks"); 371 | const pinnedItems = loadPinnedItems(); 372 | if (!pinnedItems.includes(itemId)) { 373 | let addedItem = sidebarBookmarks.appendChild(item.cloneNode(true)); 374 | 375 | addedItem.lastChild.lastChild.childNodes[1].classList.add("pinned"); 376 | pinnedItems.push(itemId); 377 | savePinnedItems(pinnedItems); 378 | } 379 | 380 | } 381 | 382 | 383 | 384 | function unpinItem(itemId) { 385 | const item = document.getElementById(itemId); 386 | const sidebarBookmarks = document.getElementById("sidebarBookmarks"); 387 | const pinnedItems = loadPinnedItems(); 388 | if (pinnedItems.includes(itemId)) { 389 | sidebarBookmarks.removeChild(item); 390 | pinnedItems.splice(pinnedItems.indexOf(itemId), 1); 391 | savePinnedItems(pinnedItems); 392 | } 393 | } 394 | 395 | 396 | function removePinnedClass(itemId) { 397 | 398 | const sidebarItems = document.querySelectorAll(".sidebarItem"); 399 | 400 | 401 | sidebarItems.forEach(function (item) { 402 | 403 | if (item.id === itemId) { 404 | 405 | const path = item.querySelector("path"); 406 | if (path) { 407 | path.classList.remove("pinned"); 408 | } 409 | } 410 | }); 411 | } 412 | 413 | 414 | function loadPinnedItemsAndAddToBookmarks() { 415 | const pinnedItems = loadPinnedItems(); 416 | 417 | 418 | const sidebarBookmarks = document.getElementById("sidebarBookmarks"); 419 | pinnedItems.forEach(itemId => { 420 | pinItem(itemId); 421 | const item = document.getElementById(itemId); 422 | if (item) { 423 | let addedItem = sidebarBookmarks.appendChild(item.cloneNode(true)); 424 | 425 | addedItem.lastChild.lastChild.classList.add("pinned"); 426 | 427 | } 428 | }); 429 | } 430 | 431 | function sdExpandAll() { 432 | 433 | const categoryItems = document.querySelectorAll(".content_sidebar .sidebarCategory"); 434 | const side_bar_status = document.querySelector(".content_sidebar").dataset.expanded; 435 | const expand_node = document.getElementsByClassName("expand_node")[0]; 436 | 437 | let display_value = "true"; 438 | 439 | if (side_bar_status === "true") { 440 | 441 | display_value = "none"; 442 | expand_node.innerHTML = ` 444 | 446 | 448 | 451 | 452 | `; 453 | document.querySelector(".content_sidebar").dataset.expanded = "false"; 454 | 455 | } else { 456 | 457 | display_value = "block"; 458 | expand_node.innerHTML = ` 459 | 460 | 462 | 464 | 465 | 466 | `; 467 | document.querySelector(".content_sidebar").dataset.expanded = "true"; 468 | 469 | } 470 | 471 | 472 | categoryItems.forEach(function (categoryItem) { 473 | const displayNamesList = categoryItem.querySelector("ul"); 474 | 475 | if (expand_node) { 476 | 477 | 478 | displayNamesList.style.display = display_value; 479 | } 480 | 481 | }); 482 | } 483 | 484 | 485 | 486 | function createCategoryList() { 487 | const data = LiteGraph.registered_node_types; 488 | const categories = {}; 489 | const pinnedItems = loadPinnedItems(); 490 | for (const objKey in data) { 491 | const category = data[objKey].category; 492 | if (!categories[category]) { 493 | categories[category] = []; 494 | } 495 | categories[category].push(data[objKey]); 496 | } 497 | 498 | 499 | const sortedCategories = Object.keys(categories).sort(); 500 | 501 | 502 | const sortedCategoriesData = {}; 503 | sortedCategories.forEach(category => { 504 | sortedCategoriesData[category] = categories[category]; 505 | }); 506 | 507 | const categoriesList = document.getElementById("content_sidebar"); 508 | 509 | for (const category in sortedCategoriesData) { 510 | const categoryItem = document.createElement("li"); 511 | categoryItem.classList.add("sidebarCategory"); 512 | categoryItem.textContent = category; 513 | 514 | const displayNamesList = document.createElement("ul"); 515 | displayNamesList.style.display = "none"; 516 | categoryItem.appendChild(displayNamesList); 517 | 518 | categories[category].forEach(displayName => { 519 | try { 520 | const displayNameItem = document.createElement("li"); 521 | displayNameItem.classList.add("sidebarItem"); 522 | displayNameItem.textContent = displayName.title; 523 | displayNameItem.title = displayName.title; 524 | displayNameItem.draggable = true; 525 | 526 | displayNameItem.id = displayName.type; 527 | 528 | 529 | const pinButton = document.createElement("button"); 530 | pinButton.classList.add("pinButton"); 531 | 532 | let add_class = ""; 533 | 534 | if (pinnedItems.includes(displayName.type)) { 535 | add_class = "pinned"; 536 | } 537 | pinButton.innerHTML = ` 538 | 539 | 540 | `; 541 | 542 | 543 | displayNameItem.appendChild(pinButton); 544 | 545 | displayNamesList.appendChild(displayNameItem); 546 | } catch (err) { 547 | 548 | } 549 | }); 550 | 551 | categoryItem.addEventListener("click", function (event) { 552 | 553 | if (event.target === event.currentTarget) { 554 | 555 | displayNamesList.style.display = displayNamesList.style.display === "none" ? "block" : "none"; 556 | } 557 | }); 558 | 559 | 560 | categoriesList.appendChild(categoryItem); 561 | } 562 | 563 | 564 | const dragHandle = document.getElementById("dragHandle"); 565 | 566 | let isDragging = false; 567 | let startX; 568 | let startWidth; 569 | 570 | 571 | dragHandle.addEventListener("mousedown", function (event) { 572 | isDragging = true; 573 | startX = event.clientX; 574 | startWidth = parseInt(window.getComputedStyle(categoriesList).width); 575 | 576 | }); 577 | 578 | 579 | document.addEventListener("mouseup", function () { 580 | 581 | if (isDragging) { 582 | isDragging = false; 583 | 584 | saveSidebarWidth(categoriesList); 585 | 586 | } 587 | }); 588 | 589 | 590 | document.addEventListener("mousemove", function (event) { 591 | if (!isDragging) return; 592 | 593 | const delta = event.clientX - startX; 594 | categoriesList.style.width = (startWidth + delta) + "px"; 595 | }); 596 | 597 | loadPinnedItemsAndAddToBookmarks(); 598 | } 599 | 600 | function addSidebar() { 601 | 602 | let draggedElementId; 603 | const sidebar_width = restoreSidebarWidth(); 604 | const sidebarHtml = ` 605 | 634 | 635 | `; 636 | 637 | const sidebarElement = $el("sidebar", { 638 | parent: document.body, 639 | innerHTML: sidebarHtml 640 | }); 641 | 642 | 643 | 644 | const clearIcon = document.querySelector(".clearIcon"); 645 | const searchCategoryIcon = document.querySelector(".searchCategoryIcon"); 646 | 647 | 648 | const searchInput = document.getElementById("searchInput"); 649 | 650 | clearIcon.addEventListener("click", async function () { 651 | try { 652 | 653 | searchInput.value = ""; 654 | 655 | 656 | const searchTerm = await handleSearch(); 657 | 658 | 659 | console.log("Search term cleared:", searchTerm); 660 | 661 | 662 | } catch (error) { 663 | 664 | console.error("Error occurred during search:", error); 665 | } 666 | }); 667 | 668 | searchCategoryIcon.addEventListener("click", async function () { 669 | try { 670 | 671 | categorySearchToggle = !categorySearchToggle; 672 | 673 | 674 | searchCategoryIcon.style.opacity = categorySearchToggle ? "1" : "0.5"; 675 | 676 | 677 | const searchTerm = await handleSearch(); 678 | 679 | 680 | console.log("Search term:", searchTerm); 681 | 682 | 683 | } catch (error) { 684 | 685 | console.error("Error occurred during search:", error); 686 | } 687 | }); 688 | 689 | 690 | 691 | document.getElementById("content_sidebar").addEventListener("click", function (event) { 692 | const clickedElement = event.target; 693 | const pinnedItems = loadPinnedItems(); 694 | 695 | const pinButton = clickedElement.tagName; 696 | 697 | 698 | 699 | if (pinButton == "rect" && clickedElement.classList.contains("pin_node")) { 700 | let itemId; 701 | try { 702 | itemId = clickedElement.parentElement.parentElement.parentElement.id; 703 | } catch (err) { 704 | 705 | } 706 | 707 | if (!pinnedItems.includes(itemId)) { 708 | 709 | const sidebarItemId = itemId; 710 | 711 | 712 | pinItem(sidebarItemId); 713 | clickedElement.parentElement.getElementsByClassName("pin_normal")[0].classList.add("pinned"); 714 | 715 | }//remove from sidebar 716 | else { 717 | if (clickedElement.parentElement.parentElement.parentElement.parentElement.id == "sidebarBookmarks") { 718 | clickedElement.parentElement.parentElement.parentElement.remove(); 719 | 720 | const itemIndex = pinnedItems.indexOf(itemId); 721 | 722 | 723 | removePinnedClass(itemId); 724 | 725 | if (itemIndex !== -1) { 726 | pinnedItems.splice(itemIndex, 1); 727 | savePinnedItems(pinnedItems); 728 | } 729 | } else { 730 | 731 | unpinItem(itemId); 732 | } 733 | 734 | clickedElement.parentElement.getElementsByClassName("pin_normal")[0].classList.remove("pinned"); 735 | } 736 | 737 | 738 | } 739 | else if ((pinButton == "rect") && clickedElement.classList.contains("expand_node")) { 740 | 741 | sdExpandAll(); 742 | } 743 | 744 | }); 745 | 746 | 747 | const sidebarItems = document.querySelectorAll(".sidebar li ul li"); 748 | sidebarItems.forEach(item => { 749 | item.draggable = true; 750 | }); 751 | 752 | setTimeout(() => { 753 | createCategoryList(); 754 | 755 | }, 2000); 756 | 757 | 758 | function convertCanvasToOffset(canvas, pos, out) { 759 | out = out || [0, 0]; 760 | out[0] = pos[0] / canvas.scale - canvas.offset[0]; 761 | out[1] = pos[1] / canvas.scale - canvas.offset[1]; 762 | return out; 763 | }; 764 | 765 | 766 | function handleDrop(event) { 767 | event.preventDefault(); 768 | 769 | const coord = convertCanvasToOffset(app.canvasEl.data.ds, [event.clientX, event.clientY]); 770 | const x = coord[0]; 771 | const y = coord[1]; 772 | 773 | 774 | sidebarAddNode(draggedElementId, draggedElementId, x, y); 775 | const draggedElementId_string = draggedElementId; 776 | 777 | 778 | draggedElementId = null; 779 | } 780 | 781 | 782 | function allowDrop(event) { 783 | event.preventDefault(); 784 | } 785 | 786 | 787 | const canvas = sidebarElement; 788 | 789 | 790 | 791 | canvas.addEventListener("dragstart", function (event) { 792 | 793 | draggedElementId = event.target.id; 794 | }); 795 | 796 | 797 | 798 | function handleSearch(event) { 799 | return new Promise((resolve, reject) => { 800 | const searchTerm = document.getElementById("searchInput").value.toLowerCase(); 801 | const categoryItems = document.querySelectorAll(".sidebarCategory li"); 802 | const categories = document.querySelectorAll(".sidebarCategory"); 803 | const listItems = document.querySelectorAll(".sidebar li"); 804 | 805 | categoryItems.forEach(category => { 806 | const subItems = category.querySelectorAll("li"); 807 | category.style.display = "block"; 808 | 809 | subItems.forEach(sub => { 810 | sub.style.display = "block"; 811 | }); 812 | 813 | }); 814 | 815 | 816 | const sidebarItems = categorySearchToggle ? categories : listItems; 817 | 818 | 819 | sidebarItems.forEach(item => { 820 | 821 | let itemText = item.textContent.toLowerCase(); 822 | if (categorySearchToggle) { 823 | 824 | itemText = Array.from(item.childNodes) 825 | .filter(node => node.nodeType === Node.TEXT_NODE) 826 | .map(node => node.textContent.trim()) 827 | .join(' ').toLowerCase(); 828 | } 829 | 830 | const isInSearchTerm = itemText.includes(searchTerm); 831 | 832 | 833 | 834 | item.style.display = isInSearchTerm ? "block" : "none"; 835 | }); 836 | 837 | 838 | categories.forEach(category => { 839 | const subItems = category.querySelectorAll("li"); 840 | 841 | 842 | 843 | 844 | const areAllHidden = Array.from(subItems).every(subItem => subItem.style.display === "none"); 845 | category.style.display = areAllHidden ? "none" : category.style.display; 846 | }); 847 | 848 | 849 | resolve(searchTerm); 850 | }); 851 | } 852 | 853 | const search_bar = document.getElementById('searchInput'); 854 | search_bar.addEventListener("input", async (event) => { 855 | try { 856 | 857 | const searchTerm = await handleSearch(event); 858 | 859 | 860 | console.log("Search term:", searchTerm); 861 | 862 | 863 | } catch (error) { 864 | 865 | console.error("Error occurred during search:", error); 866 | } 867 | }); 868 | 869 | 870 | document.getElementById('switch_sidebar').addEventListener('click', function () { 871 | const side_bar = document.getElementById('content_sidebar'); 872 | 873 | search_bar.classList.toggle('closed'); 874 | side_bar.classList.toggle('closed'); 875 | clearIcon.classList.toggle('closed'); 876 | searchCategoryIcon.classList.toggle('closed'); 877 | 878 | }); 879 | 880 | document.addEventListener('drop', handleDrop); 881 | 882 | 883 | canvas.addEventListener('dragover', allowDrop); 884 | 885 | 886 | } 887 | 888 | 889 | 890 | addSidebarStyles(); 891 | addSidebar(); 892 | 893 | 894 | /* 895 | function handleKeyPress(event) { 896 | if (event.key === "x") { } 897 | } 898 | document.addEventListener("keydown", handleKeyPress); 899 | */ 900 | --------------------------------------------------------------------------------