Merge commit 'bc90c27e27df60f95a1fdc3572fb0bd5aa4fd102' into feature-clean

This commit is contained in:
2025-07-07 17:14:06 +03:00
41 changed files with 2525 additions and 340 deletions

138
.github/workflows/theseus-build.yml vendored Normal file
View File

@@ -0,0 +1,138 @@
name: AstralRinth App build
on:
push:
branches:
- main
tags:
- 'v*'
paths:
- .github/workflows/theseus-build.yml
- 'apps/app/**'
- 'apps/app-frontend/**'
- 'packages/app-lib/**'
- 'packages/app-macros/**'
- 'packages/assets/**'
- 'packages/ui/**'
- 'packages/utils/**'
workflow_dispatch:
inputs:
sign-windows-binaries:
description: Sign Windows binaries
type: boolean
default: true
required: false
jobs:
build:
name: Build
strategy:
fail-fast: false
matrix:
platform: [macos-latest, windows-latest, ubuntu-22.04]
include:
- platform: macos-latest
artifact-target-name: universal-apple-darwin
- platform: windows-latest
artifact-target-name: x86_64-pc-windows-msvc
- platform: ubuntu-22.04
artifact-target-name: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.platform }}
steps:
- name: 📥 Check out code
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: 🧰 Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
rustflags: ''
target: ${{ startsWith(matrix.platform, 'macos') && 'x86_64-apple-darwin' || '' }}
- name: 🧰 Install pnpm
uses: pnpm/action-setup@v4
- name: 🧰 Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: pnpm
- name: 🧰 Install Linux build dependencies
if: startsWith(matrix.platform, 'ubuntu')
run: |
sudo apt-get update
sudo apt-get install -yq libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev xdg-utils openjdk-11-jdk
- name: 🧰 Setup Dasel
uses: jaxxstorm/action-install-gh-release@v2.1.0
with:
repo: TomWright/dasel
tag: v2.8.1
extension-matching: disable
rename-to: ${{ startsWith(matrix.platform, 'windows') && 'dasel.exe' || 'dasel' }}
chmod: 0755
- name: ⚙️ Set application version
shell: bash
env:
APP_VERSION: ${{ startsWith(github.ref, 'refs/tags/v') && github.ref_name || format('v1.0.0-canary+{0}', github.sha) }}
run: |
dasel put -f apps/app/Cargo.toml -t string -v "${APP_VERSION#v}" 'package.version'
dasel put -f packages/app-lib/Cargo.toml -t string -v "${APP_VERSION#v}" 'package.version'
dasel put -f apps/app-frontend/package.json -t string -v "${APP_VERSION#v}" 'version'
- name: 💨 Setup Turbo cache
uses: rharkor/caching-for-turbo@v1.8
- name: 🧰 Install dependencies
run: pnpm install
- name: ✍️ Set up Windows code signing
if: startsWith(matrix.platform, 'windows')
shell: bash
run: |
if [ '${{ startsWith(github.ref, 'refs/tags/v') || inputs.sign-windows-binaries }}' = 'true' ]; then
choco install jsign --ignore-dependencies # GitHub runners come with a global Java installation already
else
dasel delete -f apps/app/tauri-release.conf.json 'bundle.windows.signCommand'
fi
- name: 🗑️ Clean up cached bundles
shell: bash
run: |
rm -rf target/release/bundle
rm -rf target/*/release/bundle || true
- name: 🔨 Build macOS app
run: pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config tauri-release.conf.json
if: startsWith(matrix.platform, 'macos')
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: 🔨 Build Linux app
run: pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json
if: startsWith(matrix.platform, 'ubuntu')
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: 🔨 Build Windows app
run: |
$env:JAVA_HOME = "$env:JAVA_HOME_11_X64"
pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json --verbose --bundles 'nsis'
if: startsWith(matrix.platform, 'windows')
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: 📤 Upload app bundles
uses: actions/upload-artifact@v3
with:
name: App bundle (${{ matrix.artifact-target-name }})
path: |
target/release/bundle/**
target/*/release/bundle/**

View File

@@ -1,180 +0,0 @@
name: 'AstralRinth App Build'
on:
push:
branches:
- feature*
tags:
- 'build*'
- 'v*'
paths:
- .github/workflows/theseus-release.yml
- 'apps/app/**'
- 'apps/app-frontend/**'
- 'packages/app-lib/**'
- 'packages/app-macros/**'
- 'packages/assets/**'
- 'packages/ui/**'
- 'packages/utils/**'
workflow_dispatch:
inputs:
sign-windows-binaries:
description: Sign Windows binaries
type: boolean
default: true
required: false
jobs:
build:
strategy:
fail-fast: false
matrix:
platform: [macos-latest, windows-latest, ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: Rust setup (mac)
if: startsWith(matrix.platform, 'macos')
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
targets: aarch64-apple-darwin, x86_64-apple-darwin
- name: Rust setup
if: "!startsWith(matrix.platform, 'macos')"
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Setup rust cache
uses: actions/cache@v4
with:
path: |
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/*.AppImage
!target/release/bundle/appimage/*.AppImage.tar.gz
!target/release/bundle/appimage/*.AppImage.tar.gz.sig
!target/release/bundle/deb/*.deb
!target/release/bundle/rpm/*.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') }}
restore-keys: |
${{ runner.os }}-rust-target-
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
- name: Install pnpm via corepack
shell: bash
run: |
corepack enable
corepack prepare --activate
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: install dependencies (ubuntu only)
if: startsWith(matrix.platform, 'ubuntu')
run: |
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 xdg-utils openjdk-17-jdk
- name: Install code signing client (Windows only)
if: startsWith(matrix.platform, 'windows')
run: choco install jsign --ignore-dependencies # GitHub runners come with a global Java installation already
- name: Install frontend dependencies
run: pnpm install
- name: Disable Windows code signing for non-final release builds
if: ${{ startsWith(matrix.platform, 'windows') && !startsWith(github.ref, 'refs/tags/v') && !inputs.sign-windows-binaries }}
run: |
jq 'del(.bundle.windows.signCommand)' apps/app/tauri-release.conf.json > apps/app/tauri-release.conf.json.new
Move-Item -Path apps/app/tauri-release.conf.json.new -Destination apps/app/tauri-release.conf.json -Force
- name: build app (macos)
run: pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config tauri-release.conf.json
if: startsWith(matrix.platform, 'macos')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: build app (Linux)
run: pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json
if: startsWith(matrix.platform, 'ubuntu')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: build app (Windows)
run: |
[System.Convert]::FromBase64String("$env:DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_BASE64") | Set-Content -Path signer-client-cert.p12 -AsByteStream
$env:DIGICERT_ONE_SIGNER_CREDENTIALS = "$env:DIGICERT_ONE_SIGNER_API_KEY|$PWD\signer-client-cert.p12|$env:DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_PASSWORD"
$env:JAVA_HOME = "$env:JAVA_HOME_11_X64"
pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json --verbose --bundles 'nsis,updater'
Remove-Item -Path signer-client-cert.p12
if: startsWith(matrix.platform, 'windows')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
DIGICERT_ONE_SIGNER_API_KEY: ${{ secrets.DIGICERT_ONE_SIGNER_API_KEY }}
DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_BASE64: ${{ secrets.DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_BASE64 }}
DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_PASSWORD: ${{ secrets.DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_PASSWORD }}
- name: upload ${{ matrix.platform }}
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.platform }}
path: |
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

30
Cargo.lock generated
View File

@@ -542,9 +542,9 @@ dependencies = [
[[package]]
name = "async-channel"
version = "2.4.0"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16c74e56284d2188cabb6ad99603d1ace887a5d7e7b695d01b728155ed9ed427"
checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
dependencies = [
"concurrent-queue",
"event-listener-strategy",
@@ -632,7 +632,7 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc"
dependencies = [
"async-channel 2.4.0",
"async-channel 2.5.0",
"async-io",
"async-lock",
"async-signal",
@@ -1082,11 +1082,11 @@ dependencies = [
[[package]]
name = "blocking"
version = "1.6.1"
version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea"
checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
dependencies = [
"async-channel 2.4.0",
"async-channel 2.5.0",
"async-task",
"futures-io",
"futures-lite 2.6.0",
@@ -6022,9 +6022,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "plist"
version = "1.7.2"
version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d77244ce2d584cd84f6a15f86195b8c9b2a0dfbfd817c09e0464244091a58ed"
checksum = "546b279bf0638ee811d9e47de2ca5b66575a543035d79fdf83959dd2f5c3b4c3"
dependencies = [
"base64 0.22.1",
"indexmap 2.10.0",
@@ -6957,9 +6957,9 @@ dependencies = [
[[package]]
name = "rgb"
version = "0.8.50"
version = "0.8.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
checksum = "a457e416a0f90d246a4c3288bd7a25b2304ca727f253f95be383dd17af56be8f"
dependencies = [
"bytemuck",
]
@@ -7355,9 +7355,9 @@ dependencies = [
[[package]]
name = "schemars"
version = "1.0.3"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1375ba8ef45a6f15d83fa8748f1079428295d403d6ea991d09ab100155fbc06d"
checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0"
dependencies = [
"dyn-clone",
"ref-cast",
@@ -7805,7 +7805,7 @@ dependencies = [
"indexmap 1.9.3",
"indexmap 2.10.0",
"schemars 0.9.0",
"schemars 1.0.3",
"schemars 1.0.4",
"serde",
"serde_derive",
"serde_json",
@@ -9036,7 +9036,7 @@ dependencies = [
[[package]]
name = "theseus"
version = "0.10.1"
version = "1.0.0-local"
dependencies = [
"ariadne",
"async-compression",
@@ -9101,7 +9101,7 @@ dependencies = [
[[package]]
name = "theseus_gui"
version = "0.10.1"
version = "1.0.0-local"
dependencies = [
"chrono",
"daedalus",

View File

@@ -1,7 +1,7 @@
{
"name": "@modrinth/app-frontend",
"private": true,
"version": "0.10.1",
"version": "1.0.0-local",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1 +0,0 @@
{"asset":{"version":"2.0","generator":"Blockbench 4.12.4 glTF exporter"},"scenes":[{"nodes":[1],"name":"blockbench_export"}],"scene":0,"nodes":[{"rotation":[0,0,0.19509032201612825,0.9807852804032304],"translation":[0.15625,1,0],"name":"Cape","mesh":0},{"children":[0]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963}],"buffers":[{"byteLength":840,"uri":"data:application/octet-stream;base64,AAAAPQAAAAAAAKA+AAAAPQAAAAAAAKC+AAAAPQAAgL8AAKA+AAAAPQAAgL8AAKC+AAAAvQAAAAAAAKC+AAAAvQAAAAAAAKA+AAAAvQAAgL8AAKC+AAAAvQAAgL8AAKA+AAAAvQAAAAAAAKC+AAAAPQAAAAAAAKC+AAAAvQAAAAAAAKA+AAAAPQAAAAAAAKA+AAAAvQAAgL8AAKA+AAAAPQAAgL8AAKA+AAAAvQAAgL8AAKC+AAAAPQAAgL8AAKC+AAAAvQAAAAAAAKA+AAAAPQAAAAAAAKA+AAAAvQAAgL8AAKA+AAAAPQAAgL8AAKA+AAAAPQAAAAAAAKC+AAAAvQAAAAAAAKC+AAAAPQAAgL8AAKC+AAAAvQAAgL8AAKC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPAAAgD0AADA+AACAPQAAgDwAAAg/AAAwPgAACD8AAEA+AACAPQAAsD4AAIA9AABAPgAACD8AALA+AAAIPwAAgDwAAAA9AACAPAAAgD0AADA+AAAAPQAAMD4AAIA9AAAwPgAAAD0AAKg+AAAAPQAAMD4AAAAAAACoPgAAAAAAAEA+AACAPQAAMD4AAIA9AABAPgAACD8AADA+AAAIPwAAAAAAAIA9AACAPAAAgD0AAAAAAAAIPwAAgDwAAAg/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUA"}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[0.03125,0,0.3125],"min":[-0.03125,-1,-0.3125],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.34375,0.53125],"min":[0,0],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{"sampler":0,"source":0,"name":"cape.png"}],"samplers":[{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071}],"images":[{"mimeType":"image/png","uri":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAABCRJREFUaEPtlktoE1EUhm/sIzFpYsBgHyn2oRZt0C5ESxWFYutKNyKIWBBR6kpBd3Ur2J0FRdCiuKmIIG4qLnzgQqTUXSlVqdoXTU0lljRpYtOHI/8dznBnOm1mphMwJWeTzJ0795zznf/cex2MMXam2S/ht38siJ8V1lgd5mOBARerc3rZnmK37rwvCyk2nE6wezMRh+4EncH9B1ql+fkU64rPsaBz5bqh4T7Daxn1Kc5zVNeEePJIci0Aq71bzenY6JChwAnA0OBHQ/OtJLnWNwqAy61R1tO3k891ueRKoDKwtqbv7MGbgCnfRgHQoqG9hyX4hc/yiho+/HNqVPFtdj2jwTpQgd/RKT7/0ZUku/o4qAJw50KYXbzr4e+3BioYzc3kwGzAUCLWFw2+UBiCb3bNTDHiPQeAP3CGNmg/6VcSBpDu3hhvDQpOCwABwrQKsRIsqYAChy/EQAXwlPiZ3a3igNPkXIyTjr8o5L7PPdzGf59c+sV/faeWeIIIAHPJ8M3kc7l1K09LKghWAIgqoPZ7djPFTlxb4D6yAgBOQfntrUVWVeRk44tplXJorOVGkVIJTBCTpw9ECFYBIEkyMfmsAXh3u1qi5MlxbbGX/x1ZSCjBAAwgfPr6h49R5UVavk0FXC2wju5p07s6iqEF0PtqSlEf+bKzDRwdgaDU7AkoySJ5Oo/D6ZRq/H0yyuJ/lxkSheG/FgCNm7kL0BoEAG32sqtY1YIHd2/mGzTMVgD3y2slqjgW9xY6ma9AThAARIMiBtOpjADwTWc0bEkB+FZsSTxTW0KBsGPXx0yvrUpEeHAAQIM7wBJLcu8DwHaP/H8i6VSND6SiSjBlRW4WWUypVIBbIgzjVgB0tpdKqLReS0KVPTMTfH0ra2cEgAmAAEd+l1z52LybqwBQYAQAyZPh6gtDW4jjuC4fHx8wXSm0JDbeI95SRYHiFZlUaWVtPQiKAugl5E8ASAUYiy8vc0C474uGasPE5PHc4g0wK/f4obom6UNimol7kTZwQLANwOuqBokqDEeQf4lfvvnNxZJcBTBAGZplWQcQ3tcgwY9oWlXinRW4ugoAgNAWWe6ocn1QvgyRTUb4RZFVljlY/3hSBYCqb6cCZo8ekuATVRZPI/gQW8FWAI1VHganVgDQUajdA6y2gAgAcZFBjTBSpG0AcAqc3VVmCMDTbxGWZvIRCaMNkJ7pFMCzVQD4liCQ8kRFUlvaBkCvL+wYw2ZmNUgCgLajFhRPJlv3ADuS1VtjPQCk823S574ffN/x1dQqy8dHR5SN2Spcbaymz2mjwNYDAD4IQn3TDpVLQIAqNjwAANRrAdoI/3sAOF7Xe1khCNQGVH1bL0JGJb1R52VtD8gVYHkAuVKpbMWZV0C2yObKunkF5EqlshVnXgHZIpsr6/4DlbxcPydnT74AAAAASUVORK5CYII="}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -59,7 +59,7 @@ watch(
<div class="mt-4 flex items-center justify-between">
<div>
<h2 class="m-0 text-lg font-extrabold text-contrast">Hide nametag</h2>
<p class="m-0 mt-1">Disables the nametag above your player on the skins page. page.</p>
<p class="m-0 mt-1">Disables the nametag above your player on the skins page.</p>
</div>
<Toggle id="hide-nametag-skins-page" v-model="settings.hide_nametag_skins_page" />
</div>

View File

@@ -1,8 +1,3 @@
<script lang="ts">
import capeModelUrl from '@/assets/models/cape.gltf?url'
import wideModelUrl from '@/assets/models/classic_player.gltf?url'
import slimModelUrl from '@/assets/models/slim_player.gltf?url'
</script>
<template>
<UploadSkinModal ref="uploadModal" />
<ModalWrapper ref="modal" @on-hide="resetState">
@@ -16,9 +11,6 @@ import slimModelUrl from '@/assets/models/slim_player.gltf?url'
<div class="max-h-[25rem] w-[16rem] min-w-[16rem] overflow-hidden relative">
<div class="absolute top-[-4rem] left-0 h-[32rem] w-[16rem] flex-shrink-0">
<SkinPreviewRenderer
:slim-model-src="slimModelUrl"
:wide-model-src="wideModelUrl"
:cape-model-src="capeModelUrl"
:variant="variant"
:texture-src="previewSkin || ''"
:cape-src="selectedCapeTexture"

View File

@@ -10,9 +10,6 @@ import {
} from '@modrinth/ui'
import { CheckIcon, XIcon } from '@modrinth/assets'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import capeModelUrl from '@/assets/models/cape.gltf?url'
import wideModelUrl from '@/assets/models/classic_player.gltf?url'
import slimModelUrl from '@/assets/models/slim_player.gltf?url'
const modal = useTemplateRef('modal')
@@ -88,9 +85,6 @@ defineExpose({
<div class="absolute top-[-4rem] left-0 h-[32rem] w-[16rem] flex-shrink-0">
<SkinPreviewRenderer
v-if="currentSkinTexture"
:slim-model-src="slimModelUrl"
:wide-model-src="wideModelUrl"
:cape-model-src="capeModelUrl"
:cape-src="currentCapeTexture"
:texture-src="currentSkinTexture"
:variant="currentSkinVariant"

View File

@@ -4,9 +4,7 @@ import { get_normalized_skin_texture, determineModelType } from '../skins'
import { reactive } from 'vue'
import { setupSkinModel, disposeCaches } from '@modrinth/utils'
import { skinPreviewStorage } from '../storage/skin-preview-storage'
import capeModelUrl from '@/assets/models/cape.gltf?url'
import wideModelUrl from '@/assets/models/classic_player.gltf?url'
import slimModelUrl from '@/assets/models/slim_player.gltf?url'
import { CapeModel, ClassicPlayerModel, SlimPlayerModel } from '@modrinth/assets'
export interface RenderResult {
forwards: string
@@ -127,11 +125,11 @@ class BatchSkinRenderer {
function getModelUrlForVariant(variant: string): string {
switch (variant) {
case 'SLIM':
return slimModelUrl
return SlimPlayerModel
case 'CLASSIC':
case 'UNKNOWN':
default:
return wideModelUrl
return ClassicPlayerModel
}
}
@@ -281,6 +279,7 @@ async function generateHeadRender(skin: Skin): Promise<string> {
headMap.set(headKey, headUrl)
try {
// @ts-expect-error - skinPreviewStorage.store expects a RenderResult, but we are storing a string url.
await skinPreviewStorage.store(headKey, headUrl)
} catch (error) {
console.warn('Failed to store head render in persistent storage:', error)
@@ -335,7 +334,7 @@ export async function generateSkinPreviews(skins: Skin[], capes: Cape[]): Promis
await get_normalized_skin_texture(skin),
modelUrl,
cape?.texture,
capeModelUrl,
CapeModel,
)
map.set(key, renderResult)

View File

@@ -97,7 +97,11 @@ export async function fixUnknownSkins(list: Skin[]) {
export function filterDefaultSkins(list: Skin[]) {
return list
.filter((s) => s.source === 'default' && (!s.name || s.variant === DEFAULT_MODELS[s.name]))
.filter(
(s) =>
s.source === 'default' &&
(!s.name || !(s.name in DEFAULT_MODELS) || s.variant === DEFAULT_MODELS[s.name]),
)
.sort((a, b) => {
const aIndex = a.name ? DEFAULT_MODEL_SORTING.indexOf(a.name) : -1
const bIndex = b.name ? DEFAULT_MODEL_SORTING.indexOf(b.name) : -1

View File

@@ -43,10 +43,6 @@ import { handleSevereError } from '@/store/error'
import { trackEvent } from '@/helpers/analytics'
import type AccountsCard from '@/components/ui/AccountsCard.vue'
import { arrayBufferToBase64 } from '@modrinth/utils'
import capeModelUrl from '@/assets/models/cape.gltf?url'
import wideModelUrl from '@/assets/models/classic_player.gltf?url'
import slimModelUrl from '@/assets/models/slim_player.gltf?url'
const editSkinModal = useTemplateRef('editSkinModal')
const selectCapeModal = useTemplateRef('selectCapeModal')
const uploadSkinModal = useTemplateRef('uploadSkinModal')
@@ -320,9 +316,6 @@ await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
</h1>
<div class="preview-container">
<SkinPreviewRenderer
:wide-model-src="wideModelUrl"
:slim-model-src="slimModelUrl"
:cape-model-src="capeModelUrl"
:cape-src="capeTexture"
:texture-src="skinTexture || ''"
:variant="skinVariant"

View File

@@ -1,6 +1,6 @@
[package]
name = "theseus_gui"
version = "0.10.1"
version = "1.0.0-local" # The actual version is set by the theseus-build workflow on tagging
description = "The Modrinth App is a desktop application for managing your Minecraft mods"
license = "GPL-3.0-only"
repository = "https://github.com/modrinth/code/apps/app/"

View File

@@ -20,30 +20,23 @@
<string>A Minecraft mod wants to access your microphone.</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<key>NSExceptionDomains</key>
<dict>
<key>asset.localhost</key>
<dict>
<key>textures.minecraft.net</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
<key>ipc.localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
<key>textures.minecraft.net</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>

View File

@@ -41,7 +41,7 @@
]
},
"productName": "AstralRinth App",
"version": "0.10.1",
"version": "../app-frontend/package.json",
"mainBinaryName": "AstralRinth App",
"identifier": "AstralRinthApp",
"plugins": {

View File

@@ -1,4 +1,4 @@
<script setup>
<script setup lang="ts">
import {
TrashIcon,
SearchIcon,
@@ -17,76 +17,134 @@ import LatestNewsRow from "~/components/ui/news/LatestNewsRow.vue";
import { homePageProjects } from "~/generated/state.json";
const os = ref(null);
const downloadWindows = ref(null);
const downloadLinux = ref(null);
const downloadSection = ref(null);
const windowsLink = ref(null);
const linuxLinks = {
appImage: null,
deb: null,
rpm: null,
thirdParty: "https://support.modrinth.com/en/articles/9298760",
};
const macLinks = {
universal: null,
};
interface LauncherPlatform {
install_urls: string[];
}
let downloadLauncher;
interface LauncherUpdates {
platforms: {
"darwin-aarch64": LauncherPlatform;
"windows-x86_64": LauncherPlatform;
"linux-x86_64": LauncherPlatform;
};
}
type OSType = "Mac" | "Windows" | "Linux" | null;
const downloadWindows = ref<HTMLAnchorElement | null>(null);
const downloadLinux = ref<HTMLAnchorElement | null>(null);
const downloadSection = ref<HTMLElement | null>(null);
const windowsLink = ref<string | null>(null);
const linuxLinks = reactive({
appImage: null as string | null,
deb: null as string | null,
rpm: null as string | null,
thirdParty: "https://support.modrinth.com/en/articles/9298760",
});
const macLinks = reactive({
universal: null as string | null,
});
const newProjects = homePageProjects.slice(0, 40);
const val = Math.ceil(newProjects.length / 6);
const rows = ref([
const rows = [
newProjects.slice(0, val),
newProjects.slice(val, val * 2),
newProjects.slice(val * 2, val * 3),
newProjects.slice(val * 3, val * 4),
newProjects.slice(val * 4, val * 5),
]);
];
const [{ data: launcherUpdates }] = await Promise.all([
await useAsyncData("launcherUpdates", () =>
$fetch("https://launcher-files.modrinth.com/updates.json"),
),
]);
const { data: launcherUpdates } = await useFetch<LauncherUpdates>(
"https://launcher-files.modrinth.com/updates.json?new",
{
server: false,
getCachedData(key, nuxtApp) {
const cached = (nuxtApp.ssrContext?.cache as any)?.[key] || nuxtApp.payload.data[key];
if (!cached) return;
macLinks.universal = launcherUpdates.value.platforms["darwin-aarch64"].install_urls[0];
windowsLink.value = launcherUpdates.value.platforms["windows-x86_64"].install_urls[0];
linuxLinks.appImage = launcherUpdates.value.platforms["linux-x86_64"].install_urls[1];
linuxLinks.deb = launcherUpdates.value.platforms["linux-x86_64"].install_urls[0];
linuxLinks.rpm = launcherUpdates.value.platforms["linux-x86_64"].install_urls[2];
const now = Date.now();
const cacheTime = cached._cacheTime || 0;
const maxAge = 5 * 60 * 1000;
onMounted(() => {
os.value = navigator?.platform.toString();
os.value = os.value?.includes("Mac")
? "Mac"
: os.value?.includes("Win")
? "Windows"
: os.value?.includes("Linux")
? "Linux"
: null;
if (now - cacheTime > maxAge) {
return null;
}
return cached;
},
transform(data) {
return {
...data,
_cacheTime: Date.now(),
};
},
},
);
const platform = computed<string>(() => {
if (import.meta.server) {
const headers = useRequestHeaders();
return headers["user-agent"] || "";
} else {
return navigator.userAgent || "";
}
});
const os = computed<OSType>(() => {
if (platform.value.includes("Mac")) {
return "Mac";
} else if (platform.value.includes("Win")) {
return "Windows";
} else if (platform.value.includes("Linux")) {
return "Linux";
} else {
return null;
}
});
const downloadLauncher = computed(() => {
if (os.value === "Windows") {
downloadLauncher = () => {
downloadWindows.value.click();
return () => {
downloadWindows.value?.click();
};
} else if (os.value === "Linux") {
downloadLauncher = () => {
downloadLinux.value.click();
return () => {
downloadLinux.value?.click();
};
} else {
downloadLauncher = () => {
return () => {
scrollToSection();
};
}
});
const handleDownload = () => {
downloadLauncher.value();
};
watch(
launcherUpdates,
(newData) => {
if (newData?.platforms) {
macLinks.universal = newData.platforms["darwin-aarch64"]?.install_urls[0] || null;
windowsLink.value = newData.platforms["windows-x86_64"]?.install_urls[0] || null;
linuxLinks.appImage = newData.platforms["linux-x86_64"]?.install_urls[1] || null;
linuxLinks.deb = newData.platforms["linux-x86_64"]?.install_urls[0] || null;
linuxLinks.rpm = newData.platforms["linux-x86_64"]?.install_urls[2] || null;
}
},
{ immediate: true },
);
const scrollToSection = () => {
nextTick(() => {
window.scrollTo({
top: downloadSection.value.offsetTop,
behavior: "smooth",
});
if (downloadSection.value) {
window.scrollTo({
top: downloadSection.value.offsetTop,
behavior: "smooth",
});
}
});
};
@@ -119,7 +177,7 @@ useSeoMeta({
v-if="os"
class="iconified-button brand-button btn btn-large"
rel="noopener nofollow"
@click="downloadLauncher"
@click="handleDownload"
>
<svg
v-if="os === 'Linux'"
@@ -485,7 +543,7 @@ useSeoMeta({
class="project button-animation gradient-border"
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}`"
>
<Avatar :src="project.icon_url" :alt="project.title" size="sm" loading="lazy" />
<Avatar :src="project.icon_url!" :alt="project.title" size="sm" />
<div class="project-info">
<span class="title">
{{ project.title }}
@@ -596,9 +654,7 @@ useSeoMeta({
</div>
<div class="description">
Modrinths launcher is fully open source. You can view the source code on our
<a href="https://github.com/modrinth/theseus" rel="noopener" :target="$external()"
>GitHub</a
>!
<a href="https://github.com/modrinth/theseus" rel="noopener" target="_blank">GitHub</a>!
</div>
</div>
<div class="point">
@@ -788,7 +844,7 @@ useSeoMeta({
Windows
</div>
<div class="description">
<a ref="downloadWindows" :href="windowsLink" download="">
<a ref="downloadWindows" :href="windowsLink || undefined" download="">
<DownloadIcon />
<span> Download the beta </span>
</a>
@@ -812,7 +868,7 @@ useSeoMeta({
Mac
</div>
<div class="description apple">
<a :href="macLinks.universal" download="">
<a :href="macLinks.universal || undefined" download="">
<DownloadIcon />
<span> Download the beta </span>
</a>
@@ -849,19 +905,19 @@ useSeoMeta({
Linux
</div>
<div class="description apple">
<a ref="downloadLinux" :href="linuxLinks.appImage" download="">
<a ref="downloadLinux" :href="linuxLinks.appImage || undefined" download="">
<DownloadIcon />
<span> Download the AppImage </span>
</a>
<a :href="linuxLinks.deb" download="">
<a :href="linuxLinks.deb || undefined" download="">
<DownloadIcon />
<span> Download the DEB </span>
</a>
<a :href="linuxLinks.rpm" download="">
<a :href="linuxLinks.rpm || undefined" download="">
<DownloadIcon />
<span> Download the RPM </span>
</a>
<a :href="linuxLinks.thirdParty" download="">
<a :href="linuxLinks.thirdParty || undefined" download="">
<LinkIcon />
<span> Third-party packages </span>
</a>

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -1,5 +1,12 @@
{
"articles": [
{
"title": "Skins — Now in Modrinth App!",
"summary": "Customize your look, save your favorite skins, and swap them out in a flash, all within Modrinth App.",
"thumbnail": "https://modrinth.com/news/article/skins-now-in-modrinth-app/thumbnail.webp",
"date": "2025-07-06T23:45:00.000Z",
"link": "https://modrinth.com/news/article/skins-now-in-modrinth-app"
},
{
"title": "Creator Updates, July 2025",
"summary": "Addressing recent growth and growing pains that have been affecting creators.",

View File

@@ -4,15 +4,23 @@
<description><![CDATA[Keep up-to-date on the latest news from Modrinth.]]></description>
<link>https://modrinth.com/news/</link>
<generator>@modrinth/blog</generator>
<lastBuildDate>Wed, 02 Jul 2025 02:42:05 GMT</lastBuildDate>
<lastBuildDate>Sat, 05 Jul 2025 21:04:25 GMT</lastBuildDate>
<atom:link href="https://modrinth.com/news/feed/rss.xml" rel="self" type="application/rss+xml"/>
<language><![CDATA[en]]></language>
<item>
<title><![CDATA[Skins — Now in Modrinth App!]]></title>
<description><![CDATA[Customize your look, save your favorite skins, and swap them out in a flash, all within Modrinth App.]]></description>
<link>https://modrinth.com/news/article/skins-now-in-modrinth-app/</link>
<guid isPermaLink="false">https://modrinth.com/news/article/skins-now-in-modrinth-app/</guid>
<pubDate>Sat, 05 Jul 2025 19:19:00 GMT</pubDate>
<content:encoded>&lt;![CDATA[&lt;p&gt;We&apos;re thrilled to roll out Modrinth App &lt;strong&gt;v0.10&lt;/strong&gt; with a beta release of one of our most highly requested features, the &lt;strong&gt;Skins page&lt;/strong&gt;. The Skins page allows you to manage all of your Minecraft skins directly within Modrinth App. You can see all your saved custom skins and the default Minecraft skins in one convenient place.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;./skins-page.webp&quot; alt=&quot;The new skins page, featuring a cute animated player model, your custom skins &amp;amp; default skins.&quot;&gt;&lt;/p&gt;&lt;p&gt;Adding a new skin is simple, even Herobrine could do it! When you add or edit a skin, you can &lt;strong&gt;upload&lt;/strong&gt; your custom texture file directly from your computer, &lt;strong&gt;choose&lt;/strong&gt; between the wide or slim arm style to match your preferred character model, and even &lt;strong&gt;assign&lt;/strong&gt; a specific cape to that look for the perfect finishing touch.&lt;/p&gt;&lt;p&gt;The interface makes it easy to preview your changes in real-time with the animated player model, so you can see exactly how your skin will look in-game before saving it.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;./edit-skin.webp&quot; alt=&quot;The edit skin modal that shows when you go to add or edit a skin.&quot;&gt;&lt;/p&gt;&lt;h2&gt;Fixes and More!&lt;/h2&gt;&lt;p&gt;Alongside this major new feature, &lt;strong&gt;v0.10&lt;/strong&gt; includes a host of improvements and bug fixes to make your experience smoother. We&apos;ve updated the news feed to use our new system, fixed issues with project descriptions, and tidied up how data is handled. For a full breakdown of all the changes, you can &lt;a href=&quot;https://modrinth.com/news/changelog?filter=app&quot;&gt;check out the complete changelog here.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;As the skins feature is in &lt;em&gt;beta&lt;/em&gt;, we&apos;re eager to hear your feedback! &lt;strong&gt;Jump in, give it a try&lt;/strong&gt;, and let us know what you think. You can share your thoughts on our &lt;a href=&quot;https://discord.modrinth.com/&quot; rel=&quot;noopener nofollow ugc&quot;&gt;Discord server&lt;/a&gt; or &lt;a href=&quot;https://support.modrinth.com&quot; rel=&quot;noopener nofollow ugc&quot;&gt;start a support chat&lt;/a&gt; if you&apos;re running into issues.&lt;/p&gt;&lt;p&gt;Thank you! We can&apos;t wait to see your skins in action. Happy customizing!&lt;/p&gt;]]&gt;</content:encoded>
</item>
<item>
<title><![CDATA[Creator Updates, July 2025]]></title>
<description><![CDATA[Addressing recent growth and growing pains that have been affecting creators.]]></description>
<link>https://modrinth.com/news/article/creator-updates-july-2025/</link>
<guid isPermaLink="false">https://modrinth.com/news/article/creator-updates-july-2025/</guid>
<pubDate>Wed, 02 Jul 2025 03:00:00 GMT</pubDate>
<pubDate>Wed, 02 Jul 2025 04:20:00 GMT</pubDate>
<content:encoded>&lt;![CDATA[&lt;p&gt;Hey all,&lt;/p&gt;&lt;p&gt;The last few months have been quite hectic for Modrinth. We&apos;ve experienced all-time highs in both traffic and new creators and have outgrown a lot of our existing systems, which has led to a lot of issues plaguing creators, especially.&lt;/p&gt;&lt;p&gt;The team has been super hard at work at this, and I&apos;m really glad to announce that we&apos;ve fixed most of these issues long term.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Upload issues (inputs not showing up, instability, etc)&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;We&apos;ve tracked these issues down to conflicting code between our ad provider and Modrinth&apos;s. For now, we&apos;ve &lt;strong&gt;disabled ads for all logged in users across the site&lt;/strong&gt; while we work on resolving these long term. Both web users and logged-in web users make a very small percentage of our ad revenue (7% for web and 0.05% for logged-in web users) so creators should see a very minimal revenue drop from this, and have a much better experience navigating and uploading to the site.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Moderation and report response times&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Creators have had to wait, in some cases, weeks to get their projects reviewed. This is unacceptable on our part and we are actively overhauling our moderation tooling to improve the moderation experience (and lowering time spent per project). We&apos;ve also hired 3 additional moderators/support staff (&lt;strong&gt;bringing our total to 7 and the total team to 17 people!&lt;/strong&gt;). We&apos;re hoping to see a significant reduction in queue times over the coming weeks.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ad revenue instability&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;While ad revenue is generally out of our control and tends to fluctuate a lot, on June 4th we noticed a sharp decrease in creator revenue (~35% less than normal levels). While our ad provider initially thought this was a display issue, after further inquiry there were 2 causes: 1) Google AdExchange falsely flagging our traffic as invalid 2) Amazon banning many gaming publishers from their network &lt;a href=&quot;https://www.adweek.com/media/exclusive-ads-from-verizon-shell-and-others-ran-next-to-explicit-videos-on-top-android-app/&quot; rel=&quot;noopener nofollow ugc&quot;&gt;due to panic in the gaming ads space&lt;/a&gt;. While the Amazon ban is now resolved, we no longer are running Google AdExchange in the desktop app due to invalid traffic issues. This will lead to a permanent revenue decrease (AdX contributed to ~20% of our ad revenue). We also updated our prebid version (the underlying tech used to run ad auctions) which has shown a measurable increase, bringing revenue back to &amp;quot;normal&amp;quot; levels. Overall, we are closely monitoring and will keep you all posted. However, despite all the issues, due to some end-of-quarter campaigns, &lt;strong&gt;revenue in June was an all time high, at $227k ($170k paid to creators)&lt;/strong&gt;!&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Payout outages&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Creators should be able to withdraw their revenue at all times, but due to slow PayPal clearing times and poor planning by us, we&apos;ve had multiple week long outages in withdrawals. While we do store funds 1:1, these &amp;quot;outages&amp;quot; happen because we primarily store creator funds in an FDIC-insured bank account, as we wouldn&apos;t want a PayPal/Tremendous account suspension to cause creators to lose funds. We&apos;ve now set up internal reporting which should never cause this to happen again (or, if it does, drastically reduce the time payout outages happen)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Platform Revenue Route&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Due to some unannounced breaking changes in Aditude&apos;s API, the platform revenue API was broken. It is now &lt;a href=&quot;https://api.modrinth.com/v3/payout/platform_revenue&quot; rel=&quot;noopener nofollow ugc&quot;&gt;working&lt;/a&gt;. You can also use &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; fields to filter any date range!&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;API and Uptime&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;We&apos;ve migrated our infrastructure for the website, app, and servers to OVH over our existing non-redundant AWS system. We&apos;ve hit 99.96% uptime on our API and 99.98% on Modrinth Servers!&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Thank you all for your patience! If you are having any more issues or have any questions about all of this, feel free to DM @geometrically on Discord or &lt;a href=&quot;https://support.modrinth.com&quot; rel=&quot;noopener nofollow ugc&quot;&gt;start a support chat&lt;/a&gt; and we will be happy to help!&lt;/p&gt;]]&gt;</content:encoded>
</item>
<item>

View File

@@ -1,6 +1,6 @@
[package]
name = "theseus"
version = "0.10.1"
version = "1.0.0-local" # The actual version is set by the theseus-build workflow on tagging
authors = ["Jai A <jaiagr+gpg@pm.me>"]
edition.workspace = true

View File

@@ -2,6 +2,7 @@ package com.modrinth.theseus;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
@@ -76,14 +77,14 @@ public final class MinecraftLaunch {
Object thisObject = null;
if (!Modifier.isStatic(mainMethod.getModifiers())) {
thisObject = mainClass.getDeclaredConstructor().newInstance();
thisObject = forceAccessible(mainClass.getDeclaredConstructor()).newInstance();
}
final Object[] parameters = mainMethod.getParameterCount() > 0 ? new Object[] {args} : new Object[] {};
mainMethod.invoke(thisObject, parameters);
} else {
findSimpleMainMethod(mainClass).invoke(null, new Object[] {args});
forceAccessible(findSimpleMainMethod(mainClass)).invoke(null, new Object[] {args});
}
}
@@ -115,4 +116,14 @@ public final class MinecraftLaunch {
private static Method findSimpleMainMethod(Class<?> mainClass) throws NoSuchMethodException {
return mainClass.getMethod("main", String[].class);
}
private static <T extends AccessibleObject> T forceAccessible(T object) throws ReflectiveOperationException {
try {
final Method setAccessible0 = AccessibleObject.class.getDeclaredMethod("setAccessible0", boolean.class);
setAccessible0.setAccessible(true);
setAccessible0.invoke(object, true);
} catch (NoSuchMethodException e) {
}
return object;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -622,6 +622,12 @@ pub async fn launch_minecraft(
.into_iter(),
);
// The java launcher requires access to java.lang.reflect in order to force access in to
// whatever module the main class is in
if java_version.parsed_version >= 9 {
command.arg("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED");
}
// The java launcher code requires internal JDK code in Java 25+ in order to support JEP 512
if java_version.parsed_version >= 25 {
command.arg("--add-opens=jdk.internal/jdk.internal.misc=ALL-UNNAMED");

View File

@@ -1,7 +1,7 @@
module.exports = {
root: true,
extends: ['custom/library'],
ignorePatterns: ['**/*.scss', '**/*.svg', 'node_modules/', 'dist/'],
ignorePatterns: ['**/*.scss', '**/*.svg', 'node_modules/', 'dist/', '**/*.gltf'],
env: {
node: true,
},

