commit ac569b2a5712ef69944e5fd4fb618535ecefcdec Author: anian Date: Mon Mar 16 00:03:35 2026 +0800 3.0.0原版 diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..ab57e00 --- /dev/null +++ b/.gitignore @@ -0,0 +1,74 @@ +### Maven +target/ +logs/ +!.mvn/wrapper/maven-wrapper.jar + +### Gradle +.gradle +/build/ +/out/ +!gradle/wrapper/gradle-wrapper.jar +bin/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +log/ + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +### Mac +.DS_Store +*/.DS_Store + +### VS Code ### +*.project +*.factorypath + +### Compiled class file +*.class + +### Log file +*.log + +### BlueJ files +*.ctxt + +### Mobile Tools for Java (J2ME) +.mtj.tmp/ + +### Package Files +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +### Local file +application-local.yml +application-local.yaml +application-local.properties + +/packages/*/node_modules/ +node_modules +/workplace/ +/src/main/resources/static/ +/src/main/resources/console/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..20d40b6 --- /dev/null +++ b/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 +. \ No newline at end of file diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000..eeb425e --- /dev/null +++ b/OWNERS @@ -0,0 +1,10 @@ +reviewers: +- ruibaby +- guqing +- JohnNiang +- wangzhen-fit2cloud + +approvers: +- ruibaby +- guqing +- JohnNiang diff --git a/README.md b/README.md new file mode 100644 index 0000000..3cd2a26 --- /dev/null +++ b/README.md @@ -0,0 +1,275 @@ +# plugin-comment-widget + +Halo 2.0 的通用评论组件插件,为前台提供完整的评论解决方案。 + +![Cover](./images/cover.png) + +## 使用方式 + +1. 下载,目前提供以下两个下载方式: + - GitHub Releases:访问 [Releases](https://github.com/halo-dev/plugin-comment-widget/releases) 下载 Assets 中的 JAR 文件。 + - Halo 应用市场:。 +2. 安装,插件安装和更新方式可参考:。 + +> 需要注意的是,此插件需要主题进行适配,不会主动在内容页加载评论组件。 + +## 开发环境 + +```bash +git clone git@github.com:halo-dev/plugin-comment-widget.git + +# 或者当你 fork 之后 + +git clone git@github.com:{your_github_id}/plugin-comment-widget.git +``` + +```bash +cd path/to/plugin-comment-widget +``` + +```bash +./gradlew pnpmInstall + +# 启动一个 Docker 容器作为开发环境并自动加载此插件 +./gradlew haloServer +``` + +Halo 插件的详细开发文档可查阅 [插件开发](https://docs.halo.run/category/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91)。 + +## 主题适配 + +### 接入 + +接入文档可参考 [自定义标签](https://docs.halo.run/developer-guide/theme/template-tag#halocomment)。 + +### 自定义样式 + +虽然目前不能直接为评论组件编写额外的样式,但可以通过一系列的 CSS 变量来自定义部分样式,开发者可以根据需求自行在主题中添加这些 CSS 变量,让评论组件和主题更好地融合。 + +目前已提供的 CSS 变量: + +| 变量名 | 描述 | +| ---------------------------- | ---------------------------------------- | +| `--halo-cw-primary-1-color` | 主要的主题色,用于按钮背景,输入框边框等 | +| `--halo-cw-primary-2-color` | 较浅的主题色 | +| `--halo-cw-primary-3-color` | 最浅的主题色 | +| `--halo-cw-text-1-color` | 主要文本颜色,用于标题、正文等 | +| `--halo-cw-text-2-color` | 次要文本颜色 | +| `--halo-cw-text-3-color` | 辅助文本颜色 | +| `--halo-cw-muted-1-color` | 弱化色,用于边框、分割线、背景等辅助元素 | +| `--halo-cw-muted-2-color` | 更浅的弱化色 | +| `--halo-cw-muted-3-color` | 最浅的弱化色 | +| `--halo-cw-base-rounded` | 基础圆角大小 | +| `--halo-cw-avatar-rounded` | 头像圆角大小 | +| `--halo-cw-avatar-size` | 头像尺寸 | +| `--halo-cw-base-font-size` | 基础字体大小 | +| `--halo-cw-base-font-family` | 基础字体族 | + +
+点击查看 CSS 代码模板 + +```css +:root { + --halo-cw-primary-1-color: ; + --halo-cw-primary-2-color: ; + --halo-cw-primary-3-color: ; + + --halo-cw-text-1-color: ; + --halo-cw-text-2-color: ; + --halo-cw-text-3-color: ; + + --halo-cw-muted-1-color: ; + --halo-cw-muted-2-color: ; + --halo-cw-muted-3-color: ; + + --halo-cw-base-rounded: ; + --halo-cw-avatar-rounded: ; + --halo-cw-avatar-size: ; + --halo-cw-base-font-size: ; + --halo-cw-base-font-family: ; +} +``` + +
+ +
+3.0.0 版本之前已废弃的 CSS 变量 + +| 变量名 | 描述 | 备注 | +| ----------------------------------------------------------------------- | ------------------------ | -------------------------------------------------- | +| `--halo-comment-widget-base-color` | 基础文字颜色 | 已废弃,后续使用 `--halo-cw-text-1-color` 代替 | +| `--halo-comment-widget-base-info-color` | 非重要突出文字 | 已废弃,后续使用 `--halo-cw-muted-*-color` 代替 | +| `--halo-comment-widget-base-border-radius` | 基础元素的圆角 | 已废弃,后续使用 `--halo-cw-base-rounded` 代替 | +| `--halo-comment-widget-base-font-size` | 基础字体大小 | 已废弃,后续使用 `--halo-cw-base-font-size` 代替 | +| `--halo-comment-widget-base-line-height` | 基础行高 | 已废弃 | +| `--halo-comment-widget-base-font-family` | 基础字体族 | 已废弃,后续使用 `--halo-cw-base-font-family` 代替 | +| `--halo-comment-widget-component-avatar-rounded` | 头像的圆角大小 | 已废弃,后续使用 `--halo-cw-avatar-rounded` 代替 | +| `--halo-comment-widget-component-avatar-size` | 头像大小 | 已废弃,后续使用 `--halo-cw-avatar-size` 代替 | +| `--halo-comment-widget-component-form-input-bg-color` | 表单输入背景颜色 | 已废弃 | +| `--halo-comment-widget-component-form-input-color` | 表单输入文字颜色 | 已废弃 | +| `--halo-comment-widget-component-form-input-border-color` | 表单输入边框颜色 | 已废弃 | +| `--halo-comment-widget-component-form-input-border-color-focus` | 表单输入焦点时边框颜色 | 已废弃,后续使用 `--halo-cw-primary-1-color` 代替 | +| `--halo-comment-widget-component-form-input-box-shadow-focus` | 表单输入焦点时的阴影 | 已废弃,后续使用 `--halo-cw-primary-2-color` 代替 | +| `--halo-comment-widget-component-form-button-login-bg-color` | 登录按钮背景颜色 | 已废弃 | +| `--halo-comment-widget-component-form-button-login-bg-color-hover` | 登录按钮悬停背景颜色 | 已废弃 | +| `--halo-comment-widget-component-form-button-login-border-color` | 登录按钮边框颜色 | 已废弃 | +| `--halo-comment-widget-component-form-button-submit-bg-color` | 提交按钮背景颜色 | 已废弃,后续使用 `--halo-cw-primary-1-color` 代替 | +| `--halo-comment-widget-component-form-button-submit-color` | 提交按钮文字颜色 | 已废弃 | +| `--halo-comment-widget-component-form-button-submit-border-color` | 提交按钮边框颜色 | 已废弃 | +| `--halo-comment-widget-component-form-button-submit-border-color-hover` | 提交按钮悬停边框颜色 | 已废弃,后续使用 `--halo-cw-primary-3-color` 代替 | +| `--halo-comment-widget-component-form-button-emoji-color` | 表情按钮颜色 | 已废弃 | +| `--halo-comment-widget-component-comment-item-action-bg-color-hover` | 评论项操作悬停背景颜色 | 已废弃 | +| `--halo-comment-widget-component-comment-item-action-color-hover` | 评论项操作悬停颜色 | 已废弃 | +| `--halo-comment-widget-component-pagination-button-bg-color-hover` | 分页按钮悬停背景颜色 | 已废弃 | +| `--halo-comment-widget-component-pagination-button-bg-color-active` | 分页按钮活动状态背景颜色 | 已废弃 | +| `--halo-comment-widget-component-pagination-button-border-color-active` | 分页按钮活动状态边框颜色 | 已废弃 | +| `--halo-comment-widget-component-emoji-picker-rgb-color` | 表情选择器颜色 | 已废弃 | +| `--halo-comment-widget-component-emoji-picker-rgb-accent` | 表情选择器强调颜色 | 已废弃 | +| `--halo-comment-widget-component-emoji-picker-rgb-background` | 表情选择器背景颜色 | 已废弃 | +| `--halo-comment-widget-component-emoji-picker-rgb-input` | 表情选择器输入颜色 | 已废弃 | +| `--halo-comment-widget-component-emoji-picker-color-border` | 表情选择器边框颜色 | 已废弃 | +| `--halo-comment-widget-component-emoji-picker-color-border-over` | 表情选择器悬停边框颜色 | 已废弃 | + +
+ +### 配色切换方案 + +根据上面提供的 CSS 变量,也可以通过定义 CSS 变量的方式为评论组件提供动态切换配色的功能。 + +以下是实现示例,你可以根据需求自行修改选择器或者媒体查询。 + +```css +@media (prefers-color-scheme: dark) { + .color-scheme-auto, + [data-color-scheme='auto'] { + color-scheme: dark; + + --halo-cw-primary-1-color: #059669; + --halo-cw-primary-2-color: #047857; + --halo-cw-primary-3-color: #065f46; + + --halo-cw-text-1-color: #f9fafb; + --halo-cw-text-2-color: #e5e7eb; + --halo-cw-text-3-color: #9ca3af; + + --halo-cw-muted-1-color: #4b5563; + --halo-cw-muted-2-color: #374151; + --halo-cw-muted-3-color: #1f2937; + + --halo-cw-emoji-picker-rgb-color: 222, 222, 221; + --halo-cw-emoji-picker-rgb-accent: 58, 130, 247; + --halo-cw-emoji-picker-rgb-background: 21, 22, 23; + --halo-cw-emoji-picker-rgb-input: 0, 0, 0; + --halo-cw-emoji-picker-color-border: rgba(255, 255, 255, 0.1); + --halo-cw-emoji-picker-color-border-over: rgba(255, 255, 255, 0.2); + } +} + +.color-scheme-dark, +.dark, +[data-color-scheme='dark'] { + color-scheme: dark; + + --halo-cw-primary-1-color: #059669; + --halo-cw-primary-2-color: #047857; + --halo-cw-primary-3-color: #065f46; + + --halo-cw-text-1-color: #f9fafb; + --halo-cw-text-2-color: #e5e7eb; + --halo-cw-text-3-color: #9ca3af; + + --halo-cw-muted-1-color: #4b5563; + --halo-cw-muted-2-color: #374151; + --halo-cw-muted-3-color: #1f2937; + + --halo-cw-emoji-picker-rgb-color: 222, 222, 221; + --halo-cw-emoji-picker-rgb-accent: 58, 130, 247; + --halo-cw-emoji-picker-rgb-background: 21, 22, 23; + --halo-cw-emoji-picker-rgb-input: 0, 0, 0; + --halo-cw-emoji-picker-color-border: rgba(255, 255, 255, 0.1); + --halo-cw-emoji-picker-color-border-over: rgba(255, 255, 255, 0.2); +} +``` + +此外,为了让主题可以更加方便的适配暗黑模式,此插件也提供了一套暗黑模式的配色方案,主题可以直接使用此方案,而不需要自己去适配暗黑模式,适配方式如下: + +1. 在 html 或者 body 标签添加 class: + 1. `color-scheme-auto`:自动模式,根据系统的暗黑模式自动切换。 + 2. `color-scheme-dark` / `dark`:强制暗黑模式。 + 3. `color-scheme-light` / `light`:强制明亮模式。 +2. 在 html 或者 body 标签添加 `data-color-scheme` 属性: + 1. `auto`:自动模式,根据系统的暗黑模式自动切换。 + 2. `dark`:强制暗黑模式。 + 3. `light`:强制明亮模式。 + +## 作为组件使用 + +此插件的 UI 部分采用 [Lit Element](https://lit.dev/) 编写,并最终编译为 Web Component,所以理论上可以在任何 JS 框架中使用。这非常适用于将 Halo 作为 Headless CMS 使用的场景。例如使用 Vue、React 等框架编写网站,并调用 Halo 的 API 来渲染网页,这个时候文章评论的解决方案就可以直接使用此组件。 + +安装: + +```bash +pnpm install @halo-dev/comment-widget +``` + +### Vue + +在 Vue 组件中使用时,需要在 Vue 的编译选项中设置将此组件标记为非 Vue 组件,以下是 Vite 的配置示例: + +```js +import vue from '@vitejs/plugin-vue' + +export default { + plugins: [ + vue({ + template: { + compilerOptions: { + isCustomElement: (tag) => tag === 'comment-widget' + } + } + }) + ] +} +``` + +然后在 SFC 中使用即可: + +```vue + + + +``` + +## React + +```ts +import "@halo-dev/comment-widget"; +import "@halo-dev/comment-widget/var.css"; + +function App() { + return ( + <> + + + ); +} + +export default App; +``` diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..e2de23f --- /dev/null +++ b/biome.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.0/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "includes": ["**", "!**/generated"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "es5" + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..740775c --- /dev/null +++ b/build.gradle @@ -0,0 +1,60 @@ +plugins { + id 'java' + id "io.freefair.lombok" version "8.14" + id "com.github.node-gradle.node" version "7.1.0" + id "run.halo.plugin.devtools" version "0.6.2" +} + +group 'run.halo.comment.widget' + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +tasks.withType(JavaCompile).configureEach { + options.encoding = "UTF-8" + options.release = 21 +} + +repositories { + mavenCentral() +} + +dependencies { + implementation platform('run.halo.tools.platform:plugin:2.21.0') + compileOnly 'run.halo.app:api' + + testImplementation 'run.halo.app:api' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +test { + useJUnitPlatform() +} + +node { + nodeProjectDir = file("${project.projectDir}") +} + +tasks.register('buildFrontend', PnpmTask) { + args = ['build'] + dependsOn('installDepsForUI') +} + +tasks.register('installDepsForUI', PnpmTask) { + args = ['install'] +} + +build { + // build frontend before build + tasks.named('compileJava').configure { + dependsOn('buildFrontend') + } +} + +halo { + version = "2.21.6" + debug = true +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..4950f0d --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +version=3.0.0 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..8bdaf60 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2a84e18 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..ef07e01 --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..5eed7ee --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/images/cover.png b/images/cover.png new file mode 100644 index 0000000..f3893c0 Binary files /dev/null and b/images/cover.png differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..2c25edf --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "license": "GPL-3.0", + "author": { + "name": "Halo", + "url": "https://github.com/halo-dev" + }, + "contributors": [ + { + "name": "Ryan Wang", + "email": "i@ryanc.cc", + "url": "https://github.com/ruibaby" + } + ], + "scripts": { + "build": "pnpm -r run build", + "check": "biome check --write", + "example:dev": "pnpm -C packages/example dev", + "prepare": "husky", + "release": "pnpm -r run release" + }, + "lint-staged": { + "*": [ + "biome check --no-errors-on-unmatched --files-ignore-unknown=true" + ] + }, + "devDependencies": { + "@biomejs/biome": "^2.2.0", + "@tsconfig/node18": "^18.2.4", + "@types/node": "^18.19.123", + "husky": "^9.1.7", + "lint-staged": "^16.1.5", + "typescript": "~5.3.3", + "vite": "^7.1.2", + "vite-plugin-dts": "^4.5.4" + }, + "packageManager": "pnpm@10.14.0", + "pnpm": { + "overrides": { + "vite": "npm:rolldown-vite@7.0.10" + } + }, + "dependencies": { + "shiki": "^3.9.2" + } +} diff --git a/packages/comment-widget/lit-localize.json b/packages/comment-widget/lit-localize.json new file mode 100644 index 0000000..fd1962f --- /dev/null +++ b/packages/comment-widget/lit-localize.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://raw.githubusercontent.com/lit/lit/main/packages/localize-tools/config.schema.json", + "sourceLocale": "en", + "targetLocales": ["es", "zh-CN", "zh-TW"], + "tsConfig": "./tsconfig.json", + "output": { + "mode": "runtime", + "outputDir": "./src/generated/locales", + "localeCodesModule": "./src/generated/locale-codes.ts" + }, + "interchange": { + "format": "xliff", + "xliffDir": "./xliff/" + } +} diff --git a/packages/comment-widget/package.json b/packages/comment-widget/package.json new file mode 100644 index 0000000..d4dcc59 --- /dev/null +++ b/packages/comment-widget/package.json @@ -0,0 +1,63 @@ +{ + "name": "@halo-dev/comment-widget", + "version": "3.0.0", + "author": { + "name": "Halo", + "url": "https://github.com/halo-dev" + }, + "maintainers": [ + { + "name": "Ryan Wang", + "url": "https://github.com/ruibaby" + } + ], + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./var.css": "./var.css" + }, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist", + "var.css" + ], + "scripts": { + "build": "vite build", + "dev": "vite build --watch", + "locale:build": "lit-localize build", + "locale:extract": "lit-localize extract" + }, + "dependencies": { + "@emoji-mart/data": "^1.2.1", + "@floating-ui/dom": "^1.7.3", + "@halo-dev/api-client": "https://pkg.pr.new/@halo-dev/api-client@7679", + "@lit/context": "^1.1.6", + "@lit/localize": "^0.12.2", + "@tiptap/core": "^3.2.0", + "@tiptap/extensions": "^3.2.0", + "@tiptap/pm": "^3.2.0", + "@tiptap/starter-kit": "^3.2.0", + "dayjs": "^1.11.13", + "emoji-mart": "^5.6.0", + "es-toolkit": "^1.39.9", + "javascript-time-ago": "^2.5.11", + "lit": "^3.3.1", + "ofetch": "^1.4.1", + "sanitize-html": "^2.17.0", + "tiptap-extension-code-block-shiki": "^0.6.0", + "ua-parser-js": "^2.0.4" + }, + "devDependencies": { + "@iconify/json": "^2.2.373", + "@lit/localize-tools": "^0.8.0", + "@types/sanitize-html": "^2.16.0", + "@unocss/reset": "^66.4.2", + "lit-analyzer": "^2.0.3", + "unocss": "^66.4.2" + } +} diff --git a/packages/comment-widget/pnpm-lock.yaml b/packages/comment-widget/pnpm-lock.yaml new file mode 100644 index 0000000..b69bf22 --- /dev/null +++ b/packages/comment-widget/pnpm-lock.yaml @@ -0,0 +1,542 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@emoji-mart/data': + specifier: ^1.1.2 + version: 1.1.2 + '@halo-dev/api-client': + specifier: ^2.12.0 + version: 2.12.0 + '@lit/context': + specifier: ^1.1.0 + version: 1.1.0 + dayjs: + specifier: ^1.11.10 + version: 1.11.10 + emoji-mart: + specifier: ^5.5.2 + version: 5.5.2 + javascript-time-ago: + specifier: ^2.5.9 + version: 2.5.9 + lit: + specifier: ^3.1.2 + version: 3.1.2 + +devDependencies: + typescript: + specifier: ^5.3.3 + version: 5.3.3 + vite: + specifier: ^5.1.4 + version: 5.1.4 + +packages: + + /@emoji-mart/data@1.1.2: + resolution: {integrity: sha512-1HP8BxD2azjqWJvxIaWAMyTySeZY0Osr83ukYjltPVkNXeJvTz7yDrPLBtnrD5uqJ3tg4CcLuuBW09wahqL/fg==} + dev: false + + /@esbuild/android-arm64@0.19.9: + resolution: {integrity: sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.19.9: + resolution: {integrity: sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.19.9: + resolution: {integrity: sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.19.9: + resolution: {integrity: sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.19.9: + resolution: {integrity: sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.19.9: + resolution: {integrity: sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.19.9: + resolution: {integrity: sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.19.9: + resolution: {integrity: sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.19.9: + resolution: {integrity: sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.19.9: + resolution: {integrity: sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.19.9: + resolution: {integrity: sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.19.9: + resolution: {integrity: sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.19.9: + resolution: {integrity: sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.19.9: + resolution: {integrity: sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.19.9: + resolution: {integrity: sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.19.9: + resolution: {integrity: sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.19.9: + resolution: {integrity: sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.19.9: + resolution: {integrity: sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.19.9: + resolution: {integrity: sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.19.9: + resolution: {integrity: sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.19.9: + resolution: {integrity: sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.19.9: + resolution: {integrity: sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@halo-dev/api-client@2.12.0: + resolution: {integrity: sha512-W/FmwtdXvq/BSwi2VMYBPzV86SmUjjn/1RO3pzyBOp8KwZM97vVVPJwYhT7IUB+W7q9HtDBUlvcFS8M0bX0RaA==} + dev: false + + /@lit-labs/ssr-dom-shim@1.1.2: + resolution: {integrity: sha512-jnOD+/+dSrfTWYfSXBXlo5l5f0q1UuJo3tkbMDCYA2lKUYq79jaxqtGEvnRoh049nt1vdo1+45RinipU6FGY2g==} + dev: false + + /@lit-labs/ssr-dom-shim@1.2.0: + resolution: {integrity: sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==} + dev: false + + /@lit/context@1.1.0: + resolution: {integrity: sha512-fCyv4dsH05wCNm3AKbB+PdYbXGJd/XT8OOwo4hVmD4COq5wOWJlQreGAMDvmHZ7osqxuu06Y4nmP6ooXpN7ErA==} + dependencies: + '@lit/reactive-element': 2.0.1 + dev: false + + /@lit/reactive-element@2.0.1: + resolution: {integrity: sha512-eu50SQXHRthFwWJMp0oAFg95Rvm6MTPjxSXWuvAu7It90WVFLFpNBoIno7XOXSDvVgTrtKnUV4OLJqys2Svn4g==} + dependencies: + '@lit-labs/ssr-dom-shim': 1.1.2 + dev: false + + /@lit/reactive-element@2.0.4: + resolution: {integrity: sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==} + dependencies: + '@lit-labs/ssr-dom-shim': 1.2.0 + dev: false + + /@rollup/rollup-android-arm-eabi@4.8.0: + resolution: {integrity: sha512-zdTObFRoNENrdPpnTNnhOljYIcOX7aI7+7wyrSpPFFIOf/nRdedE6IYsjaBE7tjukphh1tMTojgJ7p3lKY8x6Q==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-android-arm64@4.8.0: + resolution: {integrity: sha512-aiItwP48BiGpMFS9Znjo/xCNQVwTQVcRKkFKsO81m8exrGjHkCBDvm9PHay2kpa8RPnZzzKcD1iQ9KaLY4fPQQ==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-arm64@4.8.0: + resolution: {integrity: sha512-zhNIS+L4ZYkYQUjIQUR6Zl0RXhbbA0huvNIWjmPc2SL0cB1h5Djkcy+RZ3/Bwszfb6vgwUvcVJYD6e6Zkpsi8g==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-x64@4.8.0: + resolution: {integrity: sha512-A/FAHFRNQYrELrb/JHncRWzTTXB2ticiRFztP4ggIUAfa9Up1qfW8aG2w/mN9jNiZ+HB0t0u0jpJgFXG6BfRTA==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.8.0: + resolution: {integrity: sha512-JsidBnh3p2IJJA4/2xOF2puAYqbaczB3elZDT0qHxn362EIoIkq7hrR43Xa8RisgI6/WPfvb2umbGsuvf7E37A==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.8.0: + resolution: {integrity: sha512-hBNCnqw3EVCkaPB0Oqd24bv8SklETptQWcJz06kb9OtiShn9jK1VuTgi7o4zPSt6rNGWQOTDEAccbk0OqJmS+g==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.8.0: + resolution: {integrity: sha512-Fw9ChYfJPdltvi9ALJ9wzdCdxGw4wtq4t1qY028b2O7GwB5qLNSGtqMsAel1lfWTZvf4b6/+4HKp0GlSYg0ahA==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.8.0: + resolution: {integrity: sha512-BH5xIh7tOzS9yBi8dFrCTG8Z6iNIGWGltd3IpTSKp6+pNWWO6qy8eKoRxOtwFbMrid5NZaidLYN6rHh9aB8bEw==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.8.0: + resolution: {integrity: sha512-PmvAj8k6EuWiyLbkNpd6BLv5XeYFpqWuRvRNRl80xVfpGXK/z6KYXmAgbI4ogz7uFiJxCnYcqyvZVD0dgFog7Q==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.8.0: + resolution: {integrity: sha512-mdxnlW2QUzXwY+95TuxZ+CurrhgrPAMveDWI97EQlA9bfhR8tw3Pt7SUlc/eSlCNxlWktpmT//EAA8UfCHOyXg==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.8.0: + resolution: {integrity: sha512-ge7saUz38aesM4MA7Cad8CHo0Fyd1+qTaqoIo+Jtk+ipBi4ATSrHWov9/S4u5pbEQmLjgUjB7BJt+MiKG2kzmA==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.8.0: + resolution: {integrity: sha512-p9E3PZlzurhlsN5h9g7zIP1DnqKXJe8ZUkFwAazqSvHuWfihlIISPxG9hCHCoA+dOOspL/c7ty1eeEVFTE0UTw==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.8.0: + resolution: {integrity: sha512-kb4/auKXkYKqlUYTE8s40FcJIj5soOyRLHKd4ugR0dCq0G2EfcF54eYcfQiGkHzjidZ40daB4ulsFdtqNKZtBg==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@types/trusted-types@2.0.5: + resolution: {integrity: sha512-I3pkr8j/6tmQtKV/ZzHtuaqYSQvyjGRKH4go60Rr0IDLlFxuRT5V32uvB1mecM5G1EVAUyF/4r4QZ1GHgz+mxA==} + dev: false + + /dayjs@1.11.10: + resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} + dev: false + + /emoji-mart@5.5.2: + resolution: {integrity: sha512-Sqc/nso4cjxhOwWJsp9xkVm8OF5c+mJLZJFoFfzRuKO+yWiN7K8c96xmtughYb0d/fZ8UC6cLIQ/p4BR6Pv3/A==} + dev: false + + /esbuild@0.19.9: + resolution: {integrity: sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.19.9 + '@esbuild/android-arm64': 0.19.9 + '@esbuild/android-x64': 0.19.9 + '@esbuild/darwin-arm64': 0.19.9 + '@esbuild/darwin-x64': 0.19.9 + '@esbuild/freebsd-arm64': 0.19.9 + '@esbuild/freebsd-x64': 0.19.9 + '@esbuild/linux-arm': 0.19.9 + '@esbuild/linux-arm64': 0.19.9 + '@esbuild/linux-ia32': 0.19.9 + '@esbuild/linux-loong64': 0.19.9 + '@esbuild/linux-mips64el': 0.19.9 + '@esbuild/linux-ppc64': 0.19.9 + '@esbuild/linux-riscv64': 0.19.9 + '@esbuild/linux-s390x': 0.19.9 + '@esbuild/linux-x64': 0.19.9 + '@esbuild/netbsd-x64': 0.19.9 + '@esbuild/openbsd-x64': 0.19.9 + '@esbuild/sunos-x64': 0.19.9 + '@esbuild/win32-arm64': 0.19.9 + '@esbuild/win32-ia32': 0.19.9 + '@esbuild/win32-x64': 0.19.9 + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /javascript-time-ago@2.5.9: + resolution: {integrity: sha512-pQ8mNco/9g9TqWXWWjP0EWl6i/lAQScOyEeXy5AB+f7MfLSdgyV9BJhiOD1zrIac/lrxPYOWNbyl/IW8CW5n0A==} + dependencies: + relative-time-format: 1.1.6 + dev: false + + /lit-element@4.0.4: + resolution: {integrity: sha512-98CvgulX6eCPs6TyAIQoJZBCQPo80rgXR+dVBs61cstJXqtI+USQZAbA4gFHh6L/mxBx9MrgPLHLsUgDUHAcCQ==} + dependencies: + '@lit-labs/ssr-dom-shim': 1.2.0 + '@lit/reactive-element': 2.0.4 + lit-html: 3.1.2 + dev: false + + /lit-html@3.1.2: + resolution: {integrity: sha512-3OBZSUrPnAHoKJ9AMjRL/m01YJxQMf+TMHanNtTHG68ubjnZxK0RFl102DPzsw4mWnHibfZIBJm3LWCZ/LmMvg==} + dependencies: + '@types/trusted-types': 2.0.5 + dev: false + + /lit@3.1.2: + resolution: {integrity: sha512-VZx5iAyMtX7CV4K8iTLdCkMaYZ7ipjJZ0JcSdJ0zIdGxxyurjIn7yuuSxNBD7QmjvcNJwr0JS4cAdAtsy7gZ6w==} + dependencies: + '@lit/reactive-element': 2.0.4 + lit-element: 4.0.4 + lit-html: 3.1.2 + dev: false + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /postcss@8.4.35: + resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /relative-time-format@1.1.6: + resolution: {integrity: sha512-aCv3juQw4hT1/P/OrVltKWLlp15eW1GRcwP1XdxHrPdZE9MtgqFpegjnTjLhi2m2WI9MT/hQQtE+tjEWG1hgkQ==} + dev: false + + /rollup@4.8.0: + resolution: {integrity: sha512-NpsklK2fach5CdI+PScmlE5R4Ao/FSWtF7LkoIrHDxPACY/xshNasPsbpG0VVHxUTbf74tJbVT4PrP8JsJ6ZDA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.8.0 + '@rollup/rollup-android-arm64': 4.8.0 + '@rollup/rollup-darwin-arm64': 4.8.0 + '@rollup/rollup-darwin-x64': 4.8.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.8.0 + '@rollup/rollup-linux-arm64-gnu': 4.8.0 + '@rollup/rollup-linux-arm64-musl': 4.8.0 + '@rollup/rollup-linux-riscv64-gnu': 4.8.0 + '@rollup/rollup-linux-x64-gnu': 4.8.0 + '@rollup/rollup-linux-x64-musl': 4.8.0 + '@rollup/rollup-win32-arm64-msvc': 4.8.0 + '@rollup/rollup-win32-ia32-msvc': 4.8.0 + '@rollup/rollup-win32-x64-msvc': 4.8.0 + fsevents: 2.3.3 + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /vite@5.1.4: + resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.19.9 + postcss: 8.4.35 + rollup: 4.8.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true diff --git a/packages/comment-widget/src/avatar/avatar-policy.ts b/packages/comment-widget/src/avatar/avatar-policy.ts new file mode 100644 index 0000000..695a487 --- /dev/null +++ b/packages/comment-widget/src/avatar/avatar-policy.ts @@ -0,0 +1,99 @@ +import type { CommentVo, ReplyVo } from '@halo-dev/api-client'; +import { getAvatarProvider } from './providers'; + +abstract class AvatarPolicy { + abstract applyCommentPolicy( + comment: CommentVo | undefined + ): string | undefined; + abstract applyReplyPolicy(reply: ReplyVo | undefined): string | undefined; +} + +let policyInstance: AvatarPolicy | undefined; +const emailKind = 'Email'; +const emailHash = 'email-hash'; + +class AnonymousUserPolicy extends AvatarPolicy { + applyCommentPolicy(comment: CommentVo | undefined): string | undefined { + const avatarProvider = getAvatarProvider(); + const isAnonymous = comment?.owner.kind === emailKind; + if (isAnonymous) { + return avatarProvider?.getAvatarSrc( + comment?.spec.owner.annotations?.[emailHash] + ); + } + return comment?.owner.avatar; + } + applyReplyPolicy(reply: ReplyVo | undefined): string | undefined { + const avatarProvider = getAvatarProvider(); + const isAnonymous = reply?.owner.kind === emailKind; + if (isAnonymous) { + return avatarProvider?.getAvatarSrc( + reply?.spec.owner.annotations?.[emailHash] + ); + } + return reply?.owner.avatar; + } +} + +class AllUserPolicy extends AvatarPolicy { + applyCommentPolicy(comment: CommentVo | undefined): string | undefined { + const avatarProvider = getAvatarProvider(); + return avatarProvider?.getAvatarSrc( + comment?.spec.owner.annotations?.[emailHash] + ); + } + applyReplyPolicy(reply: ReplyVo | undefined): string | undefined { + const avatarProvider = getAvatarProvider(); + return avatarProvider?.getAvatarSrc( + reply?.spec.owner.annotations?.[emailHash] + ); + } +} + +class NoAvatarUserPolicy extends AvatarPolicy { + applyCommentPolicy(comment: CommentVo | undefined): string | undefined { + const avatarProvider = getAvatarProvider(); + const isAnonymous = comment?.owner.kind === emailKind; + const avatar = comment?.owner.avatar; + if (isAnonymous || !avatar) { + return avatarProvider?.getAvatarSrc( + comment?.spec.owner.annotations?.[emailHash] + ); + } + return avatar; + } + applyReplyPolicy(reply: ReplyVo | undefined): string | undefined { + const avatarProvider = getAvatarProvider(); + const isAnonymous = reply?.owner.kind === emailKind; + const avatar = reply?.owner.avatar; + if (isAnonymous || !avatar) { + return avatarProvider?.getAvatarSrc( + reply?.spec.owner.annotations?.[emailHash] + ); + } + return avatar; + } +} + +enum AvatarPolicyEnum { + ANONYMOUS_USER_POLICY = 'anonymousUser', + ALL_USER_POLICY = 'allUser', + NO_AVATAR_USER_POLICY = 'noAvatarUser', +} + +function setPolicyInstance(nPolicyInstance: AvatarPolicy | undefined) { + policyInstance = nPolicyInstance; +} + +function getPolicyInstance(): AvatarPolicy | undefined { + return policyInstance; +} + +export { + AnonymousUserPolicy, + AllUserPolicy, + NoAvatarUserPolicy, + AvatarPolicyEnum, + setPolicyInstance, + getPolicyInstance, +}; diff --git a/packages/comment-widget/src/avatar/providers/avatar-provider.ts b/packages/comment-widget/src/avatar/providers/avatar-provider.ts new file mode 100644 index 0000000..6b38042 --- /dev/null +++ b/packages/comment-widget/src/avatar/providers/avatar-provider.ts @@ -0,0 +1,23 @@ +export default abstract class AvatarProvider { + private readonly _name: string; + private _url: string; + + constructor(name: string, url: string) { + this._name = name; + this._url = url; + } + + get url(): string { + return this._url; + } + + set url(value: string) { + this._url = value; + } + + get name(): string { + return this._name; + } + + abstract getAvatarSrc(emailHash: string | undefined): string; +} diff --git a/packages/comment-widget/src/avatar/providers/gravatar.ts b/packages/comment-widget/src/avatar/providers/gravatar.ts new file mode 100644 index 0000000..08da722 --- /dev/null +++ b/packages/comment-widget/src/avatar/providers/gravatar.ts @@ -0,0 +1,9 @@ +import AvatarProvider from './avatar-provider'; + +class Gravatar extends AvatarProvider { + override getAvatarSrc(emailHash: string | undefined): string { + return `${this.url}/avatar/${emailHash}`; + } +} + +export default new Gravatar('Gravatar', 'https://gravatar.com'); diff --git a/packages/comment-widget/src/avatar/providers/index.ts b/packages/comment-widget/src/avatar/providers/index.ts new file mode 100644 index 0000000..c2151de --- /dev/null +++ b/packages/comment-widget/src/avatar/providers/index.ts @@ -0,0 +1,24 @@ +import type AvatarProvider from './avatar-provider'; +import Gravatar from './gravatar'; + +let avatarProvider: AvatarProvider | undefined; + +enum AvatarProviderEnum { + GRAVATAR = 'gravatar', +} + +export function setAvatarProvider(provider: string, mirrorUrl?: string) { + switch (provider) { + case AvatarProviderEnum.GRAVATAR: + if (mirrorUrl) { + Gravatar.url = mirrorUrl; + } + avatarProvider = Gravatar; + break; + default: + } +} + +export function getAvatarProvider() { + return avatarProvider; +} diff --git a/packages/comment-widget/src/base-comment-item.ts b/packages/comment-widget/src/base-comment-item.ts new file mode 100644 index 0000000..90c6b23 --- /dev/null +++ b/packages/comment-widget/src/base-comment-item.ts @@ -0,0 +1,134 @@ +import './user-avatar'; +import { msg } from '@lit/localize'; +import { css, html, LitElement } from 'lit'; +import { property, state } from 'lit/decorators.js'; +import baseStyles from './styles/base'; +import { formatDate, timeAgo } from './utils/date'; +import './commenter-ua-bar'; +import { consume } from '@lit/context'; +import { configMapDataContext } from './context'; +import type { ConfigMapData } from './types'; +import './comment-content'; +import { ifDefined } from 'lit/directives/if-defined.js'; +import { when } from 'lit/directives/when.js'; + +export class BaseCommentItem extends LitElement { + @property({ type: String }) + userAvatar: string | undefined; + + @property({ type: String }) + userDisplayName: string | undefined; + + @property({ type: String }) + userWebsite: string | undefined; + + @property({ type: String }) + creationTime: string | undefined; + + @property({ type: Boolean }) + approved: boolean | undefined; + + @property({ type: Boolean }) + breath: boolean | undefined; + + @property({ type: String }) + content = ''; + + @property({ type: String }) + ua: string | undefined; + + @property({ type: Boolean }) + private: boolean | undefined; + + @consume({ context: configMapDataContext }) + @state() + configMapData: ConfigMapData | undefined; + + override render() { + return html`
+
+ +
+
+
+ ${when( + this.userWebsite, + () => html` + + ${this.userDisplayName} + + `, + () => html` + ${this.userDisplayName} + ` + )} + + ${when( + this.private && this.configMapData?.basic.showPrivateCommentBadge, + () => html`
+ + ${msg('Private')} +
` + )} + + ${when(this.ua && this.configMapData?.basic.showCommenterDevice, () => html``)} + + + + ${when(!this.approved, () => html`
${msg('Reviewing')}
`)} +
+ +
+ +
+ +
+ + +
+
`; + } + + static override styles = [ + ...baseStyles, + css` + .animate-breath { + animation: breath 1s ease-in-out infinite; + } + + @keyframes breath { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.02); + } + 100% { + transform: scale(1); + } + } + + @unocss-placeholder; + `, + ]; +} + +customElements.get('base-comment-item') || + customElements.define('base-comment-item', BaseCommentItem); + +declare global { + interface HTMLElementTagNameMap { + 'base-comment-item': BaseCommentItem; + } +} diff --git a/packages/comment-widget/src/base-form.ts b/packages/comment-widget/src/base-form.ts new file mode 100644 index 0000000..b54aecd --- /dev/null +++ b/packages/comment-widget/src/base-form.ts @@ -0,0 +1,364 @@ +import type { User } from '@halo-dev/api-client'; +import { consume } from '@lit/context'; +import { debounce } from 'es-toolkit'; +import { css, html, LitElement } from 'lit'; +import { property, state } from 'lit/decorators.js'; +import { createRef, type Ref, ref } from 'lit/directives/ref.js'; +import { + allowAnonymousCommentsContext, + baseUrlContext, + configMapDataContext, + currentUserContext, + groupContext, + kindContext, + nameContext, + toastContext, +} from './context'; +import './icons/icon-loading'; +import { msg } from '@lit/localize'; +import type { ToastManager } from './lit-toast'; +import baseStyles from './styles/base'; +import type { ConfigMapData } from './types'; +import './comment-editor'; +import { when } from 'lit/directives/when.js'; +import { ofetch } from 'ofetch'; +import type { CommentEditor } from './comment-editor'; +import { cleanHtml } from './utils/html'; +import './base-tooltip'; + +export class BaseForm extends LitElement { + @consume({ context: baseUrlContext }) + @state() + baseUrl = ''; + + @consume({ context: currentUserContext, subscribe: true }) + @state() + currentUser: User | undefined; + + @consume({ context: configMapDataContext }) + @state() + configMapData: ConfigMapData | undefined; + + @consume({ context: allowAnonymousCommentsContext, subscribe: true }) + @state() + allowAnonymousComments = false; + + @consume({ context: groupContext }) + @state() + group = ''; + + @consume({ context: kindContext }) + @state() + kind = ''; + + @consume({ context: nameContext }) + @state() + name = ''; + + @property({ type: String }) + @state() + captcha = ''; + + @property({ type: Boolean }) + submitting = false; + + @consume({ context: toastContext, subscribe: true }) + @state() + toastManager: ToastManager | undefined; + + @property({ type: Boolean }) + hidePrivateCheckbox = false; + + textareaRef: Ref = createRef(); + + editorRef: Ref = createRef(); + + get customAccount() { + return JSON.parse( + localStorage.getItem('halo-comment-custom-account') || '{}' + ); + } + + get parentDomId() { + return `#comment-${[this.group?.replaceAll('.', '-'), this.kind, this.name] + .join('-') + .replaceAll(/-+/g, '-')}`; + } + + get loginUrl() { + return `/login?redirect_uri=${encodeURIComponent( + window.location.pathname + this.parentDomId + )}`; + } + + get showCaptcha() { + return ( + this.configMapData?.security.captcha.anonymousCommentCaptcha && + !this.currentUser && + this.allowAnonymousComments + ); + } + + override updated(changedProperties: Map) { + if ( + changedProperties.has('configMapData') || + changedProperties.has('currentUser') || + changedProperties.has('allowAnonymousComments') + ) { + if (this.showCaptcha) { + this.handleFetchCaptcha(); + } + } + } + + async handleFetchCaptcha() { + if (!this.showCaptcha) { + return; + } + + try { + const data = await ofetch( + `/apis/api.commentwidget.halo.run/v1alpha1/captcha/-/generate`, + { + parseResponse: (txt) => txt, + } + ); + + this.captcha = data; + } catch (error) { + console.error(error); + this.toastManager?.error(msg('Failed to obtain verification code')); + } + } + + handleOpenLoginPage() { + window.location.href = this.loginUrl; + } + + async handleLogout() { + if ( + window.confirm( + msg( + 'Click OK to jump to the logout page, Please make sure the content being edited has been saved.' + ) + ) + ) { + try { + window.location.href = `/logout?redirect_uri=${encodeURIComponent( + window.location.pathname + this.parentDomId + )}`; + } catch (error) { + console.error('Failed to logout', error); + } + } + } + + renderAccountInfo() { + return html``; + } + + onKeydown(e: KeyboardEvent) { + if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { + const form = this.shadowRoot?.querySelector('form'); + e.preventDefault(); + form?.requestSubmit(); + } + } + + override connectedCallback(): void { + super.connectedCallback(); + this.addEventListener('keydown', this.onKeydown); + } + + override disconnectedCallback(): void { + super.disconnectedCallback(); + this.removeEventListener('keydown', this.onKeydown); + } + + override render() { + return html` +
+ + + ${when( + !this.currentUser && this.allowAnonymousComments, + () => html` + + ` + )} + + +
+ `; + } + + private debouncedSubmit = debounce((data: Record) => { + const content = cleanHtml(this.editorRef.value?.editor?.getHTML()); + const characterCount = + this.editorRef.value?.editor?.storage.characterCount.characters(); + + if (!characterCount) { + this.toastManager?.warn(msg('Please enter content')); + this.editorRef.value?.setFocus(); + return; + } + + const event = new CustomEvent('submit', { + detail: { + ...data, + content, + hidden: data.hidden === 'on', + }, + }); + this.dispatchEvent(event); + }, 300); + + onSubmit(e: Event) { + e.preventDefault(); + const form = e.target as HTMLFormElement; + const formData = new FormData(form); + const data = Object.fromEntries(formData.entries()); + + // store account info + localStorage.setItem( + 'halo-comment-custom-account', + JSON.stringify({ + displayName: data.displayName, + email: data.email, + website: data.website, + }) + ); + + this.debouncedSubmit(data); + } + + resetForm() { + const form = this.shadowRoot?.querySelector('form'); + form?.reset(); + this.editorRef.value?.reset(); + } + + setFocus() { + this.textareaRef.value?.focus(); + this.editorRef.value?.setFocus(); + } + + static override styles = [ + ...baseStyles, + css` + @unocss-placeholder; + `, + ]; +} + +customElements.get('base-form') || customElements.define('base-form', BaseForm); + +declare global { + interface HTMLElementTagNameMap { + 'base-form': BaseForm; + } +} diff --git a/packages/comment-widget/src/base-tooltip.ts b/packages/comment-widget/src/base-tooltip.ts new file mode 100644 index 0000000..3fe0b21 --- /dev/null +++ b/packages/comment-widget/src/base-tooltip.ts @@ -0,0 +1,161 @@ +import { + autoUpdate, + computePosition, + flip, + offset, + shift, +} from '@floating-ui/dom'; +import type { TemplateResult } from 'lit'; +import { css, html, LitElement } from 'lit'; +import { property } from 'lit/decorators.js'; +import baseStyles from './styles/base'; + +/** + * A simple tooltip component based on Lit and floating-ui. + * + * @example + * ```html + * + * + * + * ``` + */ +export class BaseTooltip extends LitElement { + /** + * The content to display in the tooltip. + */ + @property({ type: String }) + content: string | TemplateResult = ''; + + private tooltipEl?: HTMLElement; + private cleanupFn?: () => void; + + override connectedCallback(): void { + super.connectedCallback(); + this.addEventListener('mouseenter', this._showTooltip); + this.addEventListener('mouseleave', this._hideTooltip); + } + + override disconnectedCallback(): void { + super.disconnectedCallback(); + this.removeEventListener('mouseenter', this._showTooltip); + this.removeEventListener('mouseleave', this._hideTooltip); + + // Clean up positioning if needed + if (this.cleanupFn) { + this.cleanupFn(); + this.cleanupFn = undefined; + } + } + + override firstUpdated(): void { + // Get tooltip element after the component is rendered + this.tooltipEl = this.shadowRoot?.querySelector('.tooltip') as HTMLElement; + } + + private _showTooltip = (): void => { + if (!this.tooltipEl) return; + + this.tooltipEl.classList.add('show'); + this._updatePosition(); + }; + + private _hideTooltip = (): void => { + if (!this.tooltipEl) return; + + this.tooltipEl.classList.remove('show'); + + // Clean up positioning + if (this.cleanupFn) { + this.cleanupFn(); + this.cleanupFn = undefined; + } + }; + + private _updatePosition(): void { + if (!this.tooltipEl) return; + + const floating = this.tooltipEl; + + // Clean up previous positioning if any + if (this.cleanupFn) { + this.cleanupFn(); + } + + // Use autoUpdate to reposition on scroll/resize + this.cleanupFn = autoUpdate(this, floating, async () => { + // Calculate the position + const middlewares = [ + offset(8), // 8px distance from trigger + flip(), // flip to other side if needed + shift({ padding: 5 }), // shift to keep in viewport + ]; + + // Calculate position + const { x, y } = await computePosition(this, floating, { + placement: 'top', // Default placement is top + middleware: middlewares, + }); + + // Position the tooltip + Object.assign(floating.style, { + left: `${x}px`, + top: `${y}px`, + }); + }); + } + + override render() { + return html` + +
+ ${this.content} +
+ `; + } + + static override styles = [ + ...baseStyles, + css` + :host { + display: inline-block; + } + + ::slotted(*) { + display: inline-block; + } + + .tooltip { + position: absolute; + background: rgba(0, 0, 0, 0.8); + color: white; + padding: 5px 10px; + border-radius: 4px; + font-size: 0.875em; + max-width: 300px; + width: auto; + z-index: 10; + opacity: 0; + pointer-events: none; + transition: opacity 0.2s; + top: 0; + left: 0; + white-space: normal; + word-wrap: break-word; + } + + .tooltip.show { + opacity: 1; + } + `, + ]; +} + +customElements.get('base-tooltip') || + customElements.define('base-tooltip', BaseTooltip); + +declare global { + interface HTMLElementTagNameMap { + 'base-tooltip': BaseTooltip; + } +} diff --git a/packages/comment-widget/src/comment-content.ts b/packages/comment-widget/src/comment-content.ts new file mode 100644 index 0000000..9e8367f --- /dev/null +++ b/packages/comment-widget/src/comment-content.ts @@ -0,0 +1,83 @@ +import { css, html, LitElement, type PropertyValues, unsafeCSS } from 'lit'; +import { property } from 'lit/decorators.js'; +import { unsafeHTML } from 'lit/directives/unsafe-html.js'; +import baseStyles from './styles/base'; +import contentStyles from './styles/content.css?inline'; +import { cleanHtml } from './utils/html'; + +export class CommentContent extends LitElement { + @property({ type: String }) + content: string = ''; + + protected override firstUpdated(_changedProperties: PropertyValues) { + super.firstUpdated(_changedProperties); + const codeElements = this.shadowRoot?.querySelectorAll('pre>code'); + if (!codeElements?.length) return; + + Promise.all( + Array.from(codeElements).map(async (codeblock) => { + const lang = + this.extractLanguageFromCodeElement(codeblock) || 'plaintext'; + const content = codeblock.textContent || ''; + + try { + const { codeToHtml } = await import('shiki/bundle/full'); + + const html = await codeToHtml(content, { + lang, + theme: 'github-dark', + }); + + if (codeblock.parentElement) { + codeblock.parentElement.outerHTML = html; + } + } catch (error) { + console.error('Failed to highlight code:', error); + } + }) + ); + } + + private extractLanguageFromCodeElement(codeElement: Element): string | null { + const supportedPrefixes = ['language-', 'lang-']; + + const langClass = Array.from(codeElement.classList).find((className) => + supportedPrefixes.some((prefix) => className.startsWith(prefix)) + ); + + if (langClass) { + const prefix = supportedPrefixes.find((p) => langClass.startsWith(p)); + return prefix ? langClass.substring(prefix.length) : null; + } + + return null; + } + + protected override render() { + return html` +
${unsafeHTML(cleanHtml(this.content))}
+ `; + } + + static override styles = [ + ...baseStyles, + unsafeCSS(contentStyles), + css` + :host { + display: block; + width: 100%; + } + + @unocss-placeholder; + `, + ]; +} + +customElements.get('comment-content') || + customElements.define('comment-content', CommentContent); + +declare global { + interface HTMLElementTagNameMap { + 'comment-content': CommentContent; + } +} diff --git a/packages/comment-widget/src/comment-editor-skeleton.ts b/packages/comment-widget/src/comment-editor-skeleton.ts new file mode 100644 index 0000000..5dbbe6b --- /dev/null +++ b/packages/comment-widget/src/comment-editor-skeleton.ts @@ -0,0 +1,47 @@ +import { css, html, LitElement } from 'lit'; +import { repeat } from 'lit/directives/repeat.js'; + +export class CommentEditorSkeleton extends LitElement { + protected override render() { + return html`
+
+
+
+
+ ${repeat( + Array(8), + () => html` +
+
+
+ ` + )} +
+
`; + } + + static override styles = [ + css` + :host { + display: block; + width: 100%; + } + + @unocss-placeholder; + `, + ]; +} + +customElements.get('comment-editor-skeleton') || + customElements.define('comment-editor-skeleton', CommentEditorSkeleton); + +declare global { + interface HTMLElementTagNameMap { + 'comment-editor-skeleton': CommentEditorSkeleton; + } +} diff --git a/packages/comment-widget/src/comment-editor.ts b/packages/comment-widget/src/comment-editor.ts new file mode 100644 index 0000000..4f9357d --- /dev/null +++ b/packages/comment-widget/src/comment-editor.ts @@ -0,0 +1,268 @@ +import { msg } from '@lit/localize'; +import type { Editor } from '@tiptap/core'; +import { css, html, LitElement, type PropertyValues, unsafeCSS } from 'lit'; +import { state } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; +import './emoji-button'; +import contentStyles from './styles/content.css?inline'; +import './comment-editor-skeleton'; +import { property } from 'lit/decorators.js'; +import { ifDefined } from 'lit/directives/if-defined.js'; +import { when } from 'lit/directives/when.js'; +import baseStyles from './styles/base'; +import { cleanHtml } from './utils/html'; + +interface ActionItem { + name?: string; + displayName?: () => string; + type: 'action' | 'separator'; + icon?: string; + run?: (editor?: Editor) => void; +} + +const actionItems: ActionItem[] = [ + { + name: 'bold', + displayName: () => msg('Bold'), + type: 'action', + icon: 'i-mingcute-bold-line', + run: (editor?: Editor) => editor?.chain().focus().toggleBold().run(), + }, + { + name: 'italic', + displayName: () => msg('Italic'), + type: 'action', + icon: 'i-mingcute-italic-line', + run: (editor?: Editor) => editor?.chain().focus().toggleItalic().run(), + }, + { + name: 'underline', + displayName: () => msg('Underline'), + type: 'action', + icon: 'i-mingcute-underline-line', + run: (editor?: Editor) => editor?.chain().focus().toggleUnderline().run(), + }, + { + name: 'strike', + displayName: () => msg('Strike'), + type: 'action', + icon: 'i-mingcute-strikethrough-line', + run: (editor?: Editor) => editor?.chain().focus().toggleStrike().run(), + }, + { + name: 'code', + displayName: () => msg('Code'), + type: 'action', + icon: 'i-mingcute-braces-line', + run: (editor?: Editor) => editor?.chain().focus().toggleCode().run(), + }, + { + type: 'separator', + }, + { + name: 'blockquote', + displayName: () => msg('Blockquote'), + type: 'action', + icon: 'i-mingcute-quote-left-line', + run: (editor?: Editor) => editor?.chain().focus().toggleBlockquote().run(), + }, + { + name: 'codeBlock', + displayName: () => msg('Code Block'), + type: 'action', + icon: 'i-mingcute-code-line', + run: (editor?: Editor) => editor?.chain().focus().toggleCodeBlock().run(), + }, +]; + +export class CommentEditor extends LitElement { + @property({ type: String }) + placeholder: string | undefined; + + @property({ type: Boolean, attribute: 'keep-alive' }) + keepAlive = false; + + @state() + editor: Editor | undefined; + + @state() + loading = true; + + protected override firstUpdated(_changedProperties: PropertyValues): void { + super.firstUpdated(_changedProperties); + this.createEditor(); + } + + async createEditor() { + const { Editor } = await import('@tiptap/core'); + const { Placeholder } = await import('@tiptap/extensions'); + const { StarterKit } = await import('@tiptap/starter-kit'); + const { CodeBlockShiki } = await import( + 'tiptap-extension-code-block-shiki' + ); + const { CharacterCount } = await import('@tiptap/extensions'); + + this.loading = false; + + this.editor = new Editor({ + element: this.shadowRoot?.getElementById('editor-container'), + extensions: [ + StarterKit.configure({ + heading: false, + link: { + openOnClick: false, + }, + codeBlock: false, + }), + + Placeholder.configure({ + placeholder: this.placeholder || msg('Write a comment'), + }), + + CodeBlockShiki.configure({ + defaultTheme: 'github-dark', + }), + + CharacterCount, + ], + onUpdate: () => { + this.requestUpdate(); + }, + onSelectionUpdate: () => { + this.requestUpdate(); + }, + onTransaction: () => { + this.requestUpdate(); + }, + }); + + this.editor.on('update', () => { + this.dispatchEvent( + new CustomEvent('update', { + detail: { + content: cleanHtml(this.editor?.getHTML()), + characterCount: this.editor?.storage.characterCount.characters(), + }, + }) + ); + }); + } + + override disconnectedCallback(): void { + if (!this.keepAlive) { + this.editor?.destroy(); + this.editor = undefined; + } + super.disconnectedCallback(); + } + + setFocus() { + setTimeout(() => { + this.editor?.chain().focus().run(); + }, 100); + } + + reset() { + this.editor?.commands.setContent(''); + } + + onEmojiSelect(e: CustomEvent) { + this.editor?.chain().focus().insertContent(e.detail.native).run(); + } + + protected override render() { + return html` + ${when(this.loading, () => html``)} +
+
+
    + ${repeat(actionItems, (item) => + this.renderActionItem(item, this.editor) + )} + ${this.renderActionItem({ type: 'separator' })} +
  • + +
  • +
