Compare commits
58 Commits
3.0.7
..
8b4fb07f21
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b4fb07f21 | |||
| 3b658fd8de | |||
| 2329122ef0 | |||
| 4348c36fa0 | |||
| 8c09719b43 | |||
| 03b352bc15 | |||
| 0f5eab873f | |||
| 75d444b1d5 | |||
| 779ff0c009 | |||
| dc88e9f2cc | |||
| bf8a458b5f | |||
| 3570582c6b | |||
| b9bd87c099 | |||
| 705e47a1b8 | |||
| 397c2255c1 | |||
| 00d71704ab | |||
| f631c78805 | |||
| 8d61a8b2b7 | |||
| 61d5182854 | |||
| cef085c13d | |||
| a02737ef1b | |||
| ae566f09a0 | |||
| 01905d695a | |||
| 94fa7e7bd8 | |||
| d5b2b8012c | |||
| 05866946a3 | |||
| 0fc9cb828b | |||
| e119166c65 | |||
| 6aa0a6c969 | |||
| 6369c0e8af | |||
| f11e84efd7 | |||
| d9fec1193e | |||
| 2c6b51cbb2 | |||
| 756ae1be58 | |||
| 4569d35a65 | |||
| 1adb1b7dec | |||
| ad2c91db4d | |||
| 3588203527 | |||
| dfa933fb87 | |||
| 2da2c9fa7e | |||
| 145f36f39b | |||
| 67cf564b01 | |||
| 4bd130aad9 | |||
| d0937f626d | |||
| 05b9ddd5a1 | |||
| 6790fe5ef6 | |||
| 1f1dc65a97 | |||
| bbc3d7ed67 | |||
| 917edf0eb3 | |||
| cfeda18b83 | |||
| 0eb42c138b | |||
| a4faa9dd1a | |||
| 800a424b67 | |||
| 873aeaf921 | |||
| 310c206367 | |||
| 895b71ef16 | |||
| e8f0027424 | |||
| c6fa540575 |
@@ -1,10 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Lock files
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Svelte Distribution
|
||||||
|
public/build/
|
||||||
|
|
||||||
|
# Electron Distribution
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Project Build Automation Directory
|
||||||
|
private
|
||||||
|
|
||||||
|
# Desktop Services Store on macOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
|
||||||
/build
|
|
||||||
/.svelte-kit
|
|
||||||
/package
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
vite.config.js.timestamp-*
|
|
||||||
vite.config.ts.timestamp-*
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
# Package Managers
|
|
||||||
package-lock.json
|
|
||||||
pnpm-lock.yaml
|
|
||||||
yarn.lock
|
|
||||||
bun.lock
|
|
||||||
bun.lockb
|
|
||||||
|
|
||||||
# Miscellaneous
|
|
||||||
/static/
|
|
||||||
|
|
||||||
# Tauri
|
|
||||||
/src-tauri/
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"bracketSameLine": false,
|
|
||||||
"insertPragma": false,
|
|
||||||
"arrowParens": "always",
|
|
||||||
"semi": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "es5",
|
|
||||||
"printWidth": 100,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"useTabs": false,
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"plugins": ["prettier-plugin-svelte"],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": "*.svelte",
|
|
||||||
"options": {
|
|
||||||
"parser": "svelte"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": "*.{css,pcss}",
|
|
||||||
"options": {
|
|
||||||
"tabWidth": 2,
|
|
||||||
"useTabs": false,
|
|
||||||
"singleQuote": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,3 @@
|
|||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": ["svelte.svelte-vscode"]
|
||||||
"svelte.svelte-vscode",
|
|
||||||
"tauri-apps.tauri-vscode",
|
|
||||||
"rust-lang.rust-analyzer",
|
|
||||||
"esbenp.prettier-vscode",
|
|
||||||
"fill-labs.dependi",
|
|
||||||
"mylesmurphy.prettify-ts",
|
|
||||||
"edwinhuish.better-comments-next",
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"bradlc.vscode-tailwindcss"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"svelte.enable-ts-plugin": true,
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
||||||
"[rust]": {
|
|
||||||
"editor.defaultFormatter": "rust-lang.rust-analyzer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Souleh Shaikh
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -1,673 +0,0 @@
|
|||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
Version 3, 29 June 2007
|
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
||||||
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) 2025 HorizonCode
|
|
||||||
|
|
||||||
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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
EZPPLauncher Copyright (C) 2025 HorizonCode
|
|
||||||
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
|
|
||||||
<https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
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
|
|
||||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
||||||
@@ -1,74 +1,26 @@
|
|||||||
# EZPPLauncher
|
# EZPPLauncher
|
||||||
|
|
||||||
**EZPPLauncher** is a custom launcher for the private osu! server [EZPPFarm](https://ez-pp.farm), built with **Rust** and **Svelte** using the [Tauri](https://tauri.app) framework.
|
Welcome to the EZPPLauncher! A new way to connect to the EZPPFarm server.
|
||||||
It enhances the osu! experience with quality-of-life features and integration specifically designed for the EZPPFarm community.
|
|
||||||
|
|
||||||
---
|
Just one click and you are ready to go!
|
||||||
|
|
||||||
## 🖼️ Preview
|
## Installation
|
||||||
|
|
||||||

|
The Launcher is a "plug and play thing", download it, place it on the desktop and execute!
|
||||||
|
|
||||||
---
|
## Features
|
||||||
|
|
||||||
## 🚀 Features
|
- Automatic osu! client updating before Launch
|
||||||
|
- Custom osu! Logo in MainMenu
|
||||||
|
- Account saving
|
||||||
|
|
||||||
- [x] Automatically updates osu! before launching
|
## Build from source
|
||||||
- [x] Automatic ingame login
|
|
||||||
- [x] Patches the osu! client to show misses in Relax and Autopilot
|
|
||||||
- [x] Displays your EZPPFarm stats in the launcher
|
|
||||||
- [x] Shows the number of imported beatmapsets
|
|
||||||
- [x] Shows the number of imported skins
|
|
||||||
- [x] Displays ping to the EZPPFarm server
|
|
||||||
- [x] Discord Rich Presence
|
|
||||||
- [x] Performance Display Overlay (currently in experimental patcher release stream)
|
|
||||||
|
|
||||||
---
|
- clone repo
|
||||||
|
- cd into the repo
|
||||||
|
- use `npm i` to install all dependencies
|
||||||
|
- use the buildscript `electron-pack` to build a executeable
|
||||||
|
|
||||||
## 💻 Supported Platforms
|
## License
|
||||||
|
|
||||||
| Platform | Status |
|
[AGPL-3.0](https://choosealicense.com/licenses/agpl-3.0/)
|
||||||
| -------- | ---------------------------------------- |
|
|
||||||
| Windows | ✅ Supported |
|
|
||||||
| macOS | ❌ Not supported |
|
|
||||||
| Linux | 🕧 Partially supported (via osu-winello) |
|
|
||||||
|
|
||||||
> Currently, only **Windows** is fully supported. Support for other platforms may be considered in the future.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛠 Build From Source
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
- **Rust** (installed via [rustup](https://rustup.rs/) – recommended for Windows)
|
|
||||||
- **Cargo** (comes with rustup)
|
|
||||||
- **[Bun](https://bun.sh/)** (JavaScript runtime)
|
|
||||||
|
|
||||||
### Steps
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install all dependencies
|
|
||||||
bun i
|
|
||||||
|
|
||||||
# Build the project
|
|
||||||
bun run build
|
|
||||||
```
|
|
||||||
|
|
||||||
After building, the launcher executable will be located at:
|
|
||||||
|
|
||||||
```
|
|
||||||
src-tauri/target/release/ezpplauncher.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📁 Project Structure (Simplified)
|
|
||||||
|
|
||||||
```
|
|
||||||
├── src/ # Frontend (Svelte)
|
|
||||||
├── src-tauri/ # Backend (Rust)
|
|
||||||
│ ├── tauri.conf.json # Tauri configuration
|
|
||||||
├── package.json # Bun project config
|
|
||||||
└── README.md
|
|
||||||
```
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 81 KiB |
@@ -1,869 +0,0 @@
|
|||||||
{
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"workspaces": {
|
|
||||||
"": {
|
|
||||||
"name": "ezpplauncher",
|
|
||||||
"dependencies": {
|
|
||||||
"@better-fetch/fetch": "^1.1.18",
|
|
||||||
"@fontsource/sora": "^5.2.6",
|
|
||||||
"@fontsource/space-mono": "^5.2.8",
|
|
||||||
"@number-flow/svelte": "^0.3.9",
|
|
||||||
"@tailwindcss/typography": "^0.5.16",
|
|
||||||
"@tailwindcss/vite": "^4.1.11",
|
|
||||||
"@tauri-apps/api": "^2.8.0",
|
|
||||||
"@tauri-apps/plugin-dialog": "^2.4.0",
|
|
||||||
"@tauri-apps/plugin-fs": "^2.4.2",
|
|
||||||
"@tauri-apps/plugin-shell": "^2.3.1",
|
|
||||||
"@types/umami": "^2.10.1",
|
|
||||||
"animejs": "^4.1.3",
|
|
||||||
"buffer": "^6.0.3",
|
|
||||||
"crypto-js": "^4.2.0",
|
|
||||||
"lucide-svelte": "^0.542.0",
|
|
||||||
"semver": "^7.7.2",
|
|
||||||
"svelte-confetti": "^2.3.2",
|
|
||||||
"tw-animate-css": "^1.3.8",
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@eslint/compat": "^1.3.2",
|
|
||||||
"@eslint/js": "^9.35.0",
|
|
||||||
"@iarna/toml": "2.2.5",
|
|
||||||
"@lucide/svelte": "^0.542.0",
|
|
||||||
"@sveltejs/adapter-static": "^3.0.9",
|
|
||||||
"@sveltejs/kit": "^2.37.1",
|
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.1.4",
|
|
||||||
"@tauri-apps/cli": "^2.8.4",
|
|
||||||
"@types/bun": "^1.2.21",
|
|
||||||
"@types/crypto-js": "^4.2.2",
|
|
||||||
"@types/semver": "^7.7.1",
|
|
||||||
"autoprefixer": "10.4.21",
|
|
||||||
"bits-ui": "^2.9.6",
|
|
||||||
"clsx": "^2.1.1",
|
|
||||||
"eslint": "^9.35.0",
|
|
||||||
"eslint-config-prettier": "^10.1.8",
|
|
||||||
"eslint-plugin-svelte": "^3.12.2",
|
|
||||||
"globals": "^16.3.0",
|
|
||||||
"mode-watcher": "^1.1.0",
|
|
||||||
"prettier": "^3.6.2",
|
|
||||||
"prettier-plugin-svelte": "^3.4.0",
|
|
||||||
"sass-embedded": "^1.92.1",
|
|
||||||
"svelte": "^5.38.7",
|
|
||||||
"svelte-check": "^4.3.1",
|
|
||||||
"svelte-sonner": "^1.0.5",
|
|
||||||
"tailwind-merge": "^3.3.1",
|
|
||||||
"tailwind-variants": "^3.1.0",
|
|
||||||
"tailwindcss": "^4.1.7",
|
|
||||||
"tailwindcss-animate": "^1.0.7",
|
|
||||||
"tslib": "^2.8.1",
|
|
||||||
"typescript": "^5.9.2",
|
|
||||||
"typescript-eslint": "^8.42.0",
|
|
||||||
"vite": "^7.1.5",
|
|
||||||
"vite-plugin-devtools-json": "^0.4.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
|
||||||
|
|
||||||
"@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="],
|
|
||||||
|
|
||||||
"@bufbuild/protobuf": ["@bufbuild/protobuf@2.5.2", "", {}, "sha512-foZ7qr0IsUBjzWIq+SuBLfdQCpJ1j8cTuNNT4owngTHoN5KsJb8L9t65fzz7SCeSWzescoOil/0ldqiL041ABg=="],
|
|
||||||
|
|
||||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA=="],
|
|
||||||
|
|
||||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.5", "", { "os": "android", "cpu": "arm" }, "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA=="],
|
|
||||||
|
|
||||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.5", "", { "os": "android", "cpu": "arm64" }, "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg=="],
|
|
||||||
|
|
||||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.5", "", { "os": "android", "cpu": "x64" }, "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw=="],
|
|
||||||
|
|
||||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ=="],
|
|
||||||
|
|
||||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ=="],
|
|
||||||
|
|
||||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw=="],
|
|
||||||
|
|
||||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.5", "", { "os": "linux", "cpu": "arm" }, "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ=="],
|
|
||||||
|
|
||||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.5", "", { "os": "linux", "cpu": "x64" }, "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw=="],
|
|
||||||
|
|
||||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.5", "", { "os": "none", "cpu": "arm64" }, "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw=="],
|
|
||||||
|
|
||||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.5", "", { "os": "none", "cpu": "x64" }, "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ=="],
|
|
||||||
|
|
||||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.5", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw=="],
|
|
||||||
|
|
||||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg=="],
|
|
||||||
|
|
||||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA=="],
|
|
||||||
|
|
||||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw=="],
|
|
||||||
|
|
||||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ=="],
|
|
||||||
|
|
||||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g=="],
|
|
||||||
|
|
||||||
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="],
|
|
||||||
|
|
||||||
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
|
|
||||||
|
|
||||||
"@eslint/compat": ["@eslint/compat@1.3.2", "", { "peerDependencies": { "eslint": "^8.40 || 9" }, "optionalPeers": ["eslint"] }, "sha512-jRNwzTbd6p2Rw4sZ1CgWRS8YMtqG15YyZf7zvb6gY2rB2u6n+2Z+ELW0GtL0fQgyl0pr4Y/BzBfng/BdsereRA=="],
|
|
||||||
|
|
||||||
"@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="],
|
|
||||||
|
|
||||||
"@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="],
|
|
||||||
|
|
||||||
"@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="],
|
|
||||||
|
|
||||||
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
|
|
||||||
|
|
||||||
"@eslint/js": ["@eslint/js@9.35.0", "", {}, "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw=="],
|
|
||||||
|
|
||||||
"@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
|
|
||||||
|
|
||||||
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="],
|
|
||||||
|
|
||||||
"@floating-ui/core": ["@floating-ui/core@1.7.1", "", { "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw=="],
|
|
||||||
|
|
||||||
"@floating-ui/dom": ["@floating-ui/dom@1.7.1", "", { "dependencies": { "@floating-ui/core": "^1.7.1", "@floating-ui/utils": "^0.2.9" } }, "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ=="],
|
|
||||||
|
|
||||||
"@floating-ui/utils": ["@floating-ui/utils@0.2.9", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="],
|
|
||||||
|
|
||||||
"@fontsource/sora": ["@fontsource/sora@5.2.6", "", {}, "sha512-OPknOmn82HIJg32UGg9iEjEVFdC+xOZBqrmQNUgsem6hFwBBrqXnTwIpro7leq/uFRdupkN42hc0JJKH20j22w=="],
|
|
||||||
|
|
||||||
"@fontsource/space-mono": ["@fontsource/space-mono@5.2.8", "", {}, "sha512-25X0fg1+kxxLes9/c6AozhrrAuBW1uLaOL584IRg4DZxVxNOMzcS3mHS1UtmsyTwtU2HpiIt9Kv23Q+7xQO+hg=="],
|
|
||||||
|
|
||||||
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
|
||||||
|
|
||||||
"@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
|
|
||||||
|
|
||||||
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
|
|
||||||
|
|
||||||
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
|
||||||
|
|
||||||
"@iarna/toml": ["@iarna/toml@2.2.5", "", {}, "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="],
|
|
||||||
|
|
||||||
"@internationalized/date": ["@internationalized/date@3.8.2", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA=="],
|
|
||||||
|
|
||||||
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
|
|
||||||
|
|
||||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
|
|
||||||
|
|
||||||
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
|
||||||
|
|
||||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
|
||||||
|
|
||||||
"@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
|
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
|
|
||||||
|
|
||||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
|
||||||
|
|
||||||
"@lucide/svelte": ["@lucide/svelte@0.542.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-NuWttxTVfMSURpOxcKiKvoCtma3JtEpcJWzF/0cO69saZfXlv6G8NYAvEEGLmk75YPl+I+ROe+F97WhddM8r2A=="],
|
|
||||||
|
|
||||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
|
||||||
|
|
||||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
|
||||||
|
|
||||||
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
|
||||||
|
|
||||||
"@number-flow/svelte": ["@number-flow/svelte@0.3.9", "", { "dependencies": { "esm-env": "^1.1.4", "number-flow": "0.5.8" }, "peerDependencies": { "svelte": "^4 || ^5" } }, "sha512-CTw1+e0074GzbPX2IHcNCaK8nqxGNCOIUnQUjEjhcmBwBxOAhN3GYLQ6cJHvhQnWwplVe4eQ3z+c25Vttr2stQ=="],
|
|
||||||
|
|
||||||
"@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="],
|
|
||||||
|
|
||||||
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
|
|
||||||
|
|
||||||
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.1", "", { "os": "android", "cpu": "arm" }, "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.1", "", { "os": "android", "cpu": "arm64" }, "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.44.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.44.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.44.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.44.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.44.1", "", { "os": "linux", "cpu": "arm" }, "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.44.1", "", { "os": "linux", "cpu": "arm" }, "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.44.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.44.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.44.1", "", { "os": "linux", "cpu": "none" }, "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.44.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.44.1", "", { "os": "linux", "cpu": "none" }, "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.44.1", "", { "os": "linux", "cpu": "none" }, "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.44.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.44.1", "", { "os": "linux", "cpu": "x64" }, "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.44.1", "", { "os": "linux", "cpu": "x64" }, "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.44.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.44.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A=="],
|
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.44.1", "", { "os": "win32", "cpu": "x64" }, "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug=="],
|
|
||||||
|
|
||||||
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
|
|
||||||
|
|
||||||
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="],
|
|
||||||
|
|
||||||
"@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.9", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-aytHXcMi7lb9ljsWUzXYQ0p5X1z9oWud2olu/EpmH7aCu4m84h7QLvb5Wp+CFirKcwoNnYvYWhyP/L8Vh1ztdw=="],
|
|
||||||
|
|
||||||
"@sveltejs/kit": ["@sveltejs/kit@2.37.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-4T9rF2Roe7RGvHfcn6+n92Yc2NF88k7ljFz9+wE0jWxyencqRpadr2/CvlcQbbTXf1ozmFxgMO6af+qm+1mPFw=="],
|
|
||||||
|
|
||||||
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.1.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", "deepmerge": "^4.3.1", "magic-string": "^0.30.17", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-4jfkfvsGI+U2OhHX8OPCKtMCf7g7ledXhs3E6UcA4EY0jQWsiVbe83pTAHp9XTifzYNOiD4AJieJUsI0qqxsbw=="],
|
|
||||||
|
|
||||||
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.0", "", { "dependencies": { "debug": "^4.4.1" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ=="],
|
|
||||||
|
|
||||||
"@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="],
|
|
||||||
|
|
||||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.11", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.11", "@tailwindcss/oxide-darwin-arm64": "4.1.11", "@tailwindcss/oxide-darwin-x64": "4.1.11", "@tailwindcss/oxide-freebsd-x64": "4.1.11", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", "@tailwindcss/oxide-linux-x64-musl": "4.1.11", "@tailwindcss/oxide-wasm32-wasi": "4.1.11", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" } }, "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.11", "", { "os": "android", "cpu": "arm64" }, "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11", "", { "os": "linux", "cpu": "arm" }, "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.11", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.11", "", { "os": "win32", "cpu": "x64" }, "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg=="],
|
|
||||||
|
|
||||||
"@tailwindcss/typography": ["@tailwindcss/typography@0.5.16", "", { "dependencies": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA=="],
|
|
||||||
|
|
||||||
"@tailwindcss/vite": ["@tailwindcss/vite@4.1.11", "", { "dependencies": { "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "tailwindcss": "4.1.11" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw=="],
|
|
||||||
|
|
||||||
"@tauri-apps/api": ["@tauri-apps/api@2.8.0", "", {}, "sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw=="],
|
|
||||||
|
|
||||||
"@tauri-apps/cli": ["@tauri-apps/cli@2.8.4", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.8.4", "@tauri-apps/cli-darwin-x64": "2.8.4", "@tauri-apps/cli-linux-arm-gnueabihf": "2.8.4", "@tauri-apps/cli-linux-arm64-gnu": "2.8.4", "@tauri-apps/cli-linux-arm64-musl": "2.8.4", "@tauri-apps/cli-linux-riscv64-gnu": "2.8.4", "@tauri-apps/cli-linux-x64-gnu": "2.8.4", "@tauri-apps/cli-linux-x64-musl": "2.8.4", "@tauri-apps/cli-win32-arm64-msvc": "2.8.4", "@tauri-apps/cli-win32-ia32-msvc": "2.8.4", "@tauri-apps/cli-win32-x64-msvc": "2.8.4" }, "bin": { "tauri": "tauri.js" } }, "sha512-ejUZBzuQRcjFV+v/gdj/DcbyX/6T4unZQjMSBZwLzP/CymEjKcc2+Fc8xTORThebHDUvqoXMdsCZt8r+hyN15g=="],
|
|
||||||
|
|
||||||
"@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.8.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BKu8HRkYV01SMTa7r4fLx+wjgtRK8Vep7lmBdHDioP6b8XH3q2KgsAyPWfEZaZIkZ2LY4SqqGARaE9oilNe0oA=="],
|
|
||||||
|
|
||||||
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.8.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-imb9PfSd/7G6VAO7v1bQ2A3ZH4NOCbhGJFLchxzepGcXf9NKkfun157JH9mko29K6sqAwuJ88qtzbKCbWJTH9g=="],
|
|
||||||
|
|
||||||
"@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.8.4", "", { "os": "linux", "cpu": "arm" }, "sha512-Ml215UnDdl7/fpOrF1CNovym/KjtUbCuPgrcZ4IhqUCnhZdXuphud/JT3E8X97Y03TZ40Sjz8raXYI2ET0exzw=="],
|
|
||||||
|
|
||||||
"@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.8.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-pbcgBpMyI90C83CxE5REZ9ODyIlmmAPkkJXtV398X3SgZEIYy5TACYqlyyv2z5yKgD8F8WH4/2fek7+jH+ZXAw=="],
|
|
||||||
|
|
||||||
"@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.8.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-zumFeaU1Ws5Ay872FTyIm7z8kfzEHu8NcIn8M6TxbJs0a7GRV21KBdpW1zNj2qy7HynnpQCqjAYXTUUmm9JAOw=="],
|
|
||||||
|
|
||||||
"@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.8.4", "", { "os": "linux", "cpu": "none" }, "sha512-qiqbB3Zz6IyO201f+1ojxLj65WYj8mixL5cOMo63nlg8CIzsP23cPYUrx1YaDPsCLszKZo7tVs14pc7BWf+/aQ=="],
|
|
||||||
|
|
||||||
"@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.8.4", "", { "os": "linux", "cpu": "x64" }, "sha512-TaqaDd9Oy6k45Hotx3pOf+pkbsxLaApv4rGd9mLuRM1k6YS/aw81YrsMryYPThrxrScEIUcmNIHaHsLiU4GMkw=="],
|
|
||||||
|
|
||||||
"@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.8.4", "", { "os": "linux", "cpu": "x64" }, "sha512-ot9STAwyezN8w+bBHZ+bqSQIJ0qPZFlz/AyscpGqB/JnJQVDFQcRDmUPFEaAtt2UUHSWzN3GoTJ5ypqLBp2WQA=="],
|
|
||||||
|
|
||||||
"@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.8.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-+2aJ/g90dhLiOLFSD1PbElXX3SoMdpO7HFPAZB+xot3CWlAZD1tReUFy7xe0L5GAR16ZmrxpIDM9v9gn5xRy/w=="],
|
|
||||||
|
|
||||||
"@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.8.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-yj7WDxkL1t9Uzr2gufQ1Hl7hrHuFKTNEOyascbc109EoiAqCp0tgZ2IykQqOZmZOHU884UAWI1pVMqBhS/BfhA=="],
|
|
||||||
|
|
||||||
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.8.4", "", { "os": "win32", "cpu": "x64" }, "sha512-XuvGB4ehBdd7QhMZ9qbj/8icGEatDuBNxyYHbLKsTYh90ggUlPa/AtaqcC1Fo69lGkTmq9BOKrs1aWSi7xDonA=="],
|
|
||||||
|
|
||||||
"@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.4.0", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-OvXkrEBfWwtd8tzVCEXIvRfNEX87qs2jv6SqmVPiHcJjBhSF/GUvjqUNIDmKByb5N8nvDqVUM7+g1sXwdC/S9w=="],
|
|
||||||
|
|
||||||
"@tauri-apps/plugin-fs": ["@tauri-apps/plugin-fs@2.4.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-YGhmYuTgXGsi6AjoV+5mh2NvicgWBfVJHHheuck6oHD+HC9bVWPaHvCP0/Aw4pHDejwrvT8hE3+zZAaWf+hrig=="],
|
|
||||||
|
|
||||||
"@tauri-apps/plugin-shell": ["@tauri-apps/plugin-shell@2.3.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-jjs2WGDO/9z2pjNlydY/F5yYhNsscv99K5lCmU5uKjsVvQ3dRlDhhtVYoa4OLDmktLtQvgvbQjCFibMl6tgGfw=="],
|
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.2.21", "", { "dependencies": { "bun-types": "1.2.21" } }, "sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A=="],
|
|
||||||
|
|
||||||
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
|
||||||
|
|
||||||
"@types/crypto-js": ["@types/crypto-js@4.2.2", "", {}, "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ=="],
|
|
||||||
|
|
||||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
|
||||||
|
|
||||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
|
||||||
|
|
||||||
"@types/node": ["@types/node@20.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA=="],
|
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
|
|
||||||
|
|
||||||
"@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="],
|
|
||||||
|
|
||||||
"@types/umami": ["@types/umami@2.10.1", "", {}, "sha512-CvOEMq+BFOUXj8yciq4hPsdOt0o9KBg5jQXnF1FrdOMKZMxJiwryyZNLa9Ud2NVO6nXSwU22YMKR3E+Xhbo+DA=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.42.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.42.0", "@typescript-eslint/type-utils": "8.42.0", "@typescript-eslint/utils": "8.42.0", "@typescript-eslint/visitor-keys": "8.42.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.42.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.42.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.42.0", "@typescript-eslint/types": "8.42.0", "@typescript-eslint/typescript-estree": "8.42.0", "@typescript-eslint/visitor-keys": "8.42.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.42.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.42.0", "@typescript-eslint/types": "^8.42.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.42.0", "", { "dependencies": { "@typescript-eslint/types": "8.42.0", "@typescript-eslint/visitor-keys": "8.42.0" } }, "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.42.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.42.0", "", { "dependencies": { "@typescript-eslint/types": "8.42.0", "@typescript-eslint/typescript-estree": "8.42.0", "@typescript-eslint/utils": "8.42.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.42.0", "", {}, "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.42.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.42.0", "@typescript-eslint/tsconfig-utils": "8.42.0", "@typescript-eslint/types": "8.42.0", "@typescript-eslint/visitor-keys": "8.42.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.42.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.42.0", "@typescript-eslint/types": "8.42.0", "@typescript-eslint/typescript-estree": "8.42.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.42.0", "", { "dependencies": { "@typescript-eslint/types": "8.42.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ=="],
|
|
||||||
|
|
||||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
|
||||||
|
|
||||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
|
||||||
|
|
||||||
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
|
||||||
|
|
||||||
"animejs": ["animejs@4.1.3", "", {}, "sha512-4XzlIsQsku1ycSPzchxxT0N+ohEMZObG71nOSBBkZoV4sgQvtXa/qAANkFpTE6pegdV8JnIBZiB0LfdxNoRNMw=="],
|
|
||||||
|
|
||||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
|
||||||
|
|
||||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
|
||||||
|
|
||||||
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
|
|
||||||
|
|
||||||
"autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="],
|
|
||||||
|
|
||||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
|
||||||
|
|
||||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
|
||||||
|
|
||||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
|
||||||
|
|
||||||
"bits-ui": ["bits-ui@2.9.6", "", { "dependencies": { "@floating-ui/core": "^1.7.1", "@floating-ui/dom": "^1.7.1", "esm-env": "^1.1.2", "runed": "^0.29.1", "svelte-toolbelt": "^0.9.3", "tabbable": "^6.2.0" }, "peerDependencies": { "@internationalized/date": "^3.8.1", "svelte": "^5.33.0" } }, "sha512-OzHktsQRsIz/hIMk5VwHo96Wpp/KY68q/ebUPUzTbvuFBrALB/X+QvO4KLgdczj5dfb3xHs9zpWq8yMH8ZbZlA=="],
|
|
||||||
|
|
||||||
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
|
||||||
|
|
||||||
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
|
||||||
|
|
||||||
"browserslist": ["browserslist@4.25.1", "", { "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw=="],
|
|
||||||
|
|
||||||
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
|
||||||
|
|
||||||
"buffer-builder": ["buffer-builder@0.2.0", "", {}, "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg=="],
|
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.2.21", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw=="],
|
|
||||||
|
|
||||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
|
||||||
|
|
||||||
"caniuse-lite": ["caniuse-lite@1.0.30001726", "", {}, "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw=="],
|
|
||||||
|
|
||||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
|
||||||
|
|
||||||
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
|
||||||
|
|
||||||
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
|
||||||
|
|
||||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
|
||||||
|
|
||||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
|
||||||
|
|
||||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
|
||||||
|
|
||||||
"colorjs.io": ["colorjs.io@0.5.2", "", {}, "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw=="],
|
|
||||||
|
|
||||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
|
||||||
|
|
||||||
"cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
|
|
||||||
|
|
||||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
|
||||||
|
|
||||||
"crypto-js": ["crypto-js@4.2.0", "", {}, "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="],
|
|
||||||
|
|
||||||
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
|
||||||
|
|
||||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
|
||||||
|
|
||||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
|
||||||
|
|
||||||
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
|
||||||
|
|
||||||
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
|
||||||
|
|
||||||
"detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
|
|
||||||
|
|
||||||
"devalue": ["devalue@5.3.2", "", {}, "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw=="],
|
|
||||||
|
|
||||||
"electron-to-chromium": ["electron-to-chromium@1.5.176", "", {}, "sha512-2nDK9orkm7M9ZZkjO3PjbEd3VUulQLyg5T9O3enJdFvUg46Hzd4DUvTvAuEgbdHYXyFsiG4A5sO9IzToMH1cDg=="],
|
|
||||||
|
|
||||||
"enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="],
|
|
||||||
|
|
||||||
"esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="],
|
|
||||||
|
|
||||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
|
||||||
|
|
||||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
|
||||||
|
|
||||||
"eslint": ["eslint@9.35.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.35.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg=="],
|
|
||||||
|
|
||||||
"eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="],
|
|
||||||
|
|
||||||
"eslint-plugin-svelte": ["eslint-plugin-svelte@3.12.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "globals": "^16.0.0", "known-css-properties": "^0.37.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.3.0" }, "peerDependencies": { "eslint": "^8.57.1 || ^9.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-NDYltSWcDybvnXD5P3NtrLAfdrgr2lklZsXpyIoSlQfg2d80p/E853XXccu+uVn+w4+Q/iHy4oRw00GJH9I/Cg=="],
|
|
||||||
|
|
||||||
"eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
|
|
||||||
|
|
||||||
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
|
|
||||||
|
|
||||||
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
|
|
||||||
|
|
||||||
"espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
|
|
||||||
|
|
||||||
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
|
|
||||||
|
|
||||||
"esrap": ["esrap@2.1.0", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA=="],
|
|
||||||
|
|
||||||
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
|
|
||||||
|
|
||||||
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
|
|
||||||
|
|
||||||
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
|
||||||
|
|
||||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
|
||||||
|
|
||||||
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
|
||||||
|
|
||||||
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
|
||||||
|
|
||||||
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
|
|
||||||
|
|
||||||
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
|
||||||
|
|
||||||
"fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="],
|
|
||||||
|
|
||||||
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
|
||||||
|
|
||||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
|
||||||
|
|
||||||
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
|
|
||||||
|
|
||||||
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
|
|
||||||
|
|
||||||
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
|
|
||||||
|
|
||||||
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
|
|
||||||
|
|
||||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
|
||||||
|
|
||||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
|
||||||
|
|
||||||
"globals": ["globals@16.3.0", "", {}, "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ=="],
|
|
||||||
|
|
||||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
|
||||||
|
|
||||||
"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
|
|
||||||
|
|
||||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
|
||||||
|
|
||||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
|
||||||
|
|
||||||
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
|
||||||
|
|
||||||
"immutable": ["immutable@5.1.3", "", {}, "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg=="],
|
|
||||||
|
|
||||||
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
|
||||||
|
|
||||||
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
|
||||||
|
|
||||||
"inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="],
|
|
||||||
|
|
||||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
|
||||||
|
|
||||||
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
|
||||||
|
|
||||||
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
|
||||||
|
|
||||||
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
|
|
||||||
|
|
||||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
|
||||||
|
|
||||||
"jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
|
|
||||||
|
|
||||||
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
|
||||||
|
|
||||||
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
|
||||||
|
|
||||||
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
|
||||||
|
|
||||||
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
|
||||||
|
|
||||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
|
||||||
|
|
||||||
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
|
||||||
|
|
||||||
"known-css-properties": ["known-css-properties@0.37.0", "", {}, "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ=="],
|
|
||||||
|
|
||||||
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
|
||||||
|
|
||||||
"lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
|
|
||||||
|
|
||||||
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
|
|
||||||
|
|
||||||
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
|
|
||||||
|
|
||||||
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="],
|
|
||||||
|
|
||||||
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="],
|
|
||||||
|
|
||||||
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="],
|
|
||||||
|
|
||||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
|
|
||||||
|
|
||||||
"lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
|
|
||||||
|
|
||||||
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
|
||||||
|
|
||||||
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
|
||||||
|
|
||||||
"lodash.castarray": ["lodash.castarray@4.4.0", "", {}, "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="],
|
|
||||||
|
|
||||||
"lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
|
|
||||||
|
|
||||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
|
||||||
|
|
||||||
"lucide-svelte": ["lucide-svelte@0.542.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5.0.0-next.42" } }, "sha512-KxqJycY4EWaGy1zk/7sqEcf48j4YaP9zEujYczzrsw5j9b9b5pjAJ1qGFKZvD7T6xz9reSYAfUAd6Bz62TdqGw=="],
|
|
||||||
|
|
||||||
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
|
|
||||||
|
|
||||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
|
||||||
|
|
||||||
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
|
||||||
|
|
||||||
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
||||||
|
|
||||||
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
|
||||||
|
|
||||||
"minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="],
|
|
||||||
|
|
||||||
"mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
|
|
||||||
|
|
||||||
"mode-watcher": ["mode-watcher@1.1.0", "", { "dependencies": { "runed": "^0.25.0", "svelte-toolbelt": "^0.7.1" }, "peerDependencies": { "svelte": "^5.27.0" } }, "sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g=="],
|
|
||||||
|
|
||||||
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
|
|
||||||
|
|
||||||
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
|
|
||||||
|
|
||||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
|
||||||
|
|
||||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
|
||||||
|
|
||||||
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
|
||||||
|
|
||||||
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
|
|
||||||
|
|
||||||
"node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
|
|
||||||
|
|
||||||
"normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="],
|
|
||||||
|
|
||||||
"number-flow": ["number-flow@0.5.8", "", { "dependencies": { "esm-env": "^1.1.4" } }, "sha512-FPr1DumWyGi5Nucoug14bC6xEz70A1TnhgSHhKyfqjgji2SOTz+iLJxKtv37N5JyJbteGYCm6NQ9p1O4KZ7iiA=="],
|
|
||||||
|
|
||||||
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
|
||||||
|
|
||||||
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
|
||||||
|
|
||||||
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
|
||||||
|
|
||||||
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
|
||||||
|
|
||||||
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
|
||||||
|
|
||||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
|
||||||
|
|
||||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
|
||||||
|
|
||||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
|
||||||
|
|
||||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
|
||||||
|
|
||||||
"postcss-load-config": ["postcss-load-config@3.1.4", "", { "dependencies": { "lilconfig": "^2.0.5", "yaml": "^1.10.2" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg=="],
|
|
||||||
|
|
||||||
"postcss-safe-parser": ["postcss-safe-parser@7.0.1", "", { "peerDependencies": { "postcss": "^8.4.31" } }, "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A=="],
|
|
||||||
|
|
||||||
"postcss-scss": ["postcss-scss@4.0.9", "", { "peerDependencies": { "postcss": "^8.4.29" } }, "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A=="],
|
|
||||||
|
|
||||||
"postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="],
|
|
||||||
|
|
||||||
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
|
||||||
|
|
||||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
|
||||||
|
|
||||||
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
|
||||||
|
|
||||||
"prettier-plugin-svelte": ["prettier-plugin-svelte@3.4.0", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ=="],
|
|
||||||
|
|
||||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
|
||||||
|
|
||||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
|
||||||
|
|
||||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
|
||||||
|
|
||||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
|
||||||
|
|
||||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
|
||||||
|
|
||||||
"rollup": ["rollup@4.44.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.44.1", "@rollup/rollup-android-arm64": "4.44.1", "@rollup/rollup-darwin-arm64": "4.44.1", "@rollup/rollup-darwin-x64": "4.44.1", "@rollup/rollup-freebsd-arm64": "4.44.1", "@rollup/rollup-freebsd-x64": "4.44.1", "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", "@rollup/rollup-linux-arm-musleabihf": "4.44.1", "@rollup/rollup-linux-arm64-gnu": "4.44.1", "@rollup/rollup-linux-arm64-musl": "4.44.1", "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", "@rollup/rollup-linux-riscv64-gnu": "4.44.1", "@rollup/rollup-linux-riscv64-musl": "4.44.1", "@rollup/rollup-linux-s390x-gnu": "4.44.1", "@rollup/rollup-linux-x64-gnu": "4.44.1", "@rollup/rollup-linux-x64-musl": "4.44.1", "@rollup/rollup-win32-arm64-msvc": "4.44.1", "@rollup/rollup-win32-ia32-msvc": "4.44.1", "@rollup/rollup-win32-x64-msvc": "4.44.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg=="],
|
|
||||||
|
|
||||||
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
|
||||||
|
|
||||||
"runed": ["runed@0.29.2", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA=="],
|
|
||||||
|
|
||||||
"rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
|
|
||||||
|
|
||||||
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
|
|
||||||
|
|
||||||
"sass": ["sass@1.92.1", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-ffmsdbwqb3XeyR8jJR6KelIXARM9bFQe8A6Q3W4Klmwy5Ckd5gz7jgUNHo4UOqutU5Sk1DtKLbpDP0nLCg1xqQ=="],
|
|
||||||
|
|
||||||
"sass-embedded": ["sass-embedded@1.92.1", "", { "dependencies": { "@bufbuild/protobuf": "^2.5.0", "buffer-builder": "^0.2.0", "colorjs.io": "^0.5.0", "immutable": "^5.0.2", "rxjs": "^7.4.0", "supports-color": "^8.1.1", "sync-child-process": "^1.0.2", "varint": "^6.0.0" }, "optionalDependencies": { "sass-embedded-all-unknown": "1.92.1", "sass-embedded-android-arm": "1.92.1", "sass-embedded-android-arm64": "1.92.1", "sass-embedded-android-riscv64": "1.92.1", "sass-embedded-android-x64": "1.92.1", "sass-embedded-darwin-arm64": "1.92.1", "sass-embedded-darwin-x64": "1.92.1", "sass-embedded-linux-arm": "1.92.1", "sass-embedded-linux-arm64": "1.92.1", "sass-embedded-linux-musl-arm": "1.92.1", "sass-embedded-linux-musl-arm64": "1.92.1", "sass-embedded-linux-musl-riscv64": "1.92.1", "sass-embedded-linux-musl-x64": "1.92.1", "sass-embedded-linux-riscv64": "1.92.1", "sass-embedded-linux-x64": "1.92.1", "sass-embedded-unknown-all": "1.92.1", "sass-embedded-win32-arm64": "1.92.1", "sass-embedded-win32-x64": "1.92.1" }, "bin": { "sass": "dist/bin/sass.js" } }, "sha512-28YwLnF5atAhogt3E4hXzz/NB9dwKffyw08a7DEasLh94P7+aELkG3ENSHYCWB9QFN14hYNLfwr9ozUsPDhcDQ=="],
|
|
||||||
|
|
||||||
"sass-embedded-all-unknown": ["sass-embedded-all-unknown@1.92.1", "", { "dependencies": { "sass": "1.92.1" }, "cpu": [ "!arm", "!x64", "!arm64", ] }, "sha512-5t6/YZf+vhO3OY/49h8RCL6Cwo78luva0M+TnTM9gu9ASffRXAuOVLNKciSXa3loptyemDDS6IU5/dVH5w0KmA=="],
|
|
||||||
|
|
||||||
"sass-embedded-android-arm": ["sass-embedded-android-arm@1.92.1", "", { "os": "android", "cpu": "arm" }, "sha512-4EjpVVzuksERdgAd4BqeSXFnWtWN3DSRyEIUPJ7BhcS9sfDh2Gf6miI2kNTvIQLJ2XIJynDDcEQ8a1U9KwKUTQ=="],
|
|
||||||
|
|
||||||
"sass-embedded-android-arm64": ["sass-embedded-android-arm64@1.92.1", "", { "os": "android", "cpu": "arm64" }, "sha512-Q+UruGb7yKawHagVmVDRRKsnc4mJZvWMBnuRCu2coJo2FofyqBmXohVGXbxko97sYceA9TJTrUEx3WVKQUNCbQ=="],
|
|
||||||
|
|
||||||
"sass-embedded-android-riscv64": ["sass-embedded-android-riscv64@1.92.1", "", { "os": "android", "cpu": "none" }, "sha512-nCY5btLlX7W7Jc6cCL6D2Yklpiu540EJ2G08YVGu12DrAMCBzqM347CSRf2ojp1H8jyhvmLkaFwnrJWzh+6S+w=="],
|
|
||||||
|
|
||||||
"sass-embedded-android-x64": ["sass-embedded-android-x64@1.92.1", "", { "os": "android", "cpu": "x64" }, "sha512-qYWR3bftJ77aLYwYDFuzDI4dcwVVixxqQxlIQWNGkHRCexj614qGSSHemr18C2eVj3mjXAQxTQxU68U7pkGPAA=="],
|
|
||||||
|
|
||||||
"sass-embedded-darwin-arm64": ["sass-embedded-darwin-arm64@1.92.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-g2yQ3txjMYLKMjL2cW1xRO9nnV3ijf95NbX/QShtV6tiVUETZNWDsRMDEwBNGYY6PTE/UZerjJL1R/2xpQg6WA=="],
|
|
||||||
|
|
||||||
"sass-embedded-darwin-x64": ["sass-embedded-darwin-x64@1.92.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-eH+fgxLQhTEPjZPCgPAVuX5e514Qp/4DMAUMtlNShv4cr4TD5qOp1XlsPYR/b7uE7p2cKFkUpUn/bHNqJ2ay4A=="],
|
|
||||||
|
|
||||||
"sass-embedded-linux-arm": ["sass-embedded-linux-arm@1.92.1", "", { "os": "linux", "cpu": "arm" }, "sha512-cT3w8yoQTqrtZvWLJeutEGmawITDTY4J6oSVQjeDcPnnoPt0gOFxem8YMznraACXvahw/2+KJDH33BTNgiPo0A=="],
|
|
||||||
|
|
||||||
"sass-embedded-linux-arm64": ["sass-embedded-linux-arm64@1.92.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-dNmlpGeZkry1BofhAdGFBXrpM69y9LlYuNnncf+HfsOOUtj8j0q1RwS+zb5asknhKFUOAG8GCGRY1df7Rwu35g=="],
|
|
||||||
|
|
||||||
"sass-embedded-linux-musl-arm": ["sass-embedded-linux-musl-arm@1.92.1", "", { "os": "linux", "cpu": "arm" }, "sha512-nPBos6lI31ef2zQhqTZhFOU7ar4impJbLIax0XsqS269YsiCwjhk11VmUloJTpFlJuKMiVXNo7dPx+katxhD/Q=="],
|
|
||||||
|
|
||||||
"sass-embedded-linux-musl-arm64": ["sass-embedded-linux-musl-arm64@1.92.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-TfiEBkCyNzVoOhjHXUT+vZ6+p0ueDbvRw6f4jHdkvljZzXdXMby4wh7BU1odl69rgRTkSvYKhgbErRLDR/F7pQ=="],
|
|
||||||
|
|
||||||
"sass-embedded-linux-musl-riscv64": ["sass-embedded-linux-musl-riscv64@1.92.1", "", { "os": "linux", "cpu": "none" }, "sha512-R+RcJA4EYpJDE9JM1GgPYgZo7x94FlxZ6jPodOQkEaZ1S9kvXVCuP5X/0PXRPhu08KJOfeMsAElzfdAjUf7KJg=="],
|
|
||||||
|
|
||||||
"sass-embedded-linux-musl-x64": ["sass-embedded-linux-musl-x64@1.92.1", "", { "os": "linux", "cpu": "x64" }, "sha512-/HolYRGXJjx8nLw6oj5ZrkR7PFM7X/5kE4MYZaFMpDIPIcw3bqB2fUXLo/MYlRLsw7gBAT6hJAMBrNdKuTphfw=="],
|
|
||||||
|
|
||||||
"sass-embedded-linux-riscv64": ["sass-embedded-linux-riscv64@1.92.1", "", { "os": "linux", "cpu": "none" }, "sha512-b9bxe0CMsbSsLx3nrR0cq8xpIkoAC6X36o4DGMITF3m2v3KsojC7ru9X0Gz+zUFr6rwpq/0lTNzFLNu6sPNo3w=="],
|
|
||||||
|
|
||||||
"sass-embedded-linux-x64": ["sass-embedded-linux-x64@1.92.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xuiK5Jp5NldW4bvlC7AuX1Wf7o0gLZ3md/hNg+bkTvxtCDgnUHtfdo8Q+xWP11bD9QX31xXFWpmUB8UDLi6XQQ=="],
|
|
||||||
|
|
||||||
"sass-embedded-unknown-all": ["sass-embedded-unknown-all@1.92.1", "", { "dependencies": { "sass": "1.92.1" }, "os": [ "!linux", "!win32", "!darwin", "!android", ] }, "sha512-AT9oXvtNY4N+Nd0wvoWqq9A5HjdH/X3aUH4boQUtXyaJ/9DUwnQmBpP5Gtn028ZS8exOGBdobmmWAuigv0k/OA=="],
|
|
||||||
|
|
||||||
"sass-embedded-win32-arm64": ["sass-embedded-win32-arm64@1.92.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-KvmpQjY9yTBMtTYz4WBqetlv9bGaDW1aStcu7MSTbH7YiSybX/9fnxlCAEQv1WlIidQhcJAiyk0Eae+LGK7cIQ=="],
|
|
||||||
|
|
||||||
"sass-embedded-win32-x64": ["sass-embedded-win32-x64@1.92.1", "", { "os": "win32", "cpu": "x64" }, "sha512-B6Nz/GbH7Vkpb2TkQHsGcczWM5t+70VWopWF1x5V5yxLpA8ZzVQ7NTKKi+jDoVY2Efu6ZyzgT9n5KgG2kWliXA=="],
|
|
||||||
|
|
||||||
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
|
||||||
|
|
||||||
"set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="],
|
|
||||||
|
|
||||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
|
||||||
|
|
||||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
|
||||||
|
|
||||||
"sirv": ["sirv@3.0.1", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A=="],
|
|
||||||
|
|
||||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
|
||||||
|
|
||||||
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
|
||||||
|
|
||||||
"style-to-object": ["style-to-object@1.0.9", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw=="],
|
|
||||||
|
|
||||||
"supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
|
|
||||||
|
|
||||||
"svelte": ["svelte@5.38.7", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-1ld9TPZSdUS3EtYGQzisU2nhwXoIzNQcZ71IOU9fEmltaUofQnVfW5CQuhgM/zFsZ43arZXS1BRKi0MYgUV91w=="],
|
|
||||||
|
|
||||||
"svelte-check": ["svelte-check@4.3.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-lkh8gff5gpHLjxIV+IaApMxQhTGnir2pNUAqcNgeKkvK5bT/30Ey/nzBxNLDlkztCH4dP7PixkMt9SWEKFPBWg=="],
|
|
||||||
|
|
||||||
"svelte-confetti": ["svelte-confetti@2.3.2", "", { "peerDependencies": { "svelte": ">=5.0.0" } }, "sha512-cfIwoGqMPYWRYDUz2g7mG1uHYWy7VBepelQdzCC3j/M42UrAqaBYmIi9xaoQfow4fbINHO9WuARnTyK2bjjGQg=="],
|
|
||||||
|
|
||||||
"svelte-eslint-parser": ["svelte-eslint-parser@1.3.1", "", { "dependencies": { "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "postcss": "^8.4.49", "postcss-scss": "^4.0.9", "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-0Iztj5vcOVOVkhy1pbo5uA9r+d3yaVoE5XPc9eABIWDOSJZ2mOsZ4D+t45rphWCOr0uMw3jtSG2fh2e7GvKnPg=="],
|
|
||||||
|
|
||||||
"svelte-sonner": ["svelte-sonner@1.0.5", "", { "dependencies": { "runed": "^0.28.0" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-9dpGPFqKb/QWudYqGnEz93vuY+NgCEvyNvxoCLMVGw6sDN/3oVeKV1xiEirW2E1N3vJEyj5imSBNOGltQHA7mg=="],
|
|
||||||
|
|
||||||
"svelte-toolbelt": ["svelte-toolbelt@0.9.3", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.29.0", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.30.2" } }, "sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw=="],
|
|
||||||
|
|
||||||
"sync-child-process": ["sync-child-process@1.0.2", "", { "dependencies": { "sync-message-port": "^1.0.0" } }, "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA=="],
|
|
||||||
|
|
||||||
"sync-message-port": ["sync-message-port@1.1.3", "", {}, "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg=="],
|
|
||||||
|
|
||||||
"tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="],
|
|
||||||
|
|
||||||
"tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="],
|
|
||||||
|
|
||||||
"tailwind-variants": ["tailwind-variants@3.1.0", "", { "peerDependencies": { "tailwind-merge": ">=3.0.0", "tailwindcss": "*" }, "optionalPeers": ["tailwind-merge"] }, "sha512-ieiYaEfUr+sNhw/k++dosmZfVA4VIG5bV+G1eGdJSC4FcflqQv0iSIlOLj/RbzRuTu/VrIiNSlwh1esBM3BXUg=="],
|
|
||||||
|
|
||||||
"tailwindcss": ["tailwindcss@4.1.11", "", {}, "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="],
|
|
||||||
|
|
||||||
"tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="],
|
|
||||||
|
|
||||||
"tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="],
|
|
||||||
|
|
||||||
"tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="],
|
|
||||||
|
|
||||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
|
||||||
|
|
||||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
|
||||||
|
|
||||||
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
|
|
||||||
|
|
||||||
"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
|
|
||||||
|
|
||||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
|
||||||
|
|
||||||
"tw-animate-css": ["tw-animate-css@1.3.8", "", {}, "sha512-Qrk3PZ7l7wUcGYhwZloqfkWCmaXZAoqjkdbIDvzfGshwGtexa/DAs9koXxIkrpEasyevandomzCBAV1Yyop5rw=="],
|
|
||||||
|
|
||||||
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
|
||||||
|
|
||||||
"typescript-eslint": ["typescript-eslint@8.42.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/parser": "8.42.0", "@typescript-eslint/typescript-estree": "8.42.0", "@typescript-eslint/utils": "8.42.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ozR/rQn+aQXQxh1YgbCzQWDFrsi9mcg+1PM3l/z5o1+20P7suOIaNg515bpr/OYt6FObz/NHcBstydDLHWeEKg=="],
|
|
||||||
|
|
||||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
|
||||||
|
|
||||||
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
|
|
||||||
|
|
||||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
|
||||||
|
|
||||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
|
||||||
|
|
||||||
"uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
|
|
||||||
|
|
||||||
"varint": ["varint@6.0.0", "", {}, "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="],
|
|
||||||
|
|
||||||
"vite": ["vite@7.1.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ=="],
|
|
||||||
|
|
||||||
"vite-plugin-devtools-json": ["vite-plugin-devtools-json@0.4.1", "", { "dependencies": { "uuid": "^11.1.0" }, "peerDependencies": { "vite": "^2.7.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-pN+QJL+NwZUV+Via8w/Sh6X2pDrVClIMDAXdl7+EteXKB6mcHhsFGGclmxrPx6ZPGKSK5ez5ns64oRpjE5wFCg=="],
|
|
||||||
|
|
||||||
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
|
|
||||||
|
|
||||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
|
||||||
|
|
||||||
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
|
||||||
|
|
||||||
"yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
|
|
||||||
|
|
||||||
"yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
|
|
||||||
|
|
||||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
|
||||||
|
|
||||||
"zimmerframe": ["zimmerframe@1.1.2", "", {}, "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="],
|
|
||||||
|
|
||||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
|
||||||
|
|
||||||
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
|
|
||||||
|
|
||||||
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
|
|
||||||
|
|
||||||
"@parcel/watcher/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
|
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
|
||||||
|
|
||||||
"chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
|
||||||
|
|
||||||
"eslint-plugin-svelte/@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="],
|
|
||||||
|
|
||||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
|
||||||
|
|
||||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
|
||||||
|
|
||||||
"mode-watcher/runed": ["runed@0.25.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg=="],
|
|
||||||
|
|
||||||
"mode-watcher/svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="],
|
|
||||||
|
|
||||||
"svelte-eslint-parser/postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="],
|
|
||||||
|
|
||||||
"svelte-sonner/runed": ["runed@0.28.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ=="],
|
|
||||||
|
|
||||||
"tinyglobby/fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
|
||||||
|
|
||||||
"vite/fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
|
||||||
|
|
||||||
"eslint-plugin-svelte/@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
|
||||||
|
|
||||||
"mode-watcher/svelte-toolbelt/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://next.shadcn-svelte.com/schema.json",
|
|
||||||
"tailwind": {
|
|
||||||
"css": "src\\app.css",
|
|
||||||
"baseColor": "gray"
|
|
||||||
},
|
|
||||||
"aliases": {
|
|
||||||
"components": "$lib/components",
|
|
||||||
"utils": "$lib/utils",
|
|
||||||
"ui": "$lib/components/ui",
|
|
||||||
"hooks": "$lib/hooks",
|
|
||||||
"lib": "$lib"
|
|
||||||
},
|
|
||||||
"typescript": true,
|
|
||||||
"registry": "https://tw3.shadcn-svelte.com/registry/default"
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
const appName = "EZPPLauncher";
|
||||||
|
const appVersion = "2.0.0";
|
||||||
|
|
||||||
|
module.exports = { appName, appVersion };
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
const sqlite = require("better-sqlite3");
|
||||||
|
const path = require("path");
|
||||||
|
const fs = require("fs");
|
||||||
|
|
||||||
|
const configFolder = path.join(
|
||||||
|
process.platform == "win32"
|
||||||
|
? process.env["LOCALAPPDATA"]
|
||||||
|
: process.env["HOME"],
|
||||||
|
"EZPPLauncher",
|
||||||
|
);
|
||||||
|
if (!fs.existsSync(configFolder)) fs.mkdirSync(configFolder);
|
||||||
|
|
||||||
|
const dbFile = path.join(configFolder, "ezpplauncher.db");
|
||||||
|
|
||||||
|
const db = sqlite(dbFile);
|
||||||
|
db.pragma("journal_mode = WAL");
|
||||||
|
|
||||||
|
db.exec(
|
||||||
|
"CREATE TABLE IF NOT EXISTS config (configKey VARCHAR PRIMARY KEY, configValue VARCHAR);",
|
||||||
|
);
|
||||||
|
|
||||||
|
const set = (key, value) => {
|
||||||
|
db.prepare(
|
||||||
|
`INSERT OR REPLACE INTO config (configKey, configValue) VALUES (?, ?)`,
|
||||||
|
).run(key, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const remove = (key) => {
|
||||||
|
db.prepare(`DELETE FROM config WHERE configKey = ?`).run(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
const get = (
|
||||||
|
key,
|
||||||
|
) => {
|
||||||
|
const result = db.prepare(
|
||||||
|
"SELECT configKey key, configValue val FROM config WHERE key = ?",
|
||||||
|
).get(key);
|
||||||
|
return result ? result.val ?? undefined : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const all = () => {
|
||||||
|
const result = db.prepare(
|
||||||
|
`SELECT configKey key, configValue val FROM config WHERE 1`,
|
||||||
|
).all();
|
||||||
|
return result ?? undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
all,
|
||||||
|
get,
|
||||||
|
set,
|
||||||
|
remove,
|
||||||
|
};
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
const cryptojs = require("crypto-js");
|
||||||
|
|
||||||
|
const encrypt = (string, salt) => {
|
||||||
|
return cryptojs.AES.encrypt(string, salt).toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const decrypt = (string, salt) => {
|
||||||
|
return cryptojs.AES.decrypt(string, salt).toString(cryptojs.enc.Utf8);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = { encrypt, decrypt };
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
const childProcess = require("child_process");
|
||||||
|
|
||||||
|
const runFile = (folder, file, args, onExit) => {
|
||||||
|
childProcess.execFile(file, args, {
|
||||||
|
cwd: folder
|
||||||
|
}, (_err, _stdout, _stdin) => {
|
||||||
|
if (onExit) onExit();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const runFileDetached = (folder, file, args) => {
|
||||||
|
const subProcess = childProcess.spawn(file + (args ? " " + args : ''), {
|
||||||
|
cwd: folder,
|
||||||
|
detached: true,
|
||||||
|
stdio: 'ignore'
|
||||||
|
});
|
||||||
|
subProcess.unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { runFile, runFileDetached };
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
function formatBytes(bytes, decimals = 2) {
|
||||||
|
if (!+bytes) return '0 Bytes'
|
||||||
|
|
||||||
|
const k = 1024
|
||||||
|
const dm = decimals < 0 ? 0 : decimals
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||||
|
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||||
|
|
||||||
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { formatBytes };
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
const child_process = require("child_process");
|
||||||
|
const options = { encoding: "ascii", windowsHide: true, timeout: 200 };
|
||||||
|
const platforms = {
|
||||||
|
win32: [
|
||||||
|
"REG QUERY HKLM\\SOFTWARE\\Microsoft\\Cryptography /v MachineGuid",
|
||||||
|
/MachineGuid\s+REG_SZ\s+(.*?)\s/,
|
||||||
|
],
|
||||||
|
darwin: [
|
||||||
|
"ioreg -rd1 -c IOPlatformExpertDevice",
|
||||||
|
/"IOPlatformUUID" = "(.*?)"/,
|
||||||
|
],
|
||||||
|
linux: [
|
||||||
|
"cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || true",
|
||||||
|
/^([\da-f]+)/,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const crypto = require("crypto");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns machine hardware id.
|
||||||
|
* Returns `undefined` if cannot determine.
|
||||||
|
* @return {string?}
|
||||||
|
*/
|
||||||
|
function getHwId() {
|
||||||
|
const getter = platforms[process.platform];
|
||||||
|
if (!getter) return;
|
||||||
|
const result = getter[1].exec(child_process.execSync(getter[0], options));
|
||||||
|
if (!result) return;
|
||||||
|
return crypto.createHash("md5").update(result[1]).digest("hex") ||
|
||||||
|
undefined;
|
||||||
|
}
|
||||||
|
exports.getHwId = getHwId;
|
||||||
@@ -0,0 +1,484 @@
|
|||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const crypto = require("crypto");
|
||||||
|
const EventEmitter = require("events");
|
||||||
|
const { default: axios } = require("axios");
|
||||||
|
const { runFile } = require("./executeUtil.js");
|
||||||
|
|
||||||
|
const checkUpdateURL =
|
||||||
|
"https://osu.ppy.sh/web/check-updates.php?action=check&stream=";
|
||||||
|
const ignoredOsuEntities = [
|
||||||
|
"osu!auth.dll",
|
||||||
|
];
|
||||||
|
const osuEntities = [
|
||||||
|
"avcodec-51.dll",
|
||||||
|
"avformat-52.dll",
|
||||||
|
"avutil-49.dll",
|
||||||
|
"bass.dll",
|
||||||
|
"bass_fx.dll",
|
||||||
|
"collection.db",
|
||||||
|
"d3dcompiler_47.dll",
|
||||||
|
"libEGL.dll",
|
||||||
|
"libGLESv2.dll",
|
||||||
|
"Microsoft.Ink.dll",
|
||||||
|
"OpenTK.dll",
|
||||||
|
"osu!.cfg",
|
||||||
|
"osu!.db",
|
||||||
|
"osu!.exe",
|
||||||
|
"osu!auth.dll",
|
||||||
|
"osu!gameplay.dll",
|
||||||
|
"osu!seasonal.dll",
|
||||||
|
"osu!ui.dll",
|
||||||
|
"presence.db",
|
||||||
|
"pthreadGC2.dll",
|
||||||
|
"scores.db",
|
||||||
|
];
|
||||||
|
|
||||||
|
const patcherFiles = [
|
||||||
|
{
|
||||||
|
name: "patcher.exe",
|
||||||
|
url_download: "https://ez-pp.farm/assets/patcher.exe",
|
||||||
|
url_hash: "https://ez-pp.farm/assets/patcher.md5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "patch.hook.dll",
|
||||||
|
url_download: "https://ez-pp.farm/assets/patch.hook.dll",
|
||||||
|
url_hash: "https://ez-pp.farm/assets/patch.hook.md5",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const uiFiles = [
|
||||||
|
{
|
||||||
|
name: "ezpp!ui.dll",
|
||||||
|
url_download: "https://ez-pp.farm/assets/ezpp!ui.dll",
|
||||||
|
url_hash: "https://ez-pp.farm/assets/ezpp!ui.md5",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function isValidOsuFolder(path) {
|
||||||
|
const allFiles = await fs.promises.readdir(path);
|
||||||
|
let matches = 0;
|
||||||
|
for (const file of allFiles) {
|
||||||
|
if (osuEntities.includes(file)) matches = matches + 1;
|
||||||
|
}
|
||||||
|
return (Math.round((matches / osuEntities.length) * 100) >= 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGlobalConfig(osuPath) {
|
||||||
|
const configFileInfo = {
|
||||||
|
name: "",
|
||||||
|
path: "",
|
||||||
|
get: async (key) => {
|
||||||
|
if (!configFileInfo.path) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const fileStream = await fs.promises.readFile(
|
||||||
|
configFileInfo.path,
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
const lines = fileStream.split(/\r?\n/);
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.includes(" = ")) {
|
||||||
|
const argsPair = line.split(" = ", 2);
|
||||||
|
const keyname = argsPair[0];
|
||||||
|
const value = argsPair[1];
|
||||||
|
if (keyname == key) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const globalOsuConfig = path.join(osuPath, "osu!.cfg");
|
||||||
|
if (fs.existsSync(globalOsuConfig)) {
|
||||||
|
configFileInfo.name = "osu!.cfg";
|
||||||
|
configFileInfo.path = globalOsuConfig;
|
||||||
|
}
|
||||||
|
return configFileInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserConfig(osuPath) {
|
||||||
|
const configFileInfo = {
|
||||||
|
name: "",
|
||||||
|
path: "",
|
||||||
|
set: async (key, newValue) => {
|
||||||
|
if (!configFileInfo.path) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const fileContents = [];
|
||||||
|
const fileStream = await fs.promises.readFile(
|
||||||
|
configFileInfo.path,
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
const lines = fileStream.split(/\r?\n/);
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.includes(" = ")) {
|
||||||
|
const argsPair = line.split(" = ", 2);
|
||||||
|
const keyname = argsPair[0];
|
||||||
|
if (keyname == key) {
|
||||||
|
fileContents.push(`${key} = ${newValue}`);
|
||||||
|
} else {
|
||||||
|
fileContents.push(line);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileContents.push(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await fs.promises.writeFile(configFileInfo.path, fileContents.join("\n"));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
get: async (key) => {
|
||||||
|
if (!configFileInfo.path) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const fileStream = await fs.promises.readFile(
|
||||||
|
configFileInfo.path,
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
const lines = fileStream.split(/\r?\n/);
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.includes(" = ")) {
|
||||||
|
const argsPair = line.split(" = ", 2);
|
||||||
|
const keyname = argsPair[0];
|
||||||
|
const value = argsPair[1];
|
||||||
|
if (keyname == key) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const userOsuConfig = path.join(
|
||||||
|
osuPath,
|
||||||
|
`osu!.${process.env["USERNAME"]}.cfg`,
|
||||||
|
);
|
||||||
|
if (fs.existsSync(userOsuConfig)) {
|
||||||
|
configFileInfo.name = `osu!.${process.env["USERNAME"]}.cfg`;
|
||||||
|
configFileInfo.path = userOsuConfig;
|
||||||
|
}
|
||||||
|
return configFileInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUpdateFiles(releaseStream) {
|
||||||
|
const releaseData = await fetch(checkUpdateURL + releaseStream);
|
||||||
|
return releaseData.ok ? await releaseData.json() : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFilesThatNeedUpdate(osuPath, releaseStreamFiles) {
|
||||||
|
const updateFiles = [];
|
||||||
|
for (const updatePatch of releaseStreamFiles) {
|
||||||
|
const fileName = updatePatch.filename;
|
||||||
|
const fileHash = updatePatch.file_hash;
|
||||||
|
|
||||||
|
const fileOnDisk = path.join(osuPath, fileName);
|
||||||
|
if (fs.existsSync(fileOnDisk)) {
|
||||||
|
if (ignoredOsuEntities.includes(fileName)) continue;
|
||||||
|
const fileHashOnDisk = crypto.createHash("md5").update(
|
||||||
|
fs.readFileSync(fileOnDisk),
|
||||||
|
).digest("hex");
|
||||||
|
if (
|
||||||
|
fileHashOnDisk.trim().toLowerCase() != fileHash.trim().toLowerCase()
|
||||||
|
) {
|
||||||
|
updateFiles.push(updatePatch);
|
||||||
|
}
|
||||||
|
} else updateFiles.push(updatePatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadUpdateFiles(osuPath, updateFiles) {
|
||||||
|
const eventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
const startDownload = async () => {
|
||||||
|
for (const updatePatch of updateFiles) {
|
||||||
|
const fileName = updatePatch.filename;
|
||||||
|
const fileSize = updatePatch.filesize;
|
||||||
|
const fileURL = updatePatch.url_full;
|
||||||
|
|
||||||
|
const axiosDownloadWithProgress = await axios.get(fileURL, {
|
||||||
|
responseType: "stream",
|
||||||
|
onDownloadProgress: (progressEvent) => {
|
||||||
|
const { loaded, total } = progressEvent;
|
||||||
|
eventEmitter.emit("data", {
|
||||||
|
fileName,
|
||||||
|
loaded,
|
||||||
|
total,
|
||||||
|
progress: Math.floor((loaded / total) * 100),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
axiosDownloadWithProgress.data.on("end", () => {
|
||||||
|
eventEmitter.emit("data", {
|
||||||
|
fileName,
|
||||||
|
loaded: fileSize,
|
||||||
|
total: fileSize,
|
||||||
|
progress: 100,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(path.join(osuPath, fileName))) {
|
||||||
|
await fs.promises.rm(path.join(osuPath, fileName), {
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
path.join(osuPath, fileName),
|
||||||
|
axiosDownloadWithProgress.data,
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
eventEmitter.emit("error", {
|
||||||
|
fileName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait until all files are downloaded
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventEmitter,
|
||||||
|
startDownload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function runOsuUpdater(osuPath, onExit) {
|
||||||
|
const osuExecuteable = path.join(osuPath, "osu!.exe");
|
||||||
|
runFile(osuPath, osuExecuteable, ["-repair"], onExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runOsuWithDevServer(osuPath, serverDomain, onExit) {
|
||||||
|
const osuExecuteable = path.join(osuPath, "osu!.exe");
|
||||||
|
runFile(osuPath, osuExecuteable, ["-devserver", serverDomain], onExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPatcherUpdates(osuPath) {
|
||||||
|
const filesToDownload = [];
|
||||||
|
|
||||||
|
const patcherDir = path.join(osuPath, "EZPPLauncher");
|
||||||
|
if (!fs.existsSync(patcherDir)) fs.mkdirSync(patcherDir);
|
||||||
|
|
||||||
|
for (const patcherFile of patcherFiles) {
|
||||||
|
if (fs.existsSync(path.join(patcherDir, patcherFile.name))) {
|
||||||
|
const latestPatchFileHash = await (await fetch(patcherFile.url_hash))
|
||||||
|
.text();
|
||||||
|
const localPatchFileHash = crypto.createHash("md5").update(
|
||||||
|
fs.readFileSync(path.join(patcherDir, patcherFile.name)),
|
||||||
|
).digest("hex");
|
||||||
|
if (
|
||||||
|
latestPatchFileHash.trim().toLowerCase() !=
|
||||||
|
localPatchFileHash.trim().toLowerCase()
|
||||||
|
) filesToDownload.push(patcherFile);
|
||||||
|
} else filesToDownload.push(patcherFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filesToDownload;
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadPatcherUpdates(osuPath, patcherUpdates) {
|
||||||
|
const eventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
const startDownload = async () => {
|
||||||
|
const patcherDir = path.join(osuPath, "EZPPLauncher");
|
||||||
|
if (!fs.existsSync(patcherDir)) fs.mkdirSync(patcherDir);
|
||||||
|
|
||||||
|
for (const patcherFile of patcherUpdates) {
|
||||||
|
const fileName = patcherFile.name;
|
||||||
|
const fileURL = patcherFile.url_download;
|
||||||
|
const axiosDownloadWithProgress = await axios.get(fileURL, {
|
||||||
|
responseType: "stream",
|
||||||
|
onDownloadProgress: (progressEvent) => {
|
||||||
|
const { loaded, total } = progressEvent;
|
||||||
|
eventEmitter.emit("data", {
|
||||||
|
fileName,
|
||||||
|
loaded,
|
||||||
|
total,
|
||||||
|
progress: Math.floor((loaded / total) * 100),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(path.join(osuPath, "EZPPLauncher", fileName))) {
|
||||||
|
await fs.promises.rm(path.join(osuPath, "EZPPLauncher", fileName), {
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
path.join(osuPath, "EZPPLauncher", fileName),
|
||||||
|
axiosDownloadWithProgress.data,
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
eventEmitter.emit("error", {
|
||||||
|
fileName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventEmitter,
|
||||||
|
startDownload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUIFiles(osuPath) {
|
||||||
|
const filesToDownload = [];
|
||||||
|
|
||||||
|
const ezppLauncherDir = path.join(osuPath, "EZPPLauncher");
|
||||||
|
if (!fs.existsSync(ezppLauncherDir)) fs.mkdirSync(ezppLauncherDir);
|
||||||
|
|
||||||
|
for (const uiFile of uiFiles) {
|
||||||
|
if (fs.existsSync(path.join(ezppLauncherDir, uiFile.name))) {
|
||||||
|
const latestPatchFileHash = await (await fetch(uiFile.url_hash)).text();
|
||||||
|
const localPatchFileHash = crypto.createHash("md5").update(
|
||||||
|
fs.readFileSync(path.join(ezppLauncherDir, uiFile.name)),
|
||||||
|
).digest("hex");
|
||||||
|
if (
|
||||||
|
latestPatchFileHash.trim().toLowerCase() !=
|
||||||
|
localPatchFileHash.trim().toLowerCase()
|
||||||
|
) filesToDownload.push(uiFile);
|
||||||
|
} else filesToDownload.push(uiFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filesToDownload;
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadUIFiles(osuPath, uiFiles) {
|
||||||
|
const eventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
const startDownload = async () => {
|
||||||
|
const ezpplauncherDir = path.join(osuPath, "EZPPLauncher");
|
||||||
|
if (!fs.existsSync(ezpplauncherDir)) fs.mkdirSync(ezpplauncherDir);
|
||||||
|
|
||||||
|
for (const uiFile of uiFiles) {
|
||||||
|
const fileName = uiFile.name;
|
||||||
|
const fileURL = uiFile.url_download;
|
||||||
|
const axiosDownloadWithProgress = await axios.get(fileURL, {
|
||||||
|
responseType: "stream",
|
||||||
|
onDownloadProgress: (progressEvent) => {
|
||||||
|
const { loaded, total } = progressEvent;
|
||||||
|
eventEmitter.emit("data", {
|
||||||
|
fileName,
|
||||||
|
loaded,
|
||||||
|
total,
|
||||||
|
progress: Math.floor((loaded / total) * 100),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(path.join(osuPath, "EZPPLauncher", fileName))) {
|
||||||
|
await fs.promises.rm(path.join(osuPath, "EZPPLauncher", fileName), {
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
path.join(osuPath, "EZPPLauncher", fileName),
|
||||||
|
axiosDownloadWithProgress.data,
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
eventEmitter.emit("error", {
|
||||||
|
fileName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventEmitter,
|
||||||
|
startDownload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function replaceUIFile(osuPath, revert) {
|
||||||
|
if (!revert) {
|
||||||
|
const ezppUIFile = path.join(osuPath, "EZPPLauncher", "ezpp!ui.dll");
|
||||||
|
const oldOsuUIFile = path.join(osuPath, "osu!ui.dll");
|
||||||
|
await fs.promises.rename(
|
||||||
|
oldOsuUIFile,
|
||||||
|
path.join(osuPath, "osu!ui.dll.bak"),
|
||||||
|
);
|
||||||
|
await fs.promises.rename(ezppUIFile, oldOsuUIFile);
|
||||||
|
} else {
|
||||||
|
const oldOsuUIFile = path.join(osuPath, "osu!ui.dll");
|
||||||
|
const ezppUIFile = path.join(osuPath, "EZPPLauncher", "ezpp!ui.dll");
|
||||||
|
await fs.promises.rename(oldOsuUIFile, ezppUIFile);
|
||||||
|
await fs.promises.rename(
|
||||||
|
path.join(osuPath, "osu!ui.dll.bak"),
|
||||||
|
oldOsuUIFile,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findOsuInstallation() {
|
||||||
|
const regedit = require("regedit-rs");
|
||||||
|
|
||||||
|
const osuLocationFromDefaultIcon =
|
||||||
|
"HKLM\\SOFTWARE\\Classes\\osu\\DefaultIcon";
|
||||||
|
const osuKey = regedit.listSync(osuLocationFromDefaultIcon);
|
||||||
|
if (osuKey[osuLocationFromDefaultIcon].exists) {
|
||||||
|
const key = osuKey[osuLocationFromDefaultIcon].values[""];
|
||||||
|
let value = key.value;
|
||||||
|
value = value.substring(1, value.length - 3);
|
||||||
|
return path.dirname(value.trim());
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateOsuConfigHashes(osuPath) {
|
||||||
|
const osuCfg = path.join(osuPath, "osu!.cfg");
|
||||||
|
const fileStream = await fs.promises.readFile(osuCfg, "utf-8");
|
||||||
|
const lines = fileStream.split(/\r?\n/);
|
||||||
|
const newLines = [];
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.includes(" = ")) {
|
||||||
|
const argsPair = line.split(" = ", 2);
|
||||||
|
const key = argsPair[0];
|
||||||
|
const value = argsPair[1];
|
||||||
|
|
||||||
|
if (key.startsWith("h_")) {
|
||||||
|
const fileName = key.substring(2, key.length);
|
||||||
|
const filePath = path.join(osuPath, fileName);
|
||||||
|
if (!fs.existsSync(filePath)) continue;
|
||||||
|
const binaryFileContents = await fs.promises.readFile(filePath);
|
||||||
|
const existingFileMD5 = crypto.createHash("md5").update(
|
||||||
|
binaryFileContents,
|
||||||
|
).digest("hex");
|
||||||
|
if (value == existingFileMD5) newLines.push(line);
|
||||||
|
else newLines.push(`${key} = ${existingFileMD5}`);
|
||||||
|
} else if (line.startsWith("u_UpdaterAutoStart")) {
|
||||||
|
newLines.push(`${key} = 0`);
|
||||||
|
} else {
|
||||||
|
newLines.push(line);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newLines.push(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.promises.writeFile(osuCfg, newLines.join("\n"), "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
isValidOsuFolder,
|
||||||
|
getUserConfig,
|
||||||
|
getGlobalConfig,
|
||||||
|
getUpdateFiles,
|
||||||
|
getFilesThatNeedUpdate,
|
||||||
|
downloadUpdateFiles,
|
||||||
|
runOsuWithDevServer,
|
||||||
|
getPatcherUpdates,
|
||||||
|
downloadPatcherUpdates,
|
||||||
|
downloadUIFiles,
|
||||||
|
getUIFiles,
|
||||||
|
replaceUIFile,
|
||||||
|
findOsuInstallation,
|
||||||
|
updateOsuConfigHashes,
|
||||||
|
runOsuUpdater,
|
||||||
|
};
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
const DiscordRPC = require("discord-auto-rpc");
|
||||||
|
const { appName, appVersion } = require("./appInfo.js");
|
||||||
|
|
||||||
|
const clientId = "1032772293220384808";
|
||||||
|
let richPresence;
|
||||||
|
let intervalId;
|
||||||
|
|
||||||
|
let currentStatus = {
|
||||||
|
details: " ",
|
||||||
|
state: "Idle in Launcher...",
|
||||||
|
startTimestamp: new Date(),
|
||||||
|
largeImageKey: "ezppfarm",
|
||||||
|
largeImageText: `${appName} ${appVersion}`,
|
||||||
|
smallImageKey: " ",
|
||||||
|
smallImageText: " ",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: "Download the Launcher",
|
||||||
|
url: "https://git.ez-pp.farm/EZPPFarm/EZPPLauncher/releases/latest",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Join EZPPFarm",
|
||||||
|
url: "https://ez-pp.farm/discord",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
instance: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
connect: () => {
|
||||||
|
if (!richPresence) {
|
||||||
|
richPresence = new DiscordRPC.AutoClient({ transport: "ipc" });
|
||||||
|
richPresence.endlessLogin({ clientId });
|
||||||
|
richPresence.once("ready", () => {
|
||||||
|
richPresence.setActivity(currentStatus);
|
||||||
|
intervalId = setInterval(() => {
|
||||||
|
richPresence.setActivity(currentStatus);
|
||||||
|
}, 2500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
disconnect: async () => {
|
||||||
|
if (richPresence) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
await richPresence.clearActivity();
|
||||||
|
await richPresence.destroy();
|
||||||
|
richPresence = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateStatus: ({ state, details }) => {
|
||||||
|
currentStatus.state = state ?? " ";
|
||||||
|
currentStatus.details = details ?? " ";
|
||||||
|
},
|
||||||
|
updateVersion: (osuVersion) => {
|
||||||
|
currentStatus.smallImageKey = osuVersion ? "osu" : " ";
|
||||||
|
currentStatus.smallImageText = osuVersion ? `osu! ${osuVersion}` : " ";
|
||||||
|
},
|
||||||
|
update: () => {
|
||||||
|
if (richPresence) {
|
||||||
|
richPresence.setActivity(currentStatus);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasPresence: () => richPresence != undefined,
|
||||||
|
};
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
const semver = require("semver");
|
||||||
|
const { appVersion } = require("./appInfo");
|
||||||
|
|
||||||
|
const repoApiUrl =
|
||||||
|
"https://git.ez-pp.farm/api/v1/repos/EZPPFarm/EZPPLauncher/releases?limit=1";
|
||||||
|
|
||||||
|
const releasesUrl =
|
||||||
|
"https://git.ez-pp.farm/EZPPFarm/EZPPLauncher/releases/latest";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
updateAvailable: async () => {
|
||||||
|
try {
|
||||||
|
const latestRelease = await fetch(repoApiUrl);
|
||||||
|
const json = await latestRelease.json();
|
||||||
|
if (json.length <= 0) return false;
|
||||||
|
return {
|
||||||
|
update: semver.lt(appVersion, json[0].tag_name),
|
||||||
|
release: json[0],
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
return { update: false };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
releasesUrl,
|
||||||
|
};
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import prettier from 'eslint-config-prettier';
|
|
||||||
import { includeIgnoreFile } from '@eslint/compat';
|
|
||||||
import js from '@eslint/js';
|
|
||||||
import svelte from 'eslint-plugin-svelte';
|
|
||||||
import globals from 'globals';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
import ts from 'typescript-eslint';
|
|
||||||
import svelteConfig from './svelte.config.js';
|
|
||||||
|
|
||||||
const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));
|
|
||||||
|
|
||||||
export default ts.config(
|
|
||||||
includeIgnoreFile(gitignorePath),
|
|
||||||
js.configs.recommended,
|
|
||||||
...ts.configs.recommended,
|
|
||||||
...svelte.configs.recommended,
|
|
||||||
prettier,
|
|
||||||
...svelte.configs.prettier,
|
|
||||||
{
|
|
||||||
languageOptions: {
|
|
||||||
globals: { ...globals.browser, ...globals.node },
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
// typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.
|
|
||||||
// see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
|
|
||||||
'no-undef': 'off',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
|
|
||||||
languageOptions: {
|
|
||||||
parserOptions: {
|
|
||||||
projectService: true,
|
|
||||||
extraFileExtensions: ['.svelte'],
|
|
||||||
parser: ts.parser,
|
|
||||||
svelteConfig,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -0,0 +1,685 @@
|
|||||||
|
// Modules to control application life and create native browser window
|
||||||
|
const { app, BrowserWindow, Menu, ipcMain, dialog, shell } = require(
|
||||||
|
"electron",
|
||||||
|
);
|
||||||
|
const path = require("path");
|
||||||
|
const serve = require("electron-serve");
|
||||||
|
const loadURL = serve({ directory: "public" });
|
||||||
|
const config = require("./electron/config");
|
||||||
|
const { setupTitlebar, attachTitlebarToWindow } = require(
|
||||||
|
"custom-electron-titlebar/main",
|
||||||
|
);
|
||||||
|
const {
|
||||||
|
isValidOsuFolder,
|
||||||
|
getUpdateFiles,
|
||||||
|
getGlobalConfig,
|
||||||
|
getFilesThatNeedUpdate,
|
||||||
|
downloadUpdateFiles,
|
||||||
|
getUserConfig,
|
||||||
|
runOsuWithDevServer,
|
||||||
|
getPatcherUpdates,
|
||||||
|
downloadPatcherUpdates,
|
||||||
|
getUIFiles,
|
||||||
|
downloadUIFiles,
|
||||||
|
replaceUIFile,
|
||||||
|
findOsuInstallation,
|
||||||
|
updateOsuConfigHashes,
|
||||||
|
runOsuUpdater,
|
||||||
|
} = require("./electron/osuUtil");
|
||||||
|
const { formatBytes } = require("./electron/formattingUtil");
|
||||||
|
const windowName = require("get-window-by-name");
|
||||||
|
const fs = require("fs");
|
||||||
|
const { runFileDetached } = require("./electron/executeUtil");
|
||||||
|
const richPresence = require("./electron/richPresence");
|
||||||
|
const cryptUtil = require("./electron/cryptoUtil");
|
||||||
|
const { getHwId } = require("./electron/hwidUtil");
|
||||||
|
const { appName, appVersion } = require("./electron/appInfo");
|
||||||
|
const { updateAvailable, releasesUrl } = require("./electron/updateCheck");
|
||||||
|
const fkill = require("fkill");
|
||||||
|
|
||||||
|
// Keep a global reference of the window object, if you don't, the window will
|
||||||
|
// be closed automatically when the JavaScript object is garbage collected.
|
||||||
|
let mainWindow;
|
||||||
|
let osuCheckInterval;
|
||||||
|
let userOsuPath;
|
||||||
|
let osuLoaded = false;
|
||||||
|
let patch = false;
|
||||||
|
|
||||||
|
let currentUser = undefined;
|
||||||
|
|
||||||
|
function isDev() {
|
||||||
|
return !app.isPackaged;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startOsuStatus() {
|
||||||
|
osuCheckInterval = setInterval(async () => {
|
||||||
|
const osuWindowTitle = windowName.getWindowText("osu!.exe");
|
||||||
|
if (osuWindowTitle.length < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const firstInstance = osuWindowTitle[0];
|
||||||
|
|
||||||
|
if (firstInstance) {
|
||||||
|
if (!osuLoaded) {
|
||||||
|
osuLoaded = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (patch) {
|
||||||
|
const patcherExecuteable = path.join(
|
||||||
|
userOsuPath,
|
||||||
|
"EZPPLauncher",
|
||||||
|
"patcher.exe",
|
||||||
|
);
|
||||||
|
if (fs.existsSync(patcherExecuteable)) {
|
||||||
|
runFileDetached(userOsuPath, patcherExecuteable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
const windowTitle = firstInstance.processTitle;
|
||||||
|
lastOsuStatus = windowTitle;
|
||||||
|
const currentStatusRequest = await fetch(
|
||||||
|
"https://api.ez-pp.farm/get_player_status?name=" + currentUser.username,
|
||||||
|
);
|
||||||
|
const currentStatus = await currentStatusRequest.json();
|
||||||
|
|
||||||
|
if (!("player_status" in currentStatus)) return;
|
||||||
|
if (!("status" in currentStatus.player_status)) return;
|
||||||
|
|
||||||
|
let details = "Idle...";
|
||||||
|
let infoText = currentStatus.player_status.status.info_text.length > 0
|
||||||
|
? currentStatus.player_status.status.info_text
|
||||||
|
: " ";
|
||||||
|
|
||||||
|
switch (currentStatus.player_status.status.action) {
|
||||||
|
case 1:
|
||||||
|
details = "AFK...";
|
||||||
|
infoText = " ";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
details = "Playing...";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
details = "Editing...";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
details = "Modding...";
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
details = "Multiplayer: Selecting a Beatmap...";
|
||||||
|
infoText = " ";
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
details = "Watching...";
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
details = "Testing...";
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
details = "Submitting...";
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
details = "Multiplayer: Idle...";
|
||||||
|
infoText = " ";
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
details = "Multiplayer: Playing...";
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
details = "Browsing osu!direct...";
|
||||||
|
infoText = " ";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
richPresence.updateStatus({
|
||||||
|
details,
|
||||||
|
state: infoText,
|
||||||
|
});
|
||||||
|
|
||||||
|
richPresence.update();
|
||||||
|
}
|
||||||
|
}, 2500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopOsuStatus() {
|
||||||
|
clearInterval(osuCheckInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerIPCPipes() {
|
||||||
|
ipcMain.handle("ezpplauncher:login", async (e, args) => {
|
||||||
|
const hwid = getHwId();
|
||||||
|
const timeout = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => timeout.abort(), 8000);
|
||||||
|
try {
|
||||||
|
const fetchResult = await fetch("https://ez-pp.farm/login/check", {
|
||||||
|
signal: timeout.signal,
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
username: args.username,
|
||||||
|
password: args.password,
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (fetchResult.ok) {
|
||||||
|
const result = await fetchResult.json();
|
||||||
|
if ("user" in result) {
|
||||||
|
if (args.saveCredentials) {
|
||||||
|
config.set("username", args.username);
|
||||||
|
config.set("password", cryptUtil.encrypt(args.password, hwid));
|
||||||
|
}
|
||||||
|
currentUser = args;
|
||||||
|
config.remove("guest");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
code: 500,
|
||||||
|
message: "Something went wrong while logging you in.",
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
return {
|
||||||
|
code: 500,
|
||||||
|
message: "Something went wrong while logging you in.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("ezpplauncher:autologin-active", async (e) => {
|
||||||
|
const username = config.get("username");
|
||||||
|
const password = config.get("password");
|
||||||
|
const guest = config.get("guest");
|
||||||
|
if (guest != undefined) return true;
|
||||||
|
return username != undefined && password != undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("ezpplauncher:autologin", async (e) => {
|
||||||
|
const hwid = getHwId();
|
||||||
|
const username = config.get("username");
|
||||||
|
const guest = config.get("guest");
|
||||||
|
if (guest) return { code: 200, message: "Login as guest", guest: true };
|
||||||
|
if (username == undefined) {
|
||||||
|
return { code: 200, message: "No autologin" };
|
||||||
|
}
|
||||||
|
const password = cryptUtil.decrypt(config.get("password"), hwid);
|
||||||
|
if (username == undefined || password == undefined) {
|
||||||
|
return { code: 200, message: "No autologin" };
|
||||||
|
}
|
||||||
|
const timeout = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => timeout.abort(), 8000);
|
||||||
|
try {
|
||||||
|
const fetchResult = await fetch("https://ez-pp.farm/login/check", {
|
||||||
|
signal: timeout.signal,
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (fetchResult.ok) {
|
||||||
|
const result = await fetchResult.json();
|
||||||
|
if ("user" in result) {
|
||||||
|
currentUser = {
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
config.remove("password");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
code: 500,
|
||||||
|
message: "Something went wrong while logging you in.",
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
return {
|
||||||
|
code: 500,
|
||||||
|
message: "Something went wrong while logging you in.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("ezpplauncher:guestlogin", (e) => {
|
||||||
|
config.remove("username");
|
||||||
|
config.remove("password");
|
||||||
|
config.set("guest", "1");
|
||||||
|
currentUser = undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("ezpplauncher:logout", (e) => {
|
||||||
|
config.remove("username");
|
||||||
|
config.remove("password");
|
||||||
|
config.remove("guest");
|
||||||
|
currentUser = undefined;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("ezpplauncher:settings", async (e) => {
|
||||||
|
return config.all();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("ezpplauncher:setting-update", async (e, args) => {
|
||||||
|
for (const key of Object.keys(args)) {
|
||||||
|
const value = args[key];
|
||||||
|
|
||||||
|
if (key == "presence") {
|
||||||
|
if (!value) richPresence.disconnect();
|
||||||
|
else richPresence.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value == "boolean") {
|
||||||
|
config.set(key, value ? "true" : "false");
|
||||||
|
} else {
|
||||||
|
config.set(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("ezpplauncher:detect-folder", async (e) => {
|
||||||
|
const detected = await findOsuInstallation();
|
||||||
|
if (detected && await isValidOsuFolder(detected)) {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:alert", {
|
||||||
|
type: "success",
|
||||||
|
message: "osu! path successfully saved!",
|
||||||
|
});
|
||||||
|
config.set("osuPath", detected);
|
||||||
|
}
|
||||||
|
return config.all();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("ezpplauncher:set-folder", async (e) => {
|
||||||
|
const folderResult = await dialog.showOpenDialog({
|
||||||
|
title: "Select osu! installation directory",
|
||||||
|
properties: ["openDirectory"],
|
||||||
|
});
|
||||||
|
if (!folderResult.canceled) {
|
||||||
|
const folder = folderResult.filePaths[0];
|
||||||
|
if (await isValidOsuFolder(folder)) {
|
||||||
|
config.set("osuPath", folder);
|
||||||
|
mainWindow.webContents.send("ezpplauncher:alert", {
|
||||||
|
type: "success",
|
||||||
|
message: "osu! path successfully saved!",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:alert", {
|
||||||
|
type: "error",
|
||||||
|
message: "invalid osu! path!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config.all();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("ezpplauncher:exitAndUpdate", async (e) => {
|
||||||
|
await shell.openExternal(releasesUrl);
|
||||||
|
app.exit();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("ezpplauncher:launch", async (e) => {
|
||||||
|
const configPatch = config.get("patch");
|
||||||
|
patch = configPatch != undefined ? configPatch == "true" : true;
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Checking osu! directory...",
|
||||||
|
});
|
||||||
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
const osuPath = config.get("osuPath");
|
||||||
|
userOsuPath = osuPath;
|
||||||
|
if (osuPath == undefined) {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchabort");
|
||||||
|
mainWindow.webContents.send("ezpplauncher:alert", {
|
||||||
|
type: "error",
|
||||||
|
message: "osu! path not set!",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(await isValidOsuFolder(osuPath))) {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchabort");
|
||||||
|
mainWindow.webContents.send("ezpplauncher:alert", {
|
||||||
|
type: "error",
|
||||||
|
message: "invalid osu! path!",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Checking for osu! updates...",
|
||||||
|
});
|
||||||
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
const releaseStream = await getGlobalConfig(osuPath).get("_ReleaseStream");
|
||||||
|
const latestFiles = await getUpdateFiles(releaseStream);
|
||||||
|
const uiFiles = await getUIFiles(osuPath);
|
||||||
|
const updateFiles = await getFilesThatNeedUpdate(osuPath, latestFiles);
|
||||||
|
if (uiFiles.length > 0) {
|
||||||
|
const uiDownloader = downloadUIFiles(osuPath, uiFiles);
|
||||||
|
let errored = false;
|
||||||
|
uiDownloader.eventEmitter.on("error", (data) => {
|
||||||
|
const filename = data.fileName;
|
||||||
|
errored = true;
|
||||||
|
mainWindow.webContents.send("ezpplauncher:alert", {
|
||||||
|
type: "error",
|
||||||
|
message:
|
||||||
|
`Failed to download/replace ${filename}!\nMaybe try to restart EZPPLauncher.`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
uiDownloader.eventEmitter.on("data", (data) => {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: Math.ceil(data.progress),
|
||||||
|
});
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${
|
||||||
|
formatBytes(data.total)
|
||||||
|
})...`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await uiDownloader.startDownload();
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: -1,
|
||||||
|
});
|
||||||
|
if (errored) {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchabort");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (updateFiles.length > 0) {
|
||||||
|
const updateDownloader = downloadUpdateFiles(osuPath, updateFiles);
|
||||||
|
let errored = false;
|
||||||
|
updateDownloader.eventEmitter.on("error", (data) => {
|
||||||
|
const filename = data.fileName;
|
||||||
|
errored = true;
|
||||||
|
mainWindow.webContents.send("ezpplauncher:alert", {
|
||||||
|
type: "error",
|
||||||
|
message:
|
||||||
|
`Failed to download/replace ${filename}!\nMaybe try to restart EZPPLauncher.`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
updateDownloader.eventEmitter.on("data", (data) => {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: Math.ceil(data.progress),
|
||||||
|
});
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${
|
||||||
|
formatBytes(data.total)
|
||||||
|
})...`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await updateDownloader.startDownload();
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: -1,
|
||||||
|
});
|
||||||
|
if (errored) {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchabort");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "osu! is now up to date!",
|
||||||
|
});
|
||||||
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
} else {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "osu! is up to date!",
|
||||||
|
});
|
||||||
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patch) {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Looking for patcher updates...",
|
||||||
|
});
|
||||||
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
const patchFiles = await getPatcherUpdates(osuPath);
|
||||||
|
if (patchFiles.length > 0) {
|
||||||
|
const patcherDownloader = downloadPatcherUpdates(osuPath, patchFiles);
|
||||||
|
let errored = false;
|
||||||
|
patcherDownloader.eventEmitter.on("error", (data) => {
|
||||||
|
const filename = data.fileName;
|
||||||
|
errored = true;
|
||||||
|
mainWindow.webContents.send("ezpplauncher:alert", {
|
||||||
|
type: "error",
|
||||||
|
message:
|
||||||
|
`Failed to download/replace ${filename}!\nMaybe try to restart EZPPLauncher.`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
patcherDownloader.eventEmitter.on("data", (data) => {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: Math.ceil(data.progress),
|
||||||
|
});
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${
|
||||||
|
formatBytes(data.total)
|
||||||
|
})...`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await patcherDownloader.startDownload();
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: -1,
|
||||||
|
});
|
||||||
|
if (errored) {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchabort");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Patcher is now up to date!",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Patcher is up to date!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Launching osu! updater to verify...",
|
||||||
|
});
|
||||||
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
|
||||||
|
await new Promise((res) => {
|
||||||
|
runOsuUpdater(osuPath, async () => {
|
||||||
|
await new Promise((res) => setTimeout(res, 500));
|
||||||
|
const terminationThread = setInterval(async () => {
|
||||||
|
const osuWindowTitle = windowName.getWindowText("osu!.exe");
|
||||||
|
if (osuWindowTitle.length < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const firstInstance = osuWindowTitle[0];
|
||||||
|
if (firstInstance) {
|
||||||
|
const processId = firstInstance.processId;
|
||||||
|
await fkill(processId, { force: true, silent: true });
|
||||||
|
clearInterval(terminationThread);
|
||||||
|
res();
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Preparing launch...",
|
||||||
|
});
|
||||||
|
|
||||||
|
await updateOsuConfigHashes(osuPath);
|
||||||
|
await replaceUIFile(osuPath, false);
|
||||||
|
|
||||||
|
const forceUpdateFiles = [
|
||||||
|
".require_update",
|
||||||
|
"help.txt",
|
||||||
|
"_pending",
|
||||||
|
];
|
||||||
|
//TODO: needs testing
|
||||||
|
try {
|
||||||
|
for (const updateFileName of forceUpdateFiles) {
|
||||||
|
const updateFile = path.join(osuPath, updateFileName);
|
||||||
|
if (fs.existsSync(updateFile)) {
|
||||||
|
await fs.promises.rm(updateFile, {
|
||||||
|
force: true,
|
||||||
|
recursive: (await fs.promises.lstat(updateFile)).isDirectory,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const userConfig = getUserConfig(osuPath);
|
||||||
|
richPresence.updateVersion(await userConfig.get("LastVersion"));
|
||||||
|
richPresence.update();
|
||||||
|
if (richPresence.hasPresence) {
|
||||||
|
await userConfig.set("DiscordRichPresence", "0");
|
||||||
|
}
|
||||||
|
await userConfig.set("ShowInterfaceDuringRelax", "1");
|
||||||
|
if (currentUser) {
|
||||||
|
await userConfig.set("CredentialEndpoint", "ez-pp.farm");
|
||||||
|
await userConfig.set("SavePassword", "1");
|
||||||
|
await userConfig.set("SaveUsername", "1");
|
||||||
|
await userConfig.set("Username", currentUser.username);
|
||||||
|
await userConfig.set("Password", currentUser.password);
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Launching osu!...",
|
||||||
|
});
|
||||||
|
|
||||||
|
const onExitHook = () => {
|
||||||
|
mainWindow.show();
|
||||||
|
mainWindow.focus();
|
||||||
|
stopOsuStatus();
|
||||||
|
richPresence.updateVersion();
|
||||||
|
richPresence.updateStatus({
|
||||||
|
state: "Idle in Launcher...",
|
||||||
|
details: undefined,
|
||||||
|
});
|
||||||
|
richPresence.update();
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Waiting for cleanup...",
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
await replaceUIFile(osuPath, true);
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchabort");
|
||||||
|
osuLoaded = false;
|
||||||
|
}, 5000);
|
||||||
|
};
|
||||||
|
runOsuWithDevServer(osuPath, "ez-pp.farm", onExitHook);
|
||||||
|
mainWindow.hide();
|
||||||
|
startOsuStatus();
|
||||||
|
|
||||||
|
/* mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: 0,
|
||||||
|
});
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: 100,
|
||||||
|
}); */
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createWindow() {
|
||||||
|
const gotTheLock = app.requestSingleInstanceLock();
|
||||||
|
if (!gotTheLock) {
|
||||||
|
app.quit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupTitlebar();
|
||||||
|
|
||||||
|
// Create the browser window.
|
||||||
|
mainWindow = new BrowserWindow({
|
||||||
|
width: 550,
|
||||||
|
height: 350,
|
||||||
|
resizable: false,
|
||||||
|
frame: false,
|
||||||
|
titleBarStyle: "hidden",
|
||||||
|
title: `${appName} ${appVersion}`,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
preload: path.join(__dirname, "preload.js"),
|
||||||
|
},
|
||||||
|
icon: path.join(__dirname, "public/favicon.png"),
|
||||||
|
show: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const menu = Menu.buildFromTemplate([]);
|
||||||
|
Menu.setApplicationMenu(menu);
|
||||||
|
|
||||||
|
// disable electron toolbar
|
||||||
|
/* if (!isDev()) */
|
||||||
|
mainWindow.setMenu(null);
|
||||||
|
|
||||||
|
attachTitlebarToWindow(mainWindow);
|
||||||
|
|
||||||
|
// This block of code is intended for development purpose only.
|
||||||
|
// Delete this entire block of code when you are ready to package the application.
|
||||||
|
if (isDev()) {
|
||||||
|
mainWindow.loadURL("http://localhost:8080/");
|
||||||
|
} else {
|
||||||
|
loadURL(mainWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerIPCPipes();
|
||||||
|
|
||||||
|
const presenceEnabled = config.get("presence");
|
||||||
|
if (presenceEnabled == undefined) {
|
||||||
|
richPresence.connect();
|
||||||
|
} else {
|
||||||
|
if (presenceEnabled == "true") {
|
||||||
|
richPresence.connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Uncomment the following line of code when app is ready to be packaged.
|
||||||
|
// loadURL(mainWindow);
|
||||||
|
|
||||||
|
// Open the DevTools and also disable Electron Security Warning.
|
||||||
|
if (isDev()) {
|
||||||
|
process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = true;
|
||||||
|
mainWindow.webContents.openDevTools({ mode: "detach" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emitted when the window is closed.
|
||||||
|
mainWindow.on("closed", function () {
|
||||||
|
// Dereference the window object, usually you would store windows
|
||||||
|
// in an array if your app supports multi windows, this is the time
|
||||||
|
// when you should delete the corresponding element.
|
||||||
|
mainWindow = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Emitted when the window is ready to be shown
|
||||||
|
// This helps in showing the window gracefully.
|
||||||
|
mainWindow.once("ready-to-show", async () => {
|
||||||
|
const updateInfo = await updateAvailable();
|
||||||
|
if (updateInfo.update) {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:update", updateInfo.release);
|
||||||
|
}
|
||||||
|
mainWindow.show();
|
||||||
|
mainWindow.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method will be called when Electron has finished
|
||||||
|
// initialization and is ready to create browser windows.
|
||||||
|
// Some APIs can only be used after this event occurs.
|
||||||
|
app.on("ready", createWindow);
|
||||||
|
|
||||||
|
// Quit when all windows are closed.
|
||||||
|
app.on("window-all-closed", async function () {
|
||||||
|
// On macOS it is common for applications and their menu bar
|
||||||
|
// to stay active until the user quits explicitly with Cmd + Q
|
||||||
|
await richPresence.disconnect();
|
||||||
|
if (process.platform !== "darwin") app.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on("activate", function () {
|
||||||
|
// On macOS it's common to re-create a window in the app when the
|
||||||
|
// dock icon is clicked and there are no other windows open.
|
||||||
|
if (mainWindow === null) createWindow();
|
||||||
|
});
|
||||||
|
// In this file you can include the rest of your app's specific main process
|
||||||
|
// code. You can also put them in separate files and require them here.
|
||||||
@@ -1,76 +1,90 @@
|
|||||||
{
|
{
|
||||||
"name": "ezpplauncher",
|
"name": "ezpplauncher-next",
|
||||||
"version": "3.0.7",
|
"version": "2.0.0",
|
||||||
"description": "",
|
"description": "EZPPLauncher rewritten with Svelte.",
|
||||||
"type": "module",
|
"private": false,
|
||||||
"scripts": {
|
"license": "MIT",
|
||||||
"debug": "bun run tauri dev",
|
"main": "main.js",
|
||||||
"build": "bun ./scripts/sync-version.ts && bun run vite:build && bun run tauri:build",
|
"author": "HorizonCode <horizoncode88@gmail.com>",
|
||||||
"vite:dev": "vite dev",
|
"build": {
|
||||||
"vite:build": "vite build",
|
"icon": "public/favicon.png",
|
||||||
"tauri:build": "tauri build",
|
"productName": "EZPPLauncher",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"files": [
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"public/**/*",
|
||||||
"tauri": "tauri",
|
"main.js",
|
||||||
"tauri:update-logo": "bun run tauri icon ./static/logo.png",
|
"preload.js",
|
||||||
"format": "prettier --write .",
|
"electron/*",
|
||||||
"lint": "prettier --check . && eslint ."
|
"electron/**/*"
|
||||||
|
],
|
||||||
|
"win": {
|
||||||
|
"target": [
|
||||||
|
"portable"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"linux": {},
|
||||||
|
"mac": {}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "rollup -c --bundleConfigAsCjs",
|
||||||
|
"rebuild": "electron-rebuild",
|
||||||
|
"dev": "rollup -c -w --bundleConfigAsCjs",
|
||||||
|
"start": "sirv public --no-clear",
|
||||||
|
"electron": "wait-on http://localhost:8080 && electron .",
|
||||||
|
"electron-dev": "concurrently \"yarn run dev\" \"yarn run electron\"",
|
||||||
|
"preelectron-pack": "electron-rebuild && yarn run build",
|
||||||
|
"electron-pack": "electron-builder",
|
||||||
|
"postinstall": "electron-builder install-app-deps",
|
||||||
|
"check": "svelte-check --tsconfig ./tsconfig.json"
|
||||||
},
|
},
|
||||||
"license": "GPL-3.0-only",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@better-fetch/fetch": "^1.1.18",
|
"@types/better-sqlite3": "^7.6.8",
|
||||||
"@fontsource/sora": "^5.2.6",
|
"axios": "^1.6.5",
|
||||||
"@fontsource/space-mono": "^5.2.8",
|
"better-sqlite3": "^9.2.2",
|
||||||
"@number-flow/svelte": "^0.3.9",
|
"crypto": "^1.0.1",
|
||||||
"@tailwindcss/typography": "^0.5.16",
|
|
||||||
"@tailwindcss/vite": "^4.1.11",
|
|
||||||
"@tauri-apps/api": "^2.8.0",
|
|
||||||
"@tauri-apps/plugin-dialog": "^2.4.0",
|
|
||||||
"@tauri-apps/plugin-fs": "^2.4.2",
|
|
||||||
"@tauri-apps/plugin-shell": "^2.3.1",
|
|
||||||
"@types/umami": "^2.10.1",
|
|
||||||
"animejs": "^4.1.3",
|
|
||||||
"buffer": "^6.0.3",
|
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"lucide-svelte": "^0.542.0",
|
"custom-electron-titlebar": "^4.2.7",
|
||||||
"semver": "^7.7.2",
|
"discord-auto-rpc": "^1.0.17",
|
||||||
"svelte-confetti": "^2.3.2",
|
"electron-serve": "^1.1.0",
|
||||||
"tw-animate-css": "^1.3.8"
|
"fkill": "^7.2.1",
|
||||||
|
"get-window-by-name": "^2.0.0",
|
||||||
|
"regedit-rs": "^1.0.2",
|
||||||
|
"semver": "^7.5.4",
|
||||||
|
"svelte-french-toast": "^1.2.0",
|
||||||
|
"sweetalert2": "^11.10.3",
|
||||||
|
"systeminformation": "^5.21.22"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.3.2",
|
"@electron/rebuild": "^3.5.0",
|
||||||
"@eslint/js": "^9.35.0",
|
"@rollup/plugin-commonjs": "^25.0.7",
|
||||||
"@iarna/toml": "2.2.5",
|
"@rollup/plugin-image": "^3.0.3",
|
||||||
"@lucide/svelte": "^0.542.0",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@sveltejs/adapter-static": "^3.0.9",
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
"@sveltejs/kit": "^2.37.1",
|
"@rollup/plugin-typescript": "^11.1.5",
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.1.4",
|
"@sveltejs/vite-plugin-svelte": "^3.0.1",
|
||||||
"@tauri-apps/cli": "^2.8.4",
|
"@tsconfig/svelte": "^5.0.2",
|
||||||
"@types/bun": "^1.2.21",
|
"autoprefixer": "^10.4.16",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"@types/semver": "^7.7.1",
|
"electron": "^28.1.2",
|
||||||
"autoprefixer": "10.4.21",
|
"electron-builder": "^24.9.1",
|
||||||
"bits-ui": "^2.9.6",
|
"flowbite": "^2.2.1",
|
||||||
"clsx": "^2.1.1",
|
"flowbite-svelte": "^0.44.21",
|
||||||
"eslint": "^9.35.0",
|
"flowbite-svelte-icons": "^0.4.5",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"postcss": "^8.4.32",
|
||||||
"eslint-plugin-svelte": "^3.12.2",
|
"postcss-load-config": "^5.0.2",
|
||||||
"globals": "^16.3.0",
|
"rollup": "^4.9.2",
|
||||||
"mode-watcher": "^1.1.0",
|
"rollup-plugin-css-only": "^4.5.2",
|
||||||
"prettier": "^3.6.2",
|
"rollup-plugin-livereload": "^2.0.5",
|
||||||
"prettier-plugin-svelte": "^3.4.0",
|
"rollup-plugin-postcss": "^4.0.2",
|
||||||
"sass-embedded": "^1.92.1",
|
"rollup-plugin-progress": "^1.1.2",
|
||||||
"svelte": "^5.38.7",
|
"rollup-plugin-svelte": "^7.1.6",
|
||||||
"svelte-check": "^4.3.1",
|
"rollup-plugin-unused": "^0.1.1",
|
||||||
"svelte-sonner": "^1.0.5",
|
"sirv-cli": "^2.0.2",
|
||||||
"tailwind-merge": "^3.3.1",
|
"svelte": "^4.2.8",
|
||||||
"tailwind-variants": "^3.1.0",
|
"svelte-check": "^3.6.2",
|
||||||
"tailwindcss": "^4.1.7",
|
"svelte-preprocess": "^5.1.3",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss": "^3.3.6",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.3.3",
|
||||||
"typescript-eslint": "^8.42.0",
|
"wait-on": "^7.2.0"
|
||||||
"vite": "^7.1.5",
|
|
||||||
"vite-plugin-devtools-json": "^0.4.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const tailwindcss = require("tailwindcss");
|
||||||
|
const autoprefixer = require("autoprefixer");
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
plugins: [
|
||||||
|
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
|
||||||
|
tailwindcss(),
|
||||||
|
//But others, like autoprefixer, need to run after,
|
||||||
|
autoprefixer,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
const { Titlebar, TitlebarColor } = require("custom-electron-titlebar");
|
||||||
|
const { ipcRenderer } = require("electron");
|
||||||
|
const { appName, appVersion } = require("./electron/appInfo");
|
||||||
|
|
||||||
|
window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const titlebar = new Titlebar({
|
||||||
|
backgroundColor: TitlebarColor.fromHex("#202020"),
|
||||||
|
itemBackgroundColor: TitlebarColor.fromHex("#202020"),
|
||||||
|
menu: null,
|
||||||
|
enableMnemonics: false,
|
||||||
|
maximizable: false,
|
||||||
|
});
|
||||||
|
titlebar.updateTitle(`${appName} ${appVersion}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("login-attempt", async (e) => {
|
||||||
|
const loginResult = await ipcRenderer.invoke("ezpplauncher:login", {
|
||||||
|
username: e.detail.username,
|
||||||
|
password: e.detail.password,
|
||||||
|
saveCredentials: e.detail.saveCredentials,
|
||||||
|
});
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("login-result", { detail: loginResult }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("autologin-active", async (e) => {
|
||||||
|
const autologin = await ipcRenderer.invoke(
|
||||||
|
"ezpplauncher:autologin-active",
|
||||||
|
);
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("autologin-result", { detail: autologin }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("autologin-attempt", async () => {
|
||||||
|
const loginResult = await ipcRenderer.invoke("ezpplauncher:autologin");
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("login-result", { detail: loginResult }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("logout", async () => {
|
||||||
|
await ipcRenderer.invoke("ezpplauncher:logout");
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("guest-login", async () => {
|
||||||
|
await ipcRenderer.invoke("ezpplauncher:guestlogin");
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("launch", async (e) => {
|
||||||
|
await ipcRenderer.invoke("ezpplauncher:launch", e.detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("settings-get", async () => {
|
||||||
|
const settings = await ipcRenderer.invoke("ezpplauncher:settings");
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("settings-result", { detail: settings }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("setting-update", async (e) => {
|
||||||
|
const detail = e.detail;
|
||||||
|
await ipcRenderer.invoke("ezpplauncher:setting-update", detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("folder-auto", async (e) => {
|
||||||
|
const result = await ipcRenderer.invoke("ezpplauncher:detect-folder");
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("settings-result", { detail: result }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("folder-set", async (e) => {
|
||||||
|
const result = await ipcRenderer.invoke("ezpplauncher:set-folder");
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("settings-result", { detail: result }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("settings-set", async (e) => {
|
||||||
|
await ipcRenderer.invoke("ezpplauncher:settings-set", e.detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("updateExit", async () => {
|
||||||
|
await ipcRenderer.invoke("ezpplauncher:exitAndUpdate");
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.addListener("ezpplauncher:launchabort", (e, args) => {
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("launch-abort"),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.addListener("ezpplauncher:alert", (e, args) => {
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("alert", { detail: args }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.addListener("ezpplauncher:launchstatus", (e, args) => {
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("launchStatusUpdate", { detail: args }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.addListener("ezpplauncher:launchprogress", (e, args) => {
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("launchProgressUpdate", { detail: args }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.addListener("ezpplauncher:update", (e, args) => {
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("update", { detail: args }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,63 @@
|
|||||||
|
html, body {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: #333;
|
||||||
|
margin: 0;
|
||||||
|
padding: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgb(0,100,200);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: rgb(0,80,160);
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, button, select, textarea {
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
-webkit-padding: 0.4em 0;
|
||||||
|
padding: 0.4em;
|
||||||
|
margin: 0 0 0.5em 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:disabled {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: #333;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:not(:disabled):active {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus {
|
||||||
|
border-color: #666;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
|
|
||||||
|
<title>EZPPLauncher</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Exo+2:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Prompt:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
||||||
|
<link rel="stylesheet" href="/global.css" />
|
||||||
|
<link rel="stylesheet" href="/build/bundle.css" />
|
||||||
|
|
||||||
|
<script defer src="/build/bundle.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="select-none bg-gray-100 dark:bg-gray-900 overflow-hidden"></body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import svelte from "rollup-plugin-svelte";
|
||||||
|
import commonjs from "@rollup/plugin-commonjs";
|
||||||
|
import resolve from "@rollup/plugin-node-resolve";
|
||||||
|
import livereload from "rollup-plugin-livereload";
|
||||||
|
import terser from "@rollup/plugin-terser";
|
||||||
|
import css from "rollup-plugin-css-only";
|
||||||
|
import postcss from "rollup-plugin-postcss";
|
||||||
|
import image from "@rollup/plugin-image";
|
||||||
|
import sveltePreprocess from "svelte-preprocess";
|
||||||
|
import typescript from "@rollup/plugin-typescript";
|
||||||
|
import progress from "rollup-plugin-progress";
|
||||||
|
|
||||||
|
const production = !process.env.ROLLUP_WATCH;
|
||||||
|
|
||||||
|
function serve() {
|
||||||
|
let server;
|
||||||
|
|
||||||
|
function toExit() {
|
||||||
|
if (server) server.kill(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
writeBundle() {
|
||||||
|
if (server) return;
|
||||||
|
server = require("child_process").spawn("npm", [
|
||||||
|
"run",
|
||||||
|
"start",
|
||||||
|
"--",
|
||||||
|
"--dev",
|
||||||
|
], {
|
||||||
|
stdio: ["ignore", "inherit", "inherit"],
|
||||||
|
shell: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("SIGTERM", toExit);
|
||||||
|
process.on("exit", toExit);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
input: "src/main.ts",
|
||||||
|
output: {
|
||||||
|
sourcemap: true,
|
||||||
|
format: "iife",
|
||||||
|
name: "app",
|
||||||
|
file: "public/build/bundle.js",
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
!production && progress({ clearLine: true }),
|
||||||
|
svelte({
|
||||||
|
preprocess: sveltePreprocess({ sourceMap: !production }),
|
||||||
|
compilerOptions: {
|
||||||
|
// enable run-time checks when not in production
|
||||||
|
dev: !production,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
// we'll extract any component CSS out into
|
||||||
|
// a separate file - better for performance
|
||||||
|
css({ output: "bundle.css" }),
|
||||||
|
postcss({ sourceMap: "inline" }),
|
||||||
|
|
||||||
|
// If you have external dependencies installed from
|
||||||
|
// npm, you'll most likely need these plugins. In
|
||||||
|
// some cases you'll need additional configuration -
|
||||||
|
// consult the documentation for details:
|
||||||
|
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||||
|
resolve({
|
||||||
|
browser: true,
|
||||||
|
dedupe: ["svelte"],
|
||||||
|
}),
|
||||||
|
typescript({
|
||||||
|
sourceMap: !production,
|
||||||
|
inlineSources: !production,
|
||||||
|
}),
|
||||||
|
commonjs(),
|
||||||
|
image(),
|
||||||
|
|
||||||
|
// In dev mode, call `npm run start` once
|
||||||
|
// the bundle has been generated
|
||||||
|
!production && serve(),
|
||||||
|
|
||||||
|
// Watch the `public` directory and refresh the
|
||||||
|
// browser on changes when not in production
|
||||||
|
!production && livereload("public"),
|
||||||
|
|
||||||
|
// If we're building for production (npm run build
|
||||||
|
// instead of npm run dev), minify
|
||||||
|
production && terser(),
|
||||||
|
],
|
||||||
|
watch: {
|
||||||
|
clearScreen: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
import * as toml from '@iarna/toml';
|
|
||||||
import { cwd } from 'process';
|
|
||||||
|
|
||||||
const cargoTomlPath = path.join(cwd(), 'src-tauri', 'Cargo.toml');
|
|
||||||
const tauriConfPath = path.join(cwd(), 'src-tauri', 'tauri.conf.json');
|
|
||||||
|
|
||||||
const cargoToml = fs.readFileSync(cargoTomlPath, 'utf8');
|
|
||||||
const cargo = toml.parse(cargoToml) as Record<string, string>;
|
|
||||||
|
|
||||||
const version = cargo.package['version'];
|
|
||||||
if (!version) throw new Error('Could not find version in Cargo.toml');
|
|
||||||
|
|
||||||
const tauriConf = JSON.parse(fs.readFileSync(tauriConfPath, 'utf8'));
|
|
||||||
if (tauriConf.version !== version) {
|
|
||||||
tauriConf.version = version;
|
|
||||||
|
|
||||||
fs.writeFileSync(tauriConfPath, JSON.stringify(tauriConf, null, 2));
|
|
||||||
console.log(`✅ Synced tauri.conf.json version to ${version}`);
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# Generated by Cargo
|
|
||||||
# will have compiled files and executables
|
|
||||||
/target/
|
|
||||||
|
|
||||||
# Generated by Tauri
|
|
||||||
# will have schema files for capabilities auto-completion
|
|
||||||
/gen/schemas
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ezpplauncher"
|
|
||||||
version = "3.0.7"
|
|
||||||
description = "EZPPLauncher redefined."
|
|
||||||
authors = ["HorizonCode"]
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
# The `_lib` suffix may seem redundant but it is necessary
|
|
||||||
# to make the lib name unique and wouldn't conflict with the bin name.
|
|
||||||
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
|
||||||
name = "ezpplauncher_lib"
|
|
||||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
tauri-build = { version = "2.4.1", features = [] }
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
tauri = { version = "2.8.5", features = [] }
|
|
||||||
tauri-plugin-shell = "2.3.1"
|
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
|
||||||
serde_json = "1.0.143"
|
|
||||||
serde_repr = "0.1.20"
|
|
||||||
tauri-plugin-dialog = "2.4.0"
|
|
||||||
tauri-plugin-fs = "2.4.2"
|
|
||||||
hardware-id = "0.3.0"
|
|
||||||
tauri-plugin-cors-fetch = "4.1.0"
|
|
||||||
sysinfo = "0.37.0"
|
|
||||||
reqwest = { version = "0.12.23", features = ["json", "stream"] }
|
|
||||||
md5 = "0.8.0"
|
|
||||||
tokio = { version = "1.47.1", features = ["full"] }
|
|
||||||
open = "5.3.2"
|
|
||||||
windows-sys = "0.61.0"
|
|
||||||
discord-rich-presence = "1.0.0"
|
|
||||||
once_cell = "1.21.3"
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
|
||||||
winreg = "0.55.0"
|
|
||||||
winapi = { version = "0.3", features = ["winuser", "wincrypt", "memoryapi", "winbase", "dpapi"] }
|
|
||||||
base64 = "0.22.1"
|
|
||||||
widestring = "1.0"
|
|
||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
|
||||||
tauri-plugin-single-instance = "2.3.4"
|
|
||||||
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
tauri_build::build()
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "../gen/schemas/desktop-schema.json",
|
|
||||||
"identifier": "default",
|
|
||||||
"description": "Capability for the main window",
|
|
||||||
"windows": ["main"],
|
|
||||||
"permissions": [
|
|
||||||
"core:default",
|
|
||||||
"shell:allow-open",
|
|
||||||
"shell:default",
|
|
||||||
"shell:allow-execute",
|
|
||||||
"dialog:default",
|
|
||||||
"core:window:allow-start-dragging",
|
|
||||||
"core:window:allow-minimize",
|
|
||||||
"core:window:allow-close",
|
|
||||||
"cors-fetch:default",
|
|
||||||
"core:window:allow-hide",
|
|
||||||
"core:window:allow-show",
|
|
||||||
"fs:default",
|
|
||||||
{
|
|
||||||
"identifier": "fs:allow-read",
|
|
||||||
"allow": [
|
|
||||||
{ "path": "$HOME/.ezpplauncher" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/*" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/**/*" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identifier": "fs:allow-write",
|
|
||||||
"allow": [
|
|
||||||
{ "path": "$HOME/.ezpplauncher" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/*" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/**/*" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identifier": "fs:allow-exists",
|
|
||||||
"allow": [
|
|
||||||
{ "path": "$HOME/.ezpplauncher" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/*" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/**/*" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identifier": "fs:allow-read-file",
|
|
||||||
"allow": [
|
|
||||||
{ "path": "$HOME/.ezpplauncher" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/*" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/**/*" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identifier": "fs:allow-write-file",
|
|
||||||
"allow": [
|
|
||||||
{ "path": "$HOME/.ezpplauncher" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/*" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/**/*" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identifier": "fs:allow-read-text-file",
|
|
||||||
"allow": [
|
|
||||||
{ "path": "$HOME/.ezpplauncher" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/*" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/**/*" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identifier": "fs:allow-mkdir",
|
|
||||||
"allow": [
|
|
||||||
{ "path": "$HOME/.ezpplauncher" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/*" },
|
|
||||||
{ "path": "$HOME/.ezpplauncher/**/*" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 133 KiB |
@@ -1,6 +0,0 @@
|
|||||||
!macro NSIS_HOOK_POSTINSTALL
|
|
||||||
${If} $PassiveMode = 1
|
|
||||||
${OrIf} ${Silent}
|
|
||||||
Exec '"$INSTDIR\${MAINBINARYNAME}.exe"'
|
|
||||||
${EndIf}
|
|
||||||
!macroend
|
|
||||||
@@ -1,808 +0,0 @@
|
|||||||
use hardware_id::get_id;
|
|
||||||
use reqwest::Client;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use sysinfo::System;
|
|
||||||
use tauri::AppHandle;
|
|
||||||
use tauri::Emitter;
|
|
||||||
use tauri::Manager;
|
|
||||||
use tokio::fs;
|
|
||||||
use tokio::io::AsyncWriteExt;
|
|
||||||
use tokio::process::Command;
|
|
||||||
use tokio::time::{Duration, sleep};
|
|
||||||
|
|
||||||
use crate::presence;
|
|
||||||
use crate::utils::{
|
|
||||||
check_folder_completeness, encrypt_password, get_osu_config, get_osu_user_config,
|
|
||||||
get_window_title_by_pid, is_net8_installed, is_osuwinello_available, is_wmctrl_available,
|
|
||||||
set_osu_config_vals, set_osu_user_config_vals,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn get_launcher_version() -> String {
|
|
||||||
env!("CARGO_PKG_VERSION").to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn get_hwid() -> String {
|
|
||||||
let hwid = get_id().unwrap();
|
|
||||||
hwid.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command(rename_all = "snake_case")]
|
|
||||||
pub fn valid_osu_folder(folder: String) -> bool {
|
|
||||||
let osu_folder_files = [
|
|
||||||
"avcodec-51.dll",
|
|
||||||
"avformat-52.dll",
|
|
||||||
"avutil-49.dll",
|
|
||||||
"bass.dll",
|
|
||||||
"bass_fx.dll",
|
|
||||||
"collection.db",
|
|
||||||
"d3dcompiler_47.dll",
|
|
||||||
"libEGL.dll",
|
|
||||||
"libGLESv2.dll",
|
|
||||||
"Microsoft.Ink.dll",
|
|
||||||
"OpenTK.dll",
|
|
||||||
"osu!.cfg",
|
|
||||||
"osu!.db",
|
|
||||||
"osu!.exe",
|
|
||||||
"osu!auth.dll",
|
|
||||||
"osu!gameplay.dll",
|
|
||||||
"osu!seasonal.dll",
|
|
||||||
"osu!ui.dll",
|
|
||||||
"presence.db",
|
|
||||||
"pthreadGC2.dll",
|
|
||||||
"scores.db",
|
|
||||||
];
|
|
||||||
|
|
||||||
let folder_files: Vec<&str> = osu_folder_files.iter().map(|&s| s).collect();
|
|
||||||
if folder_files.iter().any(|&file| file == "osu!.exe") == false {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = PathBuf::from(folder);
|
|
||||||
let match_percentage = check_folder_completeness(path, &osu_folder_files) >= 70.0;
|
|
||||||
if match_percentage {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn find_osu_installation() -> Option<String> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn find_osu_installation() -> Option<String> {
|
|
||||||
use winreg::RegKey;
|
|
||||||
use winreg::enums::*;
|
|
||||||
|
|
||||||
let hklm_registry_paths = ["SOFTWARE\\Classes\\osu\\DefaultIcon"];
|
|
||||||
|
|
||||||
let hkcr_registry_paths = [
|
|
||||||
"osustable.File.osk\\DefaultIcon",
|
|
||||||
"osustable.File.osr\\DefaultIcon",
|
|
||||||
"osustable.File.osz\\DefaultIcon",
|
|
||||||
];
|
|
||||||
|
|
||||||
let osu_folder_files = [
|
|
||||||
"avcodec-51.dll",
|
|
||||||
"avformat-52.dll",
|
|
||||||
"avutil-49.dll",
|
|
||||||
"bass.dll",
|
|
||||||
"bass_fx.dll",
|
|
||||||
"collection.db",
|
|
||||||
"d3dcompiler_47.dll",
|
|
||||||
"libEGL.dll",
|
|
||||||
"libGLESv2.dll",
|
|
||||||
"Microsoft.Ink.dll",
|
|
||||||
"OpenTK.dll",
|
|
||||||
"osu!.cfg",
|
|
||||||
"osu!.db",
|
|
||||||
"osu!.exe",
|
|
||||||
"osu!auth.dll",
|
|
||||||
"osu!gameplay.dll",
|
|
||||||
"osu!seasonal.dll",
|
|
||||||
"osu!ui.dll",
|
|
||||||
"presence.db",
|
|
||||||
"pthreadGC2.dll",
|
|
||||||
"scores.db",
|
|
||||||
];
|
|
||||||
|
|
||||||
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
|
|
||||||
|
|
||||||
for reg_path in &hklm_registry_paths {
|
|
||||||
if let Ok(subkey) = hklm.open_subkey_with_flags(reg_path, KEY_READ | KEY_WOW64_32KEY) {
|
|
||||||
let value_names = [""];
|
|
||||||
for value_name in &value_names {
|
|
||||||
if let Ok(value) = subkey.get_value::<String, _>(value_name) {
|
|
||||||
let trimmed = value.trim_matches('"');
|
|
||||||
let stripped = trimmed.strip_suffix(",0").unwrap_or(trimmed);
|
|
||||||
let path = PathBuf::from(stripped.trim());
|
|
||||||
if let Some(parent) = path.parent() {
|
|
||||||
let match_percentage = check_folder_completeness(parent, &osu_folder_files);
|
|
||||||
|
|
||||||
if match_percentage >= 70.0 {
|
|
||||||
return Some(parent.to_string_lossy().into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let hkcr = RegKey::predef(HKEY_CLASSES_ROOT);
|
|
||||||
|
|
||||||
for reg_path in &hkcr_registry_paths {
|
|
||||||
if let Ok(subkey) = hkcr.open_subkey_with_flags(reg_path, KEY_READ | KEY_WOW64_32KEY) {
|
|
||||||
let value_names = [""];
|
|
||||||
for value_name in &value_names {
|
|
||||||
if let Ok(value) = subkey.get_value::<String, _>(value_name) {
|
|
||||||
let trimmed = value.trim_matches('"');
|
|
||||||
let stripped = trimmed.strip_suffix(",1").unwrap_or(trimmed);
|
|
||||||
let path = PathBuf::from(stripped.trim());
|
|
||||||
if let Some(parent) = path.parent() {
|
|
||||||
let match_percentage = check_folder_completeness(parent, &osu_folder_files);
|
|
||||||
|
|
||||||
if match_percentage >= 70.0 {
|
|
||||||
return Some(parent.to_string_lossy().into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn get_beatmapsets_count(folder: String) -> Option<u64> {
|
|
||||||
let path = PathBuf::from(folder);
|
|
||||||
let osu_user_config = get_osu_user_config(path.clone());
|
|
||||||
let songs_path = osu_user_config
|
|
||||||
.and_then(|config| config.get("Songs").cloned())
|
|
||||||
.unwrap_or_else(|| path.join("Songs").to_string_lossy().into_owned());
|
|
||||||
let songs_folder = PathBuf::from(songs_path);
|
|
||||||
|
|
||||||
if !songs_folder.exists() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut count = 0;
|
|
||||||
if let Ok(entries) = std::fs::read_dir(songs_folder) {
|
|
||||||
for entry in entries.flatten() {
|
|
||||||
if entry.file_type().map_or(false, |ft| ft.is_dir()) {
|
|
||||||
let dir_path = entry.path();
|
|
||||||
if let Ok(files) = std::fs::read_dir(&dir_path) {
|
|
||||||
for file in files.flatten() {
|
|
||||||
if file.path().extension().map_or(false, |ext| ext == "osu") {
|
|
||||||
count += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Some(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn get_skins_count(folder: String) -> Option<u64> {
|
|
||||||
let path = PathBuf::from(folder);
|
|
||||||
let skins_folder = path.join("Skins");
|
|
||||||
|
|
||||||
if !skins_folder.exists() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut count = 0;
|
|
||||||
if let Ok(entries) = std::fs::read_dir(skins_folder) {
|
|
||||||
for entry in entries.flatten() {
|
|
||||||
if entry.file_type().map_or(false, |ft| ft.is_dir()) {
|
|
||||||
let dir_path = entry.path();
|
|
||||||
if let Ok(files) = std::fs::read_dir(&dir_path) {
|
|
||||||
for file in files.flatten() {
|
|
||||||
if file.path().extension().map_or(false, |ext| ext == "ini") {
|
|
||||||
count += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Some(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn get_osu_skin(folder: String) -> String {
|
|
||||||
let path = PathBuf::from(folder);
|
|
||||||
let osu_user_config = get_osu_user_config(path.clone());
|
|
||||||
return osu_user_config
|
|
||||||
.and_then(|config| config.get("Skin").cloned())
|
|
||||||
.unwrap_or_else(|| "Default".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn get_osu_version(folder: String) -> String {
|
|
||||||
let path = PathBuf::from(folder);
|
|
||||||
let osu_user_config = get_osu_user_config(path.clone());
|
|
||||||
return osu_user_config
|
|
||||||
.and_then(|config| config.get("LastVersion").cloned())
|
|
||||||
.unwrap_or_else(|| "failed".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn get_osu_release_stream(folder: String) -> String {
|
|
||||||
let path = PathBuf::from(folder);
|
|
||||||
let osu_config = get_osu_config(path.clone());
|
|
||||||
return osu_config
|
|
||||||
.and_then(|config| config.get("_ReleaseStream").cloned())
|
|
||||||
.unwrap_or_else(|| "Stable40".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
|
||||||
pub struct ConfigEntry {
|
|
||||||
pub key: String,
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn set_osu_user_config_values(
|
|
||||||
osu_folder_path: String,
|
|
||||||
entries: Vec<ConfigEntry>,
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
let converted: Vec<(&str, Option<&str>)> = entries
|
|
||||||
.iter()
|
|
||||||
.map(|entry| (entry.key.as_str(), Some(entry.value.as_str())))
|
|
||||||
.collect();
|
|
||||||
match set_osu_user_config_vals(&osu_folder_path, &converted) {
|
|
||||||
Ok(_) => Ok(true),
|
|
||||||
Err(_) => Ok(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn set_osu_config_values(
|
|
||||||
osu_folder_path: String,
|
|
||||||
entries: Vec<ConfigEntry>,
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
let converted: Vec<(&str, Option<&str>)> = entries
|
|
||||||
.iter()
|
|
||||||
.map(|entry| (entry.key.as_str(), Some(entry.value.as_str())))
|
|
||||||
.collect();
|
|
||||||
match set_osu_config_vals(&osu_folder_path, &converted) {
|
|
||||||
Ok(_) => Ok(true),
|
|
||||||
Err(_) => Ok(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn run_osu_updater(folder: String) -> Result<(), String> {
|
|
||||||
#[cfg(windows)]
|
|
||||||
const DETACHED_PROCESS: u32 = 0x00000008;
|
|
||||||
#[cfg(windows)]
|
|
||||||
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
|
|
||||||
|
|
||||||
let mut updater_process = {
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
let osu_exe_path = PathBuf::from(&folder).join("osu!.exe");
|
|
||||||
Command::new(&osu_exe_path)
|
|
||||||
.arg("-repair")
|
|
||||||
.creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
|
|
||||||
.spawn()
|
|
||||||
.map_err(|e| format!("Failed to spawn updater: {}", e))?
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
{
|
|
||||||
Command::new("osu-wine")
|
|
||||||
.arg("-repair")
|
|
||||||
.spawn()
|
|
||||||
.map_err(|e| format!("Failed to spawn updater: {}", e))?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
sleep(Duration::from_millis(500)).await;
|
|
||||||
|
|
||||||
let mut sys = System::new_all();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
|
|
||||||
|
|
||||||
let mut found = false;
|
|
||||||
|
|
||||||
for (_pid, process) in sys.processes() {
|
|
||||||
if process.name() == "osu!.exe" {
|
|
||||||
let pid = process.pid();
|
|
||||||
let title = get_window_title_by_pid(pid);
|
|
||||||
|
|
||||||
if !title.is_empty() && !title.contains("updater") {
|
|
||||||
let _ = process.kill_and_wait();
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if found {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sleep(Duration::from_millis(500)).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = updater_process.wait().await;
|
|
||||||
|
|
||||||
let force_update_files = [".require_update", "help.txt", "_pending"];
|
|
||||||
for update_file_name in &force_update_files {
|
|
||||||
let path = PathBuf::from(&folder).join(update_file_name);
|
|
||||||
if path.exists() {
|
|
||||||
match std::fs::symlink_metadata(&path) {
|
|
||||||
Ok(meta) => {
|
|
||||||
let res = if meta.is_dir() {
|
|
||||||
std::fs::remove_dir_all(&path)
|
|
||||||
} else {
|
|
||||||
std::fs::remove_file(&path)
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(e) = res {
|
|
||||||
eprintln!("Failed to remove {:?}: {}", path, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Could not stat {:?}: {}", path, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn run_osu(folder: String, patch: bool) -> Result<(), String> {
|
|
||||||
/* #[cfg(windows)]
|
|
||||||
use std::os::windows::process::CommandExt; */
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
const DETACHED_PROCESS: u32 = 0x00000008;
|
|
||||||
#[cfg(windows)]
|
|
||||||
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
|
|
||||||
|
|
||||||
let mut game_process = {
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
let osu_exe_path = PathBuf::from(&folder).join("osu!.exe");
|
|
||||||
Command::new(&osu_exe_path)
|
|
||||||
.arg("-devserver")
|
|
||||||
.arg("ez-pp.farm")
|
|
||||||
.creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
|
|
||||||
.spawn()
|
|
||||||
.map_err(|e| format!("Failed to spawn updater: {}", e))?
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
{
|
|
||||||
Command::new("osu-wine")
|
|
||||||
.arg("--devserver")
|
|
||||||
.arg("ez-pp.farm")
|
|
||||||
.spawn()
|
|
||||||
.map_err(|e| format!("Failed to spawn updater: {}", e))?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if patch {
|
|
||||||
sleep(Duration::from_secs(3)).await;
|
|
||||||
|
|
||||||
let patcher_exe_path = PathBuf::from(&folder)
|
|
||||||
.join("EZPPLauncher")
|
|
||||||
.join("patcher")
|
|
||||||
.join("osu!.patcher.exe");
|
|
||||||
|
|
||||||
if patcher_exe_path.exists() {
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
let _ = Command::new(&patcher_exe_path)
|
|
||||||
.creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
|
|
||||||
.spawn()
|
|
||||||
.map_err(|e| format!("Failed to run patcher: {e}"))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
game_process.wait().await.map_err(|e| e.to_string())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UpdateFile {
|
|
||||||
pub name: String,
|
|
||||||
pub folder: String,
|
|
||||||
pub url: String,
|
|
||||||
pub size: usize,
|
|
||||||
pub md5: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_ezpp_launcher_update_files(
|
|
||||||
folder: String,
|
|
||||||
update_url: String,
|
|
||||||
update_stream: String,
|
|
||||||
) -> Result<(Vec<UpdateFile>, Vec<UpdateFile>), String> {
|
|
||||||
let osu_path = PathBuf::from(folder);
|
|
||||||
let client = Client::new();
|
|
||||||
|
|
||||||
let update_files = client
|
|
||||||
.patch(update_url)
|
|
||||||
.header("User-Agent", "EZPPLauncher")
|
|
||||||
.query(&[("stream", update_stream)])
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?
|
|
||||||
.json::<Vec<UpdateFile>>()
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
let mut files_to_download = Vec::new();
|
|
||||||
|
|
||||||
for file in &update_files {
|
|
||||||
let file_path = osu_path.join(&file.folder).join(&file.name);
|
|
||||||
if file_path.exists() {
|
|
||||||
let data = fs::read(&file_path).await.map_err(|e| e.to_string())?;
|
|
||||||
let hash = format!("{:x}", md5::compute(&data));
|
|
||||||
if hash.to_lowercase() != file.md5.to_lowercase() {
|
|
||||||
files_to_download.push(file.clone());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
files_to_download.push(file.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((files_to_download, update_files))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UpdateStatus {
|
|
||||||
pub file_name: String,
|
|
||||||
pub downloaded: u64,
|
|
||||||
pub size: usize,
|
|
||||||
pub progress: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn download_ezpp_launcher_update_files(
|
|
||||||
app: AppHandle,
|
|
||||||
folder: String,
|
|
||||||
update_files: Vec<UpdateFile>,
|
|
||||||
all_files: Vec<UpdateFile>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let osu_path = PathBuf::from(folder);
|
|
||||||
let client = Client::new();
|
|
||||||
|
|
||||||
let valid_paths: HashSet<PathBuf> = all_files
|
|
||||||
.iter()
|
|
||||||
.map(|f| osu_path.join(&f.folder).join(&f.name))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for folder in all_files
|
|
||||||
.iter()
|
|
||||||
.map(|f| osu_path.join(&f.folder))
|
|
||||||
.collect::<HashSet<_>>()
|
|
||||||
{
|
|
||||||
if folder.exists() && folder != osu_path {
|
|
||||||
let mut dir = fs::read_dir(&folder).await.map_err(|e| e.to_string())?;
|
|
||||||
while let Some(entry) = dir.next_entry().await.map_err(|e| e.to_string())? {
|
|
||||||
let path = entry.path();
|
|
||||||
if !valid_paths.contains(&path) {
|
|
||||||
fs::remove_file(&path).await.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for file in update_files {
|
|
||||||
let file_path = osu_path.join(&file.folder).join(&file.name);
|
|
||||||
let parent = file_path.parent().unwrap();
|
|
||||||
if !parent.exists() {
|
|
||||||
fs::create_dir_all(parent)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut response = client
|
|
||||||
.get(&file.url)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
let mut file_out = fs::File::create(&file_path)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
let mut downloaded = 0u64;
|
|
||||||
|
|
||||||
while let Some(chunk) = response.chunk().await.map_err(|e| e.to_string())? {
|
|
||||||
downloaded += chunk.len() as u64;
|
|
||||||
file_out
|
|
||||||
.write_all(&chunk)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
app.emit(
|
|
||||||
"download-progress",
|
|
||||||
UpdateStatus {
|
|
||||||
file_name: file.name.clone(),
|
|
||||||
downloaded,
|
|
||||||
size: file.size,
|
|
||||||
progress: ((downloaded as f64 / file.size as f64 * 100.0) * 100.0).trunc()
|
|
||||||
/ 100.0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap_or_default();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(tag = "type", content = "details")]
|
|
||||||
pub enum ReplaceUIError {
|
|
||||||
FileNotFound(String),
|
|
||||||
PermissionDenied(String),
|
|
||||||
IoError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn replace_ui_files(folder: String, revert: bool) -> Result<(), ReplaceUIError> {
|
|
||||||
let osu_path = PathBuf::from(folder);
|
|
||||||
let ezpp_ui = osu_path.join("EZPPLauncher").join("ezpp!ui.dll");
|
|
||||||
let osu_ui = osu_path.join("osu!ui.dll");
|
|
||||||
let ezpp_gameplay = osu_path.join("EZPPLauncher").join("ezpp!gameplay.dll");
|
|
||||||
let osu_gameplay = osu_path.join("osu!gameplay.dll");
|
|
||||||
|
|
||||||
let osu_ui_bak = osu_path.join("osu!ui.dll.bak");
|
|
||||||
let osu_gameplay_bak = osu_path.join("osu!gameplay.dll.bak");
|
|
||||||
|
|
||||||
let try_rename = |from: &PathBuf, to: &PathBuf| -> Result<(), ReplaceUIError> {
|
|
||||||
if !from.exists() {
|
|
||||||
return Err(ReplaceUIError::FileNotFound(from.display().to_string()));
|
|
||||||
}
|
|
||||||
std::fs::rename(from, to).map_err(|e| match e.kind() {
|
|
||||||
std::io::ErrorKind::NotFound => {
|
|
||||||
ReplaceUIError::FileNotFound(from.display().to_string())
|
|
||||||
}
|
|
||||||
std::io::ErrorKind::PermissionDenied => {
|
|
||||||
ReplaceUIError::PermissionDenied(from.display().to_string())
|
|
||||||
}
|
|
||||||
_ => ReplaceUIError::IoError(e.to_string()),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
if !revert {
|
|
||||||
try_rename(&osu_ui, &osu_ui_bak)?;
|
|
||||||
try_rename(&ezpp_ui, &osu_ui)?;
|
|
||||||
|
|
||||||
try_rename(&osu_gameplay, &osu_gameplay_bak)?;
|
|
||||||
try_rename(&ezpp_gameplay, &osu_gameplay)?;
|
|
||||||
} else {
|
|
||||||
try_rename(&osu_ui, &ezpp_ui)?;
|
|
||||||
try_rename(&osu_ui_bak, &osu_ui)?;
|
|
||||||
|
|
||||||
try_rename(&osu_gameplay, &ezpp_gameplay)?;
|
|
||||||
try_rename(&osu_gameplay_bak, &osu_gameplay)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn is_osu_running() -> bool {
|
|
||||||
let mut sys = System::new_all();
|
|
||||||
sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
|
|
||||||
|
|
||||||
for process in sys.processes().values() {
|
|
||||||
if process.name().eq_ignore_ascii_case("osu!.exe") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn open_url_in_browser(url: String) -> Result<(), String> {
|
|
||||||
open::that(&url).map_err(|e| format!("Failed to open URL: {}", e))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn exit(app: AppHandle) {
|
|
||||||
app.exit(0x0100);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn get_platform() -> String {
|
|
||||||
std::env::consts::OS.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn check_for_corruption(folder: String) -> Result<bool, String> {
|
|
||||||
let osu_path = PathBuf::from(folder);
|
|
||||||
let osu_ui = osu_path.join("osu!ui.dll");
|
|
||||||
let osu_gameplay = osu_path.join("osu!gameplay.dll");
|
|
||||||
|
|
||||||
let osu_ui_bak = osu_path.join("osu!ui.dll.bak");
|
|
||||||
let osu_gameplay_bak = osu_path.join("osu!gameplay.dll.bak");
|
|
||||||
|
|
||||||
let required_files = [&osu_ui, &osu_gameplay];
|
|
||||||
|
|
||||||
for file in &required_files {
|
|
||||||
if !file.exists() {
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let bak_files = [&osu_ui_bak, &osu_gameplay_bak];
|
|
||||||
for bak in &bak_files {
|
|
||||||
if bak.exists() {
|
|
||||||
if let Err(e) = fs::remove_file(bak).await {
|
|
||||||
return Err(format!("Failed to delete {}: {}", bak.display(), e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn presence_connect() -> bool {
|
|
||||||
presence::connect().await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn presence_disconnect() {
|
|
||||||
presence::disconnect().await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct PresenceStatus {
|
|
||||||
state: Option<String>,
|
|
||||||
details: Option<String>,
|
|
||||||
large_image_key: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn presence_update_status(status: PresenceStatus) {
|
|
||||||
presence::update_status(
|
|
||||||
status.state.as_deref(),
|
|
||||||
status.details.as_deref(),
|
|
||||||
status.large_image_key.as_deref(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct PresenceUser {
|
|
||||||
username: Option<String>,
|
|
||||||
id: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn presence_update_user(user: PresenceUser) {
|
|
||||||
presence::update_user(user.username.as_deref(), user.id.as_deref());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn presence_is_connected() -> bool {
|
|
||||||
presence::has_presence().await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn has_wmctrl() -> bool {
|
|
||||||
is_wmctrl_available()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn has_osuwinello() -> bool {
|
|
||||||
is_osuwinello_available()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn has_net8() -> bool {
|
|
||||||
is_net8_installed().await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn encrypt_string(string: String, entropy: String) -> String {
|
|
||||||
let encrypted = encrypt_password(&string, &entropy);
|
|
||||||
|
|
||||||
match encrypted {
|
|
||||||
Ok(encrypted_vec) => encrypted_vec,
|
|
||||||
Err(_) => string,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn download_ezpp_launcher_update(app: AppHandle, url: String) -> Result<(), String> {
|
|
||||||
let client = Client::new();
|
|
||||||
|
|
||||||
let mut response = client.get(&url).send().await.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
if !response.status().is_success() {
|
|
||||||
return Err(format!("Failed to download update: {}", response.status()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let temp_dir = app.path().temp_dir().expect("Failed to get temp directory");
|
|
||||||
let file_path = temp_dir.join("ezpplauncher_update.exe");
|
|
||||||
|
|
||||||
let mut file_out = fs::File::create(&file_path)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
let mut downloaded = 0u64;
|
|
||||||
let size = response
|
|
||||||
.content_length()
|
|
||||||
.ok_or("Failed to get content length")? as usize;
|
|
||||||
|
|
||||||
while let Some(chunk) = response.chunk().await.map_err(|e| e.to_string())? {
|
|
||||||
downloaded += chunk.len() as u64;
|
|
||||||
file_out
|
|
||||||
.write_all(&chunk)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
app.emit(
|
|
||||||
"download-progress",
|
|
||||||
UpdateStatus {
|
|
||||||
file_name: "Update".to_string(),
|
|
||||||
downloaded,
|
|
||||||
size: size,
|
|
||||||
progress: ((downloaded as f64 / size as f64 * 100.0) * 100.0).trunc() / 100.0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap_or_default();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn install_ezpp_launcher_update(app: AppHandle) -> Result<(), String> {
|
|
||||||
let temp_dir = app.path().temp_dir().expect("Failed to get temp directory");
|
|
||||||
let file_path = temp_dir.join("ezpplauncher_update.exe");
|
|
||||||
if !file_path.exists() {
|
|
||||||
return Err("Update file does not exist".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// run this app detached and exit
|
|
||||||
|
|
||||||
const DETACHED_PROCESS: u32 = 0x00000008;
|
|
||||||
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
|
|
||||||
|
|
||||||
Command::new(&file_path)
|
|
||||||
.arg("/S")
|
|
||||||
.creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
|
|
||||||
.spawn()
|
|
||||||
.map_err(|e| format!("Failed to spawn updater: {}", e))?;
|
|
||||||
|
|
||||||
sleep(Duration::from_millis(250)).await;
|
|
||||||
|
|
||||||
app.exit(0x0100);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn install_ezpp_launcher_update(_app: AppHandle) -> Result<(), String> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
|
||||||
use tauri::Manager;
|
|
||||||
|
|
||||||
pub mod commands;
|
|
||||||
pub mod presence;
|
|
||||||
pub mod utils;
|
|
||||||
use crate::commands::{
|
|
||||||
check_for_corruption, download_ezpp_launcher_update, download_ezpp_launcher_update_files,
|
|
||||||
encrypt_string, exit, find_osu_installation, get_beatmapsets_count,
|
|
||||||
get_ezpp_launcher_update_files, get_hwid, get_launcher_version, get_osu_release_stream,
|
|
||||||
get_osu_skin, get_osu_version, get_platform, get_skins_count, has_net8, has_osuwinello,
|
|
||||||
has_wmctrl, install_ezpp_launcher_update, is_osu_running, open_url_in_browser,
|
|
||||||
presence_connect, presence_disconnect, presence_is_connected, presence_update_status,
|
|
||||||
presence_update_user, replace_ui_files, run_osu, run_osu_updater, set_osu_config_values,
|
|
||||||
set_osu_user_config_values, valid_osu_folder,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
||||||
pub fn run() {
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
unsafe {
|
|
||||||
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut builder = tauri::Builder::default();
|
|
||||||
#[cfg(desktop)]
|
|
||||||
{
|
|
||||||
builder = builder.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
|
||||||
let app_window = app.get_webview_window("main").expect("no main window");
|
|
||||||
app_window.set_focus().expect("failed to focus");
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
let app = builder
|
|
||||||
.invoke_handler(tauri::generate_handler![
|
|
||||||
get_hwid,
|
|
||||||
find_osu_installation,
|
|
||||||
valid_osu_folder,
|
|
||||||
get_beatmapsets_count,
|
|
||||||
get_skins_count,
|
|
||||||
get_osu_version,
|
|
||||||
get_osu_release_stream,
|
|
||||||
set_osu_config_values,
|
|
||||||
set_osu_user_config_values,
|
|
||||||
run_osu_updater,
|
|
||||||
run_osu,
|
|
||||||
get_osu_skin,
|
|
||||||
get_ezpp_launcher_update_files,
|
|
||||||
download_ezpp_launcher_update_files,
|
|
||||||
replace_ui_files,
|
|
||||||
is_osu_running,
|
|
||||||
open_url_in_browser,
|
|
||||||
get_launcher_version,
|
|
||||||
exit,
|
|
||||||
get_platform,
|
|
||||||
check_for_corruption,
|
|
||||||
presence_connect,
|
|
||||||
presence_disconnect,
|
|
||||||
presence_update_status,
|
|
||||||
presence_update_user,
|
|
||||||
presence_is_connected,
|
|
||||||
has_osuwinello,
|
|
||||||
has_wmctrl,
|
|
||||||
has_net8,
|
|
||||||
encrypt_string,
|
|
||||||
download_ezpp_launcher_update,
|
|
||||||
install_ezpp_launcher_update
|
|
||||||
])
|
|
||||||
.plugin(tauri_plugin_fs::init())
|
|
||||||
.plugin(tauri_plugin_dialog::init())
|
|
||||||
.plugin(tauri_plugin_shell::init())
|
|
||||||
.plugin(tauri_plugin_cors_fetch::init())
|
|
||||||
.build(tauri::generate_context!())
|
|
||||||
.expect("error while building tauri application");
|
|
||||||
|
|
||||||
app.run(|_app_handle, event| {
|
|
||||||
if let tauri::RunEvent::ExitRequested { api, .. } = event {
|
|
||||||
api.prevent_exit();
|
|
||||||
|
|
||||||
tauri::async_runtime::block_on(presence::disconnect());
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
ezpplauncher_lib::run()
|
|
||||||
}
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
use discord_rich_presence::{
|
|
||||||
DiscordIpc, DiscordIpcClient,
|
|
||||||
activity::{Activity, Assets, Button, Timestamps},
|
|
||||||
};
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use std::sync::Mutex as StdMutex;
|
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
|
||||||
use tokio::sync::{mpsc, oneshot};
|
|
||||||
use tokio::time::{Duration, interval};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PresenceData {
|
|
||||||
pub state: String,
|
|
||||||
pub details: String,
|
|
||||||
pub large_image_key: String,
|
|
||||||
pub large_image_text: String,
|
|
||||||
pub small_image_key: Option<String>,
|
|
||||||
pub small_image_text: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum PresenceCommand {
|
|
||||||
Connect(oneshot::Sender<bool>),
|
|
||||||
Disconnect(oneshot::Sender<()>),
|
|
||||||
UpdateData(PresenceData),
|
|
||||||
IsConnected(oneshot::Sender<bool>),
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PresenceActor {
|
|
||||||
receiver: mpsc::Receiver<PresenceCommand>,
|
|
||||||
client: Option<DiscordIpcClient>,
|
|
||||||
data: PresenceData,
|
|
||||||
start_timestamp: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PresenceActor {
|
|
||||||
fn new(receiver: mpsc::Receiver<PresenceCommand>) -> Self {
|
|
||||||
let start = SystemTime::now()
|
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_secs() as i64;
|
|
||||||
|
|
||||||
let data = PresenceData {
|
|
||||||
state: "Idle in Launcher...".to_string(),
|
|
||||||
details: " ".to_string(),
|
|
||||||
large_image_key: "ezppfarm".to_string(),
|
|
||||||
large_image_text: "EZPPFarm".to_string(),
|
|
||||||
small_image_key: None,
|
|
||||||
small_image_text: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
PresenceActor {
|
|
||||||
receiver,
|
|
||||||
client: None,
|
|
||||||
data,
|
|
||||||
start_timestamp: start,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(&mut self) {
|
|
||||||
let mut update_interval = interval(Duration::from_millis(2500));
|
|
||||||
|
|
||||||
loop {
|
|
||||||
tokio::select! {
|
|
||||||
Some(cmd) = self.receiver.recv() => {
|
|
||||||
match cmd {
|
|
||||||
PresenceCommand::Connect(responder) => self.handle_connect(responder).await,
|
|
||||||
PresenceCommand::Disconnect(responder) => {
|
|
||||||
self.handle_disconnect(responder).await;
|
|
||||||
},
|
|
||||||
PresenceCommand::UpdateData(new_data) => {
|
|
||||||
self.data = new_data;
|
|
||||||
},
|
|
||||||
PresenceCommand::IsConnected(responder) => {
|
|
||||||
let _ = responder.send(self.client.is_some());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ = update_interval.tick() => {
|
|
||||||
if self.client.is_some() {
|
|
||||||
self.handle_update().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_connect(&mut self, responder: oneshot::Sender<bool>) {
|
|
||||||
if self.client.is_some() {
|
|
||||||
let _ = responder.send(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Actor: Connecting to Discord...");
|
|
||||||
let mut dc_client = DiscordIpcClient::new("1032772293220384808");
|
|
||||||
match dc_client.connect() {
|
|
||||||
Ok(()) => {
|
|
||||||
self.client = Some(dc_client);
|
|
||||||
println!("Actor: Connected successfully.");
|
|
||||||
self.handle_update().await;
|
|
||||||
let _ = responder.send(true);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Failed to create Discord client: {:?}", e);
|
|
||||||
let _ = responder.send(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_disconnect(&mut self, responder: oneshot::Sender<()>) {
|
|
||||||
if let Some(mut client) = self.client.take() {
|
|
||||||
println!("Actor: Disconnecting...");
|
|
||||||
let _ = client.clear_activity().map_err(|e| e.to_string());
|
|
||||||
let _ = client.close().map_err(|e| e.to_string());
|
|
||||||
println!("Actor: Disconnected successfully.");
|
|
||||||
}
|
|
||||||
let _ = responder.send(());
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_update(&mut self) {
|
|
||||||
if let Some(client) = self.client.as_mut() {
|
|
||||||
let mut assets = Assets::new()
|
|
||||||
.large_image(&self.data.large_image_key)
|
|
||||||
.large_text(&self.data.large_image_text);
|
|
||||||
|
|
||||||
if let Some(key) = &self.data.small_image_key {
|
|
||||||
assets = assets.small_image(key);
|
|
||||||
}
|
|
||||||
if let Some(text) = &self.data.small_image_text {
|
|
||||||
assets = assets.small_text(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
let activity = Activity::new()
|
|
||||||
.state(&self.data.state)
|
|
||||||
.details(&self.data.details)
|
|
||||||
.timestamps(Timestamps::new().start(self.start_timestamp))
|
|
||||||
.assets(assets)
|
|
||||||
.buttons(vec![
|
|
||||||
Button::new(
|
|
||||||
"Download the Launcher",
|
|
||||||
"https://git.ez-pp.farm/EZPPFarm/EZPPLauncher/releases/latest",
|
|
||||||
),
|
|
||||||
Button::new("Join EZPPFarm", "https://ez-pp.farm/discord"),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if let Err(e) = client.set_activity(activity).map_err(|e| e.to_string()) {
|
|
||||||
eprintln!("Failed to set activity, disconnecting: {:?}", e);
|
|
||||||
if let Some(mut client) = self.client.take() {
|
|
||||||
let _ = client.clear_activity();
|
|
||||||
let _ = client.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static PRESENCE_TX: Lazy<mpsc::Sender<PresenceCommand>> = Lazy::new(|| {
|
|
||||||
let (tx, rx) = mpsc::channel(10);
|
|
||||||
let mut actor = PresenceActor::new(rx);
|
|
||||||
tokio::spawn(async move { actor.run().await });
|
|
||||||
tx
|
|
||||||
});
|
|
||||||
|
|
||||||
pub static PRESENCE_DATA: Lazy<StdMutex<PresenceData>> = Lazy::new(|| {
|
|
||||||
StdMutex::new(PresenceData {
|
|
||||||
state: "Idle in Launcher...".to_string(),
|
|
||||||
details: " ".to_string(),
|
|
||||||
large_image_key: "ezppfarm".to_string(),
|
|
||||||
large_image_text: "EZPPFarm".to_string(),
|
|
||||||
small_image_key: None,
|
|
||||||
small_image_text: None,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
pub async fn connect() -> bool {
|
|
||||||
let (tx, rx) = oneshot::channel();
|
|
||||||
if PRESENCE_TX.send(PresenceCommand::Connect(tx)).await.is_ok() {
|
|
||||||
return rx.await.unwrap_or(false);
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn disconnect() {
|
|
||||||
let (tx, rx) = oneshot::channel();
|
|
||||||
if PRESENCE_TX
|
|
||||||
.send(PresenceCommand::Disconnect(tx))
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
let _ = rx.await;
|
|
||||||
} else {
|
|
||||||
println!("Could not send disconnect command; actor may not be running.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn has_presence() -> bool {
|
|
||||||
let (tx, rx) = oneshot::channel();
|
|
||||||
if PRESENCE_TX
|
|
||||||
.send(PresenceCommand::IsConnected(tx))
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
return rx.await.unwrap_or(false);
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_status(state: Option<&str>, details: Option<&str>, large_image_key: Option<&str>) {
|
|
||||||
let mut data = PRESENCE_DATA.lock().unwrap();
|
|
||||||
if let Some(s) = state {
|
|
||||||
data.state = s.to_string();
|
|
||||||
}
|
|
||||||
if let Some(d) = details {
|
|
||||||
data.details = d.to_string();
|
|
||||||
}
|
|
||||||
if let Some(img) = large_image_key {
|
|
||||||
data.large_image_key = img.to_string();
|
|
||||||
}
|
|
||||||
let data_clone = data.clone();
|
|
||||||
let tx = PRESENCE_TX.clone();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
let _ = tx.send(PresenceCommand::UpdateData(data_clone)).await;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_user(username: Option<&str>, id: Option<&str>) {
|
|
||||||
let mut data = PRESENCE_DATA.lock().unwrap();
|
|
||||||
data.small_image_key = id.map(|id_str| format!("https://a.ez-pp.farm/{}", id_str));
|
|
||||||
data.small_image_text = username.map(|s| s.to_string());
|
|
||||||
let data_clone = data.clone();
|
|
||||||
let tx = PRESENCE_TX.clone();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
let _ = tx.send(PresenceCommand::UpdateData(data_clone)).await;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,388 +0,0 @@
|
|||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
use sysinfo::Pid;
|
|
||||||
|
|
||||||
pub fn check_folder_completeness<P: AsRef<Path>>(folder_path: P, required_files: &[&str]) -> f32 {
|
|
||||||
let mut found = 0;
|
|
||||||
for file in required_files {
|
|
||||||
let file_path = folder_path.as_ref().join(file);
|
|
||||||
if file_path.exists() {
|
|
||||||
found += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if required_files.is_empty() {
|
|
||||||
100.0
|
|
||||||
} else {
|
|
||||||
(found as f32 / required_files.len() as f32) * 100.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_osu_user_config<P: AsRef<Path>>(
|
|
||||||
osu_folder_path: P,
|
|
||||||
) -> Option<std::collections::HashMap<String, String>> {
|
|
||||||
if !osu_folder_path.as_ref().exists() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let current_user = std::env::var("USERNAME").unwrap_or_else(|_| "Admin".to_string());
|
|
||||||
let osu_config_path = osu_folder_path
|
|
||||||
.as_ref()
|
|
||||||
.join(format!("osu!.{}.cfg", current_user));
|
|
||||||
if !osu_config_path.exists() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut config_map = std::collections::HashMap::new();
|
|
||||||
if let Ok(contents) = std::fs::read_to_string(osu_config_path) {
|
|
||||||
for line in contents.lines() {
|
|
||||||
if let Some((key, value)) = line.split_once(" = ") {
|
|
||||||
config_map.insert(key.trim().to_string(), value.trim().to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Some(config_map);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_osu_user_config_vals(
|
|
||||||
osu_folder_path: &str,
|
|
||||||
key_values: &[(&str, Option<&str>)],
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
let current_user = std::env::var("USERNAME").unwrap_or_else(|_| "Admin".to_string());
|
|
||||||
let osu_config_path = Path::new(osu_folder_path).join(format!("osu!.{}.cfg", current_user));
|
|
||||||
|
|
||||||
if !osu_config_path.exists() {
|
|
||||||
return Err("osu! user config file does not exist".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut lines = fs::read_to_string(&osu_config_path)
|
|
||||||
.map_err(|e| e.to_string())?
|
|
||||||
.lines()
|
|
||||||
.map(|line| line.to_string())
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
|
|
||||||
let mut keys_to_set: std::collections::HashMap<&str, &str> = std::collections::HashMap::new();
|
|
||||||
let mut keys_to_add: std::collections::HashSet<&str> = std::collections::HashSet::new();
|
|
||||||
|
|
||||||
for (key, value_opt) in key_values.iter() {
|
|
||||||
if let Some(value) = value_opt {
|
|
||||||
keys_to_set.insert(*key, *value);
|
|
||||||
keys_to_add.insert(*key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut updates = Vec::new();
|
|
||||||
for (i, line) in lines.iter().enumerate() {
|
|
||||||
if let Some((existing_key, _)) = line.split_once(" = ") {
|
|
||||||
let trimmed_key = existing_key.trim();
|
|
||||||
if let Some(new_value) = keys_to_set.get(trimmed_key) {
|
|
||||||
updates.push((i, trimmed_key.to_string(), new_value.to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (i, trimmed_key, new_value) in updates {
|
|
||||||
lines[i] = format!("{} = {}", trimmed_key, new_value);
|
|
||||||
keys_to_add.remove(trimmed_key.as_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
for key in keys_to_add {
|
|
||||||
if let Some(value) = keys_to_set.get(key) {
|
|
||||||
lines.push(format!("{} = {}", key, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_content = lines.join("\n") + "\n";
|
|
||||||
fs::write(&osu_config_path, new_content).map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_osu_config_vals(
|
|
||||||
osu_folder_path: &str,
|
|
||||||
key_values: &[(&str, Option<&str>)],
|
|
||||||
) -> Result<bool, String> {
|
|
||||||
let osu_config_path = Path::new(osu_folder_path).join("osu!.cfg");
|
|
||||||
|
|
||||||
if !osu_config_path.exists() {
|
|
||||||
return Err("osu!.cfg file does not exist".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut lines = fs::read_to_string(&osu_config_path)
|
|
||||||
.map_err(|e| e.to_string())?
|
|
||||||
.lines()
|
|
||||||
.map(|line| line.to_string())
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
|
|
||||||
let mut keys_to_set: std::collections::HashMap<&str, &str> = std::collections::HashMap::new();
|
|
||||||
let mut keys_to_add: std::collections::HashSet<&str> = std::collections::HashSet::new();
|
|
||||||
|
|
||||||
for (key, value_opt) in key_values.iter() {
|
|
||||||
if let Some(value) = value_opt {
|
|
||||||
keys_to_set.insert(*key, *value);
|
|
||||||
keys_to_add.insert(*key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut updates = Vec::new();
|
|
||||||
for (i, line) in lines.iter().enumerate() {
|
|
||||||
if let Some((existing_key, _)) = line.split_once(" = ") {
|
|
||||||
let trimmed_key = existing_key.trim();
|
|
||||||
if let Some(new_value) = keys_to_set.get(trimmed_key) {
|
|
||||||
updates.push((i, trimmed_key.to_string(), new_value.to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (i, trimmed_key, new_value) in updates {
|
|
||||||
lines[i] = format!("{} = {}", trimmed_key, new_value);
|
|
||||||
keys_to_add.remove(trimmed_key.as_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
for key in keys_to_add {
|
|
||||||
if let Some(value) = keys_to_set.get(key) {
|
|
||||||
lines.push(format!("{} = {}", key, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_content = lines.join("\n") + "\n";
|
|
||||||
fs::write(&osu_config_path, new_content).map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_osu_config<P: AsRef<Path>>(
|
|
||||||
osu_folder_path: P,
|
|
||||||
) -> Option<std::collections::HashMap<String, String>> {
|
|
||||||
if !osu_folder_path.as_ref().exists() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let osu_config_path = osu_folder_path.as_ref().join("osu!.cfg");
|
|
||||||
if !osu_config_path.exists() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut config_map = std::collections::HashMap::new();
|
|
||||||
if let Ok(contents) = std::fs::read_to_string(osu_config_path) {
|
|
||||||
for line in contents.lines() {
|
|
||||||
if let Some((key, value)) = line.split_once(" = ") {
|
|
||||||
config_map.insert(key.trim().to_string(), value.trim().to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Some(config_map);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
pub fn is_osuwinello_available() -> bool {
|
|
||||||
use std::process::Command;
|
|
||||||
Command::new("osu-wine")
|
|
||||||
.arg("--info")
|
|
||||||
.stdout(std::process::Stdio::null())
|
|
||||||
.stderr(std::process::Stdio::null())
|
|
||||||
.status()
|
|
||||||
.is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub fn is_osuwinello_available() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
pub fn is_wmctrl_available() -> bool {
|
|
||||||
use std::process::Command;
|
|
||||||
Command::new("wmctrl")
|
|
||||||
.arg("-V")
|
|
||||||
.stdout(std::process::Stdio::null())
|
|
||||||
.stderr(std::process::Stdio::null())
|
|
||||||
.status()
|
|
||||||
.is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub fn is_wmctrl_available() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
pub fn get_window_title_by_pid(target_pid: Pid) -> String {
|
|
||||||
use std::process::Command;
|
|
||||||
let find_title = || -> Option<String> {
|
|
||||||
let output = Command::new("wmctrl").arg("-lp").output().ok()?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let output_str = String::from_utf8(output.stdout).ok()?;
|
|
||||||
|
|
||||||
for line in output_str.lines() {
|
|
||||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
|
||||||
if parts.len() < 4 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(pid) = parts[2].parse::<u32>() {
|
|
||||||
if pid == target_pid.as_u32() {
|
|
||||||
let title = parts[4..].join(" ");
|
|
||||||
return Some(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
find_title().unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub fn get_window_title_by_pid(pid: Pid) -> String {
|
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::os::windows::ffi::OsStringExt;
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use winapi::shared::windef::HWND;
|
|
||||||
use winapi::um::winuser::{
|
|
||||||
EnumWindows, GetWindowTextW, GetWindowThreadProcessId, IsWindowVisible,
|
|
||||||
};
|
|
||||||
|
|
||||||
extern "system" fn enum_windows_proc(
|
|
||||||
hwnd: HWND,
|
|
||||||
lparam: winapi::shared::minwindef::LPARAM,
|
|
||||||
) -> i32 {
|
|
||||||
unsafe {
|
|
||||||
let data = &mut *(lparam as *mut (u32, Arc<Mutex<Option<String>>>));
|
|
||||||
let target_pid = data.0;
|
|
||||||
let result = &data.1;
|
|
||||||
|
|
||||||
let mut window_pid = 0u32;
|
|
||||||
GetWindowThreadProcessId(hwnd, &mut window_pid);
|
|
||||||
if window_pid == target_pid && IsWindowVisible(hwnd) != 0 {
|
|
||||||
let mut title = vec![0u16; 512];
|
|
||||||
let length = GetWindowTextW(hwnd, title.as_mut_ptr(), title.len() as i32);
|
|
||||||
if length > 0 {
|
|
||||||
let title = OsString::from_wide(&title[..length as usize]);
|
|
||||||
let title_str = title.to_string_lossy().into_owned();
|
|
||||||
if !title_str.is_empty() {
|
|
||||||
*result.lock().unwrap() = Some(title_str);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = Arc::new(Mutex::new(None));
|
|
||||||
let mut data = (pid.as_u32(), Arc::clone(&result));
|
|
||||||
unsafe {
|
|
||||||
EnumWindows(
|
|
||||||
Some(enum_windows_proc),
|
|
||||||
&mut data as *mut _ as winapi::shared::minwindef::LPARAM,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
result.lock().unwrap().clone().unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn is_net8_installed() -> bool {
|
|
||||||
use tokio::process::Command;
|
|
||||||
|
|
||||||
let mut command = Command::new("dotnet");
|
|
||||||
command.arg("--list-runtimes");
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
|
||||||
command.creation_flags(CREATE_NO_WINDOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
let output_result = command.output().await;
|
|
||||||
match output_result {
|
|
||||||
Ok(output) => {
|
|
||||||
if !output.status.success() {
|
|
||||||
eprintln!(
|
|
||||||
"Error: `dotnet --list-runtimes` failed with status: {}",
|
|
||||||
output.status
|
|
||||||
);
|
|
||||||
eprintln!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let stdout_str = String::from_utf8_lossy(&output.stdout);
|
|
||||||
stdout_str.lines().any(|line| line.starts_with("Microsoft.WindowsDesktop.App 8."))
|
|
||||||
}
|
|
||||||
Err(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
pub fn encrypt_password(password: &str, _entropy: &str) -> Result<String, String> {
|
|
||||||
Ok(password.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub fn encrypt_password(password: &str, entropy: &str) -> Result<String, String> {
|
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
|
||||||
use std::ffi::OsStr;
|
|
||||||
use std::os::windows::ffi::OsStrExt;
|
|
||||||
use std::ptr;
|
|
||||||
use std::slice;
|
|
||||||
use winapi::shared::minwindef::{BYTE, DWORD, LPVOID};
|
|
||||||
use winapi::shared::ntdef::LPCWSTR;
|
|
||||||
use winapi::um::dpapi::{CRYPTPROTECT_UI_FORBIDDEN, CryptProtectData};
|
|
||||||
use winapi::um::winbase::LocalFree;
|
|
||||||
use winapi::um::wincrypt::DATA_BLOB;
|
|
||||||
|
|
||||||
let description = "Encrypted";
|
|
||||||
|
|
||||||
let password_bytes = password.as_bytes();
|
|
||||||
let mut input_blob = DATA_BLOB {
|
|
||||||
cbData: password_bytes.len() as DWORD,
|
|
||||||
pbData: password_bytes.as_ptr() as *mut BYTE,
|
|
||||||
};
|
|
||||||
|
|
||||||
let entropy_bytes = entropy.as_bytes();
|
|
||||||
let mut entropy_blob = DATA_BLOB {
|
|
||||||
cbData: entropy_bytes.len() as DWORD,
|
|
||||||
pbData: entropy_bytes.as_ptr() as *mut BYTE,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut output_blob = DATA_BLOB {
|
|
||||||
cbData: 0,
|
|
||||||
pbData: ptr::null_mut(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let wide_description: Vec<u16> = OsStrExt::encode_wide(OsStr::new(description))
|
|
||||||
.chain(Some(0))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let p_description: LPCWSTR = wide_description.as_ptr();
|
|
||||||
|
|
||||||
let result = unsafe {
|
|
||||||
CryptProtectData(
|
|
||||||
&mut input_blob,
|
|
||||||
p_description,
|
|
||||||
&mut entropy_blob,
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
CRYPTPROTECT_UI_FORBIDDEN,
|
|
||||||
&mut output_blob,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if result == 0 {
|
|
||||||
return Err("CryptProtectData failed".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
let encrypted_data =
|
|
||||||
unsafe { slice::from_raw_parts(output_blob.pbData, output_blob.cbData as usize).to_vec() };
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
LocalFree(output_blob.pbData as LPVOID);
|
|
||||||
}
|
|
||||||
|
|
||||||
let base64_string = general_purpose::STANDARD.encode(&encrypted_data);
|
|
||||||
|
|
||||||
Ok(base64_string)
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
|
||||||
"productName": "EZPPLauncher",
|
|
||||||
"version": "3.0.7",
|
|
||||||
"identifier": "farm.EZPPFarm.Launcher",
|
|
||||||
"mainBinaryName": "ezpplauncher",
|
|
||||||
"build": {
|
|
||||||
"beforeDevCommand": "bun run vite:dev",
|
|
||||||
"devUrl": "http://localhost:1420",
|
|
||||||
"beforeBuildCommand": "",
|
|
||||||
"frontendDist": "../build"
|
|
||||||
},
|
|
||||||
"app": {
|
|
||||||
"windows": [
|
|
||||||
{
|
|
||||||
"title": "EZPPLauncher",
|
|
||||||
"width": 1000,
|
|
||||||
"height": 737,
|
|
||||||
"decorations": false,
|
|
||||||
"resizable": false,
|
|
||||||
"maximizable": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"withGlobalTauri": true,
|
|
||||||
"security": {
|
|
||||||
"csp": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bundle": {
|
|
||||||
"publisher": "EZPPFarm",
|
|
||||||
"copyright": "EZPPFarm",
|
|
||||||
"homepage": "https://ez-pp.farm",
|
|
||||||
"license": "GPL-3.0",
|
|
||||||
"active": true,
|
|
||||||
"targets": [
|
|
||||||
"nsis",
|
|
||||||
"appimage"
|
|
||||||
],
|
|
||||||
"windows": {
|
|
||||||
"webviewInstallMode": {
|
|
||||||
"silent": true,
|
|
||||||
"type": "downloadBootstrapper"
|
|
||||||
},
|
|
||||||
"nsis": {
|
|
||||||
"installMode": "currentUser",
|
|
||||||
"compression": "lzma",
|
|
||||||
"installerHooks": "./nsis/nsis-hooks.nsh"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"icon": [
|
|
||||||
"icons/32x32.png",
|
|
||||||
"icons/128x128.png",
|
|
||||||
"icons/128x128@2x.png",
|
|
||||||
"icons/icon.icns",
|
|
||||||
"icons/icon.ico"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
Dropdown,
|
||||||
|
DropdownItem,
|
||||||
|
DropdownHeader,
|
||||||
|
DropdownDivider,
|
||||||
|
Button,
|
||||||
|
Indicator,
|
||||||
|
} from "flowbite-svelte";
|
||||||
|
import {
|
||||||
|
ArrowLeftSolid,
|
||||||
|
ArrowRightFromBracketSolid,
|
||||||
|
ArrowRightToBracketSolid,
|
||||||
|
HeartSolid,
|
||||||
|
UserSettingsSolid,
|
||||||
|
} from "flowbite-svelte-icons";
|
||||||
|
import ezppLogo from "../public/favicon.png";
|
||||||
|
import {
|
||||||
|
currentPage,
|
||||||
|
currentUser,
|
||||||
|
launching,
|
||||||
|
launchPercentage,
|
||||||
|
launchStatus,
|
||||||
|
} from "./storage/localStore";
|
||||||
|
import { Page } from "./consts/pages";
|
||||||
|
import Login from "./pages/Login.svelte";
|
||||||
|
import Launch from "./pages/Launch.svelte";
|
||||||
|
import toast, { Toaster } from "svelte-french-toast";
|
||||||
|
import type { User } from "./types/user";
|
||||||
|
import Settings from "./pages/Settings.svelte";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
|
||||||
|
let user: User | undefined = undefined;
|
||||||
|
let loggedIn = false;
|
||||||
|
|
||||||
|
let updateInfo: Record<string, unknown>;
|
||||||
|
|
||||||
|
currentUser.subscribe((newUser) => {
|
||||||
|
loggedIn = newUser != undefined;
|
||||||
|
user = newUser;
|
||||||
|
});
|
||||||
|
|
||||||
|
const logout = () => {
|
||||||
|
window.dispatchEvent(new CustomEvent("logout"));
|
||||||
|
currentUser.set(undefined);
|
||||||
|
currentPage.set(Page.Login);
|
||||||
|
toast.success("Successfully logged out!", {
|
||||||
|
position: "bottom-center",
|
||||||
|
className:
|
||||||
|
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("update", async (e) => {
|
||||||
|
const update = (e as CustomEvent).detail;
|
||||||
|
await Swal.fire({
|
||||||
|
html: `EZPPLauncher ${update.tag_name} is now available!<br>Click the Button bellow to download the latest release!`,
|
||||||
|
title: "It's your lucky day!",
|
||||||
|
allowOutsideClick: false,
|
||||||
|
allowEscapeKey: false,
|
||||||
|
allowEnterKey: false,
|
||||||
|
confirmButtonText: "Thanks!",
|
||||||
|
});
|
||||||
|
window.dispatchEvent(new CustomEvent("updateExit"));
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("launchStatusUpdate", (e) => {
|
||||||
|
const status = (e as CustomEvent).detail.status;
|
||||||
|
launchStatus.set(status);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("launchProgressUpdate", (e) => {
|
||||||
|
const progress = (e as CustomEvent).detail.progress;
|
||||||
|
launchPercentage.set(progress);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("launch-abort", () => {
|
||||||
|
launchPercentage.set(-1);
|
||||||
|
launchStatus.set("");
|
||||||
|
launching.set(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("alert", (e) => {
|
||||||
|
console.log((e as CustomEvent).detail);
|
||||||
|
const toastMessage = (e as CustomEvent).detail;
|
||||||
|
switch (toastMessage.type) {
|
||||||
|
case "success": {
|
||||||
|
toast.success(toastMessage.message, {
|
||||||
|
position: "bottom-center",
|
||||||
|
className:
|
||||||
|
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "error": {
|
||||||
|
toast.error(toastMessage.message, {
|
||||||
|
position: "bottom-center",
|
||||||
|
className:
|
||||||
|
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
|
||||||
|
duration: 4000,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
toast(toastMessage.message, {
|
||||||
|
icon: "ℹ",
|
||||||
|
position: "bottom-center",
|
||||||
|
className:
|
||||||
|
"dark:!bg-gray-800 border-1 dark:!border-gray-700 dark:!text-gray-100",
|
||||||
|
duration: 1500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Toaster></Toaster>
|
||||||
|
|
||||||
|
{#if !updateInfo}
|
||||||
|
<div class="p-2 flex flex-row justify-between items-center">
|
||||||
|
<div class="flex flex-row items-center animate-fadeIn opacity-0">
|
||||||
|
{#if $currentPage == Page.Settings}
|
||||||
|
<Button
|
||||||
|
class="dark:active:!bg-gray-900 !ring-0 w-10 h-10 mr-1 rounded-lg animate-sideIn opacity-0 active:scale-95 transition-transform duration-75"
|
||||||
|
color="light"
|
||||||
|
on:click={() => {
|
||||||
|
currentPage.set(Page.Launch);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ArrowLeftSolid class="outline-none border-none" size="sm" />
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
<img src={ezppLogo} alt="EZPPFarm Logo" class="w-12 h-12 mr-2" />
|
||||||
|
<span class="text-gray-700 dark:text-gray-100 text-xl font-extralight">
|
||||||
|
EZPPLauncher
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{#if $currentPage == Page.Launch}
|
||||||
|
<div
|
||||||
|
class="flex flex-row gap-2 w-fill cursor-pointer md:order-2 animate-lsideIn opacity-0"
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
class="rounded-lg border dark:border-gray-700 hover:ring-4 hover:ring-gray-200 dark:hover:ring-gray-800"
|
||||||
|
src={loggedIn
|
||||||
|
? "https://a.ez-pp.farm/" + user?.id
|
||||||
|
: "https://a.ez-pp.farm/0"}
|
||||||
|
id="avatar-menu"
|
||||||
|
/>
|
||||||
|
<!-- TODO: if user has donator, display heart indicator-->
|
||||||
|
{#if $currentUser && $currentUser.donor}
|
||||||
|
<Indicator
|
||||||
|
class="pointer-events-none"
|
||||||
|
color="red"
|
||||||
|
border
|
||||||
|
size="xl"
|
||||||
|
placement="top-right"
|
||||||
|
>
|
||||||
|
<span class="text-red-300 text-xs font-bold">
|
||||||
|
<HeartSolid class="select-none pointer-events-none" size="xs" />
|
||||||
|
</span>
|
||||||
|
</Indicator>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<Dropdown placement="bottom-start" triggeredBy="#avatar-menu">
|
||||||
|
<DropdownHeader>
|
||||||
|
<span class="block text-sm">{loggedIn ? user?.name : "Guest"}</span>
|
||||||
|
<span
|
||||||
|
class="block truncate text-sm font-medium text-gray-500 dark:text-gray-200"
|
||||||
|
>
|
||||||
|
{loggedIn ? user?.email : "Please log in!"}
|
||||||
|
</span>
|
||||||
|
</DropdownHeader>
|
||||||
|
<DropdownItem
|
||||||
|
class="flex flex-row gap-2 border-0 dark:!bg-gray-700 dark:active:!bg-gray-900 dark:hover:!bg-gray-800 transition-colors"
|
||||||
|
on:click={() => {
|
||||||
|
if (!$launching) currentPage.set(Page.Settings);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<UserSettingsSolid class="select-none outline-none border-none" />
|
||||||
|
Settings
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownDivider />
|
||||||
|
{#if loggedIn}
|
||||||
|
<DropdownItem
|
||||||
|
class="flex flex-row gap-2 border-0 dark:!bg-gray-700 dark:active:!bg-gray-900 dark:hover:!bg-gray-800 transition-colors"
|
||||||
|
on:click={() => {
|
||||||
|
if (!$launching) logout();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ArrowRightFromBracketSolid
|
||||||
|
class="select-none outline-none border-none"
|
||||||
|
/>
|
||||||
|
Sign out
|
||||||
|
</DropdownItem>
|
||||||
|
{:else}
|
||||||
|
<DropdownItem
|
||||||
|
class="flex flex-row gap-2 border-0 dark:!bg-gray-700 dark:active:!bg-gray-900 dark:hover:!bg-gray-800 transition-colors"
|
||||||
|
on:click={() => {
|
||||||
|
if (!$launching) currentPage.set(Page.Login);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ArrowRightToBracketSolid
|
||||||
|
class="select-none outline-none border-none"
|
||||||
|
/>
|
||||||
|
Login
|
||||||
|
</DropdownItem>
|
||||||
|
{/if}
|
||||||
|
</Dropdown>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if $currentPage == Page.Login}
|
||||||
|
<Login />
|
||||||
|
{:else if $currentPage == Page.Settings}
|
||||||
|
<Settings />
|
||||||
|
{:else}
|
||||||
|
<Launch />
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
@import "tailwindcss";
|
|
||||||
|
|
||||||
@import "tw-animate-css";
|
|
||||||
|
|
||||||
@custom-variant dark (&:where(.dark, .dark *));
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--radius: 0.625rem;
|
|
||||||
--background: oklch(95.36% 0.017 256.57);
|
|
||||||
--foreground: oklch(40.06% 0.038 266.38);
|
|
||||||
--card: oklch(100% 0 0);
|
|
||||||
--card-foreground: oklch(40.06% 0.038 266.38);
|
|
||||||
--popover: oklch(84.01% 0.028 258.47);
|
|
||||||
--popover-foreground: oklch(40.06% 0.038 266.38);
|
|
||||||
--primary: oklch(60.7% 0.226 299.68);
|
|
||||||
--primary-foreground: oklch(100% 0 0);
|
|
||||||
--secondary: oklch(84.01% 0.028 258.47);
|
|
||||||
--secondary-foreground: oklch(40.06% 0.038 266.38);
|
|
||||||
--muted: oklch(89.49% 0.023 256.59);
|
|
||||||
--muted-foreground: oklch(51.01% 0.028 265.36);
|
|
||||||
--accent: oklch(54.41% 0.186 230.5);
|
|
||||||
--accent-foreground: oklch(100% 0 0);
|
|
||||||
--destructive: oklch(51.68% 0.202 15.84);
|
|
||||||
--destructive-foreground: oklch(100% 0 0);
|
|
||||||
--border: oklch(78.52% 0.026 260.1);
|
|
||||||
--input: oklch(84.01% 0.028 258.47);
|
|
||||||
--ring: oklch(60.7% 0.226 299.68);
|
|
||||||
--chart-1: oklch(60.7% 0.226 299.68);
|
|
||||||
--chart-2: oklch(54.41% 0.186 230.5);
|
|
||||||
--chart-3: oklch(46.49% 0.139 139.66);
|
|
||||||
--chart-4: oklch(60.01% 0.202 55.86);
|
|
||||||
--chart-5: oklch(70.11% 0.125 39.15);
|
|
||||||
--sidebar: oklch(92.65% 0.019 256.57);
|
|
||||||
--sidebar-background: oklch(92.65% 0.019 256.57);
|
|
||||||
--sidebar-foreground: oklch(40.06% 0.038 266.38);
|
|
||||||
--sidebar-primary: oklch(60.7% 0.226 299.68);
|
|
||||||
--sidebar-primary-foreground: oklch(100% 0 0);
|
|
||||||
--sidebar-accent: oklch(54.41% 0.186 230.5);
|
|
||||||
--sidebar-accent-foreground: oklch(100% 0 0);
|
|
||||||
--sidebar-border: oklch(78.52% 0.026 260.1);
|
|
||||||
--sidebar-ring: oklch(60.7% 0.226 299.68);
|
|
||||||
|
|
||||||
--font-sans:
|
|
||||||
"Sora", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
|
||||||
"Segoe UI Symbol", "Noto Color Emoji";
|
|
||||||
--font-mono:
|
|
||||||
"Space Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
|
|
||||||
"Courier New", monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark {
|
|
||||||
--background: oklch(17.02% 0.042 282.86);
|
|
||||||
--foreground: oklch(89.24% 0.079 263.24);
|
|
||||||
--card: oklch(20.22% 0.048 282.86);
|
|
||||||
--card-foreground: oklch(89.24% 0.079 263.24);
|
|
||||||
--popover: oklch(20.3% 0.05 282.86);
|
|
||||||
--popover-foreground: oklch(89.24% 0.079 263.24);
|
|
||||||
--primary: oklch(81.45% 0.159 300.53);
|
|
||||||
--primary-foreground: oklch(20.22% 0.048 282.86);
|
|
||||||
--secondary: oklch(42.99% 0.031 270.49);
|
|
||||||
--secondary-foreground: oklch(89.24% 0.079 263.24);
|
|
||||||
--muted: oklch(24.41% 0.048 271.21);
|
|
||||||
--muted-foreground: oklch(73.83% 0.057 265.65);
|
|
||||||
--accent: oklch(86.2% 0.131 300.42);
|
|
||||||
--accent-foreground: oklch(20.22% 0.048 282.86);
|
|
||||||
--destructive: oklch(77.23% 0.146 11.66);
|
|
||||||
--destructive-foreground: oklch(20.22% 0.048 282.86);
|
|
||||||
--border: oklch(27.7% 0.041 276.87);
|
|
||||||
--input: oklch(27.7% 0.041 276.87);
|
|
||||||
--ring: oklch(81.45% 0.159 300.53);
|
|
||||||
--chart-1: oklch(81.45% 0.159 300.53);
|
|
||||||
--chart-2: oklch(77.19% 0.121 223.01);
|
|
||||||
--chart-3: oklch(79.1% 0.119 150.21);
|
|
||||||
--chart-4: oklch(79.74% 0.167 57.97);
|
|
||||||
--chart-5: oklch(92.11% 0.061 37.8);
|
|
||||||
--sidebar: oklch(13.29% 0.036 282.86);
|
|
||||||
--sidebar-background: oklch(13.29% 0.036 282.86);
|
|
||||||
--sidebar-foreground: oklch(89.24% 0.079 263.24);
|
|
||||||
--sidebar-primary: oklch(81.45% 0.159 300.53);
|
|
||||||
--sidebar-primary-foreground: oklch(20.22% 0.048 282.86);
|
|
||||||
--sidebar-accent: oklch(75.21% 0.121 293.01);
|
|
||||||
--sidebar-accent-foreground: oklch(20.22% 0.048 282.86);
|
|
||||||
--sidebar-border: oklch(35.13% 0.03 273.31);
|
|
||||||
--sidebar-ring: oklch(81.45% 0.159 300.53);
|
|
||||||
}
|
|
||||||
|
|
||||||
@theme inline {
|
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
|
||||||
--radius-lg: var(--radius);
|
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
|
||||||
--color-background: var(--background);
|
|
||||||
--color-theme: #181825;
|
|
||||||
--color-theme-50: #e9e9f1;
|
|
||||||
--color-theme-100: #dbdbe8;
|
|
||||||
--color-theme-200: #bfbfd6;
|
|
||||||
--color-theme-300: #a4a4c4;
|
|
||||||
--color-theme-400: #8888b2;
|
|
||||||
--color-theme-500: #6c6ca0;
|
|
||||||
--color-theme-600: #575786;
|
|
||||||
--color-theme-700: #45456b;
|
|
||||||
--color-theme-800: #33334f;
|
|
||||||
--color-theme-900: #212133;
|
|
||||||
--color-theme-950: #181825;
|
|
||||||
--color-foreground: var(--foreground);
|
|
||||||
--color-card: var(--card);
|
|
||||||
--color-card-foreground: var(--card-foreground);
|
|
||||||
--color-popover: var(--popover);
|
|
||||||
--color-popover-foreground: var(--popover-foreground);
|
|
||||||
--color-primary: var(--primary);
|
|
||||||
--color-primary-50: #fefdff;
|
|
||||||
--color-primary-100: #faf6fe;
|
|
||||||
--color-primary-200: #f2e9fc;
|
|
||||||
--color-primary-300: #e9dbfa;
|
|
||||||
--color-primary-400: #e1cef9;
|
|
||||||
--color-primary-500: #d9c0f7;
|
|
||||||
--color-primary-600: #c6a0f3;
|
|
||||||
--color-primary-700: #b381ef;
|
|
||||||
--color-primary-800: #a061eb;
|
|
||||||
--color-primary-900: #8d41e7;
|
|
||||||
--color-primary-950: #8331e5;
|
|
||||||
--color-primary-foreground: var(--primary-foreground);
|
|
||||||
--color-primary-50: #fefdff;
|
|
||||||
--color-primary-100: #faf6fe;
|
|
||||||
--color-secondary: var(--secondary);
|
|
||||||
--color-secondary-foreground: var(--secondary-foreground);
|
|
||||||
--color-muted: var(--muted);
|
|
||||||
--color-muted-foreground: var(--muted-foreground);
|
|
||||||
--color-accent: var(--accent);
|
|
||||||
--color-accent-foreground: var(--accent-foreground);
|
|
||||||
--color-destructive: var(--destructive);
|
|
||||||
--color-border: var(--border);
|
|
||||||
--color-input: var(--input);
|
|
||||||
--color-ring: var(--ring);
|
|
||||||
--color-chart-1: var(--chart-1);
|
|
||||||
--color-chart-2: var(--chart-2);
|
|
||||||
--color-chart-3: var(--chart-3);
|
|
||||||
--color-chart-4: var(--chart-4);
|
|
||||||
--color-chart-5: var(--chart-5);
|
|
||||||
--color-sidebar: var(--sidebar);
|
|
||||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
||||||
--color-sidebar-primary: var(--sidebar-primary);
|
|
||||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
||||||
--color-sidebar-accent: var(--sidebar-accent);
|
|
||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
|
||||||
}
|
|
||||||
|
|
||||||
@layer base {
|
|
||||||
* {
|
|
||||||
@apply border-border outline-ring/50;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
@apply bg-background text-foreground;
|
|
||||||
}
|
|
||||||
button:not([disabled]),
|
|
||||||
[role="button"]:not([disabled]) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en" class="dark">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title>EZPPLauncher</title>
|
|
||||||
%sveltekit.head%
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="bg-theme-950" data-sveltekit-preload-data="hover">
|
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/* Write your global styles here, in PostCSS syntax */
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: "Prompt";
|
||||||
|
-webkit-user-select: none !important;
|
||||||
|
user-select: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html .cet-titlebar {
|
||||||
|
background-color: #ececec !important;
|
||||||
|
color: #202020 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html .cet-titlebar .cet-control-icon svg {
|
||||||
|
fill: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cet-titlebar .cet-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cet-container {
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indeterminate {
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
90deg,
|
||||||
|
rgb(217 5 89) -1%,
|
||||||
|
rgb(217 5 89) 10%,
|
||||||
|
#d3d3d3 10%,
|
||||||
|
#d3d3d3 90%
|
||||||
|
);
|
||||||
|
background-size: 200%;
|
||||||
|
background-position-x: 15%;
|
||||||
|
animation: progress-loading 5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.indeterminate {
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
90deg,
|
||||||
|
rgb(217 5 89) -1%,
|
||||||
|
rgb(217 5 89) 10%,
|
||||||
|
#535353 10%,
|
||||||
|
#535353 90%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
html .cet-titlebar .cet-control-icon svg {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
html .cet-titlebar {
|
||||||
|
background-color: #202020 !important;
|
||||||
|
color: #ececec !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swal2-container {
|
||||||
|
background: #202020 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swal2-container .swal2-popup {
|
||||||
|
background: #323232 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animatedProgress div {
|
||||||
|
transition: width 0.35s cubic-bezier(0.65, -0.02, 0.31, 1.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
.noselect {
|
||||||
|
-webkit-user-select: none !important;
|
||||||
|
user-select: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes progress-loading {
|
||||||
|
50% {
|
||||||
|
background-position-x: -115%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
export enum Page {
|
||||||
|
Login = 0,
|
||||||
|
Launch = 1,
|
||||||
|
Settings = 2,
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="svelte" />
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cubicOut } from "svelte/easing";
|
||||||
|
import { tweened } from "svelte/motion";
|
||||||
|
import { twMerge, twJoin } from "tailwind-merge";
|
||||||
|
import { clamp } from "../util/mathUtil";
|
||||||
|
|
||||||
|
export let progress: number = 45;
|
||||||
|
export let precision = 2;
|
||||||
|
export let tweenDuration = 400;
|
||||||
|
export let animate = false;
|
||||||
|
export let size = "h-2.5";
|
||||||
|
export let labelInside = false;
|
||||||
|
export let labelOutside = "";
|
||||||
|
export let easing = cubicOut;
|
||||||
|
export let color = "primary";
|
||||||
|
export let indeterminate = false;
|
||||||
|
export let labelInsideClass =
|
||||||
|
"text-primary-100 text-xs font-medium text-center p-0.5 leading-none rounded-full";
|
||||||
|
export let divClass = "w-full bg-gray-200 rounded-full dark:bg-gray-700";
|
||||||
|
|
||||||
|
const barColors: Record<string, string> = {
|
||||||
|
primary: "bg-primary-600",
|
||||||
|
blue: "bg-blue-600",
|
||||||
|
gray: "bg-gray-600 dark:bg-gray-300",
|
||||||
|
red: "bg-red-600 dark:bg-red-500",
|
||||||
|
green: "bg-green-600 dark:bg-green-500",
|
||||||
|
yellow: "bg-yellow-400",
|
||||||
|
purple: "bg-purple-600 dark:bg-purple-500",
|
||||||
|
indigo: "bg-indigo-600 dark:bg-indigo-500",
|
||||||
|
};
|
||||||
|
|
||||||
|
let _progress = tweened(0, {
|
||||||
|
duration: tweenDuration,
|
||||||
|
easing,
|
||||||
|
});
|
||||||
|
|
||||||
|
$: {
|
||||||
|
progress = clamp(Number(progress), 0, 100);
|
||||||
|
_progress.set(progress);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if labelOutside}
|
||||||
|
<div
|
||||||
|
{...$$restProps}
|
||||||
|
class={twMerge("flex justify-between mb-1", $$props.classLabelOutside)}
|
||||||
|
>
|
||||||
|
<span class="text-base font-medium text-blue-700 dark:text-white"
|
||||||
|
>{labelOutside}</span
|
||||||
|
>
|
||||||
|
<span class="text-sm font-medium text-blue-700 dark:text-white"
|
||||||
|
>{animate
|
||||||
|
? $_progress.toFixed(precision)
|
||||||
|
: progress.toFixed(precision)}%</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class={twMerge(divClass, size, $$props.class)}>
|
||||||
|
{#if labelInside}
|
||||||
|
{#if !indeterminate}
|
||||||
|
<div
|
||||||
|
class={twJoin(labelInsideClass, barColors[color])}
|
||||||
|
style="width: {animate ? $_progress : progress}%"
|
||||||
|
>
|
||||||
|
{animate ? $_progress.toFixed(precision) : progress.toFixed(precision)}%
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div
|
||||||
|
class={twJoin(
|
||||||
|
barColors[color],
|
||||||
|
size,
|
||||||
|
"indeterminate rounded-full animate-pulse"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{:else if !indeterminate}
|
||||||
|
<div
|
||||||
|
class={twJoin(barColors[color], size, "rounded-full")}
|
||||||
|
style="width: {animate ? $_progress : progress}%"
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<div
|
||||||
|
class={twJoin(
|
||||||
|
barColors[color],
|
||||||
|
size,
|
||||||
|
"indeterminate rounded-full animate-pulse"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
import type {
|
|
||||||
EZPPUser,
|
|
||||||
EZPPUserInfoResponse,
|
|
||||||
EZPPUserResponse,
|
|
||||||
EZPPUSerStatusResponse,
|
|
||||||
} from '@/types';
|
|
||||||
import { betterFetch } from '@better-fetch/fetch';
|
|
||||||
|
|
||||||
const BANCHO_ENDPOINT = 'https://c.ez-pp.farm/';
|
|
||||||
const API_ENDPOINT = 'https://api.ez-pp.farm/';
|
|
||||||
const ENDPOINT = 'https://ez-pp.farm/';
|
|
||||||
|
|
||||||
const timeout = 5000; // 5 seconds;
|
|
||||||
|
|
||||||
export const ezppfarm = {
|
|
||||||
ping: async (): Promise<number | undefined> => {
|
|
||||||
try {
|
|
||||||
const start = Date.now();
|
|
||||||
const request = await betterFetch(BANCHO_ENDPOINT, {
|
|
||||||
timeout,
|
|
||||||
headers: {
|
|
||||||
'User-Agent': 'EZPPLauncher',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (request.error) return undefined;
|
|
||||||
const ping = Date.now() - start;
|
|
||||||
return ping;
|
|
||||||
} catch {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
login: async (
|
|
||||||
username: string,
|
|
||||||
password: string
|
|
||||||
): Promise<
|
|
||||||
| {
|
|
||||||
code: number;
|
|
||||||
message: string;
|
|
||||||
user?: EZPPUser;
|
|
||||||
}
|
|
||||||
| undefined
|
|
||||||
> => {
|
|
||||||
const request = await betterFetch<EZPPUserResponse>(`${ENDPOINT}login/check`, {
|
|
||||||
method: 'POST',
|
|
||||||
timeout,
|
|
||||||
body: JSON.stringify({
|
|
||||||
username: username,
|
|
||||||
password: password,
|
|
||||||
}),
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'User-Agent': 'EZPPLauncher',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (request.error) {
|
|
||||||
if (request.error.status >= 500 && request.error.status < 600)
|
|
||||||
throw new Error('Server not reachable');
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return request.data;
|
|
||||||
},
|
|
||||||
getUserInfo: async (userId: number, scope: 'all' | 'info' | 'stats' = 'all') => {
|
|
||||||
const request = await betterFetch<EZPPUserInfoResponse>(`${API_ENDPOINT}v1/get_player_info`, {
|
|
||||||
timeout,
|
|
||||||
query: {
|
|
||||||
id: userId,
|
|
||||||
scope,
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'User-Agent': 'EZPPLauncher',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return request.error ? undefined : request.data;
|
|
||||||
},
|
|
||||||
getUserStatus: async (userId: number) => {
|
|
||||||
const request = await betterFetch<EZPPUSerStatusResponse>(
|
|
||||||
`${API_ENDPOINT}v1/get_player_status`,
|
|
||||||
{
|
|
||||||
timeout,
|
|
||||||
query: {
|
|
||||||
id: userId,
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'User-Agent': 'EZPPLauncher',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return request.error ? undefined : request.data;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import type { Release } from '@/types';
|
|
||||||
import { betterFetch } from '@better-fetch/fetch';
|
|
||||||
import semver from 'semver';
|
|
||||||
|
|
||||||
const ENDPOINT = 'https://git.ez-pp.farm/';
|
|
||||||
|
|
||||||
const timeout = 5000; // 5 seconds;
|
|
||||||
|
|
||||||
export const git = {
|
|
||||||
hasUpdate: async (version: string): Promise<Release | undefined> => {
|
|
||||||
try {
|
|
||||||
const request = await betterFetch<Release[]>(
|
|
||||||
`${ENDPOINT}api/v1/repos/EZPPFarm/EZPPLauncher/releases?limit=1`,
|
|
||||||
{
|
|
||||||
timeout,
|
|
||||||
headers: {
|
|
||||||
'User-Agent': 'EZPPLauncher',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (request.error) return undefined;
|
|
||||||
|
|
||||||
const latestRelease = request.data[0];
|
|
||||||
if (!latestRelease) return undefined;
|
|
||||||
|
|
||||||
const hasUpdate = semver.lt(version, latestRelease.tag_name);
|
|
||||||
|
|
||||||
return hasUpdate ? latestRelease : undefined;
|
|
||||||
} catch {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import type { StreamsResult } from '@/types';
|
|
||||||
import { betterFetch } from '@better-fetch/fetch';
|
|
||||||
|
|
||||||
const API_ENDPOINT = 'https://osu.ppy.sh/api/';
|
|
||||||
|
|
||||||
const timeout = 5000; // 5 seconds;
|
|
||||||
|
|
||||||
export const osuapi = {
|
|
||||||
latestBuildVersion: async (releaseStream: string): Promise<string | undefined> => {
|
|
||||||
const request = await betterFetch<StreamsResult>(`${API_ENDPOINT}v2/changelog`, {
|
|
||||||
timeout,
|
|
||||||
query: {
|
|
||||||
stream: 'none',
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'User-Agent': 'EZPPLauncher',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (request.error) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const releaseData = request.data;
|
|
||||||
if (!releaseData || !releaseData.streams) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
if (releaseData.streams.length === 0) return undefined;
|
|
||||||
const selectedRelease = releaseData.streams.find(
|
|
||||||
(releaseBuild) =>
|
|
||||||
releaseBuild.name.toLowerCase() === releaseStream.replaceAll(' ', '').toLowerCase()
|
|
||||||
);
|
|
||||||
if (!selectedRelease) return undefined;
|
|
||||||
return selectedRelease.latest_build.display_version;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
const API_ENDPOINT = 'https://osu.direct/api/';
|
|
||||||
|
|
||||||
export const osudirect = {
|
|
||||||
osu: async (mapId: number): Promise<string | undefined> => {
|
|
||||||
try {
|
|
||||||
return await (await fetch(API_ENDPOINT + `osu/${mapId}?raw`)).text();
|
|
||||||
} catch {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
|
||||||
import { buttonVariants } from '$lib/components/ui/button/index.js';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.ActionProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Action bind:ref class={cn(buttonVariants(), className)} {...restProps} />
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
|
||||||
import { buttonVariants } from '$lib/components/ui/button/index.js';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.CancelProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Cancel
|
|
||||||
bind:ref
|
|
||||||
class={cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive, type WithoutChild } from 'bits-ui';
|
|
||||||
import AlertDialogOverlay from './alert-dialog-overlay.svelte';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
portalProps,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChild<AlertDialogPrimitive.ContentProps> & {
|
|
||||||
portalProps?: AlertDialogPrimitive.PortalProps;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Portal {...portalProps}>
|
|
||||||
<AlertDialogOverlay />
|
|
||||||
<AlertDialogPrimitive.Content
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
</AlertDialogPrimitive.Portal>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.DescriptionProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Description
|
|
||||||
bind:ref
|
|
||||||
class={cn('text-muted-foreground text-sm', className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { WithElementRef } from 'bits-ui';
|
|
||||||
import type { HTMLAttributes } from 'svelte/elements';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={ref}
|
|
||||||
class={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { WithElementRef } from 'bits-ui';
|
|
||||||
import type { HTMLAttributes } from 'svelte/elements';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={ref}
|
|
||||||
class={cn('flex flex-col space-y-2 text-center sm:text-left', className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.OverlayProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Overlay
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
level = 3,
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.TitleProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Title
|
|
||||||
bind:ref
|
|
||||||
class={cn('text-lg font-semibold', className)}
|
|
||||||
{level}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
|
||||||
import Title from './alert-dialog-title.svelte';
|
|
||||||
import Action from './alert-dialog-action.svelte';
|
|
||||||
import Cancel from './alert-dialog-cancel.svelte';
|
|
||||||
import Footer from './alert-dialog-footer.svelte';
|
|
||||||
import Header from './alert-dialog-header.svelte';
|
|
||||||
import Overlay from './alert-dialog-overlay.svelte';
|
|
||||||
import Content from './alert-dialog-content.svelte';
|
|
||||||
import Description from './alert-dialog-description.svelte';
|
|
||||||
|
|
||||||
const Root = AlertDialogPrimitive.Root;
|
|
||||||
const Trigger = AlertDialogPrimitive.Trigger;
|
|
||||||
const Portal = AlertDialogPrimitive.Portal;
|
|
||||||
|
|
||||||
export {
|
|
||||||
Root,
|
|
||||||
Title,
|
|
||||||
Action,
|
|
||||||
Cancel,
|
|
||||||
Portal,
|
|
||||||
Footer,
|
|
||||||
Header,
|
|
||||||
Trigger,
|
|
||||||
Overlay,
|
|
||||||
Content,
|
|
||||||
Description,
|
|
||||||
//
|
|
||||||
Root as AlertDialog,
|
|
||||||
Title as AlertDialogTitle,
|
|
||||||
Action as AlertDialogAction,
|
|
||||||
Cancel as AlertDialogCancel,
|
|
||||||
Portal as AlertDialogPortal,
|
|
||||||
Footer as AlertDialogFooter,
|
|
||||||
Header as AlertDialogHeader,
|
|
||||||
Trigger as AlertDialogTrigger,
|
|
||||||
Overlay as AlertDialogOverlay,
|
|
||||||
Content as AlertDialogContent,
|
|
||||||
Description as AlertDialogDescription,
|
|
||||||
};
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Avatar as AvatarPrimitive } from 'bits-ui';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AvatarPrimitive.FallbackProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AvatarPrimitive.Fallback
|
|
||||||
bind:ref
|
|
||||||
class={cn('bg-muted flex h-full w-full items-center justify-center rounded-full', className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Avatar as AvatarPrimitive } from 'bits-ui';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AvatarPrimitive.ImageProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AvatarPrimitive.Image
|
|
||||||
bind:ref
|
|
||||||
class={cn('aspect-square h-full w-full', className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Avatar as AvatarPrimitive } from 'bits-ui';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AvatarPrimitive.RootProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AvatarPrimitive.Root
|
|
||||||
bind:ref
|
|
||||||
class={cn('relative flex size-10 shrink-0 overflow-hidden rounded-full', className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import Root from './avatar.svelte';
|
|
||||||
import Image from './avatar-image.svelte';
|
|
||||||
import Fallback from './avatar-fallback.svelte';
|
|
||||||
|
|
||||||
export {
|
|
||||||
Root,
|
|
||||||
Image,
|
|
||||||
Fallback,
|
|
||||||
//
|
|
||||||
Root as Avatar,
|
|
||||||
Image as AvatarImage,
|
|
||||||
Fallback as AvatarFallback,
|
|
||||||
};
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
const prop: { beatmapId: number } = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="absolute w-screen h-screen overflow-hidden pointer-events-none rounded">
|
|
||||||
<div
|
|
||||||
style="background: url(https://osu.direct/api/media/background/{prop.beatmapId})"
|
|
||||||
class="absolute top-0 left-0 w-full h-full !bg-cover -z-10 pointer-events-none blur opacity-10 rounded"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<script lang="ts" module>
|
|
||||||
import { type VariantProps, tv } from 'tailwind-variants';
|
|
||||||
|
|
||||||
export const badgeVariants = tv({
|
|
||||||
base: 'focus:ring-ring inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2',
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
default: 'bg-primary text-primary-foreground hover:bg-primary/80 border-transparent',
|
|
||||||
secondary:
|
|
||||||
'bg-secondary text-secondary-foreground hover:bg-secondary/80 border-transparent',
|
|
||||||
destructive:
|
|
||||||
'bg-destructive text-destructive-foreground hover:bg-destructive/80 border-transparent',
|
|
||||||
outline: 'text-foreground',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
variant: 'default',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export type BadgeVariant = VariantProps<typeof badgeVariants>['variant'];
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import type { WithElementRef } from 'bits-ui';
|
|
||||||
import type { HTMLAnchorAttributes } from 'svelte/elements';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
href,
|
|
||||||
class: className,
|
|
||||||
variant = 'default',
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAnchorAttributes> & {
|
|
||||||
variant?: BadgeVariant;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:element
|
|
||||||
this={href ? 'a' : 'span'}
|
|
||||||
bind:this={ref}
|
|
||||||
{href}
|
|
||||||
class={cn(badgeVariants({ variant, className }))}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</svelte:element>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export { default as Badge } from './badge.svelte';
|
|
||||||
export { badgeVariants, type BadgeVariant } from './badge.svelte';
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
<script lang="ts" module>
|
|
||||||
import type { WithElementRef } from 'bits-ui';
|
|
||||||
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
|
||||||
import { type VariantProps, tv } from 'tailwind-variants';
|
|
||||||
|
|
||||||
export const buttonVariants = tv({
|
|
||||||
base: 'ring-offset-background focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
||||||
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
||||||
outline: 'border-input bg-background hover:bg-accent hover:text-accent-foreground border',
|
|
||||||
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
||||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
||||||
link: 'text-primary underline-offset-4 hover:underline',
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
default: 'h-10 px-4 py-2',
|
|
||||||
sm: 'h-9 rounded-md px-3',
|
|
||||||
lg: 'h-11 rounded-md px-8',
|
|
||||||
icon: 'h-10 w-10',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
variant: 'default',
|
|
||||||
size: 'default',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
|
|
||||||
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
|
|
||||||
|
|
||||||
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
|
|
||||||
WithElementRef<HTMLAnchorAttributes> & {
|
|
||||||
variant?: ButtonVariant;
|
|
||||||
size?: ButtonSize;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
class: className,
|
|
||||||
variant = 'default',
|
|
||||||
size = 'default',
|
|
||||||
ref = $bindable(null),
|
|
||||||
href = undefined,
|
|
||||||
type = 'button',
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: ButtonProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if href}
|
|
||||||
<a bind:this={ref} class={cn(buttonVariants({ variant, size, className }))} {href} {...restProps}>
|
|
||||||
{@render children?.()}
|
|
||||||
</a>
|
|
||||||
{:else}
|
|
||||||
<button
|
|
||||||
bind:this={ref}
|
|
||||||
class={cn(buttonVariants({ variant, size, className }))}
|
|
||||||
{type}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import Root, {
|
|
||||||
type ButtonProps,
|
|
||||||
type ButtonSize,
|
|
||||||
type ButtonVariant,
|
|
||||||
buttonVariants,
|
|
||||||
} from './button.svelte';
|
|
||||||
|
|
||||||
export {
|
|
||||||
Root,
|
|
||||||
type ButtonProps as Props,
|
|
||||||
//
|
|
||||||
Root as Button,
|
|
||||||
buttonVariants,
|
|
||||||
type ButtonProps,
|
|
||||||
type ButtonSize,
|
|
||||||
type ButtonVariant,
|
|
||||||
};
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Checkbox as CheckboxPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
|
|
||||||
import Check from '@lucide/svelte/icons/check';
|
|
||||||
import Minus from '@lucide/svelte/icons/minus';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
checked = $bindable(false),
|
|
||||||
indeterminate = $bindable(false),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CheckboxPrimitive.Root
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
'border-primary ring-offset-background focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground peer box-content size-4 shrink-0 rounded-sm border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
bind:checked
|
|
||||||
bind:indeterminate
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{#snippet children({ checked, indeterminate })}
|
|
||||||
<div class="flex size-4 items-center justify-center text-current">
|
|
||||||
{#if indeterminate}
|
|
||||||
<Minus class="size-3.5" />
|
|
||||||
{:else}
|
|
||||||
<Check class={cn('size-3.5', !checked && 'text-transparent')} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/snippet}
|
|
||||||
</CheckboxPrimitive.Root>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import Root from './checkbox.svelte';
|
|
||||||
export {
|
|
||||||
Root,
|
|
||||||
//
|
|
||||||
Root as Checkbox,
|
|
||||||
};
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
|
|
||||||
import Check from 'lucide-svelte/icons/check';
|
|
||||||
import Minus from 'lucide-svelte/icons/minus';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
import type { Snippet } from 'svelte';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
checked = $bindable(false),
|
|
||||||
indeterminate = $bindable(false),
|
|
||||||
class: className,
|
|
||||||
children: childrenProp,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & {
|
|
||||||
children?: Snippet;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.CheckboxItem
|
|
||||||
bind:ref
|
|
||||||
bind:checked
|
|
||||||
bind:indeterminate
|
|
||||||
class={cn(
|
|
||||||
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{#snippet children({ checked, indeterminate })}
|
|
||||||
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
|
||||||
{#if indeterminate}
|
|
||||||
<Minus class="size-4" />
|
|
||||||
{:else}
|
|
||||||
<Check class={cn('size-4', !checked && 'text-transparent')} />
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
{@render childrenProp?.()}
|
|
||||||
{/snippet}
|
|
||||||
</DropdownMenuPrimitive.CheckboxItem>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
sideOffset = 4,
|
|
||||||
portalProps,
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: DropdownMenuPrimitive.ContentProps & {
|
|
||||||
portalProps?: DropdownMenuPrimitive.PortalProps;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.Portal {...portalProps}>
|
|
||||||
<DropdownMenuPrimitive.Content
|
|
||||||
bind:ref
|
|
||||||
{sideOffset}
|
|
||||||
class={cn(
|
|
||||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md outline-none',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
</DropdownMenuPrimitive.Portal>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
inset,
|
|
||||||
...restProps
|
|
||||||
}: DropdownMenuPrimitive.GroupHeadingProps & {
|
|
||||||
inset?: boolean;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.GroupHeading
|
|
||||||
bind:ref
|
|
||||||
class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
inset,
|
|
||||||
...restProps
|
|
||||||
}: DropdownMenuPrimitive.ItemProps & {
|
|
||||||
inset?: boolean;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.Item
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
|
||||||
inset && 'pl-8',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
import { type WithElementRef } from 'bits-ui';
|
|
||||||
import type { HTMLAttributes } from 'svelte/elements';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
inset,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
||||||
inset?: boolean;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={ref}
|
|
||||||
class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChild } from 'bits-ui';
|
|
||||||
import Circle from 'lucide-svelte/icons/circle';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children: childrenProp,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChild<DropdownMenuPrimitive.RadioItemProps> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.RadioItem
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{#snippet children({ checked })}
|
|
||||||
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
|
||||||
{#if checked}
|
|
||||||
<Circle class="size-2 fill-current" />
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
{@render childrenProp?.({ checked })}
|
|
||||||
{/snippet}
|
|
||||||
</DropdownMenuPrimitive.RadioItem>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
|
||||||
import { cn } from '$lib/utils.js';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: DropdownMenuPrimitive.SeparatorProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<DropdownMenuPrimitive.Separator
|
|
||||||
bind:ref
|
|
||||||
class={cn('bg-muted -mx-1 my-1 h-px', className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||