View File

@@ -9,3 +9,8 @@ declare module '*.webp' {
const src: string
export default src
}
declare module '*?url' {
const src: string
export default src
}

View File

@@ -97,4 +97,8 @@ export const MicrosoftIcon = _MicrosoftIcon
export const PirateShipIcon = _PirateShipIcon
export const AstralRinthLogo = _AstralRinthLogo
export { default as CapeModel } from './models/cape.gltf?url'
export { default as ClassicPlayerModel } from './models/classic-player.gltf?url'
export { default as SlimPlayerModel } from './models/slim-player.gltf?url'
export * from './generated-icons'

View File

@@ -0,0 +1,92 @@
{
"asset": { "version": "2.0", "generator": "Blockbench 4.12.4 glTF exporter" },
"scenes": [{ "nodes": [1], "name": "blockbench_export" }],
"scene": 0,
"nodes": [
{
"rotation": [0, 0, 0.19509032201612825, 0.9807852804032304],
"translation": [0.15625, 1, 0],
"name": "Cape",
"mesh": 0
},
{ "children": [0] }
],
"bufferViews": [
{ "buffer": 0, "byteOffset": 0, "byteLength": 288, "target": 34962, "byteStride": 12 },
{ "buffer": 0, "byteOffset": 288, "byteLength": 288, "target": 34962, "byteStride": 12 },
{ "buffer": 0, "byteOffset": 576, "byteLength": 192, "target": 34962, "byteStride": 8 },
{ "buffer": 0, "byteOffset": 768, "byteLength": 72, "target": 34963 }
],
"buffers": [
{
"byteLength": 840,
"uri": "data:application/octet-stream;base64,AAAAPQAAAAAAAKA+AAAAPQAAAAAAAKC+AAAAPQAAgL8AAKA+AAAAPQAAgL8AAKC+AAAAvQAAAAAAAKC+AAAAvQAAAAAAAKA+AAAAvQAAgL8AAKC+AAAAvQAAgL8AAKA+AAAAvQAAAAAAAKC+AAAAPQAAAAAAAKC+AAAAvQAAAAAAAKA+AAAAPQAAAAAAAKA+AAAAvQAAgL8AAKA+AAAAPQAAgL8AAKA+AAAAvQAAgL8AAKC+AAAAPQAAgL8AAKC+AAAAvQAAAAAAAKA+AAAAPQAAAAAAAKA+AAAAvQAAgL8AAKA+AAAAPQAAgL8AAKA+AAAAPQAAAAAAAKC+AAAAvQAAAAAAAKC+AAAAPQAAgL8AAKC+AAAAvQAAgL8AAKC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPAAAgD0AADA+AACAPQAAgDwAAAg/AAAwPgAACD8AAEA+AACAPQAAsD4AAIA9AABAPgAACD8AALA+AAAIPwAAgDwAAAA9AACAPAAAgD0AADA+AAAAPQAAMD4AAIA9AAAwPgAAAD0AAKg+AAAAPQAAMD4AAAAAAACoPgAAAAAAAEA+AACAPQAAMD4AAIA9AABAPgAACD8AADA+AAAIPwAAAAAAAIA9AACAPAAAgD0AAAAAAAAIPwAAgDwAAAg/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUA"
}
],
"accessors": [
{
"bufferView": 0,
"componentType": 5126,
"count": 24,
"max": [0.03125, 0, 0.3125],
"min": [-0.03125, -1, -0.3125],
"type": "VEC3"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 24,
"max": [1, 1, 1],
"min": [-1, -1, -1],
"type": "VEC3"
},
{
"bufferView": 2,
"componentType": 5126,
"count": 24,
"max": [0.34375, 0.53125],
"min": [0, 0],
"type": "VEC2"
},
{
"bufferView": 3,
"componentType": 5123,
"count": 36,
"max": [23],
"min": [0],
"type": "SCALAR"
}
],
"materials": [
{
"pbrMetallicRoughness": {
"metallicFactor": 0,
"roughnessFactor": 1,
"baseColorTexture": { "index": 0 }
},
"alphaMode": "MASK",
"alphaCutoff": 0.05,
"doubleSided": true
}
],
"textures": [{ "sampler": 0, "source": 0, "name": "cape.png" }],
"samplers": [{ "magFilter": 9728, "minFilter": 9728, "wrapS": 33071, "wrapT": 33071 }],
"images": [
{
"mimeType": "image/png",
"uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAABCRJREFUaEPtlktoE1EUhm/sIzFpYsBgHyn2oRZt0C5ESxWFYutKNyKIWBBR6kpBd3Ur2J0FRdCiuKmIIG4qLnzgQqTUXSlVqdoXTU0lljRpYtOHI/8dznBnOm1mphMwJWeTzJ0795zznf/cex2MMXam2S/ht38siJ8V1lgd5mOBARerc3rZnmK37rwvCyk2nE6wezMRh+4EncH9B1ql+fkU64rPsaBz5bqh4T7Daxn1Kc5zVNeEePJIci0Aq71bzenY6JChwAnA0OBHQ/OtJLnWNwqAy61R1tO3k891ueRKoDKwtqbv7MGbgCnfRgHQoqG9hyX4hc/yiho+/HNqVPFtdj2jwTpQgd/RKT7/0ZUku/o4qAJw50KYXbzr4e+3BioYzc3kwGzAUCLWFw2+UBiCb3bNTDHiPQeAP3CGNmg/6VcSBpDu3hhvDQpOCwABwrQKsRIsqYAChy/EQAXwlPiZ3a3igNPkXIyTjr8o5L7PPdzGf59c+sV/faeWeIIIAHPJ8M3kc7l1K09LKghWAIgqoPZ7djPFTlxb4D6yAgBOQfntrUVWVeRk44tplXJorOVGkVIJTBCTpw9ECFYBIEkyMfmsAXh3u1qi5MlxbbGX/x1ZSCjBAAwgfPr6h49R5UVavk0FXC2wju5p07s6iqEF0PtqSlEf+bKzDRwdgaDU7AkoySJ5Oo/D6ZRq/H0yyuJ/lxkSheG/FgCNm7kL0BoEAG32sqtY1YIHd2/mGzTMVgD3y2slqjgW9xY6ma9AThAARIMiBtOpjADwTWc0bEkB+FZsSTxTW0KBsGPXx0yvrUpEeHAAQIM7wBJLcu8DwHaP/H8i6VSND6SiSjBlRW4WWUypVIBbIgzjVgB0tpdKqLReS0KVPTMTfH0ra2cEgAmAAEd+l1z52LybqwBQYAQAyZPh6gtDW4jjuC4fHx8wXSm0JDbeI95SRYHiFZlUaWVtPQiKAugl5E8ASAUYiy8vc0C474uGasPE5PHc4g0wK/f4obom6UNimol7kTZwQLANwOuqBokqDEeQf4lfvvnNxZJcBTBAGZplWQcQ3tcgwY9oWlXinRW4ugoAgNAWWe6ocn1QvgyRTUb4RZFVljlY/3hSBYCqb6cCZo8ekuATVRZPI/gQW8FWAI1VHganVgDQUajdA6y2gAgAcZFBjTBSpG0AcAqc3VVmCMDTbxGWZvIRCaMNkJ7pFMCzVQD4liCQ8kRFUlvaBkCvL+wYw2ZmNUgCgLajFhRPJlv3ADuS1VtjPQCk823S574ffN/x1dQqy8dHR5SN2Spcbaymz2mjwNYDAD4IQn3TDpVLQIAqNjwAANRrAdoI/3sAOF7Xe1khCNQGVH1bL0JGJb1R52VtD8gVYHkAuVKpbMWZV0C2yObKunkF5EqlshVnXgHZIpsr6/4DlbxcPydnT74AAAAASUVORK5CYII="
}
],
"meshes": [
{
"primitives": [
{
"mode": 4,
"attributes": { "POSITION": 0, "NORMAL": 1, "TEXCOORD_0": 2 },
"indices": 3,
"material": 0
}
]
}
]
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
---
title: 'Skins — Now in Modrinth App!'
summary: 'Customize your look, save your favorite skins, and swap them out in a flash, all within Modrinth App.'
date: 2025-07-06T16:45:00-07:00
---
We're thrilled to roll out Modrinth App **v0.10** with a beta release of one of our most highly requested features, the **Skins page**. The Skins page allows you to manage all of your Minecraft skins directly within Modrinth App. You can see all your saved custom skins and the default Minecraft skins in one convenient place.
![The new skins page, featuring a cute animated player model, your custom skins & default skins.](./skins-page.webp)
Adding a new skin is simple, even Herobrine could do it! When you add or edit a skin, you can **upload** your custom texture file directly from your computer, **choose** between the wide or slim arm style to match your preferred character model, and even **assign** a specific cape to that look for the perfect finishing touch.
The interface makes it easy to preview your changes in real-time with the animated player model, so you can see exactly how your skin will look in-game before saving it.
![The edit skin modal that shows when you go to add or edit a skin.](./edit-skin.webp)
## Fixes and More!
Alongside this major new feature, **v0.10** includes a host of improvements and bug fixes to make your experience smoother. We've updated the news feed to use our new system, fixed issues with project descriptions, and tidied up how data is handled. For a full breakdown of all the changes, you can [check out the complete changelog here.](https://modrinth.com/news/changelog?filter=app)
As the skins feature is in _beta_, we're eager to hear your feedback! **Jump in, give it a try**, and let us know what you think. You can share your thoughts on our [Discord server](https://discord.modrinth.com/) or [start a support chat](https://support.modrinth.com) if you're running into issues.
Thank you! We can't wait to see your skins in action. Happy customizing!

View File

@@ -20,6 +20,7 @@ import { article as new_site_beta } from './new_site_beta'
import { article as plugins_resource_packs } from './plugins_resource_packs'
import { article as pride_campaign_2025 } from './pride_campaign_2025'
import { article as redesign } from './redesign'
import { article as skins_now_in_modrinth_app } from './skins_now_in_modrinth_app'
import { article as two_years_of_modrinth_history } from './two_years_of_modrinth_history'
import { article as two_years_of_modrinth } from './two_years_of_modrinth'
import { article as whats_modrinth } from './whats_modrinth'
@@ -47,6 +48,7 @@ export const articles = [
plugins_resource_packs,
pride_campaign_2025,
redesign,
skins_now_in_modrinth_app,
two_years_of_modrinth_history,
two_years_of_modrinth,
whats_modrinth,

View File

@@ -0,0 +1,2 @@
// AUTO-GENERATED FILE - DO NOT EDIT
export const html = `<p>We're thrilled to roll out Modrinth App <strong>v0.10</strong> with a beta release of one of our most highly requested features, the <strong>Skins page</strong>. The Skins page allows you to manage all of your Minecraft skins directly within Modrinth App. You can see all your saved custom skins and the default Minecraft skins in one convenient place.</p><p><img src="./skins-page.webp" alt="The new skins page, featuring a cute animated player model, your custom skins &amp; default skins."></p><p>Adding a new skin is simple, even Herobrine could do it! When you add or edit a skin, you can <strong>upload</strong> your custom texture file directly from your computer, <strong>choose</strong> between the wide or slim arm style to match your preferred character model, and even <strong>assign</strong> a specific cape to that look for the perfect finishing touch.</p><p>The interface makes it easy to preview your changes in real-time with the animated player model, so you can see exactly how your skin will look in-game before saving it.</p><p><img src="./edit-skin.webp" alt="The edit skin modal that shows when you go to add or edit a skin."></p><h2>Fixes and More!</h2><p>Alongside this major new feature, <strong>v0.10</strong> includes a host of improvements and bug fixes to make your experience smoother. We've updated the news feed to use our new system, fixed issues with project descriptions, and tidied up how data is handled. For a full breakdown of all the changes, you can <a href="https://modrinth.com/news/changelog?filter=app">check out the complete changelog here.</a></p><p>As the skins feature is in <em>beta</em>, we're eager to hear your feedback! <strong>Jump in, give it a try</strong>, and let us know what you think. You can share your thoughts on our <a href="https://discord.modrinth.com/" rel="noopener nofollow ugc">Discord server</a> or <a href="https://support.modrinth.com" rel="noopener nofollow ugc">start a support chat</a> if you're running into issues.</p><p>Thank you! We can't wait to see your skins in action. Happy customizing!</p>`

View File

@@ -0,0 +1,10 @@
// AUTO-GENERATED FILE - DO NOT EDIT
export const article = {
html: () => import(`./skins_now_in_modrinth_app.content`).then((m) => m.html),
title: 'Skins — Now in Modrinth App!',
summary:
'Customize your look, save your favorite skins, and swap them out in a flash, all within Modrinth App.',
date: '2025-07-06T23:45:00.000Z',
slug: 'skins-now-in-modrinth-app',
thumbnail: true,
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -121,6 +121,7 @@ import {
loadTexture as loadSkinTexture,
} from '@modrinth/utils'
import { useDynamicFontSize } from '../../composables'
import { CapeModel, ClassicPlayerModel, SlimPlayerModel } from '@modrinth/assets'
interface AnimationConfig {
baseAnimation: string
@@ -132,9 +133,6 @@ interface AnimationConfig {
const props = withDefaults(
defineProps<{
textureSrc: string
slimModelSrc: string
wideModelSrc: string
capeModelSrc?: string
capeSrc?: string
variant?: 'SLIM' | 'CLASSIC' | 'UNKNOWN'
nametag?: string
@@ -149,7 +147,6 @@ const props = withDefaults(
antialias: false,
scale: 1,
fov: 40,
capeModelSrc: '',
capeSrc: undefined,
initialRotation: 15.75,
nametag: undefined,
@@ -176,7 +173,7 @@ const { fontSize: nametagFontSize } = useDynamicFontSize({
})
const selectedModelSrc = computed(() =>
props.variant === 'SLIM' ? props.slimModelSrc : props.wideModelSrc,
props.variant === 'SLIM' ? SlimPlayerModel : ClassicPlayerModel,
)
const scene = shallowRef<THREE.Object3D | null>(null)
@@ -421,14 +418,9 @@ async function loadModel(src: string) {
}
}
async function loadCape(src: string) {
if (!src) {
capeScene.value = null
return
}
async function loadCape() {
try {
const { scene: loadedCape } = await useGLTF(src)
const { scene: loadedCape } = await useGLTF(CapeModel)
capeScene.value = markRaw(loadedCape)
applyCapeTexture(capeScene.value, capeTexture.value, transparentTexture)
@@ -581,10 +573,6 @@ watch(capeScene, (newCapeScene) => {
})
watch(selectedModelSrc, (src) => loadModel(src))
watch(
() => props.capeModelSrc,
(src) => src && loadCape(src),
)
watch(
() => props.textureSrc,
async (newSrc) => {
@@ -599,6 +587,7 @@ watch(
watch(
() => props.capeSrc,
async (newCapeSrc) => {
await loadCape()
await loadAndApplyCapeTexture(newCapeSrc)
},
)
@@ -631,9 +620,7 @@ onBeforeMount(async () => {
await loadAndApplyCapeTexture(props.capeSrc)
}
if (props.capeModelSrc) {
await loadCape(props.capeModelSrc)
}
await loadCape()
} catch (error) {
console.error('Failed to initialize skin preview:', error)
}

View File

@@ -10,6 +10,30 @@ export type VersionEntry = {
}
const VERSIONS: VersionEntry[] = [
{
date: `2025-07-07T01:10:00-07:00`,
product: 'app',
version: `0.10.3`,
body: `### Improvements
- Added a workaround for Java 8 instances failing to load.
### Known issues
- Java installations will show as 'Failed' when you test them. This is a visual bug, and does not mean the Java installation is not working.`,
},
{
date: `2025-07-06T16:30:00-07:00`,
product: 'app',
version: `0.10.2`,
body: `### Improvements
- Added additional default skins from free official Minecraft skin packs.
- Fixed some parts of the player model on Skins page rendering incorrectly.
- Fixed a number of issues with skin images not loading on macOS.
- Fixed old Forge versions not loading properly.
- Fixed a typo in Appearance settings for hiding Skins page nametag.
### Known issues
- Java installations will show as 'Failed' when you test them. This is a visual bug, and does not mean the Java installation is not working.`,
},
{
date: `2025-07-05T12:00:00-07:00`,
product: 'app',
@@ -25,7 +49,7 @@ const VERSIONS: VersionEntry[] = [
date: `2025-07-04T12:00:00-07:00`,
product: 'app',
version: `0.10.0`,
body: `**Note: This update was pulled due to issues.**
body: `**Note: This update is no longer available to download due to issues, you should use v0.10.1**
### Added
- Added Skins page as a beta feature. There may be some minor bugs with it, but we'd love to get user feedback on this feature as it's been one of our most highly requested features.