mirror of
https://github.com/modrinth/code.git
synced 2026-07-31 21:26:40 +00:00
Compare commits
62
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb8f5eacbd | ||
|
|
71dee4de40 | ||
|
|
f74fad0cae | ||
|
|
07e81ac036 | ||
|
|
6e7835fb35 | ||
|
|
2f95c4c441 | ||
|
|
451b2d0e44 | ||
|
|
215643c846 | ||
|
|
d8b1415f9c | ||
|
|
3eeb549d20 | ||
|
|
c3fe7b4232 | ||
|
|
079a10bba9 | ||
|
|
3c3d5702ba | ||
|
|
a34576a2c3 | ||
|
|
d8e4915a31 | ||
|
|
ed723fa186 | ||
|
|
d6c8d4475b | ||
|
|
302b60d89c | ||
|
|
f106dc580f | ||
|
|
244c263e40 | ||
|
|
48bb44155d | ||
|
|
3f2e76ae7e | ||
|
|
6479eca0e9 | ||
|
|
8767bc9184 | ||
|
|
d1185414b6 | ||
|
|
510439acbf | ||
|
|
c564495e11 | ||
|
|
8dd1490c8a | ||
|
|
b72bc18a6b | ||
|
|
cee942dcef | ||
|
|
02a7774722 | ||
|
|
e9eb98f97e | ||
|
|
e5bbd9d409 | ||
|
|
83dddfd512 | ||
|
|
0ffdabb2a3 | ||
|
|
d87f93fdd5 | ||
|
|
f6fa486dea | ||
|
|
78d978b22e | ||
|
|
71559d62c8 | ||
|
|
538eda6976 | ||
|
|
882b01c7c9 | ||
|
|
a192f7857e | ||
|
|
3bf0f91cf0 | ||
|
|
3083dcd932 | ||
|
|
c8c79a6c74 | ||
|
|
f5462b6dd8 | ||
|
|
1ddbae40b7 | ||
|
|
a4f3c63fcc | ||
|
|
02e10be4db | ||
|
|
e0056bfc40 | ||
|
|
612934bf34 | ||
|
|
86d377b915 | ||
|
|
ad99ac039b | ||
|
|
6d3fdb680c | ||
|
|
840b556c51 | ||
|
|
12e5f02e57 | ||
|
|
ca1b36efde | ||
|
|
a5417e0851 | ||
|
|
45398c546c | ||
|
|
7e769c720b | ||
|
|
c1c86e3b72 | ||
|
|
c7602602e5 |
@@ -164,7 +164,7 @@ jobs:
|
||||
with:
|
||||
context: ./apps/labrinth/docker-stage
|
||||
file: ./apps/labrinth/Dockerfile
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
push: true
|
||||
tags: ${{ steps.docker-meta.outputs.tags }}
|
||||
labels: ${{ steps.docker-meta.outputs.labels }}
|
||||
annotations: ${{ steps.docker-meta.outputs.annotations }}
|
||||
|
||||
Generated
+4
-24
@@ -3059,29 +3059,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "elasticsearch"
|
||||
version = "9.1.0-alpha.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12bb303aa6e1d28c0c86b6fbfe484fd0fd3f512629aeed1ac4f6b85f81d9834a"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"dyn-clone",
|
||||
"flate2",
|
||||
"lazy_static",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"reqwest 0.12.24",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"tokio",
|
||||
"url",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.13.8"
|
||||
@@ -5275,7 +5252,6 @@ dependencies = [
|
||||
"dotenv-build",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"elasticsearch",
|
||||
"eyre",
|
||||
"futures",
|
||||
"futures-util",
|
||||
@@ -10589,6 +10565,8 @@ version = "1.0.0-local"
|
||||
dependencies = [
|
||||
"async_zip",
|
||||
"chrono",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics",
|
||||
"daedalus",
|
||||
"dashmap",
|
||||
"either",
|
||||
@@ -10596,6 +10574,7 @@ dependencies = [
|
||||
"hyper 1.7.0",
|
||||
"hyper-util",
|
||||
"native-dialog",
|
||||
"objc2-app-kit",
|
||||
"paste",
|
||||
"path-util",
|
||||
"serde",
|
||||
@@ -10621,6 +10600,7 @@ dependencies = [
|
||||
"urlencoding",
|
||||
"uuid 1.18.1",
|
||||
"webview2-com",
|
||||
"windows",
|
||||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
|
||||
+3
-1
@@ -64,6 +64,8 @@ clickhouse = "0.14.0"
|
||||
color-eyre = "0.6.5"
|
||||
color-thief = "0.2.2"
|
||||
const_format = "0.2.34"
|
||||
core-foundation = "0.10.1"
|
||||
core-graphics = "0.24.0"
|
||||
daedalus = { path = "packages/daedalus" }
|
||||
darling = { version = "0.23" }
|
||||
dashmap = "6.1.0"
|
||||
@@ -77,7 +79,6 @@ dotenv-build = "0.1.1"
|
||||
dotenvy = "0.15.7"
|
||||
dunce = "1.0.5"
|
||||
either = "1.15.0"
|
||||
elasticsearch = "9.1.0-alpha.1"
|
||||
encoding_rs = "0.8.35"
|
||||
enumset = "1.1.10"
|
||||
eyre = "0.6.12"
|
||||
@@ -124,6 +125,7 @@ murmur2 = "0.1.0"
|
||||
native-dialog = "0.9.2"
|
||||
notify = { version = "8.2.0", default-features = false }
|
||||
notify-debouncer-mini = { version = "0.7.0", default-features = false }
|
||||
objc2-app-kit = { version = "0.3.2", default-features = false }
|
||||
p256 = "0.13.2"
|
||||
parking_lot = "0.12.5"
|
||||
paste = "1.0.15"
|
||||
|
||||
@@ -13,14 +13,13 @@
|
||||
"test": "vue-tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@intercom/messenger-js-sdk": "^0.0.14",
|
||||
"@modrinth/api-client": "workspace:^",
|
||||
"@modrinth/assets": "workspace:*",
|
||||
"@modrinth/ui": "workspace:*",
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"@sentry/vue": "^8.27.0",
|
||||
"@sfirew/minecraft-motd-parser": "^1.1.6",
|
||||
"@tanstack/vue-query": "^5.90.7",
|
||||
"@tanstack/vue-query": "5.90.7",
|
||||
"@tauri-apps/api": "^2.5.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.2.1",
|
||||
"@tauri-apps/plugin-fs": "^2.4.5",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { Intercom, shutdown as shutdownIntercom } from '@intercom/messenger-js-sdk'
|
||||
import {
|
||||
AuthFeature,
|
||||
ModrinthApiError,
|
||||
NodeAuthFeature,
|
||||
nodeAuthState,
|
||||
PanelVersionFeature,
|
||||
@@ -52,6 +52,7 @@ import {
|
||||
providePopupNotificationManager,
|
||||
useDebugLogger,
|
||||
useFormatBytes,
|
||||
useHostingIntercom,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { renderString } from '@modrinth/utils'
|
||||
@@ -87,7 +88,6 @@ import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
|
||||
import QuickInstanceSwitcher from '@/components/ui/QuickInstanceSwitcher.vue'
|
||||
import SplashScreen from '@/components/ui/SplashScreen.vue'
|
||||
import WindowControls from '@/components/ui/WindowControls.vue'
|
||||
import { useIntercomPositioning } from '@/composables/intercom-positioning'
|
||||
import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
|
||||
import { config } from '@/config'
|
||||
import { hide_ads_window, init_ads_window, show_ads_window } from '@/helpers/ads.js'
|
||||
@@ -98,6 +98,7 @@ import { command_listener, warning_listener } from '@/helpers/events.js'
|
||||
import { cancelLogin, get as getCreds, login, logout } from '@/helpers/mr_auth.ts'
|
||||
import { create_profile_and_install_from_file } from '@/helpers/pack'
|
||||
import { list } from '@/helpers/profile.js'
|
||||
import { mergeUrlQuery, parseModrinthLink } from '@/helpers/project-links.ts'
|
||||
import { get as getSettings, set as setSettings } from '@/helpers/settings.ts'
|
||||
import { get_opening_command, initialize_state } from '@/helpers/state'
|
||||
import {
|
||||
@@ -107,6 +108,7 @@ import {
|
||||
getUpdateSize,
|
||||
isDev,
|
||||
isNetworkMetered,
|
||||
setRestartAfterPendingUpdate,
|
||||
} from '@/helpers/utils.js'
|
||||
import i18n from '@/i18n.config'
|
||||
import { createContentInstall, provideContentInstall } from '@/providers/content-install'
|
||||
@@ -129,15 +131,36 @@ import { AppPopupNotificationManager } from './providers/app-popup-notifications
|
||||
const themeStore = useTheming()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const intercomBubblePositioning = useIntercomPositioning({ route, themeStore })
|
||||
const {
|
||||
sidebarToggled,
|
||||
forceSidebar,
|
||||
sidebarVisible,
|
||||
intercomBubblePosition,
|
||||
updateIntercomBubbleStyles,
|
||||
clearIntercomBubbleStyles,
|
||||
} = intercomBubblePositioning
|
||||
const APP_LEFT_NAV_WIDTH = '4rem'
|
||||
const APP_SIDEBAR_WIDTH = 300
|
||||
const INTERCOM_BUBBLE_DEFAULT_PADDING = 20
|
||||
const credentials = ref()
|
||||
const sidebarToggled = ref(true)
|
||||
const unsubscribeSidebarToggle = themeStore.$subscribe(() => {
|
||||
sidebarToggled.value = !themeStore.toggleSidebar
|
||||
})
|
||||
const forceSidebar = computed(
|
||||
() => route.path.startsWith('/browse') || route.path.startsWith('/project'),
|
||||
)
|
||||
const sidebarVisible = computed(() => sidebarToggled.value || forceSidebar.value)
|
||||
const hostingRouteActive = computed(() => route.path.startsWith('/hosting'))
|
||||
const hostingIntercomIdentityKey = computed(() => {
|
||||
const rawServerId = route.params.id
|
||||
const serverId = Array.isArray(rawServerId) ? rawServerId[0] : rawServerId
|
||||
const userId = credentials.value?.user_id ?? credentials.value?.user?.id ?? 'anonymous'
|
||||
return `${userId}:${serverId ?? 'hosting'}`
|
||||
})
|
||||
const hostingIntercom = useHostingIntercom({
|
||||
enabled: computed(() => hostingRouteActive.value && !!credentials.value?.session),
|
||||
appId: 'ykeritl9',
|
||||
fetchToken: fetchIntercomToken,
|
||||
identityKey: hostingIntercomIdentityKey,
|
||||
horizontalPadding: computed(() =>
|
||||
sidebarVisible.value
|
||||
? APP_SIDEBAR_WIDTH + INTERCOM_BUBBLE_DEFAULT_PADDING
|
||||
: INTERCOM_BUBBLE_DEFAULT_PADDING,
|
||||
),
|
||||
})
|
||||
|
||||
const notificationManager = new AppNotificationManager()
|
||||
provideNotificationManager(notificationManager)
|
||||
@@ -172,7 +195,11 @@ provideModrinthClient(tauriApiClient)
|
||||
providePageContext({
|
||||
hierarchicalSidebarAvailable: ref(true),
|
||||
showAds: ref(false),
|
||||
...intercomBubblePositioning.pageContext,
|
||||
floatingActionBarOffsets: {
|
||||
left: ref(APP_LEFT_NAV_WIDTH),
|
||||
right: computed(() => (sidebarVisible.value ? `${APP_SIDEBAR_WIDTH}px` : '0px')),
|
||||
},
|
||||
intercomBubble: hostingIntercom.intercomBubble,
|
||||
featureFlags: {
|
||||
serverRamAsBytesAlwaysOn: computed(() =>
|
||||
themeStore.getFeatureFlag('server_ram_as_bytes_always_on'),
|
||||
@@ -256,8 +283,7 @@ onMounted(async () => {
|
||||
onUnmounted(async () => {
|
||||
document.querySelector('body').removeEventListener('click', handleClick)
|
||||
document.querySelector('body').removeEventListener('auxclick', handleAuxClick)
|
||||
shutdownHostingIntercom()
|
||||
clearIntercomBubbleStyles()
|
||||
unsubscribeSidebarToggle()
|
||||
|
||||
await unlistenUpdateDownload?.()
|
||||
})
|
||||
@@ -590,8 +616,6 @@ const incompatibilityWarningModal = ref()
|
||||
const installToPlayModal = ref()
|
||||
const updateToPlayModal = ref()
|
||||
|
||||
const credentials = ref()
|
||||
|
||||
const modrinthLoginFlowWaitModal = ref()
|
||||
|
||||
setupAuthProvider(credentials, async (_redirectPath) => {
|
||||
@@ -661,10 +685,6 @@ const hasPlus = computed(
|
||||
const showAd = computed(
|
||||
() => sidebarVisible.value && !hasPlus.value && credentials.value !== undefined,
|
||||
)
|
||||
const hostingRouteActive = computed(() => route.path.startsWith('/hosting'))
|
||||
|
||||
let intercomBooting = false
|
||||
let intercomBooted = false
|
||||
|
||||
async function fetchIntercomToken() {
|
||||
const creds = await getCreds()
|
||||
@@ -673,8 +693,10 @@ async function fetchIntercomToken() {
|
||||
}
|
||||
|
||||
const params = new URLSearchParams()
|
||||
if (route.path.startsWith('/hosting/manage/') && typeof route.params.id === 'string') {
|
||||
params.set('server_id', route.params.id)
|
||||
const rawServerId = route.params.id
|
||||
const serverId = Array.isArray(rawServerId) ? rawServerId[0] : rawServerId
|
||||
if (route.path.startsWith('/hosting/manage/') && typeof serverId === 'string') {
|
||||
params.set('server_id', serverId)
|
||||
}
|
||||
const query = params.size > 0 ? `?${params.toString()}` : ''
|
||||
|
||||
@@ -690,69 +712,6 @@ async function fetchIntercomToken() {
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
async function bootIntercom() {
|
||||
if (
|
||||
intercomBooting ||
|
||||
intercomBooted ||
|
||||
!hostingRouteActive.value ||
|
||||
!credentials.value?.session
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
intercomBooting = true
|
||||
console.debug('[APP][INTERCOM] initializing secure support chat')
|
||||
try {
|
||||
const { token } = await fetchIntercomToken()
|
||||
Intercom({
|
||||
app_id: 'ykeritl9',
|
||||
intercom_user_jwt: token,
|
||||
session_duration: 1000 * 60 * 60 * 24,
|
||||
alignment: 'right',
|
||||
horizontal_padding: intercomBubblePosition.value.horizontalPadding,
|
||||
vertical_padding: intercomBubblePosition.value.verticalPadding,
|
||||
})
|
||||
intercomBooted = true
|
||||
} catch (error) {
|
||||
console.warn('[APP][INTERCOM] failed to initialize secure support chat', error)
|
||||
} finally {
|
||||
intercomBooting = false
|
||||
}
|
||||
}
|
||||
|
||||
function shutdownHostingIntercom() {
|
||||
if (!intercomBooted && !intercomBooting) return
|
||||
shutdownIntercom()
|
||||
intercomBooting = false
|
||||
intercomBooted = false
|
||||
}
|
||||
|
||||
watch(
|
||||
intercomBubblePosition,
|
||||
(position) => {
|
||||
updateIntercomBubbleStyles(position)
|
||||
if (intercomBooted) {
|
||||
window.Intercom?.('update', {
|
||||
horizontal_padding: position.horizontalPadding,
|
||||
vertical_padding: position.verticalPadding,
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
[hostingRouteActive, credentials],
|
||||
([active]) => {
|
||||
if (active) {
|
||||
void bootIntercom()
|
||||
} else {
|
||||
shutdownHostingIntercom()
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(showAd, () => {
|
||||
if (!showAd.value) {
|
||||
hide_ads_window(true)
|
||||
@@ -1023,11 +982,40 @@ async function downloadUpdate(versionToDownload) {
|
||||
|
||||
async function installUpdate() {
|
||||
restarting.value = true
|
||||
try {
|
||||
await setRestartAfterPendingUpdate(true)
|
||||
} catch (e) {
|
||||
restarting.value = false
|
||||
handleError(e)
|
||||
return
|
||||
}
|
||||
setTimeout(async () => {
|
||||
await handleClose()
|
||||
}, 250)
|
||||
}
|
||||
|
||||
async function openModrinthProjectLinkInApp(parsed) {
|
||||
const { slug, pathSuffix, url } = parsed
|
||||
const loadToken = loading.begin()
|
||||
try {
|
||||
const { id } = await tauriApiClient.labrinth.projects_v2.check(slug)
|
||||
const query = mergeUrlQuery(route.query, url)
|
||||
await router.push({
|
||||
path: `/project/${id}${pathSuffix}`,
|
||||
query,
|
||||
hash: url.hash || undefined,
|
||||
})
|
||||
} catch (err) {
|
||||
if (err instanceof ModrinthApiError && err.statusCode === 404) {
|
||||
openUrl(url.href)
|
||||
} else {
|
||||
handleError(err)
|
||||
}
|
||||
} finally {
|
||||
loading.end(loadToken)
|
||||
}
|
||||
}
|
||||
|
||||
function handleClick(e) {
|
||||
let target = e.target
|
||||
while (target != null) {
|
||||
@@ -1040,7 +1028,12 @@ function handleClick(e) {
|
||||
!target.href.startsWith('https://tauri.localhost') &&
|
||||
!target.href.startsWith('http://tauri.localhost')
|
||||
) {
|
||||
openUrl(target.href)
|
||||
const parsed = parseModrinthLink(target.href)
|
||||
if (target.target !== '_blank' && parsed) {
|
||||
void openModrinthProjectLinkInApp(parsed)
|
||||
} else {
|
||||
openUrl(target.href)
|
||||
}
|
||||
}
|
||||
e.preventDefault()
|
||||
break
|
||||
@@ -1647,6 +1640,12 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
|
||||
&.app-contents::before {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
*,
|
||||
:deep(*) {
|
||||
box-shadow: none !important;
|
||||
--tw-drop-shadow:;
|
||||
}
|
||||
}
|
||||
|
||||
.app-sidebar::before {
|
||||
@@ -1779,14 +1778,6 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
|
||||
--os-handle-bg-active: var(--color-scrollbar) !important;
|
||||
}
|
||||
|
||||
.intercom-lightweight-app-launcher,
|
||||
.intercom-launcher-frame,
|
||||
iframe[name='intercom-launcher-frame'] {
|
||||
right: var(--app-support-launcher-right, 20px) !important;
|
||||
bottom: var(--app-support-launcher-bottom, 20px) !important;
|
||||
z-index: 9 !important;
|
||||
}
|
||||
|
||||
.mac {
|
||||
.app-grid-statusbar {
|
||||
padding-left: 5rem;
|
||||
|
||||
@@ -37,6 +37,6 @@ defineProps({
|
||||
|
||||
.progress-bar__fill {
|
||||
height: 100%;
|
||||
transition: width 0.3s;
|
||||
transition: width 0.3s ease-out;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -126,7 +126,7 @@ watch(
|
||||
function fakeLoadingIncrease() {
|
||||
if (loadingProgress.value < 95) {
|
||||
setTimeout(() => {
|
||||
loadingProgress.value += 1
|
||||
loadingProgress.value += 2
|
||||
fakeLoadingIncrease()
|
||||
}, 5)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
hover-color-fill="background"
|
||||
circular
|
||||
>
|
||||
<button class="relative expanded-button" @click="handleClose">
|
||||
<button class="relative expanded-button close-button" @click="handleClose">
|
||||
<XIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
@@ -82,8 +82,12 @@ const handleClose = async () => {
|
||||
</script>
|
||||
<style scoped>
|
||||
.expanded-button::before {
|
||||
inset: -6px;
|
||||
inset: -9px -6px;
|
||||
content: '';
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.expanded-button.close-button::before {
|
||||
inset: -9px -9px -9px -6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -35,7 +35,7 @@ const { handleError } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const { instance, offline, isMinecraftServer, onUnlinked } = injectInstanceSettings()
|
||||
const { instance, offline, isMinecraftServer, onUnlinked, closeModal } = injectInstanceSettings()
|
||||
|
||||
const [
|
||||
fabric_versions,
|
||||
@@ -113,6 +113,7 @@ provideAppBackup({
|
||||
})
|
||||
|
||||
provideInstallationSettings({
|
||||
closeSettings: closeModal,
|
||||
loading: ref(false),
|
||||
installationInfo: computed(() => {
|
||||
const rows = [
|
||||
|
||||
@@ -45,12 +45,19 @@ const isMinecraftServer = ref(false)
|
||||
const handleUnlinked = () => emit('unlinked')
|
||||
|
||||
const instanceRef = computed(() => props.instance)
|
||||
const queryClient = useQueryClient()
|
||||
const tabbedModal = ref<InstanceType<typeof TabbedModal> | null>(null)
|
||||
|
||||
function hide() {
|
||||
tabbedModal.value?.hide()
|
||||
}
|
||||
|
||||
provideInstanceSettings({
|
||||
instance: instanceRef,
|
||||
offline: props.offline,
|
||||
isMinecraftServer,
|
||||
onUnlinked: handleUnlinked,
|
||||
closeModal: hide,
|
||||
})
|
||||
|
||||
watch(
|
||||
@@ -113,9 +120,6 @@ const tabs = computed<TabbedModalTab[]>(() => [
|
||||
},
|
||||
])
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const tabbedModal = ref<InstanceType<typeof TabbedModal> | null>(null)
|
||||
|
||||
function show(tabIndex?: number) {
|
||||
if (props.instance.linked_data?.project_id) {
|
||||
queryClient.prefetchQuery({
|
||||
@@ -129,7 +133,7 @@ function show(tabIndex?: number) {
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ show })
|
||||
defineExpose({ show, hide })
|
||||
</script>
|
||||
<template>
|
||||
<TabbedModal
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
import { computed, onUnmounted, ref } from 'vue'
|
||||
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
|
||||
interface ThemeStore {
|
||||
toggleSidebar: boolean
|
||||
$subscribe: (callback: () => void) => () => void
|
||||
}
|
||||
|
||||
interface IntercomBubblePosition {
|
||||
horizontalPadding: number
|
||||
verticalPadding: number
|
||||
}
|
||||
|
||||
const APP_LEFT_NAV_WIDTH = '4rem'
|
||||
const APP_SIDEBAR_WIDTH = 300
|
||||
const INTERCOM_BUBBLE_DEFAULT_PADDING = 20
|
||||
const INTERCOM_BUBBLE_WIDTH = 72
|
||||
const INTERCOM_BUBBLE_RIGHT_VAR = '--app-support-launcher-right'
|
||||
const INTERCOM_BUBBLE_BOTTOM_VAR = '--app-support-launcher-bottom'
|
||||
|
||||
export function useIntercomPositioning({
|
||||
route,
|
||||
themeStore,
|
||||
}: {
|
||||
route: RouteLocationNormalizedLoaded
|
||||
themeStore: ThemeStore
|
||||
}) {
|
||||
const sidebarToggled = ref(true)
|
||||
const unsubscribeSidebarToggle = themeStore.$subscribe(() => {
|
||||
sidebarToggled.value = !themeStore.toggleSidebar
|
||||
})
|
||||
|
||||
onUnmounted(unsubscribeSidebarToggle)
|
||||
|
||||
const forceSidebar = computed(
|
||||
() => route.path.startsWith('/browse') || route.path.startsWith('/project'),
|
||||
)
|
||||
const sidebarVisible = computed(() => sidebarToggled.value || forceSidebar.value)
|
||||
const defaultIntercomBubbleHorizontalPadding = computed(() =>
|
||||
sidebarVisible.value
|
||||
? APP_SIDEBAR_WIDTH + INTERCOM_BUBBLE_DEFAULT_PADDING
|
||||
: INTERCOM_BUBBLE_DEFAULT_PADDING,
|
||||
)
|
||||
const intercomBubbleRequestedHorizontalPadding = ref<number | null>(null)
|
||||
const intercomBubbleHorizontalPadding = computed(
|
||||
() =>
|
||||
intercomBubbleRequestedHorizontalPadding.value ??
|
||||
defaultIntercomBubbleHorizontalPadding.value,
|
||||
)
|
||||
const intercomBubbleVerticalClearance = ref<number | null>(null)
|
||||
const intercomBubblePosition = computed(() => ({
|
||||
horizontalPadding: intercomBubbleHorizontalPadding.value,
|
||||
verticalPadding: intercomBubbleVerticalClearance.value ?? INTERCOM_BUBBLE_DEFAULT_PADDING,
|
||||
}))
|
||||
const intercomBubbleHorizontalPaddingRequests = new Map<symbol, number>()
|
||||
const intercomBubbleClearanceRequests = new Map<symbol, number>()
|
||||
|
||||
function requestIntercomBubbleHorizontalPadding(id: symbol, padding: number | null) {
|
||||
if (padding === null) {
|
||||
intercomBubbleHorizontalPaddingRequests.delete(id)
|
||||
} else {
|
||||
intercomBubbleHorizontalPaddingRequests.set(id, padding)
|
||||
}
|
||||
|
||||
intercomBubbleRequestedHorizontalPadding.value =
|
||||
intercomBubbleHorizontalPaddingRequests.size > 0
|
||||
? Math.max(...intercomBubbleHorizontalPaddingRequests.values())
|
||||
: null
|
||||
}
|
||||
|
||||
function requestIntercomBubbleVerticalClearance(id: symbol, clearance: number | null) {
|
||||
if (clearance === null) {
|
||||
intercomBubbleClearanceRequests.delete(id)
|
||||
} else {
|
||||
intercomBubbleClearanceRequests.set(id, clearance)
|
||||
}
|
||||
|
||||
intercomBubbleVerticalClearance.value =
|
||||
intercomBubbleClearanceRequests.size > 0
|
||||
? Math.max(...intercomBubbleClearanceRequests.values())
|
||||
: null
|
||||
}
|
||||
|
||||
function updateIntercomBubbleStyles({
|
||||
horizontalPadding,
|
||||
verticalPadding,
|
||||
}: IntercomBubblePosition) {
|
||||
if (typeof document === 'undefined') return
|
||||
|
||||
document.documentElement.style.setProperty(INTERCOM_BUBBLE_RIGHT_VAR, `${horizontalPadding}px`)
|
||||
document.documentElement.style.setProperty(INTERCOM_BUBBLE_BOTTOM_VAR, `${verticalPadding}px`)
|
||||
}
|
||||
|
||||
function clearIntercomBubbleStyles() {
|
||||
if (typeof document === 'undefined') return
|
||||
|
||||
document.documentElement.style.removeProperty(INTERCOM_BUBBLE_RIGHT_VAR)
|
||||
document.documentElement.style.removeProperty(INTERCOM_BUBBLE_BOTTOM_VAR)
|
||||
}
|
||||
|
||||
return {
|
||||
sidebarToggled,
|
||||
forceSidebar,
|
||||
sidebarVisible,
|
||||
intercomBubblePosition,
|
||||
updateIntercomBubbleStyles,
|
||||
clearIntercomBubbleStyles,
|
||||
pageContext: {
|
||||
floatingActionBarOffsets: {
|
||||
left: ref(APP_LEFT_NAV_WIDTH),
|
||||
right: computed(() => (sidebarVisible.value ? `${APP_SIDEBAR_WIDTH}px` : '0px')),
|
||||
},
|
||||
intercomBubble: {
|
||||
width: ref(INTERCOM_BUBBLE_WIDTH),
|
||||
horizontalPadding: intercomBubbleHorizontalPadding,
|
||||
requestHorizontalPadding: requestIntercomBubbleHorizontalPadding,
|
||||
requestVerticalClearance: requestIntercomBubbleVerticalClearance,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import type {
|
||||
ContentItem,
|
||||
ContentModpackCardCategory,
|
||||
ContentModpackCardProject,
|
||||
ContentModpackCardVersion,
|
||||
ContentOwner,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
import {
|
||||
get_content_items,
|
||||
get_linked_modpack_info,
|
||||
type LinkedModpackInfo,
|
||||
} from '@/helpers/profile'
|
||||
import { get_categories } from '@/helpers/tags.js'
|
||||
import type { CacheBehaviour } from '@/helpers/types'
|
||||
|
||||
export type InstanceContentData = {
|
||||
path: string
|
||||
contentItems: ContentItem[] | null
|
||||
modpack: InstanceContentModpackData | null
|
||||
}
|
||||
|
||||
export type InstanceContentModpackData = {
|
||||
project: ContentModpackCardProject
|
||||
version: ContentModpackCardVersion
|
||||
owner: ContentOwner | null
|
||||
categories: ContentModpackCardCategory[]
|
||||
hasUpdate: boolean
|
||||
updateVersionId: string | null
|
||||
}
|
||||
|
||||
export async function loadInstanceContentData(
|
||||
path: string,
|
||||
cacheBehaviour?: CacheBehaviour,
|
||||
onError?: (error: Error) => unknown,
|
||||
): Promise<InstanceContentData> {
|
||||
const [contentItems, modpackInfo, allCategories] = await Promise.all([
|
||||
get_content_items(path, cacheBehaviour).catch((error) => handleLoadError(error, onError)),
|
||||
get_linked_modpack_info(path, cacheBehaviour).catch((error) => handleLoadError(error, onError)),
|
||||
get_categories().catch((error) => handleLoadError(error, onError)),
|
||||
])
|
||||
|
||||
return {
|
||||
path,
|
||||
contentItems: (contentItems as ContentItem[] | null | undefined) ?? null,
|
||||
modpack: normalizeLinkedModpackInfo(
|
||||
modpackInfo as LinkedModpackInfo | null | undefined,
|
||||
allCategories as ContentModpackCardCategory[] | null | undefined,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
function handleLoadError(error: unknown, onError?: (error: Error) => unknown) {
|
||||
onError?.(error as Error)
|
||||
return null
|
||||
}
|
||||
|
||||
function normalizeLinkedModpackInfo(
|
||||
modpackInfo: LinkedModpackInfo | null | undefined,
|
||||
allCategories: ContentModpackCardCategory[] | null | undefined,
|
||||
): InstanceContentModpackData | null {
|
||||
if (!modpackInfo) return null
|
||||
|
||||
return {
|
||||
project: {
|
||||
...modpackInfo.project,
|
||||
slug: modpackInfo.project.slug ?? modpackInfo.project.id,
|
||||
icon_url: modpackInfo.project.icon_url ?? undefined,
|
||||
},
|
||||
version: {
|
||||
...modpackInfo.version,
|
||||
date_published: modpackInfo.version.date_published.toString(),
|
||||
},
|
||||
owner: modpackInfo.owner
|
||||
? {
|
||||
...modpackInfo.owner,
|
||||
avatar_url: modpackInfo.owner.avatar_url ?? undefined,
|
||||
}
|
||||
: null,
|
||||
categories: resolveLinkedModpackCategories(modpackInfo, allCategories),
|
||||
hasUpdate: modpackInfo.has_update,
|
||||
updateVersionId: modpackInfo.update_version_id,
|
||||
}
|
||||
}
|
||||
|
||||
function resolveLinkedModpackCategories(
|
||||
modpackInfo: LinkedModpackInfo,
|
||||
allCategories: ContentModpackCardCategory[] | null | undefined,
|
||||
) {
|
||||
if (!allCategories || !modpackInfo.project.categories) return []
|
||||
|
||||
const seen = new Set<string>()
|
||||
return allCategories.filter((category) => {
|
||||
if (modpackInfo.project.categories.includes(category.name) && !seen.has(category.name)) {
|
||||
seen.add(category.name)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import type { LocationQuery, LocationQueryRaw } from 'vue-router'
|
||||
|
||||
const MODRINTH_HOSTNAMES = new Set(['modrinth.com', 'www.modrinth.com'])
|
||||
|
||||
const SUPPORTED_PROJECT_TYPES = new Set([
|
||||
'mod',
|
||||
'modpack',
|
||||
'resourcepack',
|
||||
'datapack',
|
||||
'plugin',
|
||||
'shader',
|
||||
'server',
|
||||
'project',
|
||||
])
|
||||
|
||||
export function parseModrinthLink(
|
||||
href: string,
|
||||
): { slug: string; pathSuffix: string; url: URL } | null {
|
||||
let url: URL
|
||||
try {
|
||||
url = new URL(href)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!MODRINTH_HOSTNAMES.has(url.hostname.toLowerCase())) {
|
||||
return null
|
||||
}
|
||||
|
||||
const segments = url.pathname.split('/').filter((p) => p.length > 0)
|
||||
if (segments.length < 2) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (SUPPORTED_PROJECT_TYPES.has(segments[0].toLowerCase())) {
|
||||
const slug = segments[1]
|
||||
if (!slug) {
|
||||
return null
|
||||
}
|
||||
|
||||
const rest: string[] = segments.slice(2)
|
||||
const pathSuffix = toValidAppSubpath(rest)
|
||||
if (pathSuffix === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return { slug, pathSuffix, url }
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const SUPPORTED_SUBPATHS = ['versions', 'gallery']
|
||||
|
||||
function toValidAppSubpath(rest: string[]): string | null {
|
||||
if (rest.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const subroute = rest[0].toLowerCase()
|
||||
if (rest.length === 1 && SUPPORTED_SUBPATHS.includes(subroute)) {
|
||||
return `/${subroute}`
|
||||
}
|
||||
|
||||
if (rest.length === 2 && subroute === 'version') {
|
||||
return `/version/${rest[1]}`
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function mergeUrlQuery(routeQuery: LocationQuery, linkUrl: URL): LocationQueryRaw {
|
||||
const newQuery: LocationQueryRaw = { ...routeQuery }
|
||||
const keys = new Set<string>()
|
||||
linkUrl.searchParams.forEach((_value, key) => {
|
||||
keys.add(key)
|
||||
})
|
||||
for (const key of keys) {
|
||||
const values = linkUrl.searchParams.getAll(key)
|
||||
newQuery[key] = values.length === 1 ? values[0] : values
|
||||
}
|
||||
return newQuery
|
||||
}
|
||||
@@ -22,6 +22,10 @@ export async function removeEnqueuedUpdate() {
|
||||
return await invoke('remove_enqueued_update')
|
||||
}
|
||||
|
||||
export async function setRestartAfterPendingUpdate(should_restart) {
|
||||
return await invoke('set_restart_after_pending_update', { shouldRestart: should_restart })
|
||||
}
|
||||
|
||||
// One of 'Windows', 'Linux', 'MacOS'
|
||||
export async function getOS() {
|
||||
return await invoke('plugin:utils|get_os')
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "جاز تنزيل إصدار جافا {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "التنزيلات"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "غير متصل"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "صفحة الرئيسية"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "مكتبة"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "قد تكون خوادم مصادقة ماينكرافت معطلة حاليًا. تحقق من اتصالك بالإنترنت وحاول مرة أخرى لاحقًا."
|
||||
},
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "تعذر الوصول إلى خوادم المصادقة"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "أضف الخادم للنموذج"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "أضف الخوادم لنموذجك"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "أضف للنموذج"
|
||||
},
|
||||
@@ -29,9 +38,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "استكشف خوادم"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "تثبيت محتوى لنموذجك"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "جاري التثبيت"
|
||||
},
|
||||
@@ -50,9 +56,6 @@
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "إسم حزمة التعديل"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "حدد الملفات والمجلدات المراد تضمينها في الحزمة"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "رقم الإصدار"
|
||||
},
|
||||
@@ -572,6 +575,12 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "العالم قيد الاستخدام"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "إضافة حساب"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "اختيار الحساب"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "مقدَّم من النسخة"
|
||||
},
|
||||
|
||||
@@ -1,15 +1,114 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Stahování Javy {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Stažené Soubory"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Skrýt více spuštěných instancí"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Použít jako primární instanci"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Žádné spuštěné instance"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Primární instance"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Zobrazit další spuštěné instance"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Ukončit instanci"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Zobrazit probíhající stahování"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Zobrazit instanci"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Zobrazit záznamy"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Umožňuje pokročilé vykreslování, jako jsou efekty rozostření, které mohou způsobit problémy s výkonem bez hardwarového vykreslení."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Pokročilé vykreslování"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Vyberte si váš preferovaný motiv pro aplikaci Modrinth."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Barevný motiv"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Změňte stránku, na které se spouštěč otevře."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Domov"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Knihovna"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Výchozí úvodní stránka"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Zakáže zobrazení jmenovky nad vaší postavou na stránce skinů."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Skrýt jmenovku"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "V sekci „Vrátit se do hry“ na úvodní stránce jsou uvedeny nejnovější světy."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Skočit zpět do světů"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Minimalizovat spouštěč při spuštění procesu Minecraftu."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Minimalizovat spouštěč"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Použít systémový rámeček okna (vyžaduje restart aplikace)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Nativní dekorace"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Vyberte možnost"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Umožňí přepínat zobrazení postranního panelu."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Přepnout postranní panel"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Pokud se pokusíte nainstalovat soubor Modrinth Pack (.mrpack), který není hostován na Modrinth, zajistíme, abyste si před instalací uvědomili související rizika."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Upozorni mě před instalací neznámých modpacků"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Autořizační servery Minecraftu aktuálně nejsou k dispozici. Zkontrolujte si své připojení k internetu a zkuste to znovu později."
|
||||
},
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Připojení k autorizačním serverům se nezdařilo"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Přidat server do instance"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Přidat servery do instance"
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Přidat do instalace"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Přidat do instalace"
|
||||
@@ -23,14 +122,32 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Už přidáno"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Zpět k instanci"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Prozkoumat obsah"
|
||||
},
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Prozkoumat servery"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Nainstalovat obsah do instnce"
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Skrýt již přidané servery"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Balíčky Modifikací"
|
||||
},
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Přidání obsahu může narušit kompatibilitu při připojení k serveru. Veškerý přidaný obsah se navíc ztratí při aktualizaci obsahu instance serveru."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Instalování"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Probíhá instalace modpacku..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Přidej popis modpacku..."
|
||||
@@ -41,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Exportovat modpack"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "Zahrnout \"{file}\"?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Jméno Modpacku"
|
||||
},
|
||||
@@ -48,7 +168,7 @@
|
||||
"message": "Jméno modpacku"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Vybrat soubory a složky co zahrnout do modpacku"
|
||||
"message": "Nastavte, které soubory mají být do tohoto exportu zahrnuty"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Číslo verze"
|
||||
@@ -131,6 +251,9 @@
|
||||
"app.instance.worlds.remove-server-description": {
|
||||
"message": "„{name}“ bude odstraněn z tvého seznamu, včetně hry, a nebude žádný způsob, jak ho obnovit."
|
||||
},
|
||||
"app.instance.worlds.remove-server-description-with-address": {
|
||||
"message": "'{name}' ({address}) bude odstraněn z vašeho seznamu, a to i ve hře, a nebude možné jej obnovit."
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "Opravdu chceš odstranit {name}?"
|
||||
},
|
||||
@@ -140,6 +263,9 @@
|
||||
"app.instance.worlds.this-server": {
|
||||
"message": "tento server"
|
||||
},
|
||||
"app.modal.install-to-play.content-required": {
|
||||
"message": "Požadovaný obsah"
|
||||
},
|
||||
"app.modal.install-to-play.header": {
|
||||
"message": "Nainstaluj ke hraní"
|
||||
},
|
||||
@@ -149,12 +275,21 @@
|
||||
"app.modal.install-to-play.mod-count": {
|
||||
"message": "{count, plural,one {#mód} other {#módy}}"
|
||||
},
|
||||
"app.modal.install-to-play.required-modpack": {
|
||||
"message": "Povinný modpack"
|
||||
},
|
||||
"app.modal.install-to-play.server-requires-mods": {
|
||||
"message": "Tento server k hraní vyžaduje módy. Klikni na instalovat pro získání potřebných módů z Modrinth."
|
||||
},
|
||||
"app.modal.install-to-play.shared-instance": {
|
||||
"message": "Sdílená instance"
|
||||
},
|
||||
"app.modal.install-to-play.shared-server-instance": {
|
||||
"message": "Sdílená serverová instance"
|
||||
},
|
||||
"app.modal.install-to-play.view-contents": {
|
||||
"message": "Zobrazit obsah"
|
||||
},
|
||||
"app.modal.update-to-play.header": {
|
||||
"message": "Updatuj ke hraní"
|
||||
},
|
||||
@@ -164,6 +299,12 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "Je nutná aktualizace ke hraní {name}. Prosím updatuj na nejnovější verzi k spuštění hry."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Tento projekt je již nainstalován"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Nainstalovat obsah do instnce"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Vývojářský režim povolen."
|
||||
},
|
||||
@@ -188,9 +329,33 @@
|
||||
"app.settings.tabs.resource-management": {
|
||||
"message": "Správa zdrojů"
|
||||
},
|
||||
"app.update-popup.body": {
|
||||
"message": "Aplikace Modrinth v{version} je připravena k instalaci! Nainstalujte aktualizaci nyní nebo automaticky po zavření aplikace Modrinth."
|
||||
},
|
||||
"app.update-popup.body.download-complete": {
|
||||
"message": "Stahování aplikace Modrinth v{version} bylo dokončeno. Nainstalujte aktualizaci nyní nebo automaticky po zavření aplikace Modrinth."
|
||||
},
|
||||
"app.update-popup.body.linux": {
|
||||
"message": "Aplikace Modrinth v{version} je k dispozici. Aktualizujte pomocí svého správce balíčků, abyste získali nejnovější funkce a opravy!"
|
||||
},
|
||||
"app.update-popup.body.metered": {
|
||||
"message": "Aplikace Modrinth v{version} je nyní k dispozici! Protože jste v měřené síti, nebyla stažena automaticky."
|
||||
},
|
||||
"app.update-popup.changelog": {
|
||||
"message": "Seznam změn"
|
||||
},
|
||||
"app.update-popup.download": {
|
||||
"message": "Stáhnout ({size})"
|
||||
},
|
||||
"app.update-popup.download-complete": {
|
||||
"message": "Stahování bylo dokončeno"
|
||||
},
|
||||
"app.update-popup.reload": {
|
||||
"message": "Načíst znovu"
|
||||
},
|
||||
"app.update-popup.title": {
|
||||
"message": "Je k dispozici aktualizace"
|
||||
},
|
||||
"app.update.complete-toast.text": {
|
||||
"message": "Kliknutím sem zobrazíte seznam změn."
|
||||
},
|
||||
@@ -206,6 +371,24 @@
|
||||
"app.update.reload-to-update": {
|
||||
"message": "Restartovat aplikaci pro nainstalování aktualizace"
|
||||
},
|
||||
"app.world.server-modal.placeholder-address": {
|
||||
"message": "priklad.modrinth.gg"
|
||||
},
|
||||
"app.world.server-modal.select-an-option": {
|
||||
"message": "Vyberte možnost"
|
||||
},
|
||||
"app.world.world-item.incompatible-version": {
|
||||
"message": "Nekompatibilní verze {version}"
|
||||
},
|
||||
"app.world.world-item.not-played-yet": {
|
||||
"message": "Ještě nehráno"
|
||||
},
|
||||
"app.world.world-item.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.world.world-item.players-online": {
|
||||
"message": "{count} online"
|
||||
},
|
||||
"friends.action.add-friend": {
|
||||
"message": "Přidat přítele"
|
||||
},
|
||||
@@ -305,6 +488,12 @@
|
||||
"instance.edit-world.title": {
|
||||
"message": "Upravit svět"
|
||||
},
|
||||
"instance.files.adding-files": {
|
||||
"message": "Přidávání souborů ({completed}/{total})"
|
||||
},
|
||||
"instance.files.save-as": {
|
||||
"message": "Uložit jako..."
|
||||
},
|
||||
"instance.server-modal.address": {
|
||||
"message": "Adresa"
|
||||
},
|
||||
@@ -485,6 +674,9 @@
|
||||
"instance.worlds.incompatible_server": {
|
||||
"message": "Server je nekompatibilní"
|
||||
},
|
||||
"instance.worlds.linked_server": {
|
||||
"message": "Spravováno projektem serveru"
|
||||
},
|
||||
"instance.worlds.no_contact": {
|
||||
"message": "Server se nepodařilo spojit"
|
||||
},
|
||||
@@ -503,6 +695,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Svět je použit"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Přidat účet"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Účet Minecraft"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Nejste přihlášeni"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Odstranit účet"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Vyberte účet"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Přihlásit se do Minecraftu"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Poskytováno instancí"
|
||||
},
|
||||
@@ -515,6 +725,9 @@
|
||||
"search.filter.locked.instance.sync": {
|
||||
"message": "Synchronizováno z instancí"
|
||||
},
|
||||
"search.filter.locked.server": {
|
||||
"message": "Poskytováno serverem"
|
||||
},
|
||||
"search.filter.locked.server-environment.title": {
|
||||
"message": "Jen klientské mody mohou být přidány na server"
|
||||
},
|
||||
@@ -523,5 +736,26 @@
|
||||
},
|
||||
"search.filter.locked.server-loader.title": {
|
||||
"message": "Loader zprostředkovává server"
|
||||
},
|
||||
"unknown-pack-warning-modal.body": {
|
||||
"message": "Soubor je zkontrolován pouze v případě, že je nahrán na Modrinth, a to bez ohledu na jeho formát (včetně formátu .mrpack)."
|
||||
},
|
||||
"unknown-pack-warning-modal.dont-show-again": {
|
||||
"message": "Toto varování již nezobrazovat"
|
||||
},
|
||||
"unknown-pack-warning-modal.header": {
|
||||
"message": "Potvrdit instalaci"
|
||||
},
|
||||
"unknown-pack-warning-modal.install-anyway": {
|
||||
"message": "Přesto nainstalovat"
|
||||
},
|
||||
"unknown-pack-warning-modal.malware-statement": {
|
||||
"message": "Malware se často šíří prostřednictvím souborů s modpacky, které se sdílejí na platformách jako Discord."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.body": {
|
||||
"message": "Tento soubor jsme na Modrinthu nenašli. Důrazně doporučujeme instalovat pouze soubory z důvěryhodných zdrojů."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.title": {
|
||||
"message": "Upozornění na neznámý soubor"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,88 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Downloader Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Downloads"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Gem flere kørende instances"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Gør til primære instance"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Ingen instances kørende"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Primære instance"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Vis flere kørende instances"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Stop instance"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Vis aktive downloads"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Vis instance"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Vis logs"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Avanceret rendering"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Vælg dit foretrukne farvetema til Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Farvetema"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Skift hvilken side launcheren åbner i."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Hjem"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Bibliotek"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Standard startside"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Slår nametagget over din spiller på skins siden fra."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Gem nametag"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Inkluderer de sidste verdener i afsnittet \"Hop tilbage\" på startsiden."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Hop tilbage ind i verdener"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Minimer launcheren når Minecraft starter."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Minimer launcher"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Vælg en mulighed"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Hvis du prøver at installere en Modrinth Pack fil (.mrpack) som ikke er hosted på Modrinth, så skal vi nok sikre os du forstår riskoen før du installere den."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Advar mig før jeg installere en ukendt modpacks"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Minecraft authentication servere kan måske være nede lige nu. Tjek din internet forbindelse og prøv igen senere."
|
||||
},
|
||||
@@ -35,9 +119,6 @@
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Modpack navn"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Vælg filer og mapper til at inkludere i pakken"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Versionsnummer"
|
||||
},
|
||||
|
||||
@@ -104,11 +104,11 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Authentifizierungsserver sind nicht erreichbar"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Server zu Instanz hinzufügen"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Server zu deiner Instanz hinzufügen"
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Zu Instanz hinzufügen"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Zu Instanz hinzufügen"
|
||||
@@ -122,6 +122,9 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Bereits hinzugefügt"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Zu Instanz zurückgehen"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Inhalte entdecken"
|
||||
},
|
||||
@@ -131,20 +134,11 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Bereits hinzugefügte Server ausblenden"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Bereits installierte Inhalte ausblenden"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Inhalt in Instanz installieren"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Installieren"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Installiert"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Das hinzufügen von Inhalten kann Kompatibilitätsprobleme beim beitreten eines Servers verursachen. Sämtliche hinzugefügte Inhalten gehen ausserdem beim aktualisieren der Server Instanz-Inhalte verloren."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Wird installiert"
|
||||
@@ -164,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Modpack exportieren"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "\"{file}\" einschliessen?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Modpaketname"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "Modpaketname"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Wähle Dateien und Ordner zum hinzufügen im Paket aus"
|
||||
"message": "Konfiguriere, welche Dateien in diesem Export miteinbezogen werden"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Versionsnummer"
|
||||
@@ -302,6 +299,15 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "Eine aktualisierung zum spielen von {name} ist benötigt. Bitte aktualisiere auf die neuste Version um das Spiel zu starten."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Dieses Projekt ist bereits installiert"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "Zurück zum Entdecken"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Inhalt in Instanz installieren"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Entwicklermodus aktiviert."
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"message": "Zur primären Instanz machen"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Keine Instanzen laufen"
|
||||
"message": "Keine aktiven Instanzen"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
@@ -48,7 +48,7 @@
|
||||
"message": "Farbschema"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Ändere die Seite, die beim Öffnen des Launchers angezeigt wird."
|
||||
"message": "Ändere die Kategorie, die beim Öffnen des Launchers angezeigt wird."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Start"
|
||||
@@ -104,11 +104,11 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Authentifizierungsserver sind nicht erreichbar"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Server zur Instanz hinzufügen"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Server zu deiner Instanz hinzufügen"
|
||||
"message": "Server zu Instanz hinzufügen"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Zu Instanz hinzufügen"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Zur Instanz hinzufügen"
|
||||
@@ -122,6 +122,9 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Bereits hinzugefügt"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Zurück zur Instanz"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Inhalte entdecken"
|
||||
},
|
||||
@@ -131,20 +134,11 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Bereits hinzugefügte Server ausblenden"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Bereits installierte Inhalte ausblenden"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Inhalt in Instanz installieren"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Installieren"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Installiert"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Das Hinzufügen von Inhalten kann die Kompatibilität beim Beitreten zum Server beeinträchtigen. Außerdem gehen alle hinzugefügten Inhalte verloren, wenn du die Inhalte der Serverinstanz aktualisierst."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Wird installiert"
|
||||
@@ -164,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Modpack exportieren"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "\"{file}\" einschließen?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Modpackname"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "Modpackname"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Wähle Dateien und Ordner aus, die in das Paket sollen"
|
||||
"message": "Konfiguriere, welche Dateien in diesen Export enthalten sind"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Versionsnummer"
|
||||
@@ -302,6 +299,15 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "Zum Spielen von {name} ist eine Aktualisierung erforderlich. Bitte aktualisiere auf die neueste Version, um das Spiel zu starten."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Dieses Projekt ist bereits installiert"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "Zurück zum Entdecken"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Inhalt in Instanz installieren"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Entwicklermodus aktiviert."
|
||||
},
|
||||
@@ -519,7 +525,7 @@
|
||||
"message": "Wird gelöscht..."
|
||||
},
|
||||
"instance.settings.tabs.general.duplicate-button": {
|
||||
"message": "Kopieren"
|
||||
"message": "Duplizieren"
|
||||
},
|
||||
"instance.settings.tabs.general.duplicate-button.tooltip.installing": {
|
||||
"message": "Kann während der Installation nicht dupliziert werden."
|
||||
|
||||
@@ -303,7 +303,7 @@
|
||||
"message": "This project is already installed"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "Back to browse"
|
||||
"message": "Back to discover"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Install content to instance"
|
||||
|
||||
@@ -12,43 +12,43 @@
|
||||
"message": "Hacer instancia principal"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Ninguna instancia en ejecución"
|
||||
"message": "No hay instancias en ejecución"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Sin conexión"
|
||||
"message": "Desconectado"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Instancia principal"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Mostrar mas instancias en ejecución"
|
||||
"message": "Mostrar más instancias en ejecución"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Finalizar instancia"
|
||||
"message": "Detener instancia"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Mostrar descargas activas"
|
||||
"message": "Ver descargas en curso"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Mostrar instancia"
|
||||
"message": "Ver instancia"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Mostrar registros"
|
||||
"message": "Ver registros"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Habilita el renderizado avanzado, como los efectos de desenfoque, que pueden causar problemas de rendimiento sin renderizado acelerado por hardware."
|
||||
"message": "Activa el renderizado avanzado, como los efectos de desenfoque, que pueden afectar el rendimiento sin aceleración por hardware."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Renderizado avanzado"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Seleccione su tema de color preferido para Modrinth en este dispositivo."
|
||||
"message": "Selecciona el tema de color que prefieras para la Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Tema de color"
|
||||
"message": "Color de la interfaz"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Cambia la página en la que el launcher se abre en."
|
||||
"message": "Cambia la página en la que se abre el launcher."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Inicio"
|
||||
@@ -57,28 +57,28 @@
|
||||
"message": "Librería"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Página predeterminada"
|
||||
"message": "Página de inicio predeterminada"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Desactiva la etiqueta arriba de tu personaje en la página de skins."
|
||||
"message": "Desactiva la etiqueta de nombre arriba de tu personaje en la página de skins."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Ocultar etiqueta"
|
||||
"message": "Ocultar etiqueta de nombre"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Incluye los mundos más recientes en la sección \"Vuelve a jugar tus mundos\" en la página de inicio."
|
||||
"message": "Incluye los mundos recientes en la sección \"Volver a jugar\" de la página de inicio."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Vuelve a jugar tus mundos"
|
||||
"message": "Volver a jugar mundos"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Minimiza el launcher cuando un proceso de Minecraft empieza."
|
||||
"message": "Minimiza el launcher al iniciar un proceso de Minecraft."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Minimiza el launcher"
|
||||
"message": "Minimizar launcher"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Usa los sistemas de frame de Windows (se requiere resetear la aplicación)."
|
||||
"message": "Usar el borde de ventana del sistema (requiere reiniciar la aplicación)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Decoraciones nativas"
|
||||
@@ -87,16 +87,16 @@
|
||||
"message": "Selecciona una opción"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Activa la posibilidad de alternar la barra lateral."
|
||||
"message": "Permite mostrar u ocultar la barra lateral."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Alternar barra lateral"
|
||||
"message": "Mostrar u ocultar la barra lateral"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Si intentas instalar un archivo de paquete de Modrinth (.mrpack) que no esté alojado en Modrinth, te advertiremos de los riesgos antes de instalarlo."
|
||||
"message": "Si intentas instalar un paquete de Modrinth (.mrpack) que no está alojado en Modrinth, te explicaremos los riesgos antes de instalarlo."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Adviérteme antes de instalarme modpacks desconocidos"
|
||||
"message": "Advertir antes de instalar modpacks desconocidos"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Los servidores de autenticación de Minecraft pueden no estar funcionando en este momento. Verifica tu conexión a internet e inténtalo de nuevo más tarde."
|
||||
@@ -104,11 +104,11 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "No se puede acceder a los servidores de autenticación"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Añadir servidor a instancia"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Añade servidores a tu instancia"
|
||||
"message": "Añadiendo servidor a instancia"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Añadir a una instancia"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Añadir a instancia"
|
||||
@@ -122,6 +122,9 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Ya se ha añadido"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Volver a instancia"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Descubrir contenido"
|
||||
},
|
||||
@@ -131,20 +134,11 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Ocultar servidores ya añadidos"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Ocultar contenido instalado"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Instalar contenido a la instancia"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Instalar"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Instalado"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Añadir contenido puede romper la compatibilidad cuando te unes al servidor. Cualquier contenido añadido tambien se perderá cuando actualizes el contenido en la instancia del servidor."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Instalando"
|
||||
@@ -153,7 +147,7 @@
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Instalando el modpack..."
|
||||
"message": "Instalando modpack..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Introduce la descripción del modpack..."
|
||||
@@ -164,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Exportar modpack"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "¿Incluir \"{file}\"?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Nombre del modpack"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "Nombre del modpack"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Selecciona archivos y carpetas para incluirlos en el pack"
|
||||
"message": "Configura que archivos incluir en esta exportación"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Número de la versión"
|
||||
@@ -270,7 +267,7 @@
|
||||
"message": "Contenido requerido"
|
||||
},
|
||||
"app.modal.install-to-play.header": {
|
||||
"message": "Instala para jugar"
|
||||
"message": "Instalar para jugar"
|
||||
},
|
||||
"app.modal.install-to-play.install-button": {
|
||||
"message": "Instalar"
|
||||
@@ -294,7 +291,7 @@
|
||||
"message": "Ver contenidos"
|
||||
},
|
||||
"app.modal.update-to-play.header": {
|
||||
"message": "Actualiza para jugar"
|
||||
"message": "Actualizar para jugar"
|
||||
},
|
||||
"app.modal.update-to-play.update-required": {
|
||||
"message": "Actualización requerida"
|
||||
@@ -302,6 +299,15 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "Se requiere una actualización para jugar {name}. Por favor, actualiza a la versión más reciente para iniciar el juego."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Este proyecto está actualmente instalado"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "Volver al explorador"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Instalar contenido a la instancia"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Modo desarrollador activado."
|
||||
},
|
||||
@@ -692,6 +698,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "El mundo ya está en uso"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Añadir cuenta"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Cuenta de Minecraft"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "No has inciado sesión"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Eliminar cuenta"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Seleccionar cuenta"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Inicia sesión en Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Proporcionado por la instancia"
|
||||
},
|
||||
@@ -720,7 +744,7 @@
|
||||
"message": "Un archivo solo es revisado si es subido a Modrinth, independientemente de su formato de archivo (incluyendo .mrpack)."
|
||||
},
|
||||
"unknown-pack-warning-modal.dont-show-again": {
|
||||
"message": "No mostrar esta advertencia otra vez"
|
||||
"message": "No volver a mostrar esta advertencia"
|
||||
},
|
||||
"unknown-pack-warning-modal.header": {
|
||||
"message": "Confirmar instalación"
|
||||
@@ -729,7 +753,7 @@
|
||||
"message": "Instalar de todos modos"
|
||||
},
|
||||
"unknown-pack-warning-modal.malware-statement": {
|
||||
"message": "El malware es normalmente distribuido por archivos de modpack que son normalmente compartidas en aplicaciones como Discord."
|
||||
"message": "El malware a menudo es distribuido mediante modpacks compartidos en aplicaciones como Discord."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.body": {
|
||||
"message": "No pudimos encontrar este archivo en Modrinth. Recomendamos solo instalar archivos de sitios de confianza."
|
||||
|
||||
@@ -104,12 +104,6 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "No se puede conectar con los servidores de autenticación"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Añadir servidor a la instancia"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Añadir servidor a tu instancia"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Añadir a la instancia"
|
||||
},
|
||||
@@ -131,21 +125,9 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Ocultar servidores ya añadidos"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Ocultar contenido instalado"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Instalar contenido a una instancia"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Instalar"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Instalado"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Instalando"
|
||||
},
|
||||
@@ -170,9 +152,6 @@
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Nombre del modpack"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Seleccione archivos y carpetas para incluir en el paquete"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Número de versión"
|
||||
},
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Todennuspalvelimiin ei saada yhteyttä"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Lisää palvelin instanssiin"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Lisää palvelimia instanssiisi"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Lisää instanssiin"
|
||||
},
|
||||
@@ -29,9 +23,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Löydä palvelimia"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Lataa sisältöä instanssiin"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Lisää modipaketin kuvaus..."
|
||||
},
|
||||
@@ -47,9 +38,6 @@
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Modipaketin nimi"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Valitse tiedostot ja kansiot pakettiin"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Versio numero"
|
||||
},
|
||||
|
||||
@@ -1,16 +1,109 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Dina-download ang Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Mga downloads"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Itago ang mas marami pang tumatakbong instansiya"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Gumawa ng pangunahing instansiya"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Walang instansiya ang tumatakbo"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Pangunahing instansiya"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Ipakita ang mas marami pang tumatakbong instansiya"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Itigil ang instansiya"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Tignan ang mga active downloads"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Tignan ang instansiya"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Tignan ang logs"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Pagpapagana ng masalimuot na pagrerender katulad ng paglalabo na maaaring magdudulot ng mga isyu sa performance kapag walang hardware-acceleration."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Masalimuot na pagrerender"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Piliin ang iyong mas gustong kulay para sa Modrinth app."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Tema ng kulay"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Pagpapalit ng pahina na bubuksan ng launcher."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Tahanan"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Librarya"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Panimulang pahina ng paglapag"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Pagtatago ng nametag na nasa itaas ng iyong manlalaro sa loob ng pahina ng mga skin."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Itago ang nametag"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Pagsasali ng mga kamakailang mundo sa seksiyong \"Jump back in\" sa pahina ng Tahanan."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Bumalik sa mga mundo"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Liitan ang launcher kung nag-start ang isang proseso ng Minecraft."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Liitin ang launcher"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Gamitin ang system window frame (kailangan i-restart ang app)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Native decorations"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Pumili ng opsiyon"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Pagpapagana na mata-toggle ang sidebar."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "I-toggle ang sidebar"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Kung susubukan mong mag-install ng Modrinth Pack file (.mrpack) na hindi naka-host sa Modrinth, titiyakin namin na nakaaalam ka sa mga panganib bago ito ma-install."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Pakipaalala bago ko ma-install ang mga di-kilalang modpack"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Maaaring hindi maaabot ang mga authentication server ng Minecraft sa ngayon. Tingnan mo ang iyong internet connection at muling subukan mamaya."
|
||||
},
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Hindi maabot ang mga authentication server"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Idagdag ang server sa instansiya"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Idagdag ang mga server sa iyong instansiya"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Idagdag sa instansiya"
|
||||
},
|
||||
@@ -29,8 +122,20 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Tumuklas ng mga server"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "I-install ang kontento sa instansiya"
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Itago ang mga nailagay na mga servers"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Ini-install"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Ini-install ang modpack..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Ilagay ang paglalarawan ng modpack..."
|
||||
@@ -41,6 +146,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Iluwas ang modpack"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "Salihin ang \"{file}\"?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Pangalan ng Modpack"
|
||||
},
|
||||
@@ -48,7 +156,7 @@
|
||||
"message": "Pangalan ng modpack"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Pumili ng mga talaksan at folder na isasali sa pack"
|
||||
"message": "Isaayos kung anong mga file ang isasali sa pagluwas"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Numero ng bersiyon"
|
||||
@@ -101,6 +209,15 @@
|
||||
"app.instance.worlds.add-server": {
|
||||
"message": "Magdagdag ng server"
|
||||
},
|
||||
"app.instance.worlds.browse-servers": {
|
||||
"message": "Mag-browse ng servers"
|
||||
},
|
||||
"app.instance.worlds.delete-world-description": {
|
||||
"message": "'{name}' ay **permanenteng mabubura**, at walang paraan upang maibalik muli."
|
||||
},
|
||||
"app.instance.worlds.delete-world-title": {
|
||||
"message": "Sigurado ka bang gusto mong permanenteng burahin ang world na ito?"
|
||||
},
|
||||
"app.instance.worlds.filter-modded": {
|
||||
"message": "Modded"
|
||||
},
|
||||
@@ -113,6 +230,18 @@
|
||||
"app.instance.worlds.filter-vanilla": {
|
||||
"message": "Vanilla"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-description": {
|
||||
"message": "Maglagay ng server o mag-browse upang mag-simula"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-heading": {
|
||||
"message": "Walang servers o worlds ang nalalagay"
|
||||
},
|
||||
"app.instance.worlds.remove-server-description": {
|
||||
"message": "'{name}' ay matatangal sa iyong listahan, pati sa loob ng laro, at walang paraan upang maibalik ito."
|
||||
},
|
||||
"app.instance.worlds.remove-server-description-with-address": {
|
||||
"message": "'{name}' ({address}) ay matatangal sa iyong listahan, pati sa loob ng laro, at walang paraan upang maibalik ito."
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "Sigurado ka bang gusto mong tanggalin ang {name}?"
|
||||
},
|
||||
@@ -233,9 +362,15 @@
|
||||
"app.world.world-item.incompatible-version": {
|
||||
"message": "Di-magkatugmang bersiyong {version}"
|
||||
},
|
||||
"app.world.world-item.not-played-yet": {
|
||||
"message": "Hindi pa nalalaro"
|
||||
},
|
||||
"app.world.world-item.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.world.world-item.players-online": {
|
||||
"message": "{count} online"
|
||||
},
|
||||
"friends.action.add-friend": {
|
||||
"message": "Magdagdag ng kaibigan"
|
||||
},
|
||||
@@ -335,6 +470,12 @@
|
||||
"instance.edit-world.title": {
|
||||
"message": "Baguhin ang mundo"
|
||||
},
|
||||
"instance.files.adding-files": {
|
||||
"message": "Idinadagdag ang mga file ({completed}/{total})"
|
||||
},
|
||||
"instance.files.save-as": {
|
||||
"message": "I-save bilang..."
|
||||
},
|
||||
"instance.server-modal.address": {
|
||||
"message": "Adres"
|
||||
},
|
||||
@@ -536,6 +677,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Ginagamit ang mundo"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Maglagay ng account"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Minecraft account"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Hindi pa naka sign in"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Itanggal ang account"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Pumili ng account"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Mag sign in kay Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Sagot na ng instansiya"
|
||||
},
|
||||
@@ -559,5 +718,26 @@
|
||||
},
|
||||
"search.filter.locked.server-loader.title": {
|
||||
"message": "Ang loader ay handog na ng server"
|
||||
},
|
||||
"unknown-pack-warning-modal.body": {
|
||||
"message": "Ang file ay nasusuri lamang kapag ito ay na-upload sa Modrinth, walang pili sa file format nito (kabilang na ang .mrpack)."
|
||||
},
|
||||
"unknown-pack-warning-modal.dont-show-again": {
|
||||
"message": "Huwag ipakita ang babalang ito ulit"
|
||||
},
|
||||
"unknown-pack-warning-modal.header": {
|
||||
"message": "Kumpirmahin ang installation"
|
||||
},
|
||||
"unknown-pack-warning-modal.install-anyway": {
|
||||
"message": "I-install parin"
|
||||
},
|
||||
"unknown-pack-warning-modal.malware-statement": {
|
||||
"message": "Ang malware ay madalas naidadala sa mga modpack files sa pamamagitan ng pag-bigay ng mga ito sa mga platforms kagaya ng Discord."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.body": {
|
||||
"message": "Hindi namin mahanap ang file na ito sa Modrinth. Mahalagang mag-install ka lamang ng files galing sa mga mapagkakatiwalang sources."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.title": {
|
||||
"message": "Hindi kilalang file"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Java version {version} en cours de téléchargement"
|
||||
"message": "Téléchargement de Java {version} en cours"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Téléchargements"
|
||||
@@ -104,11 +104,11 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Impossible de contacter les serveurs d'authentification"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Ajouter le serveur à l'instance"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Ajouter des serveurs à votre instance"
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Ajouter à une instance"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Ajouter à l'instance"
|
||||
@@ -122,6 +122,9 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Déjà ajouté"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Retour à l'instance"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Découvrir du contenu"
|
||||
},
|
||||
@@ -131,20 +134,11 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Masquer les serveurs déjà ajoutés"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Masquer le contenu déjà ajouté"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Installer du contenu à l'instance"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Installer"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Installé"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Ajouter du contenu peut briser la comptabilité en rejoignant le serveur. Tout contenu en plus sera également perdu lorsque vous mettrez à jour le contenu de l'instance du serveur."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Installation en cours"
|
||||
@@ -164,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Exporter le modpack"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "Inclure « {file} » ?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Nom du modpack"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "Nom du modpack"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Sélectionnez des fichiers et des dossiers à inclure dans le pack"
|
||||
"message": "Configurez quels fichiers sont inclus dans cette exportation"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Numéro de version"
|
||||
@@ -192,7 +189,7 @@
|
||||
"message": "Supprimer l'instance"
|
||||
},
|
||||
"app.instance.modpack-already-installed.body": {
|
||||
"message": "Ce modpack est déjà installé sur l'instance <bold>{instanceName}</bold>. Êtes-vous sûr.e de vouloir le dupliquer ?"
|
||||
"message": "Ce modpack est déjà installé sur l'instance <bold>{instanceName}</bold>. Êtes-vous sûr·e de vouloir le dupliquer ?"
|
||||
},
|
||||
"app.instance.modpack-already-installed.create": {
|
||||
"message": "Créer"
|
||||
@@ -231,7 +228,7 @@
|
||||
"message": "« {name} » sera supprimé **pour toujours**, et il sera impossible de le récupérer."
|
||||
},
|
||||
"app.instance.worlds.delete-world-title": {
|
||||
"message": "Êtes-vous sûr.e de vouloir supprimer ce monde pour toujours ?"
|
||||
"message": "Êtes-vous sûr·e de vouloir supprimer ce monde pour toujours ?"
|
||||
},
|
||||
"app.instance.worlds.filter-modded": {
|
||||
"message": "Moddé"
|
||||
@@ -258,7 +255,7 @@
|
||||
"message": "« {name} » ({address}) sera retiré de votre liste, y compris en jeu, et il sera impossible de le récupérer."
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "Êtes-vous sûr.e de vouloir retirer {name} ?"
|
||||
"message": "Êtes-vous sûr·e de vouloir retirer {name} ?"
|
||||
},
|
||||
"app.instance.worlds.search-worlds-placeholder": {
|
||||
"message": "Rechercher {count} mondes..."
|
||||
@@ -302,6 +299,15 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "Une mise à jour est requise pour jouer à {name}. Veuillez mettre à jour à la dernière version pour lancer le jeu."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Ce projet a déjà été installé"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "Retour vers la page découvrir"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Installer du contenu à l'instance"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Mode développeur activé."
|
||||
},
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "לא ניתן לגשת לשרתי האימות"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "הוסף שרת להתקנה"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "הוסף שרתים להתקנה שלך"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "הוספה להתקנה"
|
||||
},
|
||||
@@ -29,9 +23,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "גלה שרתים"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "הוספת התוכן להתקנה"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "הזן את תיאור חבילת המודים..."
|
||||
},
|
||||
@@ -47,9 +38,6 @@
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "שם חבילת המודים"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "בחר קבצים ותיקיות להכללה בחבילה הזו"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "מספר גרסה"
|
||||
},
|
||||
|
||||
@@ -6,19 +6,97 @@
|
||||
"message": "Letöltések"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "További futó példányok elrejtése"
|
||||
"message": "További futó profilok elrejtése"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Elsődleges példány létrehozása"
|
||||
"message": "Beállítás elsődleges profilként"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Nincsenek futó példányok"
|
||||
"message": "Nincsenek futó profilok"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Elsődleges példány"
|
||||
"message": "Elsődleges profil"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "További futó profilok megjelenítése"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Profil megállítása"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Aktív letöltések megtekintése"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Profil megtekintése"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Naplók megtekintése"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Lehetővé teszi olyan speciális megjelenítési effektusok használatát, mint például az elmosódás, amelyek hardveres gyorsítás nélkül teljesítménycsökkenést okozhatnak."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Fejlett renderelés"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Válaszd ki a Modrinth Apphoz a kívánt színsémát."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Téma"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Állítsd be, hogy az indító melyik oldalon nyíljon meg."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Kezdőlap"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Könyvtár"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Alapértelmezett nyitóoldal"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Letiltja a játékosod felett megjelenő névcímkét a Skinek oldalon."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Névcímke eltüntetése"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "A kezdőlap „Folytasd itt” részében a legújabb világok is megtalálhatók."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "„Folytasd itt” a világokat is"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Minimalizáld az indítót a Minecraft indításakor."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Indító minimalizálása"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "A rendszer ablakkeret használata (az alkalmazás újraindítása szükséges)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Rendszer ablakkeret használata"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Válassz egy lehetőséget"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Lehetővé teszi az oldalsáv be- és kikapcsolását."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Oldalsáv megjelenítése/elrejtése"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Ha olyan Modrinth Csomagfájlt (.mrpack) próbálsz telepíteni, amely nem a Modrinth szerverén található, a telepítés előtt gondoskodunk arról, hogy tisztában legyél a kockázatokkal."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Figyelmeztess, mielőtt ismeretlen modcsomagokat telepítenék"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "A Minecraft hitelesítő szerverek lehet, hogy nem üzemelnek. Bizonyosodj meg róla, hogy van internetkapcsolatod és próbáld meg újra."
|
||||
@@ -26,11 +104,11 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Nem lehet elérni a hitelesítési kiszolgálókat"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Szerver hozzáadása a profilhoz"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Szerverek hozzáadása a profilodhoz"
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Hozzáadás egy profilhoz"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Hozzáadás a profilhoz"
|
||||
@@ -44,6 +122,9 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Már hozzá van adva"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Vissza a profilhoz"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Tartalom böngészése"
|
||||
},
|
||||
@@ -53,24 +134,21 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Már hozzáadott szerverek elrejtése"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Már telepített tartalom elrejtése"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Tartalom letöltése a profilhoz"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modcsomagok"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Letöltés"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Letöltve"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "A tartalom hozzáadása ronthatja a kompatibilitást a szerverhez való csatlakozáskor. A hozzáadott tartalom elveszik, ha frissíted a szerverprofil tartalmát."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Letöltés..."
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Modcsomag telepítése..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Írd be a modcsomag leírását..."
|
||||
},
|
||||
@@ -80,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Modcsomag exportálása"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "Tartalmaz „{file}” fájlokat?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "A modcsomag neve"
|
||||
},
|
||||
@@ -87,7 +168,7 @@
|
||||
"message": "A modcsomag neve"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Válaszd ki a csomagba felveendő fájlokat és mappákat"
|
||||
"message": "Állítsd be, hogy mely fájlok kerüljenek bele az exportba"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Verziószám"
|
||||
@@ -216,7 +297,16 @@
|
||||
"message": "Frissítés szükséges"
|
||||
},
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "\nFrissítés szükséges ehhez: {name}. Kérjük, frissíts a legújabb verzióra a játék elindításához"
|
||||
"message": "Frissítés szükséges ehhez: {name}. Kérlek, frissíts a legújabb verzióra a játék elindításához."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Ez a projekt már telepítve van"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "Vissza a böngészéshez"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Tartalom telepítése a profilba"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Fejlesztői mód bekapcsolva."
|
||||
@@ -243,10 +333,10 @@
|
||||
"message": "Erőforráskezelés"
|
||||
},
|
||||
"app.update-popup.body": {
|
||||
"message": "A Modrinth App v{version} telepítésre kész! Frissítéshez válaszd ki a Frissítés opciót, vagy az alkalmazás bezárásakor automatikusan frissül."
|
||||
"message": "A Modrinth App v{version} telepítésre kész! Frissítéshez válaszd ki a Frissítés opciót, vagy az alkalmazás a bezárásakor automatikusan frissül."
|
||||
},
|
||||
"app.update-popup.body.download-complete": {
|
||||
"message": "A Modrinth App v{version} letöltése befejeződött. Frissítéshez válaszd ki a Frissítés opciót, vagy az alkalmazás bezárásakor automatikusan frissül."
|
||||
"message": "A Modrinth App v{version} letöltése befejeződött. Frissítéshez válaszd ki a Frissítés opciót, vagy az alkalmazás a bezárásakor automatikusan frissül."
|
||||
},
|
||||
"app.update-popup.body.linux": {
|
||||
"message": "A Modrinth App v{version} elérhető. Használd a csomagkezelőt a legújabb funkciók és javítások frissítéséhez!"
|
||||
@@ -579,7 +669,7 @@
|
||||
"message": "Ne mutasd a kezdőlapon"
|
||||
},
|
||||
"instance.worlds.game_already_open": {
|
||||
"message": "A profil már meg van nyitva"
|
||||
"message": "A profil már el van indítva"
|
||||
},
|
||||
"instance.worlds.hardcore": {
|
||||
"message": "Hardcore mód"
|
||||
@@ -608,6 +698,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "A világ használatban van"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Fiók hozzáadása"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Minecraft-fiók"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Nem vagy bejelentkezve"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Fiók törlése"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Fiók kiválasztása"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Bejelentkezés a Minecraftba"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "A profil által van megadva"
|
||||
},
|
||||
@@ -631,5 +739,26 @@
|
||||
},
|
||||
"search.filter.locked.server-loader.title": {
|
||||
"message": "A betöltőt a szerver biztosítja"
|
||||
},
|
||||
"unknown-pack-warning-modal.body": {
|
||||
"message": "Egy fájlt csak akkor vizsgálunk meg, ha azt feltöltik a Modrinthra, függetlenül a fájlformátumtól (beleértve a .mrpack formátumot is)."
|
||||
},
|
||||
"unknown-pack-warning-modal.dont-show-again": {
|
||||
"message": "Ne mutasd újra ezt a figyelmeztetést"
|
||||
},
|
||||
"unknown-pack-warning-modal.header": {
|
||||
"message": "Telepítés megerősítése"
|
||||
},
|
||||
"unknown-pack-warning-modal.install-anyway": {
|
||||
"message": "Letöltés mindenképpen"
|
||||
},
|
||||
"unknown-pack-warning-modal.malware-statement": {
|
||||
"message": "A rosszindulatú programokat gyakran modcsomag-fájlokon keresztül terjesztik, például a Discordhoz hasonló platformokon történő megosztás révén."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.body": {
|
||||
"message": "Ezt a fájlt nem találtuk meg a Modrinthon. Határozottan javasoljuk, hogy kizárólag megbízható forrásokból származó fájlokat telepíts."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.title": {
|
||||
"message": "Figyelmeztetés ismeretlen fájlról"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,25 +3,73 @@
|
||||
"message": "Mengunduh Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Mengunduh"
|
||||
"message": "Unduhan"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Sembunyikan lebih banyak instance yang sedang berjalan"
|
||||
"message": "Sembunyikan lebih banyak instans yang sedang berjalan"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Jadikan sebagai instance utama"
|
||||
"message": "Jadikan sebagai instans utama"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Tidak ada instance yang sedang berjalan"
|
||||
"message": "Tidak ada instans yang sedang berjalan"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
"message": "Luring"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Instance utama"
|
||||
"message": "Instans utama"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Tampilkan lebih banyak instance yang sedang berjalan"
|
||||
"message": "Tampilkan lebih banyak instans yang sedang berjalan"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Hentikan instans"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Lihat pengunduhan berlangsung"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Lihat instans"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Lihat catatan"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Menghidupkan renderasi tingkat lanjut seperti efek buram yang dapat menyebabkan masalah kinerja tanpa renderasi yang dipercepat perangkat keras."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Renderasi tingkat lanjut"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Pilih tema warna pilihan Anda untuk Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Tema warna"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Ubah halaman utama yang dibuka oleh peluncur."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Beranda"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Pustaka"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Mematikan label nama di atas wujud pemain Anda pada halaman rupa."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Sembunyikan label nama"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Kecilkan pluncur ketika proses Minecraft berjalan."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Kecilkan peluncur"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Pilih opsi"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Server autentikasi Minecraft mungkin sedang tidak tersedia saat ini. Periksa koneksi internet Anda dan coba lagi nanti."
|
||||
@@ -29,11 +77,11 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Tidak dapat terhubung ke server autentikasi"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Tambah server ke instans"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Tambah server ke instans Anda"
|
||||
"message": "Menambah server ke instans"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Tambah ke instans"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Tambah ke instans"
|
||||
@@ -47,14 +95,29 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Telah ditambahkan"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Kembali ke instans"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Temukan konten"
|
||||
},
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Temukan server"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Pasang konten ke instans"
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Sembunyikan server yang sudah ditambah"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Paket Mod"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Memasang"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Memasang paket mod..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Masukkan deskripsi paket mod..."
|
||||
@@ -65,6 +128,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Ekspor paket mod"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "Sertakan \"{file}\"?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Nama Paket Mod"
|
||||
},
|
||||
@@ -72,7 +138,7 @@
|
||||
"message": "Nama paket mod"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Pilih berkas dan folder yang ingin dimasukkan ke paket"
|
||||
"message": "Konfigurasikan berkas mana yang disertakan dalam pengeksporan ini"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Nomor versi"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Scaricando Java {version}"
|
||||
"message": "Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Download"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Nascondi le istanze in esecuzione"
|
||||
"message": "Mostra meno istanze in esecuzione"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Rendi primaria"
|
||||
@@ -21,19 +21,19 @@
|
||||
"message": "Primaria"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Mostra le istanze in esecuzione"
|
||||
"message": "Mostra più istanze in esecuzione"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Ferma l'istanza"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Vedi i download attivi"
|
||||
"message": "Mostra i download attivi"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Vedi l'istanza"
|
||||
"message": "Mostra l'istanza"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Vedi i log"
|
||||
"message": "Mostra i log"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Abilita alcuni effetti come la sfocatura, ma può causare problemi di prestazioni senza accelerazione hardware."
|
||||
@@ -63,28 +63,28 @@
|
||||
"message": "Rimuove il nametag sopra la testa del giocatore nella pagina delle skin."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Nascondi nametag"
|
||||
"message": "Nascondi il nome"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Aggiunge i mondi recenti nella sezione \"Avvio rapido\" della pagina Home."
|
||||
"message": "Elenca i mondi recenti nella sezione \"Avvio rapido\" della pagina Home."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Avvio rapido nei mondi"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Riduci il launcher quanto si avvia Minecraft."
|
||||
"message": "Riduci il launcher a icona quando si avvia Minecraft."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Riduci il launcher"
|
||||
"message": "Riduci a icona"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Usa la cornice di sistema (riavvio richiesto)."
|
||||
"message": "Usa la cornice di sistema (riavvio necessario)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Decorazioni native"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Seleziona un'opzione"
|
||||
"message": "Scegli una pagina"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Scegli se mostrare o nascondere la barra laterale."
|
||||
@@ -93,10 +93,10 @@
|
||||
"message": "Abilita la barra laterale"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Apparirà un avviso prima di installare un file Modrinth Pack (.mrpack) non proveniente da Modrinth, così da conoscerne i rischi."
|
||||
"message": "Visti i rischi, apparirà un avviso prima di installare un file Modrinth Pack (.mrpack) non proveniente da Modrinth."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Avvisami prima di installare pacchetti sconosciuti"
|
||||
"message": "Avvisami se installo pacchetti sconosciuti"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "I server di autenticazione di Minecraft stanno riscontrando problemi. Controlla la tua connessione a Internet e riprova più tardi."
|
||||
@@ -104,11 +104,11 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Impossibile raggiungere i server di autenticazione"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Aggiungi server all'istanza"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Aggiungi i server alla tua istanza"
|
||||
"message": "Aggiungendo il server all'istanza"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Aggiungi a un'istanza"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Aggiungi all'istanza"
|
||||
@@ -122,6 +122,9 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Già aggiunto"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Torna all'istanza"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Esplora i contenuti"
|
||||
},
|
||||
@@ -131,23 +134,14 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Nascondi server già aggiunti"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Nascondi contenuti già installati"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Installa contenuti nell'istanza"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Pacchetti di mod"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Installa"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Installato"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Aggiungere dei contenuti potrebbe portare problemi entrando nel server. Qualsiasi contenuto aggiunto sarà anche perso aggiornando i contenuti dell'istanza del server."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Installando"
|
||||
"message": "Installazione"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
@@ -164,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Esporta pacchetto"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "Includere \"{file}\" nell'esportazione?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Nome del pacchetto"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "Nome del pacchetto"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Seleziona i file e le cartelle da includere nel pacchetto"
|
||||
"message": "Seleziona i file da includere in questa esportazione"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Numero di versione"
|
||||
@@ -302,6 +299,15 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "{name} richiede degli aggiornamenti. Installa l'ultima versione per poter giocare."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Questo progetto è già stato installato"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "Torna a Esplora"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Installa il contenuto nell'istanza"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Modalità sviluppatore attiva."
|
||||
},
|
||||
@@ -441,7 +447,7 @@
|
||||
"message": "Nessuna amicizia corrisponde a ''{query}''"
|
||||
},
|
||||
"friends.search-friends-placeholder": {
|
||||
"message": "Cerca amicizie..."
|
||||
"message": "Cerca tra le amicizie..."
|
||||
},
|
||||
"friends.section.heading": {
|
||||
"message": "{title} - {count}"
|
||||
@@ -480,7 +486,7 @@
|
||||
"message": "Mondo di Minecraft"
|
||||
},
|
||||
"instance.edit-world.reset-icon": {
|
||||
"message": "Resetta icona"
|
||||
"message": "Ripristina icona"
|
||||
},
|
||||
"instance.edit-world.title": {
|
||||
"message": "Modifica mondo"
|
||||
@@ -540,7 +546,7 @@
|
||||
"message": "Cambia icona"
|
||||
},
|
||||
"instance.settings.tabs.general.edit-icon.select": {
|
||||
"message": "Seleziona icona"
|
||||
"message": "Scegli un'icona"
|
||||
},
|
||||
"instance.settings.tabs.general.library-groups": {
|
||||
"message": "Gruppi della libreria"
|
||||
@@ -735,7 +741,7 @@
|
||||
"message": "Il loader è determinato dal server"
|
||||
},
|
||||
"unknown-pack-warning-modal.body": {
|
||||
"message": "Un file è esaminato quando è caricato su Modrinth, indipendentemente dal suo formato (.mrpack inclusi)."
|
||||
"message": "Solo i file caricati su Modrinth vengono esaminati, qualunque sia il loro formato (.mrpack inclusi)."
|
||||
},
|
||||
"unknown-pack-warning-modal.dont-show-again": {
|
||||
"message": "Non mostrare più questo avviso"
|
||||
@@ -747,10 +753,10 @@
|
||||
"message": "Installa comunque"
|
||||
},
|
||||
"unknown-pack-warning-modal.malware-statement": {
|
||||
"message": "Molto spesso i malware sono distribuiti attraverso i pacchetti di mod, distribuiti in piattaforme come Discord."
|
||||
"message": "Spesso i malware vengono nascosti nei pacchetti di mod, poi distribuiti su piattaforme come Discord."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.body": {
|
||||
"message": "Non siamo riusciti a trovare questo file su Modrinth. Consigliamo di installare file solo da fonti di cui ti fidi."
|
||||
"message": "Non è stato possibile trovare questo file su Modrinth. Consigliamo di installare file solo da fonti attendibili."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.title": {
|
||||
"message": "Tipo di file sconosciuto"
|
||||
|
||||
@@ -104,12 +104,6 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "認証サーバーにアクセスできません"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "サーバーをインスタンスに追加"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "サーバーを自身のインスタンスに追加"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "インスタンスに追加"
|
||||
},
|
||||
@@ -131,21 +125,9 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "既に追加済みのサーバーを非表示にする"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "既にインストールされているコンテンツを非表示にする"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "コンテンツをインスタンスにインストールする"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modパック"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "インストール"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "インストール済み"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "インストール中"
|
||||
},
|
||||
@@ -170,9 +152,6 @@
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Modパック名"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "パックに含めるファイルとフォルダーを選択"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "バージョン番号"
|
||||
},
|
||||
@@ -692,6 +671,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "ワールドは使用中"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "アカウントを追加"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Minecraftアカウント"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "サインインしていません"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "アカウントを一覧から削除"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "アカウントを選択"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Minecraftにサインイン"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "インスタンスによる条件"
|
||||
},
|
||||
|
||||
@@ -104,12 +104,12 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "인증 서버에 연결할 수 없습니다"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "인스턴스에 서버 추가"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "인스턴스에 서버 추가"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "인스턴스에 추가"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "인스턴스에 추가"
|
||||
},
|
||||
@@ -122,6 +122,9 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "이미 추가됨"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "인스턴스로 돌아가기"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "콘텐츠 탐색하기"
|
||||
},
|
||||
@@ -131,20 +134,11 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "이미 추가된 서버 숨기기"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "이미 설치된 콘텐츠 숨기기"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "인스턴스에 콘텐츠 설치"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "모드팩"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "설치"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "설치됨"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "콘텐츠를 추가하면 서버 접속 시 호환이 안 될 수 있습니다. 또한 서버 인스턴스 콘텐츠를 업데이트하면 임의로 추가된 콘텐츠는 모두 손실됩니다."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "설치 중"
|
||||
@@ -164,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "모드팩 내보내기"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "\"{file}\"(을)를 포함할까요?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "모드팩 이름"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "모드팩 이름"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "팩에 포함할 파일과 폴더 선택"
|
||||
"message": "내보내기에 어느 파일이 포함될지 구성"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "버전 구분"
|
||||
@@ -302,6 +299,15 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "{name}을(를) 플레이하려면 업데이트가 필요합니다. 게임을 실행하려면 최신 버전으로 업데이트해 주세요."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "이미 설치되어 있음"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "탐색으로 돌아가기"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "인스턴스에 콘텐츠 설치"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "개발자 모드가 활성화되었습니다."
|
||||
},
|
||||
@@ -692,6 +698,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "사용 중인 세계"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "계정 추가"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "마인크래프트 계정"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "로그인하지 않음"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "계정 제거"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "계정 선택"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "마인크래프트 계정으로 로그인"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "인스턴스에서 관리"
|
||||
},
|
||||
|
||||
@@ -5,20 +5,110 @@
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Muat Turun"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Sembunyikan lebih banyak pemasangan yang berjalan"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Jadikan pemasangan utama"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Tiada pemasangan yang berjalan"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Luar Talian"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Pemasangan utama"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Tunjukkan lebih banyak pemasangan yang berjalan"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Hentikan pemasangan"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Lihat muat turun aktif"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Lihat pemasangan"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Lihat log"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Mendayakan pemaparan lanjutan seperti kesan kabur yang boleh menyebabkan masalah prestasi tanpa pemaparan dipercepatkan perkakasan."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Pemaparan lanjutan"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Pilih tema warna pilihan anda untuk Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Tema warna"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Tukar laman utama yang dibuka oleh pelancar."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Laman utama"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Pustaka"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Laman pendaratan lalai"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Menyahdayakan tanda nama di atas pemain anda pada halaman kekulit."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Sembunyikan tanda nama"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Sertakan dunia terkini dalam bahagian \"Lompat kembali\" pada Laman Utama."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Lompat kembali ke dalam dunia"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Meminimumkan pelancar apabila proses Minecraft bermula."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Minimumkan pelancar"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Gunakan bingkai tetingkap sistem (aplikasi perlu dimulakan semula)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Hiasan asli"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Pilih satu pilihan"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Membolehkan keupayaan untuk menogol bar sisi."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Togol bar sisi"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Jika anda cuba memasang fail Modrinth Pack (.mrpack) yang tidak dihoskan pada Modrinth, kami akan memastikan anda memahami risikonya sebelum memasangnya."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Beri saya amaran sebelum memasang pek mod yang tidak diketahui"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Pelayan pengesahan Minecraft mungkin sedang tergendala sekarang. Periksa sambungan internet anda dan cuba lagi kemudian."
|
||||
},
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Tidak dapat mencapai pelayan pengesahan"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Tambahkan pelayan ke dalam pemasangan"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Tambahkan pelayan ke dalam pemasangan anda"
|
||||
"message": "Menambah pelayan ke dalam pemasangan"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Tambahkan ke dalam pemasangan"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Tambahkan ke dalam pemasangan"
|
||||
@@ -32,14 +122,32 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Sudah ditambah"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Kembali ke pemasangan"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Temui kandungan"
|
||||
},
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Temui pelayan"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Pasang kandungan ke dalam pemasangan"
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Sembunyikan pelayan yang sudah ditambah"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Pek Mod"
|
||||
},
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Menambah kandungan boleh merosakkan keserasian apabila menyertai pelayan. Sebarang kandungan yang ditambah juga akan hilang apabila anda mengemas kini kandungan pemasangan pelayan."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Sedang memasang"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Sedang memasang pek mod..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Masukkan keterangan pek mod..."
|
||||
@@ -50,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Eksport pek mod"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "Sertakan \"{file}\"?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Nama Pek Mod"
|
||||
},
|
||||
@@ -57,7 +168,7 @@
|
||||
"message": "Nama pek mod"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Pilih fail dan folder untuk disertakan dalam pek mod"
|
||||
"message": "Konfigurasikan fail yang disertakan dalam eksport ini"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Nombor versi"
|
||||
@@ -188,6 +299,12 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "Kemas kini diperlukan untuk memainkan {name}. Sila kemas kini kepada versi terkini untuk melancarkan permainan."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Projek ini sudah dipasang"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Pasang kandungan ke dalam pemasangan"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Mod pembangun didayakan."
|
||||
},
|
||||
@@ -578,6 +695,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Dunia sedang digunakan"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Tambah akaun"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Akaun Minecraft"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Tidak didaftar masuk"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Alih keluar akaun"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Pilih akaun"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Daftar masuk ke dalam Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Disediakan oleh pemasangan ini"
|
||||
},
|
||||
@@ -601,5 +736,26 @@
|
||||
},
|
||||
"search.filter.locked.server-loader.title": {
|
||||
"message": "Pemuat adalah disediakan oleh pelayan"
|
||||
},
|
||||
"unknown-pack-warning-modal.body": {
|
||||
"message": "Sesuatu fail hanya disemak jika ia dimuat naik ke Modrinth, tanpa mengira format failnya (termasuk .mrpack)."
|
||||
},
|
||||
"unknown-pack-warning-modal.dont-show-again": {
|
||||
"message": "Jangan tunjukkan amaran ini lagi"
|
||||
},
|
||||
"unknown-pack-warning-modal.header": {
|
||||
"message": "Sahkan pemasangan"
|
||||
},
|
||||
"unknown-pack-warning-modal.install-anyway": {
|
||||
"message": "Pasangkan juga"
|
||||
},
|
||||
"unknown-pack-warning-modal.malware-statement": {
|
||||
"message": "Perisian hasad sering diedarkan melalui fail pek mod dengan berkongsinya di platform seperti Discord."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.body": {
|
||||
"message": "Kami tidak dapat menemui fail ini di Modrinth. Kami sangat mengesyorkan anda untuk hanya memasang fail daripada sumber yang anda percayai."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.title": {
|
||||
"message": "Amaran fail tidak diketahui"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,114 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Java {version} aan het downloaden"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Downloads"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Verberg meer actieve instanties"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Maak primaire instantie"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Geen actieve instanties"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Primaire instantie"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Toon meer actieve instanties"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Stop instantie"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Bekijk actieve downloads"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Bekijk instantie"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Bekijk logs"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Activeert geavanceerde weergaves zoals blur effecten die mogelijke prestatieproblemen opleveren zonder hardware-versnelde weergeving."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Geavanceerde weergave"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Selecteer je voorkeur kleurenthema voor de Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Kleurenthema"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Wijzig de pagina waarop de launcher opent."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Home"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Bibliotheek"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Standaard openingspagina"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Schakelt het nametag boven jouw speler op de skins pagina uit."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Verberg nametag"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Inclusief recente werelden in het gedeelte \"Ga terug\" op de startpagina."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Ga terug in werelden"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Minimaliseer de launcher als een Minecraft proces wordt gestart."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Minimaliseer launcher"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Gebruik systeem venster frame (app opnieuw starten vereist)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Oorspronkelijke versieringen"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Selecteer een optie"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Schakelt de mogelijkheid om de zijbalk in- of uit te schakelen in."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Schakel zijbalk"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Als je probeert een Modrinth Pack-bestand (.mrpack) te installeren dat niet op Modrinth zelf wordt gehost, zorgen we ervoor dat je de risico's begrijpt voordat je het installeert."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Waarschuw mij voordat ik onbekende modpacks installeer"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Minecraft authenticatie servers zijn mogelijk offline. Controleer je internetverbinding en probeer opnieuw later."
|
||||
},
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Authenticatieservers kunnen niet worden bereikt"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Voeg server aan instantie toe"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Voeg servers aan instantie toe"
|
||||
"message": "Server toevoegen aan instantie"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Voeg toe aan een instantie"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Voeg aan instantie toe"
|
||||
@@ -23,14 +122,32 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Al toegevoegd"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Terug naar instantie"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Ontdek content"
|
||||
},
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Ontdek servers"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Installeer inhoud naar instantie"
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Verberg al toegevoegde servers"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Het toevoegen van content kan de compatibiliteit verbreken bij het verbinden met de server. Alle toegevoegde inhoud gaat ook verloren wanneer u de content van de serverinstantie bijwerkt."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Installeren"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Modpack aan het installeren..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Voeg modpack beschrijving in..."
|
||||
@@ -41,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Exporteer modpack"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "Betrek \"{file}\"?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Modpack Naam"
|
||||
},
|
||||
@@ -48,7 +168,7 @@
|
||||
"message": "Modpack naam"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Selecteer bestanden en mappen om toe te voegen aan pack"
|
||||
"message": "Configureer welke bestanden bij deze export zijn betrokken"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Versie nummer"
|
||||
@@ -179,6 +299,12 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "Een update is vereist om {name} te spelen. Update naar de laatste versie om het spel te starten."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Dit project is al geïnstalleerd"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Installeer content naar instantie"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Ontwikkelaarsmodus ingeschakeld."
|
||||
},
|
||||
@@ -569,6 +695,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Wereld wordt gebruikt"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Voeg account toe"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Minecraft account"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Niet ingelogd"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Verwijder account"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Selecteer account"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Log in bij Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Aangeboden door de instantie"
|
||||
},
|
||||
@@ -592,5 +736,26 @@
|
||||
},
|
||||
"search.filter.locked.server-loader.title": {
|
||||
"message": "Loader is gegeven door de server"
|
||||
},
|
||||
"unknown-pack-warning-modal.body": {
|
||||
"message": "Een bestand wordt alleen beoordeeld als het naar Modrinth is geüpload, ongeacht het bestandsformaat (inclusief .mrpack)."
|
||||
},
|
||||
"unknown-pack-warning-modal.dont-show-again": {
|
||||
"message": "Toon deze waarschuwing niet opnieuw"
|
||||
},
|
||||
"unknown-pack-warning-modal.header": {
|
||||
"message": "Bevestig installatie"
|
||||
},
|
||||
"unknown-pack-warning-modal.install-anyway": {
|
||||
"message": "Installeer toch"
|
||||
},
|
||||
"unknown-pack-warning-modal.malware-statement": {
|
||||
"message": "Malware wordt vaak verspreid via modpack-bestanden door ze te delen op platforms zoals Discord."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.body": {
|
||||
"message": "We konden dit bestand niet vinden op Modrinth. We raden ten zeerste aan om alleen bestanden te installeren van bronnen die u vertrouwt."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.title": {
|
||||
"message": "Waarschuwing voor onbekend bestand"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,11 +104,11 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Nie udało się połączyć się z serwerami uwierzytelniania"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Dodaj serwer do instancji"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Dodaj serwery do swojej instancji"
|
||||
"message": "Dodawanie serwera do instancji"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Dodaj do instancji"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Dodaj do instancji"
|
||||
@@ -122,6 +122,9 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Już dodano"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Powrót do instancji"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Odkrywaj zawartość"
|
||||
},
|
||||
@@ -131,20 +134,11 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Ukryj już dodane serwery"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Ukryj już dodane zasoby"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Zainstaluj zasoby do instancji"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Paczki modów"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Zainstaluj"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Zainstalowano"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Dodawanie zawartości może zepsuć kompatybilność podczas dołączania na serwer. Jakakolwiek dodana zawartość zostanie stracona, gdy zaktualizujesz zawartość instancji serwera."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Instalowanie"
|
||||
@@ -164,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Eksportuj paczkę modów"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "Dołączyć \"{file}\"?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Nazwa paczki modów"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "Nazwa paczki modów"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Wybierz pliki i foldery, które mają znaleźć się w paczce"
|
||||
"message": "Skonfiguruje jakie pliki zostaną załączone w tym eksporcie"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Numer wersji"
|
||||
@@ -180,7 +177,7 @@
|
||||
"message": "1.0.0"
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-body": {
|
||||
"message": "Wszystkie dane z Twojej instancji zostaną trwale usunięte, w tym Twoje światy, pliki konfiguracji i wszystkie zainstalowane treści."
|
||||
"message": "Wszystkie dane z Twojej instancji zostaną trwale usunięte, w tym Twoje światy, pliki konfiguracji i jakakolwiek dodana zawartość."
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-header": {
|
||||
"message": "Tej akcji nie można odwrócić"
|
||||
@@ -302,6 +299,15 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "Aktualizacja jest wymagana, aby grać w {name}. Proszę zaktualizować do najnowszej wersji, aby uruchomić grę."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Ten projekt jest już zainstalowany"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "Powróć do odkrywania"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Zainstaluj zasoby do instancji"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Tryb dewelopera włączony."
|
||||
},
|
||||
@@ -692,6 +698,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Świat jest w użytku"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Dodaj konto"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Konto Minecraft"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Nie zalogowano"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Usuń konto"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Wybierz konto"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Zaloguj się do Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Podane przez instancję"
|
||||
},
|
||||
|
||||
@@ -104,11 +104,11 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Não foi possível acessar os servidores de autenticação"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Adicionar servidor à instância"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Adicionar servidores à sua instância"
|
||||
"message": "Adicionando servidor à instância"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Adicionar a uma instância"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Adicionar à instância"
|
||||
@@ -122,6 +122,9 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Já adicionado"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Voltar a instância"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Descubra conteúdo"
|
||||
},
|
||||
@@ -131,20 +134,11 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Ocultar servidores já adicionados"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Ocultar conteúdo já adicionado"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Instalar conteúdo na instância"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Pacotes de mods"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Instalar"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Instalado"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Adicionar conteúdo pode quebrar a compatibilidade ao entrar no servidor. Qualquer conteúdo adicionado também será perdido ao atualizar o conteúdo da instância do servidor."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Instalando"
|
||||
@@ -164,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Exportar pacote de mods"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "Incluir \"{file}\"?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Nome do pacote de mods"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "Nome do pacote de mods"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Selecione arquivos e pastas para incluir no pacote"
|
||||
"message": "Configure quais arquivos serão incluídos nessa exportação"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Número da versão"
|
||||
@@ -302,6 +299,15 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "É necessária uma atualização para jogar {name}. Atualize para a versão mais recente para iniciar o jogo."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Este projeto já foi instalado"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "Voltar ao descobrir"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Instalar conteúdo para a instância"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Modo de desenvolvedor ativado."
|
||||
},
|
||||
|
||||
@@ -1,16 +1,67 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "A descarregar a versão do java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Downloads"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Esconder mais instâncias a correr"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Transformar na instância principal"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Nenhuma instância a correr"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Instância principal"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Mostrar mais instâncias a correr"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Para instância"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Ver instâncias ativas"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Ver instância"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Ver logs"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Renderização avançada"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Seleciona a tua cor preferida para a Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Cor de tema"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Mudar a página em que o launcher abre."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Casa"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Livraria"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Página de carregamento principal"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Os servidores de autenticação do Minecraft poderão estar em baixo de momento. Verifica a tua ligação à internet e tenta novamente mais tarde."
|
||||
},
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Não foi possível alcançar os servidores de autenticação"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Adicionar servidor à instância"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Adicionar servidores à tua instância"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Adicionar à instância"
|
||||
},
|
||||
@@ -29,9 +80,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Descobrir servidores"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Instalar conteúdo à instância"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Insere a descrição do modpack..."
|
||||
},
|
||||
@@ -47,9 +95,6 @@
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Nome do Modpack"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Seleciona as pastas e ficheiros a incluir no pack"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Número da versão"
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"message": "Скачивание Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Скачивания"
|
||||
"message": "Скачивание"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Скрыть другие активные сборки"
|
||||
@@ -104,12 +104,12 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Нет связи с серверами аутентификации"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Добавить сервер в сборку"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Добавление серверов в сборку"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Добавить в сборку"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Добавить в сборку"
|
||||
},
|
||||
@@ -122,6 +122,9 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Уже добавлено"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Вернуться к сборке"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Поиск проектов"
|
||||
},
|
||||
@@ -131,20 +134,11 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Скрывать добавленные"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Скрывать установленные"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Установка контента в сборку"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Сборки"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Установить"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Установлено"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Добавление контента может нарушить совместимость при подключении к серверу. Также весь добавленный контент будет удалён при обновлении содержимого серверной сборки."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Установка"
|
||||
@@ -164,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Экспорт сборки"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "Добавить «{file}» в экспорт?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Название сборки"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "Название сборки"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Выберите файлы и папки для экспорта"
|
||||
"message": "Выберите файлы для экспорта"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Номер версии"
|
||||
@@ -302,6 +299,15 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "Обновите {name} до последней версии, чтобы запустить игру."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Этот проект уже установлен"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "Вернуться к поиску"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Установка контента в сборку"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Режим разработчика включён."
|
||||
},
|
||||
@@ -692,6 +698,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Мир используется"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Добавить"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Учётная запись Minecraft"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Вход не выполнен"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Удалить"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Выберите профиль"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Войти в Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Управляется сборкой"
|
||||
},
|
||||
@@ -717,7 +741,7 @@
|
||||
"message": "Загрузчик управляется сервером"
|
||||
},
|
||||
"unknown-pack-warning-modal.body": {
|
||||
"message": "Проверяются только файлы, загруженные на Modrinth, в том числе файлы .mrpack."
|
||||
"message": "Файлы любого формата проверены на безопасность, если они загружены на Modrinth (в том числе файлы .mrpack)."
|
||||
},
|
||||
"unknown-pack-warning-modal.dont-show-again": {
|
||||
"message": "Больше не предупреждать"
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Serveri za autentifikaciju su nedostupni"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Dodaj server na instancu"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Dodaj servere na tvoju instancu"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Dodaj na instancu"
|
||||
},
|
||||
@@ -29,9 +23,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Otkrij servere"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Instaliraj sadržaj na instancu"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Ukucaj opis modpacka..."
|
||||
},
|
||||
@@ -47,9 +38,6 @@
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Ime modpacka"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Izaberi fajlove i foldere da uključiš u pack"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Broj verzije"
|
||||
},
|
||||
|
||||
@@ -1,16 +1,67 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "Laddar ner Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "Nedladdningar"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Göm fler pågående instanser"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Gör till primärinstans"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Ingen pågående instans"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Primärinstans"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Visa fler pågående instanser"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Avsluta instans"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Visa aktiva nedladdningar"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Visa instans"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Visa loggar"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Aktiverar avancerad rendering som till exempel oskärpa vilket kan leda till prestandaproblem utan hårdvaruaccelererad rendering."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Avancerad rendering"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Välj färgtema för Modrinth-appen."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Färgtema"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Hem"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "Bibliotek"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Dölj namnskylt"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Minecrafts autentiseringsservrar kan vara nere just nu. Kontrollera din internetanslutning och försök igen senare."
|
||||
},
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Kan ej nå autentiseringsservrarna"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Lägg till server till instans"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Lägg till servrar till din instans"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Lägg till i instans"
|
||||
},
|
||||
@@ -29,18 +80,9 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Upptäck servrar"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Installera innehåll till instans"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpaket"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Installera"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Installerad"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Installerar"
|
||||
},
|
||||
@@ -59,9 +101,6 @@
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Modpaketets namn"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Välj filer och mappar att inkludera i paketet"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Versionsnummer"
|
||||
},
|
||||
|
||||
@@ -1,18 +1,111 @@
|
||||
{
|
||||
"app.action-bar.downloading-java": {
|
||||
"message": "ดาวน์โหลดเวอร์ชัน Java {version}"
|
||||
},
|
||||
"app.action-bar.downloads": {
|
||||
"message": "ดาวน์โหลด"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "ซ่อนการดำเนินการโปรแกรมเบื้องหลัง"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "กำหนดโปรแกรมหลัก"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "ไม่มีโปรแกรมใดดำเนินการ"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "ออฟไลน์"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "โปรแกรมหลัก"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "แสดงการดำเนินการโปรแกรมเบื้องหลังเพิ่มเติม"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "หยุดการทำงานโปรแกรม"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "ดูยอดการดาวน์โหลดที่ยังใช้งาน"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "ดูโปรแกรม"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "ดู Log"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "เปิดการใช้งานการเรนเดอร์ขั้นสูง เช่น เอฟเฟคการเบลอ อาจทำให้เกมเกิดปัญหาด้านประสิทธิภาพได้หากไม่มีอุปกรณ์ฮาร์ดแวร์ที่สามารถรองรับการเรนเดอร์ได้"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "การเรนเดอร์ขั้นสูง"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "เลือกธีมสีที่คุณชื่นชอบสำหรับ Modrinth บนอุปกรณ์นี้"
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "ธีมสี"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "เปลี่ยนหน้าไปหน้าต่างที่ลันเชอร์เปิดขึ้นมา"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "หน้าหลัก"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.library": {
|
||||
"message": "รายการ"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "หน้าเริ่มต้น"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "ปิดป้ายชื่อบนศีรษะของผู้เล่นในหน้าแสดงสกิน"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "ซ่อนป้ายชื่อ"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "การแสดงผลโลกล่าสุดที่ผู้เล่นเล่นในส่วน \"กระโดดกลับเข้าไป\" ในหน้าหลัก"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "กระโดดกลับเข้าไปในโลก"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "ย่อลันเชอร์เมื่อกระบวนการการรัน Minecraft เริ่มต้นแล้ว"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "ย่อลันเชอร์"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "ใช้ระบบกรอบหน้าต่าง (ต้องการการรีสตาร์ทโปรแกรมใหม่)"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "ของตกแต่งดั้งเดิม"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "เลือกตัวเลิือก"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "เปิดใช้งานตัวเลือกในการเลือกย่อหรือขยายแถบด้านข้าง"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "ย่อแถบด้านข้าง"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "หากคุณกำลังติดตั้งไฟล์แพ็กของ Modrinth (.mrpack) ที่ไม่ได้โฮสต์โดยตรงบน Modrinth เราจะแจ้งให้คุณตรวจสอบให้มั่นใจก่อนที่จะติดตั้งไฟล์แพ็กดังกล่าว"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "เตือนฉันก่อนติดตั้งแพ็กม็อดที่ไม่รู้จัก"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "เซิร์ฟเวอร์ตรวจสอบสิทธิ์ของ Minecraft อาจใช้งานไม่ได้ในขณะนี้ โปรดตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณและลองใหม่อีกครั้งในภายหลัง"
|
||||
},
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "ไม่สามารถเชื่อมต่อถึงเซิร์ฟเวอร์ได้"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "เพิ่มเซิร์ฟเวอร์ลงในอินสแตนซ์"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "เพิ่มเซิร์ฟเวอร์ลงในอินสแตนซ์ของคุณ"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "เพิ่มลงในอินสแตนซ์"
|
||||
"message": "เพิ่มลงในโปรแกรม"
|
||||
},
|
||||
"app.browse.add-to-instance-name": {
|
||||
"message": "เพิ่มลงใน {instanceName}"
|
||||
@@ -23,44 +116,77 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "เพิ่มแล้ว"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "ติดตั้งเนื้อหาลงในอินสแตนซ์"
|
||||
"app.browse.discover-content": {
|
||||
"message": "สำรวจเนื้อหา"
|
||||
},
|
||||
"app.browse.discover-servers": {
|
||||
"message": "สำรวจเซิร์ฟเวอร์"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "ซ่อนเซิร์ฟเวอร์ที่ดำเนินการเพิ่มแล้ว"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "แพ็กม็อด"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "กำลังติดตั้ง"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "กำลังติดตั้งแพ็กม็อด..."
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "กรอกคำอธิบายมอดแพ็ก.."
|
||||
},
|
||||
"app.export-modal.export-button": {
|
||||
"message": "ส่งออก"
|
||||
},
|
||||
"app.export-modal.header": {
|
||||
"message": "ส่งออกแพ็กม็อด"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "รวมถึง \"{file}\" ใช่หรือไม่"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "ชื่อมอดแพ็ก"
|
||||
"message": "ชื่อแพ็กม็อด"
|
||||
},
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "ชื่อ ม็อดแพ็ก"
|
||||
"message": "ชื่อแพ็กม็อด"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "กำหนดไฟล์ที่ต้องการจะส่งออก"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "หมายเลขเวอร์ชัน"
|
||||
},
|
||||
"app.export-modal.version-number-placeholder": {
|
||||
"message": "1.0.0"
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-body": {
|
||||
"message": "ข้อมูลทั้งหมดในอินสแตนซ์ของคุณจะถูกลบอย่างถาวร รวมถึงโลก การตั้งค่า และเนื้อหาที่ติดตั้งไว้ทั้งหมด"
|
||||
"message": "ข้อมูลทั้งหมดที่ถูกเก็บไว้ในโปรแกรมจะถูกลบออกอย่างถาวร รวมถึงโลก การตั้งค่า และเนื้อหาอื่นที่ติดตั้งไว้ทั้งหมด"
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-header": {
|
||||
"message": "การดำเนินการนี้ไม่สามารถย้อนคืนได้"
|
||||
},
|
||||
"app.instance.confirm-delete.delete-button": {
|
||||
"message": "ลบอินสแตนซ์"
|
||||
"message": "ลบโปรแกรม"
|
||||
},
|
||||
"app.instance.confirm-delete.header": {
|
||||
"message": "ลบอินสแตนซ์"
|
||||
"message": "ลบโปรแกรม"
|
||||
},
|
||||
"app.instance.modpack-already-installed.body": {
|
||||
"message": "มอดแพ็กนี้ถูกติดตั้งไว้ในอินสแตนซ์ <bold>{instanceName}</bold> อยู่แล้ว คุณแน่ใจหรือไม่ว่าต้องการสร้างสำเนาซ้ำ?"
|
||||
"message": "แพ็กม็อดดังกล่าวติดตั้งไว้ใน <bold>{instanceName}</bold> อยู่แล้ว คุณแน่ใจหรือไม่ว่าต้องการจะทำซ้ำ?"
|
||||
},
|
||||
"app.instance.modpack-already-installed.create": {
|
||||
"message": "สร้าง"
|
||||
},
|
||||
"app.instance.modpack-already-installed.header": {
|
||||
"message": "ติดตั้งมอดแพ็กนี้อยู่แล้ว"
|
||||
"message": "แพ็กม็อดดังกล่าวถูกติดตั้งไปแล้ว"
|
||||
},
|
||||
"app.instance.modpack-already-installed.instance": {
|
||||
"message": "อินสแตนซ์"
|
||||
"message": "โปรแกรม"
|
||||
},
|
||||
"app.instance.mods.content-type-project": {
|
||||
"message": "โปรเจกต์"
|
||||
@@ -72,7 +198,10 @@
|
||||
"message": "เพิ่ม {count} โปรเจกต์เรียบร้อยแล้ว"
|
||||
},
|
||||
"app.instance.mods.share-text": {
|
||||
"message": "มาตรวจดูโปรเจกต์ต่าง ๆ ที่ฉันใช้ในมอดแพ็กนี้กัน!"
|
||||
"message": "มาตรวจดูโปรเจกต์ต่าง ๆ ที่ฉันใช้ในแพ็กม็อดนี้กัน!"
|
||||
},
|
||||
"app.instance.mods.share-title": {
|
||||
"message": "แบ่งปันเนื้อหาแพ็กม็อด"
|
||||
},
|
||||
"app.instance.mods.successfully-uploaded": {
|
||||
"message": "อัปโหลดสำเร็จ"
|
||||
@@ -81,16 +210,16 @@
|
||||
"message": "เพิ่มเซิร์ฟเวอร์"
|
||||
},
|
||||
"app.instance.worlds.browse-servers": {
|
||||
"message": "ค้นหาเซิฟเวอร์"
|
||||
"message": "ค้นหาเซิร์ฟเวอร์"
|
||||
},
|
||||
"app.instance.worlds.delete-world-description": {
|
||||
"message": "'{name}' จะถูกลบอย่างถาวร** และจะไม่สามารถกู้คืนได้**"
|
||||
"message": "'{name}' จะ**ถูกลบออกอย่างถาวร** และจะไม่มีทางสามารถกู้คืนได้"
|
||||
},
|
||||
"app.instance.worlds.delete-world-title": {
|
||||
"message": "คุณแน่ใจหรือไม่ว่าต้องการลบโลกนี้อย่างถาวร?"
|
||||
},
|
||||
"app.instance.worlds.filter-modded": {
|
||||
"message": "แบบมีมอด"
|
||||
"message": "ม็อด"
|
||||
},
|
||||
"app.instance.worlds.filter-offline": {
|
||||
"message": "ออฟไลน์"
|
||||
@@ -99,31 +228,31 @@
|
||||
"message": "ออนไลน์"
|
||||
},
|
||||
"app.instance.worlds.filter-vanilla": {
|
||||
"message": "ต้นฉบับ"
|
||||
"message": "วานิลลา"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-description": {
|
||||
"message": "เพิ่มเซิร์ฟเวอร์หรือสำรวจเพื่อเริ่มต้น"
|
||||
"message": "เพิ่มเซิร์ฟเวอร์หรือค้นหาเพื่อเริ่มต้น"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-heading": {
|
||||
"message": "ไม่มีเซิร์ฟเวอร์หรือโลกที่เพิ่มไว้"
|
||||
},
|
||||
"app.instance.worlds.remove-server-description": {
|
||||
"message": "'{name}' จะถูกลบออกจากรายชื่อของคุณ รวมถึงในเกมด้วย และจะไม่สามารถกู้คืนได้"
|
||||
"message": "'{name}' จะถูกลบออกจากรายการของคุณ ซึ่งจะรวมถึงในเกม และการลบดังกล่าวจะไม่มีทางกู้คืนได้อีก"
|
||||
},
|
||||
"app.instance.worlds.remove-server-description-with-address": {
|
||||
"message": "'{name}' ({address}) จะถูกลบออกจากรายชื่อของคุณ รวมถึงในเกมด้วย และจะไม่สามารถกู้คืนได้"
|
||||
"message": "'{name}' ({address}) จะถูกลบออกจากรายการของคุณ ซึ่งจะรวมถึงในเกม และการลบดังกล่าวจะไม่มีทางกู้คืนได้อีก"
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "คุณแน่ใจหรือไม่ว่าต้องการลบ {name}?"
|
||||
},
|
||||
"app.instance.worlds.search-worlds-placeholder": {
|
||||
"message": "ค้นหา {count} โลก"
|
||||
"message": "ค้าหาโลกทั้งหมด {count} โลก"
|
||||
},
|
||||
"app.instance.worlds.this-server": {
|
||||
"message": "เซิร์ฟเวอร์อันนี้"
|
||||
"message": "เซิร์ฟเวอร์นี้"
|
||||
},
|
||||
"app.modal.install-to-play.content-required": {
|
||||
"message": "จำเป็นต้องมีเนื้อหา"
|
||||
"message": "เนื้อหาที่จำเป็น"
|
||||
},
|
||||
"app.modal.install-to-play.header": {
|
||||
"message": "ติดตั้งเพื่อเล่น"
|
||||
@@ -131,17 +260,20 @@
|
||||
"app.modal.install-to-play.install-button": {
|
||||
"message": "ติดตั้ง"
|
||||
},
|
||||
"app.modal.install-to-play.mod-count": {
|
||||
"message": "{count, plural, other {# ม็อด}}"
|
||||
},
|
||||
"app.modal.install-to-play.required-modpack": {
|
||||
"message": "มอดแพ็กที่จำเป็น"
|
||||
},
|
||||
"app.modal.install-to-play.server-requires-mods": {
|
||||
"message": "เซิร์ฟเวอร์นี้จำเป็นต้องใช้มอดเพื่อเข้าเล่น คลิกติดตั้ง เพื่อตั้งค่าไฟล์ที่จำเป็นจาก Modrinth จากนั้นจึงจะเข้าสู่เซิร์ฟเวอร์โดยตรงได้"
|
||||
"message": "เซิร์ฟเวอร์ดังกล่าวจำเป็นต้องใช้ม็อดเพื่อเล่น โปรดติดตั้งและตั้งค่าไฟล์อื่นใดที่จำเป็นจาก Modrinth ก่อน จากนั้นถึงจะสามารถเข้าเล่นเซิร์ฟเวอร์ได้"
|
||||
},
|
||||
"app.modal.install-to-play.shared-instance": {
|
||||
"message": "อินสแตนซ์ที่ใช้ร่วมกัน"
|
||||
"message": "โปรแกรมที่มีร่วมกัน"
|
||||
},
|
||||
"app.modal.install-to-play.shared-server-instance": {
|
||||
"message": "อินสแตนซ์เซิร์ฟเวอร์ที่ใช้ร่วมกัน"
|
||||
"message": "เซิร์ฟเวอร์ของโปรแกรมที่มีร่วมกัน"
|
||||
},
|
||||
"app.modal.install-to-play.view-contents": {
|
||||
"message": "ดูเนื้อหา"
|
||||
@@ -159,13 +291,13 @@
|
||||
"message": "โหมดนักพัฒนาเปิดอยู่"
|
||||
},
|
||||
"app.settings.downloading": {
|
||||
"message": "ดาวน์โหลด เวอร์ชั่น{version}"
|
||||
"message": "ดาวน์โหลดเวอร์ชัน {version}"
|
||||
},
|
||||
"app.settings.tabs.appearance": {
|
||||
"message": "หน้าตา"
|
||||
},
|
||||
"app.settings.tabs.default-instance-options": {
|
||||
"message": "ตัวเลือกอินสแตนซ์เริ่มต้น"
|
||||
"message": "ตั้งค่าโปรแกรมเริ่มต้น"
|
||||
},
|
||||
"app.settings.tabs.java-installations": {
|
||||
"message": "การจัดการ Java ที่ติดตั้ง"
|
||||
@@ -180,22 +312,28 @@
|
||||
"message": "การจัดการทรัพยากร"
|
||||
},
|
||||
"app.update-popup.body": {
|
||||
"message": "แอฟ Modrinth v{version} พร้อมสำหรับการติดตั้งแล้ว! รีโหลดแอปเพื่ออัปเดต หรือ อัปเดตอัตโนมัติเมื่อคุณปิดแอฟ Modrinth"
|
||||
"message": "Modrinth v{version} พร้อมสำหรับการติดตั้งแล้ว! เปิดโปรแกรมใหม่อีกครั้งเพื่ออัปเดตตอนนี้ หรือจะรออัปเดตอัตโนมัติ ซึ่งจะเกิดขึ้นเมื่อคุณกดปิดโปรแกรม Modrinth"
|
||||
},
|
||||
"app.update-popup.body.download-complete": {
|
||||
"message": "แอฟ Modrinth v{version} ดาวน์โหลดเสร็จแล้ว รีโหลดแอปเพื่ออัปเดต หรือ อัปเดตอัตโนมัติเมื่อคุณปิดแอฟ Modrinth"
|
||||
"message": "ดาวน์โหลด Modrinth v{version} สำเร็จแล้ว เปิดโปรแกรมใหม่อีกครั้งเพื่ออัปเดตตอนนี้ หรือจะรออัปเดตอัตโนมัติ ซึ่งจะเกิดขึ้นเมื่อคุณกดปิดโปรแกรม Modrinth"
|
||||
},
|
||||
"app.update-popup.body.linux": {
|
||||
"message": "Modrinth App v{version} พร้อมให้อัปเดตแล้ว โปรดใช้ตัวจัดการแพ็กเกจ ของคุณเพื่ออัปเดตฟีเจอร์ใหม่และตัวแก้ไขล่าสุด!"
|
||||
"message": "Modrinth v{version} พร้อมให้อัปเดตแล้ว โปรดไปที่การจัดการแพ็กเกจของคุณสำหรับการอัปเดตคุณสมบัติใหม่และการแก้ไขต่าง ๆ"
|
||||
},
|
||||
"app.update-popup.body.metered": {
|
||||
"message": "แอฟ Modrinth v{version} พร้อมแล้ว! เนื่องจากคุณเปิดโหมดจำกัดปริมาณข้อมูล เราจึงไม่ได้อัปเดตอัตโนมัติ"
|
||||
"message": "Modrinth v{version} พร้อมให้บริการแล้ว! แต่เนื่องจากคุณเปิดโหมดจำกัดปริมาณการใช้ข้อมูลเครือข่าย เราจึงไม่ได้อัปเดตเนื้อหาโดยอัตโนมัติ"
|
||||
},
|
||||
"app.update-popup.changelog": {
|
||||
"message": "บันทึกการเปลี่ยนแปลง"
|
||||
},
|
||||
"app.update-popup.download": {
|
||||
"message": "ดาวโหลด ({size})"
|
||||
},
|
||||
"app.update-popup.download-complete": {
|
||||
"message": "ดาวน์โหลดเสร็จสมบูรณ์"
|
||||
"message": "การดาวน์โหลดเสร็จสมบูรณ์"
|
||||
},
|
||||
"app.update-popup.reload": {
|
||||
"message": "โหลดใหม่"
|
||||
},
|
||||
"app.update-popup.title": {
|
||||
"message": "มีการอัปเดตใหม่"
|
||||
@@ -207,13 +345,13 @@
|
||||
"message": "เวอร์ชั่น {version} ถูกติดตั้งแล้ว"
|
||||
},
|
||||
"app.update.download-update": {
|
||||
"message": "ดาวน์โหลดอัพเดต"
|
||||
"message": "ดาวน์โหลดอัปเดต"
|
||||
},
|
||||
"app.update.downloading-update": {
|
||||
"message": "ดาวน์โหลดอัพเดตไปแล้ว ({percent}%)"
|
||||
"message": "ดาวน์โหลดอัปเดตไปแล้ว ({percent}%)"
|
||||
},
|
||||
"app.update.reload-to-update": {
|
||||
"message": "รีโหลดเพื่อติดตั้งอัพเดต"
|
||||
"message": "รีโหลดเพื่อติดตั้งอัปเดต"
|
||||
},
|
||||
"app.world.server-modal.placeholder-address": {
|
||||
"message": "example.modrinth.gg"
|
||||
@@ -222,7 +360,10 @@
|
||||
"message": "เลือกตัวเลือก"
|
||||
},
|
||||
"app.world.world-item.incompatible-version": {
|
||||
"message": "เวอร์ชัน {version} ไม่รองรับ"
|
||||
"message": "ไม่สามารถเข้ากับเวอร์ชัน {version} ได้"
|
||||
},
|
||||
"app.world.world-item.not-played-yet": {
|
||||
"message": "ยังไม่เคยเล่น"
|
||||
},
|
||||
"app.world.world-item.offline": {
|
||||
"message": "ออฟไลน์"
|
||||
@@ -233,6 +374,9 @@
|
||||
"friends.action.add-friend": {
|
||||
"message": "เพิ่มเพื่อน"
|
||||
},
|
||||
"friends.action.view-friend-requests": {
|
||||
"message": "คำขอเป็นเพื่อน {count} {count, plural, other {คน}}"
|
||||
},
|
||||
"friends.add-friend.submit": {
|
||||
"message": "ส่งคำขอเป็นเพื่อน"
|
||||
},
|
||||
@@ -240,13 +384,13 @@
|
||||
"message": "กำลังเพิ่มเพื่อน"
|
||||
},
|
||||
"friends.add-friend.username.description": {
|
||||
"message": "อาจจะแตกต่างกับชื่อใน Minecraft"
|
||||
"message": "มันอาจแตกต่างจากชื่อในเกม Minecraft ของพวกเขา!"
|
||||
},
|
||||
"friends.add-friend.username.placeholder": {
|
||||
"message": "ใส่ชื่อบน Modrinth"
|
||||
"message": "กรอกชื่อผู้ใช้ Modrinth..."
|
||||
},
|
||||
"friends.add-friend.username.title": {
|
||||
"message": "เพื่อนของคุณมีชื่อบน Modrinth ว่าอะไร"
|
||||
"message": "เพื่อนของคุณมีชื่อผู้ใช้ Modrinth ว่าอะไร?"
|
||||
},
|
||||
"friends.add-friends-to-share": {
|
||||
"message": "<link>เพิ่มเพื่อน</link> เพื่อเห็นว่าพวกเขากำลังเล่นอะไรอยู่!"
|
||||
@@ -273,13 +417,13 @@
|
||||
"message": "ออฟไลน์"
|
||||
},
|
||||
"friends.heading.online": {
|
||||
"message": "ออฟไลน์"
|
||||
"message": "ออนไลน์"
|
||||
},
|
||||
"friends.heading.pending": {
|
||||
"message": "กําลังดำเนินการ"
|
||||
},
|
||||
"friends.no-friends-match": {
|
||||
"message": "ไม่มีเพื่อนชื่อว่า \"{query}\""
|
||||
"message": "ไม่มีรายชื่อเพื่อนที่ตรงกับ \"{query}\""
|
||||
},
|
||||
"friends.search-friends-placeholder": {
|
||||
"message": "ค้นหาเพื่อน"
|
||||
@@ -321,7 +465,7 @@
|
||||
"message": "โลก Minecraft"
|
||||
},
|
||||
"instance.edit-world.reset-icon": {
|
||||
"message": "รีเซ็ตรูปตัวอย่าง"
|
||||
"message": "รีเซ็ตรูปสัญลักษณ์"
|
||||
},
|
||||
"instance.edit-world.title": {
|
||||
"message": "แก้ไขโลก"
|
||||
@@ -348,13 +492,13 @@
|
||||
"message": "ทั่วไป"
|
||||
},
|
||||
"instance.settings.tabs.general.delete": {
|
||||
"message": "ลบอินสแตนซ์"
|
||||
"message": "ลบโปรแกรม"
|
||||
},
|
||||
"instance.settings.tabs.general.delete.button": {
|
||||
"message": "ลบอินสแตนซ์"
|
||||
"message": "ลบโปรแกรม"
|
||||
},
|
||||
"instance.settings.tabs.general.delete.description": {
|
||||
"message": "ลบอินสแตนซ์ออกจากอุปกรณ์ของคุณอย่างถาวร รวมถึงโลก การตั้งค่า และเนื้อหาทั้งหมดที่ติดตั้งไว้ โปรดระมัดระวัง เนื่องจากเมื่อถูกลบแล้วจะไม่สามารถกู้คืนได้อีก"
|
||||
"message": "การกระทำดังกล่าวจะลบโปรแกรมออกจากอุปกรณ์ของคุณอย่างถาวร รวมถึงโลก การตั้งค่า และเนื้อหาทั้งหมดที่ติดตั้งไว้ โปรดระมัดระวัง เนื่องจากเมื่อถูกลบแล้วจะไม่สามารถกู้คืนได้อีก"
|
||||
},
|
||||
"instance.settings.tabs.general.deleting.button": {
|
||||
"message": "กำลังลบ..."
|
||||
@@ -366,31 +510,31 @@
|
||||
"message": "ไม่สามารถทำซ้ำได้ขณะติดตั้ง"
|
||||
},
|
||||
"instance.settings.tabs.general.duplicate-instance": {
|
||||
"message": "สร้างสำเนาอินสแตนซ์"
|
||||
"message": "สร้างสำเนาโปรแกรม"
|
||||
},
|
||||
"instance.settings.tabs.general.duplicate-instance.description": {
|
||||
"message": "สร้างสำเนาของอินสแตนซ์นี้ รวมถึงโลก การตั้งค่า ม็อด และอื่นๆ"
|
||||
"message": "สร้างสำเนาของโปรแกรมนี้ รวมถึงโลก การตั้งค่า ม็อด และอื่น ๆ"
|
||||
},
|
||||
"instance.settings.tabs.general.edit-icon": {
|
||||
"message": "แก้ไขไอคอน"
|
||||
"message": "แก้ไขสัญลักษณ์"
|
||||
},
|
||||
"instance.settings.tabs.general.edit-icon.remove": {
|
||||
"message": "ลบไอคอน"
|
||||
"message": "ลบสัญลักษณ์"
|
||||
},
|
||||
"instance.settings.tabs.general.edit-icon.replace": {
|
||||
"message": "เปลี่ยนไอคอน"
|
||||
"message": "แทนที่สัญลักษณ์"
|
||||
},
|
||||
"instance.settings.tabs.general.edit-icon.select": {
|
||||
"message": "เลือกรูปไอคอน"
|
||||
"message": "เลือกรูปสัญลักษณ์"
|
||||
},
|
||||
"instance.settings.tabs.general.library-groups": {
|
||||
"message": "การจัดกลุ่ม"
|
||||
"message": "จัดกลุ่ม"
|
||||
},
|
||||
"instance.settings.tabs.general.library-groups.create": {
|
||||
"message": "สร้างกลุ่มใหม่"
|
||||
},
|
||||
"instance.settings.tabs.general.library-groups.description": {
|
||||
"message": "กลุ่มไลบรารีช่วยให้คุณจัดระเบียบอินสแตนซ์ของคุณเป็นหมวดหมู่ต่างๆ ภายในไลบรารี"
|
||||
"message": "การจัดกลุ่มจะช่วยจัดระเบียบโปรแกรมของคุณภายในหน้ารายการให้เป็นหมวดหมู่"
|
||||
},
|
||||
"instance.settings.tabs.general.library-groups.enter-name": {
|
||||
"message": "ใส่ชื่อกลุ่ม"
|
||||
@@ -399,43 +543,58 @@
|
||||
"message": "ชื่อ"
|
||||
},
|
||||
"instance.settings.tabs.hooks": {
|
||||
"message": "ตัวดักการปล่อย"
|
||||
"message": "Launch Hook"
|
||||
},
|
||||
"instance.settings.tabs.hooks.custom-hooks": {
|
||||
"message": "กำหนดเอง"
|
||||
"message": "กำหนด Launch Hook เอง"
|
||||
},
|
||||
"instance.settings.tabs.hooks.description": {
|
||||
"message": "ตัวดักจับช่วยให้ผู้ใช้ระดับสูงสามารถรันคำสั่งระบบบางอย่างได้ ทั้งก่อนและหลังการเปิดเกม"
|
||||
"message": "Launch Hook จะช่วยให้ผู้เล่นที่ชำนาญสามารถใช้คำสั่งต่อระบบบางอย่างได้ ทั้งก่อนและหลังเกมถูกเปิด"
|
||||
},
|
||||
"instance.settings.tabs.hooks.post-exit": {
|
||||
"message": "Post-exit"
|
||||
},
|
||||
"instance.settings.tabs.hooks.post-exit.description": {
|
||||
"message": "จะรันหลังจากที่ตัวเกมถูกปิด"
|
||||
},
|
||||
"instance.settings.tabs.hooks.post-exit.enter": {
|
||||
"message": "ป้อนคำสั่งหลังจบการทำงาน..."
|
||||
"message": "ป้อนคำสั่ง..."
|
||||
},
|
||||
"instance.settings.tabs.hooks.pre-launch": {
|
||||
"message": "ก่อนรัน"
|
||||
"message": "Pre-launch"
|
||||
},
|
||||
"instance.settings.tabs.hooks.pre-launch.description": {
|
||||
"message": "จะรันก่อนที่โปรแกรมจะทำงาน"
|
||||
},
|
||||
"instance.settings.tabs.hooks.pre-launch.enter": {
|
||||
"message": "ป้อนคำสั่ง..."
|
||||
},
|
||||
"instance.settings.tabs.hooks.title": {
|
||||
"message": "ฮุกการเปิดเกม"
|
||||
"message": "Game Launch Hook"
|
||||
},
|
||||
"instance.settings.tabs.hooks.wrapper": {
|
||||
"message": "ตัวห่อหุ้ม"
|
||||
"message": "Wrapper"
|
||||
},
|
||||
"instance.settings.tabs.hooks.wrapper.description": {
|
||||
"message": "คำสั่งตัวห่อหุ้มสำหรับเปิด ไมน์คราฟต์"
|
||||
"message": "คำสั่ง Wrapper สำหรับใช้ในการรันเปิด Minecraft"
|
||||
},
|
||||
"instance.settings.tabs.hooks.wrapper.enter": {
|
||||
"message": "ป้อนตัวห่อหุ้มคำสั่ง..."
|
||||
"message": "ป้อนคำสั่ง Wrapper..."
|
||||
},
|
||||
"instance.settings.tabs.installation": {
|
||||
"message": "การติดตั้ง"
|
||||
},
|
||||
"instance.settings.tabs.installation.loader-version": {
|
||||
"message": "เวอร์ชั่น {loader}"
|
||||
"message": "เวอร์ชัน {loader}"
|
||||
},
|
||||
"instance.settings.tabs.java": {
|
||||
"message": "Java และ หน่วยความจำ"
|
||||
},
|
||||
"instance.settings.tabs.java.environment-variables": {
|
||||
"message": "Environment Variables"
|
||||
},
|
||||
"instance.settings.tabs.java.hooks": {
|
||||
"message": "ฮุก"
|
||||
"message": "Hook"
|
||||
},
|
||||
"instance.settings.tabs.java.java-arguments": {
|
||||
"message": "อาร์กิวเมนต์ Java"
|
||||
@@ -449,6 +608,9 @@
|
||||
"instance.settings.tabs.window": {
|
||||
"message": "หน้าต่าง"
|
||||
},
|
||||
"instance.settings.tabs.window.custom-window-settings": {
|
||||
"message": "การตั้งค่าการปรับแต่งหน้าต่าง"
|
||||
},
|
||||
"instance.settings.tabs.window.fullscreen": {
|
||||
"message": "เต็มหน้าจอ"
|
||||
},
|
||||
@@ -474,7 +636,7 @@
|
||||
"message": "ป้อนความกว้าง..."
|
||||
},
|
||||
"instance.worlds.a_minecraft_server": {
|
||||
"message": "เซิร์ฟเวอร์ ไมน์คราฟต์"
|
||||
"message": "เซิร์ฟเวอร์ Minecraft"
|
||||
},
|
||||
"instance.worlds.cant_connect": {
|
||||
"message": "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้"
|
||||
@@ -486,7 +648,7 @@
|
||||
"message": "ไม่ต้องแสดงในหน้าแรก"
|
||||
},
|
||||
"instance.worlds.game_already_open": {
|
||||
"message": "อินสแตนซ์ถูกเปิดอยู่แล้ว"
|
||||
"message": "โปรแกรมถูกเปิดอยู่แล้ว"
|
||||
},
|
||||
"instance.worlds.hardcore": {
|
||||
"message": "โหมดฮาร์ดคอร์"
|
||||
@@ -507,24 +669,75 @@
|
||||
"message": "คุณสามารถเข้าสู่โลกผู้เล่นคนเดียวได้โดยตรงบน Minecraft 1.20 ขึ้นไปเท่านั้น"
|
||||
},
|
||||
"instance.worlds.play_instance": {
|
||||
"message": "เริ่มเล่นอินสแตนซ์"
|
||||
"message": "เริ่มรันโปรแกรม"
|
||||
},
|
||||
"instance.worlds.view_instance": {
|
||||
"message": "ดูอินสแตนซ์"
|
||||
"message": "ดูโปรแกรม"
|
||||
},
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "โลกนี้กำลังถูกใช้งานอยู่"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "เพิ่มบัญชี"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "บัญชี Minecraft"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "ยังไม่ได้ลงชื่อเข้าใช้"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "ลบบัญชี"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "เลือกบัญชี"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "ลงชื่อเข้าใช้ Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "อินสแตนซ์เป็นผู้จัดเตรียม"
|
||||
"message": "โปรแกรมเป็นผู้กำหนด"
|
||||
},
|
||||
"search.filter.locked.instance-game-version.title": {
|
||||
"message": "เวอร์ชันเกมถูกกำหนดไว้โดยโปรแกรมแล้ว"
|
||||
},
|
||||
"search.filter.locked.instance-loader.title": {
|
||||
"message": "ตัวรันถูกกำหนดไว้โดยโปรแกรมแล้ว"
|
||||
},
|
||||
"search.filter.locked.instance.sync": {
|
||||
"message": "ซิงค์กับอินสแตนซ์"
|
||||
"message": "เชื่อมต่อกับโปรแกรม"
|
||||
},
|
||||
"search.filter.locked.server": {
|
||||
"message": "เซิร์ฟเวอร์เป็นผู้จัดเตรียม"
|
||||
"message": "เซิร์ฟเวอร์เป็นผู้กำหนด"
|
||||
},
|
||||
"search.filter.locked.server-environment.title": {
|
||||
"message": "มีเพียงม็อดสำหรับฝั่งเครื่องของผู้เล่นเท่านั้นที่สามารถเพิ่มลงในโปรแกรมเซิร์ฟเวอร์ดังกล่าวได้"
|
||||
},
|
||||
"search.filter.locked.server-game-version.title": {
|
||||
"message": "เซิร์ฟเวอร์เป็นผู้จัดเตรียมเวอร์ชันเกม"
|
||||
"message": "เวอร์ชันเกมถูกกำหนดโดยเซิร์ฟเวอร์แล้ว"
|
||||
},
|
||||
"search.filter.locked.server-loader.title": {
|
||||
"message": "ตัวรันถูกกำหนดโดยเซิร์ฟเวอร์แล้ว"
|
||||
},
|
||||
"unknown-pack-warning-modal.body": {
|
||||
"message": "ไฟล์จะได้รับการตรวจสอบโดยไม่คำนึงถึงประเภทของไฟล์ (รวมทั้ง .mrpack) เมื่อไฟล์ถูกอัปโหลดขึ้น Modrinth"
|
||||
},
|
||||
"unknown-pack-warning-modal.dont-show-again": {
|
||||
"message": "อย่าแสดงคำเตือนนี้อีก"
|
||||
},
|
||||
"unknown-pack-warning-modal.header": {
|
||||
"message": "ยืนยันการติดตั้ง"
|
||||
},
|
||||
"unknown-pack-warning-modal.install-anyway": {
|
||||
"message": "ดำเนินการติดตั้งต่อไป"
|
||||
},
|
||||
"unknown-pack-warning-modal.malware-statement": {
|
||||
"message": "มัลแวร์มักแฝงตัวมากับไฟล์แพ็กม็อดผ่านการแชร์ผ่านแพลตฟอร์มที่ไม่ใช่แพลตฟอร์มเฉพาะ เช่น ดิสคอร์ด"
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.body": {
|
||||
"message": "เราไม่สามารถค้นหาไฟล์ดังกล่าวได้บน Modrinth พวกเราขอแนะนำอย่างมากกว่าควรติดตั้งไฟล์จากแหล่งที่น่าเชื่อถือเท่านั้น"
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.title": {
|
||||
"message": "แจ้งเตือนไฟล์ไม่รู้จัก"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,12 +104,6 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Doğrulama sunucularına erişilemedi"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Sunucuyu kurulumu ekle"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Kurulumunuza sunucular ekleyin"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Kuruluma ekle"
|
||||
},
|
||||
@@ -131,21 +125,9 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Zaten eklenmiş sunucuları gizle"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Zaten yüklenmiş içerikleri gizle"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "İçeriği kuruluma yükle"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Mod paketleri"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Yükle"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Yüklü"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Yükleniyor"
|
||||
},
|
||||
@@ -170,9 +152,6 @@
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Modpaketi adı"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Pakete dahil edilecek dosya ve klasörleri seçin"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Sürüm numarası"
|
||||
},
|
||||
@@ -692,6 +671,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Dünya şu anda kullanımda"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Hesap ekle"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Minecraft hesabı"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Giriş yapılmadı"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Hesabı kaldır"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Hesap seçin"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Minecraft'a giriş yapın"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Kurulum tarafından sağlanan"
|
||||
},
|
||||
|
||||
@@ -6,49 +6,49 @@
|
||||
"message": "Завантаження"
|
||||
},
|
||||
"app.action-bar.hide-more-running-instances": {
|
||||
"message": "Приховати більше запущених екземплярів"
|
||||
"message": "Приховати більше запущених профілів"
|
||||
},
|
||||
"app.action-bar.make-primary-instance": {
|
||||
"message": "Зробити основним екземпляром"
|
||||
"message": "Зробити основним профілем"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "Немає запущених екземплярів"
|
||||
"message": "Немає запущених профілів"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "Офлайн"
|
||||
"message": "Поза мережею"
|
||||
},
|
||||
"app.action-bar.primary-instance": {
|
||||
"message": "Основний екземпляр"
|
||||
"message": "Основний профіль"
|
||||
},
|
||||
"app.action-bar.show-more-running-instances": {
|
||||
"message": "Показати більше запущених екземплярів"
|
||||
"message": "Показати більше запущених профілів"
|
||||
},
|
||||
"app.action-bar.stop-instance": {
|
||||
"message": "Зупинити екземпляр"
|
||||
"message": "Зупинити профіль"
|
||||
},
|
||||
"app.action-bar.view-active-downloads": {
|
||||
"message": "Переглянути активні завантаження"
|
||||
},
|
||||
"app.action-bar.view-instance": {
|
||||
"message": "Переглянути екземпляр"
|
||||
"message": "Переглянути профіль"
|
||||
},
|
||||
"app.action-bar.view-logs": {
|
||||
"message": "Переглянути журнали"
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.description": {
|
||||
"message": "Дозволяє розширений рендеринг, такий як ефекти розмиття, які можуть спричиняти проблеми з продуктивністю без апаратно-прискореного рендерингу."
|
||||
"message": "Дозволяє розширене промальовування, як-от ефекти розмиття, які можуть спричиняти проблеми з продуктивністю без апаратно-прискореного промальовування."
|
||||
},
|
||||
"app.appearance-settings.advanced-rendering.title": {
|
||||
"message": "Розширений рендеринг"
|
||||
"message": "Розширене промальовування"
|
||||
},
|
||||
"app.appearance-settings.color-theme.description": {
|
||||
"message": "Виберіть бажану колірну тему для програми Modrinth App."
|
||||
"message": "Виберіть бажану колірну тему для Modrinth App."
|
||||
},
|
||||
"app.appearance-settings.color-theme.title": {
|
||||
"message": "Колірна тема"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.description": {
|
||||
"message": "Змініть сторінку, на якій відкривається панель запуску."
|
||||
"message": "Змініть сторінку, на якій відкривається запускач."
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.home": {
|
||||
"message": "Головна"
|
||||
@@ -57,46 +57,46 @@
|
||||
"message": "Бібліотека"
|
||||
},
|
||||
"app.appearance-settings.default-landing-page.title": {
|
||||
"message": "Цільова сторінка за замовчуванням"
|
||||
"message": "Усталена цільова сторінка"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "Вимикає іменний бейдж над вашим гравцем на сторінці скінів."
|
||||
"message": "Вимикає ім'я над вашим гравцем на сторінці скінів."
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "Приховати бейдж з іменем"
|
||||
"message": "Приховати ім'я"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "Включає нещодавні світи в розділі «Повернення» на головній сторінці."
|
||||
"message": "Уключає нещодавні світи в розділі «Повернутися» на головній сторінці."
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.title": {
|
||||
"message": "Стрибни назад у світи"
|
||||
"message": "Повернутися у світи"
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.description": {
|
||||
"message": "Згорнути лаунчер під час запуску процесу Minecraft."
|
||||
"message": "Згортає запускач під час запуску процесу Minecraft."
|
||||
},
|
||||
"app.appearance-settings.minimize-launcher.title": {
|
||||
"message": "Згорнути панель запуску"
|
||||
"message": "Мінімізувати запускач"
|
||||
},
|
||||
"app.appearance-settings.native-decorations.description": {
|
||||
"message": "Використовувати рамку системного вікна (потрібно перезапустити програму)."
|
||||
},
|
||||
"app.appearance-settings.native-decorations.title": {
|
||||
"message": "Рідні прикраси"
|
||||
"message": "Системне оформлення"
|
||||
},
|
||||
"app.appearance-settings.select-option": {
|
||||
"message": "Виберіть опцію"
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.description": {
|
||||
"message": "Вмикає можливість перемикання бічної панелі."
|
||||
"message": "Умикає можливість перемикання бокової панелі."
|
||||
},
|
||||
"app.appearance-settings.toggle-sidebar.title": {
|
||||
"message": "Перемикання бічної панелі"
|
||||
"message": "Перемикання бокової панелі"
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.description": {
|
||||
"message": "Якщо ви спробуєте встановити файл пакета Modrinth (.mrpack), який не розміщено на Modrinth, ми переконаємося, що ви розумієте ризики, перш ніж встановлювати його."
|
||||
"message": "Якщо ви спробуєте встановити файл пакета Modrinth (.mrpack), який не розміщено на Modrinth, ми переконаємося, що ви розумієте ризики, перш ніж установлювати його."
|
||||
},
|
||||
"app.appearance-settings.unknown-pack-warning.title": {
|
||||
"message": "Попереджати мене перед встановленням невідомих модпаків"
|
||||
"message": "Попереджати мене перед установленням невідомих збірок"
|
||||
},
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Сервери автентифікації Minecraft можуть зараз не працювати. Перевірте з’єднання з інтернетом та спробуйте пізніше."
|
||||
@@ -104,11 +104,11 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Не вдається зв’язатися зі серверами автентифікації"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Додати сервер до профілю"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Додати сервера до вашого профілю"
|
||||
"message": "Додавання сервера до профілю"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "Додати до профілю"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Додати до профілю"
|
||||
@@ -122,6 +122,9 @@
|
||||
"app.browse.already-added": {
|
||||
"message": "Уже додано"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "Назад до профілю"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Дослідити вміст"
|
||||
},
|
||||
@@ -129,34 +132,25 @@
|
||||
"message": "Дослідити сервера"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Скрити наявні сервери"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Скрити доданий контент"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Установити вміст в профіль"
|
||||
"message": "Сховати наявні сервери"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Модпаки"
|
||||
"message": "Збірки"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "Установлено"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "Встановлено"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "Додавання вмісту може порушити сумісність при приєднанні до сервера. Усі додані матеріали також будуть утрачені під час оновлення вмісту профілю сервера."
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "Встановлення"
|
||||
"message": "Установлення"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.description": {
|
||||
"message": "{fileName}"
|
||||
},
|
||||
"app.creation-modal.installing-modpack.title": {
|
||||
"message": "Встановлення модпаку..."
|
||||
"message": "Установлення збірки…"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Уведіть опис збірки..."
|
||||
"message": "Уведіть опис збірки…"
|
||||
},
|
||||
"app.export-modal.export-button": {
|
||||
"message": "Експортувати"
|
||||
@@ -164,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "Експортувати збірку"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "Уключити «{file}»?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Назва збірки"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "Назва збірки"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Виберіть файли й теки, щоб додати їх до збірки"
|
||||
"message": "Змінити які файли додані до експорту"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Номер версії"
|
||||
@@ -180,7 +177,7 @@
|
||||
"message": "1.0.0"
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-body": {
|
||||
"message": "Усі дані вашого профілю будуть видалені назавжди, включно з вашими світами, конфігураціями та усім встановленим контентом."
|
||||
"message": "Усі дані вашого профілю будуть видалені назавжди, включно з вашими світами, налаштуваннями та всім установленим умістом."
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-header": {
|
||||
"message": "Ця дія є незворотною"
|
||||
@@ -234,7 +231,7 @@
|
||||
"message": "Ви впевнені, що хочете назавжди видалити цей світ?"
|
||||
},
|
||||
"app.instance.worlds.filter-modded": {
|
||||
"message": "Модифікований"
|
||||
"message": "Модифіковано"
|
||||
},
|
||||
"app.instance.worlds.filter-offline": {
|
||||
"message": "Поза мережею"
|
||||
@@ -243,7 +240,7 @@
|
||||
"message": "У мережі"
|
||||
},
|
||||
"app.instance.worlds.filter-vanilla": {
|
||||
"message": "Ванільний"
|
||||
"message": "Стандартний"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-description": {
|
||||
"message": "Додати сервер або переглянути, щоб почати"
|
||||
@@ -261,7 +258,7 @@
|
||||
"message": "Ви впевнені, що хочете видалити «{name}»?"
|
||||
},
|
||||
"app.instance.worlds.search-worlds-placeholder": {
|
||||
"message": "Пошук {count} світів..."
|
||||
"message": "Пошук {count} світів…"
|
||||
},
|
||||
"app.instance.worlds.this-server": {
|
||||
"message": "цей сервер"
|
||||
@@ -302,6 +299,15 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "«{name}» потребує оновлення, щоб грати. Будь ласка, оновіть гру до останньої версії, щоб запустити її."
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "Проєкт уже встановлено"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "Повернутися до дослідження"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "Установити вміст до профілю"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "Увімкнено режим розробника."
|
||||
},
|
||||
@@ -441,7 +447,7 @@
|
||||
"message": "Немає друзів, які збігаються з «{query}»"
|
||||
},
|
||||
"friends.search-friends-placeholder": {
|
||||
"message": "Пошук друзів..."
|
||||
"message": "Пошук друзів…"
|
||||
},
|
||||
"friends.section.heading": {
|
||||
"message": "{title} — {count}"
|
||||
@@ -489,7 +495,7 @@
|
||||
"message": "Додавання файлів ({completed}/{total})"
|
||||
},
|
||||
"instance.files.save-as": {
|
||||
"message": "Зберегти як..."
|
||||
"message": "Зберегти як…"
|
||||
},
|
||||
"instance.server-modal.address": {
|
||||
"message": "Адреса"
|
||||
@@ -692,6 +698,24 @@
|
||||
"instance.worlds.world_in_use": {
|
||||
"message": "Світ наразі використовується"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "Додати обліковий запис"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Майнкрафт аккаунт"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "Не ввійдено в систему"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "Видалити обліковий запис"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "Обрати обліковий запис"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "Увійти в Майнкрафт"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "Надано профілем"
|
||||
},
|
||||
@@ -729,7 +753,7 @@
|
||||
"message": "Усе одно інсталювати"
|
||||
},
|
||||
"unknown-pack-warning-modal.malware-statement": {
|
||||
"message": "Шкідливе програмне забезпечення часто поширюють через файли модпаків, які публікуються на таких платформах, як Discord."
|
||||
"message": "Шкідливе програмне забезпечення часто поширюють через файли збірок, які публікуються на таких платформах, як Discord."
|
||||
},
|
||||
"unknown-pack-warning-modal.warning.body": {
|
||||
"message": "Ми не змогли знайти цей файл на Modrinth. Ми рекомендуємо встановлювати файли лише з тих джерел яким ви довіряєте."
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Không thể kết nối đến máy chủ xác thực"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Thêm máy chủ vào hồ sơ"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Thêm máy chủ vào hồ sơ của bạn"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Thêm vào hồ sơ"
|
||||
},
|
||||
@@ -29,9 +23,6 @@
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Khám phá máy chủ"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Tải nội dung này vào hồ sơ"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Thêm miêu tả cho gói modpack..."
|
||||
},
|
||||
@@ -47,9 +38,6 @@
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Tên modpack"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Chọn tệp và gói để thêm vào gói tài nguyên"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Phiên bản"
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"message": "设为主实例"
|
||||
},
|
||||
"app.action-bar.no-instances-running": {
|
||||
"message": "没有实例正在运行"
|
||||
"message": "没有运行中的实例"
|
||||
},
|
||||
"app.action-bar.offline": {
|
||||
"message": "离线"
|
||||
@@ -60,10 +60,10 @@
|
||||
"message": "默认起始页"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.description": {
|
||||
"message": "在皮肤页面中禁用你玩家头顶上的名称标签。"
|
||||
"message": "在皮肤页面中禁用你玩家头顶上的名牌。"
|
||||
},
|
||||
"app.appearance-settings.hide-nametag.title": {
|
||||
"message": "隐藏名称标签"
|
||||
"message": "隐藏名牌"
|
||||
},
|
||||
"app.appearance-settings.jump-back-into-worlds.description": {
|
||||
"message": "在主页的“快速回到”部分包含最近的世界。"
|
||||
@@ -104,14 +104,14 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "无法连接到身份验证服务器"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "将服务器添加到实例实例"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "将服务器添加到你的实例"
|
||||
"message": "将服务器添加到实例"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "添加到实例"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "添加到实例实例"
|
||||
"message": "添加到实例"
|
||||
},
|
||||
"app.browse.add-to-instance-name": {
|
||||
"message": "添加到 {instanceName}"
|
||||
@@ -120,7 +120,10 @@
|
||||
"message": "已添加"
|
||||
},
|
||||
"app.browse.already-added": {
|
||||
"message": "已添加"
|
||||
"message": "已添加过"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "返回实例"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "发现内容"
|
||||
@@ -131,20 +134,11 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "隐藏已添加的服务器"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "隐藏已安装的内容"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "将内容安装到实例中"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "整合包"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "安装"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "已安装"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "加入服务器时,添加内容可能会破坏兼容性。当更新服务器实例内容时任何添加的内容也将丢失。"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "安装中"
|
||||
@@ -164,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "导出整合包"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "包含 \"{file}\"?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "整合包名称"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "整合包名称"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "选择要包含在包中的文件和文件夹"
|
||||
"message": "配置此导出中包含哪些文件"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "版本号"
|
||||
@@ -302,6 +299,15 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "需要更新至最新版本才能运行 {name}。请更新后启动游戏。"
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "此项目已安装"
|
||||
},
|
||||
"app.project.install-context.back-to-browse": {
|
||||
"message": "返回发现页"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "将内容安装到实例中"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "开发者模式已启用。"
|
||||
},
|
||||
@@ -561,7 +567,7 @@
|
||||
"message": "启动 Hooks"
|
||||
},
|
||||
"instance.settings.tabs.hooks.custom-hooks": {
|
||||
"message": "自定义启动Hooks"
|
||||
"message": "自定义启动 Hooks"
|
||||
},
|
||||
"instance.settings.tabs.hooks.description": {
|
||||
"message": "Hooks允许高级用户在启动游戏前后运行特定的系统命令。"
|
||||
|
||||
@@ -104,11 +104,11 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "無法連線到驗證伺服器"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "將伺服器新增到實例"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "將伺服器新增到實例"
|
||||
"message": "將伺服器新增至實例"
|
||||
},
|
||||
"app.browse.add-to-an-instance": {
|
||||
"message": "新增至實例"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "新增至實例"
|
||||
@@ -120,7 +120,10 @@
|
||||
"message": "已新增"
|
||||
},
|
||||
"app.browse.already-added": {
|
||||
"message": "已新增"
|
||||
"message": "已經新增"
|
||||
},
|
||||
"app.browse.back-to-instance": {
|
||||
"message": "回到實例"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "探索內容"
|
||||
@@ -131,20 +134,11 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "隱藏已新增的伺服器"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "隱藏已安裝的內容"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "安裝至實例中"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "模組包"
|
||||
},
|
||||
"app.browse.server.install": {
|
||||
"message": "安裝"
|
||||
},
|
||||
"app.browse.server.installed": {
|
||||
"message": "已安裝"
|
||||
"app.browse.server-instance-content-warning": {
|
||||
"message": "加入內容可能會導致加入伺服器時發生相容性問題。當你更新伺服器實例內容時,任何新增的內容也會遺失。"
|
||||
},
|
||||
"app.browse.server.installing": {
|
||||
"message": "安裝中"
|
||||
@@ -164,6 +158,9 @@
|
||||
"app.export-modal.header": {
|
||||
"message": "匯出模組包"
|
||||
},
|
||||
"app.export-modal.include-file-accessibility-label": {
|
||||
"message": "要包含「{file}」嗎?"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "模組包名稱"
|
||||
},
|
||||
@@ -171,7 +168,7 @@
|
||||
"message": "模組包名稱"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "選擇要包含在模組包中的檔案與資料夾"
|
||||
"message": "設定要包含在此匯出檔案中的檔案"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "版本號碼"
|
||||
@@ -198,7 +195,7 @@
|
||||
"message": "建立"
|
||||
},
|
||||
"app.instance.modpack-already-installed.header": {
|
||||
"message": "模組包已經安裝"
|
||||
"message": "模組包已安裝"
|
||||
},
|
||||
"app.instance.modpack-already-installed.instance": {
|
||||
"message": "實例"
|
||||
@@ -302,6 +299,12 @@
|
||||
"app.modal.update-to-play.update-required-description": {
|
||||
"message": "需要更新才能遊玩「{name}」。請更新至最新版本以啟動遊戲。"
|
||||
},
|
||||
"app.project.install-button.already-installed": {
|
||||
"message": "這個專案已安裝"
|
||||
},
|
||||
"app.project.install-context.install-content-to-instance": {
|
||||
"message": "將內容安裝至實例"
|
||||
},
|
||||
"app.settings.developer-mode-enabled": {
|
||||
"message": "開發人員模式已啟用。"
|
||||
},
|
||||
@@ -693,22 +696,22 @@
|
||||
"message": "世界正在使用中"
|
||||
},
|
||||
"minecraft-account.add-account": {
|
||||
"message": "添加賬號"
|
||||
"message": "新增帳號"
|
||||
},
|
||||
"minecraft-account.label": {
|
||||
"message": "Minecraft 賬號"
|
||||
"message": "Minecraft 帳號"
|
||||
},
|
||||
"minecraft-account.not-signed-in": {
|
||||
"message": "未登錄"
|
||||
"message": "未登入"
|
||||
},
|
||||
"minecraft-account.remove-account": {
|
||||
"message": "移除賬號"
|
||||
"message": "移除帳號"
|
||||
},
|
||||
"minecraft-account.select-account": {
|
||||
"message": "選擇賬號"
|
||||
"message": "選擇帳號"
|
||||
},
|
||||
"minecraft-account.sign-in": {
|
||||
"message": "登錄 Minecraft"
|
||||
"message": "登入 Minecraft"
|
||||
},
|
||||
"search.filter.locked.instance": {
|
||||
"message": "由該實例提供"
|
||||
|
||||
@@ -218,7 +218,11 @@
|
||||
:key="instance.path"
|
||||
>
|
||||
<template v-if="Component">
|
||||
<Suspense :key="instance.path">
|
||||
<Suspense
|
||||
:key="instance.path"
|
||||
@pending="subpagePending = true"
|
||||
@resolve="subpagePending = false"
|
||||
>
|
||||
<component
|
||||
:is="Component"
|
||||
:instance="instance"
|
||||
@@ -228,6 +232,7 @@
|
||||
:installed="instance.install_stage !== 'installed'"
|
||||
:is-server-instance="isServerInstance"
|
||||
:open-settings="() => settingsModal?.show(1)"
|
||||
v-bind="contentSubpageProps"
|
||||
@play="updatePlayState"
|
||||
@stop="() => stopInstance('InstanceSubpage')"
|
||||
></component>
|
||||
@@ -295,6 +300,7 @@ import {
|
||||
ServerPing,
|
||||
ServerRecentPlays,
|
||||
ServerRegion,
|
||||
useLoadingBarToken,
|
||||
} from '@modrinth/ui'
|
||||
import { useQueryClient } from '@tanstack/vue-query'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
@@ -312,6 +318,7 @@ import { useInstanceConsole } from '@/composables/useInstanceConsole'
|
||||
import { trackEvent } from '@/helpers/analytics'
|
||||
import { get_project_v3 } from '@/helpers/cache.js'
|
||||
import { process_listener, profile_listener } from '@/helpers/events'
|
||||
import { type InstanceContentData, loadInstanceContentData } from '@/helpers/instance-content'
|
||||
import { get_by_profile_path } from '@/helpers/process'
|
||||
import { finish_install, get, get_full_path, kill, run } from '@/helpers/profile'
|
||||
import type { GameInstance } from '@/helpers/types'
|
||||
@@ -331,6 +338,7 @@ const route = useRoute()
|
||||
|
||||
const router = useRouter()
|
||||
const breadcrumbs = useBreadcrumbs()
|
||||
const contentSubpageRouteNames = new Set(['Mods', 'ModsFilter'])
|
||||
|
||||
const offline = ref(!navigator.onLine)
|
||||
window.addEventListener('offline', () => {
|
||||
@@ -341,12 +349,16 @@ window.addEventListener('online', () => {
|
||||
})
|
||||
|
||||
const instance = ref<GameInstance>()
|
||||
const preloadedContent = ref<InstanceContentData | null>(null)
|
||||
const playing = ref(false)
|
||||
const loading = ref(false)
|
||||
const subpagePending = ref(false)
|
||||
const stopping = ref(false)
|
||||
const exportModal = ref<InstanceType<typeof ExportModal>>()
|
||||
const updateToPlayModal = ref<InstanceType<typeof UpdateToPlayModal>>()
|
||||
|
||||
useLoadingBarToken(subpagePending)
|
||||
|
||||
const isServerInstance = ref(false)
|
||||
const linkedProjectV3 = ref<Labrinth.Projects.v3.Project>()
|
||||
const selected = ref<unknown[]>([])
|
||||
@@ -361,36 +373,55 @@ const playersOnline = ref<number | undefined>(undefined)
|
||||
const ping = ref<number | undefined>(undefined)
|
||||
const loadingServerPing = ref(false)
|
||||
|
||||
function isContentSubpageRoute(routeName = route.name) {
|
||||
return typeof routeName === 'string' && contentSubpageRouteNames.has(routeName)
|
||||
}
|
||||
|
||||
async function fetchInstance() {
|
||||
isServerInstance.value = false
|
||||
linkedProjectV3.value = undefined
|
||||
preloadedContent.value = null
|
||||
ping.value = undefined
|
||||
playersOnline.value = undefined
|
||||
loadingServerPing.value = false
|
||||
|
||||
instance.value = await get(route.params.id as string).catch(handleError)
|
||||
const nextInstance = await get(route.params.id as string).catch(handleError)
|
||||
let nextLinkedProjectV3: Labrinth.Projects.v3.Project | undefined
|
||||
let nextIsServerInstance = false
|
||||
|
||||
if (!offline.value && instance.value?.linked_data && instance.value.linked_data.project_id) {
|
||||
const contentPreloadPromise =
|
||||
nextInstance && isContentSubpageRoute()
|
||||
? loadInstanceContentData(nextInstance.path, undefined, handleError)
|
||||
: Promise.resolve(null)
|
||||
|
||||
if (!offline.value && nextInstance?.linked_data && nextInstance.linked_data.project_id) {
|
||||
try {
|
||||
linkedProjectV3.value = await get_project_v3(
|
||||
instance.value.linked_data.project_id,
|
||||
nextLinkedProjectV3 = await get_project_v3(
|
||||
nextInstance.linked_data.project_id,
|
||||
'must_revalidate',
|
||||
)
|
||||
|
||||
if (linkedProjectV3.value?.minecraft_server != null) {
|
||||
isServerInstance.value = true
|
||||
if (nextLinkedProjectV3?.minecraft_server != null) {
|
||||
nextIsServerInstance = true
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error as Error)
|
||||
}
|
||||
}
|
||||
|
||||
const nextPreloadedContent = await contentPreloadPromise
|
||||
|
||||
instance.value = nextInstance ?? undefined
|
||||
linkedProjectV3.value = nextLinkedProjectV3
|
||||
isServerInstance.value = nextIsServerInstance
|
||||
preloadedContent.value = nextPreloadedContent
|
||||
|
||||
fetchDeferredData()
|
||||
|
||||
if (instance.value) {
|
||||
if (nextInstance) {
|
||||
queryClient.prefetchQuery({
|
||||
queryKey: ['worlds', instance.value.path],
|
||||
queryFn: () => refreshWorlds(instance.value!.path),
|
||||
queryKey: ['worlds', nextInstance.path],
|
||||
queryFn: () => refreshWorlds(nextInstance.path),
|
||||
staleTime: 30_000,
|
||||
})
|
||||
}
|
||||
@@ -448,6 +479,9 @@ const renderMode = computed<'scroll' | 'fixed'>(() =>
|
||||
route.meta.renderMode === 'fixed' ? 'fixed' : 'scroll',
|
||||
)
|
||||
const isFixedRender = computed(() => renderMode.value === 'fixed')
|
||||
const contentSubpageProps = computed(() =>
|
||||
isContentSubpageRoute() ? { preloadedContent: preloadedContent.value } : {},
|
||||
)
|
||||
|
||||
const tabs = computed(() => [
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
:modpack-name="linkedModpackProject?.title"
|
||||
:modpack-icon-url="linkedModpackProject?.icon_url ?? undefined"
|
||||
:enable-toggle="!props.isServerInstance"
|
||||
:busy="isBulkOperating"
|
||||
:get-overflow-options="getOverflowOptions"
|
||||
:switch-version="handleSwitchVersion"
|
||||
@update:enabled="handleModpackContentToggle"
|
||||
@@ -87,12 +88,12 @@ import {
|
||||
ReadyTransition,
|
||||
useDebugLogger,
|
||||
useVIntl,
|
||||
versionChangesGameVersion,
|
||||
} from '@modrinth/ui'
|
||||
import { getCurrentWebview } from '@tauri-apps/api/webview'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
import { computed, nextTick, onUnmounted, ref, watch } from 'vue'
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import ExportModal from '@/components/ui/ExportModal.vue'
|
||||
@@ -100,22 +101,20 @@ import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
|
||||
import { trackEvent } from '@/helpers/analytics'
|
||||
import { get_project_versions, get_version } from '@/helpers/cache.js'
|
||||
import { profile_listener } from '@/helpers/events.js'
|
||||
import { type InstanceContentData, loadInstanceContentData } from '@/helpers/instance-content'
|
||||
import {
|
||||
add_project_from_path,
|
||||
add_project_from_version,
|
||||
duplicate,
|
||||
edit,
|
||||
get,
|
||||
get_content_items,
|
||||
get_linked_modpack_content,
|
||||
get_linked_modpack_info,
|
||||
list,
|
||||
remove_project,
|
||||
toggle_disable_project,
|
||||
update_managed_modrinth_version,
|
||||
update_project,
|
||||
} from '@/helpers/profile'
|
||||
import { get_categories } from '@/helpers/tags.js'
|
||||
import type { CacheBehaviour, GameInstance } from '@/helpers/types'
|
||||
import { highlightModInProfile } from '@/helpers/utils.js'
|
||||
import { injectContentInstall } from '@/providers/content-install'
|
||||
@@ -160,6 +159,7 @@ const props = defineProps<{
|
||||
instance: GameInstance
|
||||
isServerInstance?: boolean
|
||||
openSettings?: () => void
|
||||
preloadedContent?: InstanceContentData | null
|
||||
}>()
|
||||
|
||||
const loading = ref(true)
|
||||
@@ -212,6 +212,7 @@ const contentUpdaterModal = ref<InstanceType<typeof ContentUpdaterModal> | null>
|
||||
const modpackContentModal = ref<InstanceType<typeof ModpackContentModal> | null>()
|
||||
const modpackUpdateConfirmModal = ref<InstanceType<typeof ConfirmModpackUpdateModal> | null>()
|
||||
|
||||
// TODO: Extract content operation and updater modal state into composables; this page currently owns file mutations, dependency installs, busy flags, and version selection flow.
|
||||
const updatingProject = ref<ContentItem | null>(null)
|
||||
const updatingProjectVersions = ref<Labrinth.Versions.v2.Version[]>([])
|
||||
const loadingVersions = ref(false)
|
||||
@@ -219,6 +220,78 @@ const loadingChangelog = ref(false)
|
||||
const updatingModpack = ref(false)
|
||||
const pendingModpackUpdateVersion = ref<Labrinth.Versions.v2.Version | null>(null)
|
||||
const isModpackUpdateDowngrade = ref(false)
|
||||
const activeContentOperationKeys = ref(new Set<string>())
|
||||
|
||||
let activeContentOperationCount = 0
|
||||
let updateRequestId = 0
|
||||
const activeUpdateRequestId = ref(0)
|
||||
|
||||
function fileNameFromPath(path: string) {
|
||||
return path.split('/').pop() ?? path
|
||||
}
|
||||
|
||||
function getContentOperationKeys(item: ContentItem) {
|
||||
return [item.id, item.file_path, item.file_name, item.project?.id, item.version?.id].filter(
|
||||
(key): key is string => !!key,
|
||||
)
|
||||
}
|
||||
|
||||
function hasContentOperation(item: ContentItem) {
|
||||
const keys = getContentOperationKeys(item)
|
||||
return keys.some((key) => activeContentOperationKeys.value.has(key))
|
||||
}
|
||||
|
||||
function setContentItemBusy(item: ContentItem, busy: boolean, originalFileName = item.file_name) {
|
||||
item.installing = busy
|
||||
modpackContentModal.value?.updateItem(originalFileName, {
|
||||
installing: busy,
|
||||
disabled: busy,
|
||||
})
|
||||
if (item.file_name !== originalFileName) {
|
||||
modpackContentModal.value?.updateItem(item.file_name, {
|
||||
installing: busy,
|
||||
disabled: busy,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function beginContentOperation(item: ContentItem) {
|
||||
if (hasContentOperation(item)) return null
|
||||
|
||||
const keys = getContentOperationKeys(item)
|
||||
activeContentOperationKeys.value = new Set([...activeContentOperationKeys.value, ...keys])
|
||||
activeContentOperationCount++
|
||||
isBulkOperating.value = true
|
||||
setContentItemBusy(item, true)
|
||||
|
||||
return { keys, originalFileName: item.file_name }
|
||||
}
|
||||
|
||||
function finishContentOperation(
|
||||
item: ContentItem,
|
||||
operation: { keys: string[]; originalFileName: string },
|
||||
) {
|
||||
const nextKeys = new Set(activeContentOperationKeys.value)
|
||||
for (const key of operation.keys) {
|
||||
nextKeys.delete(key)
|
||||
}
|
||||
activeContentOperationKeys.value = nextKeys
|
||||
activeContentOperationCount = Math.max(0, activeContentOperationCount - 1)
|
||||
setContentItemBusy(item, false, operation.originalFileName)
|
||||
if (activeContentOperationCount === 0) {
|
||||
isBulkOperating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function beginUpdateRequest() {
|
||||
updateRequestId++
|
||||
activeUpdateRequestId.value = updateRequestId
|
||||
return updateRequestId
|
||||
}
|
||||
|
||||
function isActiveUpdateRequest(requestId: number) {
|
||||
return activeUpdateRequestId.value === requestId
|
||||
}
|
||||
|
||||
async function handleBrowseContent() {
|
||||
if (!props.instance) return
|
||||
@@ -265,9 +338,21 @@ async function handleUploadFiles() {
|
||||
}
|
||||
|
||||
async function toggleDisableMod(mod: ContentItem) {
|
||||
if (!mod.file_path) return
|
||||
const operation = beginContentOperation(mod)
|
||||
if (!operation) return
|
||||
|
||||
try {
|
||||
mod.file_path = await toggle_disable_project(props.instance.path, mod.file_path!)
|
||||
const newPath = await toggle_disable_project(props.instance.path, mod.file_path)
|
||||
const newFileName = fileNameFromPath(newPath)
|
||||
mod.file_path = newPath
|
||||
mod.file_name = newFileName
|
||||
mod.enabled = !mod.enabled
|
||||
modpackContentModal.value?.updateItem(operation.originalFileName, {
|
||||
file_path: newPath,
|
||||
file_name: newFileName,
|
||||
enabled: mod.enabled,
|
||||
})
|
||||
|
||||
trackEvent('InstanceProjectDisable', {
|
||||
loader: props.instance.loader,
|
||||
@@ -279,33 +364,48 @@ async function toggleDisableMod(mod: ContentItem) {
|
||||
})
|
||||
} catch (err) {
|
||||
handleError(err as Error)
|
||||
} finally {
|
||||
finishContentOperation(mod, operation)
|
||||
}
|
||||
}
|
||||
|
||||
const toggleDisableDebounced = useDebounceFn(toggleDisableMod, 20)
|
||||
const toggleDisableDebounced = toggleDisableMod
|
||||
|
||||
async function removeMod(mod: ContentItem) {
|
||||
await remove_project(props.instance.path, mod.file_path!).catch(handleError)
|
||||
projects.value = projects.value.filter((x) => mod.file_path !== x.file_path)
|
||||
if (!mod.file_path) return
|
||||
const operation = beginContentOperation(mod)
|
||||
if (!operation) return
|
||||
|
||||
trackEvent('InstanceProjectRemove', {
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
id: mod.project?.id,
|
||||
name: mod.project?.title ?? mod.file_name,
|
||||
project_type: mod.project_type,
|
||||
})
|
||||
try {
|
||||
const removedPath = mod.file_path
|
||||
await remove_project(props.instance.path, removedPath)
|
||||
projects.value = projects.value.filter((x) => removedPath !== x.file_path)
|
||||
|
||||
trackEvent('InstanceProjectRemove', {
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
id: mod.project?.id,
|
||||
name: mod.project?.title ?? mod.file_name,
|
||||
project_type: mod.project_type,
|
||||
})
|
||||
} catch (err) {
|
||||
handleError(err as Error)
|
||||
} finally {
|
||||
finishContentOperation(mod, operation)
|
||||
}
|
||||
}
|
||||
|
||||
async function updateProject(mod: ContentItem) {
|
||||
try {
|
||||
const newPath = await update_project(props.instance.path, mod.file_path!)
|
||||
mod.file_path = newPath
|
||||
if (!mod.file_path) return
|
||||
const operation = beginContentOperation(mod)
|
||||
if (!operation) return
|
||||
|
||||
if (mod.update_version_id) {
|
||||
const versionData = await get_version(mod.update_version_id, 'must_revalidate').catch(
|
||||
handleError,
|
||||
)
|
||||
try {
|
||||
const updateVersionId = mod.update_version_id
|
||||
await update_project(props.instance.path, mod.file_path)
|
||||
|
||||
if (updateVersionId) {
|
||||
const versionData = await get_version(updateVersionId, 'must_revalidate').catch(handleError)
|
||||
|
||||
if (versionData) {
|
||||
const profile = await get(props.instance.path).catch(handleError)
|
||||
@@ -316,12 +416,6 @@ async function updateProject(mod: ContentItem) {
|
||||
}
|
||||
}
|
||||
|
||||
mod.has_update = false
|
||||
if (mod.version && mod.update_version_id) {
|
||||
mod.version.id = mod.update_version_id
|
||||
}
|
||||
mod.update_version_id = null
|
||||
|
||||
trackEvent('InstanceProjectUpdate', {
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
@@ -331,32 +425,55 @@ async function updateProject(mod: ContentItem) {
|
||||
})
|
||||
} catch (err) {
|
||||
handleError(err as Error)
|
||||
} finally {
|
||||
await refreshContentState('must_revalidate')
|
||||
finishContentOperation(mod, operation)
|
||||
}
|
||||
}
|
||||
|
||||
async function switchProjectVersion(mod: ContentItem, version: Labrinth.Versions.v2.Version) {
|
||||
isBulkOperating.value = true
|
||||
mod.installing = true
|
||||
if (mod.version) {
|
||||
mod.version.id = version.id
|
||||
mod.version.version_number = version.version_number
|
||||
}
|
||||
if (!mod.file_path) return
|
||||
const operation = beginContentOperation(mod)
|
||||
if (!operation) return
|
||||
|
||||
const oldPath = mod.file_path
|
||||
const wasDisabled = mod.enabled === false || oldPath.endsWith('.disabled')
|
||||
let newPath: string | null = null
|
||||
let shouldRemoveNewOnError = false
|
||||
|
||||
try {
|
||||
await remove_project(props.instance.path, mod.file_path!)
|
||||
const newPath = await add_project_from_version(props.instance.path, version.id, 'standalone')
|
||||
newPath = await add_project_from_version(props.instance.path, version.id, 'update')
|
||||
shouldRemoveNewOnError = newPath !== oldPath
|
||||
|
||||
if (wasDisabled) {
|
||||
newPath = await toggle_disable_project(props.instance.path, newPath)
|
||||
}
|
||||
|
||||
const profile = await get(props.instance.path).catch(handleError)
|
||||
if (profile) {
|
||||
await installVersionDependencies(profile, version, 'update').catch(handleError)
|
||||
}
|
||||
|
||||
mod.file_path = newPath
|
||||
shouldRemoveNewOnError = false
|
||||
if (newPath !== oldPath) {
|
||||
await remove_project(props.instance.path, oldPath)
|
||||
}
|
||||
|
||||
trackEvent('InstanceProjectUpdate', {
|
||||
loader: props.instance.loader,
|
||||
game_version: props.instance.game_version,
|
||||
id: mod.project?.id,
|
||||
name: mod.project?.title ?? mod.file_name,
|
||||
project_type: mod.project_type,
|
||||
})
|
||||
} catch (err) {
|
||||
if (shouldRemoveNewOnError && newPath && newPath !== oldPath) {
|
||||
await remove_project(props.instance.path, newPath).catch(() => {})
|
||||
}
|
||||
handleError(err as Error)
|
||||
} finally {
|
||||
mod.installing = false
|
||||
isBulkOperating.value = false
|
||||
await initProjects()
|
||||
await refreshContentState('must_revalidate')
|
||||
finishContentOperation(mod, operation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,6 +481,8 @@ async function handleUpdate(id: string) {
|
||||
const item = projects.value.find((p) => p.id === id)
|
||||
if (!item?.has_update || !item.project?.id || !item.version?.id) return
|
||||
|
||||
const requestId = beginUpdateRequest()
|
||||
|
||||
debug('handleUpdate triggered', {
|
||||
fileName: item.file_name,
|
||||
projectType: item.project_type,
|
||||
@@ -384,12 +503,47 @@ async function handleUpdate(id: string) {
|
||||
|
||||
await nextTick()
|
||||
|
||||
contentUpdaterModal.value?.show(item.update_version_id ?? undefined)
|
||||
const initialVersionId = item.update_version_id ?? undefined
|
||||
debug('handleUpdate: opening content updater modal', {
|
||||
type: 'content',
|
||||
initialVersionId,
|
||||
item: {
|
||||
id: item.id,
|
||||
fileName: item.file_name,
|
||||
projectType: item.project_type,
|
||||
projectId: item.project.id,
|
||||
projectTitle: item.project.title,
|
||||
currentVersionId: item.version.id,
|
||||
currentVersionNumber: item.version.version_number,
|
||||
updateVersionId: item.update_version_id,
|
||||
},
|
||||
instance: {
|
||||
path: props.instance.path,
|
||||
name: props.instance.name,
|
||||
gameVersion: props.instance.game_version,
|
||||
loader: props.instance.loader,
|
||||
linkedData: props.instance.linked_data,
|
||||
},
|
||||
modalStateBeforeFetch: {
|
||||
updatingModpack: updatingModpack.value,
|
||||
updatingProjectId: updatingProject.value?.id,
|
||||
updatingProjectVersions: updatingProjectVersions.value.map((version) => ({
|
||||
id: version.id,
|
||||
versionNumber: version.version_number,
|
||||
gameVersions: version.game_versions,
|
||||
loaders: version.loaders,
|
||||
datePublished: version.date_published,
|
||||
})),
|
||||
},
|
||||
})
|
||||
contentUpdaterModal.value?.show(initialVersionId)
|
||||
|
||||
const versions = (await get_project_versions(item.project.id).catch((e) => {
|
||||
return handleError(e)
|
||||
})) as Labrinth.Versions.v2.Version[] | null
|
||||
|
||||
if (!isActiveUpdateRequest(requestId) || updatingProject.value?.id !== item.id) return
|
||||
|
||||
loadingVersions.value = false
|
||||
|
||||
if (!versions) {
|
||||
@@ -414,6 +568,25 @@ async function handleUpdate(id: string) {
|
||||
versions.sort(
|
||||
(a, b) => new Date(b.date_published).getTime() - new Date(a.date_published).getTime(),
|
||||
)
|
||||
const preselectedVersion =
|
||||
versions.find((version) => version.id === initialVersionId) ?? versions[0] ?? null
|
||||
debug('handleUpdate: resolved content updater preselection', {
|
||||
type: 'content',
|
||||
initialVersionId,
|
||||
foundInitialVersion: versions.some((version) => version.id === initialVersionId),
|
||||
preselectedVersion: preselectedVersion
|
||||
? {
|
||||
id: preselectedVersion.id,
|
||||
versionNumber: preselectedVersion.version_number,
|
||||
gameVersions: preselectedVersion.game_versions,
|
||||
loaders: preselectedVersion.loaders,
|
||||
datePublished: preselectedVersion.date_published,
|
||||
}
|
||||
: null,
|
||||
versionCount: versions.length,
|
||||
currentVersionId: item.version.id,
|
||||
updateVersionId: item.update_version_id,
|
||||
})
|
||||
|
||||
updatingProjectVersions.value = versions
|
||||
}
|
||||
@@ -421,6 +594,8 @@ async function handleUpdate(id: string) {
|
||||
async function handleSwitchVersion(item: ContentItem) {
|
||||
if (!item.project?.id || !item.version?.id) return
|
||||
|
||||
const requestId = beginUpdateRequest()
|
||||
|
||||
updatingModpack.value = false
|
||||
updatingProject.value = item
|
||||
updatingProjectVersions.value = []
|
||||
@@ -435,6 +610,8 @@ async function handleSwitchVersion(item: ContentItem) {
|
||||
return handleError(e)
|
||||
})) as Labrinth.Versions.v2.Version[] | null
|
||||
|
||||
if (!isActiveUpdateRequest(requestId) || updatingProject.value?.id !== item.id) return
|
||||
|
||||
loadingVersions.value = false
|
||||
|
||||
if (!versions) return
|
||||
@@ -468,9 +645,28 @@ async function handleModpackContent() {
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshModpackContentItems(cacheBehaviour?: CacheBehaviour) {
|
||||
if (!props.instance?.path) return
|
||||
|
||||
const contentItems = await get_linked_modpack_content(props.instance.path, cacheBehaviour).catch(
|
||||
handleError,
|
||||
)
|
||||
|
||||
if (contentItems) {
|
||||
modpackContentModal.value?.setItems(contentItems)
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshContentState(cacheBehaviour?: CacheBehaviour) {
|
||||
await initProjects(cacheBehaviour)
|
||||
await refreshModpackContentItems(cacheBehaviour)
|
||||
}
|
||||
|
||||
async function handleModpackUpdate() {
|
||||
if (!props.instance?.linked_data?.project_id) return
|
||||
|
||||
const requestId = beginUpdateRequest()
|
||||
|
||||
updatingModpack.value = true
|
||||
updatingProject.value = null
|
||||
updatingProjectVersions.value = []
|
||||
@@ -479,14 +675,42 @@ async function handleModpackUpdate() {
|
||||
|
||||
await nextTick()
|
||||
|
||||
contentUpdaterModal.value?.show(
|
||||
linkedModpackUpdateVersionId.value ?? props.instance?.linked_data?.version_id ?? undefined,
|
||||
)
|
||||
const initialVersionId =
|
||||
linkedModpackUpdateVersionId.value ?? props.instance?.linked_data?.version_id ?? undefined
|
||||
debug('handleModpackUpdate: opening modpack updater modal', {
|
||||
type: 'modpack',
|
||||
initialVersionId,
|
||||
linkedModpackUpdateVersionId: linkedModpackUpdateVersionId.value,
|
||||
linkedModpackProject: linkedModpackProject.value,
|
||||
linkedModpackVersion: linkedModpackVersion.value,
|
||||
linkedModpackHasUpdate: linkedModpackHasUpdate.value,
|
||||
instance: {
|
||||
path: props.instance.path,
|
||||
name: props.instance.name,
|
||||
gameVersion: props.instance.game_version,
|
||||
loader: props.instance.loader,
|
||||
linkedData: props.instance.linked_data,
|
||||
},
|
||||
modalStateBeforeFetch: {
|
||||
updatingModpack: updatingModpack.value,
|
||||
updatingProjectId: updatingProject.value?.id,
|
||||
updatingProjectVersions: updatingProjectVersions.value.map((version) => ({
|
||||
id: version.id,
|
||||
versionNumber: version.version_number,
|
||||
gameVersions: version.game_versions,
|
||||
loaders: version.loaders,
|
||||
datePublished: version.date_published,
|
||||
})),
|
||||
},
|
||||
})
|
||||
contentUpdaterModal.value?.show(initialVersionId)
|
||||
|
||||
const versions = (await get_project_versions(props.instance.linked_data.project_id).catch(
|
||||
handleError,
|
||||
)) as Labrinth.Versions.v2.Version[] | null
|
||||
|
||||
if (!isActiveUpdateRequest(requestId) || !updatingModpack.value) return
|
||||
|
||||
loadingVersions.value = false
|
||||
|
||||
if (!versions) return
|
||||
@@ -494,6 +718,25 @@ async function handleModpackUpdate() {
|
||||
versions.sort(
|
||||
(a, b) => new Date(b.date_published).getTime() - new Date(a.date_published).getTime(),
|
||||
)
|
||||
const preselectedVersion =
|
||||
versions.find((version) => version.id === initialVersionId) ?? versions[0] ?? null
|
||||
debug('handleModpackUpdate: resolved modpack updater preselection', {
|
||||
type: 'modpack',
|
||||
initialVersionId,
|
||||
foundInitialVersion: versions.some((version) => version.id === initialVersionId),
|
||||
preselectedVersion: preselectedVersion
|
||||
? {
|
||||
id: preselectedVersion.id,
|
||||
versionNumber: preselectedVersion.version_number,
|
||||
gameVersions: preselectedVersion.game_versions,
|
||||
loaders: preselectedVersion.loaders,
|
||||
datePublished: preselectedVersion.date_published,
|
||||
}
|
||||
: null,
|
||||
versionCount: versions.length,
|
||||
linkedModpackUpdateVersionId: linkedModpackUpdateVersionId.value,
|
||||
currentLinkedVersionId: props.instance.linked_data.version_id,
|
||||
})
|
||||
|
||||
updatingProjectVersions.value = versions
|
||||
}
|
||||
@@ -502,10 +745,12 @@ async function fetchAndSpliceVersion(
|
||||
versionId: string,
|
||||
cacheBehaviour?: Parameters<typeof get_version>[1],
|
||||
onError?: (err: unknown) => void,
|
||||
requestId = activeUpdateRequestId.value,
|
||||
) {
|
||||
const fullVersion = (await get_version(versionId, cacheBehaviour).catch(
|
||||
onError ?? (() => null),
|
||||
)) as Labrinth.Versions.v2.Version | null
|
||||
if (!isActiveUpdateRequest(requestId)) return
|
||||
if (!fullVersion) return
|
||||
const index = updatingProjectVersions.value.findIndex((v) => v.id === versionId)
|
||||
if (index !== -1) {
|
||||
@@ -517,17 +762,26 @@ async function fetchAndSpliceVersion(
|
||||
|
||||
async function handleVersionSelect(version: Labrinth.Versions.v2.Version) {
|
||||
if (version.changelog != null) return
|
||||
const requestId = activeUpdateRequestId.value
|
||||
loadingChangelog.value = true
|
||||
await fetchAndSpliceVersion(version.id, 'must_revalidate', handleError as (err: unknown) => void)
|
||||
loadingChangelog.value = false
|
||||
await fetchAndSpliceVersion(
|
||||
version.id,
|
||||
'must_revalidate',
|
||||
handleError as (err: unknown) => void,
|
||||
requestId,
|
||||
)
|
||||
if (isActiveUpdateRequest(requestId)) {
|
||||
loadingChangelog.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleVersionHover(version: Labrinth.Versions.v2.Version) {
|
||||
if (version.changelog != null) return
|
||||
await fetchAndSpliceVersion(version.id)
|
||||
await fetchAndSpliceVersion(version.id, undefined, undefined, activeUpdateRequestId.value)
|
||||
}
|
||||
|
||||
function resetUpdateState() {
|
||||
activeUpdateRequestId.value = 0
|
||||
updatingModpack.value = false
|
||||
updatingProject.value = null
|
||||
updatingProjectVersions.value = []
|
||||
@@ -535,13 +789,23 @@ function resetUpdateState() {
|
||||
loadingChangelog.value = false
|
||||
}
|
||||
|
||||
function handleModpackUpdateRequest(selectedVersion: Labrinth.Versions.v2.Version) {
|
||||
async function handleModpackUpdateRequest(selectedVersion: Labrinth.Versions.v2.Version) {
|
||||
pendingModpackUpdateVersion.value = selectedVersion
|
||||
|
||||
const currentVersionId = props.instance?.linked_data?.version_id
|
||||
const currentVersion = updatingProjectVersions.value.find((v) => v.id === currentVersionId)
|
||||
isModpackUpdateDowngrade.value = currentVersion
|
||||
? new Date(selectedVersion.date_published) < new Date(currentVersion.date_published)
|
||||
: false
|
||||
const shouldShowWarning =
|
||||
isModpackUpdateDowngrade.value ||
|
||||
versionChangesGameVersion(selectedVersion, props.instance.game_version)
|
||||
|
||||
if (!shouldShowWarning) {
|
||||
await handleModpackUpdateConfirm()
|
||||
return
|
||||
}
|
||||
|
||||
modpackUpdateConfirmModal.value?.show()
|
||||
}
|
||||
|
||||
@@ -551,6 +815,7 @@ async function handleModpackUpdateConfirm() {
|
||||
const version = pendingModpackUpdateVersion.value
|
||||
pendingModpackUpdateVersion.value = null
|
||||
|
||||
contentUpdaterModal.value?.hide()
|
||||
isModpackUpdating.value = true
|
||||
try {
|
||||
await update_managed_modrinth_version(props.instance.path, version.id)
|
||||
@@ -574,7 +839,7 @@ async function handleModalUpdate(
|
||||
pendingModpackUpdateVersion.value = selectedVersion
|
||||
await handleModpackUpdateConfirm()
|
||||
} else {
|
||||
handleModpackUpdateRequest(selectedVersion)
|
||||
await handleModpackUpdateRequest(selectedVersion)
|
||||
}
|
||||
} else if (updatingProject.value) {
|
||||
const mod = updatingProject.value
|
||||
@@ -662,51 +927,31 @@ function getOverflowOptions(item: ContentItem): OverflowMenuOption[] {
|
||||
async function initProjects(cacheBehaviour?: CacheBehaviour) {
|
||||
if (!props.instance) return
|
||||
|
||||
const [contentItems, modpackInfo, allCategories] = await Promise.all([
|
||||
get_content_items(props.instance.path, cacheBehaviour).catch(handleError),
|
||||
get_linked_modpack_info(props.instance.path, cacheBehaviour).catch(handleError),
|
||||
get_categories().catch(handleError),
|
||||
])
|
||||
const contentData = await loadInstanceContentData(
|
||||
props.instance.path,
|
||||
cacheBehaviour,
|
||||
handleError,
|
||||
)
|
||||
applyContentData(contentData)
|
||||
}
|
||||
|
||||
if (!contentItems) {
|
||||
function applyContentData(contentData: InstanceContentData) {
|
||||
if (contentData.path !== props.instance.path) return false
|
||||
|
||||
if (!contentData.contentItems) {
|
||||
loading.value = false
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
projects.value = contentItems
|
||||
projects.value = contentData.contentItems
|
||||
|
||||
if (modpackInfo) {
|
||||
linkedModpackProject.value = {
|
||||
...modpackInfo.project,
|
||||
slug: modpackInfo.project.slug ?? modpackInfo.project.id,
|
||||
icon_url: modpackInfo.project.icon_url ?? undefined,
|
||||
}
|
||||
linkedModpackVersion.value = {
|
||||
...modpackInfo.version,
|
||||
date_published: modpackInfo.version.date_published.toString(),
|
||||
}
|
||||
linkedModpackOwner.value = modpackInfo.owner
|
||||
? {
|
||||
...modpackInfo.owner,
|
||||
avatar_url: modpackInfo.owner.avatar_url ?? undefined,
|
||||
}
|
||||
: null
|
||||
|
||||
linkedModpackHasUpdate.value = modpackInfo.has_update
|
||||
linkedModpackUpdateVersionId.value = modpackInfo.update_version_id
|
||||
|
||||
if (allCategories && modpackInfo.project.categories) {
|
||||
const seen = new Set<string>()
|
||||
linkedModpackCategories.value = allCategories.filter((cat: { name: string }) => {
|
||||
if (modpackInfo.project.categories.includes(cat.name) && !seen.has(cat.name)) {
|
||||
seen.add(cat.name)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
} else {
|
||||
linkedModpackCategories.value = []
|
||||
}
|
||||
if (contentData.modpack) {
|
||||
linkedModpackProject.value = contentData.modpack.project
|
||||
linkedModpackVersion.value = contentData.modpack.version
|
||||
linkedModpackOwner.value = contentData.modpack.owner
|
||||
linkedModpackCategories.value = contentData.modpack.categories
|
||||
linkedModpackHasUpdate.value = contentData.modpack.hasUpdate
|
||||
linkedModpackUpdateVersionId.value = contentData.modpack.updateVersionId
|
||||
} else {
|
||||
linkedModpackProject.value = null
|
||||
linkedModpackVersion.value = null
|
||||
@@ -717,6 +962,7 @@ async function initProjects(cacheBehaviour?: CacheBehaviour) {
|
||||
}
|
||||
|
||||
loading.value = false
|
||||
return true
|
||||
}
|
||||
|
||||
provideAppBackup({
|
||||
@@ -844,10 +1090,22 @@ provideContentManager({
|
||||
filterPersistKey: props.instance.path,
|
||||
})
|
||||
|
||||
await initProjects()
|
||||
type UnlistenFn = () => void
|
||||
|
||||
const initialContentReady = loadInitialContent()
|
||||
void initialContentReady.then(restoreModpackContentModalState).catch(handleError)
|
||||
|
||||
function loadInitialContent() {
|
||||
if (props.preloadedContent && applyContentData(props.preloadedContent)) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return initProjects()
|
||||
}
|
||||
|
||||
async function restoreModpackContentModalState() {
|
||||
if (!savedModalState) return
|
||||
|
||||
// Restore modpack content modal state if returning via back navigation
|
||||
if (savedModalState) {
|
||||
const stateToRestore = savedModalState
|
||||
savedModalState = null
|
||||
await nextTick()
|
||||
@@ -860,18 +1118,32 @@ const removeBeforeEach = router.beforeEach(() => {
|
||||
savedModalState = state ?? null
|
||||
})
|
||||
|
||||
const unlisten = await getCurrentWebview().onDragDropEvent(async (event) => {
|
||||
if (event.payload.type !== 'drop' || !props.instance) return
|
||||
let isUnmounted = false
|
||||
let unlistenDragDrop: UnlistenFn | null = null
|
||||
let unlistenProfiles: UnlistenFn | null = null
|
||||
|
||||
for (const file of event.payload.paths) {
|
||||
if (file.endsWith('.mrpack')) continue
|
||||
await add_project_from_path(props.instance.path, file).catch(handleError)
|
||||
}
|
||||
await initProjects()
|
||||
})
|
||||
onMounted(() => {
|
||||
void getCurrentWebview()
|
||||
.onDragDropEvent(async (event) => {
|
||||
if (event.payload.type !== 'drop' || !props.instance) return
|
||||
|
||||
const unlistenProfiles = await profile_listener(
|
||||
async (event: { event: string; profile_path_id: string }) => {
|
||||
for (const file of event.payload.paths) {
|
||||
if (file.endsWith('.mrpack')) continue
|
||||
await add_project_from_path(props.instance.path, file).catch(handleError)
|
||||
}
|
||||
await initProjects()
|
||||
})
|
||||
.then((unlisten) => {
|
||||
if (isUnmounted) {
|
||||
unlisten()
|
||||
return
|
||||
}
|
||||
|
||||
unlistenDragDrop = unlisten
|
||||
})
|
||||
.catch(handleError)
|
||||
|
||||
void profile_listener(async (event: { event: string; profile_path_id: string }) => {
|
||||
if (
|
||||
props.instance &&
|
||||
event.profile_path_id === props.instance.path &&
|
||||
@@ -881,8 +1153,17 @@ const unlistenProfiles = await profile_listener(
|
||||
) {
|
||||
await initProjects()
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
.then((unlisten) => {
|
||||
if (isUnmounted) {
|
||||
unlisten()
|
||||
return
|
||||
}
|
||||
|
||||
unlistenProfiles = unlisten
|
||||
})
|
||||
.catch(handleError)
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.instance?.install_stage,
|
||||
@@ -905,8 +1186,9 @@ watch(
|
||||
)
|
||||
|
||||
onUnmounted(() => {
|
||||
isUnmounted = true
|
||||
removeBeforeEach()
|
||||
unlisten()
|
||||
unlistenProfiles()
|
||||
unlistenDragDrop?.()
|
||||
unlistenProfiles?.()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -334,7 +334,7 @@ const { formatMessage } = useVIntl()
|
||||
const messages = defineMessages({
|
||||
backToBrowse: {
|
||||
id: 'app.project.install-context.back-to-browse',
|
||||
defaultMessage: 'Back to browse',
|
||||
defaultMessage: 'Back to discover',
|
||||
},
|
||||
installContentToInstance: {
|
||||
id: 'app.project.install-context.install-content-to-instance',
|
||||
@@ -415,7 +415,7 @@ const projectGalleryHref = computed(() => buildProjectHref(`/project/${route.par
|
||||
const projectBrowseBackUrl = computed(() => {
|
||||
const browsePath = route.query.b
|
||||
if (typeof browsePath === 'string' && browsePath.startsWith('/browse/')) return browsePath
|
||||
const type = data.value?.project_type ? `${data.value.project_type}s` : 'mods'
|
||||
const type = data.value?.project_type ? `${data.value.project_type}` : 'mod'
|
||||
return `/browse/${type}`
|
||||
})
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface InstanceSettingsContext {
|
||||
offline?: boolean
|
||||
isMinecraftServer: Ref<boolean>
|
||||
onUnlinked: () => void
|
||||
closeModal?: () => void
|
||||
}
|
||||
|
||||
export const [injectInstanceSettings, provideInstanceSettings] =
|
||||
|
||||
@@ -5,15 +5,19 @@ import {
|
||||
type BrowseSelectedProject,
|
||||
createContext,
|
||||
type CreationFlowContextValue,
|
||||
flushInstallQueue,
|
||||
flushStoredServerAddonInstallQueue,
|
||||
getStoredServerAddonInstallQueue,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
type PendingServerContentInstall,
|
||||
type PendingServerContentInstallType,
|
||||
readPendingServerContentInstalls,
|
||||
readStoredServerInstallQueue,
|
||||
removePendingServerContentInstall,
|
||||
writePendingServerContentInstallBaseline,
|
||||
writeStoredServerInstallQueue,
|
||||
} from '@modrinth/ui'
|
||||
import { useQueryClient } from '@tanstack/vue-query'
|
||||
import { computed, type ComputedRef, nextTick, type Ref, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
@@ -89,37 +93,6 @@ function readQueryString(value: unknown): string | null {
|
||||
return typeof value === 'string' && value.length > 0 ? value : null
|
||||
}
|
||||
|
||||
function getQueueStorageKey(serverId: string | null, worldId: string | null) {
|
||||
if (!serverId || !worldId) return null
|
||||
return `server-install-queue:${serverId}:${worldId}`
|
||||
}
|
||||
|
||||
function readStoredQueue(serverId: string | null, worldId: string | null) {
|
||||
const key = getQueueStorageKey(serverId, worldId)
|
||||
if (!key) return new Map<string, BrowseInstallPlan<InstallableSearchResult>>()
|
||||
try {
|
||||
const raw = localStorage.getItem(key)
|
||||
if (!raw) return new Map<string, BrowseInstallPlan<InstallableSearchResult>>()
|
||||
return new Map<string, BrowseInstallPlan<InstallableSearchResult>>(JSON.parse(raw))
|
||||
} catch {
|
||||
return new Map<string, BrowseInstallPlan<InstallableSearchResult>>()
|
||||
}
|
||||
}
|
||||
|
||||
function writeStoredQueue(
|
||||
serverId: string | null,
|
||||
worldId: string | null,
|
||||
plans: Map<string, BrowseInstallPlan<InstallableSearchResult>>,
|
||||
) {
|
||||
const key = getQueueStorageKey(serverId, worldId)
|
||||
if (!key) return
|
||||
if (plans.size === 0) {
|
||||
localStorage.removeItem(key)
|
||||
return
|
||||
}
|
||||
localStorage.setItem(key, JSON.stringify(Array.from(plans.entries())))
|
||||
}
|
||||
|
||||
function getQueuedInstallOwnerFallback(project: InstallableSearchResult) {
|
||||
if (project.organization) {
|
||||
const ownerId = project.organization_id ?? project.organization
|
||||
@@ -235,7 +208,8 @@ export function createServerInstallContent(opts: {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const client = injectModrinthClient()
|
||||
const { addNotification, handleError } = injectNotificationManager()
|
||||
const { handleError } = injectNotificationManager()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const serverIdQuery = computed(() => readQueryString(route.query.sid))
|
||||
const worldIdQuery = computed(() => readQueryString(route.query.wid))
|
||||
@@ -340,7 +314,7 @@ export function createServerInstallContent(opts: {
|
||||
}
|
||||
|
||||
if (resolvedWorldId) {
|
||||
queuedServerInstalls.value = readStoredQueue(sid, resolvedWorldId)
|
||||
queuedServerInstalls.value = readStoredServerInstallQueue(sid, resolvedWorldId)
|
||||
await refreshServerInstalledContent(sid, resolvedWorldId)
|
||||
}
|
||||
}
|
||||
@@ -358,7 +332,7 @@ export function createServerInstallContent(opts: {
|
||||
if (sid !== prevSid) {
|
||||
serverContentProjectIds.value = new Set()
|
||||
serverContentInstallKeys.value = new Set()
|
||||
queuedServerInstalls.value = readStoredQueue(sid, wid)
|
||||
queuedServerInstalls.value = readStoredServerInstallQueue(sid, wid)
|
||||
try {
|
||||
serverContextServerData.value = await client.archon.servers_v0.get(sid)
|
||||
} catch (err) {
|
||||
@@ -367,7 +341,7 @@ export function createServerInstallContent(opts: {
|
||||
}
|
||||
|
||||
if (wid !== prevWid) {
|
||||
queuedServerInstalls.value = readStoredQueue(sid, wid)
|
||||
queuedServerInstalls.value = readStoredServerInstallQueue(sid, wid)
|
||||
}
|
||||
|
||||
if (wid && (sid !== prevSid || wid !== prevWid)) {
|
||||
@@ -432,11 +406,21 @@ export function createServerInstallContent(opts: {
|
||||
setQueuedServerInstallPlans(nextPlans)
|
||||
}
|
||||
|
||||
function setStoredServerInstallPlans(
|
||||
serverId: string,
|
||||
worldId: string,
|
||||
plans: Map<string, BrowseInstallPlan<InstallableSearchResult>>,
|
||||
) {
|
||||
if (serverId === serverIdQuery.value && worldId === effectiveServerWorldId.value) {
|
||||
queuedServerInstalls.value = plans
|
||||
}
|
||||
writeStoredServerInstallQueue(serverId, worldId, plans)
|
||||
}
|
||||
|
||||
async function flushQueuedServerInstalls(
|
||||
serverId: string | null = serverIdQuery.value,
|
||||
worldId: string | null = effectiveServerWorldId.value,
|
||||
) {
|
||||
if (queuedServerInstalls.value.size === 0) return true
|
||||
if (isInstallingQueuedServerInstalls.value) return false
|
||||
|
||||
if (!serverId || !worldId) {
|
||||
@@ -444,50 +428,56 @@ export function createServerInstallContent(opts: {
|
||||
return false
|
||||
}
|
||||
|
||||
const installedProjectIds = new Set<string>()
|
||||
const queuedPlans = getStoredServerAddonInstallQueue<InstallableSearchResult>(serverId, worldId)
|
||||
if (queuedPlans.size === 0) return true
|
||||
|
||||
isInstallingQueuedServerInstalls.value = true
|
||||
queuedInstallProgress.value = {
|
||||
completed: 0,
|
||||
total: queuedServerInstalls.value.size,
|
||||
total: queuedPlans.size,
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await flushInstallQueue({
|
||||
queue: {
|
||||
get: () => queuedServerInstalls.value,
|
||||
set: (plans: Map<string, BrowseInstallPlan<InstallableSearchResult>>) => {
|
||||
queuedServerInstalls.value = plans
|
||||
writeStoredQueue(serverId, worldId, plans)
|
||||
},
|
||||
},
|
||||
install: async (plan) => {
|
||||
await client.archon.content_v1.addAddon(serverId, worldId, {
|
||||
project_id: plan.projectId,
|
||||
version_id: plan.versionId,
|
||||
})
|
||||
installedProjectIds.add(plan.projectId)
|
||||
},
|
||||
onError: (error, plan) => {
|
||||
removePendingServerContentInstall(serverId, worldId, plan.projectId)
|
||||
handleError(error as Error)
|
||||
},
|
||||
onProgress: (completed, total) => {
|
||||
queuedInstallProgress.value = { completed, total }
|
||||
},
|
||||
const result = await flushStoredServerAddonInstallQueue({
|
||||
serverId,
|
||||
worldId,
|
||||
install: (plans) =>
|
||||
client.archon.content_v1.addAddons(
|
||||
serverId,
|
||||
worldId,
|
||||
plans.map((plan) => ({
|
||||
project_id: plan.projectId,
|
||||
version_id: plan.versionId,
|
||||
})),
|
||||
),
|
||||
onQueueChange: (plans) => setStoredServerInstallPlans(serverId, worldId, plans),
|
||||
})
|
||||
|
||||
if (installedProjectIds.size > 0) {
|
||||
serverContentProjectIds.value = new Set([
|
||||
...serverContentProjectIds.value,
|
||||
...installedProjectIds,
|
||||
])
|
||||
serverContentInstallKeys.value = new Set([
|
||||
...serverContentInstallKeys.value,
|
||||
...installedProjectIds,
|
||||
])
|
||||
if (!result.ok) {
|
||||
for (const plan of result.attemptedPlans) {
|
||||
removePendingServerContentInstall(serverId, worldId, plan.projectId)
|
||||
}
|
||||
handleError(result.error as Error)
|
||||
return false
|
||||
}
|
||||
|
||||
return result.ok
|
||||
queuedInstallProgress.value = {
|
||||
completed: result.flushedPlans.length,
|
||||
total: result.flushedPlans.length,
|
||||
}
|
||||
serverContentProjectIds.value = new Set([
|
||||
...serverContentProjectIds.value,
|
||||
...result.flushedPlans.map((plan) => plan.projectId),
|
||||
])
|
||||
serverContentInstallKeys.value = new Set([
|
||||
...serverContentInstallKeys.value,
|
||||
...result.flushedPlans.map((plan) => plan.projectId),
|
||||
])
|
||||
if (result.flushedPlans.length > 0) {
|
||||
await queryClient.invalidateQueries({ queryKey: ['content', 'list', 'v1', serverId] })
|
||||
}
|
||||
|
||||
return true
|
||||
} finally {
|
||||
isInstallingQueuedServerInstalls.value = false
|
||||
queuedInstallProgress.value = { completed: 0, total: 0 }
|
||||
@@ -506,6 +496,7 @@ export function createServerInstallContent(opts: {
|
||||
const plans = new Map(queuedServerInstalls.value)
|
||||
|
||||
if (sid && wid) {
|
||||
writeStoredServerInstallQueue(sid, wid, plans)
|
||||
writePendingServerContentInstallBaseline(sid, wid, serverContentInstallKeys.value)
|
||||
addPendingServerContentInstalls(sid, wid, getQueuedInstallPlaceholderFallbacks(plans))
|
||||
void getQueuedInstallPlaceholders(client, plans)
|
||||
@@ -522,17 +513,7 @@ export function createServerInstallContent(opts: {
|
||||
.catch((err) => handleError(err as Error))
|
||||
}
|
||||
await router.push(backUrl)
|
||||
|
||||
const ok = await flushQueuedServerInstalls(sid, wid)
|
||||
if (!ok) {
|
||||
queuedServerInstalls.value = new Map()
|
||||
writeStoredQueue(sid, wid, new Map())
|
||||
addNotification({
|
||||
type: 'error',
|
||||
title: 'Some projects failed to install',
|
||||
text: 'Failed projects were not added. You can try installing them again.',
|
||||
})
|
||||
}
|
||||
void flushQueuedServerInstalls(sid, wid)
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -545,7 +526,7 @@ export function createServerInstallContent(opts: {
|
||||
plans: Map<string, BrowseInstallPlan<InstallableSearchResult>>,
|
||||
) {
|
||||
queuedServerInstalls.value = plans
|
||||
writeStoredQueue(serverIdQuery.value, effectiveServerWorldId.value, plans)
|
||||
writeStoredServerInstallQueue(serverIdQuery.value, effectiveServerWorldId.value, plans)
|
||||
}
|
||||
|
||||
function onServerFlowBack() {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"types": ["vite/client"],
|
||||
|
||||
"paths": {
|
||||
"@modrinth/api-client": ["../../packages/api-client/src/index.ts"],
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@ import tauriConf from '../app/tauri.conf.json'
|
||||
|
||||
const projectRootDir = resolve(__dirname)
|
||||
const appLibEnvDir = resolve(projectRootDir, '../../packages/app-lib')
|
||||
const apiClientSource = resolve(projectRootDir, '../../packages/api-client/src/index.ts')
|
||||
|
||||
// Load .env from app-lib manually instead of using Vite's envDir, which would auto-load .env.local and override values
|
||||
const envFilePath = resolve(appLibEnvDir, '.env')
|
||||
@@ -37,6 +38,10 @@ export default defineConfig({
|
||||
},
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: '@modrinth/api-client',
|
||||
replacement: apiClientSource,
|
||||
},
|
||||
{
|
||||
find: '@',
|
||||
replacement: resolve(projectRootDir, 'src'),
|
||||
|
||||
@@ -51,8 +51,21 @@ tauri-build = { workspace = true, features = ["codegen"] }
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
tauri-plugin-updater = { workspace = true, optional = true }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-foundation.workspace = true
|
||||
core-graphics.workspace = true
|
||||
objc2-app-kit = { workspace = true, features = [
|
||||
"NSWindow",
|
||||
] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
webview2-com.workspace = true
|
||||
windows = { workspace = true, features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Dwm",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
] }
|
||||
windows-core.workspace = true
|
||||
|
||||
[features]
|
||||
|
||||
+127
-17
@@ -11,13 +11,22 @@ use tokio::sync::RwLock;
|
||||
pub struct AdsState {
|
||||
pub shown: bool,
|
||||
pub modal_shown: bool,
|
||||
pub occluded: bool,
|
||||
pub last_click: Option<Instant>,
|
||||
pub malicious_origins: HashSet<String>,
|
||||
}
|
||||
|
||||
const AD_LINK: &str = "https://modrinth.com/wrapper/app-ads-cookie";
|
||||
#[cfg(any(windows, target_os = "macos"))]
|
||||
pub(super) const OCCLUDED_AREA_THRESHOLD: f64 = 0.5;
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
const ADS_USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36";
|
||||
const ADS_USER_AGENT: &str = concat!(
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ",
|
||||
"(KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ",
|
||||
"ModrinthApp/",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
" (Modrinth App)",
|
||||
);
|
||||
|
||||
#[cfg(windows)]
|
||||
fn ads_user_agent_override_params() -> String {
|
||||
@@ -28,12 +37,14 @@ fn ads_user_agent_override_params() -> String {
|
||||
"brands": [
|
||||
{ "brand": "Chromium", "version": "128" },
|
||||
{ "brand": "Google Chrome", "version": "128" },
|
||||
{ "brand": "Modrinth App", "version": env!("CARGO_PKG_VERSION") },
|
||||
{ "brand": "Not=A?Brand", "version": "99" },
|
||||
],
|
||||
"fullVersion": "128.0.0.0",
|
||||
"fullVersionList": [
|
||||
{ "brand": "Chromium", "version": "128.0.0.0" },
|
||||
{ "brand": "Google Chrome", "version": "128.0.0.0" },
|
||||
{ "brand": "Modrinth App", "version": env!("CARGO_PKG_VERSION") },
|
||||
{ "brand": "Not=A?Brand", "version": "99.0.0.0" },
|
||||
],
|
||||
"platform": "Windows",
|
||||
@@ -52,8 +63,8 @@ fn configure_ads_cookie_settings(
|
||||
core_webview2: &webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2,
|
||||
) {
|
||||
use webview2_com::Microsoft::Web::WebView2::Win32::{
|
||||
COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE, ICoreWebView2,
|
||||
ICoreWebView2_13, ICoreWebView2Profile3,
|
||||
COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE, ICoreWebView2_13,
|
||||
ICoreWebView2Profile3,
|
||||
};
|
||||
use windows_core::Interface;
|
||||
|
||||
@@ -83,10 +94,12 @@ fn configure_ads_cookie_settings(
|
||||
}
|
||||
}
|
||||
|
||||
fn set_webview_visible<R: Runtime>(
|
||||
webview: &tauri::Webview<R>,
|
||||
_visible: bool,
|
||||
) {
|
||||
fn set_webview_visible<R: Runtime>(webview: &tauri::Webview<R>, visible: bool) {
|
||||
#[cfg(not(any(windows, target_os = "macos")))]
|
||||
{
|
||||
_ = visible;
|
||||
}
|
||||
|
||||
webview
|
||||
.with_webview(
|
||||
#[allow(unused_variables)]
|
||||
@@ -94,11 +107,18 @@ fn set_webview_visible<R: Runtime>(
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let controller = wv.controller();
|
||||
unsafe { controller.SetIsVisible(_visible) }.ok();
|
||||
unsafe { controller.SetIsVisible(visible) }.ok();
|
||||
}
|
||||
},
|
||||
)
|
||||
.ok();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
if visible {
|
||||
webview.show().ok();
|
||||
} else {
|
||||
webview.hide().ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn set_webview_visible_for_window<R: Runtime>(
|
||||
@@ -111,7 +131,76 @@ fn set_webview_visible_for_window<R: Runtime>(
|
||||
.and_then(|window| window.is_minimized().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
set_webview_visible(webview, visible && !is_minimized);
|
||||
let is_occluded = app
|
||||
.state::<RwLock<AdsState>>()
|
||||
.try_read()
|
||||
.map(|state| state.occluded)
|
||||
.unwrap_or(false);
|
||||
|
||||
set_webview_visible(webview, visible && !is_minimized && !is_occluded);
|
||||
}
|
||||
|
||||
#[cfg(any(windows, target_os = "macos"))]
|
||||
fn compute_ads_webview_occlusion<R: Runtime>(
|
||||
app: &tauri::AppHandle<R>,
|
||||
) -> Option<bool> {
|
||||
let main_window = app.get_window("main")?;
|
||||
let webviews = app.webviews();
|
||||
let webview = webviews.get("ads-window")?;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let position = webview.position().ok()?;
|
||||
let size = webview.size().ok()?;
|
||||
|
||||
Some(
|
||||
crate::api::ads_occlusion_macos::is_ads_webview_occluded(
|
||||
&main_window,
|
||||
position.x,
|
||||
position.y,
|
||||
size.width,
|
||||
size.height,
|
||||
)
|
||||
.unwrap_or(false),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let position = webview.position().ok()?;
|
||||
let size = webview.size().ok()?;
|
||||
let hwnd = main_window.hwnd().ok()?;
|
||||
|
||||
Some(crate::api::ads_occlusion_windows::is_ads_webview_occluded(
|
||||
hwnd,
|
||||
position.x,
|
||||
position.y,
|
||||
size.width,
|
||||
size.height,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(windows, target_os = "macos"))]
|
||||
async fn sync_ads_occlusion<R: Runtime>(app: &tauri::AppHandle<R>) {
|
||||
let Some(occluded) = compute_ads_webview_occlusion(app) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let state = app.state::<RwLock<AdsState>>();
|
||||
let mut state = state.write().await;
|
||||
|
||||
if state.occluded == occluded {
|
||||
return;
|
||||
}
|
||||
|
||||
state.occluded = occluded;
|
||||
let visible = state.shown && !state.modal_shown;
|
||||
drop(state);
|
||||
|
||||
if let Some(webview) = app.webviews().get("ads-window") {
|
||||
set_webview_visible_for_window(app, webview, visible);
|
||||
}
|
||||
}
|
||||
|
||||
fn sync_webview_visibility_for_main_window<R: Runtime>(
|
||||
@@ -132,7 +221,7 @@ fn sync_webview_visibility_for_main_window<R: Runtime>(
|
||||
false
|
||||
} else {
|
||||
match app.state::<RwLock<AdsState>>().try_read() {
|
||||
Ok(state) => state.shown && !state.modal_shown,
|
||||
Ok(state) => state.shown && !state.modal_shown && !state.occluded,
|
||||
Err(_) => false,
|
||||
}
|
||||
};
|
||||
@@ -165,20 +254,28 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
app.manage(RwLock::new(AdsState {
|
||||
shown: true,
|
||||
modal_shown: false,
|
||||
occluded: false,
|
||||
last_click: None,
|
||||
malicious_origins: HashSet::new(),
|
||||
}));
|
||||
|
||||
// We refresh the ads window every 5 minutes to mitigate memory leak issues.
|
||||
// While this loop doesn't include explicit checks to see if the window is still
|
||||
// visible when we refresh, the Aditude wrapper will not make any ad requests
|
||||
// unless Chromium reports the page as visible. The refresh does not reset the
|
||||
// visibility state.
|
||||
// We refresh the ads window periodically to mitigate memory leak issues.
|
||||
// Skip refreshes when app state has hidden the ads WebView. The refresh does
|
||||
// not reset the visibility state.
|
||||
let refresh_app = app.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
loop {
|
||||
if let Some(webview) =
|
||||
refresh_app.webviews().get_mut("ads-window")
|
||||
let should_refresh = refresh_app
|
||||
.state::<RwLock<AdsState>>()
|
||||
.try_read()
|
||||
.map(|state| {
|
||||
state.shown && !state.modal_shown && !state.occluded
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
if should_refresh
|
||||
&& let Some(webview) =
|
||||
refresh_app.webviews().get_mut("ads-window")
|
||||
{
|
||||
let _ = webview.navigate(AD_LINK.parse().unwrap());
|
||||
}
|
||||
@@ -216,6 +313,19 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(any(windows, target_os = "macos"))]
|
||||
{
|
||||
let app_handle = app.clone();
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
loop {
|
||||
sync_ads_occlusion(&app_handle).await;
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(200)).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use core_foundation::array::CFArray;
|
||||
use core_foundation::base::{CFType, TCFType};
|
||||
use core_foundation::dictionary::CFDictionary;
|
||||
use core_foundation::number::CFNumber;
|
||||
use core_foundation::string::CFString;
|
||||
use core_graphics::display::CGDisplay;
|
||||
use core_graphics::geometry::{CGPoint, CGRect, CGSize};
|
||||
use core_graphics::window::{
|
||||
CGWindowID, kCGWindowAlpha, kCGWindowBounds, kCGWindowLayer,
|
||||
kCGWindowListExcludeDesktopElements,
|
||||
kCGWindowListOptionOnScreenAboveWindow, kCGWindowListOptionOnScreenOnly,
|
||||
kCGWindowNumber, kCGWindowOwnerName, kCGWindowOwnerPID,
|
||||
};
|
||||
use objc2_app_kit::NSWindow;
|
||||
|
||||
pub fn is_ads_webview_occluded<R: tauri::Runtime>(
|
||||
main_window: &tauri::Window<R>,
|
||||
ad_x: i32,
|
||||
ad_y: i32,
|
||||
ad_width: u32,
|
||||
ad_height: u32,
|
||||
) -> Option<bool> {
|
||||
let scale_factor = main_window.scale_factor().ok()?;
|
||||
let maybe_ns_window = main_window.ns_window().ok()?;
|
||||
let ns_window = NonNull::new(maybe_ns_window)?.cast::<NSWindow>();
|
||||
let ns_window = unsafe { ns_window.as_ref() };
|
||||
let window_number = ns_window.windowNumber();
|
||||
let main_window_id = if window_number > 0 {
|
||||
window_number as CGWindowID
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let window_infos = CGDisplay::window_list_info(
|
||||
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
|
||||
None,
|
||||
)?;
|
||||
|
||||
let window_infos = unsafe {
|
||||
CFArray::<CFDictionary<CFString, CFType>>::wrap_under_get_rule(
|
||||
window_infos.as_concrete_TypeRef(),
|
||||
)
|
||||
};
|
||||
let main_window_rect = window_infos
|
||||
.iter()
|
||||
.find(|window_info| window_id(window_info) == Some(main_window_id))
|
||||
.and_then(|window_info| window_rect(&window_info))?;
|
||||
let ad_rect = ad_rect_from_main_window(
|
||||
&main_window_rect,
|
||||
ad_x,
|
||||
ad_y,
|
||||
ad_width,
|
||||
ad_height,
|
||||
scale_factor,
|
||||
);
|
||||
|
||||
if is_empty_rect(&ad_rect) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ad_area = rect_area(&ad_rect);
|
||||
|
||||
if ad_area == 0.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let app_process_id = std::process::id() as i32;
|
||||
let windows_above_main = CGDisplay::window_list_info(
|
||||
kCGWindowListOptionOnScreenAboveWindow
|
||||
| kCGWindowListExcludeDesktopElements,
|
||||
Some(main_window_id),
|
||||
)?;
|
||||
let windows_above_main = unsafe {
|
||||
CFArray::<CFDictionary<CFString, CFType>>::wrap_under_get_rule(
|
||||
windows_above_main.as_concrete_TypeRef(),
|
||||
)
|
||||
};
|
||||
let mut occluded_area = 0.0;
|
||||
|
||||
for window_info in windows_above_main.iter() {
|
||||
if window_id(&window_info) == Some(main_window_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if window_process_id(&window_info) == Some(app_process_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let owner_name = window_owner_name(&window_info);
|
||||
|
||||
if owner_name.as_deref().is_some_and(is_system_window_owner) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let layer = window_layer(&window_info);
|
||||
|
||||
if layer != Some(0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let alpha = window_alpha(&window_info);
|
||||
|
||||
if alpha.is_some_and(|alpha| alpha <= 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(rect) = window_rect(&window_info) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(intersection) = intersect_rects(&ad_rect, &rect) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
occluded_area += rect_area(&intersection);
|
||||
let occluded_ratio = occluded_area / ad_area;
|
||||
|
||||
if occluded_ratio >= super::ads::OCCLUDED_AREA_THRESHOLD {
|
||||
return Some(true);
|
||||
}
|
||||
}
|
||||
|
||||
Some(false)
|
||||
}
|
||||
|
||||
fn ad_rect_from_main_window(
|
||||
main_window_rect: &CGRect,
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
scale_factor: f64,
|
||||
) -> CGRect {
|
||||
let scale_factor = if scale_factor > 0.0 {
|
||||
scale_factor
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
||||
CGRect::new(
|
||||
&CGPoint::new(
|
||||
main_window_rect.origin.x + x as f64 / scale_factor,
|
||||
main_window_rect.origin.y + y as f64 / scale_factor,
|
||||
),
|
||||
&CGSize::new(width as f64 / scale_factor, height as f64 / scale_factor),
|
||||
)
|
||||
}
|
||||
|
||||
fn window_id(
|
||||
window_info: &CFDictionary<CFString, CFType>,
|
||||
) -> Option<CGWindowID> {
|
||||
number_value(window_info, unsafe { kCGWindowNumber })
|
||||
.and_then(|value| u32::try_from(value).ok())
|
||||
}
|
||||
|
||||
fn window_process_id(
|
||||
window_info: &CFDictionary<CFString, CFType>,
|
||||
) -> Option<i32> {
|
||||
number_value(window_info, unsafe { kCGWindowOwnerPID })
|
||||
.and_then(|value| i32::try_from(value).ok())
|
||||
}
|
||||
|
||||
fn window_layer(window_info: &CFDictionary<CFString, CFType>) -> Option<i32> {
|
||||
number_value(window_info, unsafe { kCGWindowLayer })
|
||||
.and_then(|value| i32::try_from(value).ok())
|
||||
}
|
||||
|
||||
fn window_alpha(window_info: &CFDictionary<CFString, CFType>) -> Option<f64> {
|
||||
let key = unsafe { CFString::wrap_under_get_rule(kCGWindowAlpha) };
|
||||
|
||||
window_info.find(&key)?.downcast::<CFNumber>()?.to_f64()
|
||||
}
|
||||
|
||||
fn window_owner_name(
|
||||
window_info: &CFDictionary<CFString, CFType>,
|
||||
) -> Option<String> {
|
||||
let key = unsafe { CFString::wrap_under_get_rule(kCGWindowOwnerName) };
|
||||
|
||||
Some(window_info.find(&key)?.downcast::<CFString>()?.to_string())
|
||||
}
|
||||
|
||||
fn is_system_window_owner(owner_name: &str) -> bool {
|
||||
matches!(owner_name, "WindowManager")
|
||||
}
|
||||
|
||||
fn number_value(
|
||||
window_info: &CFDictionary<CFString, CFType>,
|
||||
key: core_foundation::string::CFStringRef,
|
||||
) -> Option<i64> {
|
||||
let key = unsafe { CFString::wrap_under_get_rule(key) };
|
||||
|
||||
window_info.find(&key)?.downcast::<CFNumber>()?.to_i64()
|
||||
}
|
||||
|
||||
fn window_rect(window_info: &CFDictionary<CFString, CFType>) -> Option<CGRect> {
|
||||
let key = unsafe { CFString::wrap_under_get_rule(kCGWindowBounds) };
|
||||
let bounds = window_info.find(&key)?.downcast::<CFDictionary>()?;
|
||||
let rect = CGRect::from_dict_representation(&bounds)?;
|
||||
|
||||
if is_empty_rect(&rect) {
|
||||
None
|
||||
} else {
|
||||
Some(rect)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty_rect(rect: &CGRect) -> bool {
|
||||
rect.size.width <= 0.0 || rect.size.height <= 0.0
|
||||
}
|
||||
|
||||
fn rect_area(rect: &CGRect) -> f64 {
|
||||
if is_empty_rect(rect) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
rect.size.width * rect.size.height
|
||||
}
|
||||
|
||||
fn intersect_rects(a: &CGRect, b: &CGRect) -> Option<CGRect> {
|
||||
let left = a.origin.x.max(b.origin.x);
|
||||
let top = a.origin.y.max(b.origin.y);
|
||||
let right = (a.origin.x + a.size.width).min(b.origin.x + b.size.width);
|
||||
let bottom = (a.origin.y + a.size.height).min(b.origin.y + b.size.height);
|
||||
let rect = CGRect::new(
|
||||
&CGPoint::new(left, top),
|
||||
&CGSize::new(right - left, bottom - top),
|
||||
);
|
||||
|
||||
if is_empty_rect(&rect) {
|
||||
None
|
||||
} else {
|
||||
Some(rect)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
use windows::Win32::Foundation::{HWND, POINT, RECT};
|
||||
use windows::Win32::Graphics::Dwm::{
|
||||
DWMWA_CLOAKED, DWMWA_EXTENDED_FRAME_BOUNDS, DwmGetWindowAttribute,
|
||||
};
|
||||
use windows::Win32::Graphics::Gdi::ClientToScreen;
|
||||
use windows::Win32::UI::WindowsAndMessaging::{
|
||||
GA_ROOT, GW_HWNDNEXT, GetAncestor, GetTopWindow, GetWindow, GetWindowRect,
|
||||
GetWindowThreadProcessId, IsIconic, IsWindowVisible,
|
||||
};
|
||||
|
||||
pub fn is_ads_webview_occluded(
|
||||
main_hwnd: HWND,
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> bool {
|
||||
let Some(ad_rect) = ad_rect_in_screen(main_hwnd, x, y, width, height)
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if is_empty_rect(&ad_rect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let ad_area = rect_area(&ad_rect);
|
||||
if ad_area == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut occluded_area = 0u64;
|
||||
let app_root = unsafe { GetAncestor(main_hwnd, GA_ROOT) };
|
||||
let app_process_id = std::process::id();
|
||||
let mut hwnd = match unsafe { GetTopWindow(None) } {
|
||||
Ok(hwnd) => hwnd,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
while !hwnd.is_invalid() {
|
||||
let window_root = unsafe { GetAncestor(hwnd, GA_ROOT) };
|
||||
|
||||
if window_root == app_root {
|
||||
return false;
|
||||
}
|
||||
|
||||
if window_process_id(hwnd) == Some(app_process_id) {
|
||||
hwnd = match unsafe { GetWindow(hwnd, GW_HWNDNEXT) } {
|
||||
Ok(hwnd) => hwnd,
|
||||
Err(_) => break,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
if window_counts_as_occluder(hwnd)
|
||||
&& let Some(occluder_rect) = window_rect(hwnd)
|
||||
&& let Some(intersection) =
|
||||
intersect_rects(&ad_rect, &occluder_rect)
|
||||
{
|
||||
occluded_area =
|
||||
occluded_area.saturating_add(rect_area(&intersection));
|
||||
|
||||
if (occluded_area as f64 / ad_area as f64)
|
||||
>= super::ads::OCCLUDED_AREA_THRESHOLD
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
hwnd = match unsafe { GetWindow(hwnd, GW_HWNDNEXT) } {
|
||||
Ok(hwnd) => hwnd,
|
||||
Err(_) => break,
|
||||
};
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn ad_rect_in_screen(
|
||||
main_hwnd: HWND,
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Option<RECT> {
|
||||
let mut origin = POINT { x: 0, y: 0 };
|
||||
|
||||
if !unsafe { ClientToScreen(main_hwnd, &mut origin).as_bool() } {
|
||||
return None;
|
||||
}
|
||||
|
||||
let left = origin.x.saturating_add(x);
|
||||
let top = origin.y.saturating_add(y);
|
||||
let right = left.saturating_add(width as i32);
|
||||
let bottom = top.saturating_add(height as i32);
|
||||
|
||||
Some(RECT {
|
||||
left,
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
})
|
||||
}
|
||||
|
||||
fn window_counts_as_occluder(hwnd: HWND) -> bool {
|
||||
if !unsafe { IsWindowVisible(hwnd).as_bool() } {
|
||||
return false;
|
||||
}
|
||||
|
||||
if unsafe { IsIconic(hwnd).as_bool() } {
|
||||
return false;
|
||||
}
|
||||
|
||||
if is_dwm_cloaked(hwnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn window_process_id(hwnd: HWND) -> Option<u32> {
|
||||
let mut process_id = 0u32;
|
||||
|
||||
unsafe {
|
||||
GetWindowThreadProcessId(hwnd, Some(&mut process_id));
|
||||
}
|
||||
|
||||
(process_id != 0).then_some(process_id)
|
||||
}
|
||||
|
||||
fn is_dwm_cloaked(hwnd: HWND) -> bool {
|
||||
let mut cloaked = 0u32;
|
||||
|
||||
unsafe {
|
||||
DwmGetWindowAttribute(
|
||||
hwnd,
|
||||
DWMWA_CLOAKED,
|
||||
&mut cloaked as *mut u32 as *mut _,
|
||||
std::mem::size_of::<u32>() as u32,
|
||||
)
|
||||
}
|
||||
.is_ok()
|
||||
&& cloaked != 0
|
||||
}
|
||||
|
||||
fn window_rect(hwnd: HWND) -> Option<RECT> {
|
||||
let mut rect = RECT::default();
|
||||
|
||||
if unsafe {
|
||||
DwmGetWindowAttribute(
|
||||
hwnd,
|
||||
DWMWA_EXTENDED_FRAME_BOUNDS,
|
||||
&mut rect as *mut RECT as *mut _,
|
||||
std::mem::size_of::<RECT>() as u32,
|
||||
)
|
||||
}
|
||||
.is_err()
|
||||
&& unsafe { GetWindowRect(hwnd, &mut rect) }.is_err()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
if is_empty_rect(&rect) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(rect)
|
||||
}
|
||||
|
||||
fn is_empty_rect(rect: &RECT) -> bool {
|
||||
rect.right <= rect.left || rect.bottom <= rect.top
|
||||
}
|
||||
|
||||
fn rect_area(rect: &RECT) -> u64 {
|
||||
if is_empty_rect(rect) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
(rect.right - rect.left) as u64 * (rect.bottom - rect.top) as u64
|
||||
}
|
||||
|
||||
fn intersect_rects(a: &RECT, b: &RECT) -> Option<RECT> {
|
||||
let rect = RECT {
|
||||
left: a.left.max(b.left),
|
||||
top: a.top.max(b.top),
|
||||
right: a.right.min(b.right),
|
||||
bottom: a.bottom.min(b.bottom),
|
||||
};
|
||||
|
||||
(!is_empty_rect(&rect)).then_some(rect)
|
||||
}
|
||||
@@ -18,6 +18,10 @@ pub mod tags;
|
||||
pub mod utils;
|
||||
|
||||
pub mod ads;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod ads_occlusion_macos;
|
||||
#[cfg(windows)]
|
||||
mod ads_occlusion_windows;
|
||||
pub mod cache;
|
||||
pub mod files;
|
||||
pub mod friends;
|
||||
|
||||
+52
-14
@@ -6,6 +6,7 @@
|
||||
|
||||
use native_dialog::{DialogBuilder, MessageLevel};
|
||||
use std::env;
|
||||
use std::sync::atomic::Ordering;
|
||||
use tauri::{Listener, Manager};
|
||||
use tauri_plugin_fs::FsExt;
|
||||
use theseus::prelude::*;
|
||||
@@ -96,6 +97,17 @@ fn restart_app(app: tauri::AppHandle) {
|
||||
app.restart();
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn set_restart_after_pending_update(
|
||||
should_restart: bool,
|
||||
) -> api::Result<()> {
|
||||
let state = State::get().await?;
|
||||
state
|
||||
.restart_after_pending_update
|
||||
.store(should_restart, Ordering::Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// if Tauri app is called with arguments, then those arguments will be treated as commands
|
||||
// ie: deep links or filepaths for .mrpacks
|
||||
fn main() {
|
||||
@@ -245,6 +257,7 @@ fn main() {
|
||||
get_update_size,
|
||||
enqueue_update_for_installation,
|
||||
remove_enqueued_update,
|
||||
set_restart_after_pending_update,
|
||||
toggle_decorations,
|
||||
show_window,
|
||||
restart_app,
|
||||
@@ -262,7 +275,13 @@ fn main() {
|
||||
#[cfg(feature = "updater")]
|
||||
if matches!(event, tauri::RunEvent::Exit) {
|
||||
let update_data = app.state::<PendingUpdateData>().inner();
|
||||
if let Some((update, data)) = &*update_data.0.lock().unwrap() {
|
||||
let should_restart = State::get_if_initialized()
|
||||
.map(|s| {
|
||||
s.restart_after_pending_update.load(Ordering::Relaxed)
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if let Some((update, data)) = &*update_data.0.lock().unwrap()
|
||||
{
|
||||
fn set_changelog_toast(version: Option<String>) {
|
||||
let toast_result: theseus::Result<()> = tauri::async_runtime::block_on(async move {
|
||||
let mut settings = settings::get().await?;
|
||||
@@ -271,27 +290,46 @@ fn main() {
|
||||
Ok(())
|
||||
});
|
||||
if let Err(e) = toast_result {
|
||||
tracing::warn!("Failed to set pending_update_toast: {e}")
|
||||
tracing::warn!(
|
||||
"Failed to set pending_update_toast: {e}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
set_changelog_toast(Some(update.version.clone()));
|
||||
if let Err(e) = update.install(data) {
|
||||
tracing::error!("Error while updating: {e}");
|
||||
set_changelog_toast(None);
|
||||
match update.install(data) {
|
||||
Ok(()) => {
|
||||
if should_restart {
|
||||
tracing::info!(
|
||||
"Pending update installed successfully (version {}); restarting because user requested reload",
|
||||
update.version
|
||||
);
|
||||
app.restart();
|
||||
} else {
|
||||
tracing::info!(
|
||||
"Pending update installed successfully (version {}); exiting without relaunch (user did not request reload)",
|
||||
update.version
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!(
|
||||
"Pending update install failed (version {}): {e}",
|
||||
update.version
|
||||
);
|
||||
set_changelog_toast(None);
|
||||
|
||||
DialogBuilder::message()
|
||||
.set_level(MessageLevel::Error)
|
||||
.set_title("Update error")
|
||||
.set_text(format!("Failed to install update due to an error:\n{e}"))
|
||||
.alert()
|
||||
.show()
|
||||
.unwrap();
|
||||
DialogBuilder::message()
|
||||
.set_level(MessageLevel::Error)
|
||||
.set_title("Update error")
|
||||
.set_text(format!("Failed to install update due to an error:\n{e}"))
|
||||
.alert()
|
||||
.show()
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
app.restart();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
if let tauri::RunEvent::Opened { urls } = event {
|
||||
tracing::info!("Handling webview open {urls:?}");
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { GenericModrinthClient, type Labrinth } from '@modrinth/api-client'
|
||||
import serverSidedVue from '@vitejs/plugin-vue'
|
||||
import fs from 'fs/promises'
|
||||
import { defineNuxtConfig } from 'nuxt/config'
|
||||
import { fileURLToPath } from 'url'
|
||||
import svgLoader from 'vite-svg-loader'
|
||||
|
||||
import { GenericModrinthClient, type Labrinth } from '../../packages/api-client/src/index.ts'
|
||||
|
||||
const STAGING_API_URL = 'https://staging-api.modrinth.com/v2/'
|
||||
const API_CLIENT_SOURCE = fileURLToPath(
|
||||
new URL('../../packages/api-client/src/index.ts', import.meta.url),
|
||||
)
|
||||
|
||||
const preloadedFonts = [
|
||||
'inter/Inter-Regular.woff2',
|
||||
@@ -24,6 +29,9 @@ const STAGING_MODRINTH_URL = 'https://staging.modrinth.com'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
srcDir: 'src/',
|
||||
alias: {
|
||||
'@modrinth/api-client': API_CLIENT_SOURCE,
|
||||
},
|
||||
app: {
|
||||
head: {
|
||||
htmlAttrs: {
|
||||
@@ -82,6 +90,9 @@ export default defineNuxtConfig({
|
||||
},
|
||||
cacheDir: '../../node_modules/.vite/apps/knossos',
|
||||
resolve: {
|
||||
alias: {
|
||||
'@modrinth/api-client': API_CLIENT_SOURCE,
|
||||
},
|
||||
dedupe: ['vue'],
|
||||
},
|
||||
plugins: [
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "^0.5.4",
|
||||
"@intercom/messenger-js-sdk": "^0.0.14",
|
||||
"@ltd/j-toml": "^1.38.0",
|
||||
"@modrinth/api-client": "workspace:*",
|
||||
"@modrinth/assets": "workspace:*",
|
||||
@@ -47,7 +46,7 @@
|
||||
"@modrinth/ui": "workspace:*",
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"@sentry/nuxt": "^10.33.0",
|
||||
"@tanstack/vue-query": "^5.90.7",
|
||||
"@tanstack/vue-query": "5.90.7",
|
||||
"@types/three": "^0.172.0",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vue-email/components": "^0.0.21",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { BlueskyIcon, DiscordIcon, GithubIcon, MastodonIcon, TwitterIcon } from '@modrinth/assets'
|
||||
import {
|
||||
BlueskyIcon,
|
||||
DiscordIcon,
|
||||
GithubIcon,
|
||||
MastodonIcon,
|
||||
ToggleRightIcon,
|
||||
TwitterIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
AutoLink,
|
||||
ButtonStyled,
|
||||
@@ -10,6 +17,7 @@ import {
|
||||
type MessageDescriptor,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { commonSettingsMessages } from '@modrinth/ui/src/utils/common-messages.js'
|
||||
|
||||
import TextLogo from '~/components/brand/TextLogo.vue'
|
||||
|
||||
@@ -233,11 +241,21 @@ function developerModeIncrement() {
|
||||
role="region"
|
||||
:aria-label="formatMessage(messages.modrinthInformation)"
|
||||
>
|
||||
<TextLogo
|
||||
aria-hidden="true"
|
||||
class="text-logo button-base h-6 w-auto text-contrast lg:h-8"
|
||||
@click="developerModeIncrement()"
|
||||
/>
|
||||
<div class="flex items-center gap-2">
|
||||
<TextLogo
|
||||
aria-hidden="true"
|
||||
class="text-logo button-base h-6 w-auto text-contrast lg:h-8"
|
||||
@click="developerModeIncrement()"
|
||||
/>
|
||||
<ButtonStyled v-if="flags.developerMode" circular type="transparent" color="brand">
|
||||
<nuxt-link
|
||||
v-tooltip="formatMessage(commonSettingsMessages.featureFlags)"
|
||||
to="/settings/flags"
|
||||
>
|
||||
<ToggleRightIcon />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<div class="flex flex-wrap justify-center gap-px sm:-mx-2">
|
||||
<ButtonStyled
|
||||
v-for="(social, index) in socialLinks"
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { defineMessages, PagewideBanner, useVIntl } from '@modrinth/ui'
|
||||
import { XCircleIcon, XIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, defineMessages, PagewideBanner, useVIntl } from '@modrinth/ui'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const flags = useFeatureFlags()
|
||||
|
||||
const tempIgnored = ref(false)
|
||||
|
||||
const messages = defineMessages({
|
||||
title: {
|
||||
@@ -13,16 +17,34 @@ const messages = defineMessages({
|
||||
defaultMessage:
|
||||
"This deploy of Modrinth's frontend failed to generate state from the API. This may be due to an outage or an error in configuration. Rebuild when the API is available. Error codes: {errors}; Current API URL is: {url}",
|
||||
},
|
||||
ignoreErrors: {
|
||||
id: 'layout.banner.build-fail.ignore',
|
||||
defaultMessage: 'Ignore',
|
||||
},
|
||||
alwaysIgnore: {
|
||||
id: 'layout.banner.build-fail.always-ignore',
|
||||
defaultMessage: 'Always ignore',
|
||||
},
|
||||
})
|
||||
|
||||
defineProps<{
|
||||
errors: any[] | undefined
|
||||
apiUrl: string
|
||||
}>()
|
||||
|
||||
function alwaysIgnoreBanner() {
|
||||
flags.value.alwaysIgnoreErrorBanner = true
|
||||
saveFeatureFlags()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PagewideBanner v-if="errors?.length" variant="error">
|
||||
<PagewideBanner
|
||||
v-if="
|
||||
flags.showAllBanners || (errors?.length && !tempIgnored && !flags.alwaysIgnoreErrorBanner)
|
||||
"
|
||||
variant="error"
|
||||
>
|
||||
<template #title>
|
||||
<span>{{ formatMessage(messages.title) }}</span>
|
||||
</template>
|
||||
@@ -34,5 +56,19 @@ defineProps<{
|
||||
})
|
||||
}}
|
||||
</template>
|
||||
<template #actions_right>
|
||||
<ButtonStyled color="red" type="transparent" hover-color-fill="background">
|
||||
<button @click="alwaysIgnoreBanner">
|
||||
<XCircleIcon />
|
||||
{{ formatMessage(messages.alwaysIgnore) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="tempIgnored = true">
|
||||
<XIcon />
|
||||
{{ formatMessage(messages.ignoreErrors) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</PagewideBanner>
|
||||
</template>
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
const { formatMessage } = useVIntl()
|
||||
const flags = useFeatureFlags()
|
||||
const config = useRuntimeConfig()
|
||||
const route = useRoute()
|
||||
|
||||
const messages = defineMessages({
|
||||
title: {
|
||||
@@ -21,7 +22,7 @@ const messages = defineMessages({
|
||||
},
|
||||
description: {
|
||||
id: 'layout.banner.preview.description',
|
||||
defaultMessage: `If you meant to access the official Modrinth website, visit <link>https://modrinth.com</link>. This preview deploy is used by Modrinth staff for testing purposes. It was built using <branch-link>{owner}/{branch}</branch-link> @ {commit}.`,
|
||||
defaultMessage: `If you meant to access the official Modrinth website, visit {url}. This preview deploy is used by Modrinth staff for testing purposes. It was built using <branch-link>{owner}/{branch}</branch-link> @ {commit}.`,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -29,10 +30,12 @@ function hidePreviewBanner() {
|
||||
flags.value.hidePreviewBanner = true
|
||||
saveFeatureFlags()
|
||||
}
|
||||
|
||||
const url = computed(() => `https://modrinth.com${route.fullPath}`)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PagewideBanner v-if="!flags.hidePreviewBanner" variant="info">
|
||||
<PagewideBanner v-if="!flags.hidePreviewBanner || flags.showAllBanners" variant="info">
|
||||
<template #title>
|
||||
<span>{{ formatMessage(messages.title) }}</span>
|
||||
</template>
|
||||
@@ -45,9 +48,9 @@ function hidePreviewBanner() {
|
||||
branch: config.public.branch,
|
||||
}"
|
||||
>
|
||||
<template #link="{ children }">
|
||||
<a href="https://modrinth.com" target="_blank" rel="noopener" class="text-link">
|
||||
<component :is="() => normalizeChildren(children)" />
|
||||
<template #url>
|
||||
<a :href="url" target="_blank" rel="noopener" class="text-link">
|
||||
{{ url }}
|
||||
</a>
|
||||
</template>
|
||||
<template #branch-link="{ children }">
|
||||
@@ -75,7 +78,7 @@ function hidePreviewBanner() {
|
||||
</IntlFormatted>
|
||||
</span>
|
||||
</template>
|
||||
<template #actions_right>
|
||||
<template #actions_top_right>
|
||||
<ButtonStyled type="transparent" circular>
|
||||
<button :aria-label="formatMessage(commonMessages.closeButton)" @click="hidePreviewBanner">
|
||||
<XIcon aria-hidden="true" />
|
||||
|
||||
@@ -47,7 +47,7 @@ function hideRussiaCensorshipBanner() {
|
||||
<span class="text-xs font-medium">(Перевод на русский)</span>
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<ButtonStyled type="transparent" hover-color-fill="background">
|
||||
<nuxt-link to="/news/article/standing-by-our-values">
|
||||
<BookTextIcon /> Read our full statement
|
||||
<span class="text-xs font-medium">(English)</span>
|
||||
@@ -55,7 +55,7 @@ function hideRussiaCensorshipBanner() {
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions_right>
|
||||
<template #actions_top_right>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<button
|
||||
v-tooltip="formatMessage(commonMessages.closeButton)"
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const cosmetics = useCosmetics()
|
||||
const flags = useFeatureFlags()
|
||||
|
||||
const messages = defineMessages({
|
||||
title: {
|
||||
@@ -29,14 +30,14 @@ function hideStagingBanner() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PagewideBanner v-if="!cosmetics.hideStagingBanner" variant="warning">
|
||||
<PagewideBanner v-if="flags.showAllBanners || !cosmetics.hideStagingBanner" variant="warning">
|
||||
<template #title>
|
||||
<span>{{ formatMessage(messages.title) }}</span>
|
||||
</template>
|
||||
<template #description>
|
||||
{{ formatMessage(messages.description) }}
|
||||
</template>
|
||||
<template #actions_right>
|
||||
<template #actions_top_right>
|
||||
<ButtonStyled type="transparent" circular>
|
||||
<button :aria-label="formatMessage(commonMessages.closeButton)" @click="hideStagingBanner">
|
||||
<XIcon aria-hidden="true" />
|
||||
|
||||
@@ -29,8 +29,8 @@ const messages = defineMessages({
|
||||
<template #description>
|
||||
<span>{{ formatMessage(messages.description) }}</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<ButtonStyled>
|
||||
<template #actions_right>
|
||||
<ButtonStyled color="red">
|
||||
<nuxt-link to="/settings/billing">
|
||||
<SettingsIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.action) }}
|
||||
|
||||
@@ -55,7 +55,7 @@ function openTaxForm(e: MouseEvent) {
|
||||
formatMessage(messages.description, { threshold: formatMoney(taxThreshold) })
|
||||
}}</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<template #actions_right>
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="openTaxForm"><FileTextIcon /> {{ formatMessage(messages.action) }}</button>
|
||||
</ButtonStyled>
|
||||
|
||||
@@ -29,7 +29,7 @@ const messages = defineMessages({
|
||||
<template #description>
|
||||
<span>{{ formatMessage(messages.description) }}</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<template #actions_right>
|
||||
<div class="flex w-fit flex-row">
|
||||
<ButtonStyled color="red">
|
||||
<nuxt-link to="https://support.modrinth.com" target="_blank" rel="noopener">
|
||||
|
||||
@@ -96,14 +96,12 @@ async function handleResendEmailVerification() {
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<ButtonStyled v-if="hasEmail">
|
||||
<button @click="handleResendEmailVerification">
|
||||
<template #actions_right>
|
||||
<ButtonStyled color="orange">
|
||||
<button v-if="hasEmail" @click="handleResendEmailVerification">
|
||||
{{ formatMessage(verifyEmailBannerMessages.action) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else>
|
||||
<nuxt-link to="/settings/account">
|
||||
<nuxt-link v-else to="/settings/account">
|
||||
<SettingsIcon aria-hidden="true" />
|
||||
{{ formatMessage(addEmailBannerMessages.action) }}
|
||||
</nuxt-link>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
import { PagewideBanner } from '@modrinth/ui'
|
||||
|
||||
const flags = useFeatureFlags()
|
||||
const route = useRoute()
|
||||
|
||||
const url = computed(() => `https://modrinth.com${route.fullPath}`)
|
||||
|
||||
const bannerRoot = ref<HTMLElement | null>(null)
|
||||
|
||||
function onProdLinkClick(e: MouseEvent) {
|
||||
e.preventDefault()
|
||||
const el = bannerRoot.value
|
||||
if (el) {
|
||||
const { height } = el.getBoundingClientRect()
|
||||
window.scrollBy({ top: Math.ceil(height), behavior: 'auto' })
|
||||
}
|
||||
window.open(url.value, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="flags.showViewProdRouteBanner || flags.showAllBanners" ref="bannerRoot">
|
||||
<PagewideBanner variant="info" slim>
|
||||
<template #description>
|
||||
<span>
|
||||
View route on production:
|
||||
<a
|
||||
:href="url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-link"
|
||||
@click="onProdLinkClick"
|
||||
>
|
||||
{{ url }}
|
||||
</a>
|
||||
</span>
|
||||
</template>
|
||||
</PagewideBanner>
|
||||
</div>
|
||||
</template>
|
||||
+15
-14
@@ -2,28 +2,29 @@
|
||||
<div
|
||||
class="flex h-11 items-center justify-between gap-2 rounded-xl bg-button-bg px-4 py-1 text-button-text"
|
||||
>
|
||||
<div class="grid max-w-[75%] grid-cols-[auto_1fr_auto] items-center gap-2">
|
||||
<div class="flex min-w-0 flex-1 items-center justify-start gap-2">
|
||||
<Avatar v-if="icon" :src="icon" alt="dependency-icon" size="20px" :no-shadow="true" />
|
||||
|
||||
<span v-tooltip="name || projectId" class="truncate font-semibold text-contrast">
|
||||
<span
|
||||
v-tooltip="name || projectId"
|
||||
class="min-w-0 max-w-fit flex-1 truncate font-semibold text-contrast"
|
||||
>
|
||||
{{ name || 'Unknown Project' }}
|
||||
</span>
|
||||
<span
|
||||
v-if="versionNumber"
|
||||
v-tooltip="versionNumber"
|
||||
class="min-w-0 max-w-fit flex-1 truncate whitespace-nowrap text-sm font-medium"
|
||||
>
|
||||
{{ versionNumber }}
|
||||
</span>
|
||||
|
||||
<TagItem class="shrink-0 border !border-solid border-surface-5 capitalize">
|
||||
{{ dependencyType }}
|
||||
</TagItem>
|
||||
</div>
|
||||
|
||||
<span
|
||||
v-if="versionName"
|
||||
v-tooltip="versionName"
|
||||
class="truncate whitespace-nowrap font-medium"
|
||||
:class="!hideRemove ? 'max-w-[35%]' : 'max-w-[50%]'"
|
||||
>
|
||||
{{ versionName }}
|
||||
</span>
|
||||
|
||||
<div v-if="!hideRemove" class="flex items-center justify-end gap-1">
|
||||
<div v-if="!hideRemove" class="flex shrink-0 items-center justify-end gap-1">
|
||||
<ButtonStyled size="standard" :circular="true">
|
||||
<button aria-label="Remove file" class="-mr-2 !shadow-none" @click="emitRemove">
|
||||
<XIcon aria-hidden="true" />
|
||||
@@ -43,12 +44,12 @@ const emit = defineEmits<{
|
||||
(e: 'remove'): void
|
||||
}>()
|
||||
|
||||
const { projectId, name, icon, dependencyType, versionName, hideRemove } = defineProps<{
|
||||
const { projectId, name, icon, dependencyType, versionNumber, hideRemove } = defineProps<{
|
||||
projectId: string
|
||||
name?: string
|
||||
icon?: string
|
||||
dependencyType: Labrinth.Versions.v2.DependencyType
|
||||
versionName?: string
|
||||
versionNumber?: string
|
||||
hideRemove?: boolean
|
||||
}>()
|
||||
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@
|
||||
:name="dependency.name"
|
||||
:icon="dependency.icon"
|
||||
:dependency-type="dependency.dependencyType"
|
||||
:version-name="dependency.versionName"
|
||||
:version-number="dependency.versionNumber"
|
||||
:hide-remove="disableRemove"
|
||||
@remove="() => removeDependency(index)"
|
||||
/>
|
||||
@@ -35,7 +35,7 @@ const addedDependencies = computed(() =>
|
||||
if (!dep.project_id) return null
|
||||
|
||||
const dependencyProject = dependencyProjects.value[dep.project_id]
|
||||
const versionName = dependencyVersions.value[dep.version_id || '']?.name ?? ''
|
||||
const versionNumber = dependencyVersions.value[dep.version_id || '']?.version_number ?? ''
|
||||
|
||||
if (!dependencyProject && projectsFetchLoading.value) return null
|
||||
|
||||
@@ -44,7 +44,7 @@ const addedDependencies = computed(() =>
|
||||
name: dependencyProject?.name,
|
||||
icon: dependencyProject?.icon_url,
|
||||
dependencyType: dep.dependency_type,
|
||||
versionName,
|
||||
versionNumber,
|
||||
}
|
||||
})
|
||||
.filter(Boolean),
|
||||
|
||||
+91
-23
@@ -3,11 +3,16 @@
|
||||
v-model="projectId"
|
||||
placeholder="Select project"
|
||||
:options="options"
|
||||
:searchable="true"
|
||||
:search-value="selectedProjectOption?.label"
|
||||
search-placeholder="Search by name or paste ID..."
|
||||
:no-options-message="searchLoading ? 'Loading...' : 'No results found'"
|
||||
:disable-search-filter="true"
|
||||
searchable
|
||||
disable-search-filter
|
||||
select-search-text-on-focus
|
||||
:show-chevron="false"
|
||||
@search-input="(query) => handleSearch(query)"
|
||||
@search-blur="handleSearchBlur"
|
||||
@select="handleSelect"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -15,15 +20,37 @@
|
||||
import type { ComboboxOption } from '@modrinth/ui'
|
||||
import { Combobox, injectModrinthClient, injectNotificationManager } from '@modrinth/ui'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
import { defineAsyncComponent, h } from 'vue'
|
||||
import { defineAsyncComponent, h, markRaw, ref, watch } from 'vue'
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const projectId = defineModel<string>()
|
||||
|
||||
const searchLoading = ref(false)
|
||||
const options = ref<ComboboxOption<string>[]>([])
|
||||
const selectedProjectOption = ref<ComboboxOption<string>>()
|
||||
const selectedProjectSearchQuery = ref('')
|
||||
|
||||
const { labrinth } = injectModrinthClient()
|
||||
let latestSearchQuery = ''
|
||||
|
||||
function hitToOption(hit: { title: string; project_id: string; icon_url?: string | null }) {
|
||||
return {
|
||||
label: hit.title,
|
||||
value: hit.project_id,
|
||||
icon: markRaw(
|
||||
defineAsyncComponent(() =>
|
||||
Promise.resolve({
|
||||
setup: () => () =>
|
||||
h('img', {
|
||||
src: hit.icon_url,
|
||||
alt: hit.title,
|
||||
class: 'h-5 w-5 rounded',
|
||||
}),
|
||||
}),
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const search = async (query: string) => {
|
||||
query = query.trim()
|
||||
@@ -53,36 +80,77 @@ const search = async (query: string) => {
|
||||
facets: [[`project_id:${query.replace(/[^a-zA-Z0-9]/g, '')}`]], // remove any non-alphanumeric characters
|
||||
})
|
||||
|
||||
options.value = [...resultsByProjectId.hits, ...results.hits].map((hit) => ({
|
||||
label: hit.title,
|
||||
value: hit.project_id,
|
||||
icon: markRaw(
|
||||
defineAsyncComponent(() =>
|
||||
Promise.resolve({
|
||||
setup: () => () =>
|
||||
h('img', {
|
||||
src: hit.icon_url,
|
||||
alt: hit.title,
|
||||
class: 'h-5 w-5 rounded',
|
||||
}),
|
||||
}),
|
||||
),
|
||||
),
|
||||
}))
|
||||
if (query !== latestSearchQuery) return
|
||||
|
||||
options.value = [...resultsByProjectId.hits, ...results.hits].map(hitToOption)
|
||||
} catch (error: any) {
|
||||
if (query !== latestSearchQuery) return
|
||||
|
||||
addNotification({
|
||||
title: 'An error occurred',
|
||||
text: error.data ? error.data.description : error,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
searchLoading.value = false
|
||||
|
||||
if (query === latestSearchQuery) {
|
||||
searchLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const throttledSearch = useDebounceFn(search, 500)
|
||||
const throttledSearch = useDebounceFn(search, 250)
|
||||
|
||||
const runSearch = async (query: string, debounce: boolean) => {
|
||||
query = query.trim()
|
||||
latestSearchQuery = query
|
||||
|
||||
if (!query) {
|
||||
searchLoading.value = false
|
||||
options.value = []
|
||||
await throttledSearch(query)
|
||||
return
|
||||
}
|
||||
|
||||
searchLoading.value = true
|
||||
await (debounce ? throttledSearch(query) : search(query))
|
||||
}
|
||||
|
||||
const handleSearch = async (query: string) => {
|
||||
searchLoading.value = true
|
||||
await throttledSearch(query)
|
||||
await runSearch(query, true)
|
||||
}
|
||||
|
||||
const handleSelect = (option: ComboboxOption<string>) => {
|
||||
selectedProjectOption.value = option
|
||||
selectedProjectSearchQuery.value = latestSearchQuery
|
||||
}
|
||||
|
||||
const handleSearchBlur = async () => {
|
||||
if (!projectId.value) return
|
||||
|
||||
const selectedOption =
|
||||
options.value.find((option) => option.value === projectId.value) ??
|
||||
(selectedProjectOption.value?.value === projectId.value
|
||||
? selectedProjectOption.value
|
||||
: undefined)
|
||||
if (!selectedOption) return
|
||||
|
||||
await runSearch(selectedProjectSearchQuery.value || selectedOption.label, false)
|
||||
|
||||
if (!options.value.some((option) => option.value === selectedOption.value)) {
|
||||
options.value = [selectedOption, ...options.value]
|
||||
}
|
||||
}
|
||||
|
||||
watch(projectId, (value) => {
|
||||
if (!value) {
|
||||
selectedProjectOption.value = undefined
|
||||
selectedProjectSearchQuery.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
const option = options.value.find((option) => option.value === value)
|
||||
if (option) {
|
||||
selectedProjectOption.value = option
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
:name="dependency.name"
|
||||
:icon="dependency.icon"
|
||||
:dependency-type="dependency.dependency_type"
|
||||
:version-name="dependency.versionName"
|
||||
:version-number="dependency.versionNumber"
|
||||
@on-add-suggestion="
|
||||
() =>
|
||||
handleAddSuggestion({
|
||||
|
||||
+17
-14
@@ -1,28 +1,31 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex items-center justify-between gap-2 rounded-xl border-2 border-dashed border-surface-5 px-4 py-1 text-button-text"
|
||||
class="flex h-11 items-center justify-between gap-2 rounded-xl border-2 border-dashed border-surface-5 px-4 py-1 text-button-text"
|
||||
>
|
||||
<div class="grid max-w-[75%] grid-cols-[auto_1fr_auto] items-center gap-2">
|
||||
<div class="flex min-w-0 flex-1 items-center justify-start gap-2">
|
||||
<Avatar v-if="icon" :src="icon" alt="dependency-icon" size="20px" :no-shadow="true" />
|
||||
|
||||
<span v-tooltip="name || 'Unknown Project'" class="truncate font-semibold text-contrast">
|
||||
<span
|
||||
v-tooltip="name || 'Unknown Project'"
|
||||
class="min-w-0 max-w-fit flex-1 truncate font-semibold text-contrast"
|
||||
>
|
||||
{{ name || 'Unknown Project' }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="versionNumber"
|
||||
v-tooltip="versionNumber"
|
||||
class="min-w-0 max-w-fit flex-1 truncate whitespace-nowrap text-sm font-medium"
|
||||
>
|
||||
{{ versionNumber }}
|
||||
</span>
|
||||
|
||||
<TagItem class="shrink-0 border !border-solid border-surface-5 capitalize">
|
||||
{{ dependencyType }}
|
||||
</TagItem>
|
||||
</div>
|
||||
|
||||
<span
|
||||
v-if="versionName"
|
||||
v-tooltip="versionName"
|
||||
class="max-w-[35%] truncate whitespace-nowrap font-medium"
|
||||
>
|
||||
{{ versionName }}
|
||||
</span>
|
||||
|
||||
<div class="flex items-center justify-end gap-1">
|
||||
<div class="flex shrink-0 items-center justify-end gap-1">
|
||||
<ButtonStyled size="standard" :circular="true" type="transparent">
|
||||
<button aria-label="Add dependency" class="!shadow-none" @click="emitAddSuggestion">
|
||||
<PlusIcon aria-hidden="true" />
|
||||
@@ -41,11 +44,11 @@ const emit = defineEmits<{
|
||||
(e: 'onAddSuggestion'): void
|
||||
}>()
|
||||
|
||||
const { name, icon, dependencyType, versionName } = defineProps<{
|
||||
const { name, icon, dependencyType, versionNumber } = defineProps<{
|
||||
name?: string
|
||||
icon?: string
|
||||
dependencyType: Labrinth.Versions.v2.DependencyType
|
||||
versionName?: string
|
||||
versionNumber?: string
|
||||
}>()
|
||||
|
||||
function emitAddSuggestion() {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<template>
|
||||
<NavTabs
|
||||
<Tabs
|
||||
v-if="editingVersion"
|
||||
mode="local"
|
||||
:links="editTabLinks"
|
||||
:active-index="2"
|
||||
class="mb-4 border border-solid border-surface-5 shadow-none drop-shadow-none"
|
||||
@tab-click="setEditTab"
|
||||
value="add-files"
|
||||
:tabs="editTabs"
|
||||
class="mb-5 border border-solid border-surface-5 !shadow-none !drop-shadow-none"
|
||||
@change="setEditTab"
|
||||
/>
|
||||
<div class="flex w-full flex-col gap-4">
|
||||
<template
|
||||
@@ -99,7 +98,8 @@ import {
|
||||
defineMessages,
|
||||
DropzoneFileInput,
|
||||
injectProjectPageContext,
|
||||
NavTabs,
|
||||
Tabs,
|
||||
type TabsTab,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { acceptFileFromProjectType } from '@modrinth/utils'
|
||||
@@ -124,18 +124,14 @@ const {
|
||||
handleNewFiles,
|
||||
} = injectManageVersionContext()
|
||||
|
||||
const editTabs = [
|
||||
{ label: 'Metadata', href: 'metadata', stage: 'metadata' },
|
||||
{ label: 'Details', href: 'details', stage: 'add-details' },
|
||||
{ label: 'Files', href: 'files', stage: 'add-files' },
|
||||
] as const
|
||||
const editTabs: TabsTab[] = [
|
||||
{ label: 'Metadata', value: 'metadata' },
|
||||
{ label: 'Details', value: 'add-details' },
|
||||
{ label: 'Files', value: 'add-files' },
|
||||
]
|
||||
|
||||
const editTabLinks = editTabs.map(({ label, href }) => ({ label, href }))
|
||||
|
||||
function setEditTab(index: number) {
|
||||
const tab = editTabs[index]
|
||||
if (!tab) return
|
||||
modal.value?.setStage(tab.stage)
|
||||
function setEditTab(tab: TabsTab) {
|
||||
modal.value?.setStage(tab.value)
|
||||
}
|
||||
|
||||
function handleRemoveFile(index: number) {
|
||||
|
||||
+43
-167
@@ -1,182 +1,58 @@
|
||||
<template>
|
||||
<div class="flex w-full max-w-full flex-col gap-6">
|
||||
<div class="flex flex-col gap-4">
|
||||
<span class="font-semibold text-contrast">Add dependency</span>
|
||||
<div class="flex flex-col gap-3 rounded-2xl border border-solid border-surface-5 p-4">
|
||||
<div class="grid gap-2.5">
|
||||
<span class="font-semibold text-contrast">Project</span>
|
||||
<DependencySelect v-model="newDependencyProjectId" />
|
||||
</div>
|
||||
<div class="flex w-full max-w-full flex-col gap-3">
|
||||
<div class="grid gap-2.5">
|
||||
<span class="font-semibold text-contrast">Project</span>
|
||||
<DependencySelect v-model="newDependencyProjectId" />
|
||||
</div>
|
||||
|
||||
<template v-if="newDependencyProjectId">
|
||||
<div class="grid gap-2.5">
|
||||
<span class="font-semibold text-contrast"> Version </span>
|
||||
<Combobox
|
||||
v-model="newDependencyVersionId"
|
||||
placeholder="Select version"
|
||||
:options="[{ label: 'Any version', value: null }, ...newDependencyVersions]"
|
||||
:searchable="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2.5">
|
||||
<span class="font-semibold text-contrast"> Dependency relation </span>
|
||||
<Combobox
|
||||
v-model="newDependencyType"
|
||||
placeholder="Select dependency type"
|
||||
:options="[
|
||||
{ label: 'Required', value: 'required' },
|
||||
{ label: 'Optional', value: 'optional' },
|
||||
{ label: 'Incompatible', value: 'incompatible' },
|
||||
{ label: 'Embedded', value: 'embedded' },
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ButtonStyled color="green">
|
||||
<button
|
||||
class="self-start"
|
||||
:disabled="!newDependencyProjectId"
|
||||
@click="
|
||||
() =>
|
||||
addDependency(
|
||||
toRaw({
|
||||
project_id: newDependencyProjectId,
|
||||
version_id: newDependencyVersionId || undefined,
|
||||
dependency_type: newDependencyType,
|
||||
}),
|
||||
)
|
||||
"
|
||||
>
|
||||
Add Dependency
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
<template v-if="newDependencyProjectId">
|
||||
<div class="grid gap-2.5">
|
||||
<span class="font-semibold text-contrast"> Version </span>
|
||||
<Combobox
|
||||
v-model="newDependencyVersionId"
|
||||
placeholder="Select version"
|
||||
:options="newDependencyVersionOptions"
|
||||
:search-value="selectedNewDependencyVersionLabel"
|
||||
:searchable="true"
|
||||
:select-search-text-on-focus="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="visibleSuggestedDependencies.length" class="flex flex-col gap-4">
|
||||
<span class="font-semibold text-contrast">Suggested dependencies</span>
|
||||
<SuggestedDependencies @on-add-suggestion="handleAddSuggestedDependency" />
|
||||
</div>
|
||||
|
||||
<div v-if="addedDependencies.length" class="flex flex-col gap-4">
|
||||
<span class="font-semibold text-contrast">Added dependencies</span>
|
||||
<DependenciesList />
|
||||
</div>
|
||||
<div class="grid gap-2.5">
|
||||
<span class="font-semibold text-contrast"> Dependency relation </span>
|
||||
<Combobox
|
||||
v-model="newDependencyType"
|
||||
placeholder="Select dependency type"
|
||||
:options="[
|
||||
{ label: 'Required', value: 'required' },
|
||||
{ label: 'Optional', value: 'optional' },
|
||||
{ label: 'Incompatible', value: 'incompatible' },
|
||||
{ label: 'Embedded', value: 'embedded' },
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Combobox,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
} from '@modrinth/ui'
|
||||
import type { ComboboxOption } from '@modrinth/ui/src/components/base/Combobox.vue'
|
||||
import { Combobox } from '@modrinth/ui'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import DependencySelect from '~/components/ui/create-project-version/components/DependencySelect.vue'
|
||||
import { injectManageVersionContext } from '~/providers/version/manage-version-modal'
|
||||
|
||||
import DependenciesList from '../components/DependenciesList.vue'
|
||||
import SuggestedDependencies from '../components/SuggestedDependencies/SuggestedDependencies.vue'
|
||||
const { newDependencyProjectId, newDependencyType, newDependencyVersionId, newDependencyVersions } =
|
||||
injectManageVersionContext()
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { labrinth } = injectModrinthClient()
|
||||
|
||||
const {
|
||||
draftVersion,
|
||||
dependencyProjects,
|
||||
dependencyVersions,
|
||||
projectsFetchLoading,
|
||||
visibleSuggestedDependencies,
|
||||
} = injectManageVersionContext()
|
||||
|
||||
const errorNotification = (err: any) => {
|
||||
addNotification({
|
||||
title: 'An error occurred',
|
||||
text: err.data ? err.data.description : err,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
|
||||
const newDependencyProjectId = ref<string>()
|
||||
const newDependencyType = ref<Labrinth.Versions.v2.DependencyType>('required')
|
||||
const newDependencyVersionId = ref<string | null>(null)
|
||||
|
||||
const newDependencyVersions = ref<ComboboxOption<string>[]>([])
|
||||
|
||||
// reset to defaults when select different project
|
||||
watch(newDependencyProjectId, async () => {
|
||||
newDependencyVersionId.value = null
|
||||
newDependencyType.value = 'required'
|
||||
|
||||
if (!newDependencyProjectId.value) {
|
||||
newDependencyVersions.value = []
|
||||
} else {
|
||||
try {
|
||||
const versions = await labrinth.versions_v3.getProjectVersions(newDependencyProjectId.value)
|
||||
newDependencyVersions.value = versions.map((version) => ({
|
||||
label: version.name,
|
||||
value: version.id,
|
||||
}))
|
||||
} catch (error: any) {
|
||||
errorNotification(error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const addedDependencies = computed(() =>
|
||||
(draftVersion.value.dependencies || [])
|
||||
.map((dep) => {
|
||||
if (!dep.project_id) return null
|
||||
|
||||
const dependencyProject = dependencyProjects.value[dep.project_id]
|
||||
const versionName = dependencyVersions.value[dep.version_id || '']?.name ?? ''
|
||||
|
||||
if (!dependencyProject && projectsFetchLoading.value) return null
|
||||
|
||||
return {
|
||||
projectId: dep.project_id,
|
||||
name: dependencyProject?.name,
|
||||
icon: dependencyProject?.icon_url,
|
||||
dependencyType: dep.dependency_type,
|
||||
versionName,
|
||||
}
|
||||
})
|
||||
.filter(Boolean),
|
||||
const newDependencyVersionOptions = computed(() => [
|
||||
{ label: 'Any version', value: null },
|
||||
...newDependencyVersions.value,
|
||||
])
|
||||
const selectedNewDependencyVersionLabel = computed(
|
||||
() =>
|
||||
newDependencyVersionOptions.value.find(
|
||||
(option) => option.value === newDependencyVersionId.value,
|
||||
)?.label,
|
||||
)
|
||||
|
||||
const addDependency = (dependency: Labrinth.Versions.v3.Dependency) => {
|
||||
if (!draftVersion.value.dependencies) draftVersion.value.dependencies = []
|
||||
|
||||
const alreadyAdded = draftVersion.value.dependencies.some((existing) => {
|
||||
if (existing.project_id !== dependency.project_id) return false
|
||||
if (!existing.version_id && !dependency.version_id) return true
|
||||
return existing.version_id === dependency.version_id
|
||||
})
|
||||
|
||||
if (alreadyAdded) {
|
||||
addNotification({
|
||||
title: 'Dependency already added',
|
||||
text: 'You cannot add the same dependency twice.',
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
projectsFetchLoading.value = true
|
||||
draftVersion.value.dependencies.push(dependency)
|
||||
newDependencyProjectId.value = undefined
|
||||
}
|
||||
|
||||
const handleAddSuggestedDependency = (dependency: Labrinth.Versions.v3.Dependency) => {
|
||||
draftVersion.value.dependencies?.push({
|
||||
project_id: dependency.project_id,
|
||||
version_id: dependency.version_id,
|
||||
dependency_type: dependency.dependency_type,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<template>
|
||||
<NavTabs
|
||||
<Tabs
|
||||
v-if="editingVersion"
|
||||
mode="local"
|
||||
:links="editTabLinks"
|
||||
:active-index="1"
|
||||
class="mb-4 border border-solid border-surface-5 shadow-none drop-shadow-none"
|
||||
@tab-click="setEditTab"
|
||||
value="add-details"
|
||||
:tabs="editTabs"
|
||||
class="mb-5 border border-solid border-surface-5 !shadow-none !drop-shadow-none"
|
||||
@change="setEditTab"
|
||||
/>
|
||||
<div class="flex w-full flex-col gap-6">
|
||||
<div class="flex flex-col gap-2">
|
||||
@@ -18,6 +17,7 @@
|
||||
:never-empty="true"
|
||||
:capitalize="true"
|
||||
:disabled="isUploading"
|
||||
hide-checkmark-icon
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
@@ -46,7 +46,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="font-semibold text-contrast"> Version changlog </span>
|
||||
<span class="font-semibold text-contrast"> Version changelog </span>
|
||||
|
||||
<div class="w-full">
|
||||
<MarkdownEditor
|
||||
@@ -61,25 +61,21 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Chips, MarkdownEditor, NavTabs, StyledInput } from '@modrinth/ui'
|
||||
import { Chips, MarkdownEditor, StyledInput, Tabs, type TabsTab } from '@modrinth/ui'
|
||||
|
||||
import { useImageUpload } from '~/composables/image-upload.ts'
|
||||
import { injectManageVersionContext } from '~/providers/version/manage-version-modal'
|
||||
|
||||
const { draftVersion, isUploading, editingVersion, modal } = injectManageVersionContext()
|
||||
|
||||
const editTabs = [
|
||||
{ label: 'Metadata', href: 'metadata', stage: 'metadata' },
|
||||
{ label: 'Details', href: 'details', stage: 'add-details' },
|
||||
{ label: 'Files', href: 'files', stage: 'add-files' },
|
||||
] as const
|
||||
const editTabs: TabsTab[] = [
|
||||
{ label: 'Metadata', value: 'metadata' },
|
||||
{ label: 'Details', value: 'add-details' },
|
||||
{ label: 'Files', value: 'add-files' },
|
||||
]
|
||||
|
||||
const editTabLinks = editTabs.map(({ label, href }) => ({ label, href }))
|
||||
|
||||
function setEditTab(index: number) {
|
||||
const tab = editTabs[index]
|
||||
if (!tab) return
|
||||
modal.value?.setStage(tab.stage)
|
||||
function setEditTab(tab: TabsTab) {
|
||||
modal.value?.setStage(tab.value)
|
||||
}
|
||||
|
||||
async function onImageUpload(file: File) {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<template>
|
||||
<NavTabs
|
||||
<Tabs
|
||||
v-if="editingVersion"
|
||||
mode="local"
|
||||
:links="editTabLinks"
|
||||
:active-index="0"
|
||||
class="mb-2 border border-solid border-surface-5 shadow-none drop-shadow-none"
|
||||
@tab-click="setEditTab"
|
||||
value="metadata"
|
||||
:tabs="editTabs"
|
||||
class="mb-3 border border-solid border-surface-5 !shadow-none !drop-shadow-none"
|
||||
@change="setEditTab"
|
||||
/>
|
||||
<div class="flex flex-col gap-6">
|
||||
<div v-if="!editingVersion" class="flex flex-col gap-1">
|
||||
@@ -161,40 +160,32 @@
|
||||
</template>
|
||||
|
||||
<template v-if="!noDependenciesProject">
|
||||
<div v-if="visibleSuggestedDependencies.length" class="flex flex-col gap-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="font-semibold text-contrast"> Suggested dependencies </span>
|
||||
<div class="flex flex-col gap-2.5">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="font-semibold text-contrast"> Dependencies </span>
|
||||
|
||||
<ButtonStyled type="transparent" size="standard">
|
||||
<button @click="editDependencies">
|
||||
<EditIcon />
|
||||
Edit
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<SuggestedDependencies @on-add-suggestion="handleAddSuggestedDependency" />
|
||||
</div>
|
||||
<ButtonStyled type="transparent" size="standard">
|
||||
<button @click="addDependency">
|
||||
<PlusIcon />
|
||||
Add dependency
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!visibleSuggestedDependencies.length || draftVersion.dependencies?.length"
|
||||
class="flex flex-col gap-1"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="font-semibold text-contrast"> Dependencies </span>
|
||||
|
||||
<ButtonStyled type="transparent" size="standard">
|
||||
<button @click="editDependencies">
|
||||
<EditIcon />
|
||||
Edit
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<div v-if="draftVersion.dependencies?.length" class="flex flex-col gap-4">
|
||||
<DependenciesList />
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-1.5 gap-y-4 rounded-xl bg-surface-2 p-3 py-4">
|
||||
<span class="text-sm font-medium">No dependencies added.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="draftVersion.dependencies?.length" class="flex flex-col gap-4">
|
||||
<DependenciesList disable-remove />
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-1.5 gap-y-4 rounded-xl bg-surface-2 p-3 py-4">
|
||||
<span class="text-sm font-medium">No dependencies added.</span>
|
||||
<div v-if="visibleSuggestedDependencies.length" class="flex flex-col gap-2.5">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="font-medium"> Suggested </span>
|
||||
</div>
|
||||
<SuggestedDependencies @on-add-suggestion="handleAddSuggestedDependency" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -203,14 +194,15 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { EditIcon, getLoaderIcon, UnknownIcon } from '@modrinth/assets'
|
||||
import { EditIcon, getLoaderIcon, PlusIcon, UnknownIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
ENVIRONMENTS_COPY,
|
||||
FormattedTag,
|
||||
injectProjectPageContext,
|
||||
NavTabs,
|
||||
Tabs,
|
||||
type TabsTab,
|
||||
TagItem,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
@@ -239,18 +231,14 @@ const { projectV2 } = injectProjectPageContext()
|
||||
const generatedState = useGeneratedState()
|
||||
const loaders = computed(() => generatedState.value.loaders)
|
||||
|
||||
const editTabs = [
|
||||
{ label: 'Metadata', href: 'metadata', stage: 'metadata' },
|
||||
{ label: 'Details', href: 'details', stage: 'add-details' },
|
||||
{ label: 'Files', href: 'files', stage: 'add-files' },
|
||||
] as const
|
||||
const editTabs: TabsTab[] = [
|
||||
{ label: 'Metadata', value: 'metadata' },
|
||||
{ label: 'Details', value: 'add-details' },
|
||||
{ label: 'Files', value: 'add-files' },
|
||||
]
|
||||
|
||||
const editTabLinks = editTabs.map(({ label, href }) => ({ label, href }))
|
||||
|
||||
function setEditTab(index: number) {
|
||||
const tab = editTabs[index]
|
||||
if (!tab) return
|
||||
modal.value?.setStage(tab.stage)
|
||||
function setEditTab(tab: TabsTab) {
|
||||
modal.value?.setStage(tab.value)
|
||||
}
|
||||
|
||||
const isModpack = computed(() => projectType.value === 'modpack')
|
||||
@@ -279,7 +267,7 @@ const editEnvironment = () => {
|
||||
const editFiles = () => {
|
||||
modal.value?.setStage('from-details-files')
|
||||
}
|
||||
const editDependencies = () => {
|
||||
const addDependency = () => {
|
||||
modal.value?.setStage('from-details-dependencies')
|
||||
}
|
||||
|
||||
|
||||
@@ -131,10 +131,9 @@
|
||||
class="h-10"
|
||||
/>
|
||||
|
||||
<StyledInput
|
||||
<DatePicker
|
||||
v-else-if="field.type === 'date'"
|
||||
v-model="formData[field.name]"
|
||||
type="date"
|
||||
wrapper-class="w-full"
|
||||
/>
|
||||
|
||||
@@ -230,6 +229,7 @@ import {
|
||||
Admonition,
|
||||
Checkbox,
|
||||
Combobox,
|
||||
DatePicker,
|
||||
defineMessages,
|
||||
financialMessages,
|
||||
formFieldLabels,
|
||||
|
||||
@@ -95,11 +95,11 @@
|
||||
<span class="text-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<DatePicker
|
||||
v-model="formData.dateOfBirth"
|
||||
type="date"
|
||||
:max="maxDate"
|
||||
:max-date="maxDate"
|
||||
autocomplete="bday"
|
||||
placeholder="Select date of birth"
|
||||
wrapper-class="w-full"
|
||||
/>
|
||||
</div>
|
||||
@@ -213,6 +213,7 @@
|
||||
import {
|
||||
Chips,
|
||||
Combobox,
|
||||
DatePicker,
|
||||
defineMessages,
|
||||
formFieldLabels,
|
||||
formFieldPlaceholders,
|
||||
|
||||
@@ -233,7 +233,7 @@ const visibleNags = computed<Nag[]>(() => {
|
||||
link: {
|
||||
path: 'moderation',
|
||||
title: messages.visitModerationPage,
|
||||
shouldShow: () => props.routeName !== 'type-id-moderation',
|
||||
shouldShow: () => props.routeName !== 'type-project-moderation',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="shadow-card rounded-2xl border border-solid border-surface-5 bg-surface-3 p-4">
|
||||
<div class="shadow-card rounded-2xl border border-solid border-surface-4 bg-surface-3 p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<NuxtLink
|
||||
@@ -107,8 +107,8 @@
|
||||
<LinkIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-tooltip="'Begin review'" circular color="orange">
|
||||
<button @click="openProjectForReview">
|
||||
<ButtonStyled circular color="orange">
|
||||
<button v-tooltip="'Begin review'" @click="openProjectForReview">
|
||||
<ScaleIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
|
||||
@@ -143,7 +143,7 @@
|
||||
:expand-text="expandText"
|
||||
collapse-text="Collapse thread"
|
||||
>
|
||||
<div class="bg-surface-2 p-4 pt-2">
|
||||
<div class="bg-surface-2 pt-2">
|
||||
<ThreadView
|
||||
v-if="threadWithReportBody"
|
||||
ref="reportThread"
|
||||
|
||||
@@ -1075,6 +1075,7 @@ async function handleSubmitReview(verdict: 'safe' | 'unsafe') {
|
||||
mode="local"
|
||||
:links="navTabsLinks"
|
||||
:active-index="activeTabIndex"
|
||||
class="bg-surface-3! shadow-none!"
|
||||
@tab-click="handleTabClick"
|
||||
/>
|
||||
</div>
|
||||
@@ -1087,7 +1088,7 @@ async function handleSubmitReview(verdict: 'safe' | 'unsafe') {
|
||||
collapse-text="Collapse thread"
|
||||
class="border-x border-b border-solid border-surface-3"
|
||||
>
|
||||
<div class="bg-surface-2 p-4 pt-0">
|
||||
<div class="bg-surface-2 pt-0">
|
||||
<!-- DEV-531 -->
|
||||
<!-- @vue-expect-error TODO: will convert ThreadView to use api-client types at a later date -->
|
||||
<ThreadView
|
||||
|
||||
@@ -358,26 +358,44 @@
|
||||
|
||||
<div v-else-if="generatedMessage" class="flex items-center gap-2">
|
||||
<ButtonStyled>
|
||||
<button @click="goBackToStages">
|
||||
<button :disabled="loadingModerationDecision" @click="goBackToStages">
|
||||
<LeftArrowIcon aria-hidden="true" />
|
||||
Edit
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="sendMessage('rejected')">
|
||||
<XIcon aria-hidden="true" />
|
||||
<button :disabled="loadingModerationDecision" @click="sendMessage('rejected')">
|
||||
<SpinnerIcon
|
||||
v-if="moderationDecision === 'rejected'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<XIcon v-else aria-hidden="true" />
|
||||
Reject
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="sendMessage('withheld')">
|
||||
<EyeOffIcon aria-hidden="true" />
|
||||
<button :disabled="loadingModerationDecision" @click="sendMessage('withheld')">
|
||||
<SpinnerIcon
|
||||
v-if="moderationDecision === 'withheld'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<LinkIcon v-else aria-hidden="true" />
|
||||
Withhold
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="green">
|
||||
<button @click="sendMessage(projectV2.requested_status ?? 'approved')">
|
||||
<CheckIcon aria-hidden="true" />
|
||||
<button
|
||||
:disabled="loadingModerationDecision"
|
||||
@click="sendMessage(approveSendStatus)"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="moderationDecision === approveSendStatus"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckIcon v-else aria-hidden="true" />
|
||||
Approve
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
@@ -428,14 +446,15 @@ import {
|
||||
BrushCleaningIcon,
|
||||
CheckIcon,
|
||||
DropdownIcon,
|
||||
EyeOffIcon,
|
||||
FileTextIcon,
|
||||
KeyboardIcon,
|
||||
LeftArrowIcon,
|
||||
LinkIcon,
|
||||
ListBulletedIcon,
|
||||
LockIcon,
|
||||
RightArrowIcon,
|
||||
ScaleIcon,
|
||||
SpinnerIcon,
|
||||
ToggleLeftIcon,
|
||||
ToggleRightIcon,
|
||||
XIcon,
|
||||
@@ -720,8 +739,8 @@ async function navigateToNextUnlockedProject(): Promise<boolean> {
|
||||
} else {
|
||||
// Fallback: use project ID (will trigger middleware redirect)
|
||||
navigateTo({
|
||||
name: 'type-id',
|
||||
params: { type: 'project', id: next.projectId },
|
||||
name: 'type-project',
|
||||
params: { type: 'project', project: next.projectId },
|
||||
state: { showChecklist: true },
|
||||
})
|
||||
}
|
||||
@@ -760,6 +779,12 @@ const message = ref(
|
||||
)
|
||||
const generatedMessage = ref(persistedGeneratedMessage.generated === true)
|
||||
const loadingMessage = ref(false)
|
||||
const moderationDecision = ref<ProjectStatus | null>(null)
|
||||
const loadingModerationDecision = computed(() => moderationDecision.value !== null)
|
||||
const approveSendStatus = computed<ProjectStatus>(() => {
|
||||
const requested = projectV2.value.requested_status
|
||||
return requested ?? 'approved'
|
||||
})
|
||||
const done = ref(false)
|
||||
|
||||
function persistGeneratedMessageState() {
|
||||
@@ -1042,8 +1067,8 @@ async function skipToNextProject() {
|
||||
} else {
|
||||
// Fallback: use project ID
|
||||
navigateTo({
|
||||
name: 'type-id',
|
||||
params: { type: 'project', id },
|
||||
name: 'type-project',
|
||||
params: { type: 'project', project: id },
|
||||
state: { showChecklist: true },
|
||||
})
|
||||
}
|
||||
@@ -1074,6 +1099,7 @@ function resetProgress() {
|
||||
done.value = false
|
||||
clearGeneratedMessageState()
|
||||
loadingMessage.value = false
|
||||
moderationDecision.value = null
|
||||
|
||||
localStorage.removeItem(`modpack-permissions-${projectV2.value.id}`)
|
||||
localStorage.removeItem(`modpack-permissions-index-${projectV2.value.id}`)
|
||||
@@ -1190,7 +1216,7 @@ function handleKeybinds(event: KeyboardEvent) {
|
||||
tryResetProgress: resetProgress,
|
||||
tryExitModeration: handleExit,
|
||||
|
||||
tryApprove: () => sendMessage(projectV2.value.requested_status ?? 'approved'),
|
||||
tryApprove: () => sendMessage(approveSendStatus.value),
|
||||
tryReject: () => sendMessage('rejected'),
|
||||
tryWithhold: () => sendMessage('withheld'),
|
||||
tryEditMessage: goBackToStages,
|
||||
@@ -1977,6 +2003,7 @@ async function sendMessage(status: ProjectStatus) {
|
||||
return
|
||||
}
|
||||
|
||||
moderationDecision.value = status
|
||||
try {
|
||||
await useBaseFetch(`project/${projectId}`, {
|
||||
method: 'PATCH',
|
||||
@@ -2026,6 +2053,8 @@ async function sendMessage(status: ProjectStatus) {
|
||||
text: 'Failed to submit moderation decision. Please try again.',
|
||||
type: 'error',
|
||||
})
|
||||
} finally {
|
||||
moderationDecision.value = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2098,8 +2127,8 @@ async function endChecklist(status?: string) {
|
||||
} else {
|
||||
// Fallback: use project ID
|
||||
navigateTo({
|
||||
name: 'type-id',
|
||||
params: { type: 'project', id },
|
||||
name: 'type-project',
|
||||
params: { type: 'project', project: id },
|
||||
state: { showChecklist: true },
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<section class="universal-card">
|
||||
<section>
|
||||
<Breadcrumbs
|
||||
v-if="breadcrumbsStack"
|
||||
:current-title="`Report ${reportId}`"
|
||||
:link-stack="breadcrumbsStack"
|
||||
/>
|
||||
<h2>Report details</h2>
|
||||
<ReportInfo :report="report" :show-thread="false" :show-message="false" :auth="auth" />
|
||||
<ReportInfo
|
||||
:report="report"
|
||||
:show-thread="false"
|
||||
:show-message="false"
|
||||
:auth="auth"
|
||||
class="card-shadow mb-4 rounded-2xl border border-solid border-surface-4 bg-surface-2 p-4"
|
||||
/>
|
||||
</section>
|
||||
<section v-if="report && thread" class="universal-card">
|
||||
<h2>Messages</h2>
|
||||
<section
|
||||
v-if="report && thread"
|
||||
class="card-shadow rounded-2xl border border-solid border-surface-4 bg-surface-3"
|
||||
>
|
||||
<h2 class="m-4 mb-2 text-xl font-semibold text-contrast">Messages with the moderators</h2>
|
||||
<p class="mx-4 mt-0">
|
||||
Make sure to include evidence of all claims you make, or your report may be closed without
|
||||
action.
|
||||
</p>
|
||||
<ConversationThread
|
||||
class="overflow-clip rounded-b-2xl border-0 border-t border-solid border-surface-4 bg-surface-2"
|
||||
:thread="thread"
|
||||
:report="report"
|
||||
:auth="auth"
|
||||
|
||||
@@ -1,28 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<Modal
|
||||
<NewModal
|
||||
ref="modalSubmit"
|
||||
:header="isRejected(project) ? 'Resubmit for review' : 'Submit for review'"
|
||||
:header="
|
||||
formatMessage(
|
||||
isRejected(project)
|
||||
? messages.resubmitModalHeaderResubmitting
|
||||
: messages.resubmitModalHeaderSubmitting,
|
||||
)
|
||||
"
|
||||
>
|
||||
<div class="modal-submit universal-body">
|
||||
<span>
|
||||
You're submitting <span class="project-title">{{ project.title }}</span> to be reviewed
|
||||
again by the moderators.
|
||||
</span>
|
||||
<span>
|
||||
Make sure you have addressed the comments from the moderation team.
|
||||
<span class="known-errors">
|
||||
Repeated submissions without addressing the moderators' comments may result in an
|
||||
account suspension.
|
||||
</span>
|
||||
</span>
|
||||
<div class="flex max-w-[35rem] flex-col gap-3">
|
||||
<p class="m-0">
|
||||
<IntlFormatted
|
||||
:message-id="messages.resubmitModalDescription"
|
||||
:message-values="{ projectTitle: project.title }"
|
||||
>
|
||||
<template #project-title="{ children }">
|
||||
<span class="font-semibold text-contrast">
|
||||
<component :is="() => children" />
|
||||
</span>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
</p>
|
||||
<p class="m-0">{{ formatMessage(messages.resubmitModalReminder) }}</p>
|
||||
<p class="m-0 font-semibold text-red">
|
||||
{{ formatMessage(messages.resubmitModalWarning) }}
|
||||
</p>
|
||||
<Checkbox
|
||||
v-model="submissionConfirmation"
|
||||
description="Confirm I have addressed the messages from the moderators"
|
||||
:description="formatMessage(messages.resubmitModalConfirmationDescription)"
|
||||
>
|
||||
I confirm that I have properly addressed the moderators' comments.
|
||||
{{ formatMessage(messages.resubmitModalConfirmationLabel) }}
|
||||
</Checkbox>
|
||||
<div class="input-group push-right">
|
||||
<div class="flex flex-wrap items-center justify-end gap-2">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="modalSubmit.hide()">
|
||||
<XIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="orange">
|
||||
<button
|
||||
:disabled="!submissionConfirmation || isLoading"
|
||||
@@ -34,33 +51,37 @@
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ScaleIcon v-else aria-hidden="true" />
|
||||
Resubmit for review
|
||||
{{ formatMessage(messages.actionResubmitForReview) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal ref="modalReply" header="Reply to thread">
|
||||
<div class="modal-submit universal-body">
|
||||
<span>
|
||||
Your project is already approved. As such, the moderation team does not actively monitor
|
||||
this thread. However, they may still see your message if there is a problem with your
|
||||
project.
|
||||
</span>
|
||||
<span>
|
||||
If you need to get in contact with the moderation team, please use the
|
||||
<a class="text-link" href="https://support.modrinth.com" target="_blank">
|
||||
Modrinth Help Center
|
||||
</a>
|
||||
and click the green bubble to contact support.
|
||||
</span>
|
||||
</NewModal>
|
||||
<NewModal ref="modalReply" :header="formatMessage(messages.replyModalHeader)">
|
||||
<div class="flex max-w-[45rem] flex-col gap-3">
|
||||
<p class="m-0">{{ formatMessage(messages.replyModalDescription) }}</p>
|
||||
<p class="m-0">
|
||||
<IntlFormatted :message-id="messages.replyModalHelpCenterNote">
|
||||
<template #help-center-link="{ children }">
|
||||
<a class="text-link" href="https://support.modrinth.com" target="_blank">
|
||||
<component :is="() => children" />
|
||||
</a>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
</p>
|
||||
<Checkbox
|
||||
v-model="replyConfirmation"
|
||||
description="Confirm moderators do not actively monitor this"
|
||||
:description="formatMessage(messages.replyModalConfirmationDescription)"
|
||||
>
|
||||
I acknowledge that the moderators do not actively monitor the thread.
|
||||
{{ formatMessage(messages.replyModalConfirmationLabel) }}
|
||||
</Checkbox>
|
||||
<div class="input-group push-right">
|
||||
<div class="flex flex-wrap items-center justify-end gap-2">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="modalReply.hide()">
|
||||
<XIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
:disabled="!replyConfirmation || isLoading"
|
||||
@@ -72,289 +93,318 @@
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ReplyIcon v-else aria-hidden="true" />
|
||||
Reply to thread
|
||||
{{ formatMessage(messages.actionReplyToThread) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<div v-if="flags.developerMode" class="thread-id">
|
||||
</NewModal>
|
||||
<div v-if="flags.developerMode" class="mx-4 mb-3 font-semibold">
|
||||
Thread ID:
|
||||
<CopyCode :text="thread.id" />
|
||||
</div>
|
||||
<div v-if="sortedMessages.length > 0" class="messages universal-card recessed">
|
||||
<ThreadMessage
|
||||
v-for="message in sortedMessages"
|
||||
:key="'message-' + message.id"
|
||||
:thread="thread"
|
||||
:message="message"
|
||||
:members="members"
|
||||
:report="report"
|
||||
:auth="auth"
|
||||
raised
|
||||
@update-thread="() => updateThreadLocal()"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="report && report.closed">
|
||||
<p>This thread is closed and new messages cannot be sent to it.</p>
|
||||
<ButtonStyled v-if="isStaff(auth.user)">
|
||||
<button :disabled="isLoading" @click="runBlockingAction('reopen', () => reopenReport())">
|
||||
<SpinnerIcon v-if="loadingAction === 'reopen'" class="animate-spin" aria-hidden="true" />
|
||||
<CheckCircleIcon v-else aria-hidden="true" />
|
||||
Reopen thread
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
<template v-else-if="!report || !report.closed">
|
||||
<div class="markdown-editor-spacing">
|
||||
<MarkdownEditor
|
||||
v-model="replyBody"
|
||||
:placeholder="sortedMessages.length > 0 ? 'Reply to thread...' : 'Send a message...'"
|
||||
:on-image-upload="onUploadImage"
|
||||
<div v-bind="$attrs" class="flex flex-col">
|
||||
<div v-if="sortedMessages.length > 0" class="flex flex-col pt-2">
|
||||
<ThreadMessage
|
||||
v-for="message in sortedMessages"
|
||||
:key="'message-' + message.id"
|
||||
:thread="thread"
|
||||
:message="message"
|
||||
:members="members"
|
||||
:report="report"
|
||||
:auth="auth"
|
||||
raised
|
||||
@update-thread="() => updateThreadLocal()"
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
v-if="sortedMessages.length > 0"
|
||||
:disabled="!replyBody || isLoading"
|
||||
@click="
|
||||
isApproved(project) && !isStaff(auth.user)
|
||||
? openReplyModal()
|
||||
: runBlockingAction('reply', () => sendReply())
|
||||
"
|
||||
>
|
||||
<SpinnerIcon v-if="loadingAction === 'reply'" class="animate-spin" aria-hidden="true" />
|
||||
<ReplyIcon v-else aria-hidden="true" />
|
||||
Reply
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
:disabled="!replyBody || isLoading"
|
||||
@click="
|
||||
isApproved(project) && !isStaff(auth.user)
|
||||
? openReplyModal()
|
||||
: runBlockingAction('send', () => sendReply())
|
||||
"
|
||||
>
|
||||
<SpinnerIcon v-if="loadingAction === 'send'" class="animate-spin" aria-hidden="true" />
|
||||
<SendIcon v-else aria-hidden="true" />
|
||||
Send
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<template v-if="report && report.closed">
|
||||
<p>{{ formatMessage(messages.closedThreadDescription) }}</p>
|
||||
<ButtonStyled v-if="isStaff(auth.user)">
|
||||
<button
|
||||
:disabled="!replyBody || isLoading"
|
||||
@click="runBlockingAction('private-note', () => sendReply(null, true))"
|
||||
>
|
||||
<button :disabled="isLoading" @click="runBlockingAction('reopen', () => reopenReport())">
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'private-note'"
|
||||
v-if="loadingAction === 'reopen'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ScaleIcon v-else aria-hidden="true" />
|
||||
Add private note
|
||||
<CheckCircleIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionReopenThread) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<template v-if="currentMember && !isStaff(auth.user)">
|
||||
<template v-if="isRejected(project)">
|
||||
<ButtonStyled color="orange">
|
||||
<button v-if="replyBody" :disabled="isLoading" @click="openResubmitModal(true)">
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
Resubmit for review with reply
|
||||
</template>
|
||||
<template v-else-if="!report || !report.closed">
|
||||
<div class="mx-4 mb-2 mt-2">
|
||||
<MarkdownEditor
|
||||
v-model="replyBody"
|
||||
:placeholder="
|
||||
formatMessage(
|
||||
sortedMessages.length > 0
|
||||
? messages.replyEditorPlaceholderReply
|
||||
: messages.replyEditorPlaceholderSend,
|
||||
)
|
||||
"
|
||||
:on-image-upload="onUploadImage"
|
||||
/>
|
||||
</div>
|
||||
<div class="m-4 mt-3 flex flex-wrap items-center justify-between gap-4">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
v-if="sortedMessages.length > 0"
|
||||
:disabled="!replyBody || isLoading"
|
||||
@click="
|
||||
isApproved(project)
|
||||
? openReplyModal()
|
||||
: runBlockingAction('reply', () => sendReply())
|
||||
"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'reply'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ReplyIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionReply) }}
|
||||
</button>
|
||||
<button v-else :disabled="isLoading" @click="openResubmitModal(false)">
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
Resubmit for review
|
||||
<button
|
||||
v-else
|
||||
:disabled="!replyBody || isLoading"
|
||||
@click="
|
||||
isApproved(project)
|
||||
? openReplyModal()
|
||||
: runBlockingAction('send', () => sendReply())
|
||||
"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'send'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<SendIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionSend) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</template>
|
||||
<div class="spacer"></div>
|
||||
<div class="input-group extra-options">
|
||||
<template v-if="report">
|
||||
<template v-if="isStaff(auth.user)">
|
||||
<ButtonStyled color="red">
|
||||
<button
|
||||
v-if="replyBody"
|
||||
:disabled="isLoading"
|
||||
@click="runBlockingAction('close-with-reply', () => closeReport(true))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'close-with-reply'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckCircleIcon v-else aria-hidden="true" />
|
||||
Close with reply
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
:disabled="isLoading"
|
||||
@click="runBlockingAction('close', () => closeReport())"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'close'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckCircleIcon v-else aria-hidden="true" />
|
||||
Close thread
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-if="isStaff(auth.user)">
|
||||
<button
|
||||
:disabled="!replyBody || isLoading"
|
||||
@click="runBlockingAction('private-note', () => sendReply(null, true))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'private-note'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ScaleIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionAddPrivateNote) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<template v-if="currentMember && !currentMember.staffOnly">
|
||||
<template v-if="isRejected(project)">
|
||||
<ButtonStyled color="orange">
|
||||
<button v-if="replyBody" :disabled="isLoading" @click="openResubmitModal(true)">
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionResubmitForReviewWithReply) }}
|
||||
</button>
|
||||
<button v-else :disabled="isLoading" @click="openResubmitModal(false)">
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionResubmitForReview) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="project">
|
||||
<template v-if="isStaff(auth.user)">
|
||||
<ButtonStyled v-if="replyBody" color="green">
|
||||
<button
|
||||
:disabled="isApproved(project) || isLoading"
|
||||
@click="runBlockingAction('approve-with-reply', () => sendReply(requestedStatus))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'approve-with-reply'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckIcon v-else aria-hidden="true" />
|
||||
Approve with reply
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else color="green">
|
||||
<button
|
||||
:disabled="isApproved(project) || isLoading"
|
||||
@click="runBlockingAction('approve', () => setStatus(requestedStatus))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'approve'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckIcon v-else aria-hidden="true" />
|
||||
Approve
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<div class="joined-buttons">
|
||||
<ButtonStyled v-if="replyBody" color="red">
|
||||
<button
|
||||
:disabled="project.status === 'rejected' || isLoading"
|
||||
@click="runBlockingAction('reject-with-reply', () => sendReply('rejected'))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'reject-with-reply'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<XIcon v-else aria-hidden="true" />
|
||||
Reject with reply
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else color="red">
|
||||
<button
|
||||
:disabled="project.status === 'rejected' || isLoading"
|
||||
@click="runBlockingAction('reject', () => setStatus('rejected'))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'reject'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<XIcon v-else aria-hidden="true" />
|
||||
Reject
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<template v-if="report">
|
||||
<template v-if="isStaff(auth.user)">
|
||||
<ButtonStyled color="red">
|
||||
<OverflowMenu
|
||||
class="btn-dropdown-animation"
|
||||
<button
|
||||
v-if="replyBody"
|
||||
:disabled="isLoading"
|
||||
:options="
|
||||
replyBody
|
||||
? [
|
||||
{
|
||||
id: 'withhold-reply',
|
||||
color: 'danger',
|
||||
action: () =>
|
||||
runBlockingAction('withhold-reply', () => sendReply('withheld')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'withheld' || isLoading,
|
||||
},
|
||||
{
|
||||
id: 'set-to-draft-reply',
|
||||
action: () =>
|
||||
runBlockingAction('set-to-draft-reply', () => sendReply('draft')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'draft' || isLoading,
|
||||
},
|
||||
{
|
||||
id: 'send-to-review-reply',
|
||||
action: () =>
|
||||
runBlockingAction('send-to-review-reply', () =>
|
||||
sendReply('processing', true),
|
||||
),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'processing' || isLoading,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
id: 'withhold',
|
||||
color: 'danger',
|
||||
action: () =>
|
||||
runBlockingAction('withhold', () => setStatus('withheld')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'withheld' || isLoading,
|
||||
},
|
||||
{
|
||||
id: 'set-to-draft',
|
||||
action: () =>
|
||||
runBlockingAction('set-to-draft', () => setStatus('draft')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'draft' || isLoading,
|
||||
},
|
||||
{
|
||||
id: 'send-to-review',
|
||||
action: () =>
|
||||
runBlockingAction('send-to-review', () => setStatus('processing')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'processing' || isLoading,
|
||||
},
|
||||
]
|
||||
@click="runBlockingAction('close-with-reply', () => closeReport(true))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'close-with-reply'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckCircleIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionCloseWithReply) }}
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
:disabled="isLoading"
|
||||
@click="runBlockingAction('close', () => closeReport())"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'close'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckCircleIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionCloseThread) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="project">
|
||||
<template v-if="isStaff(auth.user)">
|
||||
<ButtonStyled v-if="replyBody" color="green">
|
||||
<button
|
||||
:disabled="isApproved(project) || isLoading"
|
||||
@click="
|
||||
runBlockingAction('approve-with-reply', () => sendReply(requestedStatus))
|
||||
"
|
||||
>
|
||||
<SpinnerIcon v-if="isDropdownLoading" class="animate-spin" aria-hidden="true" />
|
||||
<DropdownIcon v-else aria-hidden="true" />
|
||||
<template #withhold-reply>
|
||||
<EyeOffIcon aria-hidden="true" />
|
||||
Withhold with reply
|
||||
</template>
|
||||
<template #withhold>
|
||||
<EyeOffIcon aria-hidden="true" />
|
||||
Withhold
|
||||
</template>
|
||||
<template #set-to-draft-reply>
|
||||
<FileTextIcon aria-hidden="true" />
|
||||
Set to draft with reply
|
||||
</template>
|
||||
<template #set-to-draft>
|
||||
<FileTextIcon aria-hidden="true" />
|
||||
Set to draft
|
||||
</template>
|
||||
<template #send-to-review-reply>
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
Send to review with reply
|
||||
</template>
|
||||
<template #send-to-review>
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
Send to review
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'approve-with-reply'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionApproveWithReply) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<ButtonStyled v-else color="green">
|
||||
<button
|
||||
:disabled="isApproved(project) || isLoading"
|
||||
@click="runBlockingAction('approve', () => setStatus(requestedStatus))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'approve'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<CheckIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionApprove) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<div class="joined-buttons">
|
||||
<ButtonStyled v-if="replyBody" color="red">
|
||||
<button
|
||||
:disabled="project.status === 'rejected' || isLoading"
|
||||
@click="runBlockingAction('reject-with-reply', () => sendReply('rejected'))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'reject-with-reply'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<XIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionRejectWithReply) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else color="red">
|
||||
<button
|
||||
:disabled="project.status === 'rejected' || isLoading"
|
||||
@click="runBlockingAction('reject', () => setStatus('rejected'))"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="loadingAction === 'reject'"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<XIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionReject) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<OverflowMenu
|
||||
class="btn-dropdown-animation"
|
||||
:disabled="isLoading"
|
||||
:options="
|
||||
replyBody
|
||||
? [
|
||||
{
|
||||
id: 'withhold-reply',
|
||||
color: 'danger',
|
||||
action: () =>
|
||||
runBlockingAction('withhold-reply', () => sendReply('withheld')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'withheld' || isLoading,
|
||||
},
|
||||
{
|
||||
id: 'set-to-draft-reply',
|
||||
action: () =>
|
||||
runBlockingAction('set-to-draft-reply', () => sendReply('draft')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'draft' || isLoading,
|
||||
},
|
||||
{
|
||||
id: 'send-to-review-reply',
|
||||
action: () =>
|
||||
runBlockingAction('send-to-review-reply', () =>
|
||||
sendReply('processing', true),
|
||||
),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'processing' || isLoading,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
id: 'withhold',
|
||||
color: 'danger',
|
||||
action: () =>
|
||||
runBlockingAction('withhold', () => setStatus('withheld')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'withheld' || isLoading,
|
||||
},
|
||||
{
|
||||
id: 'set-to-draft',
|
||||
action: () =>
|
||||
runBlockingAction('set-to-draft', () => setStatus('draft')),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'draft' || isLoading,
|
||||
},
|
||||
{
|
||||
id: 'send-to-review',
|
||||
action: () =>
|
||||
runBlockingAction('send-to-review', () =>
|
||||
setStatus('processing'),
|
||||
),
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'processing' || isLoading,
|
||||
},
|
||||
]
|
||||
"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="isDropdownLoading"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<DropdownIcon v-else aria-hidden="true" />
|
||||
<template #withhold-reply>
|
||||
<EyeOffIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionWithholdWithReply) }}
|
||||
</template>
|
||||
<template #withhold>
|
||||
<EyeOffIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionWithhold) }}
|
||||
</template>
|
||||
<template #set-to-draft-reply>
|
||||
<FileTextIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionSetToDraftWithReply) }}
|
||||
</template>
|
||||
<template #set-to-draft>
|
||||
<FileTextIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionSetToDraft) }}
|
||||
</template>
|
||||
<template #send-to-review-reply>
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionSendToReviewWithReply) }}
|
||||
</template>
|
||||
<template #send-to-review>
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.actionSendToReview) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -374,19 +424,179 @@ import {
|
||||
import {
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
commonMessages,
|
||||
CopyCode,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
IntlFormatted,
|
||||
MarkdownEditor,
|
||||
NewModal,
|
||||
OverflowMenu,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
import Modal from '~/components/ui/Modal.vue'
|
||||
import ThreadMessage from '~/components/ui/thread/ThreadMessage.vue'
|
||||
import { useImageUpload } from '~/composables/image-upload.ts'
|
||||
import { isApproved, isRejected } from '~/helpers/projects.js'
|
||||
import { isStaff } from '~/helpers/users.js'
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const messages = defineMessages({
|
||||
resubmitModalHeaderResubmitting: {
|
||||
id: 'conversation-thread.resubmit-modal.header.resubmitting',
|
||||
defaultMessage: 'Resubmitting for review',
|
||||
},
|
||||
resubmitModalHeaderSubmitting: {
|
||||
id: 'conversation-thread.resubmit-modal.header.submitting',
|
||||
defaultMessage: 'Submitting for review',
|
||||
},
|
||||
resubmitModalDescription: {
|
||||
id: 'conversation-thread.resubmit-modal.description',
|
||||
defaultMessage:
|
||||
"You're submitting <project-title>{projectTitle}</project-title> to be reviewed again by the moderators.",
|
||||
},
|
||||
resubmitModalReminder: {
|
||||
id: 'conversation-thread.resubmit-modal.reminder',
|
||||
defaultMessage: 'Make sure you have addressed all the comments from the moderation team.',
|
||||
},
|
||||
resubmitModalWarning: {
|
||||
id: 'conversation-thread.resubmit-modal.warning',
|
||||
defaultMessage:
|
||||
"Repeated submissions without addressing the moderators' comments may result in an account suspension.",
|
||||
},
|
||||
resubmitModalConfirmationDescription: {
|
||||
id: 'conversation-thread.resubmit-modal.confirmation.description',
|
||||
defaultMessage: 'Confirm I have addressed the messages from the moderators',
|
||||
},
|
||||
resubmitModalConfirmationLabel: {
|
||||
id: 'conversation-thread.resubmit-modal.confirmation.label',
|
||||
defaultMessage: "I confirm that I have properly addressed the moderators' comments.",
|
||||
},
|
||||
replyModalHeader: {
|
||||
id: 'conversation-thread.reply-modal.header',
|
||||
defaultMessage: 'Reply to thread',
|
||||
},
|
||||
replyModalDescription: {
|
||||
id: 'conversation-thread.reply-modal.description',
|
||||
defaultMessage:
|
||||
'Your project is already approved. As such, the moderation team does not actively monitor this thread. However, they may still see your message if there is a problem with your project.',
|
||||
},
|
||||
replyModalHelpCenterNote: {
|
||||
id: 'conversation-thread.reply-modal.help-center-note',
|
||||
defaultMessage:
|
||||
'If you need to get in contact with the moderation team, please use the <help-center-link>Modrinth Help Center</help-center-link> and click the blue bubble in the bottom right corner to contact support.',
|
||||
},
|
||||
replyModalConfirmationDescription: {
|
||||
id: 'conversation-thread.reply-modal.confirmation.description',
|
||||
defaultMessage: 'Confirm moderators do not actively monitor this',
|
||||
},
|
||||
replyModalConfirmationLabel: {
|
||||
id: 'conversation-thread.reply-modal.confirmation.label',
|
||||
defaultMessage: 'I acknowledge that the moderators do not actively monitor the thread.',
|
||||
},
|
||||
closedThreadDescription: {
|
||||
id: 'conversation-thread.closed-thread.description',
|
||||
defaultMessage: 'This thread is closed and new messages cannot be sent to it.',
|
||||
},
|
||||
replyEditorPlaceholderReply: {
|
||||
id: 'conversation-thread.reply-editor.placeholder.reply',
|
||||
defaultMessage: 'Reply to thread...',
|
||||
},
|
||||
replyEditorPlaceholderSend: {
|
||||
id: 'conversation-thread.reply-editor.placeholder.send',
|
||||
defaultMessage: 'Send a message...',
|
||||
},
|
||||
actionResubmitForReview: {
|
||||
id: 'conversation-thread.action.resubmit-for-review',
|
||||
defaultMessage: 'Resubmit for review',
|
||||
},
|
||||
actionReplyToThread: {
|
||||
id: 'conversation-thread.action.reply-to-thread',
|
||||
defaultMessage: 'Reply to thread',
|
||||
},
|
||||
actionReopenThread: {
|
||||
id: 'conversation-thread.action.reopen-thread',
|
||||
defaultMessage: 'Reopen thread',
|
||||
},
|
||||
actionReply: {
|
||||
id: 'conversation-thread.action.reply',
|
||||
defaultMessage: 'Reply',
|
||||
},
|
||||
actionSend: {
|
||||
id: 'conversation-thread.action.send',
|
||||
defaultMessage: 'Send',
|
||||
},
|
||||
actionAddPrivateNote: {
|
||||
id: 'conversation-thread.action.add-private-note',
|
||||
defaultMessage: 'Add private note',
|
||||
},
|
||||
actionResubmitForReviewWithReply: {
|
||||
id: 'conversation-thread.action.resubmit-for-review-with-reply',
|
||||
defaultMessage: 'Resubmit for review with reply',
|
||||
},
|
||||
actionCloseWithReply: {
|
||||
id: 'conversation-thread.action.close-with-reply',
|
||||
defaultMessage: 'Close with reply',
|
||||
},
|
||||
actionCloseThread: {
|
||||
id: 'conversation-thread.action.close-thread',
|
||||
defaultMessage: 'Close thread',
|
||||
},
|
||||
actionApproveWithReply: {
|
||||
id: 'conversation-thread.action.approve-with-reply',
|
||||
defaultMessage: 'Approve with reply',
|
||||
},
|
||||
actionApprove: {
|
||||
id: 'conversation-thread.action.approve',
|
||||
defaultMessage: 'Approve',
|
||||
},
|
||||
actionRejectWithReply: {
|
||||
id: 'conversation-thread.action.reject-with-reply',
|
||||
defaultMessage: 'Reject with reply',
|
||||
},
|
||||
actionReject: {
|
||||
id: 'conversation-thread.action.reject',
|
||||
defaultMessage: 'Reject',
|
||||
},
|
||||
actionWithholdWithReply: {
|
||||
id: 'conversation-thread.action.withhold-with-reply',
|
||||
defaultMessage: 'Withhold with reply',
|
||||
},
|
||||
actionWithhold: {
|
||||
id: 'conversation-thread.action.withhold',
|
||||
defaultMessage: 'Withhold',
|
||||
},
|
||||
actionSetToDraftWithReply: {
|
||||
id: 'conversation-thread.action.set-to-draft-with-reply',
|
||||
defaultMessage: 'Set to draft with reply',
|
||||
},
|
||||
actionSetToDraft: {
|
||||
id: 'conversation-thread.action.set-to-draft',
|
||||
defaultMessage: 'Set to draft',
|
||||
},
|
||||
actionSendToReviewWithReply: {
|
||||
id: 'conversation-thread.action.send-to-review-with-reply',
|
||||
defaultMessage: 'Send to review with reply',
|
||||
},
|
||||
actionSendToReview: {
|
||||
id: 'conversation-thread.action.send-to-review',
|
||||
defaultMessage: 'Send to review',
|
||||
},
|
||||
errorSendingMessage: {
|
||||
id: 'conversation-thread.error.sending-message',
|
||||
defaultMessage: 'Error sending message',
|
||||
},
|
||||
errorClosingReport: {
|
||||
id: 'conversation-thread.error.closing-report',
|
||||
defaultMessage: 'Error closing report',
|
||||
},
|
||||
errorReopeningReport: {
|
||||
id: 'conversation-thread.error.reopening-report',
|
||||
defaultMessage: 'Error reopening report',
|
||||
},
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
thread: {
|
||||
@@ -532,7 +742,7 @@ async function sendReply(status = null, privateMessage = false) {
|
||||
}
|
||||
} catch (err) {
|
||||
addNotification({
|
||||
title: 'Error sending message',
|
||||
title: formatMessage(messages.errorSendingMessage),
|
||||
text: err.data ? err.data.description : err,
|
||||
type: 'error',
|
||||
})
|
||||
@@ -554,7 +764,7 @@ async function closeReport(reply) {
|
||||
await updateThreadLocal()
|
||||
} catch (err) {
|
||||
addNotification({
|
||||
title: 'Error closing report',
|
||||
title: formatMessage(messages.errorClosingReport),
|
||||
text: err.data ? err.data.description : err,
|
||||
type: 'error',
|
||||
})
|
||||
@@ -572,7 +782,7 @@ async function reopenReport() {
|
||||
await updateThreadLocal()
|
||||
} catch (err) {
|
||||
addNotification({
|
||||
title: 'Error reopening report',
|
||||
title: formatMessage(messages.errorReopeningReport),
|
||||
text: err.data ? err.data.description : err,
|
||||
type: 'error',
|
||||
})
|
||||
@@ -604,44 +814,8 @@ async function resubmit() {
|
||||
}
|
||||
|
||||
const requestedStatus = computed(() => props.project.requested_status ?? 'approved')
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.markdown-editor-spacing {
|
||||
margin-bottom: var(--gap-md);
|
||||
}
|
||||
|
||||
.messages {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(--spacing-card-md);
|
||||
}
|
||||
|
||||
.thread-id {
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
font-weight: bold;
|
||||
color: var(--color-heading);
|
||||
}
|
||||
|
||||
.input-group {
|
||||
.spacer {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.extra-options {
|
||||
flex-basis: fit-content;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-submit {
|
||||
padding: var(--spacing-card-bg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-card-lg);
|
||||
|
||||
.project-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<template>
|
||||
<div
|
||||
class="message"
|
||||
class="message px-4 py-3"
|
||||
:class="{
|
||||
'has-body': message.body.type === 'text' && !forceCompact,
|
||||
'no-actions': noLinks,
|
||||
private: isPrivateMessage,
|
||||
'show-private-bg': flags.showModeratorPrivateMessageHighlight,
|
||||
}"
|
||||
>
|
||||
<template v-if="members[message.author_id]">
|
||||
@@ -22,11 +23,6 @@
|
||||
/>
|
||||
</AutoLink>
|
||||
<span :class="`message__author role-${members[message.author_id].role}`">
|
||||
<LockIcon
|
||||
v-if="isPrivateMessage"
|
||||
v-tooltip="'Only visible to moderators'"
|
||||
class="private-icon"
|
||||
/>
|
||||
<AutoLink :to="noLinks ? '' : `/user/${members[message.author_id].username}`">
|
||||
{{ members[message.author_id].username }}
|
||||
</AutoLink>
|
||||
@@ -35,6 +31,11 @@
|
||||
v-else-if="members[message.author_id].role === 'admin'"
|
||||
v-tooltip="'Modrinth Team'"
|
||||
/>
|
||||
<EyeOffIcon
|
||||
v-if="isPrivateMessage"
|
||||
v-tooltip="'Only visible to moderators'"
|
||||
class="ml-1 text-orange"
|
||||
/>
|
||||
<MicrophoneIcon
|
||||
v-if="report && message.author_id === report.reporter_user?.id"
|
||||
v-tooltip="'Reporter'"
|
||||
@@ -79,6 +80,12 @@
|
||||
<span v-if="message.body.new_status === 'processing'">
|
||||
submitted the project for review.
|
||||
</span>
|
||||
<span v-else-if="message.body.old_status === 'processing'">
|
||||
reviewed the project and set its status to <Badge :type="message.body.new_status" />.
|
||||
</span>
|
||||
<span v-else-if="message.body.new_status === 'draft'">
|
||||
reverted this project back to a <Badge :type="message.body.new_status" />.
|
||||
</span>
|
||||
<span v-else>
|
||||
changed the project's status from <Badge :type="message.body.old_status" /> to
|
||||
<Badge :type="message.body.new_status" />.
|
||||
@@ -126,7 +133,7 @@
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
LockIcon,
|
||||
EyeOffIcon,
|
||||
MicrophoneIcon,
|
||||
ModrinthIcon,
|
||||
MoreHorizontalIcon,
|
||||
@@ -178,6 +185,7 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update-thread'])
|
||||
const flags = useFeatureFlags()
|
||||
|
||||
const formattedMessage = computed(() => {
|
||||
const body = renderString(props.message.body.body)
|
||||
@@ -222,15 +230,13 @@ async function deleteMessage() {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.message {
|
||||
--gap-size: var(--spacing-card-xs);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--gap-size);
|
||||
gap: var(--spacing-card-sm);
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
border-radius: var(--size-rounded-card);
|
||||
padding: var(--spacing-card-md);
|
||||
word-break: break-word;
|
||||
position: relative;
|
||||
|
||||
.avatar,
|
||||
.backed-svg {
|
||||
@@ -238,14 +244,12 @@ async function deleteMessage() {
|
||||
}
|
||||
|
||||
&.has-body {
|
||||
--gap-size: var(--spacing-card-sm);
|
||||
display: grid;
|
||||
grid-template:
|
||||
'icon author actions'
|
||||
'icon body actions'
|
||||
'date date date';
|
||||
grid-template-columns: min-content auto 1fr;
|
||||
column-gap: var(--gap-size);
|
||||
row-gap: var(--spacing-card-xs);
|
||||
|
||||
.message__icon {
|
||||
@@ -260,13 +264,22 @@ async function deleteMessage() {
|
||||
|
||||
&:not(.no-actions):hover,
|
||||
&:not(.no-actions):focus-within {
|
||||
background-color: var(--color-table-alternate-row);
|
||||
background-color: var(--surface-2-5);
|
||||
|
||||
.message__actions {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.private.show-private-bg::before {
|
||||
content: '';
|
||||
inset: 0;
|
||||
position: absolute;
|
||||
background-color: var(--color-orange);
|
||||
opacity: 0.05;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.no-actions {
|
||||
padding: 0;
|
||||
|
||||
@@ -346,10 +359,6 @@ a:active + .message__author a,
|
||||
color: var(--color-purple);
|
||||
}
|
||||
|
||||
.private-icon {
|
||||
color: var(--color-gray);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px) {
|
||||
.message {
|
||||
//grid-template:
|
||||
@@ -363,6 +372,7 @@ a:active + .message__author a,
|
||||
'icon body actions'
|
||||
'date date date';
|
||||
grid-template-columns: min-content auto 1fr;
|
||||
grid-template-rows: min-content 1fr auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -377,7 +387,7 @@ a:active + .message__author a,
|
||||
'icon author date actions'
|
||||
'icon body body actions';
|
||||
grid-template-columns: min-content auto 1fr;
|
||||
grid-template-rows: min-content 1fr auto;
|
||||
grid-template-rows: min-content 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="flags.developerMode" class="mt-4 font-bold text-heading">
|
||||
<div v-if="flags.developerMode" class="m-4 font-bold text-heading">
|
||||
Thread ID:
|
||||
<CopyCode :text="thread.id" />
|
||||
</div>
|
||||
|
||||
<div v-if="sortedMessages.length > 0" class="flex flex-col space-y-4 rounded-xl p-3 sm:p-4">
|
||||
<div v-if="sortedMessages.length > 0" class="flex flex-col rounded-xl">
|
||||
<ThreadMessage
|
||||
v-for="message in sortedMessages"
|
||||
:key="'message-' + message.id"
|
||||
@@ -28,7 +28,7 @@
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div>
|
||||
<div class="px-4 py-2">
|
||||
<MarkdownEditor
|
||||
v-model="replyBody"
|
||||
:placeholder="sortedMessages.length > 0 ? 'Reply to thread...' : 'Send a message...'"
|
||||
@@ -37,7 +37,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mt-4 flex flex-col items-stretch justify-between gap-3 sm:flex-row sm:items-center sm:gap-2"
|
||||
class="mt-4 flex flex-col items-stretch justify-between gap-3 px-4 pb-4 sm:flex-row sm:items-center sm:gap-2"
|
||||
>
|
||||
<div class="flex flex-col items-stretch gap-2 sm:flex-row sm:items-center">
|
||||
<ButtonStyled v-if="sortedMessages.length > 0" color="brand">
|
||||
|
||||
@@ -49,6 +49,11 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({
|
||||
labrinthApiCanary: false,
|
||||
dismissedExternalProjectsInfo: false,
|
||||
modpackPermissionsPage: false,
|
||||
showAllBanners: false,
|
||||
alwaysIgnoreErrorBanner: false,
|
||||
showViewProdRouteBanner: false,
|
||||
showModeratorProjectMemberUi: false,
|
||||
showModeratorPrivateMessageHighlight: true,
|
||||
} as const)
|
||||
|
||||
export type FeatureFlag = keyof typeof DEFAULT_FEATURE_FLAGS
|
||||
|
||||
@@ -0,0 +1,746 @@
|
||||
import type { Archon, Labrinth } from '@modrinth/api-client'
|
||||
import type {
|
||||
BrowseInstallContentType,
|
||||
BrowseInstallPlan,
|
||||
BrowseSearchState,
|
||||
CreationFlowContextValue,
|
||||
FilterValue,
|
||||
PendingServerContentInstall,
|
||||
PendingServerContentInstallType,
|
||||
} from '@modrinth/ui'
|
||||
import {
|
||||
addPendingServerContentInstalls,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
flushStoredServerAddonInstallQueue,
|
||||
getStoredServerAddonInstallQueue,
|
||||
getTargetInstallPreferences,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
readPendingServerContentInstalls,
|
||||
readStoredServerInstallQueue,
|
||||
removePendingServerContentInstall,
|
||||
requestInstall,
|
||||
useVIntl,
|
||||
writePendingServerContentInstallBaseline,
|
||||
writeStoredServerInstallQueue,
|
||||
} from '@modrinth/ui'
|
||||
import { useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import type { ComputedRef, Ref } from 'vue'
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
|
||||
import { navigateTo, useRoute } from '#app'
|
||||
import { queryAsString } from '~/utils/router'
|
||||
|
||||
type PendingServerContentInstallInput = Omit<PendingServerContentInstall, 'createdAt'>
|
||||
type ServerInstallBrowseSearchState = Pick<
|
||||
BrowseSearchState,
|
||||
'currentFilters' | 'overriddenProvidedFilterTypes'
|
||||
>
|
||||
type ServerInstallProjectType = {
|
||||
id?: string
|
||||
}
|
||||
type ServerInstallDebug = (...args: unknown[]) => void
|
||||
|
||||
export interface ServerInstallModalHandle {
|
||||
show: () => void | Promise<void>
|
||||
hide: () => void
|
||||
ctx?: CreationFlowContextValue | null
|
||||
}
|
||||
|
||||
export interface ServerInstallSearchResult extends Labrinth.Search.v2.ResultSearchProject {
|
||||
installed?: boolean
|
||||
}
|
||||
|
||||
export interface UseServerInstallContentOptions {
|
||||
projectType: ComputedRef<ServerInstallProjectType | undefined>
|
||||
onboardingModalRef: Ref<ServerInstallModalHandle | null>
|
||||
debug?: ServerInstallDebug
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
unsupportedContentType: {
|
||||
id: 'discover.install.error.unsupported-content-type',
|
||||
defaultMessage: 'This content type cannot be installed to a server from browse.',
|
||||
},
|
||||
noServerWorld: {
|
||||
id: 'discover.install.error.no-server-world',
|
||||
defaultMessage: 'No server world is available for install.',
|
||||
},
|
||||
backToSetup: {
|
||||
id: 'discover.install.back-to-setup',
|
||||
defaultMessage: 'Back to setup',
|
||||
},
|
||||
cancelReset: {
|
||||
id: 'discover.install.cancel-reset',
|
||||
defaultMessage: 'Cancel reset',
|
||||
},
|
||||
backToServer: {
|
||||
id: 'discover.install.back-to-server',
|
||||
defaultMessage: 'Back to server',
|
||||
},
|
||||
resetModpackHeading: {
|
||||
id: 'discover.install.heading.reset-modpack',
|
||||
defaultMessage: 'Selecting modpack to install after reset',
|
||||
},
|
||||
})
|
||||
|
||||
function getQueuedInstallOwnerFallback(project: ServerInstallSearchResult) {
|
||||
if (project.organization) {
|
||||
const ownerId = project.organization_id ?? project.organization
|
||||
return {
|
||||
id: ownerId,
|
||||
name: project.organization,
|
||||
type: 'organization' as const,
|
||||
link: `/organization/${ownerId}`,
|
||||
}
|
||||
}
|
||||
|
||||
if (!project.author) return null
|
||||
|
||||
const ownerId = project.author_id ?? project.author
|
||||
return {
|
||||
id: ownerId,
|
||||
name: project.author,
|
||||
type: 'user' as const,
|
||||
link: `/user/${ownerId}`,
|
||||
}
|
||||
}
|
||||
|
||||
function getQueuedAddonInstallPlans(
|
||||
plans: Map<string, BrowseInstallPlan<ServerInstallSearchResult>>,
|
||||
) {
|
||||
return Array.from(plans.values()).filter((plan) => plan.contentType !== 'modpack')
|
||||
}
|
||||
|
||||
export function useServerInstallContent({
|
||||
projectType,
|
||||
onboardingModalRef,
|
||||
debug = () => {},
|
||||
}: UseServerInstallContentOptions) {
|
||||
const { formatMessage } = useVIntl()
|
||||
const client = injectModrinthClient()
|
||||
const queryClient = useQueryClient()
|
||||
const route = useRoute()
|
||||
const { handleError } = injectNotificationManager()
|
||||
let browseSearchState: ServerInstallBrowseSearchState | null = null
|
||||
|
||||
const currentServerId = computed(() => queryAsString(route.query.sid) || null)
|
||||
const fromContext = computed(() => queryAsString(route.query.from) || null)
|
||||
const currentWorldId = computed(() => queryAsString(route.query.wid) || null)
|
||||
|
||||
const {
|
||||
data: serverData,
|
||||
isLoading: serverDataLoading,
|
||||
error: serverDataError,
|
||||
} = useQuery({
|
||||
queryKey: computed(() => ['servers', 'detail', currentServerId.value] as const),
|
||||
queryFn: () => {
|
||||
debug('serverData queryFn firing for:', currentServerId.value)
|
||||
return client.archon.servers_v0.get(currentServerId.value!)
|
||||
},
|
||||
enabled: computed(() => {
|
||||
const enabled = !!currentServerId.value
|
||||
debug('serverData enabled:', enabled)
|
||||
return enabled
|
||||
}),
|
||||
})
|
||||
|
||||
watch(serverData, (val) =>
|
||||
debug('serverData changed:', val?.server_id, val?.name, val?.loader, val?.mc_version),
|
||||
)
|
||||
watch(serverDataLoading, (val) => debug('serverData loading:', val))
|
||||
watch(serverDataError, (val) => {
|
||||
if (val) debug('serverData error:', val)
|
||||
})
|
||||
|
||||
const serverIcon = computed(() => {
|
||||
if (!currentServerId.value || !import.meta.client) return null
|
||||
return localStorage.getItem(`server-icon-${currentServerId.value}`)
|
||||
})
|
||||
|
||||
const serverHideInstalled = ref(false)
|
||||
const hideSelectedServerInstalls = ref(false)
|
||||
const installingProjectIds = ref<Set<string>>(new Set())
|
||||
const optimisticallyInstalledProjectIds = ref<Set<string>>(new Set())
|
||||
const hiddenInstalledProjectIds = ref<Set<string>>(new Set())
|
||||
const hiddenInstalledProjectIdsInitialized = ref(false)
|
||||
const queuedServerInstalls = ref<Map<string, BrowseInstallPlan<ServerInstallSearchResult>>>(
|
||||
readStoredServerInstallQueue(currentServerId.value, currentWorldId.value),
|
||||
)
|
||||
const queuedServerInstallProjectIds = computed(() => new Set(queuedServerInstalls.value.keys()))
|
||||
const queuedServerInstallCount = computed(() => queuedServerInstalls.value.size)
|
||||
const selectedServerInstallProjects = computed(() =>
|
||||
Array.from(queuedServerInstalls.value.values()).map((plan) => ({
|
||||
id: plan.projectId,
|
||||
name: plan.project.title ?? formatMessage(commonMessages.projectLabel),
|
||||
iconUrl: plan.project.icon_url ?? null,
|
||||
})),
|
||||
)
|
||||
const isInstallingQueuedServerInstalls = ref(false)
|
||||
const queuedInstallProgress = ref({ completed: 0, total: 0 })
|
||||
const serverInstallQueue = {
|
||||
get: () => queuedServerInstalls.value,
|
||||
set: (plans: Map<string, BrowseInstallPlan<ServerInstallSearchResult>>) => {
|
||||
queuedServerInstalls.value = plans
|
||||
writeStoredServerInstallQueue(currentServerId.value, currentWorldId.value, plans)
|
||||
},
|
||||
}
|
||||
|
||||
const contentQueryKey = computed(() => ['content', 'list', currentServerId.value ?? ''] as const)
|
||||
const { data: serverContentData, error: serverContentError } = useQuery({
|
||||
queryKey: contentQueryKey,
|
||||
queryFn: () =>
|
||||
client.archon.content_v1.getAddons(currentServerId.value!, currentWorldId.value!),
|
||||
enabled: computed(() => !!currentServerId.value && !!currentWorldId.value),
|
||||
})
|
||||
|
||||
function setBrowseSearchState(state: ServerInstallBrowseSearchState) {
|
||||
browseSearchState = state
|
||||
}
|
||||
|
||||
function setStoredServerInstallPlans(
|
||||
serverId: string,
|
||||
worldId: string,
|
||||
plans: Map<string, BrowseInstallPlan<ServerInstallSearchResult>>,
|
||||
) {
|
||||
if (serverId === currentServerId.value && worldId === currentWorldId.value) {
|
||||
queuedServerInstalls.value = plans
|
||||
}
|
||||
writeStoredServerInstallQueue(serverId, worldId, plans)
|
||||
}
|
||||
|
||||
async function getQueuedInstallOwner(project: ServerInstallSearchResult) {
|
||||
const fallback = getQueuedInstallOwnerFallback(project)
|
||||
|
||||
try {
|
||||
if (project.organization) {
|
||||
const organization = await client.labrinth.projects_v3.getOrganization(project.project_id)
|
||||
if (organization) {
|
||||
return {
|
||||
id: organization.id,
|
||||
name: organization.name,
|
||||
type: 'organization' as const,
|
||||
avatar_url: organization.icon_url ?? undefined,
|
||||
link: `/organization/${organization.slug}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const members = await client.labrinth.projects_v3.getMembers(project.project_id)
|
||||
const owner =
|
||||
members.find((member) => member.user.id === project.author_id)?.user ??
|
||||
members.find((member) => member.is_owner || member.role === 'Owner')?.user ??
|
||||
members[0]?.user
|
||||
|
||||
if (owner) {
|
||||
return {
|
||||
id: owner.id,
|
||||
name: owner.username,
|
||||
type: 'user' as const,
|
||||
avatar_url: owner.avatar_url,
|
||||
link: `/user/${owner.username}`,
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
||||
|
||||
function getQueuedInstallPlaceholder(
|
||||
plan: BrowseInstallPlan<ServerInstallSearchResult>,
|
||||
owner: PendingServerContentInstallInput['owner'],
|
||||
): PendingServerContentInstallInput {
|
||||
return {
|
||||
projectId: plan.projectId,
|
||||
versionId: plan.versionId,
|
||||
contentType: plan.contentType as PendingServerContentInstallType,
|
||||
title: plan.project.title ?? formatMessage(commonMessages.projectLabel),
|
||||
versionName: plan.versionName ?? null,
|
||||
versionNumber: plan.versionNumber ?? null,
|
||||
fileName: plan.fileName ?? null,
|
||||
owner,
|
||||
slug: plan.project.slug ?? plan.projectId,
|
||||
iconUrl: plan.project.icon_url ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
function getQueuedInstallPlaceholderFallbacks(
|
||||
plans: Map<string, BrowseInstallPlan<ServerInstallSearchResult>>,
|
||||
) {
|
||||
return getQueuedAddonInstallPlans(plans).map((plan) =>
|
||||
getQueuedInstallPlaceholder(plan, getQueuedInstallOwnerFallback(plan.project)),
|
||||
)
|
||||
}
|
||||
|
||||
async function getQueuedInstallPlaceholders(
|
||||
plans: Map<string, BrowseInstallPlan<ServerInstallSearchResult>>,
|
||||
) {
|
||||
return Promise.all(
|
||||
getQueuedAddonInstallPlans(plans).map(async (plan) =>
|
||||
getQueuedInstallPlaceholder(plan, await getQueuedInstallOwner(plan.project)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
function setProjectInstalling(projectId: string, installing: boolean) {
|
||||
const next = new Set(installingProjectIds.value)
|
||||
if (installing) {
|
||||
next.add(projectId)
|
||||
} else {
|
||||
next.delete(projectId)
|
||||
}
|
||||
installingProjectIds.value = next
|
||||
}
|
||||
|
||||
function markProjectInstalled(projectId: string) {
|
||||
optimisticallyInstalledProjectIds.value = new Set([
|
||||
...optimisticallyInstalledProjectIds.value,
|
||||
projectId,
|
||||
])
|
||||
}
|
||||
|
||||
function getServerInstalledProjectIds(data = serverContentData.value) {
|
||||
return new Set(
|
||||
(data?.addons ?? [])
|
||||
.map((addon) => addon.project_id)
|
||||
.filter((projectId): projectId is string => !!projectId),
|
||||
)
|
||||
}
|
||||
|
||||
function getServerInstalledContentKeys(data = serverContentData.value) {
|
||||
return new Set((data?.addons ?? []).map((addon) => addon.project_id ?? addon.filename))
|
||||
}
|
||||
|
||||
function syncHiddenInstalledProjectIds() {
|
||||
hiddenInstalledProjectIds.value = new Set([
|
||||
...getServerInstalledProjectIds(),
|
||||
...optimisticallyInstalledProjectIds.value,
|
||||
])
|
||||
hiddenInstalledProjectIdsInitialized.value = true
|
||||
}
|
||||
|
||||
const serverFilters = computed<FilterValue[]>(() => {
|
||||
debug(
|
||||
'serverFilters recomputing, serverData:',
|
||||
!!serverData.value,
|
||||
'projectType:',
|
||||
projectType.value?.id,
|
||||
)
|
||||
const filters: FilterValue[] = []
|
||||
if (serverData.value && projectType.value?.id !== 'modpack') {
|
||||
const gameVersion = serverData.value.mc_version
|
||||
if (gameVersion) {
|
||||
filters.push({ type: 'game_version', option: gameVersion })
|
||||
}
|
||||
|
||||
const platform = serverData.value.loader?.toLowerCase()
|
||||
|
||||
const modLoaders = ['fabric', 'forge', 'quilt', 'neoforge']
|
||||
if (platform && modLoaders.includes(platform)) {
|
||||
filters.push({ type: 'mod_loader', option: platform })
|
||||
}
|
||||
|
||||
const pluginLoaders = ['paper', 'purpur']
|
||||
if (platform && pluginLoaders.includes(platform)) {
|
||||
filters.push({ type: 'plugin_loader', option: platform })
|
||||
}
|
||||
|
||||
if (projectType.value?.id === 'mod') {
|
||||
filters.push({ type: 'environment', option: 'server' })
|
||||
}
|
||||
|
||||
if (serverHideInstalled.value && hiddenInstalledProjectIds.value.size > 0) {
|
||||
for (const x of hiddenInstalledProjectIds.value) {
|
||||
filters.push({
|
||||
type: 'project_id',
|
||||
option: `project_id:${x}`,
|
||||
negative: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (hideSelectedServerInstalls.value && queuedServerInstallProjectIds.value.size > 0) {
|
||||
for (const id of queuedServerInstallProjectIds.value) {
|
||||
filters.push({
|
||||
type: 'project_id',
|
||||
option: `project_id:${id}`,
|
||||
negative: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentServerId.value && projectType.value?.id === 'modpack') {
|
||||
filters.push(
|
||||
{ type: 'environment', option: 'client' },
|
||||
{ type: 'environment', option: 'server' },
|
||||
)
|
||||
}
|
||||
debug('serverFilters result:', filters)
|
||||
return filters
|
||||
})
|
||||
|
||||
function getCurrentServerInstallType(): BrowseInstallContentType {
|
||||
const type = projectType.value?.id
|
||||
if (type === 'modpack' || type === 'mod' || type === 'plugin' || type === 'datapack') {
|
||||
return type
|
||||
}
|
||||
throw new Error(formatMessage(messages.unsupportedContentType))
|
||||
}
|
||||
|
||||
function getServerInstallTargetPreferences(contentType: BrowseInstallContentType) {
|
||||
return getTargetInstallPreferences(
|
||||
{
|
||||
gameVersion: serverData.value?.mc_version,
|
||||
loader: serverData.value?.loader,
|
||||
},
|
||||
contentType,
|
||||
)
|
||||
}
|
||||
|
||||
function getInstallProjectVersions(projectId: string) {
|
||||
return client.labrinth.versions_v2.getProjectVersions(projectId, {
|
||||
include_changelog: false,
|
||||
})
|
||||
}
|
||||
|
||||
function clearQueuedServerInstalls() {
|
||||
serverInstallQueue.set(new Map())
|
||||
}
|
||||
|
||||
function removeQueuedServerInstall(projectId: string) {
|
||||
const nextPlans = new Map(queuedServerInstalls.value)
|
||||
nextPlans.delete(projectId)
|
||||
serverInstallQueue.set(nextPlans)
|
||||
}
|
||||
|
||||
async function flushQueuedServerInstalls(
|
||||
serverId: string | null = currentServerId.value,
|
||||
worldId: string | null = currentWorldId.value,
|
||||
) {
|
||||
if (isInstallingQueuedServerInstalls.value) return false
|
||||
|
||||
if (!serverId || !worldId) {
|
||||
handleError(new Error(formatMessage(messages.noServerWorld)))
|
||||
return false
|
||||
}
|
||||
|
||||
const queuedPlans = getStoredServerAddonInstallQueue<ServerInstallSearchResult>(
|
||||
serverId,
|
||||
worldId,
|
||||
)
|
||||
if (queuedPlans.size === 0) return true
|
||||
|
||||
isInstallingQueuedServerInstalls.value = true
|
||||
queuedInstallProgress.value = {
|
||||
completed: 0,
|
||||
total: queuedPlans.size,
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await flushStoredServerAddonInstallQueue({
|
||||
serverId,
|
||||
worldId,
|
||||
install: (plans) =>
|
||||
client.archon.content_v1.addAddons(
|
||||
serverId,
|
||||
worldId,
|
||||
plans.map((plan) => ({
|
||||
project_id: plan.projectId,
|
||||
version_id: plan.versionId,
|
||||
})),
|
||||
),
|
||||
onQueueChange: (plans) => setStoredServerInstallPlans(serverId, worldId, plans),
|
||||
})
|
||||
|
||||
if (!result.ok) {
|
||||
for (const plan of result.attemptedPlans) {
|
||||
removePendingServerContentInstall(serverId, worldId, plan.projectId)
|
||||
}
|
||||
handleError(result.error as Error)
|
||||
return false
|
||||
}
|
||||
|
||||
for (const plan of result.flushedPlans) {
|
||||
markProjectInstalled(plan.projectId)
|
||||
}
|
||||
queuedInstallProgress.value = {
|
||||
completed: result.flushedPlans.length,
|
||||
total: result.flushedPlans.length,
|
||||
}
|
||||
if (result.flushedPlans.length > 0) {
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: ['content', 'list', 'v1', serverId] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['content', 'list'] }),
|
||||
])
|
||||
}
|
||||
|
||||
return true
|
||||
} finally {
|
||||
isInstallingQueuedServerInstalls.value = false
|
||||
queuedInstallProgress.value = { completed: 0, total: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
async function discardQueuedServerInstallsAndBack() {
|
||||
clearQueuedServerInstalls()
|
||||
await navigateTo(serverBackUrl.value)
|
||||
}
|
||||
|
||||
async function installQueuedServerInstallsAndBack() {
|
||||
const sid = currentServerId.value
|
||||
const wid = currentWorldId.value
|
||||
const backUrl = serverBackUrl.value
|
||||
const plans = new Map(queuedServerInstalls.value)
|
||||
|
||||
if (sid && wid) {
|
||||
writeStoredServerInstallQueue(sid, wid, plans)
|
||||
writePendingServerContentInstallBaseline(sid, wid, [
|
||||
...getServerInstalledContentKeys(),
|
||||
...optimisticallyInstalledProjectIds.value,
|
||||
])
|
||||
addPendingServerContentInstalls(sid, wid, getQueuedInstallPlaceholderFallbacks(plans))
|
||||
void getQueuedInstallPlaceholders(plans)
|
||||
.then((items) => {
|
||||
const pendingProjectIds = new Set(
|
||||
readPendingServerContentInstalls(sid, wid).map((item) => item.projectId),
|
||||
)
|
||||
addPendingServerContentInstalls(
|
||||
sid,
|
||||
wid,
|
||||
items.filter((item) => pendingProjectIds.has(item.projectId)),
|
||||
)
|
||||
})
|
||||
.catch((err) => handleError(err as Error))
|
||||
}
|
||||
await navigateTo(backUrl)
|
||||
void flushQueuedServerInstalls(sid, wid)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async function serverInstall(project: ServerInstallSearchResult) {
|
||||
if (!serverData.value || !currentServerId.value || !currentWorldId.value) {
|
||||
handleError(new Error('No server to install to.'))
|
||||
return
|
||||
}
|
||||
|
||||
if (!browseSearchState) {
|
||||
handleError(new Error('Search state is not ready.'))
|
||||
return
|
||||
}
|
||||
|
||||
const contentType = getCurrentServerInstallType()
|
||||
const isModpack = contentType === 'modpack'
|
||||
|
||||
try {
|
||||
if (!isModpack && queuedServerInstallProjectIds.value.has(project.project_id)) {
|
||||
removeQueuedServerInstall(project.project_id)
|
||||
return
|
||||
}
|
||||
|
||||
if (isModpack || !queuedServerInstallProjectIds.value.has(project.project_id)) {
|
||||
setProjectInstalling(project.project_id, true)
|
||||
}
|
||||
|
||||
await requestInstall({
|
||||
project,
|
||||
contentType,
|
||||
mode: isModpack ? 'immediate' : 'queue',
|
||||
selectedFilters: isModpack ? [] : browseSearchState.currentFilters.value,
|
||||
providedFilters: isModpack ? [] : serverFilters.value,
|
||||
overriddenProvidedFilterTypes: isModpack
|
||||
? []
|
||||
: browseSearchState.overriddenProvidedFilterTypes.value,
|
||||
targetPreferences: getServerInstallTargetPreferences(contentType),
|
||||
getProjectVersions: getInstallProjectVersions,
|
||||
queue: serverInstallQueue,
|
||||
install: async (plan) => {
|
||||
const modalInstance = onboardingModalRef.value
|
||||
if (!modalInstance) {
|
||||
setProjectInstalling(plan.projectId, false)
|
||||
return
|
||||
}
|
||||
|
||||
onboardingInstallingProject.value = plan.project
|
||||
modalInstance.show()
|
||||
await nextTick()
|
||||
const ctx = modalInstance.ctx
|
||||
if (!ctx) return
|
||||
|
||||
ctx.setupType.value = 'modpack'
|
||||
ctx.modpackSelection.value = {
|
||||
projectId: plan.projectId,
|
||||
versionId: plan.versionId,
|
||||
name: plan.project.title,
|
||||
iconUrl: plan.project.icon_url ?? undefined,
|
||||
}
|
||||
ctx.modal.value?.setStage('final-config')
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
if (isModpack) {
|
||||
setProjectInstalling(project.project_id, false)
|
||||
}
|
||||
handleError(e instanceof Error ? e : new Error(`Error installing content ${e}`))
|
||||
} finally {
|
||||
if (!isModpack) {
|
||||
setProjectInstalling(project.project_id, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onboardingInstallingProject = ref<ServerInstallSearchResult | null>(null)
|
||||
|
||||
function onOnboardingHide() {
|
||||
if (onboardingInstallingProject.value) {
|
||||
setProjectInstalling(onboardingInstallingProject.value.project_id, false)
|
||||
onboardingInstallingProject.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function onOnboardingBack() {
|
||||
onboardingModalRef.value?.hide()
|
||||
}
|
||||
|
||||
async function onModpackFlowCreate(config: CreationFlowContextValue) {
|
||||
if (!currentServerId.value || !currentWorldId.value || !config.modpackSelection.value) return
|
||||
|
||||
try {
|
||||
await client.archon.content_v1.installContent(currentServerId.value, currentWorldId.value, {
|
||||
content_variant: 'modpack',
|
||||
spec: {
|
||||
platform: 'modrinth',
|
||||
project_id: config.modpackSelection.value.projectId,
|
||||
version_id: config.modpackSelection.value.versionId,
|
||||
},
|
||||
soft_override: false,
|
||||
properties: config.buildProperties(),
|
||||
} satisfies Archon.Content.v1.InstallWorldContent)
|
||||
|
||||
if (fromContext.value === 'onboarding') {
|
||||
await client.archon.servers_v1.endIntro(currentServerId.value)
|
||||
queryClient.invalidateQueries({ queryKey: ['servers', 'detail', currentServerId.value] })
|
||||
navigateTo(`/hosting/manage/${currentServerId.value}/content`)
|
||||
} else {
|
||||
navigateTo(`/hosting/manage/${currentServerId.value}?openSettings=installation`)
|
||||
}
|
||||
} catch (e) {
|
||||
handleError(new Error(`Error installing modpack: ${e}`))
|
||||
config.loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const serverBackUrl = computed(() => {
|
||||
if (!serverData.value) return ''
|
||||
const id = serverData.value.server_id
|
||||
if (fromContext.value === 'onboarding') return `/hosting/manage/${id}?resumeModal=setup-type`
|
||||
if (fromContext.value === 'reset-server')
|
||||
return `/hosting/manage/${id}?openSettings=installation`
|
||||
return `/hosting/manage/${id}/content`
|
||||
})
|
||||
|
||||
const serverBackLabel = computed(() => {
|
||||
if (fromContext.value === 'onboarding') return formatMessage(messages.backToSetup)
|
||||
if (fromContext.value === 'reset-server') return formatMessage(messages.cancelReset)
|
||||
return formatMessage(messages.backToServer)
|
||||
})
|
||||
|
||||
const serverBrowseHeading = computed(() =>
|
||||
fromContext.value === 'reset-server'
|
||||
? formatMessage(messages.resetModpackHeading)
|
||||
: formatMessage(commonMessages.installingContentLabel),
|
||||
)
|
||||
|
||||
const installContext = computed(() => {
|
||||
if (!serverData.value) return null
|
||||
return {
|
||||
name: serverData.value.name,
|
||||
loader: serverData.value.loader ?? '',
|
||||
gameVersion: serverData.value.mc_version ?? '',
|
||||
serverId: currentServerId.value,
|
||||
upstream: serverData.value.upstream,
|
||||
iconSrc: serverIcon.value,
|
||||
isMedal: serverData.value.is_medal,
|
||||
backUrl: serverBackUrl.value,
|
||||
backLabel: serverBackLabel.value,
|
||||
heading: serverBrowseHeading.value,
|
||||
queuedCount: queuedServerInstallCount.value,
|
||||
selectedProjects: selectedServerInstallProjects.value,
|
||||
isInstallingSelected: isInstallingQueuedServerInstalls.value,
|
||||
installProgress: queuedInstallProgress.value,
|
||||
clearQueued: clearQueuedServerInstalls,
|
||||
clearSelected: clearQueuedServerInstalls,
|
||||
onBack: flushQueuedServerInstalls,
|
||||
discardSelectedAndBack: discardQueuedServerInstallsAndBack,
|
||||
installSelected: installQueuedServerInstallsAndBack,
|
||||
}
|
||||
})
|
||||
|
||||
watch(serverContentError, (error) => {
|
||||
if (error) {
|
||||
console.error('Failed to load server content:', error)
|
||||
handleError(error)
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
serverContentData,
|
||||
(data) => {
|
||||
if (!data) return
|
||||
if (!hiddenInstalledProjectIdsInitialized.value) {
|
||||
syncHiddenInstalledProjectIds()
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
if (route.query.shi && projectType.value?.id !== 'modpack') {
|
||||
serverHideInstalled.value = route.query.shi === 'true'
|
||||
}
|
||||
|
||||
watch(serverHideInstalled, (hideInstalled) => {
|
||||
if (hideInstalled) {
|
||||
syncHiddenInstalledProjectIds()
|
||||
}
|
||||
})
|
||||
|
||||
watch([currentServerId, currentWorldId], ([serverId, worldId], [prevServerId, prevWorldId]) => {
|
||||
if (serverId !== prevServerId || worldId !== prevWorldId) {
|
||||
queuedServerInstalls.value = readStoredServerInstallQueue(serverId, worldId)
|
||||
}
|
||||
})
|
||||
|
||||
watch(queuedServerInstallCount, (count) => {
|
||||
if (count === 0) {
|
||||
hideSelectedServerInstalls.value = false
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
currentServerId,
|
||||
fromContext,
|
||||
currentWorldId,
|
||||
serverData,
|
||||
serverContentData,
|
||||
serverFilters,
|
||||
serverHideInstalled,
|
||||
hideSelectedServerInstalls,
|
||||
installingProjectIds,
|
||||
optimisticallyInstalledProjectIds,
|
||||
queuedServerInstallProjectIds,
|
||||
queuedServerInstallCount,
|
||||
isInstallingQueuedServerInstalls,
|
||||
installContext,
|
||||
setBrowseSearchState,
|
||||
syncHiddenInstalledProjectIds,
|
||||
serverInstall,
|
||||
onOnboardingHide,
|
||||
onOnboardingBack,
|
||||
onModpackFlowCreate,
|
||||
}
|
||||
}
|
||||
@@ -28,26 +28,44 @@ export function createLoaderParsers(
|
||||
if (metadata.dependencies) {
|
||||
const neoForgeDependency = Object.values(metadata.dependencies)
|
||||
.flat()
|
||||
.find((dependency: any) => dependency.modId === 'neoforge')
|
||||
.filter((dependency: any) => dependency.modId === 'neoforge')
|
||||
.map((dependency: any) => dependency.versionRange)
|
||||
.find((range) => range)
|
||||
const minecraftDependency = Object.values(metadata.dependencies)
|
||||
.flat()
|
||||
.filter((dependency: any) => dependency.modId === 'minecraft')
|
||||
.map((dependency: any) => dependency.versionRange)
|
||||
.find((range) => range)
|
||||
|
||||
if (neoForgeDependency) {
|
||||
try {
|
||||
// https://docs.neoforged.net/docs/gettingstarted/versioning/#neoforge
|
||||
const mcVersionRange = (neoForgeDependency as any).versionRange
|
||||
.replace('-beta', '')
|
||||
.replace(
|
||||
/(\d+)(?:\.(\d+))?(?:\.(\d+)?)?/g,
|
||||
(_match: string, major: string, minor: string) => {
|
||||
return `1.${major}${minor ? '.' + minor : ''}`
|
||||
},
|
||||
)
|
||||
newGameVersions = getGameVersionsMatchingMavenRange(
|
||||
mcVersionRange,
|
||||
simplifiedGameVersions,
|
||||
)
|
||||
} catch {
|
||||
// Ignore parsing errors, just leave game_versions empty
|
||||
}
|
||||
if (minecraftDependency) {
|
||||
newGameVersions = getGameVersionsMatchingMavenRange(
|
||||
minecraftDependency,
|
||||
simplifiedGameVersions,
|
||||
)
|
||||
} else if (neoForgeDependency) {
|
||||
// https://docs.neoforged.net/docs/gettingstarted/versioning/#neoforge
|
||||
// NeoForge's versioning changed after Mojang changed from 1.<major>.<minor> to <year>.<drop>.<patch>, so both cases need to be handled
|
||||
const neoPre26Regex = /^(?<mc_major>\d+)(?:\.(?<mc_minor>\d+))?(?:\.(?<neo>\d+)?)?$/
|
||||
const neoPost26Regex =
|
||||
/^(?<mc_year>\d+)(?:\.(?<mc_drop>\d+))?(?:\.(?<mc_patch>\d+)?)?(?:\.(\d+))?$/
|
||||
|
||||
newGameVersions = getGameVersionsMatchingMavenRange(
|
||||
neoForgeDependency.replace('-beta', ''),
|
||||
simplifiedGameVersions,
|
||||
(version) => {
|
||||
const matchPre26 = version.match(neoPre26Regex)
|
||||
if (matchPre26 && matchPre26.groups) {
|
||||
const { mc_major, mc_minor } = matchPre26.groups
|
||||
return mc_minor ? `1.${mc_major}.${mc_minor}` : `1.${mc_major}`
|
||||
}
|
||||
const matchPost26 = version.match(neoPost26Regex)
|
||||
if (matchPost26 && matchPost26.groups) {
|
||||
const { mc_year, mc_drop, mc_patch } = matchPost26.groups
|
||||
return `${mc_year}${mc_drop ? `.${mc_drop}` : ''}${mc_patch ? `.${mc_patch}` : ''}`
|
||||
}
|
||||
return version
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ export function getGameVersionsMatchingSemverRange(
|
||||
export function getGameVersionsMatchingMavenRange(
|
||||
range: string | undefined,
|
||||
gameVersions: string[],
|
||||
processor: (version: string) => string | null = (v) => v,
|
||||
): string[] {
|
||||
if (!range) {
|
||||
return []
|
||||
@@ -63,24 +64,42 @@ export function getGameVersionsMatchingMavenRange(
|
||||
|
||||
for (const range of ranges) {
|
||||
let result
|
||||
if ((result = range.match(LESS_THAN_EQUAL))) {
|
||||
semverRanges.push(`<=${result[1]}`)
|
||||
} else if ((result = range.match(LESS_THAN))) {
|
||||
semverRanges.push(`<${result[1]}`)
|
||||
} else if ((result = range.match(EQUAL))) {
|
||||
semverRanges.push(`${result[1]}`)
|
||||
} else if ((result = range.match(GREATER_THAN_EQUAL))) {
|
||||
semverRanges.push(`>=${result[1]}`)
|
||||
} else if ((result = range.match(GREATER_THAN))) {
|
||||
semverRanges.push(`>${result[1]}`)
|
||||
} else if ((result = range.match(BETWEEN))) {
|
||||
semverRanges.push(`>${result[1]} <${result[2]}`)
|
||||
} else if ((result = range.match(BETWEEN_EQUAL))) {
|
||||
semverRanges.push(`>=${result[1]} <=${result[2]}`)
|
||||
} else if ((result = range.match(BETWEEN_LESS_THAN_EQUAL))) {
|
||||
semverRanges.push(`>${result[1]} <=${result[2]}`)
|
||||
} else if ((result = range.match(BETWEEN_GREATER_THAN_EQUAL))) {
|
||||
semverRanges.push(`>=${result[1]} <${result[2]}`)
|
||||
let version1
|
||||
let version2
|
||||
if ((result = range.match(LESS_THAN_EQUAL)) && (version1 = processor(result[1]))) {
|
||||
semverRanges.push(`<=${version1}`)
|
||||
} else if ((result = range.match(LESS_THAN)) && (version1 = processor(result[1]))) {
|
||||
semverRanges.push(`<${version1}`)
|
||||
} else if ((result = range.match(EQUAL)) && (version1 = processor(result[1]))) {
|
||||
semverRanges.push(`${version1}`)
|
||||
} else if ((result = range.match(GREATER_THAN_EQUAL)) && (version1 = processor(result[1]))) {
|
||||
semverRanges.push(`>=${version1}`)
|
||||
} else if ((result = range.match(GREATER_THAN)) && (version1 = processor(result[1]))) {
|
||||
semverRanges.push(`>${version1}`)
|
||||
} else if (
|
||||
(result = range.match(BETWEEN)) &&
|
||||
(version1 = processor(result[1])) &&
|
||||
(version2 = processor(result[2]))
|
||||
) {
|
||||
semverRanges.push(`>${version1} <${version2}`)
|
||||
} else if (
|
||||
(result = range.match(BETWEEN_EQUAL)) &&
|
||||
(version1 = processor(result[1])) &&
|
||||
(version2 = processor(result[2]))
|
||||
) {
|
||||
semverRanges.push(`>=${version1} <=${version2}`)
|
||||
} else if (
|
||||
(result = range.match(BETWEEN_LESS_THAN_EQUAL)) &&
|
||||
(version1 = processor(result[1])) &&
|
||||
(version2 = processor(result[2]))
|
||||
) {
|
||||
semverRanges.push(`>${version1} <=${version2}`)
|
||||
} else if (
|
||||
(result = range.match(BETWEEN_GREATER_THAN_EQUAL)) &&
|
||||
(version1 = processor(result[1])) &&
|
||||
(version2 = processor(result[2]))
|
||||
) {
|
||||
semverRanges.push(`>=${version1} <${version2}`)
|
||||
}
|
||||
}
|
||||
return getGameVersionsMatchingSemverRange(semverRanges, gameVersions)
|
||||
|
||||
@@ -34,25 +34,39 @@
|
||||
'modrinth-parent__no-modal-blurs': !cosmetics.advancedRendering,
|
||||
}"
|
||||
>
|
||||
<RussiaBanner v-if="isRussia" />
|
||||
<TaxIdMismatchBanner v-if="showTinMismatchBanner" />
|
||||
<TaxComplianceBanner v-if="showTaxComplianceBanner" />
|
||||
<RussiaBanner v-if="flags.showAllBanners || isRussia" />
|
||||
<TaxIdMismatchBanner v-if="flags.showAllBanners || showTinMismatchBanner" />
|
||||
<TaxComplianceBanner v-if="flags.showAllBanners || showTaxComplianceBanner" />
|
||||
<VerifyEmailBanner
|
||||
v-if="auth.user && !auth.user.email_verified && route.path !== '/auth/verify-email'"
|
||||
v-if="
|
||||
flags.showAllBanners ||
|
||||
(auth.user && !auth.user.email_verified && route.path !== '/auth/verify-email')
|
||||
"
|
||||
:has-email="!!auth?.user?.email"
|
||||
/>
|
||||
<SubscriptionPaymentFailedBanner
|
||||
v-if="
|
||||
user.subscriptions.some((x) => x.status === 'payment-failed') &&
|
||||
route.path !== '/settings/billing'
|
||||
flags.showAllBanners ||
|
||||
(user.subscriptions.some((x) => x.status === 'payment-failed') &&
|
||||
route.path !== '/settings/billing')
|
||||
"
|
||||
/>
|
||||
<PreviewBanner
|
||||
v-if="
|
||||
flags.showAllBanners || (config.public.buildEnv === 'production' && config.public.preview)
|
||||
"
|
||||
/>
|
||||
<StagingBanner
|
||||
v-if="
|
||||
flags.showAllBanners ||
|
||||
config.public.apiBaseUrl.startsWith('https://staging-api.modrinth.com')
|
||||
"
|
||||
/>
|
||||
<PreviewBanner v-if="config.public.buildEnv === 'production' && config.public.preview" />
|
||||
<StagingBanner v-if="config.public.apiBaseUrl.startsWith('https://staging-api.modrinth.com')" />
|
||||
<GeneratedStateErrorsBanner
|
||||
:errors="generatedStateErrors"
|
||||
:api-url="config.public.apiBaseUrl"
|
||||
/>
|
||||
<ViewOnModrinthBanner />
|
||||
<header
|
||||
class="desktop-only relative z-[5] mx-auto grid max-w-[1280px] grid-cols-[1fr_auto] items-center gap-2 px-6 py-4 lg:grid-cols-[auto_1fr_auto]"
|
||||
>
|
||||
@@ -747,9 +761,13 @@ import {
|
||||
commonMessages,
|
||||
commonProjectTypeCategoryMessages,
|
||||
commonSettingsMessages,
|
||||
createHostingIntercomIdentityKey,
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
injectPageContext,
|
||||
OverflowMenu,
|
||||
providePageContext,
|
||||
useHostingIntercom,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import TeleportOverflowMenu from '@modrinth/ui/src/components/base/TeleportOverflowMenu.vue'
|
||||
@@ -767,6 +785,7 @@ import SubscriptionPaymentFailedBanner from '~/components/ui/banner/Subscription
|
||||
import TaxComplianceBanner from '~/components/ui/banner/TaxComplianceBanner.vue'
|
||||
import TaxIdMismatchBanner from '~/components/ui/banner/TaxIdMismatchBanner.vue'
|
||||
import VerifyEmailBanner from '~/components/ui/banner/VerifyEmailBanner.vue'
|
||||
import ViewOnModrinthBanner from '~/components/ui/banner/ViewOnModrinthBanner.vue'
|
||||
import CollectionCreateModal from '~/components/ui/create/CollectionCreateModal.vue'
|
||||
import OrganizationCreateModal from '~/components/ui/create/OrganizationCreateModal.vue'
|
||||
import ProjectCreateModal from '~/components/ui/create/ProjectCreateModal.vue'
|
||||
@@ -793,6 +812,25 @@ const router = useNativeRouter()
|
||||
const signInRouteObj = computed(() => getSignInRouteObj(route))
|
||||
const link = config.public.siteUrl + route.path.replace(/\/+$/, '')
|
||||
const client = injectModrinthClient()
|
||||
const pageContext = injectPageContext()
|
||||
const hostingIntercomActive = computed(() => route.path.startsWith('/hosting') && !!auth.value.user)
|
||||
const hostingIntercomServerId = computed(() => {
|
||||
const rawId = route.params.id
|
||||
return Array.isArray(rawId) ? rawId[0] : rawId
|
||||
})
|
||||
const hostingIntercom = useHostingIntercom({
|
||||
enabled: hostingIntercomActive,
|
||||
appId: computed(() => config.public.intercomAppId),
|
||||
fetchToken: fetchIntercomToken,
|
||||
identityKey: computed(() =>
|
||||
createHostingIntercomIdentityKey(auth.value.user, hostingIntercomServerId.value),
|
||||
),
|
||||
})
|
||||
|
||||
providePageContext({
|
||||
...pageContext,
|
||||
intercomBubble: hostingIntercom.intercomBubble,
|
||||
})
|
||||
|
||||
const { data: payoutBalance } = useQuery({
|
||||
queryKey: ['payout', 'balance'],
|
||||
@@ -821,6 +859,12 @@ const showTinMismatchBanner = computed(() => {
|
||||
|
||||
const basePopoutId = useId()
|
||||
|
||||
async function fetchIntercomToken() {
|
||||
return $fetch('/api/intercom/messenger-jwt', {
|
||||
query: hostingIntercomServerId.value ? { server_id: hostingIntercomServerId.value } : {},
|
||||
})
|
||||
}
|
||||
|
||||
const navMenuMessages = defineMessages({
|
||||
home: {
|
||||
id: 'layout.nav.home',
|
||||
@@ -1133,7 +1177,7 @@ const isDiscovering = computed(
|
||||
)
|
||||
|
||||
const isDiscoveringSubpage = computed(
|
||||
() => route.name && route.name.startsWith('type-id') && !route.query.sid,
|
||||
() => route.name && route.name.startsWith('type-project') && !route.query.sid,
|
||||
)
|
||||
|
||||
const isRussia = computed(() => country.value === 'ru')
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
"app-marketing.download.options-title": {
|
||||
"message": "Stahovácí možnosti"
|
||||
},
|
||||
"app-marketing.download.terms": {
|
||||
"message": "Stažením aplikace Modrinth vyjadřujete souhlas s našimi <terms-link>Podmínkami</terms-link> a <privacy-link>Zásadami ochrany osobních údajů</privacy-link>."
|
||||
},
|
||||
"app-marketing.download.title": {
|
||||
"message": "Stáhnout Modrinth App (Beta)"
|
||||
},
|
||||
@@ -506,6 +509,12 @@
|
||||
"create.project.title": {
|
||||
"message": "Vytvořit projekt"
|
||||
},
|
||||
"create.project.type-project": {
|
||||
"message": "Projekt"
|
||||
},
|
||||
"create.project.type-server": {
|
||||
"message": "Server"
|
||||
},
|
||||
"create.project.url-label": {
|
||||
"message": "URL"
|
||||
},
|
||||
@@ -944,9 +953,6 @@
|
||||
"moderation.page.reports": {
|
||||
"message": "Hlášení"
|
||||
},
|
||||
"moderation.page.technicalReview": {
|
||||
"message": "Technické hodnocení"
|
||||
},
|
||||
"muralpay.country.at": {
|
||||
"message": "Rakousko"
|
||||
},
|
||||
|
||||
@@ -407,6 +407,51 @@
|
||||
"collection.title": {
|
||||
"message": "{name} - Kollektion"
|
||||
},
|
||||
"conversation-thread.action.add-private-note": {
|
||||
"message": "Private Notiz hinzufügen"
|
||||
},
|
||||
"conversation-thread.action.approve": {
|
||||
"message": "Genehmigen"
|
||||
},
|
||||
"conversation-thread.action.approve-with-reply": {
|
||||
"message": "Mit Antwort genehmigen"
|
||||
},
|
||||
"conversation-thread.action.close-thread": {
|
||||
"message": ""
|
||||
},
|
||||
"conversation-thread.action.reject": {
|
||||
"message": "Ablehnen"
|
||||
},
|
||||
"conversation-thread.action.reject-with-reply": {
|
||||
"message": "Mit Antwort ablehnen"
|
||||
},
|
||||
"conversation-thread.action.reply": {
|
||||
"message": "Antworten"
|
||||
},
|
||||
"conversation-thread.action.send": {
|
||||
"message": "Senden"
|
||||
},
|
||||
"conversation-thread.action.withhold": {
|
||||
"message": "Zurückhalten"
|
||||
},
|
||||
"conversation-thread.action.withhold-with-reply": {
|
||||
"message": "Mit Antwort zurückhalten"
|
||||
},
|
||||
"conversation-thread.reply-editor.placeholder.send": {
|
||||
"message": "Sende eine Nachricht..."
|
||||
},
|
||||
"conversation-thread.reply-modal.confirmation.label": {
|
||||
"message": "Mir ist bewusst, dass dieser Thread nicht aktiv von den Moderatoren überwacht wird."
|
||||
},
|
||||
"conversation-thread.resubmit-modal.description": {
|
||||
"message": "Du schickst <project-title>{projectTitle}</project-title> zur Wiederprüfung."
|
||||
},
|
||||
"conversation-thread.resubmit-modal.header.resubmitting": {
|
||||
"message": "Erneut zur Überprüfung einreichen"
|
||||
},
|
||||
"conversation-thread.resubmit-modal.header.submitting": {
|
||||
"message": "Erneut zur Überprüfung einreichen"
|
||||
},
|
||||
"create-project-version.create-modal.stage.add-files.admonition": {
|
||||
"message": "Ergänzende Dateien dienen zur unterstützung von Ressourcen wie Quellcode, nicht für alternative Versionen oder Varianten."
|
||||
},
|
||||
@@ -875,23 +920,8 @@
|
||||
"dashboard.head-title": {
|
||||
"message": "Dashboard"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "Alle als gelesen markieren"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "Verlauf ansehen"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "Du hast keine ungelesenen Nachrichten."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "Fehler beim laden von Benachrichtigungen:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "Verlauf"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "Benachrichtigungsverlauf"
|
||||
"message": "Du hast keine ungelesenen Benachrichtigungen."
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "Alle ansehen"
|
||||
@@ -902,9 +932,6 @@
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "Siehe {extraNotifs} weitere {extraNotifs, plural, one {Benachrichtigung} other {Benachrichtigungen}}"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "Lade Benachrichtigungen..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "Organisation erstellen"
|
||||
},
|
||||
@@ -920,6 +947,27 @@
|
||||
"dashboard.organizations.title": {
|
||||
"message": "Organisationen"
|
||||
},
|
||||
"dashboard.overview.notifications.button.mark-all-as-read": {
|
||||
"message": "Alle als gelesen markieren"
|
||||
},
|
||||
"dashboard.overview.notifications.button.view-history": {
|
||||
"message": "Verlauf ansehen"
|
||||
},
|
||||
"dashboard.overview.notifications.empty.no-unread": {
|
||||
"message": "Du hast keine ungelesenen Benachrichtigungen."
|
||||
},
|
||||
"dashboard.overview.notifications.error.loading": {
|
||||
"message": "Fehler beim Laden von Benachrichtigungen:"
|
||||
},
|
||||
"dashboard.overview.notifications.history.label": {
|
||||
"message": "Verlauf"
|
||||
},
|
||||
"dashboard.overview.notifications.history.title": {
|
||||
"message": "Benachrichtigungsverlauf"
|
||||
},
|
||||
"dashboard.overview.notifications.loading": {
|
||||
"message": "Benachrichtigungen werden geladen..."
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "Du kannst mehrere Projekte gleichzeitig bearbeiten, indem du sie unden auswählst."
|
||||
},
|
||||
@@ -1229,6 +1277,33 @@
|
||||
"dashboard.withdraw.error.tax-form.title": {
|
||||
"message": "Bitte Steuerformular ausfüllen"
|
||||
},
|
||||
"discover.install.back-to-server": {
|
||||
"message": "Zurück zum Server"
|
||||
},
|
||||
"discover.install.back-to-setup": {
|
||||
"message": "Zurück zur Einrichtung"
|
||||
},
|
||||
"discover.install.cancel-reset": {
|
||||
"message": "Reset abbrechen"
|
||||
},
|
||||
"discover.install.error.no-server-world": {
|
||||
"message": "Keine Serverwelt ist zur installation verfügbar."
|
||||
},
|
||||
"discover.install.error.unsupported-content-type": {
|
||||
"message": "Diese Art von Inhalt kann nicht vom durchstöbern zu einem Server hinzugefügt werden."
|
||||
},
|
||||
"discover.install.heading.reset-modpack": {
|
||||
"message": "Modpack zum installieren nach dem Reset auswählen"
|
||||
},
|
||||
"discover.seo.description": {
|
||||
"message": "Suche und durchstöbere tausende von Minecraft {projectType, select, mod {Mods} modpack {Modpacks} resourcepack {Ressourcenpakete} shader {Shader} plugin {Plugins} datapack {Datenpakete} other {Projekte}} auf Modrinth sofortigen, genauen Suchergebnissen. Unsere Filter helfen dir beim schnellen finden der besten Minecraft {projectType, select, mod {Mods} modpack {Modpacks} resourcepack {Ressourcenpakete} shader {Shader} plugin {Plugins} datapack {Datenpakete} other {Projekte}}."
|
||||
},
|
||||
"discover.seo.title": {
|
||||
"message": "Durchsuche {projectType, select, mod {Mods} modpack {Modpacks} resourcepack {Ressourcenpakete} shader {Shader} plugin {Plugins} datapack {Datenpakete} other {Projekte}}"
|
||||
},
|
||||
"discover.seo.title-with-query": {
|
||||
"message": "Durchsuche {projectType, select, mod {Mods} modpack {Modpacks} resourcepack {Ressourcenpakete} shader {Shader} plugin {Plugins} datapack {Datenpakete} other {Projekte}} | {query}"
|
||||
},
|
||||
"error.collection.404.list_item.1": {
|
||||
"message": "Du hast eventuell die Kollektions-URL falsch geschrieben."
|
||||
},
|
||||
@@ -1244,6 +1319,12 @@
|
||||
"error.collection.404.title": {
|
||||
"message": "Kollektion nicht gefunden"
|
||||
},
|
||||
"error.generic.401.signed-in-as": {
|
||||
"message": "Angemeldet als:"
|
||||
},
|
||||
"error.generic.401.title": {
|
||||
"message": "Du hast keinen Zugriff auf diese Seite"
|
||||
},
|
||||
"error.generic.404.subtitle": {
|
||||
"message": "Die Seite nach welcher du suchst, scheint nicht zu existieren."
|
||||
},
|
||||
@@ -1670,6 +1751,9 @@
|
||||
"layout.action.create-new": {
|
||||
"message": "Erstellen..."
|
||||
},
|
||||
"layout.action.external-projects": {
|
||||
"message": "Externe Projekte"
|
||||
},
|
||||
"layout.action.file-lookup": {
|
||||
"message": "Datei ansehen"
|
||||
},
|
||||
@@ -1718,14 +1802,20 @@
|
||||
"layout.banner.add-email.description": {
|
||||
"message": "Aus Sicherheitsgründen verlangt Modrinth, dass du eine E-Mail Adresse für dein Konto registrierst."
|
||||
},
|
||||
"layout.banner.build-fail.always-ignore": {
|
||||
"message": "Immer ignorieren"
|
||||
},
|
||||
"layout.banner.build-fail.description": {
|
||||
"message": "Diese Deployment von Modrinth's Frontend konnte keinen Status von der API generieren. Dies könnte aufgrund eines Ausfalls oder eines Konfigurationsfehlers sein. Erstelle neu wenn die API verfügbar ist. Fehlercodes: {errors}; Aktuelle API URL ist: {url}"
|
||||
},
|
||||
"layout.banner.build-fail.ignore": {
|
||||
"message": "Ignorieren"
|
||||
},
|
||||
"layout.banner.build-fail.title": {
|
||||
"message": "Fehler beim generieren des Status von der API beim erstellen."
|
||||
},
|
||||
"layout.banner.preview.description": {
|
||||
"message": "Falls du die offizielle Modrinth-Website aufrufen wolltest, besuch <link>https://modrinth.com</link>. Diese Vorschauversion wird von Modrinth-Mitarbeitern zu Testzwecken genutzt. Sie wurde unter Verwendung von <branch-link>{owner}/{branch}</branch-link> @ {commit} erstellt."
|
||||
"message": "Falls du die offizielle Modrinth-Website aufrufen wolltest, besuche {url}. Diese Vorschauversion wird von Modrinth-Mitarbeitern zu Testzwecken genutzt. Sie wurde unter Verwendung von <branch-link>{owner}/{branch}</branch-link> @ {commit} erstellt."
|
||||
},
|
||||
"layout.banner.preview.title": {
|
||||
"message": "Dies ist eine Vorschauversion der Modrinth-Webseite."
|
||||
@@ -1922,6 +2012,9 @@
|
||||
"moderation.moderate": {
|
||||
"message": "Moderieren"
|
||||
},
|
||||
"moderation.page.external-projects": {
|
||||
"message": "Externe Projekte"
|
||||
},
|
||||
"moderation.page.projects": {
|
||||
"message": "Projekte"
|
||||
},
|
||||
@@ -2552,6 +2645,30 @@
|
||||
"project.license.title": {
|
||||
"message": "Lizenz"
|
||||
},
|
||||
"project.moderation.admonition.approved.body.private": {
|
||||
"message": "Dein Projekt ist privat, was bedeutet, dass nur du und von dir eingeladene Personen darauf zugreifen können."
|
||||
},
|
||||
"project.moderation.admonition.approved.header": {
|
||||
"message": "Projekt genehmigt"
|
||||
},
|
||||
"project.moderation.admonition.rejected.header": {
|
||||
"message": "Änderungen angefragt"
|
||||
},
|
||||
"project.moderation.admonition.under-review.body.1": {
|
||||
"message": "Dein Projekt ist in der Warteschlange, um von Modrinths Moderationsteam überprüft zu werden."
|
||||
},
|
||||
"project.moderation.admonition.under-review.header": {
|
||||
"message": "Projekt wird überprüft"
|
||||
},
|
||||
"project.moderation.admonition.withheld.header": {
|
||||
"message": "Von Mitarbeitern entlistet"
|
||||
},
|
||||
"project.moderation.error.unauthorized": {
|
||||
"message": "Nicht autorisiert"
|
||||
},
|
||||
"project.moderation.thread.title": {
|
||||
"message": "Moderationsnachrichten"
|
||||
},
|
||||
"project.moderation.title": {
|
||||
"message": "Moderation"
|
||||
},
|
||||
@@ -2615,6 +2732,45 @@
|
||||
"project.settings.general.url.title": {
|
||||
"message": "URL"
|
||||
},
|
||||
"project.settings.permissions.attention-needed.description.proj-approved": {
|
||||
"message": "Bitte lege Nachweise vor, dass du die Erlaubnis zur Weiterverbreitung fgolgender Dateien hast; alle zurückgehaltenen Versionen werden automatisch veröffentlicht."
|
||||
},
|
||||
"project.settings.permissions.attention-needed.description.proj-draft": {
|
||||
"message": "Bitte lege Nachweise vor, dass du die Erlaubnis zur Weiterverbreitung fgolgender Dateien hast, bevor du dein Projekt zur überprüfung einreichen kannst."
|
||||
},
|
||||
"project.settings.permissions.attention-needed.title": {
|
||||
"message": "Unbekannter eingebetteter Inhalt"
|
||||
},
|
||||
"project.settings.permissions.completed.description": {
|
||||
"message": "Alle externen Inhalte haben Quellenangaben."
|
||||
},
|
||||
"project.settings.permissions.completed.title": {
|
||||
"message": "Zuordnungen abgschlossen!"
|
||||
},
|
||||
"project.settings.permissions.empty-state.description": {
|
||||
"message": "Keine deiner Versionen enrhalten externe Inhalte, also musst du dir keine Sorgen über die Einholung von Berechtigungen machen."
|
||||
},
|
||||
"project.settings.permissions.empty-state.heading": {
|
||||
"message": "Du bist startklar!"
|
||||
},
|
||||
"project.settings.permissions.fail.description": {
|
||||
"message": "Du hast keine Berechtigung, einige der von dir hinzugefügten externen Inhalte weiterzuverbreiten. Um auf Modrinth veröffentlichen zu können, entferne bitte die urheberrechtswidrigen Inhalte."
|
||||
},
|
||||
"project.settings.permissions.fail.title": {
|
||||
"message": "Einige Inhalte können nicht enthalten sein"
|
||||
},
|
||||
"project.settings.permissions.info-banner.description": {
|
||||
"message": "Wenn du Inhalte einbindest, welche nicht auf Modrinth gehostet werden, musst du uns deren Quelle mitteilen und bestätigen, dass du die Berechtigung zur Verbreitung der Dateien hast. In <link>unserer Anleitung (Englisch)</link> lernst du, wie du dies am besten machst!"
|
||||
},
|
||||
"project.settings.permissions.info-banner.title": {
|
||||
"message": "Lerne wie Zuschreibungen funktionieren"
|
||||
},
|
||||
"project.settings.permissions.learn-more": {
|
||||
"message": "Mehr erfahren"
|
||||
},
|
||||
"project.settings.permissions.search-placeholder": {
|
||||
"message": "Durchsuche {count} {count, plural, one {externes Projekt} other {externe Projekte}}..."
|
||||
},
|
||||
"project.settings.title": {
|
||||
"message": "Einstellungen"
|
||||
},
|
||||
@@ -2633,6 +2789,15 @@
|
||||
"project.versions.title": {
|
||||
"message": "Versionen"
|
||||
},
|
||||
"project.versions.withheld-versions-warning.description": {
|
||||
"message": "{count, plural, one {Diese Version ist} other {Diese Versionen sind}} derzeit zurückgehalten und nicht öffentlich gelistet. Bitte stelle einen Nachweis bereit, dass du die Erlaubnis hast, bestimmte Dateien weiterzuverbreiten, die in {count, plural, one {der Modpack-Version} other {den Modpack-Versionen}} enthalten sind."
|
||||
},
|
||||
"project.versions.withheld-versions-warning.resolve-button": {
|
||||
"message": "Beheben"
|
||||
},
|
||||
"project.versions.withheld-versions-warning.title": {
|
||||
"message": "{count, plural, one {Version {version_name}} other {Versionen}} zurückgehalten aufgrund von unbekannten eingebetteten Inhalten"
|
||||
},
|
||||
"report.already-reported": {
|
||||
"message": "Du hast bereits {title} gemeldet"
|
||||
},
|
||||
@@ -3002,6 +3167,9 @@
|
||||
"search.filter.locked.server.sync": {
|
||||
"message": "Mit Server synchronisieren"
|
||||
},
|
||||
"servers.manage.content.title": {
|
||||
"message": "Inhalt - {serverName} - Modrinth"
|
||||
},
|
||||
"servers.notice.actions": {
|
||||
"message": "Aktionen"
|
||||
},
|
||||
|
||||
@@ -407,6 +407,51 @@
|
||||
"collection.title": {
|
||||
"message": "{name} - Kollektion"
|
||||
},
|
||||
"conversation-thread.action.add-private-note": {
|
||||
"message": "Private Notiz hinzufügen"
|
||||
},
|
||||
"conversation-thread.action.approve": {
|
||||
"message": "Genehmigen"
|
||||
},
|
||||
"conversation-thread.action.approve-with-reply": {
|
||||
"message": "Mit Antwort genehmigen"
|
||||
},
|
||||
"conversation-thread.action.close-thread": {
|
||||
"message": ""
|
||||
},
|
||||
"conversation-thread.action.reject": {
|
||||
"message": "Ablehnen"
|
||||
},
|
||||
"conversation-thread.action.reject-with-reply": {
|
||||
"message": "Mit Antwort ablehnen"
|
||||
},
|
||||
"conversation-thread.action.reply": {
|
||||
"message": "Antworten"
|
||||
},
|
||||
"conversation-thread.action.send": {
|
||||
"message": "Senden"
|
||||
},
|
||||
"conversation-thread.action.withhold": {
|
||||
"message": "Zurückhalten"
|
||||
},
|
||||
"conversation-thread.action.withhold-with-reply": {
|
||||
"message": "Mit Antwort zurückhalten"
|
||||
},
|
||||
"conversation-thread.reply-editor.placeholder.send": {
|
||||
"message": "Sende eine Nachricht..."
|
||||
},
|
||||
"conversation-thread.reply-modal.confirmation.label": {
|
||||
"message": "Mir ist bewusst, dass dieser Thread nicht aktiv von den Moderatoren überwacht wird."
|
||||
},
|
||||
"conversation-thread.resubmit-modal.description": {
|
||||
"message": "Du schickst <project-title>{projectTitle}</project-title> zur Wiederprüfung."
|
||||
},
|
||||
"conversation-thread.resubmit-modal.header.resubmitting": {
|
||||
"message": "Erneut zur Überprüfung einreichen"
|
||||
},
|
||||
"conversation-thread.resubmit-modal.header.submitting": {
|
||||
"message": "Erneut zur Überprüfung einreichen"
|
||||
},
|
||||
"create-project-version.create-modal.stage.add-files.admonition": {
|
||||
"message": "Ergänzende Dateien dienen zur Unterstützung von Ressourcen wie Quellcode, nicht für alternative Versionen oder Varianten."
|
||||
},
|
||||
@@ -875,24 +920,9 @@
|
||||
"dashboard.head-title": {
|
||||
"message": "Dashboard"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "Alle als gelesen markieren"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "Verlauf ansehen"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "Du hast keine ungelesenen Benachrichtigungen."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "Fehler beim Laden von Benachrichtigungen:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "Verlauf"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "Benachrichtigungsverlauf"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "Alle ansehen"
|
||||
},
|
||||
@@ -902,9 +932,6 @@
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "Siehe {extraNotifs} weitere {extraNotifs, plural, one {Benachrichtigung} other {Benachrichtigungen}} an"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "Benachrichtigungen werden geladen..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "Organisation erstellen"
|
||||
},
|
||||
@@ -920,6 +947,27 @@
|
||||
"dashboard.organizations.title": {
|
||||
"message": "Organisationen"
|
||||
},
|
||||
"dashboard.overview.notifications.button.mark-all-as-read": {
|
||||
"message": "Alle als gelesen markieren"
|
||||
},
|
||||
"dashboard.overview.notifications.button.view-history": {
|
||||
"message": "Verlauf ansehen"
|
||||
},
|
||||
"dashboard.overview.notifications.empty.no-unread": {
|
||||
"message": "Du hast keine ungelesenen Benachrichtigungen."
|
||||
},
|
||||
"dashboard.overview.notifications.error.loading": {
|
||||
"message": "Fehler beim Laden von Benachrichtigungen:"
|
||||
},
|
||||
"dashboard.overview.notifications.history.label": {
|
||||
"message": "Verlauf"
|
||||
},
|
||||
"dashboard.overview.notifications.history.title": {
|
||||
"message": "Benachrichtigungsverlauf"
|
||||
},
|
||||
"dashboard.overview.notifications.loading": {
|
||||
"message": "Benachrichtigungen werden geladen..."
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "Du kannst mehrere Projekte gleichzeitig bearbeiten, indem du sie unten auswählst."
|
||||
},
|
||||
@@ -1229,6 +1277,33 @@
|
||||
"dashboard.withdraw.error.tax-form.title": {
|
||||
"message": "Bitte Steuerformular ausfüllen"
|
||||
},
|
||||
"discover.install.back-to-server": {
|
||||
"message": "Zurück zum Server"
|
||||
},
|
||||
"discover.install.back-to-setup": {
|
||||
"message": "Zurück zur Einrichtung"
|
||||
},
|
||||
"discover.install.cancel-reset": {
|
||||
"message": "Zurücksetzung abbrechen"
|
||||
},
|
||||
"discover.install.error.no-server-world": {
|
||||
"message": "Keine Serverwelt ist zur Installation verfügbar."
|
||||
},
|
||||
"discover.install.error.unsupported-content-type": {
|
||||
"message": "Dieser Inhaltstyp kann nicht vom Durchstöbern zu einem Server hinzugefügt werden."
|
||||
},
|
||||
"discover.install.heading.reset-modpack": {
|
||||
"message": "Modpack zur Installation nach dem Zurücksetzen auswählen"
|
||||
},
|
||||
"discover.seo.description": {
|
||||
"message": "Durchsuche und entdecke tausende Minecraft {projectType, select, mod {Mods} modpack {Modpacks} resourcepack {Ressourcenpakete} shader {Shader} plugin {Plugins} datapack {Datenpakete} other {Projekte}} auf Modrinth mit sofortigen und präzisen Suchergebnissen. Unsere Filter helfen dir dabei, schnell die besten Minecraft {projectType, select, mod {Mods} modpack {Modpacks} resourcepack {Ressourcenpakete} shader {Shader} plugin {Plugins} datapack {Datenpakete} other {Projekte}} zu finden."
|
||||
},
|
||||
"discover.seo.title": {
|
||||
"message": "Durchsuche {projectType, select, mod {Mods} modpack {Modpacks} resourcepack {Ressourcenpakete} shader {Shader} plugin {Plugins} datapack {Datenpakete} other {Projekte}}"
|
||||
},
|
||||
"discover.seo.title-with-query": {
|
||||
"message": "Durchsuche {projectType, select, mod {Mods} modpack {Modpacks} resourcepack {Ressourcenpakete} shader {Shader} plugin {Plugins} datapack {Datenpakete} other {Projekte}} | {query}"
|
||||
},
|
||||
"error.collection.404.list_item.1": {
|
||||
"message": "Eventuell hast du die URL der Kollektion falsch eingegeben."
|
||||
},
|
||||
@@ -1244,6 +1319,12 @@
|
||||
"error.collection.404.title": {
|
||||
"message": "Kollektion nicht gefunden"
|
||||
},
|
||||
"error.generic.401.signed-in-as": {
|
||||
"message": "Du bist aktuell angemeldet als:"
|
||||
},
|
||||
"error.generic.401.title": {
|
||||
"message": "Du hast keinen Zugriff auf diese Seite"
|
||||
},
|
||||
"error.generic.404.subtitle": {
|
||||
"message": "Die von dir gesuchte Seite scheint nicht zu existieren."
|
||||
},
|
||||
@@ -1670,6 +1751,9 @@
|
||||
"layout.action.create-new": {
|
||||
"message": "Erstellen..."
|
||||
},
|
||||
"layout.action.external-projects": {
|
||||
"message": "Externe Projekte"
|
||||
},
|
||||
"layout.action.file-lookup": {
|
||||
"message": "Dateisuche"
|
||||
},
|
||||
@@ -1718,14 +1802,20 @@
|
||||
"layout.banner.add-email.description": {
|
||||
"message": "Aus Sicherheitsgründen verlangt Modrinth, dass du eine E-Mail-Adresse für dein Konto registrierst."
|
||||
},
|
||||
"layout.banner.build-fail.always-ignore": {
|
||||
"message": "Immer ignorieren"
|
||||
},
|
||||
"layout.banner.build-fail.description": {
|
||||
"message": "Dieses Deployment des Modrinth-Frontends konnte keinen Status aus der API generieren. Dies kann an einer Störung oder einem Fehler in der Konfiguration liegen. Erneut erstellen, wenn die API verfügbar ist. Fehlercodes: {errors}; Aktuelle API-URL lautet: {url}"
|
||||
},
|
||||
"layout.banner.build-fail.ignore": {
|
||||
"message": "Ignorieren"
|
||||
},
|
||||
"layout.banner.build-fail.title": {
|
||||
"message": "Fehler beim Generieren des Status aus der API beim Erstellen."
|
||||
},
|
||||
"layout.banner.preview.description": {
|
||||
"message": "Falls du die offizielle Modrinth-Website aufrufen wolltest, besuch <link>https://modrinth.com</link>. Diese Vorschauversion wird von Modrinth-Mitarbeitern zu Testzwecken genutzt. Sie wurde unter Verwendung von <branch-link>{owner}/{branch}</branch-link> @ {commit} erstellt."
|
||||
"message": "Falls du die offizielle Modrinth-Website aufrufen wolltest, besuche {url}. Diese Vorschauversion wird von Modrinth-Mitarbeitern zu Testzwecken genutzt. Sie wurde unter Verwendung von <branch-link>{owner}/{branch}</branch-link> @ {commit} erstellt."
|
||||
},
|
||||
"layout.banner.preview.title": {
|
||||
"message": "Dies ist eine Vorschauversion der Modrinth-Website."
|
||||
@@ -1922,6 +2012,9 @@
|
||||
"moderation.moderate": {
|
||||
"message": "Moderieren"
|
||||
},
|
||||
"moderation.page.external-projects": {
|
||||
"message": "Externe Projekte"
|
||||
},
|
||||
"moderation.page.projects": {
|
||||
"message": "Projekte"
|
||||
},
|
||||
@@ -2552,6 +2645,30 @@
|
||||
"project.license.title": {
|
||||
"message": "Lizenz"
|
||||
},
|
||||
"project.moderation.admonition.approved.body.private": {
|
||||
"message": "Dein Projekt ist privat, was bedeutet, dass nur du und von dir eingeladene Personen darauf zugreifen können."
|
||||
},
|
||||
"project.moderation.admonition.approved.header": {
|
||||
"message": "Projekt genehmigt"
|
||||
},
|
||||
"project.moderation.admonition.rejected.header": {
|
||||
"message": "Änderungen angefragt"
|
||||
},
|
||||
"project.moderation.admonition.under-review.body.1": {
|
||||
"message": "Dein Projekt ist in der Warteschlange, um von Modrinths Moderationsteam überprüft zu werden."
|
||||
},
|
||||
"project.moderation.admonition.under-review.header": {
|
||||
"message": "Projekt wird überprüft"
|
||||
},
|
||||
"project.moderation.admonition.withheld.header": {
|
||||
"message": "Von Mitarbeitern entlistet"
|
||||
},
|
||||
"project.moderation.error.unauthorized": {
|
||||
"message": "Nicht autorisiert"
|
||||
},
|
||||
"project.moderation.thread.title": {
|
||||
"message": "Moderationsnachrichten"
|
||||
},
|
||||
"project.moderation.title": {
|
||||
"message": "Moderation"
|
||||
},
|
||||
@@ -2615,6 +2732,45 @@
|
||||
"project.settings.general.url.title": {
|
||||
"message": "URL"
|
||||
},
|
||||
"project.settings.permissions.attention-needed.description.proj-approved": {
|
||||
"message": "Bitte lege Nachweise vor, dass du die Erlaubnis zur Weiterverbreitung folgender Dateien hast; alle zurückgehaltenen Versionen werden automatisch veröffentlicht."
|
||||
},
|
||||
"project.settings.permissions.attention-needed.description.proj-draft": {
|
||||
"message": "Bitte lege Nachweise vor, dass du die Erlaubnis zur Weiterverbreitung aller folgenden Dateien hast, bevor du dein Projekt zur Überprüfung einreichen kannst."
|
||||
},
|
||||
"project.settings.permissions.attention-needed.title": {
|
||||
"message": "Unbekannter eingebetteter Inhalt"
|
||||
},
|
||||
"project.settings.permissions.completed.description": {
|
||||
"message": "Alle externen Inhalte haben Quellenangaben."
|
||||
},
|
||||
"project.settings.permissions.completed.title": {
|
||||
"message": "Quellenangaben vollständig!"
|
||||
},
|
||||
"project.settings.permissions.empty-state.description": {
|
||||
"message": "Keine deiner Versionen enthält externe Inhalte, du musst du dir keine Gedanken über die Einholung von Genehmigungen machen."
|
||||
},
|
||||
"project.settings.permissions.empty-state.heading": {
|
||||
"message": "Du bist startklar!"
|
||||
},
|
||||
"project.settings.permissions.fail.description": {
|
||||
"message": "Du hast keine Berechtigung, einige der von dir hinzugefügten externen Inhalte weiterzuverbreiten. Entferne die urheberrechtsverletzenden Inhalte, um auf Modrinth veröffentlichen zu können."
|
||||
},
|
||||
"project.settings.permissions.fail.title": {
|
||||
"message": "Bestimmte Inhalte können nicht enthalten sein"
|
||||
},
|
||||
"project.settings.permissions.info-banner.description": {
|
||||
"message": "Wenn du Inhalte einbindest, welche nicht auf Modrinth gehostet werden, musst du uns deren Quelle mitteilen und bestätigen, dass du die Berechtigung zur Verbreitung der Dateien hast. In <link>unserer Anleitung</link> lernst du, wie du dies am besten machst!"
|
||||
},
|
||||
"project.settings.permissions.info-banner.title": {
|
||||
"message": "So funktionieren Quellenangaben"
|
||||
},
|
||||
"project.settings.permissions.learn-more": {
|
||||
"message": "Mehr erfahren"
|
||||
},
|
||||
"project.settings.permissions.search-placeholder": {
|
||||
"message": "Durchsuche {count} {count, plural, one {externes Projekt} other {externe Projekte}}..."
|
||||
},
|
||||
"project.settings.title": {
|
||||
"message": "Einstellungen"
|
||||
},
|
||||
@@ -2633,6 +2789,15 @@
|
||||
"project.versions.title": {
|
||||
"message": "Versionen"
|
||||
},
|
||||
"project.versions.withheld-versions-warning.description": {
|
||||
"message": "{count, plural, one {Diese Version ist} other {Diese Versionen sind}} derzeit zurückgehalten und nicht öffentlich gelistet. Bitte stelle einen Nachweis bereit, dass du die Erlaubnis hast, bestimmte Dateien weiterzuverbreiten, die im Modpack in {count, plural, one {Version} other {Versionen}} enthalten sind."
|
||||
},
|
||||
"project.versions.withheld-versions-warning.resolve-button": {
|
||||
"message": "Beheben"
|
||||
},
|
||||
"project.versions.withheld-versions-warning.title": {
|
||||
"message": "{count, plural, one {Version {version_name} wurde} other {Versionen wurden}} aufgrund unbekannter eingebetteter Inhalte zurückgehalten"
|
||||
},
|
||||
"report.already-reported": {
|
||||
"message": "Du hast {title} bereits gemeldet"
|
||||
},
|
||||
@@ -3002,6 +3167,9 @@
|
||||
"search.filter.locked.server.sync": {
|
||||
"message": "Mit Server synchronisieren"
|
||||
},
|
||||
"servers.manage.content.title": {
|
||||
"message": "Inhalt - {serverName} - Modrinth"
|
||||
},
|
||||
"servers.notice.actions": {
|
||||
"message": "Aktionen"
|
||||
},
|
||||
@@ -3561,7 +3729,7 @@
|
||||
"message": "Methode aktualisieren"
|
||||
},
|
||||
"settings.billing.upgrade": {
|
||||
"message": "Verbessern"
|
||||
"message": "Upgraden"
|
||||
},
|
||||
"settings.display.banner.developer-mode.button": {
|
||||
"message": "Entwicklermodus deaktivieren"
|
||||
|
||||
@@ -407,6 +407,117 @@
|
||||
"collection.title": {
|
||||
"message": "{name} - Collection"
|
||||
},
|
||||
"conversation-thread.action.add-private-note": {
|
||||
"message": "Add private note"
|
||||
},
|
||||
"conversation-thread.action.approve": {
|
||||
"message": "Approve"
|
||||
},
|
||||
"conversation-thread.action.approve-with-reply": {
|
||||
"message": "Approve with reply"
|
||||
},
|
||||
"conversation-thread.action.close-thread": {
|
||||
"message": "Close thread"
|
||||
},
|
||||
"conversation-thread.action.close-with-reply": {
|
||||
"message": "Close with reply"
|
||||
},
|
||||
"conversation-thread.action.reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"conversation-thread.action.reject-with-reply": {
|
||||
"message": "Reject with reply"
|
||||
},
|
||||
"conversation-thread.action.reopen-thread": {
|
||||
"message": "Reopen thread"
|
||||
},
|
||||
"conversation-thread.action.reply": {
|
||||
"message": "Reply"
|
||||
},
|
||||
"conversation-thread.action.reply-to-thread": {
|
||||
"message": "Reply to thread"
|
||||
},
|
||||
"conversation-thread.action.resubmit-for-review": {
|
||||
"message": "Resubmit for review"
|
||||
},
|
||||
"conversation-thread.action.resubmit-for-review-with-reply": {
|
||||
"message": "Resubmit for review with reply"
|
||||
},
|
||||
"conversation-thread.action.send": {
|
||||
"message": "Send"
|
||||
},
|
||||
"conversation-thread.action.send-to-review": {
|
||||
"message": "Send to review"
|
||||
},
|
||||
"conversation-thread.action.send-to-review-with-reply": {
|
||||
"message": "Send to review with reply"
|
||||
},
|
||||
"conversation-thread.action.set-to-draft": {
|
||||
"message": "Set to draft"
|
||||
},
|
||||
"conversation-thread.action.set-to-draft-with-reply": {
|
||||
"message": "Set to draft with reply"
|
||||
},
|
||||
"conversation-thread.action.withhold": {
|
||||
"message": "Withhold"
|
||||
},
|
||||
"conversation-thread.action.withhold-with-reply": {
|
||||
"message": "Withhold with reply"
|
||||
},
|
||||
"conversation-thread.closed-thread.description": {
|
||||
"message": "This thread is closed and new messages cannot be sent to it."
|
||||
},
|
||||
"conversation-thread.error.closing-report": {
|
||||
"message": "Error closing report"
|
||||
},
|
||||
"conversation-thread.error.reopening-report": {
|
||||
"message": "Error reopening report"
|
||||
},
|
||||
"conversation-thread.error.sending-message": {
|
||||
"message": "Error sending message"
|
||||
},
|
||||
"conversation-thread.reply-editor.placeholder.reply": {
|
||||
"message": "Reply to thread..."
|
||||
},
|
||||
"conversation-thread.reply-editor.placeholder.send": {
|
||||
"message": "Send a message..."
|
||||
},
|
||||
"conversation-thread.reply-modal.confirmation.description": {
|
||||
"message": "Confirm moderators do not actively monitor this"
|
||||
},
|
||||
"conversation-thread.reply-modal.confirmation.label": {
|
||||
"message": "I acknowledge that the moderators do not actively monitor the thread."
|
||||
},
|
||||
"conversation-thread.reply-modal.description": {
|
||||
"message": "Your project is already approved. As such, the moderation team does not actively monitor this thread. However, they may still see your message if there is a problem with your project."
|
||||
},
|
||||
"conversation-thread.reply-modal.header": {
|
||||
"message": "Reply to thread"
|
||||
},
|
||||
"conversation-thread.reply-modal.help-center-note": {
|
||||
"message": "If you need to get in contact with the moderation team, please use the <help-center-link>Modrinth Help Center</help-center-link> and click the blue bubble in the bottom right corner to contact support."
|
||||
},
|
||||
"conversation-thread.resubmit-modal.confirmation.description": {
|
||||
"message": "Confirm I have addressed the messages from the moderators"
|
||||
},
|
||||
"conversation-thread.resubmit-modal.confirmation.label": {
|
||||
"message": "I confirm that I have properly addressed the moderators' comments."
|
||||
},
|
||||
"conversation-thread.resubmit-modal.description": {
|
||||
"message": "You're submitting <project-title>{projectTitle}</project-title> to be reviewed again by the moderators."
|
||||
},
|
||||
"conversation-thread.resubmit-modal.header.resubmitting": {
|
||||
"message": "Resubmitting for review"
|
||||
},
|
||||
"conversation-thread.resubmit-modal.header.submitting": {
|
||||
"message": "Submitting for review"
|
||||
},
|
||||
"conversation-thread.resubmit-modal.reminder": {
|
||||
"message": "Make sure you have addressed all the comments from the moderation team."
|
||||
},
|
||||
"conversation-thread.resubmit-modal.warning": {
|
||||
"message": "Repeated submissions without addressing the moderators' comments may result in an account suspension."
|
||||
},
|
||||
"create-project-version.create-modal.stage.add-files.admonition": {
|
||||
"message": "Supplementary files are for supporting resources like source code, not for alternative versions or variants."
|
||||
},
|
||||
@@ -875,23 +986,8 @@
|
||||
"dashboard.head-title": {
|
||||
"message": "Dashboard"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "Mark all as read"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "View history"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "You don't have any unread notifications."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "Error loading notifications:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "History"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "Notification history"
|
||||
"message": "You have no unread notifications."
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "See all"
|
||||
@@ -902,9 +998,6 @@
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "View {extraNotifs} more {extraNotifs, plural, one {notification} other {notifications}}"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "Loading notifications..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "Create organization"
|
||||
},
|
||||
@@ -920,6 +1013,27 @@
|
||||
"dashboard.organizations.title": {
|
||||
"message": "Organizations"
|
||||
},
|
||||
"dashboard.overview.notifications.button.mark-all-as-read": {
|
||||
"message": "Mark all as read"
|
||||
},
|
||||
"dashboard.overview.notifications.button.view-history": {
|
||||
"message": "View history"
|
||||
},
|
||||
"dashboard.overview.notifications.empty.no-unread": {
|
||||
"message": "You don't have any unread notifications."
|
||||
},
|
||||
"dashboard.overview.notifications.error.loading": {
|
||||
"message": "Error loading notifications:"
|
||||
},
|
||||
"dashboard.overview.notifications.history.label": {
|
||||
"message": "History"
|
||||
},
|
||||
"dashboard.overview.notifications.history.title": {
|
||||
"message": "Notification history"
|
||||
},
|
||||
"dashboard.overview.notifications.loading": {
|
||||
"message": "Loading notifications..."
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "You can edit multiple projects at once by selecting them below."
|
||||
},
|
||||
@@ -1241,12 +1355,6 @@
|
||||
"discover.install.error.no-server-world": {
|
||||
"message": "No server world is available for install."
|
||||
},
|
||||
"discover.install.error.some-projects-failed.description": {
|
||||
"message": "Failed projects were not added. You can try installing them again."
|
||||
},
|
||||
"discover.install.error.some-projects-failed.title": {
|
||||
"message": "Some projects failed to install"
|
||||
},
|
||||
"discover.install.error.unsupported-content-type": {
|
||||
"message": "This content type cannot be installed to a server from browse."
|
||||
},
|
||||
@@ -1428,7 +1536,7 @@
|
||||
"message": "Where are Modrinth Hosting servers located? Can I choose a region?"
|
||||
},
|
||||
"hosting-marketing.faq.location.answer": {
|
||||
"message": "We have servers available in North America, Europe, and Southeast Asia at the moment that you can choose upon purchase. More regions to come in the future! If you'd like to switch your region, please contact support."
|
||||
"message": "We have servers available across North America, Europe, and Southeast Asia at the moment that you can choose upon purchase. More regions to come in the future! If you'd like to switch your region, please contact support."
|
||||
},
|
||||
"hosting-marketing.faq.versions-loaders": {
|
||||
"message": "What Minecraft versions and loaders can be used?"
|
||||
@@ -1760,14 +1868,20 @@
|
||||
"layout.banner.add-email.description": {
|
||||
"message": "For security reasons, Modrinth needs you to register an email address to your account."
|
||||
},
|
||||
"layout.banner.build-fail.always-ignore": {
|
||||
"message": "Always ignore"
|
||||
},
|
||||
"layout.banner.build-fail.description": {
|
||||
"message": "This deploy of Modrinth's frontend failed to generate state from the API. This may be due to an outage or an error in configuration. Rebuild when the API is available. Error codes: {errors}; Current API URL is: {url}"
|
||||
},
|
||||
"layout.banner.build-fail.ignore": {
|
||||
"message": "Ignore"
|
||||
},
|
||||
"layout.banner.build-fail.title": {
|
||||
"message": "Error generating state from API when building."
|
||||
},
|
||||
"layout.banner.preview.description": {
|
||||
"message": "If you meant to access the official Modrinth website, visit <link>https://modrinth.com</link>. This preview deploy is used by Modrinth staff for testing purposes. It was built using <branch-link>{owner}/{branch}</branch-link> @ {commit}."
|
||||
"message": "If you meant to access the official Modrinth website, visit {url}. This preview deploy is used by Modrinth staff for testing purposes. It was built using <branch-link>{owner}/{branch}</branch-link> @ {commit}."
|
||||
},
|
||||
"layout.banner.preview.title": {
|
||||
"message": "This is a preview deploy of the Modrinth website."
|
||||
@@ -2597,6 +2711,84 @@
|
||||
"project.license.title": {
|
||||
"message": "License"
|
||||
},
|
||||
"project.moderation.admonition.approved.body.private": {
|
||||
"message": "Your project is private, meaning it can only be accessed by you and people you invite."
|
||||
},
|
||||
"project.moderation.admonition.approved.body.public": {
|
||||
"message": "Your project is published and discoverable on Modrinth."
|
||||
},
|
||||
"project.moderation.admonition.approved.body.unlisted": {
|
||||
"message": "Your project is unlisted, meaning it can only be accessed with a direct link and is not discoverable on Modrinth."
|
||||
},
|
||||
"project.moderation.admonition.approved.body.visibility-message": {
|
||||
"message": "You can change the visibility of your project in your project's <visibility-settings-link>visibility settings</visibility-settings-link>."
|
||||
},
|
||||
"project.moderation.admonition.approved.header": {
|
||||
"message": "Project approved"
|
||||
},
|
||||
"project.moderation.admonition.draft.body": {
|
||||
"message": "This is a draft project that cannot be seen by others until submitted for review and approved by Modrinth's moderation team."
|
||||
},
|
||||
"project.moderation.admonition.draft.header": {
|
||||
"message": "Draft project"
|
||||
},
|
||||
"project.moderation.admonition.draft.submit-for-review": {
|
||||
"message": "Once you have completed all required steps and ensured your project complies with Modrinth's <rules-link>Content Rules</rules-link> you can submit your project for review."
|
||||
},
|
||||
"project.moderation.admonition.rejected.address-all-concerns": {
|
||||
"message": "Please address all moderation concerns, including any issues listed in messages below, before resubmitting this project."
|
||||
},
|
||||
"project.moderation.admonition.rejected.header": {
|
||||
"message": "Changes requested"
|
||||
},
|
||||
"project.moderation.admonition.rejected.spam-notice": {
|
||||
"message": "Repeatedly submitting your project without addressing all moderation concerns first may result in account suspension."
|
||||
},
|
||||
"project.moderation.admonition.under-review.body.1": {
|
||||
"message": "Your project is in queue to be reviewed by Modrinth's moderation team."
|
||||
},
|
||||
"project.moderation.admonition.under-review.body.2": {
|
||||
"message": "Your project will be scanned and then reviewed by human moderators to ensure it meets Modrinth's <rules-link>Content Rules</rules-link> and <terms-link>Terms of Use</terms-link>."
|
||||
},
|
||||
"project.moderation.admonition.under-review.body.3": {
|
||||
"message": "You can still modify your project, it won't affect your position in the queue."
|
||||
},
|
||||
"project.moderation.admonition.under-review.body.4": {
|
||||
"message": "We aim to review submissions in 24-48 hours, but some projects may face delays. This does not reflect an issue with your submission."
|
||||
},
|
||||
"project.moderation.admonition.under-review.body.5": {
|
||||
"message": "<emphasis>We appreciate your patience while our moderators work hard to keep Modrinth safe, and look forward to helping you share your content! 💚</emphasis>"
|
||||
},
|
||||
"project.moderation.admonition.under-review.header": {
|
||||
"message": "Project under review"
|
||||
},
|
||||
"project.moderation.admonition.withheld.body": {
|
||||
"message": "Your project will not appear publicly and can only be accessed with a direct link.{requestedStatus, select, unlisted { Based on your selected <visibility-settings-link>visibility settings</visibility-settings-link>, most likely no action is necessary.} other { Please address all moderation concerns, including any issues listed in messages below before resubmitting this project.}}"
|
||||
},
|
||||
"project.moderation.admonition.withheld.header": {
|
||||
"message": "Unlisted by staff"
|
||||
},
|
||||
"project.moderation.error.unauthorized": {
|
||||
"message": "Unauthorized"
|
||||
},
|
||||
"project.moderation.thread.approved-warning": {
|
||||
"message": "This thread is not actively monitored, but may be reviewed for information about your project if needed."
|
||||
},
|
||||
"project.moderation.thread.help-center-note.1": {
|
||||
"message": "Content moderators cannot provide support for most issues and messages to this thread do not notify staff."
|
||||
},
|
||||
"project.moderation.thread.help-center-note.2": {
|
||||
"message": "If you need assistance or have additional inquiries, please visit the <help-center-link>Modrinth Help Center</help-center-link> and click the blue bubble to contact support."
|
||||
},
|
||||
"project.moderation.thread.moderator-see-user-ui-toggle": {
|
||||
"message": "Show member UI"
|
||||
},
|
||||
"project.moderation.thread.private-description": {
|
||||
"message": "This is a private conversation thread with the Modrinth moderators. They may message you with issues concerning this project."
|
||||
},
|
||||
"project.moderation.thread.title": {
|
||||
"message": "Moderation messages"
|
||||
},
|
||||
"project.moderation.title": {
|
||||
"message": "Moderation"
|
||||
},
|
||||
|
||||
@@ -407,6 +407,96 @@
|
||||
"collection.title": {
|
||||
"message": "{name} - Colección"
|
||||
},
|
||||
"conversation-thread.action.add-private-note": {
|
||||
"message": "Añadir nota privada"
|
||||
},
|
||||
"conversation-thread.action.approve": {
|
||||
"message": "Aprovar"
|
||||
},
|
||||
"conversation-thread.action.approve-with-reply": {
|
||||
"message": "Aprovar con respuesta"
|
||||
},
|
||||
"conversation-thread.action.close-thread": {
|
||||
"message": "Cerrar hilo"
|
||||
},
|
||||
"conversation-thread.action.close-with-reply": {
|
||||
"message": "Cerrar con respuesta"
|
||||
},
|
||||
"conversation-thread.action.reject": {
|
||||
"message": "Rechazar"
|
||||
},
|
||||
"conversation-thread.action.reject-with-reply": {
|
||||
"message": "Rechazar con respuesta"
|
||||
},
|
||||
"conversation-thread.action.reopen-thread": {
|
||||
"message": "Reabrir hilo"
|
||||
},
|
||||
"conversation-thread.action.reply": {
|
||||
"message": "Responder"
|
||||
},
|
||||
"conversation-thread.action.reply-to-thread": {
|
||||
"message": "Responder hilo"
|
||||
},
|
||||
"conversation-thread.action.resubmit-for-review": {
|
||||
"message": "Resubir para revisión"
|
||||
},
|
||||
"conversation-thread.action.resubmit-for-review-with-reply": {
|
||||
"message": "Resubir para revisión con respuesta"
|
||||
},
|
||||
"conversation-thread.action.send": {
|
||||
"message": "Enviar"
|
||||
},
|
||||
"conversation-thread.action.send-to-review": {
|
||||
"message": "Enviar a revisión"
|
||||
},
|
||||
"conversation-thread.action.send-to-review-with-reply": {
|
||||
"message": "Enviar a revisión con respuesta"
|
||||
},
|
||||
"conversation-thread.action.set-to-draft": {
|
||||
"message": "Cambiar a borrador"
|
||||
},
|
||||
"conversation-thread.action.set-to-draft-with-reply": {
|
||||
"message": "Cambiar a borrador con respuesta"
|
||||
},
|
||||
"conversation-thread.action.withhold": {
|
||||
"message": "Retener"
|
||||
},
|
||||
"conversation-thread.action.withhold-with-reply": {
|
||||
"message": "Retener con respuesta"
|
||||
},
|
||||
"conversation-thread.closed-thread.description": {
|
||||
"message": "Este hilo está cerrado y no se pueden enviar nuevos mensajes."
|
||||
},
|
||||
"conversation-thread.error.closing-report": {
|
||||
"message": "Error cerrando el reporte"
|
||||
},
|
||||
"conversation-thread.error.reopening-report": {
|
||||
"message": "Error al reabrir el reporte"
|
||||
},
|
||||
"conversation-thread.error.sending-message": {
|
||||
"message": "Error al enviar el mensaje"
|
||||
},
|
||||
"conversation-thread.reply-editor.placeholder.reply": {
|
||||
"message": "Respuesta al hilo..."
|
||||
},
|
||||
"conversation-thread.reply-editor.placeholder.send": {
|
||||
"message": "Envía un mensaje..."
|
||||
},
|
||||
"conversation-thread.reply-modal.confirmation.description": {
|
||||
"message": "Confirmación que moderadores no monitorean esto activamente"
|
||||
},
|
||||
"conversation-thread.reply-modal.confirmation.label": {
|
||||
"message": "Comprendo que los moderadores no monitorean este hilo activamente."
|
||||
},
|
||||
"conversation-thread.reply-modal.description": {
|
||||
"message": "Tu proyecto ya ha sido aprovado. Por ello, el equipo de moderación no monitorea activamente este hilo. Sin embargo, puede que vean tu mensaje de todos modos de haber algún problema con tu proyecto."
|
||||
},
|
||||
"conversation-thread.reply-modal.header": {
|
||||
"message": "Responder al hilo"
|
||||
},
|
||||
"conversation-thread.reply-modal.help-center-note": {
|
||||
"message": "Si necesitas ponerte en contacto con el equipo de moderación, por favor utiliza el <help-center-link>Centro de ayuda de Modrinth</help-center-link> y haz clic en la burbuja azúl en la esquina inferior derecha para contactar soporte."
|
||||
},
|
||||
"create-project-version.create-modal.stage.add-files.admonition": {
|
||||
"message": "Los archivos complementarios sirven para recursos de apoyo, como código fuente, no para versiones o variantes alternativas."
|
||||
},
|
||||
@@ -875,24 +965,6 @@
|
||||
"dashboard.head-title": {
|
||||
"message": "Panel de control"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "Marcar todas cómo leídas"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "Ver historial"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "No tienes ningúna notificación sin leer."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "Error al cargar las notificaciones:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "Historial"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "Historial de notificaciones"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "Ver todas"
|
||||
},
|
||||
@@ -902,9 +974,6 @@
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "Ver {extraNotifs} {extraNotifs, plural,one {notificación} other {notificaciones}} más"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "Cargando notificaciones..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "Crear organización"
|
||||
},
|
||||
@@ -912,7 +981,7 @@
|
||||
"message": "¡Haz una organización!"
|
||||
},
|
||||
"dashboard.organizations.error.fetch": {
|
||||
"message": "No se pudo obtener las organizaciones"
|
||||
"message": "Fallo en obtener las organizaciones"
|
||||
},
|
||||
"dashboard.organizations.member-count": {
|
||||
"message": "{count} {count, plural,one {miembro} other {miembros}}"
|
||||
@@ -920,11 +989,29 @@
|
||||
"dashboard.organizations.title": {
|
||||
"message": "Organizaciones"
|
||||
},
|
||||
"dashboard.overview.notifications.button.mark-all-as-read": {
|
||||
"message": "Marcar todo como leído"
|
||||
},
|
||||
"dashboard.overview.notifications.button.view-history": {
|
||||
"message": "Ver historial"
|
||||
},
|
||||
"dashboard.overview.notifications.error.loading": {
|
||||
"message": "Error cargando notificaciones:"
|
||||
},
|
||||
"dashboard.overview.notifications.history.label": {
|
||||
"message": "Historial"
|
||||
},
|
||||
"dashboard.overview.notifications.history.title": {
|
||||
"message": "Historial de notificaciones"
|
||||
},
|
||||
"dashboard.overview.notifications.loading": {
|
||||
"message": "Cargando notificaciones..."
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "Puedes editar varios proyectos a la vez seleccionándolos a continuación."
|
||||
},
|
||||
"dashboard.projects.bulk-edit.server-disabled": {
|
||||
"message": "Proyectos de servidor no soportan edición masiva"
|
||||
"message": "Los proyectos no son compatible con la edición masiva de servidores"
|
||||
},
|
||||
"dashboard.projects.empty": {
|
||||
"message": "Aún no tienes ningún proyecto. Haz clic en el botón verde de arriba para empezar."
|
||||
@@ -942,19 +1029,19 @@
|
||||
"message": "Editar enlaces"
|
||||
},
|
||||
"dashboard.projects.links.changes-applied": {
|
||||
"message": "Cambios seran a aplicados a {count} {count, plural,one {proyecto} other {proyectos}}."
|
||||
"message": "Los cambios se aplicaran en <strong>{count}</strong>{count, plural,one {proyecto}other {proyectos}}."
|
||||
},
|
||||
"dashboard.projects.links.description": {
|
||||
"message": "Los enlaces que especifique a continuación se sobrescribirán en cada uno de los proyectos seleccionados. Los que deje en blanco se ignorarán. Puede eliminar un enlace de todos los proyectos seleccionados mediante el botón de la papelera."
|
||||
"message": "Cualquier enlace escrito más abajo se modificará en los proyectos seleccionados. Los que dejes en blanco serán ignorados. Puedes quitar un enlace de todos los proyectos seleccionados usando el botón de basurero."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.description": {
|
||||
"message": "Un link de invitación a tu servidor de Discord."
|
||||
"message": "Una invitación a tu servidor de Discord."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.label": {
|
||||
"message": "Invitación de Discord"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.description": {
|
||||
"message": "Un lugar para usuarios para reportar bugs, errores, y preocupaciones sobre tu proyecto."
|
||||
"message": "Un lugar donde los usuarios reporten errores, problemas y preocupaciones sobre tu proyecto."
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.label": {
|
||||
"message": "Registro de errores"
|
||||
@@ -984,7 +1071,7 @@
|
||||
"message": "Página wiki"
|
||||
},
|
||||
"dashboard.projects.notification.bulk-edit-success": {
|
||||
"message": "Edición masiva seleccionada de tus links de proyecto."
|
||||
"message": "Se editaron los enlaces de los proyectos seleccionados."
|
||||
},
|
||||
"dashboard.projects.project.icon-alt": {
|
||||
"message": "Icono de {title}"
|
||||
@@ -1229,6 +1316,33 @@
|
||||
"dashboard.withdraw.error.tax-form.title": {
|
||||
"message": "Por favor, llena el formulario fiscal"
|
||||
},
|
||||
"discover.install.back-to-server": {
|
||||
"message": "Volver al servidor"
|
||||
},
|
||||
"discover.install.back-to-setup": {
|
||||
"message": "Volver al setup"
|
||||
},
|
||||
"discover.install.cancel-reset": {
|
||||
"message": "Cancelar reinicio"
|
||||
},
|
||||
"discover.install.error.no-server-world": {
|
||||
"message": "No hay mundos de servidores disponibles para la instalación."
|
||||
},
|
||||
"discover.install.error.unsupported-content-type": {
|
||||
"message": "Este tipo de contenido no puede ser instalado a un servidor por búsqueda."
|
||||
},
|
||||
"discover.install.heading.reset-modpack": {
|
||||
"message": "Seleccionando modpack para instalar antes del reinicio"
|
||||
},
|
||||
"discover.seo.description": {
|
||||
"message": "Busca y navega miles de Minecraft {projectType, select, mod {mods} modpack {modpacks} packs de recursos {resource packs} shader {shaders} plugin {plugins} datapack {datapacks} other {projects}} en Modrinth con resultados de busqueda instantáneos y precisos. Nuestros filtradores te ayudan a encontrar rápidamente lo mejor de Minecraft {projectType, select, mod {mods} modpack {modpacks} packs de recursos {resource packs} shader {shaders} plugin {plugins} datapack {datapacks} other {projects}}."
|
||||
},
|
||||
"discover.seo.title": {
|
||||
"message": "Buscar {projectType, select, mod {mods} modpack {modpacks} packs de recursos {resource packs} shader {shaders} plugin {plugins} datapack {datapacks} other {projects}"
|
||||
},
|
||||
"discover.seo.title-with-query": {
|
||||
"message": "Buscar {projectType, select, mod {mods} modpack {modpacks} packs de recursos {resource packs} shader {shaders} plugin {plugins} datapack {datapacks} other {projects} | {query}"
|
||||
},
|
||||
"error.collection.404.list_item.1": {
|
||||
"message": "Puede que hayas escrito mal la URL de la colección."
|
||||
},
|
||||
@@ -1244,6 +1358,12 @@
|
||||
"error.collection.404.title": {
|
||||
"message": "Colección no encontrada"
|
||||
},
|
||||
"error.generic.401.signed-in-as": {
|
||||
"message": "Has iniciado sesión como:"
|
||||
},
|
||||
"error.generic.401.title": {
|
||||
"message": "No tienes acceso a esta página"
|
||||
},
|
||||
"error.generic.404.subtitle": {
|
||||
"message": "Parece que la página que estabas buscando no existe."
|
||||
},
|
||||
@@ -1659,7 +1779,7 @@
|
||||
"message": "Para jugadores"
|
||||
},
|
||||
"landing.section.for-players.tagline": {
|
||||
"message": "Descubre sobre las creaciones de {count, number}"
|
||||
"message": "Descubre más de {count, number} creaciones"
|
||||
},
|
||||
"landing.subheading": {
|
||||
"message": "Descubre, juega y comparte contenido de Minecraft a través de nuestra plataforma de código abierto creada para la comunidad."
|
||||
@@ -1670,6 +1790,9 @@
|
||||
"layout.action.create-new": {
|
||||
"message": "Crear nuevo..."
|
||||
},
|
||||
"layout.action.external-projects": {
|
||||
"message": "Proyectos externos"
|
||||
},
|
||||
"layout.action.file-lookup": {
|
||||
"message": "Buscar archivo"
|
||||
},
|
||||
@@ -1718,15 +1841,18 @@
|
||||
"layout.banner.add-email.description": {
|
||||
"message": "Por motivos de seguridad, Modrinth necesita que registres una dirección de correo electrónico en tu cuenta."
|
||||
},
|
||||
"layout.banner.build-fail.always-ignore": {
|
||||
"message": "Ignorar siempre"
|
||||
},
|
||||
"layout.banner.build-fail.description": {
|
||||
"message": "Esta implementación del frontend de Modrinth no pudo generar el estado desde la API. Esto puede deberse a una interrupción del servicio o a un error de configuración. Vuelve a construir cuando la API esté disponible. Códigos de error: {errors}; La URL actual de la API es: {url}"
|
||||
},
|
||||
"layout.banner.build-fail.ignore": {
|
||||
"message": "Ignorar"
|
||||
},
|
||||
"layout.banner.build-fail.title": {
|
||||
"message": "Error al generar el estado desde la API durante la compilación."
|
||||
},
|
||||
"layout.banner.preview.description": {
|
||||
"message": "Si desea acceder al sitio web oficial de Modrinth, visite <link>https://modrinth.com</link>. El equipo de Modrinth utiliza esta versión preliminar para realizar pruebas. Se creó con <branch-link>{owner}/{branch}</branch-link> @ {commit}.."
|
||||
},
|
||||
"layout.banner.preview.title": {
|
||||
"message": "Esta es una vista previa de la implementación del sitio web de Modrinth."
|
||||
},
|
||||
@@ -1922,6 +2048,9 @@
|
||||
"moderation.moderate": {
|
||||
"message": "Moderar"
|
||||
},
|
||||
"moderation.page.external-projects": {
|
||||
"message": "Proyectos externos"
|
||||
},
|
||||
"moderation.page.projects": {
|
||||
"message": "Proyectos"
|
||||
},
|
||||
@@ -2552,6 +2681,9 @@
|
||||
"project.license.title": {
|
||||
"message": "Licencia"
|
||||
},
|
||||
"project.moderation.thread.title": {
|
||||
"message": "Mensajes de moredación"
|
||||
},
|
||||
"project.moderation.title": {
|
||||
"message": "Moderación"
|
||||
},
|
||||
@@ -2615,6 +2747,45 @@
|
||||
"project.settings.general.url.title": {
|
||||
"message": "URL"
|
||||
},
|
||||
"project.settings.permissions.attention-needed.description.proj-approved": {
|
||||
"message": "Por favor proporciona pruebas de que tienes permiso para redistribuir todos estos archivos y cualquier versión retenida se publicará automáticamente."
|
||||
},
|
||||
"project.settings.permissions.attention-needed.description.proj-draft": {
|
||||
"message": "Por favor proporciona pruebas de que tienes permiso para redistribuir todos estos archivos antes de enviar tu proyecto a revisión."
|
||||
},
|
||||
"project.settings.permissions.attention-needed.title": {
|
||||
"message": "Incluye contenido desconocido"
|
||||
},
|
||||
"project.settings.permissions.completed.description": {
|
||||
"message": "Todo el contenido externo proporciona atribuciones."
|
||||
},
|
||||
"project.settings.permissions.completed.title": {
|
||||
"message": "¡Atribuciones completadas!"
|
||||
},
|
||||
"project.settings.permissions.empty-state.description": {
|
||||
"message": "Ninguna de tus versiones presenta contenido externo, por lo que no necesitas preocuparte de obtener permisos."
|
||||
},
|
||||
"project.settings.permissions.empty-state.heading": {
|
||||
"message": "¡Ya estás listo!"
|
||||
},
|
||||
"project.settings.permissions.fail.description": {
|
||||
"message": "No tienes permiso de redistribuir parte del contenido externo que añadiste. Para poder publicar en Modrinth, quite el contenido infractor."
|
||||
},
|
||||
"project.settings.permissions.fail.title": {
|
||||
"message": "No se puede incluir parte del contenido"
|
||||
},
|
||||
"project.settings.permissions.info-banner.description": {
|
||||
"message": "Si incluyes contenido que no está en Modrinth, debes dejarnos saber de donde es y verificar que tienes permiso de distribuir los archivos. ¡Mira <link>nuestra guía</link> para aprender como hacerlo!"
|
||||
},
|
||||
"project.settings.permissions.info-banner.title": {
|
||||
"message": "Aprende cómo funcionan las atribuciones"
|
||||
},
|
||||
"project.settings.permissions.learn-more": {
|
||||
"message": "Aprende más"
|
||||
},
|
||||
"project.settings.permissions.search-placeholder": {
|
||||
"message": "Busca {count} {count, plural, one {proyecto externo} other {proyectos externos}}..."
|
||||
},
|
||||
"project.settings.title": {
|
||||
"message": "Configuración"
|
||||
},
|
||||
@@ -2633,14 +2804,23 @@
|
||||
"project.versions.title": {
|
||||
"message": "Versiones"
|
||||
},
|
||||
"project.versions.withheld-versions-warning.description": {
|
||||
"message": "{count, plural, one {Esta versión se encuentra retenida y no listada} other {Estas versiones se encuentran retenidas y no listadas}} publicamente. Por favor proporcione pruebas que tiene permiso de redistribuir algunos de los archivos presentes en {count, plural, one {la versión} other {las versiones}} del modpack."
|
||||
},
|
||||
"project.versions.withheld-versions-warning.resolve-button": {
|
||||
"message": "Resolver"
|
||||
},
|
||||
"project.versions.withheld-versions-warning.title": {
|
||||
"message": "{count, plural, one {Versión {version_name} retenida} other {Versiones retenidas}} debido a que incluye contenido desconocido"
|
||||
},
|
||||
"report.already-reported": {
|
||||
"message": "Ya has reportado {title}"
|
||||
},
|
||||
"report.already-reported-description": {
|
||||
"message": "Ya tienes un reporte abierto para este {item, select, project {project} version {version} user {user} other {content}}. Puedes añadir más detalles a tu reporte si tienes más información que agregar."
|
||||
"message": "Ya tienes un reporte abierto para {item, select, project {este proyecto} version {esta versión} user {este usuario} other {este contenido}}. Puedes añadir más detalles a tu reporte si tienes más información que agregar."
|
||||
},
|
||||
"report.back-to-item": {
|
||||
"message": "Volver a {item, select, project {proyecto} version {version} user {usuario} other {contenido}}"
|
||||
"message": "Volver {item, select, project {al proyecto} version {a la versión} user {al usuario} other {al contenido}}"
|
||||
},
|
||||
"report.body.description": {
|
||||
"message": "Incluye enlaces e imágenes si es posible y relevante. Los reportes vacíos o insuficientes serán cerrados e ignorados."
|
||||
@@ -3002,6 +3182,9 @@
|
||||
"search.filter.locked.server.sync": {
|
||||
"message": "Sincronizar con el servidor"
|
||||
},
|
||||
"servers.manage.content.title": {
|
||||
"message": "Contenido - {serverName} - Modrinth"
|
||||
},
|
||||
"servers.notice.actions": {
|
||||
"message": "Acciones"
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user