Compare commits
1 Commits
shared-ins
...
plus-theme
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7d096e768 |
@@ -13,6 +13,3 @@ max_line_length = 100
|
|||||||
[*.md]
|
[*.md]
|
||||||
max_line_length = off
|
max_line_length = off
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
[*.rs]
|
|
||||||
indent_size = 4
|
|
||||||
@@ -6,7 +6,6 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- 'v*'
|
||||||
paths:
|
paths:
|
||||||
- .github/workflows/app-release.yml
|
|
||||||
- 'apps/app/**'
|
- 'apps/app/**'
|
||||||
- 'apps/app-frontend/**'
|
- 'apps/app-frontend/**'
|
||||||
- 'packages/app-lib/**'
|
- 'packages/app-lib/**'
|
||||||
@@ -21,12 +20,12 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
platform: [macos-latest, windows-latest, ubuntu-22.04]
|
platform: [macos-latest, windows-latest, ubuntu-20.04]
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Rust setup (mac)
|
- name: Rust setup (mac)
|
||||||
if: startsWith(matrix.platform, 'macos')
|
if: startsWith(matrix.platform, 'macos')
|
||||||
@@ -44,34 +43,13 @@ jobs:
|
|||||||
- name: Setup rust cache
|
- name: Setup rust cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: target/**
|
||||||
target/**
|
|
||||||
!target/*/release/bundle/*/*.dmg
|
|
||||||
!target/*/release/bundle/*/*.app.tar.gz
|
|
||||||
!target/*/release/bundle/*/*.app.tar.gz.sig
|
|
||||||
!target/release/bundle/*/*.dmg
|
|
||||||
!target/release/bundle/*/*.app.tar.gz
|
|
||||||
!target/release/bundle/*/*.app.tar.gz.sig
|
|
||||||
|
|
||||||
!target/release/bundle/*/*.AppImage
|
|
||||||
!target/release/bundle/*/*.AppImage.tar.gz
|
|
||||||
!target/release/bundle/*/*.AppImage.tar.gz.sig
|
|
||||||
!target/release/bundle/*/*.deb
|
|
||||||
!target/release/bundle/*/*.rpm
|
|
||||||
|
|
||||||
!target/release/bundle/msi/*.msi
|
|
||||||
!target/release/bundle/msi/*.msi.zip
|
|
||||||
!target/release/bundle/msi/*.msi.zip.sig
|
|
||||||
|
|
||||||
!target/release/bundle/nsis/*.exe
|
|
||||||
!target/release/bundle/nsis/*.nsis.zip
|
|
||||||
!target/release/bundle/nsis/*.nsis.zip.sig
|
|
||||||
key: ${{ runner.os }}-rust-target-${{ hashFiles('**/Cargo.lock') }}
|
key: ${{ runner.os }}-rust-target-${{ hashFiles('**/Cargo.lock') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-rust-target-
|
${{ runner.os }}-rust-target-
|
||||||
|
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
@@ -88,7 +66,7 @@ jobs:
|
|||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Setup pnpm cache
|
- name: Setup pnpm cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
@@ -99,13 +77,14 @@ jobs:
|
|||||||
if: startsWith(matrix.platform, 'ubuntu')
|
if: startsWith(matrix.platform, 'ubuntu')
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev pkg-config libayatana-appindicator3-dev librsvg2-dev
|
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf libselinux1
|
||||||
|
|
||||||
- name: Install frontend dependencies
|
- name: Install frontend dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: build app (macos)
|
- name: build app (macos)
|
||||||
run: pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config "tauri-release.conf.json"
|
uses: tauri-apps/tauri-action@v0
|
||||||
|
id: build_os_mac
|
||||||
if: startsWith(matrix.platform, 'macos')
|
if: startsWith(matrix.platform, 'macos')
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -116,40 +95,34 @@ jobs:
|
|||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
with:
|
||||||
|
args: "--target universal-apple-darwin --config ./apps/app/tauri-release.conf.json"
|
||||||
|
working-directory: ./apps/app
|
||||||
|
|
||||||
- name: build app
|
- name: build app
|
||||||
run: pnpm --filter=@modrinth/app run tauri build --config "tauri-release.conf.json"
|
uses: tauri-apps/tauri-action@v0
|
||||||
id: build_os
|
id: build_os
|
||||||
if: "!startsWith(matrix.platform, 'macos')"
|
if: "!startsWith(matrix.platform, 'macos')"
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
with:
|
||||||
|
args: "--config ./apps/app/tauri-release.conf.json"
|
||||||
|
working-directory: ./apps/app
|
||||||
|
|
||||||
- name: upload ${{ matrix.platform }}
|
- name: upload ${{ matrix.platform }}
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
|
if: startsWith(matrix.platform, 'macos')
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.platform }}
|
name: ${{ matrix.platform }}
|
||||||
path: |
|
path: "${{ join(fromJSON(steps.build_os_mac.outputs.artifactPaths), '\n') }}"
|
||||||
target/*/release/bundle/*/*.dmg
|
|
||||||
target/*/release/bundle/*/*.app.tar.gz
|
|
||||||
target/*/release/bundle/*/*.app.tar.gz.sig
|
|
||||||
target/release/bundle/*/*.dmg
|
|
||||||
target/release/bundle/*/*.app.tar.gz
|
|
||||||
target/release/bundle/*/*.app.tar.gz.sig
|
|
||||||
|
|
||||||
target/release/bundle/*/*.AppImage
|
- name: upload ${{ matrix.platform }}
|
||||||
target/release/bundle/*/*.AppImage.tar.gz
|
uses: actions/upload-artifact@v3
|
||||||
target/release/bundle/*/*.AppImage.tar.gz.sig
|
if: "!startsWith(matrix.platform, 'macos')"
|
||||||
target/release/bundle/*/*.deb
|
with:
|
||||||
target/release/bundle/*/*.rpm
|
name: ${{ matrix.platform }}
|
||||||
|
path: "${{ join(fromJSON(steps.build_os.outputs.artifactPaths), '\n') }}"
|
||||||
target/release/bundle/msi/*.msi
|
|
||||||
target/release/bundle/msi/*.msi.zip
|
|
||||||
target/release/bundle/msi/*.msi.zip.sig
|
|
||||||
|
|
||||||
target/release/bundle/nsis/*.exe
|
|
||||||
target/release/bundle/nsis/*.nsis.zip
|
|
||||||
target/release/bundle/nsis/*.nsis.zip.sig
|
|
||||||
@@ -11,7 +11,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build, Test, and Lint
|
name: Build, Test, and Lint
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
@@ -30,7 +30,7 @@ jobs:
|
|||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf libselinux1
|
||||||
|
|
||||||
- name: Setup Node.JS environment
|
- name: Setup Node.JS environment
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
@@ -62,19 +62,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
env:
|
|
||||||
SQLX_OFFLINE: true
|
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: pnpm lint
|
run: pnpm lint
|
||||||
env:
|
|
||||||
SQLX_OFFLINE: true
|
|
||||||
|
|
||||||
- name: Start docker compose
|
|
||||||
run: docker compose up -d
|
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: pnpm test
|
run: pnpm test
|
||||||
env:
|
|
||||||
SQLX_OFFLINE: true
|
|
||||||
DATABASE_URL: postgresql://labrinth:labrinth@localhost/postgres
|
|
||||||
43
.github/workflows/daedalus-docker.yml
vendored
43
.github/workflows/daedalus-docker.yml
vendored
@@ -1,43 +0,0 @@
|
|||||||
name: daedalus-docker-build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main" ]
|
|
||||||
paths:
|
|
||||||
- .github/workflows/daedalus-docker.yml
|
|
||||||
- 'apps/daedalus_client/**'
|
|
||||||
pull_request:
|
|
||||||
types: [ opened, synchronize ]
|
|
||||||
paths:
|
|
||||||
- .github/workflows/daedalus-docker.yml
|
|
||||||
- 'apps/daedalus_client/**'
|
|
||||||
merge_group:
|
|
||||||
types: [ checks_requested ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Fetch docker metadata
|
|
||||||
id: docker_meta
|
|
||||||
uses: docker/metadata-action@v3
|
|
||||||
with:
|
|
||||||
images: ghcr.io/modrinth/daedalus
|
|
||||||
-
|
|
||||||
name: Login to GitHub Images
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
-
|
|
||||||
name: Build and push
|
|
||||||
id: docker_build
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
file: ./apps/daedalus_client/Dockerfile
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.docker_meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
|
||||||
52
.github/workflows/daedalus-run.yml
vendored
52
.github/workflows/daedalus-run.yml
vendored
@@ -1,52 +0,0 @@
|
|||||||
name: Run Meta
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '*/5 * * * *'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run-docker:
|
|
||||||
if: github.repository_owner == 'modrinth'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
|
|
||||||
- name: Pull Docker image from GHCR
|
|
||||||
run: docker pull ghcr.io/modrinth/daedalus:main
|
|
||||||
|
|
||||||
- name: Run Docker container
|
|
||||||
env:
|
|
||||||
BASE_URL: ${{ secrets.BASE_URL }}
|
|
||||||
S3_ACCESS_TOKEN: ${{ secrets.S3_ACCESS_TOKEN }}
|
|
||||||
S3_SECRET: ${{ secrets.S3_SECRET }}
|
|
||||||
S3_URL: ${{ secrets.S3_URL }}
|
|
||||||
S3_REGION: ${{ secrets.S3_REGION }}
|
|
||||||
S3_BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME }}
|
|
||||||
CLOUDFLARE_INTEGRATION: ${{ secrets.CLOUDFLARE_INTEGRATION }}
|
|
||||||
CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }}
|
|
||||||
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
|
|
||||||
run: |
|
|
||||||
docker run \
|
|
||||||
--name daedalus \
|
|
||||||
-e RUST_LOG=warn,daedalus_client=trace \
|
|
||||||
-e BASE_URL=$BASE_URL \
|
|
||||||
-e S3_ACCESS_TOKEN=$S3_ACCESS_TOKEN \
|
|
||||||
-e S3_SECRET=$S3_SECRET \
|
|
||||||
-e S3_URL=$S3_URL \
|
|
||||||
-e S3_REGION=$S3_REGION \
|
|
||||||
-e S3_BUCKET_NAME=$S3_BUCKET_NAME \
|
|
||||||
-e CLOUDFLARE_INTEGRATION=$CLOUDFLARE_INTEGRATION \
|
|
||||||
-e CLOUDFLARE_TOKEN=$CLOUDFLARE_TOKEN \
|
|
||||||
-e CLOUDFLARE_ZONE_ID=$CLOUDFLARE_ZONE_ID \
|
|
||||||
ghcr.io/modrinth/daedalus:main
|
|
||||||
8
.github/workflows/frontend-pages.yml
vendored
8
.github/workflows/frontend-pages.yml
vendored
@@ -1,9 +1,6 @@
|
|||||||
name: Clear pages cache
|
name: Deploy frontend
|
||||||
|
|
||||||
on:
|
on: push
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- prod
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
wait:
|
wait:
|
||||||
@@ -19,6 +16,7 @@ jobs:
|
|||||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||||
accountId: '9ddae624c98677d68d93df6e524a6061'
|
accountId: '9ddae624c98677d68d93df6e524a6061'
|
||||||
project: 'frontend'
|
project: 'frontend'
|
||||||
|
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
commitHash: ${{ steps.push-changes.outputs.commit-hash }}
|
commitHash: ${{ steps.push-changes.outputs.commit-hash }}
|
||||||
- name: Purge cache
|
- name: Purge cache
|
||||||
if: github.ref == 'refs/heads/prod'
|
if: github.ref == 'refs/heads/prod'
|
||||||
|
|||||||
46
.github/workflows/labrinth-docker.yml
vendored
46
.github/workflows/labrinth-docker.yml
vendored
@@ -1,46 +0,0 @@
|
|||||||
name: docker-build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main" ]
|
|
||||||
paths:
|
|
||||||
- .github/workflows/labrinth-docker.yml
|
|
||||||
- 'apps/labrinth/**'
|
|
||||||
pull_request:
|
|
||||||
types: [ opened, synchronize ]
|
|
||||||
paths:
|
|
||||||
- .github/workflows/labrinth-docker.yml
|
|
||||||
- 'apps/labrinth/**'
|
|
||||||
merge_group:
|
|
||||||
types: [ checks_requested ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./apps/labrinth
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Fetch docker metadata
|
|
||||||
id: docker_meta
|
|
||||||
uses: docker/metadata-action@v3
|
|
||||||
with:
|
|
||||||
images: ghcr.io/modrinth/labrinth
|
|
||||||
-
|
|
||||||
name: Login to GitHub Images
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
-
|
|
||||||
name: Build and push
|
|
||||||
id: docker_build
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: ./apps/labrinth
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.docker_meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -55,9 +55,3 @@ generated
|
|||||||
|
|
||||||
# app testing dir
|
# app testing dir
|
||||||
app-playground-data/*
|
app-playground-data/*
|
||||||
|
|
||||||
# soley because i need the PORT to be 3002 due to WSL stuff
|
|
||||||
.env
|
|
||||||
apps/frontend/.env
|
|
||||||
|
|
||||||
.astro
|
|
||||||
|
|||||||
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
15
.idea/daedalus.iml
generated
15
.idea/daedalus.iml
generated
@@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="JAVA_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
|
||||||
<exclude-output />
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/daedalus_client_new/src" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/apps/daedalus_client/src" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/packages/daedalus/src" isTestSource="false" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
7
.idea/discord.xml
generated
7
.idea/discord.xml
generated
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="DiscordProjectSettings">
|
|
||||||
<option name="show" value="PROJECT_FILES" />
|
|
||||||
<option name="description" value="" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
26
.idea/libraries/KotlinJavaRuntime.xml
generated
26
.idea/libraries/KotlinJavaRuntime.xml
generated
@@ -1,26 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="KotlinJavaRuntime" type="repository">
|
|
||||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0" />
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.0/kotlin-stdlib-jdk8-1.8.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.0/kotlin-stdlib-1.8.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.0/kotlin-stdlib-common-1.8.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.0/kotlin-stdlib-jdk7-1.8.0.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC>
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.0/kotlin-stdlib-jdk8-1.8.0-javadoc.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.0/kotlin-stdlib-1.8.0-javadoc.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.0/kotlin-stdlib-common-1.8.0-javadoc.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.0/kotlin-stdlib-jdk7-1.8.0-javadoc.jar!/" />
|
|
||||||
</JAVADOC>
|
|
||||||
<SOURCES>
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.0/kotlin-stdlib-jdk8-1.8.0-sources.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.0/kotlin-stdlib-1.8.0-sources.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.0/kotlin-stdlib-common-1.8.0-sources.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.0/kotlin-stdlib-jdk7-1.8.0-sources.jar!/" />
|
|
||||||
</SOURCES>
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/daedalus.iml" filepath="$PROJECT_DIR$/.idea/daedalus.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
6645
Cargo.lock
generated
6645
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -3,10 +3,7 @@ resolver = '2'
|
|||||||
members = [
|
members = [
|
||||||
'./packages/app-lib',
|
'./packages/app-lib',
|
||||||
'./apps/app-playground',
|
'./apps/app-playground',
|
||||||
'./apps/app',
|
'./apps/app'
|
||||||
'./apps/labrinth',
|
|
||||||
'./apps/daedalus_client',
|
|
||||||
'./packages/daedalus',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Optimize for speed and reduce size on release builds
|
# Optimize for speed and reduce size on release builds
|
||||||
@@ -19,6 +16,3 @@ strip = true # Remove debug symbols
|
|||||||
|
|
||||||
[profile.dev.package.sqlx-macros]
|
[profile.dev.package.sqlx-macros]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
[patch.crates-io]
|
|
||||||
wry = { git = "https://github.com/modrinth/wry", rev = "27fb16b" }
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ This repository contains two primary packages. For detailed development informat
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
We welcome contributions! Before submitting any contributions, please read our [contributing guidelines](https://docs.modrinth.com/contributing/getting-started/).
|
We welcome contributions! Before submitting any contributions, please read our [contributing guidelines](https://support.modrinth.com/en/articles/8802215-contributing-to-modrinth).
|
||||||
|
|
||||||
If you plan to fork this repository for your own purposes, please review our [copying guidelines](COPYING.md).
|
If you plan to fork this repository for your own purposes, please review our [copying guidelines](COPYING.md).
|
||||||
|
|
||||||
|
|||||||
4
apps/app-frontend/.eslintrc.cjs
Normal file
4
apps/app-frontend/.eslintrc.cjs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
extends: ['custom/vue'],
|
||||||
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { createConfigForNuxt } from '@nuxt/eslint-config/flat'
|
|
||||||
import { fixupPluginRules } from '@eslint/compat'
|
|
||||||
import turboPlugin from 'eslint-plugin-turbo'
|
|
||||||
|
|
||||||
export default createConfigForNuxt().append([
|
|
||||||
{
|
|
||||||
name: 'turbo',
|
|
||||||
plugins: {
|
|
||||||
turbo: fixupPluginRules(turboPlugin),
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
'turbo/no-undeclared-env-vars': 'error',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'modrinth',
|
|
||||||
rules: {
|
|
||||||
'vue/html-self-closing': 'off',
|
|
||||||
'vue/multi-word-component-names': 'off',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Modrinth App</title>
|
<title>Modrinth App</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/src/assets/stylesheets/global.scss" />
|
<link rel="stylesheet" href="/src/assets/stylesheets/global.scss" />
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "@modrinth/app-frontend",
|
"name": "@modrinth/app-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.8.9",
|
"version": "0.8.3-1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc --noEmit && vite build",
|
"build": "vite build",
|
||||||
"tsc:check": "vue-tsc --noEmit",
|
|
||||||
"lint": "eslint . && prettier --check .",
|
"lint": "eslint . && prettier --check .",
|
||||||
"fix": "eslint . --fix && prettier --write ."
|
"fix": "eslint . --fix && prettier --write ."
|
||||||
},
|
},
|
||||||
@@ -14,19 +13,14 @@
|
|||||||
"@modrinth/assets": "workspace:*",
|
"@modrinth/assets": "workspace:*",
|
||||||
"@modrinth/ui": "workspace:*",
|
"@modrinth/ui": "workspace:*",
|
||||||
"@modrinth/utils": "workspace:*",
|
"@modrinth/utils": "workspace:*",
|
||||||
"@sentry/vue": "^8.27.0",
|
"@tauri-apps/api": "^1.6.0",
|
||||||
"@tauri-apps/api": "^2.0.0-rc.3",
|
|
||||||
"@tauri-apps/plugin-dialog": "^2.0.0-rc.0",
|
|
||||||
"@tauri-apps/plugin-os": "^2.0.0-rc.0",
|
|
||||||
"@tauri-apps/plugin-shell": "^2.0.0-rc.0",
|
|
||||||
"@tauri-apps/plugin-updater": "^2.0.0-rc.0",
|
|
||||||
"@tauri-apps/plugin-window-state": "^2.0.0-rc.0",
|
|
||||||
"@vintl/vintl": "^4.4.1",
|
"@vintl/vintl": "^4.4.1",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"floating-vue": "^5.2.2",
|
"floating-vue": "^5.2.2",
|
||||||
|
"mixpanel-browser": "^2.49.0",
|
||||||
"ofetch": "^1.3.4",
|
"ofetch": "^1.3.4",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"posthog-js": "^1.158.2",
|
"tauri-plugin-window-state-api": "github:tauri-apps/tauri-plugin-window-state#v1",
|
||||||
"vite-svg-loader": "^5.1.0",
|
"vite-svg-loader": "^5.1.0",
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.21",
|
||||||
"vue-multiselect": "3.0.0",
|
"vue-multiselect": "3.0.0",
|
||||||
@@ -34,21 +28,17 @@
|
|||||||
"vue-virtual-scroller": "v2.0.0-beta.8"
|
"vue-virtual-scroller": "v2.0.0-beta.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.1.1",
|
"@tauri-apps/cli": "^1.6.0",
|
||||||
"@nuxt/eslint-config": "^0.5.6",
|
|
||||||
"@vitejs/plugin-vue": "^5.0.4",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"eslint": "^9.9.1",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-custom": "workspace:*",
|
"eslint-config-custom": "workspace:*",
|
||||||
"eslint-plugin-turbo": "^2.1.1",
|
|
||||||
"postcss": "^8.4.39",
|
"postcss": "^8.4.39",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"sass": "^1.74.1",
|
"sass": "^1.74.1",
|
||||||
"tailwindcss": "^3.4.4",
|
"tailwindcss": "^3.4.4",
|
||||||
"tsconfig": "workspace:*",
|
"tsconfig": "workspace:*",
|
||||||
"typescript": "^5.5.4",
|
"vite": "^5.2.8"
|
||||||
"vite": "^5.2.8",
|
|
||||||
"vue-tsc": "^2.1.6"
|
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.4.0"
|
"packageManager": "pnpm@9.4.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, onMounted } from 'vue'
|
import { computed, ref, onMounted } from 'vue'
|
||||||
import { RouterView, RouterLink, useRouter, useRoute } from 'vue-router'
|
import { RouterView, RouterLink, useRouter, useRoute } from 'vue-router'
|
||||||
import {
|
import { HomeIcon, SearchIcon, LibraryIcon, PlusIcon, SettingsIcon, XIcon } from '@modrinth/assets'
|
||||||
HomeIcon,
|
|
||||||
SearchIcon,
|
|
||||||
LibraryIcon,
|
|
||||||
PlusIcon,
|
|
||||||
SettingsIcon,
|
|
||||||
XIcon,
|
|
||||||
DownloadIcon,
|
|
||||||
} from '@modrinth/assets'
|
|
||||||
import { Button, Notifications } from '@modrinth/ui'
|
import { Button, Notifications } from '@modrinth/ui'
|
||||||
import { useLoading, useTheming } from '@/store/state'
|
import { useLoading, useTheming } from '@/store/state'
|
||||||
import AccountsCard from '@/components/ui/AccountsCard.vue'
|
import AccountsCard from '@/components/ui/AccountsCard.vue'
|
||||||
@@ -23,26 +15,28 @@ import ModrinthLoadingIndicator from '@/components/modrinth-loading-indicator'
|
|||||||
import { handleError, useNotifications } from '@/store/notifications.js'
|
import { handleError, useNotifications } from '@/store/notifications.js'
|
||||||
import { command_listener, warning_listener } from '@/helpers/events.js'
|
import { command_listener, warning_listener } from '@/helpers/events.js'
|
||||||
import { MinimizeIcon, MaximizeIcon } from '@/assets/icons'
|
import { MinimizeIcon, MaximizeIcon } from '@/assets/icons'
|
||||||
import { type } from '@tauri-apps/plugin-os'
|
import { type } from '@tauri-apps/api/os'
|
||||||
import { isDev, getOS, restartApp } from '@/helpers/utils.js'
|
import { appWindow } from '@tauri-apps/api/window'
|
||||||
import { initAnalytics, debugAnalytics, optOutAnalytics, trackEvent } from '@/helpers/analytics'
|
import { isDev, getOS } from '@/helpers/utils.js'
|
||||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
import {
|
||||||
|
mixpanel_track,
|
||||||
|
mixpanel_init,
|
||||||
|
mixpanel_opt_out_tracking,
|
||||||
|
mixpanel_is_loaded,
|
||||||
|
} from '@/helpers/mixpanel'
|
||||||
|
import { saveWindowState, StateFlags } from 'tauri-plugin-window-state-api'
|
||||||
import { getVersion } from '@tauri-apps/api/app'
|
import { getVersion } from '@tauri-apps/api/app'
|
||||||
|
import { window as TauriWindow } from '@tauri-apps/api'
|
||||||
|
import { TauriEvent } from '@tauri-apps/api/event'
|
||||||
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
||||||
import { install_from_file } from './helpers/pack'
|
import { install_from_file } from './helpers/pack'
|
||||||
import { useError } from '@/store/error.js'
|
import { useError } from '@/store/error.js'
|
||||||
import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
|
|
||||||
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
|
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
|
||||||
import IncompatibilityWarningModal from '@/components/ui/install_flow/IncompatibilityWarningModal.vue'
|
import IncompatibilityWarningModal from '@/components/ui/install_flow/IncompatibilityWarningModal.vue'
|
||||||
import InstallConfirmModal from '@/components/ui/install_flow/InstallConfirmModal.vue'
|
import InstallConfirmModal from '@/components/ui/install_flow/InstallConfirmModal.vue'
|
||||||
import { useInstall } from '@/store/install.js'
|
import { useInstall } from '@/store/install.js'
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
import { open } from '@tauri-apps/plugin-shell'
|
|
||||||
import { get_opening_command, initialize_state } from '@/helpers/state'
|
import { get_opening_command, initialize_state } from '@/helpers/state'
|
||||||
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
|
|
||||||
import { renderString } from '@modrinth/utils'
|
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
|
||||||
import { check } from '@tauri-apps/plugin-updater'
|
|
||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
|
|
||||||
@@ -63,12 +57,6 @@ const os = ref('')
|
|||||||
|
|
||||||
const stateInitialized = ref(false)
|
const stateInitialized = ref(false)
|
||||||
|
|
||||||
const criticalErrorMessage = ref()
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
await useCheckDisableMouseover()
|
|
||||||
})
|
|
||||||
|
|
||||||
async function setupApp() {
|
async function setupApp() {
|
||||||
stateInitialized.value = true
|
stateInitialized.value = true
|
||||||
const {
|
const {
|
||||||
@@ -91,23 +79,21 @@ async function setupApp() {
|
|||||||
showOnboarding.value = !onboarded
|
showOnboarding.value = !onboarded
|
||||||
|
|
||||||
nativeDecorations.value = native_decorations
|
nativeDecorations.value = native_decorations
|
||||||
if (os.value !== 'MacOS') await getCurrentWindow().setDecorations(native_decorations)
|
if (os.value !== 'MacOS') await appWindow.setDecorations(native_decorations)
|
||||||
|
|
||||||
themeStore.setThemeState(theme)
|
themeStore.setThemeState(theme)
|
||||||
themeStore.collapsedNavigation = collapsed_navigation
|
themeStore.collapsedNavigation = collapsed_navigation
|
||||||
themeStore.advancedRendering = advanced_rendering
|
themeStore.advancedRendering = advanced_rendering
|
||||||
|
|
||||||
initAnalytics()
|
mixpanel_init('014c7d6a336d0efaefe3aca91063748d', { debug: dev, persistence: 'localStorage' })
|
||||||
if (!telemetry) {
|
if (!telemetry) {
|
||||||
optOutAnalytics()
|
mixpanel_opt_out_tracking()
|
||||||
}
|
}
|
||||||
if (dev) debugAnalytics()
|
mixpanel_track('Launched', { version, dev, onboarded })
|
||||||
trackEvent('Launched', { version, dev, onboarded })
|
|
||||||
|
|
||||||
if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault())
|
if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault())
|
||||||
|
|
||||||
const osType = await type()
|
if ((await type()) === 'Darwin') {
|
||||||
if (osType === 'macos') {
|
|
||||||
document.getElementsByTagName('html')[0].classList.add('mac')
|
document.getElementsByTagName('html')[0].classList.add('mac')
|
||||||
} else {
|
} else {
|
||||||
document.getElementsByTagName('html')[0].classList.add('windows')
|
document.getElementsByTagName('html')[0].classList.add('windows')
|
||||||
@@ -121,18 +107,7 @@ async function setupApp() {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
useFetch(
|
|
||||||
`https://api.modrinth.com/appCriticalAnnouncement.json?version=${version}`,
|
|
||||||
'criticalAnnouncements',
|
|
||||||
true,
|
|
||||||
).then((res) => {
|
|
||||||
if (res && res.header && res.body) {
|
|
||||||
criticalErrorMessage.value = res
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
get_opening_command().then(handleCommand)
|
get_opening_command().then(handleCommand)
|
||||||
checkUpdates()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateFailed = ref(false)
|
const stateFailed = ref(false)
|
||||||
@@ -152,12 +127,18 @@ initialize_state()
|
|||||||
|
|
||||||
const handleClose = async () => {
|
const handleClose = async () => {
|
||||||
await saveWindowState(StateFlags.ALL)
|
await saveWindowState(StateFlags.ALL)
|
||||||
await getCurrentWindow().close()
|
await TauriWindow.getCurrent().close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TauriWindow.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
|
||||||
|
await handleClose()
|
||||||
|
})
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
router.afterEach((to, from, failure) => {
|
router.afterEach((to, from, failure) => {
|
||||||
trackEvent('PageView', { path: to.path, fromPath: from.path, failed: failure })
|
if (mixpanel_is_loaded()) {
|
||||||
|
mixpanel_track('PageView', { path: to.path, fromPath: from.path, failed: failure })
|
||||||
|
}
|
||||||
})
|
})
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const isOnBrowse = computed(() => route.path.startsWith('/browse'))
|
const isOnBrowse = computed(() => route.path.startsWith('/browse'))
|
||||||
@@ -197,10 +178,15 @@ document.querySelector('body').addEventListener('click', function (e) {
|
|||||||
['http://', 'https://', 'mailto:', 'tel:'].some((v) => target.href.startsWith(v)) &&
|
['http://', 'https://', 'mailto:', 'tel:'].some((v) => target.href.startsWith(v)) &&
|
||||||
!target.classList.contains('router-link-active') &&
|
!target.classList.contains('router-link-active') &&
|
||||||
!target.href.startsWith('http://localhost') &&
|
!target.href.startsWith('http://localhost') &&
|
||||||
!target.href.startsWith('https://tauri.localhost') &&
|
!target.href.startsWith('https://tauri.localhost')
|
||||||
!target.href.startsWith('http://tauri.localhost')
|
|
||||||
) {
|
) {
|
||||||
open(target.href)
|
window.__TAURI_INVOKE__('tauri', {
|
||||||
|
__tauriModule: 'Shell',
|
||||||
|
message: {
|
||||||
|
cmd: 'open',
|
||||||
|
path: target.href,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
break
|
break
|
||||||
@@ -233,7 +219,7 @@ async function handleCommand(e) {
|
|||||||
// RunMRPack should directly install a local mrpack given a path
|
// RunMRPack should directly install a local mrpack given a path
|
||||||
if (e.path.endsWith('.mrpack')) {
|
if (e.path.endsWith('.mrpack')) {
|
||||||
await install_from_file(e.path).catch(handleError)
|
await install_from_file(e.path).catch(handleError)
|
||||||
trackEvent('InstanceCreate', {
|
mixpanel_track('InstanceCreate', {
|
||||||
source: 'CreationModalFileDrop',
|
source: 'CreationModalFileDrop',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -242,20 +228,6 @@ async function handleCommand(e) {
|
|||||||
urlModal.value.show(e)
|
urlModal.value.show(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateAvailable = ref(false)
|
|
||||||
async function checkUpdates() {
|
|
||||||
const update = await check()
|
|
||||||
console.log(update)
|
|
||||||
updateAvailable.value = !!update
|
|
||||||
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
checkUpdates()
|
|
||||||
},
|
|
||||||
5 * 1000 * 60,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -289,14 +261,6 @@ async function checkUpdates() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings pages-list">
|
<div class="settings pages-list">
|
||||||
<button
|
|
||||||
v-if="updateAvailable"
|
|
||||||
v-tooltip="'Install update'"
|
|
||||||
class="btn btn-outline btn-primary icon-only collapsed-button"
|
|
||||||
@click="restartApp()"
|
|
||||||
>
|
|
||||||
<DownloadIcon />
|
|
||||||
</button>
|
|
||||||
<Button
|
<Button
|
||||||
v-tooltip="'Create profile'"
|
v-tooltip="'Create profile'"
|
||||||
class="sleek-primary collapsed-button"
|
class="sleek-primary collapsed-button"
|
||||||
@@ -312,10 +276,6 @@ async function checkUpdates() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="view">
|
<div class="view">
|
||||||
<div v-if="criticalErrorMessage" class="critical-error-banner" data-tauri-drag-region>
|
|
||||||
<h1>{{ criticalErrorMessage.header }}</h1>
|
|
||||||
<div class="markdown-body" v-html="renderString(criticalErrorMessage.body ?? '')"></div>
|
|
||||||
</div>
|
|
||||||
<div class="appbar-row">
|
<div class="appbar-row">
|
||||||
<div data-tauri-drag-region class="appbar">
|
<div data-tauri-drag-region class="appbar">
|
||||||
<section class="navigation-controls">
|
<section class="navigation-controls">
|
||||||
@@ -328,14 +288,10 @@ async function checkUpdates() {
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<section v-if="!nativeDecorations" class="window-controls">
|
<section v-if="!nativeDecorations" class="window-controls">
|
||||||
<Button class="titlebar-button" icon-only @click="() => getCurrentWindow().minimize()">
|
<Button class="titlebar-button" icon-only @click="() => appWindow.minimize()">
|
||||||
<MinimizeIcon />
|
<MinimizeIcon />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button class="titlebar-button" icon-only @click="() => appWindow.toggleMaximize()">
|
||||||
class="titlebar-button"
|
|
||||||
icon-only
|
|
||||||
@click="() => getCurrentWindow().toggleMaximize()"
|
|
||||||
>
|
|
||||||
<MaximizeIcon />
|
<MaximizeIcon />
|
||||||
</Button>
|
</Button>
|
||||||
<Button class="titlebar-button close" icon-only @click="handleClose">
|
<Button class="titlebar-button close" icon-only @click="handleClose">
|
||||||
@@ -428,16 +384,6 @@ async function checkUpdates() {
|
|||||||
width: calc(100% - var(--sidebar-width));
|
width: calc(100% - var(--sidebar-width));
|
||||||
background-color: var(--color-raised-bg);
|
background-color: var(--color-raised-bg);
|
||||||
|
|
||||||
.critical-error-banner {
|
|
||||||
margin-top: -1.25rem;
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: rgba(203, 34, 69, 0.1);
|
|
||||||
border-left: 2px solid var(--color-red);
|
|
||||||
border-bottom: 2px solid var(--color-red);
|
|
||||||
border-right: 2px solid var(--color-red);
|
|
||||||
border-radius: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.appbar {
|
.appbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ import {
|
|||||||
SearchIcon,
|
SearchIcon,
|
||||||
XIcon,
|
XIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { Button, Card, DropdownSelect } from '@modrinth/ui'
|
import { ConfirmModal, Button, Card, DropdownSelect } from '@modrinth/ui'
|
||||||
import { formatCategoryHeader } from '@modrinth/utils'
|
import { formatCategoryHeader } from '@modrinth/utils'
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
import { useTheming } from '@/store/theme.js'
|
||||||
import { duplicate, remove } from '@/helpers/profile.js'
|
import { duplicate, remove } from '@/helpers/profile.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
instances: {
|
instances: {
|
||||||
@@ -35,6 +35,7 @@ const props = defineProps({
|
|||||||
const instanceOptions = ref(null)
|
const instanceOptions = ref(null)
|
||||||
const instanceComponents = ref(null)
|
const instanceComponents = ref(null)
|
||||||
|
|
||||||
|
const themeStore = useTheming()
|
||||||
const currentDeleteInstance = ref(null)
|
const currentDeleteInstance = ref(null)
|
||||||
const confirmModal = ref(null)
|
const confirmModal = ref(null)
|
||||||
|
|
||||||
@@ -229,12 +230,13 @@ const filteredResults = computed(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<ConfirmModalWrapper
|
<ConfirmModal
|
||||||
ref="confirmModal"
|
ref="confirmModal"
|
||||||
title="Are you sure you want to delete this instance?"
|
title="Are you sure you want to delete this instance?"
|
||||||
description="If you proceed, all data for your instance will be removed. You will not be able to recover it."
|
description="If you proceed, all data for your instance will be removed. You will not be able to recover it."
|
||||||
:has-to-type="false"
|
:has-to-type="false"
|
||||||
proceed-label="Delete"
|
proceed-label="Delete"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
@proceed="deleteProfile"
|
@proceed="deleteProfile"
|
||||||
/>
|
/>
|
||||||
<Card class="header">
|
<Card class="header">
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
EyeIcon,
|
EyeIcon,
|
||||||
ChevronRightIcon,
|
ChevronRightIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
|
import { ConfirmModal } from '@modrinth/ui'
|
||||||
import Instance from '@/components/ui/Instance.vue'
|
import Instance from '@/components/ui/Instance.vue'
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
@@ -22,7 +22,8 @@ import { handleError } from '@/store/notifications.js'
|
|||||||
import { duplicate, kill, remove, run } from '@/helpers/profile.js'
|
import { duplicate, kill, remove, run } from '@/helpers/profile.js'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
import { useTheming } from '@/store/state.js'
|
||||||
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
import { install as installVersion } from '@/store/install.js'
|
import { install as installVersion } from '@/store/install.js'
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ const instanceComponents = ref(null)
|
|||||||
const rows = ref(null)
|
const rows = ref(null)
|
||||||
const deleteConfirmModal = ref(null)
|
const deleteConfirmModal = ref(null)
|
||||||
|
|
||||||
|
const themeStore = useTheming()
|
||||||
const currentDeleteInstance = ref(null)
|
const currentDeleteInstance = ref(null)
|
||||||
|
|
||||||
async function deleteProfile() {
|
async function deleteProfile() {
|
||||||
@@ -123,14 +125,14 @@ const handleOptionsClick = async (args) => {
|
|||||||
await run(args.item.path).catch((err) =>
|
await run(args.item.path).catch((err) =>
|
||||||
handleSevereError(err, { profilePath: args.item.path }),
|
handleSevereError(err, { profilePath: args.item.path }),
|
||||||
)
|
)
|
||||||
trackEvent('InstanceStart', {
|
mixpanel_track('InstanceStart', {
|
||||||
loader: args.item.loader,
|
loader: args.item.loader,
|
||||||
game_version: args.item.game_version,
|
game_version: args.item.game_version,
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'stop':
|
case 'stop':
|
||||||
await kill(args.item.path).catch(handleError)
|
await kill(args.item.path).catch(handleError)
|
||||||
trackEvent('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: args.item.loader,
|
loader: args.item.loader,
|
||||||
game_version: args.item.game_version,
|
game_version: args.item.game_version,
|
||||||
})
|
})
|
||||||
@@ -205,12 +207,13 @@ onUnmounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ConfirmModalWrapper
|
<ConfirmModal
|
||||||
ref="deleteConfirmModal"
|
ref="deleteConfirmModal"
|
||||||
title="Are you sure you want to delete this instance?"
|
title="Are you sure you want to delete this instance?"
|
||||||
description="If you proceed, all data for your instance will be removed. You will not be able to recover it."
|
description="If you proceed, all data for your instance will be removed. You will not be able to recover it."
|
||||||
:has-to-type="false"
|
:has-to-type="false"
|
||||||
proceed-label="Delete"
|
proceed-label="Delete"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
@proceed="deleteProfile"
|
@proceed="deleteProfile"
|
||||||
/>
|
/>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
v-tooltip.right="'Minecraft accounts'"
|
v-tooltip.right="'Minecraft accounts'"
|
||||||
class="button-base avatar-button"
|
class="button-base avatar-button"
|
||||||
:class="{ expanded: mode === 'expanded' }"
|
:class="{ expanded: mode === 'expanded' }"
|
||||||
@click="toggleMenu"
|
@click="showCard = !showCard"
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
:size="mode === 'expanded' ? 'xs' : 'sm'"
|
:size="mode === 'expanded' ? 'xs' : 'sm'"
|
||||||
@@ -70,10 +70,9 @@ import {
|
|||||||
get_default_user,
|
get_default_user,
|
||||||
} from '@/helpers/auth'
|
} from '@/helpers/auth'
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { process_listener } from '@/helpers/events'
|
import { process_listener } from '@/helpers/events'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
mode: {
|
mode: {
|
||||||
@@ -119,7 +118,7 @@ async function login() {
|
|||||||
await refreshValues()
|
await refreshValues()
|
||||||
}
|
}
|
||||||
|
|
||||||
trackEvent('AccountLogIn')
|
mixpanel_track('AccountLogIn')
|
||||||
}
|
}
|
||||||
|
|
||||||
const logout = async (id) => {
|
const logout = async (id) => {
|
||||||
@@ -131,12 +130,12 @@ const logout = async (id) => {
|
|||||||
} else {
|
} else {
|
||||||
emit('change')
|
emit('change')
|
||||||
}
|
}
|
||||||
trackEvent('AccountLogOut')
|
mixpanel_track('AccountLogOut')
|
||||||
}
|
}
|
||||||
|
|
||||||
const showCard = ref(false)
|
let showCard = ref(false)
|
||||||
const card = ref(null)
|
let card = ref(null)
|
||||||
const button = ref(null)
|
let button = ref(null)
|
||||||
const handleClickOutside = (event) => {
|
const handleClickOutside = (event) => {
|
||||||
const elements = document.elementsFromPoint(event.clientX, event.clientY)
|
const elements = document.elementsFromPoint(event.clientX, event.clientY)
|
||||||
if (
|
if (
|
||||||
@@ -145,20 +144,7 @@ const handleClickOutside = (event) => {
|
|||||||
!elements.includes(card.value.$el) &&
|
!elements.includes(card.value.$el) &&
|
||||||
!button.value.contains(event.target)
|
!button.value.contains(event.target)
|
||||||
) {
|
) {
|
||||||
toggleMenu(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleMenu(override = true) {
|
|
||||||
if (showCard.value || !override) {
|
|
||||||
if (showCard.value) {
|
|
||||||
show_ads_window()
|
|
||||||
}
|
|
||||||
|
|
||||||
showCard.value = false
|
showCard.value = false
|
||||||
} else {
|
|
||||||
hide_ads_window()
|
|
||||||
showCard.value = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { DropdownIcon, FolderOpenIcon, SearchIcon } from '@modrinth/assets'
|
import { DropdownIcon, FolderOpenIcon, SearchIcon } from '@modrinth/assets'
|
||||||
import { Button, OverflowMenu } from '@modrinth/ui'
|
import { Button, OverflowMenu } from '@modrinth/ui'
|
||||||
import { open } from '@tauri-apps/plugin-dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import { add_project_from_path } from '@/helpers/profile.js'
|
import { add_project_from_path } from '@/helpers/profile.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@@ -20,7 +20,7 @@ const handleAddContentFromFile = async () => {
|
|||||||
if (!newProject) return
|
if (!newProject) return
|
||||||
|
|
||||||
for (const project of newProject) {
|
for (const project of newProject) {
|
||||||
await add_project_from_path(props.instance.path, project.path ?? project).catch(handleError)
|
await add_project_from_path(props.instance.path, project).catch(handleError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
|
|
||||||
|
|
||||||
const emit = defineEmits(['menu-closed', 'option-clicked'])
|
const emit = defineEmits(['menu-closed', 'option-clicked'])
|
||||||
|
|
||||||
@@ -38,7 +37,6 @@ const shown = ref(false)
|
|||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
showMenu: (event, passedItem, passedOptions) => {
|
showMenu: (event, passedItem, passedOptions) => {
|
||||||
hide_ads_window()
|
|
||||||
item.value = passedItem
|
item.value = passedItem
|
||||||
options.value = passedOptions
|
options.value = passedOptions
|
||||||
|
|
||||||
@@ -71,9 +69,6 @@ const isLinkedData = (item) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hideContextMenu = () => {
|
const hideContextMenu = () => {
|
||||||
if (shown.value) {
|
|
||||||
show_ads_window()
|
|
||||||
}
|
|
||||||
shown.value = false
|
shown.value = false
|
||||||
emit('menu-closed')
|
emit('menu-closed')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { XIcon, HammerIcon, LogInIcon, UpdatedIcon } from '@modrinth/assets'
|
import { XIcon, HammerIcon, LogInIcon, UpdatedIcon } from '@modrinth/assets'
|
||||||
|
import { Modal } from '@modrinth/ui'
|
||||||
import { ChatIcon } from '@/assets/icons'
|
import { ChatIcon } from '@/assets/icons'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { login as login_flow, set_default_user } from '@/helpers/auth.js'
|
import { login as login_flow, set_default_user } from '@/helpers/auth.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
|
import mixpanel from 'mixpanel-browser'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
import { cancel_directory_change } from '@/helpers/settings.js'
|
import { cancel_directory_change } from '@/helpers/settings.js'
|
||||||
import { install } from '@/helpers/profile.js'
|
import { install } from '@/helpers/profile.js'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
|
||||||
|
|
||||||
const errorModal = ref()
|
const errorModal = ref()
|
||||||
const error = ref()
|
const error = ref()
|
||||||
@@ -85,7 +85,7 @@ async function loginMinecraft() {
|
|||||||
await set_default_user(loggedIn.id).catch(handleError)
|
await set_default_user(loggedIn.id).catch(handleError)
|
||||||
}
|
}
|
||||||
|
|
||||||
await trackEvent('AccountLogIn', { source: 'ErrorModal' })
|
await mixpanel.track('AccountLogIn')
|
||||||
loadingMinecraft.value = false
|
loadingMinecraft.value = false
|
||||||
errorModal.value.hide()
|
errorModal.value.hide()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -121,7 +121,7 @@ async function repairInstance() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ModalWrapper ref="errorModal" :header="title" :closable="closable">
|
<Modal ref="errorModal" :header="title" :closable="closable">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="markdown-body">
|
<div class="markdown-body">
|
||||||
<template v-if="errorType === 'minecraft_auth'">
|
<template v-if="errorType === 'minecraft_auth'">
|
||||||
@@ -230,7 +230,7 @@ async function repairInstance() {
|
|||||||
</p>
|
</p>
|
||||||
<p>You may be able to fix it through one of the following ways:</p>
|
<p>You may be able to fix it through one of the following ways:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Ensuring you are connected to the internet, then try restarting the app.</li>
|
<li>Ennsuring you are connected to the internet, then try restarting the app.</li>
|
||||||
<li>Redownloading the app.</li>
|
<li>Redownloading the app.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
@@ -272,7 +272,7 @@ async function repairInstance() {
|
|||||||
<button v-if="closable" class="btn" @click="errorModal.hide()"><XIcon /> Close</button>
|
<button v-if="closable" class="btn" @click="errorModal.hide()"><XIcon /> Close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { XIcon, PlusIcon } from '@modrinth/assets'
|
import { XIcon, PlusIcon } from '@modrinth/assets'
|
||||||
import { Button, Checkbox } from '@modrinth/ui'
|
import { Button, Checkbox, Modal } from '@modrinth/ui'
|
||||||
import { PackageIcon, VersionIcon } from '@/assets/icons'
|
import { PackageIcon, VersionIcon } from '@/assets/icons'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { export_profile_mrpack, get_pack_export_candidates } from '@/helpers/profile.js'
|
import { export_profile_mrpack, get_pack_export_candidates } from '@/helpers/profile.js'
|
||||||
import { open } from '@tauri-apps/plugin-dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
import { useTheming } from '@/store/theme'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
instance: {
|
instance: {
|
||||||
@@ -30,6 +30,8 @@ const files = ref([])
|
|||||||
const folders = ref([])
|
const folders = ref([])
|
||||||
const showingFiles = ref(false)
|
const showingFiles = ref(false)
|
||||||
|
|
||||||
|
const themeStore = useTheming()
|
||||||
|
|
||||||
const initFiles = async () => {
|
const initFiles = async () => {
|
||||||
const newFolders = new Map()
|
const newFolders = new Map()
|
||||||
const sep = '/'
|
const sep = '/'
|
||||||
@@ -104,7 +106,7 @@ const exportPack = async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ModalWrapper ref="exportModal" header="Export modpack">
|
<Modal ref="exportModal" header="Export modpack" :noblur="!themeStore.advancedRendering">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="labeled_input">
|
<div class="labeled_input">
|
||||||
<p>Modpack Name</p>
|
<p>Modpack Name</p>
|
||||||
@@ -206,7 +208,7 @@ const exportPack = async () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import { onUnmounted, ref, computed } from 'vue'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { StopCircleIcon, PlayIcon } from '@modrinth/assets'
|
import { StopCircleIcon, PlayIcon } from '@modrinth/assets'
|
||||||
import { Card, Avatar, AnimatedLogo } from '@modrinth/ui'
|
import { Card, Avatar, AnimatedLogo } from '@modrinth/ui'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
import { kill, run } from '@/helpers/profile'
|
import { kill, run } from '@/helpers/profile'
|
||||||
import { get_by_profile_path } from '@/helpers/process'
|
import { get_by_profile_path } from '@/helpers/process'
|
||||||
import { process_listener } from '@/helpers/events'
|
import { process_listener } from '@/helpers/events'
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||||
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
instance: {
|
instance: {
|
||||||
@@ -45,7 +45,7 @@ const play = async (e, context) => {
|
|||||||
)
|
)
|
||||||
modLoading.value = false
|
modLoading.value = false
|
||||||
|
|
||||||
trackEvent('InstancePlay', {
|
mixpanel_track('InstancePlay', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
@@ -58,7 +58,7 @@ const stop = async (e, context) => {
|
|||||||
|
|
||||||
await kill(props.instance.path).catch(handleError)
|
await kill(props.instance.path).catch(handleError)
|
||||||
|
|
||||||
trackEvent('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<ModalWrapper ref="modal" header="Create instance">
|
<Modal ref="modal" header="Create instance" :noblur="!themeStore.advancedRendering">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<Chips v-model="creationType" :items="['custom', 'from file', 'import from launcher']" />
|
<Chips v-model="creationType" :items="['custom', 'from file', 'import from launcher']" />
|
||||||
</div>
|
</div>
|
||||||
@@ -193,11 +193,10 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
|
||||||
import {
|
import {
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
UploadIcon,
|
UploadIcon,
|
||||||
@@ -208,16 +207,18 @@ import {
|
|||||||
FolderSearchIcon,
|
FolderSearchIcon,
|
||||||
UpdatedIcon,
|
UpdatedIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { Avatar, Button, Chips, Checkbox } from '@modrinth/ui'
|
import { Avatar, Button, Chips, Modal, Checkbox } from '@modrinth/ui'
|
||||||
import { computed, onUnmounted, ref, shallowRef } from 'vue'
|
import { computed, onUnmounted, ref, shallowRef } from 'vue'
|
||||||
import { get_loaders } from '@/helpers/tags'
|
import { get_loaders } from '@/helpers/tags'
|
||||||
import { create } from '@/helpers/profile'
|
import { create } from '@/helpers/profile'
|
||||||
import { open } from '@tauri-apps/plugin-dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
import { tauri } from '@tauri-apps/api'
|
||||||
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
|
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import Multiselect from 'vue-multiselect'
|
import Multiselect from 'vue-multiselect'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
import { useTheming } from '@/store/state.js'
|
||||||
|
import { listen } from '@tauri-apps/api/event'
|
||||||
import { install_from_file } from '@/helpers/pack.js'
|
import { install_from_file } from '@/helpers/pack.js'
|
||||||
import {
|
import {
|
||||||
get_default_launcher_path,
|
get_default_launcher_path,
|
||||||
@@ -225,7 +226,8 @@ import {
|
|||||||
import_instance,
|
import_instance,
|
||||||
} from '@/helpers/import.js'
|
} from '@/helpers/import.js'
|
||||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||||
import { getCurrentWebview } from '@tauri-apps/api/webview'
|
|
||||||
|
const themeStore = useTheming()
|
||||||
|
|
||||||
const profile_name = ref('')
|
const profile_name = ref('')
|
||||||
const game_version = ref('')
|
const game_version = ref('')
|
||||||
@@ -255,22 +257,20 @@ defineExpose({
|
|||||||
isShowing.value = true
|
isShowing.value = true
|
||||||
modal.value.show()
|
modal.value.show()
|
||||||
|
|
||||||
unlistener.value = await getCurrentWebview().onDragDropEvent(async (event) => {
|
unlistener.value = await listen('tauri://file-drop', async (event) => {
|
||||||
// Only if modal is showing
|
// Only if modal is showing
|
||||||
if (!isShowing.value) return
|
if (!isShowing.value) return
|
||||||
if (event.payload.type !== 'drop') return
|
|
||||||
if (creationType.value !== 'from file') return
|
if (creationType.value !== 'from file') return
|
||||||
hide()
|
hide()
|
||||||
const { paths } = event.payload
|
if (event.payload && event.payload.length > 0 && event.payload[0].endsWith('.mrpack')) {
|
||||||
if (paths && paths.length > 0 && paths[0].endsWith('.mrpack')) {
|
await install_from_file(event.payload[0]).catch(handleError)
|
||||||
await install_from_file(paths[0]).catch(handleError)
|
mixpanel_track('InstanceCreate', {
|
||||||
trackEvent('InstanceCreate', {
|
|
||||||
source: 'CreationModalFileDrop',
|
source: 'CreationModalFileDrop',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
trackEvent('InstanceCreateStart', { source: 'CreationModal' })
|
mixpanel_track('InstanceCreateStart', { source: 'CreationModal' })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -360,7 +360,7 @@ const create_instance = async () => {
|
|||||||
icon.value,
|
icon.value,
|
||||||
).catch(handleError)
|
).catch(handleError)
|
||||||
|
|
||||||
trackEvent('InstanceCreate', {
|
mixpanel_track('InstanceCreate', {
|
||||||
profile_name: profile_name.value,
|
profile_name: profile_name.value,
|
||||||
game_version: game_version.value,
|
game_version: game_version.value,
|
||||||
loader: loader.value,
|
loader: loader.value,
|
||||||
@@ -371,7 +371,7 @@ const create_instance = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const upload_icon = async () => {
|
const upload_icon = async () => {
|
||||||
const res = await open({
|
icon.value = await open({
|
||||||
multiple: false,
|
multiple: false,
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
@@ -381,10 +381,8 @@ const upload_icon = async () => {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
icon.value = res.path ?? res
|
|
||||||
|
|
||||||
if (!icon.value) return
|
if (!icon.value) return
|
||||||
display_icon.value = convertFileSrc(icon.value)
|
display_icon.value = tauri.convertFileSrc(icon.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const reset_icon = () => {
|
const reset_icon = () => {
|
||||||
@@ -419,9 +417,9 @@ const openFile = async () => {
|
|||||||
const newProject = await open({ multiple: false })
|
const newProject = await open({ multiple: false })
|
||||||
if (!newProject) return
|
if (!newProject) return
|
||||||
hide()
|
hide()
|
||||||
await install_from_file(newProject.path ?? newProject).catch(handleError)
|
await install_from_file(newProject).catch(handleError)
|
||||||
|
|
||||||
trackEvent('InstanceCreate', {
|
mixpanel_track('InstanceCreate', {
|
||||||
source: 'CreationModalFileOpen',
|
source: 'CreationModalFileOpen',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -464,7 +462,7 @@ const promises = profileOptions.value.map(async (option) => {
|
|||||||
option.name,
|
option.name,
|
||||||
instances.map((name) => ({ name, selected: false })),
|
instances.map((name) => ({ name, selected: false })),
|
||||||
)
|
)
|
||||||
} catch {
|
} catch (error) {
|
||||||
// Allow failure silently
|
// Allow failure silently
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<ModalWrapper ref="detectJavaModal" header="Select java version">
|
<Modal ref="detectJavaModal" header="Select java version" :noblur="!themeStore.advancedRendering">
|
||||||
<div class="auto-detect-modal">
|
<div class="auto-detect-modal">
|
||||||
<div class="table">
|
<div class="table">
|
||||||
<div class="table-row table-head">
|
<div class="table-row table-head">
|
||||||
@@ -32,16 +32,18 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { PlusIcon, CheckIcon, XIcon } from '@modrinth/assets'
|
import { PlusIcon, CheckIcon, XIcon } from '@modrinth/assets'
|
||||||
import { Button } from '@modrinth/ui'
|
import { Modal, Button } from '@modrinth/ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { find_filtered_jres } from '@/helpers/jre.js'
|
import { find_filtered_jres } from '@/helpers/jre.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
import { useTheming } from '@/store/theme.js'
|
||||||
|
|
||||||
|
const themeStore = useTheming()
|
||||||
|
|
||||||
const chosenInstallOptions = ref([])
|
const chosenInstallOptions = ref([])
|
||||||
const detectJavaModal = ref(null)
|
const detectJavaModal = ref(null)
|
||||||
@@ -65,7 +67,7 @@ const emit = defineEmits(['submit'])
|
|||||||
function setJavaInstall(javaInstall) {
|
function setJavaInstall(javaInstall) {
|
||||||
emit('submit', javaInstall)
|
emit('submit', javaInstall)
|
||||||
detectJavaModal.value.hide()
|
detectJavaModal.value.hide()
|
||||||
trackEvent('JavaAutoDetect', {
|
mixpanel_track('JavaAutoDetect', {
|
||||||
path: javaInstall.path,
|
path: javaInstall.path,
|
||||||
version: javaInstall.version,
|
version: javaInstall.version,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -63,10 +63,10 @@ import {
|
|||||||
import { Button } from '@modrinth/ui'
|
import { Button } from '@modrinth/ui'
|
||||||
import { auto_install_java, find_filtered_jres, get_jre, test_jre } from '@/helpers/jre.js'
|
import { auto_install_java, find_filtered_jres, get_jre, test_jre } from '@/helpers/jre.js'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { open } from '@tauri-apps/plugin-dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import JavaDetectionModal from '@/components/ui/JavaDetectionModal.vue'
|
import JavaDetectionModal from '@/components/ui/JavaDetectionModal.vue'
|
||||||
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
version: {
|
version: {
|
||||||
@@ -113,7 +113,7 @@ async function testJava() {
|
|||||||
)
|
)
|
||||||
testingJava.value = false
|
testingJava.value = false
|
||||||
|
|
||||||
trackEvent('JavaTest', {
|
mixpanel_track('JavaTest', {
|
||||||
path: props.modelValue ? props.modelValue.path : '',
|
path: props.modelValue ? props.modelValue.path : '',
|
||||||
success: testingJavaSuccess.value,
|
success: testingJavaSuccess.value,
|
||||||
})
|
})
|
||||||
@@ -124,19 +124,20 @@ async function testJava() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleJavaFileInput() {
|
async function handleJavaFileInput() {
|
||||||
const filePath = await open()
|
let filePath = await open()
|
||||||
|
|
||||||
if (filePath) {
|
if (filePath) {
|
||||||
let result = await get_jre(filePath.path ?? filePath)
|
let result = await get_jre(filePath)
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = {
|
result = {
|
||||||
path: filePath.path ?? filePath,
|
path: filePath,
|
||||||
version: props.version.toString(),
|
version: props.version.toString(),
|
||||||
architecture: 'x86',
|
architecture: 'x86',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trackEvent('JavaManualSelect', {
|
mixpanel_track('JavaManualSelect', {
|
||||||
|
path: filePath,
|
||||||
version: props.version,
|
version: props.version,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -149,7 +150,7 @@ async function autoDetect() {
|
|||||||
if (!props.compact) {
|
if (!props.compact) {
|
||||||
detectJavaModal.value.show(props.version, props.modelValue)
|
detectJavaModal.value.show(props.version, props.modelValue)
|
||||||
} else {
|
} else {
|
||||||
const versions = await find_filtered_jres(props.version).catch(handleError)
|
let versions = await find_filtered_jres(props.version).catch(handleError)
|
||||||
if (versions.length > 0) {
|
if (versions.length > 0) {
|
||||||
emit('update:modelValue', versions[0])
|
emit('update:modelValue', versions[0])
|
||||||
}
|
}
|
||||||
@@ -169,7 +170,7 @@ async function reinstallJava() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trackEvent('JavaReInstall', {
|
mixpanel_track('JavaReInstall', {
|
||||||
path: path,
|
path: path,
|
||||||
version: props.version,
|
version: props.version,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { CheckIcon } from '@modrinth/assets'
|
import { CheckIcon } from '@modrinth/assets'
|
||||||
import { Button, Badge } from '@modrinth/ui'
|
import { Button, Modal, Badge } from '@modrinth/ui'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
import { useTheming } from '@/store/theme'
|
||||||
import { update_managed_modrinth_version } from '@/helpers/profile'
|
import { update_managed_modrinth_version } from '@/helpers/profile'
|
||||||
import { releaseColor } from '@/helpers/utils'
|
import { releaseColor } from '@/helpers/utils'
|
||||||
import { SwapIcon } from '@/assets/icons/index.js'
|
import { SwapIcon } from '@/assets/icons/index.js'
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
versions: {
|
versions: {
|
||||||
@@ -33,6 +33,8 @@ const installedVersion = computed(() => props.instance?.linked_data?.version_id)
|
|||||||
const installing = computed(() => props.instance.install_stage !== 'installed')
|
const installing = computed(() => props.instance.install_stage !== 'installed')
|
||||||
const inProgress = ref(false)
|
const inProgress = ref(false)
|
||||||
|
|
||||||
|
const themeStore = useTheming()
|
||||||
|
|
||||||
const switchVersion = async (versionId) => {
|
const switchVersion = async (versionId) => {
|
||||||
inProgress.value = true
|
inProgress.value = true
|
||||||
await update_managed_modrinth_version(props.instance.path, versionId)
|
await update_managed_modrinth_version(props.instance.path, versionId)
|
||||||
@@ -41,10 +43,11 @@ const switchVersion = async (versionId) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ModalWrapper
|
<Modal
|
||||||
ref="modpackVersionModal"
|
ref="modpackVersionModal"
|
||||||
class="modpack-version-modal"
|
class="modpack-version-modal"
|
||||||
header="Change modpack version"
|
header="Change modpack version"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
>
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<Card v-if="instance.linked_data" class="mod-card">
|
<Card v-if="instance.linked_data" class="mod-card">
|
||||||
@@ -108,7 +111,7 @@ const switchVersion = async (versionId) => {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,20 +1,12 @@
|
|||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import { Promotion } from '@modrinth/ui'
|
||||||
import { get as getCreds } from '@/helpers/mr_auth.js'
|
import { get as getCreds } from '@/helpers/mr_auth.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { get_user } from '@/helpers/cache.js'
|
import { get_user } from '@/helpers/cache.js'
|
||||||
import { ChevronRightIcon } from '@modrinth/assets'
|
|
||||||
import { init_ads_window, open_ads_link, record_ads_click } from '@/helpers/ads.js'
|
|
||||||
import { listen } from '@tauri-apps/api/event'
|
|
||||||
|
|
||||||
const showAd = ref(true)
|
const showAd = ref(true)
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
scroll() {
|
|
||||||
updateAdPosition()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const creds = await getCreds().catch(handleError)
|
const creds = await getCreds().catch(handleError)
|
||||||
if (creds && creds.user_id) {
|
if (creds && creds.user_id) {
|
||||||
const user = await get_user(creds.user_id).catch(handleError)
|
const user = await get_user(creds.user_id).catch(handleError)
|
||||||
@@ -24,103 +16,8 @@ if (creds && creds.user_id) {
|
|||||||
showAd.value = false
|
showAd.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const adsWrapper = ref(null)
|
|
||||||
let resizeObserver
|
|
||||||
let scrollHandler
|
|
||||||
let intersectionObserver
|
|
||||||
let mutationObserver
|
|
||||||
onMounted(() => {
|
|
||||||
if (showAd.value) {
|
|
||||||
updateAdPosition(true)
|
|
||||||
|
|
||||||
resizeObserver = new ResizeObserver(() => updateAdPosition())
|
|
||||||
resizeObserver.observe(adsWrapper.value)
|
|
||||||
|
|
||||||
intersectionObserver = new IntersectionObserver(() => updateAdPosition())
|
|
||||||
intersectionObserver.observe(adsWrapper.value)
|
|
||||||
|
|
||||||
mutationObserver = new MutationObserver(() => updateAdPosition())
|
|
||||||
mutationObserver.observe(adsWrapper.value, { attributes: true, childList: true, subtree: true })
|
|
||||||
|
|
||||||
// Add scroll event listener
|
|
||||||
scrollHandler = () => {
|
|
||||||
requestAnimationFrame(() => updateAdPosition())
|
|
||||||
}
|
|
||||||
window.addEventListener('scroll', scrollHandler, { passive: true })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function updateAdPosition(overrideShown = false) {
|
|
||||||
if (adsWrapper.value) {
|
|
||||||
const rect = adsWrapper.value.getBoundingClientRect()
|
|
||||||
|
|
||||||
let y = rect.top + window.scrollY
|
|
||||||
let height = rect.bottom - rect.top
|
|
||||||
|
|
||||||
// Prevent ad from overlaying the app bar
|
|
||||||
if (y <= 52) {
|
|
||||||
y = 52
|
|
||||||
height = rect.bottom - 52
|
|
||||||
|
|
||||||
if (height < 0) {
|
|
||||||
height = 0
|
|
||||||
y = -1000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init_ads_window(rect.left + window.scrollX, y, rect.right - rect.left, height, overrideShown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function openPlusLink() {
|
|
||||||
await record_ads_click()
|
|
||||||
await open_ads_link('https://modrinth.com/plus', 'https://modrinth.com')
|
|
||||||
}
|
|
||||||
|
|
||||||
const unlisten = await listen('ads-scroll', (event) => {
|
|
||||||
if (adsWrapper.value) {
|
|
||||||
adsWrapper.value.parentNode.scrollTop += event.payload.scroll
|
|
||||||
updateAdPosition()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
if (resizeObserver) {
|
|
||||||
resizeObserver.disconnect()
|
|
||||||
}
|
|
||||||
if (intersectionObserver) {
|
|
||||||
intersectionObserver.disconnect()
|
|
||||||
}
|
|
||||||
if (mutationObserver) {
|
|
||||||
mutationObserver.disconnect()
|
|
||||||
}
|
|
||||||
if (scrollHandler) {
|
|
||||||
window.removeEventListener('scroll', scrollHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
unlisten()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<Promotion v-if="showAd" :external="false" query-param="?r=launcher" />
|
||||||
v-if="showAd"
|
|
||||||
ref="adsWrapper"
|
|
||||||
class="ad-parent relative mb-3 flex w-full justify-center rounded-2xl bg-bg-raised cursor-pointer"
|
|
||||||
>
|
|
||||||
<div class="flex max-h-[250px] min-h-[250px] min-w-[300px] max-w-[300px] flex-col gap-4 p-6">
|
|
||||||
<p class="m-0 text-2xl font-bold text-contrast">75% of ad revenue goes to creators</p>
|
|
||||||
<button
|
|
||||||
class="mt-auto items-center gap-1 text-purple hover:underline bg-transparent border-none text-left cursor-pointer outline-none"
|
|
||||||
@click="openPlusLink"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Support creators and Modrinth ad-free with
|
|
||||||
<span class="font-bold">Modrinth+</span>
|
|
||||||
</span>
|
|
||||||
<ChevronRightIcon class="relative top-[3px] h-5 w-5" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -117,9 +117,9 @@ import { useRouter } from 'vue-router'
|
|||||||
import { progress_bars_list } from '@/helpers/state.js'
|
import { progress_bars_list } from '@/helpers/state.js'
|
||||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { ChatIcon } from '@/assets/icons'
|
import { ChatIcon } from '@/assets/icons'
|
||||||
import { get_many } from '@/helpers/profile.js'
|
import { get_many } from '@/helpers/profile.js'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const card = ref(null)
|
const card = ref(null)
|
||||||
@@ -164,7 +164,7 @@ const stop = async (process) => {
|
|||||||
try {
|
try {
|
||||||
await killProcess(process.uuid).catch(handleError)
|
await killProcess(process.uuid).catch(handleError)
|
||||||
|
|
||||||
trackEvent('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: process.profile.loader,
|
loader: process.profile.loader,
|
||||||
game_version: process.profile.game_version,
|
game_version: process.profile.game_version,
|
||||||
source: 'AppBar',
|
source: 'AppBar',
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="!hidden" class="splash-screen dark" :class="{ 'fade-out': doneLoading }">
|
<div v-if="!hidden" class="splash-screen dark" :class="{ 'fade-out': doneLoading }">
|
||||||
<div v-if="os !== 'MacOS'" class="app-buttons">
|
<div v-if="os !== 'MacOS'" class="app-buttons">
|
||||||
<button class="btn icon-only transparent" icon-only @click="() => getCurrent().minimize()">
|
<button class="btn icon-only transparent" icon-only @click="() => appWindow.minimize()">
|
||||||
<MinimizeIcon />
|
<MinimizeIcon />
|
||||||
</button>
|
</button>
|
||||||
<button class="btn icon-only transparent" @click="() => getCurrent().toggleMaximize()">
|
<button class="btn icon-only transparent" @click="() => appWindow.toggleMaximize()">
|
||||||
<MaximizeIcon />
|
<MaximizeIcon />
|
||||||
</button>
|
</button>
|
||||||
<button class="btn icon-only transparent" @click="handleClose">
|
<button class="btn icon-only transparent" @click="handleClose">
|
||||||
@@ -85,9 +85,12 @@
|
|||||||
import { ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||||
import { loading_listener } from '@/helpers/events.js'
|
import { loading_listener } from '@/helpers/events.js'
|
||||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
import { appWindow } from '@tauri-apps/api/window'
|
||||||
import { XIcon } from '@modrinth/assets'
|
import { XIcon } from '@modrinth/assets'
|
||||||
import { MaximizeIcon, MinimizeIcon } from '@/assets/icons/index.js'
|
import { MaximizeIcon, MinimizeIcon } from '@/assets/icons/index.js'
|
||||||
|
import { window as TauriWindow } from '@tauri-apps/api'
|
||||||
|
import { TauriEvent } from '@tauri-apps/api/event'
|
||||||
|
import { saveWindowState, StateFlags } from 'tauri-plugin-window-state-api'
|
||||||
import { getOS } from '@/helpers/utils.js'
|
import { getOS } from '@/helpers/utils.js'
|
||||||
import { useLoading } from '@/store/loading.js'
|
import { useLoading } from '@/store/loading.js'
|
||||||
|
|
||||||
@@ -128,22 +131,20 @@ const os = ref('')
|
|||||||
getOS().then((x) => (os.value = x))
|
getOS().then((x) => (os.value = x))
|
||||||
|
|
||||||
loading_listener(async (e) => {
|
loading_listener(async (e) => {
|
||||||
console.log(e)
|
|
||||||
if (e.event.type === 'directory_move') {
|
if (e.event.type === 'directory_move') {
|
||||||
loadingProgress.value = 100 * (e.fraction ?? 1)
|
loadingProgress.value = 100 * (e.fraction ?? 1)
|
||||||
message.value = 'Updating app directory...'
|
message.value = 'Updating app directory...'
|
||||||
} else if (e.event.type === 'launcher_update') {
|
|
||||||
loadingProgress.value = 100 * (e.fraction ?? 1)
|
|
||||||
message.value = 'Updating Modrinth App...'
|
|
||||||
} else if (e.event.type === 'checking_for_updates') {
|
|
||||||
loadingProgress.value = 100 * (e.fraction ?? 1)
|
|
||||||
message.value = 'Checking for updates...'
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleClose = async () => {
|
const handleClose = async () => {
|
||||||
await getCurrentWindow().close()
|
await saveWindowState(StateFlags.ALL)
|
||||||
|
await TauriWindow.getCurrent().close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TauriWindow.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
|
||||||
|
await handleClose()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Button } from '@modrinth/ui'
|
import { Modal, Button } from '@modrinth/ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import SearchCard from '@/components/ui/SearchCard.vue'
|
import SearchCard from '@/components/ui/SearchCard.vue'
|
||||||
import { get_categories } from '@/helpers/tags.js'
|
import { get_categories } from '@/helpers/tags.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { get_version, get_project } from '@/helpers/cache.js'
|
import { get_version, get_project } from '@/helpers/cache.js'
|
||||||
import { install as installVersion } from '@/store/install.js'
|
import { install as installVersion } from '@/store/install.js'
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
|
||||||
|
|
||||||
const confirmModal = ref(null)
|
const confirmModal = ref(null)
|
||||||
const project = ref(null)
|
const project = ref(null)
|
||||||
@@ -42,7 +41,7 @@ async function install() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ModalWrapper ref="confirmModal" :header="`Install ${project?.title}`">
|
<Modal ref="confirmModal" :header="`Install ${project?.title}`">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<SearchCard
|
<SearchCard
|
||||||
:project="project"
|
:project="project"
|
||||||
@@ -61,7 +60,7 @@ async function install() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<ModalWrapper ref="incompatibleModal" header="Incompatibility warning" :on-hide="onInstall">
|
<Modal
|
||||||
|
ref="incompatibleModal"
|
||||||
|
header="Incompatibility warning"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
:on-hide="onInstall"
|
||||||
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>
|
<p>
|
||||||
This {{ versions?.length > 0 ? 'project' : 'version' }} is not compatible with the instance
|
This {{ versions?.length > 0 ? 'project' : 'version' }} is not compatible with the instance
|
||||||
@@ -46,18 +51,19 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
|
||||||
import { XIcon, DownloadIcon } from '@modrinth/assets'
|
import { XIcon, DownloadIcon } from '@modrinth/assets'
|
||||||
import { Button, DropdownSelect } from '@modrinth/ui'
|
import { Button, Modal, DropdownSelect } from '@modrinth/ui'
|
||||||
import { formatCategory } from '@modrinth/utils'
|
import { formatCategory } from '@modrinth/utils'
|
||||||
import { add_project_from_version as installMod } from '@/helpers/profile'
|
import { add_project_from_version as installMod } from '@/helpers/profile'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError, useTheming } from '@/store/state.js'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
|
||||||
|
const themeStore = useTheming()
|
||||||
|
|
||||||
const instance = ref(null)
|
const instance = ref(null)
|
||||||
const project = ref(null)
|
const project = ref(null)
|
||||||
@@ -66,7 +72,7 @@ const selectedVersion = ref(null)
|
|||||||
const incompatibleModal = ref(null)
|
const incompatibleModal = ref(null)
|
||||||
const installing = ref(false)
|
const installing = ref(false)
|
||||||
|
|
||||||
const onInstall = ref(() => {})
|
let onInstall = ref(() => {})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show: (instanceVal, projectVal, projectVersions, callback) => {
|
show: (instanceVal, projectVal, projectVersions, callback) => {
|
||||||
@@ -81,7 +87,7 @@ defineExpose({
|
|||||||
|
|
||||||
incompatibleModal.value.show()
|
incompatibleModal.value.show()
|
||||||
|
|
||||||
trackEvent('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
|
mixpanel_track('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -92,7 +98,7 @@ const install = async () => {
|
|||||||
onInstall.value(selectedVersion.value.id)
|
onInstall.value(selectedVersion.value.id)
|
||||||
incompatibleModal.value.hide()
|
incompatibleModal.value.hide()
|
||||||
|
|
||||||
trackEvent('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: instance.value.loader,
|
loader: instance.value.loader,
|
||||||
game_version: instance.value.game_version,
|
game_version: instance.value.game_version,
|
||||||
id: project.value,
|
id: project.value,
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { XIcon, DownloadIcon } from '@modrinth/assets'
|
import { XIcon, DownloadIcon } from '@modrinth/assets'
|
||||||
import { Button } from '@modrinth/ui'
|
import { Button, Modal } from '@modrinth/ui'
|
||||||
import { install as pack_install } from '@/helpers/pack'
|
import { install as pack_install } from '@/helpers/pack'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
import { useTheming } from '@/store/theme.js'
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
|
||||||
|
const themeStore = useTheming()
|
||||||
|
|
||||||
const versionId = ref()
|
const versionId = ref()
|
||||||
const project = ref()
|
const project = ref()
|
||||||
const confirmModal = ref(null)
|
const confirmModal = ref(null)
|
||||||
const installing = ref(false)
|
const installing = ref(false)
|
||||||
|
|
||||||
const onInstall = ref(() => {})
|
let onInstall = ref(() => {})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show: (projectVal, versionIdVal, callback) => {
|
show: (projectVal, versionIdVal, callback) => {
|
||||||
@@ -23,7 +25,7 @@ defineExpose({
|
|||||||
|
|
||||||
onInstall.value = callback
|
onInstall.value = callback
|
||||||
|
|
||||||
trackEvent('PackInstallStart')
|
mixpanel_track('PackInstallStart')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -37,7 +39,7 @@ async function install() {
|
|||||||
project.value.title,
|
project.value.title,
|
||||||
project.value.icon_url,
|
project.value.icon_url,
|
||||||
).catch(handleError)
|
).catch(handleError)
|
||||||
trackEvent('PackInstall', {
|
mixpanel_track('PackInstall', {
|
||||||
id: project.value.id,
|
id: project.value.id,
|
||||||
version_id: versionId.value,
|
version_id: versionId.value,
|
||||||
title: project.value.title,
|
title: project.value.title,
|
||||||
@@ -50,7 +52,12 @@ async function install() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ModalWrapper ref="confirmModal" header="Are you sure?" :on-hide="onInstall">
|
<Modal
|
||||||
|
ref="confirmModal"
|
||||||
|
header="Are you sure?"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
:on-hide="onInstall"
|
||||||
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>You already have this modpack installed. Are you sure you want to install it again?</p>
|
<p>You already have this modpack installed. Are you sure you want to install it again?</p>
|
||||||
<div class="input-group push-right">
|
<div class="input-group push-right">
|
||||||
@@ -60,7 +67,7 @@ async function install() {
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
RightArrowIcon,
|
RightArrowIcon,
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { Avatar, Button, Card } from '@modrinth/ui'
|
import { Avatar, Modal, Button, Card } from '@modrinth/ui'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import {
|
import {
|
||||||
add_project_from_version as installMod,
|
add_project_from_version as installMod,
|
||||||
@@ -16,14 +16,15 @@ import {
|
|||||||
list,
|
list,
|
||||||
create,
|
create,
|
||||||
} from '@/helpers/profile'
|
} from '@/helpers/profile'
|
||||||
import { open } from '@tauri-apps/plugin-dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import { installVersionDependencies } from '@/store/install.js'
|
import { installVersionDependencies } from '@/store/install.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
import { useTheming } from '@/store/theme.js'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
import { tauri } from '@tauri-apps/api'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
|
||||||
|
|
||||||
|
const themeStore = useTheming()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const versions = ref()
|
const versions = ref()
|
||||||
@@ -48,7 +49,7 @@ const shownProfiles = computed(() =>
|
|||||||
return profile.name.toLowerCase().includes(searchFilter.value.toLowerCase())
|
return profile.name.toLowerCase().includes(searchFilter.value.toLowerCase())
|
||||||
})
|
})
|
||||||
.filter((profile) => {
|
.filter((profile) => {
|
||||||
const loaders = versions.value.flatMap((v) => v.loaders)
|
let loaders = versions.value.flatMap((v) => v.loaders)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
versions.value.flatMap((v) => v.game_versions).includes(profile.game_version) &&
|
versions.value.flatMap((v) => v.game_versions).includes(profile.game_version) &&
|
||||||
@@ -59,7 +60,7 @@ const shownProfiles = computed(() =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const onInstall = ref(() => {})
|
let onInstall = ref(() => {})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show: async (projectVal, versionsVal, callback) => {
|
show: async (projectVal, versionsVal, callback) => {
|
||||||
@@ -77,7 +78,7 @@ defineExpose({
|
|||||||
onInstall.value = callback
|
onInstall.value = callback
|
||||||
|
|
||||||
const profilesVal = await list().catch(handleError)
|
const profilesVal = await list().catch(handleError)
|
||||||
for (const profile of profilesVal) {
|
for (let profile of profilesVal) {
|
||||||
profile.installing = false
|
profile.installing = false
|
||||||
profile.installedMod = await check_installed(profile.path, project.value.id).catch(
|
profile.installedMod = await check_installed(profile.path, project.value.id).catch(
|
||||||
handleError,
|
handleError,
|
||||||
@@ -87,7 +88,7 @@ defineExpose({
|
|||||||
|
|
||||||
installModal.value.show()
|
installModal.value.show()
|
||||||
|
|
||||||
trackEvent('ProjectInstallStart', { source: 'ProjectInstallModal' })
|
mixpanel_track('ProjectInstallStart', { source: 'ProjectInstallModal' })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ async function install(instance) {
|
|||||||
instance.installedMod = true
|
instance.installedMod = true
|
||||||
instance.installing = false
|
instance.installing = false
|
||||||
|
|
||||||
trackEvent('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: instance.loader,
|
loader: instance.loader,
|
||||||
game_version: instance.game_version,
|
game_version: instance.game_version,
|
||||||
id: project.value.id,
|
id: project.value.id,
|
||||||
@@ -136,12 +137,12 @@ const toggleCreation = () => {
|
|||||||
loader.value = null
|
loader.value = null
|
||||||
|
|
||||||
if (showCreation.value) {
|
if (showCreation.value) {
|
||||||
trackEvent('InstanceCreateStart', { source: 'ProjectInstallModal' })
|
mixpanel_track('InstanceCreateStart', { source: 'ProjectInstallModal' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const upload_icon = async () => {
|
const upload_icon = async () => {
|
||||||
const res = await open({
|
icon.value = await open({
|
||||||
multiple: false,
|
multiple: false,
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
@@ -150,10 +151,9 @@ const upload_icon = async () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
icon.value = res.path ?? res
|
|
||||||
|
|
||||||
if (!icon.value) return
|
if (!icon.value) return
|
||||||
display_icon.value = convertFileSrc(icon.value)
|
display_icon.value = tauri.convertFileSrc(icon.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const reset_icon = () => {
|
const reset_icon = () => {
|
||||||
@@ -186,7 +186,7 @@ const createInstance = async () => {
|
|||||||
const instance = await get(id, true)
|
const instance = await get(id, true)
|
||||||
await installVersionDependencies(instance, versions.value[0])
|
await installVersionDependencies(instance, versions.value[0])
|
||||||
|
|
||||||
trackEvent('InstanceCreate', {
|
mixpanel_track('InstanceCreate', {
|
||||||
profile_name: name.value,
|
profile_name: name.value,
|
||||||
game_version: versions.value[0].game_versions[0],
|
game_version: versions.value[0].game_versions[0],
|
||||||
loader: loader,
|
loader: loader,
|
||||||
@@ -195,7 +195,7 @@ const createInstance = async () => {
|
|||||||
source: 'ProjectInstallModal',
|
source: 'ProjectInstallModal',
|
||||||
})
|
})
|
||||||
|
|
||||||
trackEvent('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: loader,
|
loader: loader,
|
||||||
game_version: versions.value[0].game_versions[0],
|
game_version: versions.value[0].game_versions[0],
|
||||||
id: project.value,
|
id: project.value,
|
||||||
@@ -213,7 +213,12 @@ const createInstance = async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ModalWrapper ref="installModal" header="Install project to instance" :on-hide="onInstall">
|
<Modal
|
||||||
|
ref="installModal"
|
||||||
|
header="Install project to instance"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
:on-hide="onInstall"
|
||||||
|
>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input
|
<input
|
||||||
v-model="searchFilter"
|
v-model="searchFilter"
|
||||||
@@ -230,7 +235,7 @@ const createInstance = async () => {
|
|||||||
@click="installModal.hide()"
|
@click="installModal.hide()"
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
:src="profile.icon_path ? convertFileSrc(profile.icon_path) : null"
|
:src="profile.icon_path ? tauri.convertFileSrc(profile.icon_path) : null"
|
||||||
class="profile-image"
|
class="profile-image"
|
||||||
/>
|
/>
|
||||||
{{ profile.name }}
|
{{ profile.name }}
|
||||||
@@ -299,7 +304,7 @@ const createInstance = async () => {
|
|||||||
<Button @click="installModal.hide()">Cancel</Button>
|
<Button @click="installModal.hide()">Cancel</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { ConfirmModal } from '@modrinth/ui'
|
|
||||||
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
|
|
||||||
import { useTheming } from '@/store/theme.js'
|
|
||||||
|
|
||||||
const themeStore = useTheming()
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
confirmationText: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
hasToType: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: 'No title defined',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: 'No description defined',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
proceedLabel: {
|
|
||||||
type: String,
|
|
||||||
default: 'Proceed',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits(['proceed'])
|
|
||||||
const modal = ref(null)
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
show: () => {
|
|
||||||
hide_ads_window()
|
|
||||||
modal.value.show()
|
|
||||||
},
|
|
||||||
hide: () => {
|
|
||||||
onModalHide()
|
|
||||||
modal.value.hide()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
function onModalHide() {
|
|
||||||
show_ads_window()
|
|
||||||
}
|
|
||||||
|
|
||||||
function proceed() {
|
|
||||||
emit('proceed')
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ConfirmModal
|
|
||||||
ref="modal"
|
|
||||||
:confirmation-text="confirmationText"
|
|
||||||
:has-to-type="hasToType"
|
|
||||||
:title="title"
|
|
||||||
:description="description"
|
|
||||||
:proceed-label="proceedLabel"
|
|
||||||
:on-hide="onModalHide"
|
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
@proceed="proceed"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { Modal } from '@modrinth/ui'
|
|
||||||
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
|
|
||||||
import { useTheming } from '@/store/theme.js'
|
|
||||||
|
|
||||||
const themeStore = useTheming()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
header: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
closable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
onHide: {
|
|
||||||
type: Function,
|
|
||||||
default() {
|
|
||||||
return () => {}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const modal = ref(null)
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
show: () => {
|
|
||||||
hide_ads_window()
|
|
||||||
modal.value.show()
|
|
||||||
},
|
|
||||||
hide: () => {
|
|
||||||
onModalHide()
|
|
||||||
modal.value.hide()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
function onModalHide() {
|
|
||||||
show_ads_window()
|
|
||||||
props.onHide()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Modal ref="modal" :header="header" :noblur="!themeStore.advancedRendering" @hide="onModalHide">
|
|
||||||
<slot />
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { ShareModal } from '@modrinth/ui'
|
|
||||||
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
|
|
||||||
import { useTheming } from '@/store/theme.js'
|
|
||||||
|
|
||||||
const themeStore = useTheming()
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
header: {
|
|
||||||
type: String,
|
|
||||||
default: 'Share',
|
|
||||||
},
|
|
||||||
shareTitle: {
|
|
||||||
type: String,
|
|
||||||
default: 'Modrinth',
|
|
||||||
},
|
|
||||||
shareText: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
link: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
openInNewTab: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const modal = ref(null)
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
show: (passedContent) => {
|
|
||||||
hide_ads_window()
|
|
||||||
modal.value.show(passedContent)
|
|
||||||
},
|
|
||||||
hide: () => {
|
|
||||||
onModalHide()
|
|
||||||
modal.value.hide()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
function onModalHide() {
|
|
||||||
show_ads_window()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ShareModal
|
|
||||||
ref="modal"
|
|
||||||
:header="header"
|
|
||||||
:share-title="shareTitle"
|
|
||||||
:share-text="shareText"
|
|
||||||
:link="link"
|
|
||||||
:open-in-new-tab="openInNewTab"
|
|
||||||
:on-hide="onModalHide"
|
|
||||||
:noblur="!themeStore.advancedRendering"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { UserIcon, LockIcon, MailIcon } from '@modrinth/assets'
|
import { UserIcon, LockIcon, MailIcon } from '@modrinth/assets'
|
||||||
import { Button, Card, Checkbox } from '@modrinth/ui'
|
import { Button, Card, Checkbox, Modal } from '@modrinth/ui'
|
||||||
import {
|
import {
|
||||||
DiscordIcon,
|
DiscordIcon,
|
||||||
GithubIcon,
|
GithubIcon,
|
||||||
@@ -13,7 +13,6 @@ import { login, login_2fa, create_account, login_pass } from '@/helpers/mr_auth.
|
|||||||
import { handleError, useNotifications } from '@/store/state.js'
|
import { handleError, useNotifications } from '@/store/state.js'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
callback: {
|
callback: {
|
||||||
@@ -133,7 +132,7 @@ async function createAccount() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ModalWrapper ref="modal" :on-hide="removeWidget">
|
<Modal ref="modal" :on-hide="removeWidget">
|
||||||
<Card>
|
<Card>
|
||||||
<template v-if="twoFactorFlow">
|
<template v-if="twoFactorFlow">
|
||||||
<h1>Enter two-factor code</h1>
|
<h1>Enter two-factor code</h1>
|
||||||
@@ -218,17 +217,17 @@ async function createAccount() {
|
|||||||
v-else-if="loggingIn"
|
v-else-if="loggingIn"
|
||||||
color="primary"
|
color="primary"
|
||||||
large
|
large
|
||||||
:disabled="!turnstileToken"
|
|
||||||
@click="signIn"
|
@click="signIn"
|
||||||
|
:disabled="!turnstileToken"
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</Button>
|
</Button>
|
||||||
<Button v-else color="primary" large :disabled="!turnstileToken" @click="createAccount">
|
<Button v-else color="primary" large @click="createAccount" :disabled="!turnstileToken">
|
||||||
Create account
|
Create account
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core'
|
|
||||||
import cssContent from '@/assets/stylesheets/macFix.css?inline'
|
|
||||||
|
|
||||||
export async function useCheckDisableMouseover() {
|
|
||||||
try {
|
|
||||||
// Fetch the CSS content from the Rust backend
|
|
||||||
let should_disable_mouseover = await invoke('plugin:utils|should_disable_mouseover')
|
|
||||||
|
|
||||||
if (should_disable_mouseover) {
|
|
||||||
// Create a style element and set its content
|
|
||||||
const styleElement = document.createElement('style')
|
|
||||||
styleElement.innerHTML = cssContent
|
|
||||||
|
|
||||||
// Append the style element to the document's head
|
|
||||||
document.head.appendChild(styleElement)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error checking OS version from Rust backend', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core'
|
|
||||||
|
|
||||||
export async function init_ads_window(x, y, width, height, overrideShown = false) {
|
|
||||||
return await invoke('plugin:ads|init_ads_window', { x, y, width, height, overrideShown })
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function show_ads_window() {
|
|
||||||
return await invoke('plugin:ads|show_ads_window')
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function hide_ads_window(reset) {
|
|
||||||
return await invoke('plugin:ads|hide_ads_window', { reset })
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function record_ads_click() {
|
|
||||||
return await invoke('plugin:ads|record_ads_click')
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function open_ads_link(path, origin) {
|
|
||||||
return await invoke('plugin:ads|open_link', { path, origin })
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { posthog } from 'posthog-js'
|
|
||||||
|
|
||||||
export const initAnalytics = () => {
|
|
||||||
posthog.init('phc_hm2ihMpTAoE86xIm7XzsCB8RPiTRKivViK5biiHedm', {
|
|
||||||
persistence: 'localStorage',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const debugAnalytics = () => {
|
|
||||||
posthog.debug()
|
|
||||||
}
|
|
||||||
|
|
||||||
export const optOutAnalytics = () => {
|
|
||||||
posthog.opt_out_capturing()
|
|
||||||
}
|
|
||||||
|
|
||||||
export const optInAnalytics = () => {
|
|
||||||
posthog.opt_in_capturing()
|
|
||||||
}
|
|
||||||
|
|
||||||
export const trackEvent = (eventName, properties) => {
|
|
||||||
posthog.capture(eventName, properties)
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
// Example function:
|
// Example function:
|
||||||
// User goes to auth_url to complete flow, and when completed, authenticate_await_completion() returns the credentials
|
// User goes to auth_url to complete flow, and when completed, authenticate_await_completion() returns the credentials
|
||||||
@@ -13,46 +13,35 @@ import { invoke } from '@tauri-apps/api/core'
|
|||||||
// await authenticate_await_completion()
|
// await authenticate_await_completion()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
/**
|
/// Authenticate a user with Hydra - part 1
|
||||||
* Authenticate a user with Hydra - part 1.
|
/// This begins the authentication flow quasi-synchronously
|
||||||
* This begins the authentication flow quasi-synchronously.
|
/// This returns a DeviceLoginSuccess object, with two relevant fields:
|
||||||
*
|
/// - verification_uri: the URL to go to to complete the flow
|
||||||
* @returns {Promise<DeviceLoginSuccess>} A DeviceLoginSuccess object with two relevant fields:
|
/// - user_code: the code to enter on the verification_uri page
|
||||||
* @property {string} verification_uri - The URL to go to complete the flow.
|
|
||||||
* @property {string} user_code - The code to enter on the verification_uri page.
|
|
||||||
*/
|
|
||||||
export async function login() {
|
export async function login() {
|
||||||
return await invoke('plugin:auth|login')
|
return await invoke('auth_login')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Retrieves the default user
|
||||||
* Retrieves the default user
|
/// user is UUID
|
||||||
* @return {Promise<UUID | undefined>}
|
|
||||||
*/
|
|
||||||
export async function get_default_user() {
|
export async function get_default_user() {
|
||||||
return await invoke('plugin:auth|get_default_user')
|
return await invoke('plugin:auth|auth_get_default_user')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Updates the default user
|
||||||
* Updates the default user
|
/// user is UUID
|
||||||
* @param {UUID} user
|
|
||||||
*/
|
|
||||||
export async function set_default_user(user) {
|
export async function set_default_user(user) {
|
||||||
return await invoke('plugin:auth|set_default_user', { user })
|
return await invoke('plugin:auth|auth_set_default_user', { user })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Remove a user account from the database
|
||||||
* Remove a user account from the database
|
/// user is UUID
|
||||||
* @param {UUID} user
|
|
||||||
*/
|
|
||||||
export async function remove_user(user) {
|
export async function remove_user(user) {
|
||||||
return await invoke('plugin:auth|remove_user', { user })
|
return await invoke('plugin:auth|auth_remove_user', { user })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Returns a list of users
|
||||||
* Returns a list of users
|
/// Returns an Array of Credentials
|
||||||
* @returns {Promise<Credential[]>}
|
|
||||||
*/
|
|
||||||
export async function users() {
|
export async function users() {
|
||||||
return await invoke('plugin:auth|get_users')
|
return await invoke('plugin:auth|auth_users')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
export async function get_project(id, cacheBehaviour) {
|
export async function get_project(id, cacheBehaviour) {
|
||||||
return await invoke('plugin:cache|get_project', { id, cacheBehaviour })
|
return await invoke('plugin:cache|get_project', { id, cacheBehaviour })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
import { create } from './profile'
|
import { create } from './profile'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -27,7 +27,7 @@ import { create } from './profile'
|
|||||||
/// eg: get_importable_instances("MultiMC", "C:/MultiMC")
|
/// eg: get_importable_instances("MultiMC", "C:/MultiMC")
|
||||||
/// returns ["Instance 1", "Instance 2"]
|
/// returns ["Instance 1", "Instance 2"]
|
||||||
export async function get_importable_instances(launcherType, basePath) {
|
export async function get_importable_instances(launcherType, basePath) {
|
||||||
return await invoke('plugin:import|get_importable_instances', { launcherType, basePath })
|
return await invoke('plugin:import|import_get_importable_instances', { launcherType, basePath })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Import an instance from a launcher type and base path
|
/// Import an instance from a launcher type and base path
|
||||||
@@ -38,7 +38,7 @@ export async function import_instance(launcherType, basePath, instanceFolder) {
|
|||||||
// fs watching will be enabled once the instance is imported
|
// fs watching will be enabled once the instance is imported
|
||||||
const profilePath = await create(instanceFolder, '1.19.4', 'vanilla', 'latest', null, true)
|
const profilePath = await create(instanceFolder, '1.19.4', 'vanilla', 'latest', null, true)
|
||||||
|
|
||||||
return await invoke('plugin:import|import_instance', {
|
return await invoke('plugin:import|import_import_instance', {
|
||||||
profilePath,
|
profilePath,
|
||||||
launcherType,
|
launcherType,
|
||||||
basePath,
|
basePath,
|
||||||
@@ -49,7 +49,7 @@ export async function import_instance(launcherType, basePath, instanceFolder) {
|
|||||||
/// Checks if this instance is valid for importing, given a certain launcher type
|
/// Checks if this instance is valid for importing, given a certain launcher type
|
||||||
/// eg: is_valid_importable_instance("C:/MultiMC/Instance 1", "MultiMC")
|
/// eg: is_valid_importable_instance("C:/MultiMC/Instance 1", "MultiMC")
|
||||||
export async function is_valid_importable_instance(instanceFolder, launcherType) {
|
export async function is_valid_importable_instance(instanceFolder, launcherType) {
|
||||||
return await invoke('plugin:import|is_valid_importable_instance', {
|
return await invoke('plugin:import|import_is_valid_importable_instance', {
|
||||||
instanceFolder,
|
instanceFolder,
|
||||||
launcherType,
|
launcherType,
|
||||||
})
|
})
|
||||||
@@ -59,5 +59,5 @@ export async function is_valid_importable_instance(instanceFolder, launcherType)
|
|||||||
/// null if it can't be found or doesn't exist
|
/// null if it can't be found or doesn't exist
|
||||||
/// eg: get_default_launcher_path("MultiMC")
|
/// eg: get_default_launcher_path("MultiMC")
|
||||||
export async function get_default_launcher_path(launcherType) {
|
export async function get_default_launcher_path(launcherType) {
|
||||||
return await invoke('plugin:import|get_default_launcher_path', { launcherType })
|
return await invoke('plugin:import|import_get_default_launcher_path', { launcherType })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A log is a struct containing the filename string, stdout, and stderr, as follows:
|
A log is a struct containing the filename string, stdout, and stderr, as follows:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
/// Gets the game versions from daedalus
|
/// Gets the game versions from daedalus
|
||||||
// Returns a VersionManifest
|
// Returns a VersionManifest
|
||||||
|
|||||||
57
apps/app-frontend/src/helpers/mixpanel.js
Normal file
57
apps/app-frontend/src/helpers/mixpanel.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import mixpanel from 'mixpanel-browser'
|
||||||
|
|
||||||
|
// mixpanel_track
|
||||||
|
function trackWrapper(originalTrack) {
|
||||||
|
return function (event_name, properties = {}) {
|
||||||
|
try {
|
||||||
|
originalTrack(event_name, properties)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const mixpanel_track = trackWrapper(mixpanel.track.bind(mixpanel))
|
||||||
|
|
||||||
|
// mixpanel_opt_out_tracking()
|
||||||
|
function optOutTrackingWrapper(originalOptOutTracking) {
|
||||||
|
return function () {
|
||||||
|
try {
|
||||||
|
originalOptOutTracking()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const mixpanel_opt_out_tracking = optOutTrackingWrapper(
|
||||||
|
mixpanel.opt_out_tracking.bind(mixpanel),
|
||||||
|
)
|
||||||
|
|
||||||
|
// mixpanel_opt_in_tracking()
|
||||||
|
function optInTrackingWrapper(originalOptInTracking) {
|
||||||
|
return function () {
|
||||||
|
try {
|
||||||
|
originalOptInTracking()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const mixpanel_opt_in_tracking = optInTrackingWrapper(
|
||||||
|
mixpanel.opt_in_tracking.bind(mixpanel),
|
||||||
|
)
|
||||||
|
|
||||||
|
// mixpanel_init
|
||||||
|
function initWrapper(originalInit) {
|
||||||
|
return function (token, config = {}) {
|
||||||
|
try {
|
||||||
|
originalInit(token, config)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const mixpanel_init = initWrapper(mixpanel.init.bind(mixpanel))
|
||||||
|
|
||||||
|
export const mixpanel_is_loaded = () => {
|
||||||
|
return mixpanel.__loaded
|
||||||
|
}
|
||||||
@@ -3,22 +3,22 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
export async function login(provider) {
|
export async function login(provider) {
|
||||||
return await invoke('modrinth_auth_login', { provider })
|
return await invoke('modrinth_auth_login', { provider })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function login_pass(username, password, challenge) {
|
export async function login_pass(username, password, challenge) {
|
||||||
return await invoke('plugin:mr-auth|login_pass', { username, password, challenge })
|
return await invoke('plugin:mr_auth|login_pass', { username, password, challenge })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function login_2fa(code, flow) {
|
export async function login_2fa(code, flow) {
|
||||||
return await invoke('plugin:mr-auth|login_2fa', { code, flow })
|
return await invoke('plugin:mr_auth|login_2fa', { code, flow })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create_account(username, email, password, challenge, signUpNewsletter) {
|
export async function create_account(username, email, password, challenge, signUpNewsletter) {
|
||||||
return await invoke('plugin:mr-auth|create_account', {
|
return await invoke('plugin:mr_auth|create_account', {
|
||||||
username,
|
username,
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
@@ -28,9 +28,9 @@ export async function create_account(username, email, password, challenge, signU
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function logout() {
|
export async function logout() {
|
||||||
return await invoke('plugin:mr-auth|logout')
|
return await invoke('plugin:mr_auth|logout')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get() {
|
export async function get() {
|
||||||
return await invoke('plugin:mr-auth|get')
|
return await invoke('plugin:mr_auth|get')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
import { create } from './profile'
|
import { create } from './profile'
|
||||||
|
|
||||||
// Installs pack from a version ID
|
// Installs pack from a version ID
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
/// Gets all running process IDs with a given profile path
|
/// Gets all running process IDs with a given profile path
|
||||||
/// Returns [u32]
|
/// Returns [u32]
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
/// Add instance
|
/// Add instance
|
||||||
/*
|
/*
|
||||||
@@ -19,7 +19,7 @@ import { invoke } from '@tauri-apps/api/core'
|
|||||||
export async function create(name, gameVersion, modloader, loaderVersion, iconPath, skipInstall) {
|
export async function create(name, gameVersion, modloader, loaderVersion, iconPath, skipInstall) {
|
||||||
//Trim string name to avoid "Unable to find directory"
|
//Trim string name to avoid "Unable to find directory"
|
||||||
name = name.trim()
|
name = name.trim()
|
||||||
return await invoke('plugin:profile-create|profile_create', {
|
return await invoke('plugin:profile_create|profile_create', {
|
||||||
name,
|
name,
|
||||||
gameVersion,
|
gameVersion,
|
||||||
modloader,
|
modloader,
|
||||||
@@ -31,7 +31,7 @@ export async function create(name, gameVersion, modloader, loaderVersion, iconPa
|
|||||||
|
|
||||||
// duplicate a profile
|
// duplicate a profile
|
||||||
export async function duplicate(path) {
|
export async function duplicate(path) {
|
||||||
return await invoke('plugin:profile-create|profile_duplicate', { path })
|
return await invoke('plugin:profile_create|profile_duplicate', { path })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove a profile
|
// Remove a profile
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
// Settings object
|
// Settings object
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
// Initialize the theseus API state
|
// Initialize the theseus API state
|
||||||
// This should be called during the initializion/opening of the launcher
|
// This should be called during the initializion/opening of the launcher
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
|
||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
// Gets cached category tags
|
// Gets cached category tags
|
||||||
export async function get_categories() {
|
export async function get_categories() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { get_full_path, get_mod_full_path } from '@/helpers/profile'
|
import { get_full_path, get_mod_full_path } from '@/helpers/profile'
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
export async function isDev() {
|
export async function isDev() {
|
||||||
return await invoke('is_dev')
|
return await invoke('is_dev')
|
||||||
@@ -33,10 +33,6 @@ export async function highlightModInProfile(profilePath, projectPath) {
|
|||||||
return await highlightInFolder(fullPath)
|
return await highlightInFolder(fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function restartApp() {
|
|
||||||
return await invoke('restart_app')
|
|
||||||
}
|
|
||||||
|
|
||||||
export const releaseColor = (releaseType) => {
|
export const releaseColor = (releaseType) => {
|
||||||
switch (releaseType) {
|
switch (releaseType) {
|
||||||
case 'release':
|
case 'release':
|
||||||
@@ -56,7 +52,7 @@ export function debounce(fn, wait) {
|
|||||||
if (timer) {
|
if (timer) {
|
||||||
clearTimeout(timer) // clear any pre-existing timer
|
clearTimeout(timer) // clear any pre-existing timer
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
const context = this // get the current context
|
const context = this // get the current context
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
fn.apply(context, args) // call the function if time expires
|
fn.apply(context, args) // call the function if time expires
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import App from '@/App.vue'
|
|||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import FloatingVue from 'floating-vue'
|
import FloatingVue from 'floating-vue'
|
||||||
import 'floating-vue/dist/style.css'
|
import 'floating-vue/dist/style.css'
|
||||||
|
import loadCssMixin from './mixins/macCssFix.js'
|
||||||
import { createPlugin } from '@vintl/vintl/plugin'
|
import { createPlugin } from '@vintl/vintl/plugin'
|
||||||
import * as Sentry from '@sentry/vue'
|
|
||||||
|
|
||||||
const VIntlPlugin = createPlugin({
|
const VIntlPlugin = createPlugin({
|
||||||
controllerOpts: {
|
controllerOpts: {
|
||||||
@@ -27,17 +27,10 @@ const VIntlPlugin = createPlugin({
|
|||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
|
|
||||||
let app = createApp(App)
|
let app = createApp(App)
|
||||||
|
|
||||||
Sentry.init({
|
|
||||||
app,
|
|
||||||
dsn: 'https://9508775ee5034536bc70433f5f531dd4@o485889.ingest.us.sentry.io/4504579615227904',
|
|
||||||
integrations: [Sentry.browserTracingIntegration({ router })],
|
|
||||||
tracesSampleRate: 0.1,
|
|
||||||
})
|
|
||||||
|
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(pinia)
|
app.use(pinia)
|
||||||
app.use(FloatingVue)
|
app.use(FloatingVue)
|
||||||
|
app.mixin(loadCssMixin)
|
||||||
app.use(VIntlPlugin)
|
app.use(VIntlPlugin)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
27
apps/app-frontend/src/mixins/macCssFix.js
Normal file
27
apps/app-frontend/src/mixins/macCssFix.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
import cssContent from '@/assets/stylesheets/macFix.css?inline'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async mounted() {
|
||||||
|
await this.checkDisableMouseover()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async checkDisableMouseover() {
|
||||||
|
try {
|
||||||
|
// Fetch the CSS content from the Rust backend
|
||||||
|
const should_disable_mouseover = await invoke('plugin:utils|should_disable_mouseover')
|
||||||
|
|
||||||
|
if (should_disable_mouseover) {
|
||||||
|
// Create a style element and set its content
|
||||||
|
const styleElement = document.createElement('style')
|
||||||
|
styleElement.innerHTML = cssContent
|
||||||
|
|
||||||
|
// Append the style element to the document's head
|
||||||
|
document.head.appendChild(styleElement)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking OS version from Rust backend', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ import { get_categories, get_loaders, get_game_versions } from '@/helpers/tags'
|
|||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import SearchCard from '@/components/ui/SearchCard.vue'
|
import SearchCard from '@/components/ui/SearchCard.vue'
|
||||||
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile.js'
|
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile.js'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
import { get_search_results } from '@/helpers/cache.js'
|
import { get_search_results } from '@/helpers/cache.js'
|
||||||
import { debounce } from '@/helpers/utils.js'
|
import { debounce } from '@/helpers/utils.js'
|
||||||
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
|
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
|
||||||
@@ -381,20 +381,20 @@ const sortedCategories = computed(() => {
|
|||||||
// identifier[0], then if it ties, identifier[1], etc
|
// identifier[0], then if it ties, identifier[1], etc
|
||||||
async function sortByNameOrNumber(sortable, identifiers) {
|
async function sortByNameOrNumber(sortable, identifiers) {
|
||||||
sortable.sort((a, b) => {
|
sortable.sort((a, b) => {
|
||||||
for (const identifier of identifiers) {
|
for (let identifier of identifiers) {
|
||||||
const aNum = parseFloat(a[identifier])
|
let aNum = parseFloat(a[identifier])
|
||||||
const bNum = parseFloat(b[identifier])
|
let bNum = parseFloat(b[identifier])
|
||||||
if (isNaN(aNum) && isNaN(bNum)) {
|
if (isNaN(aNum) && isNaN(bNum)) {
|
||||||
// Both are strings, sort alphabetically
|
// Both are strings, sort alphabetically
|
||||||
const stringComp = a[identifier].localeCompare(b[identifier])
|
let stringComp = a[identifier].localeCompare(b[identifier])
|
||||||
if (stringComp != 0) return stringComp
|
if (stringComp != 0) return stringComp
|
||||||
} else if (!isNaN(aNum) && !isNaN(bNum)) {
|
} else if (!isNaN(aNum) && !isNaN(bNum)) {
|
||||||
// Both are numbers, sort numerically
|
// Both are numbers, sort numerically
|
||||||
const numComp = aNum - bNum
|
let numComp = aNum - bNum
|
||||||
if (numComp != 0) return numComp
|
if (numComp != 0) return numComp
|
||||||
} else {
|
} else {
|
||||||
// One is a number and one is a string, numbers go first
|
// One is a number and one is a string, numbers go first
|
||||||
const numStringComp = isNaN(aNum) ? 1 : -1
|
let numStringComp = isNaN(aNum) ? 1 : -1
|
||||||
if (numStringComp != 0) return numStringComp
|
if (numStringComp != 0) return numStringComp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -528,8 +528,7 @@ const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.valu
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="searchWrapper" class="search-container">
|
<div ref="searchWrapper" class="search-container">
|
||||||
<aside class="filter-panel" @scroll="$refs.promo.scroll()">
|
<aside class="filter-panel">
|
||||||
<PromotionWrapper ref="promo" />
|
|
||||||
<Card v-if="instanceContext" class="small-instance">
|
<Card v-if="instanceContext" class="small-instance">
|
||||||
<router-link :to="`/instance/${encodeURIComponent(instanceContext.path)}`" class="instance">
|
<router-link :to="`/instance/${encodeURIComponent(instanceContext.path)}`" class="instance">
|
||||||
<Avatar
|
<Avatar
|
||||||
@@ -676,7 +675,8 @@ const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.valu
|
|||||||
</Card>
|
</Card>
|
||||||
</aside>
|
</aside>
|
||||||
<div class="search">
|
<div class="search">
|
||||||
<Card class="project-type-container mt-4">
|
<PromotionWrapper class="mt-4" />
|
||||||
|
<Card class="project-type-container">
|
||||||
<NavRow :links="selectableProjectTypes" />
|
<NavRow :links="selectableProjectTypes" />
|
||||||
</Card>
|
</Card>
|
||||||
<Card class="search-panel-container">
|
<Card class="search-panel-container">
|
||||||
@@ -878,13 +878,13 @@ const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.valu
|
|||||||
|
|
||||||
.filter-panel {
|
.filter-panel {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
width: 20rem;
|
||||||
padding: 1rem 0.5rem 1rem 1rem;
|
padding: 1rem 0.5rem 1rem 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
min-height: calc(100vh - 3.25rem);
|
min-height: calc(100vh - 3.25rem);
|
||||||
max-height: calc(100vh - 3.25rem);
|
max-height: calc(100vh - 3.25rem);
|
||||||
width: 20rem;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
@@ -903,8 +903,8 @@ const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.valu
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
margin: 0 1rem 0.5rem calc(20rem + 1rem);
|
margin: 0 1rem 0.5rem 20.5rem;
|
||||||
width: calc(100% - calc(20rem + 1rem));
|
width: calc(100% - 20.5rem);
|
||||||
|
|
||||||
.offline {
|
.offline {
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted, computed } from 'vue'
|
import { ref, onUnmounted, computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import RowDisplay from '@/components/RowDisplay.vue'
|
import RowDisplay from '@/components/RowDisplay.vue'
|
||||||
import { list } from '@/helpers/profile.js'
|
import { list } from '@/helpers/profile.js'
|
||||||
@@ -8,11 +8,6 @@ import { useBreadcrumbs } from '@/store/breadcrumbs'
|
|||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { get_search_results } from '@/helpers/cache.js'
|
import { get_search_results } from '@/helpers/cache.js'
|
||||||
import { hide_ads_window } from '@/helpers/ads.js'
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
hide_ads_window(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
const featuredModpacks = ref({})
|
const featuredModpacks = ref({})
|
||||||
const featuredMods = ref({})
|
const featuredMods = ref({})
|
||||||
@@ -47,7 +42,7 @@ const getInstances = async () => {
|
|||||||
return dateB - dateA
|
return dateB - dateA
|
||||||
})
|
})
|
||||||
|
|
||||||
const filters = []
|
let filters = []
|
||||||
for (const instance of recentInstances.value) {
|
for (const instance of recentInstances.value) {
|
||||||
if (instance.linked_data && instance.linked_data.project_id) {
|
if (instance.linked_data && instance.linked_data.project_id) {
|
||||||
filters.push(`NOT"project_id"="${instance.linked_data.project_id}"`)
|
filters.push(`NOT"project_id"="${instance.linked_data.project_id}"`)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, onUnmounted, ref, shallowRef } from 'vue'
|
import { onUnmounted, ref, shallowRef } from 'vue'
|
||||||
import GridDisplay from '@/components/GridDisplay.vue'
|
import GridDisplay from '@/components/GridDisplay.vue'
|
||||||
import { list } from '@/helpers/profile.js'
|
import { list } from '@/helpers/profile.js'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
@@ -10,11 +10,6 @@ import { Button } from '@modrinth/ui'
|
|||||||
import { PlusIcon } from '@modrinth/assets'
|
import { PlusIcon } from '@modrinth/assets'
|
||||||
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
||||||
import { NewInstanceImage } from '@/assets/icons'
|
import { NewInstanceImage } from '@/assets/icons'
|
||||||
import { hide_ads_window } from '@/helpers/ads.js'
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
hide_ads_window(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const breadcrumbs = useBreadcrumbs()
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, onMounted } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import { LogOutIcon, LogInIcon, BoxIcon, FolderSearchIcon, TrashIcon } from '@modrinth/assets'
|
import { LogOutIcon, LogInIcon, BoxIcon, FolderSearchIcon, TrashIcon } from '@modrinth/assets'
|
||||||
import { Card, Slider, DropdownSelect, Toggle, Button } from '@modrinth/ui'
|
import { Card, Slider, DropdownSelect, Toggle, ConfirmModal, Button } from '@modrinth/ui'
|
||||||
import { handleError, useTheming } from '@/store/state'
|
import { handleError, useTheming } from '@/store/state'
|
||||||
import { get, set } from '@/helpers/settings'
|
import { get, set } from '@/helpers/settings'
|
||||||
import { get_java_versions, get_max_memory, set_java_version } from '@/helpers/jre'
|
import { get_java_versions, get_max_memory, set_java_version } from '@/helpers/jre'
|
||||||
import { get as getCreds, logout } from '@/helpers/mr_auth.js'
|
import { get as getCreds, logout } from '@/helpers/mr_auth.js'
|
||||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||||
import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue'
|
import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue'
|
||||||
import { optOutAnalytics, optInAnalytics } from '@/helpers/analytics'
|
import { mixpanel_opt_out_tracking, mixpanel_opt_in_tracking } from '@/helpers/mixpanel'
|
||||||
import { open } from '@tauri-apps/plugin-dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import { getOS } from '@/helpers/utils.js'
|
import { getOS } from '@/helpers/utils.js'
|
||||||
import { getVersion } from '@tauri-apps/api/app'
|
import { getVersion } from '@tauri-apps/api/app'
|
||||||
import { get_user, purge_cache_types } from '@/helpers/cache.js'
|
import { get_user, purge_cache_types } from '@/helpers/cache.js'
|
||||||
import { hide_ads_window } from '@/helpers/ads.js'
|
|
||||||
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
hide_ads_window(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
const pageOptions = ['Home', 'Library']
|
const pageOptions = ['Home', 'Library']
|
||||||
|
|
||||||
@@ -51,9 +45,9 @@ watch(
|
|||||||
const setSettings = JSON.parse(JSON.stringify(newSettings))
|
const setSettings = JSON.parse(JSON.stringify(newSettings))
|
||||||
|
|
||||||
if (setSettings.telemetry) {
|
if (setSettings.telemetry) {
|
||||||
optInAnalytics()
|
mixpanel_opt_out_tracking()
|
||||||
} else {
|
} else {
|
||||||
optOutAnalytics()
|
mixpanel_opt_in_tracking()
|
||||||
}
|
}
|
||||||
|
|
||||||
setSettings.extra_launch_args = setSettings.launchArgs.trim().split(/\s+/).filter(Boolean)
|
setSettings.extra_launch_args = setSettings.launchArgs.trim().split(/\s+/).filter(Boolean)
|
||||||
@@ -175,12 +169,13 @@ async function purgeCache() {
|
|||||||
Sign in
|
Sign in
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ConfirmModalWrapper
|
<ConfirmModal
|
||||||
ref="purgeCacheConfirmModal"
|
ref="purgeCacheConfirmModal"
|
||||||
title="Are you sure you want to purge the cache?"
|
title="Are you sure you want to purge the cache?"
|
||||||
description="If you proceed, your entire cache will be purged. This may slow down the app temporarily."
|
description="If you proceed, your entire cache will be purged. This may slow down the app temporarily."
|
||||||
:has-to-type="false"
|
:has-to-type="false"
|
||||||
proceed-label="Purge cache"
|
proceed-label="Purge cache"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
@proceed="purgeCache"
|
@proceed="purgeCache"
|
||||||
/>
|
/>
|
||||||
<div class="adjacent-input">
|
<div class="adjacent-input">
|
||||||
@@ -363,25 +358,6 @@ async function purgeCache() {
|
|||||||
<span class="label__title size-card-header">Privacy</span>
|
<span class="label__title size-card-header">Privacy</span>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="adjacent-input">
|
|
||||||
<label for="opt-out-analytics">
|
|
||||||
<span class="label__title">Personalized ads</span>
|
|
||||||
<span class="label__description">
|
|
||||||
Modrinth's ad provider, Aditude, shows ads based on your preferences. By disabling this
|
|
||||||
option, you opt out and ads will no longer be shown based on your interests.
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<Toggle
|
|
||||||
id="opt-out-analytics"
|
|
||||||
:model-value="settings.personalized_ads"
|
|
||||||
:checked="settings.personalized_ads"
|
|
||||||
@update:model-value="
|
|
||||||
(e) => {
|
|
||||||
settings.personalized_ads = e
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="adjacent-input">
|
<div class="adjacent-input">
|
||||||
<label for="opt-out-analytics">
|
<label for="opt-out-analytics">
|
||||||
<span class="label__title">Telemetry</span>
|
<span class="label__title">Telemetry</span>
|
||||||
@@ -425,14 +401,14 @@ async function purgeCache() {
|
|||||||
<span class="label__title size-card-header">Java settings</span>
|
<span class="label__title size-card-header">Java settings</span>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<template v-for="javaVersion in [21, 17, 8]" :key="`java-${javaVersion}`">
|
<template v-for="version in [21, 17, 8]">
|
||||||
<label :for="'java-' + javaVersion">
|
<label :for="'java-' + version">
|
||||||
<span class="label__title">Java {{ javaVersion }} location</span>
|
<span class="label__title">Java {{ version }} location</span>
|
||||||
</label>
|
</label>
|
||||||
<JavaSelector
|
<JavaSelector
|
||||||
:id="'java-selector-' + javaVersion"
|
:id="'java-selector-' + version"
|
||||||
v-model="javaVersions[javaVersion]"
|
v-model="javaVersions[version]"
|
||||||
:version="javaVersion"
|
:version="version"
|
||||||
@update:model-value="updateJavaVersion"
|
@update:model-value="updateJavaVersion"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@@ -473,7 +449,7 @@ async function purgeCache() {
|
|||||||
:min="8"
|
:min="8"
|
||||||
:max="maxMemory"
|
:max="maxMemory"
|
||||||
:step="64"
|
:step="64"
|
||||||
unit="MB"
|
unit="mb"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="instance-container">
|
<div class="instance-container">
|
||||||
<div class="side-cards pb-4" @scroll="$refs.promo.scroll()">
|
<div class="side-cards">
|
||||||
<Card class="instance-card" @contextmenu.prevent.stop="handleRightClick">
|
<Card class="instance-card" @contextmenu.prevent.stop="handleRightClick">
|
||||||
<Avatar size="md" :src="instance.icon_path ? convertFileSrc(instance.icon_path) : null" />
|
<Avatar size="lg" :src="instance.icon_path ? convertFileSrc(instance.icon_path) : null" />
|
||||||
<div class="instance-info">
|
<div class="instance-info">
|
||||||
<h2 class="name">{{ instance.name }}</h2>
|
<h2 class="name">{{ instance.name }}</h2>
|
||||||
<span class="metadata"> {{ instance.loader }} {{ instance.game_version }} </span>
|
<span class="metadata"> {{ instance.loader }} {{ instance.game_version }} </span>
|
||||||
@@ -61,9 +61,9 @@
|
|||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<PromotionWrapper ref="promo" class="mt-4" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
<PromotionWrapper />
|
||||||
<RouterView v-slot="{ Component }">
|
<RouterView v-slot="{ Component }">
|
||||||
<template v-if="Component">
|
<template v-if="Component">
|
||||||
<Suspense @pending="loadingBar.startLoading()" @resolve="loadingBar.stopLoading()">
|
<Suspense @pending="loadingBar.startLoading()" @resolve="loadingBar.stopLoading()">
|
||||||
@@ -131,8 +131,9 @@ import { ref, onUnmounted } from 'vue'
|
|||||||
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
||||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
|
import { useFetch } from '@/helpers/fetch'
|
||||||
import { handleSevereError } from '@/store/error.js'
|
import { handleSevereError } from '@/store/error.js'
|
||||||
import { get_project, get_version_many } from '@/helpers/cache.js'
|
import { get_project, get_version_many } from '@/helpers/cache.js'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
@@ -182,7 +183,7 @@ const startInstance = async (context) => {
|
|||||||
}
|
}
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
|
||||||
trackEvent('InstanceStart', {
|
mixpanel_track('InstanceStart', {
|
||||||
loader: instance.value.loader,
|
loader: instance.value.loader,
|
||||||
game_version: instance.value.game_version,
|
game_version: instance.value.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
@@ -219,7 +220,7 @@ const stopInstance = async (context) => {
|
|||||||
playing.value = false
|
playing.value = false
|
||||||
await kill(route.params.id).catch(handleError)
|
await kill(route.params.id).catch(handleError)
|
||||||
|
|
||||||
trackEvent('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: instance.value.loader,
|
loader: instance.value.loader,
|
||||||
game_version: instance.value.game_version,
|
game_version: instance.value.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
@@ -311,6 +312,7 @@ onUnmounted(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
width: 17rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
@@ -324,13 +326,12 @@ Button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.side-cards {
|
.side-cards {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
width: 300px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
padding: 1rem;
|
||||||
min-height: calc(100vh - 3.25rem);
|
min-height: calc(100% - 3.25rem);
|
||||||
max-height: calc(100vh - 3.25rem);
|
max-height: calc(100% - 3.25rem);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
@@ -374,7 +375,10 @@ Button {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
padding: 1rem;
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
margin-left: 19rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.instance-info {
|
.instance-info {
|
||||||
@@ -448,10 +452,10 @@ Button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
margin: 0 1rem 0.5rem 20rem;
|
width: 100%;
|
||||||
width: calc(100% - 20rem);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
padding: 1rem 1rem 0 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</RecycleScroller>
|
</RecycleScroller>
|
||||||
</div>
|
</div>
|
||||||
<ShareModalWrapper
|
<ShareModal
|
||||||
ref="shareModal"
|
ref="shareModal"
|
||||||
header="Share Log"
|
header="Share Log"
|
||||||
share-title="Instance Log"
|
share-title="Instance Log"
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { CheckIcon, ClipboardCopyIcon, ShareIcon, TrashIcon } from '@modrinth/assets'
|
import { CheckIcon, ClipboardCopyIcon, ShareIcon, TrashIcon } from '@modrinth/assets'
|
||||||
import { Button, Card, Checkbox, DropdownSelect } from '@modrinth/ui'
|
import { Button, Card, ShareModal, Checkbox, DropdownSelect } from '@modrinth/ui'
|
||||||
import {
|
import {
|
||||||
delete_logs_by_filename,
|
delete_logs_by_filename,
|
||||||
get_logs,
|
get_logs,
|
||||||
@@ -107,7 +107,6 @@ import { handleError } from '@/store/notifications.js'
|
|||||||
import { ofetch } from 'ofetch'
|
import { ofetch } from 'ofetch'
|
||||||
import { RecycleScroller } from 'vue-virtual-scroller'
|
import { RecycleScroller } from 'vue-virtual-scroller'
|
||||||
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||||||
import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
|
|
||||||
|
|
||||||
dayjs.extend(isToday)
|
dayjs.extend(isToday)
|
||||||
dayjs.extend(isYesterday)
|
dayjs.extend(isYesterday)
|
||||||
@@ -296,7 +295,7 @@ if (logs.value.length > 1 && !props.playing) {
|
|||||||
|
|
||||||
const deleteLog = async () => {
|
const deleteLog = async () => {
|
||||||
if (logs.value[selectedLogIndex.value] && selectedLogIndex.value !== 0) {
|
if (logs.value[selectedLogIndex.value] && selectedLogIndex.value !== 0) {
|
||||||
const deleteIndex = selectedLogIndex.value
|
let deleteIndex = selectedLogIndex.value
|
||||||
selectedLogIndex.value = deleteIndex - 1
|
selectedLogIndex.value = deleteIndex - 1
|
||||||
await delete_logs_by_filename(
|
await delete_logs_by_filename(
|
||||||
props.instance.path,
|
props.instance.path,
|
||||||
|
|||||||
@@ -284,7 +284,7 @@
|
|||||||
:link-function="(page) => `?page=${page}`"
|
:link-function="(page) => `?page=${page}`"
|
||||||
@switch-page="switchPage"
|
@switch-page="switchPage"
|
||||||
/>
|
/>
|
||||||
<ModalWrapper ref="deleteWarning" header="Are you sure?">
|
<Modal ref="deleteWarning" header="Are you sure?">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="markdown-body">
|
<div class="markdown-body">
|
||||||
<p>
|
<p>
|
||||||
@@ -302,8 +302,8 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
<ModalWrapper ref="deleteDisabledWarning" header="Are you sure?">
|
<Modal ref="deleteDisabledWarning" header="Are you sure?">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="markdown-body">
|
<div class="markdown-body">
|
||||||
<p>
|
<p>
|
||||||
@@ -325,8 +325,8 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
<ShareModalWrapper
|
<ShareModal
|
||||||
ref="shareModal"
|
ref="shareModal"
|
||||||
share-title="Sharing modpack content"
|
share-title="Sharing modpack content"
|
||||||
share-text="Check out the projects I'm using in my modpack!"
|
share-text="Check out the projects I'm using in my modpack!"
|
||||||
@@ -360,6 +360,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
Pagination,
|
Pagination,
|
||||||
DropdownSelect,
|
DropdownSelect,
|
||||||
|
ShareModal,
|
||||||
|
Modal,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
AnimatedLogo,
|
AnimatedLogo,
|
||||||
Avatar,
|
Avatar,
|
||||||
@@ -377,7 +379,8 @@ import {
|
|||||||
update_project,
|
update_project,
|
||||||
} from '@/helpers/profile.js'
|
} from '@/helpers/profile.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
import { listen } from '@tauri-apps/api/event'
|
||||||
import { highlightModInProfile } from '@/helpers/utils.js'
|
import { highlightModInProfile } from '@/helpers/utils.js'
|
||||||
import { MenuIcon, ToggleIcon, TextInputIcon, AddProjectImage, PackageIcon } from '@/assets/icons'
|
import { MenuIcon, ToggleIcon, TextInputIcon, AddProjectImage, PackageIcon } from '@/assets/icons'
|
||||||
import ExportModal from '@/components/ui/ExportModal.vue'
|
import ExportModal from '@/components/ui/ExportModal.vue'
|
||||||
@@ -390,9 +393,6 @@ import {
|
|||||||
get_version_many,
|
get_version_many,
|
||||||
} from '@/helpers/cache.js'
|
} from '@/helpers/cache.js'
|
||||||
import { profile_listener } from '@/helpers/events.js'
|
import { profile_listener } from '@/helpers/events.js'
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
|
||||||
import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
|
|
||||||
import { getCurrentWebview } from '@tauri-apps/api/webview'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
instance: {
|
instance: {
|
||||||
@@ -682,7 +682,7 @@ const updateAll = async () => {
|
|||||||
projects.value[project].updating = false
|
projects.value[project].updating = false
|
||||||
}
|
}
|
||||||
|
|
||||||
trackEvent('InstanceUpdateAll', {
|
mixpanel_track('InstanceUpdateAll', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
count: setProjects.length,
|
count: setProjects.length,
|
||||||
@@ -708,7 +708,7 @@ const updateProject = async (mod) => {
|
|||||||
mod.version = mod.updateVersion.version_number
|
mod.version = mod.updateVersion.version_number
|
||||||
mod.updateVersion = null
|
mod.updateVersion = null
|
||||||
|
|
||||||
trackEvent('InstanceProjectUpdate', {
|
mixpanel_track('InstanceProjectUpdate', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
id: mod.id,
|
id: mod.id,
|
||||||
@@ -717,7 +717,7 @@ const updateProject = async (mod) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const locks = {}
|
let locks = {}
|
||||||
|
|
||||||
const toggleDisableMod = async (mod) => {
|
const toggleDisableMod = async (mod) => {
|
||||||
// Use mod's id as the key for the lock. If mod doesn't have a unique id, replace `mod.id` with some unique property.
|
// Use mod's id as the key for the lock. If mod doesn't have a unique id, replace `mod.id` with some unique property.
|
||||||
@@ -725,7 +725,7 @@ const toggleDisableMod = async (mod) => {
|
|||||||
locks[mod.id] = ref(null)
|
locks[mod.id] = ref(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const lock = locks[mod.id]
|
let lock = locks[mod.id]
|
||||||
|
|
||||||
while (lock.value) {
|
while (lock.value) {
|
||||||
await lock.value
|
await lock.value
|
||||||
@@ -735,7 +735,7 @@ const toggleDisableMod = async (mod) => {
|
|||||||
.then((newPath) => {
|
.then((newPath) => {
|
||||||
mod.path = newPath
|
mod.path = newPath
|
||||||
mod.disabled = !mod.disabled
|
mod.disabled = !mod.disabled
|
||||||
trackEvent('InstanceProjectDisable', {
|
mixpanel_track('InstanceProjectDisable', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
id: mod.id,
|
id: mod.id,
|
||||||
@@ -756,7 +756,7 @@ const removeMod = async (mod) => {
|
|||||||
await remove_project(props.instance.path, mod.path).catch(handleError)
|
await remove_project(props.instance.path, mod.path).catch(handleError)
|
||||||
projects.value = projects.value.filter((x) => mod.path !== x.path)
|
projects.value = projects.value.filter((x) => mod.path !== x.path)
|
||||||
|
|
||||||
trackEvent('InstanceProjectRemove', {
|
mixpanel_track('InstanceProjectRemove', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
id: mod.id,
|
id: mod.id,
|
||||||
@@ -784,7 +784,6 @@ const deleteDisabled = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const shareNames = async () => {
|
const shareNames = async () => {
|
||||||
console.log(functionValues.value)
|
|
||||||
await shareModal.value.show(functionValues.value.map((x) => x.name).join('\n'))
|
await shareModal.value.show(functionValues.value.map((x) => x.name).join('\n'))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -879,10 +878,8 @@ async function refreshProjects() {
|
|||||||
refreshingProjects.value = false
|
refreshingProjects.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const unlisten = await getCurrentWebview().onDragDropEvent(async (event) => {
|
const unlisten = await listen('tauri://file-drop', async (event) => {
|
||||||
if (event.payload.type !== 'drop') return
|
for (const file of event.payload) {
|
||||||
|
|
||||||
for (const file of event.payload.paths) {
|
|
||||||
if (file.endsWith('.mrpack')) continue
|
if (file.endsWith('.mrpack')) continue
|
||||||
await add_project_from_path(props.instance.path, file).catch(handleError)
|
await add_project_from_path(props.instance.path, file).catch(handleError)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<ConfirmModalWrapper
|
<ConfirmModal
|
||||||
ref="modal_confirm"
|
ref="modal_confirm"
|
||||||
title="Are you sure you want to delete this instance?"
|
title="Are you sure you want to delete this instance?"
|
||||||
description="If you proceed, all data for your instance will be removed. You will not be able to recover it."
|
description="If you proceed, all data for your instance will be removed. You will not be able to recover it."
|
||||||
:has-to-type="false"
|
:has-to-type="false"
|
||||||
proceed-label="Delete"
|
proceed-label="Delete"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
@proceed="removeProfile"
|
@proceed="removeProfile"
|
||||||
/>
|
/>
|
||||||
<ModalWrapper ref="modalConfirmUnlock" header="Are you sure you want to unlock this instance?">
|
<Modal
|
||||||
|
ref="modalConfirmUnlock"
|
||||||
|
header="Are you sure you want to unlock this instance?"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
>
|
||||||
<div class="modal-delete">
|
<div class="modal-delete">
|
||||||
<div
|
<div
|
||||||
class="markdown-body"
|
class="markdown-body"
|
||||||
@@ -26,9 +31,13 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
|
|
||||||
<ModalWrapper ref="modalConfirmUnpair" header="Are you sure you want to unpair this instance?">
|
<Modal
|
||||||
|
ref="modalConfirmUnpair"
|
||||||
|
header="Are you sure you want to unpair this instance?"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
>
|
||||||
<div class="modal-delete">
|
<div class="modal-delete">
|
||||||
<div
|
<div
|
||||||
class="markdown-body"
|
class="markdown-body"
|
||||||
@@ -47,9 +56,13 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
|
|
||||||
<ModalWrapper ref="changeVersionsModal" header="Change instance versions">
|
<Modal
|
||||||
|
ref="changeVersionsModal"
|
||||||
|
header="Change instance versions"
|
||||||
|
:noblur="!themeStore.advancedRendering"
|
||||||
|
>
|
||||||
<div class="change-versions-modal universal-body">
|
<div class="change-versions-modal universal-body">
|
||||||
<div class="input-row">
|
<div class="input-row">
|
||||||
<p class="input-label">Loader</p>
|
<p class="input-label">Loader</p>
|
||||||
@@ -93,7 +106,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</Modal>
|
||||||
<section class="card">
|
<section class="card">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<h3>
|
<h3>
|
||||||
@@ -498,7 +511,18 @@ import {
|
|||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
ClipboardCopyIcon,
|
ClipboardCopyIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { Button, Toggle, Card, Slider, Checkbox, Avatar, Chips, DropdownSelect } from '@modrinth/ui'
|
import {
|
||||||
|
Button,
|
||||||
|
Toggle,
|
||||||
|
ConfirmModal,
|
||||||
|
Card,
|
||||||
|
Slider,
|
||||||
|
Checkbox,
|
||||||
|
Avatar,
|
||||||
|
Modal,
|
||||||
|
Chips,
|
||||||
|
DropdownSelect,
|
||||||
|
} from '@modrinth/ui'
|
||||||
import { SwapIcon } from '@/assets/icons'
|
import { SwapIcon } from '@/assets/icons'
|
||||||
|
|
||||||
import { Multiselect } from 'vue-multiselect'
|
import { Multiselect } from 'vue-multiselect'
|
||||||
@@ -517,16 +541,15 @@ import { computed, readonly, ref, shallowRef, watch } from 'vue'
|
|||||||
import { get_max_memory } from '@/helpers/jre.js'
|
import { get_max_memory } from '@/helpers/jre.js'
|
||||||
import { get } from '@/helpers/settings.js'
|
import { get } from '@/helpers/settings.js'
|
||||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
import { open } from '@tauri-apps/plugin-dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import { get_loader_versions } from '@/helpers/metadata.js'
|
import { get_loader_versions } from '@/helpers/metadata.js'
|
||||||
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
|
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
import { useTheming } from '@/store/theme.js'
|
||||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
|
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
|
||||||
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
|
|
||||||
|
|
||||||
const breadcrumbs = useBreadcrumbs()
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
|
||||||
@@ -547,6 +570,8 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const themeStore = useTheming()
|
||||||
|
|
||||||
const title = ref(props.instance.name)
|
const title = ref(props.instance.name)
|
||||||
const icon = ref(props.instance.icon_path)
|
const icon = ref(props.instance.icon_path)
|
||||||
const groups = ref(props.instance.groups)
|
const groups = ref(props.instance.groups)
|
||||||
@@ -565,7 +590,7 @@ const availableGroups = ref([
|
|||||||
async function resetIcon() {
|
async function resetIcon() {
|
||||||
icon.value = null
|
icon.value = null
|
||||||
await edit_icon(props.instance.path, null).catch(handleError)
|
await edit_icon(props.instance.path, null).catch(handleError)
|
||||||
trackEvent('InstanceRemoveIcon')
|
mixpanel_track('InstanceRemoveIcon')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setIcon() {
|
async function setIcon() {
|
||||||
@@ -581,10 +606,10 @@ async function setIcon() {
|
|||||||
|
|
||||||
if (!value) return
|
if (!value) return
|
||||||
|
|
||||||
icon.value = value.path ?? value
|
icon.value = value
|
||||||
await edit_icon(props.instance.path, icon.value).catch(handleError)
|
await edit_icon(props.instance.path, icon.value).catch(handleError)
|
||||||
|
|
||||||
trackEvent('InstanceSetIcon')
|
mixpanel_track('InstanceSetIcon')
|
||||||
}
|
}
|
||||||
|
|
||||||
const globalSettings = await get().catch(handleError)
|
const globalSettings = await get().catch(handleError)
|
||||||
@@ -596,12 +621,12 @@ const overrideJavaInstall = ref(!!props.instance.java_path)
|
|||||||
const optimalJava = readonly(await get_optimal_jre_key(props.instance.path).catch(handleError))
|
const optimalJava = readonly(await get_optimal_jre_key(props.instance.path).catch(handleError))
|
||||||
const javaInstall = ref({ path: optimalJava.path ?? props.instance.java_path })
|
const javaInstall = ref({ path: optimalJava.path ?? props.instance.java_path })
|
||||||
|
|
||||||
const overrideJavaArgs = ref(props.instance.extra_launch_args?.length !== undefined)
|
const overrideJavaArgs = ref(!!props.instance.extra_launch_args)
|
||||||
const javaArgs = ref(
|
const javaArgs = ref(
|
||||||
(props.instance.extra_launch_args ?? globalSettings.extra_launch_args).join(' '),
|
(props.instance.extra_launch_args ?? globalSettings.extra_launch_args).join(' '),
|
||||||
)
|
)
|
||||||
|
|
||||||
const overrideEnvVars = ref(props.instance.custom_env_vars?.length !== undefined)
|
const overrideEnvVars = ref(!!props.instance.custom_env_vars)
|
||||||
const envVars = ref(
|
const envVars = ref(
|
||||||
(props.instance.custom_env_vars ?? globalSettings.custom_env_vars)
|
(props.instance.custom_env_vars ?? globalSettings.custom_env_vars)
|
||||||
.map((x) => x.join('='))
|
.map((x) => x.join('='))
|
||||||
@@ -685,15 +710,19 @@ const editProfileObject = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (overrideJavaArgs.value) {
|
if (overrideJavaArgs.value) {
|
||||||
editProfile.extra_launch_args = javaArgs.value.trim().split(/\s+/).filter(Boolean)
|
if (javaArgs.value !== '') {
|
||||||
|
editProfile.extra_launch_args = javaArgs.value.trim().split(/\s+/).filter(Boolean)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideEnvVars.value) {
|
if (overrideEnvVars.value) {
|
||||||
editProfile.custom_env_vars = envVars.value
|
if (envVars.value !== '') {
|
||||||
.trim()
|
editProfile.custom_env_vars = envVars.value
|
||||||
.split(/\s+/)
|
.trim()
|
||||||
.filter(Boolean)
|
.split(/\s+/)
|
||||||
.map((x) => x.split('=').filter(Boolean))
|
.filter(Boolean)
|
||||||
|
.map((x) => x.split('=').filter(Boolean))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideMemorySettings.value) {
|
if (overrideMemorySettings.value) {
|
||||||
@@ -725,7 +754,7 @@ const repairing = ref(false)
|
|||||||
|
|
||||||
async function duplicateProfile() {
|
async function duplicateProfile() {
|
||||||
await duplicate(props.instance.path).catch(handleError)
|
await duplicate(props.instance.path).catch(handleError)
|
||||||
trackEvent('InstanceDuplicate', {
|
mixpanel_track('InstanceDuplicate', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
})
|
})
|
||||||
@@ -736,7 +765,7 @@ async function repairProfile(force) {
|
|||||||
await install(props.instance.path, force).catch(handleError)
|
await install(props.instance.path, force).catch(handleError)
|
||||||
repairing.value = false
|
repairing.value = false
|
||||||
|
|
||||||
trackEvent('InstanceRepair', {
|
mixpanel_track('InstanceRepair', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
})
|
})
|
||||||
@@ -767,7 +796,7 @@ async function repairModpack() {
|
|||||||
await update_repair_modrinth(props.instance.path).catch(handleError)
|
await update_repair_modrinth(props.instance.path).catch(handleError)
|
||||||
inProgress.value = false
|
inProgress.value = false
|
||||||
|
|
||||||
trackEvent('InstanceRepair', {
|
mixpanel_track('InstanceRepair', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
})
|
})
|
||||||
@@ -779,7 +808,7 @@ async function removeProfile() {
|
|||||||
await remove(props.instance.path).catch(handleError)
|
await remove(props.instance.path).catch(handleError)
|
||||||
removing.value = false
|
removing.value = false
|
||||||
|
|
||||||
trackEvent('InstanceRemove', {
|
mixpanel_track('InstanceRemove', {
|
||||||
loader: props.instance.loader,
|
loader: props.instance.loader,
|
||||||
game_version: props.instance.game_version,
|
game_version: props.instance.game_version,
|
||||||
})
|
})
|
||||||
@@ -876,7 +905,7 @@ const editing = ref(false)
|
|||||||
async function saveGvLoaderEdits() {
|
async function saveGvLoaderEdits() {
|
||||||
editing.value = true
|
editing.value = true
|
||||||
|
|
||||||
const editProfile = editProfileObject.value
|
let editProfile = editProfileObject.value
|
||||||
editProfile.loader = loader.value
|
editProfile.loader = loader.value
|
||||||
editProfile.game_version = gameVersion.value
|
editProfile.game_version = gameVersion.value
|
||||||
|
|
||||||
|
|||||||
@@ -20,14 +20,14 @@
|
|||||||
</span>
|
</span>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="expandedGalleryItem" class="expanded-image-modal" @click="hideImage">
|
<div v-if="expandedGalleryItem" class="expanded-image-modal" @click="expandedGalleryItem = null">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<img
|
<img
|
||||||
class="image"
|
class="image"
|
||||||
:class="{ 'zoomed-in': zoomedIn }"
|
:class="{ 'zoomed-in': zoomedIn }"
|
||||||
:src="
|
:src="
|
||||||
expandedGalleryItem.raw_url
|
expandedGalleryItem.url
|
||||||
? expandedGalleryItem.raw_url
|
? expandedGalleryItem.url
|
||||||
: 'https://cdn.modrinth.com/placeholder-banner.svg'
|
: 'https://cdn.modrinth.com/placeholder-banner.svg'
|
||||||
"
|
"
|
||||||
:alt="expandedGalleryItem.title ? expandedGalleryItem.title : 'gallery-image'"
|
:alt="expandedGalleryItem.title ? expandedGalleryItem.title : 'gallery-image'"
|
||||||
@@ -45,15 +45,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<Button class="close" icon-only @click="hideImage">
|
<Button class="close" icon-only @click="expandedGalleryItem = null">
|
||||||
<XIcon aria-hidden="true" />
|
<XIcon aria-hidden="true" />
|
||||||
</Button>
|
</Button>
|
||||||
<a
|
<a
|
||||||
class="open btn icon-only"
|
class="open btn icon-only"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
:href="
|
:href="
|
||||||
expandedGalleryItem.raw_url
|
expandedGalleryItem.url
|
||||||
? expandedGalleryItem.raw_url
|
? expandedGalleryItem.url
|
||||||
: 'https://cdn.modrinth.com/placeholder-banner.svg'
|
: 'https://cdn.modrinth.com/placeholder-banner.svg'
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@@ -93,8 +93,7 @@ import {
|
|||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { Button, Card } from '@modrinth/ui'
|
import { Button, Card } from '@modrinth/ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
project: {
|
project: {
|
||||||
@@ -103,14 +102,9 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const expandedGalleryItem = ref(null)
|
let expandedGalleryItem = ref(null)
|
||||||
const expandedGalleryIndex = ref(0)
|
let expandedGalleryIndex = ref(0)
|
||||||
const zoomedIn = ref(false)
|
let zoomedIn = ref(false)
|
||||||
|
|
||||||
const hideImage = () => {
|
|
||||||
expandedGalleryItem.value = null
|
|
||||||
show_ads_window()
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextImage = () => {
|
const nextImage = () => {
|
||||||
expandedGalleryIndex.value++
|
expandedGalleryIndex.value++
|
||||||
@@ -118,7 +112,7 @@ const nextImage = () => {
|
|||||||
expandedGalleryIndex.value = 0
|
expandedGalleryIndex.value = 0
|
||||||
}
|
}
|
||||||
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
|
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
|
||||||
trackEvent('GalleryImageNext', {
|
mixpanel_track('GalleryImageNext', {
|
||||||
project_id: props.project.id,
|
project_id: props.project.id,
|
||||||
url: expandedGalleryItem.value.url,
|
url: expandedGalleryItem.value.url,
|
||||||
})
|
})
|
||||||
@@ -130,37 +124,22 @@ const previousImage = () => {
|
|||||||
expandedGalleryIndex.value = props.project.gallery.length - 1
|
expandedGalleryIndex.value = props.project.gallery.length - 1
|
||||||
}
|
}
|
||||||
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
|
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
|
||||||
trackEvent('GalleryImagePrevious', {
|
mixpanel_track('GalleryImagePrevious', {
|
||||||
project_id: props.project.id,
|
project_id: props.project.id,
|
||||||
url: expandedGalleryItem.value,
|
url: expandedGalleryItem.value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const expandImage = (item, index) => {
|
const expandImage = (item, index) => {
|
||||||
hide_ads_window()
|
|
||||||
expandedGalleryItem.value = item
|
expandedGalleryItem.value = item
|
||||||
expandedGalleryIndex.value = index
|
expandedGalleryIndex.value = index
|
||||||
zoomedIn.value = false
|
zoomedIn.value = false
|
||||||
|
|
||||||
trackEvent('GalleryImageExpand', {
|
mixpanel_track('GalleryImageExpand', {
|
||||||
project_id: props.project.id,
|
project_id: props.project.id,
|
||||||
url: item.url,
|
url: item.url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function keyListener(e) {
|
|
||||||
if (expandedGalleryItem.value) {
|
|
||||||
e.preventDefault()
|
|
||||||
if (e.key === 'Escape') {
|
|
||||||
hideImage()
|
|
||||||
} else if (e.key === 'ArrowLeft') {
|
|
||||||
previousImage()
|
|
||||||
} else if (e.key === 'ArrowRight') {
|
|
||||||
nextImage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.addEventListener('keypress', keyListener)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="root-container">
|
<div class="root-container">
|
||||||
<div v-if="data" class="project-sidebar" @scroll="$refs.promo.scroll()">
|
<div v-if="data" class="project-sidebar">
|
||||||
<Card v-if="instance" class="small-instance">
|
<Card v-if="instance" class="small-instance">
|
||||||
<router-link class="instance" :to="`/instance/${encodeURIComponent(instance.path)}`">
|
<router-link class="instance" :to="`/instance/${encodeURIComponent(instance.path)}`">
|
||||||
<Avatar
|
<Avatar
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</Card>
|
</Card>
|
||||||
<Card class="sidebar-card" @contextmenu.prevent.stop="handleRightClick">
|
<Card class="sidebar-card" @contextmenu.prevent.stop="handleRightClick">
|
||||||
<Avatar size="md" :src="data.icon_url" />
|
<Avatar size="lg" :src="data.icon_url" />
|
||||||
<div class="instance-info">
|
<div class="instance-info">
|
||||||
<h2 class="name">{{ data.title }}</h2>
|
<h2 class="name">{{ data.title }}</h2>
|
||||||
{{ data.description }}
|
{{ data.description }}
|
||||||
@@ -61,9 +61,7 @@
|
|||||||
Site
|
Site
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
<hr class="card-divider" />
|
||||||
<PromotionWrapper ref="promo" />
|
|
||||||
<Card class="sidebar-card">
|
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<DownloadIcon aria-hidden="true" />
|
<DownloadIcon aria-hidden="true" />
|
||||||
@@ -165,6 +163,7 @@
|
|||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="data" class="content-container">
|
<div v-if="data" class="content-container">
|
||||||
|
<PromotionWrapper />
|
||||||
<Card class="tabs">
|
<Card class="tabs">
|
||||||
<NavRow
|
<NavRow
|
||||||
v-if="data.gallery.length > 0"
|
v-if="data.gallery.length > 0"
|
||||||
@@ -232,7 +231,15 @@ import {
|
|||||||
GlobeIcon,
|
GlobeIcon,
|
||||||
ClipboardCopyIcon,
|
ClipboardCopyIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { Categories, EnvironmentIndicator, Card, Avatar, Button, NavRow } from '@modrinth/ui'
|
import {
|
||||||
|
Categories,
|
||||||
|
EnvironmentIndicator,
|
||||||
|
Card,
|
||||||
|
Avatar,
|
||||||
|
Button,
|
||||||
|
Promotion,
|
||||||
|
NavRow,
|
||||||
|
} from '@modrinth/ui'
|
||||||
import { formatNumber } from '@modrinth/utils'
|
import { formatNumber } from '@modrinth/utils'
|
||||||
import {
|
import {
|
||||||
BuyMeACoffeeIcon,
|
BuyMeACoffeeIcon,
|
||||||
@@ -250,10 +257,10 @@ import { useRoute } from 'vue-router'
|
|||||||
import { ref, shallowRef, watch } from 'vue'
|
import { ref, shallowRef, watch } from 'vue'
|
||||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
import { install as installVersion } from '@/store/install.js'
|
import { install as installVersion } from '@/store/install.js'
|
||||||
import { get_project, get_team, get_version_many } from '@/helpers/cache.js'
|
import { get_project, get_project_many, get_team, get_version_many } from '@/helpers/cache.js'
|
||||||
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
|
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
|
||||||
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
@@ -302,13 +309,11 @@ async function fetchProjectData() {
|
|||||||
|
|
||||||
await fetchProjectData()
|
await fetchProjectData()
|
||||||
|
|
||||||
const promo = ref(null)
|
|
||||||
watch(
|
watch(
|
||||||
() => route.params.id,
|
() => route.params.id,
|
||||||
async () => {
|
async () => {
|
||||||
if (route.params.id && route.path.startsWith('/project')) {
|
if (route.params.id && route.path.startsWith('/project')) {
|
||||||
await fetchProjectData()
|
await fetchProjectData()
|
||||||
promo.value.scroll()
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -372,7 +377,7 @@ const handleOptionsClick = (args) => {
|
|||||||
|
|
||||||
.project-sidebar {
|
.project-sidebar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: calc(300px + 1.5rem);
|
width: 20rem;
|
||||||
min-height: calc(100vh - 3.25rem);
|
min-height: calc(100vh - 3.25rem);
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
max-height: calc(100vh - 3.25rem);
|
max-height: calc(100vh - 3.25rem);
|
||||||
@@ -398,7 +403,7 @@ const handleOptionsClick = (args) => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin-left: calc(300px + 1rem);
|
margin-left: 19.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-group {
|
.button-group {
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export default new createRouter({
|
|||||||
linkExactActiveClass: 'router-link-exact-active',
|
linkExactActiveClass: 'router-link-exact-active',
|
||||||
scrollBehavior() {
|
scrollBehavior() {
|
||||||
// Sometimes Vue's scroll behavior is not working as expected, so we need to manually scroll to top (especially on Linux)
|
// Sometimes Vue's scroll behavior is not working as expected, so we need to manually scroll to top (especially on Linux)
|
||||||
document.querySelector('.router-view')?.scrollTo(0, 0)
|
document.querySelector('.router-view').scrollTop = 0
|
||||||
return {
|
return {
|
||||||
el: '.router-view',
|
el: '.router-view',
|
||||||
top: 0,
|
top: 0,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { get_project, get_version_many } from '@/helpers/cache.js'
|
import { get_project, get_version_many } from '@/helpers/cache.js'
|
||||||
import { install as packInstall } from '@/helpers/pack.js'
|
import { install as packInstall } from '@/helpers/pack.js'
|
||||||
import { trackEvent } from '@/helpers/analytics.js'
|
import { mixpanel_track } from '@/helpers/mixpanel.js'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
export const useInstall = defineStore('installStore', {
|
export const useInstall = defineStore('installStore', {
|
||||||
@@ -51,7 +51,7 @@ export const install = async (projectId, versionId, instancePath, source, callba
|
|||||||
if (packs.length === 0 || !packs.find((pack) => pack.linked_data?.project_id === project.id)) {
|
if (packs.length === 0 || !packs.find((pack) => pack.linked_data?.project_id === project.id)) {
|
||||||
await packInstall(project.id, version, project.title, project.icon_url).catch(handleError)
|
await packInstall(project.id, version, project.title, project.icon_url).catch(handleError)
|
||||||
|
|
||||||
trackEvent('PackInstall', {
|
mixpanel_track('PackInstall', {
|
||||||
id: project.id,
|
id: project.id,
|
||||||
version_id: version,
|
version_id: version,
|
||||||
title: project.title,
|
title: project.title,
|
||||||
@@ -107,7 +107,7 @@ export const install = async (projectId, versionId, instancePath, source, callba
|
|||||||
await add_project_from_version(instance.path, version.id).catch(handleError)
|
await add_project_from_version(instance.path, version.id).catch(handleError)
|
||||||
await installVersionDependencies(instance, version)
|
await installVersionDependencies(instance, version)
|
||||||
|
|
||||||
trackEvent('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: instance.loader,
|
loader: instance.loader,
|
||||||
game_version: instance.game_version,
|
game_version: instance.game_version,
|
||||||
id: project.id,
|
id: project.id,
|
||||||
|
|||||||
@@ -23,3 +23,7 @@ export const handleError = (err) => {
|
|||||||
})
|
})
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const handleMixpanelError = (err) => {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2020",
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
||||||
"skipLibCheck": true,
|
|
||||||
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowJs": true,
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"moduleDetection": "force",
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
|
|
||||||
"strict": true,
|
|
||||||
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["./src/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": ["src"]
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,12 @@
|
|||||||
{
|
{
|
||||||
"files": [],
|
"compilerOptions": {
|
||||||
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"]
|
||||||
|
},
|
||||||
|
"target": "ESNext"
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2022",
|
|
||||||
"lib": ["ES2023"],
|
|
||||||
"module": "ESNext",
|
|
||||||
"skipLibCheck": true,
|
|
||||||
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"moduleDetection": "force",
|
|
||||||
"noEmit": true,
|
|
||||||
|
|
||||||
"strict": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
||||||
@@ -42,8 +42,8 @@ export default defineConfig({
|
|||||||
port: 1420,
|
port: 1420,
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
// to make use of `TAURI_ENV_DEBUG` and other env variables
|
// to make use of `TAURI_DEBUG` and other env variables
|
||||||
// https://v2.tauri.app/reference/environment-variables/#tauri-cli-hook-commands
|
// https://tauri.studio/v1/api/config#buildconfig.beforedevcommand
|
||||||
envPrefix: ['VITE_', 'TAURI_'],
|
envPrefix: ['VITE_', 'TAURI_'],
|
||||||
build: {
|
build: {
|
||||||
// Tauri supports es2021
|
// Tauri supports es2021
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ theseus = { path = "../../packages/app-lib", features = ["cli"] }
|
|||||||
|
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
tauri = { version = "1.7.1", features = ["shell-open"] }
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
webbrowser = "0.8.13"
|
webbrowser = "0.8.13"
|
||||||
dunce = "1.0.3"
|
dunce = "1.0.3"
|
||||||
|
|
||||||
|
tokio-stream = { version = "0.1", features = ["fs"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
uuid = { version = "1.1", features = ["serde", "v4"] }
|
uuid = { version = "1.1", features = ["serde", "v4"] }
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "@modrinth/app-playground",
|
"name": "@modrinth/app-playground",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "cargo build --release",
|
"build": "cargo build --release",
|
||||||
"lint": "cargo fmt --check && cargo clippy --all-targets --all-features -- -D warnings",
|
"lint": "cargo fmt --check && cargo clippy -- -D warnings",
|
||||||
"fix": "cargo fmt && cargo clippy --fix",
|
"fix": "cargo fmt && cargo clippy --fix",
|
||||||
"dev": "cargo run",
|
"dev": "cargo run",
|
||||||
"test": "cargo test"
|
"test": "cargo test"
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "theseus_gui"
|
name = "theseus_gui"
|
||||||
version = "0.8.9"
|
version = "0.8.3-1"
|
||||||
description = "The Modrinth App is a desktop application for managing your Minecraft mods"
|
description = "A Tauri App"
|
||||||
license = "GPL-3.0-only"
|
authors = ["you"]
|
||||||
repository = "https://github.com/modrinth/code/apps/app/"
|
license = ""
|
||||||
|
repository = ""
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2.0.0-rc", features = ["codegen"] }
|
tauri-build = { version = "1.5.3", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
theseus = { path = "../../packages/app-lib", features = ["tauri"] }
|
theseus = { path = "../../packages/app-lib", features = ["tauri"] }
|
||||||
@@ -16,19 +19,16 @@ theseus = { path = "../../packages/app-lib", features = ["tauri"] }
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
tauri = { version = "2.0.0-rc", features = ["devtools", "macos-private-api", "protocol-asset", "unstable"] }
|
tauri = { version = "1.7.1", features = [ "app-all", "devtools", "dialog", "dialog-confirm", "dialog-open", "macos-private-api", "os-all", "protocol-asset", "shell-open", "window-close", "window-create", "window-hide", "window-maximize", "window-minimize", "window-set-decorations", "window-show", "window-start-dragging", "window-unmaximize", "window-unminimize"] }
|
||||||
tauri-plugin-window-state = "2.0.0-rc"
|
tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||||
tauri-plugin-deep-link = "2.0.0-rc"
|
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||||
tauri-plugin-os = "2.0.0-rc"
|
tauri-plugin-deep-link = "0.1.2"
|
||||||
tauri-plugin-shell = "2.0.0-rc"
|
|
||||||
tauri-plugin-dialog = "2.0.0-rc"
|
|
||||||
tauri-plugin-updater = { version = "2.0.0-rc" }
|
|
||||||
tauri-plugin-single-instance = { version = "2.0.0-rc" }
|
|
||||||
|
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
tokio-stream = { version = "0.1", features = ["fs"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
daedalus = { path = "../../packages/daedalus" }
|
daedalus = "0.2.3"
|
||||||
chrono = "0.4.26"
|
chrono = "0.4.26"
|
||||||
|
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
@@ -56,10 +56,6 @@ window-shadows = "0.2.1"
|
|||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
cocoa = "0.25.0"
|
cocoa = "0.25.0"
|
||||||
objc = "0.2.7"
|
objc = "0.2.7"
|
||||||
rand = "0.8.5"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
tauri-plugin-updater = { version = "2.0.0-rc", optional = true, features = ["native-tls-vendored", "zip"], default-features = false }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# by default Tauri runs in production mode
|
# by default Tauri runs in production mode
|
||||||
@@ -68,4 +64,3 @@ default = ["custom-protocol"]
|
|||||||
# this feature is used for production builds where `devPath` points to the filesystem
|
# this feature is used for production builds where `devPath` points to the filesystem
|
||||||
# DO NOT remove this
|
# DO NOT remove this
|
||||||
custom-protocol = ["tauri/custom-protocol"]
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
updater = []
|
|
||||||
|
|||||||
@@ -6,17 +6,63 @@
|
|||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleURLName</key>
|
<key>CFBundleURLName</key>
|
||||||
|
<!-- Obviously needs to be replaced with your app's bundle identifier -->
|
||||||
<string>ModrinthApp</string>
|
<string>ModrinthApp</string>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
|
<!-- register the myapp:// and myscheme:// schemes -->
|
||||||
<string>modrinth</string>
|
<string>modrinth</string>
|
||||||
<string>modrinthscheme</string>
|
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>NSCameraUsageDescription</key>
|
<!-- Declare file types your app can open -->
|
||||||
<string>A Minecraft mod wants to access your camera.</string>
|
<key>CFBundleDocumentTypes</key>
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
<array>
|
||||||
<string>A Minecraft mod wants to access your microphone.</string>
|
<dict>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>Modrinth type</string>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Editor</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Owner</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>com.modrinth.theseus-type</string>
|
||||||
|
</array>
|
||||||
|
<key>NSDocumentClass</key>
|
||||||
|
<string>NSDocument</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>UTImportedTypeDeclarations</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>Modrinth File</string>
|
||||||
|
<key>UTTypeIcons</key>
|
||||||
|
<dict/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>com.modrinth.theseus-type</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>mrpack</string>
|
||||||
|
</array>
|
||||||
|
<key>public.mime-type</key>
|
||||||
|
<array>
|
||||||
|
<string>application/x-mrpack</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>A Minecraft mod wants to access your camera.</string>
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>A Minecraft mod wants to access your microphone.</string>
|
||||||
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Before you begin, ensure you have the following installed on your machine:
|
|||||||
- [Node.js](https://nodejs.org/en/)
|
- [Node.js](https://nodejs.org/en/)
|
||||||
- [pnpm](https://pnpm.io/)
|
- [pnpm](https://pnpm.io/)
|
||||||
- [Rust](https://www.rust-lang.org/tools/install)
|
- [Rust](https://www.rust-lang.org/tools/install)
|
||||||
- [Tauri](https://v2.tauri.app/start/prerequisites/)
|
- [Tauri](https://tauri.app/v1/guides/getting-started/prerequisites/#installing)
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
|
|||||||
@@ -1,239 +1,4 @@
|
|||||||
use tauri_build::{DefaultPermissionRule, InlinedPlugin};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Sadly, there is no better way to do it right now
|
// Build the Tauri app
|
||||||
// You could try parsing source code here and detecting #[tauri::command]
|
tauri_build::build();
|
||||||
// But I think it's not worth it
|
|
||||||
// https://github.com/tauri-apps/tauri/issues/10075
|
|
||||||
tauri_build::try_build(
|
|
||||||
tauri_build::Attributes::new()
|
|
||||||
.codegen(tauri_build::CodegenContext::new())
|
|
||||||
.plugin(
|
|
||||||
"auth",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"login",
|
|
||||||
"remove_user",
|
|
||||||
"get_default_user",
|
|
||||||
"set_default_user",
|
|
||||||
"get_users",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"cache",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"get_project",
|
|
||||||
"get_project_many",
|
|
||||||
"get_version",
|
|
||||||
"get_version_many",
|
|
||||||
"get_user",
|
|
||||||
"get_user_many",
|
|
||||||
"get_team",
|
|
||||||
"get_team_many",
|
|
||||||
"get_organization",
|
|
||||||
"get_organization_many",
|
|
||||||
"get_search_results",
|
|
||||||
"get_search_results_many",
|
|
||||||
"purge_cache_types",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"import",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"get_importable_instances",
|
|
||||||
"import_instance",
|
|
||||||
"is_valid_importable_instance",
|
|
||||||
"get_default_launcher_path",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"jre",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"get_java_versions",
|
|
||||||
"set_java_version",
|
|
||||||
"jre_find_filtered_jres",
|
|
||||||
"jre_get_jre",
|
|
||||||
"jre_test_jre",
|
|
||||||
"jre_auto_install_java",
|
|
||||||
"jre_get_max_memory",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"logs",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"logs_get_logs",
|
|
||||||
"logs_get_logs_by_filename",
|
|
||||||
"logs_get_output_by_filename",
|
|
||||||
"logs_delete_logs",
|
|
||||||
"logs_delete_logs_by_filename",
|
|
||||||
"logs_get_latest_log_cursor",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"metadata",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"metadata_get_game_versions",
|
|
||||||
"metadata_get_loader_versions",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"mr-auth",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"login_pass",
|
|
||||||
"login_2fa",
|
|
||||||
"create_account",
|
|
||||||
"logout",
|
|
||||||
"get",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"pack",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&["pack_install", "pack_get_profile_from_pack"])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"process",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"process_get_all",
|
|
||||||
"process_get_by_profile_path",
|
|
||||||
"process_kill",
|
|
||||||
"process_wait_for",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"profile",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"profile_remove",
|
|
||||||
"profile_get",
|
|
||||||
"profile_get_many",
|
|
||||||
"profile_get_projects",
|
|
||||||
"profile_get_optimal_jre_key",
|
|
||||||
"profile_get_full_path",
|
|
||||||
"profile_get_mod_full_path",
|
|
||||||
"profile_list",
|
|
||||||
"profile_check_installed",
|
|
||||||
"profile_install",
|
|
||||||
"profile_update_all",
|
|
||||||
"profile_update_project",
|
|
||||||
"profile_add_project_from_version",
|
|
||||||
"profile_add_project_from_path",
|
|
||||||
"profile_toggle_disable_project",
|
|
||||||
"profile_remove_project",
|
|
||||||
"profile_update_managed_modrinth_version",
|
|
||||||
"profile_repair_managed_modrinth",
|
|
||||||
"profile_run",
|
|
||||||
"profile_run_credentials",
|
|
||||||
"profile_kill",
|
|
||||||
"profile_edit",
|
|
||||||
"profile_edit_icon",
|
|
||||||
"profile_export_mrpack",
|
|
||||||
"profile_get_pack_export_candidates",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"profile-create",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&["profile_create", "profile_duplicate"])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"settings",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"settings_get",
|
|
||||||
"settings_set",
|
|
||||||
"cancel_directory_change",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"tags",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"tags_get_categories",
|
|
||||||
"tags_get_report_types",
|
|
||||||
"tags_get_loaders",
|
|
||||||
"tags_get_game_versions",
|
|
||||||
"tags_get_donation_platforms",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"utils",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"get_os",
|
|
||||||
"should_disable_mouseover",
|
|
||||||
"highlight_in_folder",
|
|
||||||
"open_path",
|
|
||||||
"show_launcher_logs_folder",
|
|
||||||
"progress_bars_list",
|
|
||||||
"get_opening_command",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.plugin(
|
|
||||||
"ads",
|
|
||||||
InlinedPlugin::new()
|
|
||||||
.commands(&[
|
|
||||||
"init_ads_window",
|
|
||||||
"hide_ads_window",
|
|
||||||
"scroll_ads_window",
|
|
||||||
"show_ads_window",
|
|
||||||
"record_ads_click",
|
|
||||||
"open_link",
|
|
||||||
"get_ads_personalization",
|
|
||||||
])
|
|
||||||
.default_permission(
|
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.expect("Failed to run tauri-build");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"identifier": "ads",
|
|
||||||
"description": "",
|
|
||||||
"local": false,
|
|
||||||
"remote": {
|
|
||||||
"urls": ["https://modrinth.com/*", "http://localhost:3000/*"]
|
|
||||||
},
|
|
||||||
"webviews": [
|
|
||||||
"ads-window"
|
|
||||||
],
|
|
||||||
"permissions": [
|
|
||||||
"ads:default"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"identifier": "core",
|
|
||||||
"description": "",
|
|
||||||
"local": true,
|
|
||||||
"windows": [
|
|
||||||
"main"
|
|
||||||
],
|
|
||||||
"permissions": [
|
|
||||||
"core:default",
|
|
||||||
"core:path:default",
|
|
||||||
"core:event:default",
|
|
||||||
"core:window:default",
|
|
||||||
"core:app:default",
|
|
||||||
"core:resources:default",
|
|
||||||
"core:menu:default",
|
|
||||||
"core:tray:default",
|
|
||||||
"core:window:allow-create",
|
|
||||||
"core:window:allow-maximize",
|
|
||||||
"core:window:allow-toggle-maximize",
|
|
||||||
"core:window:allow-unmaximize",
|
|
||||||
"core:window:allow-minimize",
|
|
||||||
"core:window:allow-unminimize",
|
|
||||||
"core:window:allow-show",
|
|
||||||
"core:window:allow-hide",
|
|
||||||
"core:window:allow-close",
|
|
||||||
"core:window:allow-set-decorations",
|
|
||||||
"core:window:allow-start-dragging",
|
|
||||||
"core:webview:allow-set-webview-zoom"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"identifier": "plugins",
|
|
||||||
"description": "",
|
|
||||||
"local": true,
|
|
||||||
"windows": ["main"],
|
|
||||||
"permissions": [
|
|
||||||
"dialog:allow-open",
|
|
||||||
"dialog:allow-confirm",
|
|
||||||
"shell:allow-open",
|
|
||||||
"os:allow-platform",
|
|
||||||
"os:allow-version",
|
|
||||||
"os:allow-os-type",
|
|
||||||
"os:allow-family",
|
|
||||||
"os:allow-arch",
|
|
||||||
"os:allow-exe-extension",
|
|
||||||
"os:allow-locale",
|
|
||||||
"os:allow-hostname",
|
|
||||||
"deep-link:default",
|
|
||||||
"window-state:default",
|
|
||||||
"window-state:allow-restore-state",
|
|
||||||
"window-state:allow-save-window-state",
|
|
||||||
|
|
||||||
"auth:default",
|
|
||||||
"import:default",
|
|
||||||
"jre:default",
|
|
||||||
"logs:default",
|
|
||||||
"metadata:default",
|
|
||||||
"mr-auth:default",
|
|
||||||
"profile-create:default",
|
|
||||||
"pack:default",
|
|
||||||
"process:default",
|
|
||||||
"profile:default",
|
|
||||||
"cache:default",
|
|
||||||
"settings:default",
|
|
||||||
"tags:default",
|
|
||||||
"utils:default",
|
|
||||||
"ads:default"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"identifier": "updater",
|
|
||||||
"description": "",
|
|
||||||
"local": true,
|
|
||||||
"windows": [
|
|
||||||
"main"
|
|
||||||
],
|
|
||||||
"permissions": [
|
|
||||||
"updater:default"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
{"ads":{"identifier":"ads","description":"","remote":{"urls":["https://modrinth.com/*","http://localhost:3000/*"]},"local":false,"webviews":["ads-window"],"permissions":["ads:default"]},"core":{"identifier":"core","description":"","local":true,"windows":["main"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","core:window:allow-create","core:window:allow-maximize","core:window:allow-toggle-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-start-dragging","core:webview:allow-set-webview-zoom"]},"plugins":{"identifier":"plugins","description":"","local":true,"windows":["main"],"permissions":["dialog:allow-open","dialog:allow-confirm","shell:allow-open","os:allow-platform","os:allow-version","os:allow-os-type","os:allow-family","os:allow-arch","os:allow-exe-extension","os:allow-locale","os:allow-hostname","deep-link:default","window-state:default","window-state:allow-restore-state","window-state:allow-save-window-state","auth:default","import:default","jre:default","logs:default","metadata:default","mr-auth:default","profile-create:default","pack:default","process:default","profile:default","cache:default","settings:default","tags:default","utils:default","ads:default"]},"updater":{"identifier":"updater","description":"","local":true,"windows":["main"],"permissions":["updater:default"]}}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
346
apps/app/msi/main.wxs
Normal file
346
apps/app/msi/main.wxs
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
<?if $(sys.BUILDARCH)="x86"?>
|
||||||
|
<?define Win64 = "no" ?>
|
||||||
|
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
|
||||||
|
<?elseif $(sys.BUILDARCH)="x64"?>
|
||||||
|
<?define Win64 = "yes" ?>
|
||||||
|
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
|
||||||
|
<?else?>
|
||||||
|
<?error Unsupported value of sys.BUILDARCH=$(sys.BUILDARCH)?>
|
||||||
|
<?endif?>
|
||||||
|
|
||||||
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
|
<Product
|
||||||
|
Id="*"
|
||||||
|
Name="{{product_name}}"
|
||||||
|
UpgradeCode="{{upgrade_code}}"
|
||||||
|
Language="!(loc.TauriLanguage)"
|
||||||
|
Manufacturer="{{manufacturer}}"
|
||||||
|
Version="{{version}}">
|
||||||
|
|
||||||
|
<Package Id="*"
|
||||||
|
Keywords="Installer"
|
||||||
|
InstallerVersion="450"
|
||||||
|
Languages="0"
|
||||||
|
Compressed="yes"
|
||||||
|
InstallScope="perMachine"
|
||||||
|
SummaryCodepage="!(loc.TauriCodepage)"/>
|
||||||
|
|
||||||
|
<!-- https://docs.microsoft.com/en-us/windows/win32/msi/reinstallmode -->
|
||||||
|
<!-- reinstall all files; rewrite all registry entries; reinstall all shortcuts -->
|
||||||
|
<Property Id="REINSTALLMODE" Value="amus" />
|
||||||
|
|
||||||
|
{{#if allow_downgrades}}
|
||||||
|
<MajorUpgrade Schedule="afterInstallInitialize" AllowDowngrades="yes" />
|
||||||
|
{{else}}
|
||||||
|
<MajorUpgrade Schedule="afterInstallInitialize" DowngradeErrorMessage="!(loc.DowngradeErrorMessage)" AllowSameVersionUpgrades="yes" />
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<RemoveShortcuts>Installed AND NOT UPGRADINGPRODUCTCODE</RemoveShortcuts>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
|
<Media Id="1" Cabinet="app.cab" EmbedCab="yes" />
|
||||||
|
|
||||||
|
{{#if banner_path}}
|
||||||
|
<WixVariable Id="WixUIBannerBmp" Value="{{banner_path}}" />
|
||||||
|
{{/if}}
|
||||||
|
{{#if dialog_image_path}}
|
||||||
|
<WixVariable Id="WixUIDialogBmp" Value="{{dialog_image_path}}" />
|
||||||
|
{{/if}}
|
||||||
|
{{#if license}}
|
||||||
|
<WixVariable Id="WixUILicenseRtf" Value="{{license}}" />
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<Icon Id="ProductIcon" SourceFile="{{icon_path}}"/>
|
||||||
|
<Property Id="ARPPRODUCTICON" Value="ProductIcon" />
|
||||||
|
<Property Id="ARPNOREPAIR" Value="yes" Secure="yes" /> <!-- Remove repair -->
|
||||||
|
<SetProperty Id="ARPNOMODIFY" Value="1" After="InstallValidate" Sequence="execute"/>
|
||||||
|
|
||||||
|
<!-- initialize with previous InstallDir -->
|
||||||
|
<Property Id="INSTALLDIR">
|
||||||
|
<RegistrySearch Id="PrevInstallDirReg" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="InstallDir" Type="raw"/>
|
||||||
|
</Property>
|
||||||
|
|
||||||
|
<!-- launch app checkbox -->
|
||||||
|
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="!(loc.LaunchApp)" />
|
||||||
|
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1"/>
|
||||||
|
<Property Id="WixShellExecTarget" Value="[!Path]" />
|
||||||
|
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
|
||||||
|
|
||||||
|
<UI>
|
||||||
|
<!-- launch app checkbox -->
|
||||||
|
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
|
||||||
|
|
||||||
|
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
|
||||||
|
|
||||||
|
{{#unless license}}
|
||||||
|
<!-- Skip license dialog -->
|
||||||
|
<Publish Dialog="WelcomeDlg"
|
||||||
|
Control="Next"
|
||||||
|
Event="NewDialog"
|
||||||
|
Value="InstallDirDlg"
|
||||||
|
Order="2">1</Publish>
|
||||||
|
<Publish Dialog="InstallDirDlg"
|
||||||
|
Control="Back"
|
||||||
|
Event="NewDialog"
|
||||||
|
Value="WelcomeDlg"
|
||||||
|
Order="2">1</Publish>
|
||||||
|
{{/unless}}
|
||||||
|
</UI>
|
||||||
|
|
||||||
|
<UIRef Id="WixUI_InstallDir" />
|
||||||
|
|
||||||
|
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||||
|
<Directory Id="DesktopFolder" Name="Desktop">
|
||||||
|
<Component Id="ApplicationShortcutDesktop" Guid="*">
|
||||||
|
<Shortcut Id="ApplicationDesktopShortcut" Name="{{product_name}}" Description="Runs {{product_name}}" Target="[!Path]" WorkingDirectory="INSTALLDIR" />
|
||||||
|
<RemoveFolder Id="DesktopFolder" On="uninstall" />
|
||||||
|
<RegistryValue Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="Desktop Shortcut" Type="integer" Value="1" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
<Directory Id="$(var.PlatformProgramFilesFolder)" Name="PFiles">
|
||||||
|
<Directory Id="INSTALLDIR" Name="{{product_name}}"/>
|
||||||
|
</Directory>
|
||||||
|
<Directory Id="ProgramMenuFolder">
|
||||||
|
<Directory Id="ApplicationProgramsFolder" Name="{{product_name}}"/>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<DirectoryRef Id="INSTALLDIR">
|
||||||
|
<Component Id="RegistryEntries" Guid="*">
|
||||||
|
<RegistryKey Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}">
|
||||||
|
<RegistryValue Name="InstallDir" Type="string" Value="[INSTALLDIR]" KeyPath="yes" />
|
||||||
|
</RegistryKey>
|
||||||
|
</Component>
|
||||||
|
|
||||||
|
<Component Id="Path" Guid="{{path_component_guid}}" Win64="$(var.Win64)">
|
||||||
|
<File Id="Path" Source="{{app_exe_source}}" KeyPath="yes" Checksum="yes"/>
|
||||||
|
<!-- THESEUS -->
|
||||||
|
<ProgId Id="theseus.mrpack.Document" Description="Modrinth File">
|
||||||
|
<Extension Id="mrpack" ContentType="application/mrpack">
|
||||||
|
<!-- no flags on argument, so we can hijack deep link library-->
|
||||||
|
<Verb Id="open" Command="Open" TargetFile="Path" Argument=""%1"" />
|
||||||
|
</Extension>
|
||||||
|
</ProgId>
|
||||||
|
<!-- /THESEUS -->
|
||||||
|
</Component>
|
||||||
|
{{#each binaries as |bin| ~}}
|
||||||
|
<Component Id="{{ bin.id }}" Guid="{{bin.guid}}" Win64="$(var.Win64)">
|
||||||
|
<File Id="Bin_{{ bin.id }}" Source="{{bin.path}}" KeyPath="yes"/>
|
||||||
|
</Component>
|
||||||
|
{{/each~}}
|
||||||
|
{{#if enable_elevated_update_task}}
|
||||||
|
<Component Id="UpdateTask" Guid="C492327D-9720-4CD5-8DB8-F09082AF44BE" Win64="$(var.Win64)">
|
||||||
|
<File Id="UpdateTask" Source="update.xml" KeyPath="yes" Checksum="yes"/>
|
||||||
|
</Component>
|
||||||
|
<Component Id="UpdateTaskInstaller" Guid="011F25ED-9BE3-50A7-9E9B-3519ED2B9932" Win64="$(var.Win64)">
|
||||||
|
<File Id="UpdateTaskInstaller" Source="install-task.ps1" KeyPath="yes" Checksum="yes"/>
|
||||||
|
</Component>
|
||||||
|
<Component Id="UpdateTaskUninstaller" Guid="D4F6CC3F-32DC-5FD0-95E8-782FFD7BBCE1" Win64="$(var.Win64)">
|
||||||
|
<File Id="UpdateTaskUninstaller" Source="uninstall-task.ps1" KeyPath="yes" Checksum="yes"/>
|
||||||
|
</Component>
|
||||||
|
{{/if}}
|
||||||
|
{{resources}}
|
||||||
|
<Component Id="CMP_UninstallShortcut" Guid="*">
|
||||||
|
|
||||||
|
<Shortcut Id="UninstallShortcut"
|
||||||
|
Name="Uninstall {{product_name}}"
|
||||||
|
Description="Uninstalls {{product_name}}"
|
||||||
|
Target="[System64Folder]msiexec.exe"
|
||||||
|
Arguments="/x [ProductCode]" />
|
||||||
|
|
||||||
|
<RemoveFolder Id="INSTALLDIR"
|
||||||
|
On="uninstall" />
|
||||||
|
|
||||||
|
<RegistryValue Root="HKCU"
|
||||||
|
Key="Software\\{{manufacturer}}\\{{product_name}}"
|
||||||
|
Name="Uninstaller Shortcut"
|
||||||
|
Type="integer"
|
||||||
|
Value="1"
|
||||||
|
KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
|
||||||
|
<!-- THESEUS -->
|
||||||
|
<Component Id="FileTypeAssociationsReg" Guid="*">
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities" Name="ApplicationDescription" Value="theseus" Type="string" />
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities" Name="ApplicationIcon" Value="[INSTALLDIR]theseus,0" Type="string" />
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities" Name="ApplicationName" Value="theseus" Type="string" />
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities\DefaultIcon" Value="[INSTALLDIR]theseus,1" Type="string" />
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities\FileAssociations" Name=".mrpack" Value="theseus.mrpack.Document" Type="string" />
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities\MIMEAssociations" Name="application/mrpack" Value="theseus.mrpack.Document" Type="string" />
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\modrinth\theseus\Capabilities\shell\Open\command" Value=""[INSTALLDIR]theseus" -e "%1"" Type="string" />
|
||||||
|
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\RegisteredApplications" Name="theseus" Value="SOFTWARE\modrinth\theseus\Capabilities" Type="string" KeyPath="yes" />
|
||||||
|
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\theseus.mrpack.Document" Name="MRPACK File" Value="Modrinth Modpack Installer" Type="string" />
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\.mrpack" Name="Content Type" Value="application/mrpack" Type="string" />
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\.mrpack\OpenWithList\theseus" Value="" Type="string" />
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\.mrpack\OpenWithProgids" Name="theseus.mrpack.Document" Value="" Type="string" />
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\Applications\mrpack\SupportedTypes" Name=".mrpack" Value="" Type="string" />
|
||||||
|
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\Applications\mrpack\shell\open" Name="FriendlyAppName" Value="theseus" Type="string" />
|
||||||
|
</Component>
|
||||||
|
<!-- /THESEUS -->
|
||||||
|
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
|
<DirectoryRef Id="ApplicationProgramsFolder">
|
||||||
|
<Component Id="ApplicationShortcut" Guid="*">
|
||||||
|
<Shortcut Id="ApplicationStartMenuShortcut"
|
||||||
|
Name="{{product_name}}"
|
||||||
|
Description="Runs {{product_name}}"
|
||||||
|
Target="[!Path]"
|
||||||
|
Icon="ProductIcon"
|
||||||
|
WorkingDirectory="INSTALLDIR">
|
||||||
|
<ShortcutProperty Key="System.AppUserModel.ID" Value="{{bundle_id}}"/>
|
||||||
|
</Shortcut>
|
||||||
|
<RemoveFolder Id="ApplicationProgramsFolder" On="uninstall"/>
|
||||||
|
<RegistryValue Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="Start Menu Shortcut" Type="integer" Value="1" KeyPath="yes"/>
|
||||||
|
</Component>
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
|
{{#each merge_modules as |msm| ~}}
|
||||||
|
<DirectoryRef Id="TARGETDIR">
|
||||||
|
<Merge Id="{{ msm.name }}" SourceFile="{{ msm.path }}" DiskId="1" Language="!(loc.TauriLanguage)" />
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
|
<Feature Id="{{ msm.name }}" Title="{{ msm.name }}" AllowAdvertise="no" Display="hidden" Level="1">
|
||||||
|
<MergeRef Id="{{ msm.name }}"/>
|
||||||
|
</Feature>
|
||||||
|
{{/each~}}
|
||||||
|
|
||||||
|
<Feature
|
||||||
|
Id="MainProgram"
|
||||||
|
Title="Application"
|
||||||
|
Description="!(loc.InstallAppFeature)"
|
||||||
|
Level="1"
|
||||||
|
ConfigurableDirectory="INSTALLDIR"
|
||||||
|
AllowAdvertise="no"
|
||||||
|
Display="expand"
|
||||||
|
Absent="disallow">
|
||||||
|
|
||||||
|
<ComponentRef Id="RegistryEntries"/>
|
||||||
|
|
||||||
|
<!-- THESEUS -->
|
||||||
|
<ComponentRef Id="FileTypeAssociationsReg" />
|
||||||
|
<!-- /THESEUS -->
|
||||||
|
|
||||||
|
{{#each resource_file_ids as |resource_file_id| ~}}
|
||||||
|
<ComponentRef Id="{{ resource_file_id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
|
||||||
|
{{#if enable_elevated_update_task}}
|
||||||
|
<ComponentRef Id="UpdateTask" />
|
||||||
|
<ComponentRef Id="UpdateTaskInstaller" />
|
||||||
|
<ComponentRef Id="UpdateTaskUninstaller" />
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<Feature Id="ShortcutsFeature"
|
||||||
|
Title="Shortcuts"
|
||||||
|
Level="1">
|
||||||
|
<ComponentRef Id="Path"/>
|
||||||
|
<ComponentRef Id="CMP_UninstallShortcut" />
|
||||||
|
<ComponentRef Id="ApplicationShortcut" />
|
||||||
|
<ComponentRef Id="ApplicationShortcutDesktop" />
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
<Feature
|
||||||
|
Id="Environment"
|
||||||
|
Title="PATH Environment Variable"
|
||||||
|
Description="!(loc.PathEnvVarFeature)"
|
||||||
|
Level="1"
|
||||||
|
Absent="allow">
|
||||||
|
<ComponentRef Id="Path"/>
|
||||||
|
{{#each binaries as |bin| ~}}
|
||||||
|
<ComponentRef Id="{{ bin.id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
</Feature>
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
<Feature Id="External" AllowAdvertise="no" Absent="disallow">
|
||||||
|
{{#each component_group_refs as |id| ~}}
|
||||||
|
<ComponentGroupRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
{{#each component_refs as |id| ~}}
|
||||||
|
<ComponentRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
{{#each feature_group_refs as |id| ~}}
|
||||||
|
<FeatureGroupRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
{{#each feature_refs as |id| ~}}
|
||||||
|
<FeatureRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
{{#each merge_refs as |id| ~}}
|
||||||
|
<MergeRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
{{#if install_webview}}
|
||||||
|
<!-- WebView2 -->
|
||||||
|
<Property Id="WVRTINSTALLED">
|
||||||
|
<RegistrySearch Id="WVRTInstalledSystem" Root="HKLM" Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Name="pv" Type="raw" Win64="no" />
|
||||||
|
<RegistrySearch Id="WVRTInstalledUser" Root="HKCU" Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Name="pv" Type="raw"/>
|
||||||
|
</Property>
|
||||||
|
|
||||||
|
{{#if download_bootstrapper}}
|
||||||
|
<CustomAction Id='DownloadAndInvokeBootstrapper' Directory="INSTALLDIR" Execute="deferred" ExeCommand='powershell.exe -NoProfile -windowstyle hidden try [\{] [\[]Net.ServicePointManager[\]]::SecurityProtocol = [\[]Net.SecurityProtocolType[\]]::Tls12 [\}] catch [\{][\}]; Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -OutFile "$env:TEMP\MicrosoftEdgeWebview2Setup.exe" ; Start-Process -FilePath "$env:TEMP\MicrosoftEdgeWebview2Setup.exe" -ArgumentList ({{webview_installer_args}} '/install') -Wait' Return='check'/>
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action='DownloadAndInvokeBootstrapper' Before='InstallFinalize'>
|
||||||
|
<![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<!-- Embedded webview bootstrapper mode -->
|
||||||
|
{{#if webview2_bootstrapper_path}}
|
||||||
|
<Binary Id="MicrosoftEdgeWebview2Setup.exe" SourceFile="{{webview2_bootstrapper_path}}"/>
|
||||||
|
<CustomAction Id='InvokeBootstrapper' BinaryKey='MicrosoftEdgeWebview2Setup.exe' Execute="deferred" ExeCommand='{{webview_installer_args}} /install' Return='check' />
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action='InvokeBootstrapper' Before='InstallFinalize'>
|
||||||
|
<![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<!-- Embedded offline installer -->
|
||||||
|
{{#if webview2_installer_path}}
|
||||||
|
<Binary Id="MicrosoftEdgeWebView2RuntimeInstaller.exe" SourceFile="{{webview2_installer_path}}"/>
|
||||||
|
<CustomAction Id='InvokeStandalone' BinaryKey='MicrosoftEdgeWebView2RuntimeInstaller.exe' Execute="deferred" ExeCommand='{{webview_installer_args}} /install' Return='check' />
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action='InvokeStandalone' Before='InstallFinalize'>
|
||||||
|
<![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if enable_elevated_update_task}}
|
||||||
|
<!-- Install an elevated update task within Windows Task Scheduler -->
|
||||||
|
<CustomAction
|
||||||
|
Id="CreateUpdateTask"
|
||||||
|
Return="check"
|
||||||
|
Directory="INSTALLDIR"
|
||||||
|
Execute="commit"
|
||||||
|
Impersonate="yes"
|
||||||
|
ExeCommand="powershell.exe -WindowStyle hidden .\install-task.ps1" />
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action='CreateUpdateTask' Before='InstallFinalize'>
|
||||||
|
NOT(REMOVE)
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
<!-- Remove elevated update task during uninstall -->
|
||||||
|
<CustomAction
|
||||||
|
Id="DeleteUpdateTask"
|
||||||
|
Return="check"
|
||||||
|
Directory="INSTALLDIR"
|
||||||
|
ExeCommand="powershell.exe -WindowStyle hidden .\uninstall-task.ps1" />
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action="DeleteUpdateTask" Before='InstallFinalize'>
|
||||||
|
(REMOVE = "ALL") AND NOT UPGRADINGPRODUCTCODE
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize"/>
|
||||||
|
</Product>
|
||||||
|
</Wix>
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
!macro NSIS_HOOK_POSTINSTALL
|
|
||||||
SetShellVarContext current
|
|
||||||
|
|
||||||
IfFileExists "$LOCALAPPDATA${PRODUCTNAME}\theseus_gui.exe" file_found file_not_found
|
|
||||||
file_found:
|
|
||||||
Delete "$LOCALAPPDATA${PRODUCTNAME}\theseus_gui.exe"
|
|
||||||
|
|
||||||
Delete "$LOCALAPPDATA${PRODUCTNAME}\uninstall.exe"
|
|
||||||
RMDir "$LOCALAPPDATA${PRODUCTNAME}"
|
|
||||||
|
|
||||||
!insertmacro DeleteAppUserModelId
|
|
||||||
|
|
||||||
; Remove start menu shortcut
|
|
||||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $AppStartMenuFolder
|
|
||||||
!insertmacro IsShortcutTarget "$SMPROGRAMS$AppStartMenuFolder${PRODUCTNAME}.lnk" "$LOCALAPPDATA${PRODUCTNAME}\theseus_gui.exe"
|
|
||||||
Pop $0
|
|
||||||
${If} $0 = 1
|
|
||||||
!insertmacro UnpinShortcut "$SMPROGRAMS$AppStartMenuFolder${PRODUCTNAME}.lnk"
|
|
||||||
Delete "$SMPROGRAMS$AppStartMenuFolder${PRODUCTNAME}.lnk"
|
|
||||||
RMDir "$SMPROGRAMS$AppStartMenuFolder"
|
|
||||||
${EndIf}
|
|
||||||
!insertmacro IsShortcutTarget "$SMPROGRAMS${PRODUCTNAME}.lnk" "$LOCALAPPDATA${PRODUCTNAME}\theseus_gui.exe"
|
|
||||||
Pop $0
|
|
||||||
${If} $0 = 1
|
|
||||||
!insertmacro UnpinShortcut "$SMPROGRAMS${PRODUCTNAME}.lnk"
|
|
||||||
Delete "$SMPROGRAMS${PRODUCTNAME}.lnk"
|
|
||||||
${EndIf}
|
|
||||||
|
|
||||||
!insertmacro IsShortcutTarget "$DESKTOP${PRODUCTNAME}.lnk" "$LOCALAPPDATA${PRODUCTNAME}\theseus_gui.exe"
|
|
||||||
Pop $0
|
|
||||||
${If} $0 = 1
|
|
||||||
!insertmacro UnpinShortcut "$DESKTOP${PRODUCTNAME}.lnk"
|
|
||||||
Delete "$DESKTOP${PRODUCTNAME}.lnk"
|
|
||||||
${EndIf}
|
|
||||||
|
|
||||||
DeleteRegKey HKCU "${UNINSTKEY}"
|
|
||||||
|
|
||||||
goto end_of_test ;<== important for not continuing on the else branch
|
|
||||||
file_not_found:
|
|
||||||
end_of_test:
|
|
||||||
!macroend
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user