+
`; + } + + private renderActionItem(item: ActionItem, editor?: Editor) { + if (item.type === 'separator') { + return html``; + } + + if (item.type === 'action') { + const isActive = item.name ? editor?.isActive(item.name) : false; + return html` +
  • +
    item.run?.(editor)} + role="button" + class="size-7 hover:bg-muted-3 active:bg-muted-2 ${isActive ? 'bg-muted-3 text-text-1' : 'text-text-3 hover:text-text-1'} rounded-base flex items-center justify-center cursor-pointer transition-all" + > + +
    +
  • + `; + } + + return; + } + + static override styles = [ + ...baseStyles, + unsafeCSS(contentStyles), + css` + + :host { + display: block; + width: 100%; + } + + .tiptap { + outline: none; + border: none; + } + + .tiptap p { + padding: 0; + margin-bottom: 10px; + } + + .tiptap p.is-editor-empty:first-child::before { + content: attr(data-placeholder); + float: left; + height: 0; + pointer-events: none; + color: var(--halo-cw-text-3-color, #475569); + } + + .tiptap code br { + display: block; + } + + @unocss-placeholder; + `, + ]; +} + +customElements.get('comment-editor') || + customElements.define('comment-editor', CommentEditor); + +declare global { + interface HTMLElementTagNameMap { + 'comment-editor': CommentEditor; + } +} diff --git a/packages/comment-widget/src/comment-form.ts b/packages/comment-widget/src/comment-form.ts new file mode 100644 index 0000000..8750866 --- /dev/null +++ b/packages/comment-widget/src/comment-form.ts @@ -0,0 +1,183 @@ +import type { Comment, CommentRequest, User } from '@halo-dev/api-client'; +import { consume } from '@lit/context'; +import { html, LitElement } from 'lit'; +import { state } from 'lit/decorators.js'; +import { createRef, type Ref, ref } from 'lit/directives/ref.js'; +import './base-form'; +import { msg } from '@lit/localize'; +import { FetchError, type FetchResponse, ofetch } from 'ofetch'; +import type { BaseForm } from './base-form'; +import { + allowAnonymousCommentsContext, + baseUrlContext, + currentUserContext, + groupContext, + kindContext, + nameContext, + toastContext, + versionContext, +} from './context'; +import type { ToastManager } from './lit-toast'; +import type { ProblemDetail } from './types'; +import { + type CaptchaRequiredResponse, + getCaptchaCodeHeader, + isRequireCaptcha, +} from './utils/captcha'; + +export class CommentForm extends LitElement { + @consume({ context: baseUrlContext }) + @state() + baseUrl = ''; + + @consume({ context: currentUserContext, subscribe: true }) + @state() + currentUser: User | undefined; + + @consume({ context: groupContext }) + @state() + group = ''; + + @consume({ context: kindContext }) + @state() + kind = ''; + + @consume({ context: nameContext }) + @state() + name = ''; + + @consume({ context: versionContext }) + @state() + version = 'v1alpha1'; + + @consume({ context: allowAnonymousCommentsContext, subscribe: true }) + @state() + allowAnonymousComments = false; + + @consume({ context: toastContext, subscribe: true }) + @state() + toastManager: ToastManager | undefined; + + @state() + submitting = false; + + @state() + captcha = ''; + + baseFormRef: Ref = createRef(); + + override render() { + return html` `; + } + + async onSubmit(e: CustomEvent) { + e.preventDefault(); + + this.submitting = true; + + const data = e.detail; + + const { displayName, email, website, content, hidden } = data || {}; + + const commentRequest: CommentRequest = { + raw: content, + content: content, + // TODO: support user input + allowNotification: true, + hidden: hidden || false, + subjectRef: { + group: this.group, + kind: this.kind, + name: this.name, + version: this.version, + }, + }; + + if (!this.currentUser && !this.allowAnonymousComments) { + this.toastManager?.warn(msg('Please login first')); + this.submitting = false; + return; + } + + if (!this.currentUser && this.allowAnonymousComments) { + if (!displayName || !email) { + this.toastManager?.warn( + msg('Please log in or complete the information first') + ); + this.submitting = false; + return; + } else { + commentRequest.owner = { + displayName: displayName, + email: email, + website: website, + }; + } + } + + try { + const newComment = await ofetch( + `${this.baseUrl}/apis/api.halo.run/v1alpha1/comments`, + { + method: 'POST', + headers: { + ...getCaptchaCodeHeader(data.captchaCode), + }, + body: commentRequest, + } + ); + + this.baseFormRef.value?.handleFetchCaptcha(); + + if (newComment.spec.approved) { + this.toastManager?.success(msg('Comment submitted successfully')); + } else { + this.toastManager?.success( + msg('Comment submitted successfully, pending review') + ); + } + + window.dispatchEvent(new CustomEvent('halo:comment:created')); + + this.baseFormRef.value?.resetForm(); + } catch (error) { + if (error instanceof FetchError) { + if ( + isRequireCaptcha( + error.response as FetchResponse + ) + ) { + const { captcha, detail } = + error.data as unknown as CaptchaRequiredResponse; + this.captcha = captcha; + this.toastManager?.warn(detail); + return; + } + + const problemDetail = error.data as unknown as ProblemDetail; + this.toastManager?.error( + [problemDetail?.title, problemDetail?.detail].join(' - ') || + msg('Comment failed, please try again later') + ); + return; + } + this.toastManager?.error(msg('Comment failed, please try again later')); + } finally { + this.submitting = false; + } + } +} + +customElements.get('comment-form') || + customElements.define('comment-form', CommentForm); + +declare global { + interface HTMLElementTagNameMap { + 'comment-form': CommentForm; + } +} diff --git a/packages/comment-widget/src/comment-item.ts b/packages/comment-widget/src/comment-item.ts new file mode 100644 index 0000000..80053fc --- /dev/null +++ b/packages/comment-widget/src/comment-item.ts @@ -0,0 +1,213 @@ +import type { CommentVo } from '@halo-dev/api-client'; +import { css, html, LitElement } from 'lit'; +import { property, state } from 'lit/decorators.js'; +import baseStyles from './styles/base'; +import './comment-replies'; +import './user-avatar'; +import './base-comment-item'; +import { consume } from '@lit/context'; +import { msg } from '@lit/localize'; +import { createRef, type Ref, ref } from 'lit/directives/ref.js'; +import { when } from 'lit/directives/when.js'; +import { ofetch } from 'ofetch'; +import { getPolicyInstance } from './avatar/avatar-policy'; +import type { CommentReplies } from './comment-replies'; +import { LS_UPVOTED_COMMENTS_KEY } from './constant'; +import { baseUrlContext, configMapDataContext } from './context'; +import type { ConfigMapData } from './types'; + +export class CommentItem extends LitElement { + @consume({ context: baseUrlContext }) + @state() + baseUrl = ''; + + @property({ type: Object }) + comment: CommentVo | undefined; + + @consume({ context: configMapDataContext }) + @state() + configMapData: ConfigMapData | undefined; + + @state() + showReplies = false; + + @state() + showReplyForm = false; + + @state() + upvoted = false; + + @state() + upvoteCount = 0; + + commentRepliesRef: Ref = createRef(); + + override connectedCallback(): void { + super.connectedCallback(); + this.checkUpvotedStatus(); + + if (this.configMapData?.basic.withReplies) { + this.showReplies = true; + this.showReplyForm = false; + } + + this.upvoteCount = this.comment?.stats.upvote || 0; + } + + checkUpvotedStatus() { + const upvotedComments = JSON.parse( + localStorage.getItem(LS_UPVOTED_COMMENTS_KEY) || '[]' + ); + + if (upvotedComments.includes(this.comment?.metadata.name)) { + this.upvoted = true; + } + } + + async handleUpvote() { + const upvotedComments = JSON.parse( + localStorage.getItem(LS_UPVOTED_COMMENTS_KEY) || '[]' + ); + + if (upvotedComments.includes(this.comment?.metadata.name)) { + return; + } + + const voteRequest = { + name: this.comment?.metadata.name, + plural: 'comments', + group: 'content.halo.run', + }; + + await ofetch(`${this.baseUrl}/apis/api.halo.run/v1alpha1/trackers/upvote`, { + method: 'POST', + body: voteRequest, + }); + + upvotedComments.push(this.comment?.metadata.name); + localStorage.setItem( + LS_UPVOTED_COMMENTS_KEY, + JSON.stringify(upvotedComments) + ); + + this.upvoteCount += 1; + this.upvoted = true; + this.checkUpvotedStatus(); + } + + handleShowReplies() { + this.showReplies = !this.showReplies; + + if (!this.configMapData?.basic.withReplies) { + this.showReplyForm = !this.showReplyForm; + } + } + + onReplyCreated() { + this.commentRepliesRef.value?.fetchReplies(); + this.showReplies = true; + } + + handleToggleReplyForm() { + this.showReplyForm = !this.showReplyForm; + } + + override render() { + return html` + + + ${ + this.configMapData?.basic.withReplies && + this.comment?.status?.visibleReplyCount === 0 + ? '' + : html` + ` + } + + ${when( + this.configMapData?.basic.withReplies, + () => html` + + ` + )} + +
    + ${when( + this.showReplyForm, + () => html` +
    + +
    + ` + )} + ${when( + this.showReplies, + () => html`` + )} +
    +
    `; + } + + static override styles = [ + ...baseStyles, + css` + @unocss-placeholder; + `, + ]; +} + +customElements.get('comment-item') || + customElements.define('comment-item', CommentItem); + +function handleCommentAvatar( + comment: CommentVo | undefined +): string | undefined { + const avatarPolicy = getPolicyInstance(); + if (avatarPolicy === undefined) { + return comment?.owner.avatar; + } + return avatarPolicy.applyCommentPolicy(comment); +} + +declare global { + interface HTMLElementTagNameMap { + 'comment-item': CommentItem; + } +} diff --git a/packages/comment-widget/src/comment-list.ts b/packages/comment-widget/src/comment-list.ts new file mode 100644 index 0000000..c43ed52 --- /dev/null +++ b/packages/comment-widget/src/comment-list.ts @@ -0,0 +1,192 @@ +import { css, html, LitElement } from 'lit'; +import './icons/icon-loading'; +import type { CommentVoList } from '@halo-dev/api-client'; +import { consume } from '@lit/context'; +import { msg } from '@lit/localize'; +import { state } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; +import { + baseUrlContext, + configMapDataContext, + groupContext, + kindContext, + nameContext, + toastContext, + versionContext, +} from './context'; +import type { ToastManager } from './lit-toast'; +import type { ConfigMapData } from './types'; +import './comment-pagination'; +import './comment-item'; +import './loading-block'; +import { when } from 'lit/directives/when.js'; +import { ofetch } from 'ofetch'; +import baseStyles from './styles/base'; + +export class CommentList extends LitElement { + @consume({ context: baseUrlContext }) + @state() + baseUrl = ''; + + @consume({ context: groupContext }) + @state() + group = ''; + + @consume({ context: kindContext }) + @state() + kind = ''; + + @consume({ context: nameContext }) + @state() + name = ''; + + @consume({ context: versionContext }) + @state() + version = ''; + + @consume({ context: configMapDataContext }) + @state() + configMapData: ConfigMapData | undefined; + + @consume({ context: toastContext, subscribe: true }) + @state() + toastManager: ToastManager | undefined; + + @state() + comments: CommentVoList = { + page: 1, + size: 20, + total: 0, + items: [], + first: true, + last: false, + hasNext: false, + hasPrevious: false, + totalPages: 0, + }; + + @state() + loading = false; + + get shouldDisplayPagination() { + if (this.loading) { + return false; + } + + return this.comments.hasNext || this.comments.hasPrevious; + } + + async onPageChange(e: CustomEvent) { + const data = e.detail; + this.comments.page = data.page; + await this.fetchComments({ scrollIntoView: true }); + } + + override connectedCallback(): void { + super.connectedCallback(); + this.fetchComments(); + + // Handle halo:comment:created event, then reload the comment list + window.addEventListener('halo:comment:created', () => { + this.fetchComments({ + page: 1, + scrollIntoView: true, + }); + }); + } + + async fetchComments(options?: { page?: number; scrollIntoView?: boolean }) { + const { page, scrollIntoView } = options || {}; + try { + if (this.comments.items.length === 0) { + this.loading = true; + } + + if (page) { + this.comments.page = page; + } + + const data = await ofetch( + `${this.baseUrl}/apis/api.halo.run/v1alpha1/comments`, + { + query: { + group: this.group, + kind: this.kind, + name: this.name, + page: this.comments.page, + size: this.configMapData?.basic.size || 20, + version: this.version, + withReplies: this.configMapData?.basic.withReplies || false, + replySize: this.configMapData?.basic.replySize || 10, + }, + } + ); + + this.comments = data; + } catch (_error) { + console.error(_error); + this.toastManager?.error( + msg('Failed to load comment list, please try again later') + ); + } finally { + this.loading = false; + + if (scrollIntoView) { + this.scrollIntoView({ + block: 'start', + inline: 'start', + behavior: 'smooth', + }); + } + } + } + + override render() { + return html`${ + this.loading + ? html` ` + : html` +
    +
    + ${msg(html`${this.comments.total} Comments`)} +
    + +
    + ${repeat( + this.comments.items, + (item) => item.metadata.name, + (item) => html` ` + )} +
    +
    + ` + } + ${when( + this.shouldDisplayPagination, + () => html` + ` + )} + `; + } + + static override styles = [ + ...baseStyles, + css` + @unocss-placeholder; + `, + ]; +} + +customElements.get('comment-list') || + customElements.define('comment-list', CommentList); + +declare global { + interface HTMLElementTagNameMap { + 'comment-list': CommentList; + } +} diff --git a/packages/comment-widget/src/comment-pagination.ts b/packages/comment-widget/src/comment-pagination.ts new file mode 100644 index 0000000..698d02c --- /dev/null +++ b/packages/comment-widget/src/comment-pagination.ts @@ -0,0 +1,88 @@ +import { msg } from '@lit/localize'; +import { css, html, LitElement } from 'lit'; +import { property } from 'lit/decorators.js'; +import baseStyles from './styles/base'; + +export class CommentPagination extends LitElement { + @property({ type: Number }) + total = 0; + @property({ type: Number }) + page = 1; + @property({ type: Number }) + size = 10; + + get totalPages() { + return Math.ceil(this.total / this.size); + } + + gotoPage(page: number | string) { + if (page !== this.page) { + this.dispatchEvent( + new CustomEvent('page-change', { + detail: { page }, + bubbles: true, + composed: true, + }) + ); + } + } + + override render() { + return html` + + `; + } + + static override styles = [ + ...baseStyles, + css` + :host { + display: flex; + justify-content: center; + } + + @unocss-placeholder; + `, + ]; +} + +customElements.get('comment-pagination') || + customElements.define('comment-pagination', CommentPagination); + +declare global { + interface HTMLElementTagNameMap { + 'comment-pagination': CommentPagination; + } +} diff --git a/packages/comment-widget/src/comment-replies.ts b/packages/comment-widget/src/comment-replies.ts new file mode 100644 index 0000000..564d585 --- /dev/null +++ b/packages/comment-widget/src/comment-replies.ts @@ -0,0 +1,170 @@ +import type { CommentVo, ReplyVo, ReplyVoList } from '@halo-dev/api-client'; +import { consume } from '@lit/context'; +import { css, html, LitElement } from 'lit'; +import { property, state } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; +import { baseUrlContext, configMapDataContext, toastContext } from './context'; +import './reply-item'; +import './loading-block'; +import './reply-form'; +import { msg } from '@lit/localize'; +import { when } from 'lit/directives/when.js'; +import { ofetch } from 'ofetch'; +import type { ToastManager } from './lit-toast'; +import baseStyles from './styles/base'; +import type { ConfigMapData } from './types'; + +export class CommentReplies extends LitElement { + @consume({ context: baseUrlContext }) + @state() + baseUrl = ''; + + @consume({ context: configMapDataContext }) + @state() + configMapData: ConfigMapData | undefined; + + @property({ type: Object }) + comment: CommentVo | undefined; + + @property({ type: Boolean }) + showReplyForm = false; + + @state() + replies: ReplyVo[] = []; + + @state() + page = 1; + + @state() + hasNext = false; + + @state() + loading = false; + + @state() + activeQuoteReply: ReplyVo | undefined = undefined; + + @consume({ context: toastContext, subscribe: true }) + @state() + toastManager: ToastManager | undefined; + + override render() { + return html`
    + ${when( + this.replies.length, + () => html`
    + ${repeat( + this.replies, + (item) => item.metadata.name, + (item) => + html`` + )} +
    ` + )} + ${when(this.loading, () => html` `)} + ${when( + this.hasNext && !this.loading, + () => html`
    + +
    ` + )} +
    `; + } + + onSetActiveQuoteReply(event: CustomEvent) { + this.activeQuoteReply = event.detail.quoteReply; + } + + async fetchReplies(options?: { append: boolean }) { + try { + this.loading = true; + + // Reload replies list + if (!options?.append) { + this.page = 1; + } + + const data = await ofetch( + `${this.baseUrl}/apis/api.halo.run/v1alpha1/comments/${ + this.comment?.metadata.name + }/reply`, + { + query: { + page: this.page || 1, + size: this.configMapData?.basic.replySize || 10, + }, + } + ); + + if (options?.append) { + this.replies = this.replies.concat(data.items); + } else { + this.replies = data.items; + } + + this.hasNext = data.hasNext; + this.page = data.page; + } catch (error) { + console.error(error); + this.toastManager?.error( + msg('Failed to load reply list, please try again later') + ); + } finally { + this.loading = false; + } + } + + async fetchNext() { + if (this.configMapData?.basic.withReplies) { + // if withReplies is true, we need to reload the replies list + await this.fetchReplies({ append: !(this.page === 1) }); + this.page++; + } else { + this.page++; + await this.fetchReplies({ append: true }); + } + } + + override connectedCallback(): void { + super.connectedCallback(); + + if (this.configMapData?.basic.withReplies) { + // TODO: Fix ts error + // Needs @halo-dev/api-client@2.14.0 + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + this.replies = this.comment?.replies.items; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + this.page = this.comment?.replies.page; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + this.hasNext = this.comment?.replies.hasNext; + } else { + this.fetchReplies(); + } + } + + static override styles = [ + ...baseStyles, + css` + @unocss-placeholder; + `, + ]; +} + +customElements.get('comment-replies') || + customElements.define('comment-replies', CommentReplies); + +declare global { + interface HTMLElementTagNameMap { + 'comment-replies': CommentReplies; + } +} diff --git a/packages/comment-widget/src/comment-widget.ts b/packages/comment-widget/src/comment-widget.ts new file mode 100644 index 0000000..b6d93a7 --- /dev/null +++ b/packages/comment-widget/src/comment-widget.ts @@ -0,0 +1,180 @@ +import type { DetailedUser, User } from '@halo-dev/api-client'; +import { provide } from '@lit/context'; +import { css, html, LitElement } from 'lit'; +import { property, state } from 'lit/decorators.js'; +import { + AllUserPolicy, + AnonymousUserPolicy, + AvatarPolicyEnum, + NoAvatarUserPolicy, + setPolicyInstance, +} from './avatar/avatar-policy'; +import { setAvatarProvider } from './avatar/providers'; +import './comment-form'; +import './comment-item'; +import './comment-pagination'; +import { + allowAnonymousCommentsContext, + baseUrlContext, + configMapDataContext, + currentUserContext, + groupContext, + kindContext, + nameContext, + toastContext, + versionContext, +} from './context'; +import { ToastManager } from './lit-toast'; +import baseStyles from './styles/base'; +import type { ConfigMapData } from './types'; +import './comment-list'; +import { ofetch } from 'ofetch'; +import './comment-editor-skeleton'; + +export class CommentWidget extends LitElement { + @provide({ context: baseUrlContext }) + @property({ type: String, attribute: 'base-url' }) + baseUrl = ''; + + @provide({ context: kindContext }) + @property({ type: String }) + kind = ''; + + @provide({ context: groupContext }) + @property({ type: String }) + group = ''; + + @provide({ context: versionContext }) + @property({ type: String }) + version = ''; + + @provide({ context: nameContext }) + @property({ type: String }) + name = ''; + + @provide({ context: currentUserContext }) + @state() + currentUser: User | undefined; + + @provide({ context: allowAnonymousCommentsContext }) + @state() + allowAnonymousComments = false; + + @provide({ context: configMapDataContext }) + @state() + configMapData: ConfigMapData | undefined; + + @provide({ context: toastContext }) + @state() + toastManager: ToastManager | undefined; + + @state() + isInitialized = false; + + override render() { + return html`
    + ${ + !this.isInitialized + ? html`` + : html` + + + ` + } +
    `; + } + + async fetchGlobalInfo() { + const data = await ofetch(`${this.baseUrl}/actuator/globalinfo`); + this.allowAnonymousComments = data.allowAnonymousComments; + } + + async fetchConfigMapData() { + const data = await ofetch( + `${this.baseUrl}/apis/api.commentwidget.halo.run/v1alpha1/config` + ); + this.configMapData = data; + } + + async fetchCurrentUser() { + const data = await ofetch( + `${this.baseUrl}/apis/api.console.halo.run/v1alpha1/users/-` + ); + this.currentUser = + data.user.metadata.name === 'anonymousUser' ? undefined : data.user; + } + + initAvatarProvider() { + if (!this.configMapData?.avatar.enable) { + return; + } + setAvatarProvider( + this.configMapData.avatar.provider, + this.configMapData.avatar.providerMirror + ); + } + + initAvatarPolicy() { + if (!this.configMapData?.avatar.enable) { + setPolicyInstance(undefined); + return; + } + switch (this.configMapData.avatar.policy) { + case AvatarPolicyEnum.ALL_USER_POLICY: { + setPolicyInstance(new AllUserPolicy()); + break; + } + case AvatarPolicyEnum.NO_AVATAR_USER_POLICY: { + setPolicyInstance(new NoAvatarUserPolicy()); + break; + } + default: + setPolicyInstance(new AnonymousUserPolicy()); + } + } + + override connectedCallback(): void { + super.connectedCallback(); + this.init(); + } + + async init() { + this.toastManager = new ToastManager(); + try { + await this.fetchGlobalInfo(); + await this.fetchConfigMapData(); + await this.fetchCurrentUser(); + } catch (error) { + if (error instanceof Error) { + this.toastManager?.show(error.message, 'error'); + } + return; + } finally { + this.isInitialized = true; + } + this.initAvatarProvider(); + this.initAvatarPolicy(); + } + + static override styles = [ + ...baseStyles, + css` + :host { + width: 100%; + display: flex; + justify-content: center; + } + + @unocss-placeholder; + `, + ]; +} + +customElements.get('comment-widget') || + customElements.define('comment-widget', CommentWidget); + +declare global { + interface HTMLElementTagNameMap { + 'comment-widget': CommentWidget; + } +} diff --git a/packages/comment-widget/src/commenter-ua-bar.ts b/packages/comment-widget/src/commenter-ua-bar.ts new file mode 100644 index 0000000..42aa29e --- /dev/null +++ b/packages/comment-widget/src/commenter-ua-bar.ts @@ -0,0 +1,134 @@ +import { css, html, LitElement } from 'lit'; +import { property, state } from 'lit/decorators.js'; +import { when } from 'lit/directives/when.js'; +import type { UAParser } from 'ua-parser-js'; +import baseStyles from './styles/base'; + +const OS_ICON_MAP = { + Windows: 'i-logos:microsoft-windows-icon', + macOS: 'i-logos:apple', + Linux: 'i-logos:linux-tux', + Android: 'i-logos:android-icon', + iOS: 'i-logos:apple', + 'Chrome OS': 'i-logos:chrome', + Arch: 'i-logos:archlinux', + Manjaro: 'i-logos:manjaro', + Ubuntu: 'i-logos:ubuntu', + Fedora: 'i-logos:fedora', + HarmonyOS: 'i-simple-icons:harmonyos', +}; + +const BROWSER_ICON_MAP = { + Chrome: 'i-logos:chrome', + 'Mobile Chrome': 'i-logos:chrome', + 'Chrome WebView': 'i-logos:chrome', + Firefox: 'i-logos:firefox', + 'Mobile Firefox': 'i-logos:firefox', + Safari: 'i-logos:safari', + 'Mobile Safari': 'i-logos:safari', + Edge: 'i-logos:microsoft-edge', + 'Edge WebView': 'i-logos:microsoft-edge', + 'Edge WebView2': 'i-logos:microsoft-edge', + Opera: 'i-logos:opera', + 'Huawei Browser': 'i-simple-icons:huawei text-[#D6000B]', + 'MIUI Browser': 'i-simple-icons:xiaomi text-[#FF6901]', +}; + +export class CommenterUABar extends LitElement { + @property({ type: String }) + ua: string = ''; + + @state() + parser: UAParser | undefined; + + @state() + isInitialized = false; + + getOSIcon(os?: string) { + return OS_ICON_MAP[os as keyof typeof OS_ICON_MAP]; + } + + getBrowserIcon(browser?: string) { + return BROWSER_ICON_MAP[browser as keyof typeof BROWSER_ICON_MAP]; + } + + override connectedCallback(): void { + super.connectedCallback(); + this.init(); + } + + async init() { + const { UAParser } = await import('ua-parser-js'); + this.parser = new UAParser(this.ua); + this.isInitialized = true; + } + + protected override render() { + if (!this.parser) { + return this.renderSkeleton(); + } + + const os = this.parser.getOS(); + const browser = this.parser.getBrowser(); + + if (!os.name && !browser.name) { + return html` +
    + ${this.ua} +
    + `; + } + + const osIcon = this.getOSIcon(os?.name); + const browserIcon = this.getBrowserIcon(browser?.name); + + return html` +
    + ${when( + os, + () => html`
    + ${when(osIcon, () => html``)} + ${os.name} +
    ` + )} + ${when( + browser, + () => html`
    + ${when(browserIcon, () => html``)} + ${[browser.name, browser.major].filter(Boolean).join(' ')} +
    ` + )} +
    `; + } + + private renderSkeleton() { + return html` +
    +
    +
    + +
    +
    +
    + +
    +
    + `; + } + + static override styles = [ + ...baseStyles, + css` + @unocss-placeholder; + `, + ]; +} + +customElements.get('commenter-ua-bar') || + customElements.define('commenter-ua-bar', CommenterUABar); + +declare global { + interface HTMLElementTagNameMap { + 'commenter-ua-bar': CommenterUABar; + } +} diff --git a/packages/comment-widget/src/constant/index.ts b/packages/comment-widget/src/constant/index.ts new file mode 100644 index 0000000..0802d02 --- /dev/null +++ b/packages/comment-widget/src/constant/index.ts @@ -0,0 +1,5 @@ +/** + * Upvoted comments and replies local storage keys + */ +export const LS_UPVOTED_COMMENTS_KEY = 'halo.upvoted.comments'; +export const LS_UPVOTED_REPLIES_KEY = 'halo.upvoted.replies'; diff --git a/packages/comment-widget/src/context/index.ts b/packages/comment-widget/src/context/index.ts new file mode 100644 index 0000000..8882226 --- /dev/null +++ b/packages/comment-widget/src/context/index.ts @@ -0,0 +1,26 @@ +import type { User } from '@halo-dev/api-client'; +import { createContext } from '@lit/context'; +import type { ToastManager } from '../lit-toast'; +import type { ConfigMapData } from '../types'; + +export const baseUrlContext = createContext(Symbol('baseUrl')); +export const kindContext = createContext(Symbol('kind')); +export const groupContext = createContext(Symbol('group')); +export const nameContext = createContext(Symbol('name')); +export const versionContext = createContext(Symbol('version')); + +export const allowAnonymousCommentsContext = createContext( + Symbol('allowAnonymousComments') +); + +export const currentUserContext = createContext( + Symbol('currentUser') +); + +export const toastContext = createContext( + Symbol('toastContext') +); + +export const configMapDataContext = createContext( + Symbol('configMapData') +); diff --git a/packages/comment-widget/src/emoji-button.ts b/packages/comment-widget/src/emoji-button.ts new file mode 100644 index 0000000..623e51c --- /dev/null +++ b/packages/comment-widget/src/emoji-button.ts @@ -0,0 +1,287 @@ +import './icons/icon-loading'; +import en from '@emoji-mart/data/i18n/en.json'; +import es from '@emoji-mart/data/i18n/es.json'; +import zh from '@emoji-mart/data/i18n/zh.json'; +import { + autoUpdate, + computePosition, + flip, + offset, + type Placement, + shift, +} from '@floating-ui/dom'; +import { msg } from '@lit/localize'; +import type { Picker } from 'emoji-mart'; +import { css, html, LitElement } from 'lit'; +import { state } from 'lit/decorators.js'; +import { createRef, type Ref, ref } from 'lit/directives/ref.js'; +import { getLocale } from './locale'; +import baseStyles from './styles/base'; + +const localeMap = { + 'zh-CN': zh, + 'zh-TW': zh, + en: en, + es: es, +}; + +const sharedEmojiPanel = { + wrapper: null as HTMLDivElement | null, + picker: null as Picker | null, + activeButton: null as EmojiButton | null, + + init() { + if (this.wrapper) return this.wrapper; + + const styleElement = document.createElement('style'); + styleElement.textContent = ` + .form__emoji-panel { + box-shadow: 0 0.5em 1em rgba(0, 0, 0, 0.1); + border-radius: 0.875em; + overflow: hidden; + display: none; + } + + .form__emoji-panel.visible { + display: block; + animation: fadeInUp 0.3s both; + } + + @keyframes fadeInUp { + from { + opacity: 0; + transform: translate3d(0, 5%, 0); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } + } + + em-emoji-picker { + --rgb-color: var(--halo-cw-emoji-picker-rgb-color, 34, 36, 39); + --rgb-accent: var(--halo-cw-emoji-picker-rgb-accent, 34, 102, 237); + --rgb-background: var(--halo-cw-emoji-picker-rgb-background, 255, 255, 255); + --rgb-input: var(--halo-cw-emoji-picker-rgb-input, 255, 255, 255); + --color-border: var(--halo-cw-emoji-picker-color-border, rgba(0, 0, 0, .05)); + --color-border-over: var(--halo-cw-emoji-picker-color-border-over, rgba(0, 0, 0, .1)); + } + `; + document.head.appendChild(styleElement); + + this.wrapper = document.createElement('div'); + this.wrapper.className = 'form__emoji-panel'; + this.wrapper.style.position = 'absolute'; + this.wrapper.style.zIndex = '9999'; + document.body.appendChild(this.wrapper); + + return this.wrapper; + }, + + show() { + if (this.wrapper) { + this.wrapper.classList.add('visible'); + } + }, + + hide() { + if (this.wrapper) { + this.wrapper.classList.remove('visible'); + } + }, +}; + +export class EmojiButton extends LitElement { + @state() + emojiPickerVisible = false; + + @state() + emojiLoading = false; + + buttonRef: Ref = createRef(); + + cleanupAutoUpdate: (() => void) | null = null; + + placement: Placement = 'bottom'; + + constructor() { + super(); + this.emojiPickerVisible = false; + this.handleClickOutside = this.handleClickOutside.bind(this); + } + + override connectedCallback() { + super.connectedCallback(); + document.addEventListener('click', this.handleClickOutside, true); + sharedEmojiPanel.init(); + } + + override disconnectedCallback() { + document.removeEventListener('click', this.handleClickOutside, true); + this.cleanupFloating(); + if (sharedEmojiPanel.activeButton === this) { + sharedEmojiPanel.activeButton = null; + sharedEmojiPanel.hide(); + } + super.disconnectedCallback(); + } + + cleanupFloating() { + if (this.cleanupAutoUpdate) { + this.cleanupAutoUpdate(); + this.cleanupAutoUpdate = null; + } + } + + handleClickOutside(event: Event) { + if ( + this.emojiPickerVisible && + !event.composedPath().includes(this) && + sharedEmojiPanel.wrapper && + !event.composedPath().includes(sharedEmojiPanel.wrapper) + ) { + this.closeEmojiPicker(); + } + } + + closeEmojiPicker() { + this.emojiPickerVisible = false; + this.cleanupFloating(); + sharedEmojiPanel.hide(); + if (sharedEmojiPanel.activeButton === this) { + sharedEmojiPanel.activeButton = null; + } + } + + async handleOpenEmojiPicker(e: Event) { + e.stopPropagation(); + e.preventDefault(); + + if (this.emojiPickerVisible) { + this.closeEmojiPicker(); + return; + } + + if ( + sharedEmojiPanel.activeButton && + sharedEmojiPanel.activeButton !== this + ) { + sharedEmojiPanel.activeButton.closeEmojiPicker(); + } + + sharedEmojiPanel.activeButton = this; + + if (sharedEmojiPanel.wrapper?.children.length) { + this.emojiPickerVisible = true; + sharedEmojiPanel.show(); + this.setupFloating(); + return; + } + + this.emojiLoading = true; + + const { Picker } = await import('emoji-mart'); + const { default: data } = await import('@emoji-mart/data'); + + sharedEmojiPanel.picker = new Picker({ + data, + onEmojiSelect: ({ native }: { native: string }) => { + const activeButton = sharedEmojiPanel.activeButton; + if (activeButton) { + activeButton.dispatchEvent( + new CustomEvent('emoji-select', { detail: { native } }) + ); + activeButton.closeEmojiPicker(); + } + }, + i18n: localeMap[getLocale()], + }); + + if (sharedEmojiPanel.wrapper) { + sharedEmojiPanel.wrapper.appendChild( + sharedEmojiPanel.picker as unknown as Node + ); + sharedEmojiPanel.show(); + } + + this.emojiPickerVisible = true; + this.emojiLoading = false; + this.setupFloating(); + } + + setupFloating() { + if (!this.buttonRef.value || !sharedEmojiPanel.wrapper) return; + + this.cleanupFloating(); + + this.cleanupAutoUpdate = autoUpdate( + this.buttonRef.value, + sharedEmojiPanel.wrapper, + () => this.updatePosition() + ); + + this.updatePosition(); + } + + async updatePosition() { + const reference = this.buttonRef.value; + const floating = sharedEmojiPanel.wrapper; + + if (!reference || !floating) return; + + const { x, y } = await computePosition(reference, floating, { + placement: this.placement, + middleware: [ + offset({ + mainAxis: 8, + crossAxis: 0, + alignmentAxis: 0, + }), + flip(), + shift({ padding: 8 }), + ], + }); + + Object.assign(floating.style, { + left: `${x}px`, + top: `${y}px`, + }); + } + + override render() { + return html`
    + ${ + this.emojiLoading + ? html`` + : html`
    ` + } +
    `; + } + + static override styles = [ + ...baseStyles, + css` + :host { + display: inline-flex; + } + @unocss-placeholder; + `, + ]; +} + +customElements.get('emoji-button') || + customElements.define('emoji-button', EmojiButton); + +declare global { + interface HTMLElementTagNameMap { + 'emoji-button': EmojiButton; + } +} diff --git a/packages/comment-widget/src/generated/locale-codes.ts b/packages/comment-widget/src/generated/locale-codes.ts new file mode 100644 index 0000000..23dbf36 --- /dev/null +++ b/packages/comment-widget/src/generated/locale-codes.ts @@ -0,0 +1,27 @@ +// Do not modify this file by hand! +// Re-generate this file by running lit-localize. + +/** + * The locale code that templates in this source code are written in. + */ +export const sourceLocale = `en`; + +/** + * The other locale codes that this application is localized into. Sorted + * lexicographically. + */ +export const targetLocales = [ + `es`, + `zh-CN`, + `zh-TW`, +] as const; + +/** + * All valid project locale codes. Sorted lexicographically. + */ +export const allLocales = [ + `en`, + `es`, + `zh-CN`, + `zh-TW`, +] as const; diff --git a/packages/comment-widget/src/generated/locales/es.ts b/packages/comment-widget/src/generated/locales/es.ts new file mode 100644 index 0000000..3d2a405 --- /dev/null +++ b/packages/comment-widget/src/generated/locales/es.ts @@ -0,0 +1,53 @@ + + // Do not modify this file by hand! + // Re-generate this file by running lit-localize + + import {html} from 'lit'; + + + /* eslint-disable no-irregular-whitespace */ + /* eslint-disable @typescript-eslint/no-explicit-any */ + + export const templates = { + 'h6a83761bb66bbcc5': html`${0} Comentarios`, +'s053945ad43e9d1d6': `Por favor, ingrese el código de verificación`, +'s09c7e0ff5f4e18cb': `Cancelar respuesta`, +'s0b7ae9543c001867': `Responder`, +'s0fbf6dc6a1966408': `Siguiente`, +'s1c6fefb092506753': `Error al cargar la lista de comentarios, por favor intente más tarde`, +'s1e3e30a26025484c': `Error al cargar la lista de respuestas, por favor intente más tarde`, +'s26e4d65f2801ac9c': `Por favor, ingrese el contenido`, +'s299b10f3a58a09fd': `Haga clic en OK para ir a la página de cierre de sesión, Por favor, asegúrese de que el contenido editado haya sido guardado.`, +'s35c3125c7750681e': `Cita`, +'s3643189d1abbb7f4': `Código`, +'s3fb33d17bad61aa9': `Comentario enviado con éxito, pendiente de revisión`, +'s4c0e15f9073382e6': `Error al obtener el código de verificación`, +'s5184a3f3e2f7b603': `Actualmente anónimo. Después de seleccionar la opción privada, el comentario solo será visible para el administrador del sitio.`, +'s523eb9043213ff0d': `Itálica`, +'s58a3c1ecd4dd06cf': `Tachado`, +'s67749057edb2586b': `Cerrar sesión`, +'s6cb61eeccda272d5': `Bloque de código`, +'s7437373e541a8037': `Apodo`, +'s7584ded3d749c75e': `Cargar más`, +'s82665b2ffabc9c0a': `Sitio web`, +'s838e512973be01d4': `Actualmente conectado. Después de seleccionar la opción privada, los comentarios solo serán visibles para usted y el administrador del sitio.`, +'s84b033b2f7360187': `Por favor, inicie sesión o complete la información primero`, +'s98a5f7789c49dd3f': `En revisión`, +'s9f2ed66340f019c6': `Escribir un comentario`, +'sa10fbe3fcd6ae148': `Agregar respuesta`, +'sa82fbb7f361c6f44': `Mostrar respuestas`, +'sa8dddacbaa66f8e0': `Por favor, inicie sesión primero`, +'sb206d700d26b14ff': `Subrayado`, +'sb3d4f79d9d8b71e5': `Enviar comentario`, +'sb5a28aa35006ff08': `Comentario enviado con éxito`, +'sc8da3cc71de63832': `Iniciar sesión`, +'sd1f44f1a8bc20e67': `Correo electrónico`, +'sd5e242ab9574958a': `Error al comentar, por favor intente más tarde`, +'se7bee6e9a9b5394c': `Íntimo`, +'sea7e567ed89dc0d7': `Seleccionar emoticono`, +'sf3ff78cc329d3528': `Anterior`, +'sf77128b082955d42': `(O iniciar sesión)`, +'sf7c0eba7c822e3d6': `Negrita`, +'sf9709983d4da2c54': `Me gusta`, + }; + \ No newline at end of file diff --git a/packages/comment-widget/src/generated/locales/zh-CN.ts b/packages/comment-widget/src/generated/locales/zh-CN.ts new file mode 100644 index 0000000..3adc0e7 --- /dev/null +++ b/packages/comment-widget/src/generated/locales/zh-CN.ts @@ -0,0 +1,53 @@ + + // Do not modify this file by hand! + // Re-generate this file by running lit-localize + + import {html} from 'lit'; + + + /* eslint-disable no-irregular-whitespace */ + /* eslint-disable @typescript-eslint/no-explicit-any */ + + export const templates = { + 'h6a83761bb66bbcc5': html`${0} 条评论`, +'s053945ad43e9d1d6': `请输入验证码`, +'s09c7e0ff5f4e18cb': `取消回复`, +'s0b7ae9543c001867': `回复`, +'s0fbf6dc6a1966408': `下一页`, +'s1c6fefb092506753': `加载评论列表失败,请稍后重试`, +'s1e3e30a26025484c': `加载回复列表失败,请稍后重试`, +'s26e4d65f2801ac9c': `请输入内容`, +'s299b10f3a58a09fd': `点击确定将跳转至退出登录页面,请确保正在编辑的内容已保存。`, +'s35c3125c7750681e': `引用`, +'s3643189d1abbb7f4': `代码`, +'s3fb33d17bad61aa9': `评论成功,请等待审核`, +'s4c0e15f9073382e6': `获取验证码失败`, +'s5184a3f3e2f7b603': `当前是匿名状态,选择私密选项后,评论将仅对网站管理员可见。`, +'s523eb9043213ff0d': `斜体`, +'s58a3c1ecd4dd06cf': `删除线`, +'s67749057edb2586b': `退出登录`, +'s6cb61eeccda272d5': `代码块`, +'s7437373e541a8037': `昵称`, +'s7584ded3d749c75e': `加载更多`, +'s82665b2ffabc9c0a': `网站`, +'s838e512973be01d4': `当前已登录,选择私密选项后,评论将仅对您和网站管理员可见。`, +'s84b033b2f7360187': `请先登录或者完善信息`, +'s98a5f7789c49dd3f': `审核中`, +'s9f2ed66340f019c6': `编写评论`, +'sa10fbe3fcd6ae148': `加入回复`, +'sa82fbb7f361c6f44': `显示回复`, +'sa8dddacbaa66f8e0': `请先登录`, +'sb206d700d26b14ff': `下划线`, +'sb3d4f79d9d8b71e5': `提交评论`, +'sb5a28aa35006ff08': `评论成功`, +'sc8da3cc71de63832': `登录`, +'sd1f44f1a8bc20e67': `电子邮件`, +'sd5e242ab9574958a': `评论失败,请稍后重试`, +'se7bee6e9a9b5394c': `私密`, +'sea7e567ed89dc0d7': `选择表情`, +'sf3ff78cc329d3528': `上一页`, +'sf77128b082955d42': `(或登录账号)`, +'sf7c0eba7c822e3d6': `粗体`, +'sf9709983d4da2c54': `点赞`, + }; + \ No newline at end of file diff --git a/packages/comment-widget/src/generated/locales/zh-TW.ts b/packages/comment-widget/src/generated/locales/zh-TW.ts new file mode 100644 index 0000000..73f286e --- /dev/null +++ b/packages/comment-widget/src/generated/locales/zh-TW.ts @@ -0,0 +1,53 @@ + + // Do not modify this file by hand! + // Re-generate this file by running lit-localize + + import {html} from 'lit'; + + + /* eslint-disable no-irregular-whitespace */ + /* eslint-disable @typescript-eslint/no-explicit-any */ + + export const templates = { + 'h6a83761bb66bbcc5': html`${0} 則評論`, +'s053945ad43e9d1d6': `請輸入驗證碼`, +'s09c7e0ff5f4e18cb': `取消回覆`, +'s0b7ae9543c001867': `回覆`, +'s0fbf6dc6a1966408': `下一頁`, +'s1c6fefb092506753': `載入評論列表失敗,請稍後重試`, +'s1e3e30a26025484c': `載入回覆列表失敗,請稍後重試`, +'s26e4d65f2801ac9c': `請輸入內容`, +'s299b10f3a58a09fd': `点击确定将跳转至退出登录页面,请确保正在编辑的内容已保存。`, +'s35c3125c7750681e': `引用`, +'s3643189d1abbb7f4': `程式碼`, +'s3fb33d17bad61aa9': `評論成功,請等待審核`, +'s4c0e15f9073382e6': `獲取驗證碼失敗`, +'s5184a3f3e2f7b603': `目前是匿名狀態,選擇私密選項後,評論將僅對網站管理員可見。`, +'s523eb9043213ff0d': `斜體`, +'s58a3c1ecd4dd06cf': `刪除線`, +'s67749057edb2586b': `登出`, +'s6cb61eeccda272d5': `程式碼區塊`, +'s7437373e541a8037': `暱稱`, +'s7584ded3d749c75e': `載入更多`, +'s82665b2ffabc9c0a': `網站`, +'s838e512973be01d4': `目前已登入,選擇私密選項後,評論將僅對您和網站管理員可見。`, +'s84b033b2f7360187': `請先登入或者完善資訊`, +'s98a5f7789c49dd3f': `審核中`, +'s9f2ed66340f019c6': `撰寫評論`, +'sa10fbe3fcd6ae148': `新增回覆`, +'sa82fbb7f361c6f44': `顯示回覆`, +'sa8dddacbaa66f8e0': `請先登入`, +'sb206d700d26b14ff': `底線`, +'sb3d4f79d9d8b71e5': `提交評論`, +'sb5a28aa35006ff08': `評論成功`, +'sc8da3cc71de63832': `登入`, +'sd1f44f1a8bc20e67': `電子郵件`, +'sd5e242ab9574958a': `評論失敗,請稍後重試`, +'se7bee6e9a9b5394c': `私密`, +'sea7e567ed89dc0d7': `選擇表情`, +'sf3ff78cc329d3528': `上一頁`, +'sf77128b082955d42': `(或登入帳號)`, +'sf7c0eba7c822e3d6': `粗體`, +'sf9709983d4da2c54': `讚`, + }; + \ No newline at end of file diff --git a/packages/comment-widget/src/icons/icon-loading.ts b/packages/comment-widget/src/icons/icon-loading.ts new file mode 100644 index 0000000..f128a00 --- /dev/null +++ b/packages/comment-widget/src/icons/icon-loading.ts @@ -0,0 +1,41 @@ +import { css, html, LitElement } from 'lit'; +import baseStyles from '../styles/base'; + +export class IconLoading extends LitElement { + override render() { + return html` + + + `; + } + + static override styles = [ + ...baseStyles, + css` + :host { + display: inline-flex; + } + @unocss-placeholder; + `, + ]; +} + +customElements.get('icon-loading') || + customElements.define('icon-loading', IconLoading); + +declare global { + interface HTMLElementTagNameMap { + 'icon-loading': IconLoading; + } +} diff --git a/packages/comment-widget/src/index.ts b/packages/comment-widget/src/index.ts new file mode 100644 index 0000000..54ab2f4 --- /dev/null +++ b/packages/comment-widget/src/index.ts @@ -0,0 +1,32 @@ +import { BaseCommentItem } from './base-comment-item'; +import { BaseForm } from './base-form'; +import { CommentContent } from './comment-content'; +import { CommentEditor } from './comment-editor'; +import { CommentItem } from './comment-item'; +import { CommentList } from './comment-list'; +import { CommentPagination } from './comment-pagination'; +import { CommentReplies } from './comment-replies'; +import { CommentWidget } from './comment-widget'; +import { LitToast } from './lit-toast'; +import { LoadingBlock } from './loading-block'; +import { ReplyForm } from './reply-form'; +import { ReplyItem } from './reply-item'; +import { UserAvatar } from './user-avatar'; + +import './locale'; +export { + CommentWidget, + BaseForm, + BaseCommentItem, + CommentItem, + CommentPagination, + CommentReplies, + LoadingBlock, + ReplyForm, + ReplyItem, + UserAvatar, + CommentList, + LitToast, + CommentEditor, + CommentContent, +}; diff --git a/packages/comment-widget/src/lit-toast.ts b/packages/comment-widget/src/lit-toast.ts new file mode 100644 index 0000000..153393c --- /dev/null +++ b/packages/comment-widget/src/lit-toast.ts @@ -0,0 +1,172 @@ +import { css, html, LitElement } from 'lit'; +import { property } from 'lit/decorators.js'; +import './icons/icon-loading'; +import { classMap } from 'lit/directives/class-map.js'; +import baseStyles from './styles/base'; + +type ToastType = 'success' | 'error' | 'warn'; + +export class LitToast extends LitElement { + @property({ type: String }) + message = ''; + + @property({ type: String }) + type = 'success' as ToastType; + + override render() { + return html`
    + ${ + this.type === 'success' + ? html`` + : html`` + } ${this.message} +
    `; + } + + static override styles = [ + ...baseStyles, + css` + .toast { + border-radius: var(--halo-cw-base-rounded, 0.5em); + font-size: 0.875em; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.5em 0.625em; + justify-content: space-between; + overflow: hidden; + color: #fff; + gap: 0.5em; + box-shadow: + 0 0 #0000, + 0 0 #0000, + 0 1px 3px 0 rgb(0 0 0 / 0.1), + 0 1px 2px -1px rgb(0 0 0 / 0.1); + + animation: slideInDown 0.3s ease-out forwards; + } + + .toast--exit { + animation: slideOutUp 0.3s ease-in forwards; + } + + .toast--error { + background-color: #d71d1d; + } + + .toast--success { + background-color: #4ccba0; + } + + .toast--warn { + background-color: #f5a623; + } + + @keyframes slideInDown { + from { + transform: translateY(0); + opacity: 0; + } + to { + transform: translateY(100%); + opacity: 1; + } + } + + @keyframes slideOutUp { + from { + transform: translateY(100%); + opacity: 1; + } + to { + transform: translateY(0); + opacity: 0; + } + } + + @unocss-placeholder; + `, + ]; +} + +export class LitToastContainer extends LitElement { + override render() { + return html``; + } + + static override styles = [ + ...baseStyles, + css` + :host { + position: fixed; + top: 1em; + z-index: 1000; + display: flex; + width: 100%; + justify-content: center; + align-items: center; + flex-direction: column; + gap: 0.5em; + } + `, + ]; +} + +customElements.get('lit-toast') || customElements.define('lit-toast', LitToast); +customElements.get('lit-toast-container') || + customElements.define('lit-toast-container', LitToastContainer); + +declare global { + interface HTMLElementTagNameMap { + 'lit-toast': LitToast; + 'lit-toast-container': LitToastContainer; + } +} + +export class ToastManager { + private body: HTMLBodyElement = document.body as HTMLBodyElement; + private toastContainer: LitToastContainer; + + constructor() { + const container = this.body.querySelector('lit-toast-container'); + + if (!container) { + this.toastContainer = new LitToastContainer(); + this.body.appendChild(this.toastContainer); + } else { + this.toastContainer = container as LitToastContainer; + } + } + + show(message: string, type: ToastType) { + const toast = new LitToast(); + toast.message = message; + toast.type = type; + this.toastContainer.appendChild(toast); + + setTimeout(() => { + toast.classList.add('toast--exit'); + setTimeout(() => { + this.toastContainer?.removeChild(toast); + }, 300); + }, 3000); + } + + success(message: string) { + this.show(message, 'success'); + } + + error(message: string) { + this.show(message, 'error'); + } + + warn(message: string) { + this.show(message, 'warn'); + } +} diff --git a/packages/comment-widget/src/loading-block.ts b/packages/comment-widget/src/loading-block.ts new file mode 100644 index 0000000..2481169 --- /dev/null +++ b/packages/comment-widget/src/loading-block.ts @@ -0,0 +1,23 @@ +import { css, html, LitElement } from 'lit'; +import './icons/icon-loading'; + +export class LoadingBlock extends LitElement { + override render() { + return html`
    + +
    `; + } + + static override styles = css` + @unocss-placeholder; + `; +} + +customElements.get('loading-block') || + customElements.define('loading-block', LoadingBlock); + +declare global { + interface HTMLElementTagNameMap { + 'loading-block': LoadingBlock; + } +} diff --git a/packages/comment-widget/src/locale/index.ts b/packages/comment-widget/src/locale/index.ts new file mode 100644 index 0000000..af4db8e --- /dev/null +++ b/packages/comment-widget/src/locale/index.ts @@ -0,0 +1,38 @@ +import { configureLocalization, type LocaleModule } from '@lit/localize'; +import { + allLocales, + sourceLocale, + targetLocales, +} from '../generated/locale-codes'; +import * as templates_es from '../generated/locales/es'; +import * as templates_zh_CN from '../generated/locales/zh-CN'; +import * as templates_zh_TW from '../generated/locales/zh-TW'; + +const localizedTemplates = new Map([ + ['es', templates_es], + ['zh-CN', templates_zh_CN], + ['zh-TW', templates_zh_TW], +]); + +const { setLocale } = configureLocalization({ + sourceLocale, + targetLocales, + loadLocale: async (locale) => localizedTemplates.get(locale) as LocaleModule, +}); + +setLocale(getLocale()); + +function getLanguageFromCookie() { + const match = document.cookie.match(/(^| )language=([^;]+)/); + const matchedLanguage = allLocales.find((locale) => locale === match?.[2]); + return matchedLanguage; +} + +function getLanguageFromBrowser() { + const language = allLocales.find((locale) => locale === navigator.language); + return language; +} + +export function getLocale() { + return getLanguageFromCookie() || getLanguageFromBrowser() || sourceLocale; +} diff --git a/packages/comment-widget/src/reply-form.ts b/packages/comment-widget/src/reply-form.ts new file mode 100644 index 0000000..19c15d6 --- /dev/null +++ b/packages/comment-widget/src/reply-form.ts @@ -0,0 +1,188 @@ +import type { + CommentVo, + Reply, + ReplyRequest, + ReplyVo, + User, +} from '@halo-dev/api-client'; +import { consume } from '@lit/context'; +import { html, LitElement } from 'lit'; +import { property, state } from 'lit/decorators.js'; +import { createRef, type Ref, ref } from 'lit/directives/ref.js'; +import './base-form'; +import { msg } from '@lit/localize'; +import { FetchError, type FetchResponse, ofetch } from 'ofetch'; +import type { BaseForm } from './base-form'; +import { + allowAnonymousCommentsContext, + baseUrlContext, + currentUserContext, + toastContext, +} from './context'; +import type { ToastManager } from './lit-toast'; +import type { ProblemDetail } from './types'; +import { + type CaptchaRequiredResponse, + getCaptchaCodeHeader, + isRequireCaptcha, +} from './utils/captcha'; + +export class ReplyForm extends LitElement { + @consume({ context: baseUrlContext }) + @state() + baseUrl = ''; + + @consume({ context: currentUserContext, subscribe: true }) + @state() + currentUser: User | undefined; + + @property({ type: Object }) + comment: CommentVo | undefined; + + @property({ type: Object }) + quoteReply: ReplyVo | undefined; + + @consume({ context: allowAnonymousCommentsContext, subscribe: true }) + @state() + allowAnonymousComments = false; + + @consume({ context: toastContext, subscribe: true }) + @state() + toastManager: ToastManager | undefined; + + @state() + submitting = false; + + @state() + captcha = ''; + + baseFormRef: Ref = createRef(); + + override connectedCallback(): void { + super.connectedCallback(); + + setTimeout(() => { + this.scrollIntoView({ + block: 'center', + inline: 'start', + behavior: 'smooth', + }); + this.baseFormRef.value?.setFocus(); + }, 0); + } + + override render() { + return html` `; + } + + async onSubmit(e: CustomEvent) { + e.preventDefault(); + + this.submitting = true; + + const data = e.detail; + + const { displayName, email, website, content } = data || {}; + + const replyRequest: ReplyRequest = { + raw: content, + content: content, + // TODO: support user input + allowNotification: true, + }; + + if (this.quoteReply) { + replyRequest.quoteReply = this.quoteReply.metadata.name; + } + + if (!this.currentUser && !this.allowAnonymousComments) { + this.toastManager?.warn(msg('Please login first')); + this.submitting = false; + return; + } + + if (!this.currentUser && this.allowAnonymousComments) { + if (!displayName || !email) { + this.toastManager?.warn( + msg('Please log in or complete the information first') + ); + this.submitting = false; + return; + } else { + replyRequest.owner = { + displayName: displayName, + email: email, + website: website, + }; + } + } + + try { + const newReply = await ofetch( + `${this.baseUrl}/apis/api.halo.run/v1alpha1/comments/${this.comment?.metadata.name}/reply`, + { + method: 'POST', + headers: { + ...getCaptchaCodeHeader(data.captchaCode), + }, + body: replyRequest, + } + ); + + this.baseFormRef.value?.handleFetchCaptcha(); + + if (newReply.spec.approved) { + this.toastManager?.success(msg('Comment submitted successfully')); + } else { + this.toastManager?.success( + msg('Comment submitted successfully, pending review') + ); + } + + this.dispatchEvent(new CustomEvent('reload')); + window.dispatchEvent(new CustomEvent('halo:comment-reply:created')); + + this.baseFormRef.value?.resetForm(); + } catch (error) { + if (error instanceof FetchError) { + if ( + isRequireCaptcha( + error.response as FetchResponse + ) + ) { + const { captcha, detail } = + error.data as unknown as CaptchaRequiredResponse; + this.captcha = captcha; + this.toastManager?.warn(detail); + return; + } + + const problemDetail = error.data as unknown as ProblemDetail; + this.toastManager?.error( + [problemDetail?.title, problemDetail?.detail].join(' - ') || + msg('Comment failed, please try again later') + ); + return; + } + + this.toastManager?.error(msg('Comment failed, please try again later')); + } finally { + this.submitting = false; + } + } +} + +customElements.get('reply-form') || + customElements.define('reply-form', ReplyForm); + +declare global { + interface HTMLElementTagNameMap { + 'reply-form': ReplyForm; + } +} diff --git a/packages/comment-widget/src/reply-item.ts b/packages/comment-widget/src/reply-item.ts new file mode 100644 index 0000000..bedf6dc --- /dev/null +++ b/packages/comment-widget/src/reply-item.ts @@ -0,0 +1,195 @@ +import type { CommentVo, ReplyVo } from '@halo-dev/api-client'; +import { css, html, LitElement } from 'lit'; +import { property, state } from 'lit/decorators.js'; +import baseStyles from './styles/base'; +import './user-avatar'; +import './base-comment-item'; +import './reply-form'; +import { consume } from '@lit/context'; +import { msg } from '@lit/localize'; +import { when } from 'lit/directives/when.js'; +import { ofetch } from 'ofetch'; +import { getPolicyInstance } from './avatar/avatar-policy'; +import { LS_UPVOTED_REPLIES_KEY } from './constant'; +import { baseUrlContext } from './context'; + +export class ReplyItem extends LitElement { + @consume({ context: baseUrlContext }) + @state() + baseUrl = ''; + + @property({ type: Object }) + comment: CommentVo | undefined; + + @property({ type: Object }) + reply: ReplyVo | undefined; + + @property({ type: Object }) + activeQuoteReply: ReplyVo | undefined = undefined; + + @property({ type: Array }) + replies: ReplyVo[] = []; + + @state() + showReplyForm = false; + + @state() + upvoted = false; + + @state() + upvoteCount = 0; + + get quoteReply() { + const { quoteReply: quoteReplyName } = this.reply?.spec || {}; + if (!quoteReplyName) { + return undefined; + } + return this.replies.find((reply) => reply.metadata.name === quoteReplyName); + } + + get isQuoteReplyHovered() { + return this.activeQuoteReply?.metadata.name === this.reply?.metadata.name; + } + + handleSetActiveQuoteReply(quoteReply?: ReplyVo) { + this.dispatchEvent( + new CustomEvent('set-active-quote-reply', { + detail: { quoteReply }, + bubbles: true, + composed: true, + }) + ); + } + + override connectedCallback(): void { + super.connectedCallback(); + this.checkUpvotedStatus(); + + this.upvoteCount = this.reply?.stats.upvote || 0; + } + + checkUpvotedStatus() { + const upvotedReplies = JSON.parse( + localStorage.getItem(LS_UPVOTED_REPLIES_KEY) || '[]' + ); + + if (upvotedReplies.includes(this.reply?.metadata.name)) { + this.upvoted = true; + } + } + + handleToggleReplyForm() { + this.showReplyForm = !this.showReplyForm; + } + + async handleUpvote() { + const upvotedReplies = JSON.parse( + localStorage.getItem(LS_UPVOTED_REPLIES_KEY) || '[]' + ); + + if (upvotedReplies.includes(this.reply?.metadata.name)) { + return; + } + + const voteRequest = { + name: this.reply?.metadata.name, + plural: 'replies', + group: 'content.halo.run', + }; + + await ofetch(`${this.baseUrl}/apis/api.halo.run/v1alpha1/trackers/upvote`, { + method: 'POST', + body: voteRequest, + }); + + upvotedReplies.push(this.reply?.metadata.name); + localStorage.setItem( + LS_UPVOTED_REPLIES_KEY, + JSON.stringify(upvotedReplies) + ); + + this.upvoteCount += 1; + this.upvoted = true; + this.checkUpvotedStatus(); + } + + override render() { + return html` + + + + ${when( + this.showReplyForm, + () => html`
    + this.dispatchEvent(new CustomEvent('reload'))} + > +
    ` + )} + ${when( + this.quoteReply, + () => html` this.handleSetActiveQuoteReply(this.quoteReply)} + @mouseleave=${() => this.handleSetActiveQuoteReply()} + class="quote-badge cursor-pointer inline-flex items-center gap-1 px-2 py-1.5 rounded-base bg-muted-3 text-text-2 hover:-translate-y-0.5 hover:text-text-1 hover:bg-muted-2 transition-all text-sm font-medium" + >${this.quoteReply?.owner.displayName} + +
    ` + )} +
    + `; + } + + static override styles = [ + ...baseStyles, + css` + @unocss-placeholder; + `, + ]; +} + +customElements.get('reply-item') || + customElements.define('reply-item', ReplyItem); + +function handleReplyAvatar(reply: ReplyVo | undefined): string | undefined { + const avatarPolicy = getPolicyInstance(); + if (avatarPolicy === undefined) { + return reply?.owner.avatar; + } + return avatarPolicy.applyReplyPolicy(reply); +} + +declare global { + interface HTMLElementTagNameMap { + 'reply-item': ReplyItem; + } +} diff --git a/packages/comment-widget/src/styles/base.ts b/packages/comment-widget/src/styles/base.ts new file mode 100644 index 0000000..15ade4f --- /dev/null +++ b/packages/comment-widget/src/styles/base.ts @@ -0,0 +1,30 @@ +import resetStyles from '@unocss/reset/tailwind.css?inline'; +import { css, unsafeCSS } from 'lit'; + +const baseStyles = [ + unsafeCSS(resetStyles), + css` + :host { + font-size: var(--halo-cw-base-font-size, 1rem); + font-family: var( + --halo-cw-base-font-family, + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + Segoe UI, + Roboto, + Helvetica Neue, + Arial, + Noto Sans, + sans-serif, + 'Apple Color Emoji', + 'Segoe UI Emoji', + Segoe UI Symbol, + 'Noto Color Emoji' + ); + } + `, +]; + +export default baseStyles; diff --git a/packages/comment-widget/src/styles/content.css b/packages/comment-widget/src/styles/content.css new file mode 100644 index 0000000..f9fc959 --- /dev/null +++ b/packages/comment-widget/src/styles/content.css @@ -0,0 +1,302 @@ +.content { + word-wrap: break-word; + white-space: pre-wrap; + @apply text-text-1 text-sm leading-relaxed; +} + +.content details, +.content figcaption, +.content figure { + display: block; +} + +.content summary { + display: list-item; +} + +.content a { + background-color: transparent; + text-decoration: none; + @apply text-primary-1; +} + +.content b, +.content strong { + font-weight: 600; +} + +.content mark { + background-color: #fff8c5; + @apply text-text-1; +} + +.content sub, +.content sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +.content sub { + bottom: -0.25em; +} + +.content sup { + top: -0.5em; +} + +.content img { + border-style: none; + max-width: 100%; + box-sizing: content-box; +} + +.content code, +.content pre { + font-family: monospace; + font-size: 1em; +} + +.content figure { + margin: 1em 2.5em; +} + +.content hr { + box-sizing: content-box; + overflow: hidden; + background: transparent; + height: 0.25em; + padding: 0; + margin: 1.4em 0; + border: 0; + @apply bg-muted-1; +} + +.content a:hover { + text-decoration: underline; +} + +.content hr::before { + display: table; + content: ""; +} + +.content hr::after { + display: table; + clear: both; + content: ""; +} + +.content details summary { + cursor: pointer; +} + +.content a:focus { + outline: 2px solid; + outline-offset: -2px; + box-shadow: none; + @apply outline-primary-1; +} + +.content a:focus:not(:focus-visible) { + outline: solid 1px transparent; +} + +.content a:focus-visible { + outline: 2px solid; + outline-offset: -2px; + box-shadow: none; + @apply outline-primary-1; +} + +.content a:not([class]):focus, +.content a:not([class]):focus-visible { + outline-offset: 0; +} + +.content p { + margin-top: 0; + margin-bottom: 10px; +} + +.content blockquote { + margin: 0; + padding: 0 1em; + border-left: 0.25em; + border-style: solid; + @apply text-text-1 border-muted-1; +} + +.content ul, +.content ol { + margin-top: 0; + margin-bottom: 0; + padding-left: 2em; +} + +.content ol ol, +.content ul ol { + list-style-type: lower-roman; +} + +.content ul ul ol, +.content ul ol ol, +.content ol ul ol, +.content ol ol ol { + list-style-type: lower-alpha; +} + +.content ul { + list-style-type: disc; +} + +.content ol { + list-style-type: decimal; +} + +.content code { + font-family: + ui-monospace, + SFMono-Regular, + SF Mono, + Menlo, + Consolas, + "Liberation Mono", + monospace; + font-size: 12px; +} + +.content pre { + margin-top: 0; + margin-bottom: 0; + font-family: + ui-monospace, + SFMono-Regular, + SF Mono, + Menlo, + Consolas, + "Liberation Mono", + monospace; + font-size: 12px; + word-wrap: normal; +} + +.content::before { + display: table; + content: ""; +} + +.content::after { + display: table; + clear: both; + content: ""; +} + +.content > *:first-child { + margin-top: 0 !important; +} + +.content > *:last-child { + margin-bottom: 0 !important; +} + +.content a:not([href]) { + color: inherit; + text-decoration: none; +} + +.content p, +.content blockquote, +.content ul, +.content ol, +.content dl, +.content pre, +.content details { + margin-top: 0; + margin-bottom: 1em; +} + +.content blockquote > :first-child { + margin-top: 0; +} + +.content blockquote > :last-child { + margin-bottom: 0; +} + +.content ul ul, +.content ul ol, +.content ol ol, +.content ol ul { + margin-top: 0; + margin-bottom: 0; +} + +.content li > p { + margin-top: 1em; +} + +.content li + li { + margin-top: 0.25em; +} + +.content code { + padding: 0.2em 0.4em; + margin: 0; + font-size: 85%; + white-space: break-spaces; + border-radius: 6px; + @apply bg-muted-3; +} + +.content code br { + display: none; +} + +.content del code { + text-decoration: inherit; +} + +.content pre code { + font-size: 100%; +} + +.content pre > code { + padding: 0; + margin: 0; + word-break: normal; + white-space: pre; + background: transparent; + border: 0; + color: #fff; +} + +.content pre { + padding: 1em; + overflow: auto; + font-size: 85%; + border-radius: 6px; +} + +.content pre code { + display: inline; + max-width: auto; + padding: 0; + margin: 0; + overflow: visible; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0; +} + +.content summary:focus:not(:focus-visible), +.content a:focus:not(:focus-visible) { + outline: none; + box-shadow: none; +} + +.content > *:first-child > .heading-element:first-child { + margin-top: 0 !important; +} diff --git a/packages/comment-widget/src/types/index.ts b/packages/comment-widget/src/types/index.ts new file mode 100644 index 0000000..6854bef --- /dev/null +++ b/packages/comment-widget/src/types/index.ts @@ -0,0 +1,46 @@ +export interface ConfigMapData { + basic: BasicConfig; + security: SecurityConfig; + avatar: AvatarConfig; + editor?: EditorConfig; +} + +interface BasicConfig { + withReplies: boolean; + size: number; + withReplySize: number; + replySize: number; + showCommenterDevice?: boolean; + enablePrivateComment?: boolean; + showPrivateCommentBadge?: boolean; +} + +interface SecurityConfig { + captcha: { + anonymousCommentCaptcha: boolean; + type: 'ALPHANUMERIC' | 'ARITHMETIC'; + ignoreCase: boolean; + captchaLength: number; + }; +} + +interface AvatarConfig { + provider: 'gravatar'; + enable: boolean; + providerMirror: string; + policy: 'anonymousUser' | 'allUser' | 'noAvatarUser'; +} + +interface EditorConfig { + placeholder?: string; +} + +export interface ProblemDetail { + type: string; + title: string; + status: number; + detail: string; + instance: string; + requestId: string; + timestamp: string; +} diff --git a/packages/comment-widget/src/user-avatar.ts b/packages/comment-widget/src/user-avatar.ts new file mode 100644 index 0000000..692e74b --- /dev/null +++ b/packages/comment-widget/src/user-avatar.ts @@ -0,0 +1,111 @@ +import './icons/icon-loading'; +import { css, html, LitElement } from 'lit'; +import { property, state } from 'lit/decorators.js'; +import baseStyles from './styles/base'; + +export class UserAvatar extends LitElement { + @property({ type: String }) + src: string | undefined; + + @property({ type: String }) + alt: string | undefined; + + @property({ type: String }) + href: string | undefined; + + @state() + error = false; + + @state() + loading = false; + + override async connectedCallback() { + super.connectedCallback(); + await this.loadImage(); + } + + async loadImage() { + if (!this.src) { + return Promise.resolve(); + } + this.loading = true; + try { + await new Promise((resolve) => { + const img = new Image(); + img.src = this.src || ''; + img.onload = () => { + this.error = false; + resolve(null); + }; + img.onerror = () => { + this.error = true; + resolve(null); + }; + }); + } catch (_e) { + this.error = true; + } finally { + this.loading = false; + } + } + + getPlaceholderText() { + if (!this.alt) { + return undefined; + } + const words = this.alt.split(' '); + if (words.length === 1) { + return words[0].charAt(0).toUpperCase(); + } + if (words.length > 1) { + return ( + words[0].charAt(0).toUpperCase() + words[1].charAt(0).toUpperCase() + ); + } + return undefined; + } + + override render() { + if (this.href) { + return html` + ${this.renderAvatarContent()} + `; + } + + return html`
    + ${this.renderAvatarContent()} +
    `; + } + + renderAvatarContent() { + if (!this.src) { + return html`${this.getPlaceholderText()}`; + } + + if (this.loading) { + return html``; + } + + if (this.error) { + return html``; + } + + return html`${this.alt || ''}`; + } + + static override styles = [ + ...baseStyles, + css` + @unocss-placeholder; + `, + ]; +} + +customElements.get('user-avatar') || + customElements.define('user-avatar', UserAvatar); + +declare global { + interface HTMLElementTagNameMap { + 'user-avatar': UserAvatar; + } +} diff --git a/packages/comment-widget/src/utils/captcha.ts b/packages/comment-widget/src/utils/captcha.ts new file mode 100644 index 0000000..d2be983 --- /dev/null +++ b/packages/comment-widget/src/utils/captcha.ts @@ -0,0 +1,24 @@ +import type { FetchResponse } from 'ofetch'; + +export const getCaptchaCodeHeader = (code: string): Record => { + if (!code || code.trim().length === 0) { + return {}; + } + return { + 'X-Captcha-Code': code, + }; +}; + +export interface CaptchaRequiredResponse { + type: string; + title: string; + status: number; + detail: string; + captcha: string; +} + +export const isRequireCaptcha = ( + response: FetchResponse +) => { + return response.status === 403 && response.headers.get('X-Require-Captcha'); +}; diff --git a/packages/comment-widget/src/utils/date.ts b/packages/comment-widget/src/utils/date.ts new file mode 100644 index 0000000..e21fca8 --- /dev/null +++ b/packages/comment-widget/src/utils/date.ts @@ -0,0 +1,41 @@ +import dayjs from 'dayjs'; +import 'dayjs/locale/zh-cn'; +import 'dayjs/locale/zh-tw'; +import 'dayjs/locale/en'; +import 'dayjs/locale/es'; +import relativeTime from 'dayjs/plugin/relativeTime'; +import timezone from 'dayjs/plugin/timezone'; +import { getLocale } from '../locale'; + +const localeMap = { + 'zh-CN': 'zh-cn', + 'zh-TW': 'zh-tw', + en: 'en', + es: 'es', +}; + +dayjs.extend(timezone); +dayjs.extend(relativeTime); + +dayjs.locale(localeMap[getLocale()]); + +export function formatDate(date: string | Date | undefined | null): string { + if (!date) { + return ''; + } + return dayjs(date).format('YYYY-MM-DD HH:mm'); +} + +export function timeAgo(date: string | Date | undefined | null): string { + if (!date) { + return ''; + } + + const currentDate = new Date(); + + if (currentDate.getFullYear() - new Date(date).getFullYear() > 0) { + return formatDate(new Date(date)); + } + + return dayjs().to(dayjs(date)); +} diff --git a/packages/comment-widget/src/utils/html.ts b/packages/comment-widget/src/utils/html.ts new file mode 100644 index 0000000..f20202a --- /dev/null +++ b/packages/comment-widget/src/utils/html.ts @@ -0,0 +1,13 @@ +import sanitizeHtml from 'sanitize-html'; +export function cleanHtml(content?: string) { + if (!content) { + return ''; + } + + return sanitizeHtml(content, { + allowedAttributes: { + ...sanitizeHtml.defaults.allowedAttributes, + code: ['class'], + }, + }); +} diff --git a/packages/comment-widget/src/vite-env.d.ts b/packages/comment-widget/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/packages/comment-widget/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/comment-widget/tsconfig.json b/packages/comment-widget/tsconfig.json new file mode 100644 index 0000000..e3cf775 --- /dev/null +++ b/packages/comment-widget/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "es2021", + "module": "es2020", + "lib": ["es2021", "DOM", "DOM.Iterable"], + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "inlineSources": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitThis": true, + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "noImplicitOverride": true, + "skipLibCheck": true, + "plugins": [ + { + "name": "ts-lit-plugin", + "strict": true + } + ] + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/comment-widget/uno.config.ts b/packages/comment-widget/uno.config.ts new file mode 100644 index 0000000..bb5058f --- /dev/null +++ b/packages/comment-widget/uno.config.ts @@ -0,0 +1,71 @@ +import { + defineConfig, + definePreset, + presetIcons, + presetWind3, + transformerDirectives, +} from 'unocss'; + +const remRE = /(-?[.\d]+)rem/g; + +const presetRemToEm = definePreset(() => { + return { + name: '@unocss/preset-rem-to-em', + postprocess: (util) => { + util.entries.forEach((i) => { + const value = i[1]; + if (typeof value === 'string' && remRE.test(value)) + i[1] = value.replace(remRE, (_, p1) => `${p1}em`); + }); + }, + }; +}); + +export default defineConfig({ + presets: [presetWind3(), presetIcons(), presetRemToEm()], + theme: { + colors: { + primary: { + 1: 'var(--halo-cw-primary-1-color, #4CCBA0)', + 2: 'var(--halo-cw-primary-2-color, #6EE7B7)', + 3: 'var(--halo-cw-primary-3-color, #99F6E4)', + }, + text: { + 1: 'var(--halo-cw-text-1-color, #0f172a)', + 2: 'var(--halo-cw-text-2-color, #1e293b)', + 3: 'var(--halo-cw-text-3-color, #475569)', + }, + muted: { + 1: 'var(--halo-cw-muted-1-color, #cbd5e1)', + 2: 'var(--halo-cw-muted-2-color, #e2e8f0)', + 3: 'var(--halo-cw-muted-3-color, #f1f5f9)', + }, + }, + borderRadius: { + base: 'var(--halo-cw-base-rounded, 0.5em)', + avatar: 'var(--halo-cw-avatar-rounded, 9999px)', + }, + }, + rules: [ + [ + 'shadow-input', + { + 'box-shadow': '0 0 0 3px var(--halo-cw-primary-3-color, #99F6E4)', + }, + ], + ], + shortcuts: { + input: + 'px-3 py-0 text-sm rounded-base bg-transparent border h-12 border-muted-1 border-solid outline-none transition-all focus:border-primary-1 focus:shadow-input placeholder:text-text-3', + 'icon-button': 'inline-flex items-center gap-[0.1em] cursor-pointer', + 'icon-button-icon': + 'inline-flex items-center justify-center rounded-full transition-all duration-150 p-2 text-text-3 group-hover:bg-muted-3 group-hover:text-text-1', + 'icon-button-text': + 'select-none text-xs text-text-3 group-hover:text-text-1', + 'pagination-button': + 'inline-flex h-10 items-center text-sm gap-1 hover:bg-muted-3 rounded-base px-3 transition-all text-text-1 opacity-80 disabled:!opacity-70 disabled:cursor-not-allowed hover:opacity-100 font-medium justify-center', + avatar: + 'rounded-avatar size-[var(--halo-cw-avatar-size,2.25em)] overflow-hidden inline-flex items-center justify-center bg-muted-2', + }, + transformers: [transformerDirectives()], +}); diff --git a/packages/comment-widget/var.css b/packages/comment-widget/var.css new file mode 100644 index 0000000..f53638d --- /dev/null +++ b/packages/comment-widget/var.css @@ -0,0 +1,50 @@ +@media (prefers-color-scheme: dark) { + .color-scheme-auto, + [data-color-scheme="auto"] { + color-scheme: dark; + + --halo-cw-primary-1-color: #059669; + --halo-cw-primary-2-color: #047857; + --halo-cw-primary-3-color: #065f46; + + --halo-cw-text-1-color: #f9fafb; + --halo-cw-text-2-color: #e5e7eb; + --halo-cw-text-3-color: #9ca3af; + + --halo-cw-muted-1-color: #4b5563; + --halo-cw-muted-2-color: #374151; + --halo-cw-muted-3-color: #1f2937; + + --halo-cw-emoji-picker-rgb-color: 222, 222, 221; + --halo-cw-emoji-picker-rgb-accent: 58, 130, 247; + --halo-cw-emoji-picker-rgb-background: 21, 22, 23; + --halo-cw-emoji-picker-rgb-input: 0, 0, 0; + --halo-cw-emoji-picker-color-border: rgba(255, 255, 255, 0.1); + --halo-cw-emoji-picker-color-border-over: rgba(255, 255, 255, 0.2); + } +} + +.color-scheme-dark, +.dark, +[data-color-scheme="dark"] { + color-scheme: dark; + + --halo-cw-primary-1-color: #059669; + --halo-cw-primary-2-color: #047857; + --halo-cw-primary-3-color: #065f46; + + --halo-cw-text-1-color: #f9fafb; + --halo-cw-text-2-color: #e2e8f0; + --halo-cw-text-3-color: #94a3b8; + + --halo-cw-muted-1-color: #64748b; + --halo-cw-muted-2-color: #475569; + --halo-cw-muted-3-color: #334155; + + --halo-cw-emoji-picker-rgb-color: 222, 222, 221; + --halo-cw-emoji-picker-rgb-accent: 58, 130, 247; + --halo-cw-emoji-picker-rgb-background: 21, 22, 23; + --halo-cw-emoji-picker-rgb-input: 0, 0, 0; + --halo-cw-emoji-picker-color-border: rgba(255, 255, 255, 0.1); + --halo-cw-emoji-picker-color-border-over: rgba(255, 255, 255, 0.2); +} diff --git a/packages/comment-widget/vite.config.ts b/packages/comment-widget/vite.config.ts new file mode 100644 index 0000000..711468b --- /dev/null +++ b/packages/comment-widget/vite.config.ts @@ -0,0 +1,24 @@ +import UnoCSS from 'unocss/vite'; +import { defineConfig, type Plugin } from 'vite'; +import dts from 'vite-plugin-dts'; + +export default defineConfig({ + experimental: { + enableNativePlugin: true, + }, + plugins: [ + dts() as Plugin, + UnoCSS({ + mode: 'shadow-dom', + configFile: './uno.config.ts', + }), + ], + build: { + lib: { + entry: ['src/index.ts'], + formats: ['es'], + fileName: 'index', + }, + minify: false, + }, +}); diff --git a/packages/comment-widget/xliff/es.xlf b/packages/comment-widget/xliff/es.xlf new file mode 100644 index 0000000..37967bf --- /dev/null +++ b/packages/comment-widget/xliff/es.xlf @@ -0,0 +1,167 @@ + + + + + + Logout + Cerrar sesión + + + Write a comment + Escribir un comentario + + + Nicename + Apodo + + + Email + Correo electrónico + + + Website + Sitio web + + + Login + Iniciar sesión + + + Submit + Enviar comentario + + + Cancel reply + Cancelar respuesta + + + Reply + Responder + + + Previous + Anterior + + + Next + Siguiente + + + (Or login) + (O iniciar sesión) + + + Please login first + Por favor, inicie sesión primero + + + Load more + Cargar más + + + Comments + Comentarios + + + Reviewing + En revisión + + + Select emoticon + Seleccionar emoticono + + + Failed to obtain verification code + Error al obtener el código de verificación + + + Please enter the verification code + Por favor, ingrese el código de verificación + + + Please log in or complete the information first + Por favor, inicie sesión o complete la información primero + + + Comment failed, please try again later + Error al comentar, por favor intente más tarde + + + Comment submitted successfully + Comentario enviado con éxito + + + Comment submitted successfully, pending review + Comentario enviado con éxito, pendiente de revisión + + + Add reply + Agregar respuesta + + + Failed to load comment list, please try again later + Error al cargar la lista de comentarios, por favor intente más tarde + + + Failed to load reply list, please try again later + Error al cargar la lista de respuestas, por favor intente más tarde + + + Click OK to jump to the logout page, Please make sure the content being edited has been saved. + Haga clic en OK para ir a la página de cierre de sesión, Por favor, asegúrese de que el contenido editado haya sido guardado. + + + Please enter content + Por favor, ingrese el contenido + + + Bold + Negrita + + + Italic + Itálica + + + Underline + Subrayado + + + Strike + Tachado + + + Code + Código + + + Blockquote + Cita + + + Code Block + Bloque de código + + + Upvote + Me gusta + + + Show replies + Mostrar respuestas + + + Private + Íntimo + + + Currently logged in. After selecting the private option, comments will only be visible to yourself and the site administrator. + Actualmente conectado. Después de seleccionar la opción privada, los comentarios solo serán visibles para usted y el administrador del sitio. + + + You are currently anonymous. After selecting the private option, the comment will only be visible to the site administrator. + Actualmente anónimo. Después de seleccionar la opción privada, el comentario solo será visible para el administrador del sitio. + + + + diff --git a/packages/comment-widget/xliff/zh-CN.xlf b/packages/comment-widget/xliff/zh-CN.xlf new file mode 100644 index 0000000..1289dc6 --- /dev/null +++ b/packages/comment-widget/xliff/zh-CN.xlf @@ -0,0 +1,167 @@ + + + + + + Logout + 退出登录 + + + Write a comment + 编写评论 + + + Nicename + 昵称 + + + Email + 电子邮件 + + + Website + 网站 + + + Login + 登录 + + + Submit + 提交评论 + + + Cancel reply + 取消回复 + + + Reply + 回复 + + + Previous + 上一页 + + + Next + 下一页 + + + (Or login) + (或登录账号) + + + Please login first + 请先登录 + + + Load more + 加载更多 + + + Comments + 条评论 + + + Reviewing + 审核中 + + + Select emoticon + 选择表情 + + + Failed to obtain verification code + 获取验证码失败 + + + Please enter the verification code + 请输入验证码 + + + Please log in or complete the information first + 请先登录或者完善信息 + + + Comment failed, please try again later + 评论失败,请稍后重试 + + + Comment submitted successfully + 评论成功 + + + Comment submitted successfully, pending review + 评论成功,请等待审核 + + + Add reply + 加入回复 + + + Failed to load comment list, please try again later + 加载评论列表失败,请稍后重试 + + + Failed to load reply list, please try again later + 加载回复列表失败,请稍后重试 + + + Click OK to jump to the logout page, Please make sure the content being edited has been saved. + 点击确定将跳转至退出登录页面,请确保正在编辑的内容已保存。 + + + Please enter content + 请输入内容 + + + Bold + 粗体 + + + Italic + 斜体 + + + Underline + 下划线 + + + Strike + 删除线 + + + Code + 代码 + + + Blockquote + 引用 + + + Code Block + 代码块 + + + Upvote + 点赞 + + + Show replies + 显示回复 + + + Private + 私密 + + + Currently logged in. After selecting the private option, comments will only be visible to yourself and the site administrator. + 当前已登录,选择私密选项后,评论将仅对您和网站管理员可见。 + + + You are currently anonymous. After selecting the private option, the comment will only be visible to the site administrator. + 当前是匿名状态,选择私密选项后,评论将仅对网站管理员可见。 + + + + diff --git a/packages/comment-widget/xliff/zh-TW.xlf b/packages/comment-widget/xliff/zh-TW.xlf new file mode 100644 index 0000000..d536733 --- /dev/null +++ b/packages/comment-widget/xliff/zh-TW.xlf @@ -0,0 +1,167 @@ + + + + + + Logout + 登出 + + + Write a comment + 撰寫評論 + + + Nicename + 暱稱 + + + Email + 電子郵件 + + + Website + 網站 + + + Login + 登入 + + + Submit + 提交評論 + + + Cancel reply + 取消回覆 + + + Reply + 回覆 + + + Previous + 上一頁 + + + Next + 下一頁 + + + (Or login) + (或登入帳號) + + + Please login first + 請先登入 + + + Load more + 載入更多 + + + Comments + 則評論 + + + Reviewing + 審核中 + + + Select emoticon + 選擇表情 + + + Failed to obtain verification code + 獲取驗證碼失敗 + + + Please enter the verification code + 請輸入驗證碼 + + + Please log in or complete the information first + 請先登入或者完善資訊 + + + Comment failed, please try again later + 評論失敗,請稍後重試 + + + Comment submitted successfully + 評論成功 + + + Comment submitted successfully, pending review + 評論成功,請等待審核 + + + Add reply + 新增回覆 + + + Failed to load comment list, please try again later + 載入評論列表失敗,請稍後重試 + + + Failed to load reply list, please try again later + 載入回覆列表失敗,請稍後重試 + + + Click OK to jump to the logout page, Please make sure the content being edited has been saved. + 点击确定将跳转至退出登录页面,请确保正在编辑的内容已保存。 + + + Please enter content + 請輸入內容 + + + Bold + 粗體 + + + Italic + 斜體 + + + Underline + 底線 + + + Strike + 刪除線 + + + Code + 程式碼 + + + Blockquote + 引用 + + + Code Block + 程式碼區塊 + + + Upvote + + + + Show replies + 顯示回覆 + + + Private + 私密 + + + Currently logged in. After selecting the private option, comments will only be visible to yourself and the site administrator. + 目前已登入,選擇私密選項後,評論將僅對您和網站管理員可見。 + + + You are currently anonymous. After selecting the private option, the comment will only be visible to the site administrator. + 目前是匿名狀態,選擇私密選項後,評論將僅對網站管理員可見。 + + + + diff --git a/packages/example/env.d.ts b/packages/example/env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/packages/example/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/example/index.html b/packages/example/index.html new file mode 100644 index 0000000..6c557d4 --- /dev/null +++ b/packages/example/index.html @@ -0,0 +1,64 @@ + + + + + + + @halo-dev/comment-widget - example + + + + + + + + + + + diff --git a/packages/example/package.json b/packages/example/package.json new file mode 100644 index 0000000..cca88ce --- /dev/null +++ b/packages/example/package.json @@ -0,0 +1,8 @@ +{ + "scripts": { + "dev": "vite" + }, + "dependencies": { + "@halo-dev/comment-widget": "workspace:*" + } +} diff --git a/packages/example/src/main.ts b/packages/example/src/main.ts new file mode 100644 index 0000000..2e1cf41 --- /dev/null +++ b/packages/example/src/main.ts @@ -0,0 +1,2 @@ +import '@halo-dev/comment-widget'; +import '@halo-dev/comment-widget/var.css'; diff --git a/packages/example/tsconfig.json b/packages/example/tsconfig.json new file mode 100644 index 0000000..596e2cf --- /dev/null +++ b/packages/example/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src"] +} diff --git a/packages/example/vite.config.ts b/packages/example/vite.config.ts new file mode 100644 index 0000000..c4a14ae --- /dev/null +++ b/packages/example/vite.config.ts @@ -0,0 +1,28 @@ +import { fileURLToPath, URL } from 'node:url'; + +import { defineConfig } from 'vite'; + +export default defineConfig({ + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + server: { + port: 4000, + proxy: { + '/actuator': { + target: 'http://localhost:8090', + changeOrigin: true, + }, + '/apis': { + target: 'http://localhost:8090', + changeOrigin: true, + }, + '/upload': { + target: 'http://localhost:8090', + changeOrigin: true, + }, + }, + }, +}); diff --git a/packages/ui/env.d.ts b/packages/ui/env.d.ts new file mode 100644 index 0000000..91db69d --- /dev/null +++ b/packages/ui/env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 0000000..5d32d86 --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,23 @@ +{ + "type": "module", + "scripts": { + "build": "rsbuild build", + "dev": "rsbuild build --watch --env-mode=development", + "type-check": "vue-tsc --build" + }, + "dependencies": { + "@halo-dev/comment-widget": "workspace:*", + "@halo-dev/components": "^2.21.0", + "@halo-dev/console-shared": "link:/Users/ryanwang/Workspace/github/ruibaby/halo-next/ui/packages/shared", + "vue": "^3.5.18" + }, + "devDependencies": { + "@halo-dev/ui-plugin-bundler-kit": "^2.21.2", + "@rsbuild/core": "^1.4.15", + "@rsbuild/plugin-vue": "^1.1.1", + "@types/node": "^20.19.10", + "@vue/tsconfig": "^0.7.0", + "typescript": "~5.8.3", + "vue-tsc": "^2.2.12" + } +} diff --git a/packages/ui/rsbuild.config.mjs b/packages/ui/rsbuild.config.mjs new file mode 100644 index 0000000..ed636a7 --- /dev/null +++ b/packages/ui/rsbuild.config.mjs @@ -0,0 +1,36 @@ +import { rsbuildConfig } from '@halo-dev/ui-plugin-bundler-kit'; +import { pluginVue } from '@rsbuild/plugin-vue'; + +const MANIFEST_PATH = '../../src/main/resources/plugin.yaml'; +const OUT_DIR_PROD = '../../src/main/resources/console'; +const OUT_DIR_DEV = '../../build/resources/main/console'; + +export default rsbuildConfig({ + manifestPath: MANIFEST_PATH, + rsbuild: ({ envMode }) => { + const isProduction = envMode === 'production'; + const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV; + + return { + resolve: { + alias: { + '@': './src', + }, + }, + plugins: [ + pluginVue({ + vueLoaderOptions: { + compilerOptions: { + isCustomElement: (tag) => tag.startsWith('comment-'), + }, + }, + }), + ], + output: { + distPath: { + root: outDir, + }, + }, + }; + }, +}); diff --git a/packages/ui/src/components/Content.vue b/packages/ui/src/components/Content.vue new file mode 100644 index 0000000..c8a73eb --- /dev/null +++ b/packages/ui/src/components/Content.vue @@ -0,0 +1,10 @@ + + diff --git a/packages/ui/src/components/Editor.vue b/packages/ui/src/components/Editor.vue new file mode 100644 index 0000000..7547918 --- /dev/null +++ b/packages/ui/src/components/Editor.vue @@ -0,0 +1,33 @@ + + diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts new file mode 100644 index 0000000..78ac7fb --- /dev/null +++ b/packages/ui/src/index.ts @@ -0,0 +1,29 @@ +import { VLoading } from '@halo-dev/components'; +import { definePlugin } from '@halo-dev/console-shared'; +import { defineAsyncComponent, markRaw } from 'vue'; + +export default definePlugin({ + components: {}, + routes: [], + extensionPoints: { + 'comment:editor:replace': () => { + return { + component: markRaw( + defineAsyncComponent({ + loader: () => import('./components/Editor.vue'), + loadingComponent: VLoading, + }) + ), + }; + }, + 'comment:list-item:content:replace': () => { + return { + component: markRaw( + defineAsyncComponent({ + loader: () => import('./components/Content.vue'), + }) + ), + }; + }, + }, +}); diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json new file mode 100644 index 0000000..913b8f2 --- /dev/null +++ b/packages/ui/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/packages/widget/package.json b/packages/widget/package.json new file mode 100644 index 0000000..86e2e85 --- /dev/null +++ b/packages/widget/package.json @@ -0,0 +1,12 @@ +{ + "scripts": { + "build": "tsc && rslib build", + "dev": "rslib build --watch" + }, + "dependencies": { + "@halo-dev/comment-widget": "workspace:*" + }, + "devDependencies": { + "@rslib/core": "^0.12.1" + } +} diff --git a/packages/widget/rslib.config.mjs b/packages/widget/rslib.config.mjs new file mode 100644 index 0000000..0d6b5f5 --- /dev/null +++ b/packages/widget/rslib.config.mjs @@ -0,0 +1,32 @@ +import { fileURLToPath } from 'node:url'; +import { defineConfig } from '@rslib/core'; + +const PLUGIN_NAME = 'PluginCommentWidget'; + +export default defineConfig({ + lib: [ + { + format: 'esm', + autoExternal: false, + }, + ], + output: { + target: 'web', + minify: true, + cleanDistPath: true, + filename: { + js: (pathData) => { + if (pathData.chunk.name === 'index') { + return 'comment-widget.js'; + } + return '[name].[contenthash:8].js'; + }, + }, + publicPath: `/plugins/${PLUGIN_NAME}/assets/static/`, + distPath: { + root: fileURLToPath( + new URL('../../src/main/resources/static', import.meta.url) + ), + }, + }, +}); diff --git a/packages/widget/src/env.d.ts b/packages/widget/src/env.d.ts new file mode 100644 index 0000000..ce107c1 --- /dev/null +++ b/packages/widget/src/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/widget/src/index.ts b/packages/widget/src/index.ts new file mode 100644 index 0000000..6702915 --- /dev/null +++ b/packages/widget/src/index.ts @@ -0,0 +1,41 @@ +import { CommentWidget } from '@halo-dev/comment-widget'; +import '@halo-dev/comment-widget/var.css'; + +export { CommentWidget }; + +interface Props { + group: string; + kind: string; + name: string; +} + +export function init(el: string, props: Props) { + const parent = document.querySelector(el) as HTMLElement; + + if (!parent) { + console.error('Element not found', el); + } + + const commentWidget = document.createElement( + 'comment-widget' + ) as CommentWidget; + + commentWidget.kind = props.kind; + commentWidget.group = props.group; + commentWidget.version = 'v1alpha1'; + commentWidget.name = props.name; + + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting && parent.childElementCount === 0) { + parent.appendChild(commentWidget); + + parent.animate([{ opacity: 0 }, { opacity: 1 }], { + duration: 300, + fill: 'forwards', + }); + } + }); + }); + observer.observe(parent as Element); +} diff --git a/packages/widget/tsconfig.json b/packages/widget/tsconfig.json new file mode 100644 index 0000000..596e2cf --- /dev/null +++ b/packages/widget/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..003c87b --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,6070 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + vite: npm:rolldown-vite@7.0.10 + +importers: + + .: + dependencies: + shiki: + specifier: ^3.9.2 + version: 3.9.2 + devDependencies: + '@biomejs/biome': + specifier: ^2.2.0 + version: 2.2.0 + '@tsconfig/node18': + specifier: ^18.2.4 + version: 18.2.4 + '@types/node': + specifier: ^18.19.123 + version: 18.19.123 + husky: + specifier: ^9.1.7 + version: 9.1.7 + lint-staged: + specifier: ^16.1.5 + version: 16.1.5 + typescript: + specifier: ~5.3.3 + version: 5.3.3 + vite: + specifier: npm:rolldown-vite@7.0.10 + version: rolldown-vite@7.0.10(@types/node@18.19.123)(esbuild@0.25.8)(jiti@2.5.1)(terser@5.43.1)(yaml@2.8.1) + vite-plugin-dts: + specifier: ^4.5.4 + version: 4.5.4(@types/node@18.19.123)(rolldown-vite@7.0.10(@types/node@18.19.123)(esbuild@0.25.8)(jiti@2.5.1)(terser@5.43.1)(yaml@2.8.1))(rollup@4.46.2)(typescript@5.3.3) + + packages/comment-widget: + dependencies: + '@emoji-mart/data': + specifier: ^1.2.1 + version: 1.2.1 + '@floating-ui/dom': + specifier: ^1.7.3 + version: 1.7.3 + '@halo-dev/api-client': + specifier: https://pkg.pr.new/@halo-dev/api-client@7679 + version: https://pkg.pr.new/@halo-dev/api-client@7679(axios@1.11.0) + '@lit/context': + specifier: ^1.1.6 + version: 1.1.6 + '@lit/localize': + specifier: ^0.12.2 + version: 0.12.2 + '@tiptap/core': + specifier: ^3.2.0 + version: 3.2.0(@tiptap/pm@3.2.0) + '@tiptap/extensions': + specifier: ^3.2.0 + version: 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + '@tiptap/pm': + specifier: ^3.2.0 + version: 3.2.0 + '@tiptap/starter-kit': + specifier: ^3.2.0 + version: 3.2.0 + dayjs: + specifier: ^1.11.13 + version: 1.11.13 + emoji-mart: + specifier: ^5.6.0 + version: 5.6.0 + es-toolkit: + specifier: ^1.39.9 + version: 1.39.9 + javascript-time-ago: + specifier: ^2.5.11 + version: 2.5.11 + lit: + specifier: ^3.3.1 + version: 3.3.1 + ofetch: + specifier: ^1.4.1 + version: 1.4.1 + sanitize-html: + specifier: ^2.17.0 + version: 2.17.0 + tiptap-extension-code-block-shiki: + specifier: ^0.6.0 + version: 0.6.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/extension-code-block@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)(shiki@3.9.2) + ua-parser-js: + specifier: ^2.0.4 + version: 2.0.4 + devDependencies: + '@iconify/json': + specifier: ^2.2.373 + version: 2.2.373 + '@lit/localize-tools': + specifier: ^0.8.0 + version: 0.8.0 + '@types/sanitize-html': + specifier: ^2.16.0 + version: 2.16.0 + '@unocss/reset': + specifier: ^66.4.2 + version: 66.4.2 + lit-analyzer: + specifier: ^2.0.3 + version: 2.0.3 + unocss: + specifier: ^66.4.2 + version: 66.4.2(postcss@8.5.6)(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + + packages/example: + dependencies: + '@halo-dev/comment-widget': + specifier: workspace:* + version: link:../comment-widget + + packages/ui: + dependencies: + '@halo-dev/comment-widget': + specifier: workspace:* + version: link:../comment-widget + '@halo-dev/components': + specifier: ^2.21.0 + version: 2.21.0(vue-router@4.5.1(vue@3.5.18(typescript@5.8.3)))(vue@3.5.18(typescript@5.8.3)) + '@halo-dev/console-shared': + specifier: link:/Users/ryanwang/Workspace/github/ruibaby/halo-next/ui/packages/shared + version: link:../../../../ruibaby/halo-next/ui/packages/shared + vue: + specifier: ^3.5.18 + version: 3.5.18(typescript@5.8.3) + devDependencies: + '@halo-dev/ui-plugin-bundler-kit': + specifier: ^2.21.2 + version: 2.21.2(@rsbuild/core@1.4.15)(@rsbuild/plugin-vue@1.1.1(@rsbuild/core@1.4.15)(esbuild@0.25.8)(vue@3.5.18(typescript@5.8.3)))(@vitejs/plugin-vue@6.0.1(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.18(typescript@5.8.3)))(axios@1.11.0)(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@rsbuild/core': + specifier: ^1.4.15 + version: 1.4.15 + '@rsbuild/plugin-vue': + specifier: ^1.1.1 + version: 1.1.1(@rsbuild/core@1.4.15)(esbuild@0.25.8)(vue@3.5.18(typescript@5.8.3)) + '@types/node': + specifier: ^20.19.10 + version: 20.19.10 + '@vue/tsconfig': + specifier: ^0.7.0 + version: 0.7.0(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3)) + typescript: + specifier: ~5.8.3 + version: 5.8.3 + vue-tsc: + specifier: ^2.2.12 + version: 2.2.12(typescript@5.8.3) + + packages/widget: + dependencies: + '@halo-dev/comment-widget': + specifier: workspace:* + version: link:../comment-widget + devDependencies: + '@rslib/core': + specifier: ^0.12.1 + version: 0.12.1(@microsoft/api-extractor@7.52.10(@types/node@20.19.10))(typescript@5.8.3) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + + '@antfu/utils@9.2.0': + resolution: {integrity: sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw==} + + '@ast-grep/napi-darwin-arm64@0.37.0': + resolution: {integrity: sha512-QAiIiaAbLvMEg/yBbyKn+p1gX2/FuaC0SMf7D7capm/oG4xGMzdeaQIcSosF4TCxxV+hIH4Bz9e4/u7w6Bnk3Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@ast-grep/napi-darwin-x64@0.37.0': + resolution: {integrity: sha512-zvcvdgekd4ySV3zUbUp8HF5nk5zqwiMXTuVzTUdl/w08O7JjM6XPOIVT+d2o/MqwM9rsXdzdergY5oY2RdhSPA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@ast-grep/napi-linux-arm64-gnu@0.37.0': + resolution: {integrity: sha512-L7Sj0lXy8X+BqSMgr1LB8cCoWk0rericdeu+dC8/c8zpsav5Oo2IQKY1PmiZ7H8IHoFBbURLf8iklY9wsD+cyA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@ast-grep/napi-linux-arm64-musl@0.37.0': + resolution: {integrity: sha512-LF9sAvYy6es/OdyJDO3RwkX3I82Vkfsng1sqUBcoWC1jVb1wX5YVzHtpQox9JrEhGl+bNp7FYxB4Qba9OdA5GA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@ast-grep/napi-linux-x64-gnu@0.37.0': + resolution: {integrity: sha512-TViz5/klqre6aSmJzswEIjApnGjJzstG/SE8VDWsrftMBMYt2PTu3MeluZVwzSqDao8doT/P+6U11dU05UOgxw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@ast-grep/napi-linux-x64-musl@0.37.0': + resolution: {integrity: sha512-/BcCH33S9E3ovOAEoxYngUNXgb+JLg991sdyiNP2bSoYd30a9RHrG7CYwW6fMgua3ijQ474eV6cq9yZO1bCpXg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@ast-grep/napi-win32-arm64-msvc@0.37.0': + resolution: {integrity: sha512-TjQA4cFoIEW2bgjLkaL9yqT4XWuuLa5MCNd0VCDhGRDMNQ9+rhwi9eLOWRaap3xzT7g+nlbcEHL3AkVCD2+b3A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@ast-grep/napi-win32-ia32-msvc@0.37.0': + resolution: {integrity: sha512-uNmVka8fJCdYsyOlF9aZqQMLTatEYBynjChVTzUfFMDfmZ0bihs/YTqJVbkSm8TZM7CUX82apvn50z/dX5iWRA==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@ast-grep/napi-win32-x64-msvc@0.37.0': + resolution: {integrity: sha512-vCiFOT3hSCQuHHfZ933GAwnPzmL0G04JxQEsBRfqONywyT8bSdDc/ECpAfr3S9VcS4JZ9/F6tkePKW/Om2Dq2g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@ast-grep/napi@0.37.0': + resolution: {integrity: sha512-Hb4o6h1Pf6yRUAX07DR4JVY7dmQw+RVQMW5/m55GoiAT/VRoKCWBtIUPPOnqDVhbx1Cjfil9b6EDrgJsUAujEQ==} + engines: {node: '>= 10'} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.0': + resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.23.9': + resolution: {integrity: sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.0': + resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + engines: {node: '>=6.9.0'} + + '@biomejs/biome@2.2.0': + resolution: {integrity: sha512-3On3RSYLsX+n9KnoSgfoYlckYBoU6VRM22cw1gB4Y0OuUVSYd/O/2saOJMrA4HFfA1Ff0eacOvMN1yAAvHtzIw==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.2.0': + resolution: {integrity: sha512-zKbwUUh+9uFmWfS8IFxmVD6XwqFcENjZvEyfOxHs1epjdH3wyyMQG80FGDsmauPwS2r5kXdEM0v/+dTIA9FXAg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.2.0': + resolution: {integrity: sha512-+OmT4dsX2eTfhD5crUOPw3RPhaR+SKVspvGVmSdZ9y9O/AgL8pla6T4hOn1q+VAFBHuHhsdxDRJgFCSC7RaMOw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.2.0': + resolution: {integrity: sha512-egKpOa+4FL9YO+SMUMLUvf543cprjevNc3CAgDNFLcjknuNMcZ0GLJYa3EGTCR2xIkIUJDVneBV3O9OcIlCEZQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.2.0': + resolution: {integrity: sha512-6eoRdF2yW5FnW9Lpeivh7Mayhq0KDdaDMYOJnH9aT02KuSIX5V1HmWJCQQPwIQbhDh68Zrcpl8inRlTEan0SXw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.2.0': + resolution: {integrity: sha512-I5J85yWwUWpgJyC1CcytNSGusu2p9HjDnOPAFG4Y515hwRD0jpR9sT9/T1cKHtuCvEQ/sBvx+6zhz9l9wEJGAg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.2.0': + resolution: {integrity: sha512-5UmQx/OZAfJfi25zAnAGHUMuOd+LOsliIt119x2soA2gLggQYrVPA+2kMUxR6Mw5M1deUF/AWWP2qpxgH7Nyfw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.2.0': + resolution: {integrity: sha512-n9a1/f2CwIDmNMNkFs+JI0ZjFnMO0jdOyGNtihgUNFnlmd84yIYY2KMTBmMV58ZlVHjgmY5Y6E1hVTnSRieggA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.2.0': + resolution: {integrity: sha512-Nawu5nHjP/zPKTIryh2AavzTc/KEg4um/MxWdXW0A6P/RZOyIpa7+QSjeXwAwX/utJGaCoXRPWtF3m5U/bB3Ww==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@emnapi/core@1.4.5': + resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==} + + '@emnapi/runtime@1.4.5': + resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==} + + '@emnapi/wasi-threads@1.0.4': + resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==} + + '@emoji-mart/data@1.2.1': + resolution: {integrity: sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==} + + '@esbuild/aix-ppc64@0.25.8': + resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.8': + resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.8': + resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.8': + resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.8': + resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.8': + resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.8': + resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.8': + resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.8': + resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.8': + resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.8': + resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.8': + resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.8': + resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.8': + resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.8': + resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.8': + resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.8': + resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.8': + resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.8': + resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.8': + resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.8': + resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.8': + resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.8': + resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.8': + resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.8': + resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.8': + resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.1.1': + resolution: {integrity: sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==} + + '@floating-ui/dom@1.7.3': + resolution: {integrity: sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==} + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@halo-dev/api-client@2.21.1': + resolution: {integrity: sha512-4sfpSui4YO2u7W73r3ECoegK+/kVzLHv/a16pT23gZUqazK+5ZAqpDqVYHhbpyBVbs/fmsUuMgI8TXVLo6kZjg==} + peerDependencies: + axios: ^1.7.x + + '@halo-dev/api-client@https://pkg.pr.new/@halo-dev/api-client@7679': + resolution: {tarball: https://pkg.pr.new/@halo-dev/api-client@7679} + version: 2.21.1 + peerDependencies: + axios: ^1.7.x + + '@halo-dev/components@2.21.0': + resolution: {integrity: sha512-hfAKAxFSW7k+690PQoDqe0CAQYBM3O5zsLZwnl+mnZs7VMw9cIQauqWQ+WwIVn+nsfnGhr1a+0VlLtyn/cAwxA==} + peerDependencies: + vue: ^3.5.16 + vue-router: ^4.5.1 + + '@halo-dev/ui-plugin-bundler-kit@2.21.2': + resolution: {integrity: sha512-CjKAxs02VlrMGyPxV7fEDEfNOPoQDT22i4RkK/sts1JvysYEHNXhe9mZ9qa9CwVX4dorLGgd++l0nWODuAsvRQ==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + '@rsbuild/core': ^1.0.0 + '@rsbuild/plugin-vue': ^1.0.0 + '@vitejs/plugin-vue': ^5.0.0 || ^6.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@iconify/json@2.2.373': + resolution: {integrity: sha512-ZgDwDLgbzf/nEyDoSdC75A1XA0L7Q4rfz0ZKGiq/ZWTztrlM7ASBvjKb6/dhs+gtvepnOc5CLUrBcDUYKZblLg==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@3.0.0': + resolution: {integrity: sha512-Bjf0HTRAB59thKK9QFvyLEXE9S793IqxqJEhNQEboh+IjOXj0nDtOIFh63oz+Y6X/ye4UWpxne5sVQ2W250iSA==} + + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + + '@jridgewell/gen-mapping@0.3.12': + resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.10': + resolution: {integrity: sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==} + + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + + '@jridgewell/trace-mapping@0.3.29': + resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + + '@lit-labs/ssr-dom-shim@1.4.0': + resolution: {integrity: sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==} + + '@lit/context@1.1.6': + resolution: {integrity: sha512-M26qDE6UkQbZA2mQ3RjJ3Gzd8TxP+/0obMgE5HfkfLhEEyYE3Bui4A5XHiGPjy0MUGAyxB3QgVuw2ciS0kHn6A==} + + '@lit/localize-tools@0.8.0': + resolution: {integrity: sha512-18HY8Ln8ZhSW2CYlXY9+CgDeEoxIY4dBvJ7W5fOe5262NvWEQghMgEC01WT9SQm4Non9C42aDGEYLEegw2ZBhw==} + hasBin: true + + '@lit/localize@0.12.2': + resolution: {integrity: sha512-Qv9kvgJKDq/JVSwXOxuWvQnnOBysHA99ti9im9a4fImCmx+fto+XXcUYQbjZHqiueEEc4V20PcRDPO+1g/6seQ==} + + '@lit/reactive-element@2.1.1': + resolution: {integrity: sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==} + + '@microsoft/api-extractor-model@7.30.7': + resolution: {integrity: sha512-TBbmSI2/BHpfR9YhQA7nH0nqVmGgJ0xH0Ex4D99/qBDAUpnhA2oikGmdXanbw9AWWY/ExBYIpkmY8dBHdla3YQ==} + + '@microsoft/api-extractor@7.52.10': + resolution: {integrity: sha512-LhKytJM5ZJkbHQVfW/3o747rZUNs/MGg6j/wt/9qwwqEOfvUDTYXXxIBuMgrRXhJ528p41iyz4zjBVHZU74Odg==} + hasBin: true + + '@microsoft/tsdoc-config@0.17.1': + resolution: {integrity: sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==} + + '@microsoft/tsdoc@0.15.1': + resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==} + + '@module-federation/error-codes@0.17.1': + resolution: {integrity: sha512-n6Elm4qKSjwAPxLUGtwnl7qt4y1dxB8OpSgVvXBIzqI9p27a3ZXshLPLnumlpPg1Qudaj8sLnSnFtt9yGpt5yQ==} + + '@module-federation/runtime-core@0.17.1': + resolution: {integrity: sha512-LCtIFuKgWPQ3E+13OyrVpuTPOWBMI/Ggwsq1Q874YeT8Px28b8tJRCj09DjyRFyhpSPyV/uG80T6iXPAUoLIfQ==} + + '@module-federation/runtime-tools@0.17.1': + resolution: {integrity: sha512-4kr6zTFFwGywJx6whBtxsc84V+COAuuBpEdEbPZN//YLXhNB0iz2IGsy9r9wDl+06h84bD+3dQ05l9euRLgXzQ==} + + '@module-federation/runtime@0.17.1': + resolution: {integrity: sha512-vKEN32MvUbpeuB/s6UXfkHDZ9N5jFyDDJnj83UTJ8n4N1jHIJu9VZ6Yi4/Ac8cfdvU8UIK9bIbfVXWbUYZUDsw==} + + '@module-federation/sdk@0.17.1': + resolution: {integrity: sha512-nlUcN6UTEi+3HWF+k8wPy7gH0yUOmCT+xNatihkIVR9REAnr7BUvHFGlPJmx7WEbLPL46+zJUbtQHvLzXwFhng==} + + '@module-federation/webpack-bundler-runtime@0.17.1': + resolution: {integrity: sha512-Swspdgf4PzcbvS9SNKFlBzfq8h/Qxwqjq/xRSqw1pqAZWondZQzwTTqPXhgrg0bFlz7qWjBS/6a8KuH/gRvGaQ==} + + '@napi-rs/wasm-runtime@1.0.1': + resolution: {integrity: sha512-KVlQ/jgywZpixGCKMNwxStmmbYEMyokZpCf2YuIChhfJA2uqfAKNEM8INz7zzTo55iEXfBhIIs3VqYyqzDLj8g==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@oxc-project/runtime@0.77.3': + resolution: {integrity: sha512-vsC/ewcGJ7xXnnwZkku7rpPH5Lxb5g4J+V6lD9eBTnRLmXVXM7Qu50y+ozD+UD5IXaSoVOvVMGTT4YSNCz2MQQ==} + engines: {node: '>=6.9.0'} + + '@oxc-project/types@0.77.3': + resolution: {integrity: sha512-5Vh+neJhhxuF0lYCjZXbxjqm2EO6YJ1jG+KuHntrd6VY67OMpYhWq2cZhUhy+xL9qLJVJRaeII7Xj9fciA6v7A==} + + '@parse5/tools@0.3.0': + resolution: {integrity: sha512-zxRyTHkqb7WQMV8kTNBKWb1BeOFUKXBXTBWuxg9H9hfvQB3IwP6Iw2U75Ia5eyRxPNltmY7E8YAlz6zWwUnjKg==} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@quansync/fs@0.1.3': + resolution: {integrity: sha512-G0OnZbMWEs5LhDyqy2UL17vGhSVHkQIfVojMtEWVenvj0V5S84VBgy86kJIuNsGDp2p7sTKlpSIpBUWdC35OKg==} + engines: {node: '>=20.0.0'} + + '@remirror/core-constants@3.0.0': + resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} + + '@rolldown/binding-android-arm64@1.0.0-beta.29': + resolution: {integrity: sha512-pDv7gg59Gdy80eFmMkEqXEaoJi3Y9W/a9T3z9M4t8Ma8aVXNldvSy9UgtlX7AK7DPqF8tULnmIZ2Z3rvGMz/NQ==} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-beta.29': + resolution: {integrity: sha512-fPqR6TfTqbzgKKCQYtcCS+Dms91YcptTbdlwJ13DxOUgMe8LgDIVsLLlEykfm7ijJd5mM4zNw0Hr2CJb6kvQZw==} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-beta.29': + resolution: {integrity: sha512-7Z4qosL0xN8i6++txHOEPCVP3/lcGLOvftUJOWATZ5aDkDskwcZDa66BGiJt/K1/DgW4kpRVmnGWUWAORHBbFA==} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-beta.29': + resolution: {integrity: sha512-0HLTfPW5Glh608s76qgayN/nPsXPchNUumavf7W5nh1eMG6qBsOO7Q1QaK0v4un7qtsn3IA/1Tgq0ZgNc0dbeg==} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.29': + resolution: {integrity: sha512-QNboxdVTJOZS4zP8kA2+XUwAegejd5QNSH5zVR4neqG2AfbxRcMFzSVRkJHN6yDaaKweD/4sUvXfmef6p/7zsw==} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.29': + resolution: {integrity: sha512-hzBmOtYdC4369XxN2SNJ3oBlXKWNif3ieWBT+oh/qvAeox4fQR0ngqyh+kIGOufBnP5Zc2rqJf9LzIbJw3Tx/Q==} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.29': + resolution: {integrity: sha512-6B35GmFJJ4RX88OgubrnUmuJBUgRh6/OTXIpy8m/VUnoc683lufIPo26HW/0LxLgxp2GM7KHr3LOULcVxbqq4Q==} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-ohos@1.0.0-beta.29': + resolution: {integrity: sha512-z3ru8fUCunQM8q9I7RbDVMT5cxzxVVVBNNKM5/qAQQrdObd1u8g0LR5z0yLtaFWzybwLVdPtJDRcXtLm5tOBFA==} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.29': + resolution: {integrity: sha512-n6fs4L7j99MIiI6vKhQDdyScv4/uMAPtIMkB0zGbUX8MKWT1osym1hvWVdlENjnS/Phf0zzhjyOgoFDzdhI1cQ==} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.29': + resolution: {integrity: sha512-C5hcJgtDN4rp6/WsPTQSDVUWrdnIC//ynMGcUIh1O0anm9KnSy47zKQ5D9EqtlEKvO+2PPqmyUVJ2DTq18nlVA==} + cpu: [x64] + os: [linux] + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.29': + resolution: {integrity: sha512-lMN1IBItdZFO182Sdus9oVuNDqyIymn/bsR5KwgeGaiqLsrmpQHBSLwkS/nKJO1nzYlpGDRugFSpnrSJ5ZmihQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.29': + resolution: {integrity: sha512-0UrXCUAOrbWdyVJskzjtne/4d3YMMhhhpBnob3SeF4jAvbKYqPhCZJ71pP7yUpvbowGXXTnHWpKfitg4Sovmtw==} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.29': + resolution: {integrity: sha512-YX0OYL1dcB7rPnsndpEa68fytYyZZj1iaWzH7momFB2oBS2lXAe1UrrDWcdLoUXdzPIyzpvtBCiS2XcDgYG7ag==} + cpu: [ia32] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.29': + resolution: {integrity: sha512-azrPWbV+NZiCFNs59AgH9Y6vFKHoAI6T/XtKKsoLxkPyP1LpbdgL5eqRfeWz+GCAUY9qhDOC4hH1GjFG8PrZIg==} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-beta.29': + resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==} + + '@rollup/pluginutils@5.2.0': + resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.46.2': + resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.46.2': + resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.46.2': + resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.46.2': + resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.46.2': + resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.46.2': + resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.46.2': + resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.46.2': + resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.46.2': + resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.46.2': + resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.46.2': + resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.46.2': + resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.46.2': + resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.46.2': + resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.46.2': + resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.46.2': + resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.46.2': + resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.46.2': + resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==} + cpu: [x64] + os: [win32] + + '@rsbuild/core@1.4.15': + resolution: {integrity: sha512-KoSTtKjzQUQwamcbeCp63Ne9kL7io1WI4+skTJe2chfLz6wsp/Gfg8aKkfs1DuyG1p+zxFDcYpwTWMsNtxqqiw==} + engines: {node: '>=16.10.0'} + hasBin: true + + '@rsbuild/core@1.5.0-beta.3': + resolution: {integrity: sha512-b4QOPQJvmhJFIWqBZ6kI7XLI3d1qx52ASpEzBoL5cnRnfh8tCMymWi9I0xUZinRmAh9V4wdIRG8EtISe899CcA==} + engines: {node: '>=18.12.0'} + hasBin: true + + '@rsbuild/plugin-vue@1.1.1': + resolution: {integrity: sha512-rRW/fCmz8DhXWbvGkl9YkDkve9lKyYTsiXLQDo3n5DIo9Bzg2Lyvjb+URaew+/klCUr5zZegkf4yS6HvxurVaw==} + peerDependencies: + '@rsbuild/core': 1.x + + '@rslib/core@0.12.1': + resolution: {integrity: sha512-ualcmPkgBk1/IgapJ/GN5wWfl0zjH++3Wpe7f8990NzM4zk5MS9JVqDjV9oljCtf/N8Va5YDC4pEULzVES7xYQ==} + engines: {node: '>=18.12.0'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7 + typescript: ^5 + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + typescript: + optional: true + + '@rspack/binding-darwin-arm64@1.4.11': + resolution: {integrity: sha512-PrmBVhR8MC269jo6uQ+BMy1uwIDx0HAJYLQRQur8gXiehWabUBCRg/d4U9KR7rLzdaSScRyc5JWXR52T7/4MfA==} + cpu: [arm64] + os: [darwin] + + '@rspack/binding-darwin-arm64@1.5.0-beta.0': + resolution: {integrity: sha512-P9m+DAZXulV6pgAmtdLubANCx4f/y6UJrNWy6sLFnVfSXAVbGvchoBcgb9LvyiNcVyVq8GZyGT8tggpi9lbhLA==} + cpu: [arm64] + os: [darwin] + + '@rspack/binding-darwin-x64@1.4.11': + resolution: {integrity: sha512-YIV8Wzy+JY0SoSsVtN4wxFXOjzxxVPnVXNswrrfqVUTPr9jqGOFYUWCGpbt8lcCgfuBFm6zN8HpOsKm1xUNsVA==} + cpu: [x64] + os: [darwin] + + '@rspack/binding-darwin-x64@1.5.0-beta.0': + resolution: {integrity: sha512-I8XHkeK+fRGdQMXnrirZXLRKfqnN52C57zKhlY2/TxTW+4gVb8tqLD0gTrGDqUTlXACHjnhx3uwJ8XkyUlgFLw==} + cpu: [x64] + os: [darwin] + + '@rspack/binding-linux-arm64-gnu@1.4.11': + resolution: {integrity: sha512-ms6uwECUIcu+6e82C5HJhRMHnfsI+l33v7XQezntzRPN0+sG3EpikEoT7SGbgt4vDwaWLR7wS20suN4qd5r3GA==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-arm64-gnu@1.5.0-beta.0': + resolution: {integrity: sha512-6fotqE8ZXE4rvywS4wCuwNwd6XprzJxEZGhEW/MqGV2ut9L7oLCI071Cz/v3pBXBJTcuh0vDuwH4xQ2uCO8SnA==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-arm64-musl@1.4.11': + resolution: {integrity: sha512-9evq0DOdxMN/H8VM8ZmyY9NSuBgILNVV6ydBfVPMHPx4r1E7JZGpWeKDegZcS5Erw3sS9kVSIxyX78L5PDzzKw==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-arm64-musl@1.5.0-beta.0': + resolution: {integrity: sha512-5u34Y3mIaqV8OPc1ojFTik+3spzRi+72/0IUgdjNfZEBSkqBHQJG/Pib5CIe6161yGNl/FF11KH/3DlK0uxaPA==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-x64-gnu@1.4.11': + resolution: {integrity: sha512-bHYFLxPPYBOSaHdQbEoCYGMQ1gOrEWj7Mro/DLfSHZi1a0okcQ2Q1y0i1DczReim3ZhLGNrK7k1IpFXCRbAobQ==} + cpu: [x64] + os: [linux] + + '@rspack/binding-linux-x64-gnu@1.5.0-beta.0': + resolution: {integrity: sha512-lV6Ni73T+0owXi56iSgEiRQcqczvm3UNHC8vfhTXJE6gyh+72t9jL6AoEfEm2vlxwZyAW2etQrplci6leshKBw==} + cpu: [x64] + os: [linux] + + '@rspack/binding-linux-x64-musl@1.4.11': + resolution: {integrity: sha512-wrm4E7q2k4+cwT6Uhp6hIQ3eUe/YoaUttj6j5TqHYZX6YeLrNPtD9+ne6lQQ17BV8wmm6NZsmoFIJ5xIptpRhQ==} + cpu: [x64] + os: [linux] + + '@rspack/binding-linux-x64-musl@1.5.0-beta.0': + resolution: {integrity: sha512-T9ik/8Eu2br8sOftAOXbMvgrBLZeiev9o/XnQw+MygnA4wczlSfWF22eJwlhssk4y5H1MPlkE8QMVBRIJrwrDw==} + cpu: [x64] + os: [linux] + + '@rspack/binding-wasm32-wasi@1.4.11': + resolution: {integrity: sha512-hiYxHZjaZ17wQtXyLCK0IdtOvMWreGVTiGsaHCxyeT+SldDG+r16bXNjmlqfZsjlfl1mkAqKz1dg+mMX28OTqw==} + cpu: [wasm32] + + '@rspack/binding-wasm32-wasi@1.5.0-beta.0': + resolution: {integrity: sha512-7wCc99kG6hWGt1hs4vrJbPK7GD2diHmoae4lBY7kcNaoP0ZpTKiICG+yCrTHmSyNlU6wbxsXGdJAabX6h4Brig==} + cpu: [wasm32] + + '@rspack/binding-win32-arm64-msvc@1.4.11': + resolution: {integrity: sha512-+HF/mnjmTr8PC1dccRt1bkrD2tPDGeqvXC1BBLYd/Klq1VbtIcnrhfmvQM6KaXbiLcY9VWKzcZPOTmnyZ8TaHQ==} + cpu: [arm64] + os: [win32] + + '@rspack/binding-win32-arm64-msvc@1.5.0-beta.0': + resolution: {integrity: sha512-3A3cN7wno4vY09NH/wrA0tzYRsv+Q1Kv5p/kXrnFLeQ8ZJ1MFyN8AFHt3UW9FO6TniJrxxJPmYd7D2szNeXujw==} + cpu: [arm64] + os: [win32] + + '@rspack/binding-win32-ia32-msvc@1.4.11': + resolution: {integrity: sha512-EU2fQGwrRfwFd/tcOInlD0jy6gNQE4Q3Ayj0Is+cX77sbhPPyyOz0kZDEaQ4qaN2VU8w4Hu/rrD7c0GAKLFvCw==} + cpu: [ia32] + os: [win32] + + '@rspack/binding-win32-ia32-msvc@1.5.0-beta.0': + resolution: {integrity: sha512-0QFpKQiH+MdyR4ILA6PP074YVkLhfVAakPLUe3UTOuBRXnN/C/i7Xop+6raWqrMmxU2qzgFi5N2zf0plYPbvnw==} + cpu: [ia32] + os: [win32] + + '@rspack/binding-win32-x64-msvc@1.4.11': + resolution: {integrity: sha512-1Nc5ZzWqfvE+iJc47qtHFzYYnHsC3awavXrCo74GdGip1vxtksM3G30BlvAQHHVtEmULotWqPbjZpflw/Xk9Ag==} + cpu: [x64] + os: [win32] + + '@rspack/binding-win32-x64-msvc@1.5.0-beta.0': + resolution: {integrity: sha512-99pXkrDCmuFe8B2AuPa4g/tu+A+GYQ2VcxOvbOrw5N4Y5ANhQJYGhxxYyZ7ijS7zyXP8SJ7PTf1mMeM8ghIzmQ==} + cpu: [x64] + os: [win32] + + '@rspack/binding@1.4.11': + resolution: {integrity: sha512-maGl/zRwnl0QVwkBCkgjn5PH20L9HdlRIdkYhEsfTepy5x2QZ0ti/0T49djjTJQrqb+S1i6wWQymMMMMMsxx6Q==} + + '@rspack/binding@1.5.0-beta.0': + resolution: {integrity: sha512-MzzSb/Ruq2fPBUfuhbezhD4V7Pg28n3otRZ7uNmeWfpvce3FtsJyyds92r99+rxwlb0Md31Zdj/NLJtpyFWBLA==} + + '@rspack/core@1.4.11': + resolution: {integrity: sha512-JtKnL6p7Kc/YgWQJF3Woo4OccbgKGyT/4187W4dyex8BMkdQcbqCNIdi6dFk02hwQzxpOOxRSBI4hlGRbz7oYQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@swc/helpers': '>=0.5.1' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@rspack/core@1.5.0-beta.0': + resolution: {integrity: sha512-9Vlk2Na6p4EEvBTXp8SFWC8T7CeQ8vGV+fvtvtK4xLuqutHZ7UDifaIVybJ44/Oi4AFQpJeBiBr5u8/JSsa2pQ==} + engines: {node: '>=18.12.0'} + peerDependencies: + '@swc/helpers': '>=0.5.1' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@rspack/lite-tapable@1.0.1': + resolution: {integrity: sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==} + engines: {node: '>=16.0.0'} + + '@rushstack/node-core-library@5.14.0': + resolution: {integrity: sha512-eRong84/rwQUlATGFW3TMTYVyqL1vfW9Lf10PH+mVGfIb9HzU3h5AASNIw+axnBLjnD0n3rT5uQBwu9fvzATrg==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/rig-package@0.5.3': + resolution: {integrity: sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==} + + '@rushstack/terminal@0.15.4': + resolution: {integrity: sha512-OQSThV0itlwVNHV6thoXiAYZlQh4Fgvie2CzxFABsbO2MWQsI4zOh3LRNigYSTrmS+ba2j0B3EObakPzf/x6Zg==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/ts-command-line@5.0.2': + resolution: {integrity: sha512-+AkJDbu1GFMPIU8Sb7TLVXDv/Q7Mkvx+wAjEl8XiXVVq+p1FmWW6M3LYpJMmoHNckSofeMecgWg5lfMwNAAsEQ==} + + '@shikijs/core@3.9.2': + resolution: {integrity: sha512-3q/mzmw09B2B6PgFNeiaN8pkNOixWS726IHmJEpjDAcneDPMQmUg2cweT9cWXY4XcyQS3i6mOOUgQz9RRUP6HA==} + + '@shikijs/engine-javascript@3.9.2': + resolution: {integrity: sha512-kUTRVKPsB/28H5Ko6qEsyudBiWEDLst+Sfi+hwr59E0GLHV0h8RfgbQU7fdN5Lt9A8R1ulRiZyTvAizkROjwDA==} + + '@shikijs/engine-oniguruma@3.9.2': + resolution: {integrity: sha512-Vn/w5oyQ6TUgTVDIC/BrpXwIlfK6V6kGWDVVz2eRkF2v13YoENUvaNwxMsQU/t6oCuZKzqp9vqtEtEzKl9VegA==} + + '@shikijs/langs@3.9.2': + resolution: {integrity: sha512-X1Q6wRRQXY7HqAuX3I8WjMscjeGjqXCg/Sve7J2GWFORXkSrXud23UECqTBIdCSNKJioFtmUGJQNKtlMMZMn0w==} + + '@shikijs/themes@3.9.2': + resolution: {integrity: sha512-6z5lBPBMRfLyyEsgf6uJDHPa6NAGVzFJqH4EAZ+03+7sedYir2yJBRu2uPZOKmj43GyhVHWHvyduLDAwJQfDjA==} + + '@shikijs/types@3.9.2': + resolution: {integrity: sha512-/M5L0Uc2ljyn2jKvj4Yiah7ow/W+DJSglVafvWAJ/b8AZDeeRAdMu3c2riDzB7N42VD+jSnWxeP9AKtd4TfYVw==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + + '@tiptap/core@3.2.0': + resolution: {integrity: sha512-1Dk1AIwzJejcyi4FOEcKoBSLMkHMfxeDiQ0daG51eS1IZ1ZSF+Xlhg9OD5sAjbUTGQjK1HfU6kGwS9wJcR/9ZQ==} + peerDependencies: + '@tiptap/pm': ^3.2.0 + + '@tiptap/extension-blockquote@3.2.0': + resolution: {integrity: sha512-ivOvkzkpj/+1i6cZwUr1M+T/knci2XMl8NW/Q6g70Kx2TinEoFjOGnDi34j1mVznyzhxVlY9jm3Xvv4s2+Ni/w==} + peerDependencies: + '@tiptap/core': ^3.2.0 + + '@tiptap/extension-bold@3.2.0': + resolution: {integrity: sha512-YuwK/muBzc0r7VFV0CcnF5Otg90zfUW2D5x+KpSF0e9bUBltSvitHgRaKWFwbPAgXYwkh6+ftxEazbXeqyTv8Q==} + peerDependencies: + '@tiptap/core': ^3.2.0 + + '@tiptap/extension-bullet-list@3.2.0': + resolution: {integrity: sha512-xfQp2tpotSQ6pU627j20Bu+Ahgvjqe3uMVmczqjJ3V9Ze9xbiKFKJ2foC6mjQ1gfEGXkUE4AL5mVRBCWcfK8oQ==} + peerDependencies: + '@tiptap/extension-list': ^3.2.0 + + '@tiptap/extension-code-block@3.2.0': + resolution: {integrity: sha512-rGkwir451oAegeTMZw0h0JwfLfbexrgXDWw5eUpQ32/5M3n07HU2+EamTSKt7VW76AxdlehEfsd7yo+FkuVlWA==} + peerDependencies: + '@tiptap/core': ^3.2.0 + '@tiptap/pm': ^3.2.0 + + '@tiptap/extension-code@3.2.0': + resolution: {integrity: sha512-8eUeWzT0YNxvT9yh6p9mRziUmZjiRqHeQhHbcRYDM7gdFKb0mgsWPxiaonns0sdj7WW1vP6g0M80PC+HYCIO+g==} + peerDependencies: + '@tiptap/core': ^3.2.0 + + '@tiptap/extension-document@3.2.0': + resolution: {integrity: sha512-2SQoew2HtOwuORpk8l7NjKDzsCBotMsij9vRSTp4nwoa4ZFGwmlVMsNIaKq/E/GuS7oiejmUUROsiD3w1stPKw==} + peerDependencies: + '@tiptap/core': ^3.2.0 + + '@tiptap/extension-dropcursor@3.2.0': + resolution: {integrity: sha512-fyTfePUfyfD/s+3BauAkhJh+3HvQ3hI/mblbNKHAi9u+QqlB+a0W04I027XWag8F76DTp2clGkZunlxHYHas4g==} + peerDependencies: + '@tiptap/extensions': ^3.2.0 + + '@tiptap/extension-gapcursor@3.2.0': + resolution: {integrity: sha512-LUElisCLru8kAOMtihJ6nRbJ+Fml2S8GOhiry3GBRRdMpGT+Hzy2pAd9TsuNsyIdqKH+yM1lLRyquW7maaNc5g==} + peerDependencies: + '@tiptap/extensions': ^3.2.0 + + '@tiptap/extension-hard-break@3.2.0': + resolution: {integrity: sha512-97CFsZK5vMHoWQzAP3thKD86dFDXqagXMs7vIKVK+SErwC3eR5KXmpvS5v5hi+iNs0I8+c+es6km+ZNMaUudKA==} + peerDependencies: + '@tiptap/core': ^3.2.0 + + '@tiptap/extension-heading@3.2.0': + resolution: {integrity: sha512-bgvRBeqrVZ9rnzOrlLVsr+2S6UR3m9SAPqkdPUr0eFeC2MnaK6FVapiOx1HoQRNyGY7Cw7QBZQVsWhYmEfJpbQ==} + peerDependencies: + '@tiptap/core': ^3.2.0 + + '@tiptap/extension-horizontal-rule@3.2.0': + resolution: {integrity: sha512-ttMzFO/WoRVpqfhDAZyIg1nBxgkDOXy1thE6xpfSQAEi0m7zbVEkp4OkATgt0meM69YNxJaEyzQjGztybMBgBw==} + peerDependencies: + '@tiptap/core': ^3.2.0 + '@tiptap/pm': ^3.2.0 + + '@tiptap/extension-italic@3.2.0': + resolution: {integrity: sha512-5OTX5Z3h7zrKcjj0snE8y+2a8dZ5G6GdZKVI1tedeDBfyUFqMSkvTZ/ygYenJ/zRkMvC9gz337ZU872IWx+ung==} + peerDependencies: + '@tiptap/core': ^3.2.0 + + '@tiptap/extension-link@3.2.0': + resolution: {integrity: sha512-ye9n73aFv9q/Adv6clFgV00hif7/rhsYf8gXpwmq5So/4teyOC7yp8Fg77CG2r8vZNhiJviZq6U6jlHVIBeTnw==} + peerDependencies: + '@tiptap/core': ^3.2.0 + '@tiptap/pm': ^3.2.0 + + '@tiptap/extension-list-item@3.2.0': + resolution: {integrity: sha512-v4cvZEWO9jQGCYUCJughMOpu5c0+9mjieTemwdF+Dd2q16WLonFx7+4YJkymB38YDaUi6dO1HFRtMKuGQRxlsg==} + peerDependencies: + '@tiptap/extension-list': ^3.2.0 + + '@tiptap/extension-list-keymap@3.2.0': + resolution: {integrity: sha512-R9r6CJpuwCFrfRQ+mU+m9vyujMjnu6C10J+8engQyvVtkc4KrgnHcABoyCkDJcZe36KRgz7mL0T6wqCezPD9lQ==} + peerDependencies: + '@tiptap/extension-list': ^3.2.0 + + '@tiptap/extension-list@3.2.0': + resolution: {integrity: sha512-/3ZL9MpgDnEOv8tPwFwW3ct/TBbxZg3r3rN9noLHOR+YgiZKwbpMt9pDSJGu8y++rzT3vIHVmRRp0xFOXBvptQ==} + peerDependencies: + '@tiptap/core': ^3.2.0 + '@tiptap/pm': ^3.2.0 + + '@tiptap/extension-ordered-list@3.2.0': + resolution: {integrity: sha512-ditrS5HVX3ugx8R/4h2cHl8KOSnF1/T3caVvLpPccgbbTJNC8WldHJK/VvX/OusDO9DmbCMQiC8vIAagHMInhg==} + peerDependencies: + '@tiptap/extension-list': ^3.2.0 + + '@tiptap/extension-paragraph@3.2.0': + resolution: {integrity: sha512-RQ6mu06nVP9xEVULE64oWH9MT92mQhHVwJpnkYQuc7XEe3KgPYVbUmaLWMzJbjBOnAhTBC4k37Wwk0JKiTUBNQ==} + peerDependencies: + '@tiptap/core': ^3.2.0 + + '@tiptap/extension-strike@3.2.0': + resolution: {integrity: sha512-gIfaIJmvQNBxjvtJfNQ8LhvWuGEFVFDMJxLa80speY4ngsDYZ3K8Ea4+0FTctKJVIXEZ5cDoHZR96ELkQMPzWA==} + peerDependencies: + '@tiptap/core': ^3.2.0 + + '@tiptap/extension-text@3.2.0': + resolution: {integrity: sha512-VDQPjVr6zd3MGh8+xPIIj+fuIhnws9tpAmP7jvBep+0tL7P2RYpN2NSj0zqeStHPcGfv9iMuXEuuMkEsi5gIZg==} + peerDependencies: + '@tiptap/core': ^3.2.0 + + '@tiptap/extension-underline@3.2.0': + resolution: {integrity: sha512-0ObBGqgeOAhQpNdNRJcu23GVQodtVp5dBYNC9vSW+mcJPRQy3Sc+kv59pqyrH0qV4SwtzzeKQSHuNaQpMPOU5A==} + peerDependencies: + '@tiptap/core': ^3.2.0 + + '@tiptap/extensions@3.2.0': + resolution: {integrity: sha512-voEvFhStH1fxw6PF5Hb7kj7PH+BMYM3d0S6d/V0bNdGB7mMsILruXkXIsxvP8Dkskv07Xmfs+iU6pdVy0hWD7Q==} + peerDependencies: + '@tiptap/core': ^3.2.0 + '@tiptap/pm': ^3.2.0 + + '@tiptap/pm@3.2.0': + resolution: {integrity: sha512-2UTlVts1Q7snQaUlxUtXfwEndNgcBcEIf74f1aEzcLzJRwBRRexCS6M0n+F50IifoB0OVBZENMBv1X/YXIPGPA==} + + '@tiptap/starter-kit@3.2.0': + resolution: {integrity: sha512-hcA12kqxVz1ypNqXhTUStQHWmDFmd2nm8NPmQmepK5T/T2FVXhVhhGSN8KsCR8sYFr3TwOk9utszxQLkT4AwOA==} + + '@tsconfig/node18@18.2.4': + resolution: {integrity: sha512-5xxU8vVs9/FNcvm3gE07fPbn9tl6tqGGWA9tSlwsUEkBxtRnTsNmwrV8gasZ9F/EobaSv9+nu8AxUKccw77JpQ==} + + '@tybys/wasm-util@0.10.0': + resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} + + '@types/argparse@1.0.38': + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + + '@types/node-fetch@2.6.13': + resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} + + '@types/node@18.19.123': + resolution: {integrity: sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg==} + + '@types/node@20.19.10': + resolution: {integrity: sha512-iAFpG6DokED3roLSP0K+ybeDdIX6Bc0Vd3mLW5uDqThPWtNos3E+EqOM11mPQHKzfWHqEBuLjIlsBQQ8CsISmQ==} + + '@types/sanitize-html@2.16.0': + resolution: {integrity: sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@unocss/astro@66.4.2': + resolution: {integrity: sha512-En3AKHwkiPxtZT95vkVrNiRYrB+DFVCikew6/dMMCWDWVKK0+5tEVUTzR1ak3+YnzAXl0NpWj8D4zHb0PxOs/A==} + peerDependencies: + vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 + peerDependenciesMeta: + vite: + optional: true + + '@unocss/cli@66.4.2': + resolution: {integrity: sha512-WsXzrB0SHbSt2nOHtD5QM91VN8j38+wObqyGcoIhtBSugqzsc+t7AdPkxV/ZaYgtPAz87bR0WFEVKcbiBRnmJw==} + engines: {node: '>=14'} + hasBin: true + + '@unocss/config@66.4.2': + resolution: {integrity: sha512-plji1gNGSzlWjuV2Uh0q6Dt5ZlNkOKCHpgxekW9J458WghGAMBeXgB9uNpWg6flilqP1g0GJQv+XvJcSkYRGpQ==} + engines: {node: '>=14'} + + '@unocss/core@66.4.2': + resolution: {integrity: sha512-cYgMQrLhB9nRekv5c+yPDDa+5dzlMkA2UMQRil0s5D9Lb5n7NsCMcr6+nfxkcSYVLy92SbwDV45c6T7vIxFTOA==} + + '@unocss/extractor-arbitrary-variants@66.4.2': + resolution: {integrity: sha512-T/eSeodfAp7HaWnQGqVLOsW4PbKUAvuybNRyvFWThMneM2qo+dOo3kFnA5my9ULAmRSFsAlyB1DnupD3qv5Klg==} + + '@unocss/inspector@66.4.2': + resolution: {integrity: sha512-ugcJK8r2ypM4eIdgetVn8RhfKrbA3AF3OQ/RohK5PPk2UPDAScqabzYpfdNW4eYQsBOZOgoiqWtnfc8weqo8LQ==} + + '@unocss/postcss@66.4.2': + resolution: {integrity: sha512-tu4lnh6K27pIAuaQHlFlhXin8korwC0r1kQl00YMmF3THiX7orXkTP6xWGcQwnkbx4uQz1dw+tBimYxeaAMrhA==} + engines: {node: '>=14'} + peerDependencies: + postcss: ^8.4.21 + + '@unocss/preset-attributify@66.4.2': + resolution: {integrity: sha512-DwFJJkkawmHpjo3pGQE8FyoPsvhbxh+QMvvaAdYpo+iZ5HRkeDml9SOj7u6SGTcmbNyI+QR61s0KM8fxx6HcVQ==} + + '@unocss/preset-icons@66.4.2': + resolution: {integrity: sha512-qJx9gmesrvrmoTe9Mqoidihad8hm2MSD4QAezhfDSAyllioJOgyT0Bev/IEWAbehe9jtqYIh8v1oCerBPbGn6Q==} + + '@unocss/preset-mini@66.4.2': + resolution: {integrity: sha512-Ry+5hM+XLmT8HrEb182mUfcZuyrZ8xR+TBe72DBcliJ1DhOV3K67TCxwQucfb0zHbGV71HNWdPmHsLKxPDgweQ==} + + '@unocss/preset-tagify@66.4.2': + resolution: {integrity: sha512-dECS09LqWJY4sYpgPUH2OAUftWU/tiZPR2XDRoTngeGU37GxSN+1sWtSmB7vwDm3C7opsdVUN20he8F1LUNubw==} + + '@unocss/preset-typography@66.4.2': + resolution: {integrity: sha512-ZOKRuR5+V0r30QTVq04/6ZoIw75me3V25v2dU2YWJXIzwpMKmQ9TUN/M1yeiEUFfXjOaruWX6Ad6CvAw2MlCew==} + + '@unocss/preset-uno@66.4.2': + resolution: {integrity: sha512-1MFtPivGcpqRQFWdjtP40Enop1y3XDb3tlZXoMQUX0IGLG8HJOT+lfQx/Xl9t73ShJ8aAJ/l6qTxC43ZGNACzA==} + + '@unocss/preset-web-fonts@66.4.2': + resolution: {integrity: sha512-4FYmleeRoM8r2DqGl6dfIjnX57tepcfZCvVfeCqYnk7475Yddmv1OYkoMjkWMnkK9MzdSxsFwHMU6CIUTmFTzQ==} + + '@unocss/preset-wind3@66.4.2': + resolution: {integrity: sha512-0Aye/PaT08M/cQhPnGKn93iEVoRJbym0/1eomMvXoL+8oc7DVry35ws06r5CLu5h1sXI6UmS6sejoePFlSkLJQ==} + + '@unocss/preset-wind4@66.4.2': + resolution: {integrity: sha512-F4RZsDqIpnSevD9hY353+Tw5gxpJuHA5HwdKjLnC/TnT9VKKVmV7qUEZ6M0jEuAk1kz2x3/ngnQ9Ftw+C2L84A==} + + '@unocss/preset-wind@66.4.2': + resolution: {integrity: sha512-z/rFYFINNqmBtl3Dh+7UCKpPnPkxM7IIUGszMnvdntky9uhLauJ11dt/Puir73sM2cAfywfgvnHyZ00m0pg7rA==} + + '@unocss/reset@66.4.2': + resolution: {integrity: sha512-s3Kq4Q6a/d3/jYe6HTCfXUx7zYAYufetId5n66DZHzQxpeu6CoBS83+b37STTKsw27SOgV28cPJlJtZ6/D6Bhw==} + + '@unocss/rule-utils@66.4.2': + resolution: {integrity: sha512-7z3IuajwXhy2cx3E0IGOFXIiuKC79/jzm4Tt56TC68nXLh/etlH0fKhxVwkZ/HbcQRpVwWyDRNcbh29pmA3DwQ==} + engines: {node: '>=14'} + + '@unocss/transformer-attributify-jsx@66.4.2': + resolution: {integrity: sha512-de6LzoyW1tkdOftlCrj6z8wEb4j6l1sqmOU1nYKkYHw7luLFGxRUELC7iujlI9KmylbM02bcKfLETAfJy/je2w==} + + '@unocss/transformer-compile-class@66.4.2': + resolution: {integrity: sha512-+oiIrV8c3T7qiJdICr6YsEWik5sjbWirXF0mlpcBvZu2HyV559hvHjzuWKr/fl7xYYZKDL9FvddbqWo3DOXh3Q==} + + '@unocss/transformer-directives@66.4.2': + resolution: {integrity: sha512-7m/dTrCUkBkZeSRKPxPEo65Rav239orQSLq6sztwZhoA4x/6H8r58xCkAK0qC9VEalyerpCpyarU3sKN4+ehNg==} + + '@unocss/transformer-variant-group@66.4.2': + resolution: {integrity: sha512-SbPDbZUrhQyL4CpvnpvUfrr1DFq8AKf8ofPGbMJDm5S2TInQ34vFaIrhNroGR0szntMZRH5Zlkq6LtVUKDRs5g==} + + '@unocss/vite@66.4.2': + resolution: {integrity: sha512-7eON9iPF3qWzuI+M6u0kq7K3y9nEbimZlLj01nGoqrgSGxEsyJpP01QQQsmT7FPRiZzRMJv7BiKMEyDQSuRRCA==} + peerDependencies: + vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 + + '@vitejs/plugin-vue@6.0.1': + resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vue: ^3.2.25 + + '@volar/language-core@2.4.15': + resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==} + + '@volar/language-core@2.4.22': + resolution: {integrity: sha512-gp4M7Di5KgNyIyO903wTClYBavRt6UyFNpc5LWfyZr1lBsTUY+QrVZfmbNF2aCyfklBOVk9YC4p+zkwoyT7ECg==} + + '@volar/source-map@2.4.15': + resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==} + + '@volar/source-map@2.4.22': + resolution: {integrity: sha512-L2nVr/1vei0xKRgO2tYVXtJYd09HTRjaZi418e85Q+QdbbqA8h7bBjfNyPPSsjnrOO4l4kaAo78c8SQUAdHvgA==} + + '@volar/typescript@2.4.15': + resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==} + + '@volar/typescript@2.4.22': + resolution: {integrity: sha512-6ZczlJW1/GWTrNnkmZxJp4qyBt/SGVlcTuCWpI5zLrdPdCZsj66Aff9ZsfFaT3TyjG8zVYgBMYPuCm/eRkpcpQ==} + + '@vscode/web-custom-data@0.4.9': + resolution: {integrity: sha512-QeCJFISE/RiTG0NECX6DYmVRPVb0jdyaUrhY0JqNMv9ruUYtYqxxQfv3PSjogb+zNghmwgXLSYuQKk6G+Xnaig==} + + '@vue/compiler-core@3.5.18': + resolution: {integrity: sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==} + + '@vue/compiler-dom@3.5.18': + resolution: {integrity: sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==} + + '@vue/compiler-sfc@3.5.18': + resolution: {integrity: sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==} + + '@vue/compiler-ssr@3.5.18': + resolution: {integrity: sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + + '@vue/language-core@2.2.0': + resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/language-core@2.2.12': + resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.5.18': + resolution: {integrity: sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==} + + '@vue/runtime-core@3.5.18': + resolution: {integrity: sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==} + + '@vue/runtime-dom@3.5.18': + resolution: {integrity: sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==} + + '@vue/server-renderer@3.5.18': + resolution: {integrity: sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==} + peerDependencies: + vue: 3.5.18 + + '@vue/shared@3.5.18': + resolution: {integrity: sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==} + + '@vue/tsconfig@0.7.0': + resolution: {integrity: sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==} + peerDependencies: + typescript: 5.x + vue: ^3.4.0 + peerDependenciesMeta: + typescript: + optional: true + vue: + optional: true + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xmldom/xmldom@0.8.10': + resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} + engines: {node: '>=10.0.0'} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + + ajv@8.13.0: + resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==} + + alien-signals@0.4.14: + resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==} + + alien-signals@1.0.13: + resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} + + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + ansis@4.1.0: + resolution: {integrity: sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==} + engines: {node: '>=14'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.11.0: + resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.25.2: + resolution: {integrity: sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + caniuse-lite@1.0.30001734: + resolution: {integrity: sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.5.0: + resolution: {integrity: sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@14.0.0: + resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + engines: {node: '>=20'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + core-js@3.45.0: + resolution: {integrity: sha512-c2KZL9lP4DjkN3hk/an4pWn5b5ZefhRJnAc42n6LJ19kSnbeRbdQZE5dSeE2LBol1OwJD3X1BQvFTAsa8ReeDA==} + + crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-europe-js@0.1.2: + resolution: {integrity: sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow==} + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + didyoumean2@4.1.0: + resolution: {integrity: sha512-qTBmfQoXvhKO75D/05C8m+fteQmn4U46FWYiLhXtZQInzitXLWY0EQ/2oKnpAz9g2lQWW8jYcLcT+hPJGT+kig==} + engines: {node: '>=10.13'} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + electron-to-chromium@1.5.199: + resolution: {integrity: sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==} + + emoji-mart@5.6.0: + resolution: {integrity: sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-toolkit@1.39.9: + resolution: {integrity: sha512-9OtbkZmTA2Qc9groyA1PUNeb6knVTkvB2RSdr/LcJXDL8IdEakaxwXLHXa7VX/Wj0GmdMJPR3WhnPGhiP3E+qg==} + + esbuild@0.25.8: + resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + exsolve@1.0.7: + resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.2.12: + resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + engines: {node: '>=8.6.0'} + + fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + floating-vue@5.2.2: + resolution: {integrity: sha512-afW+h2CFafo+7Y9Lvw/xsqjaQlKLdJV7h1fCHfcYQ1C4SVMlu7OAekqWgu5d4SgvkBVU0pVpLlVsrSTBURFRkg==} + peerDependencies: + '@nuxt/kit': ^3.2.0 + vue: ^3.2.0 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.3.1: + resolution: {integrity: sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==} + engines: {node: '>=14.14'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + gzip-size@6.0.0: + resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} + engines: {node: '>=10'} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hash-sum@2.0.0: + resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-standalone-pwa@0.1.1: + resolution: {integrity: sha512-9Cbovsa52vNQCjdXOzeQq5CnCbAcRk05aU62K20WO372NrTv0NxibLFCK6lQ4/iZEFdEA3p3t2VNOn8AJ53F5g==} + + javascript-time-ago@2.5.11: + resolution: {integrity: sha512-Zeyf5R7oM1fSMW9zsU3YgAYwE0bimEeF54Udn2ixGd8PUwu+z1Yc5t4Y8YScJDMHD6uCx6giLt3VJR5K4CMwbg==} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + jiti@2.5.1: + resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} + hasBin: true + + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonschema@1.4.1: + resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + + linkifyjs@4.3.2: + resolution: {integrity: sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==} + + lint-staged@16.1.5: + resolution: {integrity: sha512-uAeQQwByI6dfV7wpt/gVqg+jAPaSp8WwOA8kKC/dv1qw14oGpnpAisY65ibGHUGDUv0rYaZ8CAJZ/1U8hUvC2A==} + engines: {node: '>=20.17'} + hasBin: true + + listr2@9.0.1: + resolution: {integrity: sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==} + engines: {node: '>=20.0.0'} + + lit-analyzer@2.0.3: + resolution: {integrity: sha512-XiAjnwVipNrKav7r3CSEZpWt+mwYxrhPRVC7h8knDmn/HWTzzWJvPe+mwBcL2brn4xhItAMzZhFC8tzzqHKmiQ==} + hasBin: true + + lit-element@4.2.1: + resolution: {integrity: sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==} + + lit-html@3.3.1: + resolution: {integrity: sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==} + + lit@3.3.1: + resolution: {integrity: sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==} + + loader-runner@4.3.0: + resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + engines: {node: '>=6.11.5'} + + local-pkg@1.1.1: + resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==} + engines: {node: '>=14'} + + lodash.deburr@4.1.0: + resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@10.0.3: + resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + engines: {node: 20 || >=22} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + nano-spawn@1.0.2: + resolution: {integrity: sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg==} + engines: {node: '>=20.17'} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + ofetch@1.4.1: + resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.3: + resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} + + orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + + package-manager-detector@1.3.0: + resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} + + parse-srcset@1.0.2: + resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} + + parse5@5.1.0: + resolution: {integrity: sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==} + + parse5@7.2.1: + resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.2.0: + resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + prosemirror-changeset@2.3.1: + resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==} + + prosemirror-collab@1.3.1: + resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==} + + prosemirror-commands@1.7.1: + resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==} + + prosemirror-dropcursor@1.8.2: + resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==} + + prosemirror-gapcursor@1.3.2: + resolution: {integrity: sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==} + + prosemirror-history@1.4.1: + resolution: {integrity: sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==} + + prosemirror-inputrules@1.5.0: + resolution: {integrity: sha512-K0xJRCmt+uSw7xesnHmcn72yBGTbY45vm8gXI4LZXbx2Z0jwh5aF9xrGQgrVPu0WbyFVFF3E/o9VhJYz6SQWnA==} + + prosemirror-keymap@1.2.3: + resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} + + prosemirror-markdown@1.13.2: + resolution: {integrity: sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==} + + prosemirror-menu@1.2.5: + resolution: {integrity: sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==} + + prosemirror-model@1.25.3: + resolution: {integrity: sha512-dY2HdaNXlARknJbrManZ1WyUtos+AP97AmvqdOQtWtrrC5g4mohVX5DTi9rXNFSk09eczLq9GuNTtq3EfMeMGA==} + + prosemirror-schema-basic@1.2.4: + resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==} + + prosemirror-schema-list@1.5.1: + resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==} + + prosemirror-state@1.4.3: + resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==} + + prosemirror-tables@1.7.1: + resolution: {integrity: sha512-eRQ97Bf+i9Eby99QbyAiyov43iOKgWa7QCGly+lrDt7efZ1v8NWolhXiB43hSDGIXT1UXgbs4KJN3a06FGpr1Q==} + + prosemirror-trailing-node@3.0.0: + resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==} + peerDependencies: + prosemirror-model: ^1.22.1 + prosemirror-state: ^1.4.2 + prosemirror-view: ^1.33.8 + + prosemirror-transform@1.10.4: + resolution: {integrity: sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==} + + prosemirror-view@1.40.1: + resolution: {integrity: sha512-pbwUjt3G7TlsQQHDiYSupWBhJswpLVB09xXm1YiJPdkjkh9Pe7Y51XdLh5VWIZmROLY8UpUpG03lkdhm9lzIBA==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + quansync@0.2.10: + resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + + relative-time-format@1.1.6: + resolution: {integrity: sha512-aCv3juQw4hT1/P/OrVltKWLlp15eW1GRcwP1XdxHrPdZE9MtgqFpegjnTjLhi2m2WI9MT/hQQtE+tjEWG1hgkQ==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rolldown-vite@7.0.10: + resolution: {integrity: sha512-t3jMDID78NAJ2PWd0Q5RFrDpD1mFv20ouO/yDbqeHzG2Iyi2ZsjChLKClag1bUm591JJXBsoJIjP6FDkFi9qbw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + esbuild: ^0.25.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + rolldown@1.0.0-beta.29: + resolution: {integrity: sha512-EsoOi8moHN6CAYyTZipxDDVTJn0j2nBCWor4wRU45RQ8ER2qREDykXLr3Ulz6hBh6oBKCFTQIjo21i0FXNo/IA==} + hasBin: true + + rollup@4.46.2: + resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rope-sequence@1.3.4: + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + + rsbuild-plugin-dts@0.12.1: + resolution: {integrity: sha512-NXY04mekD3e1FzRKxcP5c8/idjWcnNyDcdXh4VcF10F7UxsgqT/rIOV8FT1aT+JjFncAD2mXboPq20wkqJTiIw==} + engines: {node: '>=18.12.0'} + peerDependencies: + '@microsoft/api-extractor': ^7 + '@rsbuild/core': 1.x + typescript: ^5 + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + typescript: + optional: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + sanitize-html@2.17.0: + resolution: {integrity: sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==} + + schema-utils@4.3.2: + resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} + engines: {node: '>= 10.13.0'} + + semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + shiki@3.9.2: + resolution: {integrity: sha512-t6NKl5e/zGTvw/IyftLcumolgOczhuroqwXngDeMqJ3h3EQiTY/7wmfgPlsmloD8oYfqkEDqxiaH37Pjm1zUhQ==} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sirv@3.0.1: + resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + engines: {node: '>=18'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tapable@2.2.2: + resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + engines: {node: '>=6'} + + terser-webpack-plugin@5.3.14: + resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.43.1: + resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} + engines: {node: '>=10'} + hasBin: true + + tinyexec@1.0.1: + resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tiptap-extension-code-block-shiki@0.6.0: + resolution: {integrity: sha512-yhhIm01p/WNqMpkF9+4ieu0kQ4Jh/vrxCnF+h+bqGSb60xHwVMtlJ5sWMLbKqVG86z59oRxGrzEq9xAGxucvxg==} + peerDependencies: + '@tiptap/core': ^2.3.0 || ^3.0.0 + '@tiptap/extension-code-block': ^2.3.0 || ^3.0.0 + '@tiptap/pm': ^2.3.0 || ^3.0.0 + shiki: ^3.0.0 + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + ts-simple-type@2.0.0-next.0: + resolution: {integrity: sha512-A+hLX83gS+yH6DtzNAhzZbPfU+D9D8lHlTSd7GeoMRBjOt3GRylDqLTYbdmjA4biWvq2xSfpqfIDj2l0OA/BVg==} + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.8.2: + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + ua-is-frozen@0.1.2: + resolution: {integrity: sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw==} + + ua-parser-js@2.0.4: + resolution: {integrity: sha512-XiBOnM/UpUq21ZZ91q2AVDOnGROE6UQd37WrO9WBgw4u2eGvUCNOheMmZ3EfEUj7DLHr8tre+Um/436Of/Vwzg==} + hasBin: true + + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + unconfig@7.3.2: + resolution: {integrity: sha512-nqG5NNL2wFVGZ0NA/aCFw0oJ2pxSf1lwg4Z5ill8wd7K4KX/rQbHlwbh+bjctXL5Ly1xtzHenHGOK0b+lG6JVg==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + universalify@2.0.0: + resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + engines: {node: '>= 10.0.0'} + + unocss@66.4.2: + resolution: {integrity: sha512-PsZ+4XF/ekiParR7PZEM7AchvHJ78EIfOXlqTPflTOXCYgZ77kG9NaIaIf4lHRevY+rRTyrHrjxdg1Ern2j8qw==} + engines: {node: '>=14'} + peerDependencies: + '@unocss/webpack': 66.4.2 + vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 + peerDependenciesMeta: + '@unocss/webpack': + optional: true + vite: + optional: true + + unplugin-utils@0.2.5: + resolution: {integrity: sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg==} + engines: {node: '>=18.12.0'} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite-plugin-dts@4.5.4: + resolution: {integrity: sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==} + peerDependencies: + typescript: '*' + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@7.0.6: + resolution: {integrity: sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vscode-css-languageservice@4.3.0: + resolution: {integrity: sha512-BkQAMz4oVHjr0oOAz5PdeE72txlLQK7NIwzmclfr+b6fj6I8POwB+VoXvrZLTbWt9hWRgfvgiQRkh5JwrjPJ5A==} + + vscode-html-languageservice@3.1.0: + resolution: {integrity: sha512-QAyRHI98bbEIBCqTzZVA0VblGU40na0txggongw5ZgTj9UVsVk5XbLT16O9OTcbqBGSqn0oWmFDNjK/XGIDcqg==} + + vscode-languageserver-textdocument@1.0.11: + resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==} + + vscode-languageserver-types@3.16.0-next.2: + resolution: {integrity: sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==} + + vscode-nls@4.1.2: + resolution: {integrity: sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==} + + vscode-uri@2.1.2: + resolution: {integrity: sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==} + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + vue-flow-layout@0.2.0: + resolution: {integrity: sha512-zKgsWWkXq0xrus7H4Mc+uFs1ESrmdTXlO0YNbR6wMdPaFvosL3fMB8N7uTV308UhGy9UvTrGhIY7mVz9eN+L0Q==} + + vue-loader@17.4.2: + resolution: {integrity: sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w==} + peerDependencies: + '@vue/compiler-sfc': '*' + vue: '*' + webpack: ^4.1.0 || ^5.0.0-0 + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true + vue: + optional: true + + vue-resize@2.0.0-alpha.1: + resolution: {integrity: sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==} + peerDependencies: + vue: ^3.0.0 + + vue-router@4.5.1: + resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==} + peerDependencies: + vue: ^3.2.0 + + vue-tsc@2.2.12: + resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.18: + resolution: {integrity: sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + + watchpack@2.4.4: + resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} + engines: {node: '>=10.13.0'} + + web-component-analyzer@2.0.0: + resolution: {integrity: sha512-UEvwfpD+XQw99sLKiH5B1T4QwpwNyWJxp59cnlRwFfhUW6JsQpw5jMeMwi7580sNou8YL3kYoS7BWLm+yJ/jVQ==} + hasBin: true + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webpack-sources@3.3.3: + resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + engines: {node: '>=10.13.0'} + + webpack@5.101.0: + resolution: {integrity: sha512-B4t+nJqytPeuZlHuIKTbalhljIFXeNRqrUGAQgTGlfOl2lXXKXw+yZu6bicycP+PUlM44CxBjCFD6aciKFT3LQ==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.3.0 + tinyexec: 1.0.1 + + '@antfu/utils@9.2.0': {} + + '@ast-grep/napi-darwin-arm64@0.37.0': + optional: true + + '@ast-grep/napi-darwin-x64@0.37.0': + optional: true + + '@ast-grep/napi-linux-arm64-gnu@0.37.0': + optional: true + + '@ast-grep/napi-linux-arm64-musl@0.37.0': + optional: true + + '@ast-grep/napi-linux-x64-gnu@0.37.0': + optional: true + + '@ast-grep/napi-linux-x64-musl@0.37.0': + optional: true + + '@ast-grep/napi-win32-arm64-msvc@0.37.0': + optional: true + + '@ast-grep/napi-win32-ia32-msvc@0.37.0': + optional: true + + '@ast-grep/napi-win32-x64-msvc@0.37.0': + optional: true + + '@ast-grep/napi@0.37.0': + optionalDependencies: + '@ast-grep/napi-darwin-arm64': 0.37.0 + '@ast-grep/napi-darwin-x64': 0.37.0 + '@ast-grep/napi-linux-arm64-gnu': 0.37.0 + '@ast-grep/napi-linux-arm64-musl': 0.37.0 + '@ast-grep/napi-linux-x64-gnu': 0.37.0 + '@ast-grep/napi-linux-x64-musl': 0.37.0 + '@ast-grep/napi-win32-arm64-msvc': 0.37.0 + '@ast-grep/napi-win32-ia32-msvc': 0.37.0 + '@ast-grep/napi-win32-x64-msvc': 0.37.0 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/generator@7.28.0': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + jsesc: 3.1.0 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.2 + + '@babel/runtime@7.23.9': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + + '@babel/traverse@7.28.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.2': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@biomejs/biome@2.2.0': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.2.0 + '@biomejs/cli-darwin-x64': 2.2.0 + '@biomejs/cli-linux-arm64': 2.2.0 + '@biomejs/cli-linux-arm64-musl': 2.2.0 + '@biomejs/cli-linux-x64': 2.2.0 + '@biomejs/cli-linux-x64-musl': 2.2.0 + '@biomejs/cli-win32-arm64': 2.2.0 + '@biomejs/cli-win32-x64': 2.2.0 + + '@biomejs/cli-darwin-arm64@2.2.0': + optional: true + + '@biomejs/cli-darwin-x64@2.2.0': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.2.0': + optional: true + + '@biomejs/cli-linux-arm64@2.2.0': + optional: true + + '@biomejs/cli-linux-x64-musl@2.2.0': + optional: true + + '@biomejs/cli-linux-x64@2.2.0': + optional: true + + '@biomejs/cli-win32-arm64@2.2.0': + optional: true + + '@biomejs/cli-win32-x64@2.2.0': + optional: true + + '@emnapi/core@1.4.5': + dependencies: + '@emnapi/wasi-threads': 1.0.4 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.4.5': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.0.4': + dependencies: + tslib: 2.8.1 + optional: true + + '@emoji-mart/data@1.2.1': {} + + '@esbuild/aix-ppc64@0.25.8': + optional: true + + '@esbuild/android-arm64@0.25.8': + optional: true + + '@esbuild/android-arm@0.25.8': + optional: true + + '@esbuild/android-x64@0.25.8': + optional: true + + '@esbuild/darwin-arm64@0.25.8': + optional: true + + '@esbuild/darwin-x64@0.25.8': + optional: true + + '@esbuild/freebsd-arm64@0.25.8': + optional: true + + '@esbuild/freebsd-x64@0.25.8': + optional: true + + '@esbuild/linux-arm64@0.25.8': + optional: true + + '@esbuild/linux-arm@0.25.8': + optional: true + + '@esbuild/linux-ia32@0.25.8': + optional: true + + '@esbuild/linux-loong64@0.25.8': + optional: true + + '@esbuild/linux-mips64el@0.25.8': + optional: true + + '@esbuild/linux-ppc64@0.25.8': + optional: true + + '@esbuild/linux-riscv64@0.25.8': + optional: true + + '@esbuild/linux-s390x@0.25.8': + optional: true + + '@esbuild/linux-x64@0.25.8': + optional: true + + '@esbuild/netbsd-arm64@0.25.8': + optional: true + + '@esbuild/netbsd-x64@0.25.8': + optional: true + + '@esbuild/openbsd-arm64@0.25.8': + optional: true + + '@esbuild/openbsd-x64@0.25.8': + optional: true + + '@esbuild/openharmony-arm64@0.25.8': + optional: true + + '@esbuild/sunos-x64@0.25.8': + optional: true + + '@esbuild/win32-arm64@0.25.8': + optional: true + + '@esbuild/win32-ia32@0.25.8': + optional: true + + '@esbuild/win32-x64@0.25.8': + optional: true + + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.1.1': + dependencies: + '@floating-ui/core': 1.7.3 + + '@floating-ui/dom@1.7.3': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/utils@0.2.10': {} + + '@halo-dev/api-client@2.21.1(axios@1.11.0)': + dependencies: + axios: 1.11.0 + qs: 6.14.0 + + '@halo-dev/api-client@https://pkg.pr.new/@halo-dev/api-client@7679(axios@1.11.0)': + dependencies: + axios: 1.11.0 + qs: 6.14.0 + + '@halo-dev/components@2.21.0(vue-router@4.5.1(vue@3.5.18(typescript@5.8.3)))(vue@3.5.18(typescript@5.8.3))': + dependencies: + floating-vue: 5.2.2(vue@3.5.18(typescript@5.8.3)) + vue: 3.5.18(typescript@5.8.3) + vue-router: 4.5.1(vue@3.5.18(typescript@5.8.3)) + transitivePeerDependencies: + - '@nuxt/kit' + + '@halo-dev/ui-plugin-bundler-kit@2.21.2(@rsbuild/core@1.4.15)(@rsbuild/plugin-vue@1.1.1(@rsbuild/core@1.4.15)(esbuild@0.25.8)(vue@3.5.18(typescript@5.8.3)))(@vitejs/plugin-vue@6.0.1(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.18(typescript@5.8.3)))(axios@1.11.0)(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@halo-dev/api-client': 2.21.1(axios@1.11.0) + '@rsbuild/core': 1.4.15 + '@rsbuild/plugin-vue': 1.1.1(@rsbuild/core@1.4.15)(esbuild@0.25.8)(vue@3.5.18(typescript@5.8.3)) + '@vitejs/plugin-vue': 6.0.1(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.18(typescript@5.8.3)) + js-yaml: 4.1.0 + vite: 7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + transitivePeerDependencies: + - axios + + '@iconify/json@2.2.373': + dependencies: + '@iconify/types': 2.0.0 + pathe: 1.1.2 + + '@iconify/types@2.0.0': {} + + '@iconify/utils@3.0.0': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@antfu/utils': 9.2.0 + '@iconify/types': 2.0.0 + debug: 4.4.1 + globals: 15.15.0 + kolorist: 1.8.0 + local-pkg: 1.1.1 + mlly: 1.7.4 + transitivePeerDependencies: + - supports-color + + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + + '@jridgewell/gen-mapping@0.3.12': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/trace-mapping': 0.3.29 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.10': + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + + '@jridgewell/sourcemap-codec@1.5.4': {} + + '@jridgewell/trace-mapping@0.3.29': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.4 + + '@lit-labs/ssr-dom-shim@1.4.0': {} + + '@lit/context@1.1.6': + dependencies: + '@lit/reactive-element': 2.1.1 + + '@lit/localize-tools@0.8.0': + dependencies: + '@lit/localize': 0.12.2 + '@parse5/tools': 0.3.0 + '@xmldom/xmldom': 0.8.10 + fast-glob: 3.2.12 + fs-extra: 10.1.0 + jsonschema: 1.4.1 + lit: 3.3.1 + minimist: 1.2.8 + parse5: 7.2.1 + source-map-support: 0.5.21 + typescript: 5.5.4 + + '@lit/localize@0.12.2': + dependencies: + lit: 3.3.1 + + '@lit/reactive-element@2.1.1': + dependencies: + '@lit-labs/ssr-dom-shim': 1.4.0 + + '@microsoft/api-extractor-model@7.30.7(@types/node@18.19.123)': + dependencies: + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@rushstack/node-core-library': 5.14.0(@types/node@18.19.123) + transitivePeerDependencies: + - '@types/node' + + '@microsoft/api-extractor-model@7.30.7(@types/node@20.19.10)': + dependencies: + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@rushstack/node-core-library': 5.14.0(@types/node@20.19.10) + transitivePeerDependencies: + - '@types/node' + optional: true + + '@microsoft/api-extractor@7.52.10(@types/node@18.19.123)': + dependencies: + '@microsoft/api-extractor-model': 7.30.7(@types/node@18.19.123) + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@rushstack/node-core-library': 5.14.0(@types/node@18.19.123) + '@rushstack/rig-package': 0.5.3 + '@rushstack/terminal': 0.15.4(@types/node@18.19.123) + '@rushstack/ts-command-line': 5.0.2(@types/node@18.19.123) + lodash: 4.17.21 + minimatch: 10.0.3 + resolve: 1.22.10 + semver: 7.5.4 + source-map: 0.6.1 + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/node' + + '@microsoft/api-extractor@7.52.10(@types/node@20.19.10)': + dependencies: + '@microsoft/api-extractor-model': 7.30.7(@types/node@20.19.10) + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@rushstack/node-core-library': 5.14.0(@types/node@20.19.10) + '@rushstack/rig-package': 0.5.3 + '@rushstack/terminal': 0.15.4(@types/node@20.19.10) + '@rushstack/ts-command-line': 5.0.2(@types/node@20.19.10) + lodash: 4.17.21 + minimatch: 10.0.3 + resolve: 1.22.10 + semver: 7.5.4 + source-map: 0.6.1 + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/node' + optional: true + + '@microsoft/tsdoc-config@0.17.1': + dependencies: + '@microsoft/tsdoc': 0.15.1 + ajv: 8.12.0 + jju: 1.4.0 + resolve: 1.22.10 + + '@microsoft/tsdoc@0.15.1': {} + + '@module-federation/error-codes@0.17.1': {} + + '@module-federation/runtime-core@0.17.1': + dependencies: + '@module-federation/error-codes': 0.17.1 + '@module-federation/sdk': 0.17.1 + + '@module-federation/runtime-tools@0.17.1': + dependencies: + '@module-federation/runtime': 0.17.1 + '@module-federation/webpack-bundler-runtime': 0.17.1 + + '@module-federation/runtime@0.17.1': + dependencies: + '@module-federation/error-codes': 0.17.1 + '@module-federation/runtime-core': 0.17.1 + '@module-federation/sdk': 0.17.1 + + '@module-federation/sdk@0.17.1': {} + + '@module-federation/webpack-bundler-runtime@0.17.1': + dependencies: + '@module-federation/runtime': 0.17.1 + '@module-federation/sdk': 0.17.1 + + '@napi-rs/wasm-runtime@1.0.1': + dependencies: + '@emnapi/core': 1.4.5 + '@emnapi/runtime': 1.4.5 + '@tybys/wasm-util': 0.10.0 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + + '@oxc-project/runtime@0.77.3': {} + + '@oxc-project/types@0.77.3': {} + + '@parse5/tools@0.3.0': + dependencies: + parse5: 7.2.1 + + '@polka/url@1.0.0-next.29': {} + + '@quansync/fs@0.1.3': + dependencies: + quansync: 0.2.10 + + '@remirror/core-constants@3.0.0': {} + + '@rolldown/binding-android-arm64@1.0.0-beta.29': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-beta.29': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-beta.29': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-beta.29': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.29': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.29': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.29': + optional: true + + '@rolldown/binding-linux-arm64-ohos@1.0.0-beta.29': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.29': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.29': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.29': + dependencies: + '@napi-rs/wasm-runtime': 1.0.1 + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.29': + optional: true + + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.29': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.29': + optional: true + + '@rolldown/pluginutils@1.0.0-beta.29': {} + + '@rollup/pluginutils@5.2.0(rollup@4.46.2)': + dependencies: + '@types/estree': 1.0.5 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.46.2 + + '@rollup/rollup-android-arm-eabi@4.46.2': + optional: true + + '@rollup/rollup-android-arm64@4.46.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.46.2': + optional: true + + '@rollup/rollup-darwin-x64@4.46.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.46.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.46.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.46.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.46.2': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.46.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.46.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.46.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.46.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.46.2': + optional: true + + '@rsbuild/core@1.4.15': + dependencies: + '@rspack/core': 1.4.11(@swc/helpers@0.5.17) + '@rspack/lite-tapable': 1.0.1 + '@swc/helpers': 0.5.17 + core-js: 3.45.0 + jiti: 2.5.1 + + '@rsbuild/core@1.5.0-beta.3': + dependencies: + '@rspack/core': 1.5.0-beta.0(@swc/helpers@0.5.17) + '@rspack/lite-tapable': 1.0.1 + '@swc/helpers': 0.5.17 + core-js: 3.45.0 + jiti: 2.5.1 + + '@rsbuild/plugin-vue@1.1.1(@rsbuild/core@1.4.15)(esbuild@0.25.8)(vue@3.5.18(typescript@5.8.3))': + dependencies: + '@rsbuild/core': 1.4.15 + vue-loader: 17.4.2(vue@3.5.18(typescript@5.8.3))(webpack@5.101.0(esbuild@0.25.8)) + webpack: 5.101.0(esbuild@0.25.8) + transitivePeerDependencies: + - '@swc/core' + - '@vue/compiler-sfc' + - esbuild + - uglify-js + - vue + - webpack-cli + + '@rslib/core@0.12.1(@microsoft/api-extractor@7.52.10(@types/node@20.19.10))(typescript@5.8.3)': + dependencies: + '@rsbuild/core': 1.5.0-beta.3 + rsbuild-plugin-dts: 0.12.1(@microsoft/api-extractor@7.52.10(@types/node@20.19.10))(@rsbuild/core@1.5.0-beta.3)(typescript@5.8.3) + tinyglobby: 0.2.14 + optionalDependencies: + '@microsoft/api-extractor': 7.52.10(@types/node@20.19.10) + typescript: 5.8.3 + + '@rspack/binding-darwin-arm64@1.4.11': + optional: true + + '@rspack/binding-darwin-arm64@1.5.0-beta.0': + optional: true + + '@rspack/binding-darwin-x64@1.4.11': + optional: true + + '@rspack/binding-darwin-x64@1.5.0-beta.0': + optional: true + + '@rspack/binding-linux-arm64-gnu@1.4.11': + optional: true + + '@rspack/binding-linux-arm64-gnu@1.5.0-beta.0': + optional: true + + '@rspack/binding-linux-arm64-musl@1.4.11': + optional: true + + '@rspack/binding-linux-arm64-musl@1.5.0-beta.0': + optional: true + + '@rspack/binding-linux-x64-gnu@1.4.11': + optional: true + + '@rspack/binding-linux-x64-gnu@1.5.0-beta.0': + optional: true + + '@rspack/binding-linux-x64-musl@1.4.11': + optional: true + + '@rspack/binding-linux-x64-musl@1.5.0-beta.0': + optional: true + + '@rspack/binding-wasm32-wasi@1.4.11': + dependencies: + '@napi-rs/wasm-runtime': 1.0.1 + optional: true + + '@rspack/binding-wasm32-wasi@1.5.0-beta.0': + dependencies: + '@napi-rs/wasm-runtime': 1.0.1 + optional: true + + '@rspack/binding-win32-arm64-msvc@1.4.11': + optional: true + + '@rspack/binding-win32-arm64-msvc@1.5.0-beta.0': + optional: true + + '@rspack/binding-win32-ia32-msvc@1.4.11': + optional: true + + '@rspack/binding-win32-ia32-msvc@1.5.0-beta.0': + optional: true + + '@rspack/binding-win32-x64-msvc@1.4.11': + optional: true + + '@rspack/binding-win32-x64-msvc@1.5.0-beta.0': + optional: true + + '@rspack/binding@1.4.11': + optionalDependencies: + '@rspack/binding-darwin-arm64': 1.4.11 + '@rspack/binding-darwin-x64': 1.4.11 + '@rspack/binding-linux-arm64-gnu': 1.4.11 + '@rspack/binding-linux-arm64-musl': 1.4.11 + '@rspack/binding-linux-x64-gnu': 1.4.11 + '@rspack/binding-linux-x64-musl': 1.4.11 + '@rspack/binding-wasm32-wasi': 1.4.11 + '@rspack/binding-win32-arm64-msvc': 1.4.11 + '@rspack/binding-win32-ia32-msvc': 1.4.11 + '@rspack/binding-win32-x64-msvc': 1.4.11 + + '@rspack/binding@1.5.0-beta.0': + optionalDependencies: + '@rspack/binding-darwin-arm64': 1.5.0-beta.0 + '@rspack/binding-darwin-x64': 1.5.0-beta.0 + '@rspack/binding-linux-arm64-gnu': 1.5.0-beta.0 + '@rspack/binding-linux-arm64-musl': 1.5.0-beta.0 + '@rspack/binding-linux-x64-gnu': 1.5.0-beta.0 + '@rspack/binding-linux-x64-musl': 1.5.0-beta.0 + '@rspack/binding-wasm32-wasi': 1.5.0-beta.0 + '@rspack/binding-win32-arm64-msvc': 1.5.0-beta.0 + '@rspack/binding-win32-ia32-msvc': 1.5.0-beta.0 + '@rspack/binding-win32-x64-msvc': 1.5.0-beta.0 + + '@rspack/core@1.4.11(@swc/helpers@0.5.17)': + dependencies: + '@module-federation/runtime-tools': 0.17.1 + '@rspack/binding': 1.4.11 + '@rspack/lite-tapable': 1.0.1 + optionalDependencies: + '@swc/helpers': 0.5.17 + + '@rspack/core@1.5.0-beta.0(@swc/helpers@0.5.17)': + dependencies: + '@module-federation/runtime-tools': 0.17.1 + '@rspack/binding': 1.5.0-beta.0 + '@rspack/lite-tapable': 1.0.1 + optionalDependencies: + '@swc/helpers': 0.5.17 + + '@rspack/lite-tapable@1.0.1': {} + + '@rushstack/node-core-library@5.14.0(@types/node@18.19.123)': + dependencies: + ajv: 8.13.0 + ajv-draft-04: 1.0.0(ajv@8.13.0) + ajv-formats: 3.0.1(ajv@8.13.0) + fs-extra: 11.3.1 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.10 + semver: 7.5.4 + optionalDependencies: + '@types/node': 18.19.123 + + '@rushstack/node-core-library@5.14.0(@types/node@20.19.10)': + dependencies: + ajv: 8.13.0 + ajv-draft-04: 1.0.0(ajv@8.13.0) + ajv-formats: 3.0.1(ajv@8.13.0) + fs-extra: 11.3.1 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.10 + semver: 7.5.4 + optionalDependencies: + '@types/node': 20.19.10 + optional: true + + '@rushstack/rig-package@0.5.3': + dependencies: + resolve: 1.22.10 + strip-json-comments: 3.1.1 + + '@rushstack/terminal@0.15.4(@types/node@18.19.123)': + dependencies: + '@rushstack/node-core-library': 5.14.0(@types/node@18.19.123) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 18.19.123 + + '@rushstack/terminal@0.15.4(@types/node@20.19.10)': + dependencies: + '@rushstack/node-core-library': 5.14.0(@types/node@20.19.10) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 20.19.10 + optional: true + + '@rushstack/ts-command-line@5.0.2(@types/node@18.19.123)': + dependencies: + '@rushstack/terminal': 0.15.4(@types/node@18.19.123) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/ts-command-line@5.0.2(@types/node@20.19.10)': + dependencies: + '@rushstack/terminal': 0.15.4(@types/node@20.19.10) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + optional: true + + '@shikijs/core@3.9.2': + dependencies: + '@shikijs/types': 3.9.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.9.2': + dependencies: + '@shikijs/types': 3.9.2 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.3 + + '@shikijs/engine-oniguruma@3.9.2': + dependencies: + '@shikijs/types': 3.9.2 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.9.2': + dependencies: + '@shikijs/types': 3.9.2 + + '@shikijs/themes@3.9.2': + dependencies: + '@shikijs/types': 3.9.2 + + '@shikijs/types@3.9.2': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@swc/helpers@0.5.17': + dependencies: + tslib: 2.8.1 + + '@tiptap/core@3.2.0(@tiptap/pm@3.2.0)': + dependencies: + '@tiptap/pm': 3.2.0 + + '@tiptap/extension-blockquote@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + + '@tiptap/extension-bold@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + + '@tiptap/extension-bullet-list@3.2.0(@tiptap/extension-list@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/extension-list': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + + '@tiptap/extension-code-block@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + '@tiptap/pm': 3.2.0 + + '@tiptap/extension-code@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + + '@tiptap/extension-document@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + + '@tiptap/extension-dropcursor@3.2.0(@tiptap/extensions@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/extensions': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + + '@tiptap/extension-gapcursor@3.2.0(@tiptap/extensions@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/extensions': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + + '@tiptap/extension-hard-break@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + + '@tiptap/extension-heading@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + + '@tiptap/extension-horizontal-rule@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + '@tiptap/pm': 3.2.0 + + '@tiptap/extension-italic@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + + '@tiptap/extension-link@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + '@tiptap/pm': 3.2.0 + linkifyjs: 4.3.2 + + '@tiptap/extension-list-item@3.2.0(@tiptap/extension-list@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/extension-list': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + + '@tiptap/extension-list-keymap@3.2.0(@tiptap/extension-list@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/extension-list': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + + '@tiptap/extension-list@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + '@tiptap/pm': 3.2.0 + + '@tiptap/extension-ordered-list@3.2.0(@tiptap/extension-list@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/extension-list': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + + '@tiptap/extension-paragraph@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + + '@tiptap/extension-strike@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + + '@tiptap/extension-text@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + + '@tiptap/extension-underline@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + + '@tiptap/extensions@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + '@tiptap/pm': 3.2.0 + + '@tiptap/pm@3.2.0': + dependencies: + prosemirror-changeset: 2.3.1 + prosemirror-collab: 1.3.1 + prosemirror-commands: 1.7.1 + prosemirror-dropcursor: 1.8.2 + prosemirror-gapcursor: 1.3.2 + prosemirror-history: 1.4.1 + prosemirror-inputrules: 1.5.0 + prosemirror-keymap: 1.2.3 + prosemirror-markdown: 1.13.2 + prosemirror-menu: 1.2.5 + prosemirror-model: 1.25.3 + prosemirror-schema-basic: 1.2.4 + prosemirror-schema-list: 1.5.1 + prosemirror-state: 1.4.3 + prosemirror-tables: 1.7.1 + prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.3)(prosemirror-state@1.4.3)(prosemirror-view@1.40.1) + prosemirror-transform: 1.10.4 + prosemirror-view: 1.40.1 + + '@tiptap/starter-kit@3.2.0': + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + '@tiptap/extension-blockquote': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0)) + '@tiptap/extension-bold': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0)) + '@tiptap/extension-bullet-list': 3.2.0(@tiptap/extension-list@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)) + '@tiptap/extension-code': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0)) + '@tiptap/extension-code-block': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + '@tiptap/extension-document': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0)) + '@tiptap/extension-dropcursor': 3.2.0(@tiptap/extensions@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)) + '@tiptap/extension-gapcursor': 3.2.0(@tiptap/extensions@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)) + '@tiptap/extension-hard-break': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0)) + '@tiptap/extension-heading': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0)) + '@tiptap/extension-horizontal-rule': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + '@tiptap/extension-italic': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0)) + '@tiptap/extension-link': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + '@tiptap/extension-list': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + '@tiptap/extension-list-item': 3.2.0(@tiptap/extension-list@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)) + '@tiptap/extension-list-keymap': 3.2.0(@tiptap/extension-list@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)) + '@tiptap/extension-ordered-list': 3.2.0(@tiptap/extension-list@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)) + '@tiptap/extension-paragraph': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0)) + '@tiptap/extension-strike': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0)) + '@tiptap/extension-text': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0)) + '@tiptap/extension-underline': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0)) + '@tiptap/extensions': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + '@tiptap/pm': 3.2.0 + + '@tsconfig/node18@18.2.4': {} + + '@tybys/wasm-util@0.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/argparse@1.0.38': {} + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.5': {} + + '@types/estree@1.0.8': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/json-schema@7.0.15': {} + + '@types/linkify-it@5.0.0': {} + + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdurl@2.0.0': {} + + '@types/node-fetch@2.6.13': + dependencies: + '@types/node': 20.19.10 + form-data: 4.0.4 + + '@types/node@18.19.123': + dependencies: + undici-types: 5.26.5 + + '@types/node@20.19.10': + dependencies: + undici-types: 6.21.0 + + '@types/sanitize-html@2.16.0': + dependencies: + htmlparser2: 8.0.2 + + '@types/trusted-types@2.0.7': {} + + '@types/unist@3.0.3': {} + + '@ungap/structured-clone@1.3.0': {} + + '@unocss/astro@66.4.2(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@unocss/core': 66.4.2 + '@unocss/reset': 66.4.2 + '@unocss/vite': 66.4.2(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + optionalDependencies: + vite: 7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + + '@unocss/cli@66.4.2': + dependencies: + '@ampproject/remapping': 2.3.0 + '@unocss/config': 66.4.2 + '@unocss/core': 66.4.2 + '@unocss/preset-uno': 66.4.2 + cac: 6.7.14 + chokidar: 3.6.0 + colorette: 2.0.20 + consola: 3.4.2 + magic-string: 0.30.17 + pathe: 2.0.3 + perfect-debounce: 1.0.0 + tinyglobby: 0.2.14 + unplugin-utils: 0.2.5 + + '@unocss/config@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + unconfig: 7.3.2 + + '@unocss/core@66.4.2': {} + + '@unocss/extractor-arbitrary-variants@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + + '@unocss/inspector@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + '@unocss/rule-utils': 66.4.2 + colorette: 2.0.20 + gzip-size: 6.0.0 + sirv: 3.0.1 + vue-flow-layout: 0.2.0 + + '@unocss/postcss@66.4.2(postcss@8.5.6)': + dependencies: + '@unocss/config': 66.4.2 + '@unocss/core': 66.4.2 + '@unocss/rule-utils': 66.4.2 + css-tree: 3.1.0 + postcss: 8.5.6 + tinyglobby: 0.2.14 + + '@unocss/preset-attributify@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + + '@unocss/preset-icons@66.4.2': + dependencies: + '@iconify/utils': 3.0.0 + '@unocss/core': 66.4.2 + ofetch: 1.4.1 + transitivePeerDependencies: + - supports-color + + '@unocss/preset-mini@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + '@unocss/extractor-arbitrary-variants': 66.4.2 + '@unocss/rule-utils': 66.4.2 + + '@unocss/preset-tagify@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + + '@unocss/preset-typography@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + '@unocss/preset-mini': 66.4.2 + '@unocss/rule-utils': 66.4.2 + + '@unocss/preset-uno@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + '@unocss/preset-wind3': 66.4.2 + + '@unocss/preset-web-fonts@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + ofetch: 1.4.1 + + '@unocss/preset-wind3@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + '@unocss/preset-mini': 66.4.2 + '@unocss/rule-utils': 66.4.2 + + '@unocss/preset-wind4@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + '@unocss/extractor-arbitrary-variants': 66.4.2 + '@unocss/rule-utils': 66.4.2 + + '@unocss/preset-wind@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + '@unocss/preset-wind3': 66.4.2 + + '@unocss/reset@66.4.2': {} + + '@unocss/rule-utils@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + magic-string: 0.30.17 + + '@unocss/transformer-attributify-jsx@66.4.2': + dependencies: + '@babel/parser': 7.28.0 + '@babel/traverse': 7.28.0 + '@unocss/core': 66.4.2 + transitivePeerDependencies: + - supports-color + + '@unocss/transformer-compile-class@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + + '@unocss/transformer-directives@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + '@unocss/rule-utils': 66.4.2 + css-tree: 3.1.0 + + '@unocss/transformer-variant-group@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + + '@unocss/vite@66.4.2(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@unocss/config': 66.4.2 + '@unocss/core': 66.4.2 + '@unocss/inspector': 66.4.2 + chokidar: 3.6.0 + magic-string: 0.30.17 + pathe: 2.0.3 + tinyglobby: 0.2.14 + unplugin-utils: 0.2.5 + vite: 7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + + '@vitejs/plugin-vue@6.0.1(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.18(typescript@5.8.3))': + dependencies: + '@rolldown/pluginutils': 1.0.0-beta.29 + vite: 7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vue: 3.5.18(typescript@5.8.3) + + '@volar/language-core@2.4.15': + dependencies: + '@volar/source-map': 2.4.15 + + '@volar/language-core@2.4.22': + dependencies: + '@volar/source-map': 2.4.22 + + '@volar/source-map@2.4.15': {} + + '@volar/source-map@2.4.22': {} + + '@volar/typescript@2.4.15': + dependencies: + '@volar/language-core': 2.4.15 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@volar/typescript@2.4.22': + dependencies: + '@volar/language-core': 2.4.22 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vscode/web-custom-data@0.4.9': {} + + '@vue/compiler-core@3.5.18': + dependencies: + '@babel/parser': 7.28.0 + '@vue/shared': 3.5.18 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.18': + dependencies: + '@vue/compiler-core': 3.5.18 + '@vue/shared': 3.5.18 + + '@vue/compiler-sfc@3.5.18': + dependencies: + '@babel/parser': 7.28.0 + '@vue/compiler-core': 3.5.18 + '@vue/compiler-dom': 3.5.18 + '@vue/compiler-ssr': 3.5.18 + '@vue/shared': 3.5.18 + estree-walker: 2.0.2 + magic-string: 0.30.17 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.18': + dependencies: + '@vue/compiler-dom': 3.5.18 + '@vue/shared': 3.5.18 + + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/devtools-api@6.6.4': {} + + '@vue/language-core@2.2.0(typescript@5.3.3)': + dependencies: + '@volar/language-core': 2.4.22 + '@vue/compiler-dom': 3.5.18 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.18 + alien-signals: 0.4.14 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.3.3 + + '@vue/language-core@2.2.12(typescript@5.8.3)': + dependencies: + '@volar/language-core': 2.4.15 + '@vue/compiler-dom': 3.5.18 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.18 + alien-signals: 1.0.13 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.8.3 + + '@vue/reactivity@3.5.18': + dependencies: + '@vue/shared': 3.5.18 + + '@vue/runtime-core@3.5.18': + dependencies: + '@vue/reactivity': 3.5.18 + '@vue/shared': 3.5.18 + + '@vue/runtime-dom@3.5.18': + dependencies: + '@vue/reactivity': 3.5.18 + '@vue/runtime-core': 3.5.18 + '@vue/shared': 3.5.18 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.18(vue@3.5.18(typescript@5.8.3))': + dependencies: + '@vue/compiler-ssr': 3.5.18 + '@vue/shared': 3.5.18 + vue: 3.5.18(typescript@5.8.3) + + '@vue/shared@3.5.18': {} + + '@vue/tsconfig@0.7.0(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3))': + optionalDependencies: + typescript: 5.8.3 + vue: 3.5.18(typescript@5.8.3) + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xmldom/xmldom@0.8.10': {} + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + acorn-import-phases@1.0.4(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv-draft-04@1.0.0(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + + ajv-formats@2.1.1(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + + ajv-formats@3.0.1(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + + ajv-keywords@5.1.0(ajv@8.13.0): + dependencies: + ajv: 8.13.0 + fast-deep-equal: 3.1.3 + + ajv@8.12.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + ajv@8.13.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + alien-signals@0.4.14: {} + + alien-signals@1.0.13: {} + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + ansis@4.1.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + asynckit@0.4.0: {} + + axios@1.11.0: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + binary-extensions@2.3.0: {} + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.2: + dependencies: + fill-range: 7.0.1 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.25.2: + dependencies: + caniuse-lite: 1.0.30001734 + electron-to-chromium: 1.5.199 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.2) + + buffer-from@1.1.2: {} + + cac@6.7.14: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + caniuse-lite@1.0.30001734: {} + + ccount@2.0.1: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.5.0: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chrome-trace-event@1.0.4: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comma-separated-tokens@2.0.3: {} + + commander@14.0.0: {} + + commander@2.20.3: {} + + compare-versions@6.1.1: {} + + confbox@0.1.8: {} + + confbox@0.2.2: {} + + consola@3.4.2: {} + + core-js@3.45.0: {} + + crelt@1.0.6: {} + + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + + csstype@3.1.3: {} + + dayjs@1.11.13: {} + + de-indent@1.0.2: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + deepmerge@4.3.1: {} + + defu@6.1.4: {} + + delayed-stream@1.0.0: {} + + dequal@2.0.3: {} + + destr@2.0.5: {} + + detect-europe-js@0.1.2: {} + + detect-libc@2.0.4: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + didyoumean2@4.1.0: + dependencies: + '@babel/runtime': 7.23.9 + leven: 3.1.0 + lodash.deburr: 4.1.0 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexer@0.1.2: {} + + electron-to-chromium@1.5.199: {} + + emoji-mart@5.6.0: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.2 + + entities@4.5.0: {} + + environment@1.1.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-toolkit@1.39.9: {} + + esbuild@0.25.8: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.8 + '@esbuild/android-arm': 0.25.8 + '@esbuild/android-arm64': 0.25.8 + '@esbuild/android-x64': 0.25.8 + '@esbuild/darwin-arm64': 0.25.8 + '@esbuild/darwin-x64': 0.25.8 + '@esbuild/freebsd-arm64': 0.25.8 + '@esbuild/freebsd-x64': 0.25.8 + '@esbuild/linux-arm': 0.25.8 + '@esbuild/linux-arm64': 0.25.8 + '@esbuild/linux-ia32': 0.25.8 + '@esbuild/linux-loong64': 0.25.8 + '@esbuild/linux-mips64el': 0.25.8 + '@esbuild/linux-ppc64': 0.25.8 + '@esbuild/linux-riscv64': 0.25.8 + '@esbuild/linux-s390x': 0.25.8 + '@esbuild/linux-x64': 0.25.8 + '@esbuild/netbsd-arm64': 0.25.8 + '@esbuild/netbsd-x64': 0.25.8 + '@esbuild/openbsd-arm64': 0.25.8 + '@esbuild/openbsd-x64': 0.25.8 + '@esbuild/openharmony-arm64': 0.25.8 + '@esbuild/sunos-x64': 0.25.8 + '@esbuild/win32-arm64': 0.25.8 + '@esbuild/win32-ia32': 0.25.8 + '@esbuild/win32-x64': 0.25.8 + + escalade@3.1.2: {} + + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + eventemitter3@5.0.1: {} + + events@3.3.0: {} + + exsolve@1.0.7: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.2.12: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + + fastq@1.15.0: + dependencies: + reusify: 1.0.4 + + fdir@6.4.6(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fill-range@7.0.1: + dependencies: + to-regex-range: 5.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + floating-vue@5.2.2(vue@3.5.18(typescript@5.8.3)): + dependencies: + '@floating-ui/dom': 1.1.1 + vue: 3.5.18(typescript@5.8.3) + vue-resize: 2.0.0-alpha.1(vue@3.5.18(typescript@5.8.3)) + + follow-redirects@1.15.11: {} + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.10 + jsonfile: 6.1.0 + universalify: 2.0.0 + + fs-extra@11.3.1: + dependencies: + graceful-fs: 4.2.10 + jsonfile: 6.1.0 + universalify: 2.0.0 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.3.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regexp@0.4.1: {} + + globals@15.15.0: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.10: {} + + graceful-fs@4.2.11: {} + + gzip-size@6.0.0: + dependencies: + duplexer: 0.1.2 + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hash-sum@2.0.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + he@1.2.0: {} + + html-void-elements@3.0.0: {} + + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + + husky@9.1.7: {} + + import-lazy@4.0.0: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.3.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-plain-object@5.0.0: {} + + is-standalone-pwa@0.1.1: {} + + javascript-time-ago@2.5.11: + dependencies: + relative-time-format: 1.1.6 + + jest-worker@27.5.1: + dependencies: + '@types/node': 20.19.10 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jiti@2.5.1: {} + + jju@1.4.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@1.0.0: {} + + json5@2.2.3: {} + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.0 + optionalDependencies: + graceful-fs: 4.2.10 + + jsonschema@1.4.1: {} + + kolorist@1.8.0: {} + + leven@3.1.0: {} + + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 + + lilconfig@3.1.3: {} + + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + + linkifyjs@4.3.2: {} + + lint-staged@16.1.5: + dependencies: + chalk: 5.5.0 + commander: 14.0.0 + debug: 4.4.1 + lilconfig: 3.1.3 + listr2: 9.0.1 + micromatch: 4.0.8 + nano-spawn: 1.0.2 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.8.1 + transitivePeerDependencies: + - supports-color + + listr2@9.0.1: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + lit-analyzer@2.0.3: + dependencies: + '@vscode/web-custom-data': 0.4.9 + chalk: 2.4.2 + didyoumean2: 4.1.0 + fast-glob: 3.2.12 + parse5: 5.1.0 + ts-simple-type: 2.0.0-next.0 + vscode-css-languageservice: 4.3.0 + vscode-html-languageservice: 3.1.0 + web-component-analyzer: 2.0.0 + + lit-element@4.2.1: + dependencies: + '@lit-labs/ssr-dom-shim': 1.4.0 + '@lit/reactive-element': 2.1.1 + lit-html: 3.3.1 + + lit-html@3.3.1: + dependencies: + '@types/trusted-types': 2.0.7 + + lit@3.3.1: + dependencies: + '@lit/reactive-element': 2.1.1 + lit-element: 4.2.1 + lit-html: 3.3.1 + + loader-runner@4.3.0: {} + + local-pkg@1.1.1: + dependencies: + mlly: 1.7.4 + pkg-types: 2.2.0 + quansync: 0.2.10 + + lodash.deburr@4.1.0: {} + + lodash@4.17.21: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + math-intrinsics@1.1.0: {} + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdn-data@2.12.2: {} + + mdurl@2.0.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromatch@4.0.5: + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-function@5.0.1: {} + + minimatch@10.0.3: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + mlly@1.7.4: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + nano-spawn@1.0.2: {} + + nanoid@3.3.11: {} + + neo-async@2.6.2: {} + + node-fetch-native@1.6.7: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-releases@2.0.19: {} + + normalize-path@3.0.0: {} + + object-inspect@1.13.4: {} + + ofetch@1.4.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.1 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.3: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.0.1 + regex-recursion: 6.0.2 + + orderedmap@2.1.1: {} + + package-manager-detector@1.3.0: {} + + parse-srcset@1.0.2: {} + + parse5@5.1.0: {} + + parse5@7.2.1: + dependencies: + entities: 4.5.0 + + path-browserify@1.0.1: {} + + path-parse@1.0.7: {} + + pathe@1.1.2: {} + + pathe@2.0.3: {} + + perfect-debounce@1.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pidtree@0.6.0: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.3 + + pkg-types@2.2.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.7 + pathe: 2.0.3 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + property-information@7.1.0: {} + + prosemirror-changeset@2.3.1: + dependencies: + prosemirror-transform: 1.10.4 + + prosemirror-collab@1.3.1: + dependencies: + prosemirror-state: 1.4.3 + + prosemirror-commands@1.7.1: + dependencies: + prosemirror-model: 1.25.3 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + + prosemirror-dropcursor@1.8.2: + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.40.1 + + prosemirror-gapcursor@1.3.2: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.3 + prosemirror-state: 1.4.3 + prosemirror-view: 1.40.1 + + prosemirror-history@1.4.1: + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.40.1 + rope-sequence: 1.3.4 + + prosemirror-inputrules@1.5.0: + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + + prosemirror-keymap@1.2.3: + dependencies: + prosemirror-state: 1.4.3 + w3c-keyname: 2.2.8 + + prosemirror-markdown@1.13.2: + dependencies: + '@types/markdown-it': 14.1.2 + markdown-it: 14.1.0 + prosemirror-model: 1.25.3 + + prosemirror-menu@1.2.5: + dependencies: + crelt: 1.0.6 + prosemirror-commands: 1.7.1 + prosemirror-history: 1.4.1 + prosemirror-state: 1.4.3 + + prosemirror-model@1.25.3: + dependencies: + orderedmap: 2.1.1 + + prosemirror-schema-basic@1.2.4: + dependencies: + prosemirror-model: 1.25.3 + + prosemirror-schema-list@1.5.1: + dependencies: + prosemirror-model: 1.25.3 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + + prosemirror-state@1.4.3: + dependencies: + prosemirror-model: 1.25.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.40.1 + + prosemirror-tables@1.7.1: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.3 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + prosemirror-view: 1.40.1 + + prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.3)(prosemirror-state@1.4.3)(prosemirror-view@1.40.1): + dependencies: + '@remirror/core-constants': 3.0.0 + escape-string-regexp: 4.0.0 + prosemirror-model: 1.25.3 + prosemirror-state: 1.4.3 + prosemirror-view: 1.40.1 + + prosemirror-transform@1.10.4: + dependencies: + prosemirror-model: 1.25.3 + + prosemirror-view@1.40.1: + dependencies: + prosemirror-model: 1.25.3 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.4 + + proxy-from-env@1.1.0: {} + + punycode.js@2.3.1: {} + + punycode@2.3.1: {} + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + quansync@0.2.10: {} + + queue-microtask@1.2.3: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + regenerator-runtime@0.14.1: {} + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + + relative-time-format@1.1.6: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.0.4: {} + + rfdc@1.4.1: {} + + rolldown-vite@7.0.10(@types/node@18.19.123)(esbuild@0.25.8)(jiti@2.5.1)(terser@5.43.1)(yaml@2.8.1): + dependencies: + fdir: 6.4.6(picomatch@4.0.3) + lightningcss: 1.30.1 + picomatch: 4.0.3 + postcss: 8.5.6 + rolldown: 1.0.0-beta.29 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 18.19.123 + esbuild: 0.25.8 + fsevents: 2.3.3 + jiti: 2.5.1 + terser: 5.43.1 + yaml: 2.8.1 + + rolldown@1.0.0-beta.29: + dependencies: + '@oxc-project/runtime': 0.77.3 + '@oxc-project/types': 0.77.3 + '@rolldown/pluginutils': 1.0.0-beta.29 + ansis: 4.1.0 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-beta.29 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.29 + '@rolldown/binding-darwin-x64': 1.0.0-beta.29 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.29 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.29 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.29 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.29 + '@rolldown/binding-linux-arm64-ohos': 1.0.0-beta.29 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.29 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.29 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.29 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.29 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.29 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.29 + + rollup@4.46.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.46.2 + '@rollup/rollup-android-arm64': 4.46.2 + '@rollup/rollup-darwin-arm64': 4.46.2 + '@rollup/rollup-darwin-x64': 4.46.2 + '@rollup/rollup-freebsd-arm64': 4.46.2 + '@rollup/rollup-freebsd-x64': 4.46.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.46.2 + '@rollup/rollup-linux-arm-musleabihf': 4.46.2 + '@rollup/rollup-linux-arm64-gnu': 4.46.2 + '@rollup/rollup-linux-arm64-musl': 4.46.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.46.2 + '@rollup/rollup-linux-ppc64-gnu': 4.46.2 + '@rollup/rollup-linux-riscv64-gnu': 4.46.2 + '@rollup/rollup-linux-riscv64-musl': 4.46.2 + '@rollup/rollup-linux-s390x-gnu': 4.46.2 + '@rollup/rollup-linux-x64-gnu': 4.46.2 + '@rollup/rollup-linux-x64-musl': 4.46.2 + '@rollup/rollup-win32-arm64-msvc': 4.46.2 + '@rollup/rollup-win32-ia32-msvc': 4.46.2 + '@rollup/rollup-win32-x64-msvc': 4.46.2 + fsevents: 2.3.3 + + rope-sequence@1.3.4: {} + + rsbuild-plugin-dts@0.12.1(@microsoft/api-extractor@7.52.10(@types/node@20.19.10))(@rsbuild/core@1.5.0-beta.3)(typescript@5.8.3): + dependencies: + '@ast-grep/napi': 0.37.0 + '@rsbuild/core': 1.5.0-beta.3 + magic-string: 0.30.17 + picocolors: 1.1.1 + tinyglobby: 0.2.14 + tsconfig-paths: 4.2.0 + optionalDependencies: + '@microsoft/api-extractor': 7.52.10(@types/node@20.19.10) + typescript: 5.8.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-buffer@5.2.1: {} + + sanitize-html@2.17.0: + dependencies: + deepmerge: 4.3.1 + escape-string-regexp: 4.0.0 + htmlparser2: 8.0.2 + is-plain-object: 5.0.0 + parse-srcset: 1.0.2 + postcss: 8.5.6 + + schema-utils@4.3.2: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.13.0 + ajv-formats: 2.1.1(ajv@8.13.0) + ajv-keywords: 5.1.0(ajv@8.13.0) + + semver@7.5.4: + dependencies: + lru-cache: 6.0.0 + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + shiki@3.9.2: + dependencies: + '@shikijs/core': 3.9.2 + '@shikijs/engine-javascript': 3.9.2 + '@shikijs/engine-oniguruma': 3.9.2 + '@shikijs/langs': 3.9.2 + '@shikijs/themes': 3.9.2 + '@shikijs/types': 3.9.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@4.1.0: {} + + sirv@3.0.1: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + space-separated-tokens@2.0.2: {} + + sprintf-js@1.0.3: {} + + string-argv@0.3.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tapable@2.2.2: {} + + terser-webpack-plugin@5.3.14(esbuild@0.25.8)(webpack@5.101.0(esbuild@0.25.8)): + dependencies: + '@jridgewell/trace-mapping': 0.3.29 + jest-worker: 27.5.1 + schema-utils: 4.3.2 + serialize-javascript: 6.0.2 + terser: 5.43.1 + webpack: 5.101.0(esbuild@0.25.8) + optionalDependencies: + esbuild: 0.25.8 + + terser@5.43.1: + dependencies: + '@jridgewell/source-map': 0.3.10 + acorn: 8.15.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + tinyexec@1.0.1: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 + + tiptap-extension-code-block-shiki@0.6.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/extension-code-block@3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0)(shiki@3.9.2): + dependencies: + '@tiptap/core': 3.2.0(@tiptap/pm@3.2.0) + '@tiptap/extension-code-block': 3.2.0(@tiptap/core@3.2.0(@tiptap/pm@3.2.0))(@tiptap/pm@3.2.0) + '@tiptap/pm': 3.2.0 + shiki: 3.9.2 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + totalist@3.0.1: {} + + tr46@0.0.3: {} + + trim-lines@3.0.1: {} + + ts-simple-type@2.0.0-next.0: {} + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + typescript@5.2.2: {} + + typescript@5.3.3: {} + + typescript@5.5.4: {} + + typescript@5.8.2: {} + + typescript@5.8.3: {} + + ua-is-frozen@0.1.2: {} + + ua-parser-js@2.0.4: + dependencies: + '@types/node-fetch': 2.6.13 + detect-europe-js: 0.1.2 + is-standalone-pwa: 0.1.1 + node-fetch: 2.7.0 + ua-is-frozen: 0.1.2 + transitivePeerDependencies: + - encoding + + uc.micro@2.1.0: {} + + ufo@1.6.1: {} + + unconfig@7.3.2: + dependencies: + '@quansync/fs': 0.1.3 + defu: 6.1.4 + jiti: 2.5.1 + quansync: 0.2.10 + + undici-types@5.26.5: {} + + undici-types@6.21.0: {} + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + universalify@2.0.0: {} + + unocss@66.4.2(postcss@8.5.6)(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + dependencies: + '@unocss/astro': 66.4.2(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@unocss/cli': 66.4.2 + '@unocss/core': 66.4.2 + '@unocss/postcss': 66.4.2(postcss@8.5.6) + '@unocss/preset-attributify': 66.4.2 + '@unocss/preset-icons': 66.4.2 + '@unocss/preset-mini': 66.4.2 + '@unocss/preset-tagify': 66.4.2 + '@unocss/preset-typography': 66.4.2 + '@unocss/preset-uno': 66.4.2 + '@unocss/preset-web-fonts': 66.4.2 + '@unocss/preset-wind': 66.4.2 + '@unocss/preset-wind3': 66.4.2 + '@unocss/preset-wind4': 66.4.2 + '@unocss/transformer-attributify-jsx': 66.4.2 + '@unocss/transformer-compile-class': 66.4.2 + '@unocss/transformer-directives': 66.4.2 + '@unocss/transformer-variant-group': 66.4.2 + '@unocss/vite': 66.4.2(vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + optionalDependencies: + vite: 7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + transitivePeerDependencies: + - postcss + - supports-color + + unplugin-utils@0.2.5: + dependencies: + pathe: 2.0.3 + picomatch: 4.0.3 + + update-browserslist-db@1.1.3(browserslist@4.25.2): + dependencies: + browserslist: 4.25.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite-plugin-dts@4.5.4(@types/node@18.19.123)(rolldown-vite@7.0.10(@types/node@18.19.123)(esbuild@0.25.8)(jiti@2.5.1)(terser@5.43.1)(yaml@2.8.1))(rollup@4.46.2)(typescript@5.3.3): + dependencies: + '@microsoft/api-extractor': 7.52.10(@types/node@18.19.123) + '@rollup/pluginutils': 5.2.0(rollup@4.46.2) + '@volar/typescript': 2.4.22 + '@vue/language-core': 2.2.0(typescript@5.3.3) + compare-versions: 6.1.1 + debug: 4.4.1 + kolorist: 1.8.0 + local-pkg: 1.1.1 + magic-string: 0.30.17 + typescript: 5.3.3 + optionalDependencies: + vite: rolldown-vite@7.0.10(@types/node@18.19.123)(esbuild@0.25.8)(jiti@2.5.1)(terser@5.43.1)(yaml@2.8.1) + transitivePeerDependencies: + - '@types/node' + - rollup + - supports-color + + vite@7.0.6(@types/node@20.19.10)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + dependencies: + esbuild: 0.25.8 + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.46.2 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 20.19.10 + fsevents: 2.3.3 + jiti: 2.5.1 + lightningcss: 1.30.1 + terser: 5.43.1 + yaml: 2.8.1 + + vscode-css-languageservice@4.3.0: + dependencies: + vscode-languageserver-textdocument: 1.0.11 + vscode-languageserver-types: 3.16.0-next.2 + vscode-nls: 4.1.2 + vscode-uri: 2.1.2 + + vscode-html-languageservice@3.1.0: + dependencies: + vscode-languageserver-textdocument: 1.0.11 + vscode-languageserver-types: 3.16.0-next.2 + vscode-nls: 4.1.2 + vscode-uri: 2.1.2 + + vscode-languageserver-textdocument@1.0.11: {} + + vscode-languageserver-types@3.16.0-next.2: {} + + vscode-nls@4.1.2: {} + + vscode-uri@2.1.2: {} + + vscode-uri@3.1.0: {} + + vue-flow-layout@0.2.0: {} + + vue-loader@17.4.2(vue@3.5.18(typescript@5.8.3))(webpack@5.101.0(esbuild@0.25.8)): + dependencies: + chalk: 4.1.2 + hash-sum: 2.0.0 + watchpack: 2.4.4 + webpack: 5.101.0(esbuild@0.25.8) + optionalDependencies: + vue: 3.5.18(typescript@5.8.3) + + vue-resize@2.0.0-alpha.1(vue@3.5.18(typescript@5.8.3)): + dependencies: + vue: 3.5.18(typescript@5.8.3) + + vue-router@4.5.1(vue@3.5.18(typescript@5.8.3)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.18(typescript@5.8.3) + + vue-tsc@2.2.12(typescript@5.8.3): + dependencies: + '@volar/typescript': 2.4.15 + '@vue/language-core': 2.2.12(typescript@5.8.3) + typescript: 5.8.3 + + vue@3.5.18(typescript@5.8.3): + dependencies: + '@vue/compiler-dom': 3.5.18 + '@vue/compiler-sfc': 3.5.18 + '@vue/runtime-dom': 3.5.18 + '@vue/server-renderer': 3.5.18(vue@3.5.18(typescript@5.8.3)) + '@vue/shared': 3.5.18 + optionalDependencies: + typescript: 5.8.3 + + w3c-keyname@2.2.8: {} + + watchpack@2.4.4: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + web-component-analyzer@2.0.0: + dependencies: + fast-glob: 3.2.12 + ts-simple-type: 2.0.0-next.0 + typescript: 5.2.2 + yargs: 17.7.2 + + webidl-conversions@3.0.1: {} + + webpack-sources@3.3.3: {} + + webpack@5.101.0(esbuild@0.25.8): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.25.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.2 + tapable: 2.2.2 + terser-webpack-plugin: 5.3.14(esbuild@0.25.8)(webpack@5.101.0(esbuild@0.25.8)) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + y18n@5.0.8: {} + + yallist@4.0.0: {} + + yaml@2.8.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..cd7decc --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +packages: + - packages/** +onlyBuiltDependencies: + - core-js + - esbuild diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..44e951c --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'plugin-comment' diff --git a/src/main/java/run/halo/comment/widget/CommentWidgetHeadProcessor.java b/src/main/java/run/halo/comment/widget/CommentWidgetHeadProcessor.java new file mode 100644 index 0000000..3cd21c1 --- /dev/null +++ b/src/main/java/run/halo/comment/widget/CommentWidgetHeadProcessor.java @@ -0,0 +1,43 @@ +package run.halo.comment.widget; + +import java.util.Properties; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.util.PropertyPlaceholderHelper; +import org.thymeleaf.context.ITemplateContext; +import org.thymeleaf.model.IModel; +import org.thymeleaf.model.IModelFactory; +import org.thymeleaf.processor.element.IElementModelStructureHandler; +import reactor.core.publisher.Mono; +import run.halo.app.plugin.PluginContext; +import run.halo.app.theme.dialect.TemplateHeadProcessor; + +@Component +@RequiredArgsConstructor +public class CommentWidgetHeadProcessor implements TemplateHeadProcessor { + + static final PropertyPlaceholderHelper PROPERTY_PLACEHOLDER_HELPER = new PropertyPlaceholderHelper("${", "}"); + + private final PluginContext pluginContext; + + @Override + public Mono process(ITemplateContext context, IModel model, + IElementModelStructureHandler structureHandler) { + final IModelFactory modelFactory = context.getModelFactory(); + model.add(modelFactory.createText(commentWidgetScript())); + return Mono.empty(); + } + + private String commentWidgetScript() { + + final Properties properties = new Properties(); + properties.setProperty("version", pluginContext.getVersion()); + + return PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(""" + + + + + """, properties); + } +} diff --git a/src/main/java/run/halo/comment/widget/CommentWidgetPlugin.java b/src/main/java/run/halo/comment/widget/CommentWidgetPlugin.java new file mode 100644 index 0000000..b32caf5 --- /dev/null +++ b/src/main/java/run/halo/comment/widget/CommentWidgetPlugin.java @@ -0,0 +1,16 @@ +package run.halo.comment.widget; + +import org.springframework.stereotype.Component; +import run.halo.app.plugin.BasePlugin; +import run.halo.app.plugin.PluginContext; + +/** + * @author ryanwang + * @since 2.0.0 + */ +@Component +public class CommentWidgetPlugin extends BasePlugin { + public CommentWidgetPlugin(PluginContext pluginContext) { + super(pluginContext); + } +} diff --git a/src/main/java/run/halo/comment/widget/ConfigEndpoint.java b/src/main/java/run/halo/comment/widget/ConfigEndpoint.java new file mode 100644 index 0000000..0f5577d --- /dev/null +++ b/src/main/java/run/halo/comment/widget/ConfigEndpoint.java @@ -0,0 +1,61 @@ +package run.halo.comment.widget; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.endpoint.CustomEndpoint; +import run.halo.app.extension.ConfigMap; +import run.halo.app.extension.GroupVersion; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.plugin.PluginContext; + +@Component +@RequiredArgsConstructor +public class ConfigEndpoint implements CustomEndpoint { + + private final ReactiveExtensionClient client; + + private final PluginContext context; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public RouterFunction endpoint() { + return RouterFunctions.route() + .GET("config", this::getConfig) + .build(); + } + + + private Mono getConfig(ServerRequest request) { + return client.fetch(ConfigMap.class, context.getConfigMapName()) + .flatMap(configMap -> { + Map data = configMap.getData(); + ObjectNode rootNode = objectMapper.createObjectNode(); + + data.forEach((key, value) -> { + try { + JsonNode jsonNode = objectMapper.readTree(value); + rootNode.set(key, jsonNode); + } catch (Exception e) { + rootNode.put(key, value); + } + }); + + return ServerResponse.ok().bodyValue(rootNode); + }); + } + + @Override + public GroupVersion groupVersion() { + return GroupVersion.parseAPIVersion("api.commentwidget.halo.run/v1alpha1"); + } +} diff --git a/src/main/java/run/halo/comment/widget/DefaultCommentWidget.java b/src/main/java/run/halo/comment/widget/DefaultCommentWidget.java new file mode 100644 index 0000000..c0106a7 --- /dev/null +++ b/src/main/java/run/halo/comment/widget/DefaultCommentWidget.java @@ -0,0 +1,95 @@ +package run.halo.comment.widget; + +import java.util.Properties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import org.springframework.util.PropertyPlaceholderHelper; +import org.thymeleaf.context.ITemplateContext; +import org.thymeleaf.model.IAttribute; +import org.thymeleaf.model.IProcessableElementTag; +import org.thymeleaf.processor.element.IElementTagStructureHandler; +import run.halo.app.plugin.PluginContext; +import run.halo.app.theme.dialect.CommentWidget; + +/** + * A default implementation of {@link CommentWidget}. + * + * @author guqing + * @since 2.0.0 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class DefaultCommentWidget implements CommentWidget { + static final PropertyPlaceholderHelper PROPERTY_PLACEHOLDER_HELPER = new PropertyPlaceholderHelper("${", "}"); + + private final PluginContext pluginContext; + + @Override + public void render(ITemplateContext context, + IProcessableElementTag tag, + IElementTagStructureHandler structureHandler) { + IAttribute groupAttribute = tag.getAttribute("group"); + IAttribute kindAttribute = tag.getAttribute("kind"); + IAttribute nameAttribute = tag.getAttribute("name"); + + structureHandler.replaceWith(commentHtml(groupAttribute, kindAttribute, nameAttribute), + false); + } + + private String commentHtml(IAttribute groupAttribute, IAttribute kindAttribute, + IAttribute nameAttribute) { + if (kindAttribute == null || StringUtils.isBlank(kindAttribute.getValue())) { + log.warn("Comment widget tag attributes 'kind' is missing."); + return "

    Comment widget attributes 'kind' is required but missing found.

    "; + } + if (nameAttribute == null || StringUtils.isBlank(nameAttribute.getValue())) { + log.warn("Comment widget tag attributes 'name' is missing."); + return "

    Comment widget attributes 'name' is required but missing found.

    "; + } + + String group = getGroup(groupAttribute); + + final Properties properties = new Properties(); + + properties.setProperty("version", pluginContext.getVersion()); + properties.setProperty("group", group); + properties.setProperty("kind", kindAttribute.getValue()); + properties.setProperty("name", nameAttribute.getValue()); + properties.setProperty("domId", domIdFrom(group, kindAttribute.getValue(), nameAttribute.getValue())); + + properties.setProperty("version", pluginContext.getVersion()); + + // placeholderHelper only support string, so we need to convert boolean to string + return PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(""" +
    + + """, properties); + } + + private String domIdFrom(String group, String kind, String name) { + Assert.notNull(name, "The name must not be null."); + Assert.notNull(kind, "The kind must not be null."); + String groupKindNameAsDomId = String.join("-", group, kind, name); + return "comment-" + groupKindNameAsDomId.replaceAll("[^\\-_a-zA-Z0-9\\s]", "-") + .replaceAll("(-)+", "-"); + } + + private String getGroup(IAttribute groupAttribute) { + return groupAttribute.getValue() == null ? "" + : StringUtils.defaultString(groupAttribute.getValue()); + } +} diff --git a/src/main/java/run/halo/comment/widget/SettingConfigGetter.java b/src/main/java/run/halo/comment/widget/SettingConfigGetter.java new file mode 100644 index 0000000..7262892 --- /dev/null +++ b/src/main/java/run/halo/comment/widget/SettingConfigGetter.java @@ -0,0 +1,88 @@ +package run.halo.comment.widget; + +import lombok.Data; +import lombok.Getter; +import lombok.experimental.Accessors; +import org.springframework.lang.NonNull; +import reactor.core.publisher.Mono; +import run.halo.comment.widget.captcha.CaptchaType; + +public interface SettingConfigGetter { + + /** + * Never {@link Mono#empty()}. + */ + Mono getBasicConfig(); + + /** + * Never {@link Mono#empty()}. + */ + Mono getAvatarConfig(); + + /** + * Never {@link Mono#empty()}. + */ + Mono getSecurityConfig(); + + @Data + @Accessors(chain = true) + class SecurityConfig { + public static final String GROUP = "security"; + + @Getter(onMethod_ = @NonNull) + private CaptchaConfig captcha = CaptchaConfig.empty(); + + public SecurityConfig setCaptcha(CaptchaConfig captcha) { + this.captcha = (captcha == null ? CaptchaConfig.empty() : captcha); + return this; + } + + public static SecurityConfig empty() { + return new SecurityConfig() + .setCaptcha(CaptchaConfig.empty()); + } + } + + @Data + @Accessors(chain = true) + class CaptchaConfig { + + private boolean anonymousCommentCaptcha; + + @Getter(onMethod_ = @NonNull) + private CaptchaType type = CaptchaType.ALPHANUMERIC; + + private boolean ignoreCase = true; + + private int captchaLength = 4; + + private int arithmeticRange = 90; + + public CaptchaConfig setType(CaptchaType type) { + this.type = (type == null ? CaptchaType.ALPHANUMERIC : type); + return this; + } + + public static CaptchaConfig empty() { + return new CaptchaConfig(); + } + } + + @Data + class BasicConfig { + public static final String GROUP = "basic"; + private int size; + private int replySize; + private boolean withReplies; + private int withReplySize; + } + + @Data + class AvatarConfig { + public static final String GROUP = "avatar"; + private boolean enable; + private String provider; + private String providerMirror; + private String policy; + } +} diff --git a/src/main/java/run/halo/comment/widget/SettingConfigGetterImpl.java b/src/main/java/run/halo/comment/widget/SettingConfigGetterImpl.java new file mode 100644 index 0000000..6b4fad3 --- /dev/null +++ b/src/main/java/run/halo/comment/widget/SettingConfigGetterImpl.java @@ -0,0 +1,30 @@ +package run.halo.comment.widget; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; +import run.halo.app.plugin.ReactiveSettingFetcher; + +@Component +@RequiredArgsConstructor +public class SettingConfigGetterImpl implements SettingConfigGetter { + private final ReactiveSettingFetcher settingFetcher; + + @Override + public Mono getBasicConfig() { + return settingFetcher.fetch(BasicConfig.GROUP, BasicConfig.class) + .defaultIfEmpty(new BasicConfig()); + } + + @Override + public Mono getAvatarConfig() { + return settingFetcher.fetch(AvatarConfig.GROUP, AvatarConfig.class) + .defaultIfEmpty(new AvatarConfig()); + } + + @Override + public Mono getSecurityConfig() { + return settingFetcher.fetch(SecurityConfig.GROUP, SecurityConfig.class) + .defaultIfEmpty(SecurityConfig.empty()); + } +} diff --git a/src/main/java/run/halo/comment/widget/captcha/CaptchaCookieResolver.java b/src/main/java/run/halo/comment/widget/captcha/CaptchaCookieResolver.java new file mode 100644 index 0000000..87c739f --- /dev/null +++ b/src/main/java/run/halo/comment/widget/captcha/CaptchaCookieResolver.java @@ -0,0 +1,19 @@ +package run.halo.comment.widget.captcha; + +import java.time.Duration; +import org.springframework.http.HttpCookie; +import org.springframework.lang.Nullable; +import org.springframework.web.server.ServerWebExchange; + +public interface CaptchaCookieResolver { + @Nullable + HttpCookie resolveCookie(ServerWebExchange exchange); + + void setCookie(ServerWebExchange exchange, String value); + + void expireCookie(ServerWebExchange exchange); + + String getCookieName(); + + Duration getCookieMaxAge(); +} diff --git a/src/main/java/run/halo/comment/widget/captcha/CaptchaCookieResolverImpl.java b/src/main/java/run/halo/comment/widget/captcha/CaptchaCookieResolverImpl.java new file mode 100644 index 0000000..f62cc35 --- /dev/null +++ b/src/main/java/run/halo/comment/widget/captcha/CaptchaCookieResolverImpl.java @@ -0,0 +1,49 @@ +package run.halo.comment.widget.captcha; + +import java.time.Duration; +import lombok.Getter; +import org.springframework.http.HttpCookie; +import org.springframework.http.ResponseCookie; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import org.springframework.web.server.ServerWebExchange; + +@Getter +@Component +public class CaptchaCookieResolverImpl implements CaptchaCookieResolver { + public static final String CAPTCHA_COOKIE_KEY = "comment-widget-captcha"; + + private final String cookieName = CAPTCHA_COOKIE_KEY; + + private final Duration cookieMaxAge = Duration.ofHours(1); + + @Override + @Nullable + public HttpCookie resolveCookie(ServerWebExchange exchange) { + return exchange.getRequest().getCookies().getFirst(getCookieName()); + } + + @Override + public void setCookie(ServerWebExchange exchange, String value) { + Assert.notNull(value, "'value' is required"); + exchange.getResponse().getCookies() + .set(getCookieName(), initCookie(exchange, value).build()); + } + + @Override + public void expireCookie(ServerWebExchange exchange) { + ResponseCookie cookie = initCookie(exchange, "").maxAge(0).build(); + exchange.getResponse().getCookies().set(this.cookieName, cookie); + } + + private ResponseCookie.ResponseCookieBuilder initCookie(ServerWebExchange exchange, + String value) { + return ResponseCookie.from(this.cookieName, value) + .path(exchange.getRequest().getPath().contextPath().value() + "/") + .maxAge(getCookieMaxAge()) + .httpOnly(true) + .secure("https".equalsIgnoreCase(exchange.getRequest().getURI().getScheme())) + .sameSite("Lax"); + } +} diff --git a/src/main/java/run/halo/comment/widget/captcha/CaptchaEndpoint.java b/src/main/java/run/halo/comment/widget/captcha/CaptchaEndpoint.java new file mode 100644 index 0000000..35ce325 --- /dev/null +++ b/src/main/java/run/halo/comment/widget/captcha/CaptchaEndpoint.java @@ -0,0 +1,39 @@ +package run.halo.comment.widget.captcha; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.endpoint.CustomEndpoint; +import run.halo.app.extension.GroupVersion; +import run.halo.comment.widget.SettingConfigGetter; + +@Component +@RequiredArgsConstructor +public class CaptchaEndpoint implements CustomEndpoint { + + private final CaptchaManager captchaManager; + private final SettingConfigGetter settingConfigGetter; + + @Override + public RouterFunction endpoint() { + return RouterFunctions.route() + .GET("captcha/-/generate", this::generateCaptcha) + .build(); + } + + private Mono generateCaptcha(ServerRequest request) { + return settingConfigGetter.getSecurityConfig() + .map(SettingConfigGetter.SecurityConfig::getCaptcha) + .flatMap(captchaConfig -> captchaManager.generate(request.exchange(), captchaConfig)) + .flatMap(captcha -> ServerResponse.ok().bodyValue(captcha.imageBase64())); + } + + @Override + public GroupVersion groupVersion() { + return GroupVersion.parseAPIVersion("api.commentwidget.halo.run/v1alpha1"); + } +} diff --git a/src/main/java/run/halo/comment/widget/captcha/CaptchaGenerator.java b/src/main/java/run/halo/comment/widget/captcha/CaptchaGenerator.java new file mode 100644 index 0000000..cf626c8 --- /dev/null +++ b/src/main/java/run/halo/comment/widget/captcha/CaptchaGenerator.java @@ -0,0 +1,145 @@ +package run.halo.comment.widget.captcha; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Base64; +import java.util.Random; +import java.util.function.Function; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@UtilityClass +public class CaptchaGenerator { + private static final String CHAR_STRING = "ABCDEFGHJKMNPRSTUVWXYZabcdefghjkmnpqrstuvwxyz0123456789"; + private static final int WIDTH = 120; + private static final int HEIGHT = 36; + private static final int CHAR_LENGTH = 4; + + private static final Font customFont; + + static { + customFont = loadArialFont(); + } + + public static Captcha generateMathCaptcha(int arithmeticRange) { + return generateCaptchaImage((g2d) -> drawMathCaptchaText(g2d, arithmeticRange)); + } + + public static Captcha generateSimpleCaptcha(int captchaLength) { + return generateCaptchaImage((g2d) -> drawSimpleText(g2d, captchaLength)); + } + + private static Captcha generateCaptchaImage(Function drawCaptchaTextFunc) { + BufferedImage bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = bufferedImage.createGraphics(); + + // paint white background + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, WIDTH, HEIGHT); + + g2d.setFont(customFont); + + var captchaText = drawCaptchaTextFunc.apply(g2d); + + // add some noise + Random random = new Random(); + for (int i = 0; i < 10; i++) { + g2d.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256))); + int x1 = random.nextInt(WIDTH); + int y1 = random.nextInt(HEIGHT); + int x2 = random.nextInt(WIDTH); + int y2 = random.nextInt(HEIGHT); + g2d.drawLine(x1, y1, x2, y2); + } + + g2d.dispose(); + return new Captcha(captchaText, bufferedImage); + } + + private static String drawMathCaptchaText(Graphics2D g2d, int arithmeticRange) { + Random random = new Random(); + int num1 = random.nextInt(arithmeticRange) + 1; + int num2 = random.nextInt(arithmeticRange) + 1; + char operator = getRandomOperator(); + + int result; + String mathText = switch (operator) { + case '+' -> { + result = num1 + num2; + yield num1 + " + " + num2 + " = ?"; + } + case '-' -> { + result = num1 - num2; + yield num1 + " - " + num2 + " = ?"; + } + case '*' -> { + result = num1 * num2; + yield num1 + " * " + num2 + " = ?"; + } + default -> throw new IllegalStateException("Unexpected value: " + operator); + }; + + g2d.setColor(Color.BLACK); + g2d.drawString(mathText, 20, 30); + return String.valueOf(result); + } + + public record Captcha(String code, BufferedImage image) { + } + + private static String drawSimpleText(Graphics2D g2d, int captchaLength) { + var captchaText = generateRandomText(captchaLength); + Random random = new Random(); + int charSpacing = WIDTH / (captchaLength + 1); + for (int i = 0; i < captchaText.length(); i++) { + g2d.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256))); + // 动态计算每个字符的位置 + int xPos = charSpacing + i * charSpacing; + g2d.drawString(String.valueOf(captchaText.charAt(i)), xPos, 30); + } + return captchaText; + } + + private static Font loadArialFont() { + var fontPath = "/fonts/Arial_Bold.ttf"; + try (InputStream is = CaptchaGenerator.class.getResourceAsStream(fontPath)) { + if (is == null) { + throw new RuntimeException("Cannot load font file for " + fontPath + ", please check if it exists."); + } + return Font.createFont(Font.TRUETYPE_FONT, is).deriveFont(Font.BOLD, 24); + } catch (FontFormatException | IOException e) { + log.warn("Failed to load font file for {}, fallback to default font.", fontPath); + return new Font("Serif", Font.BOLD, 24); + } + } + + private static char getRandomOperator() { + char[] operators = {'+', '-', '*'}; + Random random = new Random(); + return operators[random.nextInt(operators.length)]; + } + + private static String generateRandomText(int captchaLength) { + StringBuilder sb = new StringBuilder(captchaLength); + Random random = new Random(); + for (int i = 0; i < captchaLength; i++) { + sb.append(CHAR_STRING.charAt(random.nextInt(CHAR_STRING.length()))); + } + return sb.toString(); + } + + public static String encodeToBase64(BufferedImage image) { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + ImageIO.write(image, "png", outputStream); + byte[] imageBytes = outputStream.toByteArray(); + return Base64.getEncoder().encodeToString(imageBytes); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/run/halo/comment/widget/captcha/CaptchaManager.java b/src/main/java/run/halo/comment/widget/captcha/CaptchaManager.java new file mode 100644 index 0000000..93216ad --- /dev/null +++ b/src/main/java/run/halo/comment/widget/captcha/CaptchaManager.java @@ -0,0 +1,16 @@ +package run.halo.comment.widget.captcha; + +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; +import run.halo.comment.widget.SettingConfigGetter; + +public interface CaptchaManager { + Mono verify(String id, String captchaCode, boolean ignoreCase); + + Mono invalidate(String id); + + Mono generate(ServerWebExchange exchange, SettingConfigGetter.CaptchaConfig captchaConfig); + + record Captcha(String id, String code, String imageBase64) { + } +} diff --git a/src/main/java/run/halo/comment/widget/captcha/CaptchaManagerImpl.java b/src/main/java/run/halo/comment/widget/captcha/CaptchaManagerImpl.java new file mode 100644 index 0000000..b4060cc --- /dev/null +++ b/src/main/java/run/halo/comment/widget/captcha/CaptchaManagerImpl.java @@ -0,0 +1,65 @@ +package run.halo.comment.widget.captcha; + +import java.awt.image.BufferedImage; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; +import run.halo.comment.widget.SettingConfigGetter; + +@Component +@RequiredArgsConstructor +public class CaptchaManagerImpl implements CaptchaManager { + public static final long CODE_EXPIRATION_MINUTES = 1; + + private final Cache captchaCache = + CacheBuilder.newBuilder() + .expireAfterWrite(CODE_EXPIRATION_MINUTES, TimeUnit.MINUTES) + .maximumSize(100) + .build(); + + private final CaptchaCookieResolver captchaCookieResolver; + + @Override + public Mono verify(String key, String captchaCode, boolean ignoreCase) { + return Mono.justOrEmpty(captchaCache.getIfPresent(key)) + .filter(captcha -> ignoreCase ? captcha.code().equalsIgnoreCase(captchaCode) : captcha.code().equals(captchaCode)) + .hasElement(); + } + + @Override + public Mono invalidate(String id) { + captchaCache.invalidate(id); + return Mono.empty(); + } + + @Override + public Mono generate(ServerWebExchange exchange, SettingConfigGetter.CaptchaConfig captchaConfig) { + return doGenerate(captchaConfig) + .doOnNext(captcha -> captchaCookieResolver.setCookie(exchange, captcha.id())); + } + + private Mono doGenerate(SettingConfigGetter.CaptchaConfig captchaConfig) { + return Mono.fromSupplier(() -> { + var captcha = switch (captchaConfig.getType()) { + case ALPHANUMERIC -> CaptchaGenerator.generateSimpleCaptcha(captchaConfig.getCaptchaLength()); + case ARITHMETIC -> CaptchaGenerator.generateMathCaptcha(captchaConfig.getArithmeticRange()); + }; + var imageBase64 = encodeBufferedImageToDataUri(captcha.image()); + var id = UUID.randomUUID().toString(); + return new Captcha(id, captcha.code(), imageBase64); + }) + .subscribeOn(Schedulers.boundedElastic()) + .doOnNext(captcha -> captchaCache.put(captcha.id(), captcha)); + } + + private static String encodeBufferedImageToDataUri(BufferedImage image) { + var imageBase64 = CaptchaGenerator.encodeToBase64(image); + return "data:image/png;base64," + imageBase64; + } +} diff --git a/src/main/java/run/halo/comment/widget/captcha/CaptchaType.java b/src/main/java/run/halo/comment/widget/captcha/CaptchaType.java new file mode 100644 index 0000000..5202383 --- /dev/null +++ b/src/main/java/run/halo/comment/widget/captcha/CaptchaType.java @@ -0,0 +1,6 @@ +package run.halo.comment.widget.captcha; + +public enum CaptchaType { + ALPHANUMERIC, + ARITHMETIC +} diff --git a/src/main/java/run/halo/comment/widget/captcha/CommentCaptchaFilter.java b/src/main/java/run/halo/comment/widget/captcha/CommentCaptchaFilter.java new file mode 100644 index 0000000..62020e1 --- /dev/null +++ b/src/main/java/run/halo/comment/widget/captcha/CommentCaptchaFilter.java @@ -0,0 +1,154 @@ +package run.halo.comment.widget.captcha; + +import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.pathMatchers; + +import java.net.URI; +import java.util.Locale; +import java.util.Optional; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ProblemDetail; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.http.converter.json.ProblemDetailJacksonMixin; +import org.springframework.lang.NonNull; +import org.springframework.security.config.web.server.SecurityWebFiltersOrder; +import org.springframework.security.web.server.context.ServerSecurityContextRepository; +import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Mono; +import run.halo.app.infra.AnonymousUserConst; +import run.halo.app.security.AdditionalWebFilter; +import run.halo.comment.widget.SettingConfigGetter; + +@Component +@RequiredArgsConstructor +public class CommentCaptchaFilter implements AdditionalWebFilter { + static final String CAPTCHA_INVALID_TYPE = "https://www.halo.run/probs/captcha-invalid"; + static final String CAPTCHA_REQUIRED_TYPE = "https://www.halo.run/probs/captcha-required"; + private final static String CAPTCHA_CODE_HEADER = "X-Captcha-Code"; + private final static String CAPTCHA_REQUIRED_HEADER = "X-Require-Captcha"; + private static final String CONTENT_TYPE = "application/problem+json"; + + private final ServerWebExchangeMatcher pathMatcher = createPathMatcher(); + private final ObjectMapper objectMapper = createObjectMapper(); + + private final SettingConfigGetter settingConfigGetter; + private final CaptchaManager captchaManager; + private final CaptchaCookieResolverImpl captchaCookieResolver; + private final ServerSecurityContextRepository contextRepository; + + @Override + @NonNull + public Mono filter(@NonNull ServerWebExchange exchange, @NonNull WebFilterChain chain) { + return pathMatcher.matches(exchange) + .filter(ServerWebExchangeMatcher.MatchResult::isMatch) + .flatMap(result -> settingConfigGetter.getSecurityConfig()) + .map(SettingConfigGetter.SecurityConfig::getCaptcha) + .filterWhen(captchaConfig -> isAnonymousCommenter(exchange)) + .switchIfEmpty(chain.filter(exchange).then(Mono.empty())) + .flatMap(captchaConfig -> { + if (!captchaConfig.isAnonymousCommentCaptcha()) { + return chain.filter(exchange); + } + return validateCaptcha(exchange, chain, captchaConfig); + }); + } + + private Mono sendCaptchaRequiredResponse(ServerWebExchange exchange, + SettingConfigGetter.CaptchaConfig captchaConfig, + ResponseStatusException e) { + exchange.getResponse().getHeaders().addIfAbsent(CAPTCHA_REQUIRED_HEADER, "true"); + exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); + return captchaManager.generate(exchange, captchaConfig) + .flatMap(captcha -> { + var problemDetail = toProblemDetail(e); + problemDetail.setProperty("captcha", captcha.imageBase64()); + var responseData = getResponseData(problemDetail); + exchange.getResponse().getHeaders().addIfAbsent("Content-Type", CONTENT_TYPE); + return exchange.getResponse() + .writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(responseData))); + }); + } + + private byte[] getResponseData(ProblemDetail problemDetail) { + try { + return objectMapper.writeValueAsBytes(problemDetail); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + private Mono validateCaptcha(ServerWebExchange exchange, WebFilterChain chain, + SettingConfigGetter.CaptchaConfig captchaConfig) { + var captchaCodeOpt = getCaptchaCode(exchange); + var cookie = captchaCookieResolver.resolveCookie(exchange); + if (captchaCodeOpt.isEmpty() || cookie == null) { + return sendCaptchaRequiredResponse(exchange, captchaConfig, new CaptchaCodeMissingException()); + } + return captchaManager.verify(cookie.getValue(), captchaCodeOpt.get(), captchaConfig.isIgnoreCase()) + .flatMap(valid -> { + if (valid) { + captchaCookieResolver.expireCookie(exchange); + return chain.filter(exchange); + } + return sendCaptchaRequiredResponse(exchange, captchaConfig, new InvalidCaptchaCodeException()); + }); + } + + private static Optional getCaptchaCode(ServerWebExchange exchange) { + var captchaCode = exchange.getRequest().getHeaders().getFirst(CAPTCHA_CODE_HEADER); + return Optional.ofNullable(captchaCode) + .filter(StringUtils::isNotBlank); + } + + private OrServerWebExchangeMatcher createPathMatcher() { + var commentMatcher = pathMatchers(HttpMethod.POST, "/apis/api.halo.run/v1alpha1/comments"); + var replyMatcher = pathMatchers(HttpMethod.POST, "/apis/api.halo.run/v1alpha1/comments/{name}/reply"); + return new OrServerWebExchangeMatcher(commentMatcher, replyMatcher); + } + + static class InvalidCaptchaCodeException extends ResponseStatusException { + public InvalidCaptchaCodeException() { + super(HttpStatus.FORBIDDEN, "验证码错误,请重新输入"); + setType(URI.create(CAPTCHA_INVALID_TYPE)); + } + } + + static class CaptchaCodeMissingException extends ResponseStatusException { + public CaptchaCodeMissingException() { + super(HttpStatus.FORBIDDEN, "请先输入验证码"); + setType(URI.create(CAPTCHA_REQUIRED_TYPE)); + } + } + + ProblemDetail toProblemDetail(ResponseStatusException e) { + var problemDetail = e.updateAndGetBody(null, Locale.getDefault()); + problemDetail.setTitle("Captcha Verification"); + return problemDetail; + } + + static ObjectMapper createObjectMapper() { + return Jackson2ObjectMapperBuilder.json() + .mixIn(ProblemDetail.class, ProblemDetailJacksonMixin.class) + .build(); + } + + Mono isAnonymousCommenter(ServerWebExchange exchange) { + return contextRepository.load(exchange) + .map(context -> AnonymousUserConst.isAnonymousUser(context.getAuthentication().getName())) + .defaultIfEmpty(true); + } + + @Override + public int getOrder() { + return SecurityWebFiltersOrder.AUTHORIZATION.getOrder(); + } +} diff --git a/src/main/resources/extensions/extension-definitions.yaml b/src/main/resources/extensions/extension-definitions.yaml new file mode 100644 index 0000000..c1f69b3 --- /dev/null +++ b/src/main/resources/extensions/extension-definitions.yaml @@ -0,0 +1,9 @@ +apiVersion: plugin.halo.run/v1alpha1 +kind: ExtensionDefinition +metadata: + name: default-comment-widget +spec: + className: run.halo.comment.widget.DefaultCommentWidget + extensionPointName: comment-widget + displayName: "默认评论组件" + description: "Halo 默认提供的评论组件插件" diff --git a/src/main/resources/extensions/reverseProxy.yaml b/src/main/resources/extensions/reverseProxy.yaml new file mode 100644 index 0000000..76ddfda --- /dev/null +++ b/src/main/resources/extensions/reverseProxy.yaml @@ -0,0 +1,8 @@ +apiVersion: plugin.halo.run/v1alpha1 +kind: ReverseProxy +metadata: + name: reverse-proxy-comment-widget +rules: + - path: /static/** + file: + directory: static diff --git a/src/main/resources/extensions/role-templates.yaml b/src/main/resources/extensions/role-templates.yaml new file mode 100644 index 0000000..26a90b4 --- /dev/null +++ b/src/main/resources/extensions/role-templates.yaml @@ -0,0 +1,18 @@ +apiVersion: v1alpha1 +kind: Role +metadata: + name: comment-widget-public-apis + labels: + halo.run/role-template: "true" + halo.run/hidden: "true" + rbac.authorization.halo.run/aggregate-to-anonymous: "true" + annotations: + rbac.authorization.halo.run/module: "Comment Widget" + rbac.authorization.halo.run/display-name: "Comment Widget Public APIs" +rules: + - apiGroups: [ "api.commentwidget.halo.run" ] + resources: [ "captcha/generate" ] + verbs: [ "get" ] + - apiGroups: [ "api.commentwidget.halo.run" ] + resources: [ "config" ] + verbs: [ "list" ] diff --git a/src/main/resources/extensions/settings.yaml b/src/main/resources/extensions/settings.yaml new file mode 100644 index 0000000..9fbfe42 --- /dev/null +++ b/src/main/resources/extensions/settings.yaml @@ -0,0 +1,149 @@ +apiVersion: v1alpha1 +kind: Setting +metadata: + name: plugin-comment-widget-settings +spec: + forms: + - group: basic + label: 基本设置 + formSchema: + - $formkit: number + label: 评论分页条数 + name: size + validation: required + value: 20 + - $formkit: number + label: 回复分页条数 + name: replySize + validation: required + value: 10 + - $formkit: checkbox + label: 同时加载评论的回复 + name: withReplies + id: withReplies + key: withReplies + value: false + - $formkit: number + label: 同时加载回复的条数 + if: "$get(withReplies).value === true" + name: withReplySize + id: withReplySize + key: withReplySize + validation: required + value: 5 + - $formkit: checkbox + label: 显示评论者设备信息 + name: showCommenterDevice + value: false + - $formkit: checkbox + label: 支持私密评论 + id: enablePrivateComment + key: enablePrivateComment + name: enablePrivateComment + help: 开启之后,评论者可选是否私密评论,私密评论仅评论者(已登录)和网站管理员(包含评论查看权限)可见, + value: false + - $formkit: checkbox + if: "$get(enablePrivateComment).value === true" + label: 显示私密评论状态 + name: showPrivateCommentBadge + id: showPrivateCommentBadge + key: showPrivateCommentBadge + value: true + help: 开启后,私密评论会显示一个私密状态标识 + - group: security + label: 安全设置 + formSchema: + - $formkit: group + name: captcha + label: 验证码 + value: + enable: false + type: ALPHANUMERIC + children: + - $formkit: checkbox + label: 匿名评论需要验证码 + name: anonymousCommentCaptcha + id: anonymousCommentCaptcha + key: anonymousCommentCaptcha + value: false + - $formkit: select + label: 验证码类型 + if: "$get(anonymousCommentCaptcha).value === true" + name: type + id: type + value: "ALPHANUMERIC" + options: + - label: 字母数字组合 + value: "ALPHANUMERIC" + - label: 算术验证码 + value: "ARITHMETIC" + - $formkit: number + if: "$get(type).value === ALPHANUMERIC" + name: captchaLength + key: captchaLength + label: 验证码长度 + help: 字母数字组合验证码长度,不宜过长或过短,建议4-6位 + max: 8 + value: 4 + validation: required + - $formkit: radio + if: "$get(type).value === ALPHANUMERIC" + name: ignoreCase + key: ignoreCase + label: 是否忽略验证码大小写验证 + help: 忽略大小写验证码,建议开启 + value: true + options: + - label: 是 + value: true + - label: 否 + value: false + - $formkit: number + if: "$get(type).value === ARITHMETIC" + name: arithmeticRange + key: arithmeticRange + label: 计算范围 + help: 算术验证码计算范围 + value: 90 + validation: required + - group: avatar + label: 头像设置 + formSchema: + - $formkit: checkbox + label: 是否启用第三方头像 + name: enable + id: enable + key: enable + value: false + - $formkit: select + label: 头像服务提供者 + if: "$get(enable).value === true" + name: provider + options: + - label: "Gravatar" + value: "gravatar" + value: "gravatar" + - $formkit: text + label: 头像服务镜像地址 + if: "$get(enable).value === true" + name: providerMirror + value: "" + help: 使用官方源则留空, 示例:https://gravatar.com + - $formkit: select + label: 头像策略 + if: "$get(enable).value === true" + name: policy + options: + - label: "仅匿名用户" + value: "anonymousUser" + - label: "所有用户" + value: "allUser" + - label: "匿名&无头像用户" + value: "noAvatarUser" + value: "anonymousUser" + - group: editor + label: 编辑器 + formSchema: + - $formkit: text + name: placeholder + label: 自定义评论框占位符 diff --git a/src/main/resources/fonts/Arial_Bold.ttf b/src/main/resources/fonts/Arial_Bold.ttf new file mode 100644 index 0000000..c2eb3dd Binary files /dev/null and b/src/main/resources/fonts/Arial_Bold.ttf differ diff --git a/src/main/resources/logo.svg b/src/main/resources/logo.svg new file mode 100644 index 0000000..96a5dab --- /dev/null +++ b/src/main/resources/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/plugin.yaml b/src/main/resources/plugin.yaml new file mode 100644 index 0000000..31b093a --- /dev/null +++ b/src/main/resources/plugin.yaml @@ -0,0 +1,25 @@ +apiVersion: plugin.halo.run/v1alpha1 +kind: Plugin +metadata: + name: PluginCommentWidget + annotations: + # Add supports for Halo App Store + # https://halo.run/store/apps/app-YXyaD + "store.halo.run/app-id": "app-YXyaD" +spec: + enabled: true + requires: ">=2.21.7" + author: + name: Halo + website: https://github.com/halo-dev + logo: logo.svg + homepage: https://www.halo.run/store/apps/app-YXyaD + repo: https://github.com/halo-dev/plugin-comment-widget + issues: https://github.com/halo-dev/plugin-comment-widget/issues + displayName: "评论组件" + description: "为用户前台提供完整的评论解决方案" + configMapName: plugin-comment-widget-configmap + settingName: plugin-comment-widget-settings + license: + - name: "GPL-3.0" + url: "https://github.com/halo-dev/plugin-comment-widget/blob/main/LICENSE" diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..283c613 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + } +}