Freak-ppa commited on
Commit
6ffd0b5
1 Parent(s): eaca618

Upload 56 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. ComfyUI/custom_nodes/cg-use-everywhere/.gitattributes +2 -0
  2. ComfyUI/custom_nodes/cg-use-everywhere/.github/workflows/publish_action.yml +20 -0
  3. ComfyUI/custom_nodes/cg-use-everywhere/.gitignore +155 -0
  4. ComfyUI/custom_nodes/cg-use-everywhere/LICENSE +201 -0
  5. ComfyUI/custom_nodes/cg-use-everywhere/README.md +251 -0
  6. ComfyUI/custom_nodes/cg-use-everywhere/__init__.py +30 -0
  7. ComfyUI/custom_nodes/cg-use-everywhere/docs/ComfyUI_temp_zbfdv_00012_.png +0 -0
  8. ComfyUI/custom_nodes/cg-use-everywhere/docs/PE.png +0 -0
  9. ComfyUI/custom_nodes/cg-use-everywhere/docs/UE3.png +0 -0
  10. ComfyUI/custom_nodes/cg-use-everywhere/docs/UEQ.png +0 -0
  11. ComfyUI/custom_nodes/cg-use-everywhere/docs/UEQportrait.png +0 -0
  12. ComfyUI/custom_nodes/cg-use-everywhere/docs/auto.gif +0 -0
  13. ComfyUI/custom_nodes/cg-use-everywhere/docs/bypass_catch1.png +0 -0
  14. ComfyUI/custom_nodes/cg-use-everywhere/docs/bypass_catch2.png +0 -0
  15. ComfyUI/custom_nodes/cg-use-everywhere/docs/clashes.png +0 -0
  16. ComfyUI/custom_nodes/cg-use-everywhere/docs/conditioning.png +0 -0
  17. ComfyUI/custom_nodes/cg-use-everywhere/docs/connected.png +0 -0
  18. ComfyUI/custom_nodes/cg-use-everywhere/docs/connection-ui.png +0 -0
  19. ComfyUI/custom_nodes/cg-use-everywhere/docs/deprecated.md +33 -0
  20. ComfyUI/custom_nodes/cg-use-everywhere/docs/group.png +0 -0
  21. ComfyUI/custom_nodes/cg-use-everywhere/docs/highway.png +0 -0
  22. ComfyUI/custom_nodes/cg-use-everywhere/docs/image.png +0 -0
  23. ComfyUI/custom_nodes/cg-use-everywhere/docs/imagex.png +0 -0
  24. ComfyUI/custom_nodes/cg-use-everywhere/docs/mouseOver.gif +0 -0
  25. ComfyUI/custom_nodes/cg-use-everywhere/docs/off.png +0 -0
  26. ComfyUI/custom_nodes/cg-use-everywhere/docs/on.png +0 -0
  27. ComfyUI/custom_nodes/cg-use-everywhere/docs/portrait.png +0 -0
  28. ComfyUI/custom_nodes/cg-use-everywhere/docs/priority.gif +0 -0
  29. ComfyUI/custom_nodes/cg-use-everywhere/docs/regex.png +0 -0
  30. ComfyUI/custom_nodes/cg-use-everywhere/docs/run.png +0 -0
  31. sigma.png +0 -0
  32. ComfyUI/custom_nodes/cg-use-everywhere/docs/separate.png +0 -0
  33. ComfyUI/custom_nodes/cg-use-everywhere/docs/test-workflow-screenshot.png +0 -0
  34. ComfyUI/custom_nodes/cg-use-everywhere/docs/test-workflow.json +775 -0
  35. ComfyUI/custom_nodes/cg-use-everywhere/docs/test-workflow.png +0 -0
  36. ComfyUI/custom_nodes/cg-use-everywhere/docs/unconnected.png +0 -0
  37. ComfyUI/custom_nodes/cg-use-everywhere/docs/workflow.png +0 -0
  38. ComfyUI/custom_nodes/cg-use-everywhere/js/ue.css +39 -0
  39. ComfyUI/custom_nodes/cg-use-everywhere/js/ue_debug.js +18 -0
  40. ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere.js +272 -0
  41. ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_apply.js +31 -0
  42. ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_autocreate.js +46 -0
  43. ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_autoprompt.js +181 -0
  44. ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_classes.js +262 -0
  45. ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_graph_analysis.js +152 -0
  46. ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_nodes.js +113 -0
  47. ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_settings.js +171 -0
  48. ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_ui.js +347 -0
  49. ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_utilities.js +265 -0
  50. ComfyUI/custom_nodes/cg-use-everywhere/pyproject.toml +13 -0
ComfyUI/custom_nodes/cg-use-everywhere/.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
ComfyUI/custom_nodes/cg-use-everywhere/.github/workflows/publish_action.yml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Publish to Comfy registry
2
+ on:
3
+ workflow_dispatch:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - "pyproject.toml"
9
+
10
+ jobs:
11
+ publish-node:
12
+ name: Publish Custom Node to registry
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Check out code
16
+ uses: actions/checkout@v4
17
+ - name: Publish Custom Node
18
+ uses: Comfy-Org/publish-node-action@main
19
+ with:
20
+ personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }} ## Add your own personal access token to your Github Repository secrets and reference it here.
ComfyUI/custom_nodes/cg-use-everywhere/.gitignore ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
105
+ __pypackages__/
106
+
107
+ # Celery stuff
108
+ celerybeat-schedule
109
+ celerybeat.pid
110
+
111
+ # SageMath parsed files
112
+ *.sage.py
113
+
114
+ # Environments
115
+ .env
116
+ .venv
117
+ env/
118
+ venv/
119
+ ENV/
120
+ env.bak/
121
+ venv.bak/
122
+
123
+ # Spyder project settings
124
+ .spyderproject
125
+ .spyproject
126
+
127
+ # Rope project settings
128
+ .ropeproject
129
+
130
+ # mkdocs documentation
131
+ /site
132
+
133
+ # mypy
134
+ .mypy_cache/
135
+ .dmypy.json
136
+ dmypy.json
137
+
138
+ # Pyre type checker
139
+ .pyre/
140
+
141
+ # pytype static type analyzer
142
+ .pytype/
143
+
144
+ # Cython debug symbols
145
+ cython_debug/
146
+
147
+ # PyCharm
148
+ # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
149
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
150
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
151
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
152
+ #.idea/
153
+ .DS_Store
154
+ workflow.pastel.json
155
+ workflow.pfixed.json
ComfyUI/custom_nodes/cg-use-everywhere/LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
ComfyUI/custom_nodes/cg-use-everywhere/README.md ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # UE Nodes
2
+
3
+ Love this node? [Buy me a coffee!](https://www.buymeacoffee.com/chrisgoringe)
4
+
5
+ Getting started? Download the test workflow below and see how it works.
6
+
7
+ Problems? Jump down to [logging and debugging](https://github.com/chrisgoringe/cg-use-everywhere/blob/main/README.md#loggingdebugging)
8
+
9
+ Ideas for how to improve the nodes (or bug reports) - [raise an issue](https://github.com/chrisgoringe/cg-use-everywhere/issues)
10
+
11
+ Shameless plug for my other nodes -> Check out [Image Picker](https://github.com/chrisgoringe/cg-image-picker) for another way to make some workflows smoother. And leave a star if you like something!
12
+
13
+ ---
14
+
15
+ ## Test workflow
16
+
17
+ |This workflow uses all five nodes, and can be used to test (and understand!) the nodes. You wouldn't build it like this, it's just an example...|Here's an image with the workflow in|
18
+ |-|-|
19
+ |![screen](docs/test-workflow-screenshot.png)|![image](docs/test-workflow.png)|
20
+
21
+ Or [the workflow as json](docs/test-workflow.json)
22
+
23
+ ## Current known limitations
24
+
25
+ There are some situations that UE nodes can't cope with at present. Here are some I know about, and possible workarounds.
26
+
27
+ ### Pythonssss Preset Text
28
+
29
+ [pythonsssss](https://github.com/pythongosssss/ComfyUI-Custom-Scripts) custom nodes are great, but there are some limitations in using them with UE nodes. In particular, you can't feed the output of a Preset Text node directly into a UE node (see https://github.com/chrisgoringe/cg-use-everywhere/issues/154).
30
+
31
+ ### Group nodes
32
+
33
+ UE nodes mostly work with group nodes. But there are a couple of important things to note:
34
+
35
+ - when you create a group node the input names and node names can change. This might break UE? regex connections.
36
+
37
+ ## Latest updates
38
+
39
+ 4.9 (2nd May 2024)
40
+ - Fix incompatibility with Efficiency Nodes (#182)
41
+
42
+ 4.8 (18th March 2024)
43
+ - Group and color sending have a `send to unmatched` mode
44
+ - UE link animations can be the classic dots, or a pulsing glow (or both, or neither)
45
+ - Show UE links can now be on, off, mouseover, selected nodes, or mouseover and selected nodes
46
+
47
+ 4.7 (1st March 2024)
48
+ - UE now works in group nodes
49
+ - Autocomplete on `Anything Everywhere?` nodes
50
+
51
+ 4.6
52
+ - add Group Regex to `Anything Everywhere?` node
53
+ - if you have workflow json files saved that now don't work, try 'workflow_fixer.py'
54
+
55
+ 4.5
56
+ - add support for Comfy UI Group Nodes (UE nodes can be used to connect to group node inputs and outputs, but not within a group node)
57
+ - add `convert to real links`
58
+
59
+ 4.4
60
+ - add (limited) support for converting regex in the `Anything Everywhere?` node with inputs (only works if the link is from a node that is a simple string source)
61
+
62
+ 4.3
63
+ - added support for targetting [Highway nodes](https://github.com/chrisgoringe/cg-use-everywhere#highway-nodes)
64
+
65
+ 4.2
66
+ - improved performance of loop detection, especially with [highway nodes](https://github.com/Trung0246/ComfyUI-0246)
67
+ - updated docs to not use other custom nodes in examples
68
+
69
+ 4.1.2
70
+ - tweaks to improve handling of bypass
71
+ - fixed connecting to Seed Everywhere
72
+
73
+ 4.1.1
74
+ - added option to turn animation off
75
+
76
+ 4.1
77
+
78
+ - added [loop detection](https://github.com/chrisgoringe/cg-use-everywhere#loop-checking)
79
+ - added [group restriction](https://github.com/chrisgoringe/cg-use-everywhere#group-restriction).
80
+
81
+ The v1 nodes have been fully removed. If you were using one, you can just replace it with an `Anything Everywhere` node.
82
+
83
+ ## Installing
84
+
85
+ Use Comfy Manager. If you really want to do it manually, just clone this repository in your custom_nodes directory.
86
+
87
+ ## Anything Everywhere (start here!)
88
+
89
+ The `Anything Everywhere` node has a single input, initially labelled 'anything'. Connect anything to it (directly - not via a reroute), and the input name changes to match the input type. Disconnect and it goes back to 'anything'.
90
+
91
+ When you run the prompt, any unconnected input, anywhere in the workflow, which matches that type, will act as if it were connected to the same input.
92
+
93
+ To visualise what it's being connected to, right-click on the background canvas and select `Toggle UE Link Visibility`.
94
+
95
+ ## Anything Everywhere? - control matching with regex rules
96
+
97
+ This node adds two widgets - title_regex and input_regex. It will only send to inputs which match. So in the example, title_regex is 'Preview' so the image is sent to the Preview Image node but not the Save Image node. Note that you can rename node and input titles, which can help!
98
+
99
+ (From 4.6 you can also specify a group regex to only match inputs on nodes which are in groups that match the regex.)
100
+
101
+ ![regex](docs/regex.png)
102
+
103
+ *The matches are regular expressions, not string matches.* Most simple strings will work (matching any part of the title or input name), but some characters have special meanings (including various sorts of brackets, ^, $, /, and . in particular) so just avoid them if you aren't regex-inclined.
104
+
105
+ Using regex means you can use `^prompt` to match `prompt` at the beginning of the title only, to avoid matching `negative_prompt`.
106
+
107
+ Regex 101 - `^` means 'the start', `$` means 'the end', `.` matches any single character, `.*` matches anything of any length (including zero). For more than that, visit [regex101](https://regex101.com/) (the flavour you want is ECMAScript, though that probably won't matter).
108
+
109
+ ### Can I make the regex an input instead of a widget?
110
+
111
+ Sort of.
112
+
113
+ Because the regex needs to be known before the workflow is submitted (in order to calculate the links), you can't pass a string into the `Anything Everywhere?` node and expect it to work. The *only* thing that is supported is if the input comes *directly* from a node which sets it with a string widget. The `Simple String` node that is included in this pack will work.
114
+
115
+ |This works|This doesn't. And never will.|
116
+ |-|-|
117
+ |![Alt text](docs/image.png)|![no](docs/imagex.png)|
118
+
119
+
120
+ ## Seed Everywhere
121
+
122
+ Seed Everywhere connects to any unconnected INT input with `seed` in the input name (seed, noise_seed, etc), and it has the control_after_generate feature. So if you convert the seed widgets to inputs you can use the same seed everywhere.
123
+
124
+ ## Anything Everywhere3 - One node, three inputs.
125
+
126
+ Really just three `Anything Everywhere` nodes packaged together. Designed for the outputs of Checkpoint Loader.
127
+
128
+ ![UE3](docs/UE3.png)
129
+
130
+ ## Prompts Everywhere - two strings or conditionings
131
+
132
+ Prompt Everywhere has two inputs. They will be sent with regex matching rules of `(^prompt|^positive)` and `neg` respectively. These should match the various versions of names that get used for prompts and negative prompts or conditionings.
133
+
134
+ |strings|conditionings|
135
+ |-|-|
136
+ |![pe](docs/PE.png)|![pe](docs/conditioning.png)
137
+
138
+ # Primitives and COMBOs and the like
139
+
140
+ UE nodes don't work with primitives and COMBOs (the data type used for dropdown lists, which are also a type of primitive within Comfy). It's unlikely they ever will.
141
+
142
+ If you want to use UE to control sampler or sigma, you can do this with the built in `SamplerCustom` nodes:
143
+
144
+ ![sample and sigma](docs/sampler%20and%20sigma.png)
145
+
146
+ For more on this, see [this discussion](https://github.com/chrisgoringe/cg-use-everywhere/issues/69)
147
+
148
+ # Other features
149
+
150
+ ## Show links - visualisation and animation.
151
+
152
+ If you want to see the UE links, you can turn them on and off by right-clicking on the canvas. For finer control, the main settings menu has options to show links when the mouse moves over the node at either end, or when one of those nodes is selected.
153
+
154
+ The links can be animated to distinguish them from normal links - this animation can take the form of moving dots, a pulsing glow, or both. This may impact performance in some cases - note that the pulse animation requires less processing than the moving dots. Control this in the main settings menu.
155
+
156
+ By default the animations turn off when the workflow is running to minimise impact on CPU/GPU - you can change this in the settings too.
157
+
158
+ ## Convert to real links
159
+
160
+ If you want to share a workflow without UE nodes being required, or to save an API version of a workflow, you can replace the virtual links created by UE nodes with real links (and remove the UE nodes).
161
+
162
+ This can be done for a single node by right-clicking on it and selecting `Convert to real links`, or for all UE nodes in a workflow by right-clicking the background and selecting `Convert all UEs to real links`.
163
+
164
+ ## Shift drag
165
+
166
+ Shift click on an output node and drag then release to get an autocreate menu. This replaces the default behaviour (which gives you a search box), so you can disable it with the `Anything Everywhere replace search` setting.
167
+
168
+ ![auto](docs/auto.gif)
169
+
170
+ ## Group and color restriction
171
+
172
+ UE nodes can be restricted to send only to nodes of the same color, or only to nodes that *aren't* the same color.
173
+
174
+ They can also be restricted to send only to nodes in the same group (any group in common), or only to nodes that aren't in the same group.
175
+
176
+ Right-click on the node and select `Group restrictions` or `Color restrictions`. UE nodes which are restricted (in either or both ways) have a green circle in the top-left corner.
177
+
178
+ ## Highway nodes
179
+
180
+ Trung 0246's [Highway nodes](https://github.com/Trung0246/ComfyUI-0246) are a pretty cool way of piping data around. You can target them with an `Anything Everywhere?` node by using an `input_regex` which matches the unconnected input name with the '+', like this:
181
+ ![highway](docs/highway.png)
182
+
183
+ This is new, so please report any issues!
184
+
185
+ ## Loop checking
186
+
187
+ By default workflows are checked for loops before they are submitted (because UE can introduce them, and a loop results in a bad python outcome). If a loop is detected you'll get a JavaScript warning showing you the node ids involved. However, especially if there are other custom nodes involved, it's possible that the check will miss a loop, or flag one that isn't real.
188
+
189
+ If you get a warning and don't believe there is a loop (having checked the node ids listed!) you can turn loop checking off in the main settings menu. If something flagged as a loop runs fine, please [raise an issue](https://github.com/chrisgoringe/cg-use-everywhere/issues) and include the workflow in the report (save the json and zip it, because GitHub doesn't accept .json files). Likewise if a loop doesn't get caught.
190
+
191
+ I've written code for the core Comfy backend to catch loops, maybe it'll be included - [PR for ComfyUI](https://github.com/comfyanonymous/ComfyUI/pull/1652) - or maybe they have another plan.
192
+
193
+ ## Priorities
194
+
195
+ If there is more than one sending node that matches an input, the basic rules is that the more specific node wins. The order of priorities is:
196
+
197
+ - `Anything Everywhere?`
198
+ - `Seed Everywhere` and `Prompts Everywhere`
199
+ - `Anything Everywhere`
200
+ - `Anything Everywhere3`
201
+
202
+ For nodes of the same time, those with colour restrictions and group restriction are prioritised (colour+group > colour > group > none).
203
+
204
+ If two nodes with the same priority both match *neither will connect* - better to fail fast than have an ambiguous outcome. If there are ambiguous matches you can display them using `Show UE broadcast clashes` (right-click on background - the option only appears if there are clashes).
205
+
206
+ ## See what is sent
207
+
208
+ The nodes which only have one output can also gain a text box showing exactly what passed through the node. You need to turn this on if you want it - it's in the main settings, 'Anything Everywhere node details'.
209
+
210
+ ## Logging/Debugging
211
+
212
+ The JavaScript console (press f12 in some browsers) has logging information about what is being connected. You can change the level of detail by finding the file `[comfy_install]/custom_nodes/cg-use-everywhere/js/use_everywhre_utilities.js` and near the top finding this bit:
213
+ ```javascript
214
+ static ERROR = 0; // actual errors
215
+ static PROBLEM = 1; // things that stop the workflow working
216
+ static INFORMATION = 2; // record of good things
217
+ static DETAIL = 3; // details
218
+
219
+ static LEVEL = Logger.PROBLEM;
220
+ static TRACE = false; // most of the method calls
221
+ ```
222
+ Change the `LEVEL` to `Logger.INFORMATION` for more, or `Logger.DETAIL` for even more; set `TRACE` to `true` for some other debugging information.
223
+
224
+ If you have a problem, pressing f12 to see the JavaScript console can often help. The following steps are really helpful in making a good bug report:
225
+
226
+ - update to the latest version
227
+ - restart ComfyUI
228
+ - clear the canvas
229
+ - close the browser
230
+ - open a new Comfy window (with no workflow), look in console (f12) to see if there were any errors as ComfyUI started up
231
+ - load your workflow, and look again
232
+ - run, and look again
233
+
234
+ The other thing worth trying is clearing out all the custom node javascript from where it gets copied when ComfyUI starts:
235
+
236
+ - stop Comfy
237
+ - go to [comfy root]/web/extensions (*not* under custom_nodes)
238
+ - remove everything there EXCEPT for `core`. Leave `core` (it's ComfyUI stuff)
239
+ - restart Comfy (all custom nodes will reinstall their javascript at startup)
240
+
241
+ If you find a bug, please [raise an issue](https://github.com/chrisgoringe/cg-use-everywhere/issues) - if you can include the workflow, that's a huge help (you'll need to save it as .txt, or zip the .json file, because GitHub doesn't accept .json).
242
+
243
+ ## Cautions
244
+
245
+ Bypassing and disabling nodes works, but with one catch. If you have a UE nodes that does matching (`Anything Everywhere?` and `Prompt Everywhere`) and you bypass the node it matches to, the link won't be made. So
246
+
247
+ |If you use a ? node to send to a node...|...and bypass the recipient, it doesn't get connected |
248
+ |-|-|
249
+ |![1](docs/bypass_catch1.png)|![2](docs/bypass_catch2.png)|
250
+
251
+ This is unlikely to be fixed, but should be fairly easy to avoid!
ComfyUI/custom_nodes/cg-use-everywhere/__init__.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .use_everywhere import SeedEverywhere, AnythingEverywherePrompts
2
+
3
+ UE_VERSION = "4.8.5"
4
+
5
+ NODE_CLASS_MAPPINGS = { "Seed Everywhere": SeedEverywhere }
6
+
7
+ from .use_everywhere import AnythingEverywhere, AnythingSomewhere, AnythingEverywhereTriplet, SimpleString
8
+ NODE_CLASS_MAPPINGS["Anything Everywhere"] = AnythingEverywhere
9
+ NODE_CLASS_MAPPINGS["Anything Everywhere3"] = AnythingEverywhereTriplet
10
+ NODE_CLASS_MAPPINGS["Anything Everywhere?"] = AnythingSomewhere
11
+ NODE_CLASS_MAPPINGS["Prompts Everywhere"] = AnythingEverywherePrompts
12
+ NODE_CLASS_MAPPINGS["Simple String"] = SimpleString
13
+
14
+ import os, shutil
15
+ import folder_paths
16
+
17
+ # temporary code to remove old javascript installs
18
+ module_js_directory = os.path.join(os.path.dirname(os.path.realpath(__file__)), "js")
19
+ application_root_directory = os.path.dirname(folder_paths.__file__)
20
+ old_code_location = os.path.join(application_root_directory, "web", "extensions", "use_everywhere")
21
+ if os.path.exists(old_code_location):
22
+ shutil.rmtree(old_code_location)
23
+
24
+ old_code_location = os.path.join(application_root_directory, "web", "extensions", "cg-nodes", "use_everywhere.js")
25
+ if os.path.exists(old_code_location):
26
+ os.remove(old_code_location)
27
+ # end of temporary code
28
+
29
+ WEB_DIRECTORY = "./js"
30
+ __all__ = ["NODE_CLASS_MAPPINGS", "WEB_DIRECTORY"]
ComfyUI/custom_nodes/cg-use-everywhere/docs/ComfyUI_temp_zbfdv_00012_.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/PE.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/UE3.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/UEQ.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/UEQportrait.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/auto.gif ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/bypass_catch1.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/bypass_catch2.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/clashes.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/conditioning.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/connected.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/connection-ui.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/deprecated.md ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # Deprecated Nodes
3
+
4
+ This is the old documentation, in case you have a workflow still using the deprecated nodes.
5
+
6
+
7
+ UE nodes are "Use Everywhere". Put a UE node into your workflow, connect its input, and every node with an unconnected input of the same type will act as if connected to it.
8
+
9
+ CLIP, IMAGE, MODEL, VAE, CONDITIONING, or LATENT (want something else? Edit `__init__.py` line 3.)
10
+
11
+ Update: added INT, MASK, and CHECKPOIMNT - which combines MODEL, CLIP, and VAE, and a special node for SEEDs.
12
+
13
+ | Model, clip, vae, latent and image are all being automagically connected. | Drop this image into ComfyUI to get a working workflow. |
14
+ |-|-|
15
+ |![workflow](./workflow.png)|![portrait](./portrait.png)|
16
+
17
+ ## UE? Nodes
18
+
19
+ UE? nodes are like UE Nodes, but add two widgets, 'title' and 'input'. These are Regular Expressions, and the node will only send to nodes where the node Title and the unconnected input name match.
20
+
21
+ It doesn't need to be a complete match - the logic is `regex.match(name) || regex.match(title)`, so if you want to match the exact name `seed`, you'll need something like `^seed$` as your regex.
22
+
23
+ Regex 101 - ^ means 'the start', $ means 'the end', '.' matches anything, '.*' matches any number of anything. For more than that, visit [regex101](https://regex101.com/) (the flavour you want is ECMAScript, though that probably won't matter).
24
+
25
+ | So you can do things like: | Drop this image into ComfyUI to get a working workflow. |
26
+ |-|-|
27
+ |![this](./UEQ.png)|![drop](./UEQportrait.png)|
28
+
29
+ ## Widget?
30
+
31
+ A UE or UE? node with just one output can have the output converted to a widget. But the combination ones can't. Also note that if you convert it to a widget, you can't then change the title
32
+
33
+ Why not? because the code gets the data type from the input (weirdly the prompt doesn't contain the data type on outputs), and it's not available if it's a widget, because reasons, so the hack is to get the data type from what comes after `UE ` in the title...
ComfyUI/custom_nodes/cg-use-everywhere/docs/group.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/highway.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/image.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/imagex.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/mouseOver.gif ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/off.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/on.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/portrait.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/priority.gif ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/regex.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/run.png ADDED
sigma.png RENAMED
File without changes
ComfyUI/custom_nodes/cg-use-everywhere/docs/separate.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/test-workflow-screenshot.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/test-workflow.json ADDED
@@ -0,0 +1,775 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "last_node_id": 185,
3
+ "last_link_id": 555,
4
+ "nodes": [
5
+ {
6
+ "id": 144,
7
+ "type": "PreviewImage",
8
+ "pos": [
9
+ 928,
10
+ -39
11
+ ],
12
+ "size": {
13
+ "0": 430.8935546875,
14
+ "1": 533.0433349609375
15
+ },
16
+ "flags": {},
17
+ "order": 0,
18
+ "mode": 0,
19
+ "inputs": [
20
+ {
21
+ "name": "images",
22
+ "type": "IMAGE",
23
+ "link": null
24
+ }
25
+ ],
26
+ "properties": {
27
+ "Node name for S&R": "PreviewImage"
28
+ }
29
+ },
30
+ {
31
+ "id": 180,
32
+ "type": "Prompts Everywhere",
33
+ "pos": [
34
+ 1189,
35
+ -256
36
+ ],
37
+ "size": {
38
+ "0": 177.46200561523438,
39
+ "1": 46
40
+ },
41
+ "flags": {},
42
+ "order": 13,
43
+ "mode": 0,
44
+ "inputs": [
45
+ {
46
+ "name": "CONDITIONING",
47
+ "type": "*",
48
+ "link": 535,
49
+ "color_on": "#FFA931"
50
+ },
51
+ {
52
+ "name": "CONDITIONING",
53
+ "type": "*",
54
+ "link": 536,
55
+ "color_on": "#FFA931"
56
+ }
57
+ ],
58
+ "properties": {
59
+ "Node name for S&R": "Prompts Everywhere",
60
+ "group_restricted": false
61
+ }
62
+ },
63
+ {
64
+ "id": 148,
65
+ "type": "CheckpointLoaderSimple",
66
+ "pos": [
67
+ -356,
68
+ -204
69
+ ],
70
+ "size": {
71
+ "0": 308.89697265625,
72
+ "1": 98
73
+ },
74
+ "flags": {},
75
+ "order": 1,
76
+ "mode": 0,
77
+ "outputs": [
78
+ {
79
+ "name": "MODEL",
80
+ "type": "MODEL",
81
+ "links": [
82
+ 540
83
+ ],
84
+ "shape": 3,
85
+ "slot_index": 0
86
+ },
87
+ {
88
+ "name": "CLIP",
89
+ "type": "CLIP",
90
+ "links": [
91
+ 542
92
+ ],
93
+ "shape": 3,
94
+ "slot_index": 1
95
+ },
96
+ {
97
+ "name": "VAE",
98
+ "type": "VAE",
99
+ "links": [
100
+ 539
101
+ ],
102
+ "shape": 3,
103
+ "slot_index": 2
104
+ }
105
+ ],
106
+ "properties": {
107
+ "Node name for S&R": "CheckpointLoaderSimple"
108
+ },
109
+ "widgets_values": [
110
+ "copaxVividXL_v2.safetensors"
111
+ ]
112
+ },
113
+ {
114
+ "id": 181,
115
+ "type": "Anything Everywhere3",
116
+ "pos": [
117
+ 332,
118
+ -204
119
+ ],
120
+ "size": {
121
+ "0": 210,
122
+ "1": 66
123
+ },
124
+ "flags": {},
125
+ "order": 14,
126
+ "mode": 0,
127
+ "inputs": [
128
+ {
129
+ "name": "MODEL",
130
+ "type": "*",
131
+ "link": 541,
132
+ "color_on": "#B39DDB"
133
+ },
134
+ {
135
+ "name": "CLIP",
136
+ "type": "*",
137
+ "link": 543,
138
+ "color_on": "#FFD500"
139
+ },
140
+ {
141
+ "name": "VAE",
142
+ "type": "*",
143
+ "link": 539,
144
+ "color_on": "#FF6E6E"
145
+ }
146
+ ],
147
+ "properties": {
148
+ "Node name for S&R": "Anything Everywhere3",
149
+ "group_restricted": false
150
+ }
151
+ },
152
+ {
153
+ "id": 178,
154
+ "type": "EmptyLatentImage",
155
+ "pos": [
156
+ -350,
157
+ 1
158
+ ],
159
+ "size": {
160
+ "0": 269.2752990722656,
161
+ "1": 106
162
+ },
163
+ "flags": {},
164
+ "order": 2,
165
+ "mode": 0,
166
+ "outputs": [
167
+ {
168
+ "name": "LATENT",
169
+ "type": "LATENT",
170
+ "links": [
171
+ 544
172
+ ],
173
+ "shape": 3,
174
+ "slot_index": 0
175
+ }
176
+ ],
177
+ "properties": {
178
+ "Node name for S&R": "EmptyLatentImage"
179
+ },
180
+ "widgets_values": [
181
+ 768,
182
+ 1024,
183
+ 1
184
+ ]
185
+ },
186
+ {
187
+ "id": 183,
188
+ "type": "Anything Everywhere",
189
+ "pos": [
190
+ -316,
191
+ 179
192
+ ],
193
+ "size": {
194
+ "0": 210,
195
+ "1": 26
196
+ },
197
+ "flags": {},
198
+ "order": 10,
199
+ "mode": 0,
200
+ "inputs": [
201
+ {
202
+ "name": "LATENT",
203
+ "type": "*",
204
+ "link": 544,
205
+ "color_on": "#FF9CF9"
206
+ }
207
+ ],
208
+ "properties": {
209
+ "Node name for S&R": "Anything Everywhere",
210
+ "group_restricted": false
211
+ }
212
+ },
213
+ {
214
+ "id": 5,
215
+ "type": "KSampler",
216
+ "pos": [
217
+ 51,
218
+ -1
219
+ ],
220
+ "size": {
221
+ "0": 260.72747802734375,
222
+ "1": 249.28138732910156
223
+ },
224
+ "flags": {},
225
+ "order": 3,
226
+ "mode": 0,
227
+ "inputs": [
228
+ {
229
+ "name": "model",
230
+ "type": "MODEL",
231
+ "link": null
232
+ },
233
+ {
234
+ "name": "positive",
235
+ "type": "CONDITIONING",
236
+ "link": null
237
+ },
238
+ {
239
+ "name": "negative",
240
+ "type": "CONDITIONING",
241
+ "link": null
242
+ },
243
+ {
244
+ "name": "latent_image",
245
+ "type": "LATENT",
246
+ "link": null
247
+ },
248
+ {
249
+ "name": "seed",
250
+ "type": "INT",
251
+ "link": null,
252
+ "widget": {
253
+ "name": "seed"
254
+ }
255
+ }
256
+ ],
257
+ "outputs": [
258
+ {
259
+ "name": "LATENT",
260
+ "type": "LATENT",
261
+ "links": [
262
+ 545
263
+ ],
264
+ "shape": 3,
265
+ "slot_index": 0
266
+ }
267
+ ],
268
+ "properties": {
269
+ "Node name for S&R": "KSampler"
270
+ },
271
+ "widgets_values": [
272
+ 1125899906842624,
273
+ "increment",
274
+ 35,
275
+ 8,
276
+ "dpmpp_3m_sde",
277
+ "karras",
278
+ 1
279
+ ],
280
+ "color": "#57571a",
281
+ "bgcolor": "#6b6b2e"
282
+ },
283
+ {
284
+ "id": 184,
285
+ "type": "Anything Everywhere?",
286
+ "pos": [
287
+ 339,
288
+ 0
289
+ ],
290
+ "size": {
291
+ "0": 210,
292
+ "1": 82
293
+ },
294
+ "flags": {},
295
+ "order": 11,
296
+ "mode": 0,
297
+ "inputs": [
298
+ {
299
+ "name": "LATENT",
300
+ "type": "*",
301
+ "link": 545,
302
+ "color_on": "#FF9CF9"
303
+ }
304
+ ],
305
+ "properties": {
306
+ "Node name for S&R": "Anything Everywhere?",
307
+ "group_restricted": false
308
+ },
309
+ "widgets_values": [
310
+ ".*",
311
+ "samples"
312
+ ]
313
+ },
314
+ {
315
+ "id": 179,
316
+ "type": "Anything Everywhere",
317
+ "pos": [
318
+ 624,
319
+ 185
320
+ ],
321
+ "size": {
322
+ "0": 181.96005249023438,
323
+ "1": 26
324
+ },
325
+ "flags": {},
326
+ "order": 12,
327
+ "mode": 0,
328
+ "inputs": [
329
+ {
330
+ "name": "IMAGE",
331
+ "type": "*",
332
+ "link": 534,
333
+ "color_on": "#64B5F6"
334
+ }
335
+ ],
336
+ "properties": {
337
+ "Node name for S&R": "Anything Everywhere",
338
+ "group_restricted": false
339
+ }
340
+ },
341
+ {
342
+ "id": 7,
343
+ "type": "VAEDecode",
344
+ "pos": [
345
+ 637,
346
+ 74
347
+ ],
348
+ "size": {
349
+ "0": 140,
350
+ "1": 46
351
+ },
352
+ "flags": {},
353
+ "order": 4,
354
+ "mode": 0,
355
+ "inputs": [
356
+ {
357
+ "name": "samples",
358
+ "type": "LATENT",
359
+ "link": null
360
+ },
361
+ {
362
+ "name": "vae",
363
+ "type": "VAE",
364
+ "link": null
365
+ }
366
+ ],
367
+ "outputs": [
368
+ {
369
+ "name": "IMAGE",
370
+ "type": "IMAGE",
371
+ "links": [
372
+ 534
373
+ ],
374
+ "shape": 3,
375
+ "slot_index": 0
376
+ }
377
+ ],
378
+ "properties": {
379
+ "Node name for S&R": "VAEDecode"
380
+ },
381
+ "color": "#2e571a",
382
+ "bgcolor": "#426b2e"
383
+ },
384
+ {
385
+ "id": 182,
386
+ "type": "LoraLoader",
387
+ "pos": [
388
+ 15,
389
+ -290
390
+ ],
391
+ "size": {
392
+ "0": 273.7867126464844,
393
+ "1": 126
394
+ },
395
+ "flags": {},
396
+ "order": 9,
397
+ "mode": 0,
398
+ "inputs": [
399
+ {
400
+ "name": "model",
401
+ "type": "MODEL",
402
+ "link": 540
403
+ },
404
+ {
405
+ "name": "clip",
406
+ "type": "CLIP",
407
+ "link": 542
408
+ }
409
+ ],
410
+ "outputs": [
411
+ {
412
+ "name": "MODEL",
413
+ "type": "MODEL",
414
+ "links": [
415
+ 541
416
+ ],
417
+ "shape": 3,
418
+ "slot_index": 0
419
+ },
420
+ {
421
+ "name": "CLIP",
422
+ "type": "CLIP",
423
+ "links": [
424
+ 543
425
+ ],
426
+ "shape": 3,
427
+ "slot_index": 1
428
+ }
429
+ ],
430
+ "properties": {
431
+ "Node name for S&R": "LoraLoader"
432
+ },
433
+ "widgets_values": [
434
+ "sd_xl_offset_example-lora_1.0.safetensors",
435
+ 1,
436
+ 1
437
+ ]
438
+ },
439
+ {
440
+ "id": 185,
441
+ "type": "Note",
442
+ "pos": [
443
+ 396,
444
+ 326
445
+ ],
446
+ "size": {
447
+ "0": 437.6109619140625,
448
+ "1": 131.43035888671875
449
+ },
450
+ "flags": {},
451
+ "order": 5,
452
+ "mode": 0,
453
+ "properties": {
454
+ "text": ""
455
+ },
456
+ "widgets_values": [
457
+ "This workflow uses all the UE nodes, and can also test bypass (load LoRA)"
458
+ ],
459
+ "color": "#432",
460
+ "bgcolor": "#653"
461
+ },
462
+ {
463
+ "id": 169,
464
+ "type": "Seed Everywhere",
465
+ "pos": [
466
+ 81,
467
+ 345
468
+ ],
469
+ "size": {
470
+ "0": 210,
471
+ "1": 82
472
+ },
473
+ "flags": {},
474
+ "order": 6,
475
+ "mode": 0,
476
+ "outputs": [
477
+ {
478
+ "name": "INT",
479
+ "type": "INT",
480
+ "links": null,
481
+ "shape": 3
482
+ }
483
+ ],
484
+ "properties": {
485
+ "Node name for S&R": "Seed Everywhere",
486
+ "group_restricted": false
487
+ },
488
+ "widgets_values": [
489
+ 356735678581,
490
+ "fixed"
491
+ ]
492
+ },
493
+ {
494
+ "id": 162,
495
+ "type": "CLIPTextEncode",
496
+ "pos": [
497
+ 599,
498
+ -303
499
+ ],
500
+ "size": {
501
+ "0": 247.4329071044922,
502
+ "1": 96
503
+ },
504
+ "flags": {},
505
+ "order": 7,
506
+ "mode": 0,
507
+ "inputs": [
508
+ {
509
+ "name": "clip",
510
+ "type": "CLIP",
511
+ "link": null
512
+ }
513
+ ],
514
+ "outputs": [
515
+ {
516
+ "name": "CONDITIONING",
517
+ "type": "CONDITIONING",
518
+ "links": [
519
+ 535
520
+ ],
521
+ "shape": 3,
522
+ "slot_index": 0
523
+ }
524
+ ],
525
+ "properties": {
526
+ "Node name for S&R": "CLIPTextEncode"
527
+ },
528
+ "widgets_values": [
529
+ "atmospheric photo of woman at night"
530
+ ]
531
+ },
532
+ {
533
+ "id": 163,
534
+ "type": "CLIPTextEncode",
535
+ "pos": [
536
+ 873,
537
+ -210
538
+ ],
539
+ "size": {
540
+ "0": 247.4329071044922,
541
+ "1": 96
542
+ },
543
+ "flags": {},
544
+ "order": 8,
545
+ "mode": 0,
546
+ "inputs": [
547
+ {
548
+ "name": "clip",
549
+ "type": "CLIP",
550
+ "link": null
551
+ }
552
+ ],
553
+ "outputs": [
554
+ {
555
+ "name": "CONDITIONING",
556
+ "type": "CONDITIONING",
557
+ "links": [
558
+ 536
559
+ ],
560
+ "shape": 3,
561
+ "slot_index": 0
562
+ }
563
+ ],
564
+ "properties": {
565
+ "Node name for S&R": "CLIPTextEncode"
566
+ },
567
+ "widgets_values": [
568
+ "blue"
569
+ ]
570
+ }
571
+ ],
572
+ "links": [
573
+ [
574
+ 534,
575
+ 7,
576
+ 0,
577
+ 179,
578
+ 0,
579
+ "*"
580
+ ],
581
+ [
582
+ 535,
583
+ 162,
584
+ 0,
585
+ 180,
586
+ 0,
587
+ "*"
588
+ ],
589
+ [
590
+ 536,
591
+ 163,
592
+ 0,
593
+ 180,
594
+ 1,
595
+ "*"
596
+ ],
597
+ [
598
+ 539,
599
+ 148,
600
+ 2,
601
+ 181,
602
+ 2,
603
+ "*"
604
+ ],
605
+ [
606
+ 540,
607
+ 148,
608
+ 0,
609
+ 182,
610
+ 0,
611
+ "MODEL"
612
+ ],
613
+ [
614
+ 541,
615
+ 182,
616
+ 0,
617
+ 181,
618
+ 0,
619
+ "*"
620
+ ],
621
+ [
622
+ 542,
623
+ 148,
624
+ 1,
625
+ 182,
626
+ 1,
627
+ "CLIP"
628
+ ],
629
+ [
630
+ 543,
631
+ 182,
632
+ 1,
633
+ 181,
634
+ 1,
635
+ "*"
636
+ ],
637
+ [
638
+ 544,
639
+ 178,
640
+ 0,
641
+ 183,
642
+ 0,
643
+ "*"
644
+ ],
645
+ [
646
+ 545,
647
+ 5,
648
+ 0,
649
+ 184,
650
+ 0,
651
+ "*"
652
+ ],
653
+ [
654
+ 546,
655
+ 7,
656
+ 0,
657
+ 144,
658
+ 0,
659
+ "IMAGE"
660
+ ],
661
+ [
662
+ 547,
663
+ 182,
664
+ 0,
665
+ 5,
666
+ 0,
667
+ "MODEL"
668
+ ],
669
+ [
670
+ 548,
671
+ 162,
672
+ 0,
673
+ 5,
674
+ 1,
675
+ "CONDITIONING"
676
+ ],
677
+ [
678
+ 549,
679
+ 163,
680
+ 0,
681
+ 5,
682
+ 2,
683
+ "CONDITIONING"
684
+ ],
685
+ [
686
+ 550,
687
+ 178,
688
+ 0,
689
+ 5,
690
+ 3,
691
+ "LATENT"
692
+ ],
693
+ [
694
+ 551,
695
+ 169,
696
+ 0,
697
+ 5,
698
+ 4,
699
+ "INT"
700
+ ],
701
+ [
702
+ 552,
703
+ 5,
704
+ 0,
705
+ 7,
706
+ 0,
707
+ "LATENT"
708
+ ],
709
+ [
710
+ 553,
711
+ 148,
712
+ 2,
713
+ 7,
714
+ 1,
715
+ "VAE"
716
+ ],
717
+ [
718
+ 554,
719
+ 182,
720
+ 1,
721
+ 162,
722
+ 0,
723
+ "CLIP"
724
+ ],
725
+ [
726
+ 555,
727
+ 182,
728
+ 1,
729
+ 163,
730
+ 0,
731
+ "CLIP"
732
+ ]
733
+ ],
734
+ "groups": [
735
+ {
736
+ "title": "Model",
737
+ "bounding": [
738
+ -371,
739
+ -387,
740
+ 926,
741
+ 294
742
+ ],
743
+ "color": "#3f789e",
744
+ "font_size": 24,
745
+ "locked": false
746
+ },
747
+ {
748
+ "title": "Conditioning",
749
+ "bounding": [
750
+ 571,
751
+ -391,
752
+ 836,
753
+ 294
754
+ ],
755
+ "color": "#a1309b",
756
+ "font_size": 24,
757
+ "locked": false
758
+ },
759
+ {
760
+ "title": "Sampling",
761
+ "bounding": [
762
+ -372,
763
+ -74,
764
+ 1198,
765
+ 343
766
+ ],
767
+ "color": "#b06634",
768
+ "font_size": 24,
769
+ "locked": false
770
+ }
771
+ ],
772
+ "config": {},
773
+ "extra": {},
774
+ "version": 0.4
775
+ }
ComfyUI/custom_nodes/cg-use-everywhere/docs/test-workflow.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/unconnected.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/docs/workflow.png ADDED
ComfyUI/custom_nodes/cg-use-everywhere/js/ue.css ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ .litegraph label.ueprompt {
3
+ padding:0px;
4
+ font-size: 12px;
5
+ border-radius: 0;
6
+ border: 0;
7
+ box-shadow: none !important;
8
+ margin:0px;
9
+ height:30px;
10
+ background-color: rgba(0,0,0,0);
11
+ }
12
+
13
+ .litegraph span.ueprompttext {
14
+ margin: 0px;
15
+ min-width: 30px;
16
+ font-family: Arial, sans-serif;
17
+ color: var(--descrip-text);
18
+ text-align: right;
19
+ padding: 2px 2px 4px 0px;
20
+ background-color: inherit;
21
+ }
22
+
23
+ .litegraph span.uepromptspan {
24
+ margin: 0px;
25
+ width: 100%;
26
+ padding-left:12px;
27
+ background-color: inherit;
28
+ }
29
+
30
+ .litegraph input.uepromptinput {
31
+ padding: 0 0 0 6px;
32
+ font-size: 12px;
33
+ border-radius: 12px;
34
+ border: 2px solid var(--border-color);
35
+ color: var(--input-text);
36
+ margin: 0px;
37
+ width: 100%;
38
+ float: right;
39
+ }
ComfyUI/custom_nodes/cg-use-everywhere/js/ue_debug.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+
3
+ /*
4
+ Things that can be useful (generally as breakpoints) when debugging
5
+ */
6
+ export function add_debug() {
7
+ var dirty_canvas = true;
8
+ Object.defineProperty(app.canvas, 'dirty_canvas', {
9
+ get : () => { return dirty_canvas },
10
+ set : (v) => { dirty_canvas = v;} // a breakpoint here catches the calls that mark the canvas as dirty
11
+ })
12
+
13
+ var dirty_bg_canvas = true;
14
+ Object.defineProperty(app.canvas, 'dirty_bg_canvas', {
15
+ get : () => { return dirty_bg_canvas },
16
+ set : (v) => { dirty_bg_canvas = v;} // a breakpoint here catches the calls that mark the background canvas as dirty
17
+ })
18
+ }
ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere.js ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+ import { api } from "../../scripts/api.js";
3
+
4
+ import { is_UEnode, is_helper, inject, Logger, get_real_node } from "./use_everywhere_utilities.js";
5
+ import { displayMessage, update_input_label, indicate_restriction } from "./use_everywhere_ui.js";
6
+ import { LinkRenderController } from "./use_everywhere_ui.js";
7
+ import { autoCreateMenu } from "./use_everywhere_autocreate.js";
8
+ import { add_autoprompts } from "./use_everywhere_autoprompt.js";
9
+ import { GraphAnalyser } from "./use_everywhere_graph_analysis.js";
10
+ import { main_menu_settings, node_menu_settings, canvas_menu_settings } from "./use_everywhere_settings.js";
11
+ import { add_debug } from "./ue_debug.js";
12
+
13
+ /*
14
+ The ui component that looks after the link rendering
15
+ */
16
+ var linkRenderController;
17
+ var graphAnalyser;
18
+
19
+ /*
20
+ Inject a call to linkRenderController.mark_list_link_outdated into a method with name methodname on all objects in the array
21
+ If object is undefined, do nothing.
22
+ The injection is added at the end of the existing method (if the method didn't exist, it is created).
23
+ A Logger.trace call is added at the start with 'tracetext'
24
+ */
25
+ function inject_outdating_into_objects(array, methodname, tracetext) {
26
+ if (array) {
27
+ array.forEach((object) => { inject_outdating_into_object_method(object, methodname, tracetext); })
28
+ }
29
+ }
30
+ function inject_outdating_into_object_method(object, methodname, tracetext) {
31
+ if (object) inject(object, methodname, tracetext, linkRenderController.mark_link_list_outdated, linkRenderController);
32
+ }
33
+
34
+ const nodeHandler = {
35
+ set: function(obj, property, value) {
36
+ const oldValue = Reflect.get(obj, property, this);
37
+ const result = Reflect.set(...arguments);
38
+ if (oldValue!=value) {
39
+ if (property==='bgcolor') {
40
+ if (obj.mode!=4) linkRenderController.mark_link_list_outdated();
41
+ }
42
+ if (property==='mode') {
43
+ linkRenderController.mark_link_list_outdated();
44
+ obj.widgets?.forEach((widget) => {widget.onModeChange?.(value)});
45
+ }
46
+ }
47
+ return result;
48
+ },
49
+ }
50
+
51
+ app.registerExtension({
52
+ name: "cg.customnodes.use_everywhere",
53
+
54
+ async beforeRegisterNodeDef(nodeType, nodeData, app) {
55
+ /*
56
+ When a node is connected or unconnected, the link list is dirty.
57
+ If it is a UE node, we need to update it as well
58
+ */
59
+ const onConnectionsChange = nodeType.prototype.onConnectionsChange;
60
+ nodeType.prototype.onConnectionsChange = function (side,slot,connect,link_info,output) {
61
+ Logger.trace("onConnectionsChange", arguments, this);
62
+ if (this.IS_UE && side==1) { // side 1 is input
63
+ if (this.type=="Anything Everywhere?" && slot!=0) {
64
+ // don't do anything for the regexs
65
+ } else {
66
+ const type = (connect && link_info) ? get_real_node(link_info?.origin_id)?.outputs[link_info?.origin_slot]?.type : undefined;
67
+ this.input_type[slot] = type;
68
+ if (link_info) link_info.type = type ? type : "*";
69
+ update_input_label(this, slot, app);
70
+ }
71
+ }
72
+ linkRenderController.mark_link_list_outdated();
73
+ onConnectionsChange?.apply(this, arguments);
74
+ };
75
+
76
+ /*
77
+ Toggle the group restriction.
78
+ Any right click action on a node might make the link list dirty.
79
+ */
80
+ const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
81
+ nodeType.prototype.getExtraMenuOptions = function(_, options) {
82
+ Logger.trace("getExtraMenuOptions", arguments, this);
83
+ getExtraMenuOptions?.apply(this, arguments);
84
+ if (is_UEnode(this)) {
85
+ node_menu_settings(options, this);
86
+
87
+ }
88
+ // any right click action can make the list dirty
89
+ inject_outdating_into_objects(options,'callback',`menu option on ${this.id}`);
90
+ }
91
+
92
+ if (is_UEnode(nodeType)) {
93
+ const onNodeCreated = nodeType.prototype.onNodeCreated;
94
+ nodeType.prototype.onNodeCreated = function () {
95
+ const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
96
+ if (!this.properties) this.properties = {}
97
+ this.properties.group_restricted = 0;
98
+ this.properties.color_restricted = 0;
99
+ if (this.inputs) {
100
+ if (!this.widgets) this.widgets = [];
101
+ for (const input of this.inputs) {
102
+ if (input.widget && !this.widgets.find((w) => w.name === input.widget.name)) this.widgets.push(input.widget)
103
+ }
104
+ }
105
+ return r;
106
+ }
107
+ }
108
+ },
109
+
110
+ async nodeCreated(node) {
111
+ node.IS_UE = is_UEnode(node);
112
+ if (node.IS_UE) {
113
+ node.input_type = [undefined, undefined, undefined]; // for dynamic input types
114
+ node.displayMessage = displayMessage; // receive messages from the python code
115
+
116
+ // If a widget on a UE node is edited, link list is dirty
117
+ inject_outdating_into_objects(node.widgets,'callback',`widget callback on ${node.id}`);
118
+
119
+ // draw the indication of group restrictions
120
+ const original_onDrawTitleBar = node.onDrawTitleBar;
121
+ node.onDrawTitleBar = function(ctx, title_height) {
122
+ original_onDrawTitleBar?.apply(this, arguments);
123
+ if (node.properties.group_restricted || node.properties.color_restricted) indicate_restriction(ctx, title_height);
124
+ }
125
+ }
126
+
127
+ if (is_helper(node)) { // editing a helper node makes the list dirty
128
+ inject_outdating_into_objects(node.widgets,'callback',`widget callback on ${this.id}`);
129
+ }
130
+
131
+ // removing a node makes the list dirty
132
+ inject_outdating_into_object_method(node, 'onRemoved', `node ${node.id} removed`)
133
+
134
+ // creating a node makes the link list dirty - but give the system a moment to finish
135
+ setTimeout( ()=>{linkRenderController.mark_link_list_outdated()}, 100 );
136
+ },
137
+
138
+ loadedGraphNode(node) { if (node.flags.collapsed && node.loaded_when_collapsed) node.loaded_when_collapsed(); },
139
+
140
+ async setup() {
141
+ const head = document.getElementsByTagName('HEAD')[0];
142
+ const link = document.createElement('link');
143
+ link.rel = 'stylesheet';
144
+ link.type = 'text/css';
145
+ link.href = 'extensions/cg-use-everywhere/ue.css';
146
+ head.appendChild(link);
147
+
148
+ /*
149
+ Listen for message-handler event from python code
150
+ */
151
+ function messageHandler(event) {
152
+ const id = event.detail.id;
153
+ const message = event.detail.message;
154
+ const node = get_real_node(id);
155
+ if (node && node.displayMessage) node.displayMessage(id, message);
156
+ else (console.log(`node ${id} couldn't handle a message`));
157
+ }
158
+ api.addEventListener("ue-message-handler", messageHandler);
159
+
160
+ api.addEventListener("status", ({detail}) => {
161
+ if (linkRenderController) linkRenderController.note_queue_size(detail ? detail.exec_info.queue_remaining : 0)
162
+ });
163
+
164
+ /*
165
+ We don't want to do that if we are saving the workflow or api:
166
+ */
167
+ const _original_save_onclick = document.getElementById('comfy-save-button').onclick;
168
+ document.getElementById('comfy-save-button').onclick = function() {
169
+ graphAnalyser.pause();
170
+ _original_save_onclick();
171
+ graphAnalyser.unpause()
172
+ }
173
+ const _original_save_api_onclick = document.getElementById('comfy-dev-save-api-button').onclick;
174
+ document.getElementById('comfy-dev-save-api-button').onclick = function() {
175
+ graphAnalyser.pause();
176
+ _original_save_api_onclick();
177
+ graphAnalyser.unpause();
178
+ }
179
+
180
+ /*
181
+ Hijack drawNode to render the virtual connection points
182
+ and links to node with mouseOver
183
+ */
184
+ const original_drawNode = LGraphCanvas.prototype.drawNode;
185
+ LGraphCanvas.prototype.drawNode = function(node, ctx) {
186
+ original_drawNode.apply(this, arguments);
187
+ linkRenderController.highlight_ue_connections(node, ctx);
188
+ }
189
+
190
+ /*
191
+ When we draw connections, do the ue ones as well
192
+ */
193
+ const drawConnections = LGraphCanvas.prototype.drawConnections;
194
+ LGraphCanvas.prototype.drawConnections = function(ctx) {
195
+ drawConnections?.apply(this, arguments);
196
+ linkRenderController.render_all_ue_links(ctx);
197
+ }
198
+
199
+ main_menu_settings();
200
+
201
+ /*
202
+ Canvas menu is the right click on backdrop.
203
+ We need to add our option, and hijack the others.
204
+ */
205
+ const original_getCanvasMenuOptions = LGraphCanvas.prototype.getCanvasMenuOptions;
206
+ LGraphCanvas.prototype.getCanvasMenuOptions = function () {
207
+ // Add our items to the canvas menu
208
+ const options = original_getCanvasMenuOptions.apply(this, arguments);
209
+ canvas_menu_settings(options);
210
+
211
+ // every menu item makes our list dirty
212
+ inject_outdating_into_objects(options,'callback',`menu option on canvas`);
213
+
214
+ return options;
215
+ }
216
+
217
+ /*
218
+ When you drag from a node, showConnectionMenu is called. If shift key is pressed call ours
219
+ */
220
+ const original_showConnectionMenu = LGraphCanvas.prototype.showConnectionMenu;
221
+ LGraphCanvas.prototype.showConnectionMenu = function (optPass) {
222
+ if (optPass.e.shiftKey) {
223
+ autoCreateMenu.apply(this, arguments);
224
+ } else {
225
+ this.use_original_menu = true;
226
+ original_showConnectionMenu.apply(this, arguments);
227
+ this.use_original_menu = false;
228
+ }
229
+ }
230
+
231
+ /*
232
+ To allow us to use the shift drag above, we need to intercept 'allow_searchbox' sometimes
233
+ (because searchbox is the default behaviour when shift dragging)
234
+ */
235
+ var original_allow_searchbox = app.canvas.allow_searchbox;
236
+ Object.defineProperty(app.canvas, 'allow_searchbox', {
237
+ get : function() {
238
+ if (this.use_original_menu) { return original_allow_searchbox; }
239
+ if(app.ui.settings.getSettingValue('AE.replacesearch', true) && this.connecting_output) {
240
+ return false;
241
+ } else { return original_allow_searchbox; }
242
+ },
243
+ set : function(v) { original_allow_searchbox = v; }
244
+ });
245
+
246
+
247
+ },
248
+
249
+ init() {
250
+ graphAnalyser = GraphAnalyser.instance();
251
+ app.graphToPrompt = async function () {
252
+ return graphAnalyser.analyse_graph(true, true, false);
253
+ }
254
+
255
+ linkRenderController = LinkRenderController.instance(graphAnalyser);
256
+
257
+ add_autoprompts();
258
+ const createNode = LiteGraph.createNode;
259
+ LiteGraph.createNode = function() {
260
+ const nd = createNode.apply(this,arguments);
261
+ if (nd && nd.IS_UE) {
262
+ return new Proxy( nd, nodeHandler );
263
+ } else {
264
+ return nd;
265
+ }
266
+ }
267
+
268
+ if (false) add_debug();
269
+
270
+ }
271
+
272
+ });
ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_apply.js ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+ import { is_UEnode, get_real_node } from "./use_everywhere_utilities.js";
3
+
4
+
5
+ function _convert_to_links(ue) {
6
+ const output_node_id = ue.output[0];
7
+ const output_index = ue.output[1];
8
+ const output_node = get_real_node(output_node_id);
9
+ ue.sending_to.forEach((st) => {
10
+ const input_node_id = st.node.id;
11
+ const input_node = get_real_node(input_node_id);
12
+ const input_index = st.input_index;
13
+ output_node.connect(output_index, input_node, input_index);
14
+ });
15
+ }
16
+
17
+ function convert_to_links(ues, control_node_id) {
18
+ ues.ues.forEach((ue)=> {
19
+ if (control_node_id==-1 || ue.controller.id == control_node_id) _convert_to_links(ue);
20
+ });
21
+ }
22
+
23
+ function remove_all_ues() {
24
+ var match = app.graph._nodes.find((node)=>is_UEnode(node));
25
+ while (match) {
26
+ app.graph.remove(match);
27
+ match = app.graph._nodes.find((node)=>is_UEnode(node));
28
+ }
29
+ }
30
+
31
+ export {convert_to_links, remove_all_ues}
ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_autocreate.js ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+
3
+ function autoCreateMenu(opts) {
4
+ //opts.e.stopPropagation();
5
+ var options = ["Search",];
6
+ var search_opts;
7
+ if (opts.nodeFrom && opts.slotFrom) {
8
+ options.push(null);
9
+ options.push("Anything Everywhere");
10
+ options.push("Anything Everywhere?");
11
+ if (opts.nodeFrom?.outputs?.length==3 &&
12
+ opts.nodeFrom.outputs[0].name=='MODEL' &&
13
+ opts.nodeFrom.outputs[1].name=='CLIP' &&
14
+ opts.nodeFrom.outputs[2].name=='VAE') options.push("Anything Everywhere3");
15
+ search_opts = {node_from: opts.nodeFrom, slot_from: opts.slotFrom, type_filter_in: opts.slotFrom.type};
16
+ } else {
17
+ search_opts = {node_to: opts.nodeTo, slot_from: opts.slotTo, type_filter_out: slotTo.type};
18
+ }
19
+
20
+ var menu = new LiteGraph.ContextMenu(options, {
21
+ event: opts.e,
22
+ title: "UE Node",
23
+ callback: inner_clicked
24
+ });
25
+
26
+ const p = [ opts.e.canvasX, opts.e.canvasY ];
27
+
28
+ function inner_clicked(v,options,e) {
29
+ if (!v) return;
30
+ if (v=="Search") {
31
+ app.canvas.showSearchBox(opts.e,search_opts);
32
+ return;
33
+ }
34
+ var newNode = LiteGraph.createNode(v);
35
+ app.graph.add(newNode);
36
+ newNode.pos = p;
37
+ if (v=="Anything Everywhere3") {
38
+ for (var i=0; i<3; i++) {opts.nodeFrom.connect( i, newNode, i );}
39
+ } else {
40
+ opts.nodeFrom.connect( opts.nodeFrom.findOutputSlot(opts.slotFrom.name), newNode, 0 );
41
+ }
42
+ app.graph.change();
43
+ }
44
+ }
45
+
46
+ export {autoCreateMenu}
ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_autoprompt.js ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { is_UEnode } from "./use_everywhere_utilities.js";
2
+ import { ComfyWidgets} from "../../scripts/widgets.js";
3
+ import { app } from "../../scripts/app.js";
4
+ import { LinkRenderController } from "./use_everywhere_ui.js";
5
+
6
+ function update_picklist(node, inputname) {
7
+ const d = document.getElementById("uedynamiclist");
8
+ while (d.firstChild) { d.removeChild(d.lastChild); };
9
+ let options = [];
10
+ if (inputname=="title_regex") { options = LinkRenderController.instance().ue_list?.all_nodes_with_unmatched_input(node.input_type[0]); }
11
+ else if (inputname=="input_regex") { options = LinkRenderController.instance().ue_list?.all_unmatched_input_names(node.input_type[0]); }
12
+ else if (inputname=="group_regex") { options = LinkRenderController.instance().ue_list?.all_group_names(node.input_type[0]); }
13
+ options.forEach((option) => {
14
+ const theOption = document.createElement("option");
15
+ theOption.setAttribute("value", option);
16
+ d.appendChild(theOption)
17
+ })
18
+ }
19
+
20
+ function intersect(a, b) {
21
+ const x = Math.max(a.x, b.x);
22
+ const num1 = Math.min(a.x + a.width, b.x + b.width);
23
+ const y = Math.max(a.y, b.y);
24
+ const num2 = Math.min(a.y + a.height, b.y + b.height);
25
+ if (num1 >= x && num2 >= y) return [x, y, num1 - x, num2 - y];
26
+ else return null;
27
+ }
28
+
29
+ function union(a,b) {
30
+ if (!b) return a;
31
+ if (!a) return b;
32
+ const x = Math.min(a.x, b.x);
33
+ const y = Math.min(a.y,b.y);
34
+ const width = Math.max(a.x+a.width, b.x+b.width) - x;
35
+ const height = Math.max(a.y+a.height, b.y+b.height) - x;
36
+ return { x:x, y:y, width:width, height:height };
37
+ }
38
+
39
+ function getClipPath(node, element) {
40
+ const scale = app.canvas.ds.scale;
41
+ const widgetRect = element.getBoundingClientRect();
42
+ var onTopOfMe = false;
43
+ var clip = null;
44
+ app.graph._nodes.forEach((other_node) => {
45
+ if (other_node.id == node.id) {
46
+ onTopOfMe = true;
47
+ }
48
+ else if (onTopOfMe) {
49
+ const MARGIN = other_node.is_selected ? 7 : 2;
50
+ const bounding = other_node.getBounding();
51
+ const intersection = intersect(
52
+ { x: widgetRect.x / scale, y: widgetRect.y / scale, width: widgetRect.width / scale, height: widgetRect.height / scale },
53
+ {
54
+ x: other_node.pos[0] + app.canvas.ds.offset[0] - MARGIN,
55
+ y: other_node.pos[1] + app.canvas.ds.offset[1] - LiteGraph.NODE_TITLE_HEIGHT - MARGIN,
56
+ width: bounding[2] + MARGIN + MARGIN,
57
+ height: bounding[3] + MARGIN + MARGIN,
58
+ }
59
+ );
60
+ if (intersection) {
61
+ clip = union(clip, {
62
+ x : intersection[0] - widgetRect.x / scale,
63
+ y : intersection[1] - widgetRect.y / scale,
64
+ width : intersection[2],
65
+ height : intersection[3]
66
+ })
67
+ //const newpath = `0% 0%, 0% 100%, ${clipX} 100%, ${clipX} ${clipY}, calc(${clipX} + ${clipWidth}) ${clipY}, calc(${clipX} + ${clipWidth}) calc(${clipY} + ${clipHeight}), ${clipX} calc(${clipY} + ${clipHeight}), ${clipX} 100%, 100% 100%, 100% 0%`;
68
+ //path = path != '' ? `${path}, ${newpath}` : newpath;
69
+ }
70
+ }
71
+ })
72
+ const path = clip ? `polygon(0% 0%, 0% 100%, ${clip.x}px 100%, ${clip.x}px ${clip.y}px, ${clip.x + clip.width}px ${clip.y}px, ${clip.x + clip.width}px ${clip.y + clip.height}px, ${clip.x}px ${clip.y + clip.height}px, ${clip.x}px 100%, 100% 100%, 100% 0%)` : '';
73
+ return path;
74
+ }
75
+
76
+ function active_text_widget(node, inputname) {
77
+ const label = document.createElement("label");
78
+ label.className = "graphdialog ueprompt";
79
+ label.style.display = "none";
80
+
81
+ const label_text = document.createElement("span");
82
+ label_text.innerText = `${inputname.substring(0,5)} `;
83
+ label_text.className = "ueprompttext";
84
+ label.appendChild(label_text);
85
+
86
+ const span = document.createElement("span");
87
+ span.className = "uepromptspan";
88
+ label.appendChild(span);
89
+
90
+ const inputEl = document.createElement("input");
91
+ inputEl.setAttribute("type", "text");
92
+ inputEl.className = "uepromptinput";
93
+ span.appendChild(inputEl);
94
+
95
+ const widget = node.addDOMWidget(inputname, "input", label, {
96
+ getValue() { return inputEl.value; },
97
+ setValue(v) { inputEl.value = v; },
98
+ onDraw(w) {
99
+ // are we the most recently selected node?
100
+ if (Object.values(app.canvas.selected_nodes)[0]?.id == node.id) {
101
+ // if so, turn off DOM clipping
102
+ w.element.style.clipPath = null; w.element.style.willChange = null;
103
+ } else {
104
+ w.element.style.zIndex = 0;
105
+ const p = getClipPath(node, w.element);
106
+ w.element.style.clipPath = p;
107
+ let a;
108
+ }
109
+ }
110
+ });
111
+ widget.element.hidden = true;
112
+
113
+ inputEl.onmousedown = function(e) {
114
+ const x = app.canvas.prompt("Value",widget.value,function(v) { this.value = v; }.bind(widget), e, false );
115
+ const input = x.getElementsByClassName("value")[0];
116
+ input.setAttribute("list", "uedynamiclist");
117
+ input.parentNode.style.zIndex = `${parseInt(label.style.zIndex ? label.style.zIndex : '0')+1}`;
118
+ input.addEventListener("input", function (v) {
119
+ widget.value = this.value;
120
+ LinkRenderController.instance().mark_link_list_outdated();
121
+ app.graph.setDirtyCanvas(true,true);
122
+ }.bind(input));
123
+ update_picklist(node, inputname);
124
+ e.stopImmediatePropagation();
125
+ }
126
+
127
+ widget.computeSize = function (parent_width) {
128
+ return parent_width ? [parent_width, 27] : [400, 20];
129
+ }
130
+
131
+ inputEl.addEventListener("focus", () => {
132
+ if (inputEl.value==".*") inputEl.value = "";
133
+ });
134
+
135
+ widget.onModeChange = function (mode) {
136
+ label.style.opacity = mode==4 ? 0.2 : 1.0;
137
+ }
138
+
139
+ node.loaded_when_collapsed = function() {
140
+ node.widgets?.forEach((widget) => {
141
+ if (widget.element) {
142
+ widget.element.hidden = true;
143
+ widget.element.style.display = "none";
144
+ }
145
+ })
146
+ }
147
+
148
+ return { widget };
149
+ }
150
+
151
+ function activate(node, widget) {
152
+ if (node.flags?.collapsed) return;
153
+ widget.element.hidden = false;
154
+ widget.element.style.display = "";
155
+ }
156
+
157
+ function add_autoprompts() {
158
+ const STRING = ComfyWidgets.STRING;
159
+ ComfyWidgets.STRING = function (node, inputName, inputData, app) {
160
+ if (!is_UEnode(node) || !inputName?.includes("regex") || !app.ui.settings.getSettingValue('AE.autoprompt', true)) {
161
+ return STRING.apply(this, arguments);
162
+ }
163
+ const atw = active_text_widget(node, inputName);
164
+ const orig_onAdded = node.onAdded;
165
+ node.onAdded = function () {
166
+ orig_onAdded?.apply(this, arguments);
167
+ activate(node, atw.widget);
168
+ }
169
+ return atw;
170
+ }
171
+ const datalist = document.createElement("datalist");
172
+ datalist.id = "uedynamiclist";
173
+ document.body.append(datalist);
174
+ }
175
+
176
+ function node_added(node) {
177
+ const a = 1;
178
+ }
179
+
180
+
181
+ export { add_autoprompts }
ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_classes.js ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { nodes_in_my_group, nodes_not_in_my_group, nodes_my_color, nodes_not_my_color, nodes_in_groups_matching } from "./use_everywhere_ui.js";
2
+ import { Logger, node_is_live, get_real_node } from "./use_everywhere_utilities.js";
3
+
4
+ function display_name(node) {
5
+ if (node?.title) return node.title;
6
+ if (node?.type) return node.type;
7
+ if (node?.properties['Node name for S&R']) return node.properties['Node name for S&R'];
8
+ return "un-nameable node";
9
+ }
10
+
11
+ /*
12
+ The UseEverywhere object represents a single 'broadcast'. It generally contains
13
+ controller - the UE node that controls the broadcase
14
+ control_node_input_index - the input on that node
15
+ type - the data type
16
+ output - the output that is being rebroadcast as a list (node_id, output_index)
17
+ title_regex, input_regex - the UE? matching rules
18
+ priority - priorty :)
19
+ */
20
+ class UseEverywhere {
21
+ constructor() {
22
+ this.sending_to = [];
23
+ Object.assign(this, arguments[0]);
24
+ if (this.priority === undefined) this.priority = 0;
25
+ this.description = `source ${this?.output[0]}.${this?.output[1]} -> control ${this?.controller.id}.${this?.control_node_input_index} "${this.type}" <- (priority ${this.priority})`;
26
+ if (this.title_regex) this.description += ` - node title regex '${this.title_regex.source}'`;
27
+ if (this.input_regex) this.description += ` - input name regex '${this.input_regex.source}'`;
28
+ }
29
+
30
+ sending_differs_from(another_ue) {
31
+ if (this.sending_to.length != another_ue.sending_to.length) return true;
32
+ for (var i=0; i<this.sending_to.length; i++) {
33
+ if ( (this.sending_to[i].node != another_ue.sending_to[i].node) ||
34
+ (this.sending_to[i].input != another_ue.sending_to[i].input) ||
35
+ (this.sending_to[i].input_index != another_ue.sending_to[i].input_index) ) return true;
36
+ }
37
+ return false;
38
+ }
39
+ /*
40
+ Does this broadcast match a given node,input?
41
+ */
42
+ matches(node, input) {
43
+ if (this.restrict_to && !this.restrict_to.includes(node.id)) return false;
44
+ const input_label = input.label ? input.label : input.name;
45
+ const node_label = node.title ? node.title : (node.properties['Node name for S&R'] ? node.properties['Node name for S&R'] : node.type);
46
+ if (this.title_regex) {
47
+ if (!(this.title_regex.test(node_label))) return false;
48
+ }
49
+ if (node.type=="Highway" && typeof this.input_regex==='string') { // Highway nodes - broken if there are two matches...
50
+ const input_label_split = input_label.split(':');
51
+ if (input_label_split.length==1) {
52
+ if (input_label==this.input_regex) {
53
+ input.type = this.type;
54
+ input.name += `:${this.type}`;
55
+ return true;
56
+ }
57
+ return false;
58
+ } else {
59
+ if ((input_label_split[0]==this.input_regex) && input_label_split[1]==input.type) return true;
60
+ return false;
61
+ }
62
+ }
63
+ if (this.type != input.type) return false;
64
+ if (this.input_regex && typeof this.input_regex==='string') return false; // input_regex started '+', which targets Highway nodes only
65
+ if (this.input_regex && !this.input_regex.test(input_label)) return false;
66
+
67
+ return true;
68
+ }
69
+ note_sending_to(node, input) {
70
+ const input_index = node.inputs.findIndex((n) => n.name==input.name);
71
+ this.sending_to.push({node:node, input:input, input_index:input_index})
72
+ }
73
+ describe_sending(){
74
+ var description = " Linked to:";
75
+ this.sending_to.forEach((st) => description += `\n -> ${display_name(st.node)}, ${st.input.name}`);
76
+ if (this.sending_to.length===0) description += ' nothing';
77
+ return description;
78
+ }
79
+ describe() {
80
+ return this.description + "\n" + this.describe_sending();
81
+ }
82
+ }
83
+
84
+ function validity_errors(params) {
85
+ if (!node_is_live(params.controller)) return `UE node ${params.output[0]} is not alive`;
86
+ if (!node_is_live(get_real_node(params.output[0]))) return `upstream node ${params.output[0]} is not alive`;
87
+ return "";
88
+ }
89
+
90
+ class UseEverywhereList {
91
+ constructor() { this.ues = []; this.unmatched_inputs = []; }
92
+
93
+ differs_from(another_uel) {
94
+ if (!another_uel) return true;
95
+ if (this.ues.length != another_uel.ues.length) return true;
96
+ for (var i=0; i<this.ues.length; i++) {
97
+ if (this.ues[i].sending_differs_from(another_uel.ues[i])) return true;
98
+ }
99
+ return false;
100
+ }
101
+
102
+ add_ue(node, control_node_input_index, type, output, title_regex, input_regex, group_regex, priority) {
103
+ const params = {
104
+ controller: node,
105
+ control_node_input_index: control_node_input_index,
106
+ type: type,
107
+ output: output,
108
+ title_regex: title_regex,
109
+ input_regex: input_regex,
110
+ group_regex: group_regex,
111
+ priority: priority
112
+ };
113
+ const real_node = get_real_node(node.id);
114
+ if (!real_node) {
115
+ Logger.log(Logger.PROBLEM, `Node ${node.id} not found`, params);
116
+ return;
117
+ }
118
+ if (real_node.properties.group_restricted == 1) {
119
+ params.restrict_to = nodes_in_my_group(node.id);
120
+ params.priority += 0.1;
121
+ }
122
+ if (real_node.properties.group_restricted == 2) {
123
+ params.restrict_to = nodes_not_in_my_group(node.id);
124
+ params.priority += 0.1;
125
+ }
126
+ if (real_node.properties.color_restricted == 1) {
127
+ params.restrict_to = nodes_my_color(node.id, params.restrict_to);
128
+ params.priority += 0.3;
129
+ }
130
+ if (real_node.properties.color_restricted == 2) {
131
+ params.restrict_to = nodes_not_my_color(node.id, params.restrict_to);
132
+ params.priority += 0.3;
133
+ }
134
+ if (group_regex) {
135
+ params.restrict_to = nodes_in_groups_matching(group_regex, params.restrict_to);
136
+ }
137
+ if (real_node.properties["priority_boost"]) params.priority += real_node.properties["priority_boost"];
138
+
139
+ const ue = new UseEverywhere(params);
140
+ const error = validity_errors(params);
141
+ if (error==="") {
142
+ this.ues.push(ue);
143
+ Logger.log(Logger.INFORMATION, `Added ${ue.description}`)
144
+ } else {
145
+ Logger.log(Logger.PROBLEM, `Rejected ${ue.description} because ${error}`, params);
146
+ }
147
+ }
148
+
149
+ find_best_match(node, input, _ambiguity_messages) {
150
+ this.unmatched_inputs.push({"node":node, "input":input});
151
+ var matches = this.ues.filter((candidate) => (
152
+ candidate.matches(node, input)
153
+ ));
154
+ if (matches.length==0) {
155
+ Logger.log(Logger.INFORMATION, `'${display_name(node)}' optional input '${input.name}' unmatched`)
156
+ return undefined;
157
+ }
158
+ if (matches.length>1) {
159
+ matches.sort((a,b) => b.priority-a.priority);
160
+ if(matches[0].priority == matches[1].priority) {
161
+ const msg = `'${display_name(node)}' (${node.id}) input '${input.name}' matches multiple Use Everwhere sources:`;
162
+ _ambiguity_messages.push(msg);
163
+ for (var i=0; i<matches.length; i++) {
164
+ if (matches[0].priority == matches[i].priority) {
165
+ const inner_msg = ` - ${matches[i].controller.type} (${matches[i].controller.id}) input ${matches[i].control_node_input_index}`;
166
+ _ambiguity_messages.push(inner_msg);
167
+ }
168
+ }
169
+ return undefined;
170
+ }
171
+ }
172
+ matches[0].note_sending_to(node, input);
173
+ Logger.log(Logger.INFORMATION,`'${display_name(node)}' input '${input.name}' matched to ${matches[0].description}`);
174
+ return matches[0];
175
+ }
176
+
177
+ print_all() {
178
+ this.ues.forEach((ue) => { console.log(ue.describe()); });
179
+ }
180
+
181
+ all_unmatched_inputs(type) {
182
+ return this.unmatched_inputs.filter((ui)=>ui.input.type==type);
183
+ }
184
+
185
+ all_nodes_with_unmatched_input(type) {
186
+ const result = new Set();
187
+ this.all_unmatched_inputs(type).forEach((ui) => {
188
+ result.add(display_name(ui.node));
189
+ })
190
+ return result;
191
+ }
192
+
193
+ all_unmatched_input_names(type) {
194
+ const result = new Set();
195
+ this.all_unmatched_inputs(type).forEach((ui) => {
196
+ result.add(ui.input.label ? ui.input.label : ui.input.name);
197
+ })
198
+ return result;
199
+ }
200
+
201
+ all_group_names() {
202
+ const result = new Set();
203
+ app.graph._groups.forEach((group) => {
204
+ result.add(group.title);
205
+ })
206
+ return result;
207
+ }
208
+
209
+ all_connected_inputs(for_node) {
210
+ const ue_connections = [];
211
+ this.ues.forEach((ue) => {
212
+ ue.sending_to.forEach((st) => {
213
+ if (st.node.id == for_node.id) {
214
+ ue_connections.push({
215
+ type : ue.type,
216
+ input_index : st.input_index,
217
+ control_node : get_real_node(ue.controller.id),
218
+ control_node_input_index : ue.control_node_input_index,
219
+ sending_to : st.node,
220
+ });
221
+ }
222
+ });
223
+ });
224
+ return ue_connections;
225
+ }
226
+
227
+ all_ue_connections() {
228
+ const ue_connections = [];
229
+ this.ues.forEach((ue) => {
230
+ ue.sending_to.forEach((st) => {
231
+ ue_connections.push({
232
+ type : ue.type,
233
+ input_index : st.input_index,
234
+ control_node : get_real_node(ue.controller.id),
235
+ control_node_input_index : ue.control_node_input_index,
236
+ sending_to : st.node,
237
+ });
238
+ });
239
+ });
240
+ return ue_connections;
241
+ }
242
+
243
+ all_ue_connections_for(node_id) {
244
+ const ue_connections = [];
245
+ this.ues.forEach((ue) => {
246
+ ue.sending_to.forEach((st) => {
247
+ if (get_real_node(st.node.id).id==node_id || get_real_node(ue.controller.id).id==node_id) {
248
+ ue_connections.push({
249
+ type : ue.type,
250
+ input_index : st.input_index,
251
+ control_node : get_real_node(ue.controller.id),
252
+ control_node_input_index : ue.control_node_input_index,
253
+ sending_to : st.node,
254
+ });
255
+ }
256
+ });
257
+ });
258
+ return ue_connections;
259
+ }
260
+ }
261
+
262
+ export {UseEverywhereList}
ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_graph_analysis.js ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { GroupNodeHandler } from "../core/groupNode.js";
2
+ import { UseEverywhereList } from "./use_everywhere_classes.js";
3
+ import { add_ue_from_node, add_ue_from_node_in_group } from "./use_everywhere_nodes.js";
4
+ import { node_in_loop, node_is_live, is_connected, is_UEnode, Logger, get_real_node } from "./use_everywhere_utilities.js";
5
+ import { app } from "../../scripts/app.js";
6
+
7
+ class GraphAnalyser {
8
+ static _instance;
9
+ static instance() {
10
+ if (!this._instance) this._instance = new GraphAnalyser();
11
+ return this._instance;
12
+ }
13
+
14
+ constructor() {
15
+ this.original_graphToPrompt = app.graphToPrompt;
16
+ this.ambiguity_messages = [];
17
+ this.pause_depth = 0;
18
+ }
19
+
20
+ pause() { this.pause_depth += 1; }
21
+ unpause() { this.pause_depth -= 1; }
22
+
23
+
24
+ async analyse_graph(modify_and_return_prompt=false, check_for_loops=false, supress_before_queued=true) {
25
+ //try {
26
+ /*if (supress_before_queued) {
27
+ app.graph._nodes.forEach((node) => {
28
+ node.widgets?.forEach((widget) => {
29
+ if (widget.beforeQueued) {
30
+ widget.__beforeQueued = widget.beforeQueued;
31
+ widget.beforeQueued = null;
32
+ }
33
+ })
34
+ if(node.seedControl && node.seedControl.lastSeedButton){ // for efficiency nodes seedControl
35
+ node.seedControl.lastSeedButton.__disabled = node.seedControl.lastSeedButton.disabled
36
+ node.seedControl.lastSeedButton.disabled = true
37
+ }
38
+ })
39
+ }*/
40
+ //return this._analyse_graph(modify_and_return_prompt, check_for_loops);
41
+ /*} finally {
42
+ if (supress_before_queued) {
43
+ app.graph._nodes.forEach((node) => {
44
+ node.widgets?.forEach((widget) => {
45
+ if (widget.__beforeQueued) {
46
+ widget.beforeQueued = widget.__beforeQueued;
47
+ widget.__beforeQueued = null;
48
+ }
49
+ })
50
+ if(node.seedControl && node.seedControl.lastSeedButton){ // for efficiency nodes seedControl
51
+ node.seedControl.lastSeedButton.disabled = node.seedControl.lastSeedButton.__disabled
52
+ }
53
+ })
54
+ }
55
+ }*/
56
+ //}
57
+ //async _analyse_graph(modify_and_return_prompt=false, check_for_loops=false) {
58
+ if (this.pause_depth > 0) { return this.original_graphToPrompt.apply(app) }
59
+ this.ambiguity_messages = [];
60
+ var p;
61
+ if (modify_and_return_prompt) {
62
+ p = await this.original_graphToPrompt.apply(app);
63
+ p = structuredClone(p);
64
+ } else {
65
+ p = { workflow:app.graph.serialize() }
66
+ }
67
+
68
+ // Create a UseEverywhereList and populate it from all live (not bypassed) nodes
69
+ const ues = new UseEverywhereList();
70
+ const live_nodes = p.workflow.nodes.filter((node) => node_is_live(node))
71
+ live_nodes.filter((node) => is_UEnode(node)).forEach(node => { add_ue_from_node(ues, node); })
72
+ live_nodes.filter((node) => (get_real_node(node.id, Logger.INFORMATION) && GroupNodeHandler.isGroupNode(get_real_node(node.id)))).forEach( groupNode => {
73
+ const group_data = GroupNodeHandler.getGroupData(get_real_node(groupNode.id));
74
+ group_data.nodeData.nodes.filter((node) => is_UEnode(node)).forEach(node => {
75
+ add_ue_from_node_in_group(ues, node, groupNode.id, group_data);
76
+ })
77
+ })
78
+
79
+ const links_added = new Set();
80
+ // Look for unconnected inputs and see if we can connect them
81
+ live_nodes.filter((node) => !is_UEnode(node)).forEach(node => {
82
+ const nd = get_real_node(node.id, Logger.INFORMATION);
83
+ if (nd) {
84
+ var gpData = GroupNodeHandler.getGroupData(nd);
85
+ const isGrp = !!gpData;
86
+ const o2n = isGrp ? Object.entries(gpData.oldToNewInputMap) : null;
87
+ node.inputs?.forEach(input => {
88
+ if (!is_connected(input) && !(node.reject_ue_connection && node.reject_ue_connection(input))) {
89
+ var ue = ues.find_best_match(node, input, this.ambiguity_messages);
90
+ if (ue && modify_and_return_prompt) {
91
+ var effective_node = node;
92
+ var effective_node_slot = -1;
93
+ if (isGrp) { // the node we are looking at is a group node
94
+ const in_index = node.inputs.findIndex((i)=>i==input);
95
+ const inner_node_index = o2n.findIndex((l)=>Object.values(l[1]).includes(in_index));
96
+ const inner_node_slot_index = Object.values(o2n[inner_node_index][1]).findIndex((l)=>l==in_index);
97
+ effective_node_slot = Object.keys(o2n[inner_node_index][1])[inner_node_slot_index];
98
+ effective_node = nd.getInnerNodes()[o2n[inner_node_index][0]];
99
+ }
100
+ const upNode = get_real_node(ue.output[0]);
101
+ var effective_output = [ue.output[0], ue.output[1]];
102
+ if (GroupNodeHandler.isGroupNode(upNode)) { // the upstream node is a group node
103
+ const upGpData = GroupNodeHandler.getGroupData(upNode);
104
+ const up_inner_node = upGpData.newToOldOutputMap[ue.output[1]].node;
105
+ const up_inner_node_index = up_inner_node.index;
106
+ const up_inner_node_id = upNode.getInnerNodes()[up_inner_node_index].id;
107
+ const up_inner_node_slot = upGpData.newToOldOutputMap[ue.output[1]].slot;
108
+ effective_output = [`${up_inner_node_id}`, up_inner_node_slot];
109
+ }
110
+ if (effective_node_slot==-1) effective_node_slot = effective_node.inputs.findIndex((i)=>(i.label ? i.label : i.name)===(input.label ? input.label : input.name));
111
+ p.output[effective_node.id].inputs[effective_node.inputs[effective_node_slot].name] = effective_output;
112
+ links_added.add({
113
+ "downstream":effective_node.id, "downstream_slot":effective_node_slot,
114
+ "upstream":effective_output[0], "upstream_slot":effective_output[1],
115
+ "controller":ue.controller.id,
116
+ "type":ue.type
117
+ });
118
+ }
119
+ }
120
+ });
121
+ }
122
+ });
123
+
124
+ if (this.ambiguity_messages.length) Logger.log(Logger.PROBLEM, "Ambiguous connections", this.ambiguity_messages, Logger.CAT_AMBIGUITY);
125
+
126
+ // if there are loops report them and raise an exception
127
+ if (check_for_loops && app.ui.settings.getSettingValue('AE.checkloops', true)) {
128
+ try {
129
+ node_in_loop(live_nodes, links_added);
130
+ } catch (e) {
131
+ if (!e.stack) throw e;
132
+ if (e.ues && e.ues.length > 0){
133
+ alert(`Loop (${e.stack}) with broadcast (${e.ues}) - not submitting workflow`);
134
+ } else {
135
+ alert(`Loop (${e.stack}) - not submitting workflow`);
136
+ }
137
+ throw new Error(`Loop Detected ${e.stack}, ${e.ues}`, {"cause":e});
138
+ }
139
+ }
140
+
141
+ if (modify_and_return_prompt) {
142
+ [...links_added].forEach((l)=>{
143
+ p.workflow.last_link_id += 1;
144
+ p.workflow.links.push([p.workflow.last_link_id, parseInt(l.upstream), l.upstream_slot, l.downstream, l.downstream_slot, l.type])
145
+ })
146
+ return p;
147
+ }
148
+ else return ues;
149
+ }
150
+ }
151
+
152
+ export { GraphAnalyser }
ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_nodes.js ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { handle_bypass, get_real_node, get_group_node } from "./use_everywhere_utilities.js";
2
+ import { app } from "../../scripts/app.js";
3
+
4
+ const CONVERTED_TYPE = "converted-widget";
5
+ // import {CONVERTED_TYPE} from "../../extensions/core/widgetInputs.js"
6
+
7
+ /*
8
+ If a widget hasn't been converted, just get it's value
9
+ If it has, *try* to go upstream
10
+ */
11
+ function get_widget_or_input_values(node_obj, widget_id) {
12
+ if (node_obj.widgets[widget_id]?.type.startsWith(CONVERTED_TYPE)) {
13
+ try {
14
+ const name = node_obj.widgets[widget_id].name;
15
+ const input_id = node_obj.inputs.findIndex((input) => input.name==name);
16
+ const connection = get_connection(node_obj, input_id, "STRING");
17
+ const upstream_node_obj = get_real_node(connection.link.origin_id.toString());
18
+ const widget = upstream_node_obj.widgets.find((w) => w.name.toLowerCase() == upstream_node_obj.outputs[connection.link.origin_slot].name.toLowerCase());
19
+ return widget.value;
20
+ } catch (error) {
21
+ return "NOT CONNECTED DONT MATCH";
22
+ }
23
+ }
24
+ return node_obj.widgets[widget_id].value;
25
+ }
26
+
27
+ function add_ue_from_node_in_group(ues, node, group_node_id, group_data) {
28
+ const group_node = get_real_node(group_node_id);
29
+ const ue_node = group_node.getInnerNodes()[node.index];
30
+ ue_node.in_group_with_data = group_data;
31
+ ue_node.getInnerNodesOfGroup = group_node.getInnerNodes;
32
+ add_ue_from_node(ues, ue_node)
33
+ }
34
+
35
+ function get_available_input_name(inputs, the_input, type) {
36
+ const used_names = [];
37
+ inputs.forEach((input) => { if (input!=the_input) used_names.push(input.name); });
38
+ const base = `UE ${type.toLowerCase()}`;
39
+ if (!used_names.includes(base)) return base;
40
+ for (var i=2; ;i++) {
41
+ if (!used_names.includes(`${base}${i}`)) return `${base}${i}`;
42
+ }
43
+ }
44
+
45
+ function get_connection(node, i, override_type) {
46
+ const in_link = node?.inputs[i].link;
47
+ var type = override_type;
48
+ var link = undefined;
49
+ if (in_link) {
50
+ if (!override_type) type = get_real_node(node.id.toString())?.input_type[i];
51
+ link = handle_bypass(app.graph.links[in_link],type);
52
+ } else if (node.in_group_with_data) {
53
+ if (node.in_group_with_data.linksTo[node.index] && node.in_group_with_data.linksTo[node.index][i]) {
54
+ const group_style_link = node.in_group_with_data.linksTo[node.index][i];
55
+ link = { "origin_id":node.getInnerNodesOfGroup()[group_style_link[0]].id, "origin_slot" : group_style_link[1] };
56
+ if (!override_type) type = group_style_link[5];
57
+ } else { // group external input
58
+ const group_node = get_group_node(node.id);
59
+ const group_node_input = group_node.inputs[node.in_group_with_data.oldToNewInputMap[node.index][i]];
60
+ const link_n = group_node_input.link;
61
+ if (link_n) {
62
+ link = app.graph.links[link_n];
63
+ if (!override_type) type = app.graph._nodes_by_id[link.origin_id].outputs[link.origin_slot].type;
64
+ // update the group input node... and the link type
65
+ group_node_input.type = type;
66
+ group_node_input.name = get_available_input_name(group_node.inputs, group_node_input, type);
67
+ link.type = type;
68
+ }
69
+ }
70
+ }
71
+ return { link:link, type:type }
72
+ }
73
+
74
+ /*
75
+ Add UseEverywhere broadcasts from this node to the list
76
+ */
77
+ function add_ue_from_node(ues, node) {
78
+ if (node.type === "Seed Everywhere") ues.add_ue(node, -1, "INT", [node.id.toString(),0],
79
+ undefined, new RegExp("seed|随机种"), undefined, 5);
80
+
81
+ if (node.type === "Anything Everywhere?") {
82
+ const connection = get_connection(node, 0);
83
+ if (connection.link) {
84
+ const node_obj = get_real_node(node.id.toString());
85
+ const w0 = get_widget_or_input_values(node_obj,0);
86
+ const r0 = new RegExp(w0);
87
+ const w1 = get_widget_or_input_values(node_obj,1);
88
+ const r1 = (w1.startsWith('+')) ? w1 : new RegExp(w1);
89
+ const w2 = get_widget_or_input_values(node_obj,2);
90
+ const r2 = (w2 && w2!=".*") ? new RegExp(w2) : null;
91
+ ues.add_ue(node, 0, connection.type, [connection.link.origin_id.toString(), connection.link.origin_slot], r0, r1, r2, 10);
92
+ }
93
+ }
94
+ if (node.type === "Prompts Everywhere") {
95
+ for (var i=0; i<2; i++) {
96
+ const connection = get_connection(node, i);
97
+ if (connection.link) ues.add_ue(node, i, connection.type, [connection.link.origin_id.toString(), connection.link.origin_slot],
98
+ undefined, new RegExp(["(_|\\b)pos(itive|_|\\b)|^prompt|正面","(_|\\b)neg(ative|_|\\b)|负面"][i]), undefined, 5);
99
+ }
100
+ }
101
+ if (node.type === "Anything Everywhere") {
102
+ const connection = get_connection(node, 0);
103
+ if (connection.link) ues.add_ue(node, 0, connection.type, [connection.link.origin_id.toString(),connection. link.origin_slot], undefined, undefined, undefined, 2);
104
+ }
105
+ if (node.type === "Anything Everywhere3") {
106
+ for (var i=0; i<3; i++) {
107
+ const connection = get_connection(node, i);
108
+ if (connection.link) ues.add_ue(node, i, connection.type, [connection.link.origin_id.toString(), connection.link.origin_slot]);
109
+ }
110
+ }
111
+ }
112
+
113
+ export {add_ue_from_node, add_ue_from_node_in_group}
ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_settings.js ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+ import { GraphAnalyser } from "./use_everywhere_graph_analysis.js";
3
+ import { LinkRenderController } from "./use_everywhere_ui.js";
4
+ import { convert_to_links, remove_all_ues } from "./use_everywhere_apply.js";
5
+ import { has_priority_boost } from "./use_everywhere_utilities.js";
6
+
7
+ function main_menu_settings() {
8
+
9
+ app.ui.settings.addSetting({
10
+ id: "AE.details",
11
+ name: "Anything Everywhere show node details",
12
+ type: "boolean",
13
+ defaultValue: false,
14
+ });
15
+ app.ui.settings.addSetting({
16
+ id: "AE.autoprompt",
17
+ name: "Anything Everywhere? autocomplete (may require page reload)",
18
+ type: "boolean",
19
+ defaultValue: true,
20
+ });
21
+ app.ui.settings.addSetting({
22
+ id: "AE.checkloops",
23
+ name: "Anything Everywhere check loops",
24
+ type: "boolean",
25
+ defaultValue: true,
26
+ });
27
+ app.ui.settings.addSetting({
28
+ id: "AE.showlinks",
29
+ name: "Anything Everywhere show links",
30
+ type: "combo",
31
+ options: [ {value:0, text:"All off"}, {value:1, text:"Selected nodes"}, {value:2, text:"Mouseover node"}, {value:3, text:"Selected and mouseover nodes"}, {value:4, text:"All on"}],
32
+ defaultValue: 0,
33
+ onChange: app.graph.change.bind(app.graph),
34
+ });
35
+ app.ui.settings.addSetting({
36
+ id: "AE.animate",
37
+ name: "Anything Everywhere animate UE links",
38
+ type: "combo",
39
+ options: [ {value:0, text:"Off"}, {value:1, text:"Dots"}, {value:2, text:"Pulse"}, {value:3, text:"Both"}, ],
40
+ defaultValue: 3,
41
+ onChange: app.graph.change.bind(app.graph),
42
+ });
43
+ app.ui.settings.addSetting({
44
+ id: "AE.stop.animation.running",
45
+ name: "Anything Everywhere turn animation off when running",
46
+ type: "boolean",
47
+ defaultValue: true,
48
+ onChange: app.graph.change.bind(app.graph),
49
+ });
50
+ app.ui.settings.addSetting({
51
+ id: "AE.highlight",
52
+ name: "Anything Everywhere highlight connected nodes",
53
+ type: "boolean",
54
+ defaultValue: true,
55
+ onChange: app.graph.change.bind(app.graph),
56
+ });
57
+ app.ui.settings.addSetting({
58
+ id: "AE.replacesearch",
59
+ name: "Anything Everywhere replace search",
60
+ type: "boolean",
61
+ defaultValue: true,
62
+ });
63
+ }
64
+
65
+ function submenu(properties, property, options, e, menu, node) {
66
+ const current = properties[property] ? (properties[property]==2 ? 3 : 2 ) : 1;
67
+ const submenu = new LiteGraph.ContextMenu(
68
+ options,
69
+ { event: e, callback: inner_function, parentMenu: menu, node: node }
70
+ );
71
+ const current_element = submenu.root.querySelector(`:nth-child(${current})`);
72
+ if (current_element) current_element.style.borderLeft = "2px solid #484";
73
+ function inner_function(v) {
74
+ if (node) {
75
+ const choice = Object.values(options).indexOf(v);
76
+ properties[property] = choice;
77
+ LinkRenderController.instance().mark_link_list_outdated();
78
+ }
79
+ }
80
+ }
81
+
82
+ const GROUP_RESTRICTION_OPTIONS = ["No restrictions", "Send only within group", "Send only not within group"]
83
+ function group_restriction_submenu(value, options, e, menu, node) {
84
+ submenu(node.properties, "group_restricted", GROUP_RESTRICTION_OPTIONS, e, menu, node);
85
+ }
86
+
87
+ const COLOR_RESTRICTION_OPTIONS = ["No restrictions", "Send only to same color", "Send only to different color"]
88
+ function color_restriction_submenu(value, options, e, menu, node) {
89
+ submenu(node.properties, "color_restricted", COLOR_RESTRICTION_OPTIONS, e, menu, node);
90
+ }
91
+
92
+ function priority_boost_submenu(value, options, e, menu, node) {
93
+ const current = (node.properties["priority_boost"] ? node.properties["priority_boost"] : 0) + 1;
94
+ const submenu = new LiteGraph.ContextMenu(
95
+ [0,1,2,3,4,5,6,7,8,9],
96
+ { event: e, callback: function (v) {
97
+ node.properties["priority_boost"] = parseInt(v);
98
+ LinkRenderController.instance().mark_link_list_outdated();
99
+ },
100
+ parentMenu: menu, node:node}
101
+ )
102
+ const current_element = submenu.root.querySelector(`:nth-child(${current})`);
103
+ if (current_element) current_element.style.borderLeft = "2px solid #484";
104
+ }
105
+
106
+ function node_menu_settings(options, node) {
107
+ options.push(null);
108
+ if (has_priority_boost(node)) options.push(
109
+ {
110
+ content: "Priority Boost",
111
+ has_submenu: true,
112
+ callback: priority_boost_submenu,
113
+ }
114
+ )
115
+ options.push(
116
+ {
117
+ content: "Group Restrictions",
118
+ has_submenu: true,
119
+ callback: group_restriction_submenu,
120
+ },
121
+ {
122
+ content: "Color Restrictions",
123
+ has_submenu: true,
124
+ callback: color_restriction_submenu,
125
+ },
126
+ {
127
+ content: "Convert to real links",
128
+ callback: async () => {
129
+ const ues = await GraphAnalyser.instance().analyse_graph();
130
+ convert_to_links(ues, node.id);
131
+ app.graph.remove(node);
132
+ }
133
+ }
134
+ )
135
+ options.push(null);
136
+ }
137
+
138
+ function canvas_menu_settings(options) {
139
+ options.push(null); // divider
140
+ options.push({
141
+ content: (app.ui.settings.getSettingValue('AE.showlinks', 0)>0) ? "Hide UE links" : "Show UE links",
142
+ callback: () => {
143
+ const setTo = (app.ui.settings.getSettingValue('AE.showlinks', 0)>0) ? 0 : 4;
144
+ app.ui.settings.setSettingValue('AE.showlinks', setTo);
145
+ app.graph.change();
146
+ }
147
+ },
148
+ {
149
+ content: "Convert all UEs to real links",
150
+ callback: async () => {
151
+ if (window.confirm("This will convert all links created by Use Everywhere to real links, and delete all the Use Everywhere nodes. Is that what you want?")) {
152
+ const ues = await GraphAnalyser.instance().analyse_graph();
153
+ LinkRenderController.instance().pause();
154
+ convert_to_links(ues, -1);
155
+ remove_all_ues();
156
+ app.graph.change();
157
+ }
158
+ }
159
+ });
160
+ if (GraphAnalyser.instance().ambiguity_messages.length) {
161
+ options.push({
162
+ content: "Show UE broadcast clashes",
163
+ callback: async () => {
164
+ alert(GraphAnalyser.instance().ambiguity_messages.join("\n"))
165
+ }
166
+ })
167
+ }
168
+ options.push(null); // divider
169
+ }
170
+
171
+ export { main_menu_settings, node_menu_settings, canvas_menu_settings }
ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_ui.js ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Logger, get_real_node, get_group_node, get_all_nodes_within } from "./use_everywhere_utilities.js";
2
+ import { ComfyWidgets } from "../../scripts/widgets.js";
3
+ import { app } from "../../scripts/app.js";
4
+
5
+ function nodes_in_my_group(node_id) {
6
+ const nodes_in = new Set();
7
+ app.graph._groups.forEach((group) => {
8
+ if (!app.canvas.selected_group_moving) group.recomputeInsideNodes();
9
+ if (group._nodes?.find((node) => { return (node.id===node_id) } )) {
10
+ group._nodes.forEach((node) => { nodes_in.add(node.id) } )
11
+ }
12
+ });
13
+ return [...nodes_in];
14
+ }
15
+
16
+ function nodes_not_in_my_group(node_id) {
17
+ const nid = nodes_in_my_group(node_id);
18
+ const nodes_not_in = [];
19
+ app.graph._nodes.forEach((node) => {
20
+ if (!nid.includes(node.id)) nodes_not_in.push(node.id);
21
+ });
22
+ return nodes_not_in;
23
+ }
24
+
25
+ function nodes_in_groups_matching(regex, already_limited_to) {
26
+ const nodes_in = new Set();
27
+ app.graph._groups.forEach((group) => {
28
+ if (regex.test(group.title)) {
29
+ if (!app.canvas.selected_group_moving) group.recomputeInsideNodes();
30
+ /*
31
+ Note for optimisation - it would be more efficient to calculate what nodes are in what groups
32
+ once at the start of analyse_graph() rather than for every group for every UE? with a group regex.
33
+ */
34
+ group._nodes.forEach((node) => {
35
+ if (!already_limited_to || already_limited_to.includes(node.id)) {
36
+ nodes_in.add(node.id)
37
+ }
38
+ } );
39
+ }
40
+ });
41
+ return [...nodes_in];
42
+ }
43
+
44
+
45
+ function nodes_my_color(node_id, already_limited_to) {
46
+ const nodes_in = new Set();
47
+ const color = get_real_node(node_id).color;
48
+ if (already_limited_to) {
49
+ already_limited_to.forEach((nid) => {
50
+ if (get_real_node(nid).color==color) nodes_in.add(nid)
51
+ })
52
+ } else {
53
+ app.graph._nodes.forEach((node) => {
54
+ if (node.color==color) nodes_in.add(node.id)
55
+ })
56
+ }
57
+ return [...nodes_in];
58
+ }
59
+
60
+ function nodes_not_my_color(node_id, already_limited_to) {
61
+ const nodes_in = new Set();
62
+ const color = get_real_node(node_id).color;
63
+ if (already_limited_to) {
64
+ already_limited_to.forEach((nid) => {
65
+ if (get_real_node(nid).color!=color) nodes_in.add(nid)
66
+ })
67
+ } else {
68
+ app.graph._nodes.forEach((node) => {
69
+ if (node.color!=color) nodes_in.add(node.id)
70
+ })
71
+ }
72
+ return [...nodes_in];
73
+ }
74
+
75
+ function indicate_restriction(ctx, title_height) {
76
+ ctx.save();
77
+ ctx.lineWidth = 2;
78
+ ctx.strokeStyle = "#6F6";
79
+ ctx.beginPath();
80
+ ctx.roundRect(5,5-title_height,20,20,8);
81
+ ctx.stroke();
82
+ ctx.restore();
83
+ }
84
+
85
+ function displayMessage(id, message) {
86
+ const node = get_real_node(id);
87
+ if (!node) return;
88
+ var w = node.widgets?.find((w) => w.name === "display_text_widget");
89
+ if (app.ui.settings.getSettingValue('AE.details', false) || w) {
90
+ if (!w) {
91
+ w = ComfyWidgets["STRING"](this, "display_text_widget", ["STRING", { multiline: true }], app).widget;
92
+ w.inputEl.readOnly = true;
93
+ w.inputEl.style.opacity = 0.6;
94
+ w.inputEl.style.fontSize = "9pt";
95
+ }
96
+ w.value = message;
97
+ this.onResize?.(this.size);
98
+ }
99
+ }
100
+
101
+ function update_input_label(node, slot, app) {
102
+ if (node.input_type[slot]) {
103
+ node.inputs[slot].name = node.input_type[slot];
104
+ node.inputs[slot].color_on = app.canvas.default_connection_color_byType[node.input_type[slot]];
105
+ } else {
106
+ node.inputs[slot].name = "anything";
107
+ node.inputs[slot].color_on = undefined;
108
+ }
109
+ }
110
+
111
+ class LinkRenderController {
112
+ static _instance;
113
+ static instance(tga) {
114
+ if (!this._instance) this._instance = new LinkRenderController();
115
+ if (tga && !this._instance.the_graph_analyser) this._instance.the_graph_analyser = tga;
116
+ return this._instance
117
+ }
118
+ constructor() {
119
+ this.the_graph_analyser = null;
120
+ this.periodically_mark_link_list_outdated();
121
+ }
122
+
123
+ ue_list = undefined; // the most current ue list - set to undefined if we know it is out of date
124
+ ue_list_reloading = false; // true when a reload has been requested but not completed
125
+ last_used_ue_list = undefined; // the last ue list we actually used to generate graphics
126
+ paused = false;
127
+ reading_list = false; // don't outdate the list while we read it (because reading it can trigger outdates!)
128
+
129
+ queue_size = null;
130
+ note_queue_size(x) { this.queue_size = x; }
131
+
132
+ pause(ms) {
133
+ this.paused = true;
134
+ if (!ms) ms = 100;
135
+ setTimeout( this.unpause.bind(this), ms );
136
+ }
137
+ unpause() {
138
+ this.paused = false;
139
+ app.graph.change();
140
+ }
141
+
142
+ // memory reuse
143
+ slot_pos1 = new Float32Array(2); //to reuse
144
+ slot_pos2 = new Float32Array(2); //to reuse
145
+
146
+ mark_link_list_outdated() {
147
+ if (this.reading_list) return;
148
+ if (this.ue_list) {
149
+ this.ue_list = undefined;
150
+ this.request_link_list_update();
151
+ Logger.log(Logger.INFORMATION, "link_list marked outdated");
152
+ } else {
153
+ Logger.log(Logger.INFORMATION, "link_list was already outdated");
154
+ }
155
+ }
156
+
157
+ periodically_mark_link_list_outdated() {
158
+ this.mark_link_list_outdated();
159
+ setTimeout(this.periodically_mark_link_list_outdated.bind(this), 1000);
160
+ }
161
+
162
+ // callback when the_graph_analyser finishes - store the result and note reloading is false
163
+ reload_resolve = function (value) {
164
+ this.ue_list = value;
165
+ this.ue_list_reloading = false;
166
+ if (this.ue_list.differs_from(this.last_used_ue_list)) app.graph.change();
167
+ Logger.log(Logger.INFORMATION, "link list update completed");
168
+ Logger.log_call(Logger.DETAIL, this.ue_list.print_all);
169
+ }.bind(this)
170
+
171
+ // callback for when the_graph_analyser fails - note reloading is false and log
172
+ reload_reject = function(reason) {
173
+ this.ue_list_reloading=false;
174
+ Logger.log(Logger.ERROR, "link list update failed");
175
+ Logger.log_error(Logger.ERROR, reason);
176
+ }.bind(this)
177
+
178
+ // request an update to the ue_list.
179
+ request_link_list_update() {
180
+ if (this.ue_list_reloading) return; // already doing it
181
+ this.ue_list_reloading = true; // stop any more requests
182
+ this.the_graph_analyser.analyse_graph().then(this.reload_resolve, this.reload_reject); // an async call is a promise; pass it two callbacks
183
+ Logger.log(Logger.INFORMATION, "link list update started");
184
+ }
185
+
186
+ highlight_ue_connections(node, ctx) {
187
+ try {
188
+ this._highlight_ue_connections(node, ctx);
189
+ } catch (e) {
190
+ console.error(e);
191
+ }
192
+ }
193
+
194
+ _highlight_ue_connections(node, ctx) {
195
+ this.reading_list = true;
196
+ if (!app.ui.settings.getSettingValue('AE.highlight', true)) return;
197
+ //if (this._ue_links_visible) return;
198
+ if (!this.list_ready()) return;
199
+
200
+ this.ue_list.all_connected_inputs(node).forEach((ue_connection) => {
201
+ if (!ue_connection.control_node) { // control node deleted...
202
+ this.mark_link_list_outdated();
203
+ return;
204
+ }
205
+ var pos2 = node.getConnectionPos(true, ue_connection.input_index, this.slot_pos1);
206
+ pos2[0] -= node.pos[0];
207
+ pos2[1] -= node.pos[1];
208
+ ctx.save();
209
+ ctx.lineWidth = 1;
210
+ var radius=5
211
+ ctx.strokeStyle = LGraphCanvas.link_type_colors[ue_connection.type];
212
+ ctx.shadowColor = "white";
213
+ ctx.shadowBlur = 10;
214
+ ctx.shadowOffsetX = 0;
215
+ ctx.shadowOffsetY = 0;
216
+ ctx.beginPath();
217
+ ctx.roundRect(pos2[0]-radius,pos2[1]-radius,2*radius,2*radius,radius);
218
+ ctx.stroke();
219
+ ctx.beginPath();
220
+ ctx.strokeStyle = "black";
221
+ ctx.shadowBlur = 0;
222
+ radius = radius - 1;
223
+ ctx.roundRect(pos2[0]-radius,pos2[1]-radius,2*radius,2*radius,radius);
224
+ ctx.stroke();
225
+
226
+ ctx.restore();
227
+ });
228
+ this.reading_list = false;
229
+ }
230
+
231
+ list_ready(make_latest) {
232
+ if (this.paused) return false;
233
+ if (!this.the_graph_analyser) return false; // we don't have the analyser yet (still loading)
234
+ if (!this.ue_list) this.request_link_list_update();
235
+ if (!this.ue_list) return false;
236
+ if (make_latest) this.last_used_ue_list = this.ue_list;
237
+ return true;
238
+ }
239
+
240
+ node_in_ueconnection(ue_connection, id) {
241
+ return (get_group_node(ue_connection.control_node.id).id == id || get_group_node(ue_connection.sending_to.id).id == id)
242
+ }
243
+
244
+ any_node_in_ueconnection(ue_connection, list_of_nodes) {
245
+ return (Object.values(list_of_nodes).find((node) => (this.node_in_ueconnection(ue_connection, node.id))))?true:false;
246
+ }
247
+
248
+ render_all_ue_links(ctx) {
249
+ try {
250
+ this._render_all_ue_links(ctx);
251
+ } catch (e) {
252
+ console.error(e);
253
+ }
254
+ }
255
+
256
+ _render_all_ue_links(ctx) {
257
+ if (!this.list_ready(true)) return;
258
+
259
+ this.reading_list = true;
260
+ ctx.save();
261
+ const orig_hqr = app.canvas.highquality_render;
262
+ app.canvas.highquality_render = false;
263
+
264
+ const mode = app.ui.settings.getSettingValue('AE.showlinks', 0);
265
+ var animate = app.ui.settings.getSettingValue('AE.animate', 3);
266
+ if (app.ui.settings.getSettingValue('AE.stop.animation.running', true) && this.queue_size>0) animate = 0;
267
+ if (animate==2 || animate==3) this.animate_step(ctx);
268
+
269
+ var any_links_shown = false;
270
+ var any_links = false;
271
+
272
+ this.ue_list.all_ue_connections().forEach((ue_connection) => {
273
+ any_links = true;
274
+ var show = false;
275
+ if (mode==4) show = true;
276
+ if ( (mode==2 || mode==3) && app.canvas.node_over && this.node_in_ueconnection(ue_connection, app.canvas.node_over.id) ) show = true;
277
+ if ( (mode==1 || mode==3) && this.any_node_in_ueconnection(ue_connection, app.canvas.selected_nodes)) show = true;
278
+
279
+ if ( show ) {
280
+ this._render_ue_link(ue_connection, ctx, animate);
281
+ any_links_shown = true;
282
+ }
283
+ });
284
+
285
+
286
+ if (animate>0) {
287
+ /*
288
+ If animating, we want to mark the visuals as changed so the animation updates - but not often!
289
+ If links shown:
290
+ - If showing dots, wait 30ms
291
+ - Otherwise, wait 100ms
292
+ If no links are shown
293
+ - If there are links, and our mode is mouseover, wait 200ms
294
+ - Otherwise don't request an update (there are no links that could be shown without something else requesting a redraw)
295
+ */
296
+ const timeout = (any_links_shown) ? ((animate%2 == 1) ? 30 : 100) : ((mode==2 || mode==3) && any_links) ? 200 : -1;
297
+ if (timeout>0) setTimeout( app.graph.change.bind(app.graph), timeout );
298
+ }
299
+
300
+ app.canvas.highquality_render = orig_hqr;
301
+ ctx.restore();
302
+ this.reading_list = false;
303
+ }
304
+
305
+
306
+ _render_ue_link(ue_connection, ctx, animate) {
307
+ try {
308
+ const node = get_real_node(ue_connection.sending_to.id);
309
+
310
+ /* this is the end node; get the position of the input */
311
+ var pos2 = node.getConnectionPos(true, ue_connection.input_index, this.slot_pos1);
312
+
313
+ /* get the position of the *input* that is being echoed - except for the Seed Anywhere node,
314
+ which is displayed with an output: the class records control_node_input_index as -ve (-1 => 0, -2 => 1...) */
315
+ const input_source = (ue_connection.control_node_input_index >= 0);
316
+ const source_index = input_source ? ue_connection.control_node_input_index : -1-ue_connection.control_node_input_index;
317
+ const pos1 = get_group_node(ue_connection.control_node.id).getConnectionPos(input_source, source_index, this.slot_pos2);
318
+
319
+ /* get the direction that we start and end */
320
+ const delta_x = pos2[0] - pos1[0];
321
+ const delta_y = pos2[1] - pos1[1];
322
+ const end_direction = LiteGraph.LEFT; // always end going into an input
323
+ const sta_direction = ((Math.abs(delta_y) > Math.abs(delta_x))) ?
324
+ ((delta_y>0) ? LiteGraph.DOWN : LiteGraph.UP) :
325
+ ((input_source && delta_x<0) ? LiteGraph.LEFT : LiteGraph.RIGHT)
326
+
327
+ var color = LGraphCanvas.link_type_colors[ue_connection.type];
328
+ if (color=="") color = app.canvas.default_link_color;
329
+ ctx.shadowColor = color;
330
+
331
+ app.canvas.renderLink(ctx, pos1, pos2, undefined, true, animate%2, color, sta_direction, end_direction, undefined);
332
+ } catch (e) {
333
+ Logger.log(Logger.PROBLEM, `Couldn't render UE link ${ue_connection}. That's ok if something just got deleted.`);
334
+ }
335
+ }
336
+
337
+ animate_step(ctx) {
338
+ const max_blur = 8;
339
+ const speed = 0.75;
340
+ var f = (LiteGraph.getTime()*0.001*speed) % 1;
341
+ const step = Math.ceil(f*2*max_blur) % (2*max_blur);
342
+ ctx.shadowBlur = (step<max_blur) ? step + 4 : 3 + 2*max_blur - step;
343
+ }
344
+ }
345
+
346
+ export {displayMessage, update_input_label, nodes_in_my_group, nodes_not_in_my_group, nodes_in_groups_matching, nodes_my_color, nodes_not_my_color, indicate_restriction}
347
+ export{ LinkRenderController}
ComfyUI/custom_nodes/cg-use-everywhere/js/use_everywhere_utilities.js ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+ import { GroupNodeHandler } from "../core/groupNode.js";
3
+
4
+ class Logger {
5
+ static ERROR = 0; // actual errors
6
+ static PROBLEM = 1; // things that stop the workflow working
7
+ static INFORMATION = 2; // record of good things
8
+ static DETAIL = 3; // details
9
+
10
+ static LEVEL = Logger.PROBLEM;
11
+ static TRACE = false; // most of the method calls
12
+
13
+ static CAT_AMBIGUITY = 1;
14
+ static last_reported_category = {};
15
+ static category_cooloff = { 1 : 5000 }
16
+
17
+ static log(level, message, array, category) {
18
+ if (category && Logger.last_reported_category[category]) {
19
+ const elapsed = (new Date()) - Logger.last_reported_category[category];
20
+ if (elapsed < Logger.category_cooloff[category]) return;
21
+ }
22
+ if (level <= Logger.LEVEL) {
23
+ console.log(message);
24
+ if (array) for (var i=0; i<array.length; i++) { console.log(array[i]) }
25
+ if (category) Logger.last_reported_category[category] = new Date();
26
+ }
27
+ }
28
+
29
+ static log_call(level, method) {
30
+ if (level <= Logger.LEVEL) {
31
+ method.apply();
32
+ }
33
+ }
34
+
35
+ static log_error(level, message) {
36
+ if (level <= Logger.LEVEL) {
37
+ console.error(message);
38
+ }
39
+ }
40
+
41
+ static trace(message, array, node) {
42
+ if (Logger.TRACE) {
43
+ if (node) { console.log(`TRACE (${node.id}) : ${message}`) } else { console.log(`TRACE : ${message}`) }
44
+ if (array && Logger.LEVEL>=Logger.INFORMATION) for (var i=0; i<array.length; i++) { console.log(` ${i} = ${array[i]}`) }
45
+ }
46
+ }
47
+ }
48
+
49
+ class LoopError extends Error {
50
+ constructor(id, stack, ues) {
51
+ super("Loop detected");
52
+ this.id = id;
53
+ this.stack = [...stack];
54
+ this.ues = [...ues];
55
+ }
56
+ }
57
+
58
+ function find_all_upstream(node_id, links_added) {
59
+ const all_upstream = [];
60
+ const node = get_real_node(node_id);
61
+ node?.inputs?.forEach((input) => { // normal links
62
+ const link_id = input.link;
63
+ if (link_id) {
64
+ const link = app.graph.links[link_id];
65
+ if (link) all_upstream.push({id:link.origin_id, slot:link.origin_slot});
66
+ }
67
+ });
68
+ links_added.forEach((la)=>{ // UE links
69
+ if (get_real_node(la.downstream).id==node.id) {
70
+ all_upstream.push({id:la.upstream, slot:la.upstream_slot, ue:la.controller.toString()})
71
+ }
72
+ });
73
+ if (node.id != get_group_node(node.id).id) { // node is in group
74
+ const grp_nd = get_group_node(node.id).id;
75
+ const group_data = GroupNodeHandler.getGroupData(get_group_node(node.id));
76
+ const indx = group_data.nodeData.nodes.findIndex((n)=>n.pos[0]==node.pos[0] && n.pos[1]==node.pos[1]);
77
+ if (indx>=0) {
78
+ if (GroupNodeHandler.getGroupData(app.graph._nodes_by_id[grp_nd])?.linksTo?.[indx] ) { // links within group
79
+ Object.values(GroupNodeHandler.getGroupData(app.graph._nodes_by_id[grp_nd]).linksTo[indx]).forEach((internal_link) => {
80
+ all_upstream.push({id:`${grp_nd}:${internal_link[0]}`, slot:internal_link[1]});
81
+ });
82
+ }
83
+ if (GroupNodeHandler.getGroupData(app.graph._nodes_by_id[grp_nd]).oldToNewInputMap?.[indx]) { // links out of group
84
+ Object.values(GroupNodeHandler.getGroupData(app.graph._nodes_by_id[grp_nd]).oldToNewInputMap?.[indx]).forEach((groupInput) => {
85
+ const link_id = get_group_node(node.id).inputs?.[groupInput]?.link;
86
+ if (link_id) {
87
+ const link = app.graph.links[link_id];
88
+ if (link) all_upstream.push({id:link.origin_id, slot:link.origin_slot});
89
+ }
90
+ })
91
+ }
92
+ }
93
+ }
94
+ return all_upstream;
95
+ }
96
+
97
+ function recursive_follow(node_id, start_node_id, links_added, stack, nodes_cleared, ues, count, slot) {
98
+ const node = get_real_node(node_id);
99
+ if (slot>=0 && GroupNodeHandler.isGroupNode(node)) { // link into group
100
+ const mapped = GroupNodeHandler.getGroupData(node).newToOldOutputMap[slot];
101
+ return recursive_follow(`${node.id}:${mapped.node.index}`, start_node_id, links_added, stack, nodes_cleared, ues, count, mapped.slot);
102
+ }
103
+ count += 1;
104
+ if (stack.includes(node.id.toString())) throw new LoopError(node.id, new Set(stack), new Set(ues));
105
+ if (nodes_cleared.has(node.id.toString())) return;
106
+ stack.push(node.id.toString());
107
+
108
+ find_all_upstream(node.id, links_added).forEach((upstream) => {
109
+ if (upstream.ue) ues.push(upstream.ue);
110
+ count = recursive_follow(upstream.id, start_node_id, links_added, stack, nodes_cleared, ues, count, upstream.slot);
111
+ if (upstream.ue) ues.pop();
112
+ })
113
+
114
+ nodes_cleared.add(node.id.toString());
115
+ stack.pop();
116
+ return count;
117
+ }
118
+
119
+ /*
120
+ Throw a LoopError if there is a loop.
121
+ live_nodes is a list of all live (ie not bypassed) nodes in the graph
122
+ links_added is a list of the UE virtuals links
123
+ */
124
+ function node_in_loop(live_nodes, links_added) {
125
+ var nodes_to_check = [];
126
+ const nodes_cleared = new Set();
127
+ live_nodes.forEach((n)=>nodes_to_check.push(get_real_node(n.id).id));
128
+ var count = 0;
129
+ while (nodes_to_check.length>0) {
130
+ const node_id = nodes_to_check.pop();
131
+ count += recursive_follow(node_id, node_id, links_added, [], nodes_cleared, [], 0, -1);
132
+ nodes_to_check = nodes_to_check.filter((nid)=>!nodes_cleared.has(nid.toString()));
133
+ }
134
+ console.log(`node_in_loop made ${count} checks`)
135
+ }
136
+
137
+ /*
138
+ Is a node alive (ie not bypassed or set to never)
139
+ */
140
+ function node_is_live(node){
141
+ if (!node) return false;
142
+ if (node.mode===0) return true;
143
+ if (node.mode===2 || node.mode===4) return false;
144
+ Logger.log(Logger.ERROR, `node ${node.id} has mode ${node.mode} - I only understand modes 0, 2 and 4`);
145
+ return true;
146
+ }
147
+
148
+ function node_is_bypassed(node) {
149
+ return (node.mode===4);
150
+ }
151
+
152
+ /*
153
+ Given a link object, and the type of the link,
154
+ go upstream, following links with the same type, until you find a parent node which isn't bypassed.
155
+ If either type or original link is null, or if the upstream thread ends, return null
156
+ */
157
+ function handle_bypass(original_link, type) {
158
+ if (!type || !original_link) return null;
159
+ var link = original_link;
160
+ var parent = get_real_node(link.origin_id);
161
+ if (!parent) return null;
162
+ while (node_is_bypassed(parent)) {
163
+ if (!parent.inputs) return null;
164
+ var link_id;
165
+ if (parent?.inputs[link.origin_slot]?.type == type) link_id = parent.inputs[link.origin_slot].link; // try matching number first
166
+ else link_id = parent.inputs.find((input)=>input.type==type)?.link;
167
+ if (!link_id) { return null; }
168
+ link = app.graph.links[link_id];
169
+ parent = get_real_node(link.origin_id);
170
+ }
171
+ return link;
172
+ }
173
+
174
+ function all_group_nodes() {
175
+ return app.graph._nodes.filter((node) => GroupNodeHandler.isGroupNode(node));
176
+ }
177
+
178
+ function is_in_group(node_id, group_node) {
179
+ return group_node.getInnerNodes().find((inner_node) => (inner_node.id==node_id));
180
+ }
181
+
182
+ /*
183
+ Return the group node if this node_id is part of a group, else return the node itself.
184
+ Returns a full node object
185
+ */
186
+ function get_group_node(node_id, level=Logger.ERROR) {
187
+ const nid = node_id.toString();
188
+ var gn = app.graph._nodes_by_id[nid];
189
+ if (!gn && nid.includes(':')) gn = app.graph._nodes_by_id[nid.split(':')[0]];
190
+ if (!gn) gn = all_group_nodes().find((group_node) => is_in_group(nid, group_node));
191
+ if (!gn) Logger.log(level, `get_group node couldn't find ${nid}`)
192
+ return gn;
193
+ }
194
+
195
+ /*
196
+ Return the node object for this node_id.
197
+ - if it's in _nodes_by_id return it
198
+ - if it is of the form x:y find it in group node x
199
+ - if it is the real node number of something in a group, get it from the group
200
+ */
201
+ function get_real_node(node_id, level=Logger.INFORMATION) {
202
+ const nid = node_id.toString();
203
+ var rn = app.graph._nodes_by_id[nid];
204
+ if (!rn && nid.includes(':')) rn = app.graph._nodes_by_id[nid.split(':')[0]]?.getInnerNodes()[nid.split(':')[1]]
205
+ if (!rn) {
206
+ all_group_nodes().forEach((node) => {
207
+ if (!rn) rn = node.getInnerNodes().find((inner_node) => (inner_node.id==nid));
208
+ })
209
+ }
210
+ if (!rn) Logger.log(level, `get_real_node couldn't find ${node_id} - ok during loading, shortly after node deletion etc.`)
211
+ return rn;
212
+ }
213
+
214
+ function get_all_nodes_within(node_id) {
215
+ const node = get_group_node(node_id);
216
+ if (GroupNodeHandler.isGroupNode(node)) return node.getInnerNodes();
217
+ return [];
218
+ }
219
+
220
+
221
+ /*
222
+ Does this input connect upstream to a live node?
223
+ */
224
+ function is_connected(input) {
225
+ const link_id = input.link;
226
+ if (link_id === null) return false; // no connection
227
+ var the_link = app.graph.links[link_id];
228
+ if (!the_link) return false;
229
+ the_link = handle_bypass(the_link, the_link.type); // find the link upstream of bypasses
230
+ if (!the_link) return false; // no source for data.
231
+ return true;
232
+ }
233
+
234
+ /*
235
+ Is this a UE node?
236
+ */
237
+ function is_UEnode(node_or_nodeType) {
238
+ const title = node_or_nodeType.type ?? node_or_nodeType.comfyClass;
239
+ return ((title) && (title.startsWith("Anything Everywhere") || title==="Seed Everywhere" || title==="Prompts Everywhere"))
240
+ }
241
+ function is_helper(node_or_nodeType) {
242
+ const title = node_or_nodeType.type ?? node_or_nodeType.comfyClass;
243
+ return ((title) && (title.startsWith("Simple String")))
244
+ }
245
+ function has_priority_boost(node_or_nodeType) {
246
+ const title = node_or_nodeType.type ?? node_or_nodeType.comfyClass;
247
+ return ((title) && (title == "Anything Everywhere?"))
248
+ }
249
+
250
+ /*
251
+ Inject a call into a method on object with name methodname.
252
+ The injection is added at the end of the existing method (if the method didn't exist, it is created)
253
+ injectionthis and injectionarguments are passed into the apply call (as the this and the arguments)
254
+ */
255
+ function inject(object, methodname, tracetext, injection, injectionthis, injectionarguments) {
256
+ const original = object[methodname];
257
+ object[methodname] = function() {
258
+ Logger.trace(`${tracetext} hijack`, arguments);
259
+ original?.apply(this, arguments);
260
+ injection.apply(injectionthis, injectionarguments);
261
+ }
262
+ }
263
+
264
+
265
+ export { node_in_loop, handle_bypass, node_is_live, is_connected, is_UEnode, is_helper, inject, Logger, get_real_node, get_group_node, get_all_nodes_within, has_priority_boost}
ComfyUI/custom_nodes/cg-use-everywhere/pyproject.toml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "cg-use-everywhere"
3
+ description = "A set of nodes that allow data to be 'broadcast' to some or all unconnected inputs. Greatly reduces link spaghetti."
4
+ version = "4.8.5"
5
+ license = "LICENSE"
6
+
7
+ [project.urls]
8
+ Repository = "https://github.com/chrisgoringe/cg-use-everywhere"
9
+
10
+ [tool.comfy]
11
+ PublisherId = "chrisgoringe"
12
+ DisplayName = "cg-use-everywhere"
13
+ Icon = ""