Compare commits

..
Author SHA1 Message Date
François-X. T. 72e528b33f Revert "Changelog"
This reverts commit 0c1c7e2fe8.
2026-03-24 16:36:42 -04:00
François-X. T. 0c1c7e2fe8 Changelog 2026-03-24 16:33:09 -04:00
François-X. T. cf16939519 Add Java 25, handle new version format 2026-03-24 16:28:37 -04:00
5 changed files with 198 additions and 218 deletions
+2 -2
View File
@@ -267,10 +267,10 @@
"message": "Enter code..."
},
"auth.sign-in.additional-options": {
"message": "<forgot-password-link>Forgot password</forgot-password-link> • Don't have an account? <create-account-link>Sign up</create-account-link>"
"message": "<forgot-password-link>Forgot password?</forgot-password-link> • <create-account-link>Create an account</create-account-link>"
},
"auth.sign-in.sign-in-with": {
"message": "Sign into Modrinth"
"message": "Sign in with"
},
"auth.sign-in.title": {
"message": "Sign In"
+21 -3
View File
@@ -8,12 +8,12 @@ useSeoMeta({
})
</script>
<template>
<NuxtPage class="auth-container universal-card border border-solid border-surface-5" />
<NuxtPage class="auth-container universal-card" />
</template>
<style>
.auth-container {
width: 28rem;
width: 26rem;
max-width: calc(100% - 2rem);
margin: 1rem auto;
display: flex;
@@ -45,6 +45,24 @@ useSeoMeta({
margin: 0 0 0 0.5rem;
}
.third-party {
display: grid;
gap: var(--gap-md);
grid-template-columns: repeat(2, 1fr);
width: 100%;
}
.third-party .btn {
width: 100%;
vertical-align: middle;
}
.third-party .btn svg {
margin-right: var(--gap-sm);
width: 1.25rem;
height: 1.25rem;
}
@media screen and (max-width: 25.5rem) {
.third-party .btn {
grid-column: 1 / 3;
@@ -66,7 +84,7 @@ useSeoMeta({
align-items: center;
display: flex;
justify-content: center;
gap: var(--gap-xs);
gap: var(--gap-md);
flex-wrap: wrap;
}
+94 -125
View File
@@ -5,7 +5,7 @@
class="fixed left-0 top-0 z-[9999] m-0 h-full w-full border-0 p-0"
></iframe>
</div>
<div v-else c>
<div v-else>
<template v-if="flow && !subtleLauncherRedirectUri">
<label for="two-factor-code">
<span class="label__title">{{ formatMessage(messages.twoFactorCodeLabel) }}</span>
@@ -28,120 +28,98 @@
</button>
</template>
<template v-else>
<div class="flex flex-col gap-6">
<div class="text-center text-2xl font-semibold text-contrast">
{{ formatMessage(messages.signInWithLabel) }}
<h1>{{ formatMessage(messages.signInWithLabel) }}</h1>
<section class="third-party">
<a class="btn" :href="getAuthUrl('discord', redirectTarget)">
<DiscordColorIcon />
<span>Discord</span>
</a>
<a class="btn" :href="getAuthUrl('github', redirectTarget)">
<GitHubColorIcon />
<span>GitHub</span>
</a>
<a class="btn" :href="getAuthUrl('microsoft', redirectTarget)">
<MicrosoftColorIcon />
<span>Microsoft</span>
</a>
<a class="btn" :href="getAuthUrl('google', redirectTarget)">
<GoogleColorIcon />
<span>Google</span>
</a>
<a class="btn" :href="getAuthUrl('steam', redirectTarget)">
<SteamColorIcon />
<span>Steam</span>
</a>
<a class="btn" :href="getAuthUrl('gitlab', redirectTarget)">
<GitLabColorIcon />
<span>GitLab</span>
</a>
</section>
<h1>{{ formatMessage(messages.usePasswordLabel) }}</h1>
<section class="auth-form">
<label for="email" hidden>{{ formatMessage(commonMessages.emailUsernameLabel) }}</label>
<StyledInput
id="email"
v-model="email"
:icon="MailIcon"
type="text"
inputmode="email"
autocomplete="username"
:placeholder="formatMessage(commonMessages.emailUsernameLabel)"
wrapper-class="w-full"
/>
<label for="password" hidden>{{ formatMessage(commonMessages.passwordLabel) }}</label>
<StyledInput
id="password"
v-model="password"
:icon="KeyIcon"
type="password"
autocomplete="current-password"
:placeholder="formatMessage(commonMessages.passwordLabel)"
wrapper-class="w-full"
/>
<HCaptcha v-if="globals?.captcha_enabled" ref="captcha" v-model="token" />
<button
class="btn btn-primary continue-btn centered-btn"
:disabled="globals?.captcha_enabled ? !token : false"
@click="beginPasswordSignIn()"
>
{{ formatMessage(commonMessages.signInButton) }} <RightArrowIcon />
</button>
<div class="auth-form__additional-options">
<IntlFormatted :message-id="messages.additionalOptionsLabel">
<template #forgot-password-link="{ children }">
<NuxtLink
class="text-link"
:to="{
path: '/auth/reset-password',
query: route.query,
}"
>
<component :is="() => children" />
</NuxtLink>
</template>
<template #create-account-link="{ children }">
<NuxtLink
class="text-link"
:to="{
path: '/auth/sign-up',
query: route.query,
}"
>
<component :is="() => children" />
</NuxtLink>
</template>
</IntlFormatted>
</div>
<section class="flex flex-col gap-2.5">
<ButtonStyled>
<a class="!shadow-none" :href="getAuthUrl('google', redirectTarget)">
<GoogleColorIcon />
<span class="ml-1">{{ formatMessage(messages.continueWithProvider, { provider: 'Google' }) }}</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a class="!shadow-none" :href="getAuthUrl('microsoft', redirectTarget)">
<MicrosoftColorIcon />
<span class="ml-1">{{ formatMessage(messages.continueWithProvider, { provider: 'Microsoft' }) }}</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a class="!shadow-none" :href="getAuthUrl('discord', redirectTarget)">
<DiscordColorIcon />
<span class="ml-1">{{ formatMessage(messages.continueWithProvider, { provider: 'Discord' }) }}</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a class="!shadow-none" :href="getAuthUrl('github', redirectTarget)">
<GitHubColorIcon />
<span class="ml-1">{{ formatMessage(messages.continueWithProvider, { provider: 'GitHub' }) }}</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a class="!shadow-none" :href="getAuthUrl('gitlab', redirectTarget)">
<GitLabColorIcon />
<span class="ml-1">{{ formatMessage(messages.continueWithProvider, { provider: 'GitLab' }) }}</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a class="!shadow-none" :href="getAuthUrl('steam', redirectTarget)">
<SteamColorIcon />
<span class="ml-1">{{ formatMessage(messages.continueWithProvider, { provider: 'Steam' }) }}</span>
</a>
</ButtonStyled>
</section>
<div class="h-px w-full bg-surface-5"></div>
<section class="auth-form">
<label for="email" hidden>{{ formatMessage(commonMessages.emailUsernameLabel) }}</label>
<StyledInput
id="email"
v-model="email"
:icon="MailIcon"
type="text"
inputmode="email"
autocomplete="username"
:placeholder="formatMessage(commonMessages.emailUsernameLabel)"
wrapper-class="w-full"
/>
<label for="password" hidden>{{ formatMessage(commonMessages.passwordLabel) }}</label>
<StyledInput
id="password"
v-model="password"
:icon="KeyIcon"
type="password"
autocomplete="current-password"
:placeholder="formatMessage(commonMessages.passwordLabel)"
wrapper-class="w-full"
/>
<HCaptcha
v-if="globals?.captcha_enabled && email && password"
ref="captcha"
v-model="token"
/>
<ButtonStyled color="brand">
<button
class="!w-full"
:disabled="globals?.captcha_enabled ? !token : false"
@click="beginPasswordSignIn()"
>
{{ formatMessage(messages.continueWithEmail) }} <RightArrowIcon />
</button>
</ButtonStyled>
<div class="auth-form__additional-options !text-base">
<IntlFormatted :message-id="messages.additionalOptionsLabel">
<template #forgot-password-link="{ children }">
<NuxtLink
class="text-link"
:to="{
path: '/auth/reset-password',
query: route.query,
}"
>
<component :is="() => children" />
</NuxtLink>
</template>
<template #create-account-link="{ children }">
<NuxtLink
class="inline text-link"
:to="{
path: '/auth/sign-up',
query: route.query,
}"
>
<component :is="() => children" />
</NuxtLink>
</template>
</IntlFormatted>
</div>
</section>
</div>
</section>
</template>
</div>
</template>
@@ -159,7 +137,6 @@ import {
SteamColorIcon,
} from '@modrinth/assets'
import {
ButtonStyled,
commonMessages,
defineMessages,
injectModrinthClient,
@@ -182,11 +159,11 @@ const messages = defineMessages({
additionalOptionsLabel: {
id: 'auth.sign-in.additional-options',
defaultMessage:
"<forgot-password-link>Forgot password</forgot-password-link> • Don't have an account? <create-account-link>Sign up</create-account-link>",
'<forgot-password-link>Forgot password?</forgot-password-link> • <create-account-link>Create an account</create-account-link>',
},
signInWithLabel: {
id: 'auth.sign-in.sign-in-with',
defaultMessage: 'Sign into Modrinth',
defaultMessage: 'Sign in with',
},
signInTitle: {
id: 'auth.sign-in.title',
@@ -208,14 +185,6 @@ const messages = defineMessages({
id: 'auth.sign-in.use-password',
defaultMessage: 'Or use a password',
},
continueWithProvider: {
id: 'auth.continue-with-provider',
defaultMessage: 'Continue with {provider}',
},
continueWithEmail: {
id: 'auth.sign-in.continue-with-email',
defaultMessage: 'Continue with Email',
},
})
useHead({
+43 -82
View File
@@ -1,56 +1,37 @@
<template>
<div class="flex flex-col gap-6">
<div class="text-center text-2xl font-semibold text-contrast">{{ formatMessage(messages.signUpWithTitle) }}</div>
<section class="flex flex-col gap-2.5">
<ButtonStyled>
<a class="!shadow-none" :href="getAuthUrl('google', redirectTarget)">
<GoogleColorIcon />
<span>{{ formatMessage(messages.continueWithProvider, { provider: 'Google' }) }}</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a class="!shadow-none" :href="getAuthUrl('microsoft', redirectTarget)">
<MicrosoftColorIcon />
<span>{{ formatMessage(messages.continueWithProvider, { provider: 'Microsoft' }) }}</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a class="!shadow-none" :href="getAuthUrl('discord', redirectTarget)">
<DiscordColorIcon />
<span>{{ formatMessage(messages.continueWithProvider, { provider: 'Discord' }) }}</span>
</a>
</ButtonStyled>
<template v-if="showOtherOptions">
<ButtonStyled>
<a class="!shadow-none" :href="getAuthUrl('github', redirectTarget)">
<GitHubColorIcon />
<span>{{ formatMessage(messages.continueWithProvider, { provider: 'GitHub' }) }}</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a class="!shadow-none" :href="getAuthUrl('gitlab', redirectTarget)">
<GitLabColorIcon />
<span>{{ formatMessage(messages.continueWithProvider, { provider: 'GitLab' }) }}</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a class="!shadow-none" :href="getAuthUrl('steam', redirectTarget)">
<SteamColorIcon />
<span>{{ formatMessage(messages.continueWithProvider, { provider: 'Steam' }) }}</span>
</a>
</ButtonStyled>
</template>
<button
class="mx-auto -mb-3 bg-transparent pt-1 text-center text-base font-semibold text-secondary transition-all hover:text-primary"
@click="showOtherOptions = !showOtherOptions"
>
{{ showOtherOptions ? formatMessage(messages.showFewerOptions) : formatMessage(messages.showOtherOptions) }}
</button>
<div>
<h1>{{ formatMessage(messages.signUpWithTitle) }}</h1>
<section class="third-party">
<a class="btn discord-btn" :href="getAuthUrl('discord', redirectTarget)">
<DiscordColorIcon />
<span>Discord</span>
</a>
<a class="btn" :href="getAuthUrl('github', redirectTarget)">
<GitHubColorIcon />
<span>GitHub</span>
</a>
<a class="btn" :href="getAuthUrl('microsoft', redirectTarget)">
<MicrosoftColorIcon />
<span>Microsoft</span>
</a>
<a class="btn" :href="getAuthUrl('google', redirectTarget)">
<GoogleColorIcon />
<span>Google</span>
</a>
<a class="btn" :href="getAuthUrl('steam', redirectTarget)">
<SteamColorIcon />
<span>Steam</span>
</a>
<a class="btn" :href="getAuthUrl('gitlab', redirectTarget)">
<GitLabColorIcon />
<span>GitLab</span>
</a>
</section>
<div class="h-px w-full bg-surface-5"></div>
<h1>{{ formatMessage(messages.createAccountTitle) }}</h1>
<section class="flex flex-col gap-2.5">
<section class="auth-form">
<label for="email" hidden>{{ formatMessage(commonMessages.emailLabel) }}</label>
<StyledInput
id="email"
@@ -117,21 +98,15 @@
</IntlFormatted>
</p>
<HCaptcha
v-if="globals?.captcha_enabled && email && password && confirmPassword && username"
ref="captcha"
v-model="token"
/>
<HCaptcha v-if="globals?.captcha_enabled" ref="captcha" v-model="token" />
<ButtonStyled color="brand">
<button
class="!w-full"
:disabled="globals?.captcha_enabled ? !token : false"
@click="createAccount"
>
{{ formatMessage(messages.continueWithEmail) }} <RightArrowIcon />
</button>
</ButtonStyled>
<button
class="btn btn-primary continue-btn centered-btn"
:disabled="globals?.captcha_enabled ? !token : false"
@click="createAccount"
>
{{ formatMessage(messages.createAccountButton) }} <RightArrowIcon />
</button>
<div class="auth-form__additional-options">
{{ formatMessage(messages.alreadyHaveAccountLabel) }}
@@ -163,7 +138,6 @@ import {
UserIcon,
} from '@modrinth/assets'
import {
ButtonStyled,
Checkbox,
commonMessages,
defineMessages,
@@ -189,7 +163,11 @@ const messages = defineMessages({
},
signUpWithTitle: {
id: 'auth.sign-up.title.sign-up-with',
defaultMessage: 'Create an Account',
defaultMessage: 'Sign up with',
},
createAccountTitle: {
id: 'auth.sign-up.title.create-account',
defaultMessage: 'Or create an account yourself',
},
subscribeLabel: {
id: 'auth.sign-up.subscribe.label',
@@ -208,22 +186,6 @@ const messages = defineMessages({
id: 'auth.sign-up.sign-in-option.title',
defaultMessage: 'Already have an account?',
},
continueWithProvider: {
id: 'auth.continue-with-provider',
defaultMessage: 'Continue with {provider}',
},
continueWithEmail: {
id: 'auth.sign-up.continue-with-email',
defaultMessage: 'Continue with Email',
},
showFewerOptions: {
id: 'auth.sign-up.show-fewer-options',
defaultMessage: 'Show fewer options',
},
showOtherOptions: {
id: 'auth.sign-up.show-other-options',
defaultMessage: 'Show other options',
},
})
useHead({
@@ -234,7 +196,6 @@ const auth = await useAuth()
const route = useNativeRoute()
const redirectTarget = route.query.redirect
const showOtherOptions = ref(false)
if (auth.value.user) {
await navigateTo('/dashboard')
@@ -150,6 +150,7 @@ const JAVA_VERSIONS = [
{ value: 11, label: 'Java 11' },
{ value: 17, label: 'Java 17' },
{ value: 21, label: 'Java 21' },
{ value: 25, label: 'Java 25' },
]
const JRE_VENDORS: { value: Archon.Content.v1.JreVendor; label: string }[] = [
@@ -203,19 +204,50 @@ const hasUnsavedChanges = computed(
// Java version filtering
const showAllVersions = ref(false)
type MinecraftReleaseVersion = {
major: number
minor: number
}
function parseMinecraftReleaseVersion(version: string): MinecraftReleaseVersion | null {
const [majorPart, minorPart] = version.split('.')
if (!majorPart || !minorPart) return null
const major = Number(majorPart)
const minor = Number(minorPart)
if (!Number.isInteger(major) || !Number.isInteger(minor)) return null
return { major, minor }
}
function filterJavaVersions(compatibleVersions: number[]) {
return JAVA_VERSIONS.filter((version) => compatibleVersions.includes(version.value))
}
const displayedJavaVersions = computed(() => {
if (showAllVersions.value) return JAVA_VERSIONS
const mcVersion = server.value?.mc_version ?? ''
if (!mcVersion) return JAVA_VERSIONS
const [, minor] = mcVersion.split('.').map(Number)
const releaseVersion = parseMinecraftReleaseVersion(mcVersion)
if (!releaseVersion) return JAVA_VERSIONS
if (minor >= 20) return JAVA_VERSIONS.filter((v) => v.value === 21)
if (minor >= 17) return JAVA_VERSIONS.filter((v) => [17, 21].includes(v.value))
if (minor >= 12) return JAVA_VERSIONS
if (minor >= 6) return JAVA_VERSIONS.filter((v) => [8, 11].includes(v.value))
return JAVA_VERSIONS.filter((v) => v.value === 8)
if (releaseVersion.major > 1) {
if (releaseVersion.major >= 26) {
return filterJavaVersions([25])
}
return JAVA_VERSIONS
}
if (releaseVersion.minor >= 20) return filterJavaVersions([21])
if (releaseVersion.minor >= 17) return filterJavaVersions([17, 21])
if (releaseVersion.minor >= 12) return filterJavaVersions([8, 11, 17, 21])
if (releaseVersion.minor >= 6) return filterJavaVersions([8, 11])
return filterJavaVersions([8])
})
// Save mutation