diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 831d042..d16204f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -15,7 +15,66 @@ on:
workflow_dispatch:
jobs:
+ check-skip:
+ runs-on: ubuntu-latest
+ outputs:
+ skip: ${{ steps.check.outputs.skip }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Check commit messages for skip keywords
+ id: check
+ shell: bash
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ set -euo pipefail
+ keywords='\[skip ci\]|\[ci skip\]|skip-ci|skip_ci|SKIP_CI|NO_CI'
+
+ skip_found=false
+
+ if [ -n "${GITHUB_SHA-}" ] && [ -n "${GITHUB_BASE_REF-}" ] || true; then
+ git fetch --no-tags --depth=50 origin +refs/heads/*:refs/remotes/origin/* || true
+ if [ -n "${{ github.event.before || '' }}" ] && [ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]; then
+ commit_range="${{ github.event.before }}..${{ github.event.after }}"
+ else
+ commit_range="${GITHUB_SHA}"
+ fi
+ messages=$(git log --pretty=%B $commit_range 2>/dev/null || true)
+ else
+ messages=""
+ fi
+
+ if [ -z "$(echo "$messages" | tr -d '[:space:]')" ]; then
+ messages="$(jq -r '.commits[].message' < "$GITHUB_EVENT_PATH" 2>/dev/null || true)"
+ fi
+
+ echo "---- Collected commit messages ----"
+ if [ -n "$(echo "$messages" | tr -d '[:space:]')" ]; then
+ printf "%s\n" "$messages"
+ else
+ echo ""
+ fi
+ echo "---- End commit messages ----"
+
+ echo "---- Matching lines (case-insensitive) ----"
+ # Print matching lines with context to aid debugging
+ echo "$messages" | nl -ba | grep -Ei --color=never "$keywords" || true
+ echo "---- End matching lines ----"
+
+ if echo "$messages" | grep -Ei "$keywords" >/dev/null; then
+ echo "Found skip keyword in commit message. Cancelling run."
+ skip_found=true
+ else
+ echo "No skip keyword found in commits. Continuing run."
+ fi
+
+ echo "skip=$skip_found" >> $GITHUB_OUTPUT
+
build:
+ needs: check-skip
+ if: needs.check-skip.outputs.skip != 'true'
strategy:
fail-fast: false
matrix:
@@ -112,6 +171,7 @@ jobs:
shell: pwsh
run: |
mkdir -p artifacts-windows
+ $version = "${{ steps.package-version-windows.outputs.current-version }}"
Get-ChildItem -Path ./src-tauri/target/release/bundle/msi/*.msi -Recurse | ForEach-Object {
Copy-Item $_.FullName -Destination artifacts-windows/
@@ -126,7 +186,11 @@ jobs:
}
Get-ChildItem -Path ./src-tauri/target/release/*.exe | ForEach-Object {
- Copy-Item $_.FullName -Destination artifacts-windows/
+ $baseName = [System.IO.Path]::GetFileNameWithoutExtension($_.Name)
+ $extension = [System.IO.Path]::GetExtension($_.Name)
+ $newName = "${baseName}_${version}${extension}"
+ $newPath = Join-Path "artifacts-windows" $newName
+ Copy-Item $_.FullName -Destination $newPath
$md5 = Get-FileHash $_.FullName -Algorithm MD5
echo "EXE_MD5=$($md5.Hash)" >> $env:GITHUB_ENV
}
@@ -137,6 +201,7 @@ jobs:
if: matrix.platform == 'ubuntu-latest'
run: |
mkdir -p artifacts-ubuntu
+ version="${{ steps.package-version-ubuntu.outputs.current-version }}"
find ./src-tauri/target/release/bundle/deb -name "*.deb" | while read file; do
cp "$file" artifacts-ubuntu/
@@ -150,6 +215,22 @@ jobs:
echo "APPIMAGE_MD5=$MD5" >> $GITHUB_ENV
done
+ # Include the standalone Linux binary with version
+ find ./src-tauri/target/release -maxdepth 1 -type f -executable -not -name "*.so" -not -name "*.d" | while read file; do
+ if [ -f "$file" ] && file "$file" | grep -q "ELF.*executable"; then
+ basename=$(basename "$file")
+ extension=""
+ if [[ "$basename" == *.* ]]; then
+ extension=".${basename##*.}"
+ basename="${basename%.*}"
+ fi
+ versioned_name="${basename}_${version}${extension}"
+ cp "$file" "artifacts-ubuntu/$versioned_name"
+ MD5=$(md5sum "$file" | awk '{ print $1 }')
+ echo "BINARY_MD5=$MD5" >> $GITHUB_ENV
+ fi
+ done
+
echo "ARTIFACT_PATH=artifacts-ubuntu" >> $GITHUB_ENV
- name: VirusTotal Scan (Windows only)
@@ -168,7 +249,6 @@ jobs:
shell: bash
run: |
ANALYSIS="${{ steps.virustotal.outputs.analysis }}"
- # Extract any VirusTotal GUI URLs from the analysis output; handles comma-separated entries
urls=$(echo "$ANALYSIS" | tr ',' '\n' | grep -oE 'https?://[^[:space:]]*virustotal[^[:space:]]*' | sed 's/[[:space:]]*$//' | uniq)
if [ -n "$urls" ]; then
printf 'VIRUSTOTAL_URL<> $GITHUB_ENV
@@ -178,13 +258,6 @@ jobs:
echo "VIRUSTOTAL_LINE=" >> $GITHUB_ENV
fi
- - name: Write VirusTotal URL into artifacts (Windows only)
- if: matrix.platform == 'windows-latest' && env.VIRUSTOTAL_URL != ''
- shell: pwsh
- run: |
- if (!(Test-Path -Path artifacts-windows)) { New-Item -ItemType Directory -Path artifacts-windows | Out-Null }
- Set-Content -Path artifacts-windows/virustotal-url.txt -Value $env:VIRUSTOTAL_URL -Encoding utf8
-
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
@@ -192,7 +265,8 @@ jobs:
path: ${{ matrix.platform == 'windows-latest' && 'artifacts-windows/**' || 'artifacts-ubuntu/**' }}
create-release:
- needs: build
+ needs: [check-skip, build]
+ if: needs.check-skip.outputs.skip != 'true'
runs-on: ubuntu-latest
permissions:
contents: write
@@ -224,29 +298,6 @@ jobs:
- name: List artifacts
run: find release-artifacts -type f
- - name: Extract VirusTotal URL (if present)
- id: virustotal-url
- run: |
- file=$(find release-artifacts -type f -name 'virustotal-url.txt' | head -n1 || true)
- if [ -n "$file" ] && [ -f "$file" ]; then
- # Read all lines and create a bullet list
- urls=$(sed 's/\r$//' "$file" | sed '/^[[:space:]]*$/d')
- if [ -n "$urls" ]; then
- # prefix each line with '- ' for bullets
- bullets=$(echo "$urls" | sed 's/^/- /')
- printf 'VIRUSTOTAL_URL<> $GITHUB_ENV
- printf 'VIRUSTOTAL_LINE<> $GITHUB_ENV
- echo "url=$urls" >> $GITHUB_OUTPUT
- else
- echo "VIRUSTOTAL_LINE=" >> $GITHUB_ENV
- echo "url=" >> $GITHUB_OUTPUT
- fi
- else
- echo "VIRUSTOTAL_LINE=" >> $GITHUB_ENV
- echo "url=" >> $GITHUB_OUTPUT
- fi
- shell: bash
-
- name: Create Release
uses: softprops/action-gh-release@v1
env:
@@ -259,8 +310,8 @@ jobs:
Automatic build from GitHub Actions
- - Windows (.msi, .exe installer, standalone .exe)
- - Linux (.deb)
+ - Windows (.msi, .exe installer, standalone .exe with version)
+ - Linux (.deb, standalone binary with version)
Note: This is an automated build from the main branch.
Commit hash: ${{ env.HASH }}
diff --git a/package-lock.json b/package-lock.json
index a01902b..feb95d1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,33 +8,33 @@
"name": "collapseloader",
"version": "0.2.0",
"dependencies": {
- "@guolao/vue-monaco-editor": "^1.5.5",
+ "@guolao/vue-monaco-editor": "1.5.5",
"@tauri-apps/api": "2.8.0",
- "@tauri-apps/plugin-dialog": "^2.4.0",
- "@tauri-apps/plugin-fs": "^2.4.2",
+ "@tauri-apps/plugin-dialog": "2.4.0",
+ "@tauri-apps/plugin-fs": "2.4.2",
"@tauri-apps/plugin-notification": "2.3.1",
"@tauri-apps/plugin-opener": "2.5.0",
- "axios": "1.12.1",
- "daisyui": "5.1.10",
+ "axios": "1.12.2",
+ "daisyui": "5.1.13",
"gsap": "3.13.0",
"lucide-vue-next": "0.544.0",
- "monaco-editor": "^0.53.0",
+ "monaco-editor": "0.53.0",
"vue": "3.5.21",
- "vue-i18n": "^11.1.12",
+ "vue-i18n": "11.1.12",
"vue3-lottie": "3.3.1"
},
"devDependencies": {
"@tailwindcss/vite": "4.1.13",
"@tauri-apps/cli": "2.8.4",
- "@types/node": "24.3.2",
- "@typescript-eslint/eslint-plugin": "^8.43.0",
- "@typescript-eslint/parser": "^8.43.0",
+ "@types/node": "24.5.2",
+ "@typescript-eslint/eslint-plugin": "8.44.0",
+ "@typescript-eslint/parser": "8.44.0",
"@vitejs/plugin-vue": "6.0.1",
- "eslint": "^9.35.0",
- "eslint-plugin-vue": "^10.4.0",
+ "eslint": "9.36.0",
+ "eslint-plugin-vue": "10.4.0",
"tailwindcss": "4.1.13",
"typescript": "5.9.2",
- "vite": "7.1.5",
+ "vite": "7.1.6",
"vue-tsc": "3.0.7"
}
},
@@ -676,9 +676,9 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.35.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz",
- "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==",
+ "version": "9.36.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz",
+ "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1819,13 +1819,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "24.3.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.2.tgz",
- "integrity": "sha512-6L8PkB+m1SSb2kaGGFk3iXENxl8lrs7cyVl7AXH6pgdMfulDfM6yUrVdjtxdnGrLrGzzuav8fFnZMY+rcscqcA==",
+ "version": "24.5.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz",
+ "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "undici-types": "~7.10.0"
+ "undici-types": "~7.12.0"
}
},
"node_modules/@types/trusted-types": {
@@ -1835,17 +1835,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.43.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.43.0.tgz",
- "integrity": "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==",
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.0.tgz",
+ "integrity": "sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.43.0",
- "@typescript-eslint/type-utils": "8.43.0",
- "@typescript-eslint/utils": "8.43.0",
- "@typescript-eslint/visitor-keys": "8.43.0",
+ "@typescript-eslint/scope-manager": "8.44.0",
+ "@typescript-eslint/type-utils": "8.44.0",
+ "@typescript-eslint/utils": "8.44.0",
+ "@typescript-eslint/visitor-keys": "8.44.0",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
@@ -1859,22 +1859,22 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.43.0",
+ "@typescript-eslint/parser": "^8.44.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.43.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.43.0.tgz",
- "integrity": "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==",
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.0.tgz",
+ "integrity": "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.43.0",
- "@typescript-eslint/types": "8.43.0",
- "@typescript-eslint/typescript-estree": "8.43.0",
- "@typescript-eslint/visitor-keys": "8.43.0",
+ "@typescript-eslint/scope-manager": "8.44.0",
+ "@typescript-eslint/types": "8.44.0",
+ "@typescript-eslint/typescript-estree": "8.44.0",
+ "@typescript-eslint/visitor-keys": "8.44.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1890,14 +1890,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
- "version": "8.43.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.43.0.tgz",
- "integrity": "sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==",
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.0.tgz",
+ "integrity": "sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.43.0",
- "@typescript-eslint/types": "^8.43.0",
+ "@typescript-eslint/tsconfig-utils": "^8.44.0",
+ "@typescript-eslint/types": "^8.44.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1912,14 +1912,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.43.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.43.0.tgz",
- "integrity": "sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==",
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.0.tgz",
+ "integrity": "sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.43.0",
- "@typescript-eslint/visitor-keys": "8.43.0"
+ "@typescript-eslint/types": "8.44.0",
+ "@typescript-eslint/visitor-keys": "8.44.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1930,9 +1930,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.43.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.43.0.tgz",
- "integrity": "sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==",
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.0.tgz",
+ "integrity": "sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1947,15 +1947,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.43.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.43.0.tgz",
- "integrity": "sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg==",
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.0.tgz",
+ "integrity": "sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.43.0",
- "@typescript-eslint/typescript-estree": "8.43.0",
- "@typescript-eslint/utils": "8.43.0",
+ "@typescript-eslint/types": "8.44.0",
+ "@typescript-eslint/typescript-estree": "8.44.0",
+ "@typescript-eslint/utils": "8.44.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
@@ -1972,9 +1972,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.43.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.43.0.tgz",
- "integrity": "sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==",
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.0.tgz",
+ "integrity": "sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1986,16 +1986,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.43.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.43.0.tgz",
- "integrity": "sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==",
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.0.tgz",
+ "integrity": "sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.43.0",
- "@typescript-eslint/tsconfig-utils": "8.43.0",
- "@typescript-eslint/types": "8.43.0",
- "@typescript-eslint/visitor-keys": "8.43.0",
+ "@typescript-eslint/project-service": "8.44.0",
+ "@typescript-eslint/tsconfig-utils": "8.44.0",
+ "@typescript-eslint/types": "8.44.0",
+ "@typescript-eslint/visitor-keys": "8.44.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -2015,16 +2015,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.43.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.43.0.tgz",
- "integrity": "sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==",
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.0.tgz",
+ "integrity": "sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.43.0",
- "@typescript-eslint/types": "8.43.0",
- "@typescript-eslint/typescript-estree": "8.43.0"
+ "@typescript-eslint/scope-manager": "8.44.0",
+ "@typescript-eslint/types": "8.44.0",
+ "@typescript-eslint/typescript-estree": "8.44.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2039,13 +2039,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.43.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.43.0.tgz",
- "integrity": "sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==",
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.0.tgz",
+ "integrity": "sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.43.0",
+ "@typescript-eslint/types": "8.44.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -2334,9 +2334,9 @@
"license": "MIT"
},
"node_modules/axios": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.1.tgz",
- "integrity": "sha512-Kn4kbSXpkFHCGE6rBFNwIv0GQs4AvDT80jlveJDKFxjbTYMUeB4QtsdPCv6H8Cm19Je7IU6VFtRl2zWZI0rudQ==",
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
+ "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
@@ -2505,9 +2505,9 @@
"license": "MIT"
},
"node_modules/daisyui": {
- "version": "5.1.10",
- "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.1.10.tgz",
- "integrity": "sha512-p1J/HME2WmaSiy6u2alIbeP3gd5PNVft3+6Bdll0BRSm/UdI4084+pD01LxFug/5wGexNewWqbcEL6nB2n2o+Q==",
+ "version": "5.1.13",
+ "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.1.13.tgz",
+ "integrity": "sha512-KWPF/4R+EHTJRqKZFNmSDPfAZ5xeS6YWB/2kS7Y6wGKg+atscUi2DOp6HoDD/OgGML0PJTtTpgwpTfeHVfjk7w==",
"license": "MIT",
"funding": {
"url": "https://github.com/saadeghi/daisyui?sponsor=1"
@@ -2705,9 +2705,9 @@
}
},
"node_modules/eslint": {
- "version": "9.35.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz",
- "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==",
+ "version": "9.36.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz",
+ "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2717,7 +2717,7 @@
"@eslint/config-helpers": "^0.3.1",
"@eslint/core": "^0.15.2",
"@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.35.0",
+ "@eslint/js": "9.36.0",
"@eslint/plugin-kit": "^0.3.5",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
@@ -4402,9 +4402,9 @@
}
},
"node_modules/undici-types": {
- "version": "7.10.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
- "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
+ "version": "7.12.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz",
+ "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==",
"dev": true,
"license": "MIT"
},
@@ -4426,9 +4426,9 @@
"license": "MIT"
},
"node_modules/vite": {
- "version": "7.1.5",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz",
- "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==",
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.6.tgz",
+ "integrity": "sha512-SRYIB8t/isTwNn8vMB3MR6E+EQZM/WG1aKmmIUCfDXfVvKfc20ZpamngWHKzAmmu9ppsgxsg4b2I7c90JZudIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index babd8d4..859df47 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "collapseloader",
"private": true,
- "version": "0.2.0",
+ "version": "0.2.1",
"type": "module",
"scripts": {
"dev": "vite",
@@ -15,33 +15,33 @@
"lint:fix": "eslint --ext .ts,.tsx,.vue src --fix"
},
"dependencies": {
- "@guolao/vue-monaco-editor": "^1.5.5",
+ "@guolao/vue-monaco-editor": "1.5.5",
"@tauri-apps/api": "2.8.0",
- "@tauri-apps/plugin-dialog": "^2.4.0",
- "@tauri-apps/plugin-fs": "^2.4.2",
+ "@tauri-apps/plugin-dialog": "2.4.0",
+ "@tauri-apps/plugin-fs": "2.4.2",
"@tauri-apps/plugin-notification": "2.3.1",
"@tauri-apps/plugin-opener": "2.5.0",
- "axios": "1.12.1",
- "daisyui": "5.1.10",
+ "axios": "1.12.2",
+ "daisyui": "5.1.13",
"gsap": "3.13.0",
"lucide-vue-next": "0.544.0",
- "monaco-editor": "^0.53.0",
+ "monaco-editor": "0.53.0",
"vue": "3.5.21",
- "vue-i18n": "^11.1.12",
+ "vue-i18n": "11.1.12",
"vue3-lottie": "3.3.1"
},
"devDependencies": {
"@tailwindcss/vite": "4.1.13",
"@tauri-apps/cli": "2.8.4",
- "@types/node": "24.3.2",
- "@typescript-eslint/eslint-plugin": "^8.43.0",
- "@typescript-eslint/parser": "^8.43.0",
+ "@types/node": "24.5.2",
+ "@typescript-eslint/eslint-plugin": "8.44.0",
+ "@typescript-eslint/parser": "8.44.0",
"@vitejs/plugin-vue": "6.0.1",
- "eslint": "^9.35.0",
- "eslint-plugin-vue": "^10.4.0",
+ "eslint": "9.36.0",
+ "eslint-plugin-vue": "10.4.0",
"tailwindcss": "4.1.13",
"typescript": "5.9.2",
- "vite": "7.1.5",
+ "vite": "7.1.6",
"vue-tsc": "3.0.7"
}
}
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index b25cf7e..643788b 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -76,6 +76,12 @@ dependencies = [
"derive_arbitrary",
]
+[[package]]
+name = "ascii"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
+
[[package]]
name = "ashpd"
version = "0.11.0"
@@ -557,7 +563,7 @@ dependencies = [
[[package]]
name = "collapseloader"
-version = "0.2.0"
+version = "0.2.1"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -568,6 +574,7 @@ dependencies = [
"futures-util",
"lazy_static",
"md5",
+ "native-dialog",
"once_cell",
"open",
"opener",
@@ -584,9 +591,9 @@ dependencies = [
"tauri-plugin-fs",
"tauri-plugin-notification",
"tauri-plugin-opener",
+ "thiserror 2.0.16",
"tokio",
"uuid 1.18.1",
- "win-msgbox",
"winreg",
"zip",
]
@@ -1017,6 +1024,12 @@ version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
[[package]]
name = "embed-resource"
version = "3.0.5"
@@ -1073,6 +1086,12 @@ dependencies = [
"syn 2.0.106",
]
+[[package]]
+name = "env_home"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
+
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -1219,6 +1238,12 @@ dependencies = [
"percent-encoding",
]
+[[package]]
+name = "formatx"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8866fac38f53fc87fa3ae1b09ddd723e0482f8fa74323518b4c59df2c55a00a"
+
[[package]]
name = "futf"
version = "0.1.5"
@@ -2066,6 +2091,15 @@ dependencies = [
"once_cell",
]
+[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
[[package]]
name = "itoa"
version = "1.0.15"
@@ -2419,6 +2453,30 @@ dependencies = [
"windows-sys 0.60.2",
]
+[[package]]
+name = "native-dialog"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f006431cea71a83e6668378cb5abc2d52af299cbac6dca1780c6eeca90822df"
+dependencies = [
+ "ascii",
+ "block2 0.6.1",
+ "dirs",
+ "dispatch2",
+ "formatx",
+ "objc2 0.6.2",
+ "objc2-app-kit",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+ "objc2-foundation 0.3.1",
+ "raw-window-handle",
+ "thiserror 2.0.16",
+ "versions",
+ "wfd",
+ "which",
+ "winapi",
+]
+
[[package]]
name = "native-tls"
version = "0.2.14"
@@ -2491,6 +2549,15 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
+[[package]]
+name = "nom"
+version = "8.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "normpath"
version = "1.4.0"
@@ -2625,7 +2692,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
dependencies = [
"bitflags 2.9.4",
+ "block2 0.6.1",
"dispatch2",
+ "libc",
"objc2 0.6.2",
]
@@ -2636,10 +2705,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4"
dependencies = [
"bitflags 2.9.4",
+ "block2 0.6.1",
"dispatch2",
+ "libc",
"objc2 0.6.2",
"objc2-core-foundation",
"objc2-io-surface",
+ "objc2-metal 0.3.1",
]
[[package]]
@@ -2725,6 +2797,17 @@ dependencies = [
"objc2-foundation 0.2.2",
]
+[[package]]
+name = "objc2-metal"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f246c183239540aab1782457b35ab2040d4259175bd1d0c58e46ada7b47a874"
+dependencies = [
+ "bitflags 2.9.4",
+ "objc2 0.6.2",
+ "objc2-foundation 0.3.1",
+]
+
[[package]]
name = "objc2-quartz-core"
version = "0.2.2"
@@ -2735,7 +2818,7 @@ dependencies = [
"block2 0.5.1",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
- "objc2-metal",
+ "objc2-metal 0.2.2",
]
[[package]]
@@ -3778,19 +3861,21 @@ dependencies = [
[[package]]
name = "semver"
-version = "1.0.26"
+version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
+checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
dependencies = [
"serde",
+ "serde_core",
]
[[package]]
name = "serde"
-version = "1.0.219"
+version = "1.0.226"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd"
dependencies = [
+ "serde_core",
"serde_derive",
]
@@ -3805,11 +3890,20 @@ dependencies = [
"typeid",
]
+[[package]]
+name = "serde_core"
+version = "1.0.226"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4"
+dependencies = [
+ "serde_derive",
+]
+
[[package]]
name = "serde_derive"
-version = "1.0.219"
+version = "1.0.226"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33"
dependencies = [
"proc-macro2",
"quote",
@@ -3829,14 +3923,15 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.143"
+version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
+checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
+ "serde_core",
]
[[package]]
@@ -5111,6 +5206,16 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+[[package]]
+name = "versions"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80a7e511ce1795821207a837b7b1c8d8aca0c648810966ad200446ae58f6667f"
+dependencies = [
+ "itertools",
+ "nom",
+]
+
[[package]]
name = "vswhom"
version = "0.1.0"
@@ -5416,12 +5521,25 @@ dependencies = [
]
[[package]]
-name = "win-msgbox"
-version = "0.2.1"
+name = "wfd"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b293d082c6da3f8e9f58a9d2fb076a61737b8f752fb559550a0a864ccfa2bd8"
+checksum = "e713040b67aae5bf1a0ae3e1ebba8cc29ab2b90da9aa1bff6e09031a8a41d7a8"
dependencies = [
- "windows-sys 0.59.0",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "which"
+version = "7.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762"
+dependencies = [
+ "either",
+ "env_home",
+ "rustix",
+ "winsafe",
]
[[package]]
@@ -5897,6 +6015,12 @@ dependencies = [
"windows-sys 0.59.0",
]
+[[package]]
+name = "winsafe"
+version = "0.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
+
[[package]]
name = "wit-bindgen"
version = "0.45.1"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 6d14037..f153b7c 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "collapseloader"
-version = "0.2.0"
+version = "0.2.1"
description = "CollapseLoader"
authors = ["dest4590"]
edition = "2021"
@@ -17,13 +17,13 @@ tauri-build = { version = "2.4.1", features = [] }
tauri = { version = "2.8.5", features = [] }
tauri-plugin-opener = "2.5.0"
tauri-plugin-notification = "2.3.1"
-serde = { version = "1.0.219", features = ["derive"] }
-serde_json = "1.0.143"
+serde = { version = "1.0.226", features = ["derive"] }
+serde_json = "1.0.145"
colored = "3.0.0"
lazy_static = "1.5.0"
rand = "0.9.2"
reqwest = { version = "0.12.23", features = ["blocking", "json", "stream"] }
-semver = "1.0.26"
+semver = "1.0.27"
zip = "5.1.1"
tokio = { version = "1.47.1", features = ["macros"] }
roxmltree = "0.20.0"
@@ -41,10 +41,11 @@ tauri-plugin-dialog = "2.4.0"
dotenvy = "0.15.7"
once_cell = "1.21.3"
tauri-plugin-fs = "2.4.2"
+thiserror = "2.0.16"
+native-dialog = "0.9.0"
[target.'cfg(windows)'.dependencies]
winreg = { version = "0.55.0" }
-win-msgbox = { version = "0.2.1" }
[profile.release.package.wry]
debug = true
diff --git a/src-tauri/build.rs b/src-tauri/build.rs
index c3dc033..98600f7 100644
--- a/src-tauri/build.rs
+++ b/src-tauri/build.rs
@@ -22,7 +22,7 @@ fn force_println(msg: &str) {
}
}
-fn main() -> io::Result<()> {
+fn main() {
const NAME: &str = "CollapseBuilder";
fn banner() {
@@ -66,12 +66,8 @@ fn main() -> io::Result<()> {
banner();
- let development: bool = std::env::var("DEVELOPMENT")
- .map(|v| {
- let lv = v.to_ascii_lowercase();
- matches!(lv.as_str(), "1" | "true" | "yes" | "y" | "on")
- })
- .unwrap_or_else(|_| {
+ let development: bool = std::env::var("DEVELOPMENT").map_or_else(
+ |_| {
std::fs::read_to_string("../.env")
.map_err(|_| {})
.ok()
@@ -90,12 +86,16 @@ fn main() -> io::Result<()> {
}
})
})
- .map(|v| {
+ .is_some_and(|v| {
let lv = v.to_ascii_lowercase();
matches!(lv.as_str(), "1" | "true" | "yes" | "y" | "on")
})
- .unwrap_or(false)
- });
+ },
+ |v| {
+ let lv = v.to_ascii_lowercase();
+ matches!(lv.as_str(), "1" | "true" | "yes" | "y" | "on")
+ },
+ );
println!("cargo:rustc-env=DEVELOPMENT={development}");
println!("cargo:rerun-if-env-changed=DEVELOPMENT");
println!("cargo:rerun-if-changed=../.env");
@@ -128,5 +128,4 @@ fn main() -> io::Result<()> {
fence();
tauri_build::build();
- Ok(())
}
diff --git a/src-tauri/src/commands/analytics.rs b/src-tauri/src/commands/analytics.rs
deleted file mode 100644
index ff245b1..0000000
--- a/src-tauri/src/commands/analytics.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-use crate::core::network::analytics::Analytics;
-
-#[tauri::command]
-pub fn send_client_analytics(client_id: u32) -> Result<(), String> {
- Analytics::send_client_analytics(client_id);
- Ok(())
-}
diff --git a/src-tauri/src/commands/clients.rs b/src-tauri/src/commands/clients.rs
index 8283791..4a447b0 100644
--- a/src-tauri/src/commands/clients.rs
+++ b/src-tauri/src/commands/clients.rs
@@ -7,7 +7,10 @@ use tauri::AppHandle;
use crate::core::{
clients::custom_clients::{CustomClient, Version},
network::analytics::Analytics,
- storage::custom_clients::{CustomClientUpdate, CUSTOM_CLIENT_MANAGER},
+ storage::{
+ custom_clients::{CustomClientUpdate, CUSTOM_CLIENT_MANAGER},
+ data::Data,
+ },
};
use crate::core::{
clients::{client::LaunchOptions, internal::agent_overlay::AgentOverlayManager},
@@ -48,6 +51,7 @@ fn get_client_by_id(id: u32) -> Result {
#[tauri::command]
pub fn get_app_logs() -> Vec {
+ log_debug!("Retrieving application logs");
logging::APP_LOGS
.lock()
.map(|logs| logs.clone())
@@ -57,11 +61,13 @@ pub fn get_app_logs() -> Vec {
#[tauri::command]
pub async fn initialize_api() -> Result<(), String> {
+ log_info!("Initializing client manager via API");
initialize_client_manager().await
}
#[tauri::command]
pub fn initialize_rpc() -> Result<(), String> {
+ log_info!("Initializing Discord RPC");
if let Err(e) = discord_rpc::initialize() {
log_error!("Failed to initialize Discord RPC: {}", e);
}
@@ -70,6 +76,7 @@ pub fn initialize_rpc() -> Result<(), String> {
#[tauri::command]
pub fn get_server_connectivity_status() -> ServerConnectivityStatus {
+ log_debug!("Fetching server connectivity status");
let servers = &SERVERS;
servers.connectivity_status.lock().unwrap().clone()
}
@@ -89,7 +96,9 @@ pub async fn launch_client(
user_token: String,
app_handle: AppHandle,
) -> Result<(), String> {
+ log_info!("Attempting to launch client with ID: {}", id);
let client = get_client_by_id(id)?;
+ log_debug!("Found client '{}' for launch", client.name);
let filename_for_if = if client.filename.contains("fabric/") {
client.filename.replace("fabric/", "")
@@ -99,23 +108,32 @@ pub async fn launch_client(
client.filename.clone()
};
- let file_name = DATA.get_filename(&client.filename);
+ let file_name = Data::get_filename(&client.filename);
let jar_path = match client.client_type {
ClientType::Default => {
DATA.get_local(&format!("{file_name}{MAIN_SEPARATOR}{}", client.filename))
}
ClientType::Fabric => DATA.get_local(&format!(
- "{file_name}{MAIN_SEPARATOR}mods{MAIN_SEPARATOR}{}",
- filename_for_if
+ "{file_name}{MAIN_SEPARATOR}mods{MAIN_SEPARATOR}{filename_for_if}"
)),
};
if !jar_path.exists() {
+ log_warn!(
+ "Launch failed: Client '{}' is not installed at path: {}",
+ client.name,
+ jar_path.display()
+ );
return Err(format!(
"Client {} is not installed. Please download it first.",
client.name
));
}
+ log_debug!(
+ "Client '{}' found at path: {}",
+ client.name,
+ jar_path.display()
+ );
let hash_verify_enabled = {
let settings = SETTINGS
@@ -125,6 +143,7 @@ pub async fn launch_client(
};
if hash_verify_enabled {
+ log_info!("Hash verification is enabled for client '{}'", client.name);
emit_to_main_window(
&app_handle,
"client-hash-verification-start",
@@ -138,8 +157,22 @@ pub async fn launch_client(
"Verifying MD5 hash for client {} before launch",
client.name
);
- let current_hash = calculate_md5_hash(&jar_path)?;
- if current_hash != client.md5_hash {
+ let current_hash = Data::calculate_md5_hash(&jar_path)?;
+ if current_hash == client.md5_hash {
+ log_info!(
+ "MD5 hash verification successful for client {}",
+ client.name
+ );
+
+ emit_to_main_window(
+ &app_handle,
+ "client-hash-verification-done",
+ &serde_json::json!({
+ "id": id,
+ "name": client.name
+ }),
+ );
+ } else {
log_warn!(
"Hash mismatch for client {}. Expected: {}, Got: {}. Redownloading...",
client.name,
@@ -189,20 +222,6 @@ pub async fn launch_client(
"Client {} redownloaded and verified successfully",
client.name
);
- } else {
- log_info!(
- "MD5 hash verification successful for client {}",
- client.name
- );
-
- emit_to_main_window(
- &app_handle,
- "client-hash-verification-done",
- &serde_json::json!({
- "id": id,
- "name": client.name
- }),
- );
}
} else {
log_debug!(
@@ -211,6 +230,7 @@ pub async fn launch_client(
);
}
+ log_info!("Verifying agent and overlay files before launch");
match AgentOverlayManager::verify_agent_overlay_files().await {
Ok(true) => {
log_debug!("Agent and overlay files verified successfully");
@@ -228,6 +248,7 @@ pub async fn launch_client(
let options = LaunchOptions::new(app_handle.clone(), user_token.clone(), false);
+ log_info!("Executing client run for '{}'", client.name);
client.run(options).await
}
@@ -240,12 +261,17 @@ pub async fn get_running_client_ids() -> Vec {
.collect()
});
- handle.await.unwrap_or_else(|_| Vec::new())
+ handle.await.unwrap_or_else(|e| {
+ log_error!("Failed to get running client IDs: {}", e);
+ Vec::new()
+ })
}
#[tauri::command]
pub async fn stop_client(id: u32) -> Result<(), String> {
+ log_info!("Attempting to stop client with ID: {}", id);
let client = get_client_by_id(id)?;
+ log_debug!("Found client '{}' to stop", client.name);
let client_clone = client.clone();
let handle = tokio::task::spawn_blocking(move || client_clone.stop());
@@ -257,6 +283,7 @@ pub async fn stop_client(id: u32) -> Result<(), String> {
#[tauri::command]
pub fn get_client_logs(id: u32) -> Vec {
+ log_debug!("Fetching logs for client ID: {}", id);
CLIENT_LOGS
.lock()
.ok()
@@ -268,7 +295,7 @@ pub fn get_client_logs(id: u32) -> Vec {
pub async fn download_client_only(id: u32, app_handle: AppHandle) -> Result<(), String> {
let client = get_client_by_id(id)?;
- log_info!("Downloading client: {} (ID: {})", client.name, id);
+ log_info!("Starting download for client: {} (ID: {})", client.name, id);
let client_download = async {
client.download().await.map_err(|e| {
@@ -287,19 +314,32 @@ pub async fn download_client_only(id: u32, app_handle: AppHandle) -> Result<(),
let requirements_download = client.download_requirements(&app_handle);
Analytics::send_client_download_analytics(id);
+ log_debug!("Sent client download analytics for ID: {}", id);
tokio::try_join!(client_download, requirements_download)?;
+ log_info!(
+ "Successfully downloaded client and requirements for '{}'",
+ client.name
+ );
+
Ok(())
}
#[tauri::command]
pub async fn reinstall_client(id: u32, app_handle: AppHandle) -> Result<(), String> {
+ log_info!("Starting reinstall for client ID: {}", id);
let client = get_client_by_id(id)?;
+ log_debug!("Found client '{}' for reinstall", client.name);
let client_clone = client.clone();
let handle = tokio::task::spawn_blocking(move || -> Result<(), String> {
+ log_info!("Removing existing installation for '{}'", client_clone.name);
client_clone.remove_installation()?;
+ log_info!(
+ "Successfully removed existing installation for '{}'",
+ client_clone.name
+ );
Ok(())
});
@@ -308,6 +348,10 @@ pub async fn reinstall_client(id: u32, app_handle: AppHandle) -> Result<(), Stri
.map_err(|e| format!("Reinstall task error: {e}"))??;
update_client_installed_status(id, false)?;
+ log_debug!(
+ "Updated installed status to false for client '{}'",
+ client.name
+ );
let download_result = client.download().await.map_err(|e| {
if e.contains("Hash verification failed") {
@@ -316,13 +360,28 @@ pub async fn reinstall_client(id: u32, app_handle: AppHandle) -> Result<(), Stri
client.name
)
} else {
+ log_error!("Client download failed during reinstall: {}", e);
e
}
});
- download_result.as_ref()?;
+ if let Err(e) = download_result.as_ref() {
+ log_error!(
+ "Aborting reinstall for '{}' due to download failure: {}",
+ client.name,
+ e
+ );
+ return Err(e.clone());
+ }
+ log_info!("Client '{}' downloaded successfully", client.name);
let result = client.download_requirements(&app_handle).await;
+ if result.is_ok() {
+ log_info!(
+ "Client requirements for '{}' downloaded successfully",
+ client.name
+ );
+ }
if result.is_ok() {
let _ = AgentOverlayManager::download_agent_overlay_files()
@@ -341,11 +400,18 @@ pub async fn reinstall_client(id: u32, app_handle: AppHandle) -> Result<(), Stri
#[tauri::command]
pub fn open_client_folder(id: u32) -> Result<(), String> {
+ log_info!("Attempting to open folder for client ID: {}", id);
let client = get_client_by_id(id)?;
+ log_debug!("Found client '{}' to open folder", client.name);
let client_dir_relative = DATA.get_as_folder(&client.filename);
if !client_dir_relative.exists() {
+ log_warn!(
+ "Cannot open folder for client '{}', it does not exist at path: {}",
+ client.name,
+ client_dir_relative.display()
+ );
return Err("Client folder does not exist".to_string());
}
@@ -353,7 +419,16 @@ pub fn open_client_folder(id: u32) -> Result<(), String> {
.canonicalize()
.map_err(|e| format!("Failed to get absolute path: {e}"))?;
+ log_debug!(
+ "Opening client folder at: {}",
+ client_dir_absolute.display()
+ );
opener::open(&client_dir_absolute).map_err(|e| {
+ log_error!(
+ "Failed to open client folder at {}: {}",
+ client_dir_absolute.display(),
+ e
+ );
format!(
"Failed to open client folder: {} at path {}",
e,
@@ -364,6 +439,7 @@ pub fn open_client_folder(id: u32) -> Result<(), String> {
#[tauri::command]
pub fn get_latest_client_logs(id: u32) -> Result {
+ log_debug!("Fetching latest logs for client ID: {}", id);
CLIENT_LOGS
.lock()
.map_err(|_| "Failed to acquire lock on client logs".to_string())?
@@ -374,113 +450,130 @@ pub fn get_latest_client_logs(id: u32) -> Result {
#[tauri::command]
pub fn update_client_installed_status(id: u32, installed: bool) -> Result<(), String> {
- let mut manager = CLIENT_MANAGER
+ log_debug!(
+ "Updating installed status for client ID {} to {}",
+ id,
+ installed
+ );
+ if let Some(client) = CLIENT_MANAGER
.lock()
- .map_err(|_| "Failed to acquire lock on client manager".to_string())?;
-
- let manager = manager
+ .map_err(|_| "Failed to acquire lock on client manager".to_string())?
.as_mut()
- .ok_or_else(|| "Client manager not initialized".to_string())?;
-
- let client = manager
+ .ok_or_else(|| "Client manager not initialized".to_string())?
.clients
.iter_mut()
.find(|c| c.id == id)
- .ok_or_else(|| "Client not found".to_string())?;
-
- client.meta.installed = installed;
- Ok(())
+ {
+ client.meta.installed = installed;
+ log_info!(
+ "Set installed status of client '{}' to {}",
+ client.name,
+ installed
+ );
+ Ok(())
+ } else {
+ log_warn!(
+ "Could not update installed status: Client ID {} not found",
+ id
+ );
+ Err("Client not found".to_string())
+ }
}
#[tauri::command]
pub async fn delete_client(id: u32) -> Result<(), String> {
+ log_info!("Attempting to delete client with ID: {}", id);
let client = get_client_by_id(id)?;
+ log_debug!("Found client '{}' for deletion", client.name);
let handle = tokio::task::spawn_blocking(move || client.remove_installation());
match handle.await {
Ok(result) => {
if result.is_ok() {
+ log_info!("Successfully deleted files for client ID: {}", id);
update_client_installed_status(id, false)?;
+ } else {
+ log_error!("Failed to delete files for client ID {}: {:?}", id, result);
}
result
}
- Err(e) => Err(format!("Delete task error: {e}")),
+ Err(e) => {
+ log_error!("Task to delete client ID {} failed: {}", id, e);
+ Err(format!("Delete task error: {e}"))
+ }
}
}
#[tauri::command]
pub async fn get_client_details(client_id: u32) -> Result {
+ log_debug!("Fetching details for client ID: {}", client_id);
let api_url = get_auth_url().await?;
let url = format!("{api_url}api/client/{client_id}/detailed");
+ log_debug!("Requesting client details from URL: {}", url);
let client = reqwest::Client::new();
- let response = client
- .get(&url)
- .send()
- .await
- .map_err(|e| format!("Failed to fetch client details: {e}"))?;
+ let response = client.get(&url).send().await.map_err(|e| {
+ log_error!("Failed to fetch client details from {}: {}", url, e);
+ format!("Failed to fetch client details: {e}")
+ })?;
if !response.status().is_success() {
+ log_warn!(
+ "API returned non-success status ({}) for client details request to {}",
+ response.status(),
+ url
+ );
return Err(format!("API returned error: {}", response.status()));
}
- let details: serde_json::Value = response
- .json()
- .await
- .map_err(|e| format!("Failed to parse client details: {e}"))?;
+ let details: serde_json::Value = response.json().await.map_err(|e| {
+ log_error!("Failed to parse client details JSON: {}", e);
+ format!("Failed to parse client details: {e}")
+ })?;
+ log_info!("Successfully fetched details for client ID: {}", client_id);
Ok(details)
}
#[tauri::command]
pub fn increment_client_counter(id: u32, counter_type: String) -> Result<(), String> {
- let mut manager = CLIENT_MANAGER
+ if let Some(client) = CLIENT_MANAGER
.lock()
- .map_err(|_| "Failed to acquire lock on client manager".to_string())?;
-
- let manager = manager
+ .map_err(|_| "Failed to acquire lock on client manager".to_string())?
.as_mut()
- .ok_or_else(|| "Client manager not initialized".to_string())?;
-
- let client = manager
+ .ok_or_else(|| "Client manager not initialized".to_string())?
.clients
.iter_mut()
.find(|c| c.id == id)
- .ok_or_else(|| "Client not found".to_string())?;
-
- match counter_type.as_str() {
- "download" => {
- client.downloads += 1;
- log_info!(
- "Incremented download counter for client {} (ID: {}). New count: {}",
- client.name,
- id,
- client.downloads
- );
- }
- "launch" => {
- client.launches += 1;
- log_info!(
- "Incremented launch counter for client {} (ID: {}). New count: {}",
- client.name,
- id,
- client.launches
- );
- }
- _ => {
- return Err(format!("Invalid counter type: {counter_type}"));
+ {
+ match counter_type.as_str() {
+ "download" => {
+ client.downloads += 1;
+ log_info!(
+ "Incremented download counter for client {} (ID: {}). New count: {}",
+ client.name,
+ id,
+ client.downloads
+ );
+ }
+ "launch" => {
+ client.launches += 1;
+ log_info!(
+ "Incremented launch counter for client {} (ID: {}). New count: {}",
+ client.name,
+ id,
+ client.launches
+ );
+ }
+ _ => {
+ return Err(format!("Invalid counter type: {counter_type}"));
+ }
}
+ Ok(())
+ } else {
+ Err("Client not found".to_string())
}
-
- Ok(())
-}
-
-fn calculate_md5_hash(path: &std::path::PathBuf) -> Result {
- let bytes = std::fs::read(path).map_err(|e| format!("Failed to read file for hashing: {e}"))?;
-
- let digest = md5::compute(&bytes);
- Ok(format!("{digest:x}"))
}
#[tauri::command]
@@ -500,6 +593,7 @@ pub fn add_custom_client(
file_path: String,
main_class: String,
) -> Result<(), String> {
+ log_info!("Adding new custom client: '{}'", name);
let mut manager = CUSTOM_CLIENT_MANAGER
.lock()
.map_err(|_| "Failed to acquire lock on custom client manager".to_string())?;
@@ -509,11 +603,13 @@ pub fn add_custom_client(
let custom_client = CustomClient::new(0, name, version_enum, filename, path_buf, main_class);
+ log_debug!("New custom client details: {:?}", custom_client);
manager.add_client(custom_client)
}
#[tauri::command]
pub fn remove_custom_client(id: u32) -> Result<(), String> {
+ log_info!("Removing custom client with ID: {}", id);
let mut manager = CUSTOM_CLIENT_MANAGER
.lock()
.map_err(|_| "Failed to acquire lock on custom client manager".to_string())?;
@@ -528,6 +624,7 @@ pub fn update_custom_client(
version: Option,
main_class: Option,
) -> Result<(), String> {
+ log_info!("Updating custom client with ID: {}", id);
let mut manager = CUSTOM_CLIENT_MANAGER
.lock()
.map_err(|_| "Failed to acquire lock on custom client manager".to_string())?;
@@ -540,6 +637,7 @@ pub fn update_custom_client(
main_class,
};
+ log_debug!("Applying updates to custom client ID {}: {:?}", id, updates);
manager.update_client(id, updates)
}
@@ -549,6 +647,7 @@ pub async fn launch_custom_client(
user_token: String,
app_handle: AppHandle,
) -> Result<(), String> {
+ log_info!("Attempting to launch custom client with ID: {}", id);
let custom_client = {
let mut manager = CUSTOM_CLIENT_MANAGER
.lock()
@@ -559,6 +658,11 @@ pub async fn launch_custom_client(
.ok_or_else(|| "Custom client not found".to_string())?;
client.launches += 1;
+ log_debug!(
+ "Incremented launch count for custom client '{}' to {}",
+ client.name,
+ client.launches
+ );
let client_clone = client.clone();
manager.save_to_disk();
@@ -566,6 +670,7 @@ pub async fn launch_custom_client(
};
custom_client.validate_file()?;
+ log_debug!("Custom client file validated for '{}'", custom_client.name);
log_info!("Launching custom client: {}", custom_client.name);
@@ -593,11 +698,15 @@ pub async fn get_running_custom_client_ids() -> Vec {
.collect()
});
- handle.await.unwrap_or_else(|_| Vec::new())
+ handle.await.unwrap_or_else(|e| {
+ log_error!("Failed to get running custom client IDs: {}", e);
+ Vec::new()
+ })
}
#[tauri::command]
pub async fn stop_custom_client(id: u32) -> Result<(), String> {
+ log_info!("Attempting to stop custom client with ID: {}", id);
let custom_client = {
let manager = CUSTOM_CLIENT_MANAGER
.lock()
@@ -608,6 +717,7 @@ pub async fn stop_custom_client(id: u32) -> Result<(), String> {
.cloned()
.ok_or_else(|| "Custom client not found".to_string())?
};
+ log_debug!("Found custom client '{}' to stop", custom_client.name);
let client_clone = custom_client.clone();
let handle = tokio::task::spawn_blocking(move || client_clone.stop());
diff --git a/src-tauri/src/commands/discord_rpc.rs b/src-tauri/src/commands/discord_rpc.rs
index 16f7491..3925f00 100644
--- a/src-tauri/src/commands/discord_rpc.rs
+++ b/src-tauri/src/commands/discord_rpc.rs
@@ -1,7 +1,12 @@
-use crate::core::utils::discord_rpc;
+use crate::{core::utils::discord_rpc, log_debug};
#[tauri::command]
pub fn update_presence(details: String, state: String) -> Result<(), String> {
+ log_debug!(
+ "Updating Discord presence: details='{}', state='{}'",
+ details,
+ state
+ );
discord_rpc::update_activity_async(details, state);
Ok(())
}
diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs
index 31839a1..e257577 100644
--- a/src-tauri/src/commands/mod.rs
+++ b/src-tauri/src/commands/mod.rs
@@ -1,4 +1,3 @@
-pub mod analytics;
pub mod clients;
pub mod discord_rpc;
pub mod presets;
diff --git a/src-tauri/src/commands/presets.rs b/src-tauri/src/commands/presets.rs
index f12961e..e6c6923 100644
--- a/src-tauri/src/commands/presets.rs
+++ b/src-tauri/src/commands/presets.rs
@@ -1,4 +1,5 @@
use crate::core::storage::presets::{ThemePreset, PRESET_MANAGER};
+use crate::{log_debug, log_info, log_warn};
use chrono::Utc;
use uuid::Uuid;
@@ -67,23 +68,26 @@ pub struct UpdatePresetInput {
#[tauri::command]
pub fn get_all_presets() -> Result, String> {
- let preset_manager = PRESET_MANAGER.lock().unwrap();
- let presets = preset_manager
- .get_all_presets()
- .into_iter()
- .cloned()
- .collect();
- Ok(presets)
+ log_debug!("Fetching all theme presets");
+ PRESET_MANAGER
+ .lock()
+ .map(|p| p.get_all_presets())
+ .map_err(|e| {
+ log_warn!("Failed to get presets: {}", e);
+ "Failed to get presets".to_string()
+ })
}
#[tauri::command]
pub fn get_preset(id: String) -> Result
-
+
{{
$t('modals.change_password_confirm.warning')
}}
@@ -13,11 +13,11 @@
@@ -25,11 +25,7 @@