├── .DS_Store
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ └── publish.yml
├── .gitignore
├── LICENSE
├── README.md
├── __init__.py
├── app
├── .DS_Store
├── css
│ └── base_style_sb.css
├── html
│ └── sidebar.html
├── img
│ └── home.svg
├── js
│ ├── fts_fuzzy_match.js
│ ├── functions
│ │ ├── sb_fn.js
│ │ ├── settings.js
│ │ └── utils.js
│ └── sidebar_node.js
├── panels
│ ├── .DS_Store
│ ├── assets_downloader
│ │ ├── assets_downloader.html
│ │ ├── assets_downloader.jsb
│ │ └── style.css
│ ├── custom_categories
│ │ ├── custom_categories.html
│ │ ├── custom_categories.jsb
│ │ └── style.css
│ ├── custom_templates
│ │ ├── custom_templates.html
│ │ ├── custom_templates.jsb
│ │ └── style.css
│ ├── custom_workflows
│ │ ├── custom_workflows.html
│ │ ├── custom_workflows.jsb
│ │ └── style.css
│ └── terminal
│ │ ├── style.css
│ │ ├── terminal.html
│ │ └── terminal.jsb
└── views
│ ├── custom_views.json
│ ├── views.json
│ └── views_default.json
├── images
├── .DS_Store
├── YouTube.svg
├── assets.gif
├── custom_categories.gif
├── dd.gif
├── expand_collapse.gif
├── fuzzysearch.gif
├── header.png
├── patreon_badge.png
├── pin.gif
├── pin_reorder.gif
├── preview.gif
├── search_categories.gif
├── search_nodes.gif
├── settings.png
├── templates.gif
├── theme.gif
└── workflows.gif
├── pyproject.toml
└── requirements.txt
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/.DS_Store
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Prerequisites**
11 |
12 | Before submitting a bug report, please ensure you have followed these steps:
13 |
14 | 1. **Browser Verification:**
15 | - Check if the issue occurs in a different browser.
16 | - Alternatively, try to reproduce the issue in incognito mode (this helps rule out cache-related issues).
17 | - If the issue is related to cache, clear the cache for the ComfyUI address (usually 127.0.0.1:8188 if it's running in local) in your browser settings or try with force Reload: Ctrl + F5 on Windows or Cmd + Shift + R on macOS
18 |
19 | 2. **Sidebar Test:**
20 | - Remove additional nodes by doing the following:
21 | - Navigate to the `custom_nodes` folder.
22 | - Cut all nodes and paste them into a temporary folder outside of `custom_nodes`, except for `ComfyUI-N-Sidebar` and, if present, `ComfyUI-Manager`**.
23 | - Launch ComfyUI and test if the problem persists.
24 |
25 | ** you can restore the original situation by moving back the nodes from the temporary folder to the `custom_nodes` folder and restarting ComfyUI.
26 |
27 | 3. **Identify Node Conflicts:**
28 | - If the problem is resolved, it is likely caused by a conflict with one of the removed nodes.
29 | - Try to identify the conflicting node: maybe it was added recently if the problem did not exist before.
30 | - If you find the conflicting node, please open an issue to inform me so I can attempt to resolve the problem (if possible).
31 |
32 | **Bug Report**
33 |
34 | If the problem persists after following the above steps, please provide the following information:
35 |
36 | - **Description:**
37 | - Provide a brief description of the bug.
38 |
39 | - **Steps to Reproduce:**
40 | - List the steps you took to encounter the issue.
41 |
42 | - **Expected Behavior:**
43 | - Describe what you expected to happen.
44 |
45 | - **Actual Behavior:**
46 | - Describe what actually happened.
47 |
48 | - **System Information:**
49 | - **Operating System:**
50 | - **Browser:**
51 |
52 | **Additional Context**
53 |
54 | Provide any additional information that may help in resolving the issue.
55 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[Feature request] "
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to Comfy registry
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - "pyproject.toml"
9 |
10 | permissions:
11 | issues: write
12 |
13 | jobs:
14 | publish-node:
15 | name: Publish Custom Node to registry
16 | runs-on: ubuntu-latest
17 | if: ${{ github.repository_owner == 'Nuked88' }}
18 | steps:
19 | - name: Check out code
20 | uses: actions/checkout@v4
21 | - name: Publish Custom Node
22 | uses: Comfy-Org/publish-node-action@v1
23 | with:
24 | ## Add your own personal access token to your Github Repository secrets and reference it here.
25 | personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Config file
2 | app/settings.json
3 | app/settings.json.bak
4 | app/views/views_backup.json
5 | metadata/*
6 | # Byte-compiled / optimized / DLL files
7 | __pycache__/
8 | *.py[cod]
9 | *$py.class
10 |
11 | # vscode
12 | *.code-workspace
13 | .vscode/*
14 |
15 | # C extensions
16 | *.so
17 |
18 | # Distribution / packaging
19 | .Python
20 | build/
21 | develop-eggs/
22 | dist/
23 | downloads/
24 | eggs/
25 | .eggs/
26 | lib/
27 | lib64/
28 | parts/
29 | sdist/
30 | var/
31 | wheels/
32 | share/python-wheels/
33 | *.egg-info/
34 | .installed.cfg
35 | *.egg
36 | MANIFEST
37 |
38 | # PyInstaller
39 | # Usually these files are written by a python script from a template
40 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
41 | *.manifest
42 | *.spec
43 |
44 | # Installer logs
45 | pip-log.txt
46 | pip-delete-this-directory.txt
47 |
48 | # Unit test / coverage reports
49 | htmlcov/
50 | .tox/
51 | .nox/
52 | .coverage
53 | .coverage.*
54 | .cache
55 | nosetests.xml
56 | coverage.xml
57 | *.cover
58 | *.py,cover
59 | .hypothesis/
60 | .pytest_cache/
61 | cover/
62 |
63 | # Translations
64 | *.mo
65 | *.pot
66 |
67 | # Django stuff:
68 | *.log
69 | local_settings.py
70 | db.sqlite3
71 | db.sqlite3-journal
72 |
73 | # Flask stuff:
74 | instance/
75 | .webassets-cache
76 |
77 | # Scrapy stuff:
78 | .scrapy
79 |
80 | # Sphinx documentation
81 | docs/_build/
82 |
83 | # PyBuilder
84 | .pybuilder/
85 | target/
86 |
87 | # Jupyter Notebook
88 | .ipynb_checkpoints
89 |
90 | # IPython
91 | profile_default/
92 | ipython_config.py
93 |
94 | # pyenv
95 | # For a library or package, you might want to ignore these files since the code is
96 | # intended to run in multiple environments; otherwise, check them in:
97 | # .python-version
98 |
99 | # pipenv
100 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
101 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
102 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
103 | # install all needed dependencies.
104 | #Pipfile.lock
105 |
106 | # poetry
107 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
108 | # This is especially recommended for binary packages to ensure reproducibility, and is more
109 | # commonly ignored for libraries.
110 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
111 | #poetry.lock
112 |
113 | # pdm
114 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
115 | #pdm.lock
116 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
117 | # in version control.
118 | # https://pdm.fming.dev/#use-with-ide
119 | .pdm.toml
120 |
121 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
122 | __pypackages__/
123 |
124 | # Celery stuff
125 | celerybeat-schedule
126 | celerybeat.pid
127 |
128 | # SageMath parsed files
129 | *.sage.py
130 |
131 | # Environments
132 | .env
133 | .venv
134 | env/
135 | venv/
136 | ENV/
137 | env.bak/
138 | venv.bak/
139 |
140 | # Spyder project settings
141 | .spyderproject
142 | .spyproject
143 |
144 | # Rope project settings
145 | .ropeproject
146 |
147 | # mkdocs documentation
148 | /site
149 |
150 | # mypy
151 | .mypy_cache/
152 | .dmypy.json
153 | dmypy.json
154 |
155 | # Pyre type checker
156 | .pyre/
157 |
158 | # pytype static type analyzer
159 | .pytype/
160 |
161 | # Cython debug symbols
162 | cython_debug/
163 |
164 | # PyCharm
165 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
166 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
167 | # and can be added to the global gitignore or merged into this file. For a more nuclear
168 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
169 | #.idea/
--------------------------------------------------------------------------------
/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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 |
4 |
5 |
6 | [](https://www.youtube.com/channel/UCnu819ZX2xiusPpbQ4KzSmA)
7 |
8 | # ComfyUI-N-Sidebar
9 | A simple sidebar for ComfyUI.
10 | For what i know nobody did it, so i did it.
11 | Maybe you don't need it. I need it >.<
12 |
13 |
14 | # Updates
15 |
16 | - 08-09-2024
17 | - Custom shortcuts!
18 |
19 | - 04-09-2024
20 | - Changed the resize system of the n-sidebar (it can now be resized directly from its sides NOTE: the top side wilL only move the bar)
21 | - Added the ability to directly download CIVITAI models into chosen folders (this includes Checkpoint, Textual Inversion, Hypernetwork, Aesthetic Gradient, LORA, LoCon, DoRA, Controlnet, Upscaler, Motion Module, VAE, Poses, Wildcards, Workflows). [More Info](#assets-downloader).
22 | - Added the ability to import/export your settings.
23 | - EXPERIMENTAL: You can now embed ComfyUI's native bar within the n-sidebar
24 | - EXPERIMENTAL: Added a small console (useful if you don’t have direct access to the machine). If you want to remove this feature, simply delete the `terminal` folder within `ComfyUI-N-Sidebar/app/panels`
25 | - EXPERIMENTAL: Added the ability to edit the layout of the bar, this functionality is highly unstable and its use may lead to unexpected behaviors
26 | - Redesigned configuration menu
27 | - Fixed some bugs
28 |
29 | - 01-08-2024
30 | - A lot of people are having trouble with the CSS class change in the last update because of a browser cache issue: the old CSS file keeps loading from disk instead of the new one with the latest changes (thanks, modern browsers!). To fix this, I've changed the name of the CSS file, which SHOULD force browsers to load the new one. If this doesn't fix the issue, I'm not sure what will 😭.
31 | - Added the new option `Show at Startup` in the settings. This lets you have the sidebar open at startup (if it's not in fixed mode).
32 | - Changed the refresh icon so that it displays correctly in Chromium browsers on macOS.
33 | - Minor change to the CSS
34 |
35 | - 30-07-2024
36 | - Changed the name of the sidebar class to prevent a conflict with the `comfyui-search-navigation` node.
37 |
38 |
39 | Previous Updates
40 |
41 | - 21-07-2024
42 | - Fixed a cache bug on the templates panel
43 |
44 | - 19-07-2024
45 | - Added OSX shortcut support
46 |
47 | - 05-07-2024
48 | - Fixed bug on dragging templates
49 | - Added multi user support on templates
50 |
51 |
52 | - 04-06-2024
53 | - Fixed some bugs
54 | - Changed icon bar positioning to the border of the screen
55 | - Implemented the DAMN subcategory view on the node panel!!! xD (you can disable it in the settings if you don't want it)
56 | - Added the ability to disable the "Auto Show" feature
57 | - Added the ability to view folders inside the workflow panel
58 | - Added some tooltips for better UX
59 | - Changed the Custom Node Categories icon (I didn't like the old one)
60 | - Added a little icon in custom categories and folders/subcategories
61 | - Added the ability to move workflows in custom categories via the context menu
62 | - Added the ability to open workflows in another TAB
63 |
64 | NOTE: If you had some custom categories in the workflow panel, at the first start they will be migrated to the new system (since I did a full rewrite to support the folders). It is possible that you need to refresh the page more than once to see them again. Also, a backup of the settings.json will be created before the migration in case anything goes wrong.
65 |
66 | NOTE2: I hope I did not break anything T_T
67 |
68 |
69 | - 25-05-2024
70 | - Fixed some bugs
71 | - Added Templates support**
72 | - Added ability categorize custom categories
73 | - Added preview (list of used nodes)
74 |
75 | - Added Workflow support!
76 | - Added ability to rename workflows
77 | - Added ability to remove workflows
78 | - Added ability to categorize workflows
79 | - Added preview (list of used nodes), (disabled by default)
80 |
81 | ** i did not enabled the **rename** and **delete** because it's conflicing with the TemplateManager
82 |
83 |
84 | - 21-04-2024
85 | - Rewritten a lot of the code to improve performance and readability, and slightly adjusted the UI to ensure compatibility with future updates.
86 | - ADDED CUSTOM CATEGORIES
87 | - Added custom setting panel
88 | - Re-Implemented the 'original' search function to support translated versions of ComfyUI (requires testing)
89 | - Added LEFT and RIGHT positioning options
90 | - Added "Auto Hide" feature
91 | - Now you have the option to use the default node ordering in ComfyUI instead of the alphabetical one
92 | - Added the possibility to perform a Soft or Factory Reset of the configuration
93 | - Now most configuration settings are stored in the settings.json file.
94 | - Fixed some bugs
95 | - Added some other bugs for sure!
96 |
97 | - 31-03-2024
98 | - Added fuzzy search (work in reverse too!) (enabled by default) - You can disable it in the settings
99 | - Added description of the node under the preview (Visible only if the description is set in the node)
100 | - Added possibility to collapse pinned node/custom nodes sections
101 | - Added possibility to set the bottom space for the sidebar
102 | - Added the scroll to top button
103 | - Fixed some bugs
104 | - Started migration from cookies to localstorage
105 | - 🐰 Happy Easter! 🐰
106 |
107 |
108 | # Installation
109 |
110 | 1. Clone the repository:
111 | `git clone https://github.com/Nuked88/ComfyUI-N-Sidebar.git`
112 | to your ComfyUI `custom_nodes` directory
113 | 2. Enjoy!
114 |
115 | NOTE: If you choose to use a different method to install the ComfyUI-N-Sidebar, please ensure that you rename the folder to `ComfyUI-N-Sidebar`.
116 |
117 | # Uninstall
118 | - Delete the `ComfyUI-N-Sidebar` folder in `custom_nodes`
119 |
120 |
121 | # Update
122 | 1. Navigate to the cloned repo e.g. `custom_nodes/ComfyUI-N-Sidebar`
123 | 2. `git pull`
124 |
125 |
126 | # Settings
127 | The most important settings are stored in `custom_nodes/ComfyUI-N-Sidebar/settings.json`
128 |
129 |
130 | # Keyboard Shortcuts
131 |
132 | - `Alt+Z` or `Option+Z` to toggle show/hide sidebar
133 | - `Alt+X` or `Option+X` to focus on the search field
134 | - `Alt+G` or `Option+G` to open the settings
135 |
136 | # How To Set Workflows Folder
137 |
138 | 1. Open the settings with the ⚙ icon or use the shortcut `Alt+G` (`Option+G` on Mac).
139 | 2. In **WORKFLOW PATHS**, set the folder path to where your workflows are located. If you want to set multiple paths, just put one folder path per line!
140 | 3. Restart ComfyUI
141 |
142 | # Features
143 |
144 | ### 🖱️Drag and Drop Nodes🖱️
145 | 
146 |
147 |
148 | ### 📌Pin Your Favorite Node📌
149 | 
150 |
151 | ### 🔍Search within your nodes📄
152 | 
153 |
154 | ### 🔍Search within categories📂
155 | 
156 |
157 | ### 🔍Fuzzy Search🔄
158 | 
159 | 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)
160 |
161 | ### 🎨 Custom Categories 📂
162 | 
163 |
164 |
165 | ### ➕Expand/Collapse Categories/Sidebar➖
166 | 
167 |
168 | ### 🔁Reorder Nodes🔁
169 | 
170 |
171 | **Note**: The node will be placed **before** the element on which it is dragged!
172 |
173 | ### 👁 Preview Node 👁
174 | 
175 |
176 | ### 📄 Templates Support 📄
177 | 
178 |
179 | #### 📄 Workflow Support 📄
180 | 
181 |
182 | ### 🎨 ComfyUI Themes Support 🎨
183 | 
184 |
185 | ### 🎨 New Settings Panel 🎨
186 | 
187 |
188 | ### ASSETS DOWNLOADER
189 | 
190 |
191 | This new panel allows you to download CIVITAI models (Checkpoint, TextualInversion, Hypernetwork, AestheticGradient, LORA, LoCon, DoRA, Controlnet, Upscaler, MotionModule, VAE, Poses, Wildcards, Workflows).
192 | The various models will be downloaded into the folders set in the settings.
193 | There is also the option to rebuild the "My Library" section (the dots at the top right corner):
194 | once the reference folders are set in the settings, by launching the rebuild, the already existing models will be identified and added to the library (if they exist in the CivitAI database).
195 |
196 | NOTE: you can set your own API key for models that are not publicly available.
197 |
198 |
199 |
200 |
201 |
202 | ### Todo:
203 | - [x] Reordering pinned nodes
204 | - [x] Node preview (i don't think it will be an image)
205 | - [x] Color integration with Jovimetrix
206 | - [x] Better search
207 | - [x] Custom Categories!!
208 | - [x] Workflows
209 | - [x] Templates
210 | - [x] Export and Import Settings
211 | - [x] Custom Shortcuts
212 | - [ ] Touch Support
213 |
214 |
215 | ### Known Issues:
216 | - After you drag a template onto the workflow, if you drag any other file from your PC that does not include a workflow, it will paste the last template you dragged again.
217 |
218 | ## Contributing
219 |
220 | 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.
221 |
222 | ## Donations
223 |
224 | If you'd like to support the project, consider making a donation ❤️
225 |
226 | [](https://ko-fi.com/C0C0AJECJ)
227 |
228 |
229 | ## License
230 |
231 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
232 |
233 |
--------------------------------------------------------------------------------
/app/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/app/.DS_Store
--------------------------------------------------------------------------------
/app/css/base_style_sb.css:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | .litegraph .dialog {
5 | z-index: 100 !important;
6 | }
7 |
8 | :root {
9 | --n-sidebar-width: 30px;
10 | --sidebar-menu-item-width: 24px;
11 | --main-menubar-height:35px;
12 | }
13 | .sidebar {
14 |
15 | position: absolute;
16 | top: 0;
17 | left: -250px;
18 | left: -250px;
19 | width: fit-content;
20 | height: calc(100% - 19px);
21 | color: var(--drag-text);
22 | transition: left 0.3s ease;
23 | z-index: 2;
24 | padding-top: 19px;
25 | left: 0;
26 | user-select: none;
27 | box-sizing: content-box;
28 |
29 |
30 | display: flex;
31 | flex-direction: row;
32 | justify-content: space-around;
33 | align-items: flex-start;
34 | }
35 |
36 |
37 | sidebar * {
38 | box-sizing: revert;
39 | }
40 |
41 | .sidebar ul {
42 | list-style-type: none;
43 | /* border-bottom: 6px solid rgb(from var(--comfy-input-bg) r g b / 60%);*/
44 |
45 | padding-left: 5px;
46 | padding-right: 5px;
47 | /*margin-bottom: 8px;*/
48 | margin-top: 0;
49 | padding-top: 5px;
50 | width: 100%;
51 | }
52 |
53 | .sidebar li {
54 | padding: 10px;
55 | cursor: pointer;
56 | user-select: none;
57 | color: var(--input-text);
58 | margin-left: 4px;
59 | }
60 |
61 | .content_sidebar {
62 |
63 | background: rgb(from var(--comfy-menu-bg) r g b / 100%);
64 | overflow-y: scroll;
65 | overflow-x: hidden;
66 | height: 100%;
67 | float: left;
68 | backdrop-filter: blur(5px);
69 | width: calc(100% - 45px);
70 |
71 |
72 |
73 |
74 |
75 | }
76 |
77 | .dragHandle {
78 | position: relative;
79 | float: left;
80 | right: 5;
81 | top: 0;
82 | height: 100%;
83 | width: 5px;
84 | cursor: ew-resize;
85 | background: rgb(119 111 111 / 0%);
86 | /*background: linear-gradient(90deg, rgb(62 62 62 / 46%) 0%, rgb(39 39 39 / 47%) 50%, rgb(28 28 28 / 31%) 100%);*/
87 | }
88 |
89 | .dragHandleTB {
90 | background: #ffffff00;
91 | height: 10px;
92 | width: calc(100% - 10px);
93 | position: absolute;
94 | cursor: n-resize;
95 |
96 | }
97 |
98 | #dragHandleV {
99 | bottom: -5px;
100 | }
101 |
102 | #dragHandleT {
103 |
104 | top: 15px;
105 | z-index: 999999;
106 | }
107 | #searchInputSB {
108 | box-sizing: border-box;
109 | width: 100%;
110 | border-radius: 5px;
111 | padding: 10px;
112 | border: none;
113 | user-select: none;
114 | background: var(--comfy-input-bg);
115 | color: var(--input-text);
116 | line-height: 1.4;
117 | border: 1px solid var(--border-color);
118 | }
119 |
120 | .sidebar-header {
121 | position: absolute;
122 | width: calc(100% - 75px);
123 | margin-top: 5px;
124 | margin-bottom: 10px;
125 | margin-left: var(--n-sidebar-width);
126 |
127 | z-index: 400;
128 | width: calc(100% - 60px);
129 | margin-top: 5px;
130 | left: 11px;
131 | }
132 |
133 | .clearIcon, .searchCategoryIcon, .clearIconCustomCategory, .searchCategoryIconCustomCategory, .clearIconCustomTemplate, .searchTemplateIconCustomTemplate, .clearIconCustomWorkflow, .searchWorkflowIconCustomWorkflow {
134 | position: absolute;
135 | padding: 5px;
136 | right: 30px;
137 | color: var(--input-text);
138 | font-size: larger;
139 | cursor: pointer;
140 | opacity: 0.5;
141 | user-select: none;
142 | line-height: 1.6;
143 | top: 0;
144 |
145 |
146 | display: flex;
147 | align-content: center;
148 | justify-content: center;
149 | align-items: center;
150 | height: -moz-available; /* WebKit-based browsers will ignore this. */
151 | height: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
152 | height: fill-available;
153 | }
154 |
155 |
156 | .clearIcon:hover, .clearIconCustomCategory:hover, .clearIconCustomTemplate:hover, .clearIconCustomWorkflow:hover {
157 | opacity: 1.0;
158 | }
159 | .clearIcon,.clearIconCustomTemplate,.clearIconCustomWorkflow {
160 | line-height: 1.6 !important
161 | }
162 |
163 | .searchCategoryIcon, .searchCategoryIconCustomCategory, .searchTemplateIconCustomTemplate, .searchWorkflowIconCustomWorkflow {
164 | right: 0px;
165 | padding: 6px;
166 | font-size: larger;
167 | background: var(--border-color);
168 | color: var(--drag-text);
169 | margin: 4px;
170 | border-radius: 5px;
171 |
172 | /* text-align: center; */
173 | height: -moz-available; /* WebKit-based browsers will ignore this. */
174 | height: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
175 | height: fill-available;
176 |
177 | /* top: 0; */
178 | /* box-sizing: content-box; */
179 | display: flex;
180 | align-content: center;
181 | justify-content: center;
182 | line-height: 1;
183 | align-items: center;
184 |
185 |
186 | }
187 |
188 | .searchCategoryIcon:hover, .searchCategoryIconCustomCategory:hover, .searchTemplateIconCustomTemplate:hover, .searchWorkflowIconCustomWorkflow:hover {
189 | opacity: 0.5;
190 | }
191 |
192 | .sidebarCategory, #sidebarBookmarks {
193 | list-style-type: none;
194 | font-family: 'Open Sans', sans-serif;
195 | text-transform: capitalize;
196 | margin: 2px;
197 | background-color: var(--comfy-input-bg);
198 | opacity: 1;
199 | border-radius: 8px;
200 | padding-top: 11px;
201 | font-size: 15px;
202 | padding-bottom: 8px;
203 | }
204 | .sidebarCategory {
205 | border-bottom:var(--comfy-input-bg) solid 1px;
206 | text-wrap: nowrap;
207 | }
208 | #sidebarCustomNodes {
209 | list-style-type: none;
210 | font-family: 'Open Sans', sans-serif;
211 | text-transform: capitalize;
212 | margin: 0px;
213 | background-color: transparent;
214 | opacity: 1;
215 | border-radius: 8px;
216 | font-size: 15px;
217 | padding-left: 0;
218 | padding-right: 0px;
219 | padding-top: 0px;
220 | padding-bottom: 40px;
221 | }
222 |
223 | .sidebarItem:hover {
224 | background: color-mix(in srgb, currentColor 15%, transparent);
225 | }
226 |
227 | .sidebarItem {
228 | list-style-type: none;
229 | font-family: 'Open Sans', sans-serif;
230 | text-transform: capitalize;
231 | margin: 2px;
232 | background: color-mix(in srgb, var(--border-color) 35%, transparent);
233 | opacity: 1;
234 | border-radius: 8px;
235 |
236 | white-space: nowrap;
237 | text-overflow: ellipsis;
238 | overflow: hidden;
239 | /*max-width: calc(100% - 39px);*/
240 | }
241 |
242 | .displayName{
243 | float: left;
244 | width: 87%;
245 | text-overflow: ellipsis;
246 | overflow: hidden;
247 | white-space: nowrap;
248 | /* margin: 2% auto;*/
249 | /*display: flex;*/
250 | align-items: center;
251 | padding-left: 3%;
252 | /*height: 14px;*/
253 | }
254 | .categoryName{
255 |
256 | width: 100%;
257 | text-overflow: ellipsis;
258 | overflow: hidden;
259 | }
260 |
261 | /* Firefox-specific styles */
262 | @-moz-document url-prefix() {
263 | .content_sidebar,#settingsContainer {
264 | scrollbar-width: thin; /* Scrollbar thickness */
265 | scrollbar-color: hsla(0, 0%, 50%, .8) transparent; /* Thumb and track colors */
266 | }
267 | #settingsContainer::-moz-scrollbar-thumb:hover,
268 | .content_sidebar::-moz-scrollbar-thumb:hover {
269 | background-color: rgba(150, 150, 150, 1); /* Thumb color on hover */
270 | }
271 |
272 | #settingsContainer::-moz-scrollbar-thumb,
273 | .content_sidebar::-moz-scrollbar-thumb {
274 | background-color: hsla(0, 0%, 50%, .8); /* Thumb color */
275 | border-radius: 9px;
276 | }
277 | }
278 | #settingsContainer::-webkit-scrollbar,
279 | .content_sidebar::-webkit-scrollbar {
280 | margin-top: 0.5rem;
281 | height: 1rem;
282 | width: .5rem;
283 | top: 10px;
284 | }
285 | #settingsContainer::-webkit-scrollbar:horizontal,
286 | .content_sidebar::-webkit-scrollbar:horizontal {
287 | height: .5rem;
288 | width: 1rem
289 | }
290 |
291 | #settingsContainer::-webkit-scrollbar-track,
292 | .content_sidebar::-webkit-scrollbar-track {
293 | background-color: transparent !important;
294 | border-radius: 9999px
295 | }
296 |
297 | #settingsContainer::-webkit-scrollbar-thumb,
298 | .content_sidebar::-webkit-scrollbar-thumb {
299 | --tw-border-opacity: 1;
300 | background-color: hsla(0, 0%, 50%, .8);
301 | border-color: rgba(255, 255, 255, 0, 0, 0);
302 | border-radius: 9999px;
303 | border-width: 1px
304 | }
305 |
306 | #settingsContainer::-webkit-scrollbar-thumb:hover,
307 | .content_sidebar::-webkit-scrollbar-thumb:hover {
308 | --tw-bg-opacity: 1;
309 |
310 | background-color: rgba(150, 150, 150, var(--tw-bg-opacity))
311 | }
312 |
313 | #spacer {
314 | height: 45px;
315 | }
316 |
317 |
318 | .view_button {
319 | position: relative;
320 | float: unset;
321 | cursor: pointer;
322 | user-select: none;
323 | padding: 5px 0 5px;
324 | font-size: 20px;
325 | /*background-color: var(--comfy-input-bg);*/
326 | opacity: 0.7;
327 | /*border-radius: 5px;*/
328 |
329 | margin-top:2px;
330 | box-sizing: content-box;
331 |
332 | display: flex;
333 | justify-content: center;
334 | align-items: center;
335 | height: var(--sidebar-menu-item-width);
336 | /*border-right: 2px solid var(--content-bg);*/
337 | border-color: var(--content-bg);
338 |
339 | }
340 | .view_button:hover {
341 | opacity: 1.0;
342 | }
343 |
344 |
345 | .sb_button_active {
346 | border-color: var(--content-hover-fg);
347 | color: var(--content-hover-fg);
348 | }
349 | #sidebar_views {
350 | position: relative;
351 | float: left;
352 | text-align: center;
353 |
354 | cursor: pointer;
355 | user-select: none;
356 | padding: 0px 2px 2px 2px;
357 | font-size: 20px;
358 |
359 | height: 100%;
360 | width: var(--n-sidebar-width);
361 | border: 0;
362 | border-left: 2px rgb(from var(--comfy-menu-bg) r g b / 100%);
363 | border-style: solid;
364 | padding-bottom: 0px;
365 | backdrop-filter: blur(16px) !important;
366 | background: rgb(from var(--comfy-menu-bg) r g b / 100%);
367 | z-index: 99999;
368 | }
369 |
370 |
371 | #searchInputSB.closed {
372 | display: none;
373 | }
374 |
375 | .content_sidebar.closed {
376 | width: 0 !important;
377 | }
378 |
379 | .searchCategoryIcon.closed {
380 | display: none;
381 | }
382 |
383 | .clearIcon.closed {
384 | display: none;
385 | }
386 |
387 | .sidebarCategory .pinButton, .sidebarItem .pinButton {
388 | background-color: transparent;
389 | border: 0;
390 | position: absolute;
391 | width: 10%;
392 | float: left;
393 | /* padding-top: 4px; */
394 | /* font-size: 39px; */
395 | height: 1.5em;
396 | }
397 |
398 | #sb_scrollToTopButton.closed {
399 | display: none !important;
400 | }
401 |
402 | #sidebarBookmarks .pinButton {
403 |
404 | background-color: transparent;
405 | border: 0;
406 |
407 | /*position: absolute;
408 | right: 0;*/
409 |
410 | position: relative;
411 | width: 10%;
412 | float: left;
413 |
414 | }
415 |
416 | .previewButton {
417 | background-color: transparent;
418 | border: 0;
419 | position: absolute;
420 | right: 35px;
421 | }
422 |
423 | .pinned {
424 | fill: var(--descrip-text) !important;
425 | opacity: 1 !important;
426 | }
427 |
428 | .svg_class {
429 | width: 24px;
430 | height: 18px;
431 | fill: var(--border-color);
432 | cursor: pointer;
433 | /* hacky fix
434 | margin-top: -5px;
435 | margin-left: -25px;*/
436 | }
437 |
438 | .pin_normal {
439 | opacity: 0.5;
440 | }
441 |
442 | .pin_normal:hover {
443 | opacity: 1;
444 | }
445 |
446 | .svg_class:hover {
447 | fill: var(--border-color);
448 |
449 | }
450 |
451 |
452 | #sidebarBookmarks .sidebarItem {
453 | margin-left: 0px;
454 | margin-right: 0px;
455 |
456 | }
457 | #sidebarBookmarks {
458 | width: calc(100% - 16px);
459 | margin-left: 3px;
460 | }
461 | .sb_label {
462 | font-family: 'Open Sans', sans-serif;
463 | position: relative;
464 | margin: 5px;
465 | font-weight: bold;
466 |
467 | background: var(--comfy-input-bg);
468 | border-radius: 3px;
469 | padding-left: 6px;
470 | padding-right: 6px;
471 | display: block;
472 | display: flex;
473 | width: calc(100% - 20px);
474 | text-align: center;
475 | user-select: none;
476 | cursor: pointer;
477 |
478 | align-items: center;
479 | height: 28px;
480 | justify-content: center;
481 | padding-top: 1px;
482 | }
483 |
484 | .expand_node, .pin_node {
485 | position: absolute !important;
486 | right: 5px;
487 | background: transparent;
488 | border: 0;
489 |
490 | }
491 |
492 | .expand_node svg, .pin_node svg {
493 |
494 | width: 20px;
495 | height: 20px;
496 | background: transparent;
497 | fill: var(--input-text);
498 | cursor: pointer;
499 | }
500 |
501 | .sb_dot {
502 | width: 8px;
503 | height: 8px;
504 | border-radius: 50%;
505 | background-color: grey;
506 | }
507 |
508 | .node_header {
509 | line-height: 1;
510 | padding: 8px 13px 7px;
511 | background: var(--comfy-input-bg);
512 | margin-bottom: 5px;
513 | font-size: 15px;
514 | text-wrap: nowrap;
515 | overflow: hidden;
516 | }
517 |
518 | .headdot {
519 | width: 10px;
520 | height: 10px;
521 | float: inline-start;
522 | margin-right: 8px;
523 | margin-top: 3px;
524 | }
525 |
526 |
527 | .IMAGE {
528 | background-color: #64b5f6;
529 | }
530 |
531 | .VAE {
532 | background-color: #ff6e6e;
533 | }
534 |
535 | .LATENT {
536 | background-color: #ff9cf9;
537 | }
538 |
539 | .MASK {
540 | background-color: #81c784;
541 | }
542 |
543 | .CONDITIONING {
544 | background-color: #ffa931;
545 | }
546 |
547 | .CLIP {
548 | background-color: #ffd500;
549 | }
550 |
551 | .MODEL {
552 | background-color: #b39ddb;
553 | }
554 |
555 | .CONTROL_NET {
556 | background-color: #a5d6a7;
557 | }
558 |
559 |
560 | #previewDiv {
561 | position: absolute;
562 | background-color: var(--comfy-menu-bg);
563 | font-family: 'Open Sans', sans-serif;
564 | font-size: small;
565 | color: var(--descrip-text);
566 | border: 1px solid var(--descrip-text);
567 | display: none;
568 | min-width: 300px;
569 | width: min-content;
570 | height: fit-content;
571 | z-index: 9999;
572 | border-radius: 12px;
573 | overflow: hidden;
574 | font-size: 12px;
575 | padding-bottom: 10px;
576 |
577 | }
578 |
579 | #previewDiv .sb_description {
580 | margin: 10px;
581 | padding: 6px;
582 | background: var(--border-color);
583 | border-radius: 5px;
584 | font-style: italic;
585 | font-weight: 500;
586 | font-size: 0.9rem;
587 | }
588 |
589 | .sb_table {
590 | display: grid;
591 |
592 | grid-column-gap: 10px;
593 |
594 | width: 100%;
595 |
596 | }
597 |
598 | .sb_row {
599 | display: grid;
600 | grid-template-columns: 10px 1fr 1fr 0fr 10px;
601 | grid-column-gap: 10px;
602 | align-items: baseline;
603 | padding-left: 9px;
604 | padding-right: 9px;
605 | }
606 |
607 | .sb_row_string {
608 | grid-template-columns: 10px 1fr 1fr 10fr 1fr;
609 | }
610 |
611 | .sb_col {
612 | border: 0px solid #000;
613 | display: flex;
614 | align-items: flex-end;
615 | flex-direction: row-reverse;
616 | flex-wrap: nowrap;
617 | align-content: flex-start;
618 | justify-content: flex-end;
619 | }
620 |
621 | .sb_inherit {
622 | display: inherit;
623 | width: max-content;
624 | flex: revert-layer;
625 | }
626 |
627 | .sb_inherit_orig {
628 | display: inherit;
629 |
630 | }
631 |
632 | .long_field {
633 | background: var(--bg-color);
634 | border: 2px solid var(--border-color);
635 | margin: 5px 5px 0 5px;
636 | border-radius: 10px;
637 | line-height: 1.7;
638 | }
639 |
640 | .sb_arrow {
641 | color: var(--fg-color);
642 | }
643 |
644 | .sb_preview_badge {
645 | text-align: center;
646 | background: var(--comfy-input-bg);
647 | font-weight: bold;
648 | color: var(--error-text);
649 | margin-bottom: 3px;
650 | }
651 |
652 | #sb_scrollToTopButton {
653 | position: absolute;
654 | bottom: 5px;
655 | right: 56px;
656 | cursor: pointer;
657 | display: none;
658 |
659 | border-radius: 20%;
660 | background: var(--comfy-menu-bg);
661 | padding: 4px;
662 | border: 1px solid #4c4c4c;
663 | }
664 |
665 | #sb_scrollToTopButton svg {
666 | width: 20px;
667 | height: 20px;
668 | fill: var(--descrip-text);
669 | }
670 |
671 |
672 |
673 |
674 | .panel_sidebar{
675 | min-height: 200px;
676 |
677 | border-radius: 5px;
678 | margin-left: 7px;
679 |
680 | }
681 |
682 | .panel_sidebar label{
683 | text-transform: uppercase;
684 | }
685 |
686 |
687 |
688 |
689 |
690 |
691 | .custom-menu {
692 | 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);
693 | font-family: Arial, sans-serif;
694 | display: none;
695 | position: absolute;
696 | background-color: var(--bg-color);
697 | border: 0px solid #313131;
698 | color: var(--input-text);
699 | padding: 5px 0;
700 | border-radius: 5px;
701 | z-index: 9999;
702 | /* backdrop-filter: blur(5px);
703 | background: rgb(from #353535 r g b / 50%);*/
704 | }
705 | .custom-menu ul {
706 | list-style-type: none;
707 | padding: 0;
708 | margin: 0;
709 | width: 100%;
710 | }
711 | .custom-menu li {
712 | padding: 8px 15px;
713 | cursor: pointer;
714 |
715 | }
716 | #menu-options li {
717 |
718 | min-width: 134px;
719 | }
720 |
721 |
722 |
723 | .custom-menu .sub-menu li:first-child {
724 | border-top-left-radius: 5px;
725 | border-top-right-radius: 5px;
726 | }
727 |
728 |
729 | .custom-menu .sub-menu li:last-child {
730 | border-bottom-left-radius: 5px;
731 | border-bottom-right-radius: 5px;
732 | }
733 | .custom-menu li:hover {
734 | background-color: var(--border-color);
735 | }
736 | .sub-menu {
737 |
738 | 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);
739 | font-family: Arial, sans-serif;
740 | display: none;
741 | position: absolute;
742 | top: 0;
743 | left: 100%;
744 | background-color: var(--bg-color);
745 | border: 0px solid #313131;
746 | border-radius: 5px;
747 | border-top-left-radius: 0px;
748 |
749 | z-index: 1001; /* Ensure sub-menu appears above main menu */
750 | /*
751 | backdrop-filter: blur(5px);
752 | background: rgb(from #353535 r g b / 50%);*/
753 | }
754 |
755 | .cat_empty {
756 | display: none !important;
757 | }
758 |
759 |
760 |
761 | /* Settings */
762 | #switch_settings{
763 | /*bottom: 5px;
764 | position: absolute;*/
765 | width: -moz-available; /* WebKit-based browsers will ignore this. */
766 | width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
767 | width: fill-available;
768 | height: 20px;
769 | border: 0 !important;
770 | line-height: 1;
771 | box-sizing: content-box;
772 | }
773 |
774 | #sb_settingsDiv,#sb_settingsDiv_export, #sb_settingsDiv_import, #sb_settingsDiv_shortcut {
775 | outline: 0;
776 | border: 0;
777 | border-radius: 6px;
778 | background: var(--bg-color);
779 | color: var(--input-text);
780 | 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);
781 | max-width: 800px;
782 | width: 650px;
783 | box-sizing: border-box;
784 | font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
785 | font-size: 0.8rem;
786 | padding: 5px 20px 20px 20px;
787 | position: fixed;
788 | top: 50%;
789 | left: 50%;
790 | transform: translate(-50%, -50%);
791 | z-index: 1001;
792 | text-transform: uppercase;
793 | font-weight: bold;
794 | }
795 | #sb_settingsDiv_export, #sb_settingsDiv_import{
796 | z-index: 1005;
797 | width: 30vh;
798 | }
799 | #sb_settingsDiv_shortcut {
800 | z-index: 1005;
801 | width: 100%;
802 | }
803 | #sb_settingsDiv .setting{
804 | height: 28px;
805 | width: 100%;
806 | margin-bottom: 7px;
807 | }
808 | #sb_settingsDiv .setting input[type="range"]{
809 | height: 28px;
810 |
811 | }
812 |
813 | #sb_settingsDiv .setting input[type="text"], #sb_settingsDiv_shortcut .setting input[type="text"]{
814 | background: var(--comfy-menu-bg);
815 | color: var(--drag-text);
816 | padding: 5px;
817 | float: right;
818 | width: 60%;
819 | margin-right: 0px;
820 | border-radius: 5px;
821 | border: solid 1px var(--descrip-text);
822 | }
823 |
824 | #sb_settingsDiv_shortcut .setting input[type="text"] {
825 |
826 | width: 48% !important;
827 | }
828 | #sb_settingsDiv .setting textarea{
829 | background: var(--comfy-menu-bg);
830 | color: var(--drag-text);
831 | padding: 5px;
832 | float: right;
833 | width: calc(100% - 60px);
834 | resize: vertical;
835 | margin-right: 47px;
836 | border-radius: 5px;
837 | border: solid 1px var(--descrip-text);
838 | margin-bottom: 20px;
839 | }
840 | #sb_settingsDiv .setting input[type="checkbox"], #sb_settingsDiv_export .setting input[type="checkbox"], #sb_settingsDiv_import .setting input[type="checkbox"]{
841 | width: 18px;
842 | height: 18px;
843 | }
844 | #sb_settingsDiv_export .setting, #sb_settingsDiv_import .setting , #sb_settingsDiv_shortcut .setting{
845 | height: 36px;
846 | }
847 | #sb_settingsDiv .setting input, #sb_settingsDiv_export .setting input , #sb_settingsDiv_import .setting input, #sb_settingsDiv_shortcut .setting input{
848 | float: right;
849 | }
850 | #sb_settingsDiv .setting label{
851 | line-height: 1.8;
852 | }
853 | #sb_settingsDiv .slider-container{
854 | float: right;
855 | width: 36px;
856 | background: var(--comfy-menu-bg);
857 | padding: 3px;
858 | border-radius: 5px;
859 | text-align: center;
860 | margin: 5px auto;
861 | }
862 |
863 | #sb_settingsDiv_export h1, #sb_settingsDiv_import h1, #sb_settingsDiv_shortcut h1{
864 | font-size: medium;
865 |
866 | }
867 |
868 | #sb_settingsDiv h1,#sb_settingsDiv_export h1, #sb_settingsDiv_import h1 , #sb_settingsDiv_shortcut h1{
869 | margin: 0 0 20px 0;
870 | padding: 0 0 5px 0;
871 | text-align: center;
872 | border-bottom: 5px solid var(--border-color);
873 | text-transform: uppercase;
874 | }
875 |
876 | #settingsContainer button{
877 |
878 | padding: 9px;
879 | /* position: absolute; */
880 | bottom: 0;
881 | width: 100%;
882 | margin: 0 0 20px 0;
883 | background: var(--comfy-menu-bg);
884 | color: var(--input-text);
885 | border: 1px solid var(--border-color);
886 | border-radius: 5px;
887 | cursor: pointer;
888 | text-transform: uppercase;
889 | font-weight: bold;
890 | }
891 | #settingsContainer button:hover{
892 | filter: brightness(120%);
893 | }
894 | #settingsContainer{
895 | height: 50vh;
896 | overflow-x: hidden;
897 | overflow-y: auto;
898 | margin-right: -10px;
899 | padding-right: 10px;
900 | }
901 | #sb_buttonsContainer {
902 | display: grid;
903 | grid-template-columns: repeat(2, 1fr); /* Due colonne, larghezza uguale */
904 | grid-template-rows: auto; /* Le righe si adattano all'altezza degli elementi */
905 | gap: 5px; /* Spaziatura tra i bottoni */
906 | width: 100%; /* Si adatta alla larghezza del contenitore padre */
907 | margin-bottom: 8px;
908 | }
909 |
910 | #sb_buttonsContainer button, #sb_buttonsContainerCustomize button {
911 | padding: 9px;
912 | width: 100%; /* Il bottone si adatta alla colonna */
913 | background: var(--comfy-menu-bg);
914 | color: var(--input-text);
915 | border: 1px solid var(--border-color);
916 | margin: 0 0 0px 0;
917 | border-radius: 5px;
918 | cursor: pointer;
919 | text-transform: uppercase;
920 | font-weight: bold;
921 | box-sizing: border-box; /* Include il padding e il bordo nel calcolo della larghezza */
922 | }
923 |
924 | #sb_buttonsContainer #sb_runExportButton{
925 | width: 200%;
926 | }
927 |
928 |
929 |
930 | #sb_buttonsContainerCustomize{
931 | display: grid;
932 | grid-template-columns: repeat(3, 1fr); /* Due colonne, larghezza uguale */
933 | grid-template-rows: auto; /* Le righe si adattano all'altezza degli elementi */
934 | gap: 5px; /* Spaziatura tra i bottoni */
935 | width: 100%; /* Si adatta alla larghezza del contenitore padre */
936 | margin-bottom: 8px;
937 | }
938 |
939 | #sb_settingsDiv i{
940 | margin-left: 4px;
941 | color: #0075ff;
942 | font-style: normal;
943 | font-size: larger;
944 | }
945 |
946 | .remove-section-btn{
947 | padding: 5px;
948 | position: static;
949 | float: right;
950 | right: 25px;
951 | }
952 | /* MODALS */
953 | .sb-modal-title{
954 | position: absolute;
955 | top: 4px;
956 | margin-top: 0;
957 | text-transform: uppercase;
958 | }
959 |
960 | .sb-modal-backdrop, .sb-modal-backdrop-settings {
961 | position: fixed;
962 | top: 0;
963 | left: 0;
964 | width: 100%;
965 | height: 100%;
966 | background-color: rgba(0, 0, 0, 0.2); /* Add desired opacity */
967 | z-index: 1000; /* Make sure the backdrop is behind the modal */
968 | }
969 |
970 |
971 | #sb_settingsDiv select{
972 | background: var(--comfy-menu-bg);
973 | color: var(--drag-text);
974 | padding: 5px;
975 | float: right;
976 | width: 129px;
977 | margin-right: 47px;
978 | border-radius: 5px;
979 | }
980 |
981 | .colorBlob {
982 | float: right;
983 | overflow: hidden;
984 | margin-top: 34px;
985 | width: 100%;
986 | height: 125px;
987 | border-radius: 6px;
988 | }
989 |
990 | .colorBlob input[type='color'] {
991 | width: 106%;
992 | height: 112%;
993 | margin: -6px;
994 | }
995 |
996 |
997 |
998 | /*#switch_home,#switch_custom_view{
999 | width: 1.3rem;
1000 | height: 1.3rem;
1001 |
1002 | }*/
1003 |
1004 | #switch_sidebar{
1005 |
1006 | margin-top: 5px;
1007 | }
1008 |
1009 | #sbModal{
1010 | display: none;
1011 | z-index: 9999;
1012 | }
1013 |
1014 | .full_rounded{
1015 | border-radius: 5px;
1016 | }
1017 |
1018 | .p-button {
1019 | overflow: visible !important;
1020 |
1021 | }
1022 | /*************/
1023 | /*ALPHA: INGLOBE SIDEBAR */
1024 |
1025 | .splitter-overlay{
1026 | /*width: 486px !important;*/
1027 | z-index: 100 !important;
1028 |
1029 |
1030 | }
1031 |
1032 |
1033 |
1034 | #official_button{
1035 | display: flex;
1036 | flex-direction: column;
1037 | position: absolute;
1038 | height: auto;
1039 | bottom: 50%;
1040 |
1041 | }
1042 |
1043 | #official_button button{
1044 | width: var(--n-sidebar-width);
1045 | height: var(--n-sidebar-width);
1046 | }
1047 |
1048 | #official_button i {
1049 | font-size: 20px !important;
1050 | }
1051 |
1052 | /* setup tooltips */
1053 |
1054 | #openModalButton{
1055 | z-index: 1000 !important;
1056 | }
1057 |
1058 |
1059 | [data-tooltip] {
1060 | --arrow-size: 5px;
1061 | position: relative;
1062 | /*z-index: 10;*/
1063 | }
1064 |
1065 | /* Positioning and visibility settings of the tooltip */
1066 | [data-tooltip]:before,
1067 | [data-tooltip]:after {
1068 | position: absolute;
1069 | visibility: hidden;
1070 | opacity: 0;
1071 | left: 50%;
1072 | bottom: calc(100% + var(--arrow-size));
1073 | pointer-events: none;
1074 | transition: 0.2s;
1075 | will-change: transform;
1076 | }
1077 |
1078 | /* The actual tooltip with a dynamic width */
1079 | [data-tooltip]:before {
1080 | content: attr(data-tooltip);
1081 | padding: 10px 18px;
1082 | min-width: 50px;
1083 | max-width: 350px;
1084 | width: max-content;
1085 | width: -moz-max-content;
1086 | border-radius: 6px;
1087 | font-size: 14px;
1088 | background-color: var(--border-color);;
1089 | background-image: linear-gradient(30deg,
1090 | rgba(59, 72, 80, 0.44),
1091 | rgba(59, 68, 75, 0.44),
1092 | rgba(60, 82, 88, 0.44));
1093 | box-shadow: 0px 0px 24px rgba(0, 0, 0, 0.2);
1094 | color: #fff;
1095 | white-space: pre-wrap;
1096 | transform: translate(-50%, calc(0px - var(--arrow-size))) scale(0.5);
1097 | z-index: 10;
1098 | }
1099 |
1100 | /* Tooltip arrow */
1101 | [data-tooltip]:after {
1102 | content: '';
1103 | border-style: solid;
1104 | border-width: var(--arrow-size) var(--arrow-size) 0px var(--arrow-size); /* CSS triangle */
1105 | border-color: var(--border-color) transparent transparent transparent;
1106 | transition-duration: 0s; /* If the mouse leaves the element,
1107 | the transition effects for the
1108 | tooltip arrow are "turned off" */
1109 | transform-origin: top; /* Orientation setting for the
1110 | slide-down effect */
1111 | transform: translateX(-50%) scaleY(0);
1112 | }
1113 |
1114 | /* Tooltip becomes visible at hover */
1115 | [data-tooltip]:hover:before,
1116 | [data-tooltip]:hover:after {
1117 | visibility: visible;
1118 | opacity: 1;
1119 | }
1120 | /* Scales from 0.5 to 1 -> grow effect */
1121 | [data-tooltip]:hover:before {
1122 | transition-delay: 0.8s;
1123 | transform: translate(-50%, calc(0px - var(--arrow-size))) scale(1);
1124 | }
1125 | /*
1126 | Arrow slide down effect only on mouseenter (NOT on mouseleave)
1127 | */
1128 | [data-tooltip]:hover:after {
1129 | transition-delay: 0.8s; /* Starting after the grow effect */
1130 | transition-duration: 0.2s;
1131 | transform: translateX(-50%) scaleY(1);
1132 | }
1133 | /*
1134 | That's it for the basic tooltip.
1135 |
1136 | If you want some adjustability
1137 | here are some orientation settings you can use:
1138 | */
1139 |
1140 | /* LEFT */
1141 | /* Tooltip + arrow */
1142 | .left:before,
1143 | .left:after {
1144 | left: auto;
1145 | right: calc(100% + var(--arrow-size));
1146 | bottom: 50%;
1147 | }
1148 |
1149 | /* Tooltip */
1150 | .left:before {
1151 | transform: translate(calc(0px - var(--arrow-size)), 50%) scale(0.5);
1152 | }
1153 | .left:hover:before {
1154 | transform: translate(calc(0px - var(--arrow-size)), 50%) scale(1);
1155 | }
1156 |
1157 | /* Arrow */
1158 | .left:after {
1159 | border-width: var(--arrow-size) 0px var(--arrow-size) var(--arrow-size);
1160 | border-color: transparent transparent transparent var(--border-color);;
1161 | transform-origin: left;
1162 | transform: translateY(50%) scaleX(0);
1163 | }
1164 | .left:hover:after {
1165 | transform: translateY(50%) scaleX(1);
1166 | }
1167 |
1168 |
1169 |
1170 | /* RIGHT */
1171 | .right:before,
1172 | .right:after {
1173 | left: calc(100% + var(--arrow-size));
1174 | bottom: 50%;
1175 | }
1176 |
1177 | .right:before {
1178 | transform: translate(var(--arrow-size), 50%) scale(0.5);
1179 | }
1180 | .right:hover:before {
1181 | transform: translate(var(--arrow-size), 50%) scale(1);
1182 | }
1183 |
1184 | .right:after {
1185 | border-width: var(--arrow-size) var(--arrow-size) var(--arrow-size) 0px;
1186 | border-color: transparent var(--border-color) transparent transparent;
1187 | transform-origin: right;
1188 | transform: translateY(50%) scaleX(0);
1189 | }
1190 | .right:hover:after {
1191 | transform: translateY(50%) scaleX(1);
1192 | }
1193 |
1194 |
1195 |
1196 | /* BOTTOM */
1197 | [data-tooltip-location="bottom"]:before,
1198 | [data-tooltip-location="bottom"]:after {
1199 | top: calc(100% + var(--arrow-size));
1200 | bottom: auto;
1201 | }
1202 |
1203 | [data-tooltip-location="bottom"]:before {
1204 | transform: translate(-50%, var(--arrow-size)) scale(0.5);
1205 | }
1206 | [data-tooltip-location="bottom"]:hover:before {
1207 | transform: translate(-50%, var(--arrow-size)) scale(1);
1208 | }
1209 |
1210 | [data-tooltip-location="bottom"]:after {
1211 | border-width: 0px var(--arrow-size) var(--arrow-size) var(--arrow-size);
1212 | border-color: transparent transparent var(--border-color) transparent;
1213 | transform-origin: bottom;
1214 | }
1215 |
1216 |
1217 |
1218 | /* Settings that make the pen look nicer */
1219 |
1220 | @keyframes moveFocus {
1221 | 0% { background-position: 0% 100% }
1222 | 100% { background-position: 100% 0% }
1223 | }
1224 |
1225 |
1226 |
1227 | @media (max-height: 450px) {
1228 | main {
1229 | margin: 2rem 0;
1230 | }
1231 | }
1232 |
1233 | @media (max-width: 800px) {
1234 | html {
1235 | font-size: 0.9em;
1236 | }
1237 | }
1238 |
1239 |
1240 |
1241 |
1242 |
1243 |
1244 |
1245 |
1246 |
1247 |
1248 | /*SETTINGS TAB&CUSTOMIZE*/
1249 |
1250 | .n-sb-tab-container {
1251 | margin-bottom: 1rem;
1252 | display: grid;
1253 | grid-template-columns: repeat(2, 1fr);
1254 | grid-template-rows: auto;
1255 | gap: 5px;
1256 | width: 100%;
1257 | margin-bottom: 8px;
1258 | }
1259 |
1260 | .n-sb-tab {
1261 | padding: 10px 20px;
1262 | cursor: pointer;
1263 | margin-right: 5px;
1264 | border-bottom: 3px solid rgb(255, 255, 255);
1265 | text-align: center;
1266 | }
1267 |
1268 | .n-sb-tab.n-sb-active {
1269 |
1270 | border-color: #0075ff;
1271 | }
1272 |
1273 |
1274 | .n-sb-tab-content {
1275 | display: none;
1276 | height: 50vh;
1277 | overflow-y: auto;
1278 | }
1279 |
1280 | .n-sb-active {
1281 | display: block !important;
1282 | }
1283 |
1284 | .sb_hidden {
1285 | display: none;
1286 | }
1287 |
1288 | .n-sb-tab-container .n-sb-tab {
1289 | padding: 9px;
1290 | width: 100%;
1291 | color: var(--input-text);
1292 | margin: 0 0 0px 0;
1293 | cursor: pointer;
1294 | text-transform: uppercase;
1295 | font-weight: bold;
1296 | box-sizing: border-box;
1297 | font-size: medium;
1298 | }
1299 |
1300 | .n-sb-section {
1301 | display: flex;
1302 | flex-direction: column;
1303 | margin-bottom: 20px;
1304 | padding: 10px;
1305 |
1306 | border-radius: 5px;
1307 | background-color: var(--comfy-menu-bg);
1308 | }
1309 | .n-sb-section-header {
1310 | display: flex;
1311 | align-items: center;
1312 | margin-bottom: 10px;
1313 | }
1314 | .n-sb-section-header input {
1315 | margin-left: 10px;
1316 | height: 23px;
1317 | }
1318 | .n-sb-section-panels {
1319 |
1320 | margin-bottom: 10px;
1321 | }
1322 | .n-sb-section-panels .add-panel-btn{
1323 | padding: 5px;
1324 | float: right;
1325 | }
1326 | .draggable {
1327 | cursor: move;
1328 | }
1329 | .panel-list {
1330 | list-style-type: none;
1331 | padding: 0;
1332 | }
1333 | .panel-list li {
1334 | margin-bottom: 5px;
1335 | margin-right: 5px;
1336 | padding: 5px !important;
1337 | background: var(--bg-color);
1338 | }
1339 | .panel-select{
1340 | margin-left: 9px !important;
1341 | width: 70% !important;
1342 | float: left !important;
1343 | }
1344 | .remove-panel {
1345 | bottom: 0;
1346 | float: right;
1347 | background: var(--comfy-menu-bg);
1348 | color: var(--input-text);
1349 | border: 1px solid var(--border-color);
1350 | border-radius: 5px;
1351 | cursor: pointer;
1352 | text-transform: uppercase;
1353 | font-weight: bold;
1354 | }
1355 | .n-sb-section-header label {
1356 | padding: 0 0 0px 10px;
1357 | }
1358 | .icon-input{
1359 | width: 20px;
1360 | text-align: center;
1361 | }
--------------------------------------------------------------------------------
/app/html/sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/home.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/js/fts_fuzzy_match.js:
--------------------------------------------------------------------------------
1 | // LICENSE
2 | //
3 | // This software is dual-licensed to the public domain and under the following
4 | // license: you are granted a perpetual, irrevocable license to copy, modify,
5 | // publish, and distribute this file as you see fit.
6 | //
7 | // VERSION
8 | // 0.1.0 (2016-03-28) Initial release
9 | //
10 | // AUTHOR
11 | // Forrest Smith
12 | //
13 | // CONTRIBUTORS
14 | // J�rgen Tjern� - async helper
15 | // Anurag Awasthi - updated to 0.2.0
16 |
17 | const SEQUENTIAL_BONUS = 25; // bonus for adjacent matches
18 | const SEPARATOR_BONUS = 30; // bonus if match occurs after a separator
19 | const CAMEL_BONUS = 30; // bonus if match is uppercase and prev is lower
20 | const FIRST_LETTER_BONUS = 15; // bonus if the first letter is matched
21 |
22 | const LEADING_LETTER_PENALTY = -5; // penalty applied for every letter in str before the first match
23 | const MAX_LEADING_LETTER_PENALTY = -15; // maximum penalty for leading letters
24 | const UNMATCHED_LETTER_PENALTY = -1;
25 |
26 | /**
27 | * Returns true if each character in pattern is found sequentially within str
28 | * @param {*} pattern string
29 | * @param {*} str string
30 | */
31 | function fuzzyMatchSimple(pattern, str) {
32 | let patternIdx = 0;
33 | let strIdx = 0;
34 | const patternLength = pattern.length;
35 | const strLength = str.length;
36 |
37 | while (patternIdx != patternLength && strIdx != strLength) {
38 | const patternChar = pattern.charAt(patternIdx).toLowerCase();
39 | const strChar = str.charAt(strIdx).toLowerCase();
40 | if (patternChar == strChar) ++patternIdx;
41 | ++strIdx;
42 | }
43 |
44 | return patternLength != 0 && strLength != 0 && patternIdx == patternLength
45 | ? true
46 | : false;
47 | }
48 |
49 | /**
50 | * Does a fuzzy search to find pattern inside a string.
51 | * @param {*} pattern string pattern to search for
52 | * @param {*} str string string which is being searched
53 | * @returns [boolean, number] a boolean which tells if pattern was
54 | * found or not and a search score
55 | */
56 | function fuzzyMatch(pattern, str) {
57 | const recursionCount = 0;
58 | const recursionLimit = 10;
59 | const matches = [];
60 | const maxMatches = 256;
61 |
62 | return fuzzyMatchRecursive(
63 | pattern,
64 | str,
65 | 0 /* patternCurIndex */,
66 | 0 /* strCurrIndex */,
67 | null /* srcMatces */,
68 | matches,
69 | maxMatches,
70 | 0 /* nextMatch */,
71 | recursionCount,
72 | recursionLimit
73 | );
74 | }
75 |
76 | function fuzzyMatchRecursive(
77 | pattern,
78 | str,
79 | patternCurIndex,
80 | strCurrIndex,
81 | srcMatces,
82 | matches,
83 | maxMatches,
84 | nextMatch,
85 | recursionCount,
86 | recursionLimit
87 | ) {
88 | let outScore = 0;
89 |
90 | // Return if recursion limit is reached.
91 | if (++recursionCount >= recursionLimit) {
92 | return [false, outScore];
93 | }
94 |
95 | // Return if we reached ends of strings.
96 | if (patternCurIndex === pattern.length || strCurrIndex === str.length) {
97 | return [false, outScore];
98 | }
99 |
100 | // Recursion params
101 | let recursiveMatch = false;
102 | let bestRecursiveMatches = [];
103 | let bestRecursiveScore = 0;
104 |
105 | // Loop through pattern and str looking for a match.
106 | let firstMatch = true;
107 | while (patternCurIndex < pattern.length && strCurrIndex < str.length) {
108 | // Match found.
109 | if (
110 | pattern[patternCurIndex].toLowerCase() === str[strCurrIndex].toLowerCase()
111 | ) {
112 | if (nextMatch >= maxMatches) {
113 | return [false, outScore];
114 | }
115 |
116 | if (firstMatch && srcMatces) {
117 | matches = [...srcMatces];
118 | firstMatch = false;
119 | }
120 |
121 | const recursiveMatches = [];
122 | const [matched, recursiveScore] = fuzzyMatchRecursive(
123 | pattern,
124 | str,
125 | patternCurIndex,
126 | strCurrIndex + 1,
127 | matches,
128 | recursiveMatches,
129 | maxMatches,
130 | nextMatch,
131 | recursionCount,
132 | recursionLimit
133 | );
134 |
135 | if (matched) {
136 | // Pick best recursive score.
137 | if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
138 | bestRecursiveMatches = [...recursiveMatches];
139 | bestRecursiveScore = recursiveScore;
140 | }
141 | recursiveMatch = true;
142 | }
143 |
144 | matches[nextMatch++] = strCurrIndex;
145 | ++patternCurIndex;
146 | }
147 | ++strCurrIndex;
148 | }
149 |
150 | const matched = patternCurIndex === pattern.length;
151 |
152 | if (matched) {
153 | outScore = 100;
154 |
155 | // Apply leading letter penalty
156 | let penalty = LEADING_LETTER_PENALTY * matches[0];
157 | penalty =
158 | penalty < MAX_LEADING_LETTER_PENALTY
159 | ? MAX_LEADING_LETTER_PENALTY
160 | : penalty;
161 | outScore += penalty;
162 |
163 | //Apply unmatched penalty
164 | const unmatched = str.length - nextMatch;
165 | outScore += UNMATCHED_LETTER_PENALTY * unmatched;
166 |
167 | // Apply ordering bonuses
168 | for (let i = 0; i < nextMatch; i++) {
169 | const currIdx = matches[i];
170 |
171 | if (i > 0) {
172 | const prevIdx = matches[i - 1];
173 | if (currIdx == prevIdx + 1) {
174 | outScore += SEQUENTIAL_BONUS;
175 | }
176 | }
177 |
178 | // Check for bonuses based on neighbor character value.
179 | if (currIdx > 0) {
180 | // Camel case
181 | const neighbor = str[currIdx - 1];
182 | const curr = str[currIdx];
183 | if (
184 | neighbor !== neighbor.toUpperCase() &&
185 | curr !== curr.toLowerCase()
186 | ) {
187 | outScore += CAMEL_BONUS;
188 | }
189 | const isNeighbourSeparator = neighbor == "_" || neighbor == " ";
190 | if (isNeighbourSeparator) {
191 | outScore += SEPARATOR_BONUS;
192 | }
193 | } else {
194 | // First letter
195 | outScore += FIRST_LETTER_BONUS;
196 | }
197 | }
198 |
199 | // Return best result
200 | if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
201 | // Recursive score is better than "this"
202 | matches = [...bestRecursiveMatches];
203 | outScore = bestRecursiveScore;
204 | return [true, outScore];
205 | } else if (matched) {
206 | // "this" score is better than recursive
207 | return [true, outScore];
208 | } else {
209 | return [false, outScore];
210 | }
211 | }
212 | return [false, outScore];
213 | }
214 |
215 | /**
216 | * Strictly optional utility to help make using fts_fuzzy_match easier for large data sets
217 | * Uses setTimeout to process matches before a maximum amount of time before sleeping
218 | *
219 | * To use:
220 | * const asyncMatcher = new ftsFuzzyMatchAsync(fuzzyMatch, "fts", "ForrestTheWoods",
221 | * function(results) { console.log(results); });
222 | * asyncMatcher.start();
223 | *
224 | * @param {*} matchFn function Matching function - fuzzyMatchSimple or fuzzyMatch.
225 | * @param {*} pattern string Pattern to search for.
226 | * @param {*} dataSet array Array of string in which pattern is searched.
227 | * @param {*} onComplete function Callback function which is called after search is complete.
228 | */
229 | function ftsFuzzyMatchAsync(matchFn, pattern, dataSet, onComplete) {
230 | const ITEMS_PER_CHECK = 1000; // performance.now can be very slow depending on platform
231 | const results = [];
232 | const max_ms_per_frame = 1000.0 / 30.0; // 30FPS
233 | let dataIndex = 0;
234 | let resumeTimeout = null;
235 |
236 | // Perform matches for at most max_ms
237 | function step() {
238 | clearTimeout(resumeTimeout);
239 | resumeTimeout = null;
240 |
241 | var stopTime = performance.now() + max_ms_per_frame;
242 |
243 | for (; dataIndex < dataSet.length; ++dataIndex) {
244 | if (dataIndex % ITEMS_PER_CHECK == 0) {
245 | if (performance.now() > stopTime) {
246 | resumeTimeout = setTimeout(step, 1);
247 | return;
248 | }
249 | }
250 |
251 | var str = dataSet[dataIndex];
252 | var result = matchFn(pattern, str);
253 |
254 | // A little gross because fuzzy_match_simple and fuzzy_match return different things
255 | if (matchFn === fuzzyMatchSimple && result == true) results.push(str);
256 | else if (matchFn === fuzzyMatch && result[0] == true) results.push([result[1], str]);
257 | }
258 |
259 | onComplete(results);
260 | return null;
261 | }
262 |
263 | // Abort current process
264 | this.cancel = function() {
265 | if (resumeTimeout !== null) clearTimeout(resumeTimeout);
266 | };
267 |
268 | // Must be called to start matching.
269 | // I tried to make asyncMatcher auto-start via "var resumeTimeout = step();"
270 | // However setTimout behaving in an unexpected fashion as onComplete insisted on triggering twice.
271 | this.start = function() {
272 | step();
273 | };
274 |
275 | // Process full list. Blocks script execution until complete
276 | this.flush = function() {
277 | max_ms_per_frame = Infinity;
278 | step();
279 | };
280 | }
281 |
282 | export { fuzzyMatch, fuzzyMatchSimple, ftsFuzzyMatchAsync };
283 |
--------------------------------------------------------------------------------
/app/js/functions/sb_fn.js:
--------------------------------------------------------------------------------
1 |
2 | //load folder name
3 |
4 |
5 | function getNameFolderSync() {
6 | const xhr = new XMLHttpRequest();
7 | xhr.open("GET", "sidebar/current", false); // false = sincrona (blasfemia)
8 | xhr.send(null);
9 |
10 | if (xhr.status === 200) {
11 | return JSON.parse(xhr.responseText);
12 | } else {
13 | throw new Error("Errore nella richiesta");
14 | }
15 | }
16 | const nameFolder = getNameFolderSync();
17 | const cnPath = `../extensions/${nameFolder}/`;
18 |
19 | function addSidebarStyles(cssPath) {
20 | const timestamp = new Date().getTime();
21 | const linkElement = document.createElement("link");
22 | linkElement.rel = "stylesheet";
23 | linkElement.type = "text/css";
24 | linkElement.href =`${cnPath}${cssPath}?v=${timestamp}`
25 | document.head.appendChild(linkElement);
26 | }
27 |
28 |
29 | function sidebarAddNode(name, text, x, y) {
30 | const node = LiteGraph.createNode(name, text)
31 | if (node) {
32 | const pos =
33 | [
34 | x,
35 | y
36 | ]
37 | node.pos = pos;
38 | app.graph.add(node)
39 | }
40 | }
41 |
42 | function sdExpandAll(forceExpand = 0) {
43 | const categoryItems = document.querySelectorAll(".content_sidebar .sidebarCategory");
44 | const side_bar_status = document.querySelector(".content_sidebar").dataset.expanded;
45 | const expand_nodes = document.querySelectorAll(".expand_node");
46 |
47 | let display_value = "true";
48 |
49 | if (side_bar_status === "true" && forceExpand === 0) {
50 | display_value = "none";
51 | expand_nodes.forEach(node => {
52 | if (node.tagName.toLowerCase() === "button") {
53 | node.innerHTML = ``;
64 | }
65 | });
66 | document.querySelector(".content_sidebar").dataset.expanded = "false";
67 |
68 | } else {
69 | display_value = "block";
70 | expand_nodes.forEach(node => {
71 | if (node.tagName.toLowerCase() === "button") {
72 | node.innerHTML = ``;
81 | }
82 | });
83 | document.querySelector(".content_sidebar").dataset.expanded = "true";
84 | }
85 |
86 | categoryItems.forEach(function (categoryItem) {
87 | const displayNamesList = categoryItem.querySelector("ul");
88 |
89 | if (displayNamesList) {
90 | displayNamesList.style.display = display_value;
91 | }
92 | });
93 | }
94 |
95 |
96 |
97 | function handleSearch(categorySearchToggle,searchSection,searchIdInput) {
98 |
99 |
100 | return new Promise((resolve, reject) => {
101 | const options = {
102 | keys: ['textContent','category'],
103 | threshold: 0.5,
104 | useExtendedSearch: true
105 | };
106 |
107 |
108 | const searchTerm = document.getElementById(searchIdInput).value.toLowerCase();
109 | const categoryItems = document.querySelectorAll(".sidebarCategory li");
110 | const categories = document.querySelectorAll(searchSection+" .sidebarCategory");
111 | const listItems = document.querySelectorAll(searchSection+" .sidebarItem");
112 |
113 |
114 | /*reset*/
115 | categoryItems.forEach(category => {
116 | const subItems = category.querySelectorAll("li");
117 | category.style.display = "block";
118 |
119 | subItems.forEach(sub => {
120 | sub.style.display = "block";
121 | });
122 |
123 | });
124 |
125 |
126 |
127 | const sidebarItems = categorySearchToggle ? categories : listItems;
128 |
129 | sidebarItems.forEach(item => {
130 | let itemText = item.textContent.toLowerCase();
131 | if (categorySearchToggle) {
132 |
133 | itemText = Array.from(item.childNodes)
134 | .filter(node => node.nodeType === Node.TEXT_NODE)
135 | .map(node => node.textContent.trim())
136 | .join(' ').toLowerCase();
137 | }
138 |
139 | //fts_fuzzy_match(searchTerm, cleanText(itemText))
140 | const isInSearchTerm = itemText.includes(searchTerm);
141 | item.style.display = isInSearchTerm ? "block" : "none";
142 | });
143 |
144 | /* hide empty categories */
145 | categories.forEach(category => {
146 | const subItems = category.querySelectorAll("li");
147 | const areAllHidden = Array.from(subItems).every(subItem => subItem.style.display === "none");
148 | category.style.display = areAllHidden ? "none" : category.style.display;
149 | });
150 |
151 | sdExpandAll(1);
152 | resolve(searchTerm);
153 | });
154 | }
155 |
156 | //PREVIEW
157 | function safeObjectKeys(obj) {
158 | try {
159 | return Object.keys(obj);
160 | } catch (error) {
161 | //console.error('Error while trying to get object keys:', error);
162 | return [];
163 | }
164 | }
165 |
166 | function createNodePreview(nodeID) {
167 | let description = "";
168 | let rows = "";
169 | let last_rows = "";
170 | let error = '';
171 | const data = LiteGraph.registered_node_types;
172 |
173 |
174 | try{
175 | description = data[nodeID].nodeData.description;
176 | }catch(err){
177 | description = "";
178 | }
179 | try{
180 | let inputs = data[nodeID].nodeData.input; //divided between optional and required
181 | let outputs_name = data[nodeID].nodeData.output_name;
182 | let outputs = data[nodeID].nodeData.output;
183 | //console.log(category, inputs, outputs);
184 | //create array with objectkeys from input and output
185 | const inputArray = [];
186 | const outputArray = [];
187 |
188 | const inputKeysRequired = safeObjectKeys(inputs.required)
189 | const inputKeysOptional = safeObjectKeys(inputs.optional)
190 |
191 |
192 | for (let i = 0; i < inputKeysRequired.length; i++) {
193 | try{
194 | let thirdV = null;
195 | let secondV = inputs.required[inputKeysRequired[i]][0];
196 |
197 | if (Array.isArray(secondV)){
198 | //array
199 | secondV = inputs.required[inputKeysRequired[i]][0][0];
200 | thirdV = inputs.required[inputKeysRequired[i]][0][0];
201 | }
202 |
203 | let isArr = false
204 |
205 | try {
206 | if( typeof inputs.required[inputKeysRequired[i]][0] === 'object'){
207 | isArr = true
208 | }
209 | }
210 | catch(err) {
211 | console.log("error",err)
212 | }
213 | if (inputs.required[inputKeysRequired[i]][1]!=undefined && Object.keys(inputs.required[inputKeysRequired[i]][1]).length > 0){
214 |
215 |
216 | if (!isArr){
217 | //object
218 | thirdV = inputs.required[inputKeysRequired[i]][1].default;
219 | }
220 | else{
221 | thirdV = inputs.required[inputKeysRequired[i]][0][0];
222 | }
223 |
224 |
225 | }
226 |
227 |
228 | inputArray.push([inputKeysRequired[i],secondV,thirdV]);
229 | }catch(err){
230 | console.log("error",err)
231 | }
232 | }
233 |
234 | try{
235 | for (let i = 0; i < inputKeysOptional.length; i++) {
236 | try{
237 | let thirdV = null;
238 | let secondV = inputs.optional[inputKeysOptional[i]][0];
239 | if (Array.isArray(secondV)){
240 | //array
241 | secondV = inputs.optional[inputKeysOptional[i]][0][0];
242 | thirdV = inputs.optional[inputKeysOptional[i]][0][0];
243 | }
244 |
245 | let isArr = false
246 |
247 | try {
248 | if( typeof inputs.optional[inputKeysOptional[i]][0] === 'object'){
249 | isArr = true
250 | }
251 | }
252 | catch(err) {
253 | console.log("error",err)
254 | }
255 |
256 | if (inputs.optional[inputKeysOptional[i]][1]!=undefined && Object.keys(inputs.optional[inputKeysOptional[i]][1]).length > 0){
257 |
258 | if (!isArr){
259 | //object
260 | thirdV = inputs.optional[inputKeysOptional[i]][1].default;
261 | }
262 | else{
263 | thirdV = inputs.optional[inputKeysOptional[i]][0][0];
264 | }
265 | }
266 | inputArray.push([inputKeysOptional[i],secondV,thirdV]);
267 | }catch(err){
268 | console.log("error",err)
269 | }
270 | }
271 |
272 | }catch(err){
273 | console.log(err)
274 | }
275 |
276 |
277 |
278 |
279 | for (let i = 0; i < outputs.length; i++) {
280 | if (outputs_name[i] != undefined) {
281 | outputArray.push([outputs[i],outputs_name[i]]);
282 | }
283 | else{
284 | outputArray.push(outputs[i],outputs[i]);
285 | }
286 |
287 |
288 | }
289 |
290 |
291 |
292 | let length_loop = outputArray.length;
293 | if (inputArray.length> outputArray.length){
294 |
295 | length_loop = inputArray.length;
296 | }
297 | for (let i = 0; i < length_loop; i++) {
298 |
299 | const inputList= inputArray[i] ? inputArray[i] : null;
300 | const outputList= outputArray[i] ? outputArray[i] : null;
301 | let inputName = "";
302 | let inputType = "";
303 | let inputValue = null;
304 | let outputType = "";
305 | let outputName = "";
306 |
307 | if (inputList != null){
308 | inputName = inputList[0];
309 | inputType = inputList[1];
310 | inputValue = inputList[2];
311 | }else
312 | {
313 | inputName = "";
314 | inputType = "sb_hidden";
315 | inputValue = null;
316 | }
317 | if (outputList != null){
318 | outputType = outputList[0];
319 | outputName = outputList[1];
320 |
321 | }else
322 | {
323 | outputType = "sb_hidden";
324 | outputName = "";
325 | }
326 |
327 | array_list_type = [
328 | "BOOLEAN",
329 | "CLIP",
330 | "CLIP_VISION",
331 | "CLIP_VISION_OUTPUT",
332 | "CONDITIONING",
333 | "CONTROL_NET",
334 | "CONTROL_NET_WEIGHTS",
335 | "FLOAT",
336 | "GLIGEN",
337 | "IMAGE",
338 | "IMAGEUPLOAD",
339 | "INT",
340 | "LATENT",
341 | "LATENT_KEYFRAME",
342 | "MASK",
343 | "MODEL",
344 | "SAMPLER",
345 | "SIGMAS",
346 | "STRING",
347 | "STYLE_MODEL",
348 | "T2I_ADAPTER_WEIGHTS",
349 | "TAESD",
350 | "TIMESTEP_KEYFRAME",
351 | "UPSCALE_MODEL",
352 | "VAE",
353 | ]
354 | if (inputValue != null){
355 | if (inputType == "STRING"){
356 | last_rows += `
357 |
358 |
${inputName}
359 |
360 |
${inputValue}
361 |
362 |
`;
363 |
364 | }else{
365 | last_rows += `
366 |
◀
367 |
${inputName}
368 |
369 |
${inputValue}
370 |
▶
371 |
`;
372 |
373 |
374 | }
375 | inputType = "sb_hidden";
376 | inputName = "";
377 | }
378 | else{
379 | if (inputType == "STRING"){
380 | last_rows += `
381 |
382 |
${inputName}
383 |
384 |
385 |
386 |
`;
387 | }
388 |
389 | }
390 | if (inputType != "STRING"){
391 | rows += `
392 |
393 |
${inputName}
394 |
395 |
${outputName}
396 |
397 |
`;
398 |
399 | }
400 |
401 |
402 |
403 | }
404 | //console.log( inputArray,outputArray);
405 |
406 | ////create preview
407 | //const nodePreview = document.createElement("div");
408 | //nodePreview.classList.add("node_preview");
409 | //nodePreview.id = nodeID;
410 | error = "";
411 | }catch(err){
412 | error = "NOT AVAILABLE";
413 | rows = ``
414 | last_rows = ``
415 | }
416 | return [`
417 |
418 |
PREVIEW ${error}
419 | ${rows}
420 | ${last_rows}
421 |
`,description];
422 | }
423 |
424 |
425 |
426 | async function getPreview(item, previewDiv, sidebad_view_width, contentFunction, configFunction = null) {
427 | try {
428 | item.addEventListener('mouseover', async function () {
429 | let shouldShowPreview = true;
430 |
431 |
432 | if (configFunction) {
433 | shouldShowPreview = await configFunction();
434 | }
435 |
436 | if (shouldShowPreview && this.classList.contains('sidebarItem') && this.tagName === 'LI') {
437 | let descriptionDiv = "";
438 | const itemPosition = getElementPosition(this);
439 | let previewDivTop = 0;
440 |
441 | // Usa la funzione contentFunction per ottenere il contenuto del preview
442 | const [previewContent, node_description] = await contentFunction(item);
443 |
444 | if (node_description) {
445 | descriptionDiv = "" + node_description + "
";
446 | }
447 |
448 | previewDiv.innerHTML = previewContent + descriptionDiv;
449 | previewDiv.style.display = 'block';
450 | const correction_offset = 45;
451 | let sbtop = calcSBTop();
452 |
453 | if (itemPosition.top - this.offsetHeight >= 0 && itemPosition.top + previewDiv.offsetHeight < document.body.offsetHeight) {
454 | previewDivTop = itemPosition.top - this.offsetHeight - sbtop;
455 | } else if (itemPosition.top - this.offsetHeight - previewDiv.offsetHeight <= 0) {
456 | previewDivTop = 0 + correction_offset - sbtop;
457 | } else {
458 | previewDivTop = (itemPosition.top + this.offsetHeight) - previewDiv.offsetHeight - sbtop;
459 | }
460 |
461 | let sidebar_width = parseInt(getVar("sidebarWidth")) || 500;
462 |
463 | previewDiv.style.top = `${previewDivTop}px`;
464 |
465 | const previewDivLeft = sidebar_width - sidebad_view_width + correction_offset;
466 |
467 | if (sbPosition == "left") {
468 | previewDiv.style.left = `${previewDivLeft}px`;
469 | } else {
470 | previewDiv.style.right = `${previewDivLeft}px`;
471 | }
472 | }
473 | });
474 |
475 | item.addEventListener('mouseout', function () {
476 | previewDiv.style.display = 'none';
477 | });
478 | } catch (error) {
479 | console.log(error);
480 | }
481 | }
482 |
483 |
484 |
485 |
486 | //change view
487 | function switchTab(tabId) {
488 | if (tabId != null) {
489 | const tabContents = document.querySelectorAll('.content_sidebar');
490 | const tabHead = document.querySelectorAll('.sidebar-header');
491 |
492 | let switch_id= tabId.replace("panel", "switch");
493 | const switchButton = document.getElementById(switch_id);
494 |
495 | if (tabId=="panel_home"){
496 | const tabIdHead = document.getElementById("sidebar-header");
497 | tabIdHead.classList.remove('sb_hidden');
498 | document.getElementById("switch_home").classList.add('sb_button_active');
499 |
500 | }
501 | else{
502 | tabHead.forEach(content => {
503 | content.classList.add('sb_hidden');
504 |
505 | })
506 | }
507 | // Hide all tabs
508 | tabContents.forEach(content => {
509 | content.classList.add('sb_hidden');
510 | let switch_id_disbled= content.id.replace("panel", "switch");
511 | const switchButtonDisbled = document.getElementById(switch_id_disbled);
512 | switchButtonDisbled.classList.remove('sb_button_active');
513 | });
514 |
515 |
516 |
517 | // Show the content of the clicked tab
518 | document.getElementById(tabId).classList.remove('sb_hidden');
519 | switchButton.classList.add('sb_button_active');
520 |
521 | setVar('sb_current_tab', tabId);
522 | }
523 | }
524 |
525 |
526 |
527 | // PINNED ITEMS
528 | function savePinnedItems(pinnedItems) {
529 | const pinnedItemsString = JSON.stringify(pinnedItems);
530 | //setVar('pinnedItems', pinnedItemsString, 9999);
531 | updateConfiguration('sb_pinnedItems',pinnedItemsString)
532 | }
533 |
534 |
535 | async function loadPinnedItems() {
536 |
537 | const pinnedItemsString = await getConfiguration('sb_pinnedItems');//getVar('pinnedItems');
538 |
539 | if (pinnedItemsString) {
540 | return JSON.parse(pinnedItemsString);
541 | }
542 | return [];
543 | }
544 |
545 |
546 | function migrationSettings() {
547 | 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"];
548 | const newSettingsList = ["sb_pinnedItems","sb_noderadius","sb_barbottom","sb_bartop","sb_blur","sb_opacity","sb_font","sb_node"];
549 |
550 | oldSettingsList.forEach((setting, index) => {
551 | try{
552 | const newSetting = newSettingsList[index];
553 | const oldSettingValue = getVar(setting);
554 | if (oldSettingValue) {
555 | if (setting != "pinnedItems") {
556 | updateConfiguration(newSetting, parseFloat(oldSettingValue.replace('"',"").replace('"',"")));
557 | } else {
558 | updateConfiguration(newSetting, oldSettingValue);
559 | }
560 | renameVar(setting, "bk_"+setting)
561 | }
562 | }catch(err){
563 | console.log(err)
564 | }
565 | })
566 | // updateConfiguration('sb_pinnedItems',pinnedItemsString) ;
567 | }
568 |
569 |
570 |
571 | function toggleSHSB(force = undefined) {
572 | const side_bars = document.querySelectorAll(".content_sidebar");
573 | const main_sidebar = document.getElementById('sidebar');
574 | const search_bar = document.getElementById('searchInputSB');
575 | const scrollToTopButton = document.getElementById("sb_scrollToTopButton");
576 | const clearIcon = document.querySelector(".clearIcon");
577 | const searchCategoryIcon = document.querySelector(".searchCategoryIcon");
578 | const switch_sidebar = document.getElementById('switch_sidebar');
579 | const sidebar_views = document.getElementById("sidebar_views");
580 |
581 | //search_bar.classList.toggle('closed',force);
582 |
583 | side_bars.forEach(side_bar => {
584 | //side_bar.classList.toggle('closed',force);
585 |
586 | if (force !== undefined) {
587 | if (force) {
588 |
589 | setVar("sb_state", "closed");
590 | side_bar.classList.add('closed');
591 | clearIcon.classList.add('closed');
592 | searchCategoryIcon.classList.add('closed');
593 | search_bar.classList.add('closed');
594 | scrollToTopButton.classList.add('closed');
595 | sidebar_views.classList.add('full_rounded');
596 |
597 | } else {
598 |
599 | setVar("sb_state", "open");
600 | side_bar.classList.remove('closed');
601 | clearIcon.classList.remove('closed');
602 | searchCategoryIcon.classList.remove('closed');
603 | search_bar.classList.remove('closed');
604 | scrollToTopButton.classList.remove('closed');
605 | sidebar_views.classList.remove('full_rounded');
606 | setVar("sb_minimized", "true");
607 | }
608 | } else {
609 |
610 | if (side_bar.classList.contains('closed')) {
611 |
612 | setVar("sb_state", "open");
613 |
614 | side_bar.classList.remove('closed');
615 | clearIcon.classList.remove('closed');
616 | searchCategoryIcon.classList.remove('closed');
617 | search_bar.classList.remove('closed');
618 | sidebar_views.classList.remove('full_rounded');
619 | scrollToTopButton.classList.remove('closed');
620 | //fix for keyboard shortcuts
621 | if (getVar("sb_minimized") == "true") {
622 | switch_sidebar.style.filter = "brightness(0.8)";
623 | }
624 |
625 | } else {
626 | if (getVar("sb_minimized") == "false") {
627 |
628 | setVar("sb_state", "closed");
629 | side_bar.classList.add('closed');
630 | clearIcon.classList.add('closed');
631 | searchCategoryIcon.classList.add('closed');
632 | search_bar.classList.add('closed');
633 | scrollToTopButton.classList.add('closed');
634 | sidebar_views.classList.add('full_rounded');
635 | switch_sidebar.style.filter = "brightness(1.0)";
636 | } else {
637 | switch_sidebar.style.filter = "brightness(0.8)";
638 | }
639 | }
640 | }
641 |
642 | });
643 |
644 |
645 |
646 | if (getVar("sb_minimized") == "false") {
647 |
648 | if (force == undefined) {
649 |
650 | setVar("sb_state", "closed");
651 | setVar("sb_minimized", true);
652 | }
653 | main_sidebar.style.width = '45px';
654 | } else {
655 |
656 | if (force == undefined) {
657 |
658 | setVar("sb_state", "open");
659 | setVar("sb_minimized", false);
660 | main_sidebar.style.width = getVar("sidebarWidth") + 'px' || '300px';
661 | } else if (force == true) {
662 |
663 |
664 | setVar("sb_state", "closed");
665 |
666 | main_sidebar.style.width = '45px';
667 |
668 | } else {
669 |
670 | setVar("sb_state", "open");
671 | main_sidebar.style.width = getVar("sidebarWidth") + 'px' || '300px';
672 | }
673 |
674 | }
675 |
676 | }
677 |
--------------------------------------------------------------------------------
/app/js/functions/utils.js:
--------------------------------------------------------------------------------
1 |
2 | let new_menu_options = [];
3 | let new_submenu_options = [];
4 | let new_menu_options_callback = [];
5 | let new_submenu_options_callback = [];
6 | const settingsStyle = `
7 |
8 | #comfy-settings-dialog input{
9 | background: var(--comfy-input-bg);
10 | color: var(--input-text);
11 | border: 1px solid var(--border-color);
12 | padding: 5px;
13 | }
14 | #comfy-settings-dialog tr > td:first-child {
15 | text-align: left;
16 | }
17 | #comfy-settings-dialog select{
18 | background: var(--bg-color);
19 | color: var(--drag-text);
20 | padding: 5px;
21 | }
22 |
23 | #comfy-settings-dialog {
24 |
25 | background: var(--bg-color);
26 | }
27 |
28 | #comfy-settings-dialog::-webkit-scrollbar {
29 | margin-top: 0.5rem;
30 | height: 1rem;
31 | width: .6rem;
32 | top: 10px;
33 | background-color: transparent;
34 | }
35 |
36 | #comfy-settings-dialog::-webkit-scrollbar:horizontal {
37 | height: .5rem;
38 | width: 2.5rem
39 | }
40 |
41 | #comfy-settings-dialog::-webkit-scrollbar-track {
42 | background-color: var(--comfy-input-bg);
43 | border-radius: 9999px
44 |
45 | }
46 |
47 | #comfy-settings-dialog::-webkit-scrollbar-thumb {
48 | --tw-border-opacity: 1;
49 | background-color: var(--border-color);
50 | border: 0;
51 | border-radius: 9999px;
52 |
53 | }
54 |
55 | #comfy-settings-dialog::-webkit-scrollbar-thumb:hover {
56 | --tw-bg-opacity: 1;
57 | background-color: var(--border-color);
58 |
59 | }
60 |
61 | .comfy-table td, .comfy-table th {
62 | border: 0px solid var(--border-color);
63 | padding: 8px;
64 | }
65 |
66 |
67 | `
68 | async function api_get(url) {
69 | var response = await api.fetchApi(url, { cache: "no-store" })
70 | return await response.json()
71 | }
72 | function getDynamicCSSRule(selector, property) {
73 | const styleId = 'dynamic-styles';
74 | const customStyle = document.getElementById(styleId);
75 |
76 | if (!customStyle) {
77 | return null;
78 | }
79 |
80 | const existingRule = Array.from(customStyle.sheet.cssRules).find(rule => rule.selectorText === selector);
81 |
82 | if (existingRule) {
83 | return existingRule.style.getPropertyValue(property);
84 | }
85 |
86 | return null;
87 | }
88 |
89 |
90 | function addDynamicCSSRule(selector, property, value) {
91 | const styleId = 'dynamic-styles';
92 | let customStyle = document.getElementById(styleId);
93 |
94 | if (!customStyle) {
95 | customStyle = document.createElement('style');
96 | customStyle.type = 'text/css';
97 | customStyle.id = styleId;
98 | document.head.appendChild(customStyle);
99 | }
100 |
101 | const existingRule = Array.from(customStyle.sheet.cssRules).find(rule => rule.selectorText === selector);
102 | if (existingRule) {
103 | existingRule.style.setProperty(property, value, 'important');
104 | } else {
105 |
106 | customStyle.sheet.insertRule(`${selector} { ${property}: ${value} !important; }`, customStyle.sheet.cssRules.length);
107 | }
108 | }
109 |
110 | function addJavascriptVar(name, value) {
111 | const scripta = document.createElement('script');
112 | scripta.type = 'text/javascript';
113 | scripta.innerHTML = "var "+name+" = "+value;
114 |
115 | document.head.appendChild(scripta);
116 |
117 | }
118 | function getVar(name) {
119 | var varValue = localStorage.getItem(name);
120 | if (varValue === null) {
121 | if (name=="pinnedItems") {
122 | //get cookie pinnedItems
123 | var cookies = document.cookie.split('; ');
124 | cookies.forEach(cookie => {
125 | const [cookieName, cookieValue] = cookie.split('=');
126 | if (cookieName === name) {
127 |
128 | setVar(name, cookieValue)
129 |
130 | }
131 | })
132 | //delete cookie pinnedItems
133 | document.cookie = name + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
134 | }
135 | else {
136 | return null;
137 | }
138 |
139 | }else {
140 | return varValue;
141 | }
142 |
143 | return null;
144 | }
145 |
146 | function setVar(name, value) {
147 | /*migration to localstorage*/
148 | localStorage.setItem(name, value);
149 |
150 | }
151 | function removeVar(name) {
152 | localStorage.removeItem(name);
153 | }
154 |
155 | function renameVar(oldName, newName) {
156 | const oldValue = getVar(oldName);
157 | setVar(newName, oldValue);
158 | removeVar(oldName);
159 |
160 | }
161 |
162 | function getElementPosition(element) {
163 | const rect = element.getBoundingClientRect();
164 | return {
165 | top: rect.top,
166 | left: rect.left
167 | };
168 | }
169 |
170 | function reverseAndAppend(originalName) {
171 | let words;
172 | let lastWord;
173 | let reversedWords;
174 | let reversedString;
175 | let finalName;
176 | if (originalName.includes(' ')) {
177 | const words = originalName.split(' ');
178 |
179 | lastWord = words.pop();
180 |
181 |
182 | reversedWords = words.reverse();
183 | reversedString = reversedWords.join(' ');
184 |
185 | finalName = originalName + ' ' + reversedString;
186 |
187 | return finalName;
188 |
189 | } else {
190 |
191 |
192 | words = originalName.split(/(?=[A-Z])/);
193 | lastWord = words.pop();
194 | reversedWords = words.reverse();
195 | reversedString = reversedWords.join('');
196 | finalName = originalName + reversedString;
197 |
198 | return finalName;
199 |
200 | }
201 | }
202 | function cleanText(text) {
203 | let cleanedText = text.replace(/[^a-zA-Z0-9\s\n\-]/g, '');
204 | cleanedText = cleanedText.replace(/\s+/g, ' ');
205 | cleanedText = cleanedText.replace(/\n/g, '');
206 | return cleanedText;
207 | }
208 |
209 | function calcSBTop() {
210 | let sb = document.getElementById('sidebar');
211 | let sbtop = window.getComputedStyle(sb).getPropertyValue('top').replace('px', '');
212 | if (getVar('Comfy.Settings.Comfy.UseNewMenu') === '"Top"') {
213 |
214 | return parseInt(sbtop) + 35;
215 | }
216 | else{
217 | return sbtop;
218 | }
219 | }
220 | //CONTEXT MENU
221 | function createContextMenu(event, subMenus,settingsData) {
222 | event.preventDefault();
223 | hideAllSubMenus();
224 |
225 | const sbcontextMenu = document.getElementById('customMenu');
226 | const menuOptions = document.getElementById('menu-options');
227 |
228 | // Clear existing menu items
229 | menuOptions.innerHTML = '';
230 |
231 | // Populate main options
232 | settingsData.menuctx_options.forEach((option, index) => {
233 | const li = document.createElement('li');
234 | li.textContent = option;
235 | li.addEventListener('mouseenter', () => {
236 | hideAllSubMenus();
237 | showSubMenu(subMenus[index], li);
238 | });
239 | try {
240 |
241 |
242 | if (settingsData.menuctx_opt_callback[index].indexOf(".") !== -1) {
243 |
244 | // Handle sub menu option
245 | var mainfunc = settingsData.menuctx_opt_callback[index].split(".")[0];
246 | var subfunc = settingsData.menuctx_opt_callback[index].split(".")[1];
247 | if (typeof (window[mainfunc][subfunc]) === 'function') {
248 |
249 |
250 | li.addEventListener('click', () => {
251 | // Handle sub menu click
252 | window[mainfunc][subfunc](event);
253 | });
254 |
255 | }
256 | }
257 | else{
258 |
259 | if (typeof (window[settingsData.menuctx_opt_callback[index]]) === 'function') {
260 | li.addEventListener('click', () => {
261 | // Handle main option click
262 | window[settingsData.menuctx_opt_callback[index]](event);
263 |
264 | });
265 | }
266 | }
267 | } catch (error) {
268 | console.log(error);
269 |
270 | }
271 | menuOptions.appendChild(li);
272 | });
273 |
274 | // Show context menu
275 | sbcontextMenu.style.display = 'block';
276 | let sbtop = calcSBTop();
277 |
278 | sbcontextMenu.style.top = (event.clientY - sbtop) + 'px';
279 | if (sbPosition === "left") {
280 |
281 | sbcontextMenu.style.left = event.clientX + 'px';
282 | } else {
283 | // const sidebar_width = getVar("sidebarWidth");
284 | //console.log(parseInt(sidebar_width))
285 | //console.log(event)
286 | //console.log(event.clientX - parseInt(sidebar_width) )
287 | //temp bugfix
288 | sbcontextMenu.style.left = event.layerX + 'px';
289 | }
290 |
291 | // Hide context menu on body click
292 | document.body.addEventListener('click', hideContextMenu);
293 | }
294 |
295 | function showSubMenu(subMenu, element) {
296 | try{
297 | if (subMenu) {
298 | subMenu.style.display = 'block';
299 |
300 | const rect = element.getBoundingClientRect();
301 | subMenu.style.top = element.offsetTop + 'px';
302 |
303 | subMenuVisible = true;
304 | }
305 | } catch (error) {
306 | console.log(error);
307 | }
308 | }
309 |
310 | function hideAllSubMenus() {
311 | const subMenus = document.querySelectorAll('.sub-menu');
312 | subMenus.forEach(subMenu => {
313 | subMenu.style.display = 'none';
314 | });
315 | subMenuVisible = false;
316 | }
317 |
318 | function hideContextMenu() {
319 | const sbcontextMenu = document.getElementById('customMenu');
320 | sbcontextMenu.style.display = 'none';
321 | document.body.removeEventListener('click', hideContextMenu);
322 | }
323 |
324 | let subMenuVisible = false;
325 | let lastTargetClicked = null;
326 |
327 | async function setContextMenu(settingsData,class_menu_item,main=0,exluded_class=null,included_class = null) {
328 |
329 |
330 | if (main==1){
331 | settingsData.menuctx_options = settingsData.menuctx_options.concat(new_menu_options);
332 | settingsData.menuctx_subOptions = settingsData.menuctx_subOptions.concat(new_submenu_options);
333 | settingsData.menuctx_opt_callback = settingsData.menuctx_opt_callback.concat(new_menu_options_callback);
334 | settingsData.menuctx_sub_opt_callback = settingsData.menuctx_sub_opt_callback.concat(new_submenu_options_callback);
335 | }
336 | const sbcontextMenu = document.getElementById('customMenu');
337 | const menuOptions = document.createElement("ul");
338 | menuOptions.id = "menu-options";
339 | sbcontextMenu.appendChild(menuOptions);
340 |
341 | const subMenus = [];
342 | for (let i = 0; i < settingsData.menuctx_subOptions.length; i++) {
343 |
344 |
345 |
346 | const subMenu = document.createElement("ul");
347 | subMenu.id = `sub-menu-op${i + 1}`;
348 | subMenu.classList.add("sub-menu");
349 | if (settingsData.menuctx_subOptions[i].length == 0) {
350 | subMenu.classList.add("cat_empty");
351 | }
352 |
353 | sbcontextMenu.appendChild(subMenu);
354 | subMenus.push(subMenu);
355 |
356 | for (let j = 0; j < settingsData.menuctx_subOptions[i].length; j++) {
357 | const li = document.createElement("li");
358 | li.textContent = settingsData.menuctx_subOptions[i][j];
359 | subMenu.appendChild(li);
360 | try {
361 |
362 | if (settingsData.menuctx_sub_opt_callback[i][j].indexOf(".") !== -1) {
363 |
364 | // Handle sub menu option
365 | var mainfunc = settingsData.menuctx_sub_opt_callback[i][j].split(".")[0];
366 | var subfunc = settingsData.menuctx_sub_opt_callback[i][j].split(".")[1];
367 | if (typeof (window[mainfunc][subfunc]) === 'function') {
368 |
369 |
370 | li.addEventListener('click', () => {
371 | // Handle sub menu click
372 |
373 | window[mainfunc][subfunc](lastTargetClicked,settingsData.menuctx_subOptions[i][j]);
374 |
375 | });
376 |
377 | }
378 | }
379 | else{
380 |
381 | if (typeof (window[settingsData.menuctx_sub_opt_callback[i][j]]) === 'function') {
382 | li.addEventListener('click', () => {
383 | // Handle main option click
384 |
385 |
386 | window[settingsData.menuctx_sub_opt_callback[i][j]](lastTargetClicked,settingsData.menuctx_subOptions[i][j]);
387 |
388 | });
389 | }
390 | }
391 |
392 |
393 |
394 |
395 |
396 |
397 | } catch (error) {
398 | console.log(error);
399 | }
400 | }
401 |
402 | }
403 |
404 | const sidebarItems = document.querySelectorAll(class_menu_item);
405 | //const sb =document.getElementById('panel_home');
406 |
407 |
408 | for (const sidebarItem of sidebarItems) {
409 | sidebarItem.addEventListener('contextmenu', (event) => {
410 |
411 | if(!event.target.classList.contains(exluded_class)){
412 |
413 | if (included_class != null && !event.target.classList.contains(included_class)){
414 | return;
415 | }
416 | createContextMenu (event, subMenus,settingsData);
417 | lastTargetClicked = event;
418 | }
419 | });
420 | }
421 |
422 | //ContextMenu
423 | //sb.addEventListener("contextmenu", function(event) {
424 | // event.preventDefault();
425 | //
426 | // const customMenu = document.getElementById("customMenu");
427 | // customMenu.style.display = "block";
428 | // customMenu.style.left = event.pageX + "px";
429 | // customMenu.style.top = event.pageY + "px";
430 | //});
431 | //
432 | window.addEventListener("click", function(event) {
433 | const customMenu = document.getElementById("customMenu");
434 | customMenu.style.display = "none";
435 | });
436 | }
437 |
438 |
439 | // Function to check for special characters
440 | function containsSpecialCharacters(name, level=0) {
441 | // Define a regular expression pattern to match special characters
442 | if (level == 0) {
443 | var specialCharacters = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/;
444 | } else if (level == 1) {
445 | var specialCharacters = /[\*':"\\|,<>\/?]/g;
446 | } else{
447 | var specialCharacters = /['"]/gi;
448 | }
449 |
450 | // Return true if the name contains any special characters, false otherwise
451 | return specialCharacters.test(name);
452 | }
453 |
454 | async function reloadCtxMenu() {
455 | //load folder name
456 | var nameRequest = await fetch('sidebar/current');
457 | var nameFolder = await nameRequest.json();
458 |
459 | const cnPath = `../extensions/${nameFolder}/`
460 |
461 | const response3 = await fetch(cnPath +'settings.json');
462 | const settingsData = await response3.json();
463 |
464 | setContextMenu(settingsData,"#panel_home .sidebarItem",1);
465 |
466 | }
467 |
468 | function setNodeStatus(varname,node,status) {
469 | try{//'sb_categoryNodeStatus'
470 | const categoryNodeStatus = JSON.parse(localStorage.getItem(varname)) || {};
471 | categoryNodeStatus[node] = [status];
472 | localStorage.setItem(varname, JSON.stringify(categoryNodeStatus));
473 | }catch(err){ }
474 | }
475 |
476 | function getNodeStatus(varname,node) {
477 | try{
478 | const categoryNodeStatus = JSON.parse(localStorage.getItem(varname)) || {};
479 | return categoryNodeStatus[node] || "none";
480 | }catch(err){ }
481 | return "none";
482 | }
483 |
484 | function getNodesStatus() {
485 | let categoryNodeStatus = JSON.parse(localStorage.getItem(varname)) || {};
486 | return categoryNodeStatus;
487 | }
488 |
489 |
490 | // MODAL
491 |
492 | function setModalContext(addon=null) {
493 | if (addon==null) {
494 | return `
495 | Add New Category
496 |
500 |
501 | `;
502 | }
503 | else {
504 | return addon
505 | }
506 | }
507 |
508 | function openModal(addon=null) {
509 | // Create HTML elements for the modal
510 | const modalBackdrop = document.createElement('div');
511 | modalBackdrop.classList.add('sb-modal-backdrop');
512 |
513 | const modal = document.createElement('div');
514 | modal.classList.add('sb-category-dialog');
515 | modal.innerHTML = setModalContext(addon)
516 |
517 | // Add the modal to the DOM
518 | document.body.appendChild(modalBackdrop);
519 | document.body.appendChild(modal);
520 |
521 | // Add an event listener for the close button
522 | const closeModalButton = modal.querySelector('#closeModalButton');
523 | closeModalButton.addEventListener('click', closeModal);
524 | try{
525 | // Focus the input field
526 | inputElement = document.getElementById('sb-categoryName');
527 | inputElement.focus();
528 | const length = inputElement.value.length;
529 | inputElement.setSelectionRange(length, length);
530 |
531 | }catch(err){
532 |
533 | }
534 | }
535 |
536 |
537 |
538 | // Function to close the modal
539 | function closeModal() {
540 | const modalBackdrop = document.querySelector('.sb-modal-backdrop');
541 | const modal = document.querySelector('.sb-category-dialog');
542 | try{
543 | // Remove the modal from the DOM
544 | modalBackdrop.parentNode.removeChild(modalBackdrop);
545 | modal.parentNode.removeChild(modal);
546 | }
547 | catch(err){
548 | console.log(err);
549 | }
550 | }
551 |
552 |
553 | function rgbToHex(rgb) {
554 | // Verifica se il colore è nel formato "rgb()"
555 | if (rgb.indexOf('rgb') !== -1) {
556 | // Esegue il parsing dei valori di rosso, verde e blu
557 | const [r, g, b] = rgb.match(/\d+/g);
558 |
559 | // Converte i valori in esadecimale e li concatena con "#"
560 | 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')}`;
561 | }
562 | return rgb; // Se il colore è già nel formato con "#", lo restituisce direttamente
563 | }
564 |
565 |
566 | function hexToRgb(hex) {
567 | try{
568 | // Rimuovi il carattere # se presente
569 | hex = hex.replace(/^#/, '');
570 |
571 | // Estrai i valori R, G e B
572 | let r = parseInt(hex.substring(0, 2), 16);
573 | let g = parseInt(hex.substring(2, 4), 16);
574 | let b = parseInt(hex.substring(4, 6), 16);
575 |
576 | // Restituisci il colore in formato RGB
577 | return `${r}, ${g}, ${b}`;
578 | }catch(err){
579 | console.log(err)
580 | return "#000000";
581 | }
582 | }
583 |
584 | //click anywhere outside the modal to close it
585 | document.body.addEventListener('click', function(event) {
586 | const modalBackdrop = document.querySelector('.sb-modal-backdrop');
587 | const modal = document.querySelector('.sb-category-dialog');
588 | if (event.target === modalBackdrop) {
589 | closeModal();
590 | }
591 | })
592 |
593 |
594 |
595 |
596 |
597 | function showSettings() {
598 | const sb_settingsDiv = document.getElementById("sb_settingsDiv");
599 | const sb_modal_backdrop = document.getElementById("sb-modal-backdrop-settings");
600 | sb_settingsDiv.classList.remove('sb_hidden');
601 | sb_modal_backdrop.classList.remove('sb_hidden');
602 | }
603 |
604 |
605 |
606 |
607 | function showIESettings(operation) {
608 | const sb_settingsDiv = document.getElementById("sb_settingsDiv_"+operation);
609 | sb_settingsDiv.classList.remove('sb_hidden');
610 |
611 | }
612 |
613 | function isBottomEdgeVisible(el) {
614 |
615 | var rect = el.getBoundingClientRect();
616 | var viewportHeight = window.innerHeight || document.documentElement.clientHeight;
617 |
618 | if (rect.bottom <= viewportHeight) {
619 | return true;
620 | }
621 |
622 | return false;
623 | }
624 |
625 | function convertCanvasToOffset(canvas, pos, out) {
626 | out = out || [0, 0];
627 | out[0] = pos[0] / canvas.scale - canvas.offset[0];
628 | out[1] = pos[1] / canvas.scale - canvas.offset[1];
629 | return out;
630 | };
631 |
--------------------------------------------------------------------------------
/app/panels/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/app/panels/.DS_Store
--------------------------------------------------------------------------------
/app/panels/assets_downloader/assets_downloader.html:
--------------------------------------------------------------------------------
1 |
2 |
Explore
3 |
My Library
4 |
5 |
119 |
120 |
--------------------------------------------------------------------------------
/app/panels/assets_downloader/style.css:
--------------------------------------------------------------------------------
1 | #assets_downloader_main,#assets_downloader_header,.sb-mymodels {
2 |
3 | justify-content: center;
4 | align-items: center;
5 | height: 44px;
6 | margin: 0;
7 | }
8 | #assets_downloader_main, .sb-mymodels {
9 | height: auto;
10 | padding-top: 0px;
11 | padding-bottom: 150px;
12 | }
13 | #assets_downloader_header {
14 | margin: 10px 0px;
15 | font-size: 16px;
16 |
17 | }
18 | /* General styles for the header */
19 | #assets_downloader_header {
20 | display: flex;
21 | align-items: center;
22 | padding: 10px;
23 |
24 | }
25 |
26 | #assets_downloader_header .h-header {
27 | display: flex;
28 | align-items: center;
29 | flex: 1;
30 | position: relative;
31 | width: 100%;
32 | flex-direction: column;
33 | }
34 |
35 | .n-sb-searchmodels {
36 | box-sizing: border-box;
37 | width: 100%;
38 | border-radius: 5px;
39 | padding: 10px;
40 | border: none;
41 | user-select: none;
42 | background: var(--comfy-input-bg);
43 | color: var(--input-text);
44 | line-height: 1.4;
45 | border: 1px solid var(--border-color);
46 |
47 |
48 |
49 | padding: 10px;
50 | font-size: 16px;
51 | border-radius: 4px;
52 | margin-right: 10px;
53 | }
54 |
55 | #assets_downloader_header select {
56 | padding: 10px;
57 | font-size: 16px;
58 | width : 145px;
59 | border-radius: 4px;
60 |
61 | border-color: var(--comfy-input-bg);
62 | background: var(--comfy-input-bg);
63 | }
64 |
65 |
66 |
67 | /* Main container styles */
68 | #assets_downloader_main {
69 | padding: 4px;
70 | }
71 |
72 | /* Styles for the results container */
73 | #results-container {
74 | display: flex;
75 | justify-content: center; /* Center horizontally */
76 | align-items: center; /* Center vertically */
77 | height: 100%; /* Ensure the container has defined height */
78 | min-height: 200px; /* Minimum height for the container, adjust if needed */
79 | position: relative;
80 | }
81 |
82 | /* Styles for the loading icon */
83 | .pi-spinner-dotted {
84 | font-size: 2rem; /* Size of the loading icon */
85 | }
86 |
87 |
88 | .result-container,#library-container {
89 | display: flex;
90 | flex-wrap: wrap;
91 | gap: 10px;
92 | justify-content: center;
93 |
94 | }
95 | .result-card {
96 | background-color: var(--bg-color);
97 | border-radius: 8px;
98 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
99 | width: 287px;
100 | padding: 8px;
101 | height: 372px;
102 | text-align: center;
103 | position: relative;
104 |
105 | }
106 | .result-card img,
107 | .result-card video {
108 | max-width: 100%;
109 | min-width: 100%;
110 | height: 60%;
111 | width: 100%;
112 | border-radius: 8px;
113 | /* height: auto; */
114 | object-fit: cover;
115 | object-position: center center;
116 | transition: transform 400ms;
117 |
118 | }
119 | .result-card h3 {
120 | margin: 10px 0;
121 | font-size: 18px;
122 | }
123 | .result-card p {
124 | color: #555;
125 | margin: 5px 0;
126 | }
127 | .result-card .username {
128 | font-weight: bold;
129 | color: #007bff;
130 | }
131 | .download-button {
132 | display: inline-block;
133 | margin-top: 10px;
134 | padding: 10px 20px;
135 | background-color: #007bff;
136 | color: #fff;
137 | text-decoration: none;
138 | border-radius: 5px;
139 | font-size: 16px;
140 | cursor: pointer;
141 | }
142 | .download-button:hover {
143 | background-color: #0056b3;
144 | }
145 |
146 |
147 | /* multiselect from: https://codeshack.io/multi-select-dropdown-html-javascript/ */
148 | .multi-select {
149 | display: flex;
150 | box-sizing: border-box;
151 | flex-direction: column;
152 | position: relative;
153 | width: 100%;
154 | user-select: none;
155 |
156 | }
157 | .multi-select .multi-select-header {
158 | border: 1px solid #dee2e6;
159 | padding: 7px 30px 7px 12px;
160 | overflow: hidden;
161 | gap: 7px;
162 | min-height: 43px;
163 | }
164 | .multi-select .multi-select-header::after {
165 | content: "";
166 | display: block;
167 | position: absolute;
168 | top: 50%;
169 | right: 15px;
170 | transform: translateY(-50%);
171 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23949ba3' viewBox='0 0 16 16'%3E%3Cpath d='M8 13.1l-8-8 2.1-2.2 5.9 5.9 5.9-5.9 2.1 2.2z'/%3E%3C/svg%3E");
172 | height: 12px;
173 | width: 12px;
174 | }
175 | .multi-select .multi-select-header.multi-select-header-active {
176 | border-color: var(--comfy-input-bg);
177 | background: var(--comfy-input-bg);
178 | }
179 | .multi-select .multi-select-header.multi-select-header-active::after {
180 | transform: translateY(-50%) rotate(180deg);
181 | }
182 | .multi-select .multi-select-header.multi-select-header-active + .multi-select-options {
183 | display: flex;
184 | }
185 | .multi-select .multi-select-header .multi-select-header-placeholder {
186 | color: var(--content-fg);
187 | }
188 | .multi-select .multi-select-header .multi-select-header-option {
189 | display: inline-flex;
190 | align-items: center;
191 | background-color: var(--comfy-menu-bg);
192 | font-size: 14px;
193 | padding: 3px 8px;
194 | border-radius: 4px;
195 | text-wrap: nowrap;
196 | }
197 | .multi-select .multi-select-header .multi-select-header-max {
198 | font-size: 14px;
199 | color: var(--content-fg);
200 | }
201 | .multi-select .multi-select-options {
202 | display: none;
203 | box-sizing: border-box;
204 | flex-flow: wrap;
205 | position: absolute;
206 | top: 100%;
207 | /* left: 0; */
208 | right: 0;
209 | z-index: 999;
210 | margin-top: 5px;
211 | /* padding: 5px; */
212 | background-color: var(--bg-color);
213 | border-radius: 5px;
214 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
215 | /* max-height: 200px; */
216 | overflow-y: auto;
217 | overflow-x: hidden;
218 | width: fit-content;
219 | }
220 | .multi-select .multi-select-options::-webkit-scrollbar {
221 | width: 5px;
222 | }
223 | .multi-select .multi-select-options::-webkit-scrollbar-track {
224 | background: #f0f1f3;
225 | }
226 | .multi-select .multi-select-options::-webkit-scrollbar-thumb {
227 | background: #cdcfd1;
228 | }
229 | .multi-select .multi-select-options::-webkit-scrollbar-thumb:hover {
230 | background: #b2b6b9;
231 | }
232 | .multi-select .multi-select-options .multi-select-option, .multi-select .multi-select-options .multi-select-all {
233 | padding: 4px 12px;
234 | height: 42px;
235 | }
236 | .multi-select .multi-select-options .multi-select-option .multi-select-option-radio, .multi-select .multi-select-options .multi-select-all .multi-select-option-radio {
237 | margin-right: 14px;
238 | height: 16px;
239 | width: 16px;
240 | border: 1px solid #ced4da;
241 | border-radius: 4px;
242 | }
243 | .multi-select .multi-select-options .multi-select-option .multi-select-option-text, .multi-select .multi-select-options .multi-select-all .multi-select-option-text {
244 | box-sizing: border-box;
245 | flex: 1;
246 | overflow: hidden;
247 | text-overflow: ellipsis;
248 | white-space: nowrap;
249 | color: inherit;
250 | font-size: 16px;
251 | line-height: 20px;
252 | }
253 | .multi-select .multi-select-options .multi-select-option.multi-select-selected .multi-select-option-radio, .multi-select .multi-select-options .multi-select-all.multi-select-selected .multi-select-option-radio {
254 | border-color: var(--border-color);
255 | background-color: var(--bg-color);
256 | }
257 | .multi-select .multi-select-options .multi-select-option.multi-select-selected .multi-select-option-radio::after, .multi-select .multi-select-options .multi-select-all.multi-select-selected .multi-select-option-radio::after {
258 | content: "";
259 | display: block;
260 | width: 3px;
261 | height: 7px;
262 | margin: 0.12em 0 0 0.27em;
263 | border: solid #fff;
264 | border-width: 0 0.15em 0.15em 0;
265 | transform: rotate(45deg);
266 | }
267 | .multi-select .multi-select-options .multi-select-option.multi-select-selected .multi-select-option-text, .multi-select .multi-select-options .multi-select-all.multi-select-selected .multi-select-option-text {
268 | color: var(--content-fg);
269 | }
270 | .multi-select .multi-select-options .multi-select-option:hover, .multi-select .multi-select-options .multi-select-option:active, .multi-select .multi-select-options .multi-select-all:hover, .multi-select .multi-select-options .multi-select-all:active {
271 | background-color: var(--content-bg);
272 | border-radius: 0;
273 | }
274 | .multi-select .multi-select-options .multi-select-all {
275 | border-bottom: 1px solid #f1f3f5;
276 | border-radius: 0;
277 | }
278 | .multi-select .multi-select-options .multi-select-search {
279 | padding: 7px 10px;
280 | border: 1px solid #dee2e6;
281 | border-radius: 5px;
282 | margin: 10px 10px 5px 10px;
283 | width: 100%;
284 | outline: none;
285 | font-size: 16px;
286 | }
287 | .multi-select .multi-select-options .multi-select-search::placeholder {
288 | color: #b2b5b9;
289 | }
290 | .multi-select .multi-select-header, .multi-select .multi-select-option, .multi-select .multi-select-all {
291 | display: flex;
292 | flex-wrap: wrap;
293 | box-sizing: border-box;
294 | align-items: center;
295 | border-radius: 5px;
296 | cursor: pointer;
297 | display: flex;
298 | align-items: center;
299 | width: 100%;
300 | font-size: 16px;
301 | color: var(--content-fg);
302 | border-color: var(--comfy-input-bg);
303 | background: var(--comfy-input-bg);
304 | min-width: max-content;
305 | }
306 |
307 |
308 | #baseModelFilter{
309 | width: 157px !important;
310 | }
311 | #t1, #t2{
312 | display: none;
313 | }
314 |
315 |
316 | #assets_downloader_header {
317 | display: flex;
318 | flex: 1;
319 | position: relative;
320 | width: calc(100% - 5px);
321 | flex-direction: column;
322 | justify-content: space-between;
323 | align-items: center;
324 | height: auto;
325 | margin-top: 10px;
326 | }
327 |
328 | #assets_downloader_header input:focus, #assets_downloader_header select:focus, .multi-select .multi-select-header:active {
329 | outline: var(--border-color) solid 2px;
330 | }
331 | #assets_downloader_main a, .sb-mymodels a {
332 | text-decoration: none;
333 | color: #fff;
334 | }
335 | .search-row {
336 | width: 100%;
337 | display: flex;
338 | justify-content: center;
339 | }
340 |
341 | .n-sb-searchmodels {
342 | padding: 10px;
343 | font-size: 16px;
344 | border-radius: 4px;
345 | width: 100%; /* Usa tutta la larghezza disponibile nella riga */
346 | max-width: 100%; /* Nessun limite alla larghezza massima */
347 | }
348 |
349 | .type-checkpoint {
350 | position: absolute;
351 | top: 0;
352 | right: 0px;
353 | padding: 5px;
354 | background: var(--content-fg);
355 | border-radius: 5px;
356 | margin: 10px 10px 0px 0px !important;
357 | opacity: 0.7;
358 | text-transform: uppercase;
359 | font-weight: bold;
360 | font-size: smaller;
361 | }
362 | #assets_downloader_main .stats, .sb-mymodels .stats {
363 | position: absolute;
364 | bottom: 0;
365 | background: #404040;
366 | padding: 5px;
367 | border-radius: 5px;
368 | color: var(--content-fg);
369 | right: 3px;
370 | }
371 |
372 | #assets_downloader_main .username, .sb-mymodels .username {
373 | position: absolute;
374 | bottom: 0px;
375 | right: 0;
376 | margin: 5px;
377 | background: var(--comfy-menu-bg);
378 | padding: 6px;
379 | border-radius: 5px;
380 | }
381 | /* Filtri */
382 | .filters-row {
383 | width: 100%;
384 | max-width: 600px;
385 | display: flex;
386 | flex-wrap: wrap;
387 | justify-content: center;
388 | gap: 3px;
389 | margin-right: 15px;
390 | }
391 |
392 | #t1 .filters-row > * {
393 | flex: 1 1 22%;
394 | /* min-width: 120px; */
395 | }
396 | /* Stili per le selezioni */
397 | #assets_downloader_header select {
398 | padding: 10px;
399 | font-size: 16px;
400 | border-radius: 4px;
401 | width: 100%;
402 | max-width: 150px;
403 | height: 42px;
404 | margin-top: 10px;
405 | }
406 |
407 | /* Multi-select */
408 | #assets_downloader_header .multi-select {
409 | width: 145px; /* Larghezza massima per il multi-select */
410 | margin-top: 10px;
411 | }
412 |
413 | /* Adattamento responsive */
414 | @media (max-width: 768px) {
415 | .filters-row {
416 | flex-direction: column; /* I filtri si dispongono in colonna su schermi più piccoli */
417 | align-items: center; /* Centratura orizzontale dei filtri su schermi più piccoli */
418 | }
419 |
420 | select, .multi-select,.n-sb-searchmodels {
421 | width: 100%; /* Usa tutta la larghezza disponibile */
422 | max-width: 100%; /* Nessun limite alla larghezza massima */
423 | }
424 | }
425 |
426 |
427 | .sb-kebab-menu {
428 | position: relative;
429 | display: inline-block;
430 | color: var(--content-fg);
431 | margin-right: 10px;
432 | display: flex;
433 | align-items: center;
434 | }
435 |
436 | .sb-kebab-icon {
437 | font-size: 24px;
438 | cursor: pointer;
439 | }
440 |
441 | .sb-menu {
442 | display: none;
443 | position: absolute;
444 | right: 0;
445 | background-color:var(--bg-color);
446 | box-shadow: 0px 8px 16px rgba(0,0,0,0.2);
447 | list-style-type: none;
448 | padding: 10px 0;
449 | margin: 0;
450 | border-radius: 4px;
451 | z-index: 1;
452 | width: max-content !important;
453 | }
454 |
455 | .sb-menu li {
456 | padding: 8px 16px;
457 | cursor: pointer;
458 | }
459 |
460 | .sb-menu li:hover {
461 | background-color: var(--comfy-menu-bg);
462 | }
463 |
464 |
465 | .sb-rebuild-bg-pgs {
466 | text-align: left;
467 | width: inherit;
468 | padding-right: 28px;
469 | display: none;
470 | }
471 | .sb-rebuild-bg-pgs label {
472 | font-size: 0.6rem;
473 | line-height: 1.6;
474 | position: absolute;
475 | padding-left: 5px;
476 | }
477 | .sb-rebuild-pgs {
478 | width: 0%;
479 | background: #1da874a6;
480 | border-radius: 1px;
481 | height: 16px;
482 | font-size: 0.7vh;
483 | text-align: center;
484 | animation: pulsate 1.5s ease-in-out infinite;
485 | }
486 |
487 | @keyframes pulsate {
488 | 0% {
489 | background-color: #1da874a6; /* Colore iniziale */
490 | }
491 | 50% {
492 | background-color: #28a745; /* Colore più brillante o diverso */
493 | }
494 | 100% {
495 | background-color: #1da874a6; /* Torna al colore iniziale */
496 | }
497 | }
498 |
499 | #assets_downloader_main .card, .sb-mymodels .card {
500 | position: relative;
501 | width: 220px;
502 | height: 340px;
503 | overflow: hidden;
504 | border-radius: 10px;
505 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
506 | background-size: cover;
507 | background-position: center;
508 | /*box-shadow: 0 0 7px 0px rgb(170 133 32 / 70%); */
509 | cursor: pointer;
510 | }
511 |
512 | #assets_downloader_main .card img , .sb-mymodels .card img {
513 | position: absolute;
514 | width: 100%;
515 | height: 100%;
516 | object-fit: cover;
517 | z-index: 1;
518 | }
519 |
520 | #assets_downloader_main .header,#assets_downloader_main .footer , .sb-mymodels .header, .sb-mymodels .footer {
521 | position: absolute;
522 | width: 100%;
523 | left: 0;
524 | z-index: 2;
525 | text-align: left;
526 | color: white;
527 | padding: 10px;
528 | }
529 |
530 | #assets_downloader_main .header, .sb-mymodels .header {
531 | top: 0;
532 | text-align: left;
533 | width: -moz-available;
534 | width: -webkit-fill-available;
535 |
536 | }
537 |
538 | #assets_downloader_main .footer, .sb-mymodels .footer {
539 | bottom: 0;
540 |
541 | }
542 |
543 | #assets_downloader_main .content, .sb-mymodels .content {
544 | /*position: absolute;
545 | top: 50%;
546 | left: 50%;
547 | transform: translate(-50%, -50%);
548 | z-index: 2;
549 | text-align: center;*/
550 | color: white;
551 | }
552 | #assets_downloader_main .card .header .main-stripe, .sb-mymodels .card .header .main-stripe {
553 | box-sizing: border-box;
554 | -webkit-box-flex: 0;
555 |
556 | }
557 |
558 | #assets_downloader_main .card .header .sub-stripe, .sb-mymodels .card .header .sub-stripe, #assets_downloader_main .card .footer .sub-stripe, .sb-mymodels .card .footer .sub-stripe {
559 | display: inline-flex;
560 | -webkit-box-align: center;
561 | align-items: center;
562 | -webkit-box-pack: center;
563 | justify-content: center;
564 | width: auto;
565 | text-transform: uppercase;
566 | font-weight: 700;
567 | letter-spacing: 0.25px;
568 | cursor: inherit;
569 | text-overflow: ellipsis;
570 | overflow: hidden;
571 | background: rgba(0, 0, 0, 0.51);
572 | border: 1px solid transparent;
573 | color: rgb(254, 254, 254);
574 | border-radius: 32px;
575 | height: 26px;
576 | font-size: 9px;
577 | line-height: 18px;
578 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
579 | text-decoration: none;
580 | padding: 0px 10.6667px;
581 | box-sizing: border-box;
582 | }
583 | #assets_downloader_main .card .header .main-stripe .separator, .sb-mymodels .card .header .sub-stripe .separator {
584 | margin: -4px 8px;
585 | border-left-color: rgba(255, 255, 255, 0.31);
586 | border-right: 1px solid rgba(0, 0, 0, 0.2);
587 | border-left-width: 1px;
588 | border-left-style: solid;
589 | }
590 | #assets_downloader_main .card .card-main-text, .sb-mymodels .card .card-main-text {
591 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
592 | -webkit-tap-highlight-color: transparent;
593 | overflow: hidden;
594 | text-overflow: ellipsis;
595 | display: -webkit-box;
596 | -webkit-line-clamp: 3;
597 | -webkit-box-orient: vertical;
598 | color: inherit;
599 | font-size: 1.5vh;
600 | text-decoration: none;
601 | font-weight: 700;
602 | filter: drop-shadow(rgba(0, 0, 0, 0.8) 1px 1px 1px);
603 | line-height: 1.2;
604 | margin-bottom: 5px;
605 | }
606 |
607 | #assets_downloader_main .stats-section, .sb-mymodels .stats-section{
608 | -webkit-tap-highlight-color: transparent;
609 |
610 | font-size: 11px;
611 | line-height: 18px;
612 | text-decoration: none;
613 | padding: 0px 10.6667px;
614 | box-sizing: border-box;
615 | display: inline-flex;
616 | -webkit-box-align: center;
617 | align-items: center;
618 | width: auto;
619 | font-weight: 700;
620 | letter-spacing: 0.25px;
621 | cursor: inherit;
622 | text-overflow: ellipsis;
623 | overflow: hidden;
624 | background: rgb(0 0 0 / 45%);
625 | border: 1px solid transparent;
626 | align-self: flex-start;
627 | color: rgb(254, 254, 254);
628 | border-radius: 32px;
629 | height: 26px;
630 | }
631 | #assets_downloader_main .card .creator-name, .sb-mymodels .card .creator-name{
632 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
633 | -webkit-tap-highlight-color: transparent;
634 | overflow: hidden;
635 | text-overflow: ellipsis;
636 | display: -webkit-box;
637 | -webkit-line-clamp: 1;
638 | -webkit-box-orient: vertical;
639 | color: #ffffff;
640 | font-size: 0.70vw;
641 | line-height: 1.55;
642 | /* text-decoration: none; */
643 | font-weight: 500;
644 | /* background-clip: text; */
645 | /* -webkit-text-fill-color: transparent; */
646 | vertical-align: middle;
647 | filter: drop-shadow(rgba(0, 0, 0, 0.8) 3px 1px 0px);
648 | text-align: left;
649 | padding: 0;
650 | margin: 0px;
651 |
652 | }
653 | .card .model-image {
654 | transition: transform 0.3s ease-in-out;
655 | }
656 | /* .card img:hover, .image-overlay:hover{
657 | transform: scale(1.05);
658 | }*/
659 | .card .stat-item i {
660 | padding: 0 5px;
661 | }
662 |
663 | /* Overlay con effetto vetro */
664 | .card .image-overlay {
665 | position: absolute;
666 | top: 0;
667 | left: 0;
668 | width: 100%;
669 | height: 100%;
670 | background: linear-gradient(to bottom, rgb(14 14 14 / 0%) 0%, rgb(47 47 58 / 79%) 100%);
671 |
672 | display: flex;
673 | justify-content: center;
674 | align-items: center;
675 | color: white;
676 | font-size: 24px;
677 | text-align: center;
678 | z-index: 99;
679 | }
680 |
681 |
682 | .card .progress-container {
683 | width: 100%;
684 | height: 100%;
685 | background-color: #e0e0e0;
686 | position: relative;
687 |
688 | }
689 |
690 | .card .progress-bar {
691 | z-index: 98;
692 | height: 100%;
693 | background-color: #76c7c04f;
694 | width: 0%;
695 | transition: width 0.3s;
696 | position: absolute;
697 | }
698 |
699 |
700 |
701 | .card .sub-stripe-status{
702 | font-size: 1rem;
703 | background: #00000073;
704 | padding: 3px 10px;
705 | border-radius: 11px;
706 | display: none;
707 | }
708 |
709 |
710 | .sub-stripe-manager {
711 | display: flex;
712 | flex-direction: column; /* I div figli saranno disposti verticalmente */
713 | align-items: flex-start; /* Allineamento a sinistra */
714 | }
715 | .sub-stripe-manager div {
716 |
717 | padding: 2px 9px;
718 | transition: filter 0.3s ease;
719 | }
720 | .sub-stripe-manager div:hover {
721 | filter: brightness(180%); /* Aumenta la luminosità del 10% */
722 | }
723 | .sub-stripe-manager{
724 | position: absolute;
725 | right: 28px;
726 | top: 0;
727 | right: 0;
728 | }
729 |
730 | .card .sub-stripe-dw{
731 | background: #ff4141;
732 | border-bottom-left-radius: 6px;
733 | }
734 |
735 | .card .sub-stripe-ps{
736 | background: #ff6720;
737 | display: none;
738 | filter: brightness(80%);
739 |
740 | }
741 |
742 | .card .sub-stripe-cn{
743 | background: #ff4141;
744 | border-bottom-left-radius: 6px;
745 | display: none;
746 | filter: brightness(80%);
747 | }
748 | .card .sub-stripe-complete{
749 | background: #00c027;
750 |
751 | display: none;
752 |
753 | }
754 | .card .sub-stripe-delete{
755 | background: #ff4141;
756 | border-bottom-left-radius: 6px;
757 | display: none;
758 |
759 | }
760 |
761 | .sb-pi-toast-center {
762 | padding-top: 5px;
763 | }
--------------------------------------------------------------------------------
/app/panels/custom_categories/custom_categories.html:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/panels/custom_categories/custom_categories.jsb:
--------------------------------------------------------------------------------
1 | //console.log(LiteGraph.registered_node_types);
2 | var custom_categories = (function () {
3 | let currentElement = null;
4 |
5 | function createCustomHtml(type, name, elem = null) {
6 | if (type == "rename") {
7 | return `Rename Category
8 |
12 |
13 |
14 | `
15 | } else if (type == "color") {
16 | currentElement = elem;
17 | return `Color ${name}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | `
26 | }
27 | }
28 | // Function to open the modal
29 |
30 | //insert button in label with id panel_title_custom_categories
31 | let categorySearchToggleCustom = false;
32 | const panel_title_custom_categories = document.getElementById("panel_title_custom_categories");
33 |
34 | const button_custom_categories = document.createElement("button");
35 | button_custom_categories.classList = "expand_node";
36 | button_custom_categories.title = "Expand/Collapse";
37 | button_custom_categories.innerHTML = ``;
46 |
47 | panel_title_custom_categories.appendChild(button_custom_categories);
48 |
49 | button_custom_categories.addEventListener("click", (event) => {
50 | const clickedElement = event.target;
51 | const pinButton = clickedElement.tagName;
52 | if ((pinButton == "rect") && clickedElement.classList.contains("expand_node")) {
53 |
54 | sdExpandAll();
55 | }
56 |
57 | })
58 | const clearIconCustomCategory = document.querySelector(".clearIconCustomCategory");
59 | const itemSearchInput = document.getElementById("searchInputCn");
60 | clearIconCustomCategory.addEventListener("click", async function () {
61 | try {
62 |
63 | itemSearchInput.value = "";
64 | renderList("custom_categories_main");
65 |
66 | } catch (error) {
67 | console.error("Error occurred during search:", error);
68 | }
69 | });
70 |
71 |
72 |
73 |
74 |
75 | function afterRender() {
76 |
77 | customMenu = `
78 | {
79 | "menuctx_category": "Context Menu",
80 | "menuctx_options": [
81 | "Rename Category",
82 | "Delete Category",
83 | "Change Color"
84 | ],
85 | "menuctx_subOptions": [
86 | [
87 | ]
88 | ],
89 | "menuctx_opt_callback": [
90 | "custom_categories.renameCategoryCallback",
91 | "custom_categories.deleteCategory",
92 | "custom_categories.colorCategoryCallback"
93 |
94 | ],
95 | "menuctx_sub_opt_callback": [
96 | [
97 | ],
98 | [
99 | ]
100 | ]
101 | }`
102 |
103 | customMenuItem = `
104 | {
105 | "menuctx_category": "Context Menu",
106 | "menuctx_options": [
107 | "Delete from Category"
108 | ],
109 | "menuctx_subOptions": [
110 | [
111 | ]
112 | ],
113 | "menuctx_opt_callback": [
114 | "custom_categories.removeNodeFromCategoryCallback"
115 |
116 | ],
117 | "menuctx_sub_opt_callback": [
118 | [
119 | ],
120 | [
121 | ]
122 | ]
123 | }`
124 |
125 |
126 | customMenuJSON = JSON.parse(customMenu);
127 | customMenuItemJSON = JSON.parse(customMenuItem);
128 |
129 | setContextMenu(customMenuJSON, "#custom_categories_main .sidebarCategory", 0, "sidebarItem", "sidebarCategory");
130 |
131 | setContextMenu(customMenuItemJSON, "#custom_categories_main .sidebarItem");
132 |
133 |
134 |
135 | // ORDER CUSTOM CATEGORIES
136 |
137 |
138 | const custom_categories_main = document.getElementById("custom_categories_main");
139 | custom_categories_main.querySelectorAll(".sidebarCategory").forEach(function (item) {
140 | item.addEventListener("dragstart", function (event) {
141 | dragItem = event.target;
142 |
143 |
144 | });
145 |
146 | });
147 |
148 | custom_categories_main.addEventListener("dragover", function (event) {
149 | event.preventDefault();
150 | });
151 |
152 | custom_categories_main.addEventListener("drop", function (event) {
153 | event.preventDefault();
154 |
155 | if (dragItem) {
156 |
157 |
158 | if (dragItem.parentElement === event.target.parentElement) {
159 | custom_categories_main.insertBefore(dragItem, event.target);
160 | reorderCategories(getCustomCategoryOrder());
161 | }
162 | dragItem = null;
163 | }
164 | });
165 |
166 |
167 |
168 |
169 |
170 | // ORDER CUSTOM NODES
171 |
172 |
173 | custom_categories_main.querySelectorAll(".sidebarItem").forEach(function (item) {
174 | item.addEventListener("dragstart", function (event) {
175 | dragItem = event.target;
176 |
177 |
178 | });
179 |
180 | });
181 |
182 |
183 | }
184 |
185 | function getCustomCategoryOrder() {
186 | let cat = [];
187 | const custom_categories_main = document.getElementById("custom_categories_main");
188 | custom_categories_main.querySelectorAll(".sidebarCategory").forEach(function (item) {
189 | cat.push(item.dataset.namecategory)
190 |
191 | });
192 | return cat
193 | }
194 |
195 |
196 | function getCustomNodeOrder(idcat) {
197 | let node = [];
198 | const objMain = document.getElementById(idcat);
199 | objMain.querySelectorAll(".sidebarItem").forEach(function (item) {
200 | node.push(item.dataset.type)
201 |
202 | });
203 | return node
204 | }
205 |
206 |
207 | async function renderList(elementID) {
208 | const data = LiteGraph.registered_node_types;
209 |
210 |
211 | const myPromise = new Promise(async (resolve, reject) => {
212 | const categories = await readCategories();
213 | //const pinnedItems = loadPinnedItems();
214 |
215 | //console.log(categories);
216 | const sidebarCustomNodes = document.getElementById(elementID);
217 | //clear elementid
218 | sidebarCustomNodes.innerHTML = "";
219 |
220 |
221 | for (const category in categories) {
222 | //data[objKey].title.toLowerCase();
223 | let categoryName = categories[category]
224 | const categoryItem = document.createElement("li");
225 | categoryItem.classList.add("sidebarCategory");
226 | categoryItem.dataset.namecategory = categoryName;
227 | categoryItem.textContent = "⌬ " + categoryName;
228 | categoryItem.draggable = true;
229 |
230 | const currentColor = await getValueFromConfig("sb_ColorCustomCategories", categoryName);
231 | if (currentColor) {
232 | const value_perc = await getConfiguration("sb_opacity") || "1.0";
233 |
234 | const rgbColor = hexToRgb(currentColor);
235 | categoryItem.style.setProperty('background-color', `rgba(${rgbColor}, ${value_perc})`, 'important');
236 |
237 | //categoryItem.style.backgroundColor = `rgba(${rgbColor}, ${value_perc})`;
238 | }
239 |
240 |
241 |
242 |
243 | const displayNamesList = document.createElement("ul");
244 | displayNamesList.id = categoryName + "_ul";
245 | displayNamesList.style.display = getNodeStatus('sb_categoryNodeStatus',categoryName);
246 | categoryItem.appendChild(displayNamesList);
247 |
248 | let displayName = category;
249 |
250 |
251 | const listNodeForCategory = await getNodesForCategory(categoryName);
252 |
253 |
254 |
255 | listNodeForCategory.forEach(displayName => {
256 | try {
257 | const displayNameItem = document.createElement("li");
258 | displayNameItem.classList.add("sidebarItem");
259 | displayNameItem.textContent = data[displayName].title;
260 | displayNameItem.title = data[displayName].title;
261 | displayNameItem.draggable = true;
262 | displayNameItem.dataset.type = data[displayName].type;
263 |
264 |
265 |
266 |
267 | displayNameItem.id = displayName//.replace(" ", "_");
268 |
269 | /* Create Pin Button
270 | const pinButton = document.createElement("button");
271 | pinButton.classList.add("pinButton");
272 |
273 | let add_class = "";
274 | if (pinnedItems.includes(displayNameItem.dataset.type)) {
275 | add_class = "pinned";
276 | }
277 |
278 | pinButton.innerHTML = ``;
282 |
283 |
284 | displayNameItem.appendChild(pinButton);
285 | /* End Pin Button */
286 |
287 |
288 | displayNamesList.appendChild(displayNameItem);
289 |
290 |
291 | displayNamesList.addEventListener("drop", function (event) {
292 | event.preventDefault();
293 |
294 | if (dragItem) {
295 |
296 | if (dragItem.parentElement == event.target.parentElement) {
297 |
298 | displayNamesList.insertBefore(dragItem, event.target);
299 |
300 | reorderNodeInCategory(categoryItem.dataset.namecategory, getCustomNodeOrder(dragItem.parentElement.id))
301 |
302 | }
303 | dragItem = null;
304 |
305 |
306 | }
307 | });
308 |
309 |
310 |
311 |
312 |
313 | }
314 | catch (err) {
315 | console.log(err);
316 | }
317 | })
318 |
319 |
320 |
321 |
322 | try {
323 | sidebarCustomNodes.appendChild(categoryItem);
324 | } catch (err) {
325 | console.log(err);
326 | }
327 |
328 |
329 | }
330 |
331 |
332 |
333 |
334 | // Preview
335 | const sidebarItems_cat = document.querySelectorAll('#' + elementID + ' .sidebarItem');
336 | const previewDiv = document.getElementById('previewDiv');
337 | let sidebad_view_width = document.getElementById("sidebar_views").offsetWidth;
338 |
339 | sidebarItems_cat.forEach(item => {
340 |
341 | getPreview(item,previewDiv,sidebad_view_width,(item) => {
342 | return createNodePreview(item.id);
343 | });
344 |
345 | });
346 |
347 |
348 |
349 | const categoriesList = document.getElementsByClassName("content_sidebar")[1];
350 | categoriesList.addEventListener('scroll', function () {
351 | previewDiv.style.display = 'none';
352 | });
353 |
354 | window.addEventListener('click', function (event) {
355 |
356 | if (!event.target.classList.contains('sidebarItem')) {
357 | previewDiv.style.display = 'none';
358 |
359 | }
360 | });
361 |
362 |
363 |
364 | const categoryItems = document.querySelectorAll("#custom_categories_main .sidebarCategory");
365 | categoryItems.forEach(function (categoryItem) {
366 | categoryItem.addEventListener("click", function (event) {
367 |
368 | if (event.target === event.currentTarget) {
369 | const displayNamesList = event.target.querySelector("ul");
370 |
371 | displayNamesList.style.display = displayNamesList.style.display === "none" ? "block" : "none";
372 |
373 | setNodeStatus('sb_categoryNodeStatus',displayNamesList.parentElement.dataset.namecategory, displayNamesList.style.display)
374 | }
375 | });
376 |
377 |
378 |
379 | });
380 | resolve(afterRender);
381 |
382 |
383 |
384 |
385 |
386 | });
387 | myPromise.then((callback) => {
388 |
389 | callback();
390 | }).catch((error) => {
391 | console.error("An error occurred:", error);
392 | });
393 | }
394 | function searchT(e) {
395 |
396 | if (e.value != "") {
397 | //search in all .sidebarItem
398 | handleSearch(categorySearchToggleCustom, "#custom_categories_main", "searchInputCn")
399 |
400 |
401 | } else {
402 |
403 | renderList("custom_categories_main");
404 | }
405 |
406 | }
407 |
408 |
409 |
410 | async function getNodeMap() {
411 | let categoryNodeMap = JSON.parse(await getConfiguration('sb_categoryNodeMap')) || {};
412 | return categoryNodeMap;
413 | }
414 |
415 | function getNodesStatus() {
416 | let categoryNodeStatus = JSON.parse(localStorage.getItem('sb_categoryNodeStatus')) || {};
417 | return categoryNodeStatus;
418 | }
419 |
420 |
421 | async function addCategory() {
422 |
423 | const newCategoryName = document.getElementById('sb-categoryName').value;
424 | // Check if the category name contains special characters
425 | if (containsSpecialCharacters(newCategoryName)) {
426 | // Alert the user and return if special characters are found
427 | alert("Category name cannot contain special characters.");
428 | return;
429 | }
430 |
431 | let categoryNodeMap = await getNodeMap();
432 |
433 | if (!categoryNodeMap[newCategoryName]) {
434 |
435 | categoryNodeMap[newCategoryName] = [];
436 | updateConfiguration('sb_categoryNodeMap', JSON.stringify(categoryNodeMap));
437 | createContextualMenu();
438 | reloadCtxMenu()
439 | renderList("custom_categories_main");
440 | closeModal()
441 | } else {
442 | alert("Category already exists!");
443 | return;
444 | }
445 | }
446 |
447 |
448 | // Function to rename a category
449 |
450 | async function renameCategory(oldCategoryName, newCategoryName) {
451 |
452 |
453 | if (containsSpecialCharacters(newCategoryName)) {
454 | // Alert the user and return if special characters are found
455 | alert("Category name cannot contain special characters.");
456 | return;
457 | }
458 | if (oldCategoryName === newCategoryName) {
459 | closeModal()
460 | return;
461 | }
462 |
463 | let categoryNodeMap = await getNodeMap();
464 |
465 | if (categoryNodeMap[newCategoryName]) {
466 | alert("Category already exists!");
467 | return;
468 | }
469 |
470 |
471 | let categoryNodeStatus = getNodesStatus();
472 | if (categoryNodeMap[oldCategoryName]) {
473 | // Categoty Current Order
474 | let keys = Object.keys(categoryNodeMap);
475 |
476 | categoryNodeMap[newCategoryName] = categoryNodeMap[oldCategoryName];
477 | delete categoryNodeMap[oldCategoryName];
478 |
479 | // Rebuild Order
480 | let updatedMap = {};
481 | keys.forEach(key => {
482 | if (key === oldCategoryName) {
483 | updatedMap[newCategoryName] = categoryNodeMap[newCategoryName];
484 | } else {
485 | updatedMap[key] = categoryNodeMap[key];
486 | }
487 | });
488 |
489 |
490 |
491 | updateConfiguration('sb_categoryNodeMap', JSON.stringify(updatedMap));
492 | //rename status
493 | if (categoryNodeStatus[oldCategoryName]) {
494 |
495 | categoryNodeStatus[newCategoryName] = categoryNodeStatus[oldCategoryName];
496 | delete categoryNodeStatus[oldCategoryName];
497 | localStorage.setItem('sb_categoryNodeStatus', JSON.stringify(categoryNodeStatus));
498 | }
499 | //rename color
500 | renameKeyConfig("sb_ColorCustomCategories", oldCategoryName, newCategoryName)
501 |
502 |
503 | createContextualMenu();
504 | reloadCtxMenu()
505 | renderList("custom_categories_main");
506 | closeModal()
507 | } else {
508 | alert("Category not found!");
509 | }
510 | }
511 | async function readCategories() {
512 | let categoryNodeMap = await getNodeMap();
513 | if (categoryNodeMap) {
514 | return Object.keys(categoryNodeMap);
515 | }
516 | else {
517 | return [];
518 | }
519 | }
520 | // Function to delete a category
521 | async function deleteCategory(e) {
522 |
523 | const name = e.target.dataset.namecategory;
524 | confirmation = confirm("Are you sure you want to delete the category " + name + "?");
525 | if (!confirmation) { return; }
526 |
527 | let categoryNodeMap = await getNodeMap();
528 | // If the category name is found, remove it from the array
529 | if (categoryNodeMap[name]) {
530 | removeNodeMapCategory(name);
531 | createContextualMenu();
532 | removeKeyFromConfig("sb_ColorCustomCategories", name)
533 | reloadCtxMenu()
534 | setTimeout(() => {
535 |
536 | renderList("custom_categories_main");
537 | },300)
538 |
539 | } else {
540 | alert("Category not found!");
541 | }
542 | }
543 |
544 |
545 |
546 |
547 |
548 | /////////////////////////////////////////////////
549 |
550 | async function getConfig(configName) {
551 | let categoryNodeMap = JSON.parse(await getConfiguration(configName)) || {};
552 | return categoryNodeMap;
553 | }
554 |
555 |
556 |
557 | async function getValueFromConfig(configName, key) {
558 | let config = await getConfig(configName);
559 | return config[key] || null;
560 | }
561 |
562 | async function assignValueToConfig(configName, key, value) {
563 | let config = await getConfig(configName);
564 | config[key] = value;
565 | updateConfiguration(configName, JSON.stringify(config));
566 | }
567 |
568 | async function removeKeyFromConfig(configName, key) {
569 | let config = await getConfig(configName);
570 | delete config[key];
571 | updateConfiguration(configName, JSON.stringify(config));
572 |
573 |
574 | }
575 | async function renameKeyConfig(configName, oldKey, newKey) {
576 | let config = await getConfig(configName);
577 | let value = config[oldKey];
578 | delete config[oldKey];
579 | config[newKey] = value;
580 | updateConfiguration(configName, JSON.stringify(config));
581 | }
582 |
583 | async function removeKeyConfig(configName) {
584 | removeConfiguration(configName)
585 | renderList("custom_categories_main");
586 | }
587 |
588 |
589 |
590 | ////////////////////////////////////////////////
591 |
592 | async function getCategoriesForNode(nodeName) {
593 | let categoryNodeMap = await getNodeMap();
594 |
595 | let categories = [];
596 | for (let category in categoryNodeMap) {
597 | if (categoryNodeMap.hasOwnProperty(category)) {
598 | if (categoryNodeMap[category].includes(nodeName)) {
599 | categories.push(category);
600 | }
601 | }
602 | }
603 | return categories;
604 | }
605 |
606 | async function getNodesForCategory(categoryName) {
607 | let categoryNodeMap = await getNodeMap();
608 |
609 | return categoryNodeMap[categoryName] || [];
610 | }
611 |
612 |
613 | async function assignNodeToCategory(nodeName, categoryNames) {
614 | let categoryNodeMap = await getNodeMap();
615 | let assignedCategories = await getCategoriesForNode(nodeName);
616 | categoryNames = categoryNames.filter(category => !assignedCategories.includes(category));
617 |
618 | categoryNames.forEach(categoryName => {
619 | if (!categoryNodeMap[categoryName]) {
620 | categoryNodeMap[categoryName] = [];
621 | }
622 | categoryNodeMap[categoryName].push(nodeName);
623 | });
624 | updateConfiguration('sb_categoryNodeMap', JSON.stringify(categoryNodeMap));
625 | renderList("custom_categories_main");
626 | }
627 |
628 |
629 | async function removeNodeFromCategory(nodeName, categoryName) {
630 | let categoryNodeMap = await getNodeMap();
631 |
632 | if (categoryNodeMap[categoryName]) {
633 | categoryNodeMap[categoryName] = categoryNodeMap[categoryName].filter(node => node !== nodeName);
634 | updateConfiguration('sb_categoryNodeMap', JSON.stringify(categoryNodeMap));
635 | renderList("custom_categories_main");
636 |
637 | }
638 | }
639 |
640 |
641 | async function removeNodeMapCategory(categoryName) {
642 | let categoryNodeMap = await getNodeMap();
643 | let categoryNodeStatus = getNodesStatus();
644 | delete categoryNodeMap[categoryName];
645 | updateConfiguration('sb_categoryNodeMap', JSON.stringify(categoryNodeMap));
646 |
647 | if (categoryNodeStatus[categoryName]) {
648 | delete categoryNodeStatus[categoryName];
649 | localStorage.setItem('sb_categoryNodeStatus', JSON.stringify(categoryNodeStatus));
650 |
651 | }
652 | }
653 |
654 |
655 | function editNodeCategory(oldCategoryName, newCategoryName, nodeName) {
656 |
657 | removeNodeFromCategory(nodeName, oldCategoryName);
658 |
659 | assignNodeToCategory(nodeName, [newCategoryName]);
660 | }
661 |
662 |
663 |
664 | async function reorderCategories(orderedKeys) {
665 | let categoryNodeMap = await getNodeMap();
666 | let reorderedCategoryNodeMap = {};
667 |
668 |
669 | orderedKeys.forEach(categoryKey => {
670 | if (categoryNodeMap.hasOwnProperty(categoryKey)) {
671 | reorderedCategoryNodeMap[categoryKey] = categoryNodeMap[categoryKey];
672 | }
673 | });
674 |
675 | updateConfiguration('sb_categoryNodeMap', JSON.stringify(reorderedCategoryNodeMap));
676 | }
677 |
678 |
679 | async function reorderNodeInCategory(categoryName, orderedKeys) {
680 | let categoryNodeMap = await getNodeMap();
681 |
682 | if (categoryNodeMap[categoryName]) {
683 | categoryNodeMap[categoryName] = orderedKeys;
684 | updateConfiguration('sb_categoryNodeMap', JSON.stringify(categoryNodeMap));
685 | }
686 | }
687 |
688 | // Callbacks
689 | function removeNodeFromCategoryCallback(e, trge) {
690 |
691 | removeNodeFromCategory(e.target.dataset.type, e.target.parentElement.parentElement.dataset.namecategory)
692 | }
693 |
694 | function assignNodeToCategoryCallback(e, trge) {
695 | assignNodeToCategory(e.target.id, [trge]);
696 | }
697 |
698 | function renameCategoryCallback(e, trge) {
699 | const name = e.target.dataset.namecategory;
700 | openModal(createCustomHtml("rename", name))
701 |
702 | }
703 |
704 | function colorCategoryCallback(e) {
705 |
706 | const name = e.target.dataset.namecategory;
707 | openModal(createCustomHtml("color", name, e.target))
708 | //colorCategory(e.target.id, e.target.parentElement.parentElement.dataset.namecategory)
709 | }
710 |
711 |
712 |
713 |
714 | async function colorCategory(name, value) {
715 |
716 |
717 | const value_perc = await getConfiguration("sb_opacity") || "1.0";
718 | const rgbColor = hexToRgb(value);
719 |
720 |
721 | currentElement.style.setProperty('background-color', `rgba(${rgbColor}, ${value_perc})`, 'important');
722 |
723 | assignValueToConfig("sb_ColorCustomCategories", name, value)
724 | }
725 | // Add an event listener to the button to open the modal
726 | function resetColorCategory(name) {
727 |
728 | removeKeyFromConfig("sb_ColorCustomCategories", name)
729 | renderList("custom_categories_main");
730 |
731 | }
732 |
733 |
734 | async function createContextualMenu() {
735 |
736 | //const menuOptions = document.getElementById('menu-options');
737 | new_menu_options = [];
738 | new_submenu_options = [];
739 | new_menu_options_callback = [];
740 | new_submenu_options_callback = [];
741 | new_menu_options.push("Add to category");
742 | /*new_menu_options.push("Pin It");
743 | new_menu_options_callback.push("dummy");
744 | new_menu_options_callback.push("test_altert");*/
745 | //for each category
746 | category_menu = await readCategories();
747 | callback_menu = [];
748 | for (let i = 0; i < category_menu.length; i++) {
749 | callback_menu.push("custom_categories.assignNodeToCategoryCallback");
750 |
751 | }
752 | new_submenu_options.push(category_menu);
753 | new_submenu_options_callback.push(callback_menu);
754 |
755 |
756 | console.log("custom category panel loaded")
757 |
758 | }
759 |
760 |
761 | function test_altert(e, trge) {
762 | console.log(trge);
763 | console.log(e.target.id);
764 | console.log(e.target.parentElement.parentElement.dataset.namecategory);
765 |
766 | assignNodeToCategory(e.target.id, [trge]);
767 | }
768 |
769 | function dummy(e, trge) {
770 |
771 | }
772 |
773 |
774 |
775 | const searchCategoryIconCustomCategory = document.querySelector(".searchCategoryIconCustomCategory");
776 |
777 |
778 | searchCategoryIconCustomCategory.addEventListener("click", async function () {
779 |
780 | try {
781 |
782 | categorySearchToggleCustom = !categorySearchToggleCustom;
783 | searchCategoryIconCustomCategory.style.opacity = categorySearchToggleCustom ? "1" : "0.5";
784 |
785 |
786 | } catch (error) {
787 |
788 | console.error("Error occurred during search:", error);
789 | }
790 | });
791 | return {
792 | openModal,
793 | addSidebarStyles,
794 | createContextualMenu,
795 | renderList,
796 | renameCategory,
797 | colorCategory,
798 | removeKeyConfig,
799 | searchT,
800 | deleteCategory,
801 | addCategory,
802 | editNodeCategory,
803 | removeNodeMapCategory,
804 | reorderNodeInCategory,
805 | removeNodeFromCategoryCallback,
806 | assignNodeToCategoryCallback,
807 | renameCategoryCallback,
808 | renameCategoryCallback,
809 | colorCategoryCallback,
810 | resetColorCategory
811 | };
812 |
813 |
814 | })();
815 | custom_categories.addSidebarStyles("panels/custom_categories/style.css");
816 | custom_categories.createContextualMenu()
817 | custom_categories.renderList("custom_categories_main");
--------------------------------------------------------------------------------
/app/panels/custom_categories/style.css:
--------------------------------------------------------------------------------
1 | #custom_categories_main,#custom_categories_header {
2 |
3 | justify-content: center;
4 | align-items: center;
5 | height: 100%;
6 | margin: 0;
7 | }
8 | #custom_categories_main{
9 |
10 | padding-top: 44px;
11 | padding-bottom: 150px;
12 | }
13 | #custom_categories_header {
14 | margin: 10px 20px;
15 | font-size: 16px;
16 |
17 | }
18 | #custom_categories_header #h_search {
19 | position: absolute;
20 | width: calc(100% - 76px);
21 | margin-top: 0px;
22 | left: 10px;
23 | z-index: 400;
24 |
25 | }
26 |
27 | #custom_categories_header button {
28 | /* padding: 10px 20px; */
29 | height: 35px;
30 | width: 35px;
31 | font-size: 16px;
32 | background: var(--comfy-input-bg);
33 | border: 2px solid var(--border-color);
34 | color: var(--input-text);
35 | border-radius: 5px;
36 | float: right;
37 | position: relative;
38 | margin: 3px 2px 2px 8px;
39 | cursor: pointer;
40 | z-index: 999;
41 | }
42 |
43 | #custom_categories_header button:hover {border: 2px solid var(--descrip-text);
44 |
45 | }
46 | .sb-category-dialog {
47 | outline: 0;
48 | border: 0;
49 | border-radius: 6px;
50 | background: var(--bg-color);
51 | color: #fff;
52 | 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);
53 | max-width: 800px;
54 | min-width: 30rem;
55 | box-sizing: border-box;
56 | font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
57 | font-size: 1rem;
58 | padding: 20px;
59 | position: fixed;
60 | top: 50%;
61 | left: 50%;
62 | z-index: 1001;
63 | }
64 |
65 | #sb-category-form {
66 | margin-top: 10px;
67 | text-align: center;
68 | }
69 |
70 | .sb-input {
71 | width: 100%;
72 | padding: 8px;
73 | margin-bottom: 10px;
74 | box-sizing: border-box;
75 | border: 2px solid var(--border-color);
76 | border-radius: 4px;
77 | margin-top: 16px;
78 | background: var(--bg-color);
79 | color: var(--input-text);
80 |
81 | }
82 | .sb-input:focus {
83 | outline: none;
84 | border: 2px solid var(--descrip-text);
85 | }
86 |
87 | .sb-button {
88 | background-color: var(--comfy-menu-bg);
89 | color: var(--input-text);
90 | padding: 10px 20px;
91 | border: none;
92 | border-radius: 4px;
93 | cursor: pointer;
94 | margin-top: 10px;
95 | text-transform: uppercase;
96 | font-weight: bold;
97 | }
98 |
99 | .sb-button:hover {
100 | filter: brightness(120%);
101 | }
102 |
103 | #closeModalButton {
104 | background-color: var(--comfy-menu-bg);
105 | position: absolute;
106 | top: 0;
107 | right: 0;
108 | margin: 0;
109 | }
110 |
111 | #closeModalButton:hover {
112 | filter: brightness(120%);
113 |
114 | }
115 | #searchInputCn {
116 | box-sizing: border-box;
117 | width: 100%;
118 | border-radius: 5px;
119 | padding: 10px;
120 | border: none;
121 | user-select: none;
122 | background: var(--comfy-input-bg);
123 | color: var(--input-text);
124 | line-height: 1.4;
125 | border: 1px solid var(--border-color);
126 | }
--------------------------------------------------------------------------------
/app/panels/custom_templates/custom_templates.html:
--------------------------------------------------------------------------------
1 |
12 |
13 |
--------------------------------------------------------------------------------
/app/panels/custom_templates/style.css:
--------------------------------------------------------------------------------
1 | #custom_templates_main,#custom_templates_header {
2 |
3 | justify-content: center;
4 | align-items: center;
5 | height: 44px;
6 | margin: 0;
7 | }
8 | #custom_templates_main{
9 | height: auto;
10 | padding-top: 0px;
11 | padding-bottom: 150px;
12 | }
13 | #custom_templates_header {
14 | margin: 10px 0px;
15 | font-size: 16px;
16 |
17 | }
18 | #custom_templates_header #h_search {
19 | position: absolute;
20 | width: calc(100% - 92px);
21 | margin-top: 0px;
22 | left: 10px;
23 | z-index: 400;
24 |
25 | }
26 |
27 | #custom_templates_header button {
28 | /* padding: 10px 20px; */
29 | height: 35px;
30 | width: 35px;
31 | font-size: 16px;
32 | background: var(--comfy-input-bg);
33 | border: 2px solid var(--border-color);
34 | color: var(--input-text);
35 | border-radius: 5px;
36 | float: right;
37 | position: relative;
38 | margin: 3px 2px 2px 2px;
39 | cursor: pointer;
40 | z-index: 999;
41 | }
42 |
43 | #custom_templates_header button:hover {
44 | border: 2px solid var(--descrip-text);
45 |
46 | }
47 | .sb-template-dialog {
48 | outline: 0;
49 | border: 0;
50 | border-radius: 6px;
51 | background: var(--bg-color);
52 | color: #fff;
53 | 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);
54 | max-width: 800px;
55 | min-width: 30rem;
56 | box-sizing: border-box;
57 | font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
58 | font-size: 1rem;
59 | padding: 20px;
60 | position: fixed;
61 | top: 50%;
62 | left: 50%;
63 | z-index: 1001;
64 | }
65 |
66 | #sb-template-form {
67 | margin-top: 10px;
68 | text-align: center;
69 | }
70 |
71 | .sb-input {
72 | width: 100%;
73 | padding: 8px;
74 | margin-bottom: 10px;
75 | box-sizing: border-box;
76 | border: 2px solid var(--border-color);
77 | border-radius: 4px;
78 | margin-top: 16px;
79 | background: var(--bg-color);
80 | color: var(--input-text);
81 |
82 | }
83 | .sb-input:focus {
84 | outline: none;
85 | border: 2px solid var(--descrip-text);
86 | }
87 |
88 | .sb-button {
89 | background-color: var(--comfy-menu-bg);
90 | color: var(--input-text);
91 | padding: 10px 20px;
92 | border: none;
93 | border-radius: 4px;
94 | cursor: pointer;
95 | margin-top: 10px;
96 | text-transform: uppercase;
97 | font-weight: bold;
98 | }
99 |
100 | .sb-button:hover {
101 | filter: brightness(120%);
102 | }
103 |
104 | #closeModalButton {
105 | background-color: var(--comfy-menu-bg);
106 | position: absolute;
107 | top: 0;
108 | right: 0;
109 | margin: 0;
110 | }
111 |
112 | #closeModalButton:hover {
113 | filter: brightness(120%);
114 |
115 | }
116 | #searchTemplateInput {
117 | box-sizing: border-box;
118 | width: 100%;
119 | border-radius: 5px;
120 | padding: 10px;
121 | border: none;
122 | user-select: none;
123 | background: var(--comfy-input-bg);
124 | color: var(--input-text);
125 | line-height: 1.4;
126 | border: 1px solid var(--border-color);
127 | }
128 |
129 |
130 | #previewDiv .sb_table {
131 |
132 | text-align: center;
133 | }
134 |
135 | .sidebar #previewDiv .sidebarItem, #custom_templates_main .sidebarItem {
136 |
137 | max-width: 100% !important;
138 | }
139 |
140 | #refreshlButton{
141 | margin-left: 48px !important;
142 | }
143 |
144 |
--------------------------------------------------------------------------------
/app/panels/custom_workflows/custom_workflows.html:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/app/panels/custom_workflows/style.css:
--------------------------------------------------------------------------------
1 | #custom_workflows_main,#custom_workflows_header {
2 |
3 | justify-content: center;
4 | align-items: center;
5 | height: 100%;
6 | margin: 0;
7 | }
8 | #custom_workflows_main{
9 |
10 | padding-top: 44px;
11 | padding-bottom: 150px;
12 | }
13 | #custom_workflows_header {
14 | margin: 10px 0px;
15 | font-size: 16px;
16 |
17 | }
18 | #custom_workflows_header #h_search {
19 | position: absolute;
20 | width: calc(100% - 92px);
21 | margin-top: 0px;
22 | left: 10px;
23 | z-index: 400;
24 |
25 | }
26 |
27 | #custom_workflows_header button {
28 | /* padding: 10px 20px; */
29 | height: 35px;
30 | width: 35px;
31 | font-size: 16px;
32 | background: var(--comfy-input-bg);
33 | border: 2px solid var(--border-color);
34 | color: var(--input-text);
35 | border-radius: 5px;
36 | float: right;
37 | position: relative;
38 | margin: 3px 2px 2px 2px;
39 | cursor: pointer;
40 | z-index: 999;
41 | }
42 |
43 | #custom_workflows_header button:hover {
44 | border: 2px solid var(--descrip-text);
45 |
46 | }
47 |
48 |
49 | .sidebarFolder {
50 | list-style-type: none;
51 | font-family: 'Open Sans', sans-serif;
52 | text-transform: capitalize;
53 | margin: 2px;
54 | background-color: var(--comfy-input-bg);
55 | opacity: 1;
56 | border-radius: 8px;
57 | padding-top: 11px;
58 | font-size: 15px;
59 | padding-bottom: 8px;
60 | }
61 |
62 |
63 |
64 | .sb-workflow-dialog {
65 | outline: 0;
66 | border: 0;
67 | border-radius: 6px;
68 | background: var(--bg-color);
69 | color: #fff;
70 | 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);
71 | max-width: 800px;
72 | min-width: 30rem;
73 | box-sizing: border-box;
74 | font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
75 | font-size: 1rem;
76 | padding: 20px;
77 | position: fixed;
78 | top: 50%;
79 | left: 50%;
80 | z-index: 1001;
81 | }
82 |
83 | #sb-workflow-form {
84 | margin-top: 10px;
85 | text-align: center;
86 | }
87 |
88 | .sb-input {
89 | width: 100%;
90 | padding: 8px;
91 | margin-bottom: 10px;
92 | box-sizing: border-box;
93 | border: 2px solid var(--border-color);
94 | border-radius: 4px;
95 | margin-top: 16px;
96 | background: var(--bg-color);
97 | color: var(--input-text);
98 |
99 | }
100 | .sb-input:focus {
101 | outline: none;
102 | border: 2px solid var(--descrip-text);
103 | }
104 |
105 | .sb-button {
106 | background-color: var(--comfy-menu-bg);
107 | color: var(--input-text);
108 | padding: 10px 20px;
109 | border: none;
110 | border-radius: 4px;
111 | cursor: pointer;
112 | margin-top: 10px;
113 | text-transform: uppercase;
114 | font-weight: bold;
115 | }
116 |
117 | .sb-button:hover {
118 | filter: brightness(120%);
119 | }
120 |
121 | #closeModalButton {
122 | background-color: var(--comfy-menu-bg);
123 | position: absolute;
124 | top: 0;
125 | right: 0;
126 | margin: 0;
127 | }
128 |
129 | #closeModalButton:hover {
130 | filter: brightness(120%);
131 |
132 | }
133 | #searchWorkflowInput {
134 | box-sizing: border-box;
135 | width: 100%;
136 | border-radius: 5px;
137 | padding: 10px;
138 | border: none;
139 | user-select: none;
140 | background: var(--comfy-input-bg);
141 | color: var(--input-text);
142 | line-height: 1.4;
143 | border: 1px solid var(--border-color);
144 | }
145 |
146 |
147 | #previewDiv .sb_table {
148 |
149 | text-align: center;
150 | }
151 |
152 | .sidebar #previewDiv .sidebarItem, #custom_workflows_main .sidebarItem {
153 |
154 | max-width: 100% !important;
155 | }
156 | #refreshlButton{
157 | margin-left: 48px !important;
158 | }
159 |
160 |
161 | .TIPSidebarItem {
162 | color: var(--border-color);
163 |
164 | font-family: 'Open Sans';
165 | font-size: 2rem;
166 | font-weight: bold;
167 | text-align: center;
168 |
169 | }
--------------------------------------------------------------------------------
/app/panels/terminal/style.css:
--------------------------------------------------------------------------------
1 | .terminal {
2 | width: 100%;
3 | height: 400px;
4 | background-color: #000;
5 | padding: 10px;
6 | overflow-y: auto;
7 | white-space: pre-wrap; /* Maintains spaces and line breaks */
8 | }
9 | .input-area {
10 | display: flex;
11 | }
12 | .input-field {
13 | flex: 1;
14 | background-color: #333;
15 | color: #fff;
16 | border: none;
17 | padding: 5px;
18 | }
19 | .submit-btn {
20 | background-color: #007bff;
21 | color: #fff;
22 | padding: 5px 10px;
23 | border: none;
24 | cursor: pointer;
25 | }
--------------------------------------------------------------------------------
/app/panels/terminal/terminal.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/panels/terminal/terminal.jsb:
--------------------------------------------------------------------------------
1 | var custom_example= (function () {
2 |
3 | let history = [];
4 | let historyIndex = -1;
5 |
6 | function escapeHtml(html) {
7 | const text = document.createTextNode(html);
8 | const p = document.createElement('p');
9 | p.appendChild(text);
10 | return p.innerHTML;
11 | }
12 |
13 | function sendCommand() {
14 | const inputField = document.getElementById('terminal-input');
15 | const command = inputField.value;
16 |
17 | if (command.trim() === '') return;
18 |
19 | // Add command to terminal
20 | document.getElementById('terminal-output').innerHTML += `> ${escapeHtml(command)}
`;
21 |
22 | // Send command to backend
23 | fetch('/sidebar/execute', {
24 | method: 'POST',
25 | headers: {
26 | 'Content-Type': 'application/json',
27 | },
28 | body: JSON.stringify({ command }),
29 | })
30 | .then(response => response.json())
31 | .then(data => {
32 | // Display output
33 | const output = data.output;//.replace(/\n/g, '
'); // Convert line breaks to
34 | document.getElementById('terminal-output').innerHTML += `${escapeHtml(output)}
`;
35 | // Scroll to the bottom
36 | const terminal = document.getElementById('terminal-output');
37 | terminal.scrollTop = terminal.scrollHeight;
38 | })
39 | .catch(error => {
40 | document.getElementById('terminal-output').innerHTML += `Error: ${escapeHtml(error.message)}
`;
41 | // Scroll to the bottom
42 | const terminal = document.getElementById('terminal-output');
43 | terminal.scrollTop = terminal.scrollHeight;
44 | });
45 |
46 | // Add command to history and clear input field
47 | if (history.length === 0 || history[history.length - 1] !== command) {
48 | history.push(command);
49 | }
50 | historyIndex = history.length;
51 | inputField.value = '';
52 | }
53 |
54 | function handleKeyDown(event) {
55 | const inputField = document.getElementById('terminal-input');
56 | if (event.key === 'Enter') {
57 | event.preventDefault(); // Prevent the default action (form submission or newline)
58 | sendCommand();
59 | } else if (event.key === 'ArrowUp') {
60 | event.preventDefault();
61 | if (historyIndex > 0) {
62 | historyIndex--;
63 | inputField.value = history[historyIndex];
64 | }
65 | } else if (event.key === 'ArrowDown') {
66 | event.preventDefault();
67 | if (historyIndex < history.length - 1) {
68 | historyIndex++;
69 | inputField.value = history[historyIndex];
70 | } else {
71 | historyIndex = history.length;
72 | inputField.value = '';
73 | }
74 | }
75 | }
76 |
77 | // Add event listener for Enter key and Arrow keys
78 | document.getElementById('terminal-input').addEventListener('keydown', handleKeyDown);
79 |
80 | return {
81 | sendCommand
82 | };
83 |
84 |
85 | })();
86 | addSidebarStyles("panels/terminal/style.css?v=3421");
87 |
88 |
--------------------------------------------------------------------------------
/app/views/custom_views.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 |
4 | }
5 |
6 | ]
7 |
--------------------------------------------------------------------------------
/app/views/views.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "custom_categories",
4 | "description": "Custom Node Categories",
5 | "icon": "⌬",
6 | "panels": [
7 | "custom_categories"
8 | ]
9 | },
10 | {
11 | "id": "custom_templates",
12 | "description": "Templates list",
13 | "icon": "Τ",
14 | "panels": [
15 | "custom_templates"
16 | ]
17 | },
18 | {
19 | "id": "custom_workflows",
20 | "description": "Workflows list",
21 | "icon": "W",
22 | "panels": [
23 | "custom_workflows"
24 | ]
25 | },
26 | {
27 | "id": "section_-281285428",
28 | "description": "Assets Downloader",
29 | "icon": "A",
30 | "panels": [
31 | "assets_downloader"
32 | ]
33 | },
34 | {
35 | "id": "section_-1116604772",
36 | "description": "Terminal",
37 | "icon": ">",
38 | "panels": [
39 | "terminal"
40 | ]
41 | }
42 | ]
--------------------------------------------------------------------------------
/app/views/views_default.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "custom_categories",
4 | "description": "Custom Node Categories",
5 | "icon": "⌬",
6 | "panels": [
7 | "custom_categories"
8 | ]
9 | },
10 | {
11 | "id": "custom_templates",
12 | "description": "Templates list",
13 | "icon": "Τ",
14 | "panels": [
15 | "custom_templates"
16 | ]
17 | },
18 | {
19 | "id": "custom_workflows",
20 | "description": "Workflows list",
21 | "icon": "W",
22 | "panels": [
23 | "custom_workflows"
24 | ]
25 | },
26 | {
27 | "id": "assets_downloader",
28 | "description": "Assets Downloader",
29 | "icon": "A",
30 | "panels": [
31 | "assets_downloader"
32 | ]
33 | },
34 | {
35 | "id": "section_terminal",
36 | "description": "Terminal",
37 | "icon": ">",
38 | "panels": [
39 | "terminal"
40 | ]
41 | }
42 | ]
--------------------------------------------------------------------------------
/images/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/.DS_Store
--------------------------------------------------------------------------------
/images/YouTube.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/assets.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/assets.gif
--------------------------------------------------------------------------------
/images/custom_categories.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/custom_categories.gif
--------------------------------------------------------------------------------
/images/dd.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/dd.gif
--------------------------------------------------------------------------------
/images/expand_collapse.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/expand_collapse.gif
--------------------------------------------------------------------------------
/images/fuzzysearch.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/fuzzysearch.gif
--------------------------------------------------------------------------------
/images/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/header.png
--------------------------------------------------------------------------------
/images/patreon_badge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/patreon_badge.png
--------------------------------------------------------------------------------
/images/pin.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/pin.gif
--------------------------------------------------------------------------------
/images/pin_reorder.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/pin_reorder.gif
--------------------------------------------------------------------------------
/images/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/preview.gif
--------------------------------------------------------------------------------
/images/search_categories.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/search_categories.gif
--------------------------------------------------------------------------------
/images/search_nodes.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/search_nodes.gif
--------------------------------------------------------------------------------
/images/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/settings.png
--------------------------------------------------------------------------------
/images/templates.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/templates.gif
--------------------------------------------------------------------------------
/images/theme.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/theme.gif
--------------------------------------------------------------------------------
/images/workflows.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuked88/ComfyUI-N-Sidebar/dff702527de2747fb8d649c6ebfed362df2f340e/images/workflows.gif
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "comfyui-n-sidebar"
3 | description = "A simple sidebar for ComfyUI."
4 | version = "1.6.3"
5 | license = { file = "LICENSE" }
6 |
7 | [project.urls]
8 | Repository = "https://github.com/Nuked88/ComfyUI-N-Sidebar"
9 | # Used by Comfy Registry https://comfyregistry.org
10 |
11 | [tool.comfy]
12 | PublisherId = "nuked"
13 | DisplayName = "ComfyUI-N-Sidebar"
14 | Icon = ""
15 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | aiofiles
--------------------------------------------------------------------------------