From e1467bda8e6ed2fadb17d21bc217f7dcefbf5345 Mon Sep 17 00:00:00 2001 From: relhamdi Date: Thu, 22 Jan 2026 14:30:13 +0100 Subject: [PATCH] First version --- .editorconfig | 24 + .gitignore | 80 +++ .python-version | 1 + LICENSE | 675 ++++++++++++++++++++++++ README.md | 59 +++ __init__.py | 20 + docs/images/PromptPresetSelector_v1.png | Bin 0 -> 64298 bytes pyproject.toml | 11 + src/__init__.py | 0 src/config.py | 2 + src/nodes/__init__.py | 0 src/nodes/prompt_preset_selector.py | 93 ++++ 12 files changed, 965 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 LICENSE create mode 100644 README.md create mode 100644 __init__.py create mode 100644 docs/images/PromptPresetSelector_v1.png create mode 100644 pyproject.toml create mode 100644 src/__init__.py create mode 100644 src/config.py create mode 100644 src/nodes/__init__.py create mode 100644 src/nodes/prompt_preset_selector.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a5c46c7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +# http://editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true +charset = utf-8 +end_of_line = lf + +[*.md] +trim_trailing_whitespace = false + +[*.bat] +indent_style = tab +end_of_line = crlf + +[LICENSE] +insert_final_newline = false + +[Makefile] +indent_style = tab diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4a9a2d --- /dev/null +++ b/.gitignore @@ -0,0 +1,80 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +# lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Cython debug symbols +cython_debug/ + +############################ + +.vscode/ diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..c8cfe39 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d634ba0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,675 @@ +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + +Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + +Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS + +0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +1. Source Code. + +The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + +The Corresponding Source for a work in source code form is that +same work. + +2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + +4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + +a) The work must carry prominent notices stating that you modified +it, and giving a relevant date. + +b) The work must carry prominent notices stating that it is +released under this License and any conditions added under section +7. This requirement modifies the requirement in section 4 to +"keep intact all notices". + +c) You must license the entire work, as a whole, under this +License to anyone who comes into possession of a copy. This +License will therefore apply, along with any applicable section 7 +additional terms, to the whole of the work, and all its parts, +regardless of how they are packaged. This License gives no +permission to license the work in any other way, but it does not +invalidate such permission if you have separately received it. + +d) If the work has interactive user interfaces, each must display +Appropriate Legal Notices; however, if the Program has interactive +interfaces that do not display Appropriate Legal Notices, your +work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + +a) Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by the +Corresponding Source fixed on a durable physical medium +customarily used for software interchange. + +b) Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by a +written offer, valid for at least three years and valid for as +long as you offer spare parts or customer support for that product +model, to give anyone who possesses the object code either (1) a +copy of the Corresponding Source for all the software in the +product that is covered by this License, on a durable physical +medium customarily used for software interchange, for a price no +more than your reasonable cost of physically performing this +conveying of source, or (2) access to copy the +Corresponding Source from a network server at no charge. + +c) Convey individual copies of the object code with a copy of the +written offer to provide the Corresponding Source. This +alternative is allowed only occasionally and noncommercially, and +only if you received the object code with such an offer, in accord +with subsection 6b. + +d) Convey the object code by offering access from a designated +place (gratis or for a charge), and offer equivalent access to the +Corresponding Source in the same way through the same place at no +further charge. You need not require recipients to copy the +Corresponding Source along with the object code. If the place to +copy the object code is a network server, the Corresponding Source +may be on a different server (operated by you or a third party) +that supports equivalent copying facilities, provided you maintain +clear directions next to the object code saying where to find the +Corresponding Source. Regardless of what server hosts the +Corresponding Source, you remain obligated to ensure that it is +available for as long as needed to satisfy these requirements. + +e) Convey the object code using peer-to-peer transmission, provided +you inform other peers where the object code and Corresponding +Source of the work are being offered to the general public at no +charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + +a) Disclaiming warranty or limiting liability differently from the +terms of sections 15 and 16 of this License; or + +b) Requiring preservation of specified reasonable legal notices or +author attributions in that material or in the Appropriate Legal +Notices displayed by works containing it; or + +c) Prohibiting misrepresentation of the origin of that material, or +requiring that modified versions of such material be marked in +reasonable ways as different from the original version; or + +d) Limiting the use for publicity purposes of names of licensors or +authors of the material; or + +e) Declining to grant rights under trademark law for use of some +trade names, trademarks, or service marks; or + +f) Requiring indemnification of licensors and authors of that +material by anyone who conveys the material (or modified versions of +it) with contractual assumptions of liability to the recipient, for +any liability that these contractual assumptions directly impose on +those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + +8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + +Copyright (C) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) +This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + +The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..8f9b5db --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# ComfyUI - RE-CustomUtils + +A collection of custom nodes for ComfyUI. + +> [!NOTE] +> This project was inspired by the [cookiecutter](https://github.com/Comfy-Org/cookiecutter-comfy-extension) template, though the full architecture is a bit different. + + +## Quickstart + +1. Install [ComfyUI](https://docs.comfy.org/get_started). +2. Install [ComfyUI-Manager](https://github.com/ltdrdata/ComfyUI-Manager) +3. Look up this extension in ComfyUI-Manager. If you are installing manually, clone this repository under `ComfyUI/custom_nodes`. +4. Restart ComfyUI. + + +## Features + +### PromptPresetSelector node + +![PromptPresetSelector v1](docs/images/PromptPresetSelector_v1.png) + +Input: +- `separator` (str), default to | +- `preset_index` (int) in range 1 to 50, default to 1 +- `text` (str), a multiline textarea + +Output: +- `prompt` (str), the formatted prompt +- `preset_index` (int), the selected index + +This node helps introducing variations to a text, usually a prompt, by supporting templates. Basically, you can include a template like this `{% %}` in your `text`, that will hold various options, separated by the `separator`. + +Depending on the chosen `preset_index`, the correct preset option will be parsed and add to the text. + +> Example: +> +``` +This is {% an example | a text | a text %} for {% the PromptPresetSelector | the README | %} +``` + +- With `preset_index` == 1: This is an example for the PromptPresetSelector +- With `preset_index` == 2: This is an text for the README +- With `preset_index` == 3: This is an text for + +The node validates that the `separator` is filled, that you registered the same amount of options in all of the templates, and that you selected a `preset_index` within the length of the configured presets. + +> Example: + +If we fix `preset_index` to 3: + +- *This is **{% a text | a message %}** for **{% the PromptPresetSelector %}*** => Error, first template with 2 presets while the second only got 1 +- *This is **{% a text | a message %}** for **{% the PromptPresetSelector | the README %}*** => Error, only 2 presets were configured + + +## TODO + +- Add JS to transform the preset_index in a dynamic dropdown, based on the configured presets +- Add field to name the different presets \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..236a51e --- /dev/null +++ b/__init__.py @@ -0,0 +1,20 @@ +"""Top-level package for the node pack.""" + +from .src.nodes.prompt_preset_selector import ( + NODE_CLASS_MAPPINGS as PromptPresetSelectorMappings, +) +from .src.nodes.prompt_preset_selector import ( + NODE_DISPLAY_NAME_MAPPINGS as PromptPresetSelectorDisplayMappings, +) + +NODE_CLASS_MAPPINGS = {} +NODE_CLASS_MAPPINGS.update(PromptPresetSelectorMappings) + +NODE_DISPLAY_NAME_MAPPINGS = {} +NODE_DISPLAY_NAME_MAPPINGS.update(PromptPresetSelectorDisplayMappings) + +# Node registration +__all__ = [ + "NODE_CLASS_MAPPINGS", + "NODE_DISPLAY_NAME_MAPPINGS", +] diff --git a/docs/images/PromptPresetSelector_v1.png b/docs/images/PromptPresetSelector_v1.png new file mode 100644 index 0000000000000000000000000000000000000000..679c885cc43812967eba7889fc8da637b7da9f47 GIT binary patch literal 64298 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 G*?$2aGCR8f literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..bd993b8 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,11 @@ +[project] +name = "ComfyUI-RE-CustomUtils" +version = "0.1.0" +description = "A collection of custom nodes for ComfyUI." +license = { file = "LICENSE" } +readme = "README.md" +requires-python = ">=3.10" +dependencies = [] + +[project.urls] +Repository = "https://github.com/relhamdi/ComfyUI-RE-CustomUtils" diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/config.py b/src/config.py new file mode 100644 index 0000000..511d7f8 --- /dev/null +++ b/src/config.py @@ -0,0 +1,2 @@ +# Constants +NODE_CATEGORY = "RE-CustomUtils" diff --git a/src/nodes/__init__.py b/src/nodes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/nodes/prompt_preset_selector.py b/src/nodes/prompt_preset_selector.py new file mode 100644 index 0000000..83b6727 --- /dev/null +++ b/src/nodes/prompt_preset_selector.py @@ -0,0 +1,93 @@ +import re + +from .. import config + + +class PromptPresetSelector: + """ + Selects a variant from inline prompt blocks of the form: + {% option1 | option2 | option3 %} + + The selected option is injected based on the chosen preset index. + Preset indices start at 1. + """ + + CATEGORY = config.NODE_CATEGORY + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "separator": ( + "STRING", + { + "default": "|", + "tooltip": "Preset separator character", + }, + ), + "preset_index": ( + "INT", + { + "default": 1, + "min": 1, + "max": 50, + "step": 1, + }, + ), + "text": ("STRING", {"multiline": True}), + }, + } + + RETURN_TYPES = ("STRING", "INT") + RETURN_NAMES = ("prompt", "preset_index") + FUNCTION = "process" + + TEMPLATE_PATTERN = r"{%(.*?)%}" + + @classmethod + def VALIDATE_INPUTS(cls, separator, preset_index, text): + if not separator: + return "Separator cannot be empty" + + blocks = re.findall(cls.TEMPLATE_PATTERN, text, re.DOTALL) + + expected_count = None + for block in blocks: + parts = [p.strip() for p in block.split(separator)] + if expected_count is None: + expected_count = len(parts) + elif len(parts) != expected_count: + return "All preset blocks must have the same number of options" + + if preset_index > len(parts): + return f"Preset index {preset_index} out of range (max {len(parts)})" + + return True + + def process(self, separator, preset_index, text): + def replace_block(match: re.Match[str]) -> str: + content = match.group(1) + parts = [p.strip() for p in content.split(separator)] + + # index is 1-based + return parts[preset_index - 1] + + def cleanup(text: str) -> str: + text = re.sub(r"\s+,", ",", text) + text = re.sub(r",\s*,", ",", text) + text = re.sub(r"\n\s*\n", "\n", text) + return text.strip() + + result = re.sub(self.TEMPLATE_PATTERN, replace_block, text, flags=re.DOTALL) + result = cleanup(result) + + return (result, preset_index) + + +NODE_CLASS_MAPPINGS = { + "PromptPresetSelector": PromptPresetSelector, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "PromptPresetSelector": "Prompt Preset Selector", +}