diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e863216 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +end_of_line = lf + +[*.{js,json,ts,tsx}] +indent_style = space +indent_size = 2 + +[Makefile] +indent_style = tab + +[*.{md,markdown}] +trim_trailing_whitespace = false diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index e92f06b..4c73814 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -6,9 +6,6 @@ on: jobs: publish-tauri: - strategy: - fail-fast: false - runs-on: windows-latest steps: - uses: actions/checkout@v3 @@ -16,10 +13,31 @@ jobs: uses: actions/setup-node@v3 with: node-version: 16 + + - name: Cache node modules + uses: actions/cache@v3 + env: + cache-name: cache-node-modules-v1 + with: + path: | + node_modules + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + - name: install Rust stable uses: dtolnay/rust-toolchain@stable - - name: install app dependencies and build it - run: yarn && yarn build + + - name: Rust cache + uses: swatinem/rust-cache@v2 + with: + workspaces: './src-tauri -> target' + + - name: Yarn install FE dependencies + run: yarn --prefer-offline --frozen-lockfile install + + - name: Build it + run: yarn build - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000..be7a097 --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,78 @@ +name: 'Tests on PR' +on: [pull_request] + +jobs: + js-build-pretier: + name: Check build & Prettier + runs-on: ubuntu-latest + + concurrency: + group: tests-${{ github.ref }} + cancel-in-progress: true + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '16' + + - name: Cache node modules + uses: actions/cache@v3 + env: + cache-name: cache-node-modules-v1 + with: + path: | + node_modules + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + + - name: Yarn install + run: yarn --prefer-offline --frozen-lockfile install + - name: Check JS build + run: yarn build + - name: Run prettier + run: | + yarn lint:prettier + + test-tauri: + name: Test Tauri build + runs-on: windows-latest + + concurrency: + group: tests-tauri-build-${{ github.ref }} + cancel-in-progress: true + + steps: + - uses: actions/checkout@v3 + - name: setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Cache node modules + uses: actions/cache@v3 + env: + cache-name: cache-node-modules-v1 + with: + path: | + node_modules + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + + - name: install Rust stable + uses: dtolnay/rust-toolchain@stable + + - name: Rust cache + uses: swatinem/rust-cache@v2 + with: + workspaces: './src-tauri -> target' + + - name: Yarn install FE dependencies + run: yarn --prefer-offline --frozen-lockfile install + - uses: tauri-apps/tauri-action@v0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /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 +. diff --git a/Readme.md b/Readme.md index dbfd729..c0d34ea 100644 --- a/Readme.md +++ b/Readme.md @@ -1,4 +1,4 @@ -# Coh3 Stats Desktop App +# COH3 Stats Desktop App View stats of players in the current game of Company of Heroes 3. @@ -31,11 +31,112 @@ View stats of players in the current game of Company of Heroes 3.
14. Scale the source to match the Coh3 source size +## Custom CSS for the overlay +If you don't like the default style of overlay, you can modify it with custom CSS. +All the elements in the overlay have CSS classes assigned. The styling is as follows: +``` + +.coh3stats-overlay { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + align-items: stretch; + position: absolute; + left: calc((100vw / 2) - 485px); + right: calc((100vw / 2) - 485px); + top: 65px; +} + +.coh3stats-overlay-left { + flex-grow: 1; + flex-basis: 0; + padding-right: 40px; + padding-left: 10px; +} + +.coh3stats-overlay-right { + flex-grow: 1; + flex-basis: 0; + padding-left: 40px; + padding-right: 10px; +} + +.coh3stats-overlay-player { + color: white; + font-size: 20px; + font-family: Tilt Warp; +} + +.coh3stats-overlay-player-factionIcon { + padding-right: 10px; + width: 25px; + height: 25px; +} + +.coh3stats-overlay-player-flagIcon { + padding-right: 10px; + width: 25px; + height: 25px; +} + +.coh3stats-overlay-player-rank { + padding-right: 10px; + min-width: 4ch; + display: inline-block; + text-align: center; +} + +.coh3stats-overlay-player-rating { + padding-right: 10px; + min-width: 4ch; + display: inline-block; + text-align: center; +} + +.coh3stats-overlay-player-name { + max-width: 17ch; + display: inline-block; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} +``` + +Steps when you want to change something. +Let's say I want to move it lower and change the color to red. +1. Pick the classes you want to change and do the changes: +``` +.coh3stats-overlay { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + align-items: stretch; + position: absolute; + left: calc((100vw / 2) - 485px); + right: calc((100vw / 2) - 485px); + top: 250px; +} + +.coh3stats-overlay-player { + color: red; + font-size: 20px; + font-family: Tilt Warp; +} +``` +2. Open the configuration of "Overlay" in OBS +![image](https://github.com/cohstats/coh3-stats-desktop-app/assets/8086995/fa62f8df-0c08-4a1f-a12b-ca1598b2deb6) +3. Paste the 2 modified classes into the box Custom CSS +![image](https://github.com/cohstats/coh3-stats-desktop-app/assets/8086995/c6f4cb56-f250-40f9-be93-5f65fefe8421) +4. Click OK and observe the changes +![image](https://github.com/cohstats/coh3-stats-desktop-app/assets/8086995/ee77f6f8-2a8b-4da5-960c-e2c15f119d48) + + + ## Development Install rust on your system using rustup https://www.rust-lang.org/tools/install -Install all dependecies with: +Install all dependencies with: ``` yarn install @@ -57,6 +158,19 @@ yarn tauri build The build output can be found in `src-tauri/target/release`. The installer can be found in `src-tauri/target/release/bundle/msi`. +Don't forget to run prettier with `yarn fix`. + +### Release +- Increase the version in files: + - `package.json` + - `src-tauri/tauri.conf.json` + - `src-tauri/Cargo.toml` +- Commit the updated version +- Make a new tag on master +- Merge master into release branch (TODO: Why we have separate release branch?) + + + ## Project Architecture ### Frontend diff --git a/package.json b/package.json index 00bacf8..44c848c 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,29 @@ { "name": "coh3-stats-desktop-app", "private": true, - "version": "1.2.2", + "version": "1.2.3", "type": "module", "scripts": { "dev": "vite", "build": "tsc && vite build", "preview": "vite preview", - "prettier": "prettier --write src", + "fix": "prettier --write src", + "lint:prettier": "prettier --check src", "tauri": "tauri" }, "dependencies": { - "@emotion/react": "^11.10.6", - "@mantine/core": "^6.0.0", - "@mantine/hooks": "^6.0.0", - "@tabler/icons-react": "^2.7.0", + "@emotion/react": "^11.11.1", + "@mantine/core": "^6.0.15", + "@mantine/hooks": "^6.0.15", + "@tabler/icons-react": "^2.23.0", "@tauri-apps/api": "^1.2.0", - "axios": "^1.3.4", + "axios": "^1.4.0", "coh-stats-components": "github:cohstats/coh-stats-components#0.0.7", "coh3-data-types-library": "github:cohstats/coh3-data-types-library#0.0.2", - "mixpanel-browser": "^2.45.0", + "mixpanel-browser": "^2.47.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.4.5", + "react-router-dom": "^6.14.0", "tauri-plugin-fs-watch-api": "https://github.com/tauri-apps/tauri-plugin-fs-watch", "tauri-plugin-log-api": "https://github.com/tauri-apps/tauri-plugin-log", "tauri-plugin-store-api": "https://github.com/tauri-apps/tauri-plugin-store" @@ -30,11 +31,11 @@ "devDependencies": { "@tauri-apps/cli": "^1.2.2", "@types/mixpanel-browser": "^2.38.1", - "@types/react": "^18.0.26", - "@types/react-dom": "^18.0.9", + "@types/react": "^18.2.14", + "@types/react-dom": "^18.2.6", "@vitejs/plugin-react": "^3.0.0", - "prettier": "^2.8.1", - "typescript": "^4.9.3", - "vite": "^4.0.0" + "prettier": "^2.8.8", + "typescript": "^5.1.6", + "vite": "^4.3.9" } } diff --git a/public/factions/american.webp b/public/factions/american.webp index 6b03914..441601a 100644 Binary files a/public/factions/american.webp and b/public/factions/american.webp differ diff --git a/public/factions/british.webp b/public/factions/british.webp index ed7009d..e11be2a 100644 Binary files a/public/factions/british.webp and b/public/factions/british.webp differ diff --git a/public/factions/dak.webp b/public/factions/dak.webp index 3678c8a..02bf49d 100644 Binary files a/public/factions/dak.webp and b/public/factions/dak.webp differ diff --git a/public/factions/german.webp b/public/factions/german.webp index cd30887..b0653b3 100644 Binary files a/public/factions/german.webp and b/public/factions/german.webp differ diff --git a/public/factions/plain/american.webp b/public/factions/plain/american.webp new file mode 100644 index 0000000..6b03914 Binary files /dev/null and b/public/factions/plain/american.webp differ diff --git a/public/factions/plain/british.webp b/public/factions/plain/british.webp new file mode 100644 index 0000000..ed7009d Binary files /dev/null and b/public/factions/plain/british.webp differ diff --git a/public/factions/plain/dak.webp b/public/factions/plain/dak.webp new file mode 100644 index 0000000..3678c8a Binary files /dev/null and b/public/factions/plain/dak.webp differ diff --git a/public/factions/plain/german.webp b/public/factions/plain/german.webp new file mode 100644 index 0000000..cd30887 Binary files /dev/null and b/public/factions/plain/german.webp differ diff --git a/public/icons/ranks/00_placement_medium.webp b/public/icons/ranks/00_placement_medium.webp new file mode 100644 index 0000000..e825219 Binary files /dev/null and b/public/icons/ranks/00_placement_medium.webp differ diff --git a/public/icons/ranks/01_brass_01_medium.webp b/public/icons/ranks/01_brass_01_medium.webp new file mode 100644 index 0000000..bf770ef Binary files /dev/null and b/public/icons/ranks/01_brass_01_medium.webp differ diff --git a/public/icons/ranks/01_brass_02_medium.webp b/public/icons/ranks/01_brass_02_medium.webp new file mode 100644 index 0000000..bb2a3e7 Binary files /dev/null and b/public/icons/ranks/01_brass_02_medium.webp differ diff --git a/public/icons/ranks/01_brass_03_medium.webp b/public/icons/ranks/01_brass_03_medium.webp new file mode 100644 index 0000000..251bc6c Binary files /dev/null and b/public/icons/ranks/01_brass_03_medium.webp differ diff --git a/public/icons/ranks/02_bronze_01_medium.webp b/public/icons/ranks/02_bronze_01_medium.webp new file mode 100644 index 0000000..df772b0 Binary files /dev/null and b/public/icons/ranks/02_bronze_01_medium.webp differ diff --git a/public/icons/ranks/02_bronze_02_medium.webp b/public/icons/ranks/02_bronze_02_medium.webp new file mode 100644 index 0000000..bcaf98e Binary files /dev/null and b/public/icons/ranks/02_bronze_02_medium.webp differ diff --git a/public/icons/ranks/02_bronze_03_medium.webp b/public/icons/ranks/02_bronze_03_medium.webp new file mode 100644 index 0000000..c7aa807 Binary files /dev/null and b/public/icons/ranks/02_bronze_03_medium.webp differ diff --git a/public/icons/ranks/03_iron_01_medium.webp b/public/icons/ranks/03_iron_01_medium.webp new file mode 100644 index 0000000..a040e36 Binary files /dev/null and b/public/icons/ranks/03_iron_01_medium.webp differ diff --git a/public/icons/ranks/03_iron_02_medium.webp b/public/icons/ranks/03_iron_02_medium.webp new file mode 100644 index 0000000..8e82997 Binary files /dev/null and b/public/icons/ranks/03_iron_02_medium.webp differ diff --git a/public/icons/ranks/03_iron_03_medium.webp b/public/icons/ranks/03_iron_03_medium.webp new file mode 100644 index 0000000..68d65ca Binary files /dev/null and b/public/icons/ranks/03_iron_03_medium.webp differ diff --git a/public/icons/ranks/04_silver_01_medium.webp b/public/icons/ranks/04_silver_01_medium.webp new file mode 100644 index 0000000..b39e1db Binary files /dev/null and b/public/icons/ranks/04_silver_01_medium.webp differ diff --git a/public/icons/ranks/04_silver_02_medium.webp b/public/icons/ranks/04_silver_02_medium.webp new file mode 100644 index 0000000..ce06233 Binary files /dev/null and b/public/icons/ranks/04_silver_02_medium.webp differ diff --git a/public/icons/ranks/04_silver_03_medium.webp b/public/icons/ranks/04_silver_03_medium.webp new file mode 100644 index 0000000..f4c0a14 Binary files /dev/null and b/public/icons/ranks/04_silver_03_medium.webp differ diff --git a/public/icons/ranks/05_gold_01_medium.webp b/public/icons/ranks/05_gold_01_medium.webp new file mode 100644 index 0000000..12c34f0 Binary files /dev/null and b/public/icons/ranks/05_gold_01_medium.webp differ diff --git a/public/icons/ranks/05_gold_02_medium.webp b/public/icons/ranks/05_gold_02_medium.webp new file mode 100644 index 0000000..b66760c Binary files /dev/null and b/public/icons/ranks/05_gold_02_medium.webp differ diff --git a/public/icons/ranks/05_gold_03_medium.webp b/public/icons/ranks/05_gold_03_medium.webp new file mode 100644 index 0000000..4a8a9a9 Binary files /dev/null and b/public/icons/ranks/05_gold_03_medium.webp differ diff --git a/public/icons/ranks/06_master_01_medium.webp b/public/icons/ranks/06_master_01_medium.webp new file mode 100644 index 0000000..2623252 Binary files /dev/null and b/public/icons/ranks/06_master_01_medium.webp differ diff --git a/public/icons/ranks/06_master_02_medium.webp b/public/icons/ranks/06_master_02_medium.webp new file mode 100644 index 0000000..4aa254c Binary files /dev/null and b/public/icons/ranks/06_master_02_medium.webp differ diff --git a/public/icons/ranks/06_master_03_medium.webp b/public/icons/ranks/06_master_03_medium.webp new file mode 100644 index 0000000..757129a Binary files /dev/null and b/public/icons/ranks/06_master_03_medium.webp differ diff --git a/public/icons/ranks/06_master_04_medium.webp b/public/icons/ranks/06_master_04_medium.webp new file mode 100644 index 0000000..8548cd0 Binary files /dev/null and b/public/icons/ranks/06_master_04_medium.webp differ diff --git a/public/icons/ranks/06_master_05_medium.webp b/public/icons/ranks/06_master_05_medium.webp new file mode 100644 index 0000000..cb8c0a7 Binary files /dev/null and b/public/icons/ranks/06_master_05_medium.webp differ diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 7a8e570..fb05318 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -32,6 +32,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anyhow" version = "1.0.66" @@ -122,7 +128,7 @@ checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -139,7 +145,7 @@ checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -188,6 +194,17 @@ dependencies = [ "url", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -342,6 +359,12 @@ dependencies = [ "toml", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.0.77" @@ -388,6 +411,54 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "ciborium" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" + +[[package]] +name = "ciborium-ll" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "3.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +dependencies = [ + "bitflags", + "clap_lex", + "indexmap", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cocoa" version = "0.24.1" @@ -421,8 +492,9 @@ dependencies = [ [[package]] name = "coh3-stats-desktop-app" -version = "1.2.2" +version = "1.2.3" dependencies = [ + "criterion", "log", "machine-uid", "nom", @@ -530,6 +602,42 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +dependencies = [ + "anes", + "atty", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + [[package]] name = "crossbeam-channel" version = "0.5.6" @@ -540,6 +648,30 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.8.0", + "scopeguard", +] + [[package]] name = "crossbeam-utils" version = "0.8.14" @@ -573,7 +705,7 @@ dependencies = [ "proc-macro2", "quote", "smallvec", - "syn", + "syn 1.0.105", ] [[package]] @@ -583,7 +715,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e" dependencies = [ "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -593,7 +725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -623,7 +755,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 1.0.105", ] [[package]] @@ -634,7 +766,7 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -656,7 +788,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -669,7 +801,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.0", - "syn", + "syn 1.0.105", ] [[package]] @@ -750,6 +882,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "embed_plist" version = "1.2.2" @@ -783,7 +921,7 @@ checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -946,7 +1084,7 @@ checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -1166,7 +1304,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -1261,9 +1399,15 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "hashbrown" version = "0.12.3" @@ -1311,7 +1455,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -1436,6 +1580,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" @@ -1698,6 +1851,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1922,7 +2084,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -1979,6 +2141,12 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "open" version = "3.2.0" @@ -2012,7 +2180,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -2065,6 +2233,12 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "os_str_bytes" +version = "6.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" + [[package]] name = "overload" version = "0.1.1" @@ -2216,7 +2390,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -2230,7 +2404,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -2283,6 +2457,34 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "plotters" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" + +[[package]] +name = "plotters-svg" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +dependencies = [ + "plotters-backend", +] + [[package]] name = "png" version = "0.17.7" @@ -2343,7 +2545,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.105", "version_check", ] @@ -2366,9 +2568,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] @@ -2384,9 +2586,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -2481,6 +2683,28 @@ dependencies = [ "cty", ] +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -2538,9 +2762,12 @@ dependencies = [ [[package]] name = "rev_lines" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18eb52b6664d331053136fcac7e4883bdc6f5fc04a6aab3b0f75eafb80ab88b3" +checksum = "ed62916ac7a5ccbf13fa5e1d303029ff015600fee841756dfc134a1ac62bf05f" +dependencies = [ + "thiserror", +] [[package]] name = "rfd" @@ -2720,7 +2947,7 @@ checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -2742,7 +2969,7 @@ checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -2776,7 +3003,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -2798,7 +3025,7 @@ checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -2982,7 +3209,7 @@ dependencies = [ "heck 0.3.3", "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -2996,6 +3223,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "system-deps" version = "5.0.0" @@ -3187,7 +3425,7 @@ dependencies = [ "heck 0.4.0", "proc-macro2", "quote", - "syn", + "syn 1.0.105", "tauri-codegen", "tauri-utils", ] @@ -3363,6 +3601,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + [[package]] name = "thin-slice" version = "0.1.1" @@ -3371,22 +3615,22 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.22", ] [[package]] @@ -3427,6 +3671,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -3485,7 +3739,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -3714,7 +3968,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.105", "wasm-bindgen-shared", ] @@ -3748,7 +4002,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3836,7 +4090,7 @@ checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] [[package]] @@ -3940,7 +4194,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" dependencies = [ - "syn", + "syn 1.0.105", "windows-tokens", ] @@ -4281,7 +4535,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn", + "syn 1.0.105", "zvariant_utils", ] @@ -4330,7 +4584,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.105", "zvariant_utils", ] @@ -4342,5 +4596,5 @@ checksum = "53b22993dbc4d128a17a3b6c92f1c63872dd67198537ee728d8b5d7c40640a8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.105", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 0a3af38..7d4d21c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "coh3-stats-desktop-app" -version = "1.2.2" -description = "A Tauri App" -authors = ["you"] +version = "1.2.3" +description = "COH3 Stats Desktop App" +authors = ["coh3stats team"] license = "" repository = "" edition = "2021" @@ -11,15 +11,15 @@ rust-version = "1.57" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -tauri-build = {version = "1.2", features = [] } +tauri-build = { version = "1.2", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = {version = "1.2", features = ["api-all", "updater"] } +tauri = { version = "1.2", features = ["api-all", "updater"] } notify = "5.0.0" nom = "7.1.1" -rev_lines = "0.2.1" +rev_lines = "0.3.0" window-shadows = "0.2.0" tauri-plugin-window-state = "0.1" tauri-plugin-fs-watch = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" } @@ -29,10 +29,17 @@ log = "^0.4" tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" } machine-uid = "0.2.0" +[dev-dependencies] +criterion = { version = "0.4", features = ["html_reports"] } + [features] # by default Tauri runs in production mode # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL -default = [ "custom-protocol" ] -# this feature is used used for production builds where `devPath` points to the filesystem +default = ["custom-protocol"] +# this feature is used for production builds where `devPath` points to the filesystem # DO NOT remove this -custom-protocol = [ "tauri/custom-protocol" ] +custom-protocol = ["tauri/custom-protocol"] + +[[bench]] +name = "parse_log_file_reverse" +harness = false diff --git a/src-tauri/benches/parse_log_file_reverse.rs b/src-tauri/benches/parse_log_file_reverse.rs new file mode 100644 index 0000000..5b5a964 --- /dev/null +++ b/src-tauri/benches/parse_log_file_reverse.rs @@ -0,0 +1,11 @@ +use coh3_stats_desktop_app::parse_log_file::parse_log_file_reverse; +use criterion::{criterion_group, criterion_main, Criterion}; + +fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("parse_log_file_reverse 2mb", |b| { + b.iter(|| parse_log_file_reverse("tests/warnings-2mb.log".to_string())) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/src-tauri/build.rs b/src-tauri/build.rs index 795b9b7..d860e1e 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -1,3 +1,3 @@ fn main() { - tauri_build::build() + tauri_build::build() } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..b1cd324 --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1 @@ +pub mod parse_log_file; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 7f1d146..c29078e 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -4,40 +4,48 @@ )] extern crate machine_uid; -use tauri_plugin_single_instance; -use tauri_plugin_fs_watch; +use coh3_stats_desktop_app::parse_log_file; use std::path::Path; use tauri::Manager; +use tauri_plugin_log::LogTarget; use window_shadows::set_shadow; -use tauri_plugin_log::{LogTarget}; -mod parse_log_file; #[derive(Clone, serde::Serialize)] struct Payload { - args: Vec, - cwd: String, + args: Vec, + cwd: String, } fn main() { tauri::Builder::default() - .invoke_handler(tauri::generate_handler![get_default_log_file_path, check_log_file_exists, get_machine_id, parse_log_file::parse_log_file_reverse]) + .invoke_handler(tauri::generate_handler![ + get_default_log_file_path, + check_log_file_exists, + get_machine_id, + parse_log_file::parse_log_file_reverse + ]) .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| { let window = app.get_window("main").unwrap(); window.set_focus().ok(); - window.request_user_attention(Some(tauri::UserAttentionType::Informational)).ok(); - + window + .request_user_attention(Some(tauri::UserAttentionType::Informational)) + .ok(); + //println!("{}, {argv:?}, {cwd}", app.package_info().name); - app.emit_all("single-instance", Payload { args: argv, cwd }).unwrap(); + app.emit_all("single-instance", Payload { args: argv, cwd }) + .unwrap(); })) - .plugin(tauri_plugin_log::Builder::default().targets([ - LogTarget::LogDir, - LogTarget::Stdout, - ]).build()) + .plugin( + tauri_plugin_log::Builder::default() + .targets([LogTarget::LogDir, LogTarget::Stdout]) + .build(), + ) .plugin(tauri_plugin_fs_watch::init()) .plugin(tauri_plugin_window_state::Builder::default().build()) .plugin(tauri_plugin_store::Builder::default().build()) - .setup(|app| { // Add window shadows + .setup(|app| { + // Add window shadows let window = app.get_window("main").unwrap(); set_shadow(&window, true).expect("Unsupported platform!"); Ok(()) @@ -66,4 +74,4 @@ fn check_log_file_exists(path: &str) -> bool { #[tauri::command] fn get_machine_id() -> String { machine_uid::get().unwrap() -} \ No newline at end of file +} diff --git a/src-tauri/src/parse_log_file.rs b/src-tauri/src/parse_log_file.rs index b63a9d3..61231e5 100644 --- a/src-tauri/src/parse_log_file.rs +++ b/src-tauri/src/parse_log_file.rs @@ -1,342 +1,364 @@ +use log::info; +use rev_lines::RawRevLines; use serde::{Deserialize, Serialize}; -use nom; -use std::io::BufReader; -use std::io::BufRead; use std::fs::File; -use log::{info}; #[derive(Serialize, Deserialize, Clone)] pub enum GameState { - Closed, - Menu, - Loading, - InGame + Closed, + Menu, + Loading, + InGame, } #[derive(Serialize, Deserialize, Clone)] pub enum GameType { - Classic, - AI, - Custom + Classic, + AI, + Custom, } #[derive(Serialize, Deserialize, Clone, PartialEq)] pub enum TeamSide { - Axis, - Allies, - Mixed + Axis, + Allies, + Mixed, } #[derive(Serialize, Deserialize, Clone)] pub struct PlayerData { - pub ai: bool, - pub faction: String, - pub relic_id: String, - pub name: String, - pub position: u8, - pub steam_id: String, - pub rank: i64, + pub ai: bool, + pub faction: String, + pub relic_id: String, + pub name: String, + pub position: u8, + pub steam_id: String, + pub rank: i64, } #[derive(Serialize, Deserialize, Clone)] pub struct TeamData { - pub players: Vec, - pub side: TeamSide, + pub players: Vec, + pub side: TeamSide, } #[derive(Serialize, Deserialize, Clone)] pub struct LogFileData { - pub game_state: GameState, - pub game_type: GameType, - pub timestamp: String, - pub duration: u64, - pub map: String, - pub win_condition: String, - pub left: TeamData, - pub right: TeamData, - pub player_name: String, - pub player_steam_id: String, - pub language_code: String + pub game_state: GameState, + pub game_type: GameType, + pub timestamp: String, + pub duration: u64, + pub map: String, + pub win_condition: String, + pub left: TeamData, + pub right: TeamData, + pub player_name: String, + pub player_steam_id: String, + pub language_code: String, } #[tauri::command] pub fn parse_log_file_reverse(path: String) -> LogFileData { - let file = File::open(path).unwrap(); - let reader = BufReader::new(file); - - let mut string_array: Vec = Vec::new(); - - // Because some of the lines are not UTF-8, I needed to skip them while parsing - // this is less effective than using RevLines because we first load the whole log - // and than read it backwards. But I couldn't figure out how to fix it with revlines. - for result in reader.lines() { - match result { - Ok(line) => { - // If the conversion is successful, process the line - string_array.push(line); - } - Err(_) => { - // If the conversion fails, skip the line - // println!("Skipped non-UTF-8 line"); - } - } - } - - let mut full_game = false; - let mut game_running = true; - let mut game_loading = false; - let mut game_started = false; - let mut game_ended = false; - let mut map = "".to_string(); - let mut win_condition = "".to_string(); - let mut timestamp = "".to_string(); - let mut game_duration: u64 = 0; - let mut left: Vec = Vec::new(); - let mut right: Vec = Vec::new(); - let mut player_name = "".to_string(); - let mut player_steam_id = "".to_string(); - let mut language_code = "".to_string(); - - // Read log file in reverse order line by line - for line in string_array.iter().rev() { - - // Is the line when the game is being closed correctly - if nom::bytes::complete::tag::<&str, &str, ()>("Application closed")(line.as_str()).is_ok() { - game_running = false; - continue; - } - - if let Ok((tail, parsed_timestamp)) = get_timestamped_line(line.as_str()) { - - // Is the line where a game starts - if is_game_start_line(tail) { - timestamp = parsed_timestamp.to_string(); - continue; - } - - // Is the line that logs the player steam id - if let Ok((steam_id, _)) = get_game_player_steam_id(tail) { - player_steam_id = steam_id.to_string(); - continue; - } - - if let Ok((tail, param)) = get_param_line(tail) { - if param == "GAME" { - - if let Ok((tail, sub_param)) = get_game_sub_param(tail) { - if sub_param == "Scenario" { - if let Ok((parsed_map, _)) = get_map_name(tail) { - if !full_game { - map = parsed_map.to_string(); - //println!("Map {}", map); - full_game = true; - } - } - } else if sub_param == "Win Condition Name" && !full_game { - win_condition = tail.trim().to_string(); - game_loading = true; - //println!("Win Condition {}", win_condition); - } else if sub_param == "Starting mission" && !full_game { - game_started = true; - } else if sub_param == "Human Player" && !full_game { - if let Ok((without_space, _)) = get_without_leading_space(tail) { - if let Ok((tail, position_str)) = nom::bytes::complete::take_until1::<&str, &str, ()>(" ")(without_space) { - if let Ok((tail, _)) = nom::bytes::complete::tag::<&str, &str, ()>(" ")(tail) { - if let Ok((faction, front)) = get_last_separated_by_space(tail) { - if let Ok((side_str, front)) = get_last_separated_by_space(front) { - if let Ok((relic_id, user_name)) = get_last_separated_by_space(front) { - if let Ok(position) = position_str.parse::() { - if let Ok(side) = side_str.parse::() { - let player_data = PlayerData { - ai: false, - position, - faction: faction.to_string(), - relic_id: relic_id.to_string(), - name: user_name.to_string(), - steam_id: "".to_string(), - rank: -1, - }; - if side == 0 { - left.push(player_data); - } else { - right.push(player_data); - } - //println!("{}", position); - //println!("{}", faction); - //println!("{}", side); - //println!("{}", relic_id); - //println!("{}", user_name); + let mut full_game = false; + let mut game_running = true; + let mut game_loading = false; + let mut game_started = false; + let mut game_ended = false; + let mut map = "".to_string(); + let mut win_condition = "".to_string(); + let mut timestamp = "".to_string(); + let mut game_duration: u64 = 0; + let mut left: Vec = Vec::new(); + let mut right: Vec = Vec::new(); + let mut player_name = "".to_string(); + let mut player_steam_id = "".to_string(); + let mut language_code = "".to_string(); + + // Read log file in reverse order line by line + let log_file = File::open(path).unwrap(); + let rev_lines = RawRevLines::new(log_file); + + for line in rev_lines { + let line = line.unwrap(); + let line = String::from_utf8_lossy(&line); + + // Is the line when the game is being closed correctly + if nom::bytes::complete::tag::<&str, &str, ()>("Application closed")(line.as_ref()).is_ok() + { + game_running = false; + continue; + } + + if let Ok((tail, parsed_timestamp)) = get_timestamped_line(line.as_ref()) { + // Is the line where a game starts + if is_game_start_line(tail) { + timestamp = parsed_timestamp.to_string(); + continue; + } + + // Is the line that logs the player steam id + if let Ok((steam_id, _)) = get_game_player_steam_id(tail) { + player_steam_id = steam_id.to_string(); + continue; + } + + if let Ok((tail, param)) = get_param_line(tail) { + if param == "GAME" { + if let Ok((tail, sub_param)) = get_game_sub_param(tail) { + if sub_param == "Scenario" { + if let Ok((parsed_map, _)) = get_map_name(tail) { + if !full_game { + map = parsed_map.to_string(); + //println!("Map {}", map); + full_game = true; + } + } + } else if sub_param == "Win Condition Name" && !full_game { + win_condition = tail.trim().to_string(); + game_loading = true; + //println!("Win Condition {}", win_condition); + } else if sub_param == "Starting mission" && !full_game { + game_started = true; + } else if sub_param == "Human Player" && !full_game { + if let Ok((without_space, _)) = get_without_leading_space(tail) { + if let Ok((tail, position_str)) = + nom::bytes::complete::take_until1::<&str, &str, ()>(" ")( + without_space, + ) + { + if let Ok((tail, _)) = + nom::bytes::complete::tag::<&str, &str, ()>(" ")(tail) + { + if let Ok((faction, front)) = + get_last_separated_by_space(tail) + { + if let Ok((side_str, front)) = + get_last_separated_by_space(front) + { + if let Ok((relic_id, user_name)) = + get_last_separated_by_space(front) + { + if let Ok(position) = position_str.parse::() + { + if let Ok(side) = side_str.parse::() { + let player_data = PlayerData { + ai: false, + position, + faction: faction.to_string(), + relic_id: relic_id.to_string(), + name: user_name.to_string(), + steam_id: "".to_string(), + rank: -1, + }; + if side == 0 { + left.push(player_data); + } else { + right.push(player_data); + } + //println!("{}", position); + //println!("{}", faction); + //println!("{}", side); + //println!("{}", relic_id); + //println!("{}", user_name); + } + } + } + } + } + } + } + } + } else if sub_param == "AI Player" && !full_game { + if let Ok((without_space, _)) = get_without_leading_space(tail) { + if let Ok((tail, position_str)) = + nom::bytes::complete::take_until1::<&str, &str, ()>(" ")( + without_space, + ) + { + if let Ok((tail, _)) = + nom::bytes::complete::tag::<&str, &str, ()>(" ")(tail) + { + if let Ok((faction, front)) = + get_last_separated_by_space(tail) + { + if let Ok((side_str, front)) = + get_last_separated_by_space(front) + { + if let Ok((_, user_name)) = + get_last_separated_by_space(front) + { + if let Ok(position) = position_str.parse::() + { + if let Ok(side) = side_str.parse::() { + let player_data = PlayerData { + ai: true, + position, + faction: faction.to_string(), + relic_id: "-1".to_string(), + name: user_name.to_string(), + steam_id: "".to_string(), + rank: -1, + }; + if side == 0 { + left.push(player_data); + } else { + right.push(player_data); + } + //println!("{}", position); + //println!("{}", faction); + //println!("{}", side); + //println!("{}", user_name); + } + } + } + } + } + } + } } - } } - } + + // Is the line that logs the playing players name + } else if let Ok((steam_name, _)) = get_game_player_name(tail) { + player_name = steam_name.to_string(); + break; + + // Is the line that logs the games language + } else if let Ok((game_language, _)) = get_game_language(tail) { + language_code = game_language.to_string(); } - } - } - } - } else if sub_param == "AI Player" && !full_game { - if let Ok((without_space, _)) = get_without_leading_space(tail) { - if let Ok((tail, position_str)) = nom::bytes::complete::take_until1::<&str, &str, ()>(" ")(without_space) { - if let Ok((tail, _)) = nom::bytes::complete::tag::<&str, &str, ()>(" ")(tail) { - if let Ok((faction, front)) = get_last_separated_by_space(tail) { - if let Ok((side_str, front)) = get_last_separated_by_space(front) { - if let Ok((_, user_name)) = get_last_separated_by_space(front) { - if let Ok(position) = position_str.parse::() { - if let Ok(side) = side_str.parse::() { - let player_data = PlayerData { - ai: true, - position, - faction: faction.to_string(), - relic_id: "-1".to_string(), - name: user_name.to_string(), - steam_id: "".to_string(), - rank: -1, - }; - if side == 0 { - left.push(player_data); - } else { - right.push(player_data); - } - //println!("{}", position); - //println!("{}", faction); - //println!("{}", side); - //println!("{}", user_name); + } else if param == "MOD" { + if let Ok((duration_str, _)) = get_game_over(tail) { + if !full_game { + if let Ok(duration) = duration_str.parse::() { + game_duration = duration / 8; + //println!("Game Duration {}s", duration/8); } - } + game_ended = true; } - } } - } } - } - } - - // Is the line that logs the playing players name - } else if let Ok((steam_name, _)) = get_game_player_name(tail) { - player_name = steam_name.to_string(); - break; - - // Is the line that logs the games language - } else if let Ok((game_language, _)) = get_game_language(tail) { - language_code = game_language.to_string(); - } - } else if param == "MOD" { - if let Ok((duration_str, _)) = get_game_over(tail) { - if !full_game { - if let Ok(duration) = duration_str.parse::() { - game_duration = duration / 8; - //println!("Game Duration {}s", duration/8); - } - game_ended = true; } - } } - } } - - } - - let game_state = determine_game_state(game_running, game_ended, game_loading, game_started); - let left_team = get_team_data(left); - let right_team = get_team_data(right); - - info!("Log file parsed: Found {} players", left_team.players.len() + right_team.players.len()); - - LogFileData { - game_state, - game_type: determine_game_type(&left_team, &right_team), - timestamp, - duration: game_duration, - map, - win_condition, - left: left_team, - right: right_team, - player_name: player_name, - player_steam_id: player_steam_id, - language_code: language_code - } + let game_state = determine_game_state(game_running, game_ended, game_loading, game_started); + let left_team = get_team_data(left); + let right_team = get_team_data(right); + + info!( + "Log file parsed: Found {} players", + left_team.players.len() + right_team.players.len() + ); + + LogFileData { + game_state, + game_type: determine_game_type(&left_team, &right_team), + timestamp, + duration: game_duration, + map, + win_condition, + left: left_team, + right: right_team, + player_name, + player_steam_id, + language_code, + } } fn determine_game_state(running: bool, ended: bool, loading: bool, started: bool) -> GameState { - if !running { - return GameState::Closed; - } else if ended || !loading { - return GameState::Menu; - } else if started { - return GameState::InGame; - } else if loading { - return GameState::Loading; - } - GameState::Menu + if !running { + return GameState::Closed; + } else if ended || !loading { + return GameState::Menu; + } else if started { + return GameState::InGame; + } else if loading { + return GameState::Loading; + } + GameState::Menu } fn determine_game_type(left_team: &TeamData, right_team: &TeamData) -> GameType { - let left_ai_count = get_ai_count(&left_team); - let right_ai_count = get_ai_count(&right_team); - if left_team.side != TeamSide::Mixed && right_team.side != TeamSide::Mixed && left_team.side != right_team.side { - if (left_ai_count + right_ai_count) == 0 { - return GameType::Classic - } else if (left_ai_count == 0 && right_ai_count == right_team.players.len()) || (right_ai_count == 0 && left_ai_count == left_team.players.len()) { - return GameType::AI + let left_ai_count = get_ai_count(left_team); + let right_ai_count = get_ai_count(right_team); + if left_team.side != TeamSide::Mixed + && right_team.side != TeamSide::Mixed + && left_team.side != right_team.side + { + if (left_ai_count + right_ai_count) == 0 { + return GameType::Classic; + } else if (left_ai_count == 0 && right_ai_count == right_team.players.len()) + || (right_ai_count == 0 && left_ai_count == left_team.players.len()) + { + return GameType::AI; + } } - } - GameType::Custom + GameType::Custom } fn get_ai_count(team: &TeamData) -> usize { - let mut count: usize = 0; - for player in &team.players { - if player.ai { - count += 1; + let mut count: usize = 0; + for player in &team.players { + if player.ai { + count += 1; + } } - } - count + count } fn get_team_data(players: Vec) -> TeamData { - let mut mixed = false; - let mut last = TeamSide::Mixed; - for player in &players { - if player.faction == "german" || player.faction == "west_german" { - if last == TeamSide::Allies { - mixed = true; - break; - } - last = TeamSide::Axis; - } else { - if last == TeamSide::Axis { - mixed = true; - break; - } - last = TeamSide::Allies; + let mut mixed = false; + let mut last = TeamSide::Mixed; + for player in &players { + if player.faction == "german" || player.faction == "west_german" { + if last == TeamSide::Allies { + mixed = true; + break; + } + last = TeamSide::Axis; + } else { + if last == TeamSide::Axis { + mixed = true; + break; + } + last = TeamSide::Allies; + } + } + if mixed { + return TeamData { + players, + side: TeamSide::Mixed, + }; + } + TeamData { + players, + side: last, } - } - if mixed { - return TeamData { players: players, side: TeamSide::Mixed } - } - TeamData { players: players, side: last } } // look for blocks like this: -// (I) [11:43:31.404] [000007332]: -// (E) [11:44:07.831] [000007332]: +// (I) [11:43:31.404] [000007332]: +// (E) [11:44:07.831] [000007332]: // if searched tags are found // take time code -> eg: 11:44:07.831 // and return remaining line // if not stop with error as soon as tag cannot be found fn get_timestamped_line(line: &str) -> nom::IResult<&str, &str> { - let (tail, _) = nom::bytes::complete::take_until1("[")(line)?; - let (tail, _) = nom::bytes::complete::tag("[")(tail)?; - let (tail, time_code) = nom::bytes::complete::take_until1("]")(tail)?; - let (tail, _) = nom::bytes::complete::tag("]")(tail)?; - let (tail, _) = nom::bytes::complete::take_until1("]: ")(tail)?; - let (tail, _) = nom::bytes::complete::tag("]: ")(tail)?; - Ok((tail,time_code)) + let (tail, _) = nom::bytes::complete::take_until1("[")(line)?; + let (tail, _) = nom::bytes::complete::tag("[")(tail)?; + let (tail, time_code) = nom::bytes::complete::take_until1("]")(tail)?; + let (tail, _) = nom::bytes::complete::tag("]")(tail)?; + let (tail, _) = nom::bytes::complete::take_until1("]: ")(tail)?; + let (tail, _) = nom::bytes::complete::tag("]: ")(tail)?; + Ok((tail, time_code)) } fn is_game_start_line(timestamped_tail: &str) -> bool { - nom::bytes::complete::tag::<_, _, nom::error::Error<_>>("GameApp::SetState : new (Game)")(timestamped_tail).is_ok() + nom::bytes::complete::tag::<_, _, nom::error::Error<_>>("GameApp::SetState : new (Game)")( + timestamped_tail, + ) + .is_ok() } /*fn get_match_started_line(timestamped_tail: &str) -> nom::IResult<&str, ()> { @@ -345,70 +367,72 @@ fn is_game_start_line(timestamped_tail: &str) -> bool { }*/ fn get_param_line(timestamped_tail: &str) -> nom::IResult<&str, &str> { - let (tail, param) = nom::bytes::complete::take_until1(" -- ")(timestamped_tail)?; - let (tail, _) = nom::bytes::complete::tag(" -- ")(tail)?; - Ok((tail,param)) + let (tail, param) = nom::bytes::complete::take_until1(" -- ")(timestamped_tail)?; + let (tail, _) = nom::bytes::complete::tag(" -- ")(tail)?; + Ok((tail, param)) } fn get_game_sub_param(game_param_tail: &str) -> nom::IResult<&str, &str> { - let (tail, sub_param) = nom::bytes::complete::take_until1(":")(game_param_tail)?; - let (tail, _) = nom::bytes::complete::tag(":")(tail)?; - Ok((tail,sub_param)) + let (tail, sub_param) = nom::bytes::complete::take_until1(":")(game_param_tail)?; + let (tail, _) = nom::bytes::complete::tag(":")(tail)?; + Ok((tail, sub_param)) } fn get_game_player_name(game_param_tail: &str) -> nom::IResult<&str, ()> { - let (name_tail, _) = nom::bytes::complete::tag("Current Steam name is [")(game_param_tail)?; - let (name, _) = get_till_last_tag(name_tail, "]")?; - //let (_, name) = nom::bytes::complete::take_until1("]")(name_tail)?; - Ok((name,())) + let (name_tail, _) = nom::bytes::complete::tag("Current Steam name is [")(game_param_tail)?; + let (name, _) = get_till_last_tag(name_tail, "]")?; + //let (_, name) = nom::bytes::complete::take_until1("]")(name_tail)?; + Ok((name, ())) } fn get_game_language(game_param_tail: &str) -> nom::IResult<&str, ()> { - let (language_tail, _) = nom::bytes::complete::tag("[Company of Heroes 3] set to language [")(game_param_tail)?; - let (_, language) = nom::bytes::complete::take_until1("]")(language_tail)?; - Ok((language,())) + let (language_tail, _) = + nom::bytes::complete::tag("[Company of Heroes 3] set to language [")(game_param_tail)?; + let (_, language) = nom::bytes::complete::take_until1("]")(language_tail)?; + Ok((language, ())) } fn get_game_player_steam_id(timestamped_tail: &str) -> nom::IResult<&str, ()> { - let (steam_id, _) = nom::bytes::complete::tag("Found profile: /steam/")(timestamped_tail)?; - Ok((steam_id,())) + let (steam_id, _) = nom::bytes::complete::tag("Found profile: /steam/")(timestamped_tail)?; + Ok((steam_id, ())) } fn get_map_name(scenario_tail: &str) -> nom::IResult<&str, &str> { - let (tail, front) = nom::bytes::complete::take_until1("\\")(scenario_tail)?; - let (tail, _) = nom::bytes::complete::tag("\\")(tail)?; - if let Ok((tail, front)) = get_map_name(tail) { - return Ok((tail,front)) - } - Ok((tail,front)) + let (tail, front) = nom::bytes::complete::take_until1("\\")(scenario_tail)?; + let (tail, _) = nom::bytes::complete::tag("\\")(tail)?; + if let Ok((tail, front)) = get_map_name(tail) { + return Ok((tail, front)); + } + Ok((tail, front)) } fn get_game_over(mod_param_tail: &str) -> nom::IResult<&str, &str> { - let (duration, game_over_message) = nom::bytes::complete::tag("Game Over at frame ")(mod_param_tail)?; - Ok((duration, game_over_message)) + let (duration, game_over_message) = + nom::bytes::complete::tag("Game Over at frame ")(mod_param_tail)?; + Ok((duration, game_over_message)) } fn get_last_separated_by_space(line: &str) -> nom::IResult<&str, &str> { - let (tail, front) = nom::bytes::complete::take_until(" ")(line)?; - let (tail, _) = nom::bytes::complete::tag(" ")(tail)?; - if let Ok((tail, _)) = get_last_separated_by_space(tail) { - return Ok((tail, &line[0..(line.len() - tail.len() - 1)])) - } - Ok((tail, front)) + let (tail, front) = nom::bytes::complete::take_until(" ")(line)?; + let (tail, _) = nom::bytes::complete::tag(" ")(tail)?; + if let Ok((tail, _)) = get_last_separated_by_space(tail) { + return Ok((tail, &line[0..(line.len() - tail.len() - 1)])); + } + Ok((tail, front)) } fn get_till_last_tag<'a>(line: &'a str, tag: &'a str) -> nom::IResult<&'a str, &'a str> { - let (tail, front) = nom::bytes::complete::take_until(tag)(line)?; - let (tail, _) = nom::bytes::complete::tag(tag)(tail)?; - if let Ok((front, tail)) = get_till_last_tag(tail, tag) { - return Ok((front, tail)) - } - Ok((front, tail)) + let (tail, front) = nom::bytes::complete::take_until(tag)(line)?; + let (tail, _) = nom::bytes::complete::tag(tag)(tail)?; + if let Ok((front, tail)) = get_till_last_tag(tail, tag) { + return Ok((front, tail)); + } + Ok((front, tail)) } fn get_without_leading_space(line: &str) -> nom::IResult<&str, ()> { - let (without_space, _) = nom::bytes::complete::tag(" ")(line)?; - Ok((without_space, ())) + let (without_space, _) = nom::bytes::complete::tag(" ")(line)?; + Ok((without_space, ())) } /*fn test_logging_solution(line: &str) -> nom::IResult<&str, ()> { @@ -421,4 +445,21 @@ fn get_without_leading_space(line: &str) -> nom::IResult<&str, ()> { } }; Ok(("without_space", ())) -}*/ \ No newline at end of file +}*/ + +#[cfg(test)] +mod tests { + use super::parse_log_file_reverse; + + #[test] + fn test_parse_log_file_reverse() { + println!("{}", file!()); + parse_log_file_reverse("tests/warnings.log".to_string()); + } + + #[test] + fn test_parse_log_file_reverse_big_file() { + println!("{}", file!()); + parse_log_file_reverse("tests/warnings-2mb.log".to_string()); + } +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e714195..5ff9973 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -7,7 +7,7 @@ }, "package": { "productName": "Coh3 Stats Desktop App", - "version": "1.2.2" + "version": "1.2.3" }, "tauri": { "allowlist": { @@ -15,7 +15,7 @@ "http": { "all": true, "request": true, - "scope": ["https://coh3-api.reliclink.com/*"] + "scope": ["https://coh3-api.reliclink.com/*", "https://api.steampowered.com/*"] }, "fs": { "all": true, @@ -80,7 +80,7 @@ }, "updater": { "active": true, - "endpoints": ["https://dev.coh3stats.com/api/appUpdateRoute"], + "endpoints": ["https://coh3stats.com/api/appUpdateRoute"], "dialog": true, "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IERFNjMzQTcwRjBEQjUwNTUKUldSVlVOdndjRHBqM21pc1BGYndieG03VVZOSGhxdUxPeDNkRkFqaXVpcEMyL2l1WUN6VDNCK1kK" }, @@ -90,7 +90,7 @@ "fullscreen": false, "resizable": true, "focus": true, - "title": "Coh3 Stats Desktop App", + "title": "COH3 Stats Desktop App", "width": 1200, "minWidth": 900, "height": 600, diff --git a/src/About.tsx b/src/About.tsx index b00c3a4..acc8925 100644 --- a/src/About.tsx +++ b/src/About.tsx @@ -86,7 +86,7 @@ export const About: React.FC = () => { Ko-fi donations Donate diff --git a/src/Game.tsx b/src/Game.tsx index 3e6a278..bd6353b 100644 --- a/src/Game.tsx +++ b/src/Game.tsx @@ -2,6 +2,7 @@ import { useGameData } from "./game-data-provider/GameDataProvider" import { Title, Grid, Loader, Group, Box, Badge } from "@mantine/core" import { PlayerCard } from "./components/PlayerCard" import { useLogFilePath } from "./game-data-provider/configValues" +import { OnlinePlayers } from "./components/Online-players" export const Game: React.FC = () => { const gameData = useGameData() @@ -9,9 +10,16 @@ export const Game: React.FC = () => { return ( <> {gameData ? ( - - Game State: {gameData.gameData.state} - + <> + + + Game State {gameData.gameData.state} + + + + + + ) : null} {logFilePath !== undefined ? ( <> diff --git a/src/Settings.tsx b/src/Settings.tsx index 8f67ae0..8d045c4 100644 --- a/src/Settings.tsx +++ b/src/Settings.tsx @@ -90,7 +90,11 @@ export const Settings: React.FC = () => {
- + @@ -146,14 +150,16 @@ export const Settings: React.FC = () => { events.settings_changed("play_sound_volume", value) }} /> - - - + + + + +
@@ -201,7 +207,7 @@ export const Settings: React.FC = () => {
- Follow the Setup instructions{" "} + Follow the{" "} openLink( @@ -209,12 +215,22 @@ export const Settings: React.FC = () => { ) } > - Here + Setup instructions + {" "}&{" "} + openLink( + "https://github.com/cohstats/coh3-stats-desktop-app#custom-css-for-the-overlay" + ) + } + > + Custom CSS instructions + + Path to streamerOverlay.html: - + { diff --git a/src/components/Online-players.tsx b/src/components/Online-players.tsx new file mode 100644 index 0000000..a1edec4 --- /dev/null +++ b/src/components/Online-players.tsx @@ -0,0 +1,89 @@ +import { useEffect, useState } from "react" +import { Badge, Group, Tooltip } from "@mantine/core" +import { getNumberOfOnlinePlayersSteamUrl } from "../utils/steam-api" +import { fetch } from "@tauri-apps/api/http" +import { SteamIcon } from "./other/Steam-icon" + +export const OnlinePlayers: React.FC = () => { + const [onlinePlayersData, setOnlinePlayersData] = useState(null) + + useEffect(() => { + ;(async () => { + try { + if ( + (onlinePlayersData && + onlinePlayersData.timeStampMs < + new Date().getTime() - 1000 * 60 * 4) || + !onlinePlayersData + ) { + const fetchResponse = await fetch(getNumberOfOnlinePlayersSteamUrl()) + // @ts-ignore + const { response } = fetchResponse.data + setOnlinePlayersData({ + playerCount: response.player_count, + timeStampMs: new Date().getTime(), + }) + } + + // Update the data every 5 minutes + const intervalId = setInterval(async () => { + try { + if ( + (onlinePlayersData && + onlinePlayersData.timeStampMs < + new Date().getTime() - 1000 * 60 * 4) || + !onlinePlayersData + ) { + const fetchResponse = await fetch( + getNumberOfOnlinePlayersSteamUrl() + ) + // @ts-ignore + const { response } = fetchResponse.data + setOnlinePlayersData({ + playerCount: response.player_count, + timeStampMs: new Date().getTime(), + }) + } + } catch (e) { + console.error(e) + } + }, 1000 * 60 * 5) + + return () => { + clearInterval(intervalId) + } + } catch (e) { + console.error(e) + } + })() + }, []) + + return ( + +
+ + + Players in game + + {onlinePlayersData?.playerCount} + + +
+
+ ) +} diff --git a/src/components/PlayerCard.tsx b/src/components/PlayerCard.tsx index df77a58..fb28345 100644 --- a/src/components/PlayerCard.tsx +++ b/src/components/PlayerCard.tsx @@ -20,6 +20,8 @@ import { PlayerStreak } from "./PlayerStreak" import { PlayerWinRatio } from "./PlayerWinRatio" import { PlayerWins } from "./PlayerWins" import { open } from "@tauri-apps/api/shell" +import { getFactionName, getCountryName } from "../utils/renameLabels" +import RankIcon from "./other/rank-icon"; export interface PlayerCardProps extends FullPlayerData {} @@ -37,12 +39,15 @@ export const PlayerCard: React.FC = ({ ai, self, }) => { + const factionName = getFactionName(faction) + const countryName = country ? getCountryName(country) : "" + return ( <> - + {faction} = ({ + {!ai ? ( - {country} + + {country} + ) : null} = ({ {/**/} + + + diff --git a/src/components/other/Steam-icon.tsx b/src/components/other/Steam-icon.tsx new file mode 100644 index 0000000..010f112 --- /dev/null +++ b/src/components/other/Steam-icon.tsx @@ -0,0 +1,37 @@ +import React from "react" + +export const SteamIcon: React.FC<{ size?: number }> = ({ size }) => { + size = size || 22 + return ( + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/src/components/other/rank-icon.tsx b/src/components/other/rank-icon.tsx new file mode 100644 index 0000000..0e16af0 --- /dev/null +++ b/src/components/other/rank-icon.tsx @@ -0,0 +1,23 @@ +import { Tooltip, Image } from "@mantine/core"; +import {calculatePlayerTier} from "../../utils/utils"; + + +type Props = { + size: number; + rating: number; + rank: number; +}; + +const RankIcon = ({ size, rank, rating }: Props) => { + const rankTier = calculatePlayerTier(rank, rating); + + return ( + <> + + {rankTier.name} + + + ); +}; + +export default RankIcon; diff --git a/src/streamer-overlay/SPECIAL-REACT/HTML.tsx b/src/streamer-overlay/SPECIAL-REACT/HTML.tsx index 3828d24..d60f85a 100644 --- a/src/streamer-overlay/SPECIAL-REACT/HTML.tsx +++ b/src/streamer-overlay/SPECIAL-REACT/HTML.tsx @@ -6,7 +6,7 @@ export interface HTMLProps { /** * You cannot use mantine here! - * This react component is meant for the streamerOverlay where only inline styles work! + * This React component is meant for the streamerOverlay where only inline styles work! */ export const HTML: React.FC = ({ html }) => { return ( @@ -30,12 +30,74 @@ export const HTML: React.FC = ({ html }) => { href="https://fonts.googleapis.com/css2?family=Tilt+Warp&display=swap" rel="stylesheet" /> - Coh3 Stats Desktop App Overlay + COH3 Stats Desktop App Overlay @@ -47,7 +109,7 @@ export const HTML: React.FC = ({ html }) => { } /* - background: url(https://i.ytimg.com/vi/NVqOvsHxqFc/maxresdefault.jpg) no-repeat center center fixed; + background: url(https://i.ytimg.com/vi/NVqOvsHxqFc/maxresdefault.jpg) no-repeat center center fixed; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; diff --git a/src/streamer-overlay/SPECIAL-REACT/OverlayApp.tsx b/src/streamer-overlay/SPECIAL-REACT/OverlayApp.tsx index e65f5fd..ffde3e7 100644 --- a/src/streamer-overlay/SPECIAL-REACT/OverlayApp.tsx +++ b/src/streamer-overlay/SPECIAL-REACT/OverlayApp.tsx @@ -25,24 +25,10 @@ export const OverlayApp: React.FC = ({ gameData.state === "Loading" || gameData.state === "InGame" ? (
{gameData.left.players.map((player, index) => ( = ({ ))}
{gameData.right.players.map((player, index) => ( = ({ }) => { return (
{flags ? ( ) : null} {playerData.rank === undefined || playerData.rank === -1 ? "-" : "#" + playerData.rank} {" "} {playerData.rating === undefined || playerData.rating === -1 ? "-" : playerData.rating} {" "} {playerData.name} diff --git a/src/utils/coh3-data.ts b/src/utils/coh3-data.ts new file mode 100644 index 0000000..8703c5c --- /dev/null +++ b/src/utils/coh3-data.ts @@ -0,0 +1,156 @@ + +export type PlayerRank = { name: string; url: string; min: number; max: number; rank: number }; + +export const PlayerRanks: Record = { + // Requires 10 matches to get the placement rank. + NO_RANK: { + name: "Placement", + url: "/icons/ranks/00_placement_medium.webp", + min: -1, + max: -1, + rank: 0, + }, + // All other ranks after completing 10 matches. + BRASS_3: { + name: "Brass 3", + url: "/icons/ranks/01_brass_03_medium.webp", + min: 0, + max: 299, + rank: 0, + }, + BRASS_2: { + name: "Brass 2", + url: "/icons/ranks/01_brass_02_medium.webp", + min: 300, + max: 599, + rank: 0, + }, + BRASS_1: { + name: "Brass 1", + url: "/icons/ranks/01_brass_01_medium.webp", + min: 600, + max: 799, + rank: 0, + }, + BRONZE_3: { + name: "Bronze 3", + url: "/icons/ranks/02_bronze_03_medium.webp", + min: 800, + max: 999, + rank: 0, + }, + BRONZE_2: { + name: "Bronze 2", + url: "/icons/ranks/02_bronze_02_medium.webp", + min: 1000, + max: 1049, + rank: 0, + }, + BRONZE_1: { + name: "Bronze 1", + url: "/icons/ranks/02_bronze_01_medium.webp", + min: 1050, + max: 1099, + rank: 0, + }, + IRON_3: { + name: "Iron 3", + url: "/icons/ranks/03_iron_03_medium.webp", + min: 1100, + max: 1149, + rank: 0, + }, + IRON_2: { + name: "Iron 2", + url: "/icons/ranks/03_iron_02_medium.webp", + min: 1150, + max: 1199, + rank: 0, + }, + IRON_1: { + name: "Iron 1", + url: "/icons/ranks/03_iron_01_medium.webp", + min: 1200, + max: 1249, + rank: 0, + }, + SILVER_3: { + name: "Silver 3", + url: "/icons/ranks/04_silver_03_medium.webp", + min: 1250, + max: 1299, + rank: 0, + }, + SILVER_2: { + name: "Silver 2", + url: "/icons/ranks/04_silver_02_medium.webp", + min: 1300, + max: 1349, + rank: 0, + }, + SILVER_1: { + name: "Silver 1", + url: "/icons/ranks/04_silver_01_medium.webp", + min: 1350, + max: 1399, + rank: 0, + }, + GOLD_3: { + name: "Gold 3", + url: "/icons/ranks/05_gold_03_medium.webp", + min: 1400, + max: 1499, + rank: 0, + }, + GOLD_2: { + name: "Gold 2", + url: "/icons/ranks/05_gold_02_medium.webp", + min: 1500, + max: 1599, + rank: 0, + }, + // Not in the top 50 players per leaderboard. + GOLD_1: { + name: "Gold 1", + url: "/icons/ranks/05_gold_01_medium.webp", + min: 1600, + max: 5000, + rank: 0, + }, + // These ranks need the special "top" field to identify those players above +1600 ELO in the leaderboard. + CHALLENGER_5: { + name: "Challenger 5", + url: "/icons/ranks/06_master_05_medium.webp", + min: 1600, + max: 5000, + rank: 50, + }, + CHALLENGER_4: { + name: "Challenger 4", + url: "/icons/ranks/06_master_04_medium.webp", + min: 1600, + max: 5000, + rank: 25, + }, + CHALLENGER_3: { + name: "Challenger 3", + url: "/icons/ranks/06_master_03_medium.webp", + min: 1600, + max: 5000, + rank: 10, + }, + CHALLENGER_2: { + name: "Challenger 2", + url: "/icons/ranks/06_master_02_medium.webp", + min: 1600, + max: 5000, + rank: 5, + }, + CHALLENGER_1: { + name: "Challenger 1", + url: "/icons/ranks/06_master_01_medium.webp", + min: 1600, + max: 5000, + rank: 1, + }, +}; diff --git a/src/utils/renameLabels.ts b/src/utils/renameLabels.ts new file mode 100644 index 0000000..8ea9e7e --- /dev/null +++ b/src/utils/renameLabels.ts @@ -0,0 +1,284 @@ +// Helper function to get the faction name from the faction code +export const getFactionName = (factionCode: string): string => { + const countryMapping: { [code: string]: string } = { + dak: "Deutsches Afrikakorps", + german: "Wehrmacht", + american: "US Forces", + british: "British Forces", + } + + return countryMapping[factionCode] || factionCode +} + +// Helper function to get the country name from the country code +export const getCountryName = (countryCode: string): string => { + const countryMapping: { [code: string]: string } = { + ac: "Ascension Island", + ad: "Andorra", + ae: "United Arab Emirates", + af: "Afghanistan", + ag: "Antigua and Barbuda", + ai: "Anguilla", + al: "Albania", + am: "Armenia", + ao: "Angola", + aq: "Antarctica", + ar: "Argentina", + as: "American Samoa", + at: "Austria", + au: "Australia", + aw: "Aruba", + ax: "Åland Islands", + az: "Azerbaijan", + ba: "Bosnia and Herzegovina", + bb: "Barbados", + bd: "Bangladesh", + be: "Belgium", + bf: "Burkina Faso", + bg: "Bulgaria", + bh: "Bahrain", + bi: "Burundi", + bj: "Benin", + bl: "Saint Barthélemy", + bm: "Bermuda", + bn: "Brunei", + bo: "Bolivia", + bq: "Caribbean Netherlands", + br: "Brazil", + bs: "Bahamas", + bt: "Bhutan", + bv: "Bouvet Island", + bw: "Botswana", + by: "Belarus", + bz: "Belize", + ca: "Canada", + cc: "Cocos Islands", + cd: "Democratic Republic of the Congo", + cf: "Central African Republic", + cg: "Republic of the Congo", + ch: "Switzerland", + ci: "Côte d'Ivoire", + ck: "Cook Islands", + cl: "Chile", + cm: "Cameroon", + cn: "China", + co: "Colombia", + cp: "Clipperton Island", + cr: "Costa Rica", + cu: "Cuba", + cv: "Cape Verde", + cw: "Curaçao", + cx: "Christmas Island", + cy: "Cyprus", + cz: "Czech Republic", + de: "Germany", + dg: "Diego Garcia", + dj: "Djibouti", + dk: "Denmark", + dm: "Dominica", + do: "Dominican Republic", + dz: "Algeria", + ea: "Ceuta and Melilla", + ec: "Ecuador", + ee: "Estonia", + eg: "Egypt", + eh: "Western Sahara", + er: "Eritrea", + "es-ct": "Catalonia", + "es-ga": "Galicia", + es: "Spain", + et: "Ethiopia", + eu: "European Union", + fi: "Finland", + fj: "Fiji", + fk: "Falkland Islands", + fm: "Micronesia", + fo: "Faroe Islands", + fr: "France", + ga: "Gabon", + "gb-eng": "England", + "gb-nir": "Northern Ireland", + "gb-sct": "Scotland", + "gb-wls": "Wales", + gb: "United Kingdom", + gd: "Grenada", + ge: "Georgia", + gf: "French Guiana", + gg: "Guernsey", + gh: "Ghana", + gi: "Gibraltar", + gl: "Greenland", + gm: "Gambia", + gn: "Guinea", + gp: "Guadeloupe", + gq: "Equatorial Guinea", + gr: "Greece", + gs: "South Georgia and the South Sandwich Islands", + gt: "Guatemala", + gu: "Guam", + gw: "Guinea-Bissau", + gy: "Guyana", + hk: "Hong Kong", + hm: "Heard Island and McDonald Islands", + hn: "Honduras", + hr: "Croatia", + ht: "Haiti", + hu: "Hungary", + ic: "Canary Islands", + id: "Indonesia", + ie: "Ireland", + il: "Israel", + im: "Isle of Man", + in: "India", + io: "British Indian Ocean Territory", + iq: "Iraq", + ir: "Iran", + is: "Iceland", + it: "Italy", + je: "Jersey", + jm: "Jamaica", + jo: "Jordan", + jp: "Japan", + ke: "Kenya", + kg: "Kyrgyzstan", + kh: "Cambodia", + ki: "Kiribati", + km: "Comoros", + kn: "Saint Kitts and Nevis", + kp: "North Korea", + kr: "South Korea", + kw: "Kuwait", + ky: "Cayman Islands", + kz: "Kazakhstan", + la: "Laos", + lb: "Lebanon", + lc: "Saint Lucia", + li: "Liechtenstein", + lk: "Sri Lanka", + lr: "Liberia", + ls: "Lesotho", + lt: "Lithuania", + lu: "Luxembourg", + lv: "Latvia", + ly: "Libya", + ma: "Morocco", + mc: "Monaco", + md: "Moldova", + me: "Montenegro", + mf: "Saint Martin", + mg: "Madagascar", + mh: "Marshall Islands", + mk: "North Macedonia", + ml: "Mali", + mm: "Myanmar", + mn: "Mongolia", + mo: "Macau", + mp: "Northern Mariana Islands", + mq: "Martinique", + mr: "Mauritania", + ms: "Montserrat", + mt: "Malta", + mu: "Mauritius", + mv: "Maldives", + mw: "Malawi", + mx: "Mexico", + my: "Malaysia", + mz: "Mozambique", + na: "Namibia", + nc: "New Caledonia", + ne: "Niger", + nf: "Norfolk Island", + ng: "Nigeria", + ni: "Nicaragua", + nl: "Netherlands", + no: "Norway", + np: "Nepal", + nr: "Nauru", + nu: "Niue", + nz: "New Zealand", + om: "Oman", + pa: "Panama", + pe: "Peru", + pf: "French Polynesia", + pg: "Papua New Guinea", + ph: "Philippines", + pk: "Pakistan", + pl: "Poland", + pm: "Saint Pierre and Miquelon", + pn: "Pitcairn Islands", + pr: "Puerto Rico", + ps: "Palestine", + pt: "Portugal", + pw: "Palau", + py: "Paraguay", + qa: "Qatar", + re: "Réunion", + ro: "Romania", + rs: "Serbia", + ru: "Russia", + rw: "Rwanda", + sa: "Saudi Arabia", + sb: "Solomon Islands", + sc: "Seychelles", + sd: "Sudan", + se: "Sweden", + sg: "Singapore", + sh: "Saint Helena", + si: "Slovenia", + sj: "Svalbard and Jan Mayen", + sk: "Slovakia", + sl: "Sierra Leone", + sm: "San Marino", + sn: "Senegal", + so: "Somalia", + sr: "Suriname", + ss: "South Sudan", + st: "São Tomé and Príncipe", + sv: "El Salvador", + sx: "Sint Maarten", + sy: "Syria", + sz: "Eswatini", + ta: "Tristan da Cunha", + tc: "Turks and Caicos Islands", + td: "Chad", + tf: "French Southern Territories", + tg: "Togo", + th: "Thailand", + tj: "Tajikistan", + tk: "Tokelau", + tl: "Timor-Leste", + tm: "Turkmenistan", + tn: "Tunisia", + to: "Tonga", + tr: "Turkey", + tt: "Trinidad and Tobago", + tv: "Tuvalu", + tw: "Taiwan", + tz: "Tanzania", + ua: "Ukraine", + ug: "Uganda", + um: "United States Minor Outlying Islands", + un: "United Nations", + us: "United States", + uy: "Uruguay", + uz: "Uzbekistan", + va: "Vatican City", + vc: "Saint Vincent and the Grenadines", + ve: "Venezuela", + vg: "British Virgin Islands", + vi: "U.S. Virgin Islands", + vn: "Vietnam", + vu: "Vanuatu", + wf: "Wallis and Futuna", + ws: "Samoa", + xk: "Kosovo", + xx: "Unknown or Invalid Region", + ye: "Yemen", + yt: "Mayotte", + za: "South Africa", + zm: "Zambia", + zw: "Zimbabwe", + } + + return countryMapping[countryCode] || countryCode +} diff --git a/src/utils/steam-api.ts b/src/utils/steam-api.ts new file mode 100644 index 0000000..698e32e --- /dev/null +++ b/src/utils/steam-api.ts @@ -0,0 +1,12 @@ +const baseUrl = "https://api.steampowered.com/" +const COH3_STEAM_APP_ID = 1677280 + +const getNumberOfOnlinePlayersSteamUrl = ( + appId: number | string = COH3_STEAM_APP_ID +) => { + return encodeURI( + `${baseUrl}ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=${appId}` + ) +} + +export { getNumberOfOnlinePlayersSteamUrl } diff --git a/src/utils/utils.ts b/src/utils/utils.ts new file mode 100644 index 0000000..bbc9bd8 --- /dev/null +++ b/src/utils/utils.ts @@ -0,0 +1,39 @@ +import {PlayerRanks} from "./coh3-data"; + +export const calculatePlayerTier = (rank: number, rating: number) => { + if (!rank || rank <= 0) { + return PlayerRanks.NO_RANK; + } + + if (rating < 1600) { + const playerRank = Object.values(PlayerRanks).find((x) => x.min <= rating && rating <= x.max); + + return playerRank || PlayerRanks.NO_RANK; + } + + // If rating is higher than 1600, take into account the rank. + if (rating >= 1600) { + // GOLD_1 is exception + if (rank > 50) { + return PlayerRanks.GOLD_1; + } + + // Create a sorted array of PlayerRanks entries + const sortedPlayerRanks = Object.entries(PlayerRanks).sort( + ([, rankInfoA], [, rankInfoB]) => rankInfoA.rank - rankInfoB.rank, + ); + + let rankKey = "CHALLENGER_5"; + + for (const [key, rankInfo] of sortedPlayerRanks) { + if (rank <= rankInfo.rank) { + rankKey = key; + break; + } + } + + return PlayerRanks[rankKey]; + } + + return PlayerRanks.NO_RANK; +}; diff --git a/yarn.lock b/yarn.lock index 1b27e1a..a053003 100644 --- a/yarn.lock +++ b/yarn.lock @@ -215,203 +215,203 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" -"@emotion/babel-plugin@^11.10.6": - version "11.10.6" - resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz#a68ee4b019d661d6f37dec4b8903255766925ead" - integrity sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ== +"@emotion/babel-plugin@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c" + integrity sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ== dependencies: "@babel/helper-module-imports" "^7.16.7" "@babel/runtime" "^7.18.3" - "@emotion/hash" "^0.9.0" - "@emotion/memoize" "^0.8.0" - "@emotion/serialize" "^1.1.1" + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/serialize" "^1.1.2" babel-plugin-macros "^3.1.0" convert-source-map "^1.5.0" escape-string-regexp "^4.0.0" find-root "^1.1.0" source-map "^0.5.7" - stylis "4.1.3" - -"@emotion/cache@^11.10.5": - version "11.10.5" - resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.5.tgz#c142da9351f94e47527ed458f7bbbbe40bb13c12" - integrity sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA== - dependencies: - "@emotion/memoize" "^0.8.0" - "@emotion/sheet" "^1.2.1" - "@emotion/utils" "^1.2.0" - "@emotion/weak-memoize" "^0.3.0" - stylis "4.1.3" - -"@emotion/hash@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7" - integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ== - -"@emotion/memoize@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f" - integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA== - -"@emotion/react@^11.10.6": - version "11.10.6" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.6.tgz#dbe5e650ab0f3b1d2e592e6ab1e006e75fd9ac11" - integrity sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw== + stylis "4.2.0" + +"@emotion/cache@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.11.0.tgz#809b33ee6b1cb1a625fef7a45bc568ccd9b8f3ff" + integrity sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ== + dependencies: + "@emotion/memoize" "^0.8.1" + "@emotion/sheet" "^1.2.2" + "@emotion/utils" "^1.2.1" + "@emotion/weak-memoize" "^0.3.1" + stylis "4.2.0" + +"@emotion/hash@^0.9.1": + version "0.9.1" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" + integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== + +"@emotion/memoize@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" + integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== + +"@emotion/react@^11.11.1": + version "11.11.1" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.1.tgz#b2c36afac95b184f73b08da8c214fdf861fa4157" + integrity sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA== dependencies: "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.10.6" - "@emotion/cache" "^11.10.5" - "@emotion/serialize" "^1.1.1" - "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" - "@emotion/utils" "^1.2.0" - "@emotion/weak-memoize" "^0.3.0" + "@emotion/babel-plugin" "^11.11.0" + "@emotion/cache" "^11.11.0" + "@emotion/serialize" "^1.1.2" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" + "@emotion/utils" "^1.2.1" + "@emotion/weak-memoize" "^0.3.1" hoist-non-react-statics "^3.3.1" -"@emotion/serialize@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.1.tgz#0595701b1902feded8a96d293b26be3f5c1a5cf0" - integrity sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA== +"@emotion/serialize@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.2.tgz#017a6e4c9b8a803bd576ff3d52a0ea6fa5a62b51" + integrity sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA== dependencies: - "@emotion/hash" "^0.9.0" - "@emotion/memoize" "^0.8.0" - "@emotion/unitless" "^0.8.0" - "@emotion/utils" "^1.2.0" + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/unitless" "^0.8.1" + "@emotion/utils" "^1.2.1" csstype "^3.0.2" -"@emotion/sheet@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c" - integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA== +"@emotion/sheet@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec" + integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== -"@emotion/unitless@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db" - integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw== +"@emotion/unitless@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" + integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== -"@emotion/use-insertion-effect-with-fallbacks@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df" - integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A== +"@emotion/use-insertion-effect-with-fallbacks@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz#08de79f54eb3406f9daaf77c76e35313da963963" + integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw== -"@emotion/utils@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561" - integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw== - -"@emotion/weak-memoize@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb" - integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg== - -"@esbuild/android-arm64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.4.tgz#4b31b9e3da2e4c12a8170bd682f713c775f68ab1" - integrity sha512-VPuTzXFm/m2fcGfN6CiwZTlLzxrKsWbPkG7ArRFpuxyaHUm/XFHQPD4xNwZT6uUmpIHhnSjcaCmcla8COzmZ5Q== - -"@esbuild/android-arm@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.4.tgz#057d3e8b0ee41ff59386c33ba6dcf20f4bedd1f7" - integrity sha512-rZzb7r22m20S1S7ufIc6DC6W659yxoOrl7sKP1nCYhuvUlnCFHVSbATG4keGUtV8rDz11sRRDbWkvQZpzPaHiw== - -"@esbuild/android-x64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.4.tgz#62ccab8ac1d3e6ef1df3fa2e1974bc2b8528d74a" - integrity sha512-MW+B2O++BkcOfMWmuHXB15/l1i7wXhJFqbJhp82IBOais8RBEQv2vQz/jHrDEHaY2X0QY7Wfw86SBL2PbVOr0g== - -"@esbuild/darwin-arm64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.4.tgz#c19a6489d626c36fc611c85ccd8a3333c1f2a930" - integrity sha512-a28X1O//aOfxwJVZVs7ZfM8Tyih2Za4nKJrBwW5Wm4yKsnwBy9aiS/xwpxiiTRttw3EaTg4Srerhcm6z0bu9Wg== - -"@esbuild/darwin-x64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.4.tgz#b726bbc84a1e277f6ec2509d10b8ee03f242b776" - integrity sha512-e3doCr6Ecfwd7VzlaQqEPrnbvvPjE9uoTpxG5pyLzr2rI2NMjDHmvY1E5EO81O/e9TUOLLkXA5m6T8lfjK9yAA== - -"@esbuild/freebsd-arm64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.4.tgz#364568e6ca2901297f247de0681c9b14bbe658c8" - integrity sha512-Oup3G/QxBgvvqnXWrBed7xxkFNwAwJVHZcklWyQt7YCAL5bfUkaa6FVWnR78rNQiM8MqqLiT6ZTZSdUFuVIg1w== - -"@esbuild/freebsd-x64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.4.tgz#44701ba4a5497ba64eec0a6c9e221d8f46a25e72" - integrity sha512-vAP+eYOxlN/Bpo/TZmzEQapNS8W1njECrqkTpNgvXskkkJC2AwOXwZWai/Kc2vEFZUXQttx6UJbj9grqjD/+9Q== - -"@esbuild/linux-arm64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.4.tgz#b58fb418ec9ac714d8dbb38c787ff2441eb1d9db" - integrity sha512-2zXoBhv4r5pZiyjBKrOdFP4CXOChxXiYD50LRUU+65DkdS5niPFHbboKZd/c81l0ezpw7AQnHeoCy5hFrzzs4g== - -"@esbuild/linux-arm@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.4.tgz#b37f15ecddb53eeea466e5960e31a58f33e0e87e" - integrity sha512-A47ZmtpIPyERxkSvIv+zLd6kNIOtJH03XA0Hy7jaceRDdQaQVGSDt4mZqpWqJYgDk9rg96aglbF6kCRvPGDSUA== - -"@esbuild/linux-ia32@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.4.tgz#117e32a9680b5deac184ebee122f8575369fad1b" - integrity sha512-uxdSrpe9wFhz4yBwt2kl2TxS/NWEINYBUFIxQtaEVtglm1eECvsj1vEKI0KX2k2wCe17zDdQ3v+jVxfwVfvvjw== - -"@esbuild/linux-loong64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.4.tgz#dd504fb83c280752d4b485d9acb3cf391cb7bf5b" - integrity sha512-peDrrUuxbZ9Jw+DwLCh/9xmZAk0p0K1iY5d2IcwmnN+B87xw7kujOkig6ZRcZqgrXgeRGurRHn0ENMAjjD5DEg== - -"@esbuild/linux-mips64el@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.4.tgz#9ab77e31cf3be1e35572afff94b51df8149d15bd" - integrity sha512-sD9EEUoGtVhFjjsauWjflZklTNr57KdQ6xfloO4yH1u7vNQlOfAlhEzbyBKfgbJlW7rwXYBdl5/NcZ+Mg2XhQA== - -"@esbuild/linux-ppc64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.4.tgz#69d56c2a960808bee1c7b9b84a115220ec9ce05c" - integrity sha512-X1HSqHUX9D+d0l6/nIh4ZZJ94eQky8d8z6yxAptpZE3FxCWYWvTDd9X9ST84MGZEJx04VYUD/AGgciddwO0b8g== - -"@esbuild/linux-riscv64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.4.tgz#9fc23583f4a1508a8d352bd376340e42217e8a90" - integrity sha512-97ANpzyNp0GTXCt6SRdIx1ngwncpkV/z453ZuxbnBROCJ5p/55UjhbaG23UdHj88fGWLKPFtMoU4CBacz4j9FA== - -"@esbuild/linux-s390x@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.4.tgz#4cae1f70ac2943f076dd130c3c80d28f57bf75d1" - integrity sha512-pUvPQLPmbEeJRPjP0DYTC1vjHyhrnCklQmCGYbipkep+oyfTn7GTBJXoPodR7ZS5upmEyc8lzAkn2o29wD786A== - -"@esbuild/linux-x64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.16.4.tgz#fdf494de07cda23a2dc4b71ff1e0848e4ee6539c" - integrity sha512-N55Q0mJs3Sl8+utPRPBrL6NLYZKBCLLx0bme/+RbjvMforTGGzFvsRl4xLTZMUBFC1poDzBEPTEu5nxizQ9Nlw== - -"@esbuild/netbsd-x64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.4.tgz#b59ecb49087119c575c0f64d7e66001d52799e24" - integrity sha512-LHSJLit8jCObEQNYkgsDYBh2JrJT53oJO2HVdkSYLa6+zuLJh0lAr06brXIkljrlI+N7NNW1IAXGn/6IZPi3YQ== - -"@esbuild/openbsd-x64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.4.tgz#c51e36db875948b7b11d08bafa355605a1aa289c" - integrity sha512-nLgdc6tWEhcCFg/WVFaUxHcPK3AP/bh+KEwKtl69Ay5IBqUwKDaq/6Xk0E+fh/FGjnLwqFSsarsbPHeKM8t8Sw== - -"@esbuild/sunos-x64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.4.tgz#0b50e941cd44f069e9f2573321aec984244ec228" - integrity sha512-08SluG24GjPO3tXKk95/85n9kpyZtXCVwURR2i4myhrOfi3jspClV0xQQ0W0PYWHioJj+LejFMt41q+PG3mlAQ== - -"@esbuild/win32-arm64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.4.tgz#d1c93b20f17355ab2221cd18e13ae2f1b68013e3" - integrity sha512-yYiRDQcqLYQSvNQcBKN7XogbrSvBE45FEQdH8fuXPl7cngzkCvpsG2H9Uey39IjQ6gqqc+Q4VXYHsQcKW0OMjQ== - -"@esbuild/win32-ia32@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.4.tgz#df5910e76660e0acbbdceb8d4ae6bf1efeade6ae" - integrity sha512-5rabnGIqexekYkh9zXG5waotq8mrdlRoBqAktjx2W3kb0zsI83mdCwrcAeKYirnUaTGztR5TxXcXmQrEzny83w== - -"@esbuild/win32-x64@0.16.4": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.4.tgz#6ec594468610c176933da1387c609558371d37e0" - integrity sha512-sN/I8FMPtmtT2Yw+Dly8Ur5vQ5a/RmC8hW7jO9PtPSQUPkowxWpcUZnqOggU7VwyT3Xkj6vcXWd3V/qTXwultQ== +"@emotion/utils@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.1.tgz#bbab58465738d31ae4cb3dbb6fc00a5991f755e4" + integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg== + +"@emotion/weak-memoize@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" + integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== + +"@esbuild/android-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" + integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA== + +"@esbuild/android-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d" + integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A== + +"@esbuild/android-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1" + integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww== + +"@esbuild/darwin-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276" + integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== + +"@esbuild/darwin-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb" + integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw== + +"@esbuild/freebsd-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2" + integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ== + +"@esbuild/freebsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4" + integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ== + +"@esbuild/linux-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb" + integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg== + +"@esbuild/linux-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a" + integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA== + +"@esbuild/linux-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a" + integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ== + +"@esbuild/linux-loong64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72" + integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ== + +"@esbuild/linux-mips64el@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289" + integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A== + +"@esbuild/linux-ppc64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7" + integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg== + +"@esbuild/linux-riscv64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09" + integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA== + +"@esbuild/linux-s390x@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829" + integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q== + +"@esbuild/linux-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4" + integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== + +"@esbuild/netbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462" + integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q== + +"@esbuild/openbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691" + integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g== + +"@esbuild/sunos-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273" + integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg== + +"@esbuild/win32-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f" + integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag== + +"@esbuild/win32-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03" + integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw== + +"@esbuild/win32-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" + integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== "@floating-ui/core@^1.2.1": version "1.2.1" @@ -481,35 +481,35 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@mantine/core@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@mantine/core/-/core-6.0.0.tgz#721bca646713b50dc2d7fa1e538fbd409526c33e" - integrity sha512-ik2NUAAn9fYcqmOAluGtI9R73ijrr450dZDA+MezKq/dvpUU/Fhl9yXnGoCxxZ5XF6y4i6q07318rdrVturc9w== +"@mantine/core@^6.0.15": + version "6.0.15" + resolved "https://registry.yarnpkg.com/@mantine/core/-/core-6.0.15.tgz#6956533f6163ef3b715dd84746e8096a973b6796" + integrity sha512-CN2UV2RXumxac75cWgUPMcHiE36T4ciIpFf20JwpazshnwFNu7scvy6GJDwUouf0zTBLnPMAD1S/B4mIRc3aQw== dependencies: "@floating-ui/react" "^0.19.1" - "@mantine/styles" "6.0.0" - "@mantine/utils" "6.0.0" + "@mantine/styles" "6.0.15" + "@mantine/utils" "6.0.15" "@radix-ui/react-scroll-area" "1.0.2" react-remove-scroll "^2.5.5" react-textarea-autosize "8.3.4" -"@mantine/hooks@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@mantine/hooks/-/hooks-6.0.0.tgz#08b67946e0b45f67181efa9e37df68f92a8ee6d1" - integrity sha512-boszkajLaA4qvd/ebDhqZBbMuUXlvJv8EM0jTaXz09IaGPachBKG5WKpXEcwWh2qmrUQL6pyhIbLMgPnvwS0QQ== +"@mantine/hooks@^6.0.15": + version "6.0.15" + resolved "https://registry.yarnpkg.com/@mantine/hooks/-/hooks-6.0.15.tgz#f0f95bdad5e993f12a9f0b543b2cfc1b4e85f896" + integrity sha512-2CtNKw/tdiXjeseldrg1J2jy+WKrwiCY/J6UMkZqlZ8aM0j3vFVl5cnyn46i5KzbdGqNjW01aihfJJfkeQh4oQ== -"@mantine/styles@6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@mantine/styles/-/styles-6.0.0.tgz#71b0b7d9c1885070543206b754cac82cbe763fda" - integrity sha512-TyqFvdKIhbhnGYBDEJ9QIPit4NzyzQ3ivDfdzeqzd/cJBxFPhxB0sEFU8RppXpXBUlbhLFhulYFEVl2pP6zaeg== +"@mantine/styles@6.0.15": + version "6.0.15" + resolved "https://registry.yarnpkg.com/@mantine/styles/-/styles-6.0.15.tgz#474721fb5acab6f7ac9aed2d52b77e9571708ce5" + integrity sha512-lOcEshBVbaN55gqsaiRPDY3///gfE+0o13ePp5PbCIA1sTKCvHz9aojHxXIICQV0ua9CXyleHzn5G0Ypw21/NQ== dependencies: clsx "1.1.1" csstype "3.0.9" -"@mantine/utils@6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@mantine/utils/-/utils-6.0.0.tgz#26b6b89e27a77340d571a1c41a879a8344417742" - integrity sha512-1AalSgzINKP4uv1DBTkJe/jh6yGwC2xaCQE4Atlr2bSHiLezYFMy/deGQ8XLFFv2AL0sjvewLW4ernlFujGMZg== +"@mantine/utils@6.0.15": + version "6.0.15" + resolved "https://registry.yarnpkg.com/@mantine/utils/-/utils-6.0.15.tgz#d4144221f9406aece12b979bed5888da50d86771" + integrity sha512-iVaobFQTCQWG6SRi3im0/nONKCtGRPobG7bXn9GiIT96E4t7uTPglQpo/ZktDrF1XCL8CO/HoQmks4A2iXuMFw== "@radix-ui/number@1.0.0": version "1.0.0" @@ -601,23 +601,23 @@ dependencies: "@babel/runtime" "^7.13.10" -"@remix-run/router@1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.0.5.tgz#d5c65626add4c3c185a89aa5bd38b1e42daec075" - integrity sha512-my0Mycd+jruq/1lQuO5LBB6WTlL/e8DTCYWp44DfMTDcXz8DcTlgF0ISaLsGewt+ctHN+yA8xMq3q/N7uWJPug== +"@remix-run/router@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.0.tgz#550a8d5760b78efc5d60f62b5829b0f74c1fde81" + integrity sha512-Eu1V3kz3mV0wUpVTiFHuaT8UD1gj/0VnoFHQYX35xlslQUpe8CuYoKFn9d4WZFHm3yDywz6ALZuGdnUPKrNeAw== -"@tabler/icons-react@^2.7.0": - version "2.7.0" - resolved "https://registry.yarnpkg.com/@tabler/icons-react/-/icons-react-2.7.0.tgz#365e69175dd7432d57ae228ded9f69613649ef27" - integrity sha512-WV2ZwMUuZN9QcCX4RPg9+G+ycUgoItzOWVCCQYVWbYKV581fH8szM2KVaQHQhMbttL4JYtOgf4ColJIDwsIgdw== +"@tabler/icons-react@^2.23.0": + version "2.23.0" + resolved "https://registry.yarnpkg.com/@tabler/icons-react/-/icons-react-2.23.0.tgz#c342ee171f289d8a18b6cd972bdc38181373bf28" + integrity sha512-+H4mC1EZVCzCRhnPwZEVTI0veVCJuAKlopeCnRlfsYcmzgJm6Ye234c4A2qrLPQoi1Y29uN9+kqCyuYW007jPg== dependencies: - "@tabler/icons" "2.7.0" + "@tabler/icons" "2.23.0" prop-types "^15.7.2" -"@tabler/icons@2.7.0": - version "2.7.0" - resolved "https://registry.yarnpkg.com/@tabler/icons/-/icons-2.7.0.tgz#565439df8dcbcedd6f75e555c513b3500f12239b" - integrity sha512-mSjUKhwN5fJHI9yITqFn9GXGnsJ4gBAyQDWyP4Ev5fk6hD78G7u6cHEF1KARr5FOOdiIKSZWCzNzUzyLhiNPrg== +"@tabler/icons@2.23.0": + version "2.23.0" + resolved "https://registry.yarnpkg.com/@tabler/icons/-/icons-2.23.0.tgz#8741d0bb6bd40696266489c3eb59f4c0c8c92f8d" + integrity sha512-dU54aBwaxG0H+jQ4BdrqtYFN5L7PZevvlnzyL6XeOZgfDS3+sVNCtuG3JmpTEqQSwGLYC1IEwogPGA/Iit2bOA== "@tabler/icons@^1.117.0": version "1.117.0" @@ -704,14 +704,14 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== -"@types/react-dom@^18.0.9": - version "18.0.9" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.9.tgz#ffee5e4bfc2a2f8774b15496474f8e7fe8d0b504" - integrity sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg== +"@types/react-dom@^18.2.6": + version "18.2.6" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.6.tgz#ad621fa71a8db29af7c31b41b2ea3d8a6f4144d1" + integrity sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A== dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.0.26": +"@types/react@*": version "18.0.26" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917" integrity sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug== @@ -720,6 +720,15 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/react@^18.2.14": + version "18.2.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.14.tgz#fa7a6fecf1ce35ca94e74874f70c56ce88f7a127" + integrity sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/scheduler@*": version "0.16.2" resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" @@ -755,10 +764,10 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -axios@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" - integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ== +axios@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -891,33 +900,33 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -esbuild@^0.16.3: - version "0.16.4" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.4.tgz#06c86298d233386f5e41bcc14d36086daf3f40bd" - integrity sha512-qQrPMQpPTWf8jHugLWHoGqZjApyx3OEm76dlTXobHwh/EBbavbRdjXdYi/GWr43GyN0sfpap14GPkb05NH3ROA== +esbuild@^0.17.5: + version "0.17.19" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.19.tgz#087a727e98299f0462a3d0bcdd9cd7ff100bd955" + integrity sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw== optionalDependencies: - "@esbuild/android-arm" "0.16.4" - "@esbuild/android-arm64" "0.16.4" - "@esbuild/android-x64" "0.16.4" - "@esbuild/darwin-arm64" "0.16.4" - "@esbuild/darwin-x64" "0.16.4" - "@esbuild/freebsd-arm64" "0.16.4" - "@esbuild/freebsd-x64" "0.16.4" - "@esbuild/linux-arm" "0.16.4" - "@esbuild/linux-arm64" "0.16.4" - "@esbuild/linux-ia32" "0.16.4" - "@esbuild/linux-loong64" "0.16.4" - "@esbuild/linux-mips64el" "0.16.4" - "@esbuild/linux-ppc64" "0.16.4" - "@esbuild/linux-riscv64" "0.16.4" - "@esbuild/linux-s390x" "0.16.4" - "@esbuild/linux-x64" "0.16.4" - "@esbuild/netbsd-x64" "0.16.4" - "@esbuild/openbsd-x64" "0.16.4" - "@esbuild/sunos-x64" "0.16.4" - "@esbuild/win32-arm64" "0.16.4" - "@esbuild/win32-ia32" "0.16.4" - "@esbuild/win32-x64" "0.16.4" + "@esbuild/android-arm" "0.17.19" + "@esbuild/android-arm64" "0.17.19" + "@esbuild/android-x64" "0.17.19" + "@esbuild/darwin-arm64" "0.17.19" + "@esbuild/darwin-x64" "0.17.19" + "@esbuild/freebsd-arm64" "0.17.19" + "@esbuild/freebsd-x64" "0.17.19" + "@esbuild/linux-arm" "0.17.19" + "@esbuild/linux-arm64" "0.17.19" + "@esbuild/linux-ia32" "0.17.19" + "@esbuild/linux-loong64" "0.17.19" + "@esbuild/linux-mips64el" "0.17.19" + "@esbuild/linux-ppc64" "0.17.19" + "@esbuild/linux-riscv64" "0.17.19" + "@esbuild/linux-s390x" "0.17.19" + "@esbuild/linux-x64" "0.17.19" + "@esbuild/netbsd-x64" "0.17.19" + "@esbuild/openbsd-x64" "0.17.19" + "@esbuild/sunos-x64" "0.17.19" + "@esbuild/win32-arm64" "0.17.19" + "@esbuild/win32-ia32" "0.17.19" + "@esbuild/win32-x64" "0.17.19" escalade@^3.1.1: version "3.1.1" @@ -1075,20 +1084,20 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" -mixpanel-browser@^2.45.0: - version "2.45.0" - resolved "https://registry.yarnpkg.com/mixpanel-browser/-/mixpanel-browser-2.45.0.tgz#a2973be5b66e6d2799ef95a1e55b8ad8eede57e8" - integrity sha512-PQ1DaTk68yyYtLA0iejmzPA9iNDhT4uIZpqZjRTw7HWpYfl123fydHb2laKanaKjm8YDmrGGz3+xZ4Q6joogyg== +mixpanel-browser@^2.47.0: + version "2.47.0" + resolved "https://registry.yarnpkg.com/mixpanel-browser/-/mixpanel-browser-2.47.0.tgz#4e7fd3bb660c6f63443efbd169d1cd327db71ed4" + integrity sha512-Ldrva0fRBEIFWmEibBQO1PulfpJVF3pf28Guk09lDirDaSQqqU/xs9zQLwN2rL5VwVtsP1aD3JaCgaa98EjojQ== ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -nanoid@^3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" - integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== node-releases@^2.0.6: version "2.0.6" @@ -1132,19 +1141,19 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -postcss@^8.4.20: - version "8.4.20" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.20.tgz#64c52f509644cecad8567e949f4081d98349dc56" - integrity sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g== +postcss@^8.4.23: + version "8.4.24" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df" + integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg== dependencies: - nanoid "^3.3.4" + nanoid "^3.3.6" picocolors "^1.0.0" source-map-js "^1.0.2" -prettier@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.1.tgz#4e1fd11c34e2421bc1da9aea9bd8127cd0a35efc" - integrity sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg== +prettier@^2.8.8: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== prop-types@^15.7.2: version "15.8.1" @@ -1197,20 +1206,20 @@ react-remove-scroll@^2.5.5: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" -react-router-dom@^6.4.5: - version "6.4.5" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.4.5.tgz#4fdb12efef4f3848c693a76afbeaed1f6ca02047" - integrity sha512-a7HsgikBR0wNfroBHcZUCd9+mLRqZS8R5U1Z1mzLWxFXEkUT3vR1XXmSIVoVpxVX8Bar0nQYYYc9Yipq8dWwAA== +react-router-dom@^6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.14.0.tgz#7ce6c3f73549e4d40216ba150253d3bf30812b33" + integrity sha512-YEwlApKwzMMMbGbhh+Q7MsloTldcwMgHxUY/1g0uA62+B1hZo2jsybCWIDCL8zvIDB1FA0pBKY9chHbZHt+2dQ== dependencies: - "@remix-run/router" "1.0.5" - react-router "6.4.5" + "@remix-run/router" "1.7.0" + react-router "6.14.0" -react-router@6.4.5: - version "6.4.5" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.4.5.tgz#73f382af2c8b9a86d74e761a7c5fc3ce7cb0024d" - integrity sha512-1RQJ8bM70YEumHIlNUYc6mFfUDoWa5EgPDenK/fq0bxD8DYpQUi/S6Zoft+9DBrh2xmtg92N5HMAJgGWDhKJ5Q== +react-router@6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.0.tgz#1c3e8e922d934d43a253fd862c72c82167c0a7f1" + integrity sha512-OD+vkrcGbvlwkspUFDgMzsu1RXwdjNh83YgG/28lBnDzgslhCgxIqoExLlxsfTpIygp7fc+Hd3esloNwzkm2xA== dependencies: - "@remix-run/router" "1.0.5" + "@remix-run/router" "1.7.0" react-style-singleton@^2.2.1: version "2.2.1" @@ -1247,7 +1256,7 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.19.0, resolve@^1.22.1: +resolve@^1.19.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -1256,10 +1265,10 @@ resolve@^1.19.0, resolve@^1.22.1: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -rollup@^3.7.0: - version "3.7.3" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.7.3.tgz#d440adff5f89099fd1f552e4e4333045e3bc71d4" - integrity sha512-7e68MQbAWCX6mI4/0lG1WHd+NdNAlVamg0Zkd+8LZ/oXojligdGnCNyHlzXqXCZObyjs5FRc3AH0b17iJESGIQ== +rollup@^3.21.0: + version "3.25.3" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.25.3.tgz#f9a8986f0f244bcfde2208da91ba46b8fd252551" + integrity sha512-ZT279hx8gszBj9uy5FfhoG4bZx8c+0A1sbqtr7Q3KNWIizpTdDEPZbV2xcbvHsnFp4MavCQYZyzApJ+virB8Yw== optionalDependencies: fsevents "~2.3.2" @@ -1285,10 +1294,10 @@ source-map@^0.5.7: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -stylis@4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7" - integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA== +stylis@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" + integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== supports-color@^5.3.0: version "5.5.0" @@ -1340,10 +1349,10 @@ tslib@^2.1.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== -typescript@^4.9.3: - version "4.9.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" - integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== +typescript@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== update-browserslist-db@^1.0.9: version "1.0.10" @@ -1385,15 +1394,14 @@ use-sidecar@^1.1.2: detect-node-es "^1.1.0" tslib "^2.0.0" -vite@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/vite/-/vite-4.0.1.tgz#e0a54d818c28ae47fd27bcac6a4a952c6a658502" - integrity sha512-kZQPzbDau35iWOhy3CpkrRC7It+HIHtulAzBhMqzGHKRf/4+vmh8rPDDdv98SWQrFWo6//3ozwsRmwQIPZsK9g== +vite@^4.3.9: + version "4.3.9" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.9.tgz#db896200c0b1aa13b37cdc35c9e99ee2fdd5f96d" + integrity sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg== dependencies: - esbuild "^0.16.3" - postcss "^8.4.20" - resolve "^1.22.1" - rollup "^3.7.0" + esbuild "^0.17.5" + postcss "^8.4.23" + rollup "^3.21.0" optionalDependencies: fsevents "~2.3.2"