diff --git a/.gitattributes b/.gitattributes index 94f480d..97cc7ad 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ -* text=auto eol=lf \ No newline at end of file +* text=auto eol=lf +public/profileAssets/**/*.png filter=lfs diff=lfs merge=lfs -text diff --git a/index.html b/index.html index 2cde747..d26e767 100644 --- a/index.html +++ b/index.html @@ -1,14 +1,16 @@ - - - - Yet Another Launcher - - + + + + Yet Another Launcher - -
- - + + + + + +
+ + diff --git a/package-lock.json b/package-lock.json index 0254e17..77c2092 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-progress": "^1.0.3", + "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-toggle-group": "^1.0.4", "@radix-ui/react-tooltip": "1.0.7", "@tanstack/query-sync-storage-persister": "^5.14.0", @@ -22,6 +23,7 @@ "date-fns": "^2.30.0", "dompurify": "^3.0.6", "gray-matter": "^4.0.3", + "is-online": "^11.0.0", "js-video-url-parser": "^0.5.1", "lodash": "^4.17.21", "marked": "^11.1.0", @@ -33,6 +35,8 @@ "serialize-error": "^11.0.3", "sort-array": "^4.1.5", "tauri-plugin-log-api": "github:tauri-apps/tauri-plugin-log#v1", + "tauri-settings": "^0.3.5", + "use-overflow": "^1.2.0", "uuid": "^9.0.1", "vite-plugin-top-level-await": "^1.4.1", "zustand": "^4.4.7" @@ -423,10 +427,25 @@ "node": ">=6.9.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/android-arm": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.9.tgz", - "integrity": "sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -439,9 +458,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.9.tgz", - "integrity": "sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -454,9 +473,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.9.tgz", - "integrity": "sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -469,9 +488,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.9.tgz", - "integrity": "sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -484,9 +503,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.9.tgz", - "integrity": "sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -499,9 +518,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.9.tgz", - "integrity": "sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -514,9 +533,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.9.tgz", - "integrity": "sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -529,9 +548,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.9.tgz", - "integrity": "sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -544,9 +563,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.9.tgz", - "integrity": "sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -559,9 +578,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.9.tgz", - "integrity": "sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -574,9 +593,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.9.tgz", - "integrity": "sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -589,9 +608,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.9.tgz", - "integrity": "sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -604,9 +623,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.9.tgz", - "integrity": "sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -619,9 +638,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.9.tgz", - "integrity": "sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -634,9 +653,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.9.tgz", - "integrity": "sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -649,9 +668,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.9.tgz", - "integrity": "sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -664,9 +683,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.9.tgz", - "integrity": "sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -679,9 +698,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.9.tgz", - "integrity": "sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -694,9 +713,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.9.tgz", - "integrity": "sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -709,9 +728,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.9.tgz", - "integrity": "sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -724,9 +743,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.9.tgz", - "integrity": "sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -739,9 +758,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.9.tgz", - "integrity": "sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -930,6 +949,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1455,6 +1479,261 @@ } } }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.0.tgz", + "integrity": "sha512-bZgOKB/LtZIij75FSuPzyEti/XBhJH52ExgtdVqjCIh+Nx/FW+LhnbXtbCzIi34ccyMsyOja8T0thCzoHFXNKA==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-roving-focus": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", + "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-collection": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", + "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", + "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-presence": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.0.tgz", + "integrity": "sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", + "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", + "dependencies": { + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", + "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-toggle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz", @@ -1749,9 +2028,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.0.tgz", - "integrity": "sha512-+1ge/xmaJpm1KVBuIH38Z94zj9fBD+hp+/5WLaHgyY8XLq1ibxk/zj6dTXaqM2cAbYKq8jYlhHd6k05If1W5xA==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", "cpu": [ "arm" ], @@ -1761,9 +2040,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.0.tgz", - "integrity": "sha512-im6hUEyQ7ZfoZdNvtwgEJvBWZYauC9KVKq1w58LG2Zfz6zMd8gRrbN+xCVoqA2hv/v6fm9lp5LFGJ3za8EQH3A==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", "cpu": [ "arm64" ], @@ -1773,9 +2052,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.0.tgz", - "integrity": "sha512-u7aTMskN6Dmg1lCT0QJ+tINRt+ntUrvVkhbPfFz4bCwRZvjItx2nJtwJnJRlKMMaQCHRjrNqHRDYvE4mBm3DlQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", "cpu": [ "arm64" ], @@ -1785,9 +2064,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.0.tgz", - "integrity": "sha512-8FvEl3w2ExmpcOmX5RJD0yqXcVSOqAJJUJ29Lca29Ik+3zPS1yFimr2fr5JSZ4Z5gt8/d7WqycpgkX9nocijSw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", "cpu": [ "x64" ], @@ -1797,9 +2076,21 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.0.tgz", - "integrity": "sha512-lHoKYaRwd4gge+IpqJHCY+8Vc3hhdJfU6ukFnnrJasEBUvVlydP8PuwndbWfGkdgSvZhHfSEw6urrlBj0TSSfg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", "cpu": [ "arm" ], @@ -1809,9 +2100,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.0.tgz", - "integrity": "sha512-JbEPfhndYeWHfOSeh4DOFvNXrj7ls9S/2omijVsao+LBPTPayT1uKcK3dHW3MwDJ7KO11t9m2cVTqXnTKpeaiw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", "cpu": [ "arm64" ], @@ -1821,9 +2112,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.0.tgz", - "integrity": "sha512-ahqcSXLlcV2XUBM3/f/C6cRoh7NxYA/W7Yzuv4bDU1YscTFw7ay4LmD7l6OS8EMhTNvcrWGkEettL1Bhjf+B+w==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", "cpu": [ "arm64" ], @@ -1832,10 +2123,22 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.0.tgz", - "integrity": "sha512-uwvOYNtLw8gVtrExKhdFsYHA/kotURUmZYlinH2VcQxNCQJeJXnkmWgw2hI9Xgzhgu7J9QvWiq9TtTVwWMDa+w==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", "cpu": [ "riscv64" ], @@ -1844,10 +2147,22 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.0.tgz", - "integrity": "sha512-m6pkSwcZZD2LCFHZX/zW2aLIISyzWLU3hrLLzQKMI12+OLEzgruTovAxY5sCZJkipklaZqPy/2bEEBNjp+Y7xg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", "cpu": [ "x64" ], @@ -1857,9 +2172,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.0.tgz", - "integrity": "sha512-VFAC1RDRSbU3iOF98X42KaVicAfKf0m0OvIu8dbnqhTe26Kh6Ym9JrDulz7Hbk7/9zGc41JkV02g+p3BivOdAg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", "cpu": [ "x64" ], @@ -1869,9 +2184,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.0.tgz", - "integrity": "sha512-9jPgMvTKXARz4inw6jezMLA2ihDBvgIU9Ml01hjdVpOcMKyxFBJrn83KVQINnbeqDv0+HdO1c09hgZ8N0s820Q==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", "cpu": [ "arm64" ], @@ -1881,9 +2196,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.0.tgz", - "integrity": "sha512-WE4pT2kTXQN2bAv40Uog0AsV7/s9nT9HBWXAou8+++MBCnY51QS02KYtm6dQxxosKi1VIz/wZIrTQO5UP2EW+Q==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", "cpu": [ "ia32" ], @@ -1893,9 +2208,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.0.tgz", - "integrity": "sha512-aPP5Q5AqNGuT0tnuEkK/g4mnt3ZhheiXrDIiSVIHN9mcN21OyXDVbEMqmXPE7e2OplNLDkcvV+ZoGJa2ZImFgw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", "cpu": [ "x64" ], @@ -1904,6 +2219,17 @@ "win32" ] }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, "node_modules/@sindresorhus/merge-streams": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", @@ -2310,6 +2636,17 @@ "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==" }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, "node_modules/@tanstack/query-core": { "version": "5.14.0", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.14.0.tgz", @@ -2630,8 +2967,12 @@ "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -3049,6 +3390,21 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/aggregate-error": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", + "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", + "dependencies": { + "clean-stack": "^4.0.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3403,12 +3759,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -3615,6 +3971,31 @@ "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", "dev": true }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, "node_modules/call-bind": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", @@ -3721,6 +4102,31 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/clean-stack": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", + "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clean-stack/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -3735,6 +4141,20 @@ "node": ">=12" } }, + "node_modules/clone-regexp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-3.0.0.tgz", + "integrity": "sha512-ujdnoq2Kxb8s3ItNBtnYeXdm07FcU0u8ARAT1lQ2YdMwQC+cdiXX8KoqMVuglztILivceTtp4ivqGSmEmhBUJw==", + "dependencies": { + "is-regexp": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -3768,6 +4188,17 @@ "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", "dev": true }, + "node_modules/convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -3923,12 +4354,45 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, "node_modules/define-data-property": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", @@ -4013,6 +4477,28 @@ "node": ">=8" } }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dns-socket": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/dns-socket/-/dns-socket-4.2.2.tgz", + "integrity": "sha512-BDeBd8najI4/lS00HSKpdFia+OvUMytaVjfzR9n5Lq8MlZRSvtbI+uLtx1+XmQFls5wFU9dssccTmQQ6nfpjdg==", + "dependencies": { + "dns-packet": "^5.2.4" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -4241,9 +4727,9 @@ "dev": true }, "node_modules/esbuild": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.9.tgz", - "integrity": "sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -4252,28 +4738,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.19.9", - "@esbuild/android-arm64": "0.19.9", - "@esbuild/android-x64": "0.19.9", - "@esbuild/darwin-arm64": "0.19.9", - "@esbuild/darwin-x64": "0.19.9", - "@esbuild/freebsd-arm64": "0.19.9", - "@esbuild/freebsd-x64": "0.19.9", - "@esbuild/linux-arm": "0.19.9", - "@esbuild/linux-arm64": "0.19.9", - "@esbuild/linux-ia32": "0.19.9", - "@esbuild/linux-loong64": "0.19.9", - "@esbuild/linux-mips64el": "0.19.9", - "@esbuild/linux-ppc64": "0.19.9", - "@esbuild/linux-riscv64": "0.19.9", - "@esbuild/linux-s390x": "0.19.9", - "@esbuild/linux-x64": "0.19.9", - "@esbuild/netbsd-x64": "0.19.9", - "@esbuild/openbsd-x64": "0.19.9", - "@esbuild/sunos-x64": "0.19.9", - "@esbuild/win32-arm64": "0.19.9", - "@esbuild/win32-ia32": "0.19.9", - "@esbuild/win32-x64": "0.19.9" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/escalade": { @@ -4831,9 +5318,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -4886,6 +5373,14 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "engines": { + "node": ">= 14.17" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -4941,6 +5436,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/function-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-0.1.1.tgz", + "integrity": "sha512-0NVVC0TaP7dSTvn1yMiy6d6Q8gifzbvQafO46RtLG/kHJUBNd+pVRGOBoK44wNBvtSPUJRfdVvkFdD3p0xvyZg==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/function.prototype.name": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", @@ -5021,6 +5527,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -5152,6 +5669,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/got": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", + "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -5326,6 +5867,23 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -5386,6 +5944,17 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -5424,6 +5993,17 @@ "loose-envify": "^1.0.0" } }, + "node_modules/ip-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-5.0.0.tgz", + "integrity": "sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -5619,6 +6199,21 @@ "node": ">=0.10.0" } }, + "node_modules/is-ip": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-5.0.1.tgz", + "integrity": "sha512-FCsGHdlrOnZQcp0+XT5a+pYowf33itBalCl+7ovNXC/7o5BhIpG14M3OrpPPdBSIQJCm+0M5+9mO7S9VVTTCFw==", + "dependencies": { + "ip-regex": "^5.0.0", + "super-regex": "^0.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", @@ -5680,6 +6275,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-online": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/is-online/-/is-online-11.0.0.tgz", + "integrity": "sha512-LY3UOlRGCofw5RMhsEFHQ9xQ6jJXL9wFWWIfmSdNo8vO+DrIvan3G9hAPZiMRXddVZS2v9+CV4z9PslLhBGIyA==", + "dependencies": { + "got": "^13.0.0", + "p-any": "^4.0.0", + "p-timeout": "^6.1.2", + "public-ip": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -5705,6 +6317,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-regexp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-3.1.0.tgz", + "integrity": "sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-set": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", @@ -5873,6 +6496,11 @@ "node": ">=4" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -5928,6 +6556,14 @@ "node": ">=4.0" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -6010,6 +6646,17 @@ "tslib": "^2.0.3" } }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -6094,6 +6741,17 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -6234,6 +6892,17 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6400,6 +7069,29 @@ "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", "dev": true }, + "node_modules/p-any": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-any/-/p-any-4.0.0.tgz", + "integrity": "sha512-S/B50s+pAVe0wmEZHmBs/9yJXeZ5KhHzOsgKzt0hRdgkoR3DxW9ts46fcsWi/r3VnzsnkKS7q4uimze+zjdryw==", + "dependencies": { + "p-cancelable": "^3.0.0", + "p-some": "^6.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "engines": { + "node": ">=12.20" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6430,6 +7122,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-some": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-some/-/p-some-6.0.0.tgz", + "integrity": "sha512-CJbQCKdfSX3fIh8/QKgS+9rjm7OBNUTmwWswAFQAhc8j1NR1dsEDETUEuVUtQHZpV+J03LqWBEwvu0g1Yn+TYg==", + "dependencies": { + "aggregate-error": "^4.0.0", + "p-cancelable": "^3.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz", + "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -6544,9 +7262,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -6582,9 +7300,9 @@ } }, "node_modules/postcss": { - "version": "8.4.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", - "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "funding": [ { "type": "opencollective", @@ -6601,8 +7319,8 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -6788,6 +7506,22 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, + "node_modules/public-ip": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/public-ip/-/public-ip-7.0.1.tgz", + "integrity": "sha512-DdNcqcIbI0wEeCBcqX+bmZpUCvrDMJHXE553zgyG1MZ8S1a/iCCxmK9iTjjql+SpHSv4cZkmRv5/zGYW93AlCw==", + "dependencies": { + "dns-socket": "^4.2.2", + "got": "^13.0.0", + "is-ip": "^5.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -6841,6 +7575,17 @@ } ] }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -7119,6 +7864,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -7137,6 +7887,20 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -7173,9 +7937,12 @@ } }, "node_modules/rollup": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.0.tgz", - "integrity": "sha512-bUHW/9N21z64gw8s6tP4c88P382Bq/L5uZDowHlHx6s/QWpjJXivIAbEw6LZthgSvlEizZBfLC4OAvWe7aoF7A==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "dependencies": { + "@types/estree": "1.0.5" + }, "bin": { "rollup": "dist/bin/rollup" }, @@ -7184,19 +7951,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.9.0", - "@rollup/rollup-android-arm64": "4.9.0", - "@rollup/rollup-darwin-arm64": "4.9.0", - "@rollup/rollup-darwin-x64": "4.9.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.9.0", - "@rollup/rollup-linux-arm64-gnu": "4.9.0", - "@rollup/rollup-linux-arm64-musl": "4.9.0", - "@rollup/rollup-linux-riscv64-gnu": "4.9.0", - "@rollup/rollup-linux-x64-gnu": "4.9.0", - "@rollup/rollup-linux-x64-musl": "4.9.0", - "@rollup/rollup-win32-arm64-msvc": "4.9.0", - "@rollup/rollup-win32-ia32-msvc": "4.9.0", - "@rollup/rollup-win32-x64-msvc": "4.9.0", + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", "fsevents": "~2.3.2" } }, @@ -7453,9 +8223,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "engines": { "node": ">=0.10.0" } @@ -7615,6 +8385,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/super-regex": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-0.2.0.tgz", + "integrity": "sha512-WZzIx3rC1CvbMDloLsVw0lkZVKJWbrkJ0k1ghKFmcnPrW1+jWbgTkTEWVtD9lMdmI4jZEz40+naBxl1dCUhXXw==", + "dependencies": { + "clone-regexp": "^3.0.0", + "function-timeout": "^0.1.0", + "time-span": "^5.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -7676,6 +8462,11 @@ "url": "https://opencollective.com/tauri" } }, + "node_modules/tauri-settings": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/tauri-settings/-/tauri-settings-0.3.5.tgz", + "integrity": "sha512-07v+i6ig+z1eWHm9WYAvOpFPM7afeKZZmM0egIY0aLSleaZy33zEiKmefNKI+G8ymOAJX/ZYN1N1cM2lyGOiJw==" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -7688,6 +8479,20 @@ "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", "dev": true }, + "node_modules/time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", @@ -8015,6 +8820,18 @@ } } }, + "node_modules/use-overflow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-overflow/-/use-overflow-1.2.0.tgz", + "integrity": "sha512-Uw3rvw+OizuOsX0HDWXZk9VpfSuT74lNKW7Eq/fWQ2egUUtOnunbHmEkD15NJJZjk9M0CZRY9sN4kiZtVC+EpQ==", + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/use-sidecar": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", @@ -8076,13 +8893,13 @@ } }, "node_modules/vite": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.10.tgz", - "integrity": "sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.2.tgz", + "integrity": "sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==", "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.32", - "rollup": "^4.2.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.38", + "rollup": "^4.13.0" }, "bin": { "vite": "bin/vite.js" @@ -8710,136 +9527,142 @@ "to-fast-properties": "^2.0.0" } }, + "@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "optional": true + }, "@esbuild/android-arm": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.9.tgz", - "integrity": "sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "optional": true }, "@esbuild/android-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.9.tgz", - "integrity": "sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "optional": true }, "@esbuild/android-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.9.tgz", - "integrity": "sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.9.tgz", - "integrity": "sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "optional": true }, "@esbuild/darwin-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.9.tgz", - "integrity": "sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.9.tgz", - "integrity": "sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.9.tgz", - "integrity": "sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "optional": true }, "@esbuild/linux-arm": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.9.tgz", - "integrity": "sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "optional": true }, "@esbuild/linux-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.9.tgz", - "integrity": "sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "optional": true }, "@esbuild/linux-ia32": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.9.tgz", - "integrity": "sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "optional": true }, "@esbuild/linux-loong64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.9.tgz", - "integrity": "sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.9.tgz", - "integrity": "sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.9.tgz", - "integrity": "sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.9.tgz", - "integrity": "sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "optional": true }, "@esbuild/linux-s390x": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.9.tgz", - "integrity": "sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "optional": true }, "@esbuild/linux-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.9.tgz", - "integrity": "sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.9.tgz", - "integrity": "sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.9.tgz", - "integrity": "sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "optional": true }, "@esbuild/sunos-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.9.tgz", - "integrity": "sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "optional": true }, "@esbuild/win32-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.9.tgz", - "integrity": "sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "optional": true }, "@esbuild/win32-ia32": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.9.tgz", - "integrity": "sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "optional": true }, "@esbuild/win32-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.9.tgz", - "integrity": "sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "optional": true }, "@eslint-community/eslint-utils": { @@ -8974,6 +9797,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -9254,6 +10082,126 @@ "@radix-ui/react-compose-refs": "1.0.1" } }, + "@radix-ui/react-tabs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.0.tgz", + "integrity": "sha512-bZgOKB/LtZIij75FSuPzyEti/XBhJH52ExgtdVqjCIh+Nx/FW+LhnbXtbCzIi34ccyMsyOja8T0thCzoHFXNKA==", + "requires": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-roving-focus": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "dependencies": { + "@radix-ui/primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", + "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" + }, + "@radix-ui/react-collection": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", + "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", + "requires": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0" + } + }, + "@radix-ui/react-compose-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", + "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", + "requires": {} + }, + "@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "requires": {} + }, + "@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "requires": {} + }, + "@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "requires": { + "@radix-ui/react-use-layout-effect": "1.1.0" + } + }, + "@radix-ui/react-presence": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.0.tgz", + "integrity": "sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==", + "requires": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + } + }, + "@radix-ui/react-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", + "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", + "requires": { + "@radix-ui/react-slot": "1.1.0" + } + }, + "@radix-ui/react-roving-focus": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", + "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==", + "requires": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + } + }, + "@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "requires": { + "@radix-ui/react-compose-refs": "1.1.0" + } + }, + "@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "requires": {} + }, + "@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "requires": { + "@radix-ui/react-use-callback-ref": "1.1.0" + } + }, + "@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "requires": {} + } + } + }, "@radix-ui/react-toggle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz", @@ -9403,83 +10351,106 @@ } }, "@rollup/rollup-android-arm-eabi": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.0.tgz", - "integrity": "sha512-+1ge/xmaJpm1KVBuIH38Z94zj9fBD+hp+/5WLaHgyY8XLq1ibxk/zj6dTXaqM2cAbYKq8jYlhHd6k05If1W5xA==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", "optional": true }, "@rollup/rollup-android-arm64": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.0.tgz", - "integrity": "sha512-im6hUEyQ7ZfoZdNvtwgEJvBWZYauC9KVKq1w58LG2Zfz6zMd8gRrbN+xCVoqA2hv/v6fm9lp5LFGJ3za8EQH3A==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", "optional": true }, "@rollup/rollup-darwin-arm64": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.0.tgz", - "integrity": "sha512-u7aTMskN6Dmg1lCT0QJ+tINRt+ntUrvVkhbPfFz4bCwRZvjItx2nJtwJnJRlKMMaQCHRjrNqHRDYvE4mBm3DlQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", "optional": true }, "@rollup/rollup-darwin-x64": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.0.tgz", - "integrity": "sha512-8FvEl3w2ExmpcOmX5RJD0yqXcVSOqAJJUJ29Lca29Ik+3zPS1yFimr2fr5JSZ4Z5gt8/d7WqycpgkX9nocijSw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.0.tgz", - "integrity": "sha512-lHoKYaRwd4gge+IpqJHCY+8Vc3hhdJfU6ukFnnrJasEBUvVlydP8PuwndbWfGkdgSvZhHfSEw6urrlBj0TSSfg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "optional": true + }, + "@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", "optional": true }, "@rollup/rollup-linux-arm64-gnu": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.0.tgz", - "integrity": "sha512-JbEPfhndYeWHfOSeh4DOFvNXrj7ls9S/2omijVsao+LBPTPayT1uKcK3dHW3MwDJ7KO11t9m2cVTqXnTKpeaiw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", "optional": true }, "@rollup/rollup-linux-arm64-musl": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.0.tgz", - "integrity": "sha512-ahqcSXLlcV2XUBM3/f/C6cRoh7NxYA/W7Yzuv4bDU1YscTFw7ay4LmD7l6OS8EMhTNvcrWGkEettL1Bhjf+B+w==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "optional": true + }, + "@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.0.tgz", - "integrity": "sha512-uwvOYNtLw8gVtrExKhdFsYHA/kotURUmZYlinH2VcQxNCQJeJXnkmWgw2hI9Xgzhgu7J9QvWiq9TtTVwWMDa+w==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", "optional": true }, "@rollup/rollup-linux-x64-gnu": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.0.tgz", - "integrity": "sha512-m6pkSwcZZD2LCFHZX/zW2aLIISyzWLU3hrLLzQKMI12+OLEzgruTovAxY5sCZJkipklaZqPy/2bEEBNjp+Y7xg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", "optional": true }, "@rollup/rollup-linux-x64-musl": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.0.tgz", - "integrity": "sha512-VFAC1RDRSbU3iOF98X42KaVicAfKf0m0OvIu8dbnqhTe26Kh6Ym9JrDulz7Hbk7/9zGc41JkV02g+p3BivOdAg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", "optional": true }, "@rollup/rollup-win32-arm64-msvc": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.0.tgz", - "integrity": "sha512-9jPgMvTKXARz4inw6jezMLA2ihDBvgIU9Ml01hjdVpOcMKyxFBJrn83KVQINnbeqDv0+HdO1c09hgZ8N0s820Q==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", "optional": true }, "@rollup/rollup-win32-ia32-msvc": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.0.tgz", - "integrity": "sha512-WE4pT2kTXQN2bAv40Uog0AsV7/s9nT9HBWXAou8+++MBCnY51QS02KYtm6dQxxosKi1VIz/wZIrTQO5UP2EW+Q==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", "optional": true }, "@rollup/rollup-win32-x64-msvc": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.0.tgz", - "integrity": "sha512-aPP5Q5AqNGuT0tnuEkK/g4mnt3ZhheiXrDIiSVIHN9mcN21OyXDVbEMqmXPE7e2OplNLDkcvV+ZoGJa2ZImFgw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", "optional": true }, + "@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==" + }, "@sindresorhus/merge-streams": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", @@ -9675,6 +10646,14 @@ "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==" }, + "@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "requires": { + "defer-to-connect": "^2.0.1" + } + }, "@tanstack/query-core": { "version": "5.14.0", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.14.0.tgz", @@ -9859,8 +10838,12 @@ "@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" }, "@types/json-schema": { "version": "7.0.15", @@ -10154,6 +11137,15 @@ "dev": true, "requires": {} }, + "aggregate-error": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", + "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", + "requires": { + "clean-stack": "^4.0.0", + "indent-string": "^5.0.0" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -10405,12 +11397,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "brorand": { @@ -10558,6 +11550,25 @@ "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", "dev": true }, + "cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==" + }, + "cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "requires": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + } + }, "call-bind": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", @@ -10624,6 +11635,21 @@ "safe-buffer": "^5.0.1" } }, + "clean-stack": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", + "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", + "requires": { + "escape-string-regexp": "5.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==" + } + } + }, "cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -10635,6 +11661,14 @@ "wrap-ansi": "^7.0.0" } }, + "clone-regexp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-3.0.0.tgz", + "integrity": "sha512-ujdnoq2Kxb8s3ItNBtnYeXdm07FcU0u8ARAT1lQ2YdMwQC+cdiXX8KoqMVuglztILivceTtp4ivqGSmEmhBUJw==", + "requires": { + "is-regexp": "^3.0.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -10668,6 +11702,11 @@ "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", "dev": true }, + "convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==" + }, "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -10790,12 +11829,32 @@ "ms": "2.1.2" } }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, "define-data-property": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", @@ -10867,6 +11926,22 @@ "path-type": "^4.0.0" } }, + "dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } + }, + "dns-socket": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/dns-socket/-/dns-socket-4.2.2.tgz", + "integrity": "sha512-BDeBd8najI4/lS00HSKpdFia+OvUMytaVjfzR9n5Lq8MlZRSvtbI+uLtx1+XmQFls5wFU9dssccTmQQ6nfpjdg==", + "requires": { + "dns-packet": "^5.2.4" + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -11064,32 +12139,33 @@ "dev": true }, "esbuild": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.9.tgz", - "integrity": "sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==", - "requires": { - "@esbuild/android-arm": "0.19.9", - "@esbuild/android-arm64": "0.19.9", - "@esbuild/android-x64": "0.19.9", - "@esbuild/darwin-arm64": "0.19.9", - "@esbuild/darwin-x64": "0.19.9", - "@esbuild/freebsd-arm64": "0.19.9", - "@esbuild/freebsd-x64": "0.19.9", - "@esbuild/linux-arm": "0.19.9", - "@esbuild/linux-arm64": "0.19.9", - "@esbuild/linux-ia32": "0.19.9", - "@esbuild/linux-loong64": "0.19.9", - "@esbuild/linux-mips64el": "0.19.9", - "@esbuild/linux-ppc64": "0.19.9", - "@esbuild/linux-riscv64": "0.19.9", - "@esbuild/linux-s390x": "0.19.9", - "@esbuild/linux-x64": "0.19.9", - "@esbuild/netbsd-x64": "0.19.9", - "@esbuild/openbsd-x64": "0.19.9", - "@esbuild/sunos-x64": "0.19.9", - "@esbuild/win32-arm64": "0.19.9", - "@esbuild/win32-ia32": "0.19.9", - "@esbuild/win32-x64": "0.19.9" + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "requires": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "escalade": { @@ -11515,9 +12591,9 @@ } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -11558,6 +12634,11 @@ "is-callable": "^1.1.3" } }, + "form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==" + }, "fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -11593,6 +12674,11 @@ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true }, + "function-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-0.1.1.tgz", + "integrity": "sha512-0NVVC0TaP7dSTvn1yMiy6d6Q8gifzbvQafO46RtLG/kHJUBNd+pVRGOBoK44wNBvtSPUJRfdVvkFdD3p0xvyZg==" + }, "function.prototype.name": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", @@ -11646,6 +12732,11 @@ "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", "dev": true }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, "get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -11740,6 +12831,24 @@ "get-intrinsic": "^1.1.3" } }, + "got": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", + "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", + "requires": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + } + }, "graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -11874,6 +12983,20 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, + "http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + } + }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -11908,6 +13031,11 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, + "indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -11943,6 +13071,11 @@ "loose-envify": "^1.0.0" } }, + "ip-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-5.0.0.tgz", + "integrity": "sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==" + }, "is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -12075,6 +13208,15 @@ "is-extglob": "^2.1.1" } }, + "is-ip": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-5.0.1.tgz", + "integrity": "sha512-FCsGHdlrOnZQcp0+XT5a+pYowf33itBalCl+7ovNXC/7o5BhIpG14M3OrpPPdBSIQJCm+0M5+9mO7S9VVTTCFw==", + "requires": { + "ip-regex": "^5.0.0", + "super-regex": "^0.2.0" + } + }, "is-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", @@ -12112,6 +13254,17 @@ "has-tostringtag": "^1.0.0" } }, + "is-online": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/is-online/-/is-online-11.0.0.tgz", + "integrity": "sha512-LY3UOlRGCofw5RMhsEFHQ9xQ6jJXL9wFWWIfmSdNo8vO+DrIvan3G9hAPZiMRXddVZS2v9+CV4z9PslLhBGIyA==", + "requires": { + "got": "^13.0.0", + "p-any": "^4.0.0", + "p-timeout": "^6.1.2", + "public-ip": "^7.0.1" + } + }, "is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -12128,6 +13281,11 @@ "has-tostringtag": "^1.0.0" } }, + "is-regexp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-3.1.0.tgz", + "integrity": "sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==" + }, "is-set": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", @@ -12251,6 +13409,11 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -12295,6 +13458,14 @@ "object.assign": "^4.1.3" } }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "requires": { + "json-buffer": "3.0.1" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -12359,6 +13530,11 @@ "tslib": "^2.0.3" } }, + "lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==" + }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -12427,6 +13603,11 @@ } } }, + "mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==" + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -12542,6 +13723,11 @@ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true }, + "normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -12666,6 +13852,20 @@ "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", "dev": true }, + "p-any": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-any/-/p-any-4.0.0.tgz", + "integrity": "sha512-S/B50s+pAVe0wmEZHmBs/9yJXeZ5KhHzOsgKzt0hRdgkoR3DxW9ts46fcsWi/r3VnzsnkKS7q4uimze+zjdryw==", + "requires": { + "p-cancelable": "^3.0.0", + "p-some": "^6.0.0" + } + }, + "p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==" + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -12684,6 +13884,20 @@ "p-limit": "^3.0.2" } }, + "p-some": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-some/-/p-some-6.0.0.tgz", + "integrity": "sha512-CJbQCKdfSX3fIh8/QKgS+9rjm7OBNUTmwWswAFQAhc8j1NR1dsEDETUEuVUtQHZpV+J03LqWBEwvu0g1Yn+TYg==", + "requires": { + "aggregate-error": "^4.0.0", + "p-cancelable": "^3.0.0" + } + }, + "p-timeout": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz", + "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==" + }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -12774,9 +13988,9 @@ } }, "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "picomatch": { "version": "2.3.1", @@ -12800,13 +14014,13 @@ } }, "postcss": { - "version": "8.4.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", - "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "requires": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" } }, "postcss-cli": { @@ -12928,6 +14142,16 @@ } } }, + "public-ip": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/public-ip/-/public-ip-7.0.1.tgz", + "integrity": "sha512-DdNcqcIbI0wEeCBcqX+bmZpUCvrDMJHXE553zgyG1MZ8S1a/iCCxmK9iTjjql+SpHSv4cZkmRv5/zGYW93AlCw==", + "requires": { + "dns-socket": "^4.2.2", + "got": "^13.0.0", + "is-ip": "^5.0.1" + } + }, "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -12955,6 +14179,11 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -13141,6 +14370,11 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -13153,6 +14387,14 @@ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true }, + "responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "requires": { + "lowercase-keys": "^3.0.0" + } + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -13179,23 +14421,27 @@ } }, "rollup": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.0.tgz", - "integrity": "sha512-bUHW/9N21z64gw8s6tP4c88P382Bq/L5uZDowHlHx6s/QWpjJXivIAbEw6LZthgSvlEizZBfLC4OAvWe7aoF7A==", - "requires": { - "@rollup/rollup-android-arm-eabi": "4.9.0", - "@rollup/rollup-android-arm64": "4.9.0", - "@rollup/rollup-darwin-arm64": "4.9.0", - "@rollup/rollup-darwin-x64": "4.9.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.9.0", - "@rollup/rollup-linux-arm64-gnu": "4.9.0", - "@rollup/rollup-linux-arm64-musl": "4.9.0", - "@rollup/rollup-linux-riscv64-gnu": "4.9.0", - "@rollup/rollup-linux-x64-gnu": "4.9.0", - "@rollup/rollup-linux-x64-musl": "4.9.0", - "@rollup/rollup-win32-arm64-msvc": "4.9.0", - "@rollup/rollup-win32-ia32-msvc": "4.9.0", - "@rollup/rollup-win32-x64-msvc": "4.9.0", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "requires": { + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@types/estree": "1.0.5", "fsevents": "~2.3.2" } }, @@ -13372,9 +14618,9 @@ } }, "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==" }, "sprintf-js": { "version": "1.0.3", @@ -13498,6 +14744,16 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "super-regex": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-0.2.0.tgz", + "integrity": "sha512-WZzIx3rC1CvbMDloLsVw0lkZVKJWbrkJ0k1ghKFmcnPrW1+jWbgTkTEWVtD9lMdmI4jZEz40+naBxl1dCUhXXw==", + "requires": { + "clone-regexp": "^3.0.0", + "function-timeout": "^0.1.0", + "time-span": "^5.1.0" + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -13539,6 +14795,11 @@ } } }, + "tauri-settings": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/tauri-settings/-/tauri-settings-0.3.5.tgz", + "integrity": "sha512-07v+i6ig+z1eWHm9WYAvOpFPM7afeKZZmM0egIY0aLSleaZy33zEiKmefNKI+G8ymOAJX/ZYN1N1cM2lyGOiJw==" + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -13551,6 +14812,14 @@ "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", "dev": true }, + "time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", + "requires": { + "convert-hrtime": "^5.0.0" + } + }, "timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", @@ -13771,6 +15040,12 @@ "tslib": "^2.0.0" } }, + "use-overflow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-overflow/-/use-overflow-1.2.0.tgz", + "integrity": "sha512-Uw3rvw+OizuOsX0HDWXZk9VpfSuT74lNKW7Eq/fWQ2egUUtOnunbHmEkD15NJJZjk9M0CZRY9sN4kiZtVC+EpQ==", + "requires": {} + }, "use-sidecar": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", @@ -13811,14 +15086,14 @@ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" }, "vite": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.10.tgz", - "integrity": "sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.2.tgz", + "integrity": "sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==", "requires": { - "esbuild": "^0.19.3", + "esbuild": "^0.21.3", "fsevents": "~2.3.3", - "postcss": "^8.4.32", - "rollup": "^4.2.0" + "postcss": "^8.4.38", + "rollup": "^4.13.0" } }, "vite-plugin-node-polyfills": { diff --git a/package.json b/package.json index d8b1e88..b2150c7 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-progress": "^1.0.3", + "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-toggle-group": "^1.0.4", "@radix-ui/react-tooltip": "1.0.7", "@tanstack/query-sync-storage-persister": "^5.14.0", @@ -53,6 +54,7 @@ "date-fns": "^2.30.0", "dompurify": "^3.0.6", "gray-matter": "^4.0.3", + "is-online": "^11.0.0", "js-video-url-parser": "^0.5.1", "lodash": "^4.17.21", "marked": "^11.1.0", @@ -64,6 +66,8 @@ "serialize-error": "^11.0.3", "sort-array": "^4.1.5", "tauri-plugin-log-api": "github:tauri-apps/tauri-plugin-log#v1", + "tauri-settings": "^0.3.5", + "use-overflow": "^1.2.0", "uuid": "^9.0.1", "vite-plugin-top-level-await": "^1.4.1", "zustand": "^4.4.7" diff --git a/public/fonts/ArchivoBlack/ArchivoBlack-Regular.ttf b/public/fonts/ArchivoBlack/ArchivoBlack-Regular.ttf new file mode 100644 index 0000000..ebadf62 Binary files /dev/null and b/public/fonts/ArchivoBlack/ArchivoBlack-Regular.ttf differ diff --git a/public/fonts/ArchivoBlack/archivoBlack.css b/public/fonts/ArchivoBlack/archivoBlack.css new file mode 100644 index 0000000..aa767b7 --- /dev/null +++ b/public/fonts/ArchivoBlack/archivoBlack.css @@ -0,0 +1,7 @@ +@font-face { + font-family: "Archivo Black"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("ArchivoBlack-Regular.ttf") format("truetype"); +} diff --git a/public/fonts/BigShouldersText/BigShouldersText-Regular.ttf b/public/fonts/BigShouldersText/BigShouldersText-Regular.ttf new file mode 100644 index 0000000..709aeb5 Binary files /dev/null and b/public/fonts/BigShouldersText/BigShouldersText-Regular.ttf differ diff --git a/public/fonts/BigShouldersText/bigShouldersText.css b/public/fonts/BigShouldersText/bigShouldersText.css new file mode 100644 index 0000000..6d31b36 --- /dev/null +++ b/public/fonts/BigShouldersText/bigShouldersText.css @@ -0,0 +1,8 @@ +@font-face { + font-family: "Big Shoulders Text"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("BigShouldersText-Regular.ttf") format("truetype"); +} + diff --git a/public/fonts/Inter/inter.css b/public/fonts/Inter/inter.css index f450010..89c6d47 100644 --- a/public/fonts/Inter/inter.css +++ b/public/fonts/Inter/inter.css @@ -1,180 +1,190 @@ @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url("Inter-Thin.woff2?v=3.19") format("woff2"), - url("Inter-Thin.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: normal; + font-weight: 100; + font-display: swap; + src: url("Inter-Thin.woff2?v=3.19") format("woff2"), + url("Inter-Thin.woff?v=3.19") format("woff"); } + @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 100; - font-display: swap; - src: url("Inter-ThinItalic.woff2?v=3.19") format("woff2"), - url("Inter-ThinItalic.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: italic; + font-weight: 100; + font-display: swap; + src: url("Inter-ThinItalic.woff2?v=3.19") format("woff2"), + url("Inter-ThinItalic.woff?v=3.19") format("woff"); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 200; - font-display: swap; - src: url("Inter-ExtraLight.woff2?v=3.19") format("woff2"), - url("Inter-ExtraLight.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: normal; + font-weight: 200; + font-display: swap; + src: url("Inter-ExtraLight.woff2?v=3.19") format("woff2"), + url("Inter-ExtraLight.woff?v=3.19") format("woff"); } + @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 200; - font-display: swap; - src: url("Inter-ExtraLightItalic.woff2?v=3.19") format("woff2"), - url("Inter-ExtraLightItalic.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: italic; + font-weight: 200; + font-display: swap; + src: url("Inter-ExtraLightItalic.woff2?v=3.19") format("woff2"), + url("Inter-ExtraLightItalic.woff?v=3.19") format("woff"); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url("Inter-Light.woff2?v=3.19") format("woff2"), - url("Inter-Light.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url("Inter-Light.woff2?v=3.19") format("woff2"), + url("Inter-Light.woff?v=3.19") format("woff"); } + @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url("Inter-LightItalic.woff2?v=3.19") format("woff2"), - url("Inter-LightItalic.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url("Inter-LightItalic.woff2?v=3.19") format("woff2"), + url("Inter-LightItalic.woff?v=3.19") format("woff"); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url("Inter-Regular.woff2?v=3.19") format("woff2"), - url("Inter-Regular.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("Inter-Regular.woff2?v=3.19") format("woff2"), + url("Inter-Regular.woff?v=3.19") format("woff"); } + @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url("Inter-Italic.woff2?v=3.19") format("woff2"), - url("Inter-Italic.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url("Inter-Italic.woff2?v=3.19") format("woff2"), + url("Inter-Italic.woff?v=3.19") format("woff"); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url("Inter-Medium.woff2?v=3.19") format("woff2"), - url("Inter-Medium.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url("Inter-Medium.woff2?v=3.19") format("woff2"), + url("Inter-Medium.woff?v=3.19") format("woff"); } + @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url("Inter-MediumItalic.woff2?v=3.19") format("woff2"), - url("Inter-MediumItalic.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url("Inter-MediumItalic.woff2?v=3.19") format("woff2"), + url("Inter-MediumItalic.woff?v=3.19") format("woff"); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url("Inter-SemiBold.woff2?v=3.19") format("woff2"), - url("Inter-SemiBold.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url("Inter-SemiBold.woff2?v=3.19") format("woff2"), + url("Inter-SemiBold.woff?v=3.19") format("woff"); } + @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url("Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"), - url("Inter-SemiBoldItalic.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url("Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"), + url("Inter-SemiBoldItalic.woff?v=3.19") format("woff"); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url("Inter-Bold.woff2?v=3.19") format("woff2"), - url("Inter-Bold.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url("Inter-Bold.woff2?v=3.19") format("woff2"), + url("Inter-Bold.woff?v=3.19") format("woff"); } + @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"), - url("Inter-BoldItalic.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"), + url("Inter-BoldItalic.woff?v=3.19") format("woff"); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url("Inter-ExtraBold.woff2?v=3.19") format("woff2"), - url("Inter-ExtraBold.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url("Inter-ExtraBold.woff2?v=3.19") format("woff2"), + url("Inter-ExtraBold.woff?v=3.19") format("woff"); } + @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url("Inter-ExtraBoldItalic.woff2?v=3.19") format("woff2"), - url("Inter-ExtraBoldItalic.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: italic; + font-weight: 800; + font-display: swap; + src: url("Inter-ExtraBoldItalic.woff2?v=3.19") format("woff2"), + url("Inter-ExtraBoldItalic.woff?v=3.19") format("woff"); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 900; - font-display: swap; - src: url("Inter-Black.woff2?v=3.19") format("woff2"), - url("Inter-Black.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: normal; + font-weight: 900; + font-display: swap; + src: url("Inter-Black.woff2?v=3.19") format("woff2"), + url("Inter-Black.woff?v=3.19") format("woff"); } + @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 900; - font-display: swap; - src: url("Inter-BlackItalic.woff2?v=3.19") format("woff2"), - url("Inter-BlackItalic.woff?v=3.19") format("woff"); + font-family: "Inter"; + font-style: italic; + font-weight: 900; + font-display: swap; + src: url("Inter-BlackItalic.woff2?v=3.19") format("woff2"), + url("Inter-BlackItalic.woff?v=3.19") format("woff"); } /* ------------------------------------------------------- Variable font. Usage: - html { font-family: 'Inter', sans-serif; } + html { font-family: "Inter", sans-serif; } @supports (font-variation-settings: normal) { - html { font-family: 'Inter var', sans-serif; } + html { font-family: "Inter var", sans-serif; } } */ @font-face { - font-family: 'Inter var'; - font-weight: 100 900; - font-display: swap; - font-style: normal; - font-named-instance: 'Regular'; - src: url("Inter-roman.var.woff2?v=3.19") format("woff2"); + font-family: "Inter var"; + font-weight: 100 900; + font-display: swap; + font-style: normal; + font-named-instance: "Regular"; + src: url("Inter-roman.var.woff2?v=3.19") format("woff2"); } + @font-face { - font-family: 'Inter var'; - font-weight: 100 900; - font-display: swap; - font-style: italic; - font-named-instance: 'Italic'; - src: url("Inter-italic.var.woff2?v=3.19") format("woff2"); + font-family: "Inter var"; + font-weight: 100 900; + font-display: swap; + font-style: italic; + font-named-instance: "Italic"; + src: url("Inter-italic.var.woff2?v=3.19") format("woff2"); } @@ -192,9 +202,9 @@ explicitly, e.g. */ @font-face { - font-family: 'Inter var experimental'; - font-weight: 100 900; - font-display: swap; - font-style: oblique 0deg 10deg; - src: url("Inter.var.woff2?v=3.19") format("woff2"); + font-family: "Inter var experimental"; + font-weight: 100 900; + font-display: swap; + font-style: oblique 0deg 10deg; + src: url("Inter.var.woff2?v=3.19") format("woff2"); } diff --git a/public/profileAssets/banners/Development.png b/public/profileAssets/banners/Development.png new file mode 100644 index 0000000..78fae04 --- /dev/null +++ b/public/profileAssets/banners/Development.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:992d1575eae9daa8a6f0f51c4d947f7c901be03d91a23d241b090fcaef64e7dd +size 663612 diff --git a/public/profileAssets/banners/Nightly.png b/public/profileAssets/banners/Nightly.png new file mode 100644 index 0000000..c28ee91 --- /dev/null +++ b/public/profileAssets/banners/Nightly.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6bec4422f023b430ce0cc638a87a94e3d7dfe2622d92980a6cba957e5677eb07 +size 702339 diff --git a/public/profileAssets/banners/SetlistBack.png b/public/profileAssets/banners/SetlistBack.png new file mode 100644 index 0000000..83785ab --- /dev/null +++ b/public/profileAssets/banners/SetlistBack.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b56eb4dc63bc2bbcd85f8cba489ec6cef1ecc3ebd03475604993b49ae88f747 +size 283808 diff --git a/public/profileAssets/banners/SetlistFront.png b/public/profileAssets/banners/SetlistFront.png new file mode 100644 index 0000000..3de2c77 --- /dev/null +++ b/public/profileAssets/banners/SetlistFront.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30c46b58dc3df4964bd5421838c57840b1b6e6b71bde9d17f69625f1581ac887 +size 45896 diff --git a/public/profileAssets/banners/SetlistSummertide.png b/public/profileAssets/banners/SetlistSummertide.png new file mode 100644 index 0000000..f21e680 --- /dev/null +++ b/public/profileAssets/banners/SetlistSummertide.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c79eb7082c40a1ddc54999843c45b3d40c9658c18b4e1a8998dee6b5c5ce2dd +size 287285 diff --git a/public/profileAssets/banners/SetlistWanderkid.png b/public/profileAssets/banners/SetlistWanderkid.png new file mode 100644 index 0000000..6d4e3c3 --- /dev/null +++ b/public/profileAssets/banners/SetlistWanderkid.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ae22a40effed4382ff38d73829e2eae9efab9f3f77c76da37fe0f0607b09bb5 +size 349759 diff --git a/public/profileAssets/banners/SetlistWave.png b/public/profileAssets/banners/SetlistWave.png new file mode 100644 index 0000000..3a7ce12 --- /dev/null +++ b/public/profileAssets/banners/SetlistWave.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ecae40061a9d8814b95794d79abdadcc87b87f1f5ded54162d3ea4b677c3cf5c +size 443794 diff --git a/public/profileAssets/banners/SetlistWaveAlt.png b/public/profileAssets/banners/SetlistWaveAlt.png new file mode 100644 index 0000000..99d0ef6 --- /dev/null +++ b/public/profileAssets/banners/SetlistWaveAlt.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1cca9cca219394296e166326d4e9c60a6f13d42de6188542a7a7493c40a2c3e8 +size 443694 diff --git a/public/profileAssets/banners/Stable.png b/public/profileAssets/banners/Stable.png new file mode 100644 index 0000000..a060338 --- /dev/null +++ b/public/profileAssets/banners/Stable.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95c3d3b2d696dd1fdb7bcc02abdf8b625ee658a07d17e08db5a3f2d46df97761 +size 559675 diff --git a/public/profileAssets/icons/Development.png b/public/profileAssets/icons/Development.png new file mode 100644 index 0000000..20355d8 --- /dev/null +++ b/public/profileAssets/icons/Development.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fdfd6ed3e91114279dd9a121a3d5d4ebbc31dee8cf018d4fc60dc4eaa75e1070 +size 278892 diff --git a/public/profileAssets/icons/Nightly.png b/public/profileAssets/icons/Nightly.png new file mode 100644 index 0000000..0986c5a --- /dev/null +++ b/public/profileAssets/icons/Nightly.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:803cb591d42573397e456fee473c06a67b4f43e9c405aac3375be7f46c64f53c +size 192979 diff --git a/public/profileAssets/icons/Setlist.png b/public/profileAssets/icons/Setlist.png new file mode 100644 index 0000000..2572a30 --- /dev/null +++ b/public/profileAssets/icons/Setlist.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0b98083cf75f8e3b6daf359c78db9b8bc9fa95dc885a0b5331c36b9ff6ed68f +size 15535 diff --git a/public/profileAssets/icons/SetlistDLC.png b/public/profileAssets/icons/SetlistDLC.png new file mode 100644 index 0000000..8483e70 --- /dev/null +++ b/public/profileAssets/icons/SetlistDLC.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0e9c37c6f8e85fa4bfbd4c9e6f3477a7764be51855351caaf9d9102bf2b376d +size 20074 diff --git a/public/profileAssets/icons/SetlistSummertide.png b/public/profileAssets/icons/SetlistSummertide.png new file mode 100644 index 0000000..90379d3 --- /dev/null +++ b/public/profileAssets/icons/SetlistSummertide.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dc072a72393756e1b07ee4d394d2af786a3890538aa4a6366055b61b6340b05b +size 150855 diff --git a/public/profileAssets/icons/SetlistWanderkid.png b/public/profileAssets/icons/SetlistWanderkid.png new file mode 100644 index 0000000..ac84a6a --- /dev/null +++ b/public/profileAssets/icons/SetlistWanderkid.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c94bac6ebe383cf8dea9e434a7c09b08ddfc742b5c09402d48b79e4f3ff6dc7 +size 120832 diff --git a/public/profileAssets/icons/Stable.png b/public/profileAssets/icons/Stable.png new file mode 100644 index 0000000..60407b3 --- /dev/null +++ b/public/profileAssets/icons/Stable.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b70bdbf02bc83b43bbd824fe1f2f905380dd0dbdeb427b6530172e65d13171e +size 243471 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 0232dc8..615f7f8 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -78,6 +78,55 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.86" @@ -92,13 +141,13 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -139,9 +188,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.72" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -178,18 +227,18 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bit-set" -version = "0.5.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" [[package]] name = "bitflags" @@ -199,9 +248,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitvec" @@ -259,7 +308,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", "syn_derive", ] @@ -286,9 +335,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", "regex-automata 0.4.7", @@ -336,9 +385,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.16.0" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" +checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" [[package]] name = "byteorder" @@ -348,9 +397,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" dependencies = [ "serde", ] @@ -421,13 +470,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.99" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" dependencies = [ "jobserver", "libc", - "once_cell", ] [[package]] @@ -488,7 +536,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -501,6 +549,46 @@ dependencies = [ "inout", ] +[[package]] +name = "clap" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + [[package]] name = "cocoa" version = "0.24.1" @@ -553,6 +641,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "combine" version = "4.6.7" @@ -729,7 +823,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -739,14 +833,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "darling" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -754,27 +848,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "darling_macro" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -800,15 +894,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] @@ -870,17 +964,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" -[[package]] -name = "displaydoc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "dtoa" version = "1.0.9" @@ -904,14 +987,14 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "embed-resource" -version = "2.4.2" +version = "2.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6985554d0688b687c5cb73898a34fbe3ad6c24c58c238a4d91d5e840670ee9d" +checksum = "4edcacde9351c33139a41e3c97eb2334351a81a2791bebb0b243df837128f602" dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.14", + "toml 0.8.19", "vswhom", "winreg 0.52.0", ] @@ -1047,7 +1130,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -1127,7 +1210,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -1479,7 +1562,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.2.6", + "indexmap 2.3.0", "slab", "tokio", "tokio-util", @@ -1498,7 +1581,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.2.6", + "indexmap 2.3.0", "slab", "tokio", "tokio-util", @@ -1611,9 +1694,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http 1.1.0", @@ -1628,7 +1711,7 @@ dependencies = [ "bytes", "futures-util", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1640,9 +1723,9 @@ checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" [[package]] name = "httparse" -version = "1.9.2" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3935c160d00ac752e09787e6e6bfc26494c2183cc922f1bc678a60d4733bc2" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -1652,9 +1735,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.29" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -1676,16 +1759,16 @@ dependencies = [ [[package]] name = "hyper" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", "futures-util", "h2 0.4.5", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "httparse", "itoa 1.0.11", "pin-project-lite", @@ -1694,6 +1777,23 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1701,7 +1801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper 0.14.29", + "hyper 0.14.30", "native-tls", "tokio", "tokio-native-tls", @@ -1715,7 +1815,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.3.1", + "hyper 1.4.1", "hyper-util", "native-tls", "tokio", @@ -1725,16 +1825,16 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" +checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" dependencies = [ "bytes", "futures-channel", "futures-util", "http 1.1.0", - "http-body 1.0.0", - "hyper 1.3.1", + "http-body 1.0.1", + "hyper 1.4.1", "pin-project-lite", "socket2", "tokio", @@ -1776,124 +1876,6 @@ dependencies = [ "png", ] -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -1902,14 +1884,12 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "icu_normalizer", - "icu_properties", - "smallvec", - "utf8_iter", + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -1953,9 +1933,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -1996,6 +1976,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itoa" version = "0.4.8" @@ -2053,9 +2039,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] @@ -2080,6 +2066,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + [[package]] name = "kuchikiki" version = "0.8.2" @@ -2095,9 +2092,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" @@ -2121,28 +2118,16 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", ] -[[package]] -name = "line-wrap" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd1bc4d24ad230d21fb898d1116b1801d7adfc449d42026475862ab48b11e70e" - [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" -[[package]] -name = "litemap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" - [[package]] name = "lock_api" version = "0.4.12" @@ -2155,9 +2140,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" dependencies = [ "value-bag", ] @@ -2179,9 +2164,9 @@ dependencies = [ [[package]] name = "lzma-rust" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5edcf5d1f4d78221ea7861fb69899afd15c42601751f92f09a06f7b051fb289" +checksum = "5baab2bbbd7d75a144d671e9ff79270e903957d92fb7386fd39034c709bd2661" dependencies = [ "byteorder", ] @@ -2232,9 +2217,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" @@ -2270,9 +2255,9 @@ checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", "simd-adler32", @@ -2280,13 +2265,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ + "hermit-abi", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2357,9 +2343,9 @@ dependencies = [ [[package]] name = "nt-time" -version = "0.6.13" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e567788c00c02d8f3d554fa7c0335b08517ae3b4de6a2a4d8afa84d969a510b" +checksum = "2de419e64947cd8830e66beb584acc3fb42ed411d103e3c794dda355d1b374b5" dependencies = [ "chrono", "time", @@ -2390,16 +2376,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "num_enum" version = "0.5.11" @@ -2471,9 +2447,9 @@ dependencies = [ [[package]] name = "object" -version = "0.35.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ "memchr", ] @@ -2509,11 +2485,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -2530,7 +2506,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -2541,9 +2517,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -2617,9 +2593,9 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.3", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2667,6 +2643,51 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "pest_meta" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "phf" version = "0.8.0" @@ -2771,7 +2792,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -2818,7 +2839,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -2841,13 +2862,12 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plist" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d34169e64b3c7a80c8621a48adaf44e0cf62c78a9b25dd9dd35f1881a17cf9" +checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ - "base64 0.21.7", - "indexmap 2.2.6", - "line-wrap", + "base64 0.22.1", + "indexmap 2.3.0", "quick-xml", "serde", "time", @@ -2874,9 +2894,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" +dependencies = [ + "zerocopy", +] [[package]] name = "precomputed-hash" @@ -2935,9 +2958,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -2964,9 +2987,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" dependencies = [ "memchr", ] @@ -3084,11 +3107,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -3169,7 +3192,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.29", + "hyper 0.14.30", "hyper-tls 0.5.0", "ipnet", "js-sys", @@ -3183,7 +3206,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -3199,9 +3222,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ "base64 0.22.1", "bytes", @@ -3210,9 +3233,10 @@ dependencies = [ "futures-util", "h2 0.4.5", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "http-body-util", - "hyper 1.3.1", + "hyper 1.4.1", + "hyper-rustls", "hyper-tls 0.6.0", "hyper-util", "ipnet", @@ -3227,7 +3251,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.1", "system-configuration", "tokio", "tokio-native-tls", @@ -3265,6 +3289,21 @@ dependencies = [ "windows 0.37.0", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rkyv" version = "0.7.44" @@ -3352,13 +3391,26 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.23.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -3384,6 +3436,17 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +[[package]] +name = "rustls-webpki" +version = "0.102.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -3454,11 +3517,11 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "security-framework" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -3467,9 +3530,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -3506,32 +3569,33 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "itoa 1.0.11", + "memchr", "ryu", "serde", ] @@ -3544,14 +3608,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -3570,15 +3634,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.2.6", + "indexmap 2.3.0", "serde", "serde_derive", "serde_json", @@ -3588,14 +3652,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -3632,9 +3696,9 @@ dependencies = [ [[package]] name = "sevenz-rust" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a24854621db6137e3f7ed7d82282485874d212c755c3427a7436fc0c62c3d23" +checksum = "26482cf1ecce4540dc782fc70019eba89ffc4d87b3717eb5ec524b5db6fdefef" dependencies = [ "aes", "bit-set", @@ -3752,6 +3816,12 @@ dependencies = [ "system-deps 5.0.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -3801,9 +3871,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -3818,9 +3888,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -3836,7 +3906,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -3846,15 +3916,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] -name = "synstructure" -version = "0.13.1" +name = "sync_wrapper" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] name = "sys-locale" @@ -3912,7 +3977,7 @@ dependencies = [ "cfg-expr 0.15.8", "heck 0.5.0", "pkg-config", - "toml 0.8.14", + "toml 0.8.19", "version-compare 0.2.0", ] @@ -3993,15 +4058,15 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.14" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "1.6.8" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77567d2b3b74de4588d544147142d02297f3eaa171a25a065252141d8597a516" +checksum = "336bc661a3f3250853fa83c6e5245449ed1c26dce5dcb28bdee7efedf6278806" dependencies = [ "anyhow", "base64 0.21.7", @@ -4058,9 +4123,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab30cba12974d0f9b09794f61e72cad6da2142d3ceb81e519321bab86ce53312" +checksum = "b0c6ec7a5c3296330c7818478948b422967ce4649094696c985f61d50076d29c" dependencies = [ "anyhow", "cargo_toml", @@ -4077,9 +4142,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "1.4.3" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a1d90db526a8cdfd54444ad3f34d8d4d58fa5c536463915942393743bd06f8" +checksum = "c1aed706708ff1200ec12de9cfbf2582b5d8ec05f6a7293911091effbd22036b" dependencies = [ "base64 0.21.7", "brotli", @@ -4103,9 +4168,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "1.4.4" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a582d75414250122e4a597b9dd7d3c910a2c77906648fc2ac9353845ff0feec" +checksum = "b88f831d2973ae4f81a706a0004e67dac87f2e4439973bbe98efbd73825d8ede" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -4118,7 +4183,7 @@ dependencies = [ [[package]] name = "tauri-plugin-log" version = "0.0.0" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#562425644ef7c11a7b301a30c07a8c8b545621a8" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#7c9dac1f42bfe883b43c44485843d14fa81d3dae" dependencies = [ "byte-unit", "fern", @@ -4132,9 +4197,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7ffddf36d450791018e63a3ddf54979b9581d9644c584a5fb5611e6b5f20b4" +checksum = "3068ed62b63dedc705558f4248c7ecbd5561f0f8050949859ea0db2326f26012" dependencies = [ "gtk", "http 0.2.12", @@ -4153,9 +4218,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "0.14.8" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1989b3b4d611f5428b3414a4abae6fa6df30c7eb8ed33250ca90a5f7e5bb3655" +checksum = "d4c3db170233096aa30330feadcd895bf9317be97e624458560a20e814db7955" dependencies = [ "cocoa 0.24.1", "gtk", @@ -4173,9 +4238,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "1.5.4" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "450b17a7102e5d46d4bdabae0d1590fd27953e704e691fc081f06c06d2253b35" +checksum = "2826db448309d382dac14d520f0c0a40839b87b57b977e59cf5f296b3ace6a93" dependencies = [ "brotli", "ctor", @@ -4185,6 +4250,7 @@ dependencies = [ "html5ever", "infer", "json-patch", + "json5", "kuchikiki", "log", "memchr", @@ -4242,22 +4308,22 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -4303,21 +4369,11 @@ dependencies = [ "time-core", ] -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -4330,18 +4386,17 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.0" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", "socket2", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4354,6 +4409,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.11" @@ -4390,21 +4456,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.14" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.14", + "toml_edit 0.22.20", ] [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -4415,7 +4481,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "serde", "serde_spanned", "toml_datetime", @@ -4428,22 +4494,22 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.14" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.13", + "winnow 0.6.18", ] [[package]] @@ -4492,7 +4558,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -4546,23 +4612,50 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-segmentation" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -4576,12 +4669,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8-width" version = "0.1.7" @@ -4589,16 +4676,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] -name = "utf8_iter" -version = "1.0.4" +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom 0.2.15", ] @@ -4635,9 +4722,9 @@ checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vswhom" @@ -4711,7 +4798,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", "wasm-bindgen-shared", ] @@ -4745,7 +4832,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4959,7 +5046,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -5017,7 +5104,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -5052,18 +5139,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -5078,7 +5165,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -5095,9 +5182,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -5125,9 +5212,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -5155,15 +5242,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -5191,9 +5278,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -5221,9 +5308,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -5239,9 +5326,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -5269,9 +5356,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -5284,9 +5371,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.13" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -5311,18 +5398,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - [[package]] name = "wry" version = "0.24.10" @@ -5407,14 +5482,17 @@ name = "yet_another_launcher" version = "0.3.2" dependencies = [ "async-trait", + "clap", "directories", "futures-util", + "lazy_static", "log", "minisign", "opener", - "reqwest 0.12.4", + "reqwest 0.12.5", "serde", "serde_json", + "serde_repr", "sevenz-rust", "tauri", "tauri-build", @@ -5424,71 +5502,31 @@ dependencies = [ ] [[package]] -name = "yoke" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.4" +name = "zerocopy" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" dependencies = [ - "zerofrom-derive", + "byteorder", + "zerocopy-derive", ] [[package]] -name = "zerofrom-derive" -version = "0.1.4" +name = "zerocopy-derive" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", - "synstructure", + "syn 2.0.72", ] [[package]] -name = "zerovec" -version = "0.10.2" +name = "zeroize" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zip" @@ -5541,9 +5579,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +version = "2.0.12+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" dependencies = [ "cc", "pkg-config", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 8eca426..7a34904 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -15,11 +15,16 @@ tauri-build = { version = "1.5.0", features = [] } [dependencies] log = "^0.4" tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } -tauri = { version = "1.5.2", features = [ +tauri = { version = "1.5.2", features = [ "shell-open", + "fs-read-file", + "fs-read-dir", + "fs-create-dir", + "fs-write-file", + "path-all", + "updater", + "config-json5", "os-all", "dialog-all", - "shell-open", - "updater", "window-close", "window-hide", "window-maximize", @@ -31,6 +36,7 @@ tauri = { version = "1.5.2", features = [ ] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +serde_repr = "0.1.19" reqwest = { version = "0.12.4", features = ["stream"] } futures-util = "0.3.28" zip-extract = "0.1.3" @@ -40,6 +46,8 @@ window-shadows = "0.2.2" minisign = "0.7.6" opener = { version = "0.7.1", features = ["reveal"] } async-trait = "0.1.74" +clap = { version = "4.5.13", features = ["derive"] } +lazy_static = "1.5.0" [features] # this feature is used for production builds or when `devPath` points to the filesystem diff --git a/src-tauri/src/app_profile/mod.rs b/src-tauri/src/app_profile/mod.rs deleted file mode 100644 index ce0ff97..0000000 --- a/src-tauri/src/app_profile/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -use tauri::AppHandle; -use async_trait::async_trait; - -pub mod yarg; -pub mod official_setlist; - -pub const YARG_PUB_KEY: &str = "untrusted comment: minisign public key C26EBBBEC4C1DB81 -RWSB28HEvrtuwvPn3pweVBodgVi/d+UH22xDsL3K8VBgeRqaIrDdTvps -"; - -#[derive(Clone, serde::Serialize)] -pub struct ProgressPayload { - pub state: String, - pub total: u64, - pub current: u64, -} - -#[async_trait] -pub trait AppProfile { - async fn download_and_install( - &self, - app: &AppHandle, - zip_urls: Vec, - sig_urls: Vec - ) -> Result<(), String>; - - fn install( - &self - ) -> Result<(), String>; - - fn uninstall( - &self - ) -> Result<(), String>; - - fn exists( - &self - ) -> bool; - - fn launch( - &self - ) -> Result<(), String>; - - fn reveal_folder( - &self - ) -> Result<(), String>; -} \ No newline at end of file diff --git a/src-tauri/src/app_profile/official_setlist.rs b/src-tauri/src/app_profile/official_setlist.rs deleted file mode 100644 index 487b821..0000000 --- a/src-tauri/src/app_profile/official_setlist.rs +++ /dev/null @@ -1,106 +0,0 @@ -use async_trait::async_trait; -use tauri::Manager; -use std::{path::{PathBuf, Path}, fs::{File, remove_file, read_to_string}, io::Write}; - -use crate::utils::*; - -use super::*; - -pub struct OfficialSetlistProfile { - pub root_folder: PathBuf, - pub temp_folder: PathBuf, - pub version: String, - pub profile: String -} - -#[async_trait] -impl AppProfile for OfficialSetlistProfile { - async fn download_and_install( - &self, - app: &tauri::AppHandle, - zip_urls: Vec, - _sig_urls: Vec - ) -> Result<(), String> { - let folder = self.root_folder.join(&self.profile); - - // Delete the old installation - clear_folder(&folder)?; - - // Download the zip(s) - for (index, zip_url) in zip_urls.iter().enumerate() { - // Download the current zip - let zip_path = &self.temp_folder.join(format!("setlist_{}.7z", index)); - download(Some(app), &zip_url, &zip_path).await?; - - // Emit the install - let _ = app.emit_all( - "progress_info", - ProgressPayload { - state: "installing".to_string(), - current: 0, - total: 0, - }, - ); - - // Extract the zip to the game directory - extract_setlist_part(&zip_path, &folder)?; - - // Delete zip - let _ = remove_file(zip_path); - } - - self.install()?; - - Ok(()) - } - - fn install( - &self - ) -> Result<(), String> { - let folder = self.root_folder.join(&self.profile); - - // Create a version.txt - let mut file = File::create(folder.join("version.txt")) - .map_err(|e| format!("Failed to create version file in `{:?}`.\n{:?}", folder, e))?; - file.write_all(&self.version.as_bytes()) - .map_err(|e| format!("Failed to write version file in `{:?}`.\n{:?}", folder, e))?; - - Ok(()) - } - - fn uninstall( - &self - ) -> Result<(), String> { - let folder = self.root_folder.join(&self.profile); - std::fs::remove_dir_all(folder) - .map_err(|e| format!("Failed to remove directory.\n{:?}", e)) - } - - fn exists( - &self - ) -> bool { - let path = self.root_folder.join(&self.profile); - if !Path::new(&path).exists() { - return false; - } - - let contents = match read_to_string(&path.join("version.txt")) { - Ok(contents) => contents, - _ => return false, - }; - - contents == self.version - } - - fn launch( - &self - ) -> Result<(), String> { - Err("Cannot launch the setlist!".to_string()) - } - - fn reveal_folder( - &self - ) -> Result<(), String> { - Err("Cannot reveal the setlist folder!".to_string()) - } -} \ No newline at end of file diff --git a/src-tauri/src/app_profile/yarg.rs b/src-tauri/src/app_profile/yarg.rs deleted file mode 100644 index 8386a35..0000000 --- a/src-tauri/src/app_profile/yarg.rs +++ /dev/null @@ -1,170 +0,0 @@ -use async_trait::async_trait; -use minisign::{PublicKeyBox, SignatureBox}; -use tauri::Manager; -use std::{path::{PathBuf, Path}, fs::{File, remove_file}, process::Command}; - -use crate::utils::*; - -use super::*; - -pub struct YARGAppProfile { - pub root_folder: PathBuf, - pub temp_folder: PathBuf, - pub version: String, - pub profile: String -} - -impl YARGAppProfile { - fn get_folder( - &self - ) -> PathBuf { - self.root_folder.join(&self.profile).join(&self.version) - } - - fn get_exec( - &self - ) -> Result { - let mut path = self.get_folder(); - - // Each OS has a different executable - path = match std::env::consts::OS.to_string().as_str() { - "windows" => path.join("YARG.exe"), - "linux" => { - // Stable uses "YARG.x86_64", and nightly uses "YARG". Look for both - let mut p = path.join("YARG.x86_64"); - if !p.exists() { - p = path.join("YARG"); - } - p - } - "macos" => path - .join("YARG.app") - .join("Contents") - .join("MacOS") - .join("YARG"), - _ => Err("Unknown platform for launch!")?, - }; - - Ok(path) - } -} - -#[async_trait] -impl AppProfile for YARGAppProfile { - async fn download_and_install( - &self, - app: &tauri::AppHandle, - zip_urls: Vec, - sig_urls: Vec - ) -> Result<(), String> { - let mut folder = self.root_folder.join(&self.profile); - - let zip_url = zip_urls.first().ok_or("Did not get any zip URLs.")?; - let sig_url = sig_urls.first(); - - // Delete the old installation - clear_folder(&folder)?; - - // Move into the version's folder - folder = folder.join(&self.version); - - // Download the zip - let zip_path = &self.temp_folder.join("update.zip"); - download(Some(app), &zip_url, &zip_path).await?; - - // Verify (if signature is provided) - if let Some(sig_url) = sig_url { - // Emit the verification - let _ = app.emit_all( - "progress_info", - ProgressPayload { - state: "verifying".to_string(), - current: 0, - total: 0, - }, - ); - - // Download sig file (don't pass app so it doesn't emit an update) - let sig_path = &self.temp_folder.join("update.sig"); - download(None, &sig_url, &sig_path).await?; - - // Convert public key - let pk_box = PublicKeyBox::from_string(YARG_PUB_KEY).unwrap(); - let pk = pk_box.into_public_key().unwrap(); - - // Create the signature box - let sig_box = SignatureBox::from_file(sig_path) - .map_err(|e| format!("Invalid signature file! Try reinstalling. If it keeps failing, let us know ASAP!\n{:?}", e))?; - - // Verify - let zip_file = File::open(zip_path) - .map_err(|e| format!("Failed to open zip while verifying.\n{:?}", e))?; - minisign::verify(&pk, &sig_box, zip_file, true, false, false) - .map_err(|_| "Failed to verify downloaded zip file! Try reinstalling. If it keeps failing, let us know ASAP!")?; - } - - // Emit the install (count extracting as installing) - let _ = app.emit_all( - "progress_info", - ProgressPayload { - state: "installing".to_string(), - current: 0, - total: 0, - }, - ); - - // Extract the zip to the game directory - extract(&zip_path, &folder)?; - - // Delete zip - let _ = remove_file(&zip_path); - - // Do the rest of the installation - self.install()?; - - Ok(()) - } - - fn install( - &self - ) -> Result<(), String> { - Ok(()) - } - - fn uninstall( - &self - ) -> Result<(), String> { - let folder = self.root_folder.join(&self.profile); - std::fs::remove_dir_all(folder) - .map_err(|e| format!("Failed to remove directory.\n{:?}", e)) - } - - fn exists( - &self - ) -> bool { - Path::new(&self.get_folder()).exists() - } - - fn launch( - &self - ) -> Result<(), String> { - let path = self.get_exec()?; - Command::new(&path) - .spawn() - .map_err(|e| format!("Failed to start YARG. Is it installed?\n{:?}", e))?; - Ok(()) - } - - fn reveal_folder( - &self - ) -> Result<(), String> { - if !self.exists() { - return Err("Cannot reveal something that doesn't exist!".to_string()); - } - - opener::reveal(self.get_folder()) - .map_err(|e| format!("Failed to reveal folder. Is it installed?\n{:?}", e))?; - - Ok(()) - } -} \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index ba5dcb7..eeaa1c3 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -2,334 +2,272 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] mod utils; -mod app_profile; +mod types; + +use std::{fs, path::PathBuf, process::Command, sync::{LazyLock, Mutex}}; -use app_profile::AppProfile; -use app_profile::official_setlist::OfficialSetlistProfile; -use app_profile::yarg::YARGAppProfile; use directories::BaseDirs; -use std::fs::{self, remove_file, File}; -use std::path::PathBuf; -use std::sync::RwLock; use tauri::{AppHandle, Manager}; -use utils::clear_folder; use window_shadows::set_shadow; +use utils::*; +use types::*; +use clap::Parser; -#[derive(Default, serde::Serialize, serde::Deserialize)] -pub struct Settings { - pub download_location: String, - pub initialized: bool, -} - -pub struct InnerState { - pub yarc_folder: PathBuf, - pub launcher_folder: PathBuf, - pub temp_folder: PathBuf, - pub yarg_folder: PathBuf, - pub setlist_folder: PathBuf, +static COMMAND_LINE_ARG_LAUNCH: LazyLock>> = LazyLock::new(|| Mutex::new(None)); - pub settings: Settings, -} +#[tauri::command(async)] +fn get_important_dirs() -> Result { + // Get the important directories -impl InnerState { - pub fn init(&mut self) -> Result<(), String> { - let dirs = BaseDirs::new().ok_or("Failed to get directories.")?; + let dirs = BaseDirs::new().ok_or("Failed to get base directories.")?; - self.yarc_folder = PathBuf::from(dirs.data_local_dir()); - self.yarc_folder.push("YARC"); + let mut yarc_folder = PathBuf::from(dirs.data_local_dir()); + yarc_folder.push("YARC"); - self.launcher_folder = PathBuf::from(&self.yarc_folder); - self.launcher_folder.push("Launcher"); + let mut launcher_folder = PathBuf::from(&yarc_folder); + launcher_folder.push("Launcher"); - self.temp_folder = PathBuf::from(&self.launcher_folder); - self.temp_folder.push("Temp"); + let mut temp_folder = PathBuf::from(&launcher_folder); + temp_folder.push("Temp"); - // Create launcher directory (for the settings) - std::fs::create_dir_all(&self.launcher_folder) - .map_err(|e| format!("Failed to create launcher directory.\n{:?}", e))?; + // Create the directories if they don't exist - // Load settings - let settings_path = self.launcher_folder.join("settings.json"); - if settings_path.exists() { - // Get file - let settings_file = File::open(settings_path) - .map_err(|e| format!("Failed to open settings.json file.\n{:?}", e))?; + std::fs::create_dir_all(&yarc_folder) + .map_err(|e| format!("Failed to create YARC directory.\n{:?}", e))?; + std::fs::create_dir_all(&launcher_folder) + .map_err(|e| format!("Failed to create launcher directory.\n{:?}", e))?; + std::fs::create_dir_all(&temp_folder) + .map_err(|e| format!("Failed to create temp directory.\n{:?}", e))?; - // Convert from json and save to settings - let settings: Result = serde_json::from_reader(settings_file); - if let Ok(settings) = settings { - self.settings = settings; - } else { - self.create_new_settings_file()?; - } - } else { - self.create_new_settings_file()?; - } + return Ok(ImportantDirs { + yarc_folder: path_to_string(yarc_folder)?, + launcher_folder: path_to_string(launcher_folder)?, + temp_folder: path_to_string(temp_folder)?, + }); +} - // Set the rest of the folder locations based on settings - self.set_download_locations()?; +#[tauri::command(async)] +fn get_custom_dirs(download_location: String) -> Result { + // Get the custom directories - // Delete everything temp (just in case) - clear_folder(&self.temp_folder)?; + let mut yarg_folder = PathBuf::from(&download_location); + yarg_folder.push("YARG Installs"); - Ok(()) - } + let mut setlist_folder = PathBuf::from(&download_location); + setlist_folder.push("Setlists"); - fn set_download_locations(&mut self) -> Result<(), String> { - self.yarg_folder = PathBuf::from(&self.settings.download_location); - self.yarg_folder.push("YARG Installs"); + // Create the directories if they don't exist - self.setlist_folder = PathBuf::from(&self.settings.download_location); - self.setlist_folder.push("Setlists"); + std::fs::create_dir_all(&yarg_folder) + .map_err(|e| format!("Failed to create YARG directory.\n{:?}", e))?; + std::fs::create_dir_all(&setlist_folder) + .map_err(|e| format!("Failed to create setlist directory.\n{:?}", e))?; - // Create the directories if they don't exist - std::fs::create_dir_all(&self.yarg_folder) - .map_err(|e| format!("Failed to create YARG directory.\n{:?}", e))?; - std::fs::create_dir_all(&self.setlist_folder) - .map_err(|e| format!("Failed to create setlist directory.\n{:?}", e))?; + return Ok(CustomDirs { + yarg_folder: path_to_string(yarg_folder)?, + setlist_folder: path_to_string(setlist_folder)? + }); +} - Ok(()) +#[tauri::command] +fn is_dir_empty(path: String) -> bool { + match fs::read_dir(path) { + Ok(mut entries) => entries.next().is_none(), + Err(_) => false, } +} - fn create_new_settings_file(&mut self) -> Result<(), String> { - // Create new settings - self.settings = Default::default(); - self.settings.download_location = self - .yarc_folder - .clone() - .into_os_string() - .into_string() - .unwrap(); - - // Then save - self.save_settings_file()?; +#[tauri::command(async)] +fn profile_folder_state(path: String, wanted_tag: String) -> ProfileFolderState { + let mut tag_file = PathBuf::from(&path); + tag_file.push("tag.txt"); + + let tag_file_exists = tag_file.try_exists(); + if let Ok(exists) = tag_file_exists { + if !exists { + return ProfileFolderState::FirstDownload; + } - Ok(()) + let tag = fs::read_to_string(tag_file); + if let Ok(tag_string) = tag { + if tag_string.trim() == wanted_tag { + return ProfileFolderState::UpToDate; + } else { + return ProfileFolderState::UpdateRequired; + } + } else { + println!("Failed to read tag file at `{}`", path); + return ProfileFolderState::Error; + } + } else { + println!("Failed to find if the profile exists at `{}`", path); + return ProfileFolderState::Error; } +} - pub fn save_settings_file(&mut self) -> Result<(), String> { - // Delete the old settings (if it exists) - let settings_path = self.launcher_folder.join("settings.json"); - let _ = remove_file(&settings_path); - - // Create settings file - let settings_file = File::create(settings_path) - .map_err(|e| format!("Failed to create settings file.\n{:?}", e))?; +// when i was getting disk space in rust i used "free_space" from the fs2 crate because it takes a path and works out what drive that would be - // Write to file - serde_json::to_writer(settings_file, &self.settings) - .map_err(|e| format!("Failed to write to settings file.\n{:?}", e))?; +#[tauri::command(async)] +async fn download_and_install_profile( + handle: AppHandle, + profile_path: String, + uuid: String, + tag: String, + temp_path: String, + content: Vec +) -> Result<(), String> { + let mut temp_file = PathBuf::from(&temp_path); + temp_file.push(format!("{}.temp", uuid)); + let _ = fs::remove_file(&temp_file); + + let mut install_path = PathBuf::from(&profile_path); + install_path.push("installation"); + clear_folder(&install_path)?; + + // Download and install all content + let current_os = std::env::consts::OS.to_string(); + for c in content { + // Skip release content that is not for this OS + if !c.platforms.iter().any(|i| i.to_owned() == current_os) { + continue; + } - Ok(()) + let file_count = c.files.len() as u64; + for (index, file) in c.files.iter().enumerate() { + // Download + + download( + &handle, + &file.url, + &temp_file, + file_count, + index as u64 + ).await?; + + // Extract/install + + let _ = handle.emit_all( + "progress_info", + ProgressPayload { + state: "installing".to_string(), + current: (index + 1) as u64, + total: file_count, + }, + ); + + if file.file_type == "zip" { + extract(&temp_file, &install_path)?; + } else if file.file_type == "encrypted" { + extract_encrypted(&temp_file, &install_path)?; + } else { + return Err("Unhandled release file type.".to_string()); + } + } } -} - -pub struct State(pub RwLock); -#[tauri::command(async)] -fn init(state: tauri::State) -> Result<(), String> { - let mut state_guard = state.0.write().unwrap(); - state_guard.init()?; + let mut tag_file = PathBuf::from(&profile_path); + tag_file.push("tag.txt"); + fs::write(&tag_file, tag).map_err(|e| format!("Failed to write tag file.\n{:?}", e))?; Ok(()) } #[tauri::command(async)] -fn is_initialized(state: tauri::State) -> Result { - let state_guard = state.0.read().unwrap(); - Ok(state_guard.settings.initialized) -} +fn uninstall_profile(profile_path: String) -> Result<(), String> { + let mut install_path = PathBuf::from(&profile_path); + install_path.push("installation"); + clear_folder(&install_path)?; -fn create_app_profile( - app_name: String, - state: &tauri::State, - version: String, - profile: String -) -> Result, String> { - let state_guard = state.0.read().unwrap(); - - Ok(match app_name.as_str() { - "yarg" => Box::new(YARGAppProfile { - root_folder: state_guard.yarg_folder.clone(), - temp_folder: state_guard.temp_folder.clone(), - version, - profile - }), - "official_setlist" => Box::new(OfficialSetlistProfile { - root_folder: state_guard.setlist_folder.clone(), - temp_folder: state_guard.temp_folder.clone(), - version, - profile - }), - _ => Err(format!("Unknown app profile `{}`.", app_name))? - }) + let mut tag_file = PathBuf::from(&profile_path); + tag_file.push("tag.txt"); + fs::remove_file(tag_file).map_err(|e| format!("Failed to remove tag file.\n{:?}", e))?; + + // Remove the directories if they are empty + let _ = fs::remove_dir(&install_path); + let _ = fs::remove_dir(&profile_path); + + Ok(()) } #[tauri::command] -async fn download_and_install( - state: tauri::State<'_, State>, - app_handle: AppHandle, - app_name: String, - version: String, - profile: String, - zip_urls: Vec, - sig_urls: Vec -) -> Result<(), String> { - let app_profile = create_app_profile( - app_name, - &state, - version, - profile - )?; - - let result = app_profile.download_and_install( - &app_handle, - zip_urls, - sig_urls - ); +fn launch_profile(profile_path: String, exec_path: String, arguments: Vec) -> Result<(), String> { + let mut path = PathBuf::from(&profile_path); + path.push("installation"); + path.push(exec_path); - result.await?; + Command::new(path) + .args(arguments) + .spawn() + .map_err(|e| format!("Failed to launch profile? Is the executable installed?\n{:?}", e))?; Ok(()) } -#[tauri::command(async)] -fn uninstall( - state: tauri::State, - app_name: String, - version: String, - profile: String -) -> Result<(), String> { - let app_profile = create_app_profile( - app_name, - &state, - version, - profile - )?; +#[tauri::command] +fn open_folder_profile(profile_path: String) -> Result<(), String> { + let mut path = PathBuf::from(&profile_path); + path.push("installation"); - app_profile.uninstall()?; + opener::reveal(path) + .map_err(|e| format!("Failed to reveal folder. Is it installed?\n{:?}", e))?; Ok(()) } #[tauri::command(async)] -fn exists( - state: tauri::State, - app_name: String, - version: String, - profile: String -) -> Result { - let app_profile = create_app_profile( - app_name, - &state, - version, - profile - )?; - - Ok(app_profile.exists()) +fn get_launch_argument() -> Option { + let launch_arg = COMMAND_LINE_ARG_LAUNCH.lock().unwrap(); + return launch_arg.to_owned(); } -#[tauri::command(async)] -fn launch( - state: tauri::State<'_, State>, - app_name: String, - version: String, - profile: String -) -> Result<(), String> { - let app_profile = create_app_profile( - app_name, - &state, - version, - profile - )?; - - app_profile.launch() -} + #[tauri::command(async)] +fn clean_up_old_install(yarg_folder: String, setlist_folder: String) -> Result<(), String> { + let mut stable_old = PathBuf::from(&yarg_folder); + stable_old.push("stable"); + clear_folder(&stable_old)?; + let _ = fs::remove_dir(&stable_old); -#[tauri::command(async)] -fn reveal_folder( - state: tauri::State<'_, State>, - app_name: String, - version: String, - profile: String -) -> Result<(), String> { - let app_profile = create_app_profile( - app_name, - &state, - version, - profile - )?; - - app_profile.reveal_folder() -} + let mut nightly_old = PathBuf::from(&yarg_folder); + nightly_old.push("nightly"); + clear_folder(&nightly_old)?; + let _ = fs::remove_dir(&nightly_old); -#[tauri::command] -fn get_os() -> String { - std::env::consts::OS.to_string() -} + let mut setlist_old = PathBuf::from(&setlist_folder); + setlist_old.push("official"); + clear_folder(&setlist_old)?; + let _ = fs::remove_dir(&setlist_old); -#[tauri::command] -fn is_dir_empty(path: String) -> bool { - match fs::read_dir(path) { - Ok(mut entries) => entries.next().is_none(), - Err(_) => false, - } + Ok(()) } -#[tauri::command(async)] -async fn set_download_location( - state: tauri::State<'_, State>, - path: Option, -) -> Result<(), String> { - let mut state_guard = state.0.write().unwrap(); +fn main() { + let args = CommandLineArgs::parse(); - // If this is None, just use the default - if let Some(path) = path { - state_guard.settings.download_location = path.clone(); + { + // Stores the launch option in a static so the frontend can request it later. + let mut launch_option = COMMAND_LINE_ARG_LAUNCH.lock().unwrap(); + *launch_option = args.launch; } - state_guard.settings.initialized = true; - - state_guard.set_download_locations()?; - state_guard.save_settings_file()?; - - Ok(()) -} - -#[tauri::command] -fn get_download_location(state: tauri::State<'_, State>) -> Result { - let state_guard = state.0.read().unwrap(); - Ok(state_guard.settings.download_location.clone()) -} - -fn main() { tauri::Builder::default() .plugin(tauri_plugin_log::Builder::default().build()) - .manage(State(RwLock::new(InnerState { - yarc_folder: PathBuf::new(), - launcher_folder: PathBuf::new(), - temp_folder: PathBuf::new(), - yarg_folder: PathBuf::new(), - setlist_folder: PathBuf::new(), - settings: Default::default(), - }))) .invoke_handler(tauri::generate_handler![ - init, - is_initialized, + get_important_dirs, + get_custom_dirs, + is_dir_empty, - download_and_install, - uninstall, - exists, - launch, - reveal_folder, + profile_folder_state, + download_and_install_profile, + uninstall_profile, + launch_profile, + open_folder_profile, - get_os, - is_dir_empty, + get_launch_argument, - set_download_location, - get_download_location + clean_up_old_install ]) .setup(|app| { + // Show the window's shadow let window = app.get_window("main").unwrap(); let _ = set_shadow(&window, true); Ok(()) diff --git a/src-tauri/src/types.rs b/src-tauri/src/types.rs new file mode 100644 index 0000000..fc6b24c --- /dev/null +++ b/src-tauri/src/types.rs @@ -0,0 +1,57 @@ +use clap::{command, Parser}; +use serde_repr::{Deserialize_repr, Serialize_repr}; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +pub struct CommandLineArgs { + /// UUID of the profile to launch + #[arg(short, long)] + pub launch: Option, +} + +#[derive(Clone, serde::Serialize)] +pub struct ProgressPayload { + pub state: String, + pub total: u64, + pub current: u64, +} + +#[derive(Default, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ImportantDirs { + pub yarc_folder: String, + pub launcher_folder: String, + pub temp_folder: String, +} + +#[derive(Default, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CustomDirs { + pub yarg_folder: String, + pub setlist_folder: String, +} + +// WARNING: This type is also defined in TypeScript. Make sure to change it in both places! +#[derive(Default, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ReleaseContent { + pub platforms: Vec, + pub files: Vec, +} + +#[derive(Default, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ReleaseContentFile { + pub url: String, + pub sig_url: Option, + pub file_type: String, +} + +#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug)] +#[repr(u8)] +pub enum ProfileFolderState { + Error = 0, + UpToDate = 1, + UpdateRequired = 2, + FirstDownload = 3, +} diff --git a/src-tauri/src/utils.rs b/src-tauri/src/utils.rs index c20404c..d564e6d 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -1,16 +1,40 @@ +use crate::ProgressPayload; + use futures_util::StreamExt; -use reqwest; +use lazy_static::lazy_static; +use reqwest::{self, Client}; +use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT}; use sevenz_rust::Password; -use std::path::Path; +use std::io::{BufWriter, Write}; +use std::path::{Path, PathBuf}; use std::time::{Duration, Instant}; -use std::{fs::File, io::Write}; +use std::fs::File; use tauri::{AppHandle, Manager}; -use crate::app_profile::ProgressPayload; +lazy_static! { + pub static ref REQWEST_CLIENT: reqwest::Client = { + let mut headers = HeaderMap::new(); + + // Add user-agent + headers.insert(USER_AGENT, HeaderValue::from_str("YARC-Launcher (contact@yarg.in)").unwrap()); + + Client::builder() + .tcp_keepalive(Some(Duration::from_secs(10))) + .default_headers(headers) + .build() + .expect("Failed to create Reqwest client.") + }; +} const LETTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; const EMIT_BUFFER_RATE: f64 = 1.0 / 15.0; +pub fn path_to_string(p: PathBuf) -> Result { + Ok(p.into_os_string() + .into_string() + .map_err(|e| format!("Failed to convert path to string!\n{:?}", e))?) +} + pub fn clear_folder(path: &Path) -> Result<(), String> { std::fs::remove_dir_all(path).ok(); std::fs::create_dir_all(path).map_err(|e| { @@ -25,24 +49,28 @@ pub fn clear_folder(path: &Path) -> Result<(), String> { } pub async fn download( - app: Option<&AppHandle>, + app: &AppHandle, url: &str, output_path: &Path, + file_count: u64, + file_index: u64 ) -> Result<(), String> { // Send the initial request - let download = reqwest::get(url) + let download = REQWEST_CLIENT + .get(url) + .send() .await .map_err(|e| format!("Failed to initialize download from `{}`.\n{:?}", &url, e))?; let total_size = download.content_length().unwrap(); // Create the file to download into - let mut file = File::create(output_path).map_err(|e| { + let mut file = BufWriter::new(File::create(output_path).map_err(|e| { format!( "Failed to create file `{}`.\n{:?}", &output_path.display(), e ) - })?; + })?); let mut current_downloaded: u64 = 0; let mut stream = download.bytes_stream(); @@ -50,7 +78,8 @@ pub async fn download( // Download into the file while let Some(item) = stream.next().await { - let chunk = item.map_err(|e| format!("Error while downloading file.\n{:?}", e))?; + let chunk = item + .map_err(|e| format!("Error while downloading file.\n{:?}", e))?; file.write_all(&chunk) .map_err(|e| format!("Error while writing to file.\n{:?}", e))?; @@ -60,21 +89,18 @@ pub async fn download( current_downloaded = total_size; } - // Emit the download progress - if let Some(app) = app { - // Emitting too often could cause crashes, so buffer it to the buffer rate - if emit_timer.elapsed() >= Duration::from_secs_f64(EMIT_BUFFER_RATE) { - let _ = app.emit_all( - "progress_info", - ProgressPayload { - state: "downloading".to_string(), - current: current_downloaded, - total: total_size, - }, - ); - - emit_timer = Instant::now(); - } + // Emitting too often could cause crashes, so buffer it to the buffer rate + if emit_timer.elapsed() >= Duration::from_secs_f64(EMIT_BUFFER_RATE) { + let _ = app.emit_all( + "progress_info", + ProgressPayload { + state: "downloading".to_string(), + current: current_downloaded + (total_size * file_index), + total: total_size * file_count, + }, + ); + + emit_timer = Instant::now(); } } @@ -83,8 +109,6 @@ pub async fn download( } pub fn extract(from: &Path, to: &Path) -> Result<(), String> { - clear_folder(to)?; - let file = File::open(from).map_err(|e| format!("Error while opening file.\n{:?}", e))?; zip_extract::extract(file, to, false) .map_err(|e| format!("Error while extracting zip.\n{:?}", e))?; @@ -92,7 +116,7 @@ pub fn extract(from: &Path, to: &Path) -> Result<(), String> { Ok(()) } -pub fn extract_setlist_part(zip: &Path, output_path: &Path) -> Result<(), String> { +pub fn extract_encrypted(from: &Path, to: &Path) -> Result<(), String> { // Idiot prevention let mut chars = Vec::new(); for i in 0i32..64 { @@ -108,11 +132,11 @@ pub fn extract_setlist_part(zip: &Path, output_path: &Path) -> Result<(), String } let p: &[u16] = &chars; - sevenz_rust::decompress_file_with_password(zip, output_path, Password::from(p)).map_err( + sevenz_rust::decompress_file_with_password(from, to, Password::from(p)).map_err( |e| { format!( "Failed to extract setlist part `{}`.\n{:?}", - zip.display(), + from.display(), e ) }, diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json5 similarity index 73% rename from src-tauri/tauri.conf.json rename to src-tauri/tauri.conf.json5 index 30f431b..283b167 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json5 @@ -12,13 +12,8 @@ }, "tauri": { "allowlist": { - "all": false, - "shell": { - "all": false, - "open": true - }, "window": { - "all": false, + // Used for the essential functions of the title bar "close": true, "hide": true, "show": true, @@ -29,15 +24,28 @@ "startDragging": true }, "dialog": { - "all": true, - "ask": true, - "confirm": true, - "message": true, - "open": true, - "save": true + // Used primarily for the file picker dialog + "all": true }, "os": { + // Used to determine what OS the user is on + "all": true + }, + "shell": { + // Used to open URLs using the default program + "open": true + }, + "path": { + // Required for tauri-settings "all": true + }, + "fs": { + // Required for tauri-settings + "createDir": true, + "readDir": true, + "readFile": true, + "writeFile": true, + "scope": [ "$APPCONFIG", "$APPCONFIG/*" ] } }, "bundle": { @@ -66,9 +74,9 @@ "fullscreen": false, "resizable": true, "title": "YARC Launcher", - "width": 1250, - "height": 700, - "minWidth": 1000, + "width": 1300, + "height": 800, + "minWidth": 1200, "minHeight": 575, "decorations": false } diff --git a/src/assets/Banner/Development.png b/src/assets/Banner/Development.png deleted file mode 100644 index eab8e27..0000000 Binary files a/src/assets/Banner/Development.png and /dev/null differ diff --git a/src/assets/Banner/Nightly.png b/src/assets/Banner/Nightly.png deleted file mode 100644 index d4b92e8..0000000 Binary files a/src/assets/Banner/Nightly.png and /dev/null differ diff --git a/src/assets/Banner/OfficialBanner.png b/src/assets/Banner/OfficialBanner.png deleted file mode 100644 index ee01741..0000000 Binary files a/src/assets/Banner/OfficialBanner.png and /dev/null differ diff --git a/src/assets/Banner/OfficialLogo.png b/src/assets/Banner/OfficialLogo.png deleted file mode 100644 index dcfc52d..0000000 Binary files a/src/assets/Banner/OfficialLogo.png and /dev/null differ diff --git a/src/assets/Banner/Stable.png b/src/assets/Banner/Stable.png deleted file mode 100644 index 1e2280e..0000000 Binary files a/src/assets/Banner/Stable.png and /dev/null differ diff --git a/src/assets/DevYARGIcon.png b/src/assets/DevYARGIcon.png deleted file mode 100644 index cfa81ef..0000000 Binary files a/src/assets/DevYARGIcon.png and /dev/null differ diff --git a/src/assets/Icons/Marketplace.svg b/src/assets/Icons/Marketplace.svg new file mode 100644 index 0000000..432c36d --- /dev/null +++ b/src/assets/Icons/Marketplace.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/Icons/Minimize.svg b/src/assets/Icons/Minimize.svg index 4fa542b..0ac2f25 100644 --- a/src/assets/Icons/Minimize.svg +++ b/src/assets/Icons/Minimize.svg @@ -1,3 +1,3 @@ - - + + diff --git a/src/assets/Icons/More.svg b/src/assets/Icons/More.svg new file mode 100644 index 0000000..234a121 --- /dev/null +++ b/src/assets/Icons/More.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/Icons/SidebarInQueue.svg b/src/assets/Icons/SidebarInQueue.svg new file mode 100644 index 0000000..1c43ebc --- /dev/null +++ b/src/assets/Icons/SidebarInQueue.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/Icons/SidebarUpdate.svg b/src/assets/Icons/SidebarUpdate.svg new file mode 100644 index 0000000..11e043a --- /dev/null +++ b/src/assets/Icons/SidebarUpdate.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/Icons/Verified.svg b/src/assets/Icons/Verified.svg new file mode 100644 index 0000000..668d1e4 --- /dev/null +++ b/src/assets/Icons/Verified.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/Icons/index.ts b/src/assets/Icons/index.ts index 3264f56..633994b 100644 --- a/src/assets/Icons/index.ts +++ b/src/assets/Icons/index.ts @@ -1,7 +1,7 @@ /** * Note for adding new icons: * For auto-converting SVG to React Components, we're currently using vite-plugin-svgr. - * When referencing the path for the svg, insert "?react" at the end of the path to auto-convert into a React Component. + * When referencing the path for the svg, insert "?react" at the end of the path to auto-convert into a React Component. * Also if possible, replace the static colors inside the SVG file with "currentColor" for dynamic color. */ @@ -32,6 +32,11 @@ import UnknownUserIcon from "./UnknownUser.svg?react"; import BackIcon from "./Back.svg?react"; import WarningIcon from "./Warning.svg?react"; import DropdownIcon from "./Dropdown.svg?react"; +import VerifiedIcon from "./Verified.svg?react"; +import MarketplaceIcon from "./Marketplace.svg?react"; +import MoreIcon from "./More.svg?react"; +import SidebarInQueueIcon from "./SidebarInQueue.svg?react"; +import SidebarUpdateIcon from "./SidebarUpdate.svg?react"; export { AddIcon, @@ -60,5 +65,10 @@ export { UnknownUserIcon, BackIcon, WarningIcon, - DropdownIcon -}; \ No newline at end of file + DropdownIcon, + VerifiedIcon, + MarketplaceIcon, + MoreIcon, + SidebarInQueueIcon, + SidebarUpdateIcon, +}; diff --git a/src/assets/NightlyYARGIcon.png b/src/assets/NightlyYARGIcon.png deleted file mode 100644 index 5c138ec..0000000 Binary files a/src/assets/NightlyYARGIcon.png and /dev/null differ diff --git a/src/assets/OnboardingBackground.png b/src/assets/OnboardingBackground.png new file mode 100644 index 0000000..39a44b0 Binary files /dev/null and b/src/assets/OnboardingBackground.png differ diff --git a/src/assets/SourceIcons/Official.png b/src/assets/SourceIcons/Official.png deleted file mode 100644 index d1ee897..0000000 Binary files a/src/assets/SourceIcons/Official.png and /dev/null differ diff --git a/src/assets/StableYARGIcon.png b/src/assets/StableYARGIcon.png deleted file mode 100644 index 59166e0..0000000 Binary files a/src/assets/StableYARGIcon.png and /dev/null differ diff --git a/src/assets/YARC.svg b/src/assets/YARC.svg new file mode 100644 index 0000000..80fc071 --- /dev/null +++ b/src/assets/YARC.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/components/Box/Box.module.css b/src/components/Box/Box.module.css new file mode 100644 index 0000000..703ba52 --- /dev/null +++ b/src/components/Box/Box.module.css @@ -0,0 +1,28 @@ +.box { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 6px; + align-self: stretch; + + color: #666975; + font-size: 14px; + font-weight: 400; + + padding: 18px; + border-radius: 12px; + border: 1px solid #E3E4E7; +} + +.box > header { + display: flex; + align-items: center; + gap: 8px; + align-self: stretch; + + color: #41475F; + font-size: 14px; + font-weight: 700; + text-transform: uppercase; +} diff --git a/src/components/Box/index.tsx b/src/components/Box/index.tsx new file mode 100644 index 0000000..5114fea --- /dev/null +++ b/src/components/Box/index.tsx @@ -0,0 +1,19 @@ +import styles from "./Box.module.css"; + +type Props = React.PropsWithChildren<{ + className?: string, + style?: React.CSSProperties +}>; + +const Box: React.FC = ({ children, className, style }: Props) => { + const classes = [styles.box]; + if (className !== undefined) { + classes.push(className); + } + + return
+ {children} +
; +}; + +export default Box; diff --git a/src/components/Button/Button.module.css b/src/components/Button/Button.module.css index 28e9a83..6d5ab63 100644 --- a/src/components/Button/Button.module.css +++ b/src/components/Button/Button.module.css @@ -1,17 +1,8 @@ .button { - --borderRadius: 8px; - background: none; + --borderRadius: 12px; + padding: 15px; position: relative; - isolation: isolate; - - /* Masking works WAY better than below */ - /*overflow: none;*/ - mask: linear-gradient(#000 0 0); - - border: none; - border-radius: var(--borderRadius); - padding: 0; font-size: 14px; font-weight: 700; @@ -19,58 +10,60 @@ cursor: pointer; user-select: none; -} -.bottom, -.top { - padding: 15px; + background: var(--background); + color: var(--color); + border-radius: var(--borderRadius); + + border: 2px solid transparent; display: flex; gap: 10px; justify-content: center; align-items: center; - width: 100%; - height: 100%; } -.bottom { - position: relative; - background: var(--sideBar_background); - color: var(--primary); - z-index: 1; - border-radius: calc(var(--borderRadius) + 2px); +.border { + border: 2px solid var(--border); } -.top { - position: absolute; - background: var(--background); - color: var(--color); - z-index: 2; - transition: clip-path .3s ease; - clip-path: inset(0 calc(100% - var(--progress)) 0 0); +.rounded { + --borderRadius: 50px; + padding: 15px 35px; +} + +.colorsGreen { + --background: var(--buttonGreen); + --color: var(--buttonGreenText); + --border: var(--buttonLightBorder); } -.colors_green { - --background: var(--button_green); - --color: var(--buttonText_green); +.colorsBlue { + --background: var(--buttonBlue); + --color: var(--buttonBlueText); + --border: var(--buttonLightBorder); } -.colors_blue { - --background: var(--button_blue); - --color: var(--buttonText_blue); +.colorsYellow { + --background: var(--buttonYellow); + --color: var(--buttonYellowText); + --border: var(--buttonLightBorder); } -.colors_yellow { - --background: var(--button_yellow); - --color: var(--buttonText_yellow); +.colorsDark { + --background: var(--buttonDark); + --color: var(--buttonDarkText); + --border: var(--buttonDarkBorder); } -.colors_gray { - --background: var(--button_gray); - --color: var(--buttonText_gray); +.colorsLight { + --background: var(--buttonLight); + --color: var(--buttonLightText); + --border: var(--buttonLightBorder); } -.colors_red { - --background: var(--button_red); - --color: var(--buttonText_red); -} \ No newline at end of file +.colorsRed { + --background: var(--buttonRed); + --color: var(--buttonRedText); + --border: var(--buttonLightBorder); +} diff --git a/src/components/DropdownButton/DropdownButton.module.css b/src/components/Button/DropdownButton/DropdownButton.module.css similarity index 88% rename from src/components/DropdownButton/DropdownButton.module.css rename to src/components/Button/DropdownButton/DropdownButton.module.css index acd2533..c736e44 100644 --- a/src/components/DropdownButton/DropdownButton.module.css +++ b/src/components/Button/DropdownButton/DropdownButton.module.css @@ -29,14 +29,14 @@ gap: 10px; flex-shrink: 0; - background: var(--sideBar_background); + background: var(--sideBarBackground); border: none; cursor: pointer; } .dropdown_content { width: 300px; - background: var(--sideBar_background); + background: var(--sideBarBackground); border-radius: 8px; padding: 5px; @@ -67,9 +67,9 @@ } .dropdown_item[data-highlighted] { - background: var(--sideBar_selection); + background: var(--sideBarSelection); } .dropdown_arrow { - fill: var(--sideBar_background); -} \ No newline at end of file + fill: var(--sideBarBackground); +} diff --git a/src/components/DropdownButton/index.tsx b/src/components/Button/DropdownButton/index.tsx similarity index 94% rename from src/components/DropdownButton/index.tsx rename to src/components/Button/DropdownButton/index.tsx index 557e868..ec2586e 100644 --- a/src/components/DropdownButton/index.tsx +++ b/src/components/Button/DropdownButton/index.tsx @@ -1,5 +1,5 @@ import { DropdownIcon } from "@app/assets/Icons"; -import Button, { ButtonProps } from "../Button"; +import Button, { ButtonProps } from ".."; import styles from "./DropdownButton.module.css"; import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; @@ -57,4 +57,4 @@ const DropdownItem: React.FC = (props: ItemProps) => { ; }; -export { DropdownButton, DropdownItem }; \ No newline at end of file +export { DropdownButton, DropdownItem }; diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 8496414..880bd2f 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -1,12 +1,12 @@ import styles from "./Button.module.css"; -import { CSSProperties } from "react"; +import { CSSProperties, forwardRef } from "react"; export enum ButtonColor { "GREEN", "BLUE", "YELLOW", - "GRAY", - "BLACK", + "LIGHT", + "DARK", "RED" } @@ -19,52 +19,80 @@ export type ButtonProps = React.PropsWithChildren<{ style?: React.CSSProperties, onClick?: React.MouseEventHandler, + border?: boolean, + rounded?: boolean, color?: ButtonColor, - progress?: number, + width?: number, height?: number, }>; -const Button: React.FC = (props: ButtonProps) => { +const Button = forwardRef(function Button(props, ref) { + const { + className, + style, + + border, + rounded, + color, + + width, + height, + + children, + + ...otherProps + } = props; + // Get the button color class - let colorClass; - switch (props.color) { + let classes; + switch (color) { case ButtonColor.BLUE: - colorClass = styles.colors_blue; + classes = [styles.colorsBlue]; break; case ButtonColor.GREEN: - colorClass = styles.colors_green; + classes = [styles.colorsGreen]; break; case ButtonColor.YELLOW: - colorClass = styles.colors_yellow; + classes = [styles.colorsYellow]; break; - case ButtonColor.GRAY: - colorClass = styles.colors_gray; + case ButtonColor.LIGHT: + classes = [styles.colorsLight]; break; - case ButtonColor.BLACK: - colorClass = styles.colors_black; + case ButtonColor.DARK: + classes = [styles.colorsDark]; break; case ButtonColor.RED: - colorClass = styles.colors_red; + classes = [styles.colorsRed]; break; default: - colorClass = styles.colors_blue; + classes = [styles.colorsBlue]; break; } + if (border) { + classes.push(styles.border); + } + + if (rounded) { + classes.push(styles.rounded); + } + // Get the styles const newStyles = { - width: props.width, - height: props.height, - ...props.style, - - "--progress": props.progress ? `${props.progress}%` : undefined + width, + height, + ...style, } as ButtonCSS; - return ; -}; +}); -export default Button; \ No newline at end of file +export default Button; diff --git a/src/components/GenericBox/SetlistBox.module.css b/src/components/GenericBox/SetlistBox.module.css deleted file mode 100644 index f690abc..0000000 --- a/src/components/GenericBox/SetlistBox.module.css +++ /dev/null @@ -1,38 +0,0 @@ -.box_slim { - display: flex; - flex-direction: column; - align-items: flex-start; - - border-radius: 8px; - border: 1px solid rgba(0, 0, 0, 0.1); - - color: #333; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: normal; - - overflow: hidden; -} - -.box { - padding: 10px; - gap: 15px; - align-self: stretch; -} - -.box_header { - display: flex; - padding: 1px 5px 7px 5px; - align-items: center; - gap: 5px; - align-self: stretch; - border-bottom: 1px solid rgba(46, 52, 78, 0.10); - - color: #2E344E; - font-size: 14px; - font-style: normal; - font-weight: 700; - line-height: normal; - text-transform: uppercase; -} \ No newline at end of file diff --git a/src/components/GenericBox/index.tsx b/src/components/GenericBox/index.tsx deleted file mode 100644 index 111febd..0000000 --- a/src/components/GenericBox/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import styles from "./SetlistBox.module.css"; - -type Props = React.PropsWithChildren<{ - style?: React.CSSProperties -}>; - -const GenericBox: React.FC = ({ children, style }: Props) => { - return
- {children} -
; -}; - -const GenericBoxSlim: React.FC = ({ children, style }: Props) => { - return
- {children} -
; -}; - -const GenericBoxHeader: React.FC = ({ children, style }: Props) => { - return
- {children} -
; -}; - -export { GenericBox, GenericBoxSlim, GenericBoxHeader }; \ No newline at end of file diff --git a/src/components/InputBox/InputBox.module.css b/src/components/InputBox/InputBox.module.css new file mode 100644 index 0000000..b482905 --- /dev/null +++ b/src/components/InputBox/InputBox.module.css @@ -0,0 +1,22 @@ +.input { + padding: 15px 18px; + + border-radius: 50px; + border: 1px solid #CCCED7; + background: #FFF; + + color: #41475F; + font-size: 16px; + font-weight: 400; +} + +.input:focus-visible { + outline: 2px solid #6C7088; + outline-offset: -2px; +} + +.input::placeholder { + color: #CCCED7; + font-size: 16px; + font-style: italic; +} diff --git a/src/components/InputBox/index.tsx b/src/components/InputBox/index.tsx new file mode 100644 index 0000000..5ebe7cd --- /dev/null +++ b/src/components/InputBox/index.tsx @@ -0,0 +1,23 @@ +import styles from "./InputBox.module.css"; + +interface Props { + state: string, + setState: React.Dispatch>, + + placeholder?: string, + + className?: string, + style?: React.CSSProperties, +} + +const InputBox: React.FC = ({ state, setState, className, style, placeholder }: Props) => { + return setState(e.target.value)} />; +}; + +export default InputBox; diff --git a/src/components/Launch/LaunchButton/index.tsx b/src/components/Launch/LaunchButton/index.tsx deleted file mode 100644 index 0ab5f3b..0000000 --- a/src/components/Launch/LaunchButton/index.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { YARGStates, YARGVersion } from "@app/hooks/useYARGVersion"; -import { ButtonColor } from "../../Button"; -import { InstallingIcon, UpdateIcon } from "@app/assets/Icons"; -import { calculatePayloadPercentage } from "@app/tasks/payload"; -import PayloadProgress from "../../PayloadProgress"; -import Button from "@app/components/Button"; -import { DropdownButton, DropdownItem } from "@app/components/DropdownButton"; - -interface LaunchButtonProps extends React.PropsWithChildren { - version: YARGVersion, - playName: string, - style?: React.CSSProperties -} - -export function LaunchButton(props: LaunchButtonProps) { - const { version, playName } = props; - - if (version.state === YARGStates.NEW_UPDATE) { - const buttonChildren = <> - Update {playName} - ; - - return ; - } - - if (version.state === YARGStates.DOWNLOADING) { - const buttonChildren = <> - - - ; - - return ; - } - - if (version.state === YARGStates.AVAILABLE) { - const buttonChildren = <> - Play {playName} - ; - - const dropdownChildren = <> - version.uninstall()}> - Uninstall - - version.revealFolder()}> - Open Install Folder - - ; - - return version.play()} - dropdownChildren={dropdownChildren}> - - {buttonChildren} - ; - } - - if (version.state === YARGStates.PLAYING) { - const buttonChildren = <> - Opening YARG {playName} - ; - - return ; - } - - if (version.state === YARGStates.ERROR) { - const buttonChildren = <> - Error! - ; - - return ; - } - - return ; -} \ No newline at end of file diff --git a/src/components/Launch/LaunchPage/index.tsx b/src/components/Launch/LaunchPage/index.tsx deleted file mode 100644 index ba07177..0000000 --- a/src/components/Launch/LaunchPage/index.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { YARGVersion } from "@app/hooks/useYARGVersion"; -import styles from "./styles.module.css"; -import { GenericBox, GenericBoxHeader } from "../../GenericBox"; -import { DateIcon, InformationIcon, LinkIcon } from "@app/assets/Icons"; -import TooltipWrapper from "../../TooltipWrapper"; -import { intlFormatDistance } from "date-fns"; -import NewsSection from "../../NewsSection"; -import { LaunchButton } from "../LaunchButton"; - -const INITIAL_RELEASE_DATE = new Date("2023-03-09T05:00:00.000Z"); - -interface Props { - version: YARGVersion, - releaseTag: string, - playName: string, - description: React.ReactNode, - websiteUrl: string, - icon: string, - banner: string, -} - -const LaunchPage: React.FC = ({ version, releaseTag, playName, description, websiteUrl, icon, banner }: Props) => { - // If there isn't a version, something went wrong - if (!version) { - return

Error: No version.

; - } - - return <> -
-
- YARG -
- - YARG - -
- {releaseTag} -
-
-
-
-
-
- -
- - - - - YARG {playName} - - - {description} - -
- - - - {new Intl.DateTimeFormat("en-US", { - year: "numeric", - month: "long", - day: "numeric", - }).format(INITIAL_RELEASE_DATE)} - - - - Official Website - -
-
-
-
- ; -}; - -export default LaunchPage; \ No newline at end of file diff --git a/src/components/Launch/LaunchPage/styles.module.css b/src/components/Launch/LaunchPage/styles.module.css deleted file mode 100644 index 567a8d2..0000000 --- a/src/components/Launch/LaunchPage/styles.module.css +++ /dev/null @@ -1,130 +0,0 @@ -.header { - display: flex; - height: 250px; - flex-shrink: 0; - align-self: stretch; - flex-direction: column-reverse; - - background-repeat: no-repeat; - background-size: cover; - background-position: center; -} - -.actions { - display: flex; - height: 65px; - padding: 15px 25px 15px 150px; - justify-content: space-between; - align-items: center; - align-self: stretch; - - background: rgba(0, 0, 0, 0.4); - backdrop-filter: blur(10px); - color: white; -} - -.icon_container { - position: relative; - z-index: 1; -} - -.icon { - position: absolute; - bottom: 20px; - left: 20px; - box-shadow: 0px 0px 30px 1px rgba(0, 0, 0, 0.75); - - width: 100px; - height: 100px; - border-radius: 15px; -} - -.game_info { - position: absolute; - bottom: 70px; - left: 130px; - - display: flex; - align-items: center; - gap: 12px; - - color: white; - font-size: 30px; - font-style: normal; - font-weight: 700; - line-height: normal; - text-transform: uppercase; -} - -.game_name { - text-shadow: 0px 0px 6px rgba(0,0,0,0.5); -} - -.version_badge { - display: flex; - padding: 3px 10px; - justify-content: center; - align-items: center; - gap: 10px; - - border-radius: 50px; - background: white; - - color: #7C7C7C; - font-size: 12px; - font-style: normal; - font-weight: 700; - line-height: normal; - text-transform: none; -} - -.main { - display: flex; - width: 100%; - min-height: calc(100% - 250px); - padding: 25px; - align-items: flex-start; - gap: 25px; - background: var(--white-background); - - align-items: stretch; -} - -.content { - flex: 1 0 0; - font-style: italic; - - color: rgba(1, 1, 1, 0.25); - display: flex; - align-items: center; - justify-content: center; -} - -.sidebar { - display: flex; - width: 300px; - flex-direction: column; - align-items: flex-start; - gap: 10px; - flex-shrink: 0; -} - -.info_list { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 10px; -} - -.info_entry { - display: flex; - padding: 0px 1px; - align-items: center; - gap: 6px; - - color: #333; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: normal; -} \ No newline at end of file diff --git a/src/components/LoadingScreen/LoadingScreen.module.css b/src/components/LoadingScreen/LoadingScreen.module.css index a2d0fe1..fa37fb5 100644 --- a/src/components/LoadingScreen/LoadingScreen.module.css +++ b/src/components/LoadingScreen/LoadingScreen.module.css @@ -10,7 +10,7 @@ flex: 1 0 0; align-self: stretch; - background: var(--sideBar_background); + background: var(--sideBarBackground); color: #FFF; font-size: 16px; @@ -67,7 +67,7 @@ background: var(--accent); animation-name: progressAnimation; - animation-duration: 1s; + animation-duration: 3s; } @keyframes progressAnimation { @@ -77,4 +77,4 @@ to { width: 100%; } -} \ No newline at end of file +} diff --git a/src/components/LoadingScreen/index.tsx b/src/components/LoadingScreen/index.tsx index 7cd9306..6c122d0 100644 --- a/src/components/LoadingScreen/index.tsx +++ b/src/components/LoadingScreen/index.tsx @@ -3,16 +3,29 @@ import styles from "./LoadingScreen.module.css"; import * as Progress from "@radix-ui/react-progress"; import { error as logError } from "tauri-plugin-log-api"; import { serializeError } from "serialize-error"; -import { invoke } from "@tauri-apps/api/tauri"; +import { useProfileStore } from "@app/profiles/store"; +import { settingsManager } from "@app/settings"; +import { invoke } from "@tauri-apps/api"; +import { appWindow } from "@tauri-apps/api/window"; +import { launch } from "@app/profiles/actions"; +import { getPathForProfile } from "@app/profiles/utils"; +import { useDirectories } from "@app/profiles/directories"; +import { createAndShowDialog, showErrorDialog } from "@app/dialogs"; +import { useOfflineStatus } from "@app/hooks/useOfflineStatus"; +import isOnline from "is-online"; +import { OfflineDialog } from "@app/dialogs/Dialogs/OfflineDialog"; enum LoadingState { "LOADING", + "LAUNCHING", "FADE_OUT", "DONE" } interface Props { setError: React.Dispatch; + setOnboarding: React.Dispatch; + setShowBody: React.Dispatch; } const LoadingScreen: React.FC = (props: Props) => { @@ -22,10 +35,59 @@ const LoadingScreen: React.FC = (props: Props) => { useEffect(() => { (async () => { try { - await invoke("init"); + const offline = !await isOnline(); + if (offline) { + const offlineStatus = useOfflineStatus.getState(); + offlineStatus.setOffline(true); + } + + // Make sure to save the settings afterwards in case a new key has been added + // If "get" is called and the settings didn't save, it would cause an error. + await settingsManager.initialize(); + await settingsManager.syncCache(); + + const onboardingCompleted = settingsManager.getCache("onboardingCompleted"); + + let profileStore = useProfileStore.getState(); + let directories = useDirectories.getState(); + + let downloadLocation: string | undefined = undefined; + if (onboardingCompleted) { + downloadLocation = settingsManager.getCache("downloadLocation"); + } + + await directories.setDirs(downloadLocation); + directories = useDirectories.getState(); + + await profileStore.activateProfilesFromSettings(offline); + profileStore = useProfileStore.getState(); + + if (!onboardingCompleted) { + props.setOnboarding(true); + } else { + // Check if a profile was requested to be launched by command line arguments + const launchOption: string | null = await invoke("get_launch_argument"); + if (launchOption !== null) { + setLoading(LoadingState.LAUNCHING); + + const profile = profileStore.getProfileByUUID(launchOption); + if (profile) { + const path = await getPathForProfile(directories, profile); + await launch(profile, path); + + appWindow.close(); + } else { + showErrorDialog("Invalid profile specified: " + launchOption); + } + } + } // Add a tiny bit of delay so the loading screen doesn't just instantly disappear await new Promise(r => setTimeout(r, 250)); + + if (offline) { + createAndShowDialog(OfflineDialog); + } } catch (e) { console.error(e); logError(JSON.stringify(serializeError(e))); @@ -37,6 +99,8 @@ const LoadingScreen: React.FC = (props: Props) => { return; } + props.setShowBody(true); + // The loading screen takes 250ms to fade out setLoading(LoadingState.FADE_OUT); await new Promise(r => setTimeout(r, 250)); @@ -58,10 +122,19 @@ const LoadingScreen: React.FC = (props: Props) => {
-

Fun Fact

- YARG stands for Yet Another Rhythm Game + {loading == LoadingState.LAUNCHING ? + <> +

Launching...

+ Sit tight... + : + <> +

Fun Fact

+ YARG stands for Yet Another Rhythm Game + + } +
; }; -export default LoadingScreen; \ No newline at end of file +export default LoadingScreen; diff --git a/src/components/NewsSection/NewsEntry/NewsEntry.module.css b/src/components/NewsSection/NewsEntry/NewsEntry.module.css index a4bdb0f..a6993ab 100644 --- a/src/components/NewsSection/NewsEntry/NewsEntry.module.css +++ b/src/components/NewsSection/NewsEntry/NewsEntry.module.css @@ -48,7 +48,6 @@ align-self: stretch; color: #494949; - font-family: Inter; font-size: 12px; font-style: normal; font-weight: 500; @@ -87,4 +86,4 @@ .container:hover .releaseDate { margin-right: 1em; -} \ No newline at end of file +} diff --git a/src/components/NewsSection/NewsEntry/index.tsx b/src/components/NewsSection/NewsEntry/index.tsx index 8dd9945..c751648 100644 --- a/src/components/NewsSection/NewsEntry/index.tsx +++ b/src/components/NewsSection/NewsEntry/index.tsx @@ -5,10 +5,10 @@ import { Link } from "react-router-dom"; import { Img } from "react-image"; import UnknownUserIcon from "@app/assets/Icons/UnknownUser.svg"; import { TimeIcon } from "@app/assets/Icons"; -import { intlFormatDistance } from "date-fns"; import { newsBaseURL } from "@app/utils/consts"; import { useNewsAuthorSettings } from "@app/hooks/useNewsAuthor"; import { useQueries } from "@tanstack/react-query"; +import { distanceFromToday } from "@app/utils/timeFormat"; interface Props { article: ArticleData; @@ -31,7 +31,7 @@ const NewsEntry: React.FC = ({ article }: Props) => { article.release ? (
- {intlFormatDistance(new Date(article.release), new Date())} + {distanceFromToday(article.release)}
) : "" } @@ -64,4 +64,4 @@ const NewsEntry: React.FC = ({ article }: Props) => { ; }; -export default NewsEntry; \ No newline at end of file +export default NewsEntry; diff --git a/src/components/NewsSection/NewsSection.module.css b/src/components/NewsSection/NewsSection.module.css index a274f48..f60b3da 100644 --- a/src/components/NewsSection/NewsSection.module.css +++ b/src/components/NewsSection/NewsSection.module.css @@ -6,7 +6,24 @@ align-self: stretch; } -.header_container { +.offlineContainer { + padding-top: 50px; + + display: flex; + flex-direction: column; + align-items: center; + flex: 1 0 0; + align-self: stretch; + + color: #000; + font-size: 24px; + font-weight: 700; + text-transform: uppercase; + + opacity: 0.5; +} + +.headerContainer { display: flex; padding-bottom: 0px; justify-content: space-between; @@ -17,7 +34,7 @@ border-bottom: 1px solid rgba(0, 0, 0, 0.1); } -.header_text { +.headerText { display: flex; align-items: center; gap: 5px; @@ -30,11 +47,11 @@ text-transform: uppercase; } -.load_more { +.loadMore { color: rgba(0, 0, 0, 0.5); font-size: 0.8em; font-weight: 600; cursor: pointer; text-align: center; width: 100%; -} \ No newline at end of file +} diff --git a/src/components/NewsSection/index.tsx b/src/components/NewsSection/index.tsx index b075091..3226a41 100644 --- a/src/components/NewsSection/index.tsx +++ b/src/components/NewsSection/index.tsx @@ -3,24 +3,36 @@ import styles from "./NewsSection.module.css"; import NewsEntry from "./NewsEntry"; import { useNews } from "@app/hooks/useNews"; import { useState } from "react"; +import { useOfflineStatus } from "@app/hooks/useOfflineStatus"; interface Props { - categoryFilter?: string + categoryFilter?: string, startingEntries?: number } const NewsSection: React.FC = ({ categoryFilter, startingEntries }: Props) => { + const offlineStatus = useOfflineStatus(); const { data, error, isLoading, isSuccess } = useNews(); const [displayCount, setDisplayCount] = useState(startingEntries ? startingEntries : 4); - if (isLoading) return "Loading.."; + if (offlineStatus.isOffline) { + return
+ Offline +
; + } - if (error) return `An error has occurred: ${error}`; + if (isLoading) { + return "Loading..."; + } + + if (error) { + return `An error has occurred: ${error}`; + } if (isSuccess) { return
-
-
+
+
NEWS
@@ -31,9 +43,9 @@ const NewsSection: React.FC = ({ categoryFilter, startingEntries }: Props return i.category === categoryFilter; }).slice(0, displayCount).map(article => ) } -
setDisplayCount(displayCount + 4)}>Load More...
+
setDisplayCount(displayCount + 4)}>Load More...
; } }; -export default NewsSection; \ No newline at end of file +export default NewsSection; diff --git a/src/components/Onboarding/Onboarding.module.css b/src/components/Onboarding/Onboarding.module.css new file mode 100644 index 0000000..4af8a1b --- /dev/null +++ b/src/components/Onboarding/Onboarding.module.css @@ -0,0 +1,170 @@ +.mainContainer { + position: fixed; + inset: 0; + top: var(--titleBarHeight); + + background: var(--sideBarBackground); + color: white; + + z-index: 9999; +} + +.container { + display: flex; + align-items: flex-start; + align-self: stretch; + + height: 100%; +} + +.sidebar { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: flex-start; + align-self: stretch; + + background: #070810; +} + +.sidebarTop { + display: flex; + flex-direction: column; + align-items: flex-start; + align-self: stretch; +} + +.sidebarBottom { + display: flex; + padding: 12px; + flex-direction: column; + align-items: flex-start; + gap: 10px; + align-self: stretch; +} + +.sidebarTop > header { + display: flex; + padding: 12px; + justify-content: center; + align-items: center; + gap: 10px; + align-self: stretch; + + color: #9194B1; + font-size: 20px; + font-style: normal; + font-weight: 700; + line-height: normal; + text-transform: uppercase; +} + +.navigation { + padding: 12px; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 10px; + align-self: stretch; +} + +.navigationButton { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 10px; + align-self: stretch; + + color: #B8BDD6; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: normal; + + opacity: 0.9; +} + +.navigationButton > div { + display: flex; + height: 48px; + width: 48px; + padding: 10px; + justify-content: center; + align-items: center; + + border-radius: 6px; +} + +.navigationButton.activeStep { + opacity: 1; +} + +.navigationButton.activeStep > div { + background: #0D0F23; +} + +.content { + display: flex; + padding: 50px; + justify-content: center; + align-items: flex-start; + gap: 100px; + flex: 1 0 0; + align-self: stretch; + + background: linear-gradient(182deg, rgba(5, 6, 11, 0.60) 1.91%, #05060c 47.87%), + url(/src/assets/OnboardingBackground.png) lightgray 50% / cover no-repeat; +} + +.contentContainer { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: flex-start; + flex: 1 0 0; + align-self: stretch; +} + +.stepContent { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 50px; + align-self: stretch; +} + +.stepNavigation { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 25px; + align-self: stretch; +} + +.stepNavigationButtons { + display: flex; + height: 60px; + align-items: flex-start; + gap: 25px; + align-self: stretch; +} + +.stepNavigationButtons > button { + flex: 1 0 0; +} + +.stepContainer { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 25px; + align-self: stretch; +} + +.center { + height: 100%; + + display: flex; + justify-content: center; + align-items: center; +} diff --git a/src/components/Onboarding/Pages/ComponentsPage.tsx b/src/components/Onboarding/Pages/ComponentsPage.tsx new file mode 100644 index 0000000..73f3043 --- /dev/null +++ b/src/components/Onboarding/Pages/ComponentsPage.tsx @@ -0,0 +1,111 @@ +import WarningBox from "./WarningBox"; +import styles from "./Pages.module.css"; +import { useQuery } from "@tanstack/react-query"; +import { OnboardingIndex, OnboardingOption } from "./onboardingIndex"; +import ProfileIcon from "@app/components/ProfileIcon"; +import { localizeObject } from "@app/utils/localized"; +import { useEffect, useState } from "react"; +import { CheckmarkIcon } from "@app/assets/Icons"; + +interface ComponentProps { + option: OnboardingOption, + setOption: (url: string, enabled: boolean) => void +} + +const ComponentOption: React.FC = ({ option, setOption }: ComponentProps) => { + const [selected, setSelected] = useState(option.selectedByDefault); + const localized = localizeObject(option, "en-US"); + + // Ensure options that are "selected by default" are actually selected + useEffect(() => { + setOption(option.url, option.selectedByDefault); + }, [option.selectedByDefault]); + + return
{ + setSelected(!selected); + setOption(option.url, !selected); + }}> +
+ +
+
{localized.name}
+
{localized.subText}
+
+
+ {!selected && +
+ } + {selected && +
+ +
+ } +
; +}; + +interface Props { + profileUrls: string[], + setProfileUrls: React.Dispatch>, +} + +export const ComponentsPage: React.FC = ({ profileUrls, setProfileUrls }: Props) => { + const onboardingIndexQuery = useQuery({ + queryKey: ["OnboardingIndex"], + queryFn: async (): Promise => await fetch("https://releases.yarg.in/profiles/onboarding.json") + .then(res => res.json()) + }); + + if (onboardingIndexQuery.isLoading) { + return <>Loading...; + } + + const onboardingIndex = onboardingIndexQuery.data; + if (onboardingIndexQuery.isError || onboardingIndex === undefined) { + return <> + Error: {onboardingIndexQuery.error} + ; + } + + const setOption = (url: string, enabled: boolean) => { + if (enabled) { + if (!profileUrls.includes(url)) { + profileUrls.push(url); + setProfileUrls(profileUrls); + } + } else { + profileUrls = profileUrls.filter(i => i !== url); + setProfileUrls(profileUrls); + } + }; + + return <> + + You can download other applications and songs at any time after the initial onboarding + process in the "Marketplace". + +
+
+
Applications
+
+ { + onboardingIndex.filter(i => i.type === "application").map(i => + + ) + } +
+
+
+
Songs
+
+ { + onboardingIndex.filter(i => i.type === "setlist").map(i => + + ) + } +
+
+
+ ; +}; + +export default ComponentsPage; diff --git a/src/components/Onboarding/Pages/InstallFolderPage.tsx b/src/components/Onboarding/Pages/InstallFolderPage.tsx new file mode 100644 index 0000000..0969b9b --- /dev/null +++ b/src/components/Onboarding/Pages/InstallFolderPage.tsx @@ -0,0 +1,38 @@ +import styles from "./Pages.module.css"; +import Button, { ButtonColor } from "@app/components/Button"; +import WarningBox from "./WarningBox"; +import { DriveIcon } from "@app/assets/Icons"; + +interface Props { + downloadLocation: string; + downloadEmpty: boolean; + + askForFolder: () => Promise; +} + +export const InstallFolderPage: React.FC = (props: Props) => { + return <> + + The installation folder can be changed after initial setup, + however some content may need to be re-downloaded. + +
+
+ +

{props.downloadLocation}

+
+
+ +
+
+ {!props.downloadEmpty && + + The folder you selected is not empty. Please select an empty folder to continue. + + } + ; +}; + +export default InstallFolderPage; diff --git a/src/components/Onboarding/Pages/Pages.module.css b/src/components/Onboarding/Pages/Pages.module.css new file mode 100644 index 0000000..79858a5 --- /dev/null +++ b/src/components/Onboarding/Pages/Pages.module.css @@ -0,0 +1,147 @@ +.warningBox { + display: flex; + padding: 20px; + align-items: center; + gap: 15px; + align-self: stretch; + + border-radius: 12px; + background: rgba(46, 217, 255, 0.15); + + color: #D3F7FF; + font-size: 14px; + font-weight: 400; +} + +.folderSelection { + display: flex; + height: 80px; + align-items: flex-start; + align-self: stretch; + + border-radius: 12px; + background: var(--buttonDark); + color: var(--buttonDarkText); + border: 2px solid var(--buttonDarkBorder); + + font-size: 17px; +} + +.selectedFolder { + display: flex; + height: 80px; + padding: 25px; + align-items: center; + gap: 15px; + flex: 1 0 0; +} + +.browseButtonContainer { + display: flex; + padding: 0px 15px; + justify-content: center; + align-items: center; + gap: 10px; + align-self: stretch; +} + +.componentContainer { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 25px; + align-self: stretch; +} + +.componentCategory { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 15px; + align-self: stretch; +} + +.componentCategory > header { + color: #9194B1; + font-weight: 700; + text-transform: uppercase; +} + +.componentOptionContainer { + display: flex; + height: 124px; + align-items: flex-start; + gap: 15px; + align-self: stretch; +} + +.componentOption { + display: flex; + height: 124px; + padding: 25px; + justify-content: space-between; + align-items: center; + flex: 1 0 0; + gap: 20px; + + border-radius: 15px; + border: 2px solid var(--buttonDarkBorder); + background: var(--buttonDark); + + cursor: pointer; + user-select: none; +} + +.componentOption > .left { + display: flex; + align-items: center; + gap: 25px; + flex: 1 0 0; +} + +.componentOption > .left > .icon { + flex: 0 0 75px; + height: 75px; +} + +.componentOption > .left > div { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 10px; + flex: 1 0 0; + + color: #66698D; + font-size: 12px; + font-weight: 600; +} + +.componentOption > .left > div > header { + color: #FFF; + font-family: Inter; + font-size: 20px; + font-weight: 700; +} + +.unselectedIndicator { + width: 38px; + height: 38px; + border-radius: 93px; + + border: 2px solid var(--buttonDarkBorder); + background: var(--buttonDark); +} + +.selectedIndicator { + width: 38px; + height: 38px; + border-radius: 93px; + + display: flex; + justify-content: center; + align-items: center; + + border: 2px solid var(--buttonLightBorder); + background: #17E289; + color: #006E3E; +} diff --git a/src/components/Onboarding/Pages/WarningBox.tsx b/src/components/Onboarding/Pages/WarningBox.tsx new file mode 100644 index 0000000..dbaf0a0 --- /dev/null +++ b/src/components/Onboarding/Pages/WarningBox.tsx @@ -0,0 +1,11 @@ +import styles from "./Pages.module.css"; +import { InformationIcon } from "@app/assets/Icons"; + +export const warningBox: React.FC = (props: React.PropsWithChildren) => { + return
+ + {props.children} +
; +}; + +export default warningBox; diff --git a/src/components/Onboarding/Pages/onboardingIndex.ts b/src/components/Onboarding/Pages/onboardingIndex.ts new file mode 100644 index 0000000..7a44129 --- /dev/null +++ b/src/components/Onboarding/Pages/onboardingIndex.ts @@ -0,0 +1,15 @@ +import { Localized } from "@app/utils/localized"; + +export type OnboardingIndex = OnboardingOption[]; + +export type OnboardingOption = Localized<{ + uuid: string, + type: "application" | "setlist", + url: string, + + name: string, + subText: string, + iconUrl: string, + + selectedByDefault: boolean, +}>; diff --git a/src/components/Onboarding/Sidebar.tsx b/src/components/Onboarding/Sidebar.tsx new file mode 100644 index 0000000..beecb63 --- /dev/null +++ b/src/components/Onboarding/Sidebar.tsx @@ -0,0 +1,32 @@ +import { OnboardingStep } from "."; +import StepIndicator from "./StepIndicator"; +import styles from "./Onboarding.module.css"; +import { DriveIcon, QueueIcon } from "@app/assets/Icons"; + +interface Props { + onboardingStep: OnboardingStep; +} + +const OnboardingSidebar: React.FC = ({ onboardingStep }: Props) => { + return
+
+
+ {/* OnboardingStep.LANGUAGE} /> */} + + + + + + +
+
+
+ +
+
; +}; + +export default OnboardingSidebar; + diff --git a/src/components/Onboarding/StepIndicator.tsx b/src/components/Onboarding/StepIndicator.tsx new file mode 100644 index 0000000..3712895 --- /dev/null +++ b/src/components/Onboarding/StepIndicator.tsx @@ -0,0 +1,20 @@ +import styles from "./Onboarding.module.css"; + +type Props = React.PropsWithChildren<{ + activeStep: boolean +}>; + +const StepIndicator: React.FC = ({ children, activeStep }: Props) => { + const classes = [styles.navigationButton]; + if (activeStep) { + classes.push(styles.activeStep); + } + + return
+
+ {children} +
+
; +}; + +export default StepIndicator; diff --git a/src/components/Onboarding/index.tsx b/src/components/Onboarding/index.tsx new file mode 100644 index 0000000..4a2f4d3 --- /dev/null +++ b/src/components/Onboarding/index.tsx @@ -0,0 +1,162 @@ +import { useState } from "react"; +import styles from "./Onboarding.module.css"; +import { open } from "@tauri-apps/api/dialog"; +import { invoke } from "@tauri-apps/api"; +import { settingsManager } from "@app/settings"; +import OnboardingSidebar from "./Sidebar"; +import Button, { ButtonColor } from "../Button"; +import InstallFolderPage from "./Pages/InstallFolderPage"; +import ComponentsPage from "./Pages/ComponentsPage"; +import { useDirectories } from "@app/profiles/directories"; +import { useProfileStore } from "@app/profiles/store"; +import { downloadAndInstall } from "@app/profiles/actions"; +import { getPathForProfile } from "@app/profiles/utils"; +import { useOfflineStatus } from "@app/hooks/useOfflineStatus"; + +export enum OnboardingStep { + // LANGUAGE = 0, + INSTALL_PATH = 1, + COMPONENTS = 2, +} + +interface Props { + setOnboarding: React.Dispatch; +} + +const Onboarding: React.FC = (props: Props) => { + const [loading, setLoading] = useState(false); + const [step, setStep] = useState(OnboardingStep.INSTALL_PATH); + + let directories = useDirectories(); + const offlineStatus = useOfflineStatus(); + + const defaultDownload = directories.importantDirs?.yarcFolder; + if (defaultDownload === undefined) { + throw new Error("The default installation path was not found!"); + } + + const [downloadLocation, setDownloadLocation] = useState(defaultDownload); + const [downloadEmpty, setDownloadEmpty] = useState(true); + + const [profileUrls, setProfileUrls] = useState([]); + + if (offlineStatus.isOffline) { + return
+
+ + You're offline! Please connect to the internet and restart the launcher to + finish the launcher onboarding process. + +
+
; + } + + async function askForFolder() { + const select = await open({ + directory: true + }); + + if (typeof select === "string") { + const path: string = select; + const empty: boolean = await invoke("is_dir_empty", { path: path }); + + setDownloadLocation(path); + setDownloadEmpty(empty); + } + } + + async function finish() { + setLoading(true); + + settingsManager.setCache("downloadLocation", downloadLocation); + settingsManager.setCache("onboardingCompleted", true); + await settingsManager.syncCache(); + + await directories.setDirs(downloadLocation); + directories = useDirectories.getState(); + + // If the user has old stuff installed, remove those + try { + await invoke("clean_up_old_install", { + yargFolder: directories.customDirs?.yargFolder, + setlistFolder: directories.customDirs?.setlistFolder + }); + } catch { + // Ignore + } + + for (const url of profileUrls) { + const uuid = await useProfileStore.getState().activateProfile(url); + if (uuid === undefined) { + continue; + } + + const activeProfile = useProfileStore.getState().getProfileByUUID(uuid); + if (activeProfile === undefined) { + continue; + } + + const path = await getPathForProfile(directories, activeProfile); + await downloadAndInstall(activeProfile, path); + } + + props.setOnboarding(false); + } + + return
+ {!loading && +
+ +
+
+
+ {step === OnboardingStep.INSTALL_PATH && + + } + {step === OnboardingStep.COMPONENTS && + + } +
+
+
+ + +
+
+
+
+
+ } + {loading && +
+ + Loading, please wait... + +
+ } +
; +}; + +export default Onboarding; + diff --git a/src/components/PayloadProgress/index.tsx b/src/components/PayloadProgress/index.tsx index d60f785..c2cf1ac 100644 --- a/src/components/PayloadProgress/index.tsx +++ b/src/components/PayloadProgress/index.tsx @@ -15,7 +15,7 @@ const PayloadProgress: React.FC = ({ payload, defaultText = "Loading", fu case "downloading": return ; case "installing": - return ; + return ; case "verifying": return ; default: @@ -27,12 +27,12 @@ const ProgressWaiting: React.FC = () => { return (Queued); }; -interface ProgressDownloadingProps { +interface ProgressProps { payload: TaskPayload; fullMode?: boolean; } -const ProgressDownloading: React.FC = ({ payload, fullMode }: ProgressDownloadingProps) => { +const ProgressDownloading: React.FC = ({ payload, fullMode }: ProgressProps) => { return {fullMode && "Downloading " @@ -41,12 +41,17 @@ const ProgressDownloading: React.FC = ({ payload, full ; }; -const ProgressInstalling: React.FC = () => { - return (Installing); +const ProgressInstalling: React.FC = ({ fullMode }: ProgressProps) => { + return + Installing + {fullMode && + <> – This could take a while... + } + ; }; const ProgressVerifying: React.FC = () => { return (Verifying); }; -export default PayloadProgress; \ No newline at end of file +export default PayloadProgress; diff --git a/src/components/ProfileIcon/ProfileIcon.module.css b/src/components/ProfileIcon/ProfileIcon.module.css new file mode 100644 index 0000000..f0c5f12 --- /dev/null +++ b/src/components/ProfileIcon/ProfileIcon.module.css @@ -0,0 +1,19 @@ +.icon { + aspect-ratio: 1; + + --icon-border-radius: 17%; + border-radius: var(--icon-border-radius); + + box-shadow: 2px 2px 6px 0px rgba(0, 0, 0, 0.4); +} + +.icon > img { + width: 100%; + height: 100%; + + border-radius: var(--icon-border-radius); + + --outline-width: 1px; + outline: var(--outline-width) solid rgba(255, 255, 255, 0.1); + outline-offset: calc(var(--outline-width) * -1); +} diff --git a/src/components/ProfileIcon/index.tsx b/src/components/ProfileIcon/index.tsx new file mode 100644 index 0000000..6c24475 --- /dev/null +++ b/src/components/ProfileIcon/index.tsx @@ -0,0 +1,15 @@ +import styles from "./ProfileIcon.module.css"; +import { processAssetUrl } from "@app/profiles/utils"; + +interface Props { + iconUrl: string; + className?: string; +} + +const ProfileIcon: React.FC = ({ iconUrl, className }: Props) => { + return
+ +
; +}; + +export default ProfileIcon; diff --git a/src/components/Queue/QueueEntry/QueueEntry.module.css b/src/components/Queue/QueueEntry/QueueEntry.module.css index 3b4b864..c38f57e 100644 --- a/src/components/Queue/QueueEntry/QueueEntry.module.css +++ b/src/components/Queue/QueueEntry/QueueEntry.module.css @@ -22,16 +22,6 @@ gap: 15px; } -.icon { - overflow: hidden; - display: flex; - justify-content: center; - align-items: center; - gap: 10px; - border-radius: 8px; - background: #000410; -} - .icon > * { width: 32px; height: 32px; @@ -50,10 +40,10 @@ line-height: normal; } -.info_header { +.infoHeader { color: #000; font-size: 14px; font-style: normal; font-weight: 500; line-height: normal; -} \ No newline at end of file +} diff --git a/src/components/Queue/QueueEntry/QueueEntryBanner.module.css b/src/components/Queue/QueueEntry/QueueEntryBanner.module.css index 8d39f95..b994b8e 100644 --- a/src/components/Queue/QueueEntry/QueueEntryBanner.module.css +++ b/src/components/Queue/QueueEntry/QueueEntryBanner.module.css @@ -12,19 +12,9 @@ display: none; } -.icon { - overflow: hidden; - display: flex; - justify-content: center; - align-items: center; - gap: 10px; - border-radius: 8px; - background: #000410; -} - .icon > * { - width: 42px; - height: 42px; + width: 64px; + height: 64px; } .info { @@ -39,10 +29,14 @@ line-height: normal; } -.info_header { +.infoHeader { color: #FFF; font-size: 24px; font-style: normal; font-weight: 600; line-height: normal; -} \ No newline at end of file +} + +.infoHeader > .tag { + opacity: 0.5; +} diff --git a/src/components/Queue/QueueEntry/Setlist.tsx b/src/components/Queue/QueueEntry/Setlist.tsx deleted file mode 100644 index f9aafe2..0000000 --- a/src/components/Queue/QueueEntry/Setlist.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { SetlistTask } from "@app/tasks/Processors/Setlist"; -import BaseQueue from "./base"; -import SetlistIcon from "@app/assets/SourceIcons/Official.png"; - -interface Props { - setlistTask: SetlistTask, - bannerMode: boolean, -} - -const SetlistQueue: React.FC = ({ setlistTask, bannerMode }: Props) => { - return } - versionChannel={setlistTask.version} - bannerMode={bannerMode} - />; -}; - -export default SetlistQueue; \ No newline at end of file diff --git a/src/components/Queue/QueueEntry/YARG.tsx b/src/components/Queue/QueueEntry/YARG.tsx deleted file mode 100644 index 48e2759..0000000 --- a/src/components/Queue/QueueEntry/YARG.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { YARGTask } from "@app/tasks/Processors/YARG"; -import BaseQueue from "./base"; -import StableYARGIcon from "@app/assets/StableYARGIcon.png"; -import NightlyYARGIcon from "@app/assets/NightlyYARGIcon.png"; -import { YARGChannels } from "@app/hooks/useYARGRelease"; - -interface Props { - yargTask: YARGTask, - bannerMode: boolean, -} - -const YARGQueue: React.FC = ({ yargTask, bannerMode }: Props) => { - const channelIconPath: { [key in YARGChannels]: string } = { - "stable": StableYARGIcon, - "nightly": NightlyYARGIcon - }; - - return } - version={yargTask.version} - versionChannel={yargTask.channel.toUpperCase()} - bannerMode={bannerMode} - />; -}; - -export default YARGQueue; \ No newline at end of file diff --git a/src/components/Queue/QueueEntry/base.tsx b/src/components/Queue/QueueEntry/index.tsx similarity index 59% rename from src/components/Queue/QueueEntry/base.tsx rename to src/components/Queue/QueueEntry/index.tsx index 00b6b0f..35bfae7 100644 --- a/src/components/Queue/QueueEntry/base.tsx +++ b/src/components/Queue/QueueEntry/index.tsx @@ -4,12 +4,12 @@ import stylesBanner from "./QueueEntryBanner.module.css"; interface Props { icon?: React.ReactNode; name?: string; - versionChannel?: string; - version?: string; + releaseName?: string; + tag?: string; bannerMode: boolean; } -const BaseQueue: React.FC = ({ icon, name, versionChannel, version, bannerMode }: Props) => { +const QueueEntry: React.FC = ({ icon, name, releaseName, tag, bannerMode }: Props) => { // Choose the right style let styles = stylesNormal; if (bannerMode) { @@ -20,8 +20,13 @@ const BaseQueue: React.FC = ({ icon, name, versionChannel, version, banne
{icon}
- {name} {version} - {versionChannel} + + {name} + + ({tag}) + + + {releaseName}
@@ -29,4 +34,4 @@ const BaseQueue: React.FC = ({ icon, name, versionChannel, version, banne
; }; -export default BaseQueue; \ No newline at end of file +export default QueueEntry; diff --git a/src/components/Setlist/CreditEntry/CreditEntry.module.css b/src/components/Setlist/CreditEntry/CreditEntry.module.css deleted file mode 100644 index db2864d..0000000 --- a/src/components/Setlist/CreditEntry/CreditEntry.module.css +++ /dev/null @@ -1,13 +0,0 @@ -.credit_entry { - display: flex; - align-items: center; - gap: 5px; -} - -.credit_text { - color: #333; - font-size: 14px; - font-style: normal; - font-weight: 500; - line-height: normal; -} \ No newline at end of file diff --git a/src/components/Setlist/CreditEntry/index.tsx b/src/components/Setlist/CreditEntry/index.tsx deleted file mode 100644 index 46a3ae4..0000000 --- a/src/components/Setlist/CreditEntry/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { SetlistCredit } from "@app/hooks/useSetlistRelease"; -import styles from "./CreditEntry.module.css"; -import { LinkIcon } from "@app/assets/Icons"; -import TooltipWrapper from "@app/components/TooltipWrapper"; - -interface Props { - creditEntry: SetlistCredit, -} - -const CreditEntry: React.FC = ({ creditEntry }: Props) => { - if (creditEntry.url.length == 0) { - return
- {creditEntry.name} -
; - } else { - return - - {creditEntry.name} - - ; - } -}; - -export default CreditEntry; \ No newline at end of file diff --git a/src/components/Setlist/SetlistButton/index.tsx b/src/components/Setlist/SetlistButton/index.tsx deleted file mode 100644 index b29beea..0000000 --- a/src/components/Setlist/SetlistButton/index.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { ButtonColor } from "../../Button"; -import { CheckmarkIcon, InstallingIcon, UpdateIcon } from "@app/assets/Icons"; -import { calculatePayloadPercentage } from "@app/tasks/payload"; -import PayloadProgress from "../../PayloadProgress"; -import Button from "@app/components/Button"; -import { DropdownButton, DropdownItem } from "@app/components/DropdownButton"; -import { SetlistStates, SetlistVersion } from "@app/hooks/useSetlistData"; - -interface SetlistButtonProps extends React.PropsWithChildren { - version: SetlistVersion, - style?: React.CSSProperties -} - -export function SetlistButton(props: SetlistButtonProps) { - const version = props.version; - - if (version.state === SetlistStates.NEW_UPDATE) { - const buttonChildren = <> - Update Setlist - ; - - return ; - } - - if (version.state === SetlistStates.DOWNLOADING) { - const buttonChildren = <> - - - ; - - return ; - } - - if (version.state === SetlistStates.AVAILABLE) { - const buttonChildren = <> - Downloaded - ; - - const dropdownChildren = <> - version.uninstall()}> - Uninstall - - ; - - return - - {buttonChildren} - ; - } - - if (version.state === SetlistStates.ERROR) { - const buttonChildren = <> - Error! - ; - - return ; - } - - return ; -} \ No newline at end of file diff --git a/src/components/Setlist/SetlistPage/index.tsx b/src/components/Setlist/SetlistPage/index.tsx deleted file mode 100644 index 0b15208..0000000 --- a/src/components/Setlist/SetlistPage/index.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { SetlistData } from "@app/hooks/useSetlistRelease"; -import styles from "./setlist.module.css"; -import { SetlistVersion } from "@app/hooks/useSetlistData"; -import { GenericBox, GenericBoxHeader, GenericBoxSlim } from "@app/components/GenericBox"; -import SongEntry from "@app/components/Setlist/SongEntry"; -import { InformationIcon, ChartersIcon, OrganizerIcon, DateIcon, SongIcon, TimeIcon } from "@app/assets/Icons"; -import CreditEntry from "@app/components/Setlist/CreditEntry"; -import { isConsideredNewRelease, millisToDisplayLength } from "@app/utils/timeFormat"; -import TooltipWrapper from "@app/components/TooltipWrapper"; -import { intlFormatDistance } from "date-fns"; -import NewsSection from "@app/components/NewsSection"; -import SortChanger, { SortType } from "@app/components/Setlist/SortChanger"; -import { useState } from "react"; -import sortArray from "sort-array"; -import { SetlistButton } from "@app/components/Setlist/SetlistButton"; - -interface Props { - version: SetlistVersion, - data: SetlistData, -} - -const SetlistPage: React.FC = ({ version, data }: Props) => { - // If there isn't a version, something went wrong - if (!version) { - console.log(version); - return

Error: No version.

; - } - - const [ sortType, setSortType ] = useState("title" as SortType); - - const newestSongRelease = data.songs.reduce((prev, curr) => - new Date(prev.releaseDate).getTime() > new Date(curr.releaseDate).getTime() ? prev : curr); - - return <> -
-
-
- setSortType(s)} /> - - - {sortArray(data.songs, { - by: "order", - order: sortType === "releaseDate" ? "desc" : "asc", - computed: { - order: i => { - const value = i[sortType]; - - // Speical case for release date - if (sortType === "releaseDate" && typeof value === "string") { - return new Date(value).getTime(); - } - - // Make sure strings are in all lowercase for proper sorting - if (typeof value === "string") { - return value.toLowerCase(); - } - - return value; - } - } - }).map(i => - - )} - - -
- -
-
- - - - - {data.locales["en-US"].title} - - - {data.locales["en-US"].description} - -
- - - - {new Intl.DateTimeFormat("en-US", { - year: "numeric", - month: "long", - day: "numeric", - }).format(new Date(data.releaseDate))} - - - - - {data.songs.length} songs - - - - - {millisToDisplayLength(data.songs.reduce( - (accumulator, currentValue) => accumulator + currentValue.length, - 0), true)} - - - - - {data.organizer} - -
-
- - - - - Charters - - -
- {data.credits.map(i => - - )} -
-
-
-
- ; -}; - -export default SetlistPage; \ No newline at end of file diff --git a/src/components/Setlist/SetlistPage/setlist.module.css b/src/components/Setlist/SetlistPage/setlist.module.css deleted file mode 100644 index 260b53a..0000000 --- a/src/components/Setlist/SetlistPage/setlist.module.css +++ /dev/null @@ -1,92 +0,0 @@ -.banner { - height: 250px; - flex-shrink: 0; - align-self: stretch; - - background-image: - url("/src/assets/Banner/OfficialLogo.png"), - linear-gradient(0deg, rgba(0, 0, 0, 0.25) 0%, rgba(0, 0, 0, 0.00) 100%), - url("/src/assets/Banner/OfficialBanner.png"); - background-repeat: no-repeat, repeat-x, no-repeat; - background-size: contain, contain, cover; - background-position: center, center, center; -} - -.main { - display: flex; - width: 100%; - min-height: calc(100% - 250px); - padding: 25px; - align-items: flex-start; - gap: 25px; - background: var(--white-background); - - align-items: stretch; -} - -.content { - flex: 1 0 0; - - display: flex; - flex-direction: column; - gap: 10px; -} - -.content_spacer { - height: 15px; -} - -.sidebar { - display: flex; - width: 300px; - flex-direction: column; - align-items: flex-start; - gap: 10px; - flex-shrink: 0; -} - -.song { - display: flex; - padding: 15px; - align-items: center; - gap: 10px; - align-self: stretch; - border-bottom: 1px solid rgba(0, 0, 0, 0.1); -} - -.song_title { - color: #333; - font-size: 14px; - font-style: normal; - font-weight: 600; - line-height: normal; -} - -.song_artist { - color: #333; - font-size: 14px; - font-style: normal; - font-weight: 400; - line-height: normal; - opacity: 0.5; -} - -.info_list { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 10px; -} - -.info_entry { - display: flex; - padding: 0px 1px; - align-items: center; - gap: 6px; - - color: #333; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: normal; -} \ No newline at end of file diff --git a/src/components/Setlist/SongEntry/SongEntry.module.css b/src/components/Setlist/SongEntry.module.css similarity index 52% rename from src/components/Setlist/SongEntry/SongEntry.module.css rename to src/components/Setlist/SongEntry.module.css index 2910742..62a627c 100644 --- a/src/components/Setlist/SongEntry/SongEntry.module.css +++ b/src/components/Setlist/SongEntry.module.css @@ -11,64 +11,54 @@ border-bottom: none; } -.track_container { +.trackContainer { display: flex; align-items: flex-start; gap: 10px; } -.track_title { - color: #333; +.trackTitle { + color: #41475F; font-size: 14px; - font-style: normal; font-weight: 600; - line-height: normal; } -.track_artist { - color: #333; +.trackArtist { + color: #666975; font-size: 14px; - font-style: normal; font-weight: 400; - line-height: normal; - opacity: 0.5; } -.extra_container { +.extraContainer { display: flex; align-items: center; gap: 5px; } -.extra_length { - color: #333; +.extraLength { + color: #666975; font-size: 12px; - font-style: normal; font-weight: 400; - line-height: normal; - opacity: 0.5; } .icon { + color: #666975; width: 14px; height: 14px; - opacity: 0.25; } -.new_badge { +.newBadge { display: flex; - padding: 3px 5px; - align-items: flex-start; - gap: 10px; - border-radius: 50px; - background: #FFE174; + padding: 2px 6px; + justify-content: center; + align-items: center; + gap: 5px; - color: #641E00; + border-radius: 50px; + background: #FCD548; + border: 2px solid #ffe071; + color: #4F2600; font-size: 8px; - font-style: normal; - font-weight: 700; - line-height: normal; + font-weight: 800; text-transform: uppercase; - - transform: translateY(2px); -} \ No newline at end of file +} diff --git a/src/components/Setlist/SongEntry.tsx b/src/components/Setlist/SongEntry.tsx new file mode 100644 index 0000000..a548bce --- /dev/null +++ b/src/components/Setlist/SongEntry.tsx @@ -0,0 +1,27 @@ +import { millisToDisplayLength } from "@app/utils/timeFormat"; +import { TimeIcon } from "@app/assets/Icons"; +import styles from "./SongEntry.module.css"; +import { SetlistSong } from "@app/profiles/types"; + +interface Props { + song: SetlistSong, + isNewSong: boolean, +} + +const SongEntry: React.FC = ({ song, isNewSong }: Props) => { + return
+
+ {song.title} + {song.artist} + {isNewSong && +
NEW
+ } +
+
+ {millisToDisplayLength(song.length)} + +
+
; +}; + +export default SongEntry; diff --git a/src/components/Setlist/SongEntry/index.tsx b/src/components/Setlist/SongEntry/index.tsx deleted file mode 100644 index 455e310..0000000 --- a/src/components/Setlist/SongEntry/index.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { millisToDisplayLength } from "@app/utils/timeFormat"; -import { TimeIcon } from "@app/assets/Icons"; -import styles from "./SongEntry.module.css"; - -interface Props { - title: string, - artist: string, - length: number, - newSong: boolean, -} - -const SongEntry: React.FC = ({ title, artist, length, newSong }: Props) => { - return
-
- {title} - {artist} - {newSong && -
NEW
- } -
-
- - {millisToDisplayLength(length)} -
-
; -}; - -export default SongEntry; \ No newline at end of file diff --git a/src/components/Setlist/SortChanger/SortChanger.module.css b/src/components/Setlist/SortChanger.module.css similarity index 66% rename from src/components/Setlist/SortChanger/SortChanger.module.css rename to src/components/Setlist/SortChanger.module.css index 37d3d26..48f9921 100644 --- a/src/components/Setlist/SortChanger/SortChanger.module.css +++ b/src/components/Setlist/SortChanger.module.css @@ -1,15 +1,13 @@ -button { - all: unset; -} - .container { display: flex; align-items: center; justify-content: right; - gap: 5px; + gap: 5px; } .item { + all: unset; + display: flex; padding: 7px 14px; justify-content: center; @@ -17,14 +15,12 @@ button { gap: 5px; border-radius: 50px; - background: #E9E9E9; + background: #E2E6F1; cursor: pointer; - color: black; - font-size: 12px; - font-style: normal; + color: #5C6170; + font-size: 14px; font-weight: 500; - line-height: normal; } .item svg { @@ -32,6 +28,6 @@ button { } .item[data-state="on"] { - background: black; - color: white; -} \ No newline at end of file + background: #41475F; + color: #FFF; +} diff --git a/src/components/Setlist/SortChanger/index.tsx b/src/components/Setlist/SortChanger.tsx similarity index 60% rename from src/components/Setlist/SortChanger/index.tsx rename to src/components/Setlist/SortChanger.tsx index 9b3fb7d..f60828f 100644 --- a/src/components/Setlist/SortChanger/index.tsx +++ b/src/components/Setlist/SortChanger.tsx @@ -1,20 +1,29 @@ -import { SetlistSong } from "@app/hooks/useSetlistRelease"; import styles from "./SortChanger.module.css"; import * as ToggleGroup from "@radix-ui/react-toggle-group"; import { DateIcon, NoteIcon, SongIcon, TimeIcon } from "@app/assets/Icons"; +import { SetlistSong } from "@app/profiles/types"; export type SortType = keyof SetlistSong; interface Props { - onChange: (sortType: SortType) => void; + sortType: SortType, + setSortType: React.Dispatch>; } -const SortChanger: React.FC = ({ onChange }: Props) => { +const SortChanger: React.FC = ({ sortType, setSortType }: Props) => { return onChange(value)}> + value={sortType} + onValueChange={(value: SortType | "") => { + // We need to force an update here because the toggle group will deselect (which we don't want) + if (value === "") { + setSortType(sortType); + return; + } + + setSortType(value); + }}> @@ -35,4 +44,4 @@ const SortChanger: React.FC = ({ onChange }: Props) => { ; }; -export default SortChanger; \ No newline at end of file +export default SortChanger; diff --git a/src/components/Setlist/index.tsx b/src/components/Setlist/index.tsx new file mode 100644 index 0000000..7502264 --- /dev/null +++ b/src/components/Setlist/index.tsx @@ -0,0 +1,61 @@ +import { Profile, SetlistMetadata } from "@app/profiles/types"; +import { localizeMetadata } from "@app/profiles/utils"; +import SortChanger, { SortType } from "./SortChanger"; +import { useState } from "react"; +import Box from "@app/components/Box"; +import sortArray from "sort-array"; +import SongEntry from "./SongEntry"; +import { isConsideredNewRelease } from "@app/utils/timeFormat"; + +interface Props { + profile: Profile +} + +const Setlist: React.FC = ({ profile }: Props) => { + const [ sortType, setSortType ] = useState("title"); + + if (profile.type !== "setlist") { + return <>; + } + + const metadata = localizeMetadata(profile, "en-US") as SetlistMetadata; + + const newestSongRelease = metadata.songs.reduce((prev, curr) => + Date.parse(prev.releaseDate) > Date.parse(curr.releaseDate) ? prev : curr); + + return <> + + + { + sortArray(metadata.songs, { + by: "order", + order: sortType === "releaseDate" ? "desc" : "asc", + computed: { + order: i => { + const value = i[sortType]; + + // Speical case for release date + if (sortType === "releaseDate" && typeof value === "string") { + return new Date(value).getTime(); + } + + // Make sure strings are in all lowercase for proper sorting + if (typeof value === "string") { + return value.toLowerCase(); + } + + return value; + } + } + }).map(i => + + ) + } + + ; +}; + +export default Setlist; diff --git a/src/components/Sidebar/Versions/Versions.module.css b/src/components/Sidebar/Profiles/List.module.css similarity index 76% rename from src/components/Sidebar/Versions/Versions.module.css rename to src/components/Sidebar/Profiles/List.module.css index 0a237bc..d5f085c 100644 --- a/src/components/Sidebar/Versions/Versions.module.css +++ b/src/components/Sidebar/Profiles/List.module.css @@ -15,7 +15,7 @@ font-size: 14px; font-weight: 700; text-transform: uppercase; - color: var(--sideBar_separator_color); + color: #434663; } .separator .right { @@ -25,8 +25,16 @@ } .add { - height: 15px; - color: var(--sideBar_separator_color); - opacity: 0.25; margin-left: auto; -} \ No newline at end of file +} + +.add > * { + height: 15px; + + transition: color 0.1s; + color: #434663; +} + +.add > *:hover { + color: #17E289; +} diff --git a/src/components/Sidebar/Profiles/List.tsx b/src/components/Sidebar/Profiles/List.tsx new file mode 100644 index 0000000..17b5e50 --- /dev/null +++ b/src/components/Sidebar/Profiles/List.tsx @@ -0,0 +1,64 @@ +import styles from "./List.module.css"; +import Separator from "./Separator"; +import { AddIcon } from "@app/assets/Icons"; +import { useProfileStore } from "@app/profiles/store"; +import { Link, NavLink } from "react-router-dom"; +import { Localized, localize } from "@app/utils/localized"; +import { Metadata } from "@app/profiles/types"; +import Selector from "./Selector"; + +interface Props { + isOffline: boolean; +} + +const ProfilesList: React.FC = ({ isOffline }: Props) => { + const profileStore = useProfileStore(); + + function activeProfileList(type: "application" | "setlist") { + const output = []; + + for (const activeProfile of profileStore.activeProfiles) { + const profile = activeProfile.profile; + if (profile.type !== type) { + continue; + } + + let name = activeProfile.displayName; + if (name === undefined) { + name = localize(profile.metadata as Localized, "name", "en-US"); + } + + const iconUrl = localize(profile.metadata as Localized, "iconUrl", "en-US"); + + output.push( + + + + ); + } + + return output; + } + + return
+ + {!isOffline && + + + + } + + {activeProfileList("application")} + + + {!isOffline && + + + + } + + {activeProfileList("setlist")} +
; +}; + +export default ProfilesList; diff --git a/src/components/Sidebar/Profiles/Selector.module.css b/src/components/Sidebar/Profiles/Selector.module.css new file mode 100644 index 0000000..19c3405 --- /dev/null +++ b/src/components/Sidebar/Profiles/Selector.module.css @@ -0,0 +1,90 @@ +.selector { + --border-radius: 8px; + --padding: 5px; + + padding: var(--padding); + padding-right: calc(var(--padding) * 2); + border-radius: calc(var(--border-radius) + var(--padding)); + + display: flex; + justify-content: space-between; + align-items: center; + align-self: stretch; + + background: transparent; + color: #B8BDD6; + + transition: background 0.1s; +} + +.left { + display: flex; + align-items: center; + gap: 10px; +} + +.tag { + border-radius: 50px; + + display: flex; + padding: 5px; + justify-content: center; + align-items: center; + gap: 7px; +} + +.tagInQueue { + border: 2px solid #FCD548; + color: #FCD548; + background: rgba(252, 213, 72, 0.10); +} + +.tagInQueue > * { + animation: 2s linear 0s infinite running spin; +} + +.tagUpdate { + border: 2px solid #17E289; + color: #17E289; + background: rgba(23, 226, 137, 0.10); + animation: 1s linear 0s infinite running fade; +} + +[aria-current="page"] > .selector, +.selector:hover { + background: #0D0F23; + color: #D4D9EE; +} + +.icon { + height: 42px; +} + +.text { + font-size: 16px; + font-weight: 600; + + overflow: hidden; + text-overflow: ellipsis; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@keyframes fade { + 0% { + opacity: 1; + } + 50% { + opacity: 0.25; + } + 100% { + opacity: 1; + } +} diff --git a/src/components/Sidebar/Profiles/Selector.tsx b/src/components/Sidebar/Profiles/Selector.tsx new file mode 100644 index 0000000..bc9be94 --- /dev/null +++ b/src/components/Sidebar/Profiles/Selector.tsx @@ -0,0 +1,43 @@ +import { ProfileFolderState, useProfileState } from "@app/hooks/useProfileState"; +import styles from "./Selector.module.css"; +import ProfileIcon from "@app/components/ProfileIcon"; +import { SidebarInQueueIcon, SidebarUpdateIcon } from "@app/assets/Icons"; + +interface Props { + name: string, + uuid: string, + iconUrl: string, +} + +const Selector: React.FC = ({ name, uuid, iconUrl }: Props) => { + const { + loading, + folderState, + currentTask + } = useProfileState(uuid); + + let tag = <>; + if (!loading) { + if (currentTask !== undefined) { + tag =
+ +
; + } else if (folderState === ProfileFolderState.FirstDownload || folderState === ProfileFolderState.UpdateRequired) { + tag =
+ +
; + } + } + + return
+
+ +
+ {name} +
+
+ {tag} +
; +}; + +export default Selector; diff --git a/src/components/Sidebar/Versions/Separator.tsx b/src/components/Sidebar/Profiles/Separator.tsx similarity index 61% rename from src/components/Sidebar/Versions/Separator.tsx rename to src/components/Sidebar/Profiles/Separator.tsx index 0dd512a..ce0f82c 100644 --- a/src/components/Sidebar/Versions/Separator.tsx +++ b/src/components/Sidebar/Profiles/Separator.tsx @@ -1,15 +1,15 @@ -import styles from "./Versions.module.css"; +import styles from "./List.module.css"; type Props = { name: string, children?: React.ReactNode, } -const VersionSeparator: React.FC = ({name, children}: Props) => { +const Separator: React.FC = ({name, children}: Props) => { return
{name}
{children}
; }; -export default VersionSeparator; \ No newline at end of file +export default Separator; diff --git a/src/components/Sidebar/Sidebar.module.css b/src/components/Sidebar/Sidebar.module.css index a25ef29..a0926be 100644 --- a/src/components/Sidebar/Sidebar.module.css +++ b/src/components/Sidebar/Sidebar.module.css @@ -1,14 +1,17 @@ .sidebar { - width: 300px; + width: var(--sideBarWidth); padding: 15px; - background: var(--sideBar_background); + background: var(--sideBarBackground); color: var(--primary); display: flex; flex-direction: column; flex-shrink: 0; gap: 50px; + + overflow-y: auto; + overflow-x: hidden; } .footer { @@ -48,9 +51,10 @@ .button { display: flex; - gap: 10px; - background: rgba(255, 255, 255, 0); + background: transparent; + color: #B8BDD6; + border-radius: 13px; transition: background 0.1s; @@ -58,7 +62,8 @@ [aria-current="page"] > .button, .button:hover { - background: rgba(255, 255, 255, 0.025); + background: #0D0F23; + color: #D4D9EE; } .button .icon { @@ -71,7 +76,6 @@ .button .icon > * { height: 20px; - color: var(--accent); } .button .text { @@ -80,4 +84,22 @@ align-items: center; font-size: 16px; font-weight: 600; -} \ No newline at end of file + gap: 12px; +} + +.updatedTag { + display: flex; + padding: 3px 6px; + justify-content: center; + align-items: center; + gap: 5px; + + border-radius: 50px; + background: #FCD548; + border: 2px solid #ffe071; + + color: #4F2600; + font-size: 10px; + font-weight: 800; + text-transform: uppercase; +} diff --git a/src/components/Sidebar/SidebarMenuButton.tsx b/src/components/Sidebar/SidebarMenuButton.tsx index fad59eb..77eac54 100644 --- a/src/components/Sidebar/SidebarMenuButton.tsx +++ b/src/components/Sidebar/SidebarMenuButton.tsx @@ -1,8 +1,8 @@ import styles from "./Sidebar.module.css"; interface Props { - icon?: React.ReactNode; - children?: React.ReactNode; + icon?: React.ReactNode, + children?: React.ReactNode } const SidebarMenuButton: React.FC = ({ icon, children }: Props) => { @@ -12,4 +12,4 @@ const SidebarMenuButton: React.FC = ({ icon, children }: Props) => {
; }; -export default SidebarMenuButton; \ No newline at end of file +export default SidebarMenuButton; diff --git a/src/components/Sidebar/Versions/Base.tsx b/src/components/Sidebar/Versions/Base.tsx deleted file mode 100644 index e842f0d..0000000 --- a/src/components/Sidebar/Versions/Base.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import ApplicationStyles from "./styles/Application.module.css"; -import SongStyles from "./styles/Song.module.css"; - -export enum VersionType { - "APPLICATION", - "SONG" -} - -interface Props { - type?: VersionType, - icon?: React.ReactNode; - programName?: string; - versionChannel?: string; - version?: string; - updateAvailable?: boolean; -} - -const styleType = { - [VersionType.APPLICATION]: ApplicationStyles, - [VersionType.SONG]: SongStyles -}; - -const BaseVersion: React.FC = ({ type = VersionType.APPLICATION, icon, programName, version, versionChannel, updateAvailable }: Props) => { - const styles = styleType[type]; - - return
-
{icon}
-
-
{versionChannel}
-
{programName}
-
-
{version}
-
; -}; - -export default BaseVersion; \ No newline at end of file diff --git a/src/components/Sidebar/Versions/List.tsx b/src/components/Sidebar/Versions/List.tsx deleted file mode 100644 index 9c73a66..0000000 --- a/src/components/Sidebar/Versions/List.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import YARGVersion from "./YARG"; -import styles from "./Versions.module.css"; -import SetlistVersion from "./Setlist"; -import VersionSeparator from "./Separator"; -import { AddIcon } from "@app/assets/Icons"; - -const VersionsList: React.FC = () => { - return
- - - - - - - - - -
; -}; - -export default VersionsList; \ No newline at end of file diff --git a/src/components/Sidebar/Versions/Setlist.tsx b/src/components/Sidebar/Versions/Setlist.tsx deleted file mode 100644 index bbf3772..0000000 --- a/src/components/Sidebar/Versions/Setlist.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { SetlistID, useSetlistRelease } from "@app/hooks/useSetlistRelease"; -import { SetlistStates, useSetlistData } from "@app/hooks/useSetlistData"; -import BaseVersion, { VersionType } from "./Base"; -import { NavLink } from "react-router-dom"; -import OfficialIcon from "@app/assets/SourceIcons/Official.png"; - -interface Props { - channel: SetlistID; -} - -const SetlistVersion: React.FC = ({ channel }: Props) => { - const { data: setlistData } = useSetlistRelease(channel); - const { state } = useSetlistData(setlistData, channel); - - return ( - - } // TO-DO: create a util/sourceIcon to get source icon from - type={VersionType.SONG} - programName={setlistData?.locales["en-US"].title} // TO-DO: catch the BCP 47 code - versionChannel={`${setlistData?.songs?.length} songs`} - updateAvailable={state === SetlistStates.NEW_UPDATE} - /> - - ); -}; - -export default SetlistVersion; \ No newline at end of file diff --git a/src/components/Sidebar/Versions/YARG.tsx b/src/components/Sidebar/Versions/YARG.tsx deleted file mode 100644 index 4309c20..0000000 --- a/src/components/Sidebar/Versions/YARG.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { YARGChannels, useYARGRelease } from "@app/hooks/useYARGRelease"; -import { YARGStates, useYARGVersion } from "@app/hooks/useYARGVersion"; -import BaseVersion from "./Base"; -import NightlyYARGIcon from "@app/assets/NightlyYARGIcon.png"; -import StableYARGIcon from "@app/assets/StableYARGIcon.png"; -import { NavLink } from "react-router-dom"; - -interface Props { - channel: YARGChannels -} - -const YARGVersion: React.FC = ({ channel }: Props) => { - const {data: releaseData} = useYARGRelease(channel); - const { state } = useYARGVersion(releaseData, channel); - - function getChannelIcon() { - switch (channel) { - case "stable": - return StableYARGIcon; - case "nightly": - return NightlyYARGIcon; - } - } - - function getChannelDisplayName() { - switch (channel) { - case "stable": - return "Stable"; - case "nightly": - return "Nightly"; - } - } - - return ( - - } - programName="YARG" - versionChannel={getChannelDisplayName()} - version={releaseData?.tag_name} - updateAvailable={state === YARGStates.NEW_UPDATE} - /> - - ); -}; - -export default YARGVersion; \ No newline at end of file diff --git a/src/components/Sidebar/Versions/styles/Application.module.css b/src/components/Sidebar/Versions/styles/Application.module.css deleted file mode 100644 index 7be7c88..0000000 --- a/src/components/Sidebar/Versions/styles/Application.module.css +++ /dev/null @@ -1,77 +0,0 @@ -.selector { - display: flex; - gap: 10px; - align-items: center; - --border-radius: 8px; - --padding: 5px; - - padding: var(--padding); - background: rgba(255, 255, 255, 0); - border-radius: calc(var(--border-radius) + var(--padding)); - - transition: background 0.1s; -} - -[aria-current="page"] > .selector, -.selector:hover { - background: rgba(255, 255, 255, 0.025); -} - -.selector > .icon { - height: 42px; - aspect-ratio: 1; - - display: flex; - justify-content: center; - align-items: center; -} - -.selector > .icon > * { - max-width: 100%; - max-height: 100%; - border-radius: var(--border-radius); -} - -.selector > .text > .channel { - font-size: 10px; - font-weight: 700; - text-transform: uppercase; - color: var(--primary-40); -} - -.selector > .text > .name { - font-size: 18px; - font-weight: 600; -} - -.selector > .version { - display: flex; - align-items: center; - gap: 5px; - - font-size: 14px; - font-weight: 700; - - margin-left: auto; - margin-right: 2px; - padding: 10px; - - color: var(--primary-75); - background: var(--primary-05); - border-radius: var(--border-radius); -} - -.selector > .version:empty { - display: none; -} - -.selector > .version[data-update-available="true"] { - background: var(--green-10); -} - -.selector > .version[data-update-available="true"]::after { - content: ""; - background: url("data:image/svg+xml,%3Csvg viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 6L6 4M6 4L4 6M6 4L6 8M1 6C0.999999 3.23858 3.23858 1 6 1C8.76142 1 11 3.23858 11 6C11 8.76142 8.76142 11 6 11C3.23858 11 1 8.76142 1 6Z' stroke='%2346E74F' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A"); - width: 15px; - aspect-ratio: 1; -} \ No newline at end of file diff --git a/src/components/Sidebar/Versions/styles/Song.module.css b/src/components/Sidebar/Versions/styles/Song.module.css deleted file mode 100644 index 5570b70..0000000 --- a/src/components/Sidebar/Versions/styles/Song.module.css +++ /dev/null @@ -1,73 +0,0 @@ -.selector { - display: flex; - gap: 10px; - align-items: center; - --border-radius: 8px; - --padding: 5px; - - padding: var(--padding); - background: rgba(255, 255, 255, 0); - border-radius: calc(var(--border-radius) + var(--padding)); - - transition: background 0.1s; -} - -[aria-current="page"] > .selector, -.selector:hover { - background: rgba(255, 255, 255, 0.025); -} - -.selector > .icon { - height: 30px; - aspect-ratio: 1; - - display: flex; - justify-content: center; - align-items: center; -} - -.selector > .icon > * { - max-width: 100%; - max-height: 100%; - border-radius: var(--border-radius); -} - -.selector > .text > .channel { - font-size: 10px; - font-weight: 700; - text-transform: uppercase; - color: var(--primary-40); -} - -.selector > .text > .name { - font-size: 16px; - font-weight: 600; -} - -.selector > .version { - display: none; - align-items: center; - gap: 5px; - - font-size: 14px; - font-weight: 700; - - margin-left: auto; - padding: 10px; - - color: var(--primary-75); - background: var(--primary-05); - border-radius: var(--border-radius); -} - -.selector > .version[data-update-available="true"] { - display: flex; - background: var(--green-10); -} - -.selector > .version[data-update-available="true"]::after { - content: ""; - background: url("data:image/svg+xml,%3Csvg viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 6L6 4M6 4L4 6M6 4L6 8M1 6C0.999999 3.23858 3.23858 1 6 1C8.76142 1 11 3.23858 11 6C11 8.76142 8.76142 11 6 11C3.23858 11 1 8.76142 1 6Z' stroke='%2346E74F' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A"); - width: 15px; - aspect-ratio: 1; -} \ No newline at end of file diff --git a/src/components/Sidebar/index.tsx b/src/components/Sidebar/index.tsx index 6d6c54f..4f3a60b 100644 --- a/src/components/Sidebar/index.tsx +++ b/src/components/Sidebar/index.tsx @@ -1,21 +1,28 @@ import styles from "./Sidebar.module.css"; -import { DiscordIcon, TwitterIcon, GithubIcon, HomeIcon, QueueIcon } from "@app/assets/Icons"; +import { DiscordIcon, TwitterIcon, GithubIcon, HomeIcon, QueueIcon, MarketplaceIcon } from "@app/assets/Icons"; import SidebarMenuButton from "./SidebarMenuButton"; import { NavLink } from "react-router-dom"; -import VersionsList from "./Versions/List"; -import { useEffect, useState } from "react"; -import { getVersion } from "@tauri-apps/api/app"; +import ProfilesList from "./Profiles/List"; import QueueStore from "@app/tasks/queue"; +import { useOfflineStatus } from "@app/hooks/useOfflineStatus"; +import useMarketIndex from "@app/hooks/useMarketIndex"; +import { settingsManager } from "@app/settings"; const Sidebar: React.FC = () => { - const [launcherVersion, setLauncherVersion] = useState(""); + const offlineStatus = useOfflineStatus(); const queue = QueueStore.useQueue(); - useEffect(() => { - (async () => { - setLauncherVersion(await getVersion()); - })(); - }, []); + const marketIndex = useMarketIndex(!offlineStatus.isOffline); + let isNewMarketplace = false; + + if (!marketIndex.isLoading && !marketIndex.isError && marketIndex.data !== undefined) { + try { + isNewMarketplace = Date.parse(marketIndex.data.lastUpdated) + > Date.parse(settingsManager.getCache("lastMarketplaceObserve")); + } catch { + // Ignore error in this case + } + } return
@@ -30,12 +37,23 @@ const Sidebar: React.FC = () => { Downloads {queue.size <= 0 ? "" : `(${queue.size})`} + {!offlineStatus.isOffline && + + }> + Marketplace + {isNewMarketplace && +
+ Updated! +
+ } +
+
+ }
- +
-
v{launcherVersion}
diff --git a/src/components/TitleBar/TitleBar.module.css b/src/components/TitleBar/TitleBar.module.css new file mode 100644 index 0000000..5ea5e2c --- /dev/null +++ b/src/components/TitleBar/TitleBar.module.css @@ -0,0 +1,62 @@ +.titleBar { + position: fixed; + inset: 0 0 auto 0; + + display: flex; + height: var(--titleBarHeight); + padding-left: 10px; + justify-content: space-between; + align-items: center; + align-self: stretch; + + background: #05060B; + color: #858BB5; +} + +.title { + display: flex; + align-items: center; + gap: 8px; + + pointer-events: none; +} + +.controls { + display: flex; + align-items: center; +} + +.version { + display: flex; + padding: 2px 5px; + justify-content: center; + align-items: center; + gap: 5px; + + border-radius: 33px; + border: 2px solid #262946; + + color: #A5AAD7; + font-size: 11px; + font-weight: 700; +} + +.control { + display: flex; + width: var(--titleBarHeight); + height: var(--titleBarHeight); + justify-content: center; + align-items: center; + + color: #858BB5; +} + +.control > * { + width: 12px; + height: 12px; +} + +.control:hover { + background: #262946; + color: #FFF; +} diff --git a/src/components/TitleBar/index.tsx b/src/components/TitleBar/index.tsx index 0c9cbef..c594b73 100644 --- a/src/components/TitleBar/index.tsx +++ b/src/components/TitleBar/index.tsx @@ -1,14 +1,23 @@ import { appWindow } from "@tauri-apps/api/window"; - -import styles from "./titlebar.module.css"; +import styles from "./TitleBar.module.css"; import { CloseIcon, MinimizeIcon } from "@app/assets/Icons"; import { TryCloseDialog } from "@app/dialogs/Dialogs/TryCloseDialog"; import { useCurrentTask } from "@app/tasks"; import { createAndShowDialog } from "@app/dialogs"; +import YARCLogo from "@app/assets/YARC.svg?react"; +import { useEffect, useState } from "react"; +import { getVersion } from "@tauri-apps/api/app"; const TitleBar: React.FC = () => { + const [launcherVersion, setLauncherVersion] = useState(""); const currentTask = useCurrentTask(); + useEffect(() => { + (async () => { + setLauncherVersion(await getVersion()); + })(); + }, []); + async function tryClose() { // If there is no download, just close if (!currentTask?.startedAt) { @@ -23,21 +32,27 @@ const TitleBar: React.FC = () => { } } - return
-
- YARC Launcher + return
+
+ + Launcher + {launcherVersion !== "" && +
+ {launcherVersion} +
+ }
-
-
appWindow.minimize()} className={styles.button}> +
+
appWindow.minimize()} className={styles.control}>
-
tryClose()} className={styles.button}> +
tryClose()} className={styles.control}>
; }; -export default TitleBar; \ No newline at end of file +export default TitleBar; diff --git a/src/components/TitleBar/titlebar.module.css b/src/components/TitleBar/titlebar.module.css deleted file mode 100644 index 1576ac1..0000000 --- a/src/components/TitleBar/titlebar.module.css +++ /dev/null @@ -1,45 +0,0 @@ -.title_bar { - background: var(--titleBar_background); - color: var(--titleBar_primary); - user-select: none; - - height: 30px; - position: fixed; - inset: 0 0 auto 0; - - display: flex; - align-items: center; - padding-left: 10px; -} - -.text { - display: flex; - gap: 10px; - color: var(--titleBar_accent); -} - -.text span:first-of-type { - color: var(--titleBar_primary); -} - -.buttons { - margin-left: auto; - display: flex; - height: 100%; -} - -.button { - height: 100%; - aspect-ratio: 1; - display: flex; - align-items: center; - justify-content: center; -} - -.button:hover { - background: var(--titleBar_accent); -} - -.button svg { - height: 12px; -} \ No newline at end of file diff --git a/src/dialogs/Dialogs/BaseDialog.module.css b/src/dialogs/Dialogs/BaseDialog.module.css index 2176e95..59779f0 100644 --- a/src/dialogs/Dialogs/BaseDialog.module.css +++ b/src/dialogs/Dialogs/BaseDialog.module.css @@ -26,4 +26,21 @@ .buttons > button { display: flex; flex: 1 0 0; -} \ No newline at end of file +} + +.box { + display: flex; + padding: 25px; + align-items: center; + gap: 10px; + align-self: stretch; + border-radius: 8px; + border: 1px solid rgba(0, 0, 0, 0.15); + background: rgba(255, 255, 255, 0.25); + + color: #000; + font-size: 16px; + font-style: normal; + font-weight: 300; + line-height: normal; +} diff --git a/src/dialogs/Dialogs/ErrorDialog.module.css b/src/dialogs/Dialogs/ErrorDialog.module.css deleted file mode 100644 index 809024a..0000000 --- a/src/dialogs/Dialogs/ErrorDialog.module.css +++ /dev/null @@ -1,16 +0,0 @@ -.stacktrace { - display: flex; - padding: 25px; - align-items: center; - gap: 10px; - align-self: stretch; - border-radius: 8px; - border: 1px solid rgba(0, 0, 0, 0.15); - background: rgba(255, 255, 255, 0.25); - - color: #000; - font-size: 16px; - font-style: normal; - font-weight: 300; - line-height: normal; -} \ No newline at end of file diff --git a/src/dialogs/Dialogs/ErrorDialog.tsx b/src/dialogs/Dialogs/ErrorDialog.tsx index 0b5b4d4..a5114d8 100644 --- a/src/dialogs/Dialogs/ErrorDialog.tsx +++ b/src/dialogs/Dialogs/ErrorDialog.tsx @@ -1,6 +1,6 @@ import Button, { ButtonColor } from "@app/components/Button"; import { BaseDialog } from "./BaseDialog"; -import styles from "./ErrorDialog.module.css"; +import styles from "./BaseDialog.module.css"; import { error as LogError } from "tauri-plugin-log-api"; import { serializeError } from "serialize-error"; import { closeDialog } from ".."; @@ -8,7 +8,7 @@ import { closeDialog } from ".."; export class ErrorDialog extends BaseDialog> { constructor(props: Record) { super(props); - + try { LogError( JSON.stringify(serializeError(props.error)) @@ -19,13 +19,22 @@ export class ErrorDialog extends BaseDialog> { } getInnerContents() { + let message: string; + if (this.props.error instanceof Error) { + message = JSON.stringify(serializeError(this.props.error)); + } else if (typeof this.props.error === "string") { + message = this.props.error; + } else { + message = JSON.stringify(this.props.error); + } + return <>

- A fatal error has occured. If you don't know what happened, please report this to our Discord + A fatal error has occured. If this continues to happen, please report this to our Discord or GitHub immediately. Make sure to send the below text:

-
- { this.props.error instanceof Error && "message" in this.props.error ? this.props.error.message as string : JSON.stringify(serializeError(this.props.error)) } +
+ {message}
; } @@ -36,7 +45,7 @@ export class ErrorDialog extends BaseDialog> { getButtons() { return <> - + ; } -} \ No newline at end of file +} diff --git a/src/dialogs/Dialogs/InstallFolderDialog.module.css b/src/dialogs/Dialogs/InstallFolderDialog.module.css deleted file mode 100644 index 8ba56de..0000000 --- a/src/dialogs/Dialogs/InstallFolderDialog.module.css +++ /dev/null @@ -1,59 +0,0 @@ -.folder_container { - display: flex; - padding: 25px; - justify-content: space-between; - align-items: center; - align-self: stretch; - - border-radius: 8px; - border: 1px solid #DADADA; - - cursor: pointer; - user-select: none; -} - -.folder_container:hover { - background: rgba(0, 0, 0, 0.02); -} - -.folder_info { - display: flex; - align-items: center; - gap: 10px; - - color: rgba(0, 0, 0, 0.75); - font-size: 14px; - font-style: normal; - font-weight: 600; - line-height: normal; - text-transform: uppercase; -} - -.folder_extra { - color: rgba(0, 0, 0, 0.25); - font-size: 14px; - font-style: normal; - font-weight: 600; - line-height: normal; - text-transform: uppercase; -} - -.warning_box { - margin-top: 10px; - - display: flex; - padding: 15px; - align-items: center; - gap: 10px; - align-self: stretch; - - border-radius: var(--web-radius, 8px); - border: 1px solid #FFA800; - background: rgba(255, 168, 0, 0.10); - - color: #7C5200; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: normal; -} \ No newline at end of file diff --git a/src/dialogs/Dialogs/InstallFolderDialog.tsx b/src/dialogs/Dialogs/InstallFolderDialog.tsx deleted file mode 100644 index 70e2bff..0000000 --- a/src/dialogs/Dialogs/InstallFolderDialog.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import Button, { ButtonColor } from "@app/components/Button"; -import { BaseDialog } from "./BaseDialog"; -import { open } from "@tauri-apps/api/dialog"; -import styles from "./InstallFolderDialog.module.css"; -import { DriveIcon, WarningIcon } from "@app/assets/Icons"; -import { invoke } from "@tauri-apps/api"; -import { closeDialog } from ".."; - -interface State { - path?: string; - empty: boolean; -} - -export class InstallFolderDialog extends BaseDialog { - constructor(props: Record) { - super(props); - this.state = { - path: undefined, - empty: true - }; - - // Load the default path - (async () => { - const path = await invoke("get_download_location") as string; - this.setState(() => ({ - path: path, - empty: true - })); - })(); - } - - getInnerContents() { - return <> -

- Please choose an installation folder. This folder should not be a folder that is synced with the cloud. - If you do not know what folder to choose, just click "Okay". -

-
this.askForFolder()}> -
- - {typeof this.state.path === "string" ? this.state.path : "Loading..."} -
-
- -
-
- {!this.state.empty ? -
- The folder selected is not empty! Make sure it doesn't have any files in it. -
- : "" - } - ; - } - - private async askForFolder() { - const select = await open({ - directory: true - }); - - if (typeof select === "string") { - const path: string = select; - const empty: boolean = await invoke("is_dir_empty", { path: path }); - - this.setState(() => ({ - path: path, - empty: empty - })); - } - } - - getTitle() { - return <>Install Folder; - } - - getButtons() { - return <> - - - ; - } -} \ No newline at end of file diff --git a/src/dialogs/Dialogs/LeavingLauncherDialog.tsx b/src/dialogs/Dialogs/LeavingLauncherDialog.tsx new file mode 100644 index 0000000..f780247 --- /dev/null +++ b/src/dialogs/Dialogs/LeavingLauncherDialog.tsx @@ -0,0 +1,40 @@ +import Button, { ButtonColor } from "@app/components/Button"; +import { BaseDialog } from "./BaseDialog"; +import { closeDialog } from ".."; +import styles from "./BaseDialog.module.css"; +import { openUrl } from "@app/utils/safeUrl"; + +export class LeavingLauncherDialog extends BaseDialog> { + constructor(props: Record) { + super(props); + } + + getInnerContents() { + return <> +

+ This link is taking you to the following website. Make sure you trust it before going to there! +

+
+ {this.props.url as string} +
+ ; + } + + getTitle() { + return <>Leaving Launcher; + } + + getButtons() { + return <> + + + ; + } +} diff --git a/src/dialogs/Dialogs/OfflineDialog.tsx b/src/dialogs/Dialogs/OfflineDialog.tsx new file mode 100644 index 0000000..9d7e3b7 --- /dev/null +++ b/src/dialogs/Dialogs/OfflineDialog.tsx @@ -0,0 +1,30 @@ +import Button, { ButtonColor } from "@app/components/Button"; +import { BaseDialog } from "./BaseDialog"; +import { closeDialog } from ".."; + +export class OfflineDialog extends BaseDialog> { + constructor(props: Record) { + super(props); + } + + getInnerContents() { + return <> +

+ The YARC Launcher cannot connect to the internet and is starting in offline mode. + If your internet connection returns, restart the launcher to go back into online mode. +

+ ; + } + + getTitle() { + return <>Offline Mode; + } + + getButtons() { + return <> + + ; + } +} diff --git a/src/dialogs/Dialogs/TryCloseDialog.tsx b/src/dialogs/Dialogs/TryCloseDialog.tsx index 68c8dda..d9171ba 100644 --- a/src/dialogs/Dialogs/TryCloseDialog.tsx +++ b/src/dialogs/Dialogs/TryCloseDialog.tsx @@ -22,10 +22,12 @@ export class TryCloseDialog extends BaseDialog> { getButtons() { return <> - - + ; } -} \ No newline at end of file +} diff --git a/src/dialogs/Dialogs/UninstallBeforeRemoveDialog.tsx b/src/dialogs/Dialogs/UninstallBeforeRemoveDialog.tsx new file mode 100644 index 0000000..2295a64 --- /dev/null +++ b/src/dialogs/Dialogs/UninstallBeforeRemoveDialog.tsx @@ -0,0 +1,31 @@ +import Button, { ButtonColor } from "@app/components/Button"; +import { BaseDialog } from "./BaseDialog"; +import { closeDialog } from ".."; + +export class UninstallBeforeDeleteDialog extends BaseDialog> { + constructor(props: Record) { + super(props); + } + + getInnerContents() { + return <> +

+ Before you delete the profile, you must first uninstall the application/setlist. + Although, you can always add the profile back in the future, this cannot be undone + and all profile settings will be deleted (application data will remain untouched). +

+ ; + } + + getTitle() { + return <>Cannot Delete Profile; + } + + getButtons() { + return <> + + ; + } +} diff --git a/src/dialogs/dialogUtil.ts b/src/dialogs/dialogUtil.ts deleted file mode 100644 index 5bed11f..0000000 --- a/src/dialogs/dialogUtil.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { invoke } from "@tauri-apps/api"; -import { InstallFolderDialog } from "./Dialogs/InstallFolderDialog"; -import { ErrorDialog } from "./Dialogs/ErrorDialog"; -import { createAndShowDialog } from "."; - -export async function showInstallFolderDialog() { - if (!await invoke("is_initialized")) { - const dialogOutput = await createAndShowDialog(InstallFolderDialog); - - if (dialogOutput === "cancel") { - return false; - } else { - try { - await invoke("set_download_location", { - path: dialogOutput - }); - } catch { - return false; - } - } - } - - return true; -} - -export async function showErrorDialog(error: string) { - await createAndShowDialog(ErrorDialog, { error: error }); -} \ No newline at end of file diff --git a/src/dialogs/index.ts b/src/dialogs/index.ts index 6c57257..4d48821 100644 --- a/src/dialogs/index.ts +++ b/src/dialogs/index.ts @@ -1,6 +1,7 @@ import { Component } from "react"; import { useStore } from "zustand"; import { createStore } from "zustand/vanilla"; +import { ErrorDialog } from "./Dialogs/ErrorDialog"; type DialogStore = { open: boolean, @@ -13,9 +14,9 @@ const store = createStore( () => ({open: false}) ); -export const createAndShowDialog = async (content: typeof Component, props?: Record): Promise => { +export const createAndShowDialog = async (content: typeof Component, props?: Record): Promise => { const current = store.getState(); - if(current.open) return; + if (current.open) return; store.setState({ content, @@ -48,4 +49,9 @@ export const setDialogOpen = (open: boolean) => { store.setState({ open }); -}; \ No newline at end of file +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export async function showErrorDialog(error: any) { + await createAndShowDialog(ErrorDialog, { error }); +} diff --git a/src/hooks/useMarketIndex.ts b/src/hooks/useMarketIndex.ts new file mode 100644 index 0000000..8c5ccd2 --- /dev/null +++ b/src/hooks/useMarketIndex.ts @@ -0,0 +1,13 @@ +import { MarketplaceIndex } from "@app/profiles/marketplace"; +import { useQuery } from "@tanstack/react-query"; + +const useMarketIndex = (enabled?: boolean) => { + return useQuery({ + enabled: enabled, + queryKey: ["MarketIndex"], + queryFn: async (): Promise => await fetch("https://releases.yarg.in/profiles/") + .then(res => res.json()) + }); +}; + +export default useMarketIndex; diff --git a/src/hooks/useOfflineStatus.ts b/src/hooks/useOfflineStatus.ts new file mode 100644 index 0000000..0bcc96c --- /dev/null +++ b/src/hooks/useOfflineStatus.ts @@ -0,0 +1,16 @@ +import { create } from "zustand"; + +interface OfflineStore { + isOffline: boolean; + + setOffline: (isOffline: boolean) => void; +} + +export const useOfflineStatus = create()((set) => ({ + isOffline: false, + setOffline: (isOffline) => { + set({ + isOffline: isOffline + }); + } +})); diff --git a/src/hooks/useProfileState.ts b/src/hooks/useProfileState.ts new file mode 100644 index 0000000..fed7375 --- /dev/null +++ b/src/hooks/useProfileState.ts @@ -0,0 +1,134 @@ +import { createAndShowDialog } from "@app/dialogs"; +import { UninstallBeforeDeleteDialog } from "@app/dialogs/Dialogs/UninstallBeforeRemoveDialog"; +import { downloadAndInstall, launch, openInstallFolder, uninstall } from "@app/profiles/actions"; +import { useDirectories } from "@app/profiles/directories"; +import { useProfileStore } from "@app/profiles/store"; +import { ActiveProfile } from "@app/profiles/types"; +import { getPathForProfile } from "@app/profiles/utils"; +import { useTask } from "@app/tasks"; +import { IBaseTask } from "@app/tasks/Processors/base"; +import { invoke } from "@tauri-apps/api"; +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; + +export enum ProfileFolderState { + Error = 0, + UpToDate = 1, + UpdateRequired = 2, + FirstDownload = 3 +} + +export interface ProfileState { + loading: boolean, + + activeProfile: ActiveProfile, + profilePath: string, + + folderState: ProfileFolderState, + currentTask?: IBaseTask, + + downloadAndInstall: () => Promise, + uninstall: () => Promise, + launch: () => Promise, + openInstallFolder: () => Promise, + deleteProfile: () => Promise, +} + +export const useProfileState = (profileUUID: string): ProfileState => { + const directories = useDirectories(); + const profiles = useProfileStore(); + const navigate = useNavigate(); + + const [loading, setLoading] = useState(true); + const [profilePath, setProfilePath] = useState(""); + + const [folderState, setFolderState] = useState(0); + const currentTask = useTask(profileUUID); + + const activeProfile = profiles.getProfileByUUID(profileUUID); + if (activeProfile === undefined) { + throw new Error("Undefined profile"); + } + + // Initialize + useEffect(() => { + // Set everything to default values + // This is hacky, but there's not much else we can do lol + setLoading(true); + setProfilePath(""); + setFolderState(0); + + // If the important directories aren't loaded yet, wait for them to + if (directories.importantDirs === undefined) { + return; + } + + (async () => { + const path = await getPathForProfile(directories, activeProfile); + + const result = await invoke("profile_folder_state", { + path: path, + wantedTag: activeProfile.version.tag + }) as ProfileFolderState; + + setFolderState(result); + setProfilePath(path); + setLoading(false); + })(); + }, [directories, profileUUID, activeProfile.version, currentTask]); + + return { + loading, + + activeProfile, + profilePath, + + folderState, + currentTask, + + downloadAndInstall: async () => { + if (loading) { + return; + } + + await downloadAndInstall(activeProfile, profilePath); + }, + uninstall: async () => { + if (loading) { + return; + } + + await uninstall(activeProfile, profilePath); + }, + launch: async () => { + if (loading || activeProfile.profile.type === "setlist") { + return; + } + + activeProfile.lastPlayed = new Date().toISOString(); + await profiles.updateProfile(activeProfile); + + await launch(activeProfile, profilePath); + }, + openInstallFolder: async () => { + if (loading) { + return; + } + + await openInstallFolder(activeProfile, profilePath); + }, + deleteProfile: async () => { + if (loading) { + return; + } + + if (folderState !== ProfileFolderState.FirstDownload) { + createAndShowDialog(UninstallBeforeDeleteDialog); + return; + } + + navigate("/"); + await profiles.removeProfile(activeProfile.uuid); + } + }; +}; diff --git a/src/hooks/useSetlistData.ts b/src/hooks/useSetlistData.ts deleted file mode 100644 index 1982985..0000000 --- a/src/hooks/useSetlistData.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { useSetlistState } from "@app/stores/SetlistStateStore"; -import { SetlistData } from "./useSetlistRelease"; -import { SetlistDownload, SetlistUninstall } from "@app/tasks/Processors/Setlist"; -import { useEffect } from "react"; -import { invoke } from "@tauri-apps/api/tauri"; -import { showErrorDialog, showInstallFolderDialog } from "@app/dialogs/dialogUtil"; -import { addTask, useTask } from "@app/tasks"; -import { TaskPayload, usePayload } from "@app/tasks/payload"; - -export enum SetlistStates { - "AVAILABLE", - "DOWNLOADING", - "ERROR", - "LOADING", - "NEW_UPDATE" -} - -export type SetlistVersion = { - state: SetlistStates, - download: () => Promise, - uninstall: () => Promise, - payload?: TaskPayload -} - -export const useSetlistData = (setlistData: SetlistData | undefined, setlistId: string): SetlistVersion => { - const { state, setState } = useSetlistState(setlistData?.version); - const task = useTask("setlist", setlistId); - const payload = usePayload(task?.taskUUID); - - useEffect(() => { - (async () => { - if (state || !setlistData) return; - - const exists = await invoke("exists", { - appName: "official_setlist", - version: setlistData.version, - profile: setlistData.id - }); - - setState(exists ? SetlistStates.AVAILABLE : SetlistStates.NEW_UPDATE); - })(); - }, [setlistData]); - - // If we don't have a release data yet, return a dummy loading version; - if (!setlistData) { - return { - state, - download: async () => {}, - uninstall: async () => {}, - }; - } - - const download = async () => { - if (!setlistData || state === SetlistStates.DOWNLOADING) return; - - // Ask for a download location (if required) - if (!await showInstallFolderDialog()) { - // Skip if the dialog is closed or it errors - return; - } - - setState(SetlistStates.DOWNLOADING); - - try { - const downloader = new SetlistDownload( - setlistData.downloads, - setlistData.id, - setlistData.version, - () => { setState(SetlistStates.AVAILABLE); } - ); - - addTask(downloader); - } catch (e) { - setState(SetlistStates.ERROR); - - showErrorDialog(e as string); - console.error(e); - } - }; - - const uninstall = async () => { - if (!setlistData || state === SetlistStates.DOWNLOADING) return; - - // You can't uninstall if the launcher is not initialized - if (!await invoke("is_initialized")) return; - - setState(SetlistStates.DOWNLOADING); - - try { - const downloader = new SetlistUninstall( - setlistData.id, - setlistData.version, - () => { setState(SetlistStates.NEW_UPDATE); } - ); - - addTask(downloader); - } catch (e) { - setState(SetlistStates.ERROR); - - showErrorDialog(e as string); - console.error(e); - } - }; - - return { state, download, uninstall, payload }; -}; \ No newline at end of file diff --git a/src/hooks/useSetlistRelease.ts b/src/hooks/useSetlistRelease.ts deleted file mode 100644 index 7443863..0000000 --- a/src/hooks/useSetlistRelease.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; - -export interface SetlistSong { - title: string, - artist: string, - length: number, - releaseDate: string, - category?: string, -} - -export interface SetlistCredit { - name: string, - url: string, -} - -export interface SetlistData { - id: string, - version: string, - releaseDate: string, - downloads: string[], - locales: { - [language: string]: { - title: string, - description: string, - } - }, - songs: SetlistSong[], - organizer: string, - credits: SetlistCredit[] -} - -export type SetlistID = "official"; - -export const useSetlistRelease = (id: SetlistID) => { - return useQuery({ - queryKey: ["Setlist", id], - queryFn: async (): Promise => await fetch( - `https://raw.githubusercontent.com/YARC-Official/Official-Setlist-Public/master/setlists/${id}.json`) - .then(res => res.json()) - }); -}; \ No newline at end of file diff --git a/src/hooks/useYARGRelease.ts b/src/hooks/useYARGRelease.ts deleted file mode 100644 index 42dc9a5..0000000 --- a/src/hooks/useYARGRelease.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { Endpoints } from "@octokit/types"; -import { OsType } from "@tauri-apps/api/os"; - -export type YARGChannels = "stable" | "nightly"; - -type ReleaseData = Endpoints["GET /repos/{owner}/{repo}/releases/latest"]["response"]["data"]; -export type ExtendedReleaseData = ReleaseData & { - channel: YARGChannels -}; - -export const useYARGRelease = (channel: YARGChannels) => { - const repositoryName = { - "stable": "YARG", - "nightly": "YARG-BleedingEdge" - }; - - return useQuery({ - queryKey: ["YARG", channel], - queryFn: async (): Promise => await fetch( - `https://api.github.com/repos/YARC-Official/${repositoryName[channel]}/releases/latest`) - .then(res => res.json()), - select: (data): ExtendedReleaseData => ({ ...data, channel: channel }) - }); -}; - -export const getYARGReleaseZip = (releaseData: ReleaseData, platformType: OsType) => { - const suffixesPerPlatform: {[key in OsType]: string[]} = { - "Windows_NT": ["Windows-x64.zip"], - "Darwin": ["MacOS-Universal.zip"], - "Linux": ["Linux-x86_64.zip", "Linux-x64.zip"], - }; - - const platformSuffixes = suffixesPerPlatform[platformType]; - - const asset = releaseData.assets.find(asset => { - return platformSuffixes.find(suffix => asset.name.endsWith(suffix)); - }); - - if(asset) return asset.browser_download_url; - - // Otherwise, the platform is not supported! - throw new Error(`Platform of type "${platformType}" is not supported in release "${releaseData.tag_name}"!`); -}; - -export const getYARGReleaseSigFromZipURL = (releaseData: ReleaseData, zipUrl: string) => { - const sigAssetName = zipUrl.split("/").slice(-1) + ".sig"; - - const asset = releaseData.assets.find(asset => asset.name === sigAssetName); - - if(asset) return asset.browser_download_url; - - // Otherwise, there's no signature - console.warn(`Failed to find signature file "${sigAssetName}" in release "${releaseData.tag_name}"!`); - return undefined; -}; \ No newline at end of file diff --git a/src/hooks/useYARGVersion.ts b/src/hooks/useYARGVersion.ts deleted file mode 100644 index abe884f..0000000 --- a/src/hooks/useYARGVersion.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { useEffect } from "react"; -import { ExtendedReleaseData, getYARGReleaseZip, getYARGReleaseSigFromZipURL, YARGChannels } from "./useYARGRelease"; -import { invoke } from "@tauri-apps/api/tauri"; -import { type } from "@tauri-apps/api/os"; -import { useYARGState } from "@app/stores/YARGStateStore"; -import { YARGDownload, YARGUninstall } from "@app/tasks/Processors/YARG"; -import { showErrorDialog, showInstallFolderDialog } from "@app/dialogs/dialogUtil"; -import { addTask, useTask } from "@app/tasks"; -import { usePayload, TaskPayload } from "@app/tasks/payload"; - -export enum YARGStates { - "AVAILABLE", - "DOWNLOADING", - "ERROR", - "PLAYING", - "LOADING", - "NEW_UPDATE" -} - -export type YARGVersion = { - state: YARGStates, - play: () => Promise, - download: () => Promise, - uninstall: () => Promise, - revealFolder: () => Promise, - payload?: TaskPayload -} - -export const useYARGVersion = (releaseData: ExtendedReleaseData | undefined, profileName: YARGChannels): YARGVersion => { - // Initialize hooks before returning - const { state, setState } = useYARGState(releaseData?.tag_name); - const task = useTask("yarg", profileName); - const payload = usePayload(task?.taskUUID); - - useEffect(() => { - (async () => { - if (state || !releaseData) return; - - const exists = await invoke("exists", { - appName: "yarg", - version: releaseData.tag_name, - profile: profileName - }); - - setState(exists ? YARGStates.AVAILABLE : YARGStates.NEW_UPDATE); - })(); - }, [releaseData]); - - // If we don't have a release data yet, return a dummy loading version; - if (!releaseData) { - return { - state, - play: async () => {}, - download: async () => {}, - uninstall: async () => {}, - revealFolder: async () => {}, - }; - } - - const play = async () => { - if (!releaseData) return; - - setState(YARGStates.LOADING); - - try { - await invoke("launch", { - appName: "yarg", - version: releaseData.tag_name, - profile: profileName - }); - - setState(YARGStates.PLAYING); - - // As we don't have a way to check if the YARG game process is closed, we set a timer to avoid locking the state to PLAYING - setTimeout(() => { - setState(YARGStates.AVAILABLE); - }, 10 * 1000); - } catch (e) { - setState(YARGStates.ERROR); - - showErrorDialog(e as string); - console.error(e); - } - }; - - const download = async () => { - if (!releaseData || state === YARGStates.DOWNLOADING) return; - - // Ask for a download location (if required) - if (!await showInstallFolderDialog()) { - // Skip if the dialog is closed or it errors - return; - } - - setState(YARGStates.DOWNLOADING); - - try { - const platformType = await type(); - const zipUrl = getYARGReleaseZip(releaseData, platformType); - const sigUrl = getYARGReleaseSigFromZipURL(releaseData, zipUrl); - - const downloader = new YARGDownload( - zipUrl, - sigUrl, - releaseData.channel, - releaseData.tag_name, - profileName, - () => { setState(YARGStates.AVAILABLE); } - ); - - addTask(downloader); - } catch (e) { - setState(YARGStates.ERROR); - - showErrorDialog(e as string); - console.error(e); - } - }; - - const uninstall = async () => { - if (!releaseData || state === YARGStates.DOWNLOADING) return; - - // You can't uninstall if the launcher is not initialized - if (!await invoke("is_initialized")) return; - - setState(YARGStates.DOWNLOADING); - - try { - const downloader = new YARGUninstall( - releaseData.channel, - releaseData.tag_name, - profileName, - () => { setState(YARGStates.NEW_UPDATE); } - ); - - addTask(downloader); - } catch (e) { - setState(YARGStates.ERROR); - - showErrorDialog(e as string); - console.error(e); - } - }; - - const revealFolder = async () => { - if (!releaseData) return; - - try { - await invoke("reveal_folder", { - appName: "yarg", - version: releaseData.tag_name, - profile: profileName - }); - } catch (e) { - showErrorDialog(e as string); - console.error(e); - } - }; - - return { state, play, download, uninstall, revealFolder, payload }; -}; \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index 1a85882..15a332d 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -12,6 +12,7 @@ import { ErrorScreen, onError } from "./routes/ErrorScreen"; import { error as logError } from "tauri-plugin-log-api"; import { serializeError } from "serialize-error"; import LoadingScreen from "./components/LoadingScreen"; +import Onboarding from "./components/Onboarding"; window.addEventListener("error", event => { logError(JSON.stringify(serializeError(event))); @@ -19,6 +20,8 @@ window.addEventListener("error", event => { const App: React.FC = () => { const [error, setError] = useState(null); + const [showBody, setShowBody] = useState(false); + const [onboarding, setOnboarding] = useState(false); // Show error screen if (error) { @@ -36,17 +39,24 @@ const App: React.FC = () => { // Show main screen return - - + + + - + {onboarding && + + } + + {showBody && + + } ; }; -ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(); \ No newline at end of file +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(); diff --git a/src/profiles/actions.ts b/src/profiles/actions.ts new file mode 100644 index 0000000..3e9c30c --- /dev/null +++ b/src/profiles/actions.ts @@ -0,0 +1,86 @@ +import { DownloadAndInstallTask } from "@app/tasks/Processors/DownloadAndInstallTask"; +import { ActiveProfile } from "./types"; +import { addTask } from "@app/tasks"; +import { UninstallTask } from "@app/tasks/Processors/UninstallTask"; +import { getOS } from "@app/utils/os"; +import { invoke } from "@tauri-apps/api"; +import { useDirectories } from "./directories"; +import { showErrorDialog } from "@app/dialogs"; +import { useOfflineStatus } from "@app/hooks/useOfflineStatus"; +import { settingsManager } from "@app/settings"; + +export const downloadAndInstall = async (profile: ActiveProfile, profilePath: string, onFinish?: () => void): Promise => { + const directories = useDirectories.getState(); + if (directories.importantDirs === undefined) { + return; + } + + const tempFolder = directories.importantDirs.tempFolder; + const task = new DownloadAndInstallTask(profile, profilePath, tempFolder, onFinish); + addTask(task); +}; + +export const uninstall = async (profile: ActiveProfile, profilePath: string, onFinish?: () => void): Promise => { + const directories = useDirectories.getState(); + if (directories.importantDirs === undefined) { + return; + } + + const task = new UninstallTask(profile, profilePath, onFinish); + addTask(task); +}; + +export const launch = async (activeProfile: ActiveProfile, profilePath: string): Promise => { + if (activeProfile.profile.type !== "application") { + showErrorDialog(`Cannot launch profile of type "${activeProfile.profile.type}"!`); + return; + } + + const os = await getOS(); + const launchOptions = activeProfile.version.launchOptions?.[os]; + if (launchOptions === undefined) { + showErrorDialog(`Launch options not configured on profile for "${os}"!`); + return; + } + + let customArguments: string[] = []; + if (activeProfile.launchArguments.trim().length > 0) { + customArguments = activeProfile.launchArguments.trim().split(" "); + } + + const otherArguments: string[] = []; + + if (launchOptions.offlineArgument !== undefined && useOfflineStatus.getState().isOffline) { + otherArguments.push(launchOptions.offlineArgument); + } + + if (launchOptions.downloadLocationArgument !== undefined) { + otherArguments.push(launchOptions.downloadLocationArgument); + otherArguments.push(settingsManager.getCache("downloadLocation")); + } + + try { + await invoke("launch_profile", { + profilePath: profilePath, + execPath: launchOptions.executablePath, + arguments: [...launchOptions.arguments, ...otherArguments, ...customArguments] + }); + } catch (e) { + showErrorDialog(e as string); + } +}; + +export const openInstallFolder = async (activeProfile: ActiveProfile, profilePath: string): Promise => { + if (activeProfile.profile.type !== "application") { + showErrorDialog(`Cannot open install folder of type "${activeProfile.profile.type}"!`); + return; + } + + try { + await invoke("open_folder_profile", { + profilePath: profilePath + }); + } catch (e) { + showErrorDialog(e as string); + } +}; diff --git a/src/profiles/directories.ts b/src/profiles/directories.ts new file mode 100644 index 0000000..ca5258e --- /dev/null +++ b/src/profiles/directories.ts @@ -0,0 +1,47 @@ +import { invoke } from "@tauri-apps/api"; +import { create } from "zustand"; + +export interface ImportantDirs { + yarcFolder: string, + launcherFolder: string, + tempFolder: string, +} + +export interface CustomDirs { + installFolder: string, + yargFolder: string, + setlistFolder: string, +} + +export interface DirectoriesStore { + importantDirs?: ImportantDirs, + customDirs?: CustomDirs, + + setDirs: (downloadLocation?: string) => Promise; +} + +export const useDirectories = create()((set) => ({ + setDirs: async (downloadLocation?: string) => { + const importantDirs: ImportantDirs = await invoke("get_important_dirs"); + + if (downloadLocation === undefined) { + set({ + importantDirs + }); + } else { + // If the download location is empty for whatever reason, just set it to the default one + if (downloadLocation === "") { + downloadLocation = importantDirs.yarcFolder; + } + + const customDirs: CustomDirs = await invoke("get_custom_dirs", { + downloadLocation: downloadLocation + }); + + set({ + importantDirs, + customDirs + }); + } + } +})); diff --git a/src/profiles/marketplace.ts b/src/profiles/marketplace.ts new file mode 100644 index 0000000..5fc5b88 --- /dev/null +++ b/src/profiles/marketplace.ts @@ -0,0 +1,28 @@ +import { Localized } from "@app/utils/localized"; + +export interface MarketplaceIndex { + banner: Localized<{ + backgroundUrl: string, + backgroundAccent?: string, + preHeaderText?: string, + headerText: string, + + viewProfileUUID: string, + previewUrl?: string, + }>, + lastUpdated: string, + profiles: MarketplaceProfile[] +} + +export type MarketplaceProfile = Localized<{ + uuid: string, + type: "application" | "setlist", + category: string, + url: string, + release: string, + + name: string, + subText?: string, + iconUrl: string, + bannerUrl?: string, +}>; diff --git a/src/profiles/store.ts b/src/profiles/store.ts new file mode 100644 index 0000000..b3ba6cb --- /dev/null +++ b/src/profiles/store.ts @@ -0,0 +1,194 @@ +import { create } from "zustand"; +import { ActiveProfile, Profile, Version, VersionList } from "./types"; +import { v4 as createUUID } from "uuid"; +import { settingsManager } from "@app/settings"; +import { showErrorDialog } from "@app/dialogs"; + +export interface ProfileStore { + activeProfiles: ActiveProfile[], + + getProfileByUUID: (uuid: string) => ActiveProfile | undefined, + anyOfProfileUUID: (uuid: string) => boolean, + + activateProfilesFromSettings: (offline: boolean) => Promise, + activateProfile: (profileUrl: string) => Promise, + removeProfile: (uuid: string) => Promise, + updateProfile: (activeProfile: ActiveProfile) => Promise, +} + +export const useProfileStore = create()((set, get) => ({ + activeProfiles: [], + + getProfileByUUID: (uuid) => { + return get().activeProfiles.find(i => i.uuid === uuid); + }, + anyOfProfileUUID: (uuid) => { + return get().activeProfiles.some(i => i.profile.uuid === uuid); + }, + + activateProfilesFromSettings: async (offline: boolean) => { + const activeProfiles = settingsManager.getCache("activeProfiles"); + let errored = false; + + if (!offline) { + // Attempt to update the profiles + for (const profile of activeProfiles) { + const newProfile = await tryFetchProfile(profile.originalUrl); + + if (newProfile === undefined) { + errored = true; + continue; + } + + profile.profile = newProfile; + } + + // Attempt to update versions + for (const profile of activeProfiles) { + const newVersion = await tryFetchVersion(profile.profile, profile.selectedVersion); + + if (newVersion === undefined) { + errored = true; + continue; + } + + profile.version = newVersion; + } + } + + set({ + activeProfiles + }); + + await settingsManager.set("activeProfiles", activeProfiles); + + if (errored) { + showErrorDialog("One or more active application/setlist profiles could not be fetched! Do they still exist?"); + } + }, + activateProfile: async (profileUrl: string) => { + const profile = await tryFetchProfile(profileUrl); + if (profile === undefined) { + return; + } + + const version = await tryFetchVersion(profile); + if (version === undefined) { + return; + } + + const newUUID = createUUID(); + const activeProfile: ActiveProfile = { + uuid: newUUID, + originalUrl: profileUrl, + + displayName: undefined, + selectedVersion: undefined, + launchArguments: "", + + lastPlayed: undefined, + + profile: profile, + version: version, + }; + + // Add to the profile state + const profiles = get().activeProfiles; + profiles.push(activeProfile); + set({ + activeProfiles: profiles + }); + + await settingsManager.set("activeProfiles", profiles); + + return newUUID; + }, + removeProfile: async (uuid: string) => { + let profiles = get().activeProfiles; + profiles = profiles.filter(i => i.uuid !== uuid); + set({ + activeProfiles: profiles + }); + + await settingsManager.set("activeProfiles", profiles); + }, + updateProfile: async (activeProfile: ActiveProfile) => { + const profiles = get().activeProfiles; + + const index = profiles.findIndex(i => i.uuid === activeProfile.uuid); + profiles[index] = activeProfile; + + set({ + activeProfiles: profiles + }); + + await settingsManager.set("activeProfiles", profiles); + } +})); + +export async function tryFetchProfile(profileUrl: string): Promise { + try { + const response = await fetch(profileUrl); + if (!response.ok) { + throw new Error(`Failed to fetch profile! Response status: ${response.status}`); + } + + // TODO: Schema + const json = await response.json(); + return json; + } catch (e) { + showErrorDialog(e as string); + return; + } +} + +export async function tryFetchVersion(profile: Profile, overrideVersion?: string): Promise { + if (profile.version.type === "embedded") { + return profile.version.version; + } + + try { + let versionUrl: string; + if (profile.version.type === "list") { + const response = await fetch(profile.version.listUrl); + if (!response.ok) { + throw new Error(`Failed to fetch version list! Response status: ${response.status}`); + } + + // TODO: Schema + const list: VersionList = await response.json(); + + if (list.length === 0) { + throw new Error("Profile has an empty version list!"); + } + + // Pick either the latest version, or attempt to pick from the overridden version + if (overrideVersion !== undefined) { + const found = list.find(i => i.uuid === overrideVersion); + if (found !== undefined) { + versionUrl = found.url; + } else { + versionUrl = list[0].url; + } + } else { + versionUrl = list[0].url; + } + } else if (profile.version.type === "url") { + versionUrl = profile.version.releaseUrl; + } else { + throw new Error("Unhandled version type!"); + } + + const response = await fetch(versionUrl); + if (!response.ok) { + throw new Error(`Failed to fetch version! Response status: ${response.status}`); + } + + // TODO: Schema + const json = await response.json(); + return json; + } catch (e) { + showErrorDialog(e as string); + return; + } +} diff --git a/src/profiles/types.ts b/src/profiles/types.ts new file mode 100644 index 0000000..f66c7bf --- /dev/null +++ b/src/profiles/types.ts @@ -0,0 +1,125 @@ +import { Localized } from "@app/utils/localized"; +import { OS } from "@app/utils/os"; + +// WARNING: This type is also defined in Rust. Make sure to change it in both places! +export interface ReleaseContent { + platforms?: OS[], + files: { + url: string, + sigUrl?: string, + fileType: "zip" | "encrypted", + }[]; +} + +export type VersionList = { + uuid: string, + url: string, + tag: string, + release: string, +}[]; + +export interface Version { + uuid: string, + tag: string, + release: string, + content: ReleaseContent[], + launchOptions?: { + [platform in OS]?: { + executablePath: string, + arguments: string[], + offlineArgument?: string, + languageArgument?: string, + downloadLocationArgument?: string, + } + } +} + +export type VersionInfo = VersionInfoList | VersionInfoUrl | VersionInfoEmbedded; + +export interface VersionInfoList { + type: "list", + + listUrl: string, +} + +export interface VersionInfoUrl { + type: "url", + + releaseUrl: string, +} + +export interface VersionInfoEmbedded { + type: "embedded", + + version: Version, +} + +export interface Metadata { + name: string, + description: string, + + iconUrl: string, + bannerBackUrl: string, + bannerFrontUrl?: string, + + initialRelease: string, + newsCategory?: string, + + links: { + [id: string]: { + name: string, + url: string, + } + }, +} + +export type ApplicationMetadata = Localized; + +export interface SetlistSong { + title: string, + artist: string, + length: number, + releaseDate: string, +} + +export type SetlistMetadata = Localized; + +export type Profile = ApplicationProfile | SetlistProfile; + +export interface ApplicationProfile { + type: "application", + + uuid: string, + metadata: ApplicationMetadata, + version: VersionInfo, +} + +export interface SetlistProfile { + type: "setlist", + + uuid: string, + metadata: SetlistMetadata, + version: VersionInfo, +} + +export interface ActiveProfile { + uuid: string, + originalUrl: string, + + displayName?: string, + selectedVersion?: string, + launchArguments: string, + + lastPlayed?: string, + + profile: Profile, + version: Version, +} diff --git a/src/profiles/utils.ts b/src/profiles/utils.ts new file mode 100644 index 0000000..ead26e9 --- /dev/null +++ b/src/profiles/utils.ts @@ -0,0 +1,37 @@ +import { localizeObject } from "@app/utils/localized"; +import { ActiveProfile, Metadata, Profile } from "./types"; +import { path } from "@tauri-apps/api"; +import { DirectoriesStore } from "./directories"; + +export const getPathForProfile = async (store: DirectoriesStore, activeProfile: ActiveProfile) => { + if (store.customDirs === undefined) { + throw Error("Custom directories are not initialized!"); + } + + if (activeProfile.profile.type === "setlist") { + return await path.join(store.customDirs.setlistFolder, activeProfile.uuid); + } else { + return await path.join(store.customDirs.yargFolder, activeProfile.uuid); + } +}; + +export const localizeMetadata = (profile: Profile, locale: string): Metadata => { + let out: Metadata; + + if (profile.type === "application") { + out = localizeObject(profile.metadata, locale); + } else if (profile.type === "setlist") { + out = localizeObject(profile.metadata, locale); + } else { + throw new Error("Unhandled profile type!"); + } + + return out; +}; + +export const processAssetUrl = (url: string): string => { + if (url.startsWith("@/")) { + return url.replace("@/", "/profileAssets/"); + } + return url; +}; diff --git a/src/routes/AppProfile/AppProfile.module.css b/src/routes/AppProfile/AppProfile.module.css new file mode 100644 index 0000000..ef3132c --- /dev/null +++ b/src/routes/AppProfile/AppProfile.module.css @@ -0,0 +1,152 @@ +.main { + display: flex; + flex-direction: column; + align-items: flex-start; + flex: 1 0 0; + align-self: stretch; +} + +.bannerContainer { + display: flex; + flex-direction: column; + align-items: flex-start; + align-self: stretch; + + background: linear-gradient(241deg, rgba(0, 0, 0, 0.00) 14.98%, #000 107.9%), + var(--bannerBack) lightgray 50% / cover no-repeat; +} + +.bannerApp { + display: flex; + padding: 30px; + align-items: center; + gap: 25px; + align-self: stretch; +} + +.bannerAppIcon { + width: 100px; + height: 100px; + min-width: 100px; + min-height: 100px; +} + +.bannerApp > div { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 10px; + + color: #FFF; + font-size: 30px; + font-weight: 700; + text-transform: uppercase; + + overflow: hidden; +} + +.verifiedTag { + display: flex; + height: 31px; + padding: 12px 5px 12px 10px; + justify-content: center; + align-items: center; + gap: 5px; + + border-radius: 38px; + border: 1px solid rgba(255, 255, 255, 0.30); + background: rgba(0, 0, 0, 0.50); + + color: #BDBDBD; + font-size: 15px; + font-weight: 700; +} + +.bannerOptions { + display: flex; + padding: 15px; + padding-left: 25px; + justify-content: space-between; + align-items: center; + align-self: stretch; + + background: rgba(0, 0, 0, 0.75); + backdrop-filter: blur(5px); +} + +.bannerOptionsStats { + display: flex; + align-items: center; + gap: 50px; +} + +.bannerOptionsStats > div { + display: flex; + align-items: center; + gap: 10px; + + color: #C0C3CD; + font-size: 16px; + font-weight: 400; +} + +.highlightedStat { + color: #DBDDE7; + font-weight: 500; +} + +.bannerOptionsMain { + display: flex; + align-items: center; + gap: 15px; +} + +.bannerOptionsMain > button { + font-size: 16px; +} + +.pageContainer { + display: flex; + padding: 16px; + align-items: flex-start; + gap: 25px; + flex: 1 0 0; + align-self: stretch; +} + +.content { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 10px; + flex: 1 0 0; +} + +.sidebar { + display: flex; + width: 320px; + flex-direction: column; + align-items: flex-start; + gap: 6px; +} + +.sidebar > button { + font-weight: 500; + text-transform: none; + width: 100%; + justify-content: left; + + padding: 3px; + padding-left: 15px; +} + +.sidebar > button > .icon { + width: 24px; + height: 40px; + + display: flex; + justify-content: center; + align-items: center; +} diff --git a/src/routes/AppProfile/AppSettings.module.css b/src/routes/AppProfile/AppSettings.module.css new file mode 100644 index 0000000..e8278af --- /dev/null +++ b/src/routes/AppProfile/AppSettings.module.css @@ -0,0 +1,165 @@ +.popup { + position: absolute; + + top: var(--titleBarHeight); + bottom: 0; + left: var(--sideBarWidth); + right: 0; + + background: rgba(0, 0, 0, 0.6); + + z-index: 998; +} + +.body { + position: absolute; + + inset: 32px; + border-radius: 16px; + + background-color: #FFF; + + padding: 25px; + + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: flex-start; + flex: 1 0 0; + align-self: stretch; + gap: 25px; +} + +.tabRoot { + display: flex; + flex-direction: column; + gap: 25px; + + align-self: stretch; + flex: 1 0 0; + overflow: hidden; +} + +.tabList { + display: flex; + flex-direction: row; + gap: 12px; +} + +.tabList > .tab { + flex: 1 0 0; + padding: 10px 15px; + + border: none; + border-radius: 50px; + background: #E2E6F1; + + color: #5C6170; + font-size: 14px; + font-weight: 500; +} + +.tabList > .tab[data-state="active"] { + background: #41475F; + color: #FFF; +} + +.content { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 15px; + + flex: 1 0 0; + align-self: stretch; +} + +.content[data-state="inactive"] { + display: none; +} + +.setting { + display: flex; + flex-direction: row; + align-self: stretch; + gap: 15px; +} + +.setting > p { + width: 175px; + + color: #41475F; +} + +.setting > input { + align-self: center; + flex: 1 0 0; +} + +.navigation { + display: flex; + flex-direction: row; + align-items: flex-end; + gap: 15px; + align-self: stretch; +} + +.navigation > button { + flex: 1 0 0; +} + +.versionList { + display: flex; + flex-direction: column; + + overflow: auto; +} + +.versionList[data-state="inactive"] { + display: none; +} + +.versionList > .centered { + flex: 1 0 0; + + text-align: center; + + color: #6C7088; + font-size: 16px; + font-style: italic; + font-weight: 500; +} + +.versionList > div { + display: flex; + flex-direction: row; + padding: 16px; + + cursor: pointer; + + color: #41475F; +} + +.versionList > div:nth-child(odd) { + background: #F4F4F4; +} + +.versionList > div[data-state="active"] { + color: #FFF; + background: #41475F; +} + +.versionList > div > header { + flex: 1 0 0; + + font-size: 16px; + font-weight: 700; +} + +.versionList > div > div { + flex: 1 0 0; + + font-size: 16px; + font-style: italic; + font-weight: 400; +} diff --git a/src/routes/AppProfile/AppSettings.tsx b/src/routes/AppProfile/AppSettings.tsx new file mode 100644 index 0000000..9a10ca2 --- /dev/null +++ b/src/routes/AppProfile/AppSettings.tsx @@ -0,0 +1,160 @@ +import Button, { ButtonColor } from "@app/components/Button"; +import styles from "./AppSettings.module.css"; +import { useState } from "react"; +import { ActiveProfile, VersionInfoList, VersionList } from "@app/profiles/types"; +import { localizeMetadata } from "@app/profiles/utils"; +import { tryFetchVersion, useProfileStore } from "@app/profiles/store"; +import InputBox from "@app/components/InputBox"; +import * as Tabs from "@radix-ui/react-tabs"; +import { useOfflineStatus } from "@app/hooks/useOfflineStatus"; +import { useQuery } from "@tanstack/react-query"; +import { showErrorDialog } from "@app/dialogs"; +import { distanceFromToday } from "@app/utils/timeFormat"; + +interface VersionListProps { + activeProfile: ActiveProfile, + + selectedVersion?: string, + setSelectedVersion: React.Dispatch> +} + +const VersionListComp: React.FC = ({ activeProfile, selectedVersion, setSelectedVersion }: VersionListProps) => { + const offlineStatus = useOfflineStatus(); + + const versionListQuery = useQuery({ + enabled: activeProfile.profile.version.type === "list" && !offlineStatus.isOffline, + queryKey: ["VersionList", activeProfile.profile.uuid], + queryFn: async (): Promise => + await fetch((activeProfile.profile.version as VersionInfoList).listUrl) + .then(res => res.json()) + }); + + if (versionListQuery.isLoading) { + return + Loading... + ; + } + + const versionList = versionListQuery.data; + if (versionListQuery.isError || versionList === undefined) { + showErrorDialog(versionListQuery.error); + return + Error + ; + } + + if (offlineStatus.isOffline) { + return + Offline + ; + } else if (activeProfile.profile.version.type !== "list") { + return + No version options for this profile... + ; + } else { + return <> +
setSelectedVersion(undefined)}> + +
Latest Release
+
Recommended
+
+ { + versionList.map(i => +
setSelectedVersion(i.uuid)}> + +
{i.tag}
+
{distanceFromToday(i.release)}
+
+ ) + } + ; + } +}; + +interface Props { + activeProfile: ActiveProfile, + setSettingsOpen: React.Dispatch> +} + +const AppSettings: React.FC = ({ activeProfile, setSettingsOpen }: Props) => { + let initalDisplayName = activeProfile.displayName; + if (initalDisplayName === undefined) { + initalDisplayName = localizeMetadata(activeProfile.profile, "en-US").name; + } + + const [displayName, setDisplayName] = useState(initalDisplayName); + const [launchArguments, setLaunchArguments] = useState(activeProfile.launchArguments); + const [selectedVerison, setSelectedVersion] = useState(activeProfile.selectedVersion); + + return
+
+ + + + Settings + + + Version + + + +
+

Display Name

+ +
+
+

Additional Launch Arguments

+ +
+
+ + + +
+
+ + +
+
+
; +}; + +export default AppSettings; diff --git a/src/routes/AppProfile/LaunchButton.tsx b/src/routes/AppProfile/LaunchButton.tsx new file mode 100644 index 0000000..823d45a --- /dev/null +++ b/src/routes/AppProfile/LaunchButton.tsx @@ -0,0 +1,96 @@ +import { ButtonColor } from "@app/components/Button"; +import { InstallingIcon, UpdateIcon } from "@app/assets/Icons"; +import Button from "@app/components/Button"; +import { ProfileFolderState, ProfileState } from "@app/hooks/useProfileState"; +import { localize } from "@app/utils/localized"; +import { usePayload } from "@app/tasks/payload"; +import PayloadProgress from "@app/components/PayloadProgress"; + +interface Props { + profileState: ProfileState +} + +export function LaunchButton({ profileState }: Props) { + const { + loading, + activeProfile, + folderState, + currentTask, + downloadAndInstall, + launch, + } = profileState; + + const payload = usePayload(currentTask?.taskUUID); + + const profile = activeProfile.profile; + + // Loading button + if (loading) { + return ; + } + + let releaseName = ""; + if (profile.type === "application") { + releaseName = localize(profile.metadata, "releaseName", "en-US"); + } + + // Installing button + if (currentTask !== undefined) { + if (payload !== undefined) { + return ; + } else { + return ; + } + } + + // Update/install button + if (folderState === ProfileFolderState.UpdateRequired || folderState === ProfileFolderState.FirstDownload) { + return ; + } + + // Launch/up-to-date button + if (folderState === ProfileFolderState.UpToDate) { + return ; + } + + // Errored button + if (folderState === ProfileFolderState.Error) { + return ; + } + + return ; +} diff --git a/src/routes/AppProfile/MoreDropdown.module.css b/src/routes/AppProfile/MoreDropdown.module.css new file mode 100644 index 0000000..8bac0dd --- /dev/null +++ b/src/routes/AppProfile/MoreDropdown.module.css @@ -0,0 +1,39 @@ +.content { + width: 300px; + background: var(--sideBarBackground); + + border-radius: 8px; + padding: 5px; + + box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2); +} + +.item { + display: flex; + align-items: center; + position: relative; + + border-radius: 8px; + padding: 15px; + + font-size: 14px; + font-weight: 700; + text-transform: uppercase; + background: rgba(255, 255, 255, 0); + + color: var(--primary); + + transition: background 0.1s; + + outline: none; + user-select: none; + cursor: pointer; +} + +.item[data-highlighted] { + background: rgba(255, 255, 255, 0.1); +} + +.arrow { + fill: var(--sideBarBackground); +} diff --git a/src/routes/AppProfile/MoreDropdown.tsx b/src/routes/AppProfile/MoreDropdown.tsx new file mode 100644 index 0000000..70fcf64 --- /dev/null +++ b/src/routes/AppProfile/MoreDropdown.tsx @@ -0,0 +1,61 @@ +import { MoreIcon } from "@app/assets/Icons"; +import Button, { ButtonColor } from "@app/components/Button"; +import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; +import styles from "./MoreDropdown.module.css"; +import { ProfileState } from "@app/hooks/useProfileState"; + +type ItemProps = React.PropsWithChildren<{ + onClick?: React.MouseEventHandler +}>; + +const Item: React.FC = (props: ItemProps) => { + return + {props.children} + ; +}; + +interface Props { + profileState: ProfileState +} + +const MoreDropdown: React.FC = (props: Props) => { + const { + activeProfile, + openInstallFolder, + uninstall, + deleteProfile + } = props.profileState; + + return + + + + + + + + {activeProfile.profile.type === "application" && + await openInstallFolder()}> + Open Install Folder + + } + + await uninstall()}> + Uninstall + + await deleteProfile()}> + Delete Profile + + + + + + ; +}; + +export default MoreDropdown; diff --git a/src/routes/AppProfile/index.tsx b/src/routes/AppProfile/index.tsx new file mode 100644 index 0000000..6362e41 --- /dev/null +++ b/src/routes/AppProfile/index.tsx @@ -0,0 +1,129 @@ +import { useParams } from "react-router-dom"; +import { useProfileState } from "@app/hooks/useProfileState"; +import styles from "./AppProfile.module.css"; +import { GithubIcon, InformationIcon, LinkIcon, SettingsIcon, TimeIcon, VerifiedIcon } from "@app/assets/Icons"; +import { LaunchButton } from "./LaunchButton"; +import { localizeMetadata, processAssetUrl } from "@app/profiles/utils"; +import Box from "@app/components/Box"; +import { ApplicationMetadata } from "@app/profiles/types"; +import Button, { ButtonColor } from "@app/components/Button"; +import MoreDropdown from "./MoreDropdown"; +import { distanceFromToday } from "@app/utils/timeFormat"; +import ProfileIcon from "@app/components/ProfileIcon"; +import NewsSection from "@app/components/NewsSection"; +import { askOpenUrl } from "@app/utils/safeUrl"; +import AppSettings from "./AppSettings"; +import { useEffect, useState } from "react"; +import Setlist from "../../components/Setlist"; + +function AppProfile() { + const { uuid } = useParams(); + if (!uuid) { + return <>; + } + + const [settingsOpen, setSettingsOption] = useState(false); + + // Close settings on tab change + useEffect(() => { + setSettingsOption(false); + }, [uuid]); + + const profileState = useProfileState(uuid); + if (profileState.loading) { + return <>; + } + + const activeProfile = profileState.activeProfile; + const profile = activeProfile.profile; + + const metadata = localizeMetadata(profile, "en-US"); + + return
+ {settingsOpen && + + } + +
+ +
+ +
+
+ Official +
+ {activeProfile.displayName !== undefined ? activeProfile.displayName : metadata.name} +
+
+
+
+ {profile.type === "application" && +
+ + + Last played + + {(activeProfile.lastPlayed === undefined || activeProfile.lastPlayed === null) + ? "never" + : distanceFromToday(activeProfile.lastPlayed)} + + +
+ } +
+
+ {profile.type === "application" && + + } + + + +
+
+
+
+
+ {profile.type === "setlist" && + + } + + +
+
+ +
+ + {profile.type === "application" + ? `About ${metadata.name} (${(metadata as ApplicationMetadata).releaseName})` + : `About ${metadata.name}`} +
+ {metadata.description} +
+ { + Object.entries(metadata.links).map(i => { + let icon = ; + if (i[1].url.startsWith("https://github.com")) { + icon = ; + } + + return ; + }) + } +
+
+
; +} + +export default AppProfile; diff --git a/src/routes/ErrorScreen/ErrorScreen.module.css b/src/routes/ErrorScreen/ErrorScreen.module.css index 280093e..a6678cb 100644 --- a/src/routes/ErrorScreen/ErrorScreen.module.css +++ b/src/routes/ErrorScreen/ErrorScreen.module.css @@ -1,6 +1,6 @@ .error { - background: var(--titleBar_background); - color: var(--titleBar_accent); + background: #05060B; + color: #868AA8; display: flex; flex-direction: column; @@ -30,4 +30,4 @@ text-transform: uppercase; color: white; cursor: pointer; -} \ No newline at end of file +} diff --git a/src/routes/ErrorScreen/index.tsx b/src/routes/ErrorScreen/index.tsx index ba05f72..06ac312 100644 --- a/src/routes/ErrorScreen/index.tsx +++ b/src/routes/ErrorScreen/index.tsx @@ -3,7 +3,6 @@ import { error as LogError } from "tauri-plugin-log-api"; import { FallbackProps } from "react-error-boundary"; import { appWindow } from "@tauri-apps/api/window"; - export function ErrorScreen({error}: FallbackProps) { return

@@ -19,4 +18,4 @@ export function ErrorScreen({error}: FallbackProps) { export function onError(error: Error) { LogError(`${error}`); -} \ No newline at end of file +} diff --git a/src/routes/Home/Home.module.css b/src/routes/Home/Home.module.css index f09a0bd..0754218 100644 --- a/src/routes/Home/Home.module.css +++ b/src/routes/Home/Home.module.css @@ -1,81 +1,8 @@ -.banner { - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - - background-color: #080A0E; - height: 250px; - - color: white; -} - -.banner > h1 { - font-weight: 300; -} - -.blue { - color: var(--accent); -} - .content { - display: flex; - padding: 25px; - align-items: flex-start; - gap: 25px; - align-self: stretch; - min-height: calc(100% - 250px); - - background: var(--white-background); -} - -.content_inner { display: flex; flex-direction: row; align-items: flex-start; gap: 25px; - flex: 1 0 0; -} - -.sidebar { - display: flex; - width: 350px; - flex-direction: column; - align-items: flex-start; - gap: 25px; - align-self: stretch; -} -.discord_box { - display: flex; - padding: 25px; - align-items: flex-start; - gap: 10px; - align-self: stretch; - - border-radius: 8px; - background: #5561EF; - - color: #FFF; - font-size: 20px; - font-style: normal; - font-weight: 700; - line-height: normal; - text-transform: uppercase; - - /* background-image: linear-gradient(133deg, #4458D1 45.69%, rgba(17, 52, 132, 0.00) 95.38%), url(/src/assets/DiscordBanner.png); - background-position: 0, 115.562px -55.512px; - background-size: auto, 114% 290.613%; - background-repeat: no-repeat, no-repeat; */ - - background-image: linear-gradient(90deg, #4458D1 40%, rgba(17, 52, 132, 0.00) 75%), url(/src/assets/Background.png); - background-position: 0, 115.562px -55.512px; - background-size: auto, 114% 290.613%; - background-repeat: no-repeat, no-repeat; + padding: 24px; } - -@media only screen and (max-width: 1100px) { - .sidebar { - display: none; - } -} \ No newline at end of file diff --git a/src/routes/Home/index.tsx b/src/routes/Home/index.tsx index 771ad99..4e68cec 100644 --- a/src/routes/Home/index.tsx +++ b/src/routes/Home/index.tsx @@ -1,26 +1,10 @@ -import { DiscordIcon } from "@app/assets/Icons"; import styles from "./Home.module.css"; import NewsSection from "@app/components/NewsSection"; function Home() { - return <> -

-

Welcome to the YARC Launcher Beta

-

Here you can download and install YARG, and the official YARG setlist!

-

If you encounter any bugs, please report it to us in our Discord.

-
- - ; + return
+ +
; } -export default Home; \ No newline at end of file +export default Home; diff --git a/src/routes/Marketplace/Marketplace.module.css b/src/routes/Marketplace/Marketplace.module.css new file mode 100644 index 0000000..a797df3 --- /dev/null +++ b/src/routes/Marketplace/Marketplace.module.css @@ -0,0 +1,190 @@ +.main { + display: flex; + flex-direction: column; + align-items: flex-start; + flex: 1 0 0; +} + +.banner { + display: flex; + height: 290px; + align-items: center; + justify-content: center; + gap: 5px; + align-self: stretch; + flex-direction: column; + + background: linear-gradient(68deg, var(--accent) -29.28%, transparent 110.95%), + var(--banner) lightgray 50% / cover no-repeat; +} + +.banner > .newTag { + display: flex; + padding: 5px 12px; + justify-content: center; + align-items: center; + gap: 5px; + + border-radius: 50px; + background: #FCD548; + border: 2px solid #ffe071; + margin-bottom: 10px; + + color: #4F2600; + font-size: 10px; + font-weight: 800; + text-transform: uppercase; +} + +.banner > .preHeader { + color: rgba(15, 17, 24, 0.75); + font-family: "Archivo Black", var(--backupFonts); + font-size: 20px; + font-weight: 400; + text-transform: uppercase; +} + +.banner > .header { + color: #0F1118; + font-family: "Big Shoulders Text", var(--backupFonts); + text-shadow: 4px 4px var(--accent); + line-height: 90%; + font-size: 120px; + font-weight: 400; + text-transform: uppercase; +} + +.banner > .buttons { + margin-top: 12px; + display: flex; + align-items: center; + gap: 15px; +} + +.search { + display: flex; + height: 100px; + padding: 20px 0px; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 10px; + align-self: stretch; +} + +.content { + display: flex; + padding: 24px; + flex-direction: column; + align-items: flex-start; + gap: 24px; + align-self: stretch; +} + +.section { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 10px; + align-self: stretch; +} + +.section > .title { + display: flex; + padding: 12px 0px; + justify-content: space-between; + align-items: center; + align-self: stretch; +} + +.section > .title > .left { + display: flex; + width: 179px; + justify-content: space-between; + align-items: center; + + color: #41475F; + font-size: 16px; + font-weight: 700; + text-transform: uppercase; +} + +.section > .list { + --item-width: 400px; + + max-width: 100%; + overflow-x: auto; + + padding: 12px; + + display: grid; + grid-template-columns: repeat(auto-fit, minmax(var(--item-width), auto)); + grid-gap: 12px; + grid-auto-flow: column; +} + +.section.expanded > .list { + width: 100%; + grid-auto-flow: row; +} + +.profileView { + display: flex; + + min-width: var(--item-width); + max-width: var(--item-width); + + padding: 12px; + align-items: center; + gap: 20px; + + border: none; + border-radius: 16px; + background: linear-gradient(241deg, rgba(0, 0, 0, 0.00) 14.98%, #000 107.9%), + var(--background) lightgray 50% / cover no-repeat; + + --outline-width: 2px; + outline: var(--outline-width) solid rgba(255, 255, 255, 0.15); + outline-offset: calc(var(--outline-width) * -1); + + box-shadow: none; + + cursor: pointer; + transition: transform 0.1s, box-shadow 0.3s; +} + +.expanded .profileView { + max-width: none; +} + +.profileView:hover { + box-shadow: 2px 2px 7px 0px rgba(0, 0, 0, 0.4); + transform: scale(1.025); +} + +.profileView > .icon { + width: 100px; + height: 100px; +} + +.profileView > .info { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 5px; + + text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.6); +} + +.profileView > .info > header { + color: #FFF; + font-size: 20px; + font-weight: 700; + text-transform: uppercase; +} + +.profileView > .info > span { + color: #C5C5C5; + font-size: 16px; + font-weight: 400; +} diff --git a/src/routes/Marketplace/MarketplacePopup.module.css b/src/routes/Marketplace/MarketplacePopup.module.css new file mode 100644 index 0000000..f437ce9 --- /dev/null +++ b/src/routes/Marketplace/MarketplacePopup.module.css @@ -0,0 +1,131 @@ +.popup { + position: absolute; + + top: var(--titleBarHeight); + bottom: 0; + left: var(--sideBarWidth); + right: 0; + + background: rgba(0, 0, 0, 0.6); + + z-index: 998; +} + +.body { + position: absolute; + + /* Masking prevents artifacts, unlike overflow: hidden */ + mask: linear-gradient(#000 0 0); + + top: 64px; + bottom: 64px; + left: 32px; + right: 32px; + + border-radius: 16px; + + background-color: #FFF; + + display: flex; + flex-direction: column; + align-items: flex-start; + flex: 1 0 0; + align-self: stretch; +} + +.content { + display: flex; + padding: 16px; + align-items: flex-start; + gap: 10px; + flex-direction: column; + + overflow: auto; +} + +.close { + position: absolute; + top: 16px; + right: 16px; + + width: 16px; + height: 16px; + + cursor: pointer; +} + +.close > * { + color: #FFF; + filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 1)); +} + +.bannerContainer { + display: flex; + flex-direction: column; + align-items: flex-start; + align-self: stretch; + + background: linear-gradient(241deg, rgba(0, 0, 0, 0.00) 14.98%, #000 107.9%), + var(--bannerBack) lightgray 50% / cover no-repeat; +} + +.bannerApp { + display: flex; + padding: 30px; + align-items: center; + gap: 25px; + align-self: stretch; +} + +.bannerAppIcon { + width: 100px; + height: 100px; +} + +.bannerApp > div { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 10px; + + color: #FFF; + font-size: 30px; + font-weight: 700; + text-transform: uppercase; +} + +.verifiedTag { + display: flex; + height: 31px; + padding: 12px 5px 12px 10px; + justify-content: center; + align-items: center; + gap: 5px; + + border-radius: 38px; + border: 1px solid rgba(255, 255, 255, 0.30); + background: rgba(0, 0, 0, 0.50); + + color: #BDBDBD; + font-size: 15px; + font-weight: 700; +} + +.bannerOptions { + display: flex; + padding: 15px; + padding-left: 25px; + justify-content: flex-end; + align-items: center; + align-self: stretch; + + background: rgba(0, 0, 0, 0.75); + backdrop-filter: blur(5px); +} + +.bannerOptionsMain { + display: flex; + align-items: center; + gap: 15px; +} diff --git a/src/routes/Marketplace/MarketplacePopup.tsx b/src/routes/Marketplace/MarketplacePopup.tsx new file mode 100644 index 0000000..32eabe3 --- /dev/null +++ b/src/routes/Marketplace/MarketplacePopup.tsx @@ -0,0 +1,125 @@ +import { MarketplaceProfile } from "@app/profiles/marketplace"; +import { useProfileStore } from "@app/profiles/store"; +import styles from "./MarketplacePopup.module.css"; +import { AddIcon, CloseIcon, InformationIcon, VerifiedIcon } from "@app/assets/Icons"; +import { localizeMetadata, processAssetUrl } from "@app/profiles/utils"; +import { useQuery } from "@tanstack/react-query"; +import { ApplicationMetadata, Profile } from "@app/profiles/types"; +import ProfileIcon from "@app/components/ProfileIcon"; +import Button, { ButtonColor } from "@app/components/Button"; +import { useNavigate } from "react-router-dom"; +import { useState } from "react"; +import Setlist from "@app/components/Setlist"; +import Box from "@app/components/Box"; + +interface Props { + marketplaceProfile?: MarketplaceProfile, + setSelectedProfile: React.Dispatch>, +} + +const MarketplacePopup: React.FC = ({ marketplaceProfile, setSelectedProfile }: Props) => { + const profiles = useProfileStore(); + const profileQuery = useQuery({ + enabled: marketplaceProfile !== undefined, + queryKey: ["Profile", marketplaceProfile?.uuid], + queryFn: async (): Promise => await fetch((marketplaceProfile as MarketplaceProfile).url) + .then(res => res.json()) + }); + + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); + + if (marketplaceProfile === undefined || profileQuery.isLoading) { + return <>; + } + + const anyOfProfile = profiles.anyOfProfileUUID(marketplaceProfile.uuid); + + const profile = profileQuery.data; + if (profileQuery.isError || profile === undefined) { + return <> + Error: {profileQuery.error} + ; + } + + const metadata = localizeMetadata(profile, "en-US"); + + const addToLibrary = async () => { + if (loading) { + return; + } + + setLoading(true); + const uuid = await profiles.activateProfile(marketplaceProfile.url); + setSelectedProfile(undefined); + + if (uuid !== undefined) { + navigate(`/app-profile/${uuid}`); + } + }; + + let button; + if (loading) { + button = ; + } else if (!anyOfProfile) { + button = ; + } else if (anyOfProfile && profile.type === "application") { + button = ; + } else if (anyOfProfile && profile.type === "setlist") { + button = ; + } + + return
+
+
setSelectedProfile(undefined)}> + +
+ +
+ +
+ +
+
+ Official +
+ {metadata.name} +
+
+
+
+ {button} +
+
+
+ +
+ +
+ + {profile.type === "application" + ? `About ${metadata.name} (${(metadata as ApplicationMetadata).releaseName})` + : `About ${metadata.name}`} +
+ + {metadata.description} +
+ + {profile.type === "setlist" && + + } +
+
+
; +}; + +export default MarketplacePopup; diff --git a/src/routes/Marketplace/MarketplaceProfileView.tsx b/src/routes/Marketplace/MarketplaceProfileView.tsx new file mode 100644 index 0000000..cf5ac79 --- /dev/null +++ b/src/routes/Marketplace/MarketplaceProfileView.tsx @@ -0,0 +1,34 @@ +import { MarketplaceProfile } from "@app/profiles/marketplace"; +import styles from "./Marketplace.module.css"; +import ProfileIcon from "@app/components/ProfileIcon"; +import { localizeObject } from "@app/utils/localized"; +import { processAssetUrl } from "@app/profiles/utils"; + +interface Props { + profile: MarketplaceProfile, + setSelectedProfile: React.Dispatch>, +} + +const MarketplaceProfileView: React.FC = ({ profile, setSelectedProfile }: Props) => { + const localized = localizeObject(profile, "en-US"); + let bannerUrl = localized.bannerUrl; + if (bannerUrl === undefined) { + bannerUrl = localized.iconUrl; + } + + return ; +}; + +export default MarketplaceProfileView; diff --git a/src/routes/Marketplace/MarketplaceSection.tsx b/src/routes/Marketplace/MarketplaceSection.tsx new file mode 100644 index 0000000..0c4510f --- /dev/null +++ b/src/routes/Marketplace/MarketplaceSection.tsx @@ -0,0 +1,43 @@ +import Button, { ButtonColor } from "@app/components/Button"; +import styles from "./Marketplace.module.css"; +import { useRef, useState } from "react"; +import { useOverflow } from "use-overflow"; + +type Props = React.PropsWithChildren<{ + name: string, +}>; + +const MarketplaceSection: React.FC = ({ name, children }: Props) => { + const listRef = useRef(null); + const { refXOverflowing: listOverflowing } = useOverflow(listRef); + + const [expanded, setExpanded] = useState(false); + + const classes = [styles.section]; + if (expanded) { + classes.push(styles.expanded); + } + + return
+
+
+ {name} +
+ {(expanded || listOverflowing) && + + } +
+
+ {children} +
+
; +}; + +export default MarketplaceSection; diff --git a/src/routes/Marketplace/index.tsx b/src/routes/Marketplace/index.tsx new file mode 100644 index 0000000..e31dbdf --- /dev/null +++ b/src/routes/Marketplace/index.tsx @@ -0,0 +1,101 @@ +import styles from "./Marketplace.module.css"; +import MarketplaceSection from "./MarketplaceSection"; +import MarketplaceProfileView from "./MarketplaceProfileView"; +import { processAssetUrl } from "@app/profiles/utils"; +import { localizeObject } from "@app/utils/localized"; +import Button, { ButtonColor } from "@app/components/Button"; +import { MarketplaceProfile } from "@app/profiles/marketplace"; +import { askOpenUrl } from "@app/utils/safeUrl"; +import MarketplacePopup from "./MarketplacePopup"; +import { useEffect, useState } from "react"; +import { showErrorDialog } from "@app/dialogs"; +import useMarketIndex from "@app/hooks/useMarketIndex"; +import { settingsManager } from "@app/settings"; + +function Marketplace() { + const [selectedProfile, setSelectedProfile] = useState(undefined); + const marketIndexQuery = useMarketIndex(); + + // Used for the "Updated!" tag + useEffect(() => { + (async () => { + await settingsManager.set("lastMarketplaceObserve", new Date().toISOString()); + })(); + }, []); + + if (marketIndexQuery.isLoading) { + return <>Loading...; + } + + const marketIndex = marketIndexQuery.data; + if (marketIndexQuery.isError || marketIndex === undefined) { + return <> + Error: {marketIndexQuery.error} + ; + } + + const banner = localizeObject(marketIndex.banner, "en-US"); + + return
+ + +
+ +
+ New! +
+ {banner.preHeaderText !== undefined && +
+ {banner.preHeaderText} +
+ } +
+ {banner.headerText} +
+
+ + + {banner.previewUrl !== undefined && + + } +
+
+
+ + { + marketIndex.profiles.filter(i => i.type === "application").map(i => + + ) + } + + + { + marketIndex.profiles.filter(i => i.type === "setlist").map(i => + + ) + } + +
+
; +} + +export default Marketplace; diff --git a/src/routes/Queue/index.tsx b/src/routes/Queue/index.tsx index 593bbff..ed472ed 100644 --- a/src/routes/Queue/index.tsx +++ b/src/routes/Queue/index.tsx @@ -90,4 +90,4 @@ function Queue() { ; } -export default Queue; \ No newline at end of file +export default Queue; diff --git a/src/routes/Setlist/Official.tsx b/src/routes/Setlist/Official.tsx deleted file mode 100644 index c31fc1b..0000000 --- a/src/routes/Setlist/Official.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import SetlistPage from "@app/components/Setlist/SetlistPage"; -import { useSetlistData } from "@app/hooks/useSetlistData"; -import { useSetlistRelease } from "@app/hooks/useSetlistRelease"; - -function OfficialSetlistPage() { - const { data: setlistData, error, isSuccess, isLoading } = useSetlistRelease("official"); - const setlistVersion = useSetlistData(setlistData, "official"); - - if (isLoading) return "Loading..."; - - if (error) return `An error has occurred: ${error}`; - - if (isSuccess) { - return (<> - - ); - } -} - -export default OfficialSetlistPage; \ No newline at end of file diff --git a/src/routes/YARG/Nightly.tsx b/src/routes/YARG/Nightly.tsx deleted file mode 100644 index 877e38e..0000000 --- a/src/routes/YARG/Nightly.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import LaunchPage from "@app/components/Launch/LaunchPage"; -import { useYARGRelease } from "@app/hooks/useYARGRelease"; -import { useYARGVersion } from "@app/hooks/useYARGVersion"; -import NightlyYARGIcon from "@app/assets/NightlyYARGIcon.png"; -import NightlyYARGBanner from "@app/assets/Banner/Nightly.png"; - -function NightlyYARGPage() { - const { data: releaseData, error, isSuccess, isLoading } = useYARGRelease("nightly"); - const yargVersion = useYARGVersion(releaseData, "nightly"); - - if (isLoading) return "Loading..."; - - if (error) return `An error has occurred: ${error}`; - - if (isSuccess) { - return (<> - - YARG Nightly (a.k.a. YARG bleeding-edge) is an alternative version of YARG that is updated twice - a day (if changes have been made). These builds are in an extremely early beta, so bugs are expected. - If you do notice a bug, please be sure to report it on GitHub, or on our Discord. - } - websiteUrl="https://github.com/YARC-Official/YARG-BleedingEdge" - icon={NightlyYARGIcon} - banner={NightlyYARGBanner} - /> - ); - } -} - -export default NightlyYARGPage; \ No newline at end of file diff --git a/src/routes/YARG/Stable.tsx b/src/routes/YARG/Stable.tsx deleted file mode 100644 index fd54d0b..0000000 --- a/src/routes/YARG/Stable.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import LaunchPage from "@app/components/Launch/LaunchPage"; -import { useYARGRelease } from "@app/hooks/useYARGRelease"; -import { useYARGVersion } from "@app/hooks/useYARGVersion"; -import StableYARGIcon from "@app/assets/StableYARGIcon.png"; -import StableYARGBanner from "@app/assets/Banner/Stable.png"; - -function StableYARGPage() { - const { data: releaseData, error, isSuccess, isLoading } = useYARGRelease("stable"); - const yargVersion = useYARGVersion(releaseData, "stable"); - - if (isLoading) return "Loading..."; - - if (error) return `An error has occurred: ${error}`; - - if (isSuccess) { - return (<> - - YARG (a.k.a. Yet Another Rhythm Game) is a free, open-source, plastic guitar game that is - still in development. It supports guitar (five fret), drums (plastic or e-kit), vocals, - pro-guitar, and more! - } - websiteUrl="https://github.com/YARC-Official/YARG" - icon={StableYARGIcon} - banner={StableYARGBanner} - /> - ); - } -} - -export default StableYARGPage; \ No newline at end of file diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 0e9551e..7d8aa0d 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -3,11 +3,10 @@ import { createBrowserRouter } from "react-router-dom"; import RootLayout from "@app/routes/root"; import Home from "@app/routes/Home"; import Settings from "@app/routes/Settings"; -import StableYARGPage from "./YARG/Stable"; -import NightlyYARGPage from "./YARG/Nightly"; -import OfficialSetlistPage from "./Setlist/Official"; import Queue from "@app/routes/Queue"; import NewsPage from "./NewsPage"; +import AppProfile from "./AppProfile"; +import Marketplace from "./Marketplace"; const Router = createBrowserRouter([ { @@ -18,32 +17,22 @@ const Router = createBrowserRouter([ path: "/", element: }, - { path: "/settings", element: }, - { path: "/queue", element: }, - - { - path: "/yarg/stable", - element: - }, - { - path: "/yarg/nightly", - element: + path: "/marketplace", + element: }, - { - path: "/setlist/official", - element: + path: "/app-profile/:uuid", + element: }, - { path: "/news/:md", element: @@ -52,4 +41,4 @@ const Router = createBrowserRouter([ }, ]); -export default Router; \ No newline at end of file +export default Router; diff --git a/src/routes/root.tsx b/src/routes/root.tsx index d1f363a..52f5fa2 100644 --- a/src/routes/root.tsx +++ b/src/routes/root.tsx @@ -4,14 +4,12 @@ import Sidebar from "@app/components/Sidebar"; import { Outlet } from "react-router-dom"; const RootLayout: React.FC = () => { - return (<> - + return <>
- - ); + ; }; -export default RootLayout; \ No newline at end of file +export default RootLayout; diff --git a/src/settings.ts b/src/settings.ts new file mode 100644 index 0000000..a3a948e --- /dev/null +++ b/src/settings.ts @@ -0,0 +1,18 @@ +import { SettingsManager } from "tauri-settings"; +import { ActiveProfile } from "./profiles/types"; + +export interface Settings { + onboardingCompleted: boolean, + downloadLocation: string, + lastMarketplaceObserve: string, + activeProfiles: ActiveProfile[], +} + +export const settingsManager = new SettingsManager({ + onboardingCompleted: false, + downloadLocation: "", + lastMarketplaceObserve: new Date().toISOString(), + activeProfiles: [] +}, { + prettify: true +}); diff --git a/src/stores/SetlistStateStore.ts b/src/stores/SetlistStateStore.ts deleted file mode 100644 index 3017e73..0000000 --- a/src/stores/SetlistStateStore.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { SetlistStates } from "@app/hooks/useSetlistData"; -import { create } from "zustand"; - -interface SetlistStateStore { - states: { - [key: string]: SetlistStates - }, - update: (key: string, state: SetlistStates) => void -} - -const useSetlistStateStore = create()((set) => ({ - states: {}, - update(key, state) { - return set(current => ({ - states: { - ...current.states, - [key]: state - } - })); - }, -})); - -interface useSetlistStateInterface { - state: SetlistStates; - setState: (newState: SetlistStates) => void; -} - -export const useSetlistState = (version?: string): useSetlistStateInterface => { - const store = useSetlistStateStore(); - - // If we don't have a version yet, return a dummy loading version; - if (!version) { - return { - state: SetlistStates.LOADING, - setState: () => {} - }; - } - - const state = store.states[version]; - const setState = (newState: SetlistStates) => store.update(version, newState); - - return { state, setState }; -}; \ No newline at end of file diff --git a/src/stores/YARGStateStore.ts b/src/stores/YARGStateStore.ts deleted file mode 100644 index df9a9bd..0000000 --- a/src/stores/YARGStateStore.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { YARGStates } from "@app/hooks/useYARGVersion"; -import { create } from "zustand"; - -interface YARGStateStore { - states: { - [key: string]: YARGStates - }, - update: (key: string, state: YARGStates) => void -} - -const useYARGStateStore = create()((set) => ({ - states: {}, - update(key, state) { - return set(current => ({ - states: { - ...current.states, - [key]: state - } - })); - }, -})); - -interface useYARGStateInterface { - state: YARGStates; - setState: (newState: YARGStates) => void; -} - -export const useYARGState = (version?: string): useYARGStateInterface => { - const store = useYARGStateStore(); - - // If we don't have a version yet, return a dummy loading version; - if (!version) { - return { - state: YARGStates.LOADING, - setState: () => {} - }; - } - - const state = store.states[version]; - const setState = (newState: YARGStates) => store.update(version, newState); - - return { state, setState }; -}; \ No newline at end of file diff --git a/src/styles.css b/src/styles.css index 688253a..4a08ca6 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,4 +1,7 @@ :root { + --backupFonts: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji"; + --accent-rgb: 46, 217, 255; --accent: rgb(var(--accent-rgb)); @@ -9,38 +12,32 @@ --primary-40: rgba(var(--primary-rgb), 0.4); --primary-75: rgba(var(--primary-rgb), 0.75); - --white-background: #F5F5F5; - - --green-rgb: 70, 231, 79; - --green: rgb(var(--green-rgb)); - --green-10: rgba(var(--green-rgb), .1); + --titleBarHeight: 35px; - --titleBar_background: #000209; - --titleBar_primary: #FFF; - --titleBar_accent: #868AA8; - --titleBar_height: 30px; + --sideBarBackground: #070810; + --sideBarWidth: 300px; - --sideBar_background: #05060B; - --sideBar_separator_color: #394452; - --sideBar_selection: rgba(255, 255, 255, 0.1); + --buttonGreen: #17E289; + --buttonBlue: #2ED9FF; + --buttonYellow: #FCD548; + --buttonDark: #0F121D; + --buttonLight: #F4F4F4; + --buttonRed: #F32B37; - --button_green: #46E74F; - --button_blue: #2ED9FF; - --button_yellow: #FFB800; - --button_gray: #E6E6E6; - --button_red: #F32B37; + --buttonGreenText: #005832; + --buttonBlueText: #03596B; + --buttonYellowText: #4F2600; + --buttonDarkText: #E0E1E7; + --buttonLightText: #41475F; + --buttonRedText: #FFF; - --buttonText_green: #003C03; - --buttonText_blue: #002A3D; - --buttonText_yellow: #351A00; - --buttonText_gray: #737373; - --buttonText_red: #FFF; + --buttonLightBorder: rgba(255, 255, 255, 0.4); + --buttonDarkBorder: #222638; } * { box-sizing: border-box; - font-family: "Inter", -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, - sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'; + font-family: "Inter", var(--backupFonts); } body { @@ -54,35 +51,11 @@ a { text-decoration: none; } -.titlebar { - height: 30px; - background: #329ea3; - user-select: none; - display: flex; - justify-content: flex-end; - position: fixed; - top: 0; - left: 0; - right: 0; -} - -.titlebar-button { - display: inline-flex; - justify-content: center; - align-items: center; - width: 30px; - height: 30px; -} - -.titlebar-button:hover { - background: #5bbec3; -} - #root { width: 100%; height: 100%; display: flex; - padding-top: var(--titleBar_height); + padding-top: var(--titleBarHeight); } #content { @@ -93,6 +66,7 @@ a { ::-webkit-scrollbar { width: 10px; + height: 10px; } ::-webkit-scrollbar-track { @@ -101,8 +75,9 @@ a { ::-webkit-scrollbar-thumb { background: #888; + border-radius: 5px; } ::-webkit-scrollbar-thumb:hover { background: #666; -} \ No newline at end of file +} diff --git a/src/tasks/Processors/DownloadAndInstallTask.tsx b/src/tasks/Processors/DownloadAndInstallTask.tsx new file mode 100644 index 0000000..c429f7e --- /dev/null +++ b/src/tasks/Processors/DownloadAndInstallTask.tsx @@ -0,0 +1,58 @@ +import { ActiveProfile } from "@app/profiles/types"; +import { BaseTask, IBaseTask } from "./base"; +import { invoke } from "@tauri-apps/api"; +import { ReactNode } from "react"; +import QueueEntry from "@app/components/Queue/QueueEntry"; +import { localizeObject } from "@app/utils/localized"; +import { showErrorDialog } from "@app/dialogs"; +import ProfileIcon from "@app/components/ProfileIcon"; + +export class DownloadAndInstallTask extends BaseTask implements IBaseTask { + onFinish?: () => void; + + tempPath: string; + + constructor(profile: ActiveProfile, profilePath: string, tempPath: string, onFinish?: () => void) { + super(profile, profilePath); + + this.onFinish = onFinish; + + this.tempPath = tempPath; + } + + async start(): Promise { + try { + await invoke("download_and_install_profile", { + profilePath: this.profilePath, + uuid: this.activeProfile.uuid, + tag: this.activeProfile.version.tag, + tempPath: this.tempPath, + content: this.activeProfile.version.content + }); + } catch (e) { + showErrorDialog(e as string); + } + } + + getQueueEntry(bannerMode: boolean): ReactNode { + const profile = this.activeProfile.profile; + if (profile.type === "application") { + const metadata = localizeObject(profile.metadata, "en-US"); + + return } + bannerMode={bannerMode} />; + } else { + const metadata = localizeObject(profile.metadata, "en-US"); + + return } + bannerMode={bannerMode} />; + } + } +} diff --git a/src/tasks/Processors/Setlist.tsx b/src/tasks/Processors/Setlist.tsx deleted file mode 100644 index b3cf974..0000000 --- a/src/tasks/Processors/Setlist.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { invoke } from "@tauri-apps/api/tauri"; -import { BaseTask, IBaseTask } from "./base"; -import SetlistQueue from "@app/components/Queue/QueueEntry/Setlist"; - -export abstract class SetlistTask extends BaseTask { - profile: string; - version: string; - onFinish: () => void; - - constructor(profile: string, version: string, onFinish: () => void) { - super("setlist", profile); - - this.profile = profile; - this.version = version; - this.onFinish = onFinish; - } - - getQueueEntry(bannerMode: boolean): React.ReactNode { - return ; - } -} - -export class SetlistDownload extends SetlistTask implements IBaseTask { - zipUrls: string[]; - - constructor(zipUrls: string[], profile: string, version: string, onFinish: () => void) { - super(profile, version, onFinish); - - this.zipUrls = zipUrls; - } - - async start(): Promise { - return await invoke("download_and_install", { - appName: "official_setlist", - version: this.version, - profile: this.profile, - zipUrls: this.zipUrls, - sigUrls: [], - }); - } -} - -export class SetlistUninstall extends SetlistTask implements IBaseTask { - constructor(profile: string, version: string, onFinish: () => void) { - super(profile, version, onFinish); - } - - async start(): Promise { - return await invoke("uninstall", { - appName: "official_setlist", - version: this.version, - profile: this.profile - }); - } -} \ No newline at end of file diff --git a/src/tasks/Processors/UninstallTask.tsx b/src/tasks/Processors/UninstallTask.tsx new file mode 100644 index 0000000..2c35d1c --- /dev/null +++ b/src/tasks/Processors/UninstallTask.tsx @@ -0,0 +1,47 @@ +import { ActiveProfile } from "@app/profiles/types"; +import { BaseTask, IBaseTask } from "./base"; +import { invoke } from "@tauri-apps/api"; +import { ReactNode } from "react"; +import QueueEntry from "@app/components/Queue/QueueEntry"; +import { localizeObject } from "@app/utils/localized"; +import { showErrorDialog } from "@app/dialogs"; +import ProfileIcon from "@app/components/ProfileIcon"; + +export class UninstallTask extends BaseTask implements IBaseTask { + onFinish?: () => void; + + constructor(profile: ActiveProfile, profilePath: string, onFinish?: () => void) { + super(profile, profilePath); + this.onFinish = onFinish; + } + + async start(): Promise { + try { + await invoke("uninstall_profile", { + profilePath: this.profilePath + }); + } catch (e) { + showErrorDialog(e as string); + } + } + + getQueueEntry(bannerMode: boolean): ReactNode { + const profile = this.activeProfile.profile; + if (profile.type === "application") { + const metadata = localizeObject(profile.metadata, "en-US"); + + return } + bannerMode={bannerMode} />; + } else { + const metadata = localizeObject(profile.metadata, "en-US"); + + return } + bannerMode={bannerMode} />; + } + } +} diff --git a/src/tasks/Processors/YARG.tsx b/src/tasks/Processors/YARG.tsx deleted file mode 100644 index 455db5a..0000000 --- a/src/tasks/Processors/YARG.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { invoke } from "@tauri-apps/api/tauri"; -import { BaseTask, IBaseTask } from "./base"; -import YARGQueue from "@app/components/Queue/QueueEntry/YARG"; -import { YARGChannels } from "@app/hooks/useYARGRelease"; - -export abstract class YARGTask extends BaseTask { - channel: YARGChannels; - version: string; - profile: string; - onFinish: () => void; - - constructor(channel: YARGChannels, version: string, profile: string, onFinish: () => void) { - super("yarg", profile); - - this.channel = channel; - this.version = version; - this.profile = profile; - this.onFinish = onFinish; - } - - getQueueEntry(bannerMode: boolean): React.ReactNode { - return ; - } -} - -export class YARGDownload extends YARGTask implements IBaseTask { - zipUrl: string; - sigUrl?: string; - - constructor(zipUrl: string, sigUrl: string | undefined, channel: YARGChannels, version: string, - profile: string, onFinish: () => void) { - - super(channel, version, profile, onFinish); - - this.zipUrl = zipUrl; - this.sigUrl = sigUrl; - } - - async start(): Promise { - let sigUrls: string[] = []; - if (this.sigUrl != null) { - sigUrls = [ this.sigUrl ]; - } - - return await invoke("download_and_install", { - appName: "yarg", - version: this.version, - profile: this.profile, - zipUrls: [ this.zipUrl ], - sigUrls: sigUrls, - }); - } -} - -export class YARGUninstall extends YARGTask implements IBaseTask { - constructor(channel: YARGChannels, version: string, profile: string, onFinish: () => void) { - super(channel, version, profile, onFinish); - } - - async start(): Promise { - return await invoke("uninstall", { - appName: "yarg", - version: this.version, - profile: this.profile - }); - } -} \ No newline at end of file diff --git a/src/tasks/Processors/base.ts b/src/tasks/Processors/base.ts index a5cdb0c..6df10bd 100644 --- a/src/tasks/Processors/base.ts +++ b/src/tasks/Processors/base.ts @@ -1,12 +1,12 @@ +import { ActiveProfile } from "@app/profiles/types"; import { v4 as generateUUID } from "uuid"; -export type TaskTag = "yarg" | "setlist"; - export interface IBaseTask { - startedAt?: Date, - taskUUID: string, - taskTag: TaskTag, - profile: string, + startedAt?: Date; + taskUUID: string; + + activeProfile: ActiveProfile; + profilePath: string; onFinish?: () => void; @@ -15,13 +15,16 @@ export interface IBaseTask { } export class BaseTask { + startedAt?: Date; taskUUID: string; - taskTag: TaskTag; - profile: string; - constructor(taskTag: TaskTag, profile: string) { + activeProfile: ActiveProfile; + profilePath: string; + + constructor(profile: ActiveProfile, profilePath: string) { this.taskUUID = generateUUID(); - this.taskTag = taskTag; - this.profile = profile; + + this.activeProfile = profile; + this.profilePath = profilePath; } -} \ No newline at end of file +} diff --git a/src/tasks/index.ts b/src/tasks/index.ts index 540030a..f594b90 100644 --- a/src/tasks/index.ts +++ b/src/tasks/index.ts @@ -1,19 +1,21 @@ import { useStore } from "zustand"; -import { IBaseTask, TaskTag } from "./Processors/base"; +import { IBaseTask } from "./Processors/base"; import QueueStore from "./queue"; -import { showErrorDialog } from "@app/dialogs/dialogUtil"; +import { showErrorDialog } from "@app/dialogs"; const addTask = (task: IBaseTask) => { QueueStore.add(task); - if(QueueStore.firstTask() === task) { + if (QueueStore.firstTask() === task) { processNextTask(); } }; const processNextTask = async () => { const next = QueueStore.next(); - if(!next) return; + if (!next) { + return; + } try { next.startedAt = new Date(); @@ -27,10 +29,10 @@ const processNextTask = async () => { processNextTask(); }; -const useTask = (tag: TaskTag, profile: string) => { +const useTask = (profileUUID: string) => { return useStore( QueueStore.store, - queue => QueueStore.findTask(queue, tag, profile) + queue => QueueStore.findTask(queue, profileUUID) ); }; @@ -41,4 +43,4 @@ const useCurrentTask = () => { ); }; -export { addTask, processNextTask, useTask, useCurrentTask }; \ No newline at end of file +export { addTask, processNextTask, useTask, useCurrentTask }; diff --git a/src/tasks/queue.ts b/src/tasks/queue.ts index aac004a..1571783 100644 --- a/src/tasks/queue.ts +++ b/src/tasks/queue.ts @@ -1,5 +1,5 @@ import { createStore } from "zustand/vanilla"; -import { IBaseTask, TaskTag } from "./Processors/base"; +import { IBaseTask } from "./Processors/base"; import { useStore } from "zustand"; type TaskQueueStore = Set; @@ -26,16 +26,18 @@ const remove = (task: IBaseTask) => { const next = () => { const current = firstTask(); - if(current?.startedAt) { + if (current?.startedAt) { remove(current); } return firstTask(); }; -const findTask = (queue: TaskQueueStore, tag: TaskTag, profile: string) => { - for(const task of queue) { - if(task.taskTag === tag && task.profile === profile) return task; +const findTask = (queue: TaskQueueStore, profileUUID: string) => { + for (const task of queue) { + if (task.activeProfile.uuid === profileUUID) { + return task; + } } return undefined; @@ -46,4 +48,4 @@ const useQueue = () => { }; export const QueueStore = { store, firstTask, add, remove, next, findTask, useQueue }; -export default QueueStore; \ No newline at end of file +export default QueueStore; diff --git a/src/utils/localized.ts b/src/utils/localized.ts new file mode 100644 index 0000000..096e84f --- /dev/null +++ b/src/utils/localized.ts @@ -0,0 +1,41 @@ +export type Localized = T & { + localeOverrides: { + [locales: string]: Partial + } +} + +export const localize = (obj: Localized, key: K, locale: string): T[K] => { + let value: T[K] = obj[key]; + + if (locale in obj.localeOverrides) { + const newValue = obj.localeOverrides[locale][key]; + if (newValue !== undefined) { + value = newValue as T[K]; + } + } + + return value; +}; + +export const localizeObject = (obj: Localized, locale: string): T => { + const { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + localeOverrides, + ...omittedObj + } = obj; + const newObj = omittedObj as T; + + if (locale in obj.localeOverrides) { + const override = obj.localeOverrides[locale]; + + let key: keyof T; + for (key in override) { + const value = override[key]; + if (value !== undefined) { + newObj[key] = value as T[keyof T]; + } + } + } + + return newObj; +}; diff --git a/src/utils/os.ts b/src/utils/os.ts new file mode 100644 index 0000000..38c6bb5 --- /dev/null +++ b/src/utils/os.ts @@ -0,0 +1,11 @@ +import { type } from "@tauri-apps/api/os"; + +export type OS = "windows" | "macos" | "linux"; + +export const getOS = async (): Promise => { + switch (await type()) { + case "Windows_NT": return "windows"; + case "Darwin": return "macos"; + case "Linux": return "linux"; + } +}; diff --git a/src/utils/safeUrl.ts b/src/utils/safeUrl.ts new file mode 100644 index 0000000..f7549e9 --- /dev/null +++ b/src/utils/safeUrl.ts @@ -0,0 +1,11 @@ +import { createAndShowDialog } from "@app/dialogs"; +import { LeavingLauncherDialog } from "@app/dialogs/Dialogs/LeavingLauncherDialog"; +import { open } from "@tauri-apps/api/shell"; + +export async function askOpenUrl(url: string) { + await createAndShowDialog(LeavingLauncherDialog, { url }); +} + +export function openUrl(url: string) { + open(url); +} diff --git a/src/utils/timeFormat.ts b/src/utils/timeFormat.ts index ef40475..f1e6770 100644 --- a/src/utils/timeFormat.ts +++ b/src/utils/timeFormat.ts @@ -1,7 +1,13 @@ +import { intlFormatDistance } from "date-fns"; + export const millisToDisplayLength = (length: number, long = false) => { const date = new Date(length); if (long) { - return `${date.getMinutes()} min ${date.getSeconds()} sec`; + if (date.getHours() !== 0) { + return `${date.getHours()} hr ${date.getMinutes()} min ${date.getSeconds()} sec`; + } else { + return `${date.getMinutes()} min ${date.getSeconds()} sec`; + } } else { return new Intl.DateTimeFormat("en-US", { minute: "numeric", @@ -25,4 +31,8 @@ export const isConsideredNewRelease = (releaseDate: string, newestInSetlist: str } return true; -}; \ No newline at end of file +}; + +export const distanceFromToday = (initial: string) => { + return intlFormatDistance(new Date(initial), new Date()); +};