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

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>