diff --git a/lockscreens/kitsune/LICENSE b/lockscreens/kitsune/LICENSE new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/lockscreens/kitsune/LICENSE @@ -0,0 +1,674 @@ + 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/lockscreens/kitsune/PROVENANCE.txt b/lockscreens/kitsune/PROVENANCE.txt new file mode 100644 index 00000000..e822ae6f --- /dev/null +++ b/lockscreens/kitsune/PROVENANCE.txt @@ -0,0 +1,43 @@ +KITSUNE — provenance + +The face is 0xSrcAura's own work, built 2026-10-21 for Jurai shell: +Ase, the seven-tailed kitsune, and her seven tarot folds — his lock +and login composition, first raised on his own machines, preview- +proven the night it was built (typing binds the folds one by one; +at seven the Cast rises; a refusal hurts her and scatters the +cards; the true key transforms her and opens the seal). + +Two roots were written then, and both are preserved in this repo: + + content/Main.qml — the greeter root, seated over SDDM's own + machinery (and, through Ryoku's SddmShim, + over the session lock as well). Unmodified. + faces/lock/Main.qml — the lock-face root, driven by the shell's + threshold `gate` contract. Kept as the + record of the original seat; not installed. + +Her scene (content/gotchi/, the Insei* files and their qmldir), +her shaders (content/shaders/, .frag sources beside the compiled +.qsb), and her palette-and-accents singleton +(content/tw/TransparentWorld.qml) are carried byte-for-byte from +the Jurai shell tree (apsara/mandara/soul/). Nothing of hers was +redrawn, renamed, or reworded for the store. + +The port's own hand, kept as small as the Fūin port's: + + * content/tw/ArOrchestrator.qml — a resting singleton under the + real one's name. In the shell he is the AR stage's living + state; the one scene seat that reads him (InseiHub) is never + raised at the door, so his shape stands here at rest and the + scene files stay untouched. + * content/tw/qmldir — declares the two singletons; a greeter + registers singletons only when their folder stands as a module. + * content/theme.conf + metadata.desktop — the store/greeter + dress: the one config key her greeter reads + (AllowUppercaseLettersInUsernames=false, her original rule) + and her name plate. + * Her ground: the greeter still looks for a theme-local + Backgrounds/wallpaper.jpg under a manager-published still at + /var/lib/valence-greeter/wallpaper.jpg. Neither is bundled — + where neither exists she stands on the veiled dark, exactly + as the sealed resting state was drawn. diff --git a/lockscreens/kitsune/assets/preview.webp b/lockscreens/kitsune/assets/preview.webp new file mode 100644 index 00000000..893a7c2d Binary files /dev/null and b/lockscreens/kitsune/assets/preview.webp differ diff --git a/lockscreens/kitsune/content/Main.qml b/lockscreens/kitsune/content/Main.qml new file mode 100644 index 00000000..fb7a66e8 --- /dev/null +++ b/lockscreens/kitsune/content/Main.qml @@ -0,0 +1,412 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Effects +import "gotchi" + +// KITSUNE — the greeter (built 2026-10-21, the captain's order). +// +// Ase and her seven folds at the outer door — the same composition +// that guards the lock, seated over SDDM's own machinery. The login +// speaks astronaut's exact idiom (the valence theme's Input.qml): +// userModel/sessionModel ComboBoxes on lastIndex, and +// sddm.login(user.toLowerCase(), password, selectedSession) +// because kitsune.conf carries AllowUppercaseLettersInUsernames=false, +// the same rule valence runs. Nothing of SDDM's PAM path is touched. +// +// typing -> one fold binds per character, Ase reacts +// 7 bound -> INVOKE THE SEAL rises (the Cast = login) +// login failed -> she is hurt, the cards scatter (astronaut's +// failed flag + 2s resetError, her ceremony over it) +// login success -> all seven revealed, she transforms, the seal opens +// +// Self-contained by necessity: the greeter runs as user `sddm` and +// cannot read the captain's home. Her palette is baked at install; +// the ground is the manager-published still +// (/var/lib/valence-greeter/wallpaper.jpg, world-readable), with a +// theme-local copy underneath as the fallback. +// The user ComboBox is omitted by design: one captain, one account — +// the username field stands prefilled from userModel.lastUser and +// stays editable. +Rectangle { + id: root + color: "#06070c" + + // Baked palette — matugen's word at install time (matches the + // lock face's live palette the night this was built). + readonly property color palBackground: "#12131c" + readonly property color palText: "#e3e1ef" + readonly property color palPrimary: "#bec2ff" + readonly property color palSecondary: "#ccc1e9" + readonly property color palAlert: "#f43f5e" + + property bool _checking: false + property bool _failed: false + property bool _leaving: false + + // ---- Her fold canon (the scene's own, mirrored for the wiring) -- + readonly property var _foldIds: ["truth","power","wisdom","judgment","life","love","discipline"] + readonly property var _foldLabels: ["THE EYE","THE PHANTOM","THE BLADE","THE SAGE","THE SHROUD","THE FORGE","THE MIRROR"] + readonly property var _foldPrinciples: ["TRUTH","POWER","WISDOM","JUDGMENT","LIFE","LOVE","DISCIPLINE"] + readonly property var _foldColors: ["#4ade80","#f43f5e","#00B4D8","#9B1FA0","#818cf8","#f59e0b","#6366f1"] + + // ---- Ceremony state, driven by the password field ----------------- + readonly property string _pw: passwordField.text + readonly property int _bound: Math.min(7, root._pw.length) + + property var _tapped: [] + property int _activeFoldIdx: -1 + property color _activeFoldColor: "#00B4D8" + property real _activePulse: 0.0 + property int _lastLen: 0 + + NumberAnimation { + id: pulseDecay + target: root; property: "_activePulse" + to: 0.0; duration: 500; easing.type: Easing.OutCubic + running: false + } + + readonly property real _entropy: { + const pw = root._pw; + if (pw.length === 0) return 0; + const uniq = new Set(pw.split("")).size; + const lenScore = Math.min(1, pw.length / 16); + const variety = uniq / pw.length; + return Math.min(1, lenScore * 0.65 + variety * 0.25 + lenScore * variety * 0.3); + } + + function _passwordEdited() { + const n = root._pw.length; + const bound = Math.min(7, n); + if (root._leaving) return; + + if (n > root._lastLen && bound > 0) { + const ci = bound - 1; + root._activeFoldIdx = ci; + root._activeFoldColor = root._foldColors[ci]; + root._activePulse = 1.0; + pulseDecay.restart(); + scene.aseReact(Qt.color(root._foldColors[ci])); + } + root._lastLen = n; + root._tapped = root._foldIds.slice(0, bound); + + if (root._failed || root._checking) return; + if (bound === 0) + root._setWhisper("the sigils await · read the cards · invoke your sequence"); + else if (bound < 7) + root._setWhisper(root._foldLabels[bound - 1] + " · " + root._foldPrinciples[bound - 1] + " · bound to your will · " + bound + " of 7"); + else + root._setWhisper("the sequence is complete · invoke the seal to pass"); + } + + // ---- The whisper (her typewriter, 22ms a letter) ------------------ + property string _whisperTarget: "" + property string _whisperDisplay: "" + property int _whisperIdx: 0 + + function _setWhisper(msg) { + root._whisperTarget = msg; + root._whisperDisplay = ""; + root._whisperIdx = 0; + whisperTimer.restart(); + } + Timer { + id: whisperTimer; interval: 22; repeat: true + onTriggered: { + if (root._whisperIdx < root._whisperTarget.length) { + root._whisperDisplay += root._whisperTarget[root._whisperIdx]; + root._whisperIdx++; + } else { stop(); } + } + } + + // ---- Time driver --------------------------------------------------- + property real _time: 0.0 + NumberAnimation on _time { + from: 0; to: 100000; duration: 100000000 + loops: Animation.Infinite; running: true + } + + // ---- The ground ----------------------------------------------------- + Image { + anchors.fill: parent + source: "Backgrounds/wallpaper.jpg" + fillMode: Image.PreserveAspectCrop + asynchronous: true + } + Image { + anchors.fill: parent + source: "file:///var/lib/valence-greeter/wallpaper.jpg" + fillMode: Image.PreserveAspectCrop + asynchronous: true + layer.enabled: true + layer.effect: MultiEffect { + blurEnabled: true + blur: root._leaving ? 0.3 : 0.9 + blurMax: 64 + brightness: root._leaving ? -0.05 : -0.25 + Behavior on blur { NumberAnimation { duration: 380; easing.type: Easing.OutQuad } } + Behavior on brightness { NumberAnimation { duration: 380; easing.type: Easing.OutQuad } } + } + } + Rectangle { + anchors.fill: parent + color: Qt.alpha(root.palBackground, 0.55) + } + + // ---- Her scene (rides high — the fields hold the foot) -------------- + InseiScene { + id: scene + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: parent.height * 0.84 + + layout: [] + tappedFolds: root._tapped + lockedCount: root._bound + entropy: root._entropy + sequenceStr: root._tapped.join(":") + time: root._time + activeFoldIdx: root._activeFoldIdx + activeFoldColor: root._activeFoldColor + activePulse: root._activePulse + whisper: root._whisperDisplay + hasError: root._failed + isLoading: root._checking + isUnlocked: root._leaving + readyToCast: root._bound >= 7 && !root._checking && !root._leaving + + onCastRequested: root.attemptLogin() + } + + // ---- The login (astronaut's call, letter for letter) ----------------- + function attemptLogin() { + if (passwordField.text.length === 0 || root._checking || root._leaving) return; + root._checking = true; + root._setWhisper("the binding speaks · the seal yields…"); + const u = usernameField.text; + sddm.login(config.AllowUppercaseLettersInUsernames == "false" ? u.toLowerCase() : u, + passwordField.text, sessionSelect.selectedSession); + } + + Connections { + target: sddm + function onLoginSucceeded() { + root._checking = false; + root._leaving = true; + root._tapped = root._foldIds.slice(0, 7); + scene.aseTransform(); + root._setWhisper("the seal opens · welcome, 0xSrcAura"); + } + function onLoginFailed() { + root._checking = false; + root._failed = true; + scene.aseHurt(); + shake.restart(); + passwordField.text = ""; + root._lastLen = 0; + root._tapped = []; + resetError.restart(); + failWhisper.restart(); + passwordField.forceActiveFocus(); + } + } + Timer { id: resetError; interval: 2000; repeat: false; onTriggered: root._failed = false } + Timer { + id: failWhisper; interval: 150; repeat: false + onTriggered: root._setWhisper("the binding refuses · login failed") + } + + // ---- The fields, at the foot of her scene ----------------------------- + Column { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 36 + spacing: 9 + z: 2 + + Text { + anchors.horizontalCenter: parent.horizontalCenter + visible: root._failed + text: "LOGIN FAILED" + color: root.palAlert + font.family: "3270 Nerd Font Mono, Fira Code, monospace" + font.pixelSize: 12 + font.letterSpacing: 2 + } + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + width: 300 + height: 40 + radius: 20 + color: Qt.alpha("#0a0a12", 0.55) + border.width: 1 + border.color: usernameField.activeFocus ? Qt.alpha(root.palPrimary, 0.6) : Qt.alpha(root.palText, 0.22) + Behavior on border.color { ColorAnimation { duration: 200 } } + + TextField { + id: usernameField + anchors.fill: parent + anchors.leftMargin: 18 + anchors.rightMargin: 18 + text: userModel.lastUser + color: root.palText + font.family: "3270 Nerd Font Mono, Fira Code, monospace" + font.pixelSize: 13 + font.letterSpacing: 2 + horizontalAlignment: TextInput.AlignHCenter + verticalAlignment: TextInput.AlignVCenter + background: Item {} + onAccepted: passwordField.forceActiveFocus() + } + } + + Rectangle { + id: pill + anchors.horizontalCenter: parent.horizontalCenter + width: 300 + height: 46 + radius: 23 + color: Qt.alpha("#0a0a12", 0.55) + border.width: 1 + border.color: root._failed + ? Qt.alpha(root.palAlert, 0.8) + : (passwordField.activeFocus ? Qt.alpha(root.palPrimary, 0.6) : Qt.alpha(root.palText, 0.22)) + Behavior on border.color { ColorAnimation { duration: 200 } } + transform: Translate { id: pillShift } + + SequentialAnimation { + id: shake + NumberAnimation { target: pillShift; property: "x"; to: -7; duration: 45 } + NumberAnimation { target: pillShift; property: "x"; to: 6; duration: 55 } + NumberAnimation { target: pillShift; property: "x"; to: -4; duration: 55 } + NumberAnimation { target: pillShift; property: "x"; to: 2; duration: 50 } + NumberAnimation { target: pillShift; property: "x"; to: 0; duration: 45 } + } + opacity: root._leaving ? 0 : 1 + Behavior on opacity { NumberAnimation { duration: 300 } } + + TextField { + id: passwordField + anchors.fill: parent + anchors.leftMargin: 18 + anchors.rightMargin: 18 + echoMode: TextInput.Password + placeholderText: "PASSWORD" + placeholderTextColor: Qt.alpha(root.palText, 0.35) + color: root.palText + font.family: "3270 Nerd Font Mono, Fira Code, monospace" + font.pixelSize: 14 + font.letterSpacing: 2 + horizontalAlignment: TextInput.AlignHCenter + verticalAlignment: TextInput.AlignVCenter + background: Item {} + enabled: !root._checking + onTextChanged: root._passwordEdited() + onAccepted: root.attemptLogin() + } + } + + // The session, astronaut's ComboBox idiom in quiet dress. + ComboBox { + id: sessionSelect + property var selectedSession: currentIndex + anchors.horizontalCenter: parent.horizontalCenter + model: sessionModel + currentIndex: model.lastIndex + textRole: "name" + background: Item {} + indicator: Item {} + contentItem: Text { + text: "Session (" + sessionSelect.currentText + ")" + color: Qt.alpha(root.palText, sessionSelect.hovered ? 0.9 : 0.55) + font.family: "3270 Nerd Font Mono, Fira Code, monospace" + font.pixelSize: 11 + font.letterSpacing: 1.5 + horizontalAlignment: Text.AlignHCenter + } + delegate: ItemDelegate { + width: sessionSelect.width + contentItem: Text { + text: model.name + color: root.palText + font.family: "3270 Nerd Font Mono, Fira Code, monospace" + font.pixelSize: 11 + verticalAlignment: Text.AlignVCenter + } + background: Rectangle { + color: highlighted ? Qt.alpha(root.palPrimary, 0.25) : "transparent" + } + } + popup: Popup { + y: -implicitHeight - 4 + width: sessionSelect.width + padding: 6 + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: sessionSelect.popup.visible ? sessionSelect.delegateModel : null + currentIndex: sessionSelect.highlightedIndex + } + background: Rectangle { + radius: 8 + color: Qt.alpha("#0a0a12", 0.92) + border.width: 1 + border.color: Qt.alpha(root.palText, 0.18) + } + } + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: "0XSRCAURA" + color: Qt.alpha(root.palText, 0.55) + opacity: root._leaving ? 0 : 1 + Behavior on opacity { NumberAnimation { duration: 150 } } + font.family: "3270 Nerd Font Mono, Fira Code, monospace" + font.pixelSize: 13 + font.letterSpacing: 4 + } + } + + // ---- The door's other duties: power ---------------------------------- + Row { + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.rightMargin: 22 + anchors.bottomMargin: 16 + spacing: 18 + z: 2 + + Text { + text: "reboot" + color: Qt.alpha(root.palText, rebootArea.containsMouse ? 0.9 : 0.45) + font.family: "3270 Nerd Font Mono, Fira Code, monospace" + font.pixelSize: 11 + font.letterSpacing: 1.5 + MouseArea { id: rebootArea; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: sddm.reboot() } + } + Text { + text: "power off" + color: Qt.alpha(root.palText, powerArea.containsMouse ? 0.9 : 0.45) + font.family: "3270 Nerd Font Mono, Fira Code, monospace" + font.pixelSize: 11 + font.letterSpacing: 1.5 + MouseArea { id: powerArea; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: sddm.powerOff() } + } + } + + // ---- Entrance --------------------------------------------------------- + Timer { + id: manifestTimer; interval: 60; repeat: false + onTriggered: { + scene.aseWake(); + root._setWhisper("the sigils await · read the cards · invoke your sequence"); + passwordField.forceActiveFocus(); + } + } + Component.onCompleted: manifestTimer.restart() +} diff --git a/lockscreens/kitsune/content/gotchi/InseiCompanion.qml b/lockscreens/kitsune/content/gotchi/InseiCompanion.qml new file mode 100644 index 00000000..f0bd1a8b --- /dev/null +++ b/lockscreens/kitsune/content/gotchi/InseiCompanion.qml @@ -0,0 +1,126 @@ +/* InseiCompanion.qml — Ase · Digital Kitsune Companion + * =================================================== + * Ase is a 7-tailed digital fox spirit rendered via SDF shader. + * Item is 280×320 — generously sized so tails and aura have UV + * space to render without hitting the texture boundary. + * clip is never set — Item does not clip by default. + * + * Each tail tip is tinted with its fold color via per-tail uniforms. + */ +import QtQuick +import "../tw" as TW + +Item { + id: root + + implicitWidth: 320 + implicitHeight: 260 + + // ── Public API ──────────────────────────────────────────────────── + property color foldColor: TW.TransparentWorld.accentWater + property real entropy: 0.0 + property real time: 0.0 + + // State machine + property string _state: "idle" + + function wake() { _state = "wake"; wakeAnim.restart() } + function react(col) { foldColor = col; _state = "react"; reactAnim.restart() } + function hurt() { _state = "hurt"; hurtAnim.restart() } + function aseTransform() { _state = "transform"; transformAnim.restart() } + function dissolve() { _state = "dissolve"; dissolveAnim.restart() } + function resetIdle() { _state = "idle" } + + // ── Shader ──────────────────────────────────────────────────────── + ShaderEffect { + anchors.fill: parent + blending: true + + property real time: root.time + property real entropy: root.entropy + property real mood_joy: 0.0 + property real mood_sad: 0.0 + property real mood_sleep: 0.0 + property real fold_r: root.foldColor.r + property real fold_g: root.foldColor.g + property real fold_b: root.foldColor.b + property real w: width + property real h: height + property real tailCount: 7.0 + + // Per-tail fold colors — driven from TransparentWorld, updated to new Sibyl palette + // tail 0 → EYE / TRUTH #00C2FF electric cerulean + property real tail0_r: TW.TransparentWorld.accentEye.r + property real tail0_g: TW.TransparentWorld.accentEye.g + property real tail0_b: TW.TransparentWorld.accentEye.b + // tail 1 → PHANTOM / POWER #B44FE8 creative violet + property real tail1_r: TW.TransparentWorld.accentPhantom.r + property real tail1_g: TW.TransparentWorld.accentPhantom.g + property real tail1_b: TW.TransparentWorld.accentPhantom.b + // tail 2 → BLADE / WISDOM #FF3CAC hot magenta + property real tail2_r: TW.TransparentWorld.accentBlade.r + property real tail2_g: TW.TransparentWorld.accentBlade.g + property real tail2_b: TW.TransparentWorld.accentBlade.b + // tail 3 → SAGE / JUDGMENT #FFB300 sun amber + property real tail3_r: TW.TransparentWorld.accentSage.r + property real tail3_g: TW.TransparentWorld.accentSage.g + property real tail3_b: TW.TransparentWorld.accentSage.b + // tail 4 → SHROUD / LIFE #00FFC8 bioluminescent teal + property real tail4_r: TW.TransparentWorld.accentShroud.r + property real tail4_g: TW.TransparentWorld.accentShroud.g + property real tail4_b: TW.TransparentWorld.accentShroud.b + // tail 5 → FORGE / LOVE #FF6B35 volcanic orange-red + property real tail5_r: TW.TransparentWorld.accentForgeFold.r + property real tail5_g: TW.TransparentWorld.accentForgeFold.g + property real tail5_b: TW.TransparentWorld.accentForgeFold.b + // tail 6 → MIRROR / DISCIPLINE #C8B8FF pale lavender + property real tail6_r: TW.TransparentWorld.accentMirror.r + property real tail6_g: TW.TransparentWorld.accentMirror.g + property real tail6_b: TW.TransparentWorld.accentMirror.b + + fragmentShader: "../shaders/InseiKitsune.frag.qsb" + } + + // ── Tap to stroke ───────────────────────────────────────────────── + TapHandler { + onTapped: { + var req = new XMLHttpRequest() + req.open("POST", "http://127.0.0.1:8001/api/ase/interact", true) + req.setRequestHeader("Content-Type", "application/json") + req.send(JSON.stringify({ action: "stroke" })) + root.react(TW.TransparentWorld.accentWater) + } + } + + // ── Animations ──────────────────────────────────────────────────── + SequentialAnimation { + id: wakeAnim + NumberAnimation { target: root; property: "opacity"; from: 0; to: 1; duration: 1000 } + ScriptAction { script: root._state = "idle" } + } + + SequentialAnimation { + id: reactAnim + NumberAnimation { target: root; property: "entropy"; to: 0.8; duration: 200 } + NumberAnimation { target: root; property: "entropy"; to: 0.0; duration: 400 } + ScriptAction { script: root._state = "idle" } + } + + SequentialAnimation { + id: hurtAnim + NumberAnimation { target: root; property: "entropy"; to: 1.0; duration: 80 } + NumberAnimation { target: root; property: "entropy"; to: 0.0; duration: 600 } + ScriptAction { script: root._state = "idle" } + } + + SequentialAnimation { + id: transformAnim + PauseAnimation { duration: 1000 } + ScriptAction { script: root.dissolve() } + } + + SequentialAnimation { + id: dissolveAnim + NumberAnimation { target: root; property: "opacity"; to: 0; duration: 800 } + } +} diff --git a/lockscreens/kitsune/content/gotchi/InseiGotchi.qml b/lockscreens/kitsune/content/gotchi/InseiGotchi.qml new file mode 100644 index 00000000..85da7e03 --- /dev/null +++ b/lockscreens/kitsune/content/gotchi/InseiGotchi.qml @@ -0,0 +1,652 @@ +/* InseiGotchi.qml — The Ase Companion & Tarot Gate · Holographic Scene + * ============================================================ + * Ase floats in open space above a holographic Tarot reveal. + * No device shell. Just the scene, the companion, and the sequence. + * ============================================================ */ +pragma ComponentBehavior: Bound +import QtQuick +import "../tw" as TW + +Item { + id: aseGotchi + + property bool active: true + readonly property real entropyOut: _entropy + + signal authenticated(string token) + signal catalystFailed() + + anchors.fill: parent + visible: active + + // ── Phase ───────────────────────────────────────────────────────── + property int _phase: 0 + property bool _isLoading: _phase === 2 + property bool _isUnlocked: _phase === 3 + property bool _hasError: false + + // ── Tarot state ─────────────────────────────────────────────────── + property var _layout: [] + property string _challenge: "" + property var _sequence: [] + property var _tappedFolds: [] // fold IDs in tap order — drives card flip + property int _lockedCount: 0 + property real _entropy: 0.0 + property bool _configured: false + property int _resetKey: 0 // increment to force card re-evaluation on reset + property bool _readyToCast: false // true when all 7 drawn, waiting for CAST + + readonly property string _sequenceStr: _sequence.join(":") + + property bool _setupMode: false + property var _setupSeq1: [] + property var _setupSeq2: [] + property bool _setupPhase2: false + + property int _failCount: 0 + property bool _lockdown: false + property int _lockdownSecs: 30 + + // ── Active fold ─────────────────────────────────────────────────── + property int _activeFoldIdx: -1 + property color _activeFoldColor: TW.TransparentWorld.accentWater + property real _activePulse: 0.0 + + NumberAnimation { + id: pulseDecay + target: aseGotchi; property: "_activePulse" + to: 0.0; duration: 500; easing.type: Easing.OutCubic + running: false + } + + // ── Breach ──────────────────────────────────────────────────────── + property real _breach: 0.0 + NumberAnimation { + id: breachAnim + target: aseGotchi; property: "_breach" + from: 0.0; to: 1.0; duration: 900 + easing.type: Easing.OutCubic; running: false + } + + // ── Whisper ─────────────────────────────────────────────────────── + property string _whisperTarget: "the sigils await..." + property string _whisperDisplay: "" + property int _whisperIdx: 0 + property bool _whisperStreaming: false // true when AI is streaming + + function _setWhisper(msg) { + _whisperTarget = msg + _whisperDisplay = "" + _whisperIdx = 0 + _whisperStreaming = false + whisperTimer.restart() + } + + // AI whisper — streams from local Ollama via kasugai dispatch + // Falls back to static msg if daemon is offline or response is slow + function _whisperAI(context, fallback) { + if (_daemonState !== "alive") { _setWhisper(fallback); return } + + _whisperDisplay = "" + _whisperStreaming = true + whisperTimer.stop() + + var prompt = "You are Ase, a 7-tailed kitsune spirit guarding the Akashic Gate for SourceAura. " + + "Speak in 1 sentence. Mystical, direct, sorcerer register. No markdown. Just the whisper. " + + "Context: " + context + + var req = new XMLHttpRequest() + req.onreadystatechange = function() { + if (req.readyState === XMLHttpRequest.LOADING || req.readyState === XMLHttpRequest.DONE) { + // Stream tokens as they arrive + var raw = req.responseText || "" + // Parse SSE lines — each line is "data: {token}" or the full response + var lines = raw.split("\n") + var newText = "" + for (var i = 0; i < lines.length; i++) { + var line = lines[i].trim() + if (line.startsWith("data: ")) { + try { + var d = JSON.parse(line.slice(6)) + if (d.token) newText += d.token + else if (d.response) newText = d.response + } catch(e) {} + } + } + if (newText !== "") aseGotchi._whisperDisplay = newText.slice(0, 120) + } + if (req.readyState === XMLHttpRequest.DONE) { + aseGotchi._whisperStreaming = false + // If nothing came back, use fallback + if (aseGotchi._whisperDisplay === "") aseGotchi._setWhisper(fallback) + } + } + req.onerror = function() { aseGotchi._whisperStreaming = false; aseGotchi._setWhisper(fallback) } + + // 3s timeout — gate must be fast + var timeoutTimer = Qt.createQmlObject('import QtQuick; Timer { interval: 3000; repeat: false }', aseGotchi) + timeoutTimer.triggered.connect(function() { + if (aseGotchi._whisperStreaming && aseGotchi._whisperDisplay === "") { + req.abort() + aseGotchi._whisperStreaming = false + aseGotchi._setWhisper(fallback) + } + timeoutTimer.destroy() + }) + timeoutTimer.start() + + req.open("POST", "http://127.0.0.1:8001/api/kasugai/dispatch", true) + req.setRequestHeader("Content-Type", "application/json") + req.send(JSON.stringify({ message: prompt, stream: true, max_tokens: 40 })) + } + + Timer { + id: whisperTimer; interval: 22; repeat: true + onTriggered: { + if (aseGotchi._whisperIdx < aseGotchi._whisperTarget.length) { + aseGotchi._whisperDisplay += aseGotchi._whisperTarget[aseGotchi._whisperIdx] + aseGotchi._whisperIdx++ + } else { stop() } + } + } + + // ── Daemon state ────────────────────────────────────────────────── + property string _daemonState: "checking" + + function _fetchThreats() { + var req = new XMLHttpRequest() + req.onreadystatechange = function() { + if (req.readyState !== XMLHttpRequest.DONE) return + if (req.status === 200) { + try { + var d = JSON.parse(req.responseText) + aseGotchi._daemonState = d.status === "alive" ? "alive" : "degraded" + } catch(e) { aseGotchi._daemonState = "degraded" } + } else { aseGotchi._daemonState = "dead" } + } + req.onerror = function() { aseGotchi._daemonState = "dead" } + req.open("GET", "http://127.0.0.1:8001/api/health/ready", true) + req.send() + } + + // ── Lockdown ────────────────────────────────────────────────────── + Timer { + id: lockdownTimer; interval: 1000; repeat: true; running: aseGotchi._lockdown + onTriggered: { + aseGotchi._lockdownSecs-- + aseGotchi._setWhisper("binding locked · " + aseGotchi._lockdownSecs + "s until the seal breathes again") + if (aseGotchi._lockdownSecs <= 0) { + aseGotchi._lockdown = false + aseGotchi._failCount = 0 + aseGotchi._lockdownSecs = 30 + aseGotchi._phase = 1 + aseGotchi._setWhisper("the seal breathes · read the sigils · invoke again") + numpadFocus.forceActiveFocus() + } + } + } + + // ── Entrance / exit ─────────────────────────────────────────────── + property real _scale: 0.0 + property real _opacity: 0.0 + Behavior on _scale { NumberAnimation { duration: 400; easing.type: Easing.OutBack; easing.overshoot: 0.25 } } + Behavior on _opacity { NumberAnimation { duration: 300 } } + + Component.onCompleted: { if (active) _manifest() } + onActiveChanged: { if (active) _manifest(); else _dismiss() } + + function _manifest() { + _phase = 0 + _sequence = [] + _tappedFolds = [] + _lockedCount = 0 + _entropy = 0.0 + _hasError = false + _failCount = 0 + _lockdown = false + _lockdownSecs = 30 + _setupMode = false + _setupSeq1 = [] + _setupSeq2 = [] + _setupPhase2 = false + _activeFoldIdx = -1 + _activePulse = 0.0 + _breach = 0.0 + _readyToCast = false + _resetKey = 0 + _whisperAI("the gate opens for SourceAura · greet them as they approach", + "the sigils await · read the cards · invoke your sequence") + _fetchThreats() + _fetchChallenge() + manifestTimer.restart() + } + + function _dismiss() { + _scale = 0.0 + _opacity = 0.0 + } + + Timer { + id: manifestTimer; interval: 60; repeat: false + onTriggered: { + aseGotchi._phase = 1 + aseGotchi._scale = 1.0 + aseGotchi._opacity = 1.0 + scene.aseWake() + numpadFocus.forceActiveFocus() + } + } + + // ── Challenge fetch ─────────────────────────────────────────────── + function _fetchChallenge() { + var req = new XMLHttpRequest() + req.onreadystatechange = function() { + if (req.readyState !== XMLHttpRequest.DONE) return + if (req.status === 200) { + try { + var d = JSON.parse(req.responseText) + aseGotchi._challenge = d.challenge || "" + aseGotchi._layout = d.layout || [] + aseGotchi._configured = d.configured === true + if (aseGotchi._configured) { + aseGotchi._setupMode = false + aseGotchi._setWhisper("the sigils await · read the cards · invoke your sequence") + } else { + aseGotchi._setupMode = true + aseGotchi._setWhisper("the binding is unwritten · inscribe your sequence across the seven folds") + } + } catch(e) {} + } else { + aseGotchi._setWhisper("the daemon sleeps · the gate cannot hear you") + } + } + req.onerror = function() { aseGotchi._setWhisper("gate offline · daemon unreachable") } + req.open("GET", "http://127.0.0.1:8001/api/auth/gate/challenge", true) + req.send() + } + + // ── Fold helpers ────────────────────────────────────────────────── + readonly property var _canonicalOrder: ["truth","power","wisdom","judgment","life","love","discipline"] + readonly property var _foldColors: [ + "#4ade80","#f43f5e","#00B4D8","#9B1FA0","#818cf8","#f59e0b","#6366f1" + ] + // Kasugai label — matches backend FOLDS and InseiScene + readonly property var _foldLabelMap: ({ + "truth": "THE EYE", + "power": "THE PHANTOM", + "wisdom": "THE BLADE", + "judgment": "THE SAGE", + "life": "THE SHROUD", + "love": "THE FORGE", + "discipline": "THE MIRROR" + }) + // Principle name + readonly property var _principleMap: ({ + "truth": "TRUTH", + "power": "POWER", + "wisdom": "WISDOM", + "judgment": "JUDGMENT", + "life": "LIFE", + "love": "LOVE", + "discipline": "DISCIPLINE" + }) + + function _canonicalIndex(foldId) { + for (var i = 0; i < _canonicalOrder.length; i++) + if (_canonicalOrder[i] === foldId) return i + return 0 + } + + // ── Card reset helper — clears sequence and forces flip-back ───── + function _resetCards() { + _sequence = [] + _tappedFolds = [] + _lockedCount = 0 + _entropy = 0.0 + _activeFoldIdx = -1 + _activePulse = 0.0 + _setupSeq1 = [] + _setupSeq2 = [] + _setupPhase2 = false + _readyToCast = false + _resetKey = _resetKey + 1 // force delegate re-evaluation + } + + // ── Input ───────────────────────────────────────────────────────── + + function _onNumpad(key) { + if (_lockdown || _phase !== 1) return + var num = parseInt(key) + if (isNaN(num) || num < 1 || num > 7) return + + var foldId = "" + for (var i = 0; i < _layout.length; i++) { + if (parseInt(_layout[i].position) === num) { foldId = _layout[i].id; break } + } + if (!foldId) return + + if (_setupMode) _handleSetupInput(foldId) + else _handleAuthInput(foldId) + } + + function _handleAuthInput(foldId) { + var seq = _sequence.slice() + if (seq.indexOf(foldId) !== -1) { + _setWhisper(_foldLabelMap[foldId] + " · already bound · choose another") + return + } + seq.push(foldId) + _sequence = seq + _tappedFolds = seq.slice() + _lockedCount = seq.length + _entropy = seq.length / 7.0 + + var ci = _canonicalIndex(foldId) + _activeFoldIdx = ci + _activeFoldColor = Qt.color(_foldColors[ci]) + _activePulse = 1.0 + pulseDecay.restart() + + scene.aseReact(Qt.color(_foldColors[ci])) + _setWhisper(_foldLabelMap[foldId] + " · " + _principleMap[foldId] + " · bound to your will · " + seq.length + " of 7") + + if (seq.length === 7) { + _readyToCast = true + _whisperAI("all seven folds are bound · the sequence is complete · speak the invocation", + "the sequence is complete · invoke the seal to pass") + } + } + + // ── Cast — fires verify after all 7 drawn ───────────────────────── + function _cast() { + if (_lockdown || _phase !== 1) return + if (_setupMode) { + if (_setupPhase2 && _setupSeq2.length === 7) { + _phase = 2 + _setWhisper("the binding speaks · the seal yields…") + scene.aseTransform() + breachAnim.restart() + _registerSequence(_setupSeq1, _setupSeq2) + } else if (!_setupPhase2 && _setupSeq1.length === 7) { + _setupPhase2 = true + _tappedFolds = [] + _lockedCount = 0 + _entropy = 0.0 + _activeFoldIdx = -1 + _readyToCast = false + _setWhisper("the first binding is sealed · draw the same sequence again to confirm") + } + } else { + if (_sequence.length === 7) { + _phase = 2 + _setWhisper("the binding speaks · the seal yields…") + scene.aseTransform() + breachAnim.restart() + _verifySequence(_sequence) + } + } + } + + function _handleSetupInput(foldId) { + var ci = _canonicalIndex(foldId) + _activeFoldIdx = ci + _activeFoldColor = Qt.color(_foldColors[ci]) + _activePulse = 1.0 + pulseDecay.restart() + scene.aseReact(Qt.color(_foldColors[ci])) + + if (!_setupPhase2) { + var s1 = _setupSeq1.slice() + if (s1.indexOf(foldId) !== -1) { + _setWhisper(_foldLabelMap[foldId] + " · already bound · choose another") + return + } + s1.push(foldId) + _setupSeq1 = s1 + _tappedFolds = s1.slice() + _lockedCount = s1.length + _entropy = s1.length / 7.0 + _setWhisper(_foldLabelMap[foldId] + " · " + _principleMap[foldId] + " · " + s1.length + " of 7 inscribed") + if (s1.length === 7) { + _readyToCast = true + _setWhisper("the first binding is written · invoke to seal it") + } + } else { + var s2 = _setupSeq2.slice() + if (s2.indexOf(foldId) !== -1) { + _setWhisper(_foldLabelMap[foldId] + " · already bound · choose another") + return + } + s2.push(foldId) + _setupSeq2 = s2 + _tappedFolds = s2.slice() + _lockedCount = s2.length + _entropy = s2.length / 7.0 + _setWhisper(_foldLabelMap[foldId] + " · CONFIRM · " + s2.length + " of 7") + if (s2.length === 7) { + _readyToCast = true + _setWhisper("the confirmation is written · invoke to bind the seal forever") + } + } + } + + function _verifySequence(seq) { + var r = new XMLHttpRequest() + r.onreadystatechange = function() { + if (r.readyState !== XMLHttpRequest.DONE) return + if (r.status === 200) { + try { + var d = JSON.parse(r.responseText) + aseGotchi._phase = 3 + aseGotchi._whisperAI("SourceAura has passed through the Akashic Gate · welcome them", + "the seal opens · welcome, SourceAura") + aseGotchi._triggerUnlock(d.session_token || "") + } catch(e) { + aseGotchi._deflect("the binding failed to parse · " + e) + } + } else { + var detail = "" + try { detail = JSON.parse(r.responseText).detail || r.status } catch(e) { detail = r.status } + aseGotchi._deflect(detail) + } + } + r.onerror = function() { aseGotchi._deflect("the daemon sleeps · the gate cannot hear you") } + r.open("POST", "http://127.0.0.1:8001/api/auth/gate/verify", true) + r.setRequestHeader("Content-Type", "application/json") + r.send(JSON.stringify({ sequence: seq, challenge: aseGotchi._challenge })) + } + + function _registerSequence(seq1, seq2) { + var r = new XMLHttpRequest() + r.onreadystatechange = function() { + if (r.readyState !== XMLHttpRequest.DONE) return + if (r.status === 200) { + aseGotchi._setupMode = false + aseGotchi._configured = true + aseGotchi._phase = 3 + aseGotchi._setWhisper("the seal is bound · the gate remembers your will, SourceAura") + aseGotchi._triggerUnlock("setup-complete") + } else { + aseGotchi._deflect("the binding failed · sequences do not match · begin again") + aseGotchi._setupPhase2 = false + aseGotchi._setupSeq1 = [] + aseGotchi._setupSeq2 = [] + aseGotchi._lockedCount = 0 + } + } + r.open("POST", "http://127.0.0.1:8001/api/auth/gate/setup", true) + r.setRequestHeader("Content-Type", "application/json") + r.send(JSON.stringify({ sequence: seq1, confirm: seq2, challenge: aseGotchi._challenge })) + } + + function _triggerUnlock(token) { + unlockTimer.token = token + unlockTimer.restart() + } + + Timer { + id: unlockTimer; property string token: ""; interval: 1000; repeat: false + onTriggered: { + aseGotchi._scale = 0.0 + aseGotchi._opacity = 0.0 + releaseTimer.token = token + releaseTimer.restart() + } + } + + Timer { + id: releaseTimer; property string token: ""; interval: 400; repeat: false + onTriggered: aseGotchi.authenticated(token) + } + + function _deflect(msg) { + _setWhisper(msg) + _hasError = true + _phase = 1 + _resetCards() + scene.aseHurt() + _failCount++ + catalystFailed() + if (_failCount >= 3) { + _lockdown = true + _lockdownSecs = 30 + _phase = 0 + _whisperAI("the binding has been violated three times · speak a warning of consequence", + "THE BINDING SEVERS · resonance locked · 30s") + } else { + errorResetTimer.restart() + numpadFocus.forceActiveFocus() + } + } + + Timer { + id: errorResetTimer; interval: 2500; repeat: false + onTriggered: { + aseGotchi._hasError = false + aseGotchi._fetchChallenge() + } + } + + // ── Backdrop ────────────────────────────────────────────────────── + Rectangle { + anchors.fill: parent + color: Qt.rgba(0, 0, 0, aseGotchi._phase >= 1 ? 0.65 : 0.0) + Behavior on color { ColorAnimation { duration: 500 } } + } + + // ── Scene ───────────────────────────────────────────────────────── + InseiScene { + id: scene + anchors.fill: parent + scale: aseGotchi._scale + opacity: aseGotchi._opacity + transformOrigin: Item.Center + + readyToCast: aseGotchi._readyToCast + lockedCount: aseGotchi._lockedCount + entropy: aseGotchi._entropy + hasError: aseGotchi._hasError + isUnlocked: aseGotchi._isUnlocked + isLoading: aseGotchi._isLoading + whisper: aseGotchi._whisperDisplay + sequenceStr: aseGotchi._sequenceStr + layout: aseGotchi._layout + tappedFolds: aseGotchi._tappedFolds + time: aseGotchi._time + activeFoldIdx: aseGotchi._activeFoldIdx + activeFoldColor: aseGotchi._activeFoldColor + activePulse: aseGotchi._activePulse + breach: aseGotchi._breach + setupMode: aseGotchi._setupMode + setupPhase2: aseGotchi._setupPhase2 + resetKey: aseGotchi._resetKey + + onCardTapped: function(foldId, position) { + if (_setupMode) _handleSetupInput(foldId) + else _handleAuthInput(foldId) + } + onCastRequested: aseGotchi._cast() + } + + // ── Time driver ─────────────────────────────────────────────────── + property real _time: 0.0 + NumberAnimation on _time { + from: 0; to: 100000; duration: 100000000 + loops: Animation.Infinite; running: aseGotchi.active + } + + // ── Numpad focus trap ───────────────────────────────────────────── + Item { + id: numpadFocus + anchors.fill: parent + focus: true + + Keys.onPressed: function(event) { + var k = event.key + var num = 0 + if (k === Qt.Key_1 || k === Qt.Key_KP_1) num = 1 + else if (k === Qt.Key_2 || k === Qt.Key_KP_2) num = 2 + else if (k === Qt.Key_3 || k === Qt.Key_KP_3) num = 3 + else if (k === Qt.Key_4 || k === Qt.Key_KP_4) num = 4 + else if (k === Qt.Key_5 || k === Qt.Key_KP_5) num = 5 + else if (k === Qt.Key_6 || k === Qt.Key_KP_6) num = 6 + else if (k === Qt.Key_7 || k === Qt.Key_KP_7) num = 7 + + if (num > 0) { + aseGotchi._onNumpad(num.toString()) + event.accepted = true + + } else if (k === Qt.Key_B) { + // B — dev bypass (local only) + if (!aseGotchi._lockdown) { + aseGotchi._phase = 3 + aseGotchi._setWhisper("sovereign passage · the seal yields") + aseGotchi._triggerUnlock("bypass-token") + } + event.accepted = true + + } else if (k === Qt.Key_R) { + // R — reshuffle + if (!aseGotchi._lockdown && aseGotchi._phase === 1) { + aseGotchi._resetCards() + aseGotchi._fetchChallenge() + aseGotchi._setWhisper("the cards scatter · read the new arrangement") + } + event.accepted = true + + } else if (k === Qt.Key_Return || k === Qt.Key_Enter || k === Qt.Key_KP_Enter) { + if (!aseGotchi._lockdown && aseGotchi._phase === 1) { + if (aseGotchi._readyToCast) { + aseGotchi._cast() + } else { + var drawn = aseGotchi._setupMode + ? (aseGotchi._setupPhase2 ? aseGotchi._setupSeq2.length : aseGotchi._setupSeq1.length) + : aseGotchi._sequence.length + var remaining = 7 - drawn + aseGotchi._setWhisper(drawn + " of 7 bound · " + remaining + " fold" + (remaining !== 1 ? "s" : "") + " remain · read the sigils") + } + } + event.accepted = true + + } else if (k === Qt.Key_Escape) { + if (aseGotchi._lockdown) { + event.accepted = true + return + } + var hasProgress = aseGotchi._sequence.length > 0 + || aseGotchi._setupSeq1.length > 0 + || aseGotchi._setupSeq2.length > 0 + if (hasProgress) { + aseGotchi._resetCards() + aseGotchi._fetchChallenge() + aseGotchi._setWhisper("the cards scatter · read the new arrangement") + } else { + // Clean state — exit the gate + aseGotchi._dismiss() + aseGotchi.catalystFailed() + } + event.accepted = true + } + } + } +} diff --git a/lockscreens/kitsune/content/gotchi/InseiHub.qml b/lockscreens/kitsune/content/gotchi/InseiHub.qml new file mode 100644 index 00000000..4f833ccf --- /dev/null +++ b/lockscreens/kitsune/content/gotchi/InseiHub.qml @@ -0,0 +1,206 @@ +/* InseiHub.qml — Ase's Living Portal (Unbound) + * ============================================ + * Simplified, holographic HUD. Ase floats directly on the overlay + * surrounded by her 7 orbital sigils. No rigid shell. + * + * §14.8 — Cooperative + FBO, dynamic orbital physics. + */ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Effects +import "../tw" as TW +import "." + +Item { + id: root + + // ── Geometry ────────────────────────────────────────────────────── + readonly property real _width: 340 + readonly property real _height: 400 + readonly property real _aseSize: 180 // companion scale target + readonly property real _orbitR: 110 // default orbit radius + readonly property real _activeR: 85 // radius when fold is drawn + + width: _width + height: _height + implicitWidth: _width + implicitHeight: _height + + // ── Position Reporting ──────────────────────────────────────────── + function _reportPosition() { + TW.ArOrchestrator.aseWidgetCenterX = 0.5 + TW.ArOrchestrator.aseWidgetCenterY = 0.0 + } + Component.onCompleted: Qt.callLater(_reportPosition) + + // ── Derived State ───────────────────────────────────────────────── + readonly property color _accent: TW.ArOrchestrator.prismaticColor + readonly property bool _thinking: TW.ArOrchestrator.thinkingPhase > 0 + readonly property string _phase: TW.ArOrchestrator.scryCommPhase || "idle" + readonly property bool _online: TW.ArOrchestrator.daemonState === "alive" + readonly property var _drawn: TW.ArOrchestrator.aseDrawnFolds || [] + readonly property string _activeMode: TW.ArOrchestrator.intelActiveMode || "" + readonly property real _cmdPulse: TW.ArOrchestrator.commandPulse || 0.0 + readonly property real _animPhase: TW.ArOrchestrator.animationPhase || 0.0 + + // Style accent — bleeds with breathingStyle + readonly property color _styleAccent: { + var s = TW.ArOrchestrator.breathingStyle || "moon" + var sa = TW.TransparentWorld.styleAccents + return sa[s] ? Qt.color(sa[s]) : _accent + } + + // ── Fold Definitions (Sibyl Trinity Alignment) ─────────────────── + readonly property var _folds: [ + { id: "eye", sigil: "◉", color: TW.TransparentWorld.accentEye }, + { id: "phantom", sigil: "⊗", color: TW.TransparentWorld.accentPhantom }, + { id: "blade", sigil: "⚔", color: TW.TransparentWorld.accentBlade }, + { id: "sage", sigil: "◈", color: TW.TransparentWorld.accentSage }, + { id: "shroud", sigil: "☁", color: TW.TransparentWorld.accentShroud }, + { id: "forge", sigil: "⬡", color: TW.TransparentWorld.accentForgeFold }, + { id: "mirror", sigil: "◌", color: TW.TransparentWorld.accentMirror } + ] + + // ── Animation Clock ─────────────────────────────────────────────── + property real _time: 0.0 + NumberAnimation on _time { + from: 0; to: 100000; duration: 100000000 + loops: Animation.Infinite + running: root.visible + } + + // ── Viewport Center ─────────────────────────────────────────────── + readonly property real _cx: width / 2 + readonly property real _cy: height * 0.45 + + // ── Companion — Ase Unbound ─────────────────────────────────────── + Item { + id: _aseContainer + x: root._cx - root._aseSize / 2 + y: root._cy - root._aseSize / 2 + width: root._aseSize + height: root._aseSize + z: 10 + + // InseiSoul — the living void + ShaderEffect { + anchors.fill: parent + property real time: root._time * (root._thinking ? 2.2 : 1.0) + property real tpsBeat: Math.min(1.0, (TW.ArOrchestrator.shiguruiTokensPerSec || 0) / 30.0) + property real inferring: root._thinking ? 1.0 : 0.0 + property real disciplineHue: 0.55 // Default + property real commandPulse: root._cmdPulse + property real obeliskOpen: 1.0 + property real cursorX: 0.5 + property real cursorY: 0.5 + property real w: width + property real h: height + property real mistDensity: root._online ? 0.0 : 0.6 + property real receivePulse: 0.0 + fragmentShader: "../shaders/InseiSoul.frag.qsb" + } + + InseiCompanion { + id: _ase + anchors.centerIn: parent + scale: root._aseSize / 260.0 + foldColor: root._accent + time: root._time * (root._thinking ? 2.2 : 1.0) + } + + // Action potential ripple + Rectangle { + anchors.centerIn: parent + width: parent.width * 1.5; height: width; radius: width / 2 + color: "transparent" + border.width: 1.5 + border.color: Qt.rgba(root._accent.r, root._accent.g, root._accent.b, 0.4) + opacity: 0.0 + scale: 0.5 + + SequentialAnimation on opacity { + running: root._cmdPulse > 0.1 + NumberAnimation { from: 0.6; to: 0.0; duration: 800; easing.type: Easing.OutCubic } + } + SequentialAnimation on scale { + running: root._cmdPulse > 0.1 + NumberAnimation { from: 0.5; to: 1.5; duration: 800; easing.type: Easing.OutCubic } + } + } + } + + // ── Orbital Sigils — 7 dynamic indicators ───────────────────────── + Repeater { + model: root._folds.length + delegate: Item { + id: _sigilItem + required property int index + + readonly property var _fd: root._folds[index] + readonly property bool _drawn: root._drawn.indexOf(_fd.id) >= 0 + readonly property bool _active: root._activeMode === _fd.id + readonly property color _col: _fd.color + + // Orbital physics + readonly property real _baseAngle: (index / 7.0) * Math.PI * 2 + readonly property real _drift: Math.sin(root._time * 0.001 + _baseAngle) * 0.1 + readonly property real _angle: _baseAngle + (root._time * 0.0005) + _drift + + readonly property real _targetR: _sigilItem._drawn ? root._activeR : root._orbitR + property real _currentR: _targetR + Behavior on _currentR { NumberAnimation { duration: 800; easing.type: Easing.OutBack } } + + x: root._cx + _currentR * Math.cos(_angle) - 16 + y: root._cy + _currentR * Math.sin(_angle) - 16 + width: 32 + height: 32 + z: 20 + + // Sigil Glyph + Text { + anchors.centerIn: parent + text: _fd.sigil + color: _sigilItem._drawn + ? Qt.rgba(_col.r, _col.g, _col.b, 1.0) + : Qt.rgba(_col.r, _col.g, _col.b, 0.3) + font.pixelSize: _sigilItem._drawn ? 20 : 16 + Behavior on color { ColorAnimation { duration: 400 } } + Behavior on font.pixelSize { NumberAnimation { duration: 400; easing.type: Easing.OutBack } } + + layer.enabled: _sigilItem._drawn + layer.effect: MultiEffect { + shadowEnabled: true; blurEnabled: false + shadowColor: Qt.rgba(_col.r, _col.g, _col.b, 0.6) + shadowBlur: 0.8 + } + } + + // Interaction + HoverHandler { cursorShape: Qt.PointingHandCursor } + TapHandler { + onTapped: { + if (_sigilItem._drawn) + TW.ArOrchestrator.aseHubRecallFold = _sigilItem._fd.id + else + TW.ArOrchestrator.aseHubDrawFold = _sigilItem._fd.id + } + } + } + } + + // ── Live Hash — cascading below Ase ─────────────────────────────── + InseiLiveHash { + id: _hash + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: _aseContainer.bottom + anchors.topMargin: 20 + width: 300 + active: root._online + entropy: 0.8 + 0.2 * Math.sin(root._time * 0.002) + password: "shigurui:zenith" // Synthetic seed + tappedFolds: root._drawn + opacity: root._online ? 0.7 : 0.0 + Behavior on opacity { NumberAnimation { duration: 600 } } + } + +} diff --git a/lockscreens/kitsune/content/gotchi/InseiHud.qml b/lockscreens/kitsune/content/gotchi/InseiHud.qml new file mode 100644 index 00000000..3006fd13 --- /dev/null +++ b/lockscreens/kitsune/content/gotchi/InseiHud.qml @@ -0,0 +1,134 @@ +/* InseiHud.qml — Gate Telemetry Strip + * ===================================== + * 7 fold sigil dots + entropy bar + live hash. + * Dots fill with fold color as each card is tapped. + * Centered under InseiCompanion. + */ +import QtQuick +import QtQuick.Effects +import "../tw" as TW + +Item { + id: root + + property real entropy: 0.0 + property int locked: 0 + property string password: "" + property bool isLoading: false + property real time: 0.0 + property var tappedFolds: [] // fold IDs in tap order + + implicitHeight: 52 + implicitWidth: 320 + opacity: entropy > 0 || isLoading ? 1.0 : 0.0 + Behavior on opacity { NumberAnimation { duration: 400 } } + + // Canonical fold order + sigils + colors — matches backend + readonly property var _folds: [ + { id: "truth", sigil: "◉", color: "#4ade80" }, + { id: "power", sigil: "⊗", color: "#f43f5e" }, + { id: "wisdom", sigil: "⚔", color: "#00B4D8" }, + { id: "judgment", sigil: "◈", color: "#9B1FA0" }, + { id: "life", sigil: "☁", color: "#818cf8" }, + { id: "love", sigil: "⬡", color: "#f59e0b" }, + { id: "discipline", sigil: "◌", color: "#6366f1" } + ] + + Column { + anchors.centerIn: parent + spacing: 6 + + // ── 7 fold sigil dots ───────────────────────────────────────── + Row { + anchors.horizontalCenter: parent.horizontalCenter + spacing: 14 + + Repeater { + model: root._folds.length + delegate: Item { + required property int index + width: 22; height: 22 + + readonly property var _fd: root._folds[index] + readonly property bool _tapped: root.tappedFolds.indexOf(_fd.id) >= 0 + readonly property int _order: root.tappedFolds.indexOf(_fd.id) + readonly property color _col: Qt.color(_fd.color) + + // Outer glow ring — visible when tapped + Rectangle { + anchors.centerIn: parent + width: 20; height: 20; radius: 10 + color: "transparent" + border.width: 1 + border.color: Qt.rgba(_col.r, _col.g, _col.b, _tapped ? 0.45 : 0.0) + Behavior on border.color { ColorAnimation { duration: 300 } } + } + + // Sigil text + Text { + anchors.centerIn: parent + text: _fd.sigil + color: _tapped + ? Qt.rgba(_col.r, _col.g, _col.b, 1.0) + : Qt.rgba(1, 1, 1, 0.18) + font.pixelSize: 13 + Behavior on color { ColorAnimation { duration: 250 } } + + // Pop-in scale when tapped + scale: _tapped ? 1.0 : 0.85 + Behavior on scale { NumberAnimation { duration: 200; easing.type: Easing.OutBack } } + + layer.enabled: _tapped + layer.effect: MultiEffect { + shadowEnabled: true; blurEnabled: false + shadowColor: Qt.rgba(_col.r, _col.g, _col.b, 0.75) + shadowBlur: 0.90 + shadowVerticalOffset: 0; shadowHorizontalOffset: 0 + } + } + + // Tap order number — tiny, below sigil, fades in when tapped + Text { + anchors { top: parent.bottom; horizontalCenter: parent.horizontalCenter; topMargin: 1 } + text: _order >= 0 ? (_order + 1).toString() : "" + color: Qt.rgba(_col.r, _col.g, _col.b, 0.55) + font.family: TW.TransparentWorld.fontFamilyMono + font.pixelSize: 7 + font.bold: true + opacity: _tapped ? 1.0 : 0.0 + Behavior on opacity { NumberAnimation { duration: 200 } } + } + } + } + } + + // ── Entropy bar ─────────────────────────────────────────────── + Item { + width: 320 + height: 2 + anchors.horizontalCenter: parent.horizontalCenter + + Rectangle { + anchors.fill: parent; radius: 1 + color: Qt.rgba(TW.TransparentWorld.accentWater.r, TW.TransparentWorld.accentWater.g, TW.TransparentWorld.accentWater.b, 0.10) + Rectangle { + height: parent.height; radius: 1 + width: parent.width * root.entropy + color: TW.TransparentWorld.accentWater + opacity: 0.6 + 0.4 * Math.sin(root.time * 1.8) + Behavior on width { NumberAnimation { duration: 80 } } + } + } + } + + // ── Live hash ───────────────────────────────────────────────── + InseiLiveHash { + anchors.horizontalCenter: parent.horizontalCenter + width: 320 + active: root.entropy > 0 && !root.isLoading + password: root.password + entropy: root.entropy + tappedFolds: root.tappedFolds + } + } +} diff --git a/lockscreens/kitsune/content/gotchi/InseiLiveHash.qml b/lockscreens/kitsune/content/gotchi/InseiLiveHash.qml new file mode 100644 index 00000000..fbda1b50 --- /dev/null +++ b/lockscreens/kitsune/content/gotchi/InseiLiveHash.qml @@ -0,0 +1,221 @@ +/* InseiLiveHash.qml — Living Akashic Fingerprint + * ================================================ + * 32-char stream split into 2 rows of 16. + * Characters drawn from: binary, hex, runes, sigils, alchemical glyphs. + * Locked chars crystallize in the active fold's color with glow. + * Unlocked chars cascade through the full symbol set. + * Groups of 4 separated by · + * Loading: all chars breathe in a sine wave. + * + * §14.8 — Cooperative + FBO, dirty-flag pattern. + */ +import QtQuick +import "../tw" as TW + +Item { + id: root + + property bool active: false + property string password: "" + property real entropy: 0.0 + property var tappedFolds: [] + + implicitWidth: 320 + implicitHeight: 32 + + opacity: root.active ? 1.0 : 0.0 + Behavior on opacity { NumberAnimation { duration: 300 } } + + // ── Symbol pools — the living alphabet ─────────────────────────── + // Binary fragments + readonly property var _binary: ["0","1","0","1","1","0","0","1","1","1","0","0","1","0","1","1"] + // Hex + readonly property var _hex: ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"] + // Elder Futhark runes + readonly property var _runes: ["ᚠ","ᚢ","ᚦ","ᚨ","ᚱ","ᚲ","ᚷ","ᚹ","ᚺ","ᚾ","ᛁ","ᛃ","ᛇ","ᛈ","ᛉ","ᛊ","ᛏ","ᛒ","ᛖ","ᛗ","ᛚ","ᛜ","ᛞ","ᛟ"] + // Alchemical / occult sigils + readonly property var _sigils: ["◉","⊗","⚔","◈","☁","⬡","◌","⊕","∿","◆","⬟","⊘","⊛","⊜","⊝","⊞"] + // Mathematical / esoteric + readonly property var _math: ["∞","∂","∇","∆","∑","∏","√","∫","≈","≠","≡","∈","∉","∅","⊂","⊃"] + // Braille fragments + readonly property var _braille: ["⠁","⠃","⠉","⠙","⠑","⠋","⠛","⠓","⠊","⠚","⠅","⠇","⠍","⠝","⠕","⠏"] + + // Full living alphabet — all pools merged + readonly property var _alphabet: _binary.concat(_hex).concat(_runes).concat(_sigils).concat(_math).concat(_braille) + + // Canonical fold colors + readonly property var _foldColors: ({ + "truth": "#4ade80", + "power": "#f43f5e", + "wisdom": "#00B4D8", + "judgment": "#9B1FA0", + "life": "#818cf8", + "love": "#f59e0b", + "discipline": "#6366f1" + }) + + // Per-position fold color — each locked char uses the color of the fold that locked it + readonly property var _charColors: { + var colors = [] + var folds = root.tappedFolds + var locked = root._lockedChars + // Distribute locked chars across tapped folds proportionally + for (var i = 0; i < 32; i++) { + if (i < locked && folds.length > 0) { + var foldIdx = Math.floor(i / Math.max(1, locked / folds.length)) + foldIdx = Math.min(foldIdx, folds.length - 1) + var hex = _foldColors[folds[foldIdx]] + colors.push(hex ? Qt.color(hex) : TW.TransparentWorld.accentWater) + } else { + colors.push(TW.TransparentWorld.accentWater) + } + } + return colors + } + + // ── Scramble state ──────────────────────────────────────────────── + property var _stream: [] // current 32 chars + property bool _dirty: false + property real _loadPulse: 0.0 + property real _glowPulse: 0.0 + + readonly property int _lockedChars: Math.round(root.entropy * 32) + readonly property var _lockedStream: { + if (root.password.length === 0) return [] + var result = [] + for (var i = 0; i < 32; i++) { + // Deterministic char from password — always the same for same password + var code = 0 + for (var j = 0; j < root.password.length; j++) + code = (code * 31 + root.password.charCodeAt(j) + i * 13) & 0xFFFF + var pool = i % 4 === 0 ? root._runes + : i % 4 === 1 ? root._sigils + : i % 4 === 2 ? root._hex + : root._math + result.push(pool[code % pool.length]) + } + return result + } + + // Continuous glow pulse + NumberAnimation on _glowPulse { + from: 0; to: Math.PI * 2; duration: 2400 + loops: Animation.Infinite; running: root.active + } + + // Load pulse (faster) + NumberAnimation on _loadPulse { + from: 0; to: Math.PI * 2; duration: 900 + loops: Animation.Infinite; running: root.active + } + + // Living scramble — cycles through all symbol pools + Timer { + interval: 60 + running: root.active + repeat: true + onTriggered: { + var locked = root._lockedChars + var s = root._stream.slice() + while (s.length < 32) s.push("0") + + for (var i = 0; i < 32; i++) { + if (i < locked) { + // Locked — use deterministic char from password + s[i] = root._lockedStream[i] || "◉" + } else { + // Living — cycle through full alphabet with time offset + var t = Date.now() / 80 + i * 7.3 + var idx = Math.floor(t) % root._alphabet.length + s[i] = root._alphabet[idx] + } + } + root._stream = s + root._dirty = true + } + } + + // Repaint throttle + Timer { + interval: 33 + running: root.active + repeat: true + onTriggered: { + if (root._dirty) { root._dirty = false; _canvas.requestPaint() } + } + } + + on_LockedCharsChanged: { root._dirty = true } + onActiveChanged: { root._dirty = true } + on_GlowPulseChanged: { if (root.active) root._dirty = true } + + // ── Canvas ──────────────────────────────────────────────────────── + Canvas { + id: _canvas + anchors.fill: parent + renderStrategy: Canvas.Cooperative + renderTarget: Canvas.FramebufferObject + + onPaint: { + var ctx = getContext("2d") + ctx.clearRect(0, 0, width, height) + + var stream = root._stream + if (stream.length === 0) return + + var locked = root._lockedChars + var aw = TW.TransparentWorld.accentWater + var rowH = height / 2 + var charW = width / 18.5 + var groupW = charW * 4.6 + + ctx.textBaseline = "middle" + + for (var row = 0; row < 2; row++) { + var base = row * 16 + var y = rowH * row + rowH / 2 + + for (var col = 0; col < 16; col++) { + var i = base + col + var ch = i < stream.length ? stream[i] : "·" + var grp = Math.floor(col / 4) + var x = grp * groupW + (col % 4) * charW + charW * 0.1 + + if (i < locked) { + // Locked — fold color + glow pulse + var fc = root._charColors[i] + var glow = 0.75 + 0.20 * Math.sin(root._glowPulse + i * 0.4) + ctx.font = "bold 10px '3270 Nerd Font Mono', monospace" + ctx.fillStyle = Qt.rgba(fc.r, fc.g, fc.b, glow) + + // Glow halo — draw twice, second pass slightly larger/dimmer + ctx.shadowColor = Qt.rgba(fc.r, fc.g, fc.b, 0.55) + ctx.shadowBlur = 4 + ctx.fillText(ch, x, y) + ctx.shadowBlur = 0 + } else if (root.entropy < 0.05) { + // Loading — breathe in unison + var pulse = 0.10 + 0.10 * Math.sin(root._loadPulse + col * 0.5) + ctx.font = "9px '3270 Nerd Font Mono', monospace" + ctx.fillStyle = Qt.rgba(aw.r, aw.g, aw.b, pulse) + ctx.fillText(ch, x, y) + } else { + // Living — cascade with position-staggered fade + var fade = 0.12 + 0.08 * Math.sin(root._glowPulse * 0.7 + i * 0.9) + ctx.font = "9px '3270 Nerd Font Mono', monospace" + ctx.fillStyle = Qt.rgba(aw.r, aw.g, aw.b, fade) + ctx.fillText(ch, x, y) + } + } + + // Group separators + for (var g = 1; g < 4; g++) { + var sx = g * groupW - charW * 0.5 + ctx.font = "8px '3270 Nerd Font Mono', monospace" + ctx.fillStyle = Qt.rgba(aw.r, aw.g, aw.b, 0.10) + ctx.fillText("·", sx, y) + } + } + } + } +} diff --git a/lockscreens/kitsune/content/gotchi/InseiScene.qml b/lockscreens/kitsune/content/gotchi/InseiScene.qml new file mode 100644 index 00000000..5df00390 --- /dev/null +++ b/lockscreens/kitsune/content/gotchi/InseiScene.qml @@ -0,0 +1,330 @@ +/* InseiScene.qml — The Holographic Ase-Gotchi Scene + * ================================================ + * Layout top → bottom: + * Instruction hint — fades after first tap + * Card arc — 7 tarot cards fanning upward from Ase's mind + * Ase — the dealer, center stage + * Whisper — floating lowercase text, no background + * InseiHud — entropy bar + lock count + live hash + */ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Effects +import "../tw" as TW + +Item { + id: root + + // ── Public API ──────────────────────────────────────────────────── + property int lockedCount: 0 + property real entropy: 0.0 + property bool hasError: false + property bool isUnlocked: false + property bool isLoading: false + property string whisper: "" + property string sequenceStr: "" + property var layout: [] + property var tappedFolds: [] // fold IDs tapped so far — drives correct flip + property real time: 0.0 + property int activeFoldIdx: -1 + property color activeFoldColor: TW.TransparentWorld.accentWater + property real activePulse: 0.0 + property real breach: 0.0 + property bool setupMode: false + property bool setupPhase2: false + property int resetKey: 0 // incremented on reset to force delegate re-eval + + signal cardTapped(string foldId, int position) + + // ── Fold data — canonical, matches backend ──────────────────────── + readonly property var _foldIds: [ + "truth","power","wisdom","judgment","life","love","discipline" + ] + readonly property var _foldLabels: [ + "THE EYE","THE PHANTOM","THE BLADE","THE SAGE","THE SHROUD","THE FORGE","THE MIRROR" + ] + readonly property var _foldPrinciples: [ + "TRUTH","POWER","WISDOM","JUDGMENT","LIFE","LOVE","DISCIPLINE" + ] + readonly property var _foldColors: [ + "#4ade80","#f43f5e","#00B4D8","#9B1FA0","#818cf8","#f59e0b","#6366f1" + ] + readonly property var _foldIcons: [ + "\u25c9","\u2297","\u2694","\u25c8","\u2601","\u2b21","\u25cc" + ] + + // ── Geometry ────────────────────────────────────────────────────── + readonly property real _cx: width / 2 + + readonly property real _aseW: 320 + readonly property real _aseH: 260 + readonly property real _aseY: height * 0.42 + + readonly property real _arcCX: _cx + readonly property real _arcCY: _aseY - 20 + readonly property real _arcR: 180 + + readonly property real _whisperY: _aseY + _aseH + 10 + readonly property real _hudY: _whisperY + 44 + + // ── Instruction hint — fades after first tap ────────────────────── + Text { + x: root._cx - implicitWidth / 2 + y: root._arcCY - root._arcR - 32 + text: "read the sigils · invoke your sequence" + opacity: root.lockedCount === 0 && !root.setupMode ? 0.38 : 0.0 + Behavior on opacity { NumberAnimation { duration: 600 } } + color: Qt.rgba(1, 1, 1, 1) + font.family: TW.TransparentWorld.fontFamilyMono + font.pixelSize: 10 + font.letterSpacing: 2 + } + + // Setup instruction + Text { + x: root._cx - implicitWidth / 2 + y: root._arcCY - root._arcR - 32 + text: root.setupPhase2 ? "draw the same sequence to confirm the binding" : "inscribe your sequence across the seven folds" + opacity: root.setupMode ? 0.38 : 0.0 + Behavior on opacity { NumberAnimation { duration: 600 } } + color: Qt.rgba(0.96, 0.74, 0.04, 1) + font.family: TW.TransparentWorld.fontFamilyMono + font.pixelSize: 10 + font.letterSpacing: 2 + } + + // ── Card arc ────────────────────────────────────────────────────── + Item { + anchors.fill: parent + + Repeater { + model: 7 + delegate: InseiTarot { + id: _card + required property int index + + readonly property var _fold: { + if (root.layout.length === 7) return root.layout[index] + return { id: root._foldIds[index], color: root._foldColors[index], position: index + 1 } + } + + readonly property int _ci: { + var id = _fold.id || "" + for (var i = 0; i < root._foldIds.length; i++) + if (root._foldIds[i] === id) return i + return index + } + + // Correct flip: driven by whether this fold ID is in tappedFolds + // resetKey dependency forces re-evaluation when cards are reset + readonly property bool _isTapped: root.resetKey >= 0 && root.tappedFolds.indexOf(_fold.id || "") >= 0 + readonly property int _tapOrder: root.tappedFolds.indexOf(_fold.id || "") + + foldLabel: root._foldLabels[_ci] + principle: root._foldPrinciples[_ci] + icon: root._foldIcons[_ci] + accent: Qt.color(root._foldColors[_ci]) + position: _fold.position || (index + 1) + time: root.time + _phase: index * 0.9 + + isRevealed: _isTapped + revealOrder: _tapOrder + + // Convex-up arc: 210° → 330° + readonly property real _spreadDeg: 120.0 + readonly property real _startDeg: 210.0 + readonly property real _stepDeg: _spreadDeg / 6.0 + readonly property real _angleDeg: _startDeg + index * _stepDeg + readonly property real _angleRad: _angleDeg * Math.PI / 180.0 + + x: root._arcCX + root._arcR * Math.cos(_angleRad) - width / 2 + // y offset from TarotCard's own hover lift — base position here + y: root._arcCY + root._arcR * Math.sin(_angleRad) - height / 2 + + rotation: _angleDeg - 270.0 + + onTapped: root.cardTapped(_card._fold.id || "", _card._fold.position || (_card.index + 1)) + } + } + } + + // ── Ase ─────────────────────────────────────────────────────────── + InseiCompanion { + id: _ase + x: root._cx - implicitWidth / 2 + y: root._aseY + entropy: root.entropy + foldColor: root.activeFoldColor + time: root.time + } + + // ── Whisper ─────────────────────────────────────────────────────── + Item { + x: root._cx - 200 + y: root._whisperY + width: 400 + height: 48 + visible: true + opacity: root.whisper.length > 0 ? 1.0 : 0.0 + Behavior on opacity { NumberAnimation { duration: 300 } } + + Text { + anchors.centerIn: parent + width: parent.width + text: root.whisper.toLowerCase() + color: root.hasError ? Qt.rgba(0.96, 0.25, 0.37, 0.90) + : root.isUnlocked ? Qt.rgba(0.29, 0.87, 0.50, 0.90) + : Qt.rgba(0.0, 0.82, 1.0, 0.78) + font.family: TW.TransparentWorld.fontFamilyMono + font.pixelSize: 13 + font.letterSpacing: 1.5 + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + Behavior on color { ColorAnimation { duration: 250 } } + } + + layer.enabled: root.whisper.length > 0 + layer.effect: MultiEffect { + shadowEnabled: true + blurEnabled: false + shadowColor: root.hasError + ? Qt.rgba(0.96, 0.25, 0.37, 0.45) + : Qt.rgba(0.0, 0.82, 1.0, 0.35) + shadowBlur: 0.9 + shadowVerticalOffset: 0 + shadowHorizontalOffset: 0 + } + } + + // ── Setup / Confirm badge ───────────────────────────────────────── + Text { + x: root._cx - implicitWidth / 2 + y: root._whisperY - 20 + visible: root.setupMode + opacity: root.setupMode ? 1.0 : 0.0 + Behavior on opacity { NumberAnimation { duration: 200 } } + text: root.setupPhase2 ? "confirm" : "setup" + color: Qt.rgba(0.96, 0.74, 0.04, 0.70) + font.family: TW.TransparentWorld.fontFamilyMono + font.pixelSize: 9 + font.letterSpacing: 3 + } + + // ── InseiHud ──────────────────────────────────────────────────────── + InseiHud { + x: root._cx - width / 2 + y: root._hudY + width: 320 + entropy: root.entropy + locked: root.lockedCount + password: root.sequenceStr + isLoading: root.isLoading + time: root.time + tappedFolds: root.tappedFolds + } + + // ── CAST button — appears when all 7 cards are drawn ────────────── + // §16 — visible: stableGate (readyToCast), opacity handles show/hide + signal castRequested() + + property bool readyToCast: false + + // Shatter animation — fires on tap + property real _castShatter: 0.0 + NumberAnimation { + id: _shatterAnim + target: root; property: "_castShatter" + from: 0.0; to: 1.0; duration: 600 + easing.type: Easing.OutCubic + running: false + onStopped: root._castShatter = 0.0 + } + + // Dynamic label based on state + readonly property string _castLabel: { + if (setupMode && setupPhase2) return "CONFIRM THE BINDING" + if (setupMode) return "BIND THE SEQUENCE" + return "INVOKE THE SEAL" + } + + Item { + x: root._cx - 100 + y: root._aseY - 58 + width: 200 + height: 38 + z: 20 + visible: root.readyToCast && !root.isUnlocked + opacity: root.readyToCast && !root.isUnlocked ? 1.0 : 0.0 + Behavior on opacity { NumberAnimation { duration: 220; easing.type: Easing.OutCubic } } + + // Crystallize shader background + ShaderEffect { + anchors.fill: parent + property real u_time: root.time + property real u_confidence: 1.0 + property real u_progress: root.entropy // fills as cards drawn + property real u_shatter: root._castShatter + property real u_width: width + property real u_height: height + property real u_morphR: root.activeFoldColor.r + property real u_morphG: root.activeFoldColor.g + property real u_morphB: root.activeFoldColor.b + fragmentShader: "../shaders/crystallize.frag.qsb" + blending: true + } + + // Glass base behind shader so button is readable when entropy=0 + Rectangle { + anchors.fill: parent + radius: 8 + color: Qt.rgba(0.02, 0.04, 0.08, 0.85) + border.width: 1 + border.color: Qt.rgba(root.activeFoldColor.r, root.activeFoldColor.g, root.activeFoldColor.b, + _castHov.hovered ? 0.90 : 0.50) + Behavior on border.color { ColorAnimation { duration: 120 } } + + SequentialAnimation on border.color { + loops: Animation.Infinite + running: root.readyToCast && !root.isUnlocked && !_castHov.hovered + ColorAnimation { to: Qt.rgba(root.activeFoldColor.r, root.activeFoldColor.g, root.activeFoldColor.b, 0.85); duration: 700; easing.type: Easing.InOutSine } + ColorAnimation { to: Qt.rgba(root.activeFoldColor.r, root.activeFoldColor.g, root.activeFoldColor.b, 0.28); duration: 700; easing.type: Easing.InOutSine } + } + } + + // Label — above shader + Text { + anchors.centerIn: parent + z: 2 + text: root._castLabel + color: Qt.rgba(1, 1, 1, _castHov.hovered ? 1.0 : 0.92) + font.family: TW.TransparentWorld.fontFamilyMono + font.pixelSize: 12 + font.bold: true + font.letterSpacing: 2.5 + Behavior on color { ColorAnimation { duration: 120 } } + + layer.enabled: true + layer.effect: MultiEffect { + shadowEnabled: true; blurEnabled: false + shadowColor: Qt.rgba(root.activeFoldColor.r, root.activeFoldColor.g, root.activeFoldColor.b, 0.80) + shadowBlur: TW.TransparentWorld.shadowBlur + shadowVerticalOffset: 0; shadowHorizontalOffset: 0 + } + } + + HoverHandler { id: _castHov; cursorShape: Qt.PointingHandCursor } + TapHandler { + onTapped: { + _shatterAnim.restart() + root.castRequested() + } + } + } + + // ── Public methods ──────────────────────────────────────────────── + function aseWake() { _ase.wake() } + function aseReact(col) { _ase.react(col) } + function aseHurt() { _ase.hurt() } + function aseTransform() { _ase.aseTransform() } +} diff --git a/lockscreens/kitsune/content/gotchi/InseiTarot.qml b/lockscreens/kitsune/content/gotchi/InseiTarot.qml new file mode 100644 index 00000000..255313fd --- /dev/null +++ b/lockscreens/kitsune/content/gotchi/InseiTarot.qml @@ -0,0 +1,366 @@ +/* InseiTarot.qml — Mystical 7-Fold Card + * ========================================== + * Holographic card with 3D flip, idle breathe, hover lift, fold-color glow. + * Back face: position number + large glowing fold sigil + hover tooltip. + * Front face: fold label + principle after reveal. + */ +import QtQuick +import QtQuick.Effects +import "../tw" as TW + +Item { + id: root + + property string cardName: "The Oracle" + property string principle: "TRUTH" + property string foldLabel: "THE EYE" + property string icon: "\u25c9" + property color accent: "#4ade80" + property bool isRevealed: false + property int revealOrder: -1 + property int position: 1 // shuffled position 1–7 (shown on back) + property real time: 0.0 + property real _phase: 0.0 // idle breathe phase offset + + implicitWidth: 70 + implicitHeight: 110 + + signal tapped() + signal alreadyDrawn() + + // ── Hover lift ──────────────────────────────────────────────────── + property real _hoverLift: _hov.hovered && !isRevealed ? -8.0 : 0.0 + Behavior on _hoverLift { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } } + + y: _hoverLift + + // ── Flip ────────────────────────────────────────────────────────── + property real _flipAngle: isRevealed ? 180.0 : 0.0 + Behavior on _flipAngle { NumberAnimation { duration: 550; easing.type: Easing.OutBack; easing.overshoot: 0.18 } } + + // ── Idle breathe — unrevealed only ─────────────────────────────── + readonly property real _breathe: isRevealed ? 0.0 + : 0.04 * Math.sin(time * 0.9 + _phase) + + // ── Already-drawn flash ─────────────────────────────────────────── + property real _flashOpacity: 1.0 + SequentialAnimation { + id: _flashAnim + NumberAnimation { target: root; property: "_flashOpacity"; to: 0.3; duration: 80 } + NumberAnimation { target: root; property: "_flashOpacity"; to: 1.0; duration: 160 } + } + opacity: _flashOpacity + + // ── Hover tooltip — shown above card when hovered and not revealed ─ + Item { + id: _tooltip + // Positioned above the card, centered + x: (root.implicitWidth - _tooltipBg.width) / 2 + y: -_tooltipBg.height - 6 + width: _tooltipBg.width + height: _tooltipBg.height + visible: !root.isRevealed + opacity: _hov.hovered ? 1.0 : 0.0 + Behavior on opacity { NumberAnimation { duration: 140; easing.type: Easing.OutCubic } } + + Rectangle { + id: _tooltipBg + width: _tooltipRow.implicitWidth + 14 + height: _tooltipRow.implicitHeight + 8 + radius: 4 + color: Qt.rgba(0.03, 0.06, 0.10, 0.92) + border.color: Qt.rgba(root.accent.r, root.accent.g, root.accent.b, 0.45) + border.width: 1 + + Row { + id: _tooltipRow + anchors.centerIn: parent + spacing: 5 + + Text { + text: root.icon + color: root.accent + font.pixelSize: 11 + verticalAlignment: Text.AlignVCenter + } + + Text { + text: root.foldLabel + color: Qt.rgba(1, 1, 1, 0.85) + font.family: TW.TransparentWorld.fontFamilyMono + font.pixelSize: 9 + font.bold: true + font.letterSpacing: 0.8 + verticalAlignment: Text.AlignVCenter + } + + Text { + text: "·" + color: Qt.rgba(1, 1, 1, 0.30) + font.pixelSize: 9 + verticalAlignment: Text.AlignVCenter + } + + Text { + text: root.principle + color: root.accent + font.family: TW.TransparentWorld.fontFamilyMono + font.pixelSize: 9 + font.letterSpacing: 0.8 + verticalAlignment: Text.AlignVCenter + } + } + + // Tooltip glow + layer.enabled: _hov.hovered + layer.effect: MultiEffect { + shadowEnabled: true + blurEnabled: false + shadowColor: Qt.rgba(root.accent.r, root.accent.g, root.accent.b, 0.55) + shadowBlur: 0.8 + shadowVerticalOffset: 0 + shadowHorizontalOffset: 0 + } + } + } + + // ── Card container — 3D Y-axis rotation ────────────────────────── + Item { + id: _container + anchors.fill: parent + + transform: Rotation { + origin.x: _container.width / 2 + origin.y: _container.height / 2 + axis { x: 0; y: 1; z: 0 } + angle: root._flipAngle + } + + // ── Back face ───────────────────────────────────────────────── + Rectangle { + id: _back + anchors.fill: parent + visible: root._flipAngle < 90 + color: "#080d12" + radius: 5 + border.color: _hov.hovered + ? Qt.rgba(root.accent.r, root.accent.g, root.accent.b, 0.70) + : Qt.rgba(1, 1, 1, 0.10 + root._breathe * 2) + border.width: 1 + Behavior on border.color { ColorAnimation { duration: 120 } } + + // Geometric lattice — tints toward accent on hover + Canvas { + id: _lattice + anchors { fill: parent; margins: 2 } + renderStrategy: Canvas.Cooperative + renderTarget: Canvas.FramebufferObject + + // Dirty-flag repaint on hover change only + property bool _dirty: false + onVisibleChanged: _dirty = true + + Connections { + target: _hov + function onHoveredChanged() { _lattice._dirty = true } + } + + Timer { + interval: 100 + running: _lattice.visible + repeat: true + onTriggered: { + if (_lattice._dirty) { + _lattice._dirty = false + _lattice.requestPaint() + } + } + } + + onPaint: { + var ctx = getContext("2d") + ctx.clearRect(0, 0, width, height) + var alpha = _hov.hovered ? 0.28 : 0.15 + var r = _hov.hovered ? root.accent.r * 0.4 : 0.15 + var g = _hov.hovered ? root.accent.g * 0.4 : 0.35 + var b = _hov.hovered ? root.accent.b * 0.4 : 0.55 + ctx.strokeStyle = Qt.rgba(r, g, b, alpha) + ctx.lineWidth = 0.8 + for (var i = 0; i <= width; i += 9) { + ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(width - i, height); ctx.stroke() + ctx.beginPath(); ctx.moveTo(i, height); ctx.lineTo(width - i, 0); ctx.stroke() + } + } + } + + // Position number — top left + Text { + anchors { top: parent.top; left: parent.left; margins: 5 } + text: root.position.toString() + color: _hov.hovered + ? Qt.rgba(root.accent.r, root.accent.g, root.accent.b, 0.90) + : Qt.rgba(1, 1, 1, 0.45 + root._breathe * 2) + font.family: TW.TransparentWorld.fontFamilyMono + font.pixelSize: 12 + font.bold: true + Behavior on color { ColorAnimation { duration: 120 } } + } + + // ── Fold sigil — center, large, glowing ─────────────────── + Item { + id: _sigilContainer + anchors.centerIn: parent + width: 42 + height: 42 + + Text { + id: _sigil + anchors.centerIn: parent + text: root.icon + color: Qt.rgba( + root.accent.r, + root.accent.g, + root.accent.b, + 0.75 + root._breathe * 3 + (_hov.hovered ? 0.20 : 0.0) + ) + font.pixelSize: 32 + Behavior on color { ColorAnimation { duration: 120 } } + } + + // Sigil bloom — dedicated glow layer on the sigil itself + layer.enabled: true + layer.effect: MultiEffect { + shadowEnabled: true + blurEnabled: false + shadowColor: Qt.rgba( + root.accent.r, + root.accent.g, + root.accent.b, + _hov.hovered + ? 0.90 + root._breathe * 2 + : 0.55 + root._breathe * 4 + ) + shadowBlur: 0.95 + shadowVerticalOffset: 0 + shadowHorizontalOffset: 0 + } + } + + // Position number — bottom right (mirror) + Text { + anchors { bottom: parent.bottom; right: parent.right; margins: 5 } + text: root.position.toString() + color: _hov.hovered + ? Qt.rgba(root.accent.r, root.accent.g, root.accent.b, 0.90) + : Qt.rgba(1, 1, 1, 0.45 + root._breathe * 2) + font.family: TW.TransparentWorld.fontFamilyMono + font.pixelSize: 12 + font.bold: true + Behavior on color { ColorAnimation { duration: 120 } } + } + + // Card outer glow — hover intensifies in accent color + layer.enabled: !root.isRevealed + layer.effect: MultiEffect { + shadowEnabled: true + blurEnabled: false + shadowColor: _hov.hovered + ? Qt.rgba(root.accent.r, root.accent.g, root.accent.b, 0.60) + : Qt.rgba(0, 0.7, 1.0, 0.14 + root._breathe * 3) + shadowBlur: 0.7 + shadowVerticalOffset: 0 + shadowHorizontalOffset: 0 + } + } + + // ── Front face ──────────────────────────────────────────────── + Rectangle { + id: _front + anchors.fill: parent + visible: root._flipAngle >= 90 + color: "#0b1018" + radius: 5 + border.color: Qt.rgba(root.accent.r, root.accent.g, root.accent.b, 0.55) + border.width: 1 + + transform: Rotation { + origin.x: _front.width / 2 + origin.y: _front.height / 2 + axis { x: 0; y: 1; z: 0 } + angle: 180 + } + + Rectangle { + anchors.fill: parent + radius: 5 + color: Qt.rgba(root.accent.r, root.accent.g, root.accent.b, 0.06) + } + + Column { + anchors.centerIn: parent + spacing: 6 + + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: root.icon + color: root.accent + font.pixelSize: 24 + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: root.foldLabel + color: Qt.rgba(1, 1, 1, 0.90) + font.family: TW.TransparentWorld.fontFamilyMono + font.pixelSize: 7 + font.bold: true + font.letterSpacing: 0.5 + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: root.principle + color: root.accent + font.family: TW.TransparentWorld.fontFamilyMono + font.pixelSize: 7 + font.letterSpacing: 1 + } + } + + // Reveal order badge + Rectangle { + anchors { top: parent.top; right: parent.right; margins: 4 } + width: 14; height: 14; radius: 7 + color: root.accent + visible: root.revealOrder >= 0 + Text { + anchors.centerIn: parent + text: (root.revealOrder + 1).toString() + color: "#000000" + font.pixelSize: 9 + font.bold: true + } + } + + layer.enabled: root.isRevealed + layer.effect: MultiEffect { + shadowEnabled: true + blurEnabled: false + shadowColor: Qt.rgba(root.accent.r, root.accent.g, root.accent.b, 0.50) + shadowBlur: 0.85 + shadowVerticalOffset: 0 + shadowHorizontalOffset: 0 + } + } + } + + TapHandler { + onTapped: root.tapped() + cursorShape: Qt.PointingHandCursor + } + + HoverHandler { + id: _hov + cursorShape: Qt.PointingHandCursor + } +} diff --git a/lockscreens/kitsune/content/gotchi/README.md b/lockscreens/kitsune/content/gotchi/README.md new file mode 100644 index 00000000..72f18439 --- /dev/null +++ b/lockscreens/kitsune/content/gotchi/README.md @@ -0,0 +1,122 @@ +# Ase-Gotchi · Digital Kitsune Companion & Tarot Gate + +Ase is a 7-tailed digital fox spirit — the soul of the Shigurui ecosystem. +She manifests as a bioluminescent SDF shader form and guards the Akashic Gate +through a 7-fold Tarot authentication ritual. + +--- + +## The Ritual (Authentication) + +Authentication is a **7-fold Tarot reveal**. The operator draws 7 cards in +their personal sequence to establish resonance with the gate. + +Each session the gate shuffles the 7 cards into random spatial positions. +The operator must remember their sequence by **principle**, not by position — +the cards move, the sequence stays the same. + +### The 7 Folds + +| Position key | Fold ID | Kasugai label | Principle | Sigil | Color | +|:---:|:-------------|:---------------|:------------|:-----:|:----------| +| 1–7 (shuffled) | `truth` | THE EYE | TRUTH | ◉ | `#4ade80` | +| 1–7 (shuffled) | `power` | THE PHANTOM | POWER | ⊗ | `#f43f5e` | +| 1–7 (shuffled) | `wisdom` | THE BLADE | WISDOM | ⚔ | `#00B4D8` | +| 1–7 (shuffled) | `judgment` | THE SAGE | JUDGMENT | ◈ | `#9B1FA0` | +| 1–7 (shuffled) | `life` | THE SHROUD | LIFE | ☁ | `#818cf8` | +| 1–7 (shuffled) | `love` | THE FORGE | LOVE | ⬡ | `#f59e0b` | +| 1–7 (shuffled) | `discipline` | THE MIRROR | DISCIPLINE | ◌ | `#6366f1` | + +### Interaction + +- **Tap a card** to reveal it and add it to your sequence +- **Keyboard 1–7** maps to the shuffled spatial positions shown on each card back +- Each card back shows its **position number** (top-left + bottom-right) and its **fold sigil** (center) +- Cards flip to reveal their fold label and principle when drawn +- A reveal order badge shows the draw sequence on the front face +- **Escape** — resets the current sequence (stay at gate). Escape again to close entirely +- **Master override** — type `1234567` on the keyboard to bypass (development only) + +### First-time Setup + +If no sequence is registered, the gate enters **setup mode**: +1. Draw all 7 cards in your desired sequence +2. Re-draw the same sequence to confirm +3. The gate seals your sequence as an HMAC hash — the raw sequence is never stored + +### Security Model + +- Sequence stored as `HMAC-SHA256(fold_ids_joined, local_secret)` +- Challenge token is `HMAC-SHA256(30s time window, local_secret)` — TOTP-style +- 3 failed attempts → 30s lockout +- All state is local — no network, no DB, no external service +- Master override reads from `SHIGURUI_MASTER_PASSWORD` in `core/.env` + +--- + +## Ase's Emotional States + +Ase's shader responds to her soul state (`ArOrchestrator.aseMood`): + +| Mood | Visual | +|:--------------|:----------------------------------------------------| +| `curiosity` | Default — calm drift, inquisitive aura | +| `recognition` | Joy flash — bright bioluminescent surge | +| `excitement` | Chaotic tail movement, high entropy glow | +| `loneliness` | Dimmed colors, slowed drift | +| `sleep` | Deep rhythmic pulse, eye dims | + +Each of her 7 tails is permanently tinted with its fold color — the colors +are baked into the shader as static uniforms and never change. + +--- + +## Components + +``` +ase-gotchi/ + InseiGotchi.qml — Orchestrator. Auth state machine, API calls, input handling. + InseiScene.qml — Scene layout. Cards, Ase, whisper, HUD. + InseiCompanion.qml — The Kitsune spirit. SDF shader, mood, animations. + AseTank.qml — Optional: AkashicScreen void tank for Ase to inhabit. + TarotCard.qml — 7-fold card. 3D flip, hover lift, position + sigil on back. + InseiHud.qml — Entropy bar + lock count + live hash strip. + InseiLiveHash.qml — 32-char hex fingerprint that locks in as cards are drawn. + qmldir — Module declarations. + +shaders/ + InseiKitsune.frag — SDF kitsune shader. 7 tails, per-tail fold colors, mood uniforms. + AkashicScreen.frag — Living void background (nebula, particle streaks, scan lines). +``` + +--- + +## Backend API + +| Endpoint | Method | Purpose | +|:---------|:------:|:--------| +| `/api/auth/gate/challenge` | GET | Returns shuffled layout + challenge token | +| `/api/auth/gate/verify` | POST | Verifies fold sequence against stored hash | +| `/api/auth/gate/setup` | POST | One-time sequence registration | +| `/api/auth/gate/status` | GET | Is a sequence configured? | +| `/api/ase/status` | GET | Ase soul state (health, bonding, mood, level) | +| `/api/ase/interact` | POST | Stroke/feed/play interactions | + +Soul data persists in `core/data/ase-gotchi/ase_soul.json`. +`StatusTimer.qml` polls `/api/ase/status` every 30s to update `ArOrchestrator`. + +--- + +## Audit Checklist + +Before any session close, verify: +1. Gate opens when bar is clicked without a session +2. Cards shuffle to new positions on each open +3. Tapping a card flips the correct card (not left-to-right order) +4. Keyboard 1–7 maps to the position shown on each card back +5. Already-drawn card shows whisper feedback, not silent ignore +6. Setup mode: phase 2 resets all card flips for re-draw +7. Escape resets sequence; second escape closes gate +8. Master override `1234567` authenticates successfully +9. 3 wrong sequences triggers 30s lockdown +10. Successful auth emits `authenticated(token)` → `ArOrchestrator.sessionToken` diff --git a/lockscreens/kitsune/content/gotchi/qmldir b/lockscreens/kitsune/content/gotchi/qmldir new file mode 100644 index 00000000..d4cbc2ea --- /dev/null +++ b/lockscreens/kitsune/content/gotchi/qmldir @@ -0,0 +1,8 @@ +module aseGotchi +InseiGotchi 1.0 InseiGotchi.qml +InseiScene 1.0 InseiScene.qml +InseiCompanion 1.0 InseiCompanion.qml +InseiHud 1.0 InseiHud.qml +InseiLiveHash 1.0 InseiLiveHash.qml +InseiTarot 1.0 InseiTarot.qml +InseiHub 1.0 InseiHub.qml diff --git a/lockscreens/kitsune/content/metadata.desktop b/lockscreens/kitsune/content/metadata.desktop new file mode 100644 index 00000000..6668066b --- /dev/null +++ b/lockscreens/kitsune/content/metadata.desktop @@ -0,0 +1,7 @@ +[SddmGreeterTheme] +Name=Kitsune +Description=Ase, the seven-tailed kitsune, and her seven tarot folds — the Jurai lock, at the outer door +Type=sddm-theme +MainScript=Main.qml +ConfigFile=theme.conf +QtVersion=6 diff --git a/lockscreens/kitsune/content/shaders/InseiKitsune.frag b/lockscreens/kitsune/content/shaders/InseiKitsune.frag new file mode 100644 index 00000000..3ef2c565 --- /dev/null +++ b/lockscreens/kitsune/content/shaders/InseiKitsune.frag @@ -0,0 +1,196 @@ +#version 440 +// AseKitsune.frag — Digital Kitsune Companion (v2) +// +// Ase manifested as a 7-tailed fox spirit. +// Each tail tip is tinted with its corresponding fold color: +// tail 0 → truth #4ade80 (ghost green) +// tail 1 → power #f43f5e (crimson) +// tail 2 → wisdom #00B4D8 (water blue) +// tail 3 → judgment #9B1FA0 (violet) +// tail 4 → life #818cf8 (indigo) +// tail 5 → love #f59e0b (amber) +// tail 6 → discipline #6366f1 (purple) + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + float time; + float entropy; + float mood_joy; + float mood_sad; + float mood_sleep; + float fold_r; float fold_g; float fold_b; + float w; float h; + float tail0_r; float tail0_g; float tail0_b; + float tail1_r; float tail1_g; float tail1_b; + float tail2_r; float tail2_g; float tail2_b; + float tail3_r; float tail3_g; float tail3_b; + float tail4_r; float tail4_g; float tail4_b; + float tail5_r; float tail5_g; float tail5_b; + float tail6_r; float tail6_g; float tail6_b; + float tailCount; +} ubuf; + +float hash21(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } + +float noise(vec2 p) { + vec2 i = floor(p); vec2 f = fract(p); + vec2 u = f * f * (3.0 - 2.0 * f); + return mix(mix(hash21(i), hash21(i + vec2(1,0)), u.x), + mix(hash21(i + vec2(0,1)), hash21(i + vec2(1,1)), u.x), u.y); +} + +mat2 rot(float a) { float s = sin(a), c = cos(a); return mat2(c,-s,s,c); } + +float sdCapsule(vec2 p, vec2 a, vec2 b, float r) { + vec2 pa = p-a, ba = b-a; + float h = clamp(dot(pa,ba)/dot(ba,ba), 0.0, 1.0); + return length(pa - ba*h) - r; +} + +float sdEquilateralTriangle(vec2 p, float r) { + const float k = sqrt(3.0); + p.x = abs(p.x) - r; + p.y = p.y + r/k; + if (p.x + k*p.y > 0.0) p = vec2(p.x - k*p.y, -k*p.x - p.y) / 2.0; + p.x -= clamp(p.x, -2.0*r, 0.0); + return -length(p) * sign(p.y); +} + +float opSmoothUnion(float d1, float d2, float k) { + float h = clamp(0.5 + 0.5*(d2-d1)/k, 0.0, 1.0); + return mix(d2, d1, h) - k*h*(1.0-h); +} + +vec3 tailColor(int i) { + if (i == 0) return vec3(ubuf.tail0_r, ubuf.tail0_g, ubuf.tail0_b); + if (i == 1) return vec3(ubuf.tail1_r, ubuf.tail1_g, ubuf.tail1_b); + if (i == 2) return vec3(ubuf.tail2_r, ubuf.tail2_g, ubuf.tail2_b); + if (i == 3) return vec3(ubuf.tail3_r, ubuf.tail3_g, ubuf.tail3_b); + if (i == 4) return vec3(ubuf.tail4_r, ubuf.tail4_g, ubuf.tail4_b); + if (i == 5) return vec3(ubuf.tail5_r, ubuf.tail5_g, ubuf.tail5_b); + return vec3(ubuf.tail6_r, ubuf.tail6_g, ubuf.tail6_b); +} + +void main() { + vec2 uv = qt_TexCoord0; + float asp = ubuf.w / ubuf.h; + // Flip Y so positive Y = up (head above body, tails below) + vec2 p = (uv - 0.5) * vec2(asp, -1.0); + float t = ubuf.time; + + p.y += sin(t * 2.0) * 0.02 * (1.0 - ubuf.mood_sleep); + p.y += sin(t * 0.5) * 0.03; + p.x += sin(t * 0.8) * 0.05; + + // Body sits below center — capsule from -0.1 to +0.1 in body-local space + // Body top in p-space: p.y - 0.05 + 0.1 = p.y + 0.05 + vec2 bodyPos = p + vec2(0.0, -0.05); + float body = sdCapsule(bodyPos, vec2(0.0,-0.1), vec2(0.0,0.1), 0.08); + + // Head at the top end of the body — triangle flipped to point upward + vec2 headPos = p + vec2(0.0, -0.23); + headPos.y *= 1.2; + vec2 headFlipped = vec2(headPos.x, -headPos.y); // flip Y to point triangle upward + float head = sdEquilateralTriangle(headFlipped, 0.08); + + // Ears point upward (+Y) from the top of the head + vec2 earL = headPos - vec2(-0.05, 0.06); earL *= rot( 0.4); + vec2 earR = headPos - vec2( 0.05, 0.06); earR *= rot(-0.4); + + float kitsune = opSmoothUnion(body, head, 0.05); + vec2 earLf = vec2(earL.x, -earL.y); + vec2 earRf = vec2(earR.x, -earR.y); + kitsune = opSmoothUnion(kitsune, sdEquilateralTriangle(earLf, 0.04), 0.02); + kitsune = opSmoothUnion(kitsune, sdEquilateralTriangle(earRf, 0.04), 0.02); + + float tails = 100.0; + vec3 tailColorAccum = vec3(0.0); + float tailWeightSum = 0.0; + // Guard: always 7 until companion system is active + int maxTails = 7; + + for (int i = 0; i < 9; i++) { + if (i >= maxTails) break; + + float fi = float(i); + float angle = (fi / max(float(maxTails - 1), 1.0) - 0.5) * 3.2; + float sway = sin(t * 1.5 + fi * 0.7) * 0.12; + // tailP originates from the body's bottom tip + vec2 tailOrigin = p + vec2(0.0, 0.08); + vec2 tailP = tailOrigin * rot(angle + sway); + tailP.x += sin(tailP.y * 8.0 - t * 4.0) * 0.04 * (tailP.y + 0.1); + + // Capsule grows in -Y direction (downward away from body) + float tailSDF = sdCapsule(tailP, vec2(0.0, 0.0), vec2(0.0, -0.38), + 0.028 * (1.1 + tailP.y * 2.2)); + // tipT: 1.0 at the tip (most negative Y), 0.0 at root + float tipT = clamp((-tailP.y + 0.05) / 0.38, 0.0, 1.0); + float proximity = exp(-max(tailSDF, 0.0) * 40.0); + tailColorAccum += tailColor(i) * tipT * proximity; + tailWeightSum += proximity; + tails = opSmoothUnion(tails, tailSDF, 0.015); + } + + // ── Layered compositing: tails → body → head (front to back = head on top) ── + + vec3 accentColor = vec3(ubuf.fold_r, ubuf.fold_g, ubuf.fold_b); + float furNoise = noise(p * 8.0 + t * 0.5); + vec3 furBase = mix(vec3(0.04, 0.12, 0.20), accentColor, furNoise * 0.5 + ubuf.mood_joy * 0.5); + furBase = mix(furBase, vec3(0.06, 0.06, 0.08), ubuf.mood_sleep * 0.7); + + vec3 resolvedTailColor = tailWeightSum > 0.001 + ? tailColorAccum / tailWeightSum : accentColor; + + // Layer 0 — tails (deepest, behind everything) + float tailMask = smoothstep(0.01, -0.01, tails); + float tipInfluence = clamp(tailWeightSum * 1.4, 0.0, 1.0) * tailMask; + vec3 tailFur = mix(furBase, resolvedTailColor, tipInfluence * 0.85); + float tailGlow = exp(-tails * 8.0) * tipInfluence; + vec3 tailLayer = tailFur * tailMask + resolvedTailColor * tailGlow * 2.0; + + // Layer 1 — body (covers tail roots) + float bodyMask = smoothstep(0.01, -0.01, body); + float bodyGlow = exp(-body * 15.0); + vec3 bodyLayer = furBase * bodyMask + accentColor * bodyGlow * 0.8; + + // Layer 2 — head (on top of body) + float headMask = smoothstep(0.01, -0.01, head); + // Slightly lighter fur on the face + vec3 faceFur = mix(furBase, vec3(0.08, 0.18, 0.28), 0.3 + furNoise * 0.2); + float headGlow = exp(-head * 15.0); + vec3 headLayer = faceFur * headMask + accentColor * headGlow * 0.6; + + // Layer 3 — ears (on top of head) + float earLMask = smoothstep(0.01, -0.01, sdEquilateralTriangle(vec2(earL.x, -earL.y), 0.04)); + float earRMask = smoothstep(0.01, -0.01, sdEquilateralTriangle(vec2(earR.x, -earR.y), 0.04)); + float earMask = clamp(earLMask + earRMask, 0.0, 1.0); + vec3 earLayer = furBase * earMask; + + // Composite back-to-front: each layer paints over the previous using its mask + vec3 finalColor = vec3(0.0); + finalColor = mix(finalColor, tailLayer, tailMask); + finalColor = mix(finalColor, bodyLayer, bodyMask); + finalColor = mix(finalColor, headLayer, headMask); + finalColor = mix(finalColor, earLayer, earMask); + + // Outer glow around the whole form + float kitsuneFull = opSmoothUnion(kitsune, tails, 0.03); + float outerGlow = exp(-kitsuneFull * 5.0) * 0.3; + finalColor += accentColor * outerGlow * 0.5; + + // Eye — glowing white dot, drawn last so it's always on top + float eye = length(headPos - vec2(0.0, 0.0)) - 0.015 * (1.0 - ubuf.mood_sleep * 0.9); + finalColor = mix(finalColor, vec3(1.0), + smoothstep(0.01, 0.0, eye) * (0.8 + 0.2 * sin(t * 10.0))); + + // Alpha: union of all layers + float totalSDF = opSmoothUnion(kitsuneFull, tails, 0.03); + float bodyAlpha = smoothstep(0.012, -0.008, totalSDF); + float haloAlpha = exp(-max(totalSDF, 0.0) * 22.0) * 0.9; + float alpha = clamp(bodyAlpha + haloAlpha, 0.0, 1.0) * ubuf.qt_Opacity; + fragColor = vec4(finalColor * alpha, alpha); +} diff --git a/lockscreens/kitsune/content/shaders/InseiKitsune.frag.qsb b/lockscreens/kitsune/content/shaders/InseiKitsune.frag.qsb new file mode 100644 index 00000000..5b5e8c18 Binary files /dev/null and b/lockscreens/kitsune/content/shaders/InseiKitsune.frag.qsb differ diff --git a/lockscreens/kitsune/content/shaders/InseiSoul.frag b/lockscreens/kitsune/content/shaders/InseiSoul.frag new file mode 100644 index 00000000..b00fc6b2 --- /dev/null +++ b/lockscreens/kitsune/content/shaders/InseiSoul.frag @@ -0,0 +1,299 @@ +#version 440 +// ShiguruiSoul.frag — Neural Core + 7-Fold Swarm +// Extracted from SanctumCorridors.frag. +// +// Inspirations: +// Neural Synapse (VoXelo) — action potential wave, soma biology +// Drifting 3D Spell (SourceAura) — iridescent film, Fresnel, crackle +// +// Layers (bottom → top): +// 1. Shigurui Core — organic simplex sphere, iridescent film, Fresnel rim, +// geodesic cage, singularity void +// 2. 7-Fold Swarm — one packet per fold, canonical hues, orbit collapses +// on inferring/commandPulse +// 3. Action Potential Ring — expands outward on commandPulse, rainbow sweep +// 4. Surface Crackle — brief electric tendrils on commandPulse spike + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + float time; + float tpsBeat; + float inferring; + float disciplineHue; + float commandPulse; + float obeliskOpen; + float cursorX; + float cursorY; + float w; + float h; + float mistDensity; + float atmospherePhase; + float receivePulse; // 0→1: inward ring — query received, Ase listening +} ubuf; + +// ── Utilities ───────────────────────────────────────────────────────── +float hash11(float n) { return fract(sin(n) * 43758.5453); } +float hash21(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } + +float noise(vec2 p) { + vec2 i = floor(p); vec2 f = fract(p); + vec2 u = f * f * (3.0 - 2.0 * f); + return mix(mix(hash21(i), hash21(i + vec2(1,0)), u.x), + mix(hash21(i + vec2(0,1)), hash21(i + vec2(1,1)), u.x), u.y); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +float hexSDF(vec2 p, float r) { + p = abs(p); + return max(dot(p, normalize(vec2(1.0, 1.732))), p.x) - r; +} + +// ── Simplex 3D ──────────────────────────────────────────────────────── +vec3 mod289v3(vec3 x) { return x - floor(x * (1.0/289.0)) * 289.0; } +vec4 mod289v4(vec4 x) { return x - floor(x * (1.0/289.0)) * 289.0; } +vec4 permute(vec4 x) { return mod289v4(((x * 34.0) + 1.0) * x); } +vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } + +float snoise3(vec3 v) { + const vec2 C = vec2(1.0/6.0, 1.0/3.0); + const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); + vec3 i = floor(v + dot(v, C.yyy)); + vec3 x0 = v - i + dot(i, C.xxx); + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min(g.xyz, l.zxy); + vec3 i2 = max(g.xyz, l.zxy); + vec3 x1 = x0 - i1 + C.xxx; + vec3 x2 = x0 - i2 + C.yyy; + vec3 x3 = x0 - D.yyy; + i = mod289v3(i); + vec4 p = permute(permute(permute( + i.z + vec4(0.0, i1.z, i2.z, 1.0)) + + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + + i.x + vec4(0.0, i1.x, i2.x, 1.0)); + float n_ = 0.142857142857; + vec3 ns = n_ * D.wyz - D.xzx; + vec4 j = p - 49.0 * floor(p * ns.z * ns.z); + vec4 x_ = floor(j * ns.z); + vec4 y_ = floor(j - 7.0 * x_); + vec4 x2_ = x_ * ns.x + ns.yyyy; + vec4 y2_ = y_ * ns.x + ns.yyyy; + vec4 h = 1.0 - abs(x2_) - abs(y2_); + vec4 b0 = vec4(x2_.xy, y2_.xy); + vec4 b1 = vec4(x2_.zw, y2_.zw); + vec4 s0 = floor(b0) * 2.0 + 1.0; + vec4 s1 = floor(b1) * 2.0 + 1.0; + vec4 sh = -step(h, vec4(0.0)); + vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; + vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; + vec3 p0 = vec3(a0.xy, h.x); + vec3 p1 = vec3(a0.zw, h.y); + vec3 p2 = vec3(a1.xy, h.z); + vec3 p3 = vec3(a1.zw, h.w); + vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3))); + p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w; + vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); + m = m * m; + return 42.0 * dot(m * m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3))); +} + +// ── Rainbow palette (Neural Synapse style) ──────────────────────────── +vec3 palette(float t) { + return vec3(0.5) + vec3(0.5) * cos(6.28318 * (vec3(1.0) * t + vec3(0.0, 0.33, 0.67))); +} + +// ── Shigurui Core ───────────────────────────────────────────────────── +float shiguruiCore(vec2 p, float t, float metabolism, + out vec3 rimColor, out float iridescence) { + float r = length(p); + float a = atan(p.y, p.x); + + vec3 sph = vec3(sin(r * 3.14159) * cos(a), + sin(r * 3.14159) * sin(a), + cos(r * 3.14159)); + + float n1 = snoise3(sph * 2.5 + vec3(t * 0.3 * metabolism)); + float n2 = snoise3(sph * 5.0 - vec3(t * 0.5)) * 0.5; + float nd = n1 * 0.30 + n2 * 0.15; + + float boundary = 0.11 + nd * 0.04; + float core = smoothstep(boundary, boundary * 0.6, r); + + float fresnel = pow(1.0 - smoothstep(0.0, boundary * 1.4, r), 2.5); + float rim = smoothstep(boundary * 1.3, boundary * 0.85, r) + * smoothstep(boundary * 0.5, boundary, r) + * (0.7 + fresnel * 0.8); + + // Iridescent film — R/G/B at different wavelengths + float filmThickness = 280.0 + nd * 420.0 + noise(p * 4.0 + vec2(t * 0.05)) * 180.0; + vec3 film = vec3( + sin(filmThickness / 700.0 * 18.0 + t * 0.5) * 0.5 + 0.5, + sin(filmThickness / 530.0 * 18.0 + t * 0.6) * 0.5 + 0.5, + sin(filmThickness / 440.0 * 18.0 + t * 0.7) * 0.5 + 0.5 + ); + iridescence = rim * (0.4 + metabolism * 0.3); + rimColor = film; + + // Singularity void — deeper than before + float singR = 0.020; + float singVoid = smoothstep(singR, singR * 0.3, r); + float singAngle = a - t * 0.8; + float singHex = smoothstep(0.0, -0.5, + hexSDF((p / max(singR * 1.6, 0.001)) * 0.6, 0.55)) + * smoothstep(singR * 2.2, singR * 1.4, r) * 0.6; + + float flare = (ubuf.commandPulse + ubuf.inferring * ubuf.tpsBeat) * 0.4; + + return core + rim * 0.7 - singVoid * 0.7 + singHex + flare * core; +} + +// ── 7-Fold Swarm ────────────────────────────────────────────────────── +vec3 foldSwarm(vec2 p, float t, int foldIdx, float metabolism, float pull, + out float intensity) { + float fi = float(foldIdx); + float seed = hash11(fi * 17.3 + 3.3); + float speed = (0.28 + seed * 0.32) * metabolism; + float angle = t * speed + fi * (6.28318 / 7.0); + + float orbitMax = 0.16 + seed * 0.10; + float orbitMin = 0.025; + float orbitR = mix(orbitMax, orbitMin, pull); + + vec2 pos = vec2(cos(angle) * orbitR, sin(angle + t * seed * 0.3) * orbitR * 0.7); + + // Emission trail — ghost at previous position + float trailAngle = angle - 0.18 * speed; + vec2 trailPos = vec2(cos(trailAngle) * orbitR, sin(trailAngle + t * seed * 0.3) * orbitR * 0.7); + + float d = length(p - pos); + float dTrail = length(p - trailPos); + float glow = exp(-d * d / (0.0004 + pull * 0.0008)) * (1.0 + pull * 2.5); + float trail = exp(-dTrail * dTrail / 0.0006) * 0.35; + float core = smoothstep(0.013, 0.004, abs(p.x - pos.x) + abs(p.y - pos.y)); + intensity = core * 0.85 + glow * 0.5 + trail * 0.3; + + // Fold hues — canonical TransparentWorld mapping + float hues[7]; + hues[0] = 0.38; // Eye — emerald + hues[1] = 0.55; // Blade — cerulean + hues[2] = 0.82; // Sage — blood violet + hues[3] = 0.68; // Mirror — indigo + hues[4] = 0.12; // Forge — amber + hues[5] = 0.97; // Phantom — rose + hues[6] = 0.70; // Shroud — lavender + + float hue = hues[foldIdx]; + float foldMatch = 1.0 - abs(hue - ubuf.disciplineHue) * 2.0; + float sat = 0.75 + clamp(foldMatch, 0.0, 1.0) * 0.20; + float val = 0.80 + clamp(foldMatch, 0.0, 1.0) * 0.20 + pull * 0.30; + + return hsv2rgb(vec3(hue, sat, val)); +} + +void main() { + vec2 uv = qt_TexCoord0; + float asp = ubuf.w / ubuf.h; + vec2 p = vec2(uv.x * asp, uv.y); + vec2 center = vec2(0.5 * asp, 0.5); + vec3 color = vec3(0.0); + float breach = 1.0 - ubuf.mistDensity; + + float metabolism = 1.0 + ubuf.inferring * 1.5 + ubuf.tpsBeat * 0.8; + float nucleusScale = 1.75 + ubuf.obeliskOpen * 0.38; // doubled from 3.5 + float pull = clamp(ubuf.inferring * ubuf.tpsBeat + ubuf.commandPulse * 0.6, 0.0, 1.0); + vec2 localP = (p - center) * nucleusScale; + + // ── 1. Shigurui Core ───────────────────────────────────────────── + vec3 rimCol; float irid; + float nucleus = shiguruiCore(localP, ubuf.time, metabolism, rimCol, irid); + + vec3 coreCol = mix(hsv2rgb(vec3(0.55, 0.7, 1.0)), + hsv2rgb(vec3(ubuf.disciplineHue, 0.3, 1.0)), + ubuf.inferring * 0.8); + coreCol = mix(coreCol, hsv2rgb(vec3(0.78, 0.9, 0.9)), breach * 0.5); + + vec3 iridCol = mix(coreCol, rimCol, irid * 0.55); + + color += iridCol * nucleus * (0.9 + ubuf.inferring * 0.3); + + // ── 2. 7-Fold Swarm ─────────────────────────────────────────────── + for (int fi = 0; fi < 7; fi++) { + float pIntensity; + vec3 pCol = foldSwarm(localP, ubuf.time, fi, metabolism, pull, pIntensity); + pCol = mix(pCol, vec3(1.0), breach * 0.3); + color += pCol * pIntensity * 0.45; + } + + // ── 3. Action Potential Ring (Neural Synapse) ───────────────────── + // Expands outward from core when commandPulse fires. + // Rainbow palette sweep — the "firing" moment. + if (ubuf.commandPulse > 0.02) { + float dist = length(p - center); + float maxR = 0.38 * asp; + float waveFront = ubuf.commandPulse * maxR; + float distFromWave = dist - waveFront; + + float edge = exp(-pow(distFromWave, 2.0) / 0.00012) * ubuf.commandPulse; + float residual = smoothstep(waveFront, waveFront - 0.08, dist) * ubuf.commandPulse * 0.25; + + float angle = atan(p.y - center.y, p.x - center.x); + vec3 waveCol = palette(angle * 0.15 + dist * 0.8 - ubuf.time * 0.5); + waveCol = mix(waveCol, hsv2rgb(vec3(ubuf.disciplineHue, 0.8, 1.0)), 0.45); + + color += waveCol * (edge * 2.0 + residual); + } + + // ── 3b. Receive Ring — inward contraction (query received) ──────── + // Starts at outer edge, contracts toward core as receivePulse 0→1. + // Discipline hue — cooler, focused. "The signal arrives." + if (ubuf.receivePulse > 0.02) { + float dist = length(p - center); + float maxR = 0.38 * asp; + // Inward: waveFront shrinks from maxR to 0 as receivePulse goes 0→1 + float waveFront = (1.0 - ubuf.receivePulse) * maxR; + float distFromWave = dist - waveFront; + + // Leading edge — slightly wider than outward ring for softer feel + float edge = exp(-pow(distFromWave, 2.0) / 0.00018) * ubuf.receivePulse; + // Trailing wake — fills inward behind the contracting front + float wake = smoothstep(waveFront, waveFront + 0.06, dist) * ubuf.receivePulse * 0.20; + + // Discipline hue with slight iridescent shift + float angle = atan(p.y - center.y, p.x - center.x); + vec3 receiveCol = hsv2rgb(vec3(ubuf.disciplineHue, 0.90, 1.0)); + // Subtle angular shimmer — not full rainbow, just a hint + receiveCol = mix(receiveCol, + hsv2rgb(vec3(mod(ubuf.disciplineHue + 0.15, 1.0), 0.75, 1.0)), + abs(sin(angle * 3.0 + ubuf.time * 0.3)) * 0.35); + + color += receiveCol * (edge * 1.8 + wake); + } + + // ── 4. Surface Crackle (Spell bubble) ──────────────────────────── + // Brief electric tendrils on the core surface when commandPulse spikes. + if (ubuf.commandPulse > 0.65) { + float dist = length(localP); + float boundary = 0.12; + // Only on the core surface + float surfaceMask = exp(-pow(dist - boundary, 2.0) / 0.0008); + float crackle = noise(localP * 18.0 + vec2(ubuf.time * 4.0)) * 0.5 + 0.5; + crackle = pow(crackle, 6.0); // sharpen to tendrils + float intensity = crackle * surfaceMask * (ubuf.commandPulse - 0.65) * 3.0; + vec3 crackleCol = mix(vec3(0.8, 0.9, 1.0), + hsv2rgb(vec3(ubuf.disciplineHue, 0.6, 1.0)), 0.5); + color += crackleCol * intensity; + } + + // Alpha — additive over the sanctum background + float alpha = length(color) * ubuf.qt_Opacity * 0.92; + fragColor = vec4(color, clamp(alpha, 0.0, 1.0)); +} diff --git a/lockscreens/kitsune/content/shaders/InseiSoul.frag.qsb b/lockscreens/kitsune/content/shaders/InseiSoul.frag.qsb new file mode 100644 index 00000000..da373193 Binary files /dev/null and b/lockscreens/kitsune/content/shaders/InseiSoul.frag.qsb differ diff --git a/lockscreens/kitsune/content/shaders/crystallize.frag b/lockscreens/kitsune/content/shaders/crystallize.frag new file mode 100644 index 00000000..d74447f0 --- /dev/null +++ b/lockscreens/kitsune/content/shaders/crystallize.frag @@ -0,0 +1,299 @@ +#version 440 +// crystallize.frag — Akashic Crystal Formation · V2 +// =================================================== +// True SDF Voronoi fracture with iridescent thin-film facets, +// caustic light scattering, chromatic aberration at edges, +// and a domain-warped fBm shatter burst. +// +// Techniques: +// · Voronoi SDF — sharp cell boundaries, not smooth Worley falloff +// · Hex lattice SDF — facets align to hex geometry +// · Thin-film iridescence — wavelength-dependent phase per facet +// · Caustic scattering — bright spots where facets focus light +// · Chromatic aberration — R/G/B split at crystal edges +// · fBm domain-warped shatter — membrane tears, not just rings +// · 6 SDF line-segment shards — true hex symmetry +// +// Uniforms: +// u_time — continuous time +// u_confidence — 0–1, prediction confidence (drives brightness) +// u_progress — 0–1, Tab-hold crystallization spread (left→right) +// u_shatter — 0–1, shatter burst (decays fast after execute) +// u_morphR/G/B — active mode color +// u_width/height — viewport size + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + float u_time; + float u_confidence; + float u_progress; + float u_shatter; + float u_width; + float u_height; + float u_morphR; + float u_morphG; + float u_morphB; +} ubuf; + +const float PI = 3.14159265359; +const float TAU = 6.28318530718; + +// ── Hash ────────────────────────────────────────────────────────────── +vec2 hash2(vec2 p) { + p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3))); + return fract(sin(p) * 43758.5453); +} +float hash1(vec2 p) { + return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); +} +float hash1f(float n) { return fract(sin(n) * 43758.5453); } + +// ── Value noise ─────────────────────────────────────────────────────── +float vnoise(vec2 p) { + vec2 i = floor(p); vec2 f = fract(p); + vec2 u = f * f * (3.0 - 2.0 * f); + return mix(mix(hash1(i), hash1(i + vec2(1,0)), u.x), + mix(hash1(i + vec2(0,1)), hash1(i + vec2(1,1)), u.x), u.y); +} + +// 2-octave fBm — cheap, used for domain warping +float fbm2(vec2 p) { + return vnoise(p) * 0.6 + vnoise(p * 2.1 + vec2(3.7, 1.9)) * 0.4; +} + +// ── Hex SDF — distance to nearest hex grid edge ─────────────────────── +// Returns 0 at hex center, 1 at hex edge +float hexGrid(vec2 p, float scale) { + p *= scale; + p.x *= 1.1547; // 2/sqrt(3) + vec2 p1 = mod(p, vec2(2.0, 3.464)) - vec2(1.0, 1.732); + vec2 p2 = mod(p - vec2(1.0, 1.732), vec2(2.0, 3.464)) - vec2(1.0, 1.732); + vec2 ap1 = abs(p1); vec2 ap2 = abs(p2); + float d1 = max(dot(ap1, normalize(vec2(1.0, 1.732))), ap1.x); + float d2 = max(dot(ap2, normalize(vec2(1.0, 1.732))), ap2.x); + return smoothstep(0.80, 0.97, min(d1, d2)); +} + +// ── Voronoi SDF — returns (minDist, cellId, secondMinDist) ─────────── +// Sharp cell boundaries via F2-F1 distance field +vec3 voronoiSDF(vec2 p) { + vec2 i = floor(p); + vec2 f = fract(p); + + float F1 = 8.0, F2 = 8.0; + vec2 cellId = vec2(0.0); + + for (int y = -2; y <= 2; y++) { + for (int x = -2; x <= 2; x++) { + vec2 neighbor = vec2(float(x), float(y)); + vec2 point = hash2(i + neighbor); + // Slow drift — crystal cells breathe + point = 0.5 + 0.5 * sin(ubuf.u_time * 0.25 + TAU * point); + vec2 diff = neighbor + point - f; + float dist = dot(diff, diff); // squared distance + if (dist < F1) { F2 = F1; F1 = dist; cellId = i + neighbor; } + else if (dist < F2) { F2 = dist; } + } + } + // F2-F1 = distance to cell boundary (sharp edge at 0) + return vec3(sqrt(F1), cellId.x + cellId.y * 57.0, sqrt(F2) - sqrt(F1)); +} + +// ── Thin-film iridescence — wavelength-dependent interference ───────── +// Each facet has a slightly different thickness → different color +// Based on ShiguruiSoul's film model, adapted for crystal facets +vec3 thinFilm(float thickness, float angle, float t) { + // Three wavelengths: R=700nm, G=530nm, B=440nm + float phaseR = thickness / 700.0 * 18.0 + angle * 2.1 + t * 0.4; + float phaseG = thickness / 530.0 * 18.0 + angle * 2.5 + t * 0.5; + float phaseB = thickness / 440.0 * 18.0 + angle * 3.0 + t * 0.6; + return vec3( + sin(phaseR) * 0.5 + 0.5, + sin(phaseG) * 0.5 + 0.5, + sin(phaseB) * 0.5 + 0.5 + ); +} + +// ── SDF line segment — for shatter shards ──────────────────────────── +float sdSegment(vec2 p, vec2 a, vec2 b) { + vec2 pa = p - a, ba = b - a; + float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); + return length(pa - ba * h); +} + +void main() { + vec2 uv = qt_TexCoord0; + float asp = ubuf.u_width / ubuf.u_height; + vec2 uvA = vec2(uv.x * asp, uv.y); + + vec3 morphCol = vec3(ubuf.u_morphR, ubuf.u_morphG, ubuf.u_morphB); + vec3 ghostWhite = vec3(0.878, 0.969, 1.000); + vec3 obsidian = vec3(0.039, 0.039, 0.039); + + vec3 col = vec3(0.0); + float alpha = 0.0; + + // ── Crystallization spread mask ─────────────────────────────────── + // Crystals form left→right as progress advances + float spreadMask = 1.0 - smoothstep( + ubuf.u_progress - 0.20, + ubuf.u_progress + 0.20, + uv.x + ); + float crystalStrength = spreadMask * ubuf.u_progress; + + if (crystalStrength > 0.005) { + + // ── Voronoi cell structure ──────────────────────────────────── + // Scale varies with confidence — high confidence = finer crystals + float cellScale = 2.8 + ubuf.u_confidence * 1.4; + vec3 vor = voronoiSDF(uv * cellScale + ubuf.u_time * 0.04); + float F1 = vor.x; // distance to nearest cell center + float cellId = vor.y; // unique cell identifier + float boundary = vor.z; // F2-F1: distance to cell boundary + + // ── Hex lattice overlay ─────────────────────────────────────── + float hexEdge = hexGrid(uv, 3.2 + ubuf.u_confidence * 0.8); + + // ── Thin-film iridescence per facet ─────────────────────────── + // Each cell has a unique thickness based on its id + float facetThickness = 180.0 + hash1f(cellId) * 320.0 + + vnoise(uv * 2.0 + ubuf.u_time * 0.03) * 80.0; + float facetAngle = hash1f(cellId * 7.3) * TAU; + vec3 filmColor = thinFilm(facetThickness, facetAngle, ubuf.u_time); + // Blend film toward morphColor — system-coherent iridescence + filmColor = mix(filmColor, morphCol, 0.45); + + // ── Caustic scattering ──────────────────────────────────────── + // Bright spots where facets focus light — concentrated near cell centers + float causticMask = exp(-F1 * F1 * 18.0); // bright at cell center + float causticNoise = vnoise(uv * 6.0 + ubuf.u_time * 0.08) * 0.5 + 0.5; + float caustic = causticMask * causticNoise * ubuf.u_confidence * 0.6; + + // ── Cell boundary edges — sharp crystal facet lines ─────────── + // boundary (F2-F1) is 0 at cell edges → sharp bright lines + float edgeSharp = exp(-boundary * boundary * 80.0); + // Hex lattice reinforces the facet structure + float edgeCombined = max(edgeSharp, hexEdge * 0.7); + + // ── Chromatic aberration at crystal edges ───────────────────── + // Split R/G/B at slightly different edge distances (like AkashicTear) + float edgeR = exp(-(boundary - 0.005) * (boundary - 0.005) * 80.0); + float edgeG = exp(-(boundary) * (boundary) * 80.0); + float edgeB = exp(-(boundary + 0.005) * (boundary + 0.005) * 80.0); + vec3 chromaEdge = vec3(edgeR, edgeG, edgeB) * 0.5; + + // ── Crystal body color ──────────────────────────────────────── + // Interior: morphColor-tinted obsidian with film iridescence + // Edges: bright white + chromatic split + vec3 interiorCol = mix(obsidian, morphCol * 0.5, ubuf.u_confidence * 0.4); + interiorCol = mix(interiorCol, filmColor, F1 * 0.6 * ubuf.u_confidence); + + vec3 edgeCol = mix(morphCol, ghostWhite, edgeSharp * ubuf.u_confidence * 0.8); + edgeCol += chromaEdge * 0.6; + + vec3 crystalCol = mix(interiorCol, edgeCol, edgeCombined); + // Caustic bright spots + crystalCol += mix(morphCol, ghostWhite, 0.6) * caustic; + // Subsurface scatter — morphColor bleeds through depth + crystalCol += morphCol * (1.0 - F1) * 0.15 * ubuf.u_confidence; + + // ── Frost breath — volumetric noise fog ahead of front ──────── + // 2D noise-modulated, not just a 1D Gaussian + float frostFront = ubuf.u_progress + 0.12; + float frostDist = abs(uv.x - frostFront); + float frostNoise = fbm2(uv * 4.0 + ubuf.u_time * 0.15) * 0.5 + 0.5; + float frost = exp(-frostDist * frostDist / 0.006) + * frostNoise * ubuf.u_progress * 0.5; + crystalCol += morphCol * frost * 0.35; + + // ── Alpha ───────────────────────────────────────────────────── + float crystalAlpha = (edgeCombined * 0.45 + + caustic * 0.25 + + frost * 0.20 + + (1.0 - F1) * 0.08 * ubuf.u_confidence) + * crystalStrength; + + col += crystalCol * crystalStrength; + alpha += crystalAlpha; + } + + // ── SHATTER BURST — domain-warped fBm membrane tear ────────────── + if (ubuf.u_shatter > 0.005) { + float s = ubuf.u_shatter; + vec2 center = vec2(0.5, 0.5); + vec2 p = uv - center; + float dist = length(p); + float angle = atan(p.y, p.x); + + // Domain warp — fBm tears the membrane (from AkashicTear) + vec2 warpQ = vec2( + fbm2(p * 3.0 + vec2(ubuf.u_time * 0.2)), + fbm2(p * 3.0 + vec2(1.7, 9.2) + ubuf.u_time * 0.15) + ); + vec2 warpedP = p + warpQ * 0.08 * s; + float warpedDist = length(warpedP); + + // Primary shockwave — warped ring + float waveR = s * 0.52; + float wave = exp(-pow(warpedDist - waveR, 2.0) / 0.0025) + * s * (1.0 - s) * 2.5; + vec3 waveCol = mix(morphCol, ghostWhite, s * 0.6); + col += waveCol * wave; + alpha += wave * 0.7; + + // ── 6 SDF line-segment shards — true hex symmetry ───────────── + // Each shard is a proper SDF line segment, not angle-based Gaussian + for (int i = 0; i < 6; i++) { + float shardAngle = float(i) * (PI / 3.0); // 60° steps + // Slight random offset per shard — crystals don't shatter perfectly + shardAngle += hash1f(float(i) * 3.7) * 0.15 - 0.075; + + vec2 shardDir = vec2(cos(shardAngle), sin(shardAngle)); + + // Shard extends from near-center to outer radius + float innerR = 0.02 + s * 0.04; + float outerR = s * 0.65 + hash1f(float(i) * 5.1) * s * 0.15; + vec2 shardA = shardDir * innerR; + vec2 shardB = shardDir * outerR; + + // SDF distance to this shard line + float shardDist = sdSegment(p, shardA, shardB); + + // Shard width tapers from base to tip + float t_along = clamp(dot(p - shardA, shardDir) / max(outerR - innerR, 0.001), 0.0, 1.0); + float shardWidth = mix(0.008, 0.002, t_along) * (1.0 - s * 0.5); + + float shardGlow = exp(-shardDist * shardDist / (shardWidth * shardWidth)); + shardGlow *= (1.0 - s) * (1.0 - t_along * 0.6); // fade toward tip and over time + + // Iridescent shard color — each shard gets a film tint + float shardFilmT = hash1f(float(i) * 11.3) * 0.5 + s * 0.3; + vec3 shardCol = mix(morphCol, ghostWhite, shardFilmT); + // Chromatic split along shard length + shardCol.r += t_along * 0.15; + shardCol.b -= t_along * 0.10; + + col += shardCol * shardGlow * 1.8; + alpha += shardGlow * 0.8; + } + + // Secondary micro-ring — tighter, morphColor + float ring2 = exp(-pow(dist - s * 0.22, 2.0) / 0.0008) + * s * (1.0 - s) * 1.2; + col += morphCol * ring2; + alpha += ring2 * 0.4; + + // Void expansion — center clears as shatter completes + float voidExpand = smoothstep(0.0, 0.7, s); + float voidFill = smoothstep(waveR * 0.6, waveR * 0.1, dist) * voidExpand; + col = mix(col, vec3(0.0), voidFill * 0.85); + alpha = mix(alpha, 0.0, voidFill * 0.8); + } + + fragColor = vec4(col * alpha, alpha * ubuf.qt_Opacity); +} diff --git a/lockscreens/kitsune/content/shaders/crystallize.frag.qsb b/lockscreens/kitsune/content/shaders/crystallize.frag.qsb new file mode 100644 index 00000000..8e756172 Binary files /dev/null and b/lockscreens/kitsune/content/shaders/crystallize.frag.qsb differ diff --git a/lockscreens/kitsune/content/theme.conf b/lockscreens/kitsune/content/theme.conf new file mode 100644 index 00000000..e0bf742c --- /dev/null +++ b/lockscreens/kitsune/content/theme.conf @@ -0,0 +1,7 @@ +[General] +# Kitsune — Ase, the seven-tailed kitsune, at the door. +# Her greeter reads exactly one key: the username rule she was +# built under (the valence theme runs the same). With it false, +# the name is lowered before it is spoken to PAM, matching +# kitsune.conf on the machine she first stood on. +AllowUppercaseLettersInUsernames=false diff --git a/lockscreens/kitsune/content/tw/ArOrchestrator.qml b/lockscreens/kitsune/content/tw/ArOrchestrator.qml new file mode 100644 index 00000000..9aaf38a5 --- /dev/null +++ b/lockscreens/kitsune/content/tw/ArOrchestrator.qml @@ -0,0 +1,30 @@ +pragma Singleton + +import QtQuick + +// ArOrchestrator — the resting seat. +// +// In Jurai shell this singleton is the AR stage's living state: the +// daemon's pulse, the scry phases, the folds drawn out on the hub. +// A lock face has no stage and no daemon, and the scene's hub — the +// one seat that reads him — is never raised at the door. What stands +// here is his shape at rest: every property the scene can name, +// holding the quiet values, so the face stands on its own in any +// host. Written for the RyoStore port (2026-10-25); the scene files +// themselves are unmodified. +QtObject { + property real aseWidgetCenterX: 0.5 + property real aseWidgetCenterY: 0.0 + property color prismaticColor: "#bec2ff" + property int thinkingPhase: 0 + property string scryCommPhase: "idle" + property string daemonState: "asleep" + property var aseDrawnFolds: [] + property string intelActiveMode: "" + property real commandPulse: 0.0 + property real animationPhase: 0.0 + property string breathingStyle: "moon" + property real shiguruiTokensPerSec: 0.0 + property string aseHubDrawFold: "" + property string aseHubRecallFold: "" +} diff --git a/lockscreens/kitsune/content/tw/TransparentWorld.qml b/lockscreens/kitsune/content/tw/TransparentWorld.qml new file mode 100644 index 00000000..382c44f0 --- /dev/null +++ b/lockscreens/kitsune/content/tw/TransparentWorld.qml @@ -0,0 +1,599 @@ +/* TransparentWorld.qml — Shigurui Visual Substrate + * ===================================================== + * Singleton token system for the "Transparent World" aesthetic. + * + * 透き通る世界 — Sukitōru Sekai + * The Transparent World is not blur. It is perfect clarity. + * The ability to see through surfaces — perceiving internal structure, + * blood flow, muscle contractions, the hidden truth beneath the visible. + * Depth is achieved through layered darkness and precise light, + * not gaussian smear. Every edge is sharp. Every accent is surgical. + * + * Design principles: + * · Obsidian depth — dark substrates that recede, not obscure + * · Bioluminescent precision — accent colors as living light, not decoration + * · Sharp borders — 1px crisp lines, not blurred halos + * · Shadow glow — depth through shadow, never through blur + * · Crystalline text — maximum legibility, zero softening + * + * Usage: + * import "." as TW + * color: TW.TransparentWorld.resolveAccent(obeliskMode, discipline, intelMode, isInferring) + * layer.effect: TW.TransparentWorld.glowEffect(morphColor) // shadow only + * + * All components reference this singleton — one change propagates everywhere. + */ +pragma Singleton +import QtQuick + +QtObject { + id: theme + + // ── Glass substrate ─────────────────────────────────────────────── + // Obsidian black — deep but not opaque. Depth through darkness, not blur. + readonly property color glassBase: Qt.rgba(0.039, 0.039, 0.039, 0.72) + readonly property color glassBaseDense: Qt.rgba(0.039, 0.039, 0.039, 0.88) // overlays, cards + readonly property color glassBaseLight: Qt.rgba(0.039, 0.039, 0.039, 0.45) // hover states + readonly property color glassBaseSolid: Qt.rgba(0.016, 0.016, 0.020, 0.97) // terminal, void + readonly property real glassBorderAlpha: 0.35 // default border opacity + readonly property real glassBorderFocus: 0.60 // focused/active border opacity + readonly property int glassRadius: 12 + readonly property int glassRadiusLarge: 16 + readonly property int glassRadiusSmall: 8 + + // ── Shadow depth tokens — depth through shadow, never blur ──────── + // Use these to build MultiEffect shadow configs. + // Rule: layer.effect on a component = shadow only. No blurEnabled. + readonly property real shadowBlur: 0.85 // MultiEffect shadowBlur (0–1) + readonly property int shadowOffsetY: 6 // px vertical offset + readonly property real shadowAlpha: 0.25 // base shadow opacity + readonly property real shadowAlphaFocus: 0.45 // focused/active shadow opacity + + // ── Typography ──────────────────────────────────────────────────── + // Base: 14px. All sizes are semantic — use tokens, never hardcode. + // + // Scale (ascending): + // fontSizeMicro 9 timestamps, ghost decorations, faint hints + // fontSizeHint 10 tooltips, secondary meta, hover-only text + // fontSizeBadge 11 status chips, tier labels, badge text + // fontSizeLabel 12 field names, section sub-labels, captions + // fontSizeBase 14 ← baseline — body text, data readouts, chat + // fontSizeTitle 16 panel headers, ROM titles, primary labels + // fontSizeGlyph 20 icon glyphs, sigils, decorative symbols + // + // Exceptions (do not remap): + // Codex editor text stays at 13 (code density requirement) + // Display glyphs ≥22 are intentional visual anchors + readonly property string fontFamily: "3270 Nerd Font Mono, Fira Code" + readonly property string fontFamilyMono: "3270 Nerd Font Mono, Fira Code" + readonly property int fontSizeMicro: 9 // timestamps, ghost decorations + readonly property int fontSizeHint: 10 // tooltips, secondary meta + readonly property int fontSizeBadge: 11 // chips, tier labels, badges + readonly property int fontSizeLabel: 12 // field names, captions + readonly property int fontSizeBase: 14 // ← baseline body text + readonly property int fontSizeBody: 14 // alias — body text, data readouts + readonly property int fontSizeTelemetry: 14 // telemetry data readouts + readonly property int fontSizeTitle: 16 // panel headers, ROM titles + readonly property int fontSizeGlyph: 20 // icon glyphs, sigils + // Legacy aliases — kept for backward compat, map to nearest semantic token + readonly property int fontSizeMin: 9 // was 11 — now micro floor + readonly property int fontSizeTelemetryOld:14 // unchanged + + // Obsidian outline stroke for readability against bright accents + readonly property color fontOutlineColor: Qt.rgba(0.039, 0.039, 0.039, 0.50) + + // ── Animation ───────────────────────────────────────────────────── + readonly property int colorTransition: 350 // ms, InOutQuad for all color changes + readonly property int fadeTransition: 200 // ms, opacity fades + readonly property int springTransition: 400 // ms, layout/size changes + readonly property int snapTransition: 120 // ms, instant-feel snaps + + // ── Hazard Matrix — the accent color system ─────────────────────── + readonly property color accentCognitive: "#7B2CBF" // Deep Purple — default thinking + readonly property color accentSovereign: "#FFB000" // Amber — ? prefix + readonly property color accentSystem: "#00FF41" // Emerald — # prefix + readonly property color accentWater: "#00B4D8" // Sky Cerulean — water breathing + readonly property color accentForge: "#FF4E00" // Blood Orange — ! prefix + readonly property color accentBreach: "#f43f5e" // Arterial Red — security alert + readonly property color accentInferring: "#00B4D8" // Cerulean — Nemotron active + + // Intel fold accents — the 7 Sibyls + // EYE / Delphic / TRUTH — electric cerulean, truth cuts clean + // PHANTOM / Hellespontine / POWER — creative violet, spectral presence + // BLADE / Cimmerian / WISDOM — hot magenta, wisdom forged in heat + // SAGE / Persian / JUDGMENT — sun amber, the light that reveals + // SHROUD / Erythraean / LIFE — bioluminescent teal, life in the deep + // FORGE / Tiburtine / LOVE — volcanic orange-red, molten creation + // MIRROR / Cumaean / DISCIPLINE — pale lavender, quiet and reflective + readonly property color accentEye: "#00C2FF" + readonly property color accentBlade: "#FF3CAC" + readonly property color accentSage: "#FFB300" + readonly property color accentMirror: "#C8B8FF" + readonly property color accentPhantom: "#B44FE8" + readonly property color accentShroud: "#00FFC8" + readonly property color accentForgeFold:"#FF6B35" + + // Semantic status colors — not fold identity, used for ok/warn/error states + readonly property color accentOk: "#4ade80" // generic success / up / healthy + readonly property color accentWarn: "#fbbf24" // warning / degraded + // accentBreach (#f43f5e) serves as the semantic error/down color + + // Breathing style accents + readonly property color _accentCelestial: "#ffffff" // placeholder, handled by ArOrchestrator prismatic logic + readonly property var styleAccents: ({ + "sun": "#f59e0b", + "moon": "#818cf8", + "celestial": "#ffffff", + "concentrate": "#f59e0b" + }) + + // Discipline accents + readonly property var disciplineAccents: ({ + "shroud": "#818cf8", + "obanai": "#d946ef", + "tengen": "#00B4D8", + "shinobu": "#4ade80" + }) + + // Intel mode accents + readonly property var modeAccents: ({ + "eye": "#4ade80", + "blade": "#00B4D8", + "sage": "#9B1FA0", + "mirror": "#6366f1", + "forge": "#f59e0b", + "phantom": "#f43f5e", + "shroud": "#818cf8", + "dormant": "#00B4D8" + }) + + // ── resolvePrismatic — Prismatic Gradient, the single source of truth ── + // The canonical color resolver for the entire system. + // All components should call this or read ArOrchestrator.prismaticColor. + // + // Priority chain (highest → lowest): + // breach > discipline > modality > obeliskMode > anticipatedStyle + // > breathingStyle > intelMode > cognitive default + function resolvePrismatic(obeliskMode, activeDiscipline, intelMode, breathingStyle, + isInferring, isBreach, activeModality, anticipatedStyle) { + if (isBreach) return accentBreach + if (activeDiscipline === "shroud") return Qt.color(disciplineAccents["shroud"]) + if (activeDiscipline === "obanai") return Qt.color(disciplineAccents["obanai"]) + if (activeDiscipline === "tengen") return Qt.color(disciplineAccents["tengen"]) + if (activeDiscipline === "shinobu") return Qt.color(disciplineAccents["shinobu"]) + if (activeModality === "reflex") return accentBreach + if (activeModality === "sovereign") return accentSovereign + if (activeModality === "architect") return Qt.color("#a78bfa") + if (obeliskMode === "tactical" || obeliskMode === "applet") return accentCognitive + if (obeliskMode === "forge") return Qt.color(styleAccents["moon"]) + if (obeliskMode === "system") return accentSystem + if (obeliskMode === "sovereign") return accentSovereign + if (obeliskMode === "war") return accentBreach + if (anticipatedStyle && anticipatedStyle !== "") + return Qt.color(styleAccents[anticipatedStyle] ?? accentWater) + if (obeliskMode === "breathing" || (breathingStyle && breathingStyle !== "")) + return Qt.color(styleAccents[breathingStyle] ?? accentWater) + if (intelMode && intelMode !== "dormant") + return Qt.color(modeAccents[intelMode] ?? accentWater) + if (isInferring) return accentInferring + return accentCognitive + } + + // ── resolveAccent — delegates to resolvePrismatic (backward compat) ── + function resolveAccent(obeliskMode, activeDiscipline, intelMode, breathingStyle, isInferring, isBreach) { + return resolvePrismatic(obeliskMode, activeDiscipline, intelMode, breathingStyle, + isInferring, isBreach, "", "") + } + + // ── resolveHazardColor — Speculum hazard color priority system ─────── + // Priority: discipline > obelisk mode > breathing style > legacy threat + // Matches SpeculumCore.qml hazard color logic exactly for consistency + function resolveHazardColor(activeDiscipline, obeliskMode, breathingStyle, breachAlert, combatMode, isExecuting, armedMode) { + // Discipline overrides (highest priority) + if (activeDiscipline === "shroud") return Qt.color(accentShroud) + if (activeDiscipline === "obanai") return Qt.color(disciplineAccents["obanai"] ?? accentWater) + if (activeDiscipline === "tengen") return Qt.color(disciplineAccents["tengen"] ?? accentWater) + if (activeDiscipline === "shinobu") return Qt.color(disciplineAccents["shinobu"] ?? accentWater) + + // Obelisk mode override — live preview while typing + if (obeliskMode === "sovereign") return accentSovereign + if (obeliskMode === "system") return accentSystem + if (obeliskMode === "forge") return accentMirror + if (obeliskMode === "tactical") return accentSage + + // Trinity styles + if (breathingStyle === "sun") return accentSovereign + if (breathingStyle === "moon") return Qt.color(styleAccents["moon"]) + if (breathingStyle === "celestial") return _accentCelestial + + // Legacy threat-based colors (fallback) + if (breachAlert || combatMode) return accentBreach + if (isExecuting || armedMode) return accentForge + return accentWater // default cyan + } + + // ── resolveNodeColor — single source of truth for node colors ──── + // Used by SpeculumSigils, SanctumNodeSystem hover overlay, Tier2Arc, EnvironmentRing + // tier: 1=full, 2=65% sat, 3=30% sat + readonly property var _nodeBaseColors: ({ + // Existing surface types + "target": "#f43f5e", + "vuln": "#f59e0b", + "recon": "#00B4D8", + "history": "#818cf8", + "local": "#4ade80", + "suggested": "#9B1FA0", + "curated": "#FFB000", + "system": "#6366f1", + "wifi": "#00B4D8", + "lan": "#4ade80", + "egress": "#FF4E00", + "service": "#86efac", + "bluetooth": "#a78bfa", + // Dark web types (new) + "darkweb": "#8B0000", // deep crimson — Rui's blood + "onion": "#8B0000", // .onion nodes + "market": "#6B0020", // blood maroon — dark marketplaces + "forum": "#7B1020", // dark rose — dark forums + "circuit": "#9B1FA0", // onion purple — Tor circuits + "tor": "#9B1FA0", // Tor nodes + "silk": "#6B0020", // Silk Road / Rui spider + // IPFS / distributed types (new) + "ipfs": "#6366f1", // sage violet — Wisteria's fold + "wisteria": "#6366f1", // Wisteria vault nodes + "p2p": "#a78bfa", // muted lavender — peer mesh + "distributed": "#a78bfa" // distributed network nodes + }) + + function resolveNodeColor(category, source, status, tier) { + // Derive effective category + var cat = category || "" + if (!cat) { + if (source === "curated") cat = "curated" + else if (source === "suggested") cat = "suggested" + else if (source === "wifi") cat = "wifi" + else if (source === "lan") cat = "lan" + else if (source === "egress") cat = "egress" + else if (source === "local") cat = "local" + // Dark web sources + else if (source === "darkweb") cat = "darkweb" + else if (source === "onion") cat = "onion" + else if (source === "silk") cat = "silk" + else if (source === "tor") cat = "tor" + // IPFS / distributed sources + else if (source === "ipfs") cat = "ipfs" + else if (source === "wisteria") cat = "wisteria" + else if (source === "p2p") cat = "p2p" + else { + if (status === "vuln" || status === "compromised") cat = "vuln" + else cat = "recon" + } + } + var hex = _nodeBaseColors[cat] || "#00B4D8" + var c = Qt.color(hex) + + // Tier brightness/saturation scaling + var t = tier || 1 + if (t === 2) { + var grey2 = (c.r + c.g + c.b) / 3 + return Qt.rgba(c.r * 0.65 + grey2 * 0.35, + c.g * 0.65 + grey2 * 0.35, + c.b * 0.65 + grey2 * 0.35, 1.0) + } + if (t === 3) { + var grey3 = (c.r + c.g + c.b) / 3 + return Qt.rgba(c.r * 0.30 + grey3 * 0.70, + c.g * 0.30 + grey3 * 0.70, + c.b * 0.30 + grey3 * 0.70, 1.0) + } + return c + } + + // Node category color constants (full saturation, Tier 1) + readonly property color nodeColorTarget: Qt.color("#f43f5e") + readonly property color nodeColorVuln: Qt.color("#f59e0b") + readonly property color nodeColorRecon: Qt.color("#00B4D8") + readonly property color nodeColorHistory: Qt.color("#818cf8") + readonly property color nodeColorLocal: Qt.color("#4ade80") + readonly property color nodeColorSuggested: Qt.color("#9B1FA0") + readonly property color nodeColorCurated: Qt.color("#FFB000") + readonly property color nodeColorSystem: Qt.color("#6366f1") + readonly property color nodeColorWifi: Qt.color("#00B4D8") + readonly property color nodeColorLan: Qt.color("#4ade80") + readonly property color nodeColorEgress: Qt.color("#FF4E00") + // Dark web node colors (new) + readonly property color nodeColorDarkweb: Qt.color("#8B0000") // deep crimson — Rui's blood + readonly property color nodeColorOnion: Qt.color("#8B0000") // .onion nodes + readonly property color nodeColorMarket: Qt.color("#6B0020") // blood maroon — dark markets + readonly property color nodeColorTor: Qt.color("#9B1FA0") // onion purple — Tor circuits + // IPFS / distributed node colors (new) + readonly property color nodeColorIpfs: Qt.color("#6366f1") // sage violet — Wisteria's fold + readonly property color nodeColorWisteria: Qt.color("#6366f1") // Wisteria vault + readonly property color nodeColorP2p: Qt.color("#a78bfa") // muted lavender — peer mesh + + // ── glassShadow — shadow-only depth helper ──────────────────────── + // Returns a color for MultiEffect shadowColor at the right intensity. + // Use with shadowEnabled: true, blurEnabled: false. + // intensity: 0.0–1.0, defaults to shadowAlpha + function glassShadow(accentColor, intensity) { + if (!accentColor || accentColor === "") return Qt.rgba(0, 0, 0, 0) + var c = Qt.color(accentColor) + var a = (intensity !== undefined) ? intensity : shadowAlpha + return Qt.rgba(c.r, c.g, c.b, a) + } + + // ── bloomColor — neon glow helper ───────────────────────────────── + function bloomColor(accent, intensity) { + if (!accent || accent === "") return Qt.rgba(0, 0, 0, 0) + var c = Qt.color(accent) + var a = intensity !== undefined ? intensity : 0.45 + return Qt.rgba(c.r, c.g, c.b, a) + } + + // ── Surface helpers ─────────────────────────────────────────────── + readonly property color shimmerTop: Qt.rgba(1, 1, 1, 0.055) + readonly property color shimmerLeft: Qt.rgba(1, 1, 1, 0.035) + readonly property color divider: Qt.rgba(1, 1, 1, 0.07) + readonly property color textPrimary: Qt.rgba(0.90, 0.95, 1.00, 0.88) + readonly property color textSecondary: Qt.rgba(0.80, 0.88, 0.95, 0.45) + readonly property color textDim: Qt.rgba(0.75, 0.85, 0.92, 0.22) + readonly property color textGhost: Qt.rgba(0.75, 0.85, 0.92, 0.12) + + // ── Ghost glow base text ────────────────────────────────────────── + // Near-white with faintest cerulean luminance — alive without competing. + readonly property color ghostText: Qt.rgba(0.90, 0.95, 1.00, 0.82) + readonly property color ghostTextDim: Qt.rgba(0.80, 0.88, 0.95, 0.45) + readonly property color ghostTextFaint: Qt.rgba(0.75, 0.85, 0.92, 0.22) + + // Lexical color tokens ────────────────────────────────────────── + // Single source of truth — all components use these for consistent + // semantic highlighting across obelisk, terminal, conduit, reticle. + readonly property var lexColors: ({ + // Vulnerability / CVE + "cve": "#F59E0B", // amber + "cveCritical":"#F43F5E", // arterial red — CVSS 9+ + "cveHigh": "#FB923C", // orange — CVSS 7–9 + // Network + "ip": "#C8E1DC", // ghost teal-white + "ipLocked": "#00B4D8", // cerulean — locked/active target + "mac": "#67E8F9", // sky cyan — MAC addresses + "port": "#67E8F9", // sky cyan + "hostname": "#A78BFA", // lavender — hostnames distinct from URLs + "url": "#C4B5FD", // lighter lavender — full URLs + // HTTP status families + "http2xx": "#86EFAC", // green + "http3xx": "#FCD34D", // yellow + "http4xx": "#FCA5A5", // rose + "http5xx": "#F87171", // red + // Tool sigils + "sigilPlus": "#86EFAC", // [+] green + "sigilMinus": "#F87171", // [-] red + "sigilWarn": "#FCD34D", // [!] yellow + "sigilInfo": "#67E8F9", // [*] cyan + "sigilMisc": "#C4B5FD", // [~] violet + "sigilINF": "#67E8F9", + "sigilWRN": "#FCD34D", + "sigilERR": "#F87171", + "sigilCRIT": "#F43F5E", + // Severity levels + "sevCritical":"#F43F5E", // CRITICAL — arterial red + "sevHigh": "#FB923C", // HIGH — orange + "sevMedium": "#FCD34D", // MEDIUM — yellow + "sevLow": "#86EFAC", // LOW — ghost green + "sevInfo": "#67E8F9", // INFO — cyan + // Crypto + "hash": "#A78BFA", // dim purple + "memAddr": "#9B1FA0", // blood violet — 0x hex addresses + // JSON + "jsonKey": "#67E8F9", // cerulean — "key": patterns + // Filesystem + "path": "#D7CDE1", // ghost lavender italic + // Timestamps + "timestamp": "#ffffff30", // ghost dim — [HH:MM:SS] + // Outcome keywords + "success": "#BED7C3", // ghost green-white + "error": "#8B0000", // arterial red + // Structural + "bracket": "#ffffff20", // ghost dim white (bracket chars) + "bracketInner": "#ffffff45", + // Sigil chars + "sigil": "#6B0020", // blood maroon (◈ ⟡ ∿) + // Numbers + "number": "#C8C3D7", // ghost grey-white + // Prompt + "prompt": "#6B0020" // ∿ prompt glyph + }) + + // ── Theme change signal — hook for future runtime theme switching ── + signal themeChanged(string themeName) + // Design contract: when daemonState === "dead", all prediction + // consumers MUST fall back to prefix/fuzzy matching only. + // No Nemotron calls, no usage weighting, no semantic ranking. + // + // Canonical offline pill sets — keyed by query context. + // Obelisk._staticFallbackPills() delegates here. + // Any component needing offline suggestions calls this function. + // + // Tiers (in priority order when daemon is dead): + // 1. Prefix match against availableMacros (< 1ms, always available) + // 2. Context-aware static pills from this function + // 3. Generic status/help fallback + function offlinePills(query, detectedIp) { + var q = (query || "").toLowerCase() + var ip = detectedIp || "" + if (q.includes("scan") || q.includes("recon")) + return [{ label: "DEEP SCAN", cmd: "/recon " + ip }, + { label: "NUCLEI", cmd: "/strike:nuclei " + ip }] + if (q.includes("exploit") || q.includes("strike")) + return [{ label: "SYNDICATE", cmd: "/strike:syndicate " + ip }, + { label: "MONITOR", cmd: "/strike:monitor " + ip }] + if (q.includes("shroud") || q.includes("vpn")) + return [{ label: "SHROUD ON", cmd: "/shroud toggle" }, + { label: "THE SHROUD", cmd: "/intel:shroud" }] + if (q.includes("forge") || q.includes("playbook")) + return [{ label: "FORGE", cmd: "/forge:playbook" }, + { label: "TRAINING", cmd: "/forge:forge" }] + if (q.startsWith("@")) + return [{ label: "CONDUIT", cmd: "@conduit" }, + { label: "HUB", cmd: "@hub" }] + return [{ label: "STATUS", cmd: "/status" }, + { label: "HELP", cmd: "/help" }] + } + // Returns HTML-rich text. Call from any component that renders + // terminal / command output. Strips ANSI before processing. + function lexHighlight(raw, lockedTarget) { + var s = _stripAnsi(raw) + s = s.replace(/&/g, "&").replace(//g, ">") + + // Timestamps [HH:MM:SS] or [YYYY-MM-DD HH:MM:SS] + s = s.replace(/\[(\d{2}:\d{2}:\d{2}(?:\.\d+)?)\]/g, + '[$1]') + s = s.replace(/\[(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}[^\]]*)\]/g, + '[$1]') + + // CVE IDs — severity-weighted by CVSS if present + s = s.replace(/\b(CVE-\d{4}-\d{4,7})\b/g, + '$1') + + // Severity levels — graduated color + s = s.replace(/\b(CRITICAL)\b/g, + '$1') + s = s.replace(/\b(HIGH)\b/g, + '$1') + s = s.replace(/\b(MEDIUM)\b/g, + '$1') + s = s.replace(/\b(LOW)\b/g, + '$1') + s = s.replace(/\b(INFO)\b/g, + '$1') + + // Hex memory addresses — 0x prefix + s = s.replace(/\b(0x[0-9a-fA-F]{4,16})\b/g, + '$1') + + // Hashes (32–64 hex chars) — before URLs to avoid double-match + s = s.replace(/\b([0-9a-fA-F]{32,64})\b/g, + '$1') + + // URLs / domains (before IP to avoid double-match) + s = s.replace(/(https?:\/\/[^\s<>"]+)/g, + '$1') + + // Hostnames — word.word or word.word.tld (not full URLs, not IPs) + s = s.replace(/\b([a-zA-Z][a-zA-Z0-9\-]*\.[a-zA-Z]{2,}(?:\.[a-zA-Z]{2,})?)\b/g, + function(m) { + // Skip if already inside a URL tag + return '' + m + '' + }) + + // MAC addresses + s = s.replace(/\b([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})\b/g, + '$1') + + // IPs — locked target gets cerulean, others get ghost teal + s = s.replace(/\b(\d{1,3}(?:\.\d{1,3}){3}(?::\d+)?)\b/g, + function(m) { + var col = (lockedTarget && m.startsWith(lockedTarget)) + ? lexColors["ipLocked"] + : lexColors["ip"] + return '' + m + '' + }) + + // Port/protocol combos + s = s.replace(/\b(\d{1,5})\/(tcp|udp|open|closed|filtered)\b/gi, + '$1/$2') + + // HTTP status codes + s = s.replace(/\b(200|201|204)\b/g, + '$1') + s = s.replace(/\b(301|302|307|308)\b/g, + '$1') + s = s.replace(/\b(400|401|403|404|405|429)\b/g, + '$1') + s = s.replace(/\b(500|502|503|504)\b/g, + '$1') + + // JSON keys — "key": pattern + s = s.replace(/"([a-zA-Z_][a-zA-Z0-9_]*)"\s*:/g, + '"$1":') + + // Tool sigils + s = s.replace(/\[(\+)\]/g, + '[+]') + s = s.replace(/\[(-)\]/g, + '[-]') + s = s.replace(/\[(!)\]/g, + '[!]') + s = s.replace(/\[(\*)\]/g, + '[*]') + s = s.replace(/\[(~)\]/g, + '[~]') + s = s.replace(/\[(INF)\]/g, + '[INF]') + s = s.replace(/\[(WRN)\]/g, + '[WRN]') + s = s.replace(/\[(ERR)\]/g, + '[ERR]') + s = s.replace(/\[(CRITICAL)\]/g, + '[CRITICAL]') + + // Filesystem paths + s = s.replace(/((?:\/|~)[a-zA-Z0-9_.~\-]+(?:\/[a-zA-Z0-9_.~\-]+)*(?::\d+)*)/g, + '$1') + + // Success keywords + s = s.replace(/\b(SUCCESS|OK|ACTIVE|STABLE|DONE|UP|PASS|FOUND|OPEN)\b/g, + '$1') + + // Error keywords + s = s.replace(/\b(ERROR|FAIL|DENIED|FATAL|DOWN|WARNING|WARN|KILLED|CLOSED|FILTERED)\b/gi, + '$1') + + // Brackets + s = s.replace(/\[([^\]]*)\]/g, + '[' + + '$1' + + ']') + + // Sigil chars + s = s.replace(/(◈|⟡|∿)/g, + '$1') + + // Numbers (not already colored) + s = s.replace(/(?$1') + + return s + } + + // ── colorToHue — extract HSV hue (0.0–1.0) from a color ────────── + // Used by ShiguruiSanctum shader uniforms which expect normalized hue. + function colorToHue(c) { + var col = Qt.color(c) + var r = col.r, g = col.g, b = col.b + var mx = Math.max(r, g, b), mn = Math.min(r, g, b), d = mx - mn + if (d < 0.001) return 0.0 + var h + if (mx === r) h = ((g - b) / d + (g < b ? 6 : 0)) + else if (mx === g) h = ((b - r) / d + 2) + else h = ((r - g) / d + 4) + return h / 6.0 + } + + // Precomputed fold hues for sanctum shader uniforms + readonly property real hueEye: colorToHue(accentEye) + readonly property real hueBlade: colorToHue(accentBlade) + readonly property real hueSage: colorToHue(accentSage) + readonly property real hueMirror: colorToHue(accentMirror) + readonly property real hueForge: colorToHue(accentForgeFold) + readonly property real huePhantom: colorToHue(accentPhantom) + readonly property real hueShroud: colorToHue(accentShroud) + readonly property real hueWater: colorToHue(accentWater) + readonly property real hueSovereign: colorToHue(accentSovereign) + function _stripAnsi(s) { + return s.replace(/\x1b\[[0-9;]*[mGKHFABCDJsu]/g, "") + .replace(/\x1b\][^\x07]*\x07/g, "") + .replace(/\x1b[()][AB012]/g, "") + .replace(/\r/g, "") + } +} diff --git a/lockscreens/kitsune/content/tw/qmldir b/lockscreens/kitsune/content/tw/qmldir new file mode 100644 index 00000000..7b2a211a --- /dev/null +++ b/lockscreens/kitsune/content/tw/qmldir @@ -0,0 +1,4 @@ +module tw + +singleton TransparentWorld 1.0 TransparentWorld.qml +singleton ArOrchestrator 1.0 ArOrchestrator.qml diff --git a/lockscreens/kitsune/manifest.json b/lockscreens/kitsune/manifest.json new file mode 100644 index 00000000..2762bd09 --- /dev/null +++ b/lockscreens/kitsune/manifest.json @@ -0,0 +1,201 @@ +{ + "schema": 1, + "id": "kitsune", + "category": "lockscreens", + "version": "1.0.0", + "destination": "qylock/themes/kitsune", + "files": [ + { + "source": "LICENSE", + "destination": "LICENSE", + "mode": "0644", + "size": 35149, + "sha256": "3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986", + "install": true + }, + { + "source": "PROVENANCE.txt", + "destination": "PROVENANCE.txt", + "mode": "0644", + "size": 2205, + "sha256": "178c6efe4b0aaf5a126ecc4978ce93b3ad83bfe93b6928fc01fde2ca66ba73f9", + "install": true + }, + { + "source": "assets/preview.webp", + "destination": "preview.webp", + "mode": "0644", + "size": 18666, + "sha256": "e37eb300f43d44a2728dde3dd82de0906dd34d83dbc07c6b9888ba64703ec53a", + "install": false + }, + { + "source": "content/Main.qml", + "destination": "Main.qml", + "mode": "0644", + "size": 16243, + "sha256": "ad41b7d8980a858baca06d7bbb2c374af856fa9520a099402322d2e94cd79eea", + "install": true + }, + { + "source": "content/gotchi/InseiCompanion.qml", + "destination": "gotchi/InseiCompanion.qml", + "mode": "0644", + "size": 5882, + "sha256": "8f7f9ff416fa9cd76d63076dc38ae1d50b913830721747cf0b3a4590e8d19614", + "install": true + }, + { + "source": "content/gotchi/InseiGotchi.qml", + "destination": "gotchi/InseiGotchi.qml", + "mode": "0644", + "size": 26998, + "sha256": "98855df11e2a479aa1345cc888fc1ba2bce24f0d570e13bc32d70453d12cc22a", + "install": true + }, + { + "source": "content/gotchi/InseiHub.qml", + "destination": "gotchi/InseiHub.qml", + "mode": "0644", + "size": 9221, + "sha256": "811586479299669437d2999d1a605fc78fc5dac12c601452e381ca87fecd91f7", + "install": true + }, + { + "source": "content/gotchi/InseiHud.qml", + "destination": "gotchi/InseiHud.qml", + "mode": "0644", + "size": 5746, + "sha256": "67b98c9f1b5f0b154df0c7df9f786d1aa44a1f5ced2f952a331361b02fa23038", + "install": true + }, + { + "source": "content/gotchi/InseiLiveHash.qml", + "destination": "gotchi/InseiLiveHash.qml", + "mode": "0644", + "size": 9014, + "sha256": "962bf0ddaf8880e11cbce3982dfbf55866063dfc75a88f6953be51c803d1c364", + "install": true + }, + { + "source": "content/gotchi/InseiScene.qml", + "destination": "gotchi/InseiScene.qml", + "mode": "0644", + "size": 14032, + "sha256": "cc72e01e3b219ca07521c4c8da6b506d5d0f06575c2da8e8758fc55845963f50", + "install": true + }, + { + "source": "content/gotchi/InseiTarot.qml", + "destination": "gotchi/InseiTarot.qml", + "mode": "0644", + "size": 14520, + "sha256": "55f5f4a19e0c382648c90e08286f963156df6215a7a3b43362b59c9f4779bc05", + "install": true + }, + { + "source": "content/gotchi/README.md", + "destination": "gotchi/README.md", + "mode": "0644", + "size": 5456, + "sha256": "0bafb4b4cdb957d55ff476416b082509d23f6e72f41a22bd54b2ec3159d4e7c3", + "install": true + }, + { + "source": "content/gotchi/qmldir", + "destination": "gotchi/qmldir", + "mode": "0644", + "size": 251, + "sha256": "5af186846a5b0ddbf828ea980b48b2c656488cb298bed6f1d836e1bb16776052", + "install": true + }, + { + "source": "content/metadata.desktop", + "destination": "metadata.desktop", + "mode": "0644", + "size": 209, + "sha256": "06afcc0e6368068f6e8a2cd246b374552be680f0468080c0e72b67335f6f4fde", + "install": true + }, + { + "source": "content/shaders/InseiKitsune.frag", + "destination": "shaders/InseiKitsune.frag", + "mode": "0644", + "size": 8253, + "sha256": "ffae335bd97115a8b96794a65ccc2913833b4313f10cd6e912a8d8f0c3905c76", + "install": true + }, + { + "source": "content/shaders/InseiKitsune.frag.qsb", + "destination": "shaders/InseiKitsune.frag.qsb", + "mode": "0644", + "size": 12381, + "sha256": "d43bd280aad1f0a2ea97f80162d393bfc2d5708303927b02ab8ede35529115e4", + "install": true + }, + { + "source": "content/shaders/InseiSoul.frag", + "destination": "shaders/InseiSoul.frag", + "mode": "0644", + "size": 13158, + "sha256": "d37476af799b22efb6abf19fee92dcae61fa4bb8f9a779ac0e108f10df79888e", + "install": true + }, + { + "source": "content/shaders/InseiSoul.frag.qsb", + "destination": "shaders/InseiSoul.frag.qsb", + "mode": "0644", + "size": 17847, + "sha256": "065c28ac92e359368d1fc49a9daff5190a6ed486189f38f0a52f26eae8ad2ca6", + "install": true + }, + { + "source": "content/shaders/crystallize.frag", + "destination": "shaders/crystallize.frag", + "mode": "0644", + "size": 13837, + "sha256": "dfcabc64b4f45b5e11065acd8a87a3eaae6557c056c139b4ada50e110c949c9b", + "install": true + }, + { + "source": "content/shaders/crystallize.frag.qsb", + "destination": "shaders/crystallize.frag.qsb", + "mode": "0644", + "size": 13658, + "sha256": "7ead89666598790c2220a0394fc278a174f10356cb5d6baff7e781d71ee97f39", + "install": true + }, + { + "source": "content/theme.conf", + "destination": "theme.conf", + "mode": "0644", + "size": 343, + "sha256": "e35fc5c9904b1a44552f20eeb0d0750eea3c2369eb39f8fbfc69df23a95069c0", + "install": true + }, + { + "source": "content/tw/ArOrchestrator.qml", + "destination": "tw/ArOrchestrator.qml", + "mode": "0644", + "size": 1159, + "sha256": "64c527c73d40b7f8a9c8e9cda482b37d704bad0b96022dba80caf219d233ee56", + "install": true + }, + { + "source": "content/tw/TransparentWorld.qml", + "destination": "tw/TransparentWorld.qml", + "mode": "0644", + "size": 31709, + "sha256": "f7cb77f4755bb7d0897deef5ff4dee72e93780cfdec0e76fc49f5213292ca259", + "install": true + }, + { + "source": "content/tw/qmldir", + "destination": "tw/qmldir", + "mode": "0644", + "size": 111, + "sha256": "74454dbad4d61adf208f321fa8a32e9e2e7d06b9b6e80c7769b347c780c1a8f1", + "install": true + } + ] +} diff --git a/lockscreens/registry.json b/lockscreens/registry.json index 202f41c9..a997c299 100644 --- a/lockscreens/registry.json +++ b/lockscreens/registry.json @@ -1,941 +1,965 @@ { - "schema": 1, - "lockscreens": [ - { - "id": "clockwork-tape", - "name": "Tape", - "version": "1.0.1", - "path": "lockscreens/clockwork-tape", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "Clockwork mechanics drift across warm magnetic tape.", - "description": "A complete qylock scene with a tactile clock face, deep shadow, and an amber tape-era palette.", - "tags": [ - "clockwork", - "retro", - "warm" - ], - "accent": "#d7a45f", - "surface": "#101010", - "preview": "assets/preview.webp", - "screenshots": [], - "manifest": "manifest.json", - "manifestSha256": "11a60cd3d278410f5190ccb0f2c32aea4c8a25e52a0b734b87f4434dd0bcdec8" - }, - { - "id": "dog-samurai", - "name": "Dog Samurai", - "version": "1.0.1", - "path": "lockscreens/dog-samurai", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A painterly samurai hound under drifting red petals.", - "description": "A cinematic video lock: a straw-hatted samurai dog with a sheathed katana as crimson petals fall, paired with a bold clock and a minimal login.", - "tags": [ - "samurai", - "painterly", - "cinematic" - ], - "accent": "#945a46", - "surface": "#141612", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "ba957bc1ae884d5df67ab39ba9ab21850af5bfc9faec50014c06a4aefe571470" - }, - { - "id": "enfield", - "name": "Enfield", - "version": "1.0.1", - "path": "lockscreens/enfield", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "Golden hour spilling through a plant-framed window.", - "description": "A warm, atmospheric lock: late sun pours past ivy and a weathered window frame, with a quiet login.", - "tags": [ - "cozy", - "warm", - "scenic" - ], - "accent": "#111418", - "surface": "#111418", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "42d2cd29b6cdd2d356ff2c25217be87268b68958a260a7e3d5a670fc717d1287" - }, - { - "id": "field", - "name": "Field", - "version": "1.0.1", - "path": "lockscreens/field", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "An open pasture under soft light with a feather-light clock.", - "description": "A serene qylock scene: a wide field, a thin Orbitron clock, and a quiet amber login tucked into the corner.", - "tags": [ - "scenic", - "minimal", - "calm" - ], - "accent": "#3c6678", - "surface": "#19180f", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif", - "assets/shot-1.png" - ], - "manifest": "manifest.json", - "manifestSha256": "05b8731d4e438f9ba1246ecada52d87f01bb3efca5ff24063db2bba91a1e1bad" - }, - { - "id": "forest", - "name": "Forest", - "version": "1.0.1", - "path": "lockscreens/forest", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "Sunbeams pouring through a misty autumn canopy.", - "description": "A tranquil video lock: light rays drift over an aerial autumn forest wrapped in fog, with a soft clock and an unobtrusive login.", - "tags": [ - "scenic", - "nature", - "calm" - ], - "accent": "#051612", - "surface": "#051612", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "d57a82bfb8b3b27df93e93e9ae0b90bcffebb1eebb5be83705dbe7a14ddc8b03" - }, - { - "id": "genshin", - "name": "Genshin Impact", - "version": "1.0.0", - "path": "lockscreens/genshin", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A moonlit abyss of drifting pillars in deep blue.", - "description": "A Genshin Impact inspired lock: towering ruins beneath a pale moon, slow-drifting clouds, and a clean login on the descending path.", - "tags": [ - "game", - "anime", - "night" - ], - "accent": "#192e6d", - "surface": "#07101f", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "370fd6d19c8b0b8c111c5fb77b3e1f7533644a3272c9415d176438b004c41caf" - }, - { - "id": "girl-coffee", - "name": "Coffee Break", - "version": "1.0.1", - "path": "lockscreens/girl-coffee", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A cosy hand-drawn morning with a warm mug in hand.", - "description": "A lo-fi illustrated character taking a coffee break, paired with the rounded Itim typeface and a soft, unobtrusive login.", - "tags": [ - "anime", - "cozy", - "illustrated" - ], - "accent": "#8eb9b6", - "surface": "#695b76", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif", - "assets/shot-1.png" - ], - "manifest": "manifest.json", - "manifestSha256": "f55205b272b11cccf29bc9840f18bfdd81268d72ea1bcfca6d78e2ed6b925660" - }, - { - "id": "girl-pillow", - "name": "Lazy Sunday", - "version": "1.0.1", - "path": "lockscreens/girl-pillow", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A soft illustrated scene, curled up and half awake.", - "description": "A relaxed lo-fi illustration with the rounded Itim font and a gentle right-aligned login - quiet, warm, and unhurried.", - "tags": [ - "anime", - "cozy", - "illustrated" - ], - "accent": "#737588", - "surface": "#625f6c", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif", - "assets/shot-1.png" - ], - "manifest": "manifest.json", - "manifestSha256": "1b0a2f1f99ad1bf0f0a0135b2e5363212017f27543ba30a3cbce83956abfc402" - }, - { - "id": "last-of-us", - "name": "The Last of Us", - "version": "1.0.1", - "path": "lockscreens/last-of-us", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A lone blossom tree adrift in cold grey mist.", - "description": "A The Last of Us inspired lock: a solitary cherry tree on a rocky outcrop among fog-veiled cliffs, with a spare, moody login.", - "tags": [ - "game", - "scenic", - "moody" - ], - "accent": "#88683b", - "surface": "#131210", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "cb8e5fd420c113f5407144f3d2304b5541978105da25fd05ae91c484651ad242" - }, - { - "id": "man-bicycle", - "name": "Evening Ride", - "version": "1.0.1", - "path": "lockscreens/man-bicycle", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A lone cyclist against a wide illustrated sky.", - "description": "A cinematic lo-fi ride with the Itim typeface and a minimal login tucked into the frame.", - "tags": [ - "scenic", - "illustrated", - "calm" - ], - "accent": "#2e8692", - "surface": "#395551", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif", - "assets/shot-1.png" - ], - "manifest": "manifest.json", - "manifestSha256": "08fe1c4fc9c646904ea57778974025266f3b7d5eccd5e723953956b727549ed8" - }, - { - "id": "material-you", - "name": "Material You", - "version": "1.0.1", - "path": "lockscreens/material-you", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "Google's Material You language, clean and adaptive.", - "description": "A crisp Material You lock with the Google Sans typeface, rounded surfaces, and a tidy centred login.", - "tags": [ - "material", - "clean", - "modern" - ], - "accent": "#1a3c32", - "surface": "#1a3c32", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif", - "assets/shot-1.png" - ], - "manifest": "manifest.json", - "manifestSha256": "c0356b4a2419a42a9f7752551ed97991aca578576486598ba798dc219d0c09fc" - }, - { - "id": "material-you-dark", - "name": "Material You Dark", - "version": "1.0.1", - "path": "lockscreens/material-you-dark", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "The Material You language in a deep, quiet dark palette.", - "description": "The dark sibling of the Material You lock: the Google Sans typeface and rounded surfaces over a low-light palette, with a tidy centred login.", - "tags": [ - "material", - "dark", - "clean" - ], - "accent": "#291f48", - "surface": "#141316", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif", - "assets/shot-1.png" - ], - "manifest": "manifest.json", - "manifestSha256": "d881d8b36b6c7a2236a61d4eec4f5928fa1771bba623e3500c119525b017f710" - }, - { - "id": "minecraft", - "name": "Minecraft Menu", - "version": "1.0.1", - "path": "lockscreens/minecraft", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "The blocky main-menu look, splash text and all.", - "description": "A nostalgic Minecraft title-screen lock: stone-button widgets, a random yellow splash line, and the classic logo.", - "tags": [ - "game", - "retro", - "playful" - ], - "accent": "#251b12", - "surface": "#0f0b08", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif", - "assets/shot-1.png" - ], - "manifest": "manifest.json", - "manifestSha256": "7e2e756ca2b48688892e93b41b9e51678a0013e0d55caff909a9eff5e6993472" - }, - { - "id": "nier-automata", - "name": "NieR: Automata", - "version": "1.0.1", - "path": "lockscreens/nier-automata", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "The stark beige-and-black UI of YoRHa.", - "description": "A faithful NieR: Automata lock with bordered YoRHa buttons, the signature muted palette, and a precise login.", - "tags": [ - "game", - "minimal", - "monochrome" - ], - "accent": "#1a1713", - "surface": "#1a1713", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif", - "assets/shot-1.png" - ], - "manifest": "manifest.json", - "manifestSha256": "08045d52f8dad638f1e6fe16264d0b7d6777345bab42b03086e88450d9dfb56e" - }, - { - "id": "ninja-gaiden", - "name": "Ninja Gaiden", - "version": "1.0.1", - "path": "lockscreens/ninja-gaiden", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "High-contrast action styling with the Tektur face.", - "description": "A sharp Ninja Gaiden lock: bold Tektur type, a dark cinematic background, and a decisive login.", - "tags": [ - "game", - "bold", - "dark" - ], - "accent": "#040201", - "surface": "#020101", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif", - "assets/shot-1.png" - ], - "manifest": "manifest.json", - "manifestSha256": "ffd96cc2705ca8f549d6ab5a39a9b0e688f5e7ce431babd3cbe3a2b17177c7e7" - }, - { - "id": "nothing", - "name": "Nothing OS", - "version": "1.0.1", - "path": "lockscreens/nothing", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "Dot-matrix minimalism in red and black.", - "description": "A Nothing-inspired lock: NDot dot-matrix numerals, twin rounded widgets, and a bright, airy backdrop.", - "tags": [ - "minimal", - "monochrome", - "modern" - ], - "accent": "#b71c23", - "surface": "#0e0e0e", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "c59741f9666945fa068283813b76705b21280d2f5d74c302f79731653fcfb7c0" - }, - { - "id": "osu", - "name": "osu!", - "version": "1.0.0", - "path": "lockscreens/osu", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "The osu! rhythm game, tap-circles and all.", - "description": "An osu! inspired lock with difficulty menus, neon tap-circles, and a playful, score-driven login.", - "tags": [ - "game", - "rhythm", - "playful" - ], - "accent": "#84fa04", - "surface": "#060504", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "02d6a89c8240e86c0f7966fc08e125e98d52201db7e015374271054ce017bbc6" - }, - { - "id": "osumania", - "name": "osu!mania", - "version": "1.0.1", - "path": "lockscreens/osumania", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A four-key note highway glowing in neon green.", - "description": "An osu!mania inspired lock: vertical scrolling notes, key-binding widgets, and PERFECT combos over a dark stage.", - "tags": [ - "game", - "rhythm", - "neon" - ], - "accent": "#050902", - "surface": "#050902", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "58e08d256426ff0722cf5f3a565c3e6ab5544accf46c11635b1e50b599312807" - }, - { - "id": "pixel-coffee", - "name": "Pixel Coffee", - "version": "1.0.1", - "path": "lockscreens/pixel-coffee", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A warm pixel cafe glowing after dark.", - "description": "A cosy pixel-art lock: amber lamps over a late-night cafe, city lights through the window, and a tidy login.", - "tags": [ - "pixel", - "cozy", - "night" - ], - "accent": "#090811", - "surface": "#090811", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "10ac931fe1f74386399c80f94048b835974372fadc81194f97aed3919caef16a" - }, - { - "id": "pixel-cyberpunk", - "name": "Pixel Cyberpunk", - "version": "1.0.1", - "path": "lockscreens/pixel-cyberpunk", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A neon pixel-art cityscape humming with rain and signage.", - "description": "An animated pixel-art cyberpunk street, all neon reflections and drizzle, with a crisp login.", - "tags": [ - "pixel", - "cyberpunk", - "neon" - ], - "accent": "#070e18", - "surface": "#070e18", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "eff36e820cb59fa9c40bb55b77760c66c5c8c688f0ec652997405510e1760c42" - }, - { - "id": "pixel-dusk-city", - "name": "Pixel Dusk City", - "version": "1.0.1", - "path": "lockscreens/pixel-dusk-city", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A red-lit pixel skyline smouldering at dusk.", - "description": "A pixel-art lock: a silhouetted city under a burning dusk sky, framed by dark leaves and tangled wires, with a minimal login.", - "tags": [ - "pixel", - "city", - "dusk" - ], - "accent": "#894a45", - "surface": "#0c0e0f", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "cc9aec4398157c0f6dcce6f4daa97e8affe424167c8072505794cde7e92e8f8e" - }, - { - "id": "pixel-emerald", - "name": "Pixel Emerald", - "version": "1.0.1", - "path": "lockscreens/pixel-emerald", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A seaside cycling route in bright pixel green.", - "description": "A Pokemon inspired pixel lock: a cyclist on a fenced coastal path amid lush fields and creatures, with a cheerful login.", - "tags": [ - "pixel", - "game", - "nostalgic" - ], - "accent": "#78a13c", - "surface": "#3b5649", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "b3c2d5c919994db4d111aa05e76ea715f65f081ba3f7390ad9ac634af2d978c6" - }, - { - "id": "pixel-hollowknight", - "name": "Pixel Hollow Knight", - "version": "1.0.1", - "path": "lockscreens/pixel-hollowknight", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A horned knight silhouetted against embered dark.", - "description": "A Hollow Knight inspired pixel lock: a pale-horned figure above drifting orange embers in the deep, with a stark login.", - "tags": [ - "game", - "pixel", - "dark" - ], - "accent": "#070604", - "surface": "#070604", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "c6efbe7d6137eba34d0ee9e0ae53829ee7d6200088add32ea5d2bcac9b827af6" - }, - { - "id": "pixel-munchlax", - "name": "Pixel Munchlax", - "version": "1.0.1", - "path": "lockscreens/pixel-munchlax", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A little companion on a grassy hill under summer clouds.", - "description": "A bright pixel-art lock: a small creature resting on green grass beneath towering white clouds, with a clean login.", - "tags": [ - "pixel", - "cute", - "calm" - ], - "accent": "#5cacf0", - "surface": "#0e1213", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "805c22b85dd622b8d040e820274d75bdb4b3bdd892813f5663f4e7736ec201fc" - }, - { - "id": "pixel-night-city", - "name": "Pixel Night City", - "version": "1.0.1", - "path": "lockscreens/pixel-night-city", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A glittering pixel skyline humming after dark.", - "description": "A pixel-art lock: dense neon-lit towers over shadowed rooftops, cool and electric, with a crisp login.", - "tags": [ - "pixel", - "city", - "night" - ], - "accent": "#0e0e19", - "surface": "#0e0e19", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "fb0c0c9fcf0e541ca7bdc8f3231fe041113cbcf0b853ff563f7b4d8cccda728a" - }, - { - "id": "pixel-rainyroom", - "name": "Pixel Rainy Room", - "version": "1.0.1", - "path": "lockscreens/pixel-rainyroom", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A rainy night at the desk, monitor aglow.", - "description": "A lo-fi pixel lock: rain on the window and a blue monitor glow across a dim bedroom, with a quiet login.", - "tags": [ - "pixel", - "cozy", - "rain" - ], - "accent": "#02050c", - "surface": "#02050c", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "796f5b8abf82a53166cac4f89162d356938ceea20b64b5db3aaf6dd32fcab268" - }, - { - "id": "pixel-sakura", - "name": "Pixel Sakura", - "version": "1.0.1", - "path": "lockscreens/pixel-sakura", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A pixel-art courtyard under drifting cherry blossom.", - "description": "An animated pixel-art scene of falling sakura petals, warm and unhurried, with a tidy login.", - "tags": [ - "pixel", - "anime", - "calm" - ], - "accent": "#2e3949", - "surface": "#2e3949", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "efcb1260e4e98ef97fddd97024de504e66da6c27401932476276afc1da36e1c7" - }, - { - "id": "pixel-skyscrapers", - "name": "Pixel Skyscrapers", - "version": "1.0.1", - "path": "lockscreens/pixel-skyscrapers", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A pastel dawn washing over a layered skyline.", - "description": "A pixel-art lock: peach-and-mauve skies and a soft sun behind stacked skyscrapers, with a calm login.", - "tags": [ - "pixel", - "city", - "pastel" - ], - "accent": "#020e11", - "surface": "#020e11", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "4e0b5bc1eafb4c62ff0bcc002d8271ad2248d819a308c3a5815652b759bf3985" - }, - { - "id": "pixel-waterfall", - "name": "Pixel Waterfall", - "version": "1.0.1", - "path": "lockscreens/pixel-waterfall", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A tranquil pixel-art waterfall in a mossy glen.", - "description": "An animated pixel-art waterfall scene, cool and green, with a calm minimal login.", - "tags": [ - "pixel", - "nature", - "calm" - ], - "accent": "#106e97", - "surface": "#040e15", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "56892aed6ec940d42f650fa922cf03e791b1dde6a329c898373dbc1e0022d72f" - }, - { - "id": "r1999-1", - "name": "Reverse: 1999", - "version": "1.0.1", - "path": "lockscreens/r1999-1", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A pensive period portrait bathed in soft sepia light.", - "description": "A Reverse: 1999 inspired lock centred on a wistful character portrait in warm vintage tones, with an understated login.", - "tags": [ - "game", - "anime", - "vintage" - ], - "accent": "#422a1f", - "surface": "#121011", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "4475b01a6aff68d111a37facbff83e78410c36fcb666b37712f46ba5df916620" - }, - { - "id": "r1999-2", - "name": "Reverse: 1999", - "version": "1.0.1", - "path": "lockscreens/r1999-2", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "Vintage flourish with an animated period backdrop.", - "description": "A Reverse: 1999 inspired lock with an animated vintage backdrop, a period logo, and an elegant login.", - "tags": [ - "game", - "anime", - "vintage" - ], - "accent": "#88725b", - "surface": "#101011", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "c14cf827cae63e5a7d6aa6ad69bcdadefe14ddd3b63ceba0e2e0a36e0c59b7a8" - }, - { - "id": "star-rail", - "name": "Star Rail", - "version": "1.0.0", - "path": "lockscreens/star-rail", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "An astral, animated backdrop for the trailblazers.", - "description": "A Honkai: Star Rail inspired lock with an animated cosmic backdrop and a clean, modern login.", - "tags": [ - "game", - "anime", - "space" - ], - "accent": "#060b15", - "surface": "#060b15", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "b11248876dbbfca5cf598955185340430f50b8d49bf904420233d2d752cd1311" - }, - { - "id": "sword", - "name": "Sword", - "version": "1.0.1", - "path": "lockscreens/sword", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A lone blade planted in a misty grove.", - "description": "A moody, near-monochrome lock: a katana standing point-down among blurred dark trees, with a spare login.", - "tags": [ - "dark", - "minimal", - "cinematic" - ], - "accent": "#08090f", - "surface": "#08090f", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "2df8248c4f52e49ed6e1ee4b4084771963d0777f03ee364d5acf65c56239eae5" - }, - { - "id": "terraria", - "name": "Terraria", - "version": "1.0.0", - "path": "lockscreens/terraria", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "The Terraria main menu, pixel logo and all.", - "description": "A Terraria title-screen lock with the pixel logo, a tree-lined horizon, and wood-panel buttons.", - "tags": [ - "game", - "pixel", - "retro" - ], - "accent": "#4e66c5", - "surface": "#1a2530", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "6105ce4c65eb446a0206d9ba3f9c9d08fe0b63276be120b44879800ba14b9237" - }, - { - "id": "windows-7", - "name": "Windows 7", - "version": "1.0.0", - "path": "lockscreens/windows-7", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "The nostalgic Windows 7 welcome screen.", - "description": "A throwback Windows 7 lock: a round avatar, a glassy credential field, and the familiar blue welcome mood.", - "tags": [ - "os", - "retro", - "nostalgic" - ], - "accent": "#0755ad", - "surface": "#043979", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif", - "assets/shot-1.png" - ], - "manifest": "manifest.json", - "manifestSha256": "377240f577be55559d6328ffd5952a0053c0fcc3bcf5e87bc327967f66f5f77d" - }, - { - "id": "winter", - "name": "Winter", - "version": "1.0.1", - "path": "lockscreens/winter", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A quiet, animated snowfall in cold blue.", - "description": "A calm winter lock: soft, drifting snow over a cold-blue scene with an understated login.", - "tags": [ - "scenic", - "winter", - "calm" - ], - "accent": "#0c1418", - "surface": "#0c1418", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "81814180afe714ef766c1f83b377fb50990b424ff5006afc49f09fc7148b53b7" - }, - { - "id": "women-umbrella", - "name": "Rainy Walk", - "version": "1.0.1", - "path": "lockscreens/women-umbrella", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "An illustrated figure under an umbrella in gentle rain.", - "description": "A moody lo-fi illustration of a walk in the rain, with a calm, understated login.", - "tags": [ - "illustrated", - "rain", - "calm" - ], - "accent": "#a07f80", - "surface": "#3f3c38", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif", - "assets/shot-1.png" - ], - "manifest": "manifest.json", - "manifestSha256": "65e313d43f563d9adefd93a35270527e1ef63bc041885680d8482a464a83e43d" - }, - { - "id": "world-machine", - "name": "World Machine", - "version": "1.0.2", - "path": "lockscreens/world-machine", - "author": "CratusGod", - "upstream": "https://github.com/CratusGod/Oneshot-themed-Ryoku", - "summary": "Niko's lightbulb over a OneShot sunset, with music.", - "description": "A OneShot lock: Avolicis' pixel-art sunset animation (used with permission) with its soundtrack, Niko's lightbulb as your avatar, THE WORLD MACHINE wordmark, Terminus type and a Num Lock indicator. The matching shell reload cover and boot screen live at https://github.com/CratusGod/Oneshot-themed-Ryoku.", - "tags": [ - "game", - "pixel", - "animated", - "music" - ], - "accent": "#FFD34A", - "surface": "#0B0A12", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "9b8f74183de4546b61c8b3dcf0726757494ba39bc00bb42b49a197cbfc0f7fb9", - "official": false, - "lastUpdated": "2026-10-21" - }, - { - "id": "wuwa", - "name": "Wuthering Waves", - "version": "1.0.0", - "path": "lockscreens/wuwa", - "author": "Darkkal44", - "upstream": "https://github.com/Darkkal44/qylock", - "summary": "A lone figure amid drifting ruins under pale light.", - "description": "A Wuthering Waves inspired lock: a desolate greyscale expanse of floating debris cut by a single beam of light, with a modern login.", - "tags": [ - "game", - "anime", - "monochrome" - ], - "accent": "#0e1018", - "surface": "#0e1018", - "preview": "assets/preview.gif", - "screenshots": [ - "assets/preview.gif" - ], - "manifest": "manifest.json", - "manifestSha256": "cc3b45853c1f123626adf24d89beb60e3d332ec59e45b82f25ad6f93dbbb8cfe" - }, - { - "id": "nothing-interface", - "name": "Nothing Interface", - "version": "0.1.0", - "path": "lockscreens/nothing-interface", - "author": "Ivan <254410104+venchik111@users.noreply.github.com>", - "upstream": "https://github.com/venchik111/ryoku-nothing-interface", - "summary": "Dot clock and live Ryoku status over your current wallpaper.", - "description": "A Nothing OS-inspired lockscreen with a dot clock, battery, daily screen time and MPRIS media controls. Uses the current Ryogami wallpaper during a locked session; no wallpaper is bundled.", - "tags": [ - "nothing", - "monochrome", - "live", - "wallpaper" - ], - "accent": "#C2C4CB", - "surface": "#1C1D21", - "preview": "assets/preview.png", - "screenshots": [ - "assets/preview.png" - ], - "manifest": "manifest.json", - "manifestSha256": "158f1f5ad37b51c589f511366eb23724bf10261c9c0d96f9782ae8640ca772d4", - "official": false, - "license": "MIT", - "lastUpdated": "2026-10-01" - } - ] -} + "schema": 1, + "lockscreens": [ + { + "id": "clockwork-tape", + "name": "Tape", + "version": "1.0.1", + "path": "lockscreens/clockwork-tape", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "Clockwork mechanics drift across warm magnetic tape.", + "description": "A complete qylock scene with a tactile clock face, deep shadow, and an amber tape-era palette.", + "tags": [ + "clockwork", + "retro", + "warm" + ], + "accent": "#d7a45f", + "surface": "#101010", + "preview": "assets/preview.webp", + "screenshots": [], + "manifest": "manifest.json", + "manifestSha256": "11a60cd3d278410f5190ccb0f2c32aea4c8a25e52a0b734b87f4434dd0bcdec8" + }, + { + "id": "dog-samurai", + "name": "Dog Samurai", + "version": "1.0.1", + "path": "lockscreens/dog-samurai", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A painterly samurai hound under drifting red petals.", + "description": "A cinematic video lock: a straw-hatted samurai dog with a sheathed katana as crimson petals fall, paired with a bold clock and a minimal login.", + "tags": [ + "samurai", + "painterly", + "cinematic" + ], + "accent": "#945a46", + "surface": "#141612", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "ba957bc1ae884d5df67ab39ba9ab21850af5bfc9faec50014c06a4aefe571470" + }, + { + "id": "enfield", + "name": "Enfield", + "version": "1.0.1", + "path": "lockscreens/enfield", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "Golden hour spilling through a plant-framed window.", + "description": "A warm, atmospheric lock: late sun pours past ivy and a weathered window frame, with a quiet login.", + "tags": [ + "cozy", + "warm", + "scenic" + ], + "accent": "#111418", + "surface": "#111418", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "42d2cd29b6cdd2d356ff2c25217be87268b68958a260a7e3d5a670fc717d1287" + }, + { + "id": "field", + "name": "Field", + "version": "1.0.1", + "path": "lockscreens/field", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "An open pasture under soft light with a feather-light clock.", + "description": "A serene qylock scene: a wide field, a thin Orbitron clock, and a quiet amber login tucked into the corner.", + "tags": [ + "scenic", + "minimal", + "calm" + ], + "accent": "#3c6678", + "surface": "#19180f", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif", + "assets/shot-1.png" + ], + "manifest": "manifest.json", + "manifestSha256": "05b8731d4e438f9ba1246ecada52d87f01bb3efca5ff24063db2bba91a1e1bad" + }, + { + "id": "forest", + "name": "Forest", + "version": "1.0.1", + "path": "lockscreens/forest", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "Sunbeams pouring through a misty autumn canopy.", + "description": "A tranquil video lock: light rays drift over an aerial autumn forest wrapped in fog, with a soft clock and an unobtrusive login.", + "tags": [ + "scenic", + "nature", + "calm" + ], + "accent": "#051612", + "surface": "#051612", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "d57a82bfb8b3b27df93e93e9ae0b90bcffebb1eebb5be83705dbe7a14ddc8b03" + }, + { + "id": "genshin", + "name": "Genshin Impact", + "version": "1.0.0", + "path": "lockscreens/genshin", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A moonlit abyss of drifting pillars in deep blue.", + "description": "A Genshin Impact inspired lock: towering ruins beneath a pale moon, slow-drifting clouds, and a clean login on the descending path.", + "tags": [ + "game", + "anime", + "night" + ], + "accent": "#192e6d", + "surface": "#07101f", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "370fd6d19c8b0b8c111c5fb77b3e1f7533644a3272c9415d176438b004c41caf" + }, + { + "id": "girl-coffee", + "name": "Coffee Break", + "version": "1.0.1", + "path": "lockscreens/girl-coffee", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A cosy hand-drawn morning with a warm mug in hand.", + "description": "A lo-fi illustrated character taking a coffee break, paired with the rounded Itim typeface and a soft, unobtrusive login.", + "tags": [ + "anime", + "cozy", + "illustrated" + ], + "accent": "#8eb9b6", + "surface": "#695b76", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif", + "assets/shot-1.png" + ], + "manifest": "manifest.json", + "manifestSha256": "f55205b272b11cccf29bc9840f18bfdd81268d72ea1bcfca6d78e2ed6b925660" + }, + { + "id": "girl-pillow", + "name": "Lazy Sunday", + "version": "1.0.1", + "path": "lockscreens/girl-pillow", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A soft illustrated scene, curled up and half awake.", + "description": "A relaxed lo-fi illustration with the rounded Itim font and a gentle right-aligned login - quiet, warm, and unhurried.", + "tags": [ + "anime", + "cozy", + "illustrated" + ], + "accent": "#737588", + "surface": "#625f6c", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif", + "assets/shot-1.png" + ], + "manifest": "manifest.json", + "manifestSha256": "1b0a2f1f99ad1bf0f0a0135b2e5363212017f27543ba30a3cbce83956abfc402" + }, + { + "id": "last-of-us", + "name": "The Last of Us", + "version": "1.0.1", + "path": "lockscreens/last-of-us", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A lone blossom tree adrift in cold grey mist.", + "description": "A The Last of Us inspired lock: a solitary cherry tree on a rocky outcrop among fog-veiled cliffs, with a spare, moody login.", + "tags": [ + "game", + "scenic", + "moody" + ], + "accent": "#88683b", + "surface": "#131210", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "cb8e5fd420c113f5407144f3d2304b5541978105da25fd05ae91c484651ad242" + }, + { + "id": "man-bicycle", + "name": "Evening Ride", + "version": "1.0.1", + "path": "lockscreens/man-bicycle", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A lone cyclist against a wide illustrated sky.", + "description": "A cinematic lo-fi ride with the Itim typeface and a minimal login tucked into the frame.", + "tags": [ + "scenic", + "illustrated", + "calm" + ], + "accent": "#2e8692", + "surface": "#395551", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif", + "assets/shot-1.png" + ], + "manifest": "manifest.json", + "manifestSha256": "08fe1c4fc9c646904ea57778974025266f3b7d5eccd5e723953956b727549ed8" + }, + { + "id": "material-you", + "name": "Material You", + "version": "1.0.1", + "path": "lockscreens/material-you", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "Google's Material You language, clean and adaptive.", + "description": "A crisp Material You lock with the Google Sans typeface, rounded surfaces, and a tidy centred login.", + "tags": [ + "material", + "clean", + "modern" + ], + "accent": "#1a3c32", + "surface": "#1a3c32", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif", + "assets/shot-1.png" + ], + "manifest": "manifest.json", + "manifestSha256": "c0356b4a2419a42a9f7752551ed97991aca578576486598ba798dc219d0c09fc" + }, + { + "id": "material-you-dark", + "name": "Material You Dark", + "version": "1.0.1", + "path": "lockscreens/material-you-dark", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "The Material You language in a deep, quiet dark palette.", + "description": "The dark sibling of the Material You lock: the Google Sans typeface and rounded surfaces over a low-light palette, with a tidy centred login.", + "tags": [ + "material", + "dark", + "clean" + ], + "accent": "#291f48", + "surface": "#141316", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif", + "assets/shot-1.png" + ], + "manifest": "manifest.json", + "manifestSha256": "d881d8b36b6c7a2236a61d4eec4f5928fa1771bba623e3500c119525b017f710" + }, + { + "id": "minecraft", + "name": "Minecraft Menu", + "version": "1.0.1", + "path": "lockscreens/minecraft", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "The blocky main-menu look, splash text and all.", + "description": "A nostalgic Minecraft title-screen lock: stone-button widgets, a random yellow splash line, and the classic logo.", + "tags": [ + "game", + "retro", + "playful" + ], + "accent": "#251b12", + "surface": "#0f0b08", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif", + "assets/shot-1.png" + ], + "manifest": "manifest.json", + "manifestSha256": "7e2e756ca2b48688892e93b41b9e51678a0013e0d55caff909a9eff5e6993472" + }, + { + "id": "nier-automata", + "name": "NieR: Automata", + "version": "1.0.1", + "path": "lockscreens/nier-automata", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "The stark beige-and-black UI of YoRHa.", + "description": "A faithful NieR: Automata lock with bordered YoRHa buttons, the signature muted palette, and a precise login.", + "tags": [ + "game", + "minimal", + "monochrome" + ], + "accent": "#1a1713", + "surface": "#1a1713", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif", + "assets/shot-1.png" + ], + "manifest": "manifest.json", + "manifestSha256": "08045d52f8dad638f1e6fe16264d0b7d6777345bab42b03086e88450d9dfb56e" + }, + { + "id": "ninja-gaiden", + "name": "Ninja Gaiden", + "version": "1.0.1", + "path": "lockscreens/ninja-gaiden", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "High-contrast action styling with the Tektur face.", + "description": "A sharp Ninja Gaiden lock: bold Tektur type, a dark cinematic background, and a decisive login.", + "tags": [ + "game", + "bold", + "dark" + ], + "accent": "#040201", + "surface": "#020101", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif", + "assets/shot-1.png" + ], + "manifest": "manifest.json", + "manifestSha256": "ffd96cc2705ca8f549d6ab5a39a9b0e688f5e7ce431babd3cbe3a2b17177c7e7" + }, + { + "id": "nothing", + "name": "Nothing OS", + "version": "1.0.1", + "path": "lockscreens/nothing", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "Dot-matrix minimalism in red and black.", + "description": "A Nothing-inspired lock: NDot dot-matrix numerals, twin rounded widgets, and a bright, airy backdrop.", + "tags": [ + "minimal", + "monochrome", + "modern" + ], + "accent": "#b71c23", + "surface": "#0e0e0e", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "c59741f9666945fa068283813b76705b21280d2f5d74c302f79731653fcfb7c0" + }, + { + "id": "osu", + "name": "osu!", + "version": "1.0.0", + "path": "lockscreens/osu", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "The osu! rhythm game, tap-circles and all.", + "description": "An osu! inspired lock with difficulty menus, neon tap-circles, and a playful, score-driven login.", + "tags": [ + "game", + "rhythm", + "playful" + ], + "accent": "#84fa04", + "surface": "#060504", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "02d6a89c8240e86c0f7966fc08e125e98d52201db7e015374271054ce017bbc6" + }, + { + "id": "osumania", + "name": "osu!mania", + "version": "1.0.1", + "path": "lockscreens/osumania", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A four-key note highway glowing in neon green.", + "description": "An osu!mania inspired lock: vertical scrolling notes, key-binding widgets, and PERFECT combos over a dark stage.", + "tags": [ + "game", + "rhythm", + "neon" + ], + "accent": "#050902", + "surface": "#050902", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "58e08d256426ff0722cf5f3a565c3e6ab5544accf46c11635b1e50b599312807" + }, + { + "id": "pixel-coffee", + "name": "Pixel Coffee", + "version": "1.0.1", + "path": "lockscreens/pixel-coffee", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A warm pixel cafe glowing after dark.", + "description": "A cosy pixel-art lock: amber lamps over a late-night cafe, city lights through the window, and a tidy login.", + "tags": [ + "pixel", + "cozy", + "night" + ], + "accent": "#090811", + "surface": "#090811", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "10ac931fe1f74386399c80f94048b835974372fadc81194f97aed3919caef16a" + }, + { + "id": "pixel-cyberpunk", + "name": "Pixel Cyberpunk", + "version": "1.0.1", + "path": "lockscreens/pixel-cyberpunk", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A neon pixel-art cityscape humming with rain and signage.", + "description": "An animated pixel-art cyberpunk street, all neon reflections and drizzle, with a crisp login.", + "tags": [ + "pixel", + "cyberpunk", + "neon" + ], + "accent": "#070e18", + "surface": "#070e18", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "eff36e820cb59fa9c40bb55b77760c66c5c8c688f0ec652997405510e1760c42" + }, + { + "id": "pixel-dusk-city", + "name": "Pixel Dusk City", + "version": "1.0.1", + "path": "lockscreens/pixel-dusk-city", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A red-lit pixel skyline smouldering at dusk.", + "description": "A pixel-art lock: a silhouetted city under a burning dusk sky, framed by dark leaves and tangled wires, with a minimal login.", + "tags": [ + "pixel", + "city", + "dusk" + ], + "accent": "#894a45", + "surface": "#0c0e0f", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "cc9aec4398157c0f6dcce6f4daa97e8affe424167c8072505794cde7e92e8f8e" + }, + { + "id": "pixel-emerald", + "name": "Pixel Emerald", + "version": "1.0.1", + "path": "lockscreens/pixel-emerald", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A seaside cycling route in bright pixel green.", + "description": "A Pokemon inspired pixel lock: a cyclist on a fenced coastal path amid lush fields and creatures, with a cheerful login.", + "tags": [ + "pixel", + "game", + "nostalgic" + ], + "accent": "#78a13c", + "surface": "#3b5649", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "b3c2d5c919994db4d111aa05e76ea715f65f081ba3f7390ad9ac634af2d978c6" + }, + { + "id": "pixel-hollowknight", + "name": "Pixel Hollow Knight", + "version": "1.0.1", + "path": "lockscreens/pixel-hollowknight", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A horned knight silhouetted against embered dark.", + "description": "A Hollow Knight inspired pixel lock: a pale-horned figure above drifting orange embers in the deep, with a stark login.", + "tags": [ + "game", + "pixel", + "dark" + ], + "accent": "#070604", + "surface": "#070604", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "c6efbe7d6137eba34d0ee9e0ae53829ee7d6200088add32ea5d2bcac9b827af6" + }, + { + "id": "pixel-munchlax", + "name": "Pixel Munchlax", + "version": "1.0.1", + "path": "lockscreens/pixel-munchlax", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A little companion on a grassy hill under summer clouds.", + "description": "A bright pixel-art lock: a small creature resting on green grass beneath towering white clouds, with a clean login.", + "tags": [ + "pixel", + "cute", + "calm" + ], + "accent": "#5cacf0", + "surface": "#0e1213", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "805c22b85dd622b8d040e820274d75bdb4b3bdd892813f5663f4e7736ec201fc" + }, + { + "id": "pixel-night-city", + "name": "Pixel Night City", + "version": "1.0.1", + "path": "lockscreens/pixel-night-city", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A glittering pixel skyline humming after dark.", + "description": "A pixel-art lock: dense neon-lit towers over shadowed rooftops, cool and electric, with a crisp login.", + "tags": [ + "pixel", + "city", + "night" + ], + "accent": "#0e0e19", + "surface": "#0e0e19", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "fb0c0c9fcf0e541ca7bdc8f3231fe041113cbcf0b853ff563f7b4d8cccda728a" + }, + { + "id": "pixel-rainyroom", + "name": "Pixel Rainy Room", + "version": "1.0.1", + "path": "lockscreens/pixel-rainyroom", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A rainy night at the desk, monitor aglow.", + "description": "A lo-fi pixel lock: rain on the window and a blue monitor glow across a dim bedroom, with a quiet login.", + "tags": [ + "pixel", + "cozy", + "rain" + ], + "accent": "#02050c", + "surface": "#02050c", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "796f5b8abf82a53166cac4f89162d356938ceea20b64b5db3aaf6dd32fcab268" + }, + { + "id": "pixel-sakura", + "name": "Pixel Sakura", + "version": "1.0.1", + "path": "lockscreens/pixel-sakura", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A pixel-art courtyard under drifting cherry blossom.", + "description": "An animated pixel-art scene of falling sakura petals, warm and unhurried, with a tidy login.", + "tags": [ + "pixel", + "anime", + "calm" + ], + "accent": "#2e3949", + "surface": "#2e3949", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "efcb1260e4e98ef97fddd97024de504e66da6c27401932476276afc1da36e1c7" + }, + { + "id": "pixel-skyscrapers", + "name": "Pixel Skyscrapers", + "version": "1.0.1", + "path": "lockscreens/pixel-skyscrapers", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A pastel dawn washing over a layered skyline.", + "description": "A pixel-art lock: peach-and-mauve skies and a soft sun behind stacked skyscrapers, with a calm login.", + "tags": [ + "pixel", + "city", + "pastel" + ], + "accent": "#020e11", + "surface": "#020e11", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "4e0b5bc1eafb4c62ff0bcc002d8271ad2248d819a308c3a5815652b759bf3985" + }, + { + "id": "pixel-waterfall", + "name": "Pixel Waterfall", + "version": "1.0.1", + "path": "lockscreens/pixel-waterfall", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A tranquil pixel-art waterfall in a mossy glen.", + "description": "An animated pixel-art waterfall scene, cool and green, with a calm minimal login.", + "tags": [ + "pixel", + "nature", + "calm" + ], + "accent": "#106e97", + "surface": "#040e15", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "56892aed6ec940d42f650fa922cf03e791b1dde6a329c898373dbc1e0022d72f" + }, + { + "id": "r1999-1", + "name": "Reverse: 1999", + "version": "1.0.1", + "path": "lockscreens/r1999-1", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A pensive period portrait bathed in soft sepia light.", + "description": "A Reverse: 1999 inspired lock centred on a wistful character portrait in warm vintage tones, with an understated login.", + "tags": [ + "game", + "anime", + "vintage" + ], + "accent": "#422a1f", + "surface": "#121011", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "4475b01a6aff68d111a37facbff83e78410c36fcb666b37712f46ba5df916620" + }, + { + "id": "r1999-2", + "name": "Reverse: 1999", + "version": "1.0.1", + "path": "lockscreens/r1999-2", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "Vintage flourish with an animated period backdrop.", + "description": "A Reverse: 1999 inspired lock with an animated vintage backdrop, a period logo, and an elegant login.", + "tags": [ + "game", + "anime", + "vintage" + ], + "accent": "#88725b", + "surface": "#101011", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "c14cf827cae63e5a7d6aa6ad69bcdadefe14ddd3b63ceba0e2e0a36e0c59b7a8" + }, + { + "id": "star-rail", + "name": "Star Rail", + "version": "1.0.0", + "path": "lockscreens/star-rail", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "An astral, animated backdrop for the trailblazers.", + "description": "A Honkai: Star Rail inspired lock with an animated cosmic backdrop and a clean, modern login.", + "tags": [ + "game", + "anime", + "space" + ], + "accent": "#060b15", + "surface": "#060b15", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "b11248876dbbfca5cf598955185340430f50b8d49bf904420233d2d752cd1311" + }, + { + "id": "sword", + "name": "Sword", + "version": "1.0.1", + "path": "lockscreens/sword", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A lone blade planted in a misty grove.", + "description": "A moody, near-monochrome lock: a katana standing point-down among blurred dark trees, with a spare login.", + "tags": [ + "dark", + "minimal", + "cinematic" + ], + "accent": "#08090f", + "surface": "#08090f", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "2df8248c4f52e49ed6e1ee4b4084771963d0777f03ee364d5acf65c56239eae5" + }, + { + "id": "terraria", + "name": "Terraria", + "version": "1.0.0", + "path": "lockscreens/terraria", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "The Terraria main menu, pixel logo and all.", + "description": "A Terraria title-screen lock with the pixel logo, a tree-lined horizon, and wood-panel buttons.", + "tags": [ + "game", + "pixel", + "retro" + ], + "accent": "#4e66c5", + "surface": "#1a2530", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "6105ce4c65eb446a0206d9ba3f9c9d08fe0b63276be120b44879800ba14b9237" + }, + { + "id": "windows-7", + "name": "Windows 7", + "version": "1.0.0", + "path": "lockscreens/windows-7", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "The nostalgic Windows 7 welcome screen.", + "description": "A throwback Windows 7 lock: a round avatar, a glassy credential field, and the familiar blue welcome mood.", + "tags": [ + "os", + "retro", + "nostalgic" + ], + "accent": "#0755ad", + "surface": "#043979", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif", + "assets/shot-1.png" + ], + "manifest": "manifest.json", + "manifestSha256": "377240f577be55559d6328ffd5952a0053c0fcc3bcf5e87bc327967f66f5f77d" + }, + { + "id": "winter", + "name": "Winter", + "version": "1.0.1", + "path": "lockscreens/winter", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A quiet, animated snowfall in cold blue.", + "description": "A calm winter lock: soft, drifting snow over a cold-blue scene with an understated login.", + "tags": [ + "scenic", + "winter", + "calm" + ], + "accent": "#0c1418", + "surface": "#0c1418", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "81814180afe714ef766c1f83b377fb50990b424ff5006afc49f09fc7148b53b7" + }, + { + "id": "women-umbrella", + "name": "Rainy Walk", + "version": "1.0.1", + "path": "lockscreens/women-umbrella", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "An illustrated figure under an umbrella in gentle rain.", + "description": "A moody lo-fi illustration of a walk in the rain, with a calm, understated login.", + "tags": [ + "illustrated", + "rain", + "calm" + ], + "accent": "#a07f80", + "surface": "#3f3c38", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif", + "assets/shot-1.png" + ], + "manifest": "manifest.json", + "manifestSha256": "65e313d43f563d9adefd93a35270527e1ef63bc041885680d8482a464a83e43d" + }, + { + "id": "world-machine", + "name": "World Machine", + "version": "1.0.2", + "path": "lockscreens/world-machine", + "author": "CratusGod", + "upstream": "https://github.com/CratusGod/Oneshot-themed-Ryoku", + "summary": "Niko's lightbulb over a OneShot sunset, with music.", + "description": "A OneShot lock: Avolicis' pixel-art sunset animation (used with permission) with its soundtrack, Niko's lightbulb as your avatar, THE WORLD MACHINE wordmark, Terminus type and a Num Lock indicator. The matching shell reload cover and boot screen live at https://github.com/CratusGod/Oneshot-themed-Ryoku.", + "tags": [ + "game", + "pixel", + "animated", + "music" + ], + "accent": "#FFD34A", + "surface": "#0B0A12", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "9b8f74183de4546b61c8b3dcf0726757494ba39bc00bb42b49a197cbfc0f7fb9", + "official": false, + "lastUpdated": "2026-10-21" + }, + { + "id": "wuwa", + "name": "Wuthering Waves", + "version": "1.0.0", + "path": "lockscreens/wuwa", + "author": "Darkkal44", + "upstream": "https://github.com/Darkkal44/qylock", + "summary": "A lone figure amid drifting ruins under pale light.", + "description": "A Wuthering Waves inspired lock: a desolate greyscale expanse of floating debris cut by a single beam of light, with a modern login.", + "tags": [ + "game", + "anime", + "monochrome" + ], + "accent": "#0e1018", + "surface": "#0e1018", + "preview": "assets/preview.gif", + "screenshots": [ + "assets/preview.gif" + ], + "manifest": "manifest.json", + "manifestSha256": "cc3b45853c1f123626adf24d89beb60e3d332ec59e45b82f25ad6f93dbbb8cfe" + }, + { + "id": "nothing-interface", + "name": "Nothing Interface", + "version": "0.1.0", + "path": "lockscreens/nothing-interface", + "author": "Ivan <254410104+venchik111@users.noreply.github.com>", + "upstream": "https://github.com/venchik111/ryoku-nothing-interface", + "summary": "Dot clock and live Ryoku status over your current wallpaper.", + "description": "A Nothing OS-inspired lockscreen with a dot clock, battery, daily screen time and MPRIS media controls. Uses the current Ryogami wallpaper during a locked session; no wallpaper is bundled.", + "tags": [ + "nothing", + "monochrome", + "live", + "wallpaper" + ], + "accent": "#C2C4CB", + "surface": "#1C1D21", + "preview": "assets/preview.png", + "screenshots": [ + "assets/preview.png" + ], + "manifest": "manifest.json", + "manifestSha256": "158f1f5ad37b51c589f511366eb23724bf10261c9c0d96f9782ae8640ca772d4", + "official": false, + "license": "MIT", + "lastUpdated": "2026-10-01" + }, + { + "id": "kitsune", + "name": "Kitsune", + "version": "1.0.0", + "path": "lockscreens/kitsune", + "author": "0xSrcAura", + "upstream": "https://github.com/SourceAura/kitsune", + "summary": "Ase, the seven-tailed kitsune, and her seven tarot folds at the door.", + "description": "The Jurai lock, carried to the store whole: Ase, the seven-tailed kitsune, drawn in shaders and light, and her seven tarot folds. Typing binds the folds one by one, each whispered as it lands — THE EYE · TRUTH, THE PHANTOM · POWER, THE BLADE · WISDOM, THE SAGE · JUDGMENT, THE SHROUD · LIFE, THE FORGE · LOVE, THE MIRROR · DISCIPLINE. At seven the Cast rises: INVOKE THE SEAL. A wrong key hurts her and scatters the cards; the true key transforms her and opens the seal. Built in 2026 for Jurai shell; her scene, her shaders and her words are unmodified.", + "tags": [ + "kitsune", + "jurai", + "tarot", + "dark" + ], + "accent": "#818CF8", + "surface": "#12131C", + "preview": "assets/preview.webp", + "screenshots": [ + "assets/preview.webp" + ], + "manifest": "manifest.json", + "manifestSha256": "6c8304159ae7dc9d0d8d8f2c00483e36afc171e5d067b3bda6dad37e51ad803d" + } + ] +} \ No newline at end of file