You've already forked AstralRinth
forked from didirus/AstralRinth
Switch to official launcher auth (#1118)
* Switch to official launcher auth * add debug info * Fix build
This commit is contained in:
@@ -20,7 +20,6 @@
|
||||
"ofetch": "^1.3.4",
|
||||
"omorphia": "^0.4.41",
|
||||
"pinia": "^2.1.7",
|
||||
"qrcode.vue": "^3.4.1",
|
||||
"tauri-plugin-window-state-api": "github:tauri-apps/tauri-plugin-window-state#v1",
|
||||
"vite-svg-loader": "^5.1.0",
|
||||
"vue": "^3.4.21",
|
||||
|
||||
284
theseus_gui/pnpm-lock.yaml
generated
284
theseus_gui/pnpm-lock.yaml
generated
@@ -26,9 +26,6 @@ dependencies:
|
||||
pinia:
|
||||
specifier: ^2.1.7
|
||||
version: 2.1.7(vue@3.4.21)
|
||||
qrcode.vue:
|
||||
specifier: ^3.4.1
|
||||
version: 3.4.1(vue@3.4.21)
|
||||
tauri-plugin-window-state-api:
|
||||
specifier: github:tauri-apps/tauri-plugin-window-state#v1
|
||||
version: github.com/tauri-apps/tauri-plugin-window-state/002cf15f6a1e4969a678a4ade680cd60477a8a53
|
||||
@@ -87,12 +84,12 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/@babel/helper-string-parser@7.24.1:
|
||||
resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==}
|
||||
/@babel/helper-string-parser@7.21.5:
|
||||
resolution: {integrity: sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
/@babel/helper-validator-identifier@7.22.20:
|
||||
resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
|
||||
/@babel/helper-validator-identifier@7.19.1:
|
||||
resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
/@babel/parser@7.24.4:
|
||||
@@ -100,14 +97,14 @@ packages:
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@babel/types': 7.24.0
|
||||
'@babel/types': 7.22.4
|
||||
|
||||
/@babel/types@7.24.0:
|
||||
resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
|
||||
/@babel/types@7.22.4:
|
||||
resolution: {integrity: sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.24.1
|
||||
'@babel/helper-validator-identifier': 7.22.20
|
||||
'@babel/helper-string-parser': 7.21.5
|
||||
'@babel/helper-validator-identifier': 7.19.1
|
||||
to-fast-properties: 2.0.0
|
||||
|
||||
/@esbuild/aix-ppc64@0.20.2:
|
||||
@@ -340,7 +337,7 @@ packages:
|
||||
debug: 4.3.4
|
||||
espree: 9.6.1
|
||||
globals: 13.24.0
|
||||
ignore: 5.3.1
|
||||
ignore: 5.2.4
|
||||
import-fresh: 3.3.0
|
||||
js-yaml: 4.1.0
|
||||
minimatch: 3.1.2
|
||||
@@ -354,12 +351,22 @@ packages:
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/@floating-ui/core@0.3.1:
|
||||
resolution: {integrity: sha512-ensKY7Ub59u16qsVIFEo2hwTCqZ/r9oZZFh51ivcLGHfUwTn8l1Xzng8RJUe91H/UP8PeqeBronAGx0qmzwk2g==}
|
||||
dev: false
|
||||
|
||||
/@floating-ui/core@1.6.0:
|
||||
resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==}
|
||||
dependencies:
|
||||
'@floating-ui/utils': 0.2.1
|
||||
dev: false
|
||||
|
||||
/@floating-ui/dom@0.1.10:
|
||||
resolution: {integrity: sha512-4kAVoogvQm2N0XE0G6APQJuCNuErjOfPW8Ux7DFxh8+AfugWflwVJ5LDlHOwrwut7z/30NUvdtHzQ3zSip4EzQ==}
|
||||
dependencies:
|
||||
'@floating-ui/core': 0.3.1
|
||||
dev: false
|
||||
|
||||
/@floating-ui/dom@1.1.1:
|
||||
resolution: {integrity: sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==}
|
||||
dependencies:
|
||||
@@ -415,7 +422,7 @@ packages:
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
'@nodelib/fs.scandir': 2.1.5
|
||||
fastq: 1.17.1
|
||||
fastq: 1.15.0
|
||||
dev: true
|
||||
|
||||
/@rollup/plugin-alias@5.1.0:
|
||||
@@ -438,120 +445,120 @@ packages:
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/@rollup/rollup-android-arm-eabi@4.14.0:
|
||||
resolution: {integrity: sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==}
|
||||
/@rollup/rollup-android-arm-eabi@4.14.1:
|
||||
resolution: {integrity: sha512-fH8/o8nSUek8ceQnT7K4EQbSiV7jgkHq81m9lWZFIXjJ7lJzpWXbQFpT/Zh6OZYnpFykvzC3fbEvEAFZu03dPA==}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-android-arm64@4.14.0:
|
||||
resolution: {integrity: sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==}
|
||||
/@rollup/rollup-android-arm64@4.14.1:
|
||||
resolution: {integrity: sha512-Y/9OHLjzkunF+KGEoJr3heiD5X9OLa8sbT1lm0NYeKyaM3oMhhQFvPB0bNZYJwlq93j8Z6wSxh9+cyKQaxS7PQ==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-darwin-arm64@4.14.0:
|
||||
resolution: {integrity: sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==}
|
||||
/@rollup/rollup-darwin-arm64@4.14.1:
|
||||
resolution: {integrity: sha512-+kecg3FY84WadgcuSVm6llrABOdQAEbNdnpi5X3UwWiFVhZIZvKgGrF7kmLguvxHNQy+UuRV66cLVl3S+Rkt+Q==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-darwin-x64@4.14.0:
|
||||
resolution: {integrity: sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==}
|
||||
/@rollup/rollup-darwin-x64@4.14.1:
|
||||
resolution: {integrity: sha512-2pYRzEjVqq2TB/UNv47BV/8vQiXkFGVmPFwJb+1E0IFFZbIX8/jo1olxqqMbo6xCXf8kabANhp5bzCij2tFLUA==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm-gnueabihf@4.14.0:
|
||||
resolution: {integrity: sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==}
|
||||
/@rollup/rollup-linux-arm-gnueabihf@4.14.1:
|
||||
resolution: {integrity: sha512-mS6wQ6Do6/wmrF9aTFVpIJ3/IDXhg1EZcQFYHZLHqw6AzMBjTHWnCG35HxSqUNphh0EHqSM6wRTT8HsL1C0x5g==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm64-gnu@4.14.0:
|
||||
resolution: {integrity: sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==}
|
||||
/@rollup/rollup-linux-arm64-gnu@4.14.1:
|
||||
resolution: {integrity: sha512-p9rGKYkHdFMzhckOTFubfxgyIO1vw//7IIjBBRVzyZebWlzRLeNhqxuSaZ7kCEKVkm/kuC9fVRW9HkC/zNRG2w==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm64-musl@4.14.0:
|
||||
resolution: {integrity: sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==}
|
||||
/@rollup/rollup-linux-arm64-musl@4.14.1:
|
||||
resolution: {integrity: sha512-nDY6Yz5xS/Y4M2i9JLQd3Rofh5OR8Bn8qe3Mv/qCVpHFlwtZSBYSPaU4mrGazWkXrdQ98GB//H0BirGR/SKFSw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-powerpc64le-gnu@4.14.0:
|
||||
resolution: {integrity: sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==}
|
||||
/@rollup/rollup-linux-powerpc64le-gnu@4.14.1:
|
||||
resolution: {integrity: sha512-im7HE4VBL+aDswvcmfx88Mp1soqL9OBsdDBU8NqDEYtkri0qV0THhQsvZtZeNNlLeCUQ16PZyv7cqutjDF35qw==}
|
||||
cpu: [ppc64le]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-riscv64-gnu@4.14.0:
|
||||
resolution: {integrity: sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==}
|
||||
/@rollup/rollup-linux-riscv64-gnu@4.14.1:
|
||||
resolution: {integrity: sha512-RWdiHuAxWmzPJgaHJdpvUUlDz8sdQz4P2uv367T2JocdDa98iRw2UjIJ4QxSyt077mXZT2X6pKfT2iYtVEvOFw==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-s390x-gnu@4.14.0:
|
||||
resolution: {integrity: sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==}
|
||||
/@rollup/rollup-linux-s390x-gnu@4.14.1:
|
||||
resolution: {integrity: sha512-VMgaGQ5zRX6ZqV/fas65/sUGc9cPmsntq2FiGmayW9KMNfWVG/j0BAqImvU4KTeOOgYSf1F+k6at1UfNONuNjA==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-x64-gnu@4.14.0:
|
||||
resolution: {integrity: sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==}
|
||||
/@rollup/rollup-linux-x64-gnu@4.14.1:
|
||||
resolution: {integrity: sha512-9Q7DGjZN+hTdJomaQ3Iub4m6VPu1r94bmK2z3UeWP3dGUecRC54tmVu9vKHTm1bOt3ASoYtEz6JSRLFzrysKlA==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-x64-musl@4.14.0:
|
||||
resolution: {integrity: sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==}
|
||||
/@rollup/rollup-linux-x64-musl@4.14.1:
|
||||
resolution: {integrity: sha512-JNEG/Ti55413SsreTguSx0LOVKX902OfXIKVg+TCXO6Gjans/k9O6ww9q3oLGjNDaTLxM+IHFMeXy/0RXL5R/g==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-win32-arm64-msvc@4.14.0:
|
||||
resolution: {integrity: sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==}
|
||||
/@rollup/rollup-win32-arm64-msvc@4.14.1:
|
||||
resolution: {integrity: sha512-ryS22I9y0mumlLNwDFYZRDFLwWh3aKaC72CWjFcFvxK0U6v/mOkM5Up1bTbCRAhv3kEIwW2ajROegCIQViUCeA==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-win32-ia32-msvc@4.14.0:
|
||||
resolution: {integrity: sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==}
|
||||
/@rollup/rollup-win32-ia32-msvc@4.14.1:
|
||||
resolution: {integrity: sha512-TdloItiGk+T0mTxKx7Hp279xy30LspMso+GzQvV2maYePMAWdmrzqSNZhUpPj3CGw12aGj57I026PgLCTu8CGg==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-win32-x64-msvc@4.14.0:
|
||||
resolution: {integrity: sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==}
|
||||
/@rollup/rollup-win32-x64-msvc@4.14.1:
|
||||
resolution: {integrity: sha512-wQGI+LY/Py20zdUPq+XCem7JcPOyzIJBm3dli+56DJsQOHbnXZFEwgmnC6el1TPAfC8lBT3m+z69RmLykNUbew==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
@@ -675,19 +682,23 @@ packages:
|
||||
engines: {node: '>=10.13.0'}
|
||||
dev: false
|
||||
|
||||
/@types/eslint@8.56.7:
|
||||
resolution: {integrity: sha512-SjDvI/x3zsZnOkYZ3lCt9lOZWZLB2jIlNKz+LBgCtDurK0JZcwucxYHn1w2BJkD34dgX9Tjnak0txtq4WTggEA==}
|
||||
/@types/eslint@8.40.0:
|
||||
resolution: {integrity: sha512-nbq2mvc/tBrK9zQQuItvjJl++GTN5j06DaPtp3hZCpngmG6Q3xoyEmd0TwZI0gAy/G1X0zhGBbr2imsGFdFV0g==}
|
||||
dependencies:
|
||||
'@types/estree': 1.0.5
|
||||
'@types/json-schema': 7.0.15
|
||||
'@types/estree': 1.0.1
|
||||
'@types/json-schema': 7.0.12
|
||||
dev: true
|
||||
|
||||
/@types/estree@1.0.1:
|
||||
resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
|
||||
dev: true
|
||||
|
||||
/@types/estree@1.0.5:
|
||||
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
|
||||
dev: true
|
||||
|
||||
/@types/json-schema@7.0.15:
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
/@types/json-schema@7.0.12:
|
||||
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
|
||||
dev: true
|
||||
|
||||
/@ungap/structured-clone@1.2.0:
|
||||
@@ -739,6 +750,10 @@ packages:
|
||||
'@vue/compiler-dom': 3.4.21
|
||||
'@vue/shared': 3.4.21
|
||||
|
||||
/@vue/devtools-api@6.5.0:
|
||||
resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==}
|
||||
dev: false
|
||||
|
||||
/@vue/devtools-api@6.6.1:
|
||||
resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
|
||||
dev: false
|
||||
@@ -823,8 +838,8 @@ packages:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
dev: true
|
||||
|
||||
/binary-extensions@2.3.0:
|
||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
/binary-extensions@2.2.0:
|
||||
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
@@ -865,8 +880,8 @@ packages:
|
||||
'@kurkle/color': 0.3.2
|
||||
dev: false
|
||||
|
||||
/chokidar@3.6.0:
|
||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||
/chokidar@3.5.3:
|
||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
@@ -1136,9 +1151,9 @@ packages:
|
||||
file-entry-cache: 6.0.1
|
||||
find-up: 5.0.0
|
||||
glob-parent: 6.0.2
|
||||
globals: 13.24.0
|
||||
globals: 13.20.0
|
||||
graphemer: 1.4.0
|
||||
ignore: 5.3.1
|
||||
ignore: 5.2.4
|
||||
imurmurhash: 0.1.4
|
||||
is-glob: 4.0.3
|
||||
is-path-inside: 3.0.3
|
||||
@@ -1203,8 +1218,8 @@ packages:
|
||||
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
||||
dev: true
|
||||
|
||||
/fastq@1.17.1:
|
||||
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
|
||||
/fastq@1.15.0:
|
||||
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
|
||||
dependencies:
|
||||
reusify: 1.0.4
|
||||
dev: true
|
||||
@@ -1213,7 +1228,7 @@ packages:
|
||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
dependencies:
|
||||
flat-cache: 3.2.0
|
||||
flat-cache: 3.0.4
|
||||
dev: true
|
||||
|
||||
/fill-range@7.0.1:
|
||||
@@ -1231,29 +1246,24 @@ packages:
|
||||
path-exists: 4.0.0
|
||||
dev: true
|
||||
|
||||
/flat-cache@3.2.0:
|
||||
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
|
||||
/flat-cache@3.0.4:
|
||||
resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
dependencies:
|
||||
flatted: 3.3.1
|
||||
keyv: 4.5.4
|
||||
flatted: 3.2.7
|
||||
rimraf: 3.0.2
|
||||
dev: true
|
||||
|
||||
/flatted@3.3.1:
|
||||
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
|
||||
/flatted@3.2.7:
|
||||
resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
|
||||
dev: true
|
||||
|
||||
/floating-vue@2.0.0(vue@3.4.21):
|
||||
resolution: {integrity: sha512-YSffLYOjoaaPPBZc7VQR2qMCQ7xeXuh7i8a2u8WOdSmkjTtKtZpj2aaJnLtZRHmehrMHyCgtSxLu8jFNNX2sVw==}
|
||||
/floating-vue@2.0.0-beta.20(vue@3.4.21):
|
||||
resolution: {integrity: sha512-N68otcpp6WwcYC7zP8GeJqNZVdfvS7tEY88lwmuAHeqRgnfWx1Un8enzLxROyVnBDZ3TwUoUdj5IFg+bUT7JeA==}
|
||||
peerDependencies:
|
||||
'@nuxt/kit': ^3.2.0
|
||||
vue: ^3.2.0
|
||||
peerDependenciesMeta:
|
||||
'@nuxt/kit':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.1.1
|
||||
'@floating-ui/dom': 0.1.10
|
||||
vue: 3.4.21
|
||||
vue-resize: 2.0.0-alpha.1(vue@3.4.21)
|
||||
dev: false
|
||||
@@ -1309,6 +1319,13 @@ packages:
|
||||
path-is-absolute: 1.0.1
|
||||
dev: true
|
||||
|
||||
/globals@13.20.0:
|
||||
resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
type-fest: 0.20.2
|
||||
dev: true
|
||||
|
||||
/globals@13.24.0:
|
||||
resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1325,18 +1342,18 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/highlight.js@11.9.0:
|
||||
resolution: {integrity: sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==}
|
||||
/highlight.js@11.8.0:
|
||||
resolution: {integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/ignore@5.3.1:
|
||||
resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
|
||||
/ignore@5.2.4:
|
||||
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
||||
engines: {node: '>= 4'}
|
||||
dev: true
|
||||
|
||||
/immutable@4.3.5:
|
||||
resolution: {integrity: sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==}
|
||||
/immutable@4.3.0:
|
||||
resolution: {integrity: sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==}
|
||||
dev: true
|
||||
|
||||
/import-fresh@3.3.0:
|
||||
@@ -1367,7 +1384,7 @@ packages:
|
||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
binary-extensions: 2.3.0
|
||||
binary-extensions: 2.2.0
|
||||
dev: true
|
||||
|
||||
/is-extglob@2.1.1:
|
||||
@@ -1403,10 +1420,6 @@ packages:
|
||||
argparse: 2.0.1
|
||||
dev: true
|
||||
|
||||
/json-buffer@3.0.1:
|
||||
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
|
||||
dev: true
|
||||
|
||||
/json-schema-traverse@0.4.1:
|
||||
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
||||
dev: true
|
||||
@@ -1415,12 +1428,6 @@ packages:
|
||||
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
||||
dev: true
|
||||
|
||||
/keyv@4.5.4:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
dev: true
|
||||
|
||||
/levn@0.4.1:
|
||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@@ -1463,8 +1470,8 @@ packages:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
|
||||
/markdown-it@13.0.2:
|
||||
resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==}
|
||||
/markdown-it@13.0.1:
|
||||
resolution: {integrity: sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
@@ -1540,17 +1547,16 @@ packages:
|
||||
dependencies:
|
||||
chart.js: 4.4.2
|
||||
dayjs: 1.11.10
|
||||
floating-vue: 2.0.0(vue@3.4.21)
|
||||
highlight.js: 11.9.0
|
||||
markdown-it: 13.0.2
|
||||
qrcode.vue: 3.4.1(vue@3.4.21)
|
||||
floating-vue: 2.0.0-beta.20(vue@3.4.21)
|
||||
highlight.js: 11.8.0
|
||||
markdown-it: 13.0.1
|
||||
qrcode.vue: 3.4.0(vue@3.4.21)
|
||||
vue: 3.4.21
|
||||
vue-chartjs: 5.3.0(chart.js@4.4.2)(vue@3.4.21)
|
||||
vue-chartjs: 5.3.1(chart.js@4.4.2)(vue@3.4.21)
|
||||
vue-router: 4.3.0(vue@3.4.21)
|
||||
vue-select: 4.0.0-beta.6(vue@3.4.21)
|
||||
xss: 1.0.15
|
||||
xss: 1.0.14
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
@@ -1628,9 +1634,9 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.6.1
|
||||
'@vue/devtools-api': 6.5.0
|
||||
vue: 3.4.21
|
||||
vue-demi: 0.14.7(vue@3.4.21)
|
||||
vue-demi: 0.14.5(vue@3.4.21)
|
||||
dev: false
|
||||
|
||||
/postcss-selector-parser@6.0.16:
|
||||
@@ -1660,13 +1666,13 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/punycode@2.3.1:
|
||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
/punycode@2.3.0:
|
||||
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/qrcode.vue@3.4.1(vue@3.4.21):
|
||||
resolution: {integrity: sha512-wq/zHsifH4FJ1GXQi8/wNxD1KfQkckIpjK1KPTc/qwYU5/Bkd4me0w4xZSg6EXk6xLBkVDE0zxVagewv5EMAVA==}
|
||||
/qrcode.vue@3.4.0(vue@3.4.21):
|
||||
resolution: {integrity: sha512-4XeImbv10Fin16Fl2DArCMhGyAdvIg2jb7vDT+hZiIAMg/6H6mz9nUZr/dR8jBcun5VzNzkiwKhiqOGbloinwA==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
dependencies:
|
||||
@@ -1709,28 +1715,28 @@ packages:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/rollup@4.14.0:
|
||||
resolution: {integrity: sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==}
|
||||
/rollup@4.14.1:
|
||||
resolution: {integrity: sha512-4LnHSdd3QK2pa1J6dFbfm1HN0D7vSK/ZuZTsdyUAlA6Rr1yTouUTL13HaDOGJVgby461AhrNGBS7sCGXXtT+SA==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@types/estree': 1.0.5
|
||||
optionalDependencies:
|
||||
'@rollup/rollup-android-arm-eabi': 4.14.0
|
||||
'@rollup/rollup-android-arm64': 4.14.0
|
||||
'@rollup/rollup-darwin-arm64': 4.14.0
|
||||
'@rollup/rollup-darwin-x64': 4.14.0
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.14.0
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.14.0
|
||||
'@rollup/rollup-linux-arm64-musl': 4.14.0
|
||||
'@rollup/rollup-linux-powerpc64le-gnu': 4.14.0
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.14.0
|
||||
'@rollup/rollup-linux-s390x-gnu': 4.14.0
|
||||
'@rollup/rollup-linux-x64-gnu': 4.14.0
|
||||
'@rollup/rollup-linux-x64-musl': 4.14.0
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.14.0
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.14.0
|
||||
'@rollup/rollup-win32-x64-msvc': 4.14.0
|
||||
'@rollup/rollup-android-arm-eabi': 4.14.1
|
||||
'@rollup/rollup-android-arm64': 4.14.1
|
||||
'@rollup/rollup-darwin-arm64': 4.14.1
|
||||
'@rollup/rollup-darwin-x64': 4.14.1
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.14.1
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.14.1
|
||||
'@rollup/rollup-linux-arm64-musl': 4.14.1
|
||||
'@rollup/rollup-linux-powerpc64le-gnu': 4.14.1
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.14.1
|
||||
'@rollup/rollup-linux-s390x-gnu': 4.14.1
|
||||
'@rollup/rollup-linux-x64-gnu': 4.14.1
|
||||
'@rollup/rollup-linux-x64-musl': 4.14.1
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.14.1
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.14.1
|
||||
'@rollup/rollup-win32-x64-msvc': 4.14.1
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
@@ -1745,9 +1751,9 @@ packages:
|
||||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
chokidar: 3.6.0
|
||||
immutable: 4.3.5
|
||||
source-map-js: 1.2.0
|
||||
chokidar: 3.5.3
|
||||
immutable: 4.3.0
|
||||
source-map-js: 1.0.2
|
||||
dev: true
|
||||
|
||||
/semver@7.6.0:
|
||||
@@ -1775,6 +1781,11 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/source-map-js@1.0.2:
|
||||
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/source-map-js@1.2.0:
|
||||
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -1798,8 +1809,8 @@ packages:
|
||||
has-flag: 4.0.0
|
||||
dev: true
|
||||
|
||||
/svgo@3.2.0:
|
||||
resolution: {integrity: sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==}
|
||||
/svgo@3.0.2:
|
||||
resolution: {integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
@@ -1807,7 +1818,6 @@ packages:
|
||||
commander: 7.2.0
|
||||
css-select: 5.1.0
|
||||
css-tree: 2.3.1
|
||||
css-what: 6.1.0
|
||||
csso: 5.0.5
|
||||
picocolors: 1.0.0
|
||||
dev: false
|
||||
@@ -1850,7 +1860,7 @@ packages:
|
||||
/uri-js@4.4.1:
|
||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
punycode: 2.3.0
|
||||
dev: true
|
||||
|
||||
/util-deprecate@1.0.2:
|
||||
@@ -1864,7 +1874,7 @@ packages:
|
||||
vite: '>=2'
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 4.2.1
|
||||
'@types/eslint': 8.56.7
|
||||
'@types/eslint': 8.40.0
|
||||
eslint: 8.57.0
|
||||
rollup: 2.79.1
|
||||
vite: 5.2.8(sass@1.74.1)
|
||||
@@ -1875,7 +1885,7 @@ packages:
|
||||
peerDependencies:
|
||||
vue: '>=3.2.13'
|
||||
dependencies:
|
||||
svgo: 3.2.0
|
||||
svgo: 3.0.2
|
||||
vue: 3.4.21
|
||||
dev: false
|
||||
|
||||
@@ -1909,14 +1919,14 @@ packages:
|
||||
dependencies:
|
||||
esbuild: 0.20.2
|
||||
postcss: 8.4.38
|
||||
rollup: 4.14.0
|
||||
rollup: 4.14.1
|
||||
sass: 1.74.1
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/vue-chartjs@5.3.0(chart.js@4.4.2)(vue@3.4.21):
|
||||
resolution: {integrity: sha512-8XqX0JU8vFZ+WA2/knz4z3ThClduni2Nm0BMe2u0mXgTfd9pXrmJ07QBI+WAij5P/aPmPMX54HCE1seWL37ZdQ==}
|
||||
/vue-chartjs@5.3.1(chart.js@4.4.2)(vue@3.4.21):
|
||||
resolution: {integrity: sha512-rZjqcHBxKiHrBl0CIvcOlVEBwRhpWAVf6rDU3vUfa7HuSRmGtCslc0Oc8m16oAVuk0erzc1FCtH1VCriHsrz+A==}
|
||||
peerDependencies:
|
||||
chart.js: ^4.1.1
|
||||
vue: ^3.0.0-0 || ^2.7.0
|
||||
@@ -1925,8 +1935,8 @@ packages:
|
||||
vue: 3.4.21
|
||||
dev: false
|
||||
|
||||
/vue-demi@0.14.7(vue@3.4.21):
|
||||
resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
|
||||
/vue-demi@0.14.5(vue@3.4.21):
|
||||
resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
@@ -2038,8 +2048,8 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/xss@1.0.15:
|
||||
resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==}
|
||||
/xss@1.0.14:
|
||||
resolution: {integrity: sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
use crate::api::Result;
|
||||
use chrono::{Duration, Utc};
|
||||
use tauri::plugin::TauriPlugin;
|
||||
use theseus::{hydra::init::DeviceLoginSuccess, prelude::*};
|
||||
use tauri::Manager;
|
||||
use theseus::prelude::*;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("auth")
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
auth_authenticate_begin_flow,
|
||||
auth_authenticate_await_completion,
|
||||
auth_cancel_flow,
|
||||
auth_refresh,
|
||||
auth_get_default_user,
|
||||
auth_set_default_user,
|
||||
auth_remove_user,
|
||||
auth_has_user,
|
||||
auth_users,
|
||||
auth_get_user,
|
||||
])
|
||||
@@ -20,47 +19,73 @@ pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
/// Authenticate a user with Hydra - part 1
|
||||
/// This begins the authentication flow quasi-synchronously, returning a URL to visit (that the user will sign in at)
|
||||
#[tauri::command]
|
||||
pub async fn auth_authenticate_begin_flow() -> Result<DeviceLoginSuccess> {
|
||||
Ok(auth::authenticate_begin_flow().await?)
|
||||
}
|
||||
pub async fn auth_login(app: tauri::AppHandle) -> Result<Option<Credentials>> {
|
||||
let flow = minecraft_auth::begin_login().await?;
|
||||
|
||||
/// Authenticate a user with Hydra - part 2
|
||||
/// This completes the authentication flow quasi-synchronously, returning the sign-in credentials
|
||||
/// (and also adding the credentials to the state)
|
||||
#[tauri::command]
|
||||
pub async fn auth_authenticate_await_completion() -> Result<Credentials> {
|
||||
Ok(auth::authenticate_await_complete_flow().await?)
|
||||
}
|
||||
let start = Utc::now();
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn auth_cancel_flow() -> Result<()> {
|
||||
Ok(auth::cancel_flow().await?)
|
||||
}
|
||||
if let Some(window) = app.get_window("signin") {
|
||||
window.close()?;
|
||||
}
|
||||
|
||||
/// Refresh some credentials using Hydra, if needed
|
||||
// invoke('plugin:auth|auth_refresh',user)
|
||||
#[tauri::command]
|
||||
pub async fn auth_refresh(user: uuid::Uuid) -> Result<Credentials> {
|
||||
Ok(auth::refresh(user).await?)
|
||||
}
|
||||
let window = tauri::WindowBuilder::new(
|
||||
&app,
|
||||
"signin",
|
||||
tauri::WindowUrl::External(flow.redirect_uri.parse().map_err(
|
||||
|_| {
|
||||
theseus::ErrorKind::OtherError(
|
||||
"Error parsing auth redirect URL".to_string(),
|
||||
)
|
||||
.as_error()
|
||||
},
|
||||
)?),
|
||||
)
|
||||
.title("Sign into Modrinth")
|
||||
.build()?;
|
||||
|
||||
while (Utc::now() - start) < Duration::minutes(10) {
|
||||
if window
|
||||
.url()
|
||||
.as_str()
|
||||
.starts_with("https://login.live.com/oauth20_desktop.srf")
|
||||
{
|
||||
if let Some((_, code)) =
|
||||
window.url().query_pairs().find(|x| x.0 == "code")
|
||||
{
|
||||
window.close()?;
|
||||
let val =
|
||||
minecraft_auth::finish_login(&code.clone(), flow).await?;
|
||||
|
||||
return Ok(Some(val));
|
||||
}
|
||||
}
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
|
||||
}
|
||||
|
||||
window.close()?;
|
||||
Ok(None)
|
||||
}
|
||||
#[tauri::command]
|
||||
pub async fn auth_remove_user(user: uuid::Uuid) -> Result<()> {
|
||||
Ok(auth::remove_user(user).await?)
|
||||
Ok(minecraft_auth::remove_user(user).await?)
|
||||
}
|
||||
|
||||
/// Check if a user exists in Theseus
|
||||
// invoke('plugin:auth|auth_has_user',user)
|
||||
#[tauri::command]
|
||||
pub async fn auth_has_user(user: uuid::Uuid) -> Result<bool> {
|
||||
Ok(auth::has_user(user).await?)
|
||||
pub async fn auth_get_default_user() -> Result<Option<uuid::Uuid>> {
|
||||
Ok(minecraft_auth::get_default_user().await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn auth_set_default_user(user: uuid::Uuid) -> Result<()> {
|
||||
Ok(minecraft_auth::set_default_user(user).await?)
|
||||
}
|
||||
|
||||
/// Get a copy of the list of all user credentials
|
||||
// invoke('plugin:auth|auth_users',user)
|
||||
#[tauri::command]
|
||||
pub async fn auth_users() -> Result<Vec<Credentials>> {
|
||||
Ok(auth::users().await?)
|
||||
Ok(minecraft_auth::users().await?)
|
||||
}
|
||||
|
||||
/// Get a user from the UUID
|
||||
@@ -68,5 +93,5 @@ pub async fn auth_users() -> Result<Vec<Credentials>> {
|
||||
// invoke('plugin:auth|auth_users',user)
|
||||
#[tauri::command]
|
||||
pub async fn auth_get_user(user: uuid::Uuid) -> Result<Credentials> {
|
||||
Ok(auth::get_user(user).await?)
|
||||
Ok(minecraft_auth::get_user(user).await?)
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ pub enum TheseusSerializableError {
|
||||
#[error("IO error: {0}")]
|
||||
IO(#[from] std::io::Error),
|
||||
|
||||
#[error("Tauri error: {0}")]
|
||||
Tauri(#[from] tauri::Error),
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[error("Callback error: {0}")]
|
||||
Callback(String),
|
||||
@@ -88,9 +91,12 @@ macro_rules! impl_serialize {
|
||||
#[cfg(target_os = "macos")]
|
||||
impl_serialize! {
|
||||
IO,
|
||||
Tauri,
|
||||
Callback
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
impl_serialize! {
|
||||
IO,
|
||||
Tauri,
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ fn main() {
|
||||
initialize_state,
|
||||
is_dev,
|
||||
toggle_decorations,
|
||||
api::auth::auth_login,
|
||||
]);
|
||||
|
||||
builder
|
||||
|
||||
@@ -20,6 +20,7 @@ import { get } from '@/helpers/settings'
|
||||
import Breadcrumbs from '@/components/ui/Breadcrumbs.vue'
|
||||
import RunningAppBar from '@/components/ui/RunningAppBar.vue'
|
||||
import SplashScreen from '@/components/ui/SplashScreen.vue'
|
||||
import ErrorModal from '@/components/ui/ErrorModal.vue'
|
||||
import ModrinthLoadingIndicator from '@/components/modrinth-loading-indicator'
|
||||
import { handleError, useNotifications } from '@/store/notifications.js'
|
||||
import { offline_listener, command_listener, warning_listener } from '@/helpers/events.js'
|
||||
@@ -40,15 +41,14 @@ import { TauriEvent } from '@tauri-apps/api/event'
|
||||
import { await_sync, check_safe_loading_bars_complete } from './helpers/state'
|
||||
import { confirm } from '@tauri-apps/api/dialog'
|
||||
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
||||
import StickyTitleBar from '@/components/ui/tutorial/StickyTitleBar.vue'
|
||||
import OnboardingScreen from '@/components/ui/tutorial/OnboardingScreen.vue'
|
||||
import { install_from_file } from './helpers/pack'
|
||||
import { useError } from '@/store/error.js'
|
||||
|
||||
const themeStore = useTheming()
|
||||
const urlModal = ref(null)
|
||||
const isLoading = ref(true)
|
||||
|
||||
const videoPlaying = ref(false)
|
||||
const offline = ref(false)
|
||||
const showOnboarding = ref(false)
|
||||
const nativeDecorations = ref(false)
|
||||
@@ -71,7 +71,6 @@ defineExpose({
|
||||
} = await get()
|
||||
// video should play if the user is not on linux, and has not onboarded
|
||||
os.value = await getOS()
|
||||
videoPlaying.value = !fully_onboarded && os.value !== 'Linux'
|
||||
const dev = await isDev()
|
||||
const version = await getVersion()
|
||||
showOnboarding.value = !fully_onboarded
|
||||
@@ -180,12 +179,19 @@ const isOnBrowse = computed(() => route.path.startsWith('/browse'))
|
||||
const loading = useLoading()
|
||||
|
||||
const notifications = useNotifications()
|
||||
const notificationsWrapper = ref(null)
|
||||
const notificationsWrapper = ref()
|
||||
|
||||
watch(notificationsWrapper, () => {
|
||||
notifications.setNotifs(notificationsWrapper.value)
|
||||
})
|
||||
|
||||
const error = useError()
|
||||
const errorModal = ref()
|
||||
|
||||
watch(errorModal, () => {
|
||||
error.setErrorModal(errorModal.value)
|
||||
})
|
||||
|
||||
document.querySelector('body').addEventListener('click', function (e) {
|
||||
let target = e.target
|
||||
while (target != null) {
|
||||
@@ -245,15 +251,6 @@ command_listener(async (e) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<StickyTitleBar v-if="videoPlaying" />
|
||||
<video
|
||||
v-if="videoPlaying"
|
||||
ref="onboardingVideo"
|
||||
class="video"
|
||||
src="@/assets/video.mp4"
|
||||
autoplay
|
||||
@ended="videoPlaying = false"
|
||||
/>
|
||||
<div v-if="failureText" class="failure dark-mode">
|
||||
<div class="appbar-failure dark-mode">
|
||||
<Button v-if="os != 'MacOS'" icon-only @click="TauriWindow.getCurrent().close()">
|
||||
@@ -294,7 +291,7 @@ command_listener(async (e) => {
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<SplashScreen v-else-if="!videoPlaying && isLoading" app-loading />
|
||||
<SplashScreen v-else-if="isLoading" app-loading />
|
||||
<OnboardingScreen v-else-if="showOnboarding" :finish="() => (showOnboarding = false)" />
|
||||
<div v-else class="container">
|
||||
<div class="nav-container">
|
||||
@@ -389,6 +386,7 @@ command_listener(async (e) => {
|
||||
</div>
|
||||
<URLConfirmModal ref="urlModal" />
|
||||
<Notifications ref="notificationsWrapper" />
|
||||
<ErrorModal ref="errorModal" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
Binary file not shown.
@@ -56,68 +56,22 @@
|
||||
</Button>
|
||||
</Card>
|
||||
</transition>
|
||||
<Modal ref="loginModal" class="modal" header="Signing in" :noblur="!themeStore.advancedRendering">
|
||||
<div class="modal-body">
|
||||
<QrcodeVue :value="loginUrl" class="qr-code" margin="3" size="160" />
|
||||
<div class="modal-text">
|
||||
<div class="label">Copy this code</div>
|
||||
<div class="code-text">
|
||||
<div class="code">
|
||||
{{ loginCode }}
|
||||
</div>
|
||||
<Button
|
||||
v-tooltip="'Copy code'"
|
||||
icon-only
|
||||
large
|
||||
color="raised"
|
||||
@click="() => clipboardWrite(loginCode)"
|
||||
>
|
||||
<ClipboardCopyIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<div>And enter it on Microsoft's website to sign in.</div>
|
||||
<div class="iconified-input">
|
||||
<LogInIcon />
|
||||
<input type="text" :value="loginUrl" readonly />
|
||||
<Button
|
||||
v-tooltip="'Open link'"
|
||||
icon-only
|
||||
color="raised"
|
||||
@click="() => clipboardWrite(loginUrl)"
|
||||
>
|
||||
<GlobeIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
Card,
|
||||
PlusIcon,
|
||||
TrashIcon,
|
||||
LogInIcon,
|
||||
Modal,
|
||||
GlobeIcon,
|
||||
ClipboardCopyIcon,
|
||||
} from 'omorphia'
|
||||
import { Avatar, Button, Card, PlusIcon, TrashIcon, LogInIcon } from 'omorphia'
|
||||
import { ref, computed, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
|
||||
import {
|
||||
users,
|
||||
remove_user,
|
||||
authenticate_begin_flow,
|
||||
authenticate_await_completion,
|
||||
set_default_user,
|
||||
login as login_flow,
|
||||
get_default_user,
|
||||
} from '@/helpers/auth'
|
||||
import { get, set } from '@/helpers/settings'
|
||||
import { handleError } from '@/store/state.js'
|
||||
import { useTheming } from '@/store/theme.js'
|
||||
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import QrcodeVue from 'qrcode.vue'
|
||||
import { process_listener } from '@/helpers/events'
|
||||
import { handleSevereError } from '@/store/error.js'
|
||||
|
||||
defineProps({
|
||||
mode: {
|
||||
@@ -129,16 +83,11 @@ defineProps({
|
||||
|
||||
const emit = defineEmits(['change'])
|
||||
|
||||
const loginCode = ref(null)
|
||||
|
||||
const themeStore = useTheming()
|
||||
const settings = ref({})
|
||||
const accounts = ref([])
|
||||
const loginUrl = ref('')
|
||||
const loginModal = ref(null)
|
||||
const accounts = ref({})
|
||||
const defaultUser = ref()
|
||||
|
||||
async function refreshValues() {
|
||||
settings.value = await get().catch(handleError)
|
||||
defaultUser.value = await get_default_user().catch(handleError)
|
||||
accounts.value = await users().catch(handleError)
|
||||
}
|
||||
defineExpose({
|
||||
@@ -147,46 +96,27 @@ defineExpose({
|
||||
await refreshValues()
|
||||
|
||||
const displayAccounts = computed(() =>
|
||||
accounts.value.filter((account) => settings.value.default_user !== account.id),
|
||||
accounts.value.filter((account) => defaultUser.value !== account.id),
|
||||
)
|
||||
|
||||
const selectedAccount = computed(() =>
|
||||
accounts.value.find((account) => account.id === settings.value.default_user),
|
||||
accounts.value.find((account) => account.id === defaultUser.value),
|
||||
)
|
||||
|
||||
async function setAccount(account) {
|
||||
settings.value.default_user = account.id
|
||||
await set(settings.value).catch(handleError)
|
||||
defaultUser.value = account.id
|
||||
await set_default_user(account.id).catch(handleError)
|
||||
emit('change')
|
||||
}
|
||||
|
||||
const clipboardWrite = async (a) => {
|
||||
navigator.clipboard.writeText(a)
|
||||
}
|
||||
|
||||
async function login() {
|
||||
const loginSuccess = await authenticate_begin_flow().catch(handleError)
|
||||
|
||||
loginModal.value.show()
|
||||
loginCode.value = loginSuccess.user_code
|
||||
loginUrl.value = loginSuccess.verification_uri
|
||||
await window.__TAURI_INVOKE__('tauri', {
|
||||
__tauriModule: 'Shell',
|
||||
message: {
|
||||
cmd: 'open',
|
||||
path: loginSuccess.verification_uri,
|
||||
},
|
||||
})
|
||||
|
||||
const loggedIn = await authenticate_await_completion().catch(handleError)
|
||||
loginModal.value.hide()
|
||||
const loggedIn = await login_flow().catch(handleSevereError)
|
||||
|
||||
if (loggedIn) {
|
||||
await setAccount(loggedIn)
|
||||
await refreshValues()
|
||||
}
|
||||
|
||||
loginModal.value.hide()
|
||||
mixpanel_track('AccountLogIn')
|
||||
}
|
||||
|
||||
|
||||
125
theseus_gui/src/components/ui/ErrorModal.vue
Normal file
125
theseus_gui/src/components/ui/ErrorModal.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<script setup>
|
||||
import { Modal, XIcon } from 'omorphia'
|
||||
import { ChatIcon } from '@/assets/icons'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const errorModal = ref()
|
||||
const error = ref()
|
||||
|
||||
const title = ref('An error occurred')
|
||||
const errorType = ref('unknown')
|
||||
const supportLink = ref('https://support.modrinth.com')
|
||||
const metadata = ref({})
|
||||
|
||||
defineExpose({
|
||||
async show(errorVal) {
|
||||
if (errorVal.message && errorVal.message.includes('Minecraft authentication error:')) {
|
||||
title.value = 'Unable to sign in to Minecraft'
|
||||
errorType.value = 'minecraft_auth'
|
||||
supportLink.value =
|
||||
'https://support.modrinth.com/en/articles/9038231-minecraft-sign-in-issues'
|
||||
|
||||
if (errorVal.message.includes('existing connection was forcibly closed')) {
|
||||
metadata.value.network = true
|
||||
}
|
||||
if (errorVal.message.includes('because the target machine actively refused it')) {
|
||||
metadata.value.hostsFile = true
|
||||
}
|
||||
} else {
|
||||
title.value = 'An error occurred'
|
||||
errorType.value = 'unknown'
|
||||
supportLink.value = 'https://support.modrinth.com'
|
||||
metadata.value = {}
|
||||
}
|
||||
|
||||
error.value = errorVal
|
||||
errorModal.value.show()
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal ref="errorModal" :header="title">
|
||||
<div class="modal-body">
|
||||
<div class="markdown-body">
|
||||
<template v-if="errorType === 'minecraft_auth'">
|
||||
<p>
|
||||
Signing into Microsoft account is a complex task for the launchers, and there are a lot
|
||||
of things can go wrong.
|
||||
</p>
|
||||
<template v-if="metadata.network">
|
||||
<h3>Network issues</h3>
|
||||
<p>
|
||||
It looks like there were issues with the Modrinth App connecting to Microsoft's
|
||||
servers. This is often the result of a poor connection, so we recommend trying again
|
||||
to see if it works. If issues continue to persist, follow the steps in
|
||||
<a
|
||||
href="https://support.modrinth.com/en/articles/9038231-minecraft-sign-in-issues#h_e71a5f805f"
|
||||
>
|
||||
our support article
|
||||
</a>
|
||||
to troubleshoot.
|
||||
</p>
|
||||
</template>
|
||||
<template v-else-if="metadata.hostsFile">
|
||||
<h3>Network issues</h3>
|
||||
<p>
|
||||
The Modrinth App tried to connect to Microsoft / Xbox / Minecraft services, but the
|
||||
remote server rejected the connection. This may indicate that these services are
|
||||
blocked by the hosts file. Please visit
|
||||
<a
|
||||
href="https://support.modrinth.com/en/articles/9038231-minecraft-sign-in-issues#h_d694a29256"
|
||||
>
|
||||
our support article
|
||||
</a>
|
||||
for steps on how to fix the issue.
|
||||
</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<h3>Make sure you are signing into the right Microsoft account</h3>
|
||||
<p>
|
||||
More often than not, this error is caused by you signing into an incorrect Microsoft
|
||||
account which isn't linked to Minecraft. Double check and try again!
|
||||
</p>
|
||||
<h3>Try signing in and launching through the official launcher first</h3>
|
||||
<p>
|
||||
If you just bought Minecraft, are coming from the Bedrock Edition world and have never
|
||||
played Java before, or just subscribed to PC Game Pass, you would need to start the
|
||||
game at least once using the
|
||||
<a href="https://www.minecraft.net/en-us/download">official Minecraft Launcher</a>.
|
||||
Once you're done, come back here and sign in!
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<hr />
|
||||
<p>
|
||||
If nothing is working and you need help, visit
|
||||
<a :href="supportLink">our support page</a>
|
||||
and start a chat using the widget in the bottom right and we will be more than happy to
|
||||
assist!
|
||||
</p>
|
||||
<details>
|
||||
<summary>Debug info</summary>
|
||||
{{ error.message ?? error }}
|
||||
</details>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ error.message ?? error }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="input-group push-right">
|
||||
<a :href="supportLink" class="btn" @click="errorModal.hide()"><ChatIcon /> Get support</a>
|
||||
<button class="btn btn-primary" @click="errorModal.hide()"><XIcon /> Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.modal-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-md);
|
||||
padding: var(--gap-lg);
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="action-groups">
|
||||
<a href="https://discord.modrinth.com" class="link">
|
||||
<a href="https://support.modrinth.com" class="link">
|
||||
<ChatIcon />
|
||||
<span> Get support </span>
|
||||
</a>
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
<template>
|
||||
<div ref="button" class="button-base avatar-button" :class="{ highlighted: showDemo }">
|
||||
<Avatar src="https://launcher-files.modrinth.com/assets/steve_head.png" />
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<div v-if="showDemo" class="card-section">
|
||||
<Card ref="card" class="fake-account-card expanded highlighted">
|
||||
<div class="selected account">
|
||||
<Avatar size="xs" src="https://launcher-files.modrinth.com/assets/steve_head.png" />
|
||||
<div>
|
||||
<h4>Modrinth</h4>
|
||||
<p>Selected</p>
|
||||
</div>
|
||||
<Button v-tooltip="'Log out'" icon-only color="raised">
|
||||
<TrashIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<Button>
|
||||
<PlusIcon />
|
||||
Add account
|
||||
</Button>
|
||||
</Card>
|
||||
<slot />
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Avatar, Button, Card, PlusIcon, TrashIcon } from 'omorphia'
|
||||
|
||||
defineProps({
|
||||
showDemo: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.selected {
|
||||
background: var(--color-brand-highlight);
|
||||
border-radius: var(--radius-lg);
|
||||
color: var(--color-contrast);
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.logged-out {
|
||||
background: var(--color-bg);
|
||||
border-radius: var(--radius-lg);
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.account {
|
||||
width: max-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
padding: 0.5rem 1rem;
|
||||
|
||||
h4,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.card-section {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
left: 5.5rem;
|
||||
z-index: 9;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.fake-account-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--color-button-bg);
|
||||
width: max-content;
|
||||
user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.isolated {
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.accounts-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.account-group {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.option {
|
||||
width: calc(100% - 2.25rem);
|
||||
background: var(--color-raised-bg);
|
||||
color: var(--color-base);
|
||||
box-shadow: none;
|
||||
|
||||
img {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
--size: 1.5rem !important;
|
||||
}
|
||||
|
||||
.account-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5rem;
|
||||
vertical-align: center;
|
||||
justify-content: space-between;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.avatar-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: var(--color-base);
|
||||
background-color: var(--color-raised-bg);
|
||||
border-radius: var(--radius-md);
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
|
||||
&.expanded {
|
||||
border: 1px solid var(--color-button-bg);
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-text {
|
||||
margin: auto 0 auto 0.25rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.text {
|
||||
width: 6rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.accounts-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,265 +0,0 @@
|
||||
<template>
|
||||
<div class="action-groups">
|
||||
<Button color="danger" outline @click="exit">
|
||||
<LogOutIcon />
|
||||
Exit tutorial
|
||||
</Button>
|
||||
<Button v-if="showDownload" ref="infoButton" icon-only class="icon-button show-card-icon">
|
||||
<DownloadIcon />
|
||||
</Button>
|
||||
<div v-if="showRunning" class="status highlighted">
|
||||
<span class="circle running" />
|
||||
<div ref="profileButton" class="running-text">Example Modpack</div>
|
||||
<Button v-tooltip="'Stop instance'" icon-only class="icon-button stop">
|
||||
<StopCircleIcon />
|
||||
</Button>
|
||||
<Button v-tooltip="'View logs'" icon-only class="icon-button">
|
||||
<TerminalSquareIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<div v-else class="status">
|
||||
<span class="circle stopped" />
|
||||
<span class="running-text"> No running instances </span>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="download">
|
||||
<div v-if="showDownload" class="info-section">
|
||||
<Card ref="card" class="highlighted info-card">
|
||||
<h3 class="info-title">New Modpack</h3>
|
||||
<ProgressBar :progress="50" />
|
||||
<div class="row">50% Downloading modpack</div>
|
||||
</Card>
|
||||
<slot name="download" />
|
||||
</div>
|
||||
</transition>
|
||||
<transition name="running">
|
||||
<div v-if="showRunning" class="info-section">
|
||||
<slot name="running" />
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
Button,
|
||||
DownloadIcon,
|
||||
Card,
|
||||
StopCircleIcon,
|
||||
TerminalSquareIcon,
|
||||
LogOutIcon,
|
||||
} from 'omorphia'
|
||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||
|
||||
defineProps({
|
||||
showDownload: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showRunning: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
exit: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.action-groups {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--gap-sm);
|
||||
}
|
||||
|
||||
.arrow {
|
||||
transition: transform 0.2s ease-in-out;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&.rotate {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--color-button-bg);
|
||||
padding: var(--gap-sm) var(--gap-lg);
|
||||
}
|
||||
|
||||
.running-text {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--gap-xs);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-ms-user-select: none; /* IE 10 and IE 11 */
|
||||
user-select: none;
|
||||
|
||||
&.clickable:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.circle {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: 0.25rem;
|
||||
|
||||
&.running {
|
||||
background-color: var(--color-brand);
|
||||
}
|
||||
|
||||
&.stopped {
|
||||
background-color: var(--color-base);
|
||||
}
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
box-shadow: none;
|
||||
width: 1.25rem !important;
|
||||
height: 1.25rem !important;
|
||||
|
||||
&.stop {
|
||||
--text-color: var(--color-red) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.info-section {
|
||||
position: absolute;
|
||||
top: 3.5rem;
|
||||
right: 0.75rem;
|
||||
z-index: 9;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
width: 20rem;
|
||||
background-color: var(--color-raised-bg);
|
||||
box-shadow: var(--shadow-raised);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
overflow: auto;
|
||||
transition: all 0.2s ease-in-out;
|
||||
border: 1px solid var(--color-button-bg);
|
||||
|
||||
&.hidden {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-option {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
:hover {
|
||||
background-color: var(--color-raised-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-icon {
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
display: block;
|
||||
|
||||
:deep(svg) {
|
||||
left: 1rem;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.show-card-icon {
|
||||
color: var(--color-brand);
|
||||
}
|
||||
|
||||
.download-enter-active,
|
||||
.download-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.download-enter-from,
|
||||
.download-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.info-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.profile-button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--gap-sm);
|
||||
width: 100%;
|
||||
background-color: var(--color-raised-bg);
|
||||
box-shadow: none;
|
||||
|
||||
.text {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
position: absolute;
|
||||
top: 3.5rem;
|
||||
right: 0.5rem;
|
||||
z-index: 9;
|
||||
background-color: var(--color-raised-bg);
|
||||
box-shadow: var(--shadow-raised);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
transition: all 0.2s ease-in-out;
|
||||
border: 1px solid var(--color-button-bg);
|
||||
padding: var(--gap-md);
|
||||
|
||||
&.hidden {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,222 +0,0 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { Card, DropdownSelect, SearchIcon, XIcon, Button, Avatar } from 'omorphia'
|
||||
|
||||
const search = ref('')
|
||||
const group = ref('Category')
|
||||
const filters = ref('All profiles')
|
||||
const sortBy = ref('Name')
|
||||
|
||||
defineProps({
|
||||
showFilters: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showInstances: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<Card class="header" :class="{ highlighted: showFilters }">
|
||||
<div class="iconified-input">
|
||||
<SearchIcon />
|
||||
<input v-model="search" type="text" placeholder="Search" class="search-input" />
|
||||
<Button @click="() => (search = '')">
|
||||
<XIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="labeled_button">
|
||||
<span>Sort by</span>
|
||||
<DropdownSelect
|
||||
v-model="sortBy"
|
||||
class="sort-dropdown"
|
||||
name="Sort Dropdown"
|
||||
:options="['Name', 'Last played', 'Date created', 'Date modified', 'Game version']"
|
||||
placeholder="Select..."
|
||||
/>
|
||||
</div>
|
||||
<div class="labeled_button">
|
||||
<span>Filter by</span>
|
||||
<DropdownSelect
|
||||
v-model="filters"
|
||||
class="filter-dropdown"
|
||||
name="Filter Dropdown"
|
||||
:options="['All profiles', 'Custom instances', 'Downloaded modpacks']"
|
||||
placeholder="Select..."
|
||||
/>
|
||||
</div>
|
||||
<div class="labeled_button">
|
||||
<span>Group by</span>
|
||||
<DropdownSelect
|
||||
v-model="group"
|
||||
class="group-dropdown"
|
||||
name="Group dropdown"
|
||||
:options="['Category', 'Loader', 'Game version', 'None']"
|
||||
placeholder="Select..."
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
<div class="row">
|
||||
<section class="instances">
|
||||
<Card
|
||||
v-for="project in 20"
|
||||
:key="project"
|
||||
class="instance-card-item button-base"
|
||||
:class="{ highlighted: project === 1 && showInstance }"
|
||||
>
|
||||
<Avatar
|
||||
size="sm"
|
||||
src="https://launcher-files.modrinth.com/assets/default_profile.png"
|
||||
alt="Mod card"
|
||||
class="mod-image"
|
||||
/>
|
||||
<div class="project-info">
|
||||
<p class="title">Example Profile</p>
|
||||
<p class="description">Forge/Fabric 1.20.1</p>
|
||||
</div>
|
||||
</Card>
|
||||
<slot />
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
|
||||
.divider {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
white-space: nowrap;
|
||||
color: var(--color-contrast);
|
||||
}
|
||||
|
||||
hr {
|
||||
background-color: var(--color-gray);
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
align-items: inherit;
|
||||
margin: 1rem 1rem 0 !important;
|
||||
padding: 1rem;
|
||||
width: calc(100% - 2rem);
|
||||
|
||||
.iconified-input {
|
||||
flex-grow: 1;
|
||||
|
||||
input {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.sort-dropdown {
|
||||
width: 10rem;
|
||||
}
|
||||
|
||||
.filter-dropdown {
|
||||
width: 15rem;
|
||||
}
|
||||
|
||||
.group-dropdown {
|
||||
width: 10rem;
|
||||
}
|
||||
|
||||
.labeled_button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.instances {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||
width: 100%;
|
||||
gap: 1rem;
|
||||
margin-right: auto;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.instance {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.instance-card-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
padding: var(--gap-md);
|
||||
transition: 0.1s ease-in-out all !important; /* overrides Omorphia defaults */
|
||||
margin-bottom: 0;
|
||||
|
||||
.mod-image {
|
||||
--size: 100%;
|
||||
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
max-width: unset !important;
|
||||
max-height: unset !important;
|
||||
aspect-ratio: 1 / 1 !important;
|
||||
}
|
||||
|
||||
.project-info {
|
||||
margin-top: 1rem;
|
||||
width: 100%;
|
||||
|
||||
.title {
|
||||
color: var(--color-contrast);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
line-height: 110%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: var(--color-base);
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
font-weight: 500;
|
||||
font-size: 0.775rem;
|
||||
line-height: 125%;
|
||||
margin: 0.25rem 0 0;
|
||||
text-transform: capitalize;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,376 +0,0 @@
|
||||
<script setup>
|
||||
import {
|
||||
DownloadIcon,
|
||||
ChevronRightIcon,
|
||||
formatNumber,
|
||||
CalendarIcon,
|
||||
HeartIcon,
|
||||
Avatar,
|
||||
Card,
|
||||
} from 'omorphia'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
const modsRow = ref(null)
|
||||
const rows = ref(null)
|
||||
const maxInstancesPerRow = ref(0)
|
||||
const maxProjectsPerRow = ref(0)
|
||||
|
||||
const calculateCardsPerRow = () => {
|
||||
// Calculate how many cards fit in one row
|
||||
const containerWidth = rows.value[0].clientWidth
|
||||
// Convert container width from pixels to rem
|
||||
const containerWidthInRem =
|
||||
containerWidth / parseFloat(getComputedStyle(document.documentElement).fontSize)
|
||||
maxInstancesPerRow.value = Math.floor((containerWidthInRem + 1) / 11)
|
||||
maxProjectsPerRow.value = Math.floor((containerWidthInRem + 1) / 17)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
calculateCardsPerRow()
|
||||
window.addEventListener('resize', calculateCardsPerRow)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', calculateCardsPerRow)
|
||||
})
|
||||
|
||||
defineProps({
|
||||
showInstance: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="content">
|
||||
<div
|
||||
v-for="(row, index) in ['Jump back in', 'Popular modpacks', 'Popular mods']"
|
||||
ref="rows"
|
||||
:key="row"
|
||||
class="row"
|
||||
>
|
||||
<div class="header">
|
||||
<p>{{ row }}</p>
|
||||
<ChevronRightIcon />
|
||||
</div>
|
||||
<section v-if="index < 1" ref="modsRow" class="instances">
|
||||
<Card
|
||||
v-for="project in maxInstancesPerRow"
|
||||
:key="project"
|
||||
class="instance-card-item button-base"
|
||||
:class="{ highlighted: showInstance }"
|
||||
>
|
||||
<Avatar
|
||||
size="sm"
|
||||
src="https://launcher-files.modrinth.com/assets/default_profile.png"
|
||||
alt="Mod card"
|
||||
class="mod-image"
|
||||
/>
|
||||
<div class="project-info">
|
||||
<p class="title">Example Profile</p>
|
||||
<p class="description">Forge/Fabric 1.20.1</p>
|
||||
</div>
|
||||
</Card>
|
||||
</section>
|
||||
<section v-else ref="modsRow" class="projects">
|
||||
<div v-for="project in maxProjectsPerRow" :key="project" class="wrapper">
|
||||
<Card class="project-card button-base" :class="{ highlighted: showInstance }">
|
||||
<div
|
||||
class="banner no-image"
|
||||
:style="{
|
||||
'background-image': `url(https://launcher-files.modrinth.com/assets/maze-bg.png)`,
|
||||
}"
|
||||
>
|
||||
<div class="badges">
|
||||
<div class="badge">
|
||||
<DownloadIcon />
|
||||
{{ formatNumber(69420) }}
|
||||
</div>
|
||||
<div class="badge">
|
||||
<HeartIcon />
|
||||
{{ formatNumber(69) }}
|
||||
</div>
|
||||
<div class="badge">
|
||||
<CalendarIcon />
|
||||
Today
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="badges-wrapper no-image"
|
||||
:style="{
|
||||
background:
|
||||
'linear-gradient(rgba(' +
|
||||
[27, 217, 106, 0.03].join(',') +
|
||||
'), 65%, rgba(' +
|
||||
[27, 217, 106, 0.3].join(',') +
|
||||
'))',
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
<Avatar
|
||||
class="icon"
|
||||
size="sm"
|
||||
src="https://launcher-files.modrinth.com/assets/default_profile.png"
|
||||
/>
|
||||
<div class="title">
|
||||
<div class="title-text">Example Project</div>
|
||||
<div class="author">by Modrinth</div>
|
||||
</div>
|
||||
<div class="description">
|
||||
An example project hangin on the Rinth. Very cool project, its probably on Forge and
|
||||
Fabric. Probably has a 401k and a family.
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
|
||||
&:nth-child(even) {
|
||||
background: var(--color-bg);
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
gap: var(--gap-xs);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: var(--font-size-lg);
|
||||
font-weight: bolder;
|
||||
white-space: nowrap;
|
||||
color: var(--color-contrast);
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
color: var(--color-contrast);
|
||||
}
|
||||
}
|
||||
|
||||
.instances {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||
grid-gap: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.projects {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
|
||||
grid-gap: 1rem;
|
||||
|
||||
.item {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loading-indicator {
|
||||
width: 2.5rem !important;
|
||||
height: 2.5rem !important;
|
||||
|
||||
svg {
|
||||
width: 2.5rem !important;
|
||||
height: 2.5rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.instance {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.instance-card-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
padding: var(--gap-md);
|
||||
transition: 0.1s ease-in-out all !important; /* overrides Omorphia defaults */
|
||||
margin-bottom: 0;
|
||||
|
||||
.mod-image {
|
||||
--size: 100%;
|
||||
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
max-width: unset !important;
|
||||
max-height: unset !important;
|
||||
aspect-ratio: 1 / 1 !important;
|
||||
}
|
||||
|
||||
.project-info {
|
||||
margin-top: 1rem;
|
||||
width: 100%;
|
||||
|
||||
.title {
|
||||
color: var(--color-contrast);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
line-height: 110%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: var(--color-base);
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
font-weight: 500;
|
||||
font-size: 0.775rem;
|
||||
line-height: 125%;
|
||||
margin: 0.25rem 0 0;
|
||||
text-transform: capitalize;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
|
||||
&:hover {
|
||||
.install:enabled {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-card {
|
||||
display: grid;
|
||||
grid-gap: 1rem;
|
||||
grid-template:
|
||||
'. . . .' 0
|
||||
'. icon title .' 3rem
|
||||
'banner banner banner banner' auto
|
||||
'. description description .' 3.5rem
|
||||
'. . . .' 0 / 0 3rem minmax(0, 1fr) 0;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
.icon {
|
||||
grid-area: icon;
|
||||
}
|
||||
|
||||
.title {
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
grid-area: title;
|
||||
white-space: nowrap;
|
||||
|
||||
.title-text {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: var(--font-size-md);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.author {
|
||||
font-size: var(--font-size-sm);
|
||||
grid-area: author;
|
||||
}
|
||||
|
||||
.banner {
|
||||
grid-area: banner;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
|
||||
.badges-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
mix-blend-mode: hard-light;
|
||||
}
|
||||
|
||||
.badges {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: var(--gap-sm);
|
||||
gap: var(--gap-xs);
|
||||
display: flex;
|
||||
z-index: 1;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
grid-area: description;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
background-color: var(--color-raised-bg);
|
||||
font-size: var(--font-size-xs);
|
||||
padding: var(--gap-xs) var(--gap-sm);
|
||||
border-radius: var(--radius-sm);
|
||||
|
||||
svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
margin-right: var(--gap-xs);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,496 +0,0 @@
|
||||
<script setup>
|
||||
import { computed, readonly, ref } from 'vue'
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
CalendarIcon,
|
||||
Card,
|
||||
Categories,
|
||||
Checkbox,
|
||||
ClearIcon,
|
||||
ClientIcon,
|
||||
DownloadIcon,
|
||||
DropdownSelect,
|
||||
EnvironmentIndicator,
|
||||
formatCategory,
|
||||
formatCategoryHeader,
|
||||
formatNumber,
|
||||
HeartIcon,
|
||||
NavRow,
|
||||
Pagination,
|
||||
Promotion,
|
||||
SearchFilter,
|
||||
SearchIcon,
|
||||
ServerIcon,
|
||||
StarIcon,
|
||||
XIcon,
|
||||
} from 'omorphia'
|
||||
import Multiselect from 'vue-multiselect'
|
||||
import { handleError } from '@/store/state'
|
||||
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
|
||||
import SplashScreen from '@/components/ui/SplashScreen.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const query = ref('')
|
||||
const facets = ref([])
|
||||
const orFacets = ref([])
|
||||
const selectedVersions = ref([])
|
||||
const onlyOpenSource = ref(false)
|
||||
const showSnapshots = ref(false)
|
||||
const selectedEnvironments = ref([])
|
||||
const sortTypes = readonly([
|
||||
{ display: 'Relevance', name: 'relevance' },
|
||||
{ display: 'Download count', name: 'downloads' },
|
||||
{ display: 'Follow count', name: 'follows' },
|
||||
{ display: 'Recently published', name: 'newest' },
|
||||
{ display: 'Recently updated', name: 'updated' },
|
||||
])
|
||||
const sortType = ref(sortTypes[0])
|
||||
const maxResults = ref(20)
|
||||
const currentPage = ref(1)
|
||||
const projectType = ref('modpack')
|
||||
|
||||
const searchWrapper = ref(null)
|
||||
|
||||
const sortedCategories = computed(() => {
|
||||
const values = new Map()
|
||||
for (const category of categories.value.filter((cat) => cat.project_type === 'mod')) {
|
||||
if (!values.has(category.header)) {
|
||||
values.set(category.header, [])
|
||||
}
|
||||
values.get(category.header).push(category)
|
||||
}
|
||||
return values
|
||||
})
|
||||
|
||||
const [categories, loaders, availableGameVersions] = await Promise.all([
|
||||
get_categories().catch(handleError).then(ref),
|
||||
get_loaders().catch(handleError).then(ref),
|
||||
get_game_versions().catch(handleError).then(ref),
|
||||
])
|
||||
|
||||
const pageCount = ref(1)
|
||||
|
||||
const selectableProjectTypes = computed(() => {
|
||||
return [
|
||||
{ label: 'Shaders', href: `` },
|
||||
{ label: 'Resource Packs', href: `` },
|
||||
{ label: 'Data Packs', href: `` },
|
||||
{ label: 'Mods', href: '' },
|
||||
{ label: 'Modpacks', href: '' },
|
||||
]
|
||||
})
|
||||
|
||||
defineProps({
|
||||
showSearch: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="search-container">
|
||||
<aside class="filter-panel">
|
||||
<Card class="search-panel-card" :class="{ highlighted: showSearch }">
|
||||
<Button role="button" disabled> <ClearIcon /> Clear Filters </Button>
|
||||
<div class="loaders">
|
||||
<h2>Loaders</h2>
|
||||
<div
|
||||
v-for="loader in loaders.filter(
|
||||
(l) =>
|
||||
(projectType !== 'mod' && l.supported_project_types?.includes(projectType)) ||
|
||||
(projectType === 'mod' && ['fabric', 'forge', 'quilt'].includes(l.name)),
|
||||
)"
|
||||
:key="loader"
|
||||
>
|
||||
<SearchFilter
|
||||
:active-filters="orFacets"
|
||||
:icon="loader.icon"
|
||||
:display-name="formatCategory(loader.name)"
|
||||
:facet-name="`categories:${encodeURIComponent(loader.name)}`"
|
||||
class="filter-checkbox"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="versions">
|
||||
<h2>Minecraft versions</h2>
|
||||
<Checkbox v-model="showSnapshots" class="filter-checkbox" label="Include snapshots" />
|
||||
<multiselect
|
||||
v-model="selectedVersions"
|
||||
:options="
|
||||
showSnapshots
|
||||
? availableGameVersions.map((x) => x.version)
|
||||
: availableGameVersions
|
||||
.filter((it) => it.version_type === 'release')
|
||||
.map((x) => x.version)
|
||||
"
|
||||
:multiple="true"
|
||||
:searchable="true"
|
||||
:show-no-results="false"
|
||||
:close-on-select="false"
|
||||
:clear-search-on-select="false"
|
||||
:show-labels="false"
|
||||
placeholder="Choose versions..."
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-for="categoryList in Array.from(sortedCategories)"
|
||||
:key="categoryList[0]"
|
||||
class="categories"
|
||||
>
|
||||
<h2>{{ formatCategoryHeader(categoryList[0]) }}</h2>
|
||||
<div v-for="category in categoryList[1]" :key="category.name">
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
:icon="category.icon"
|
||||
:display-name="formatCategory(category.name)"
|
||||
:facet-name="`categories:${encodeURIComponent(category.name)}`"
|
||||
class="filter-checkbox"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="projectType !== 'datapack'" class="environment">
|
||||
<h2>Environments</h2>
|
||||
<SearchFilter
|
||||
:active-filters="selectedEnvironments"
|
||||
display-name="Client"
|
||||
facet-name="client"
|
||||
class="filter-checkbox"
|
||||
>
|
||||
<ClientIcon aria-hidden="true" />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="selectedEnvironments"
|
||||
display-name="Server"
|
||||
facet-name="server"
|
||||
class="filter-checkbox"
|
||||
>
|
||||
<ServerIcon aria-hidden="true" />
|
||||
</SearchFilter>
|
||||
</div>
|
||||
<div class="open-source">
|
||||
<h2>Open source</h2>
|
||||
<Checkbox v-model="onlyOpenSource" label="Open source only" class="filter-checkbox" />
|
||||
</div>
|
||||
</Card>
|
||||
</aside>
|
||||
<div ref="searchWrapper" class="search">
|
||||
<Promotion class="promotion" :external="false" query-param="?r=launcher" />
|
||||
<Card class="project-type-container">
|
||||
<NavRow :links="selectableProjectTypes" />
|
||||
</Card>
|
||||
<Card class="search-panel-container" :class="{ highlighted: showSearch }">
|
||||
<div class="iconified-input">
|
||||
<SearchIcon aria-hidden="true" />
|
||||
<input
|
||||
v-model="query"
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
:placeholder="`Search ${projectType}s...`"
|
||||
/>
|
||||
<Button @click="() => (query = '')">
|
||||
<XIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="inline-option">
|
||||
<span>Sort by</span>
|
||||
<DropdownSelect
|
||||
v-model="sortType"
|
||||
name="Sort by"
|
||||
:options="sortTypes"
|
||||
:display-name="(option) => option?.display"
|
||||
/>
|
||||
</div>
|
||||
<div class="inline-option">
|
||||
<span>Show per page</span>
|
||||
<DropdownSelect
|
||||
v-model="maxResults"
|
||||
name="Max results"
|
||||
:options="[5, 10, 15, 20, 50, 100]"
|
||||
:default-value="maxResults"
|
||||
:model-value="maxResults"
|
||||
class="limit-dropdown"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
<Pagination :page="currentPage" :count="pageCount" class="pagination-before" />
|
||||
<SplashScreen v-if="loading" />
|
||||
<section v-else class="project-list display-mode--list instance-results" role="list">
|
||||
<Card v-for="project in 20" :key="project" class="search-card button-base">
|
||||
<div class="icon">
|
||||
<Avatar
|
||||
src="https://launcher-files.modrinth.com/assets/default_profile.png"
|
||||
size="md"
|
||||
class="search-icon"
|
||||
/>
|
||||
</div>
|
||||
<div class="content-wrapper">
|
||||
<div class="title joined-text">
|
||||
<h2>Example Modpack</h2>
|
||||
<span>by Modrinth</span>
|
||||
</div>
|
||||
<div class="description">
|
||||
A very cool project that does cool project things that you can your friends can do.
|
||||
</div>
|
||||
<div class="tags">
|
||||
<Categories
|
||||
:categories="
|
||||
categories
|
||||
.filter((cat) => cat.project_type === projectType)
|
||||
.slice(project / 2, project / 2 + 3)
|
||||
"
|
||||
:type="modpack"
|
||||
>
|
||||
<EnvironmentIndicator
|
||||
:type-only="true"
|
||||
:client-side="true"
|
||||
:server-side="true"
|
||||
type="modpack"
|
||||
:search="true"
|
||||
/>
|
||||
</Categories>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats button-group">
|
||||
<div v-if="featured" class="badge">
|
||||
<StarIcon />
|
||||
Featured
|
||||
</div>
|
||||
<div class="badge">
|
||||
<DownloadIcon />
|
||||
{{ formatNumber(420) }}
|
||||
</div>
|
||||
<div class="badge">
|
||||
<HeartIcon />
|
||||
{{ formatNumber(69) }}
|
||||
</div>
|
||||
<div class="badge">
|
||||
<CalendarIcon />
|
||||
A minute ago
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</section>
|
||||
<pagination :page="currentPage" :count="pageCount" class="pagination-after" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
|
||||
<style lang="scss">
|
||||
.small-instance {
|
||||
min-height: unset !important;
|
||||
|
||||
.instance {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
color: var(--color-contrast);
|
||||
}
|
||||
}
|
||||
|
||||
.small-instance_info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
justify-content: space-between;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-checkbox {
|
||||
margin-bottom: 0.3rem;
|
||||
font-size: 1rem;
|
||||
|
||||
svg {
|
||||
display: flex;
|
||||
align-self: center;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
button.checkbox {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.project-type-dropdown {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.promotion {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.project-type-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-panel-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 0 !important;
|
||||
min-height: min-content !important;
|
||||
}
|
||||
|
||||
.iconified-input {
|
||||
input {
|
||||
max-width: none !important;
|
||||
flex-basis: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.search-panel-container {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
padding: 1rem !important;
|
||||
white-space: nowrap;
|
||||
gap: 1rem;
|
||||
|
||||
.inline-option {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
.sort-dropdown {
|
||||
max-width: 12.25rem;
|
||||
}
|
||||
|
||||
.limit-dropdown {
|
||||
width: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.iconified-input {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.filter-panel {
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
|
||||
svg {
|
||||
margin-right: 0.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-container {
|
||||
display: flex;
|
||||
|
||||
.filter-panel {
|
||||
position: fixed;
|
||||
width: 20rem;
|
||||
padding: 1rem 0.5rem 1rem 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: fit-content;
|
||||
min-height: calc(100vh - 3.25rem);
|
||||
max-height: calc(100vh - 3.25rem);
|
||||
overflow-y: auto;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: var(--color-contrast);
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 1.16rem;
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
scroll-behavior: smooth;
|
||||
margin: 0 1rem 0.5rem 20.5rem;
|
||||
width: calc(100% - 20.5rem);
|
||||
|
||||
.loading {
|
||||
margin: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-card {
|
||||
margin-bottom: 0;
|
||||
display: grid;
|
||||
grid-template-columns: 6rem auto 7rem;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
|
||||
&:active:not(&:disabled) {
|
||||
scale: 0.98 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.joined-text {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
column-gap: 0.5rem;
|
||||
align-items: baseline;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
h2 {
|
||||
margin-bottom: 0 !important;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5rem;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.icon {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
align-self: center;
|
||||
height: 6rem;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
grid-column: 2 / 4;
|
||||
flex-direction: column;
|
||||
grid-row: 1;
|
||||
gap: 0.5rem;
|
||||
|
||||
.description {
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
|
||||
.stats {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 2;
|
||||
justify-self: stretch;
|
||||
align-self: start;
|
||||
}
|
||||
</style>
|
||||
@@ -1,270 +0,0 @@
|
||||
<script setup>
|
||||
import { Card, Slider, DropdownSelect, Toggle } from 'omorphia'
|
||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||
|
||||
const pageOptions = ['Home', 'Library']
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="settings-page">
|
||||
<Card>
|
||||
<div class="label">
|
||||
<h3>
|
||||
<span class="label__title size-card-header">Display</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="theme">
|
||||
<span class="label__title">Color theme</span>
|
||||
<span class="label__description">Change the global launcher color theme.</span>
|
||||
</label>
|
||||
<DropdownSelect
|
||||
id="theme"
|
||||
name="Theme dropdown"
|
||||
:options="['Dark']"
|
||||
:disabled="true"
|
||||
:default-value="'dark'"
|
||||
class="theme-dropdown disable-children"
|
||||
/>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="collapsed-nav">
|
||||
<span class="label__title">Collapsed navigation mode</span>
|
||||
<span class="label__description"
|
||||
>Change the style of the side navigation bar to a compact version.</span
|
||||
>
|
||||
</label>
|
||||
<Toggle id="collapsed-nav" :checked="false" :disabled="true" />
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="advanced-rendering">
|
||||
<span class="label__title">Advanced rendering</span>
|
||||
<span class="label__description">
|
||||
Enables advanced rendering such as blur effects that may cause performance issues
|
||||
without hardware-accelerated rendering.
|
||||
</span>
|
||||
</label>
|
||||
<Toggle id="advanced-rendering" :checked="true" :disabled="true" />
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="minimize-launcher">
|
||||
<span class="label__title">Minimize launcher</span>
|
||||
<span class="label__description"
|
||||
>Minimize the launcher when a Minecraft process starts.</span
|
||||
>
|
||||
</label>
|
||||
<Toggle id="minimize-launcher" :checked="false" :disabled="true" />
|
||||
</div>
|
||||
<div class="opening-page">
|
||||
<label for="opening-page">
|
||||
<span class="label__title">Default landing page</span>
|
||||
<span class="label__description">Change the page to which the launcher opens on.</span>
|
||||
</label>
|
||||
<DropdownSelect
|
||||
id="opening-page"
|
||||
name="Opening page dropdown"
|
||||
:options="pageOptions"
|
||||
default-value="Home"
|
||||
class="opening-page"
|
||||
:disabled="true"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
<Card>
|
||||
<div class="label">
|
||||
<h3>
|
||||
<span class="label__title size-card-header">Resource management</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="max-downloads">
|
||||
<span class="label__title">Maximum concurrent downloads</span>
|
||||
<span class="label__description"
|
||||
>The maximum amount of files the launcher can download at the same time. Set this to a
|
||||
lower value if you have a poor internet connection.</span
|
||||
>
|
||||
</label>
|
||||
<Slider id="max-downloads" :min="1" :max="10" :step="1" :disabled="true" />
|
||||
</div>
|
||||
|
||||
<div class="adjacent-input">
|
||||
<label for="max-writes">
|
||||
<span class="label__title">Maximum concurrent writes</span>
|
||||
<span class="label__description"
|
||||
>The maximum amount of files the launcher can write to the disk at once. Set this to a
|
||||
lower value if you are frequently getting I/O errors.</span
|
||||
>
|
||||
</label>
|
||||
<Slider id="max-writes" :min="1" :max="50" :step="1" :disabled="true" />
|
||||
</div>
|
||||
</Card>
|
||||
<Card>
|
||||
<div class="label">
|
||||
<h3>
|
||||
<span class="label__title size-card-header">Privacy</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="opt-out-analytics">
|
||||
<span class="label__title">Disable analytics</span>
|
||||
<span class="label__description">
|
||||
Modrinth collects anonymized analytics and usage data to improve our user experience and
|
||||
customize your experience. Opting out will disable this data collection.
|
||||
</span>
|
||||
</label>
|
||||
<Toggle id="opt-out-analytics" :disabled="true" />
|
||||
</div>
|
||||
</Card>
|
||||
<Card>
|
||||
<div class="label">
|
||||
<h3>
|
||||
<span class="label__title size-card-header">Java settings</span>
|
||||
</h3>
|
||||
</div>
|
||||
<label for="java-17">
|
||||
<span class="label__title">Java 17 location</span>
|
||||
</label>
|
||||
<JavaSelector id="java-17" :version="17" model-value="" :disabled="true" />
|
||||
<label for="java-8">
|
||||
<span class="label__title">Java 8 location</span>
|
||||
</label>
|
||||
<JavaSelector id="java-8" :version="8" model-value="" :disabled="true" />
|
||||
<hr class="card-divider" />
|
||||
<label for="java-args">
|
||||
<span class="label__title">Java arguments</span>
|
||||
</label>
|
||||
<input
|
||||
id="java-args"
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
class="installation-input"
|
||||
placeholder="Enter java arguments..."
|
||||
:disabled="true"
|
||||
/>
|
||||
<label for="env-vars">
|
||||
<span class="label__title">Environmental variables</span>
|
||||
</label>
|
||||
<input
|
||||
id="env-vars"
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
class="installation-input"
|
||||
placeholder="Enter environmental variables..."
|
||||
:disabled="true"
|
||||
/>
|
||||
<hr class="card-divider" />
|
||||
<div class="adjacent-input">
|
||||
<label for="max-memory">
|
||||
<span class="label__title">Java memory</span>
|
||||
<span class="label__description">
|
||||
The memory allocated to each instance when it is ran.
|
||||
</span>
|
||||
</label>
|
||||
<Slider id="max-memory" :min="256" :max="10256" :step="1" unit="mb" :disabled="true" />
|
||||
</div>
|
||||
</Card>
|
||||
<Card>
|
||||
<div class="label">
|
||||
<h3>
|
||||
<span class="label__title size-card-header">Hooks</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="pre-launch">
|
||||
<span class="label__title">Pre launch</span>
|
||||
<span class="label__description"> Ran before the instance is launched. </span>
|
||||
</label>
|
||||
<input
|
||||
id="pre-launch"
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
placeholder="Enter pre-launch command..."
|
||||
:disabled="true"
|
||||
/>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="wrapper">
|
||||
<span class="label__title">Wrapper</span>
|
||||
<span class="label__description"> Wrapper command for launching Minecraft. </span>
|
||||
</label>
|
||||
<input
|
||||
id="wrapper"
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
placeholder="Enter wrapper command..."
|
||||
:disabled="true"
|
||||
/>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="post-exit">
|
||||
<span class="label__title">Post exit</span>
|
||||
<span class="label__description"> Ran after the game closes. </span>
|
||||
</label>
|
||||
<input
|
||||
id="post-exit"
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
placeholder="Enter post-exit command..."
|
||||
:disabled="true"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
<Card>
|
||||
<div class="label">
|
||||
<h3>
|
||||
<span class="label__title size-card-header">Window size</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="width">
|
||||
<span class="label__title">Width</span>
|
||||
<span class="label__description"> The width of the game window when launched. </span>
|
||||
</label>
|
||||
<input
|
||||
id="width"
|
||||
autocomplete="off"
|
||||
type="number"
|
||||
placeholder="Enter width..."
|
||||
:disabled="true"
|
||||
/>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label for="height">
|
||||
<span class="label__title">Height</span>
|
||||
<span class="label__description"> The height of the game window when launched. </span>
|
||||
</label>
|
||||
<input
|
||||
id="height"
|
||||
autocomplete="off"
|
||||
type="number"
|
||||
class="input"
|
||||
placeholder="Enter height..."
|
||||
:disabled="true"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.settings-page {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.installation-input {
|
||||
width: 100% !important;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.theme-dropdown {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.card-divider {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.disable-children * {
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,293 +0,0 @@
|
||||
<script setup>
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Chips,
|
||||
XIcon,
|
||||
FolderOpenIcon,
|
||||
FolderSearchIcon,
|
||||
UpdatedIcon,
|
||||
} from 'omorphia'
|
||||
import { ref } from 'vue'
|
||||
import {
|
||||
get_default_launcher_path,
|
||||
get_importable_instances,
|
||||
import_instance,
|
||||
} from '@/helpers/import.js'
|
||||
import { open } from '@tauri-apps/api/dialog'
|
||||
import { handleError } from '@/store/state.js'
|
||||
|
||||
const props = defineProps({
|
||||
nextPage: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
prevPage: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const profiles = ref(
|
||||
new Map([
|
||||
['MultiMC', []],
|
||||
['GDLauncher', []],
|
||||
['ATLauncher', []],
|
||||
['Curseforge', []],
|
||||
['PrismLauncher', []],
|
||||
]),
|
||||
)
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const selectedProfileType = ref('MultiMC')
|
||||
const profileOptions = ref([
|
||||
{ name: 'MultiMC', path: '' },
|
||||
{ name: 'GDLauncher', path: '' },
|
||||
{ name: 'ATLauncher', path: '' },
|
||||
{ name: 'Curseforge', path: '' },
|
||||
{ name: 'PrismLauncher', path: '' },
|
||||
])
|
||||
|
||||
// Attempt to get import profiles on default paths
|
||||
const promises = profileOptions.value.map(async (option) => {
|
||||
const path = await get_default_launcher_path(option.name).catch(handleError)
|
||||
if (!path || path === '') return
|
||||
|
||||
// Try catch to allow failure and simply ignore default path attempt
|
||||
try {
|
||||
const instances = await get_importable_instances(option.name, path)
|
||||
|
||||
if (!instances) return
|
||||
profileOptions.value.find((profile) => profile.name === option.name).path = path
|
||||
profiles.value.set(
|
||||
option.name,
|
||||
instances.map((name) => ({ name, selected: false })),
|
||||
)
|
||||
} catch (error) {
|
||||
// Allow failure silently
|
||||
}
|
||||
})
|
||||
Promise.all(promises)
|
||||
|
||||
const selectLauncherPath = async () => {
|
||||
selectedProfileType.value.path = await open({ multiple: false, directory: true })
|
||||
|
||||
if (selectedProfileType.value.path) {
|
||||
await reload()
|
||||
}
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
const instances = await get_importable_instances(
|
||||
selectedProfileType.value.name,
|
||||
selectedProfileType.value.path,
|
||||
).catch(handleError)
|
||||
profiles.value.set(
|
||||
selectedProfileType.value.name,
|
||||
instances.map((name) => ({ name, selected: false })),
|
||||
)
|
||||
}
|
||||
|
||||
const setPath = () => {
|
||||
profileOptions.value.find((profile) => profile.name === selectedProfileType.value.name).path =
|
||||
selectedProfileType.value.path
|
||||
}
|
||||
|
||||
const next = async () => {
|
||||
loading.value = true
|
||||
for (const launcher of Array.from(profiles.value.entries()).map(([launcher, profiles]) => ({
|
||||
launcher,
|
||||
path: profileOptions.value.find((option) => option.name === launcher).path,
|
||||
profiles,
|
||||
}))) {
|
||||
for (const profile of launcher.profiles.filter((profile) => profile.selected)) {
|
||||
await import_instance(launcher.launcher, launcher.path, profile.name)
|
||||
.catch(handleError)
|
||||
.then(() => console.log(`Successfully Imported ${profile.name} from ${launcher.launcher}`))
|
||||
}
|
||||
}
|
||||
loading.value = false
|
||||
props.nextPage()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card>
|
||||
<h2>Importing external profiles</h2>
|
||||
<Chips
|
||||
v-model="selectedProfileType"
|
||||
:items="profileOptions"
|
||||
:format-label="(profile) => profile?.name"
|
||||
/>
|
||||
<div class="path-selection">
|
||||
<h3>{{ selectedProfileType.name }} path</h3>
|
||||
<div class="path-input">
|
||||
<div class="iconified-input">
|
||||
<FolderOpenIcon />
|
||||
<input
|
||||
v-model="selectedProfileType.path"
|
||||
type="text"
|
||||
placeholder="Path to launcher"
|
||||
@change="setPath"
|
||||
/>
|
||||
<Button @click="() => (selectedLauncherPath = '')">
|
||||
<XIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<Button icon-only @click="selectLauncherPath">
|
||||
<FolderSearchIcon />
|
||||
</Button>
|
||||
<Button icon-only @click="reload">
|
||||
<UpdatedIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table">
|
||||
<div class="table-head table-row">
|
||||
<div class="toggle-all table-cell">
|
||||
<Checkbox
|
||||
class="select-checkbox"
|
||||
:model-value="profiles.get(selectedProfileType.name)?.every((child) => child.selected)"
|
||||
@update:model-value="
|
||||
(newValue) =>
|
||||
profiles
|
||||
.get(selectedProfileType.name)
|
||||
?.forEach((child) => (child.selected = newValue))
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="name-cell table-cell">Profile name</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
profiles.get(selectedProfileType.name) &&
|
||||
profiles.get(selectedProfileType.name).length > 0
|
||||
"
|
||||
class="table-content"
|
||||
>
|
||||
<div
|
||||
v-for="(profile, index) in profiles.get(selectedProfileType.name)"
|
||||
:key="index"
|
||||
class="table-row"
|
||||
>
|
||||
<div class="checkbox-cell table-cell">
|
||||
<Checkbox v-model="profile.selected" class="select-checkbox" />
|
||||
</div>
|
||||
<div class="name-cell table-cell">
|
||||
{{ profile.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="table-content empty">No profiles found</div>
|
||||
</div>
|
||||
<div class="button-row">
|
||||
<Button class="transparent" @click="prevPage"> Back </Button>
|
||||
<Button
|
||||
:disabled="
|
||||
loading ||
|
||||
!Array.from(profiles.values())
|
||||
.flatMap((e) => e)
|
||||
.some((e) => e.selected)
|
||||
"
|
||||
color="primary"
|
||||
@click="next"
|
||||
>
|
||||
{{
|
||||
loading
|
||||
? 'Importing...'
|
||||
: Array.from(profiles.values())
|
||||
.flatMap((e) => e)
|
||||
.some((e) => e.selected)
|
||||
? `Import ${
|
||||
Array.from(profiles.values())
|
||||
.flatMap((e) => e)
|
||||
.filter((e) => e.selected).length
|
||||
} profiles`
|
||||
: 'Select profiles to import'
|
||||
}}
|
||||
</Button>
|
||||
<Button class="transparent" @click="nextPage"> Next </Button>
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.card {
|
||||
padding: var(--gap-xl);
|
||||
min-height: unset;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.path-selection {
|
||||
padding: var(--gap-xl);
|
||||
background-color: var(--color-bg);
|
||||
border-radius: var(--radius-lg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: var(--gap-md);
|
||||
gap: var(--gap-md);
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.path-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
gap: var(--gap-sm);
|
||||
|
||||
.iconified-input {
|
||||
flex-grow: 1;
|
||||
:deep(input) {
|
||||
width: 100%;
|
||||
flex-basis: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table {
|
||||
border: 1px solid var(--color-bg);
|
||||
margin-bottom: var(--gap-md);
|
||||
}
|
||||
|
||||
.table-row {
|
||||
grid-template-columns: min-content auto;
|
||||
}
|
||||
|
||||
.table-content {
|
||||
max-height: calc(5 * (18px + 2rem));
|
||||
height: calc(5 * (18px + 2rem));
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.select-checkbox {
|
||||
button.checkbox {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
gap: var(--gap-md);
|
||||
|
||||
.transparent {
|
||||
padding: var(--gap-sm) 0;
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bolder;
|
||||
color: var(--color-contrast);
|
||||
}
|
||||
</style>
|
||||
@@ -1,18 +1,11 @@
|
||||
<script setup>
|
||||
import { Button, LogInIcon, Modal, ClipboardCopyIcon, GlobeIcon, Card } from 'omorphia'
|
||||
import { authenticate_await_completion, authenticate_begin_flow } from '@/helpers/auth.js'
|
||||
import { Button, LogInIcon, Card } from 'omorphia'
|
||||
import { login as login_flow, set_default_user } from '@/helpers/auth.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { useTheming } from '@/store/theme.js'
|
||||
import mixpanel from 'mixpanel-browser'
|
||||
import { get, set } from '@/helpers/settings.js'
|
||||
import { ref } from 'vue'
|
||||
import QrcodeVue from 'qrcode.vue'
|
||||
|
||||
const themeStore = useTheming()
|
||||
const loginUrl = ref(null)
|
||||
const loginModal = ref()
|
||||
const loginCode = ref(null)
|
||||
const finalizedLogin = ref(false)
|
||||
import { handleSevereError } from '@/store/error.js'
|
||||
const loading = ref(false)
|
||||
|
||||
const props = defineProps({
|
||||
nextPage: {
|
||||
@@ -26,42 +19,21 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
async function login() {
|
||||
const loginSuccess = await authenticate_begin_flow().catch(handleError)
|
||||
loginUrl.value = loginSuccess.verification_uri
|
||||
loginCode.value = loginSuccess.user_code
|
||||
loginModal.value.show()
|
||||
try {
|
||||
loading.value = true
|
||||
const loggedIn = await login_flow()
|
||||
|
||||
await window.__TAURI_INVOKE__('tauri', {
|
||||
__tauriModule: 'Shell',
|
||||
message: {
|
||||
cmd: 'open',
|
||||
path: loginSuccess.verification_uri,
|
||||
},
|
||||
})
|
||||
if (loggedIn) {
|
||||
await set_default_user(loggedIn.id).catch(handleError)
|
||||
}
|
||||
|
||||
const loggedIn = await authenticate_await_completion().catch(handleError)
|
||||
loginModal.value.hide()
|
||||
|
||||
const settings = await get().catch(handleError)
|
||||
settings.default_user = loggedIn.id
|
||||
await set(settings).catch(handleError)
|
||||
finalizedLogin.value = true
|
||||
await mixpanel.track('AccountLogIn')
|
||||
props.nextPage()
|
||||
}
|
||||
|
||||
const openUrl = async () => {
|
||||
await window.__TAURI_INVOKE__('tauri', {
|
||||
__tauriModule: 'Shell',
|
||||
message: {
|
||||
cmd: 'open',
|
||||
path: loginUrl.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const clipboardWrite = async (a) => {
|
||||
navigator.clipboard.writeText(a)
|
||||
await mixpanel.track('AccountLogIn')
|
||||
loading.value = false
|
||||
props.nextPage()
|
||||
} catch (err) {
|
||||
loading.value = false
|
||||
handleSevereError(err)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -87,45 +59,15 @@ const clipboardWrite = async (a) => {
|
||||
<div class="action-row">
|
||||
<Button class="transparent" large @click="prevPage"> Back </Button>
|
||||
<div class="sign-in-pair">
|
||||
<Button color="primary" large @click="login">
|
||||
<LogInIcon v-if="!finalizedLogin" />
|
||||
{{ finalizedLogin ? 'Next' : 'Sign in' }}
|
||||
<Button color="primary" large :disabled="loading" @click="login">
|
||||
<LogInIcon />
|
||||
{{ loading ? 'Loading...' : 'Sign in' }}
|
||||
</Button>
|
||||
</div>
|
||||
<Button class="transparent" large @click="nextPage()"> Next </Button>
|
||||
<Button class="transparent" large @click="nextPage()"> Finish</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<Modal ref="loginModal" header="Signing in" :noblur="!themeStore.advancedRendering">
|
||||
<div class="modal-body">
|
||||
<QrcodeVue :value="loginUrl" class="qr-code" margin="3" size="160" />
|
||||
<div class="modal-text">
|
||||
<div class="label">Copy this code</div>
|
||||
<div class="code-text">
|
||||
<div class="code">
|
||||
{{ loginCode }}
|
||||
</div>
|
||||
<Button
|
||||
v-tooltip="'Copy code'"
|
||||
icon-only
|
||||
large
|
||||
color="raised"
|
||||
@click="() => clipboardWrite(loginCode)"
|
||||
>
|
||||
<ClipboardCopyIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<div>And enter it on Microsoft's website to sign in.</div>
|
||||
<div class="iconified-input">
|
||||
<LogInIcon />
|
||||
<input type="text" :value="loginUrl" readonly />
|
||||
<Button v-tooltip="'Open link'" icon-only color="raised" @click="openUrl">
|
||||
<GlobeIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -188,79 +130,10 @@ const clipboardWrite = async (a) => {
|
||||
}
|
||||
}
|
||||
|
||||
.qr-code {
|
||||
background-color: white !important;
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--gap-lg);
|
||||
align-items: center;
|
||||
padding: var(--gap-lg);
|
||||
|
||||
.modal-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-sm);
|
||||
width: 100%;
|
||||
|
||||
h2,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.code-text {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--gap-xs);
|
||||
align-items: center;
|
||||
|
||||
.code {
|
||||
background-color: var(--color-bg);
|
||||
border-radius: var(--radius-md);
|
||||
border: solid 1px var(--color-button-bg);
|
||||
font-family: var(--mono-font);
|
||||
letter-spacing: var(--gap-md);
|
||||
color: var(--color-contrast);
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
padding: var(--gap-sm) 0 var(--gap-sm) var(--gap-md);
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.sticker {
|
||||
width: 100%;
|
||||
max-width: 25rem;
|
||||
height: auto;
|
||||
margin-bottom: var(--gap-lg);
|
||||
}
|
||||
|
||||
.sign-in-pair {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-sm);
|
||||
align-items: center;
|
||||
}
|
||||
.code {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--gap-sm);
|
||||
|
||||
.card {
|
||||
background: var(--color-base);
|
||||
color: var(--color-contrast);
|
||||
padding: 0.5rem 1rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,28 +1,6 @@
|
||||
<script setup>
|
||||
import {
|
||||
Button,
|
||||
HomeIcon,
|
||||
SearchIcon,
|
||||
LibraryIcon,
|
||||
PlusIcon,
|
||||
SettingsIcon,
|
||||
XIcon,
|
||||
Notifications,
|
||||
} from 'omorphia'
|
||||
import { appWindow } from '@tauri-apps/api/window'
|
||||
import { saveWindowState, StateFlags } from 'tauri-plugin-window-state-api'
|
||||
import Breadcrumbs from '@/components/ui/Breadcrumbs.vue'
|
||||
import FakeAppBar from '@/components/ui/tutorial/FakeAppBar.vue'
|
||||
import FakeAccountsCard from '@/components/ui/tutorial/FakeAccountsCard.vue'
|
||||
import { MinimizeIcon, MaximizeIcon } from '@/assets/icons'
|
||||
import ModrinthLoadingIndicator from '@/components/modrinth-loading-indicator.js'
|
||||
import FakeSearch from '@/components/ui/tutorial/FakeSearch.vue'
|
||||
import FakeGridDisplay from '@/components/ui/tutorial/FakeGridDisplay.vue'
|
||||
import FakeRowDisplay from '@/components/ui/tutorial/FakeRowDisplay.vue'
|
||||
import { Button } from 'omorphia'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { window } from '@tauri-apps/api'
|
||||
import TutorialTip from '@/components/ui/tutorial/TutorialTip.vue'
|
||||
import FakeSettings from '@/components/ui/tutorial/FakeSettings.vue'
|
||||
import { get, set } from '@/helpers/settings.js'
|
||||
import mixpanel from 'mixpanel-browser'
|
||||
import GalleryImage from '@/components/ui/tutorial/GalleryImage.vue'
|
||||
@@ -30,11 +8,7 @@ import LoginCard from '@/components/ui/tutorial/LoginCard.vue'
|
||||
import StickyTitleBar from '@/components/ui/tutorial/StickyTitleBar.vue'
|
||||
import { auto_install_java, get_jre } from '@/helpers/jre.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import ImportingCard from '@/components/ui/tutorial/ImportingCard.vue'
|
||||
import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue'
|
||||
import PreImportScreen from '@/components/ui/tutorial/PreImportScreen.vue'
|
||||
|
||||
const phase = ref(0)
|
||||
const page = ref(1)
|
||||
|
||||
const props = defineProps({
|
||||
@@ -46,15 +20,6 @@ const props = defineProps({
|
||||
|
||||
const flow = ref('')
|
||||
|
||||
const nextPhase = () => {
|
||||
phase.value++
|
||||
mixpanel.track('TutorialPhase', { page: phase.value })
|
||||
}
|
||||
|
||||
const prevPhase = () => {
|
||||
phase.value--
|
||||
}
|
||||
|
||||
const nextPage = (newFlow) => {
|
||||
page.value++
|
||||
mixpanel.track('OnboardingPage', { page: page.value })
|
||||
@@ -64,10 +29,6 @@ const nextPage = (newFlow) => {
|
||||
}
|
||||
}
|
||||
|
||||
const endOnboarding = () => {
|
||||
nextPhase()
|
||||
}
|
||||
|
||||
const prevPage = () => {
|
||||
page.value--
|
||||
}
|
||||
@@ -105,18 +66,18 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="phase === 0" class="onboarding">
|
||||
<div class="onboarding">
|
||||
<StickyTitleBar />
|
||||
<GalleryImage
|
||||
v-if="page === 1"
|
||||
:gallery="[
|
||||
{
|
||||
url: 'https://cdn.discordapp.com/attachments/817413688771608587/1131109353928265809/Screenshot_2023-07-15_at_4.16.18_PM.png',
|
||||
url: 'https://launcher-files.modrinth.com/onboarding/home.png',
|
||||
title: 'Discovery',
|
||||
subtitle: 'See the latest and greatest mods and modpacks to play with from Modrinth',
|
||||
},
|
||||
{
|
||||
url: 'https://cdn.discordapp.com/attachments/817413688771608587/1131109354238640238/Screenshot_2023-07-15_at_4.17.43_PM.png',
|
||||
url: 'https://launcher-files.modrinth.com/onboarding/discover.png',
|
||||
title: 'Profile Management',
|
||||
subtitle:
|
||||
'Play, manage and search through all the amazing profiles downloaded on your computer at any time, even offline!',
|
||||
@@ -126,185 +87,7 @@ onMounted(async () => {
|
||||
>
|
||||
<Button color="primary" @click="nextPage"> Get started </Button>
|
||||
</GalleryImage>
|
||||
<LoginCard v-else-if="page === 2" :next-page="nextPage" :prev-page="prevPage" />
|
||||
<ModrinthLoginScreen
|
||||
v-else-if="page === 3"
|
||||
:modal="false"
|
||||
:next-page="nextPage"
|
||||
:prev-page="prevPage"
|
||||
:flow="flow"
|
||||
/>
|
||||
<PreImportScreen
|
||||
v-else-if="page === 4"
|
||||
:next-page="endOnboarding"
|
||||
:prev-page="prevPage"
|
||||
:import-page="nextPage"
|
||||
/>
|
||||
<ImportingCard v-else-if="page === 5" :next-page="endOnboarding" :prev-page="prevPage" />
|
||||
</div>
|
||||
<div v-else class="container">
|
||||
<StickyTitleBar v-if="phase === 9" />
|
||||
<div v-if="phase < 9" class="nav-container">
|
||||
<div class="nav-section">
|
||||
<FakeAccountsCard :show-demo="phase === 3">
|
||||
<TutorialTip
|
||||
:progress-function="nextPhase"
|
||||
:previous-function="prevPhase"
|
||||
:progress="phase"
|
||||
title="Signing in"
|
||||
description="The Modrinth App uses your Microsoft account to allow you to launch Minecraft. You can sign in with your Microsoft account here, and switch between multiple accounts."
|
||||
/>
|
||||
</FakeAccountsCard>
|
||||
<div class="pages-list">
|
||||
<div class="btn icon-only" :class="{ active: phase < 4 }">
|
||||
<HomeIcon />
|
||||
</div>
|
||||
<div
|
||||
class="btn icon-only"
|
||||
:class="{ active: phase === 4 || phase === 5, highlighted: phase === 4 }"
|
||||
>
|
||||
<SearchIcon />
|
||||
</div>
|
||||
<div
|
||||
class="btn icon-only"
|
||||
:class="{
|
||||
active: phase === 6 || phase === 7,
|
||||
highlighted: phase === 6,
|
||||
}"
|
||||
>
|
||||
<LibraryIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings pages-list">
|
||||
<Button class="sleek-primary" icon-only>
|
||||
<PlusIcon />
|
||||
</Button>
|
||||
<Button icon-only :class="{ active: phase === 8, highlighted: phase === 8 }">
|
||||
<SettingsIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="phase < 9" class="view">
|
||||
<div data-tauri-drag-region class="appbar">
|
||||
<section class="navigation-controls">
|
||||
<Breadcrumbs data-tauri-drag-region />
|
||||
</section>
|
||||
<section class="mod-stats">
|
||||
<FakeAppBar
|
||||
:show-running="phase === 7"
|
||||
:show-download="phase === 5"
|
||||
:exit="finishOnboarding"
|
||||
>
|
||||
<template #running>
|
||||
<TutorialTip
|
||||
:progress-function="nextPhase"
|
||||
:previous-function="prevPhase"
|
||||
:progress="phase"
|
||||
title="Playing modpacks"
|
||||
description="When you launch a modpack, you can manage it directly in the title bar here. You can stop the modpack, view the logs, and see all currently running packs."
|
||||
/>
|
||||
</template>
|
||||
<template #download>
|
||||
<TutorialTip
|
||||
:progress-function="nextPhase"
|
||||
:previous-function="prevPhase"
|
||||
:progress="phase"
|
||||
title="Installing modpacks"
|
||||
description="When you download a modpack, Modrinth App will automatically install it for you. You can view the progress of the installation here."
|
||||
/>
|
||||
</template>
|
||||
</FakeAppBar>
|
||||
</section>
|
||||
<section class="window-controls">
|
||||
<Button class="titlebar-button" icon-only @click="() => appWindow.minimize()">
|
||||
<MinimizeIcon />
|
||||
</Button>
|
||||
<Button class="titlebar-button" icon-only @click="() => appWindow.toggleMaximize()">
|
||||
<MaximizeIcon />
|
||||
</Button>
|
||||
<Button
|
||||
class="titlebar-button close"
|
||||
icon-only
|
||||
@click="
|
||||
() => {
|
||||
saveWindowState(StateFlags.ALL)
|
||||
window.getCurrent().close()
|
||||
}
|
||||
"
|
||||
>
|
||||
<XIcon />
|
||||
</Button>
|
||||
</section>
|
||||
</div>
|
||||
<div class="router-view">
|
||||
<ModrinthLoadingIndicator
|
||||
offset-height="var(--appbar-height)"
|
||||
offset-width="var(--sidebar-width)"
|
||||
/>
|
||||
<Notifications ref="notificationsWrapper" />
|
||||
<FakeRowDisplay v-if="phase < 4 || phase > 8" :show-instance="phase === 2" />
|
||||
<FakeGridDisplay v-if="phase === 6 || phase === 7" :show-instances="phase === 6" />
|
||||
<suspense>
|
||||
<FakeSearch v-if="phase === 4 || phase === 5" :show-search="phase === 4" />
|
||||
</suspense>
|
||||
<FakeSettings v-if="phase === 8" />
|
||||
</div>
|
||||
</div>
|
||||
<TutorialTip
|
||||
v-if="phase === 1"
|
||||
class="first-tip highlighted"
|
||||
:progress-function="nextPhase"
|
||||
:progress="phase"
|
||||
title="Enter the Modrinth App!"
|
||||
description="This is the Modrinth App guide. Key parts are marked with a green shadow. Click 'Next' to
|
||||
proceed. You can leave the tutorial anytime using the Exit button above the plus button on the bottom left."
|
||||
/>
|
||||
<div v-if="phase === 1" class="whole-page-shadow" />
|
||||
<TutorialTip
|
||||
v-if="phase === 2"
|
||||
class="sticky-tip"
|
||||
:progress-function="nextPhase"
|
||||
:previous-function="prevPhase"
|
||||
:progress="phase"
|
||||
title="Home page"
|
||||
description="This is the home page. Here you can see all the latest modpacks, mods, and other content on Modrinth. You can also see a few of your installed modpacks here."
|
||||
/>
|
||||
<TutorialTip
|
||||
v-if="phase === 4"
|
||||
class="sticky-tip"
|
||||
:progress-function="nextPhase"
|
||||
:previous-function="prevPhase"
|
||||
:progress="phase"
|
||||
title="Searching for content"
|
||||
description="You can search for content on Modrinth by navigating to the search page. You can search for mods, modpacks, and more, and install them directly from here."
|
||||
/>
|
||||
<TutorialTip
|
||||
v-if="phase === 6"
|
||||
class="sticky-tip"
|
||||
:progress-function="nextPhase"
|
||||
:previous-function="prevPhase"
|
||||
:progress="phase"
|
||||
title="Modpack library"
|
||||
description="You can view all your installed modpacks in the library. You can launch any modpack from here, or click the card to view more information about it."
|
||||
/>
|
||||
<TutorialTip
|
||||
v-if="phase === 8"
|
||||
class="sticky-tip"
|
||||
:progress-function="nextPhase"
|
||||
:previous-function="prevPhase"
|
||||
:progress="phase"
|
||||
title="Settings"
|
||||
description="You will be able to view and change the settings for the Modrinth App here. You can change the appearance, set and download new Java versions, and more."
|
||||
/>
|
||||
<TutorialTip
|
||||
v-if="phase === 9"
|
||||
class="final-tip highlighted"
|
||||
:progress-function="finishOnboarding"
|
||||
:progress="phase"
|
||||
title="Enter the Modrinth App!"
|
||||
description="That's it! You're ready to use the Modrinth App. If you need help, you can always join our discord server!"
|
||||
/>
|
||||
<LoginCard v-else-if="page === 2" :next-page="finishOnboarding" :prev-page="prevPage" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
<script setup>
|
||||
import { Button, Card, ModrinthIcon } from 'omorphia'
|
||||
import { ATLauncherIcon, PrismIcon } from '@/assets/external/index.js'
|
||||
|
||||
defineProps({
|
||||
nextPage: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
prevPage: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
importPage: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card class="import-card">
|
||||
<div class="base-ellipsis ellipsis-1" />
|
||||
<div class="base-ellipsis ellipsis-2" />
|
||||
<div class="base-ellipsis ellipsis-3" />
|
||||
<div class="base-ellipsis ellipsis-4" />
|
||||
<div class="logo">
|
||||
<ModrinthIcon />
|
||||
</div>
|
||||
<div class="launcher-stamp top-left">
|
||||
<ATLauncherIcon />
|
||||
</div>
|
||||
<div class="launcher-stamp top-right">
|
||||
<PrismIcon />
|
||||
</div>
|
||||
<div class="launcher-stamp bottom-left">
|
||||
<img src="@/assets/external/gdlauncher.png" alt="GDLauncher" />
|
||||
</div>
|
||||
<div class="launcher-stamp bottom-right">
|
||||
<img src="@/assets/external/multimc.webp" alt="MultiMC" />
|
||||
</div>
|
||||
<div class="info-section">
|
||||
<h2>Importing</h2>
|
||||
<div class="markdown-body">
|
||||
<p>
|
||||
You can import projects from other launchers by clicking below, or you can skip ahead.
|
||||
</p>
|
||||
</div>
|
||||
<div class="button-row">
|
||||
<Button class="transparent" @click="prevPage"> Back </Button>
|
||||
<Button color="primary" @click="importPage"> Import </Button>
|
||||
<Button class="transparent" @click="nextPage"> Next </Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.import-card {
|
||||
width: 40rem;
|
||||
height: 32rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.base-ellipsis {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
border-radius: 100%;
|
||||
top: calc(var(--gap-xl) + 5rem);
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100%;
|
||||
background-color: rgba(#1bd96a, 0.1);
|
||||
}
|
||||
|
||||
.ellipsis-1 {
|
||||
width: 15rem;
|
||||
height: 15rem;
|
||||
}
|
||||
|
||||
.ellipsis-2 {
|
||||
width: 30rem;
|
||||
height: 30rem;
|
||||
}
|
||||
|
||||
.ellipsis-3 {
|
||||
width: 45rem;
|
||||
height: 45rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: absolute;
|
||||
top: calc(var(--gap-xl) + 5rem);
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-accent-contrast);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
z-index: 1;
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.launcher-stamp {
|
||||
position: absolute;
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
background-color: var(--color-accent-contrast);
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
opacity: 0.65;
|
||||
padding: var(--gap-lg);
|
||||
|
||||
&.top-left {
|
||||
top: var(--gap-xl);
|
||||
left: 3rem;
|
||||
}
|
||||
|
||||
&.top-right {
|
||||
top: var(--gap-xl);
|
||||
right: 3rem;
|
||||
}
|
||||
|
||||
&.bottom-left {
|
||||
top: 12rem;
|
||||
left: 5.5rem;
|
||||
}
|
||||
|
||||
&.bottom-right {
|
||||
top: 12rem;
|
||||
right: 5.5rem;
|
||||
}
|
||||
|
||||
svg,
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.info-section {
|
||||
position: absolute;
|
||||
bottom: var(--gap-xl);
|
||||
left: 50%;
|
||||
width: 30rem;
|
||||
transform: translateX(-50%);
|
||||
padding: var(--gap-xl);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--gap-md);
|
||||
backdrop-filter: blur(1rem) brightness(0.4);
|
||||
-webkit-backdrop-filter: blur(1rem) brightness(0.4);
|
||||
border-radius: var(--radius-lg);
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
gap: var(--gap-md);
|
||||
width: 100%;
|
||||
align-content: center;
|
||||
|
||||
.transparent {
|
||||
padding: var(--gap-sm) 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,72 +0,0 @@
|
||||
<script setup>
|
||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||
import { Button, Card } from 'omorphia'
|
||||
|
||||
defineProps({
|
||||
progress: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: 'Tutorial',
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: 'This is a tutorial',
|
||||
},
|
||||
progressFunction: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
previousFunction: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card class="tutorial-card">
|
||||
<h3 class="tutorial-title">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<div class="tutorial-body">
|
||||
{{ description }}
|
||||
</div>
|
||||
<div class="tutorial-footer">
|
||||
<Button v-if="previousFunction" class="transparent" @click="previousFunction"> Back </Button>
|
||||
{{ progress }}/9
|
||||
<ProgressBar :progress="(progress / 9) * 100" />
|
||||
<Button color="primary" :action="progressFunction">
|
||||
{{ progress === 9 ? 'Finish' : 'Next' }}
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tutorial-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-md);
|
||||
border: 1px solid var(--color-button-bg);
|
||||
width: 22rem;
|
||||
}
|
||||
|
||||
.tutorial-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tutorial-footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--gap-sm);
|
||||
|
||||
.transparent {
|
||||
border: 1px solid var(--color-button-bg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -18,28 +18,20 @@ import { invoke } from '@tauri-apps/api/tauri'
|
||||
/// This returns a DeviceLoginSuccess object, with two relevant fields:
|
||||
/// - verification_uri: the URL to go to to complete the flow
|
||||
/// - user_code: the code to enter on the verification_uri page
|
||||
export async function authenticate_begin_flow() {
|
||||
return await invoke('plugin:auth|auth_authenticate_begin_flow')
|
||||
export async function login() {
|
||||
return await invoke('auth_login')
|
||||
}
|
||||
|
||||
/// Authenticate a user with Hydra - part 2
|
||||
/// This completes the authentication flow quasi-synchronously, returning the sign-in credentials
|
||||
/// (and also adding the credentials to the state)
|
||||
/// This returns a Credentials object
|
||||
export async function authenticate_await_completion() {
|
||||
return await invoke('plugin:auth|auth_authenticate_await_completion')
|
||||
}
|
||||
|
||||
export async function cancel_flow() {
|
||||
return await invoke('plugin:auth|auth_cancel_flow')
|
||||
}
|
||||
|
||||
/// Refresh some credentials using Hydra, if needed
|
||||
/// Retrieves the default user
|
||||
/// user is UUID
|
||||
/// update_name is bool
|
||||
/// Returns a Credentials object
|
||||
export async function refresh(user, update_name) {
|
||||
return await invoke('plugin:auth|auth_refresh', { user, update_name })
|
||||
export async function get_default_user() {
|
||||
return await invoke('plugin:auth|auth_get_default_user')
|
||||
}
|
||||
|
||||
/// Updates the default user
|
||||
/// user is UUID
|
||||
export async function set_default_user(user) {
|
||||
return await invoke('plugin:auth|auth_set_default_user', { user })
|
||||
}
|
||||
|
||||
/// Remove a user account from the database
|
||||
@@ -48,13 +40,6 @@ export async function remove_user(user) {
|
||||
return await invoke('plugin:auth|auth_remove_user', { user })
|
||||
}
|
||||
|
||||
// Add a path as a profile in-memory
|
||||
// user is UUID
|
||||
/// Returns a bool
|
||||
export async function has_user(user) {
|
||||
return await invoke('plugin:auth|auth_has_user', { user })
|
||||
}
|
||||
|
||||
/// Returns a list of users
|
||||
/// Returns an Array of Credentials
|
||||
export async function users() {
|
||||
|
||||
21
theseus_gui/src/store/error.js
Normal file
21
theseus_gui/src/store/error.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useError = defineStore('errorsStore', {
|
||||
state: () => ({
|
||||
errorModal: null,
|
||||
}),
|
||||
actions: {
|
||||
setErrorModal(ref) {
|
||||
this.errorModal = ref
|
||||
},
|
||||
showError(error) {
|
||||
this.errorModal.show(error)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const handleSevereError = (err) => {
|
||||
const error = useError()
|
||||
error.showError(err)
|
||||
console.error(err)
|
||||
}
|
||||
Reference in New Issue
Block a user