diff --git a/theseus_gui/package.json b/theseus_gui/package.json index 7104f500a..a65e83378 100644 --- a/theseus_gui/package.json +++ b/theseus_gui/package.json @@ -14,11 +14,12 @@ }, "dependencies": { "@tauri-apps/api": "^1.3.0", + "@vintl/vintl": "^4.4.1", "dayjs": "^1.11.7", "floating-vue": "^2.0.0-beta.20", "mixpanel-browser": "^2.47.0", "ofetch": "^1.0.1", - "omorphia": "^0.4.38", + "omorphia": "^0.7.3", "pinia": "^2.1.3", "qrcode.vue": "^3.4.0", "tauri-plugin-window-state-api": "github:tauri-apps/tauri-plugin-window-state#v1", diff --git a/theseus_gui/pnpm-lock.yaml b/theseus_gui/pnpm-lock.yaml index 34af6ce8f..a6f069f16 100644 --- a/theseus_gui/pnpm-lock.yaml +++ b/theseus_gui/pnpm-lock.yaml @@ -8,6 +8,9 @@ dependencies: '@tauri-apps/api': specifier: ^1.3.0 version: 1.3.0 + '@vintl/vintl': + specifier: ^4.4.1 + version: 4.4.1(vue@3.3.4) dayjs: specifier: ^1.11.7 version: 1.11.7 @@ -21,8 +24,8 @@ dependencies: specifier: ^1.0.1 version: 1.0.1 omorphia: - specifier: ^0.4.38 - version: 0.4.38 + specifier: ^0.7.3 + version: 0.7.3(vue@3.3.4) pinia: specifier: ^2.1.3 version: 2.1.3(vue@3.3.4) @@ -31,7 +34,7 @@ dependencies: version: 3.4.0(vue@3.3.4) tauri-plugin-window-state-api: specifier: github:tauri-apps/tauri-plugin-window-state#v1 - version: github.com/tauri-apps/tauri-plugin-window-state/5ea9eb0d4a9affd17269f92c0085935046be3f4a + version: github.com/tauri-apps/tauri-plugin-window-state/91fafb628cd0c83ad52bdf9029cad212381f740a vite-svg-loader: specifier: ^4.0.0 version: 4.0.0 @@ -105,6 +108,118 @@ packages: '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 + /@braw/async-computed@5.0.2(vue@3.3.4): + resolution: {integrity: sha512-fThqjZBTPvWtbD90Nkd4IldN7dpCkxfvthuk12ZBjkPPjh+wuRGi3HYiUqUSAOOVS0NHSxpsQFfg+qO275FtYA==} + peerDependencies: + vue: ^2.7 || ^3.2.45 + dependencies: + vue: 3.3.4 + dev: false + + /@codemirror/autocomplete@6.12.0(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0)(@lezer/common@1.2.1): + resolution: {integrity: sha512-r4IjdYFthwbCQyvqnSlx0WBHRHi8nBvU+WjJxFUij81qsBfhNudf/XKKmmC2j3m0LaOYUQTf3qiEK1J8lO1sdg==} + peerDependencies: + '@codemirror/language': ^6.0.0 + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + '@lezer/common': ^1.0.0 + dependencies: + '@codemirror/language': 6.10.0 + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + '@lezer/common': 1.2.1 + dev: false + + /@codemirror/commands@6.3.3: + resolution: {integrity: sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==} + dependencies: + '@codemirror/language': 6.10.0 + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + '@lezer/common': 1.2.1 + dev: false + + /@codemirror/lang-css@6.2.1(@codemirror/view@6.23.0): + resolution: {integrity: sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==} + dependencies: + '@codemirror/autocomplete': 6.12.0(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0)(@lezer/common@1.2.1) + '@codemirror/language': 6.10.0 + '@codemirror/state': 6.4.0 + '@lezer/common': 1.2.1 + '@lezer/css': 1.1.7 + transitivePeerDependencies: + - '@codemirror/view' + dev: false + + /@codemirror/lang-html@6.4.7: + resolution: {integrity: sha512-y9hWSSO41XlcL4uYwWyk0lEgTHcelWWfRuqmvcAmxfCs0HNWZdriWo/EU43S63SxEZpc1Hd50Itw7ktfQvfkUg==} + dependencies: + '@codemirror/autocomplete': 6.12.0(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0)(@lezer/common@1.2.1) + '@codemirror/lang-css': 6.2.1(@codemirror/view@6.23.0) + '@codemirror/lang-javascript': 6.2.1 + '@codemirror/language': 6.10.0 + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + '@lezer/common': 1.2.1 + '@lezer/css': 1.1.7 + '@lezer/html': 1.3.8 + dev: false + + /@codemirror/lang-javascript@6.2.1: + resolution: {integrity: sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==} + dependencies: + '@codemirror/autocomplete': 6.12.0(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0)(@lezer/common@1.2.1) + '@codemirror/language': 6.10.0 + '@codemirror/lint': 6.4.2 + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + '@lezer/common': 1.2.1 + '@lezer/javascript': 1.4.13 + dev: false + + /@codemirror/lang-markdown@6.2.4: + resolution: {integrity: sha512-UghkA1vSMs8bT7RSZM6vsIocigyah2bV00eRQuZy76401UmFZdsTsbQNBGdyxRQDOLeEvF5iFwap0BM8LKyd+g==} + dependencies: + '@codemirror/autocomplete': 6.12.0(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0)(@lezer/common@1.2.1) + '@codemirror/lang-html': 6.4.7 + '@codemirror/language': 6.10.0 + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + '@lezer/common': 1.2.1 + '@lezer/markdown': 1.2.0 + dev: false + + /@codemirror/language@6.10.0: + resolution: {integrity: sha512-2vaNn9aPGCRFKWcHPFksctzJ8yS5p7YoaT+jHpc0UGKzNuAIx4qy6R5wiqbP+heEEdyaABA582mNqSHzSoYdmg==} + dependencies: + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + '@lezer/common': 1.2.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.3.14 + style-mod: 4.1.0 + dev: false + + /@codemirror/lint@6.4.2: + resolution: {integrity: sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==} + dependencies: + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + crelt: 1.0.6 + dev: false + + /@codemirror/state@6.4.0: + resolution: {integrity: sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A==} + dev: false + + /@codemirror/view@6.23.0: + resolution: {integrity: sha512-/51px9N4uW8NpuWkyUX+iam5+PM6io2fm+QmRnzwqBy5v/pwGg9T0kILFtYeum8hjuvENtgsGNKluOfqIICmeQ==} + dependencies: + '@codemirror/state': 6.4.0 + style-mod: 4.1.0 + w3c-keyname: 2.2.8 + dev: false + /@esbuild/android-arm64@0.17.19: resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} engines: {node: '>=12'} @@ -350,6 +465,79 @@ packages: '@floating-ui/core': 0.3.1 dev: false + /@formatjs/ecma402-abstract@1.18.2: + resolution: {integrity: sha512-+QoPW4csYALsQIl8GbN14igZzDbuwzcpWrku9nyMXlaqAlwRBgl5V+p0vWMGFqHOw37czNXaP/lEk4wbLgcmtA==} + dependencies: + '@formatjs/intl-localematcher': 0.5.4 + tslib: 2.6.2 + dev: false + + /@formatjs/fast-memoize@2.2.0: + resolution: {integrity: sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==} + dependencies: + tslib: 2.6.2 + dev: false + + /@formatjs/icu-messageformat-parser@2.7.5: + resolution: {integrity: sha512-zCB53HdGDibh6/2ISEN3TGsFQruQ6gGKMFV94qHNyVrs0tNO6ncKhV0vq0n3Ydz8ipIQ2GaYAvfCoimNOVvKqA==} + dependencies: + '@formatjs/ecma402-abstract': 1.18.2 + '@formatjs/icu-skeleton-parser': 1.7.2 + tslib: 2.6.2 + dev: false + + /@formatjs/icu-skeleton-parser@1.7.2: + resolution: {integrity: sha512-nlIXVv280bjGW3ail5Np1+xgGKBnMhwQQIivgbk9xX0af8ESQO+y2VW9TOY7mCrs3WH786uVpZlLimXAlXH7SA==} + dependencies: + '@formatjs/ecma402-abstract': 1.18.2 + tslib: 2.6.2 + dev: false + + /@formatjs/intl-displaynames@6.6.6: + resolution: {integrity: sha512-Dg5URSjx0uzF8VZXtHb6KYZ6LFEEhCbAbKoYChYHEOnMFTw/ZU3jIo/NrujzQD2EfKPgQzIq73LOUvW6Z/LpFA==} + dependencies: + '@formatjs/ecma402-abstract': 1.18.2 + '@formatjs/intl-localematcher': 0.5.4 + tslib: 2.6.2 + dev: false + + /@formatjs/intl-listformat@7.5.5: + resolution: {integrity: sha512-XoI52qrU6aBGJC9KJddqnacuBbPlb/bXFN+lIFVFhQ1RnFHpzuFrlFdjD9am2O7ZSYsyqzYRpkVcXeT1GHkwDQ==} + dependencies: + '@formatjs/ecma402-abstract': 1.18.2 + '@formatjs/intl-localematcher': 0.5.4 + tslib: 2.6.2 + dev: false + + /@formatjs/intl-localematcher@0.4.2: + resolution: {integrity: sha512-BGdtJFmaNJy5An/Zan4OId/yR9Ih1OojFjcduX/xOvq798OgWSyDtd6Qd5jqJXwJs1ipe4Fxu9+cshic5Ox2tA==} + dependencies: + tslib: 2.6.2 + dev: false + + /@formatjs/intl-localematcher@0.5.4: + resolution: {integrity: sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==} + dependencies: + tslib: 2.6.2 + dev: false + + /@formatjs/intl@2.9.11: + resolution: {integrity: sha512-wJF5GKuopgeKy75e11JPjueC/XKAxrOndqVEZqg5zDrGuxALUD6Vo/x+oDTQwVZYf2zJnEzqZlUGtv5gSi/ChQ==} + peerDependencies: + typescript: ^4.7 || 5 + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@formatjs/ecma402-abstract': 1.18.2 + '@formatjs/fast-memoize': 2.2.0 + '@formatjs/icu-messageformat-parser': 2.7.5 + '@formatjs/intl-displaynames': 6.6.6 + '@formatjs/intl-listformat': 7.5.5 + intl-messageformat: 10.5.10 + tslib: 2.6.2 + dev: false + /@humanwhocodes/config-array@0.11.8: resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} engines: {node: '>=10.10.0'} @@ -373,6 +561,53 @@ packages: /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + /@lezer/common@1.2.1: + resolution: {integrity: sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==} + dev: false + + /@lezer/css@1.1.7: + resolution: {integrity: sha512-7BlFFAKNn/b39jJLrhdLSX5A2k56GIJvyLqdmm7UU+7XvequY084iuKDMAEhAmAzHnwDE8FK4OQtsIUssW91tg==} + dependencies: + '@lezer/common': 1.2.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.3.14 + dev: false + + /@lezer/highlight@1.2.0: + resolution: {integrity: sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==} + dependencies: + '@lezer/common': 1.2.1 + dev: false + + /@lezer/html@1.3.8: + resolution: {integrity: sha512-EXseJ3pUzWxE6XQBQdqWHZqqlGQRSuNMBcLb6mZWS2J2v+QZhOObD+3ZIKIcm59ntTzyor4LqFTb72iJc3k23Q==} + dependencies: + '@lezer/common': 1.2.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.3.14 + dev: false + + /@lezer/javascript@1.4.13: + resolution: {integrity: sha512-5IBr8LIO3xJdJH1e9aj/ZNLE4LSbdsx25wFmGRAZsj2zSmwAYjx26JyU/BYOCpRQlu1jcv1z3vy4NB9+UkfRow==} + dependencies: + '@lezer/common': 1.2.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.3.14 + dev: false + + /@lezer/lr@1.3.14: + resolution: {integrity: sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==} + dependencies: + '@lezer/common': 1.2.1 + dev: false + + /@lezer/markdown@1.2.0: + resolution: {integrity: sha512-d7MwsfAukZJo1GpPrcPGa3MxaFFOqNp0gbqF+3F7pTeNDOgeJN1muXzx1XXDPt+Ac+/voCzsH7qXqnn+xReG/g==} + dependencies: + '@lezer/common': 1.2.1 + '@lezer/highlight': 1.2.0 + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -419,8 +654,8 @@ packages: engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} dev: false - /@tauri-apps/api@1.4.0: - resolution: {integrity: sha512-Jd6HPoTM1PZSFIzq7FB8VmMu3qSSyo/3lSwLpoapW+lQ41CL5Dow2KryLg+gyazA/58DRWI9vu/XpEeHK4uMdw==} + /@tauri-apps/api@1.5.3: + resolution: {integrity: sha512-zxnDjHHKjOsrIzZm6nO5Xapb/BxqUq1tc7cGkFXsFkGTsSWgCPH1D8mm0XS9weJY2OaR73I3k3S+b7eSzJDfqA==} engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} dev: false @@ -541,6 +776,21 @@ packages: resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} dev: true + /@vintl/vintl@4.4.1(vue@3.3.4): + resolution: {integrity: sha512-1fAnK1Ru4GlUH6v2UPqPMFXvatiZuDlgF3GBrUYDBvs4mzg+j3cmH9GgX7DqBtpRLI1iqcoQF10cnJs/e/0Dvw==} + peerDependencies: + vue: ^3.2.47 + dependencies: + '@braw/async-computed': 5.0.2(vue@3.3.4) + '@formatjs/icu-messageformat-parser': 2.7.5 + '@formatjs/intl': 2.9.11 + '@formatjs/intl-localematcher': 0.4.2 + intl-messageformat: 10.5.10 + vue: 3.3.4 + transitivePeerDependencies: + - typescript + dev: false + /@vitejs/plugin-vue@4.2.3(vite@4.3.9)(vue@3.3.4): resolution: {integrity: sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -629,6 +879,10 @@ packages: /@vue/shared@3.3.4: resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==} + /@yr/monotone-cubic-spline@1.0.3: + resolution: {integrity: sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==} + dev: false + /acorn-jsx@5.3.2(acorn@8.8.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -672,6 +926,18 @@ packages: picomatch: 2.3.1 dev: true + /apexcharts@3.45.1: + resolution: {integrity: sha512-pPjj/SA6dfPvR/IKRZF0STdfBGpBh3WRt7K0DFuW9P8erypYkX17EHu3/molPRfo2zSiQwTVpshHC5ncysqfkA==} + dependencies: + '@yr/monotone-cubic-spline': 1.0.3 + svg.draggable.js: 2.2.2 + svg.easing.js: 2.0.0 + svg.filter.js: 2.0.2 + svg.pathmorphing.js: 0.1.3 + svg.resize.js: 1.4.3 + svg.select.js: 3.0.1 + dev: false + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -726,7 +992,7 @@ packages: normalize-path: 3.0.0 readdirp: 3.6.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /color-convert@2.0.1: @@ -753,6 +1019,10 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true + /crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + dev: false + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -1106,8 +1376,8 @@ packages: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true - /fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true @@ -1193,6 +1463,15 @@ packages: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true + /intl-messageformat@10.5.10: + resolution: {integrity: sha512-3yzwX6t/my9WRtNiqP05r+/UkpWxwstQiwaHAiuHmDRt7ykzWJ+nceOVjNLZYYWGiSltY+C+Likd8OIVkASepw==} + dependencies: + '@formatjs/ecma402-abstract': 1.18.2 + '@formatjs/fast-memoize': 2.2.0 + '@formatjs/icu-messageformat-parser': 2.7.5 + tslib: 2.6.2 + dev: false + /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1355,9 +1634,17 @@ packages: ufo: 1.1.2 dev: false - /omorphia@0.4.38: - resolution: {integrity: sha512-V0vEarmAart6Gf5WuPUZ58TuIiQf7rI5HJpmYU7FVbtdvZ3q08VqyKZflCddbeBSFQ4/N+A+sNr/ELf/jz+Cug==} - dependencies: + /omorphia@0.7.3(vue@3.3.4): + resolution: {integrity: sha512-Xk9o3xk/rFuZeR0LLoJNbOEvnQjAh6wOTZrksgCDAVuX30cSnpuihI9lsZNlH+4V1TXOB5FpVSHBEA/+LGzwHQ==} + peerDependencies: + vue: ^3.3.4 + dependencies: + '@codemirror/commands': 6.3.3 + '@codemirror/lang-markdown': 6.2.4 + '@codemirror/language': 6.10.0 + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + apexcharts: 3.45.1 dayjs: 1.11.7 floating-vue: 2.0.0-beta.20(vue@3.3.4) highlight.js: 11.8.0 @@ -1366,6 +1653,7 @@ packages: vue: 3.3.4 vue-router: 4.2.1(vue@3.3.4) vue-select: 4.0.0-beta.6(vue@3.3.4) + vue3-apexcharts: 1.4.4(apexcharts@3.45.1)(vue@3.3.4) xss: 1.0.14 dev: false @@ -1521,7 +1809,7 @@ packages: engines: {node: '>=10.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /rollup@3.23.0: @@ -1529,7 +1817,7 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /run-parallel@1.2.0: @@ -1589,6 +1877,10 @@ packages: engines: {node: '>=8'} dev: true + /style-mod@4.1.0: + resolution: {integrity: sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==} + dev: false + /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -1596,6 +1888,60 @@ packages: has-flag: 4.0.0 dev: true + /svg.draggable.js@2.2.2: + resolution: {integrity: sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==} + engines: {node: '>= 0.8.0'} + dependencies: + svg.js: 2.7.1 + dev: false + + /svg.easing.js@2.0.0: + resolution: {integrity: sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==} + engines: {node: '>= 0.8.0'} + dependencies: + svg.js: 2.7.1 + dev: false + + /svg.filter.js@2.0.2: + resolution: {integrity: sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==} + engines: {node: '>= 0.8.0'} + dependencies: + svg.js: 2.7.1 + dev: false + + /svg.js@2.7.1: + resolution: {integrity: sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==} + dev: false + + /svg.pathmorphing.js@0.1.3: + resolution: {integrity: sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==} + engines: {node: '>= 0.8.0'} + dependencies: + svg.js: 2.7.1 + dev: false + + /svg.resize.js@1.4.3: + resolution: {integrity: sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==} + engines: {node: '>= 0.8.0'} + dependencies: + svg.js: 2.7.1 + svg.select.js: 2.1.2 + dev: false + + /svg.select.js@2.1.2: + resolution: {integrity: sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==} + engines: {node: '>= 0.8.0'} + dependencies: + svg.js: 2.7.1 + dev: false + + /svg.select.js@3.0.1: + resolution: {integrity: sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==} + engines: {node: '>= 0.8.0'} + dependencies: + svg.js: 2.7.1 + dev: false + /svgo@3.0.2: resolution: {integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==} engines: {node: '>=14.0.0'} @@ -1624,6 +1970,10 @@ packages: is-number: 7.0.0 dev: true + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false + /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -1704,7 +2054,7 @@ packages: rollup: 3.23.0 sass: 1.62.1 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /vue-demi@0.14.5(vue@3.3.4): @@ -1789,6 +2139,16 @@ packages: vue-resize: 2.0.0-alpha.1(vue@3.3.4) dev: false + /vue3-apexcharts@1.4.4(apexcharts@3.45.1)(vue@3.3.4): + resolution: {integrity: sha512-TH89uZrxGjaDvkaYAISvj8+k6Bf1rUKFillc8oJirs5XZEPiwM1ELKZQ786wz0rfPqkSHHny2lqqUCK7Rw+LcQ==} + peerDependencies: + apexcharts: '> 3.0.0' + vue: '> 3.0.0' + dependencies: + apexcharts: 3.45.1 + vue: 3.3.4 + dev: false + /vue@3.3.4: resolution: {integrity: sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==} dependencies: @@ -1798,6 +2158,10 @@ packages: '@vue/server-renderer': 3.3.4(vue@3.3.4) '@vue/shared': 3.3.4 + /w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + dev: false + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1838,10 +2202,10 @@ packages: engines: {node: '>=10'} dev: true - github.com/tauri-apps/tauri-plugin-window-state/5ea9eb0d4a9affd17269f92c0085935046be3f4a: - resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-window-state/tar.gz/5ea9eb0d4a9affd17269f92c0085935046be3f4a} + github.com/tauri-apps/tauri-plugin-window-state/91fafb628cd0c83ad52bdf9029cad212381f740a: + resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-window-state/tar.gz/91fafb628cd0c83ad52bdf9029cad212381f740a} name: tauri-plugin-window-state-api version: 0.0.0 dependencies: - '@tauri-apps/api': 1.4.0 + '@tauri-apps/api': 1.5.3 dev: false diff --git a/theseus_gui/src/App.vue b/theseus_gui/src/App.vue index a93e3c66d..09198ecb0 100644 --- a/theseus_gui/src/App.vue +++ b/theseus_gui/src/App.vue @@ -1,20 +1,25 @@ @@ -288,8 +271,6 @@ command_listener(async (e) => { Open launcher logs - - Get support @@ -297,14 +278,35 @@ command_listener(async (e) => { - - - + + + + + + + + + + + + Home { }" > + Browse + Library - + - + + + + + + + + {{ instance.metadata.name }} + + + + + Support + + + + Settings + $refs.installationModal.show()" > + Create profile - - - + + - + + + + @@ -375,7 +415,7 @@ command_listener(async (e) => { @@ -397,9 +437,18 @@ command_listener(async (e) => { transition: all ease-in-out 0.1s; } +.logo { + height: calc(var(--appbar-height) - 2.5rem); + width: auto; + min-height: 100%; + color: var(--color-contrast); +} + .navigation-controls { - flex-grow: 1; - width: min-content; + display: flex; + flex-direction: row; + + align-items: center; } .appbar-row { @@ -422,7 +471,7 @@ command_listener(async (e) => { background-color: var(--color-raised-bg); color: var(--color-base); border-radius: 0; - height: 3.25rem; + height: var(--appbar-height); &.close { &:hover, @@ -441,8 +490,17 @@ command_listener(async (e) => { } .container { - --appbar-height: 3.25rem; + --appbar-height: 4.5rem; + + --sidebar-gap: 0.35rem; + --sidebar-width: 4.5rem; + --sidebar-open-width: 15rem; + --sidebar-padding: 0.75rem; + + --sidebar-icon-size: 1.5rem; + --sidebar-button-size: calc(var(--sidebar-width) - calc(var(--sidebar-padding) * 2)); + --sidebar-open-button-size: calc(var(--sidebar-open-width) - calc(var(--sidebar-padding) * 2)); height: 100vh; display: flex; @@ -456,11 +514,13 @@ command_listener(async (e) => { .appbar { display: flex; align-items: center; + justify-content: space-between; + flex-grow: 1; background: var(--color-raised-bg); text-align: center; padding: var(--gap-md); - height: 3.25rem; + height: var(--appbar-height); gap: var(--gap-sm); //no select user-select: none; @@ -469,7 +529,7 @@ command_listener(async (e) => { .router-view { width: 100%; - height: calc(100% - 3.125rem); + height: calc(100% - var(--appbar-height)); overflow: auto; overflow-x: hidden; background-color: var(--color-bg); @@ -488,7 +548,7 @@ command_listener(async (e) => { .appbar-failure { display: flex; /* Change to flex to align items horizontally */ justify-content: flex-end; /* Align items to the right */ - height: 3.25rem; + height: var(--appbar-height); //no select user-select: none; -webkit-user-select: none; @@ -528,12 +588,75 @@ command_listener(async (e) => { .nav-container { display: flex; flex-direction: column; + + padding-left: var(--sidebar-padding); + padding-right: var(--sidebar-padding); + padding-bottom: var(--sidebar-padding); + align-items: center; justify-content: space-between; + height: 100%; + background-color: var(--color-raised-bg); box-shadow: var(--shadow-inset-sm), var(--shadow-floating); - padding: var(--gap-md); + + transition: all ease-in-out 0.1s; + + width: var(--sidebar-width); +} + +.nav-container__open { + width: var(--sidebar-open-width); +} + +.square-collapsed-space { + height: var(--appbar-height); + width: 100%; + + user-select: none; + -webkit-user-select: none; + + display: flex; + justify-content: flex-start; + align-items: center; +} + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + .square-collapsed-space { + height: auto; + padding-bottom: var(--gap-md); + } +} + +.divider { + height: auto; + width: 100%; + + hr { + background-color: var(--color-button-bg); + border: none; + color: var(--color-button-bg); + + height: 1px; + width: 100%; + + margin: 0; + } + + margin-top: var(--sidebar-gap); + // div should always have + 1 --sidebar-gap margin to the bottom to be equal + margin-bottom: calc(var(--sidebar-gap) * 2); + + padding-left: var(--sidebar-padding); + padding-right: var(--sidebar-padding); +} + +.instances { + flex: 1; + + flex-flow: column wrap; // This hides any elements that aren't fully visible + overflow: hidden; } .pages-list { @@ -541,9 +664,12 @@ command_listener(async (e) => { flex-direction: column; align-items: flex-start; justify-content: flex-start; + width: 100%; - gap: 0.5rem; + gap: var(--sidebar-gap); + + .page-item, a { display: flex; align-items: center; @@ -551,19 +677,20 @@ command_listener(async (e) => { background: inherit; transition: all ease-in-out 0.1s; color: var(--color-base); - box-shadow: none; &.router-link-active { - color: var(--color-contrast); - background: var(--color-button-bg); - box-shadow: var(--shadow-floating); + color: var(--color-brand); + background: var(--color-brand-highlight); } &:hover { - background-color: var(--color-button-bg); color: var(--color-contrast); - box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); - text-decoration: none; + background: var(--color-button-bg); + } + + &.router-link-active:hover { + color: var(--color-brand); + background: var(--color-brand-highlight); } } @@ -573,78 +700,45 @@ command_listener(async (e) => { } } -.collapsed-button { - height: 3rem !important; - width: 3rem !important; - padding: 0.75rem; - border-radius: var(--radius-md); - box-shadow: none; - - svg { - width: 1.5rem !important; - height: 1.5rem !important; - max-width: 1.5rem !important; - max-height: 1.5rem !important; +:deep { + .non-collapse { + width: var(--sidebar-button-size) !important; } -} -.instance-list { - display: flex; - flex-direction: column; - justify-content: center; - width: 70%; - margin: 0.4rem; + .collapsed-button { + justify-content: flex-start; - p:nth-child(1) { - font-size: 0.6rem; - } + // width: var(--sidebar-icon-size); + height: var(--sidebar-button-size); + width: 100%; - & > p { - color: var(--color-base); - margin: 0.8rem 0; - font-size: 0.7rem; - line-height: 0.8125rem; - font-weight: 500; - text-transform: uppercase; - } -} + flex-shrink: 0; -.user-section { - display: flex; - justify-content: flex-start; - align-items: center; - width: 100%; - height: 4.375rem; + padding: var(--sidebar-padding) !important; + border-radius: 99999px; + box-shadow: none; - section { - display: flex; - flex-direction: column; - justify-content: flex-start; - text-align: left; - margin-left: 0.5rem; - } + white-space: nowrap; + overflow: hidden; - .username { - margin-bottom: 0.3rem; - font-weight: 400; - line-height: 1.25rem; - color: var(--color-contrast); - } + transition: all ease-in-out 0.1s; - a { - font-weight: 400; - color: var(--color-secondary); - } -} + .collapsed-button__icon, + svg { + width: var(--sidebar-icon-size) !important; + height: var(--sidebar-icon-size) !important; -.nav-section { - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: center; - width: 100%; - height: 100%; - gap: 1rem; + flex-shrink: 0; + + border-radius: var(--radius-xs); + } + + .collapsed-button__label { + word-spacing: normal; // Why is this even needed? + opacity: var(--sidebar-label-opacity); + transition: all ease-in-out 0.1s; + } + } } .video { diff --git a/theseus_gui/src/assets/icons/arrow-left-from-line.svg b/theseus_gui/src/assets/icons/arrow-left-from-line.svg new file mode 100644 index 000000000..79de99b94 --- /dev/null +++ b/theseus_gui/src/assets/icons/arrow-left-from-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/theseus_gui/src/assets/icons/arrow-right-from-line.svg b/theseus_gui/src/assets/icons/arrow-right-from-line.svg new file mode 100644 index 000000000..dae5b9319 --- /dev/null +++ b/theseus_gui/src/assets/icons/arrow-right-from-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/theseus_gui/src/assets/icons/index.js b/theseus_gui/src/assets/icons/index.js index 185e498b5..7126c2410 100644 --- a/theseus_gui/src/assets/icons/index.js +++ b/theseus_gui/src/assets/icons/index.js @@ -12,3 +12,5 @@ export { default as NewInstanceImage } from './new-instance.svg' export { default as MenuIcon } from './menu.svg' export { default as BugIcon } from './bug.svg' export { default as ChatIcon } from './messages-square.svg' +export { default as ArrowLeftFromLineIcon } from './arrow-left-from-line.svg' +export { default as ArrowRightFromLineIcon } from './arrow-right-from-line.svg' diff --git a/theseus_gui/src/assets/stylesheets/components.scss b/theseus_gui/src/assets/stylesheets/components.scss new file mode 100644 index 000000000..96f872a62 --- /dev/null +++ b/theseus_gui/src/assets/stylesheets/components.scss @@ -0,0 +1,35 @@ +// Quick snippets stolen from knossos project to make omorphia components fit + +.btn, +.button-base, +a { + // filter will change + will-change: filter; +} + +.iconified-input { + align-items: center; + display: inline-flex; + position: relative; + + input { + padding-left: 2.25rem; + width: 100%; + } + + &:focus-within svg { + color: var(--color-button-text-active); + opacity: 1; + } + + svg { + position: absolute; + left: 0.75rem; + height: 1.25rem; + width: 1.25rem; + z-index: 1; + + color: var(--color-button-text); + opacity: 0.6; + } +} diff --git a/theseus_gui/src/components/GridDisplay.vue b/theseus_gui/src/components/GridDisplay.vue index 615ff5e08..39b288776 100644 --- a/theseus_gui/src/components/GridDisplay.vue +++ b/theseus_gui/src/components/GridDisplay.vue @@ -15,7 +15,7 @@ import { XIcon, Button, formatCategoryHeader, - ModalConfirm, + ConfirmModal, } from 'omorphia' import ContextMenu from '@/components/ui/ContextMenu.vue' import dayjs from 'dayjs' @@ -233,7 +233,7 @@ const filteredResults = computed(() => { }) - { - (search = '')"> + (search = '')"> diff --git a/theseus_gui/src/components/RowDisplay.vue b/theseus_gui/src/components/RowDisplay.vue index 500fa830e..f08a3bbde 100644 --- a/theseus_gui/src/components/RowDisplay.vue +++ b/theseus_gui/src/components/RowDisplay.vue @@ -11,7 +11,7 @@ import { ExternalIcon, EyeIcon, ChevronRightIcon, - ModalConfirm, + ConfirmModal, } from 'omorphia' import Instance from '@/components/ui/Instance.vue' import { computed, onMounted, onUnmounted, ref } from 'vue' @@ -227,7 +227,7 @@ onUnmounted(() => { - clipboardWrite(loginUrl)" > diff --git a/theseus_gui/src/components/ui/Breadcrumbs.vue b/theseus_gui/src/components/ui/Breadcrumbs.vue index 6af4ac02d..23d1e3edc 100644 --- a/theseus_gui/src/components/ui/Breadcrumbs.vue +++ b/theseus_gui/src/components/ui/Breadcrumbs.vue @@ -1,53 +1,60 @@ - - - - - - - {{ breadcrumbData.resetToNames(breadcrumbs) }} - + + + + {{ - breadcrumb.name.charAt(0) === '?' - ? breadcrumbData.getName(breadcrumb.name.slice(1)) - : breadcrumb.name - }} + > + {{ breadcrumbName(breadcrumb.name) }} - {{ - breadcrumb.name.charAt(0) === '?' - ? breadcrumbData.getName(breadcrumb.name.slice(1)) - : breadcrumb.name - }} + + {{ breadcrumbName(breadcrumb.name) }} + diff --git a/theseus_gui/src/components/ui/tutorial/FakeGridDisplay.vue b/theseus_gui/src/components/ui/tutorial/FakeGridDisplay.vue index bafa22ef0..6b2fcdf2d 100644 --- a/theseus_gui/src/components/ui/tutorial/FakeGridDisplay.vue +++ b/theseus_gui/src/components/ui/tutorial/FakeGridDisplay.vue @@ -23,7 +23,7 @@ defineProps({ - (search = '')"> + (search = '')"> diff --git a/theseus_gui/src/components/ui/tutorial/FakeSearch.vue b/theseus_gui/src/components/ui/tutorial/FakeSearch.vue index 336d21f9e..b1bb94c92 100644 --- a/theseus_gui/src/components/ui/tutorial/FakeSearch.vue +++ b/theseus_gui/src/components/ui/tutorial/FakeSearch.vue @@ -189,7 +189,7 @@ defineProps({ type="text" :placeholder="`Search ${projectType}s...`" /> - (query = '')"> + (query = '')"> diff --git a/theseus_gui/src/components/ui/tutorial/ImportingCard.vue b/theseus_gui/src/components/ui/tutorial/ImportingCard.vue index 3139b5d4a..c086c1305 100644 --- a/theseus_gui/src/components/ui/tutorial/ImportingCard.vue +++ b/theseus_gui/src/components/ui/tutorial/ImportingCard.vue @@ -132,7 +132,7 @@ const next = async () => { placeholder="Path to launcher" @change="setPath" /> - (selectedLauncherPath = '')"> + (selectedLauncherPath = '')"> diff --git a/theseus_gui/src/components/ui/tutorial/LoginCard.vue b/theseus_gui/src/components/ui/tutorial/LoginCard.vue index 5a928e6c8..2f4fd32f2 100644 --- a/theseus_gui/src/components/ui/tutorial/LoginCard.vue +++ b/theseus_gui/src/components/ui/tutorial/LoginCard.vue @@ -119,7 +119,7 @@ const clipboardWrite = async (a) => { - + diff --git a/theseus_gui/src/composables/click.js b/theseus_gui/src/composables/click.js new file mode 100644 index 000000000..1f8fdb8ea --- /dev/null +++ b/theseus_gui/src/composables/click.js @@ -0,0 +1,47 @@ +import { openExternal } from '@/helpers/external' +import { onMounted } from 'vue' + +const disableMiddleClick = (e) => { + // disables middle click -> new tab + if (e.button === 1) { + e.preventDefault() + // instead do a left click + const event = new MouseEvent('click', { + view: window, + bubbles: true, + cancelable: true, + }) + e.target.dispatchEvent(event) + } +} + +const disableExternalNavigation = (e, window) => { + let target = e.target + + while (target != null) { + if (target.matches('a')) { + if ( + target.href && + ['http://', 'https://', 'mailto:', 'tel:'].some((v) => target.href.startsWith(v)) && + !target.classList.contains('router-link-active') && + !target.href.startsWith('http://localhost') && + !target.href.startsWith('https://tauri.localhost') + ) { + openExternal(window, target.href) + } + e.preventDefault() + break + } + target = target.parentElement + } +} + +export const useDisableClicks = (document, window) => { + onMounted(() => { + document + .querySelector('body') + .addEventListener('click', (e) => disableExternalNavigation(e, window)) + + document.querySelector('body').addEventListener('auxclick', disableMiddleClick) + }) +} diff --git a/theseus_gui/src/helpers/external.js b/theseus_gui/src/helpers/external.js new file mode 100644 index 000000000..9acf01352 --- /dev/null +++ b/theseus_gui/src/helpers/external.js @@ -0,0 +1,9 @@ +export const openExternal = (window, url) => { + window.__TAURI_INVOKE__('tauri', { + __tauriModule: 'Shell', + message: { + cmd: 'open', + path: url, + }, + }) +} diff --git a/theseus_gui/src/helpers/icon.js b/theseus_gui/src/helpers/icon.js new file mode 100644 index 000000000..734ddc363 --- /dev/null +++ b/theseus_gui/src/helpers/icon.js @@ -0,0 +1,12 @@ +import { convertFileSrc } from '@tauri-apps/api/tauri' + +export const iconPathAsUrl = (iconPath) => { + if (!iconPath) { + return '' + } + const startsWithHttp = iconPath.startsWith('http') + if (startsWithHttp) { + return iconPath + } + return convertFileSrc(iconPath) +} diff --git a/theseus_gui/src/main.js b/theseus_gui/src/main.js index 6086d334a..e53e0ca0f 100644 --- a/theseus_gui/src/main.js +++ b/theseus_gui/src/main.js @@ -2,8 +2,10 @@ import { createApp } from 'vue' import router from '@/routes' import App from '@/App.vue' import { createPinia } from 'pinia' +import { createPlugin as createVintl } from '@vintl/vintl/plugin' import 'omorphia/dist/style.css' import '@/assets/stylesheets/global.scss' +import '@/assets/stylesheets/components.scss' import 'floating-vue/dist/style.css' import FloatingVue from 'floating-vue' import { get_opening_command, initialize_state } from '@/helpers/state' @@ -14,9 +16,25 @@ import { isDev } from './helpers/utils.js' const pinia = createPinia() +const vintl = createVintl({ + controllerOpts: { + defaultLocale: 'en-US', + locale: 'en-US', + locales: [ + { + tag: 'en-US', + meta: { + displayName: 'American English', + }, + }, + ], + }, +}) + let app = createApp(App) app.use(router) app.use(pinia) +app.use(vintl) app.use(FloatingVue) app.mixin(loadCssMixin) @@ -54,7 +72,7 @@ initialize_state() .finally(() => { mountedApp.initialize() get_opening_command().then((command) => { - console.log(JSON.stringify(command)) // change me to use whatever FE command handler is made + console.log('Opening Command', JSON.stringify(command)) // change me to use whatever FE command handler is made }) }) }) diff --git a/theseus_gui/src/pages/Browse.vue b/theseus_gui/src/pages/Browse.vue index 5fbdd8c71..31f765603 100644 --- a/theseus_gui/src/pages/Browse.vue +++ b/theseus_gui/src/pages/Browse.vue @@ -705,7 +705,7 @@ onUnmounted(() => unlistenOffline()) :placeholder="`Search ${projectType}s...`" @input="onSearchChange(1)" /> - clearSearch()"> + clearSearch()"> @@ -843,13 +843,6 @@ onUnmounted(() => unlistenOffline()) min-height: min-content !important; } -.iconified-input { - input { - max-width: none !important; - flex-basis: auto; - } -} - .search-panel-container { display: inline-flex; flex-direction: row; diff --git a/theseus_gui/src/pages/Index.vue b/theseus_gui/src/pages/Index.vue index 0f0c008c1..3d66713b1 100644 --- a/theseus_gui/src/pages/Index.vue +++ b/theseus_gui/src/pages/Index.vue @@ -1,14 +1,13 @@ - + diff --git a/theseus_gui/src/pages/Settings.vue b/theseus_gui/src/pages/Settings.vue index 0faeae113..f8f1e453d 100644 --- a/theseus_gui/src/pages/Settings.vue +++ b/theseus_gui/src/pages/Settings.vue @@ -16,13 +16,16 @@ import { import { handleError, useTheming } from '@/store/state' import { is_dir_writeable, change_config_dir, get, set } from '@/helpers/settings' import { get_max_memory } from '@/helpers/jre' -import { get as getCreds, logout } from '@/helpers/mr_auth.js' + +import { useModrinthAuth } from '@/store/mr_auth.js' + import JavaSelector from '@/components/ui/JavaSelector.vue' import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue' import { mixpanel_opt_out_tracking, mixpanel_opt_in_tracking } from '@/helpers/mixpanel' import { open } from '@tauri-apps/api/dialog' import { getOS } from '@/helpers/utils.js' import { version } from '../../package.json' +import { storeToRefs } from 'pinia' const pageOptions = ['Home', 'Library'] @@ -105,17 +108,13 @@ watch( { deep: true } ) -const credentials = ref(await getCreds().catch(handleError)) +const mrAuth = useModrinthAuth() +const { auth } = storeToRefs(mrAuth) const loginScreenModal = ref() -async function logOut() { - await logout().catch(handleError) - credentials.value = await getCreds().catch(handleError) -} - async function signInAfter() { loginScreenModal.value.hide() - credentials.value = await getCreds().catch(handleError) + await mrAuth.get() } async function findLauncherDir() { @@ -163,12 +162,12 @@ async function refreshDir() { Manage account - - You are currently logged in as {{ credentials.user.username }}. + + You are currently logged in as {{ auth?.user.username }}. Sign in to your Modrinth account. - + Sign out @@ -187,7 +186,7 @@ async function refreshDir() { - + diff --git a/theseus_gui/src/pages/instance/Index.vue b/theseus_gui/src/pages/instance/Index.vue index 41d639ba6..699013f93 100644 --- a/theseus_gui/src/pages/instance/Index.vue +++ b/theseus_gui/src/pages/instance/Index.vue @@ -146,20 +146,39 @@ import { } from '@/helpers/process' import { offline_listener, process_listener, profile_listener } from '@/helpers/events' import { useRoute, useRouter } from 'vue-router' -import { ref, onUnmounted } from 'vue' +import { ref, onUnmounted, defineProps, watch } from 'vue' import { handleError, useBreadcrumbs, useLoading } from '@/store/state' import { isOffline, showProfileInFolder } from '@/helpers/utils.js' import ContextMenu from '@/components/ui/ContextMenu.vue' import { mixpanel_track } from '@/helpers/mixpanel' import { convertFileSrc } from '@tauri-apps/api/tauri' import { useFetch } from '@/helpers/fetch' +import { useInstances } from '@/store/instances' + +const props = defineProps({ + id: { + type: String, + required: false, + default: null, + }, +}) +const router = useRouter() const route = useRoute() -const router = useRouter() const breadcrumbs = useBreadcrumbs() -const instance = ref(await get(route.params.id).catch(handleError)) +const instance = ref(await get(route.params.id || props.id).catch(handleError)) + +watch( + () => route.params.id, + async (id) => { + if (!id) return + instance.value = await get(id).catch(handleError) + } +) + +const instancesStore = useInstances() breadcrumbs.setName( 'Instance', @@ -194,6 +213,8 @@ const startInstance = async (context) => { game_version: instance.value.metadata.game_version, source: context, }) + + await instancesStore.refreshInstances() } const checkProcess = async () => { @@ -419,6 +440,7 @@ Button { width: 100%; color: var(--color-primary); box-shadow: none; + justify-content: start; &.router-link-exact-active { box-shadow: var(--shadow-inset-lg); diff --git a/theseus_gui/src/pages/instance/Mods.vue b/theseus_gui/src/pages/instance/Mods.vue index 87032e72c..cf38efc8e 100644 --- a/theseus_gui/src/pages/instance/Mods.vue +++ b/theseus_gui/src/pages/instance/Mods.vue @@ -20,7 +20,7 @@ class="text-input" autocomplete="off" /> - (searchFilter = '')"> + (searchFilter = '')"> @@ -43,23 +43,27 @@ Update all - - + + - Add content - - - - Add from file - - + Add content + + + + + + Add from file + + + No projects found Add a project to get started - - + + + Add content + + - - - Add content - - + + - Add from file + Add from file - + { } } -const handleContentOptionClick = async (args) => { - if (args.option === 'search') { - await router.push({ - path: `/browse/${props.instance.metadata.loader === 'vanilla' ? 'datapack' : 'mod'}`, - query: { i: props.instance.path }, - }) - } else if (args.option === 'from_file') { - const newProject = await open({ multiple: true }) - if (!newProject) return +const onSearchContent = async () => { + await router.push({ + path: `/browse/${props.instance.metadata.loader === 'vanilla' ? 'datapack' : 'mod'}`, + query: { i: props.instance.path }, + }) +} - for (const project of newProject) { - await add_project_from_path(props.instance.path, project, 'mod').catch(handleError) - } - initProjects(await get(props.instance.path).catch(handleError)) +const onFileContent = async () => { + const newProject = await open({ multiple: true }) + if (!newProject) return + + for (const project of newProject) { + await add_project_from_path(props.instance.path, project, 'mod').catch(handleError) } + initProjects(await get(props.instance.path).catch(handleError)) } watch(selectAll, () => { @@ -959,9 +978,17 @@ onUnmounted(() => { white-space: nowrap; align-items: center; - :deep(.dropdown-row) { - .btn { - height: 2.5rem !important; + :deep { + .popup-container { + .btn { + height: 2.5rem !important; + } + } + + .dropdown-row { + .btn { + height: 2.5rem !important; + } } } diff --git a/theseus_gui/src/pages/instance/Options.vue b/theseus_gui/src/pages/instance/Options.vue index d8dead98c..b35f85db3 100644 --- a/theseus_gui/src/pages/instance/Options.vue +++ b/theseus_gui/src/pages/instance/Options.vue @@ -1,5 +1,5 @@ - p?.metadata?.version?.project_id === data.value.id diff --git a/theseus_gui/src/store/breadcrumbs.js b/theseus_gui/src/store/breadcrumbs.js index 268afc5a8..80905b6de 100644 --- a/theseus_gui/src/store/breadcrumbs.js +++ b/theseus_gui/src/store/breadcrumbs.js @@ -1,3 +1,5 @@ +import { computed } from 'vue' + import { defineStore } from 'pinia' export const useBreadcrumbs = defineStore('breadcrumbsStore', { @@ -34,3 +36,28 @@ export const useBreadcrumbs = defineStore('breadcrumbsStore', { }, }, }) + +export const useBreadcrumbContext = (route) => { + const breadcrumbs = useBreadcrumbs() + + const routeContext = computed(() => { + const { meta } = route + if (meta?.useContext) { + return breadcrumbs.context + } else if (meta?.useRootContext) { + return breadcrumbs.rootContext + } else { + return null + } + }) + + const routeBreadcrumbs = computed(() => { + const { meta } = route + return routeContext.value ? [routeContext.value, ...meta.breadcrumb] : meta.breadcrumb + }) + + return { + routeContext, + routeBreadcrumbs, + } +} diff --git a/theseus_gui/src/store/instances.js b/theseus_gui/src/store/instances.js new file mode 100644 index 000000000..9d6954d0c --- /dev/null +++ b/theseus_gui/src/store/instances.js @@ -0,0 +1,43 @@ +import { ref, onMounted, computed } from 'vue' +import dayjs from 'dayjs' + +import { list } from '@/helpers/profile.js' +import { handleError } from '@/store/notifications' +import { defineStore } from 'pinia' + +export const useInstances = defineStore('instancesStore', () => { + const instances = ref({}) + + const instanceList = computed(() => { + return Object.values(instances.value) + }) + const instancesByPlayed = computed(() => { + return instanceList.value.sort((a, b) => { + return dayjs(b?.metadata?.last_played ?? 0).diff(dayjs(a?.metadata?.last_played ?? 0)) + }) + }) + + const setInstances = async () => { + try { + const p = await list(true) + instances.value = p + } catch (error) { + handleError(error) + } + } + + onMounted(async () => { + await setInstances() + }) + + const refreshInstances = async () => { + await setInstances() + } + + return { + instanceList, + instancesByPlayed, + + refreshInstances, + } +}) diff --git a/theseus_gui/src/store/mr_auth.js b/theseus_gui/src/store/mr_auth.js new file mode 100644 index 000000000..9fb40cebe --- /dev/null +++ b/theseus_gui/src/store/mr_auth.js @@ -0,0 +1,40 @@ +import { ref, onMounted } from 'vue' +import { get as getCredentials, logout as removeCredentials } from '@/helpers/mr_auth.js' +import { handleError } from '@/store/state.js' +import { defineStore } from 'pinia' + +export const useModrinthAuth = defineStore('modrinthAuthStore', () => { + const auth = ref(null) + + const get = async () => { + try { + const creds = await getCredentials() + auth.value = creds + return creds + } catch (error) { + handleError(error) + } + return null + } + + const logout = async () => { + try { + const result = await removeCredentials() + auth.value = null + return result + } catch (error) { + handleError(error) + } + return null + } + + onMounted(() => { + get() + }) + + return { + auth, + get, + logout, + } +})
Add a project to get started