parlorsky commited on
Commit
2b3f139
·
verified ·
1 Parent(s): eab059f

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +5 -0
  2. v3-nodes/cg-use-everywhere/.gitattributes +2 -0
  3. v3-nodes/cg-use-everywhere/.github/ISSUE_TEMPLATE/bug_report.yaml +68 -0
  4. v3-nodes/cg-use-everywhere/.github/ISSUE_TEMPLATE/config.yml +1 -0
  5. v3-nodes/cg-use-everywhere/.github/ISSUE_TEMPLATE/feature_request.yaml +20 -0
  6. v3-nodes/cg-use-everywhere/.github/workflows/publish_action.yml +24 -0
  7. v3-nodes/cg-use-everywhere/.gitignore +155 -0
  8. v3-nodes/cg-use-everywhere/.vscode/settings.json +5 -0
  9. v3-nodes/cg-use-everywhere/LICENSE +201 -0
  10. v3-nodes/cg-use-everywhere/README-old.md +335 -0
  11. v3-nodes/cg-use-everywhere/README.md +491 -0
  12. v3-nodes/cg-use-everywhere/__init__.py +15 -0
  13. v3-nodes/cg-use-everywhere/docs/ComboClone1.png +0 -0
  14. v3-nodes/cg-use-everywhere/docs/ComboClone2.png +0 -0
  15. v3-nodes/cg-use-everywhere/docs/ComboClone3.png +0 -0
  16. v3-nodes/cg-use-everywhere/docs/PE.png +0 -0
  17. v3-nodes/cg-use-everywhere/docs/after.png +0 -0
  18. v3-nodes/cg-use-everywhere/docs/before.png +0 -0
  19. v3-nodes/cg-use-everywhere/docs/broadcastoff.png +0 -0
  20. v3-nodes/cg-use-everywhere/docs/broadcaston.png +0 -0
  21. v3-nodes/cg-use-everywhere/docs/conditioning.png +0 -0
  22. v3-nodes/cg-use-everywhere/docs/connectable.png +0 -0
  23. v3-nodes/cg-use-everywhere/docs/connectable2.png +0 -0
  24. v3-nodes/cg-use-everywhere/docs/connectable3.png +0 -0
  25. v3-nodes/cg-use-everywhere/docs/icon.png +0 -0
  26. v3-nodes/cg-use-everywhere/docs/options.png +0 -0
  27. v3-nodes/cg-use-everywhere/docs/output_set.png +0 -0
  28. v3-nodes/cg-use-everywhere/docs/outputs.png +0 -0
  29. v3-nodes/cg-use-everywhere/docs/primitives.png +0 -0
  30. v3-nodes/cg-use-everywhere/docs/redgreen.png +0 -0
  31. v3-nodes/cg-use-everywhere/docs/restrictions.png +0 -0
  32. v3-nodes/cg-use-everywhere/docs/simple-example-image.png +3 -0
  33. v3-nodes/cg-use-everywhere/docs/simple-example.png +3 -0
  34. v3-nodes/cg-use-everywhere/js/combo_clone.js +42 -0
  35. v3-nodes/cg-use-everywhere/js/connections.js +146 -0
  36. v3-nodes/cg-use-everywhere/js/floating_window.js +123 -0
  37. v3-nodes/cg-use-everywhere/js/i18n.js +106 -0
  38. v3-nodes/cg-use-everywhere/js/recursive_callbacks.js +64 -0
  39. v3-nodes/cg-use-everywhere/js/shared.js +36 -0
  40. v3-nodes/cg-use-everywhere/js/tooltip_window.js +55 -0
  41. v3-nodes/cg-use-everywhere/js/ue.css +102 -0
  42. v3-nodes/cg-use-everywhere/js/ue_properties.js +158 -0
  43. v3-nodes/cg-use-everywhere/js/ue_properties_editor.js +147 -0
  44. v3-nodes/cg-use-everywhere/js/use_everywhere.js +334 -0
  45. v3-nodes/cg-use-everywhere/js/use_everywhere_apply.js +129 -0
  46. v3-nodes/cg-use-everywhere/js/use_everywhere_cache.js +25 -0
  47. v3-nodes/cg-use-everywhere/js/use_everywhere_classes.js +373 -0
  48. v3-nodes/cg-use-everywhere/js/use_everywhere_graph_analysis.js +134 -0
  49. v3-nodes/cg-use-everywhere/js/use_everywhere_settings.js +366 -0
  50. v3-nodes/cg-use-everywhere/js/use_everywhere_subgraph_utils.js +86 -0
.gitattributes CHANGED
@@ -250,3 +250,8 @@ v3-nodes/rgthree-comfy/docs/rgthree_advanced.png filter=lfs diff=lfs merge=lfs -
250
  v3-nodes/rgthree-comfy/docs/rgthree_advanced_metadata.png filter=lfs diff=lfs merge=lfs -text
251
  v3-nodes/rgthree-comfy/docs/rgthree_context.png filter=lfs diff=lfs merge=lfs -text
252
  v3-nodes/rgthree-comfy/docs/rgthree_context_metadata.png filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
250
  v3-nodes/rgthree-comfy/docs/rgthree_advanced_metadata.png filter=lfs diff=lfs merge=lfs -text
251
  v3-nodes/rgthree-comfy/docs/rgthree_context.png filter=lfs diff=lfs merge=lfs -text
252
  v3-nodes/rgthree-comfy/docs/rgthree_context_metadata.png filter=lfs diff=lfs merge=lfs -text
253
+ v3-nodes/cg-use-everywhere/docs/simple-example-image.png filter=lfs diff=lfs merge=lfs -text
254
+ v3-nodes/cg-use-everywhere/docs/simple-example.png filter=lfs diff=lfs merge=lfs -text
255
+ v3-nodes/cg-use-everywhere/tests/compare.png filter=lfs diff=lfs merge=lfs -text
256
+ v3-nodes/cg-use-everywhere/tests/test.png filter=lfs diff=lfs merge=lfs -text
257
+ v3-nodes/cg-use-everywhere/tests/test2.png filter=lfs diff=lfs merge=lfs -text
v3-nodes/cg-use-everywhere/.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
v3-nodes/cg-use-everywhere/.github/ISSUE_TEMPLATE/bug_report.yaml ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Bug report
2
+ description: Report a bug in the UE nodes
3
+ title: "[Bug]: "
4
+ labels: 'Incoming'
5
+ assignees: 'chrisgoringe'
6
+ body:
7
+ - type: markdown
8
+ attributes:
9
+ value: I haven't made any of the fields below mandatory, but the more infomation you give me, the better the chance I will be able to help! |See [this discussion](https://github.com/chrisgoringe/cg-use-everywhere/discussions/294) for more details.
10
+
11
+ - type: textarea
12
+ id: description
13
+ attributes:
14
+ label: Describe the bug
15
+
16
+ - type: markdown
17
+ attributes:
18
+ value: "# Versions"
19
+
20
+ - type: markdown
21
+ attributes:
22
+ value: You can find the ComfyUI and ComfyUI_frontend numbers in Settings -> About, and the Use Everywhere version in Settings -> Use Everywhere.
23
+
24
+ - type: input
25
+ id: version
26
+ attributes:
27
+ label: ComfyUI version
28
+
29
+ - type: input
30
+ id: uiversion
31
+ attributes:
32
+ label: ComfyUI_frontend version
33
+
34
+ - type: input
35
+ id: UEversion
36
+ attributes:
37
+ label: Use Everywhere Version
38
+
39
+ - type: input
40
+ id: OS
41
+ attributes:
42
+ label: Operating System and Browser
43
+
44
+ - type: markdown
45
+ attributes:
46
+ value: "# Javascript Console"
47
+
48
+ - type: markdown
49
+ attributes:
50
+ value: Press f12 on your browser when the bug occurs and see if there is anthing in the console that looks like it might be relevant - copy and paste it here if so.
51
+
52
+ - type: textarea
53
+ id: console
54
+ attributes:
55
+ label: Javascript Console Log
56
+
57
+ - type: markdown
58
+ attributes:
59
+ value: "# Anything Else?"
60
+
61
+ - type: markdown
62
+ attributes:
63
+ value: Anything else that might help - screenshots, an example workflow (as simple as possible to illustrate the problem), anything you think might help.
64
+
65
+ - type: textarea
66
+ id: anythingelse
67
+ attributes:
68
+ label: Anything Else
v3-nodes/cg-use-everywhere/.github/ISSUE_TEMPLATE/config.yml ADDED
@@ -0,0 +1 @@
 
 
1
+ blank_issues_enabled: true
v3-nodes/cg-use-everywhere/.github/ISSUE_TEMPLATE/feature_request.yaml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Feature Request
2
+ description: Suggest a new feature for the UE nodes
3
+ title: "[Feature]: "
4
+ labels: 'Incoming'
5
+ assignees: 'chrisgoringe'
6
+ body:
7
+ - type: textarea
8
+ id: description
9
+ attributes:
10
+ label: What do you want to see added to the UE nodes?
11
+
12
+ - type: textarea
13
+ id: reason
14
+ attributes:
15
+ label: How will this help you, or other users?
16
+
17
+ - type: textarea
18
+ id: anythingelse
19
+ attributes:
20
+ label: Anything else that might help - screenshots, workflow etc.
v3-nodes/cg-use-everywhere/.github/workflows/publish_action.yml ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Publish to Comfy registry
2
+ on:
3
+ workflow_dispatch:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - "pyproject.toml"
9
+
10
+ permissions:
11
+ issues: write
12
+
13
+ jobs:
14
+ publish-node:
15
+ name: Publish Custom Node to registry
16
+ runs-on: ubuntu-latest
17
+ if: ${{ github.repository_owner == 'chrisgoringe' }}
18
+ steps:
19
+ - name: Check out code
20
+ uses: actions/checkout@v4
21
+ - name: Publish Custom Node
22
+ uses: Comfy-Org/publish-node-action@v1
23
+ with:
24
+ personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }} ## Add your own personal access token to your Github Repository secrets and reference it here.
v3-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
v3-nodes/cg-use-everywhere/.vscode/settings.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "python-envs.defaultEnvManager": "ms-python.python:conda",
3
+ "python-envs.defaultPackageManager": "ms-python.python:conda",
4
+ "python-envs.pythonProjects": []
5
+ }
v3-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.
v3-nodes/cg-use-everywhere/README-old.md ADDED
@@ -0,0 +1,335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-filter) for another way to make some workflows smoother. And leave a star if you like something!
12
+
13
+ ---
14
+
15
+ # Important!
16
+
17
+ I've merged v7 of UE to coincide with the update to ComfyUI, but the instructions below have not been updated.
18
+
19
+ Please see [this discussion](https://github.com/chrisgoringe/cg-use-everywhere/discussions/334) (but ignore the bits about branches).
20
+
21
+ ---
22
+
23
+ Over the next few releases I am aiming to reduce the UE zoo down to a single node that can do everything, but is as easy (or easier) to use than the current nodes.
24
+
25
+ The first step in this process is merging the `AnythingEverywhere` and `AnythingEverywhere?` nodes by moving the regex restrictions into a separate dialog that can be used by either. In future iterations the group and color restrictions will also move to this dialog (so people find them!)
26
+
27
+ ---
28
+
29
+ # 6.3 Update for ComfyUI front end (version TBC) - subgraphs
30
+
31
+ With the addition of subgraphs, group nodes are no longer supported in UE from 6.3 onwards.
32
+
33
+ Instead, UE 6.3 has the following support for subgraphs:
34
+
35
+ - UE nodes can be used in the main graph and in subgraphs.
36
+ - UE nodes only broadcast within the graph or subgraph they are in.
37
+ - Subgraph nodes are just nodes. You can connect UE nodes to their outputs, and UE links will connect to their inputs.
38
+ - Within a subgraph you can connect from the input panel to a UE node.
39
+ - UE nodes within the subgraph will *not* broadcast to the output panel.
40
+ - When you convert a set of nodes to a subgraph, UE links will work as long as:
41
+ - the UE node (Control) and the node feeding it (Source) are both in the subgraph, or
42
+ - neither the Source nor Control are in the subgraph, or
43
+ - the Source is in the subgraph, and the Control is not, and no Target nodes are in the subgraph
44
+
45
+
46
+ ## Subgraph creation
47
+
48
+ There are three nodes involved in every UE link:
49
+ - Source (the link sending the data),
50
+ - Control (the UE node connected to the source),
51
+ - Target (the node that is receiving the data as an input)
52
+
53
+ This is how those cases are treated:
54
+
55
+ |Support|Source|Control|Target||
56
+ |-|-|-|-|-|
57
+ |Yes|Graph|Graph|Graph|Nothing changes|
58
+ |Yes|Graph|Graph|Subgraph|The subgraph will have inputs for the data; in the subgraph the input panel is connected to the Target with a real link|
59
+ |No|Graph|Subgraph|Graph|Not supported|
60
+ |No*|Graph|Subgraph|Subgraph|Not supported|
61
+ |Yes|Subgraph|Graph|Graph|The subgraph will be connected to the Control|
62
+ |No|Subgraph|Graph|Subgraph|Not supported|
63
+ |Yes|Subgraph|Subgraph|Graph|The Source will be connected to the Control *and* the output panel in the subgraph, the output will be connected to the Target with a real link|
64
+ |Yes|Subgraph|Subgraph|Subgraph|All nodes will be connected in the subgraph as they were in the graph|
65
+
66
+ No* indicates a case that does not work, but might get implemented.
67
+
68
+ No indicates a case I'm unlikely ever to support
69
+
70
+ ---
71
+
72
+ ## Test workflow
73
+
74
+ |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|
75
+ |-|-|
76
+ |![screen](docs/test-workflow-screenshot.png)|![image](docs/test-workflow.png)|
77
+
78
+ Or [the workflow as json](docs/test-workflow.json)
79
+
80
+ ## Current known limitations
81
+
82
+ There are some situations that UE nodes can't cope with at present. Here are some I know about, and possible workarounds.
83
+
84
+ ### Pythonssss Preset Text
85
+
86
+ [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).
87
+
88
+ ### Group nodes
89
+
90
+ UE nodes mostly work with group nodes. But there are a couple of important things to note:
91
+
92
+ - when you create a group node the input names and node names can change. This might break UE? regex connections.
93
+
94
+ ## Latest updates
95
+
96
+ 6.3 June/July 2025
97
+ - support for ComfyUI subgraphs
98
+
99
+ 6.1 (May 2025)
100
+ - Mostly some bug-fixes (#216, #217, #300)
101
+ - New option to show UE links differently without animation for better performance
102
+
103
+ 6.0 (April 2025)
104
+ - Very major rebuild to deal with latest version of Comfy
105
+
106
+ ## Installing
107
+
108
+ Use Comfy Manager. If you really want to do it manually, just clone this repository in your custom_nodes directory.
109
+
110
+ ## Anything Everywhere (start here!)
111
+
112
+ 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'.
113
+
114
+ 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.
115
+
116
+ To visualise what it's being connected to, right-click on the background canvas and select `Toggle UE Link Visibility`.
117
+
118
+ ## Anything Everywhere? - control matching with regex rules
119
+
120
+ 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!
121
+
122
+ (From 4.6 you can also specify a group regex to only match inputs on nodes which are in groups that match the regex.)
123
+
124
+ ![regex](docs/regex.png)
125
+
126
+ *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.
127
+
128
+ Using regex means you can use `^prompt` to match `prompt` at the beginning of the title only, to avoid matching `negative_prompt`.
129
+
130
+ 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).
131
+
132
+ ### Can I make the regex an input instead of a widget?
133
+
134
+ Sort of.
135
+
136
+ 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.
137
+
138
+ |This works|This doesn't. And never will.|
139
+ |-|-|
140
+ |![Alt text](docs/image.png)|![no](docs/imagex.png)|
141
+
142
+
143
+ ## Seed Everywhere
144
+
145
+ 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.
146
+
147
+ ## Anything Everywhere3 - One node, three inputs.
148
+
149
+ Really just three `Anything Everywhere` nodes packaged together. Designed for the outputs of Checkpoint Loader.
150
+
151
+ ![UE3](docs/UE3.png)
152
+
153
+ ## Prompts Everywhere - two strings or conditionings
154
+
155
+ 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.
156
+
157
+ |strings|conditionings|
158
+ |-|-|
159
+ |![pe](docs/PE.png)|![pe](docs/conditioning.png)
160
+
161
+ # Primitives and COMBOs and the like
162
+
163
+ 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.
164
+
165
+ If you want to use UE to control sampler or sigma, you can do this with the built in `SamplerCustom` nodes:
166
+
167
+ ![sample and sigma](docs/sampler%20and%20sigma.png)
168
+
169
+ For more on this, see [this discussion](https://github.com/chrisgoringe/cg-use-everywhere/issues/69)
170
+
171
+ # Other features
172
+
173
+ ## Third Party Integration
174
+
175
+ At the suggestion of [@fighting-tx](https://github.com/fighting-tx),
176
+ I've added a method that third party nodes can use if they want to see the prompt as generated by UE.
177
+ It's attached to the `app` object, so you can check if it is present and use it something like this:
178
+
179
+ ```js
180
+ var prompt
181
+ if (app.ue_modified_prompt) {
182
+ prompt = await app.ue_modified_prompt()
183
+ } else {
184
+ prompt = await original_graphToPrompt.apply(app)
185
+ }
186
+ ```
187
+
188
+ Other methods could be exposed if there is interest - raise an issue if you'd like to see something.
189
+
190
+ ## Reject links
191
+
192
+ Right click on a node and you can set it to reject UE links
193
+
194
+ ## Show links - visualisation and animation.
195
+
196
+ 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.
197
+
198
+ 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.
199
+
200
+ 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.
201
+
202
+ ## Convert to real links
203
+
204
+ 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).
205
+
206
+ 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`.
207
+
208
+ ## Shift drag
209
+
210
+ 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.
211
+
212
+ ![auto](docs/auto.gif)
213
+
214
+ ## Group and color restriction
215
+
216
+ UE nodes can be restricted to send only to nodes of the same color, or only to nodes that *aren't* the same color.
217
+
218
+ 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.
219
+
220
+ 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.
221
+
222
+ ## Highway nodes
223
+
224
+ 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:
225
+ ![highway](docs/highway.png)
226
+
227
+ This is new, so please report any issues!
228
+
229
+ ## Loop checking
230
+
231
+ 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.
232
+
233
+ 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.
234
+
235
+ 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.
236
+
237
+ ## Priorities
238
+
239
+ 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:
240
+
241
+ - `Anything Everywhere?`
242
+ - `Seed Everywhere` and `Prompts Everywhere`
243
+ - `Anything Everywhere`
244
+ - `Anything Everywhere3`
245
+
246
+ For nodes of the same time, those with colour restrictions and group restriction are prioritised (colour+group > colour > group > none).
247
+
248
+ 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).
249
+
250
+ ## See what is sent
251
+
252
+ 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'.
253
+
254
+ ## Logging/Debugging
255
+
256
+ 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:
257
+ ```javascript
258
+ static ERROR = 0; // actual errors
259
+ static PROBLEM = 1; // things that stop the workflow working
260
+ static INFORMATION = 2; // record of good things
261
+ static DETAIL = 3; // details
262
+
263
+ static LEVEL = Logger.PROBLEM;
264
+ static TRACE = false; // most of the method calls
265
+ ```
266
+ Change the `LEVEL` to `Logger.INFORMATION` for more, or `Logger.DETAIL` for even more; set `TRACE` to `true` for some other debugging information.
267
+
268
+ 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:
269
+
270
+ - update to the latest version
271
+ - restart ComfyUI
272
+ - clear the canvas
273
+ - close the browser
274
+ - open a new Comfy window (with no workflow), look in console (f12) to see if there were any errors as ComfyUI started up
275
+ - load your workflow, and look again
276
+ - run, and look again
277
+
278
+ The other thing worth trying is clearing out all the custom node javascript from where it gets copied when ComfyUI starts:
279
+
280
+ - stop Comfy
281
+ - go to [comfy root]/web/extensions (*not* under custom_nodes)
282
+ - remove everything there EXCEPT for `core`. Leave `core` (it's ComfyUI stuff)
283
+ - restart Comfy (all custom nodes will reinstall their javascript at startup)
284
+
285
+ 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).
286
+
287
+ ## Cautions
288
+
289
+ 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
290
+
291
+ |If you use a ? node to send to a node...|...and bypass the recipient, it doesn't get connected |
292
+ |-|-|
293
+ |![1](docs/bypass_catch1.png)|![2](docs/bypass_catch2.png)|
294
+
295
+ This is unlikely to be fixed, but should be fairly easy to avoid!
296
+
297
+
298
+ ---
299
+ # Some older information
300
+
301
+ # Update for ComfyUI front end 1.16 and above
302
+
303
+ ComfyUI front end 1.16 made a major change to the way that inputs and widgets work, which had a significant impact on the UE nodes.
304
+
305
+ A widget is no longer converted to an input in order to connect to it; instead, you just connect an output to it, and it converts.
306
+
307
+ This is quite cool - except that it meant that there was no way for UE to tell if a widget was supposed to be an empty input for UE to feed.
308
+
309
+ So there is a new mechanism for this. Here's a picture of a node that I have right-clicked:
310
+
311
+ ![116](docs/116.png)
312
+
313
+ Things to notice:
314
+
315
+ - There is a new menu `UE Connectable Widgets` which lists all the widgets. Those marked as connectable have a green bar to the left of the name.
316
+ - Select a widget to toggle its value
317
+ - Next to the widget, where an input would be, the connectable widgets have a graphical indication of their state.
318
+ - The smaller dark circle (next to steps) indicate a connectable node which is not connected (widget value will be used)
319
+ - The light circle (next to cfg) and greying out the widget indicates that a UE node will provide the value
320
+ - No icon (all the other widgets) indicates that they are not UE connectable
321
+ - Other inputs still have the indications of normal connection or UE connection (here model is a UE connection)
322
+
323
+ You'll probably find that turning the `showlinks` option on helps, and the `highlight` option is required for those widget indicators which help a lot.
324
+
325
+ ![options116](docs/options116.png)
326
+
327
+ Hopefully old workflows will be automatically converted when you load them. Hopefully.
328
+
329
+ ## Naming widgets
330
+
331
+ The `Prompts Everywhere`, `Seed Everywhere`, and `Anything Everywhere?` all make use of the name of an input. On some occasions this might mean you want to rename an input.
332
+
333
+ For an input that can't be a widget, that's trivial - right click on the input dot and select `Rename Slot`. The name will be updated, and the new name displayed next to the dot.
334
+
335
+ If an input *can* be a widget, you can rename it in the same way, but the new name *does not get displayed*. The name has changed, and the UE nodes will use the new name, but the old name is still shown. I consider this to be a bug in the Comfy front-end, and have raised an issue [here](https://github.com/Comfy-Org/ComfyUI_frontend/issues/3654). Unfortunately it appears that Comfy UI is going to remove the ability to rename a widget entirely, so best to avoid using widget input names if at all possible.
v3-nodes/cg-use-everywhere/README.md ADDED
@@ -0,0 +1,491 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # UE Nodes
2
+
3
+ ## Love this node?
4
+
5
+ [Buy me a coffee!](https://www.buymeacoffee.com/chrisgoringe)
6
+
7
+ ## Shameless plug
8
+
9
+ Check out [Image Picker](https://github.com/chrisgoringe/cg-image-filter) for another way to make some workflows smoother.
10
+
11
+ # Common Questions
12
+
13
+ ## How do I get positive and negative conditionings to work?
14
+
15
+ Connect both to a single UE node, and then rename the inputs. See [here](https://github.com/chrisgoringe/cg-use-everywhere/issues/317#issuecomment-3663225670).
16
+
17
+ ## Why doesn't it work with reroutes / primitives?
18
+
19
+ Nodes which dynamically work out their data type are a real problem that isn't worth the complexity to fix. See [here](https://github.com/chrisgoringe/cg-use-everywhere/tree/main#primitives-and-reroutes) for more info.
20
+
21
+ ## Can I broadcast a COMBO, like my choice of sampler?
22
+
23
+ Yes. See Combo Clone node [here](https://github.com/chrisgoringe/cg-use-everywhere/tree/main#special-case-nodes).
24
+
25
+ # Recent Changes
26
+
27
+ ## 7.7
28
+
29
+ - Added "Apply to unrepeated output" options
30
+ - Cleaned up restrictions dialog
31
+ - Internal refactoring to conform to ComfyUI changes
32
+
33
+ ## 7.6
34
+
35
+ - Added visual indicators to subgraph nodes
36
+ - Added hover-over conflicted node to show conflict
37
+ - Added "Send to Any" option in restrictions
38
+
39
+ <details>
40
+ <summary>Older changes since 7.0</summary>
41
+
42
+ ## 7.5
43
+
44
+ - Added per-output granularity to any-node broadcasting
45
+ - Prompt Everywhere node deprecated and auto-replaced
46
+ - Convert to real links now works for any-node broadcasting
47
+ - Anything Everywhere nodes in a subgraph can broadcast to its output
48
+ - Inputs which have a clash of two more possible UE sources are marked with a red cross
49
+ - Couple of bug fixes, including one significant performance issue
50
+
51
+ ## 7.4
52
+
53
+ - Added broadcasting from [any node](#any-node-broadcasting)
54
+ - Added negative regex option
55
+ - Seed Everywhere node deprecated and auto-replaced
56
+
57
+ ## 7.3
58
+
59
+ Bugfixes:
60
+ [361](https://github.com/chrisgoringe/cg-use-everywhere/issues/361),
61
+ [379](https://github.com/chrisgoringe/cg-use-everywhere/issues/379),
62
+ [381](https://github.com/chrisgoringe/cg-use-everywhere/issues/381)
63
+
64
+ ## 7.2.1 and 7.2.2
65
+
66
+ Bugfixes:
67
+ [383](https://github.com/chrisgoringe/cg-use-everywhere/issues/383),
68
+ [384](https://github.com/chrisgoringe/cg-use-everywhere/issues/384),
69
+ [388](https://github.com/chrisgoringe/cg-use-everywhere/issues/388)
70
+
71
+ ## 7.2
72
+
73
+ - Added support for [Combo inputs](#combo-clone) via the `Combo Clone` helper node
74
+ - Fixed a number of issues related to subgraphs
75
+ - Improved handling of disconnect
76
+ - Added new options for handling multiple inputs of same type
77
+ - Added internationalisation options
78
+
79
+ ## 7.1
80
+
81
+ - Allowed individual inputs to block UE connections
82
+ - Added support for multiple inputs of the same type
83
+ - Fixed a number of minor subgraph issues
84
+ - Fixed a serious bug with UE on Safari [details](https://github.com/chrisgoringe/cg-use-everywhere/issues/359)
85
+
86
+ </details>
87
+
88
+ # What is Anything Everywhere?
89
+
90
+ The `Anything Everywhere` node takes one or more inputs and sends the data to other nodes that need it.
91
+ You can connect as many inputs to an `Anything Everywhere` node as you like, and there are a range of ways
92
+ to control where the data will be sent.
93
+
94
+ Here's the standard ComfyUI template modified to use `AnythingEverywhere`
95
+
96
+ |workflow|output image you can drop into Comfy|
97
+ |-|-|
98
+ |![simple](docs/simple-example.png)|![simple](docs/simple-example-image.png)|
99
+
100
+ The `MODEL`, `CLIP`, and `VAE` are automatically broadcast to all the places they are needed.
101
+
102
+ Doesn't make much difference in this simple case, but with complex workflows it really does.
103
+ This is what the default wan 2.2 s2v video workflow looks like:
104
+
105
+ |before|after|
106
+ |-|-|
107
+ |![before](docs/before.png)|![after](docs/after.png)|
108
+
109
+ # Any node broadcasting
110
+
111
+ <details>
112
+ <summary>New in version 7.4 - any node can broadcast it's outputs</summary>
113
+
114
+ As of version `7.4`, any node can be set to broadcast with `Add UE broadcasting` in the right-click menu. I find this really helpful for subgraphs especially.
115
+
116
+ When broadcasting, the node acts like all its outputs were connected to a single UE node, so
117
+
118
+ |This...|...does the same as this|
119
+ |-|-|
120
+ |![with](docs/broadcaston.png)|![without](docs/broadcastoff.png)|
121
+
122
+ From `7.5`, the outputs that will broadcast can also be set (like inputs):
123
+
124
+ ![output_set](docs/output_set.png)
125
+
126
+ </details>
127
+
128
+ # Where will the data be sent?
129
+
130
+ The key to using Anything Everywhere nodes is understanding where the data will be sent.
131
+
132
+ By default the data will be sent to any input of the same data type which does not have a connection,
133
+ and does not have a widget providing the value.
134
+
135
+ <details>
136
+ <summary>
137
+ At the sending end, you can constrain where the data gets send through restrictions applied to the `Anything Everywhere` node.
138
+ </summary>
139
+
140
+ Restrictions can be accessed by double-clicking the body of the node, or through the right-click menu.
141
+
142
+ ![restrictions](docs/restrictions.png)
143
+
144
+ The first three entries are [regex](https://regex101.com/) patterns.
145
+ The node will only send data to another node if the regex matches the receiving node title, the name of the input, or the name of a group the receiving node is in, respectively. Check the 'invert' checkbox to invert the regex (send only to things which _don't_ match).
146
+
147
+ The Group and Colour restrictions will constrain the node to only send to nodes in (or not in) the same group, and of the same (or different) colour.
148
+
149
+ `Repeated Types` determines behaviour when more than one input of the same type is connected to an `Anything Everywhere` node.
150
+ In this case an additional constraint is used to disambiguate which inputs match
151
+ by comparing the name of the input slot on the `Anything Everywhere` node with
152
+ either the name of the input slot on the target node, or the name of the target node.
153
+ The `match start` and `match end` options require that the input names match from the start (or end) for the full length of the shorter:
154
+ so you can match `seed` to `seed` or `noise_seed` by naming the `Anything Everywhere` input `seed` and selecting `Match end of input names`.
155
+
156
+ If `Apply to Unrepeated` is set to `Yes`, the `Repeated Types` rule will be applied to all outputs, whether or not they are repeated.
157
+
158
+ `Send To Any`(default 'no') can be used to allow this node to send data to inputs with the `Any` type.
159
+
160
+ `Priority` is used to resolve conflicts between multiple sending nodes. It has a default value (which generally follows the rule that the more restrictive the rules, the higher the priority), but you can override that value.
161
+
162
+ If you select multiple restrictions, all must be satisfied for the node to send.
163
+
164
+ If any restrictions are applied, the `Anything Everywhere` node gets a green circle in the top left hand corner, and a tooltip if you hover the mouse over it (as long as Show Links is not set to None).
165
+
166
+ You can rename input slots by right-clicking on the input dot - but you can't rename widget inputs - this is a limitation imposed by ComfyUI ([discussion](https://github.com/Comfy-Org/ComfyUI_frontend/issues/3654)). The work-around is to rename the target node (or use multiple `Anything Everywhere` nodes with other constraints, especially color matching).
167
+
168
+ </details>
169
+
170
+ <details>
171
+ <summary>
172
+ At the receiving end: you can specify that an input should not accept data, or that one with a widget should.
173
+ </summary>
174
+
175
+ In the `UE Connectable Inputs` menu (right-click on a node):
176
+ the green bar indicates an input is connectable. The `Reject UE links` option can be used to make this node completely reject UE links, regardless of other settings.
177
+
178
+ ![uec](docs/connectable.png)
179
+ </details>
180
+
181
+ <details>
182
+ <summary>
183
+ What if two or more `Anything Everywhere` nodes can send to the same input? How are conflicts resolved?
184
+ </summary>
185
+
186
+ Each node has an automatically calculated priority - in general the more restrictive the node, the higher the priority.
187
+ You can see this prority in the restrictions dialog, and you can choose to replace the automatically calculated value if you wish.
188
+
189
+ If two more more `Anything Everywhere` nodes match the same input, the higher priority node is used. If there is a tie, _no connection is made_.
190
+ The input is marked with a red cross to indicate that there is a conflict. If you hover over the node, the two (or more) conflicting UE inputs
191
+ are shown as red lines.
192
+
193
+ Alternatively, if you right-click on the canvas you will find an option to show which nodes are the problem.
194
+
195
+ </details>
196
+
197
+ # Visual Clues
198
+
199
+ Anything Everywhere has a number of ways to help you visualise what it is doing.
200
+
201
+ <details>
202
+ <summary>Show Anything Everywhere links</summary>
203
+
204
+ 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.
205
+
206
+ 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.
207
+
208
+ Animation takes quite a lot of processing, so don't use it unless you really need to. 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.
209
+ </details>
210
+
211
+ <details>
212
+ <summary>Nodes that broadcast have a badge</summary>
213
+
214
+ Any node that is capable of broadcasting data (a UE node, or another node to which broadcasting has been added) is marked with a circle in the top left hand corner.
215
+
216
+ If the circle is green, the node has no additional restrictions on where data will be sent;
217
+ if it is red, it has one or more restrictions (which you can see by hovering your mouse over it, or by editing restrictions
218
+ with the option on the right click menu, or by double clicking the node).
219
+
220
+ If the node is actually sending data, the circle (red or green) is bold; if the node is capable of sending but is not actually making
221
+ and connections, it is muted.
222
+
223
+ ![redgreen](docs/redgreen.png)
224
+ </details>
225
+
226
+ <details>
227
+ <summary>Inputs and outputs have a subtle visual clue</summary>
228
+
229
+ The state of inputs is also represented visually: a black ring and a glow on the input dot indicates it is connectable.
230
+ In the image below, `positive` has been set to not accept UE inputs, `steps` has been set to accept them, and `model` has a UE connection.
231
+
232
+ ![uec](docs/connectable2.png)
233
+
234
+ If a widget is getting data from a UE connection, it is grayed out, like `steps` below:
235
+
236
+ ![uec](docs/connectable3.png)
237
+
238
+ Similarly, if a node has had broadcasting added to it, the outputs that can broadcast are shown:
239
+ here `merged` can broadcast, but isn't, `x` can broadcast and is, and `y` is set to not broadcast.
240
+
241
+ ![outputs](docs/outputs.png)
242
+
243
+ </details>
244
+
245
+ # Special Case Nodes
246
+
247
+ <details>
248
+ <summary>To broadcast to COMBO (dropdown menu) widgets, you can either use String to Combos (above) or a Combo Clone</summary>
249
+
250
+ For each Combo type you want to use, add a Combo Clone node. Here we want to be able to broadcast `sampler_name` and `scheduler`.
251
+
252
+ ![CC1](docs/ComboClone1.png)
253
+
254
+ Connect the `Combo Clone` output to a widget you want it to replicate. The `Combo Clone` copies the options from that widget, and the widget name.
255
+
256
+ ![CC2](docs/ComboClone2.png)
257
+
258
+ Disconnect the `Combo Clone` and connect it to an `Anything Everywhere` node. The name copying means you can connect multiple Combos to a single `Anything Everywhere` node.
259
+
260
+ ![CC3](docs/ComboClone3.png)
261
+
262
+ It can now broadcast to any node with the same input type (but remember you will have to mark the Combo widget as UE Connectable, since widgets are not connectable by default).
263
+
264
+ </details>
265
+
266
+ <details>
267
+ <summary>Prompts Everywhere handles a positive and a negative prompt</summary>
268
+
269
+
270
+ Prompt Everywhere has two inputs. They will be sent with regex matching rules designed to match `prompt` or `positive`, and `neg`, respectively.
271
+
272
+ The actual regexes used are `(_|\\b)pos(itive|_|\\b)|^prompt|正面` and `(_|\\b)neg(ative|_|\\b)|负面`
273
+
274
+ |strings|conditionings|
275
+ |-|-|
276
+ |![pe](docs/PE.png)|![pe](docs/conditioning.png)
277
+
278
+ </details>
279
+
280
+
281
+ <details>
282
+ <summary>Seed Everywhere, Anything Everywhere? and Anything Everywhere3 are deprecated and will be automatically replaced</summary>
283
+
284
+ The `Seed Everywhere` node will be replaced with a primitive int, set to broadcast, with an input regex restriction that matches the localised name of the `seed` input on the base `KSampler` node - `seed` or `随机种` being the most common.
285
+
286
+ The `Anything Everywhere?` node will be replaced with an `Anything Everywhere` node with restrictions.
287
+
288
+ The `Anything Everywhere3` node will be replaced with an `Anything Everywhere` node with multiple inputs and any appropriate restrictions.
289
+
290
+ These replacements should not break any workflows. If they do... sorry.
291
+
292
+ </details>
293
+
294
+
295
+ # Options
296
+
297
+ <details>
298
+ <summary>
299
+ In the main settings menu, you will find the Use Everywhere options:
300
+ </summary>
301
+
302
+ ![options](docs/options.png)
303
+
304
+ The top set, `Graphics`, modify the visual appearance only.
305
+
306
+ The bottom set, `Options`, modify behaviour:
307
+
308
+ - When connecting, use the output slot's name as the input name. When a new connection is made to a UE node, the default is to name the input with the type. Select this option to use the output name of the node the link is from.
309
+ - Block workflow validation. This prevents other nodes from complaining about the lack of connections, or creating them. If you turn this off, there may be unexpected consequences.
310
+ - Logging. Increase the logging level if you are asked to help debug.
311
+ - Connect to bypassed nodes. When off, Use Everywhere will not connect to a bypassed node, and will attempt to work out whether an input is connected when upstream nodes are bypassed. I recommend turning this on.
312
+
313
+ </details>
314
+
315
+ # Primitives and Reroutes
316
+
317
+ <details>
318
+ <summary>
319
+ UE nodes work with the new primitives added in more recent versions of Comfy (in the `primitive` submenu)
320
+ </summary>
321
+
322
+ ![primitives](docs/primitives.png)
323
+
324
+ UE nodes do not work with the old-style `Primitive` nodes (which automatically determined what data type they needed to be),
325
+ nor do they work with reroute nodes.
326
+
327
+ In both cases that is dues to some issues that are deep within ComfyUI, related to the way that these nodes work out
328
+ the data type they represent, which makes it next to impossible for UE to correctly intereact with them.
329
+
330
+ </details>
331
+
332
+ # Other features
333
+
334
+ <details>
335
+ <summary>
336
+ Third Party Integration - the UE API
337
+ </summary>
338
+
339
+ At the suggestion of [@fighting-tx](https://github.com/fighting-tx),
340
+ I've added a method that third party nodes can use if they want to see the prompt as generated by UE.
341
+ It's attached to the `app` object, so you can check if it is present and use it something like this:
342
+
343
+ ```js
344
+ var prompt
345
+ if (app.ue_modified_prompt) {
346
+ prompt = await app.ue_modified_prompt()
347
+ } else {
348
+ prompt = await original_graphToPrompt.apply(app)
349
+ }
350
+ ```
351
+
352
+ Other methods could be exposed if there is interest - raise an issue if you'd like to see something.
353
+ </details>
354
+
355
+ <details>
356
+ <summary>
357
+ Convert to real links
358
+ </summary>
359
+
360
+ If you want to share a workflow without Anything Everywhere nodes being required, or to save an API version of a workflow, you can replace the virtual links created by Anything Everywhere nodes with real links (and remove the UE nodes).
361
+
362
+ 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 graph or subgraph by right-clicking the background and selecting `Convert all UEs to real links`.
363
+
364
+ </details>
365
+
366
+ # Subgraphs
367
+
368
+ Anything Everywhere works fairly well with the new subgraphs (and setting a subgraph to broadcast its outputs is very powerful!).
369
+ A couple of caveats:
370
+
371
+ <details>
372
+ <summary>Data is only broadcast within a graph</summary>
373
+
374
+ Anything Everywhere will not make links from nodes in one graph into a different graph. So you cannot broadcast data into a subgraph, nor can you broadcast it out.
375
+
376
+ This is a deliberate design decision, reflecting the fundamental principle of subgraphs - that they are self contained.
377
+ Data can only go in or out of a subgraph through its input and output panels.
378
+ This sort of data isolation is a very good thing in terms of maintaining a workflow, and by working consistently with the expectated
379
+ behaviour of subgraphs it is far less like that future changes in the front end will break Anything Everywhere.
380
+
381
+ This is not open for discussion. There are plenty of better ways to achieve the same end.
382
+
383
+ An Anything Everywhere node in your main graph can send to the input of a subgraph node;
384
+ you can also connect as Anything Everywhere node to one of the inputs inside a subgraph.
385
+
386
+ You can connect an output of the subgraph node to an Anything Everywhere node (or set the subgraph node to broadcast),
387
+ and you can broadcast from within the subgraph to the output panel of the subgraph.
388
+
389
+ </details>
390
+
391
+ <details>
392
+
393
+ <summary>When you create a subgraph, Anything Everywhere nodes do their best...</summary>
394
+
395
+ There are three nodes involved in every UE link:
396
+ - Source (the link sending the data),
397
+ - Control (the UE node connected to the source),
398
+ - Target (the node that is receiving the data as an input)
399
+
400
+ This is how those cases are treated:
401
+
402
+ |Support|Source|Control|Target||
403
+ |-|-|-|-|-|
404
+ |Yes|Graph|Graph|Graph|Nothing changes|
405
+ |Yes|Graph|Graph|Subgraph|The subgraph will have inputs for the data; in the subgraph the input panel is connected to the Target with a real link|
406
+ |No|Graph|Subgraph|Graph|Not supported|
407
+ |No*|Graph|Subgraph|Subgraph|Not supported|
408
+ |Yes|Subgraph|Graph|Graph|The subgraph will be connected to the Control|
409
+ |No|Subgraph|Graph|Subgraph|Not supported|
410
+ |Yes|Subgraph|Subgraph|Graph|The Source will be connected to the Control *and* the output panel in the subgraph, the output will be connected to the Target with a real link|
411
+ |Yes|Subgraph|Subgraph|Subgraph|All nodes will be connected in the subgraph as they were in the graph|
412
+
413
+ No* indicates a case that does not work, but might get implemented.
414
+
415
+ No indicates a case I'm unlikely ever to support
416
+
417
+ </details>
418
+
419
+ # Reporting a bug well
420
+
421
+ If there is a bug in Anything Everywhere, I want to fix it.
422
+ But my ability to do so depends on the quality of the information I have.
423
+
424
+ Seriously, people submit bug reports which just say "It isn't working since I updated".
425
+ Well, it's working for me, so I don't have any idea what your problem is.
426
+
427
+ Give me good information, and if I can reproduce the problem, there's a good chance I'll fix it (or suggest a work-around).
428
+
429
+ So if you have a problem, go to [the issues page](https://github.com/chrisgoringe/cg-use-everywhere/issues),
430
+ click on `New Issue`, select, `Bug Report`, and fill in as much of the information as you can.
431
+
432
+ # Suggesting new features
433
+
434
+ Go to [the issues page](https://github.com/chrisgoringe/cg-use-everywhere/issues),
435
+ click on `New Issue`, select, `Feature Request`, and tell me your idea.
436
+ If you can explain why the feature would be helpful to you, and ideally to others, I'm
437
+ much more likely to think about whether it's possible.
438
+
439
+ # Thanks to
440
+
441
+ The following people have contributed code or helpful discussions, without which these nodes would be less good!
442
+
443
+ - [DrJKL](https://github.com/DrJKL)
444
+ - [fighting-tx](https://github.com/fighting-tx)
445
+ - [huchenlei](https://github.com/huchenlei)
446
+ - [fichas](https://github.com/fichas)
447
+ - [LukeG89](https://github.com/LukeG89)
448
+ - [set-soft](https://github.com/set-soft)
449
+ - [TinyTerra](https://github.com/TinyTerra)
450
+ - [bananasss00](https://github.com/bananasss00)
451
+ - [JorgeR81](https://github.com/JorgeR81)
452
+
453
+ Feel free to [make suggestions, or implement features](https://github.com/chrisgoringe/cg-use-everywhere/issues) to get your name added here!
454
+
455
+ ---
456
+
457
+ # Anything Everywhere v7
458
+
459
+ Version 7 is a major update to the Anything Everywhere nodes, so the documentation below is all new. If you are looking for the old docs, you can find them [here](https://github.com/chrisgoringe/cg-use-everywhere/README-old).
460
+
461
+ <details>
462
+ <summary>Major changes in v7</summary>
463
+
464
+ If you used Anything Everywhere prior to v7, the major improvements are:
465
+
466
+ - The `Anything Everywhere3` and `Anything Everywhere?` nodes are deprecated, as their features are now part of the standard `Anything Everywhere` node.
467
+ - `Anything Everywhere` nodes now have dynamic inputs, so you can plug as many different things into them as you like.
468
+ - All the restrictions on what nodes data will be sent to are now in a restrictions editor, that can be accessed through the right click menu of the node, or by double-clicking the body of the node.
469
+ - In the restrictions editor you can set title, input, and group regexes, color restrictions, group restrictions, and priority (for when two nodes both match)
470
+ - The green circle is used to indicate that _any_ restrictions are in place; if you hover over a node with restrictions they will appear in a tooltip
471
+ - Subgraphs are supported (in the majority of cases). Yay subgraphs! Seriously, they are _so_ much better than group nodes.
472
+ - There are lots of odd cases with subgraphs, so if you find a case not covered properly, please [raise an issue](https://github.com/chrisgoringe/cg-use-everywhere/issues)
473
+
474
+ There are a couple of features that have been removed:
475
+
476
+ - Group nodes are no longer supported, as they are deprecated in ComfyUI in favour of the new subgraphs, which are supported (in most configurations)
477
+ - The `Simple String` mechanism to provide an input to the regex of an `Anything Everywhere?` node is no longer supported
478
+ - Other UI mechanisms to address this need are under consideration
479
+
480
+ ## Upgrade considerations
481
+
482
+ Other than the limitations noted, old workflows _should_ load and work out of the box,
483
+ with `Anything Everywhere3` and `Anything Everywhere?` nodes automatically converted to `Anything Everywhere` nodes with the appropriate restrictions applied.
484
+
485
+ However, there may be edge cases that don't work; if you have any problems, please [raise an issue](https://github.com/chrisgoringe/cg-use-everywhere/issues).
486
+
487
+ You will _not_ be able to use workflows saved using v7 with older versions of ComfyUI or older versions of UE.
488
+
489
+ **Group Nodes are no longer supported**
490
+
491
+ </details>
v3-nodes/cg-use-everywhere/__init__.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from comfy_api.latest import ComfyExtension, io
2
+ from .use_everywhere import SeedEverywhere, AnythingEverywhere, AnythingSomewhere, AnythingEverywhereTriplet, SimpleString, ComboClone, AnythingEverywherePrompts
3
+
4
+ UE_VERSION = "7.7"
5
+ WEB_DIRECTORY = "./js"
6
+ __all__ = [ "WEB_DIRECTORY"]
7
+
8
+ async def comfy_entrypoint() -> ComfyExtension:
9
+ class UseEverywhereExtension(ComfyExtension):
10
+ async def get_node_list(self) -> list[type[io.ComfyNode]]:
11
+ return [
12
+ AnythingEverywhere, AnythingSomewhere, AnythingEverywhereTriplet, SimpleString, ComboClone, SeedEverywhere, AnythingEverywherePrompts
13
+ ]
14
+
15
+ return UseEverywhereExtension()
v3-nodes/cg-use-everywhere/docs/ComboClone1.png ADDED
v3-nodes/cg-use-everywhere/docs/ComboClone2.png ADDED
v3-nodes/cg-use-everywhere/docs/ComboClone3.png ADDED
v3-nodes/cg-use-everywhere/docs/PE.png ADDED
v3-nodes/cg-use-everywhere/docs/after.png ADDED
v3-nodes/cg-use-everywhere/docs/before.png ADDED
v3-nodes/cg-use-everywhere/docs/broadcastoff.png ADDED
v3-nodes/cg-use-everywhere/docs/broadcaston.png ADDED
v3-nodes/cg-use-everywhere/docs/conditioning.png ADDED
v3-nodes/cg-use-everywhere/docs/connectable.png ADDED
v3-nodes/cg-use-everywhere/docs/connectable2.png ADDED
v3-nodes/cg-use-everywhere/docs/connectable3.png ADDED
v3-nodes/cg-use-everywhere/docs/icon.png ADDED
v3-nodes/cg-use-everywhere/docs/options.png ADDED
v3-nodes/cg-use-everywhere/docs/output_set.png ADDED
v3-nodes/cg-use-everywhere/docs/outputs.png ADDED
v3-nodes/cg-use-everywhere/docs/primitives.png ADDED
v3-nodes/cg-use-everywhere/docs/redgreen.png ADDED
v3-nodes/cg-use-everywhere/docs/restrictions.png ADDED
v3-nodes/cg-use-everywhere/docs/simple-example-image.png ADDED

Git LFS Details

  • SHA256: bcce28436629546d1d57c1e1bc531e1a5f8c80cf096604b9219f0de6a3645f35
  • Pointer size: 131 Bytes
  • Size of remote file: 391 kB
v3-nodes/cg-use-everywhere/docs/simple-example.png ADDED

Git LFS Details

  • SHA256: a0b8c0248da5946f9c17b3dee67745dd11a2e6c1f1e983578d6df489bb3a5bb8
  • Pointer size: 131 Bytes
  • Size of remote file: 101 kB
v3-nodes/cg-use-everywhere/js/combo_clone.js ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { shared } from "./shared.js"
2
+ import { Logger } from "./use_everywhere_utilities.js"
3
+
4
+ function update_me(node) {
5
+ if (node.properties.comboclone) {
6
+ Logger.log_problem(`Reseting combo clone node ${node.id}`)
7
+ node.widgets[0].options.values = [...node.properties.comboclone.options]
8
+ node.outputs[0].type = "COMBO"
9
+ node.outputs[0].label = node.properties.comboclone.name
10
+ }
11
+ }
12
+
13
+ export function is_combo_clone(node) {
14
+ return (node.type == "Combo Clone")
15
+ }
16
+
17
+ export function reset_comboclone_on_load(node) {
18
+ if (is_combo_clone(node)) update_me(node)
19
+ }
20
+
21
+ export function comboclone_on_connection(node, link_info, connect) {
22
+ if (!is_combo_clone(node)) return Logger.log_problem(`comboclone_on_connection called for node ${node.id} of type ${node.type}`)
23
+ if (shared.graph_being_configured) return
24
+ if (connect) {
25
+ if (!link_info) return
26
+ const target_node = node.graph.getNodeById(link_info.target_id)
27
+ const input_name = target_node?.inputs[link_info.target_slot].name
28
+ const widget = target_node?.widgets?.find((w)=>(w.name==input_name))
29
+
30
+ if (widget?.type=="combo" && widget?.options?.values) {
31
+ node.properties.comboclone = {
32
+ options : [...widget.options.values],
33
+ name : input_name
34
+ }
35
+ update_me(node)
36
+ node.widgets[0].value = widget.value
37
+ link_info.type = "COMBO"
38
+ }
39
+ } else {
40
+ // no action when we disconnect
41
+ }
42
+ }
v3-nodes/cg-use-everywhere/js/connections.js ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { link_is_from_subgraph_input, get_subgraph_input_type } from "./use_everywhere_subgraph_utils.js"
2
+ import { get_real_node, is_UEnode, Logger } from "./use_everywhere_utilities.js";
3
+ import { app } from "../../scripts/app.js";
4
+ import { i18n } from "./i18n.js";
5
+ import { shared } from "./shared.js";
6
+ import { reset_comboclone_on_load, is_combo_clone } from "./combo_clone.js";
7
+ import { ue_callbacks } from "./recursive_callbacks.js";
8
+
9
+ function get_type(graph, link_info) {
10
+ var type = null
11
+ if (link_info) {
12
+ if (link_is_from_subgraph_input(link_info)) { // input slot of subgraph
13
+ type = get_subgraph_input_type(graph, link_info.origin_slot)
14
+ } else {
15
+ type = get_real_node(link_info.origin_id, graph)?.outputs[link_info.origin_slot]?.type
16
+ }
17
+ if (type != link_info.type && link_info.type != '*') {
18
+ Logger.log_problem(`Type detected from upstream, ${type} != type on link_info ${link_info.type}`)
19
+ }
20
+ } else {
21
+ Logger.log_problem(`input_changed with connect true, graph_being_configured false, and no link_info`)
22
+ }
23
+ type = type || '*'
24
+ if (type!='*' && link_info) link_info.type = type
25
+ if (type=='*' && link_info) type = link_info.type
26
+ return type
27
+ }
28
+
29
+
30
+ /*
31
+ Called by onConnectionsChange for a UE_Node when side == 1 (input).
32
+ */
33
+
34
+ export function input_changed(node, slot, connect, link_info) {
35
+ if (shared.graph_being_configured) return
36
+ if (node.id==-1) return // this node isn't in the graph yet
37
+ if (!node?.inputs) return Logger.log_problem(`input_changed called but node.inputs not retrievable`)
38
+ const in_slot = node.inputs[slot]
39
+ if (!in_slot) return Logger.log_problem(`input_changed called for node #${node.id} slot ${slot} but that wasn't found`)
40
+ const graph = node.graph
41
+
42
+ if (connect) {
43
+ const type = get_type(graph, link_info)
44
+ if (type=='*') {
45
+ // no real idea what to do
46
+ } else {
47
+ if (in_slot.transient_label) {
48
+ in_slot.label = in_slot.transient_label
49
+ Logger.log("Restoring transient label")
50
+ } else if (in_slot.label && in_slot.label != i18n('anything')) {
51
+ // leave custom label alone
52
+ Logger.log("Leaving custom label")
53
+ } else if (app.ui.settings.getSettingValue("Use Everywhere.Options.use_output_name") && link_info) {
54
+ const out_slot = (link_info.origin_id==-10) ?
55
+ graph.inputNode?.allSlots[link_info.origin_slot] :
56
+ graph.getNodeById(link_info.origin_id)?.outputs[link_info.origin_slot]
57
+ in_slot.label = out_slot?.label || out_slot?.localized_name || out_slot?.name || i18n(type);
58
+ } else {
59
+ in_slot.label = i18n(type);
60
+ }
61
+ in_slot.color_on = app.canvas.default_connection_color_byType[type];
62
+ in_slot.type = type
63
+ }
64
+ } else {
65
+ in_slot.transient_label = in_slot.label
66
+ if (is_removable(node, in_slot, slot)) {
67
+ in_slot.label = i18n('anything');
68
+ in_slot.color_on = undefined;
69
+ in_slot.type = '*'
70
+ }
71
+ setTimeout(()=>{in_slot.transient_label=null}, 100)
72
+ }
73
+
74
+ }
75
+
76
+ function post_configure_fixes(node) {
77
+ if (is_combo_clone(node)) reset_comboclone_on_load(node)
78
+ if (is_UEnode(node)) fix_inputs(node, "post_configure_fixes")
79
+ }
80
+ ue_callbacks.register_allnode_callback('afterConfigureGraph', post_configure_fixes)
81
+
82
+ function add_new_input(node) {
83
+ Logger.log_info(`Adding new anything input to node ${node.id} (${fix_call_message})`)
84
+ try {
85
+ node.properties.ue_properties.next_input_index = (node.properties.ue_properties.next_input_index || 10) + 1
86
+ node.addInput(`anything${node.properties.ue_properties.next_input_index}`, "*", {label:i18n('anything')})
87
+ return true
88
+ } catch (e) { Logger.log_error(e) }
89
+ return false
90
+ }
91
+
92
+ function remove_excess_input(node) {
93
+ const idx = node.inputs.findIndex((inputslot, i)=>(is_removable(node,inputslot,i)))
94
+ if (idx>=0) {
95
+ try {
96
+ Logger.log_info(`Removing excess anything input from node ${node.id} (${fix_call_message})`)
97
+ node.removeInput(idx)
98
+ return true
99
+ } catch (e) { Logger.log_error(e) }
100
+ } else { Logger.log_problem(`Something very odd happened in fix_inputs for ${node.id}`) }
101
+ return false
102
+ }
103
+
104
+ var fix_call_message;
105
+
106
+ function to_keep(node, i) {
107
+ return (node.properties.ue_properties?.keep_inputs && node.properties.ue_properties?.keep_inputs.includes(i))
108
+ }
109
+
110
+ function is_removable(node, inputslot, i) {
111
+ return (inputslot.type=='*' && !to_keep(node,i))
112
+ }
113
+
114
+ function fix_unconnected_inputs(node) {
115
+ node.inputs.filter((input, i)=>(!input.link && !to_keep(node,i))).forEach((input)=>{
116
+ input.type = '*'
117
+ input.label = i18n('anything')
118
+ })
119
+ }
120
+
121
+ function fix_star_inputs(node) {
122
+ node.inputs.filter((input)=>(input.type=='*' && input.link)).forEach((input)=>{
123
+ const llink = node.graph.links[input.link]
124
+ if (llink.type) input.type = llink.type
125
+ })
126
+ }
127
+
128
+ export function fix_inputs(node, message) {
129
+ fix_call_message = message
130
+ if (!node.graph) return // node has been deleted prior to the fix
131
+ if (shared.graph_being_configured) return
132
+ if (node.properties.ue_properties.fixed_inputs) return
133
+
134
+ fix_unconnected_inputs(node)
135
+ fix_star_inputs(node)
136
+
137
+ const empty_removable_inputs = node.inputs.filter((inputslot, i)=>(is_removable(node, inputslot, i)))
138
+ var excess_inputs = empty_removable_inputs.length - 1
139
+
140
+ if (excess_inputs<0) {
141
+ if (add_new_input(node)) fix_inputs(node, message)
142
+ } else if (excess_inputs>0) {
143
+ if (remove_excess_input(node)) fix_inputs(node, message)
144
+ }
145
+
146
+ }
v3-nodes/cg-use-everywhere/js/floating_window.js ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { create } from "./use_everywhere_utilities.js"
2
+ import { settingsCache } from "./use_everywhere_cache.js"
3
+
4
+
5
+ export class FloatingWindow extends HTMLElement {
6
+ constructor() {
7
+ super()
8
+ this.classList.add('ue_editor')
9
+ this.header = create('div', 'ue_editor_header', this)
10
+ this.body = create('div', 'ue_editor_body', this)
11
+ this.footer = create('button', 'ue_editor_footer', this, { innerText: "Close" })
12
+
13
+ /** @type {HTMLElement | null} */
14
+ this.firstFocusable = null
15
+ /** @type {HTMLElement | null} */
16
+ this.lastFocusable = null
17
+
18
+ this.header.addEventListener('mousedown',this.start_dragging.bind(this))
19
+ this.addEventListener('mousemove', this.mousemovedover.bind(this))
20
+ document.addEventListener('mousemove', this.mousemoved.bind(this))
21
+ document.addEventListener('mouseup',this.stop_dragging.bind(this))
22
+ document.addEventListener('mouseleave',this.stop_dragging.bind(this))
23
+ this.footer.addEventListener('click', this.hide.bind(this))
24
+ this.addEventListener("keydown", this.handle_keydown.bind(this));
25
+
26
+ this.dragging = false
27
+ this.hide()
28
+ document.body.append(this)
29
+ }
30
+
31
+ show() {
32
+ this.style.display = 'flex';
33
+ const tt = document.getElementById('ue_tooltip')
34
+ if (tt) tt.style.display = 'none'
35
+ this.showing = true
36
+ }
37
+ hide() {
38
+ this.style.display = 'none'
39
+ this.showing = false
40
+ }
41
+ set_title(title) {
42
+ this.header.innerText = title
43
+ }
44
+ set_body(element) {
45
+ this.body.innerHTML = ""
46
+ this.body.appendChild(element)
47
+ this.setup_focus_trap()
48
+ }
49
+
50
+ maybe_move_to(x,y) {
51
+ if (this.position && settingsCache.getSettingValue("Use Everywhere.Graphics.preserve edit window position")) return
52
+ this.move_to(x,y)
53
+ }
54
+
55
+ move_to(x,y) {
56
+ this.position = {x:x,y:y}
57
+ this.style.left = `${this.position.x}px`
58
+ this.style.top = `${this.position.y}px`
59
+ }
60
+
61
+ swallow(e) {
62
+ e.stopPropagation()
63
+ e.preventDefault()
64
+ }
65
+
66
+ start_dragging(e) {
67
+ this.dragging = true
68
+ this.swallow(e)
69
+ }
70
+
71
+ stop_dragging(e) {
72
+ this.dragging = false
73
+ }
74
+
75
+ mousemovedover(e) {
76
+ this.mousemoved(e)
77
+ this.swallow(e)
78
+ }
79
+
80
+ mousemoved(e) {
81
+ if (this.dragging) this.move_to( this.position.x + e.movementX , this.position.y + e.movementY )
82
+ }
83
+
84
+ /** @param {KeyboardEvent} e */
85
+ handle_keydown(e) {
86
+ if (e.key === "Escape" || e.key === "Enter") {
87
+ this.hide()
88
+ return
89
+ }
90
+ if (e.key === "Tab") {
91
+ if (e.shiftKey && document.activeElement === this.firstFocusable) {
92
+ this.lastFocusable.focus()
93
+ e.preventDefault()
94
+ return
95
+ }
96
+ if (!e.shiftKey && document.activeElement === this.lastFocusable) {
97
+ this.firstFocusable.focus()
98
+ e.preventDefault()
99
+ }
100
+ }
101
+ }
102
+
103
+ setup_focus_trap() {
104
+ this.firstFocusable = null
105
+ this.lastFocusable = null
106
+
107
+ const focusableEls = this.querySelectorAll('a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled])')
108
+ if (focusableEls?.length == 0) return
109
+
110
+ this.firstFocusable = focusableEls[0] ?? null
111
+ this.lastFocusable = focusableEls[focusableEls.length - 1] ?? null
112
+
113
+ if (this.firstFocusable) {
114
+ setTimeout(() => {
115
+ this.firstFocusable.focus()
116
+ }, 0);
117
+ }
118
+ }
119
+ }
120
+
121
+
122
+ customElements.define('ue-floating', FloatingWindow)
123
+ export const edit_window = new FloatingWindow()
v3-nodes/cg-use-everywhere/js/i18n.js ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+
3
+ export var REPEATED_TYPE_OPTIONS;
4
+ export var GROUP_RESTRICTION_OPTIONS;
5
+ export var COLOR_RESTRICTION_OPTIONS;
6
+
7
+
8
+ var _FUNCTIONAL = null
9
+ var _FUNCTIONAL_REGEX = null
10
+
11
+ const _DISPLAY = {
12
+ 'en' : {
13
+
14
+ },
15
+ 'zn' : {
16
+
17
+ }
18
+ }
19
+
20
+ function DISPLAY(v, lang) { return _DISPLAY[lang || current_language()]?.[v] || _DISPLAY['en'][v] || v }
21
+
22
+ function current_language() {
23
+ return app.ui.settings.getSettingValue('Comfy.Locale')
24
+ }
25
+
26
+ export function language_changed(is_now, was_before) {
27
+ _FUNCTIONAL = null
28
+ _FUNCTIONAL_REGEX = null
29
+
30
+ REPEATED_TYPE_OPTIONS = [
31
+ i18n("Exact match of input names"),
32
+ i18n("Match start of input names"),
33
+ i18n("Match end of input names"),
34
+ i18n("Inputs matches target node name"),
35
+ ]
36
+
37
+ GROUP_RESTRICTION_OPTIONS = [
38
+ i18n("No restrictions"),
39
+ i18n("Send only within group"),
40
+ i18n("Send only not within group")
41
+ ]
42
+
43
+ COLOR_RESTRICTION_OPTIONS = [
44
+ i18n("No restrictions"),
45
+ i18n("Send only to same color"),
46
+ i18n("Send only to different color")
47
+ ]
48
+ }
49
+
50
+ function get_functional() {
51
+ const nd = LiteGraph.createNode('KSampler')
52
+ if (nd) {
53
+ _FUNCTIONAL = {
54
+ seed_input_regex : `seed|${nd.inputs.find((i)=>(i.name=='seed')).localized_name}`,
55
+ prompt_regex : `(_|\\b)pos(itive|_|\\b)|${nd.inputs.find((i)=>(i.name=='positive')).localized_name}`,
56
+ negative_regex : `(_|\\b)neg(ative|_|\\b)|${nd.inputs.find((i)=>(i.name=='negative')).localized_name}`,
57
+ seed : nd.inputs.find((i)=>(i.name=='seed')).localized_name,
58
+ positive : nd.inputs.find((i)=>(i.name=='positive')).localized_name,
59
+ negative : nd.inputs.find((i)=>(i.name=='negative')).localized_name,
60
+
61
+ }
62
+ }
63
+ }
64
+
65
+ function get_functional_regex() {
66
+ if (!_FUNCTIONAL) get_functional()
67
+ _FUNCTIONAL_REGEX = {
68
+ prompt_regex : new RegExp(_FUNCTIONAL.prompt_regex),
69
+ negative_regex : new RegExp(_FUNCTIONAL.negative_regex),
70
+ }
71
+ }
72
+
73
+ export function i18n_functional(v) {
74
+ if (!_FUNCTIONAL) get_functional()
75
+ return _FUNCTIONAL?.[v] || v
76
+ }
77
+
78
+ export function i18n_functional_regex(v) {
79
+ if (!_FUNCTIONAL_REGEX) get_functional_regex()
80
+ return _FUNCTIONAL_REGEX[v]
81
+ }
82
+
83
+ const all_requested_stings = new Set()
84
+ export function i18n(v, extras) {
85
+ all_requested_stings.add(v)
86
+ var r = DISPLAY(v, extras?.language) || LANGUAGES[v] || v
87
+ if (extras?.titlecase) r = toTitleCase(r)
88
+ return r
89
+ }
90
+
91
+ export function i18ify_settings(settings) {
92
+ settings.forEach((s)=>{
93
+ if (s.name) s.name = i18n(s.name)
94
+ if (s.tooltip) s.tooltip = i18n(s.tooltip)
95
+ if (s.options) s.options.forEach((o) => {o.text = i18n(o.text)})
96
+ })
97
+ return settings
98
+ }
99
+
100
+ const toTitleCase = (phrase) => {
101
+ return phrase
102
+ .toLowerCase()
103
+ .split(' ')
104
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
105
+ .join(' ');
106
+ };
v3-nodes/cg-use-everywhere/js/recursive_callbacks.js ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+
3
+ class RecursiveCallbacks {
4
+ constructor() {
5
+ this.event_to_node_callbacklist = {}
6
+ this.event_to_graph_callbacklist = {}
7
+ }
8
+
9
+ /*
10
+ Register a callback for a named event.
11
+ */
12
+ register_allnode_callback(event, call, first) {
13
+ if (!this.event_to_node_callbacklist[event]) this.event_to_node_callbacklist[event] = []
14
+ if (first) this.event_to_node_callbacklist[event].unshift(call)
15
+ else this.event_to_node_callbacklist[event].push(call)
16
+ }
17
+ register_allgraph_callback(event, call, first) {
18
+ if (!this.event_to_graph_callbacklist[event]) this.event_to_graph_callbacklist[event] = []
19
+ if (first) this.event_to_graph_callbacklist[event].push(call)
20
+ else this.event_to_graph_callbacklist[event].push(call)
21
+ }
22
+
23
+
24
+
25
+ /*
26
+ For every callback registered to this event,
27
+ call allnode callback once for each node, with the node as argument
28
+ call allgraph callback once for each node, with the graph as argument
29
+ */
30
+ dispatch(event) {
31
+ if (this.event_to_node_callbacklist[event]) {
32
+ this.event_to_node_callbacklist[event].forEach((callback) => {
33
+ _node_dispatch(callback, app.graph)
34
+ });
35
+ }
36
+ if (this.event_to_graph_callbacklist[event]) {
37
+ this.event_to_graph_callbacklist[event].forEach((callback) => {
38
+ _graph_dispatch(callback, app.graph)
39
+ });
40
+ }
41
+ }
42
+ }
43
+
44
+ function _node_dispatch(callback, graph) {
45
+ graph.nodes.forEach((node) => {
46
+ callback(node)
47
+ if (node.subgraph) { _node_dispatch(callback, node.subgraph) }
48
+ })
49
+ }
50
+
51
+ function _graph_dispatch(callback, graph) {
52
+ callback(graph)
53
+ graph.nodes.filter((node)=>(node.subgraph)).forEach((node) => { _graph_dispatch(callback, node.subgraph) })
54
+ }
55
+
56
+ export const ue_callbacks = new RecursiveCallbacks()
57
+
58
+ export function for_all_graphs(callback) {
59
+ _graph_dispatch(callback, app.graph)
60
+ }
61
+
62
+ export function for_all_nodes(callback) {
63
+ _node_dispatch(callback, app.graph)
64
+ }
v3-nodes/cg-use-everywhere/js/shared.js ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const VERSION = "7.7"
2
+
3
+ class Shared {
4
+ constructor() {
5
+ this.graph_being_configured = 0
6
+ this.in_queuePrompt = 0
7
+ this.in_graphToPrompt = 0
8
+ this.in_midst_of_change = 0
9
+ this.graph_currently_modified = 0
10
+ this.graphAnalyser = undefined
11
+ this.linkRenderController = undefined
12
+ this.report_keys = [ 'graph_being_configured', 'in_queuePrompt', 'in_graphToPrompt', 'in_midst_of_change', 'graph_currently_modified' ]
13
+ }
14
+ }
15
+
16
+ class Deferred {
17
+ constructor() {
18
+ this.action_list = []
19
+ setInterval( this.execute.bind(this), 200 )
20
+ }
21
+ push(x) { this.action_list.push(x) } // add action of the form: { fn:function, args:array }
22
+ execute() {
23
+ if (shared.in_midst_of_change) return;
24
+ if (shared.graph_being_configured) return;
25
+ while (this.action_list.length>0) {
26
+ const action = this.action_list.pop()
27
+ try { action?.fn(...action?.args) }
28
+ catch (e) { Logger.log_error(e) }
29
+ }
30
+ }
31
+ }
32
+
33
+ export const deferred_actions = new Deferred()
34
+
35
+ export const shared = new Shared()
36
+
v3-nodes/cg-use-everywhere/js/tooltip_window.js ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import { any_restrictions, describe_restrictions } from "./ue_properties.js";
3
+ import { app } from "../../scripts/app.js";
4
+ import { create } from "./use_everywhere_utilities.js";
5
+ import { edit_window } from "./floating_window.js";
6
+ import { settingsCache } from "./use_everywhere_cache.js";
7
+
8
+ const HOVERTIME = 500
9
+ var hover_node_id = null
10
+ var mouse_pos = [0,0]
11
+
12
+ const ue_tooltip_element = create('span', 'ue_tooltip', document.body, {id:'ue_tooltip'})
13
+
14
+ function show_tooltip() {
15
+ ue_tooltip_element.style.display = "block"
16
+ ue_tooltip_element.style.left = `${mouse_pos[0]+10}px`
17
+ ue_tooltip_element.style.top = `${mouse_pos[1]+5}px`
18
+ ue_tooltip_element.innerHTML = ""
19
+ ue_tooltip_element.appendChild(describe_restrictions(app.canvas.node_over))
20
+ ue_tooltip_element.showing = true
21
+ }
22
+
23
+ function show_on_hover() {
24
+ if (!tooltipable()) return
25
+
26
+ if (mouse_pos[0]==app.canvas.mouse[0] && mouse_pos[1]==app.canvas.mouse[1]) {
27
+ show_tooltip()
28
+ } else {
29
+ maybe_show_tooltip()
30
+ }
31
+ }
32
+
33
+ function hide_tooltip() {
34
+ ue_tooltip_element.style.display = "none"
35
+ ue_tooltip_element.showing = false
36
+ }
37
+
38
+ function tooltipable() {
39
+ if (
40
+ (!app.canvas?.node_over) ||
41
+ ( edit_window.showing) ||
42
+ (!settingsCache.getSettingValue('Use Everywhere.Graphics.tooltips')) ||
43
+ (!any_restrictions(app.canvas.node_over))
44
+ ) return false
45
+
46
+ return true
47
+ }
48
+
49
+ export function maybe_show_tooltip() {
50
+ if (!tooltipable()) return hide_tooltip()
51
+
52
+ mouse_pos = [...app.canvas.mouse]
53
+ hover_node_id = app.canvas.node_over.id
54
+ setTimeout(show_on_hover, HOVERTIME)
55
+ }
v3-nodes/cg-use-everywhere/js/ue.css ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @layer ue {
2
+ #ue_tooltip {
3
+ z-index : 9000;
4
+ border: thin solid gray;
5
+ color:black;
6
+ position: absolute;
7
+ padding: 4px;
8
+ }
9
+ #ue_tooltip table {
10
+ border-collapse: collapse;
11
+
12
+ }
13
+ #ue_tooltip tr:nth-child(odd) {
14
+ background-color: rgba(234, 255, 234, 0.67);
15
+ }
16
+ #ue_tooltip tr:nth-child(even) {
17
+ background-color: rgba(255, 234, 234, 0.67);
18
+ }
19
+ #ue_tooltip th {
20
+ text-align: right;
21
+ }
22
+
23
+ .ue_editor {
24
+ z-index : 9000;
25
+ border: thin solid gray;
26
+ color:white;
27
+ position: absolute;
28
+ padding: 0px;
29
+ display: flex;
30
+ flex-direction: column;
31
+ align-items: center;
32
+ background-color: rgb(45 24 24 / 80%);
33
+ }
34
+
35
+ .ue_properties_table {
36
+ display: flex;
37
+ flex-direction: column;
38
+ }
39
+
40
+ .ue_editor_header {
41
+ width: 100%;
42
+ text-align: center;
43
+ background-color: rgb(45 24 24 / 80%);
44
+ background-size: 100% 14px;
45
+ background-image: repeating-linear-gradient(0deg, #636363, black 1px, rgb(45 24 24 / 80%) 1px, rgb(45 24 24 / 80%) 2px);
46
+ padding-top: 4px;
47
+ border-bottom: thin dashed #ddd;
48
+ padding-bottom: 2px;
49
+ margin-bottom: 2px;
50
+ }
51
+
52
+ .ue_properties_row {
53
+ display: flex;
54
+ flex-direction: row;
55
+ }
56
+
57
+ .break_below {
58
+ border-bottom: thin dashed #ddd;
59
+ padding-bottom: 2px;
60
+ }
61
+
62
+ span.ue_properties_title {
63
+ text-align: right;
64
+ flex-grow: 1;
65
+ padding-right: 4px;
66
+ }
67
+
68
+ .ue_properties_title::after {
69
+ content: ':';
70
+ }
71
+
72
+ .ue_properties_cell {
73
+ flex-grow: 0;
74
+ width: 250px
75
+ }
76
+
77
+
78
+ .ue_editor_footer {
79
+ margin: 8px;
80
+ border: thin solid white;
81
+ padding: 2px 30px;
82
+ background-color: rgba(0, 0, 0, 0.75);
83
+ }
84
+
85
+ .regex_input_container {
86
+ display: flex;
87
+ width: 100%;
88
+ justify-content: space-between;
89
+ input {
90
+ flex-grow: 1;
91
+ }
92
+ span {
93
+ padding: 4px 8px 0px 0px;
94
+ font-size: 60%;
95
+ }
96
+ .checkbox {
97
+ flex-grow: 0;
98
+ margin-left: 0px;
99
+ }
100
+ }
101
+
102
+ }
v3-nodes/cg-use-everywhere/js/ue_properties.js ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { version_at_least, create, is_UEnode, find_duplicate_broadcasted_types } from "./use_everywhere_utilities.js"
2
+ import { i18n, i18n_functional, GROUP_RESTRICTION_OPTIONS, COLOR_RESTRICTION_OPTIONS } from "./i18n.js";
3
+ import { shared } from "./shared.js";
4
+ import { fix_inputs } from "./connections.js";
5
+ import { VERSION } from "./shared.js";
6
+
7
+ const ALL_REGEXES = ['title', 'input', 'prompt', 'negative', 'group']
8
+
9
+ function any_regex_restrictions(node) {
10
+ if (!node.properties.ue_properties) return false
11
+ var restricted = false
12
+ ALL_REGEXES.forEach((r)=>{
13
+ const reg = node.properties.ue_properties[`${r}_regex`]
14
+ restricted = restricted || (reg && reg.length>0)
15
+ })
16
+ return restricted
17
+ }
18
+
19
+ export function any_restrictions(node) {
20
+ return (
21
+ node.properties.ue_properties?.group_restricted ||
22
+ node.properties.ue_properties?.color_restricted ||
23
+ node.properties.ue_properties?.priority ||
24
+ any_regex_restrictions(node)
25
+ )
26
+ }
27
+
28
+ export function describe_restrictions(node) {
29
+ const statements = []
30
+ if (node.properties.ue_properties) {
31
+ ALL_REGEXES.forEach((r)=>{
32
+ var reg = node.properties.ue_properties[`${r}_regex`]
33
+ if (reg && reg.length>0) {
34
+ const condition = i18n((node.properties.ue_properties[`${r}_regex_invert`]) ? i18n("not match"): i18n("match"))
35
+ statements.push([`${i18n(r)} regex`, `${condition} ${reg}`])
36
+ }
37
+ })
38
+ if (node.properties.ue_properties.group_restricted) statements.push([i18n('group'),i18n(GROUP_RESTRICTION_OPTIONS[node.properties.ue_properties.group_restricted])])
39
+ if (node.properties.ue_properties.color_restricted) statements.push([i18n('color'),i18n(COLOR_RESTRICTION_OPTIONS[node.properties.ue_properties.color_restricted])])
40
+ }
41
+ if (node.properties.ue_properties.priority !== undefined) statements.push([i18n('priority'), node.properties.ue_properties.priority])
42
+ const table = create('table')
43
+ statements.forEach((s)=>{
44
+ const row = create('tr', null, table)
45
+ create('th', null, row, {innerText:`${i18n(s[0], {titlecase:true})}:`})
46
+ create('td', null, row, {innerText:s[1]})
47
+ })
48
+ return table
49
+ }
50
+
51
+ export function default_priority(node) {
52
+ var p = 10
53
+ if (node.type === "Seed Everywhere" || node.type === "Prompts Everywhere") p += 10
54
+ if (any_regex_restrictions(node) || find_duplicate_broadcasted_types(node).size) p += 20
55
+ if (node.properties.ue_properties.group_restricted > 0) p += 3
56
+ if (node.properties.ue_properties.color_restricted > 0) p += 6
57
+ return p
58
+ }
59
+
60
+ const DEFAULT_PROPERTIES = {
61
+ version : VERSION,
62
+ group_restricted : 0,
63
+ color_restricted : 0,
64
+ widget_ue_connectable : {},
65
+ input_ue_unconnectable : {},
66
+ title_regex : null,
67
+ input_regex : null,
68
+ group_regex : null,
69
+ title_regex_invert : false,
70
+ input_regex_invert : false,
71
+ group_regex_invert : false,
72
+ priority : undefined,
73
+ repeated_type_rule : 0,
74
+ apply_to_unrepeated : 0,
75
+ string_to_combo : 0,
76
+ send_to_any : 0
77
+ }
78
+
79
+ /*
80
+ Called whenever any node is created.
81
+
82
+ If the graph is still being configured, then that means the node is being created as part of a load.
83
+ In that case the properties need to be set later, in setup_ue_properties_onload
84
+ */
85
+ export function setup_ue_properties_oncreate(node) {
86
+ if (shared.graph_being_configured) return
87
+ if (!node.properties) node.properties = {}
88
+ node.properties.ue_properties = {...DEFAULT_PROPERTIES}
89
+ convert_node_types(node)
90
+ }
91
+
92
+ /*
93
+ Convert node properties when loaded.
94
+ */
95
+ export function setup_ue_properties_onload(node) {
96
+ if (!node.properties?.ue_properties) node.properties.ue_properties = {}
97
+ if ( !version_at_least(node.properties?.ue_properties?.version, "7.0") ) {
98
+ if (is_UEnode(node)) {
99
+ // convert a pre 7.0 UE node
100
+ node.properties.ue_properties = {
101
+ version : VERSION,
102
+ group_restricted : node.properties.group_restricted,
103
+ color_restricted : node.properties.color_restricted,
104
+ widget_ue_connectable : node.properties.widget_ue_connectable,
105
+ input_ue_unconnectable: {},
106
+ title_regex : node.widgets_values?.[0],
107
+ input_regex : node.widgets_values?.[1],
108
+ group_regex : node.widgets_values?.[2],
109
+ priority : undefined,
110
+ }
111
+ delete node.properties.group_restricted
112
+ delete node.properties.color_restricted
113
+ delete node.properties.widget_ue_connectable
114
+ } else {
115
+ node.properties.ue_properties.version = VERSION
116
+ node.properties.ue_properties.widget_ue_connectable = node.properties.ue_properties.widget_ue_connectable ||
117
+ node.properties.widget_ue_connectable ||
118
+ {}
119
+ node.properties.ue_properties.input_ue_unconnectable = {}
120
+ if (node.properties.widget_ue_connectable) delete node.properties.widget_ue_connectable
121
+ }
122
+ }
123
+ convert_node_types(node)
124
+
125
+ }
126
+
127
+ function convert_node_types(node) {
128
+ if (!is_UEnode(node)) return
129
+
130
+ if (node.type=="Anything Everywhere?") {
131
+ node.widgets.forEach((w)=>{w.hidden=true})
132
+ if (node.title=="Anything Everywhere?") node.title = "Anything Everywhere"
133
+ node.type = "Anything Everywhere"
134
+ } else if (node.type=="Anything Everywhere3") {
135
+ if (node.title=="Anything Everywhere3") node.title = "Anything Everywhere"
136
+ node.type = "Anything Everywhere"
137
+ } else if (node.type=="Seed Everywhere") {
138
+ if (node.title=="Anything Everywhere3") node.title = "PrimitiveInt"
139
+ node.type = "PrimitiveInt"
140
+ node.properties.ue_convert = true
141
+ node.properties.ue_properties.fixed_inputs = true
142
+ node.properties.ue_properties.seed_inputs = true
143
+ node.properties.ue_properties.input_regex = node.properties.ue_properties.input_regex || i18n_functional('seed_input_regex')
144
+ } else if (node.type=="Prompts Everywhere") {
145
+ if (node.title=="Prompts Everywhere") node.title = "Anything Everywhere"
146
+ node.properties.ue_properties.fixed_inputs = false
147
+ node.inputs[0].label = i18n_functional('positive')
148
+ node.inputs[1].label = i18n_functional('negative')
149
+ node.properties.ue_properties['keep_inputs'] = [0,1]
150
+ }
151
+
152
+ ALL_REGEXES.forEach((r)=>{
153
+ const rname = `${r}_regex`
154
+ if (node.properties.ue_properties[rname]==".*") node.properties.ue_properties[rname] = undefined
155
+ })
156
+
157
+ fix_inputs(node, "convert_node_types")
158
+ }
v3-nodes/cg-use-everywhere/js/ue_properties_editor.js ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { i18n, i18n_functional, GROUP_RESTRICTION_OPTIONS, COLOR_RESTRICTION_OPTIONS, REPEATED_TYPE_OPTIONS } from "./i18n.js";
2
+ import { app } from "../../scripts/app.js";
3
+ import { default_priority } from "./ue_properties.js";
4
+ import { edit_window } from "./floating_window.js";
5
+ import { shared } from "./shared.js";
6
+ import { Logger } from "./use_everywhere_utilities.js";
7
+
8
+ const REGEXES = ['title', 'input', 'group']
9
+ const P_REGEXES = ['prompt', 'negative']
10
+
11
+ function create_element(tag, parent, options={}) {
12
+ const elem = document.createElement(tag)
13
+ if (parent) parent.appendChild(elem)
14
+ Object.assign(elem, options)
15
+ return elem
16
+ }
17
+
18
+ export function edit_restrictions(a,b,c,d, node) { // a,b,c,d parameters are ignored - called from menu system
19
+ edit_window.set_body(create_editor_html(node))
20
+ edit_window.set_title(`Restrictions for node #${node.id}`)
21
+ edit_window.maybe_move_to(app.canvas.mouse[0]+10, app.canvas.mouse[1]+10)
22
+ edit_window.show()
23
+ }
24
+
25
+ function add_row(table, header) {
26
+ const row = document.createElement('div')
27
+ row.className = 'ue_properties_row'
28
+ table.appendChild(row)
29
+ const header_elem = document.createElement('span')
30
+ header_elem.className = 'ue_properties_title'
31
+ header_elem.innerText = header
32
+ row.appendChild(header_elem)
33
+ return row
34
+ }
35
+
36
+ function add_cell(row, cell) {
37
+ const td = document.createElement('span')
38
+ td.className = 'ue_properties_cell'
39
+ row.appendChild(td)
40
+ td.appendChild(cell)
41
+ }
42
+
43
+ function changed(node, property, value) {
44
+ node.properties.ue_properties[property] = value
45
+
46
+ if (!node.properties.ue_properties.priority) {
47
+ document.getElementById('priority_value').value = `${default_priority(node)}`
48
+ }
49
+
50
+ const elem = document.getElementById(`${property}_value`)
51
+ if (elem) elem.style.opacity = (value) ? "1" : "0.5"
52
+
53
+ shared.linkRenderController.mark_link_list_outdated()
54
+ app.canvas.setDirty(true,true)
55
+ }
56
+
57
+ function create_editor_html(node) {
58
+ const table = document.createElement('div')
59
+ table.className = 'ue_properties_table'
60
+
61
+ for (var i=0; i<=2; i++) {
62
+ const name = REGEXES[i]
63
+ const row = add_row(table, `${i18n(name)} regex`)
64
+
65
+ const contents = create_element('span', null, {'className':'regex_input_container'})
66
+
67
+ const checkbox_props = {
68
+ type:'checkbox',
69
+ id:`${name}_regex_invert`,
70
+ checked: (node.properties.ue_properties[`${name}_regex_invert`]) ? true : undefined,
71
+ className: 'checkbox'
72
+ }
73
+ create_element('input', contents, checkbox_props).
74
+ addEventListener('input', (e)=>{ changed(node, `${name}_regex_invert`, e.target.checked); } )
75
+
76
+ create_element('span', contents, {innerText:i18n('Invert'), className:'regex_checkbox_label'})
77
+
78
+ create_element('input', contents, {type:'text', value:node.properties.ue_properties[`${name}_regex`] || ''}).
79
+ addEventListener('input', (e)=>{ changed(node, `${name}_regex`, e.target.value)})
80
+
81
+ if (i==2) { row.classList.add('break_below') }
82
+
83
+ add_cell(row,contents)
84
+ }
85
+
86
+ const gr_row = add_row(table, i18n("Group"))
87
+ const gr_select = document.createElement('select')
88
+ add_cell(gr_row,gr_select)
89
+ add_select_options(node, gr_select, GROUP_RESTRICTION_OPTIONS, `group_restricted`)
90
+
91
+ const col_row = add_row(table, i18n("Color"))
92
+ const col_select = document.createElement('select')
93
+ add_cell(col_row,col_select)
94
+ add_select_options(node, col_select, COLOR_RESTRICTION_OPTIONS, `color_restricted` )
95
+ col_row.classList.add('break_below')
96
+
97
+ const repeated_type_row = add_row(table, i18n("Repeated Types"))
98
+ const repeated_type_select = document.createElement('select')
99
+ add_cell(repeated_type_row,repeated_type_select)
100
+ add_select_options(node, repeated_type_select, REPEATED_TYPE_OPTIONS, `repeated_type_rule`)
101
+
102
+ const apply_to_unrepeated_row = add_row(table, i18n("Apply to Unrepeated"))
103
+ const apply_to_unrepeated_select = document.createElement('select')
104
+ add_cell(apply_to_unrepeated_row, apply_to_unrepeated_select)
105
+ add_select_options(node, apply_to_unrepeated_select, ["no", "yes"], `apply_to_unrepeated`)
106
+ apply_to_unrepeated_row.classList.add('break_below')
107
+
108
+ if (node.inputs.find((i)=>(i.type=="STRING"))) {
109
+ const send_to_combos_row = add_row(table, i18n("String to Combos"))
110
+ const send_to_combos_select = document.createElement('select')
111
+ add_cell(send_to_combos_row, send_to_combos_select)
112
+ add_select_options(node, send_to_combos_select, ["no", "yes"], `string_to_combo`)
113
+ send_to_combos_row.classList.add('break_below')
114
+ }
115
+
116
+ const send_to_any_row = add_row(table, i18n("Send to Any"))
117
+ const send_to_any_select = document.createElement('select')
118
+ add_cell(send_to_any_row, send_to_any_select)
119
+ add_select_options(node, send_to_any_select, ["no", "yes"], `send_to_any`)
120
+ send_to_any_row.classList.add('break_below')
121
+
122
+ const priority_row = add_row(table, i18n("Priority"))
123
+ const priority_edit = document.createElement("input")
124
+ priority_edit.value = `${node.properties.ue_properties.priority || default_priority(node)}`
125
+ priority_edit.addEventListener('input', ()=>{
126
+ const p = parseInt(priority_edit.value)
127
+ if (p) changed(node, `priority`, p)
128
+ if (priority_edit.value=='') changed(node, `priority`, undefined)
129
+ })
130
+ priority_edit.id = 'priority_value'
131
+ if (!node.properties.ue_properties.priority) priority_edit.style.opacity = 0.5
132
+ add_cell(priority_row,priority_edit)
133
+ priority_row.classList.add('break_below')
134
+
135
+ return table
136
+ }
137
+
138
+ function add_select_options(node, select, OPTIONS, property) {
139
+ OPTIONS.forEach((txt, i)=>{
140
+ const option = document.createElement('option')
141
+ option.value = `${i}`
142
+ option.innerText = txt
143
+ select.appendChild(option)
144
+ })
145
+ select.value = `${node.properties.ue_properties[property] || 0}`
146
+ select.addEventListener('input', ()=>{ changed(node, property, parseInt(select.value))})
147
+ }
v3-nodes/cg-use-everywhere/js/use_everywhere.js ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+ import { api } from "../../scripts/api.js";
3
+
4
+ import { shared, deferred_actions } from "./shared.js";
5
+
6
+ import { is_UEnode, inject, Logger, graphConverter, create, node_can_broadcast } from "./use_everywhere_utilities.js";
7
+ import { title_bar_additions, LinkRenderController } from "./use_everywhere_ui.js";
8
+ import { GraphAnalyser } from "./use_everywhere_graph_analysis.js";
9
+ import { canvas_menu_settings, SETTINGS, add_extra_menu_items } from "./use_everywhere_settings.js";
10
+ import { settingsCache } from "./use_everywhere_cache.js";
11
+ import { convert_to_links } from "./use_everywhere_apply.js";
12
+ import { visible_graph, fix_new_subgraph_node } from "./use_everywhere_subgraph_utils.js";
13
+ import { setup_ue_properties_oncreate, setup_ue_properties_onload } from "./ue_properties.js";
14
+ import { edit_restrictions } from "./ue_properties_editor.js";
15
+ import { language_changed } from "./i18n.js";
16
+ import { input_changed, fix_inputs } from "./connections.js";
17
+ import { comboclone_on_connection, is_combo_clone } from "./combo_clone.js";
18
+ import { ue_callbacks } from "./recursive_callbacks.js";
19
+
20
+ /*
21
+ All nodes need the onDrawTitleBar method so they can show if they are broadcasting UE data.
22
+ */
23
+ function add_methods_to_all_nodes(node) {
24
+ if (node.ue_methods_added) return Logger.log_problem(`Node ${node.id} already has UE methods added`);
25
+
26
+ try {
27
+ add_extra_menu_items(node, inject_outdating_into_object_method) // right click menu additions
28
+
29
+ const original_onDrawTitleBar = node.onDrawTitleBar;
30
+ node.onDrawTitleBar = function(ctx, title_height) {
31
+ original_onDrawTitleBar?.apply(this, arguments);
32
+ title_bar_additions(node, ctx, title_height)
33
+ }
34
+
35
+ const original_onMouseEnter = node.onMouseEnter;
36
+ node.onMouseEnter = function(e) {
37
+ original_onMouseEnter?.apply(this, arguments)
38
+ shared.linkRenderController.node_over_changed()
39
+ }
40
+
41
+ const original_onMouseLeave = node.onMouseLeave;
42
+ node.onMouseLeave = function(e) {
43
+ original_onMouseLeave?.apply(this, arguments)
44
+ shared.linkRenderController.node_over_changed()
45
+ }
46
+
47
+ node.ue_methods_added = true;
48
+ } catch (e) {
49
+ Logger.log_error(e);
50
+ }
51
+
52
+ }
53
+
54
+ /*
55
+ Inject a call to linkRenderController.mark_list_link_outdated into a method with name methodname on all objects in the array
56
+ If object is undefined, do nothing.
57
+ The injection is added at the end of the existing method (if the method didn't exist, it is created).
58
+ */
59
+ function inject_outdating_into_objects(array, methodname, tracetext) {
60
+ if (array) {
61
+ array.forEach((object) => { inject_outdating_into_object_method(object, methodname, tracetext); })
62
+ }
63
+ }
64
+ function inject_outdating_into_object_method(object, methodname, tracetext) {
65
+ if (object) inject(object, methodname, tracetext, shared.linkRenderController.mark_link_list_outdated, shared.linkRenderController);
66
+ }
67
+
68
+ app.registerExtension({
69
+ name: "cg.customnodes.use_everywhere",
70
+ settings: SETTINGS,
71
+
72
+ async beforeRegisterNodeDef(nodeType, nodeData, app) {
73
+ /*
74
+ When a node is connected or unconnected, the link list is dirty.
75
+ If it is a UE node, we need to update it as well
76
+ */
77
+ const onConnectionsChange = nodeType.prototype.onConnectionsChange;
78
+ nodeType.prototype.onConnectionsChange = function (side,slot,connect,link_info,output) {
79
+ if (is_combo_clone(this) && !shared.in_queuePrompt) comboclone_on_connection(this, link_info, connect)
80
+ if (is_UEnode(this) && side==1) { // side 1 is input
81
+ input_changed(this, slot, connect, link_info)
82
+
83
+ if (!shared.graph_being_configured) {
84
+ // do the fix at the end of graph change
85
+ deferred_actions.push( { fn:fix_inputs, args:[this,"deferred onConnectionsChange",]} )
86
+ }
87
+ }
88
+ shared.linkRenderController?.mark_link_list_outdated();
89
+ onConnectionsChange?.apply(this, arguments);
90
+ };
91
+
92
+ /* Combo Clone can connect to COMBO or to UE nodes */
93
+ if (nodeData.name=="Combo Clone") {
94
+ const onConnectOutput = nodeType.prototype.onConnectOutput
95
+ nodeType.prototype.onConnectOutput = function(outputIndex, type, input, inputNode, inputIndex) {
96
+ if (!(type=="COMBO" || is_UEnode(inputNode))) return false;
97
+ return onConnectOutput?.apply(this,arguments)
98
+ }
99
+ }
100
+
101
+ },
102
+
103
+ async nodeCreated(node) { // the node isn't part of the graph yet, so can't do anything involving id or links.
104
+ add_methods_to_all_nodes(node)
105
+
106
+ if (shared.graph_being_configured) return
107
+
108
+ if (is_UEnode(node)) {
109
+ setup_ue_properties_oncreate(node)
110
+ } else if (!node.properties.ue_properties) {
111
+ node.properties.ue_properties = {
112
+ widget_ue_connectable : {},
113
+ input_ue_unconnectable : {}
114
+ }
115
+ }
116
+ },
117
+
118
+ // When a graph node is loaded convert it if needed
119
+ loadedGraphNode(node) {
120
+ graphConverter.convert_if_pre_116(node);
121
+ setup_ue_properties_onload(node)
122
+ },
123
+
124
+ async setup() {
125
+
126
+ create('link', null, document.getElementsByTagName('HEAD')[0],
127
+ {'rel':'stylesheet', 'type':'text/css', 'href': new URL("./ue.css", import.meta.url).href } )
128
+
129
+ api.addEventListener("status", ({detail}) => {
130
+ if (shared.linkRenderController) shared.linkRenderController.note_queue_size(detail ? detail.exec_info.queue_remaining : 0)
131
+ });
132
+
133
+ /* if we are on version 1.16 or later, stash input data to convert nodes when they are loaded */
134
+ //if (graphConverter.running_116_plus()) {
135
+ const original_loadGraphData = app.loadGraphData;
136
+ app.loadGraphData = async function (data) {
137
+ try {
138
+ graphConverter.store_node_input_map(data);
139
+ } catch (e) { Logger.log_error(e); }
140
+ const cvw_was = settingsCache.getSettingValue("Comfy.Validation.Workflows")
141
+ if (settingsCache.getSettingValue("Use Everywhere.Options.block_graph_validation")) {
142
+ app.ui.settings.setSettingValue("Comfy.Validation.Workflows", false);
143
+ }
144
+ await original_loadGraphData.apply(this, arguments);
145
+ app.ui.settings.setSettingValue("Comfy.Validation.Workflows", cvw_was);
146
+ //return v;
147
+ }
148
+ //}
149
+
150
+ /*
151
+ When we draw a node, render the virtual connection points
152
+ */
153
+ const original_drawNode = LGraphCanvas.prototype.drawNode;
154
+ LGraphCanvas.prototype.drawNode = function(node, ctx) {
155
+ try {
156
+ shared.linkRenderController.pause('drawFrontCanvas')
157
+ const v = original_drawNode.apply(this, arguments);
158
+ shared.linkRenderController.highlight_ue_connections(node, ctx);
159
+ if (node._last_seen_bg !== node.bgcolor) shared.linkRenderController.mark_link_list_outdated();
160
+ node._last_seen_bg = node.bgcolor
161
+ return v
162
+ } catch (e) {
163
+ Logger.log_error(e)
164
+ } finally {
165
+ shared.linkRenderController.unpause()
166
+ }
167
+ }
168
+
169
+ /*
170
+ Before drawing the canvas, temporarily disable all the ue connected widgets
171
+ so they get rendered as greyed out.
172
+ */
173
+ const original_drawFrontCanvas = LGraphCanvas.prototype.drawFrontCanvas
174
+ LGraphCanvas.prototype.drawFrontCanvas = function() {
175
+ try {
176
+ shared.linkRenderController.disable_all_connected_widgets()
177
+ return original_drawFrontCanvas.apply(this, arguments);
178
+ } catch (e) {
179
+ Logger.log_error(e)
180
+ } finally {
181
+ try {
182
+ shared.linkRenderController.enable_all_disabled_widgets()
183
+ } catch (e) {
184
+ Logger.log_error(e)
185
+ }
186
+
187
+ }
188
+ }
189
+
190
+ /*
191
+ When we draw connections, do the ue ones as well (logic for on/off is in lrc)
192
+ */
193
+ const drawConnections = LGraphCanvas.prototype.drawConnections;
194
+ LGraphCanvas.prototype.drawConnections = function(ctx) {
195
+ drawConnections?.apply(this, arguments);
196
+ try {
197
+ shared.linkRenderController.render_all_ue_links(ctx);
198
+ } catch (e) {
199
+ Logger.log_error(e)
200
+ }
201
+ }
202
+
203
+ },
204
+
205
+ init() {
206
+ shared.graphAnalyser = new GraphAnalyser();
207
+ shared.linkRenderController = new LinkRenderController();
208
+
209
+ /*
210
+ Modifications to the graph:
211
+ - track start and end of the graph being changed
212
+ - catch convertToSubgraph to try to fix UE links
213
+ */
214
+ const original_beforeChange = app.graph.beforeChange
215
+ app.graph.beforeChange = function () {
216
+ shared.in_midst_of_change += 1
217
+ original_beforeChange?.apply(this, arguments)
218
+ }
219
+
220
+ const original_afterChange = app.graph.afterChange
221
+ app.graph.afterChange = function () {
222
+ original_afterChange?.apply(this, arguments)
223
+ shared.in_midst_of_change = Math.max(0, shared.in_midst_of_change-1) // afterChange gets called without a beforeChange sometimes
224
+ }
225
+
226
+ const original_subgraph = app.graph.convertToSubgraph
227
+ app.graph.convertToSubgraph = function () {
228
+ const ctb_was = shared.graphAnalyser.connect_to_bypassed
229
+ shared.graphAnalyser.connect_to_bypassed = true
230
+ try {
231
+ const cur_list = shared.graphAnalyser.analyse_graph(visible_graph())
232
+ if (!cur_list) Logger.log_problem('convert to subgraph failed to get ues')
233
+ const mods = convert_to_links(cur_list, null, visible_graph());
234
+ const r = original_subgraph.apply(this, arguments);
235
+ mods.restorer()
236
+ fix_new_subgraph_node(r.node)
237
+ return r
238
+ } finally {
239
+ shared.graphAnalyser.connect_to_bypassed = ctb_was
240
+ }
241
+ }
242
+
243
+ /*
244
+ Modifications to app
245
+ - intercept graphToPrompt and queuePrompt so we know where we are
246
+ - provide API
247
+ */
248
+ const original_graphToPrompt = app.graphToPrompt;
249
+ app.graphToPrompt = async function () {
250
+ try {
251
+ shared.in_graphToPrompt += 1
252
+ if (shared.in_queuePrompt || app.ui.settings.getSettingValue("Use Everywhere.Options.always_modify_graph")) {
253
+ //Logger.log_shared('In graphToPrompt (going to modify graph):')
254
+ return await shared.graphAnalyser.call_function_with_modified_graph( original_graphToPrompt, arguments )
255
+ } else {
256
+ //Logger.log_shared('In graphToPrompt (not going to modify graph):')
257
+ return await original_graphToPrompt.apply(this, arguments)
258
+ }
259
+ } finally {
260
+ shared.in_graphToPrompt -= 1
261
+ }
262
+ }
263
+
264
+ const original_queuePrompt = app.queuePrompt;
265
+ app.queuePrompt = async function () {
266
+ try {
267
+ shared.in_queuePrompt += 1;
268
+ return await original_queuePrompt.apply(app, arguments);
269
+ } finally {
270
+ shared.in_queuePrompt -= 1;
271
+ }
272
+ }
273
+
274
+ app.ue_modified_prompt = async function () { // API function
275
+ return await shared.graphAnalyser.call_function_with_modified_graph( original_graphToPrompt )
276
+ }
277
+
278
+ /*
279
+ Modifications to the canvas
280
+ - listen for set-graph to mark the link list as out of date when we open or close a subgraph
281
+ - catch node-double-click to open the restrictions dialog
282
+ - onDrawForeground to highlight subgraph output links
283
+ */
284
+
285
+ app.canvas.canvas.addEventListener('litegraph:set-graph', ()=>{
286
+ shared.linkRenderController.mark_link_list_outdated()
287
+ setTimeout(()=>{app.canvas.setDirty(true,true)},200)
288
+ })
289
+
290
+ app.canvas.canvas.addEventListener('litegraph:canvas', (e)=>{
291
+ if (e?.detail?.subType=='node-double-click') {
292
+ const node = e.detail.node
293
+ if (node_can_broadcast(node)) {
294
+ if (app.ui.settings.getSettingValue('Comfy.Node.DoubleClickTitleToEdit') && e.detail.originalEvent.canvasY<node.pos[1]) return
295
+ edit_restrictions(null, null, null, null, node)
296
+ }
297
+ }
298
+ })
299
+
300
+ const original_onDrawForeground = app.canvas.onDrawForeground
301
+ app.canvas.onDrawForeground = function(ctx, visible_area) {
302
+ if (original_onDrawForeground) original_onDrawForeground.apply(this, arguments)
303
+ if (this.subgraph) shared.linkRenderController.highlight_subgraph_node_connections.bind(shared.linkRenderController)(this.subgraph, ctx)
304
+ }
305
+
306
+ /*
307
+ Midifications to app.ui.settings
308
+ - catch changes in language (and read initial value) for i18n
309
+ */
310
+ const locale_onChange = app.ui.settings.settingsLookup['Comfy.Locale'].onChange
311
+ app.ui.settings.settingsLookup['Comfy.Locale'].onChange = function(is_now, was_before) {
312
+ language_changed(is_now, was_before)
313
+ return locale_onChange?.apply(this, arguments)
314
+ }
315
+ language_changed(app.ui.settings.getSettingValue('Comfy.Locale'), null)
316
+ },
317
+
318
+
319
+ beforeConfigureGraph() {
320
+ shared.linkRenderController.pause("before configure", 1000)
321
+ shared.graphAnalyser.pause("before configure", 1000)
322
+ shared.graph_being_configured += 1
323
+ },
324
+
325
+ afterConfigureGraph() {
326
+ shared.graph_being_configured -= 1
327
+ ue_callbacks.dispatch('afterConfigureGraph')
328
+ },
329
+
330
+ getCanvasMenuItems(canvas) {
331
+ return canvas_menu_settings()
332
+ }
333
+
334
+ });
v3-nodes/cg-use-everywhere/js/use_everywhere_apply.js ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+ import { display_name } from "./use_everywhere_classes.js";
3
+ import { get_real_node, Logger } from "./use_everywhere_utilities.js";
4
+
5
+
6
+ function _convert_to_links(ue, added_links, removed_links) {
7
+ const output_node_id = ue.output[0];
8
+ const output_index = ue.output[1];
9
+ const output_node = get_real_node(output_node_id, ue.graph);
10
+ Logger.log_info("Adding links for " + ue.description);
11
+ ue.sending_to.forEach((st) => {
12
+ const input_node_id = st.node.id;
13
+ const input_node = get_real_node(input_node_id, ue.graph);
14
+ const input_index = st.input_index;
15
+ if (input_node.inputs?.[input_index].link) { // why would this be happening?
16
+ const llink = ue.graph.links[input_node.inputs[input_index].link]
17
+ if (llink) removed_links.push( {...llink} )
18
+ }
19
+ var new_link
20
+ if (input_node.io_node) {
21
+ new_link = input_node.io_node.slots[input_index].connect(output_node.outputs[output_index], output_node);
22
+ } else {
23
+ new_link = output_node.connect(output_index, input_node, input_index);
24
+ }
25
+
26
+ if (!new_link)
27
+ console.error("Failed to connect nodes: " +
28
+ `${output_node_id}[${output_index}] -> ` +
29
+ `${input_node_id}[${input_index}].`);
30
+ else { // Memorize the links we are adding to remove them later
31
+ if (added_links) {
32
+ new_link.control_node = ue.controller;
33
+ added_links.push(new_link);
34
+ }
35
+ Logger.log_info(` -> ${display_name(st.node)}, ${st.input.name} ` +
36
+ `(${st.node.id}.${st.input_index}) (ID: ${new_link.id})`);
37
+ }
38
+ });
39
+ }
40
+
41
+ export function convert_to_links(ues, control_node, graph) {
42
+ if (control_node) {
43
+ if (!graph) graph = control_node.graph
44
+ return _convert_graph_to_links(graph, ues, control_node.id );
45
+ } else {
46
+ if (!graph) graph = app.graph;
47
+ return _convert_graph_to_links(graph, ues, undefined);
48
+ }
49
+ }
50
+
51
+ function _convert_graph_to_links(graph, ues, control_node_id) {
52
+ if (!ues?.ues) {
53
+ Logger.log_detail('no ues in _convert_graph_to_links')
54
+ return {restorer:function(){}, added_links:[]}
55
+ }
56
+ const added_links = []
57
+ const removed_links = []
58
+ ues.ues.forEach((ue)=> {
59
+ if (control_node_id==undefined || ue.controller.id == control_node_id) _convert_to_links(ue, added_links, removed_links);
60
+ });
61
+
62
+ const restorer = function() {
63
+ const links_within_graph_ids = new Set()
64
+ const links_within_subgraph_ids = new Set()
65
+ const links_into_subgraph_ids = new Set()
66
+ const links_out_of_subgraph = new Set()
67
+ const problem_links = new Set()
68
+
69
+ const new_subgraph_node = graph.getNodeById(graph.last_node_id);
70
+ const new_subgraph = new_subgraph_node?.subgraph;
71
+
72
+ added_links.forEach(added_link => {
73
+ var id = added_link.id
74
+ try {
75
+ if (graph.links[id]) {
76
+ links_within_graph_ids.add(id);
77
+ } else {
78
+ const link_to_subgraph_node = new_subgraph_node?.inputs.find(input => input.linkIds.includes(id))?.link
79
+ if (link_to_subgraph_node) {
80
+ links_into_subgraph_ids.add(link_to_subgraph_node)
81
+ } else {
82
+ if (new_subgraph.links[id]) {
83
+ if (new_subgraph.links[id].target_id==-20) {
84
+ links_out_of_subgraph.add(added_link)
85
+ } else {
86
+ links_within_subgraph_ids.add(id)
87
+ }
88
+ } else problem_links.add(added_link)
89
+ }
90
+ }
91
+ } catch (e) {
92
+ problem_links.add(added_link)
93
+ Logger.log_error(e);
94
+ }
95
+ });
96
+
97
+ links_out_of_subgraph.forEach((llink)=>{
98
+ const control_node_id = llink.control_node?.id
99
+ const control_node_in_graph = graph.getNodeById(control_node_id)
100
+ if (control_node_in_graph) {
101
+ // the control node is outside, so we should disconnect anything else that is connected to the same output of the subgraph node
102
+ new_subgraph_node.outputs[new_subgraph.links[llink.id].target_slot].links.forEach((link_id)=>{
103
+ if (graph.links[link_id].target_id==control_node_id) {
104
+ // leave the link to the UE node
105
+ } else {
106
+ // remove others
107
+ links_within_graph_ids.add(link_id)
108
+ }
109
+ })
110
+ } else {
111
+ // control node is inside, leave the link, since we can't UE connect to the output panel
112
+ }
113
+ })
114
+
115
+ links_within_graph_ids.forEach((lid)=>{graph.removeLink(lid)})
116
+ links_into_subgraph_ids.forEach((lid)=>{graph.removeLink(lid)})
117
+ links_within_subgraph_ids.forEach((lid)=>{new_subgraph.removeLink(lid)})
118
+
119
+ if (problem_links.size>0) {
120
+ Logger.log_problem("Failed to work out how to remove some temporary links:", problem_links)
121
+ }
122
+
123
+ removed_links.forEach(llink => {
124
+ graph.getNodeById(llink.origin_id).connect(llink.origin_slot, graph.getNodeById(llink.target_id), llink.target_slot)
125
+ })
126
+ };
127
+
128
+ return {restorer:restorer, added_links:added_links}
129
+ }
v3-nodes/cg-use-everywhere/js/use_everywhere_cache.js ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+
3
+ class LocalSettingsCache {
4
+ constructor() {
5
+ this.local_settings = {}
6
+ this.callbacks = {}
7
+ }
8
+ getSettingValue(id) {
9
+ if (this.local_settings[id]===undefined) this.local_settings[id] = app.ui.settings.getSettingValue(id)
10
+ return this.local_settings[id]
11
+ }
12
+ onSettingChange(new_value, old_value) {
13
+ const id = this.id // this references the setting that has been changed
14
+ settingsCache.local_settings[id] = new_value
15
+ if (settingsCache.callbacks[id]) {
16
+ settingsCache.callbacks[id].forEach((c) => {c(new_value, old_value, this)})
17
+ }
18
+ }
19
+ addCallback(id, callback) {
20
+ if (this.callbacks[id]===undefined) this.callbacks[id] = []
21
+ this.callbacks[id].push(callback)
22
+ }
23
+ }
24
+
25
+ export const settingsCache = new LocalSettingsCache()
v3-nodes/cg-use-everywhere/js/use_everywhere_classes.js ADDED
@@ -0,0 +1,373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { i18n_functional } from "./i18n.js";
2
+ import { default_priority } from "./ue_properties.js";
3
+ import { connection_from_output_as_input, visible_graph } from "./use_everywhere_subgraph_utils.js";
4
+ 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";
5
+ import { Logger, node_is_live, get_real_node, get_connection, find_duplicate_broadcasted_types, is_able_to_broadcast } from "./use_everywhere_utilities.js";
6
+
7
+
8
+ export function display_name(node) {
9
+ if (node?.title) return node.title;
10
+ if (node?.type) return node.type;
11
+ if (node?.properties?.['Node name for S&R']) return node.properties['Node name for S&R'];
12
+ if (node?.id==-20) return "subgraph output node";
13
+ if (node?.id==-10) return "subgraph input node";
14
+ return "un-nameable node";
15
+ }
16
+
17
+ export function input_name(input) {
18
+ return input?.label || input?.localized_name || input?.name || "";
19
+ }
20
+
21
+ function regex_for(node, k) {
22
+ try {
23
+ const w0 = node.properties.ue_properties[`${k}_regex`]
24
+ return (w0 && w0!='.*') ? {regex:new RegExp(w0), invert:!!node.properties.ue_properties[`${k}_regex_invert`]} : null;
25
+ } catch (e) {
26
+ return null
27
+ }
28
+ }
29
+
30
+
31
+ const P_REGEXES = ['prompt', 'negative']
32
+ const PROMPT_REGEXES = [new RegExp(i18n_functional('prompt_regex')), new RegExp(i18n_functional('negative_regex'))]
33
+
34
+ /*
35
+ The UseEverywhere object represents a single 'broadcast'. It generally contains
36
+ controller - the UE node that controls the broadcase
37
+ control_node_input_index - the input on that node
38
+ type - the data type
39
+ output - the output that is being rebroadcast as a list (node_id, output_index)
40
+ title_regex, input_regex - the UE? matching rules
41
+ priority - priorty :)
42
+ graph - the graph or subgraph
43
+ */
44
+ class UseEverywhere {
45
+ constructor() {
46
+ this.sending_to = [];
47
+ Object.assign(this, arguments[0]);
48
+ if (this.priority === undefined) this.priority = 0;
49
+ if (this.graph === undefined) this.graph = visible_graph();
50
+ this.string_to_combo = (this.controller.properties.ue_properties.string_to_combo > 0)
51
+ this.send_to_any = (this.controller.properties.ue_properties.send_to_any > 0)
52
+
53
+ const from_node = get_real_node(this?.output[0], this.graph);
54
+ const to_node = get_real_node(this?.controller.id, this.graph)
55
+ try {
56
+ if (this.output[0]==-10) {
57
+ this.description = `source subgraph input slot ${this?.output[1]} ` +
58
+ `-> control "${display_name(to_node)}", ${to_node.inputs[this?.control_node_input_index]?.name} (${this?.controller?.id}.${this?.control_node_input_index}) ` +
59
+ `"${this.type}" <- (priority ${this.priority})`;
60
+ }
61
+ else if (this.control_node_input_index>=0) {
62
+ this.description = `source "${display_name(from_node)}", ${from_node.outputs[this?.output[1]]?.name} (${this?.output[0]}.${this?.output[1]}) ` +
63
+ `-> control "${display_name(to_node)}", ${to_node.inputs[this?.control_node_input_index]?.name} (${this?.controller.id}.${this?.control_node_input_index}) ` +
64
+ `"${this.type}" <- (priority ${this.priority})`;
65
+ } else {
66
+ this.description = `source "${display_name(from_node)}", ${from_node.outputs[this?.output[1]]?.name} (${this?.output[0]}.${this?.output[1]}) ` +
67
+ `"${this.type}" <- (priority ${this.priority})`;
68
+ }
69
+ } catch (e) {
70
+ // for breakpointing
71
+ throw e;
72
+ }
73
+ if (this.title_regex) this.description += ` - node title regex '${this.title_regex.regex?.source} ${(this.title_regex.invert) ? "(inverted)" : ""}'`;
74
+ if (this.input_regex) this.description += ` - input name regex '${this.input_regex.regex?.source} ${(this.input_regex.invert) ? "(inverted)" : ""}''`;
75
+ }
76
+
77
+ sending_differs_from(another_ue) {
78
+ if (this.sending_to.length != another_ue.sending_to.length) return true;
79
+ for (var i=0; i<this.sending_to.length; i++) {
80
+ if ( (this.sending_to[i].node.id != another_ue.sending_to[i].node.id) ||
81
+ (this.sending_to[i].input_index != another_ue.sending_to[i].input_index) ) return true;
82
+ }
83
+ return false;
84
+ }
85
+ /*
86
+ Does this broadcast match a given node,input?
87
+ Logic here is to run through a series of reasons why it would NOT match.
88
+ */
89
+ matches(node, input) {
90
+ if (!node) {
91
+ Logger.log_problem(`UseEverywhere.matches called with no node`);
92
+ return false;
93
+ }
94
+
95
+ // additional requirement function
96
+ if (this.additional_requirement && !this.additional_requirement(input, node)) return false
97
+
98
+ // don't send to self
99
+ if (this.output[0] == node.id) return false;
100
+
101
+ // type must match except in special cases
102
+ if (this.type != input.type) {
103
+ if (this.type=="STRING" && input.type=="COMBO" && this.string_to_combo) {
104
+ // allow a string to be sent to a COMBO
105
+ } else if (input.type=="*" && this.send_to_any) {
106
+ // allow sending to '*' inputs if send_to_any is true
107
+ } else {
108
+ return false
109
+ }
110
+ }
111
+
112
+ // apply restrictions (colour and group, includes group regex)
113
+ if (this.restrict_to && !this.restrict_to.includes(node.id)) return false;
114
+
115
+ // title regex
116
+ if (this.title_regex) {
117
+ if ( this.title_regex.regex.test(display_name(node)) == this.title_regex.invert ) return false;
118
+ }
119
+
120
+ // input regex
121
+ if (this.input_regex) {
122
+ if (this.input_regex.regex.test(input_name(input))==this.input_regex.invert ) return false;
123
+ }
124
+
125
+ return true;
126
+ }
127
+ note_sending_to(node, input) {
128
+ var input_index
129
+ if (node.inputs) input_index = node.inputs.findIndex((n) => n.name==input.name);
130
+ else if (node.slots) input_index = node.slots.findIndex((n) => n.name==input.name);
131
+ if (input_index===undefined) {
132
+ Logger.log_problem(`UseEverywhere.note_sending_to could not find input index for node ${node.id} input ${input.name}`);
133
+ } else {
134
+ this.sending_to.push({node:node, input:input, input_index:input_index})
135
+ }
136
+ }
137
+ describe_sending(){
138
+ var description = " Linked to:";
139
+ this.sending_to.forEach((st) => description += `\n -> ${display_name(st.node)}, ${st.input.name}`);
140
+ if (this.sending_to.length===0) description += ' nothing';
141
+ return description;
142
+ }
143
+ describe() {
144
+ return this.description + "\n" + this.describe_sending();
145
+ }
146
+ }
147
+
148
+ function validity_errors(params) {
149
+ if (!node_is_live(params.controller)) return `UE node ${params.controller.id} is not alive`;
150
+ if (params.output[0]!=-10 && !node_is_live(get_real_node(params.output[0], params.graph))) return `upstream node ${params.output[0]} is not alive`;
151
+ return "";
152
+ }
153
+
154
+ export class Ambiguity {
155
+ /*
156
+ name : display_name of node
157
+ id : node.id
158
+ input : input.name
159
+ matches : [
160
+ {
161
+ type : m[i].controller.type,
162
+ id : m[i].controller.id,
163
+ index : m[i].control_node_input_index
164
+ }
165
+ ]
166
+ */
167
+ toString() {
168
+ var m = `Node ${this.name} (${this.id}) input ${this.input} matches ${this.matches.length} sources:`
169
+ this.matches.forEach((match)=>{
170
+ m += `\n - ${match.type} (${match.id}) slot ${match.index}`
171
+ })
172
+ return m
173
+ }
174
+
175
+ }
176
+
177
+ export class UseEverywhereList {
178
+ constructor() { this.ues = []; this.unmatched_inputs = []; }
179
+
180
+ differs_from(another_uel) {
181
+ if (!another_uel || !another_uel.ues || !this.ues) return true;
182
+ if (this.ues.length != another_uel.ues.length) return true;
183
+ for (var i=0; i<this.ues.length; i++) {
184
+ if (this.ues[i].sending_differs_from(another_uel.ues[i])) return true;
185
+ }
186
+ return false;
187
+ }
188
+
189
+ add_ue(node, control_node_input_index, type, output, input_regex_override, additional_requirement) {
190
+ const params = {
191
+ controller: node,
192
+ control_node_input_index: control_node_input_index,
193
+ type: type,
194
+ output: output,
195
+ title_regex: regex_for(node, 'title'),
196
+ input_regex: input_regex_override || regex_for(node, 'input'),
197
+ group_regex: regex_for(node, 'group'),
198
+ priority: node.properties.ue_properties.priority || default_priority(node),
199
+ graph: node.graph,
200
+ additional_requirement: additional_requirement,
201
+ };
202
+
203
+ if (node.properties.ue_properties.group_restricted == 1) params.restrict_to = nodes_in_my_group(node);
204
+ if (node.properties.ue_properties.group_restricted == 2) params.restrict_to = nodes_not_in_my_group(node);
205
+
206
+ if (node.properties.ue_properties.color_restricted == 1) params.restrict_to = nodes_my_color(node, params.restrict_to);
207
+ if (node.properties.ue_properties.color_restricted == 2) params.restrict_to = nodes_not_my_color(node, params.restrict_to);
208
+
209
+ if (params.group_regex) params.restrict_to = nodes_in_groups_matching(params.group_regex, params.restrict_to, graph);
210
+
211
+ var fail = ""
212
+ var ue = null;
213
+ try {
214
+ ue = new UseEverywhere(params);
215
+ fail = validity_errors(params);
216
+ } catch (e) {
217
+ Logger.log_problem(`Error creating UseEverywhere object from node ${node.id}: ${e}`);
218
+ Logger.log_error(e);
219
+ }
220
+ if (fail==="") {
221
+ this.ues.push(ue);
222
+ Logger.log_detail(`Added ${ue.description}`)
223
+ } else {
224
+ Logger.log_info(`Rejected ${ue?.description} because ${fail}`);
225
+ }
226
+ }
227
+
228
+ find_best_match(node, input, _ambiguities) {
229
+ this.unmatched_inputs.push({"node":node, "input":input});
230
+ var matches = this.ues.filter((candidate) => (
231
+ candidate.matches(node, input)
232
+ ));
233
+ if (matches.length==0) {
234
+ Logger.log_detail(`'${display_name(node)}' optional input '${input.name}' unmatched`)
235
+ return undefined;
236
+ }
237
+ if (matches.length>1) {
238
+ matches.sort((a,b) => b.priority-a.priority);
239
+ if(matches[0].priority == matches[1].priority) {
240
+ const msg = new Ambiguity()
241
+ Object.assign(msg, { name:display_name(node), id:node.id, input:input.name, matches:[] })
242
+
243
+ matches.filter((m)=>(m.priority == matches[0].priority)).forEach((m)=>{
244
+ msg.matches.push( {
245
+ type : m.controller.type,
246
+ id : m.controller.id,
247
+ index : m.control_node_input_index,
248
+ node : node,
249
+ node_index : node.inputs.findIndex((i)=>(i==input))
250
+ })
251
+ })
252
+
253
+ _ambiguities.push(msg);
254
+ return undefined;
255
+ }
256
+ }
257
+ matches[0].note_sending_to(node, input);
258
+ Logger.log_detail(`'${display_name(node)}' input '${input.name}' matched to ${matches[0].description}`);
259
+ return matches[0];
260
+ }
261
+
262
+ all_connected_inputs(for_node) {
263
+ const ue_connections = [];
264
+ this.ues.forEach((ue) => {
265
+ ue.sending_to.forEach((st) => {
266
+ if (st.node.id == for_node.id) {
267
+ ue_connections.push({
268
+ type : ue.type,
269
+ input_index : st.input_index,
270
+ control_node : get_real_node(ue.controller.id, ue.graph),
271
+ control_node_input_index : ue.control_node_input_index,
272
+ sending_to : st.node,
273
+ });
274
+ }
275
+ });
276
+ });
277
+ return ue_connections;
278
+ }
279
+
280
+ node_sending_anywhere(node) {
281
+ return this.ues.find((ue)=>(ue.controller?.id==node.id && ue.sending_to?.length>0)) ? true : false;
282
+ }
283
+
284
+ all_sending_slots(for_node) {
285
+ const sending_slots = new Set()
286
+ this.ues.filter((ue)=>(ue.controller.id == for_node.id) && (ue.sending_to.length>0)).forEach((ue) => {
287
+ sending_slots.add(ue.control_node_input_index)
288
+ });
289
+ return sending_slots;
290
+ }
291
+
292
+ all_ue_connections() {
293
+ const ue_connections = [];
294
+ this.ues.forEach((ue) => {
295
+ ue.sending_to.forEach((st) => {
296
+ ue_connections.push({
297
+ type : ue.type,
298
+ input_index : st.input_index,
299
+ control_node : get_real_node(ue.controller.id, ue.graph),
300
+ control_node_input_index : ue.control_node_input_index,
301
+ sending_to : st.node,
302
+ graph: ue.graph,
303
+ });
304
+ });
305
+ });
306
+ return ue_connections;
307
+ }
308
+
309
+ add_ue_from_node(node) {
310
+
311
+ if (node.properties.ue_properties.seed_inputs) {
312
+ this.add_ue(node, -1, "INT", [node.id.toString(),0], regex_for(node, 'input') );
313
+ } else {
314
+ var check_if_able_to_broadcast
315
+ var the_possibles
316
+ var connection_finder
317
+ if (node.properties.ue_convert) {
318
+ connection_finder = connection_from_output_as_input
319
+ the_possibles = node.outputs
320
+ check_if_able_to_broadcast = (node, i) => {
321
+ const name = node.outputs[i].name
322
+ return is_able_to_broadcast(node, name)
323
+ }
324
+ } else {
325
+ connection_finder = get_connection
326
+ the_possibles = node.inputs
327
+ check_if_able_to_broadcast = ()=>(true)
328
+ }
329
+
330
+ const duplicated_broadcasted_types = find_duplicate_broadcasted_types(node)
331
+
332
+ the_possibles.forEach((possible, i) => {
333
+ const connection = connection_finder(node, i);
334
+ if (connection.link && check_if_able_to_broadcast(node,i)) {
335
+ const input_regex = undefined
336
+ var additional_requirement = null
337
+ if (duplicated_broadcasted_types.has(connection.type) || node.properties.ue_properties.apply_to_unrepeated) {
338
+ const input_name = possible.label || possible.name
339
+ const rule = node.properties.ue_properties?.repeated_type_rule || 0
340
+ if (rule == 0) { // 0 is exact match of input name
341
+ additional_requirement = (target_input) => {
342
+ const target_name = target_input.label || target_input.name
343
+ return (target_name == input_name)
344
+ }
345
+ }
346
+ if (rule == 1) { // 1 is match start of input name
347
+ additional_requirement = (target_input) => {
348
+ const target_name = target_input.label || target_input.name
349
+ const chars = Math.min( input_name.length, target_name.length )
350
+ return (target_name.substring(0, chars) == input_name.substring(0, chars))
351
+ }
352
+ }
353
+ if (rule == 2) { // 2 is match end of input name
354
+ additional_requirement = (target_input) => {
355
+ const target_name = target_input.label || target_input.name
356
+ const chars = Math.min( input_name.length, target_name.length )
357
+ return (target_name.substring(target_name.length - chars) == input_name.substring(input_name.length - chars))
358
+ }
359
+ }
360
+ if (rule == 3) { // 3 input is match of target node name
361
+ additional_requirement = (_, target_node) => {
362
+ const target_name = target_node.title
363
+ return (target_name == input_name)
364
+ }
365
+ }
366
+ }
367
+ this.add_ue(node, i, connection.type, [connection.link.origin_id.toString(), connection.link.origin_slot], input_regex, additional_requirement);
368
+ }
369
+ })
370
+ }
371
+ }
372
+ }
373
+
v3-nodes/cg-use-everywhere/js/use_everywhere_graph_analysis.js ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { UseEverywhereList } from "./use_everywhere_classes.js";
2
+ import { node_is_live, is_connected, is_UEnode, Logger, Pausable, node_can_broadcast } from "./use_everywhere_utilities.js";
3
+ import { convert_to_links } from "./use_everywhere_apply.js";
4
+ import { app } from "../../scripts/app.js";
5
+ import { settingsCache } from "./use_everywhere_cache.js";
6
+ import { is_connectable } from "./use_everywhere_settings.js";
7
+ import { for_all_graphs } from "./recursive_callbacks.js";
8
+ import { shared } from "./shared.js";
9
+
10
+ class GraphAnalyser extends Pausable {
11
+
12
+ constructor() {
13
+ super('GraphAnalyser')
14
+ this.original_graphToPrompt = app.graphToPrompt;
15
+ this.ambiguities = [];
16
+ this.latest_ues = null
17
+ this.mods = []
18
+ }
19
+
20
+ modify_graph(graph) {
21
+ const ues = this.analyse_graph(graph, true)
22
+ if (ues===null) {
23
+ Logger.log_problem(`modify_graph called but no ues could be obtained for ${graph.id}`)
24
+ console.trace()
25
+ }
26
+ const modifications = convert_to_links( ues, null, graph )
27
+ this.mods.push( modifications );
28
+ if (!graph.extra) graph.extra = {}
29
+ graph.extra['links_added_by_ue'] = modifications.added_links.map(x=>x.id)
30
+ }
31
+
32
+ modify_all_graphs() {
33
+ for_all_graphs(this.modify_graph.bind(this))
34
+ }
35
+
36
+ async call_function_with_modified_graph( func, args ) {
37
+ var result
38
+ this.mods = []
39
+ try {
40
+ this.pause('call_function_with_modified_graph')
41
+ Logger.log_info("Modifying graphs")
42
+ this.modify_all_graphs()
43
+ shared.graph_currently_modified += 1
44
+ result = await (args ? func(...args) : func())
45
+ } catch (e) {
46
+ Logger.log_error(e)
47
+ }
48
+
49
+ Logger.log_info("Unmodifying graphs")
50
+ this.mods.forEach((mod)=>{
51
+ try {mod.restorer()}
52
+ catch (e) {Logger.log_error(e)}
53
+ })
54
+ this.mods = []
55
+ shared.graph_currently_modified -= 1
56
+ this.unpause()
57
+
58
+ return result
59
+ }
60
+
61
+
62
+ clean_slots(links, slots) {
63
+ slots?.forEach((slot)=>{
64
+ if (slot.linkIds.find((lid)=>(!links[lid]))) {
65
+ slot.linkIds = slot.linkIds.filter((lid)=>{ return links[lid] !== undefined })
66
+ }
67
+ })
68
+ }
69
+
70
+ analyse_graph(graph, ignore_pause) {
71
+ if (this.paused() && !ignore_pause) return null
72
+
73
+ /* work around known bug in ComfyUI front end that doesn't clean up the linkIds
74
+ https://github.com/Comfy-Org/ComfyUI_frontend/issues/5673#issuecomment-3314310014
75
+
76
+ Fixed? Doesn't seems to be.
77
+ https://github.com/Comfy-Org/ComfyUI_frontend/pull/6258 */
78
+
79
+
80
+ this.clean_slots(graph.links, graph.inputNode?.slots)
81
+ this.clean_slots(graph.links, graph.outputNode?.slots)
82
+
83
+ this.ambiguities = [];
84
+ const treat_bypassed_as_live = settingsCache.getSettingValue("Use Everywhere.Options.connect_to_bypassed") || this.connect_to_bypassed
85
+ const live_nodes = graph.nodes.filter((node) => node_is_live(node, treat_bypassed_as_live))
86
+
87
+ // Create a UseEverywhereList and populate it from all live (not bypassed) UE nodes
88
+ const ues = new UseEverywhereList();
89
+ live_nodes.filter((node) => node_can_broadcast(node)).filter((node)=>node_is_live(node,false)).forEach(node => { ues.add_ue_from_node(node); })
90
+
91
+ // List all unconnected inputs on non-UE nodes which are connectable
92
+ const connectable = []
93
+ live_nodes.filter((node) => !is_UEnode(node)).forEach(node => {
94
+ if (node && !node.properties.rejects_ue_links) {
95
+ //if (!real_node._widget_name_map) real_node._widget_name_map = real_node.widgets?.map(w => w.name) || [];
96
+ node.inputs?.forEach((input,index) => {
97
+ if (!input) return; // NoteNode has input = [undefined,] !
98
+ if (is_connected(input, treat_bypassed_as_live, node.graph)) return;
99
+ if (node.reject_ue_connection && node.reject_ue_connection(input)) return;
100
+ if (is_connectable(node, input.name)) connectable.push({node, input, index});
101
+ })
102
+ }
103
+ })
104
+
105
+ if (graph.outputNode) {
106
+ graph.outputNode.slots.filter((slot)=>(slot.linkIds.length==0)).forEach((slot,index)=>{
107
+ connectable.push({node:graph.outputNode, input:slot, index});
108
+ }
109
+ )}
110
+
111
+ // see if we can connect them
112
+ const links_added = new Set();
113
+ connectable.forEach(({node, input, index}) => {
114
+ var ue = ues.find_best_match(node, input, this.ambiguities);
115
+ if (ue) {
116
+ links_added.add({
117
+ "downstream":node.id, "downstream_slot":index,
118
+ "upstream":ue.output[0], "upstream_slot":ue.output[1],
119
+ "controller":ue.controller.id,
120
+ "type":ue.type
121
+ });
122
+ }
123
+ });
124
+
125
+ graph.extra['ue_links'] = Array.from(links_added)
126
+
127
+ if (this.ambiguities.length) Logger.log_info("Ambiguous connections", this.ambiguities, true);
128
+
129
+ this.latest_ues = ues;
130
+ return this.latest_ues;
131
+ }
132
+ }
133
+
134
+ export { GraphAnalyser }
v3-nodes/cg-use-everywhere/js/use_everywhere_settings.js ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+ import { convert_to_links } from "./use_everywhere_apply.js";
3
+ import { Logger, is_UEnode, node_can_broadcast, is_able_to_broadcast } from "./use_everywhere_utilities.js";
4
+ import { settingsCache } from "./use_everywhere_cache.js";
5
+ import { visible_graph } from "./use_everywhere_subgraph_utils.js";
6
+ import { edit_restrictions } from "./ue_properties_editor.js";
7
+ import { i18ify_settings } from "./i18n.js";
8
+ import { VERSION, shared } from "./shared.js";
9
+ import { for_all_graphs } from "./recursive_callbacks.js";
10
+
11
+ const _SETTINGS = [
12
+ {
13
+ id: "Use Everywhere.About",
14
+ name: `Version ${VERSION}`,
15
+ type: () => {return document.createElement('span')},
16
+ },
17
+ {
18
+ id: "Use Everywhere.Graphics.showlinks",
19
+ name: "Show links",
20
+ type: "combo",
21
+ 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"}],
22
+ defaultValue: 3,
23
+ onChange: settingsCache.onSettingChange,
24
+ },
25
+ {
26
+ id: "Use Everywhere.Graphics.fuzzlinks",
27
+ name: "Statically distinguish UE links",
28
+ type: "boolean",
29
+ tooltip: "Render UE links, when shown, differently from normal links. Much lower performance cost than animation.",
30
+ defaultValue: true,
31
+ onChange: settingsCache.onSettingChange,
32
+ },
33
+ {
34
+ id: "Use Everywhere.Graphics.animate",
35
+ name: "Animate UE links",
36
+ type: "combo",
37
+ options: [ {value:0, text:"Off"}, {value:1, text:"Dots"}, {value:2, text:"Pulse"}, {value:3, text:"Both"}, ],
38
+ defaultValue: 0,
39
+ onChange: settingsCache.onSettingChange,
40
+ tooltip: "Animating links may have a negative impact on UI performance. Consider using Statically distinguish UE links instead."
41
+ },
42
+ {
43
+ id: "Use Everywhere.Graphics.stop_animation_when_running",
44
+ name: "Turn animation off when workflow is running",
45
+ type: "boolean",
46
+ defaultValue: true,
47
+ onChange: settingsCache.onSettingChange,
48
+ },
49
+ {
50
+ id: "Use Everywhere.Graphics.highlight",
51
+ name: "Highlight connected and connectable inputs",
52
+ type: "boolean",
53
+ defaultValue: true,
54
+ onChange: settingsCache.onSettingChange,
55
+ },
56
+ {
57
+ id: "Use Everywhere.Graphics.preserve edit window position",
58
+ name: "Save restrictions edit window position",
59
+ type: "boolean",
60
+ defaultValue: false,
61
+ onChange: settingsCache.onSettingChange,
62
+ tooltip: "If off, the edit window appears where the mouse is"
63
+ },
64
+ {
65
+ id: "Use Everywhere.Graphics.tooltips",
66
+ name: "Show restrictions as tooltip",
67
+ type: "boolean",
68
+ defaultValue: true,
69
+ onChange: settingsCache.onSettingChange,
70
+ },
71
+ {
72
+ id: "Use Everywhere.Options.connect_to_bypassed",
73
+ name: "Connect to bypassed nodes",
74
+ type: "boolean",
75
+ defaultValue: true,
76
+ onChange: settingsCache.onSettingChange,
77
+ tooltip: "By default UE links are made to the node downstream of bypassed nodes."
78
+ },
79
+ {
80
+ id: "Use Everywhere.Options.logging",
81
+ name: "Logging",
82
+ type: "combo",
83
+ options: [ {value:0, text:"Errors Only"}, {value:1, text:"Problems"}, {value:2, text:"Information"}, {value:3, text:"Detail"}, ],
84
+ defaultValue: 1,
85
+ onChange: settingsCache.onSettingChange,
86
+ },
87
+ {
88
+ id: "Use Everywhere.Options.block_graph_validation",
89
+ name: "Block workflow validation",
90
+ type: "boolean",
91
+ defaultValue: true,
92
+ tooltip: "Turn off workflow validation (which tends to replace UE links with real ones)",
93
+ onChange: settingsCache.onSettingChange,
94
+ },
95
+ {
96
+ id: "Use Everywhere.Options.always_modify_graph",
97
+ name: "Always modify graph",
98
+ type: "boolean",
99
+ defaultValue: false,
100
+ tooltip: "If turned on, the UE modifications will apply even if the trigger is unknown. This will enable API exports, but may cause incompatibility with other nodes.",
101
+ onChange: settingsCache.onSettingChange,
102
+ },
103
+ {
104
+ id: "Use Everywhere.Options.use_output_name",
105
+ name: "When connecting, use the output slot's name as the input name",
106
+ type: "boolean",
107
+ defaultValue: false,
108
+ tooltip: "By default the link type is used as the name",
109
+ onChange: settingsCache.onSettingChange,
110
+ },
111
+ ]
112
+
113
+ export const SETTINGS = i18ify_settings(_SETTINGS)
114
+
115
+ const ui_update_settings = [
116
+ "Use Everywhere.Graphics.showlinks",
117
+ "Use Everywhere.Graphics.fuzzlinks",
118
+ "Use Everywhere.Graphics.animate",
119
+ "Use Everywhere.Graphics.stop_animation_when_running",
120
+ "Use Everywhere.Graphics.highlight",
121
+ "Use Everywhere.Language.language",
122
+ ]
123
+ ui_update_settings.forEach((id) => {
124
+ settingsCache.addCallback(id, ()=>{app.graph?.change.bind(app.graph)})
125
+ })
126
+
127
+ function show_connectable(submenu_root, node) {
128
+ node.inputs.forEach((input, i) => {
129
+ const current_element = submenu_root?.querySelector(`:nth-child(${i+1})`);
130
+ if (current_element) current_element.style.borderLeft = (is_connectable(node,input.name)) ? "2px solid #484" : "";
131
+ })
132
+ }
133
+
134
+ export function is_connectable(node, input_name){
135
+ if (node.properties.rejects_ue_links) return false
136
+ const input = node.inputs.find(i => i.name==input_name);
137
+ if (!input) {
138
+ Logger.log_error(`Can't find input ${input_name} on node ${node.title}`);
139
+ return false;
140
+ }
141
+ if (input.widget) {
142
+ return !!(node.properties?.ue_properties?.widget_ue_connectable?.[input_name])
143
+ } else {
144
+ return ! (node.properties?.ue_properties?.input_ue_unconnectable?.[input_name])
145
+ }
146
+ }
147
+
148
+ function toggle_connectable(node, input_name){
149
+ const input = node.inputs.find(i => i.name==input_name);
150
+ const p = node.properties.ue_properties
151
+ if (input.widget) {
152
+ p.widget_ue_connectable[input_name] = !!!p.widget_ue_connectable?.[input_name];
153
+ } else {
154
+ p.input_ue_unconnectable[input_name] = !!!p.input_ue_unconnectable?.[input_name];
155
+ }
156
+ }
157
+
158
+ function widget_ue_submenu(value, options, e, menu, node) {
159
+ if (!(node.properties.ue_properties)) node.properties.ue_properties = {}
160
+ if (!(node.properties.ue_properties.widget_ue_connectable)) node.properties.ue_properties.widget_ue_connectable = {};
161
+ if (!(node.properties.ue_properties.input_ue_unconnectable)) node.properties.ue_properties.input_ue_unconnectable = {};
162
+
163
+ const label_to_name = {}
164
+ node.inputs
165
+ .filter(i => !i.hidden)
166
+ .filter(i => !i.name?.includes('$$'))
167
+ .forEach((input) => { label_to_name[input.label || input.name] = input.name });
168
+
169
+ const submenu = new LiteGraph.ContextMenu(
170
+ Object.keys(label_to_name),
171
+ { event: e, callback: function (label) {
172
+ const name = label_to_name[label];
173
+ toggle_connectable(node, name);
174
+ shared.linkRenderController.mark_link_list_outdated();
175
+ show_connectable(this.parentElement, node)
176
+ return true; // keep open
177
+ },
178
+ parentMenu: menu, node:node}
179
+ )
180
+ show_connectable(submenu.root, node)
181
+ }
182
+
183
+ function show_broadcasting(submenu_root, node) {
184
+ node.outputs.forEach((input, i) => {
185
+ const current_element = submenu_root?.querySelector(`:nth-child(${i+1})`);
186
+ if (current_element) current_element.style.borderLeft = (is_able_to_broadcast(node,input.name)) ? "2px solid #484" : "";
187
+ })
188
+ }
189
+
190
+ function toggle_broadcasting(node, output_name){
191
+ const p = node.properties.ue_properties
192
+ p.output_not_broadcasting[output_name] = !!!p.output_not_broadcasting[output_name]
193
+ }
194
+
195
+ function output_ue_submenu(value, options, e, menu, node) {
196
+ if (!(node.properties.ue_properties)) node.properties.ue_properties = {}
197
+ if (!(node.properties.ue_properties.output_not_broadcasting)) node.properties.ue_properties.output_not_broadcasting = {};
198
+
199
+ const label_to_name = {}
200
+ node.outputs.forEach((output) => {label_to_name[output.label || output.name] = output.name})
201
+ const submenu = new LiteGraph.ContextMenu(
202
+ Object.keys(label_to_name),
203
+ { event: e, callback: function (label) {
204
+ const name = label_to_name[label];
205
+ toggle_broadcasting(node, name);
206
+ shared.linkRenderController.mark_link_list_outdated();
207
+ show_broadcasting(this.parentElement, node)
208
+ return true; // keep open
209
+ },
210
+ parentMenu: menu, node:node}
211
+ )
212
+ show_broadcasting(submenu.root, node)
213
+ }
214
+
215
+
216
+
217
+ export function add_extra_menu_items(node, ioio) {
218
+ if (node.ue_extra_menu_items_added) return
219
+ const getExtraMenuOptions = node.getExtraMenuOptions;
220
+ node.getExtraMenuOptions = function(_, options) {
221
+ getExtraMenuOptions?.apply(this, arguments);
222
+ options.push(null);
223
+ if (node_can_broadcast(this)) {
224
+ add_restrictions_and_convert(options, this);
225
+ }
226
+ if (!is_UEnode(node)) {
227
+ add_input_and_output_settings(options, node)
228
+ }
229
+ options.push(null);
230
+ ioio(options,'callback',`menu option on ${this.id}`);
231
+ }
232
+ node.ue_extra_menu_items_added = true
233
+ }
234
+
235
+
236
+ function add_restrictions_and_convert(options, node) {
237
+ options.push(
238
+ {
239
+ content: "Edit restrictions",
240
+ callback: edit_restrictions,
241
+ }
242
+ )
243
+
244
+ options.push(
245
+ {
246
+ content: "Convert to real links",
247
+ callback: async () => {
248
+ const ues = shared.graphAnalyser.analyse_graph(visible_graph());
249
+ if (ues===null) {
250
+ Logger.log_problem("Convert to real links failed to get ues")
251
+ alert("Convert failed - press f12 to see the console log")
252
+ } else {
253
+ convert_to_links(ues, node);
254
+ if (is_UEnode(node)) {
255
+ visible_graph().remove(node);
256
+ } else {
257
+ node.properties.ue_convert = false
258
+ }
259
+ }
260
+ }
261
+ }
262
+ )
263
+ }
264
+
265
+ function add_input_and_output_settings(options, node) {
266
+ if (node.inputs?.length) {
267
+ options.push(
268
+ {
269
+ content: node.properties.rejects_ue_links ? "Allow UE Links" : "Reject UE Links",
270
+ has_submenu: false,
271
+ callback: () => { node.properties.rejects_ue_links = !!!node.properties.rejects_ue_links },
272
+ }
273
+ )
274
+
275
+ if (!node.properties.rejects_ue_links) {
276
+ options.push(
277
+ {
278
+ content: "UE Connectable Inputs",
279
+ has_submenu: true,
280
+ callback: widget_ue_submenu,
281
+ }
282
+ )
283
+ }
284
+ }
285
+
286
+ if (node.outputs.length) {
287
+ options.push(
288
+ {
289
+ content: node.properties.ue_convert ? "Remove UE broadcasting" : "Add UE broadcasting",
290
+ has_submenu: false,
291
+ callback: () => { node.properties.ue_convert = !!!node.properties.ue_convert },
292
+ }
293
+ )
294
+ if (node.properties.ue_convert) {
295
+ options.push(
296
+ {
297
+ content: "Broadcasting Outputs",
298
+ has_submenu: true,
299
+ callback: output_ue_submenu,
300
+ }
301
+ )
302
+ }
303
+ }
304
+ }
305
+
306
+ function remove_ue_nodes(graph) {
307
+ graph._nodes.filter((node)=>is_UEnode(node)).forEach((node)=>{graph.remove(node)})
308
+ }
309
+
310
+ export function canvas_menu_settings() {
311
+ const options = []
312
+ options.push(null); // divider
313
+ options.push(
314
+ {
315
+ content: (app.ui.settings.getSettingValue('Use Everywhere.Graphics.showlinks')>0) ? "Hide UE links" : "Show UE links",
316
+ callback: () => {
317
+ const setTo = (app.ui.settings.getSettingValue('Use Everywhere.Graphics.showlinks')>0) ? 0 : 4;
318
+ app.ui.settings.setSettingValue('Use Everywhere.Graphics.showlinks', setTo);
319
+ app.graph.change();
320
+ }
321
+ },
322
+ {
323
+ content: "Convert all UEs (in this graph/subgraph) to real links",
324
+ callback: async () => {
325
+ if (window.confirm("This will convert all links (in this graph/subgraph) created by Use Everywhere to real links, and delete all the Use Everywhere nodes. Is that what you want?")) {
326
+ shared.linkRenderController.pause("convert");
327
+ try {
328
+ const graph = visible_graph()
329
+ shared.graphAnalyser.modify_graph( graph )
330
+ remove_ue_nodes( graph )
331
+ } finally {
332
+ app.graph.change();
333
+ shared.linkRenderController.unpause()
334
+ }
335
+ }
336
+ }
337
+ },
338
+ {
339
+ content: "Convert all UEs to real links",
340
+ callback: async () => {
341
+ 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?")) {
342
+ shared.linkRenderController.pause("convert");
343
+ try {
344
+ for_all_graphs(shared.graphAnalyser.modify_graph.bind(shared.graphAnalyser))
345
+ for_all_graphs(remove_ue_nodes)
346
+ } finally {
347
+ app.graph.change();
348
+ shared.linkRenderController.unpause()
349
+ }
350
+
351
+ }
352
+ }
353
+ },
354
+ );
355
+ if (shared.graphAnalyser.ambiguities.length) {
356
+ options.push({
357
+ content: "Show UE broadcast clashes",
358
+ callback: async () => {
359
+ alert(shared.graphAnalyser.ambiguities.join("\n"))
360
+ }
361
+ })
362
+ }
363
+ options.push(null); // divider
364
+ return options
365
+ }
366
+
v3-nodes/cg-use-everywhere/js/use_everywhere_subgraph_utils.js ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { app } from "../../scripts/app.js";
2
+ import { ue_callbacks } from "./recursive_callbacks.js";
3
+ import { Logger } from "./use_everywhere_utilities.js";
4
+ import { shared } from "./shared.js";
5
+
6
+ export function visible_graph() { return app.canvas.graph }
7
+
8
+ export function in_visible_graph(node) {
9
+ try {
10
+ if (node.graph) return node.graph.id == app.canvas.graph.id
11
+ else if (node.subgraph) return node.subgraph.id == app.canvas.graph.id
12
+ else Logger.log_problem(`in_visible_graph: ${node.id} has no graph or subgraph`)
13
+ } catch (e) {
14
+ Logger.log_error(e)
15
+ return false
16
+ }
17
+ }
18
+
19
+ export function get_subgraph_input_type(graph, slot) { return graph.inputNode.slots[slot].type }
20
+ export function link_is_from_subgraph_input(link) { return link.origin_id==-10 }
21
+
22
+ export function connection_from_output_as_input(node, slot) {
23
+ try {
24
+ return {
25
+ type : node.outputs[slot].type,
26
+ link : {
27
+ origin_id : node.id,
28
+ origin_slot : slot,
29
+ },
30
+ }
31
+ } catch (e) {
32
+ console.error(e)
33
+ return false
34
+ }
35
+ }
36
+
37
+ export function fix_new_subgraph_node(node) {
38
+ try {
39
+ const subgraph = node.subgraph
40
+ subgraph.inputNode.slots.forEach((slot, i)=>{
41
+ slot.linkIds.forEach((lid) => {
42
+ const link = subgraph.links[lid]
43
+ const target = subgraph.getNodeById(link.target_id)
44
+ const slot_name = target.inputs[link.target_slot].name
45
+ const ue_connectable = target.properties.ue_properties.widget_ue_connectable[slot_name]
46
+ if (ue_connectable) {
47
+ const input_name = node.inputs[i]?.name
48
+ if (slot_name!=input_name) {
49
+ Logger.log_problem("In fix_new_subgraph names don't match")
50
+ } else {
51
+ node.properties.ue_properties.widget_ue_connectable[input_name] = true
52
+ }
53
+ }
54
+ })
55
+ })
56
+ } catch (e) {
57
+ Logger.log_error(e, "in fix_new_subgraph")
58
+ }
59
+ }
60
+
61
+ class WrappedIONode {
62
+ constructor(io_node) {
63
+ this.io_node = io_node;
64
+ this.graph = io_node.subgraph;
65
+ }
66
+
67
+ connect(output_index, input_node, input_index) {
68
+ this.graph.last_link_id += 1
69
+ this.graph.links[this.graph.last_link_id] = new LLink(this.graph.last_link_id, this.io_node.slots[output_index].type, -10, output_index, input_node.id, input_index)
70
+ input_node.inputs[input_index].link = this.graph.last_link_id;
71
+ this.io_node.slots[output_index].linkIds.push(this.graph.last_link_id)
72
+ return this.graph.links[this.graph.last_link_id]
73
+ }
74
+
75
+ getConnectionPos(is_input, slot_number) {
76
+ if (is_input) {
77
+ return [...this.io_node.slots[slot_number].pos]
78
+ } else {
79
+ Logger.log_problem("WrappedIONode.getConnectionPos called for output - not supported");
80
+ }
81
+ }
82
+
83
+ }
84
+ export function wrap_input(io_node) {
85
+ return new WrappedIONode(io_node);
86
+ }