From fdf2c5359c4a8cefaa8f91a148e850b7a8983393 Mon Sep 17 00:00:00 2001 From: jurai-local Date: Sun, 25 Oct 2026 12:35:20 +0000 Subject: [PATCH] =?UTF-8?q?lockscreens:=20Kitsune=20=E2=80=94=20Ase,=20the?= =?UTF-8?q?=20seven-tailed=20kitsune,=20at=20the=20door?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lockscreens/kitsune/LICENSE | 674 ++++++ lockscreens/kitsune/PROVENANCE.txt | 43 + lockscreens/kitsune/assets/preview.webp | Bin 0 -> 18666 bytes lockscreens/kitsune/content/Main.qml | 412 ++++ .../kitsune/content/gotchi/InseiCompanion.qml | 126 ++ .../kitsune/content/gotchi/InseiGotchi.qml | 652 ++++++ .../kitsune/content/gotchi/InseiHub.qml | 206 ++ .../kitsune/content/gotchi/InseiHud.qml | 134 ++ .../kitsune/content/gotchi/InseiLiveHash.qml | 221 ++ .../kitsune/content/gotchi/InseiScene.qml | 330 +++ .../kitsune/content/gotchi/InseiTarot.qml | 366 ++++ lockscreens/kitsune/content/gotchi/README.md | 122 ++ lockscreens/kitsune/content/gotchi/qmldir | 8 + lockscreens/kitsune/content/metadata.desktop | 7 + .../kitsune/content/shaders/InseiKitsune.frag | 196 ++ .../content/shaders/InseiKitsune.frag.qsb | Bin 0 -> 12381 bytes .../kitsune/content/shaders/InseiSoul.frag | 299 +++ .../content/shaders/InseiSoul.frag.qsb | Bin 0 -> 17847 bytes .../kitsune/content/shaders/crystallize.frag | 299 +++ .../content/shaders/crystallize.frag.qsb | Bin 0 -> 13658 bytes lockscreens/kitsune/content/theme.conf | 7 + .../kitsune/content/tw/ArOrchestrator.qml | 30 + .../kitsune/content/tw/TransparentWorld.qml | 599 ++++++ lockscreens/kitsune/content/tw/qmldir | 4 + lockscreens/kitsune/manifest.json | 201 ++ lockscreens/registry.json | 1904 +++++++++-------- 26 files changed, 5900 insertions(+), 940 deletions(-) create mode 100644 lockscreens/kitsune/LICENSE create mode 100644 lockscreens/kitsune/PROVENANCE.txt create mode 100644 lockscreens/kitsune/assets/preview.webp create mode 100644 lockscreens/kitsune/content/Main.qml create mode 100644 lockscreens/kitsune/content/gotchi/InseiCompanion.qml create mode 100644 lockscreens/kitsune/content/gotchi/InseiGotchi.qml create mode 100644 lockscreens/kitsune/content/gotchi/InseiHub.qml create mode 100644 lockscreens/kitsune/content/gotchi/InseiHud.qml create mode 100644 lockscreens/kitsune/content/gotchi/InseiLiveHash.qml create mode 100644 lockscreens/kitsune/content/gotchi/InseiScene.qml create mode 100644 lockscreens/kitsune/content/gotchi/InseiTarot.qml create mode 100644 lockscreens/kitsune/content/gotchi/README.md create mode 100644 lockscreens/kitsune/content/gotchi/qmldir create mode 100644 lockscreens/kitsune/content/metadata.desktop create mode 100644 lockscreens/kitsune/content/shaders/InseiKitsune.frag create mode 100644 lockscreens/kitsune/content/shaders/InseiKitsune.frag.qsb create mode 100644 lockscreens/kitsune/content/shaders/InseiSoul.frag create mode 100644 lockscreens/kitsune/content/shaders/InseiSoul.frag.qsb create mode 100644 lockscreens/kitsune/content/shaders/crystallize.frag create mode 100644 lockscreens/kitsune/content/shaders/crystallize.frag.qsb create mode 100644 lockscreens/kitsune/content/theme.conf create mode 100644 lockscreens/kitsune/content/tw/ArOrchestrator.qml create mode 100644 lockscreens/kitsune/content/tw/TransparentWorld.qml create mode 100644 lockscreens/kitsune/content/tw/qmldir create mode 100644 lockscreens/kitsune/manifest.json 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 0000000000000000000000000000000000000000..893a7c2dc07b5e958168c2c0f77fec79d42db6f4 GIT binary patch literal 18666 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5b5e8c1875f74a7f34f1b662ceb7c159845f1fb1 GIT binary patch literal 12381 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 F{|DHQ4VwS} literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..da373193a8788715799fb52391e38059ed7af197 GIT binary patch literal 17847 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 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 0000000000000000000000000000000000000000..8e7561724ac48128c909240cfefd263e6b7728a0 GIT binary patch literal 13658 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 literal 0 HcmV?d00001 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