feat: frontend explicit imports + error page fix (#4184)

* feat: frontend explicit imports

* fix: error handling

* fix: dashboard missing import

* fix: error page issues

* fix: exclude RouterView

* feat: fix lint issues

* fix: lint issues

* fix: import issues

* add getVersionLink

* make articles.json use tabs on generation so it doesn't have to be reformatted

* fix: lint issues

---------

Signed-off-by: Cal H. <hendersoncal117@gmail.com>
Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
This commit is contained in:
Cal H.
2025-08-17 12:15:49 +01:00
committed by GitHub
parent 74d2d85cb5
commit 3e735b99eb
54 changed files with 1295 additions and 1020 deletions

View File

@@ -331,7 +331,19 @@ import {
VersionIcon,
XIcon,
} from '@modrinth/assets'
import { injectNotificationManager, useRelativeTime } from '@modrinth/ui'
import {
Avatar,
Categories,
CopyCode,
DoubleIcon,
injectNotificationManager,
ProjectStatusBadge,
useRelativeTime,
} from '@modrinth/ui'
import { getProjectLink, getVersionLink } from '~/helpers/projects'
import ThreadSummary from './thread/ThreadSummary.vue'
const { addNotification } = injectNotificationManager()
const emit = defineEmits(['update:notifications'])

View File

@@ -9,7 +9,7 @@
<div class="flex gap-2">
<ButtonStyled>
<button size="sm" @click="$emit('refetch')">
<UiServersIconsLoadingIcon class="h-5 w-5" />
<LoadingIcon class="h-5 w-5" />
Try again
</button>
</ButtonStyled>
@@ -28,6 +28,8 @@
import { FileIcon, HomeIcon } from '@modrinth/assets'
import { ButtonStyled } from '@modrinth/ui'
import LoadingIcon from './icons/LoadingIcon.vue'
defineProps<{
title: string
message: string

View File

@@ -18,7 +18,7 @@
}"
data-pyro-files-virtual-list
>
<UiServersFileItem
<FileItem
v-for="item in visibleItems"
:key="item.path"
:count="item.count"
@@ -45,6 +45,8 @@
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import FileItem from './FileItem.vue'
const props = defineProps<{
items: any[]
}>()

View File

@@ -14,7 +14,7 @@
v-if="state.hasError"
class="flex h-full w-full flex-col items-center justify-center gap-8"
>
<UiServersIconsPanelErrorIcon />
<PanelErrorIcon />
<p class="m-0">{{ state.errorMessage || 'Invalid or empty image file.' }}</p>
</div>
<img
@@ -57,6 +57,8 @@ import { ZoomInIcon, ZoomOutIcon } from '@modrinth/assets'
import { ButtonStyled } from '@modrinth/ui'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import PanelErrorIcon from './icons/PanelErrorIcon.vue'
const ZOOM_MIN = 0.1
const ZOOM_MAX = 5
const ZOOM_IN_FACTOR = 1.2

View File

@@ -27,7 +27,7 @@
>
<div class="flex flex-1 items-center gap-2 truncate">
<transition-group name="status-icon" mode="out-in">
<UiServersPanelSpinner
<PanelSpinner
v-show="item.status === 'uploading'"
key="spinner"
class="absolute !size-4"
@@ -107,6 +107,8 @@ import { computed, nextTick, ref, watch } from 'vue'
import type { FSModule } from '~/composables/servers/modules/fs.ts'
import PanelSpinner from './PanelSpinner.vue'
const { addNotification } = injectNotificationManager()
interface UploadItem {

View File

@@ -5,7 +5,7 @@
:key="loader.name"
class="group relative flex items-center justify-between rounded-2xl p-2 pr-2.5 hover:bg-bg"
>
<UiServersLoaderSelectorCard
<LoaderSelectorCard
:loader="loader"
:is-current="isCurrentLoader(loader.name)"
:loader-version="data.loader_version"
@@ -24,7 +24,7 @@
:key="loader.name"
class="group relative flex items-center justify-between rounded-2xl p-2 pr-2.5 hover:bg-bg"
>
<UiServersLoaderSelectorCard
<LoaderSelectorCard
:loader="loader"
:is-current="isCurrentLoader(loader.name)"
:loader-version="data.loader_version"
@@ -44,7 +44,7 @@
:key="loader.name"
class="group relative flex items-center justify-between rounded-2xl p-2 pr-2.5 hover:bg-bg"
>
<UiServersLoaderSelectorCard
<LoaderSelectorCard
:loader="loader"
:is-current="isCurrentLoader(loader.name)"
:loader-version="data.loader_version"
@@ -58,6 +58,7 @@
</template>
<script setup lang="ts">
import LoaderSelectorCard from './LoaderSelectorCard.vue'
const props = defineProps<{
data: {
loader: string | null

View File

@@ -5,7 +5,7 @@
class="grid size-10 place-content-center rounded-xl border-[1px] border-solid border-button-border bg-button-bg shadow-sm"
:class="isCurrentLoader ? '[&&]:bg-bg-green' : ''"
>
<UiServersIconsLoaderIcon
<LoaderIcon
:loader="loader.name"
class="[&&]:size-6"
:class="isCurrentLoader ? 'text-brand' : ''"
@@ -43,6 +43,8 @@
import { CheckIcon, DownloadIcon } from '@modrinth/assets'
import { ButtonStyled } from '@modrinth/ui'
import LoaderIcon from './icons/LoaderIcon.vue'
interface LoaderInfo {
name: 'Vanilla' | 'Fabric' | 'Forge' | 'Quilt' | 'Paper' | 'NeoForge' | 'Purpur'
displayName: string

View File

@@ -6,7 +6,7 @@
Are you sure you want to
<span class="lowercase">{{ confirmActionText }}</span> the server?
</p>
<UiCheckbox
<Checkbox
v-model="dontAskAgain"
label="Don't ask me again"
class="text-sm"
@@ -34,7 +34,7 @@
:header="`All of ${serverName || 'Server'} info`"
@close="closeDetailsModal"
>
<UiServersServerInfoLabels
<ServerInfoLabels
:server-data="serverData"
:show-game-label="true"
:show-loader-label="true"
@@ -53,7 +53,7 @@
<div class="flex flex-row items-center gap-2 rounded-lg">
<ButtonStyled v-if="isInstalling" type="standard" color="brand">
<button disabled class="flex-shrink-0">
<UiServersPanelSpinner class="size-5" /> Installing...
<PanelSpinner class="size-5" /> Installing...
</button>
</ButtonStyled>
@@ -70,7 +70,7 @@
<ButtonStyled type="standard" color="brand">
<button :disabled="!canTakeAction" @click="handlePrimaryAction">
<div v-if="isTransitionState" class="grid place-content-center">
<UiServersIconsLoadingIcon />
<LoadingIcon />
</div>
<component :is="isRunning ? UpdatedIcon : PlayIcon" v-else />
<span>{{ primaryActionText }}</span>
@@ -116,12 +116,16 @@ import {
UpdatedIcon,
XIcon,
} from '@modrinth/assets'
import { ButtonStyled, NewModal } from '@modrinth/ui'
import { ButtonStyled, Checkbox, NewModal } from '@modrinth/ui'
import type { PowerAction as ServerPowerAction, ServerState } from '@modrinth/utils'
import { useStorage } from '@vueuse/core'
import { computed, ref } from 'vue'
import { useRouter } from 'vue-router'
import LoadingIcon from './icons/LoadingIcon.vue'
import PanelSpinner from './PanelSpinner.vue'
import ServerInfoLabels from './ServerInfoLabels.vue'
const flags = useFeatureFlags()
interface PowerAction {

View File

@@ -151,7 +151,7 @@
class="group"
>
<div class="flex items-center gap-2">
<UiServersLogLine :log="item" @show-full-log="showFullLogMessage" />
<LogLine :log="item" @show-full-log="showFullLogMessage" />
<div @mousedown.stop @click.stop>
<button
v-if="searchInput"
@@ -223,8 +223,8 @@
:class="{ hidden: searchInput || hasSelection || isSingleLineSelected }"
@click="toggleFullscreen"
>
<LazyUiServersIconsMinimizeIconVue v-if="isFullScreen" />
<LazyUiServersIconsFullscreenIcon v-else />
<MinimizeIconVue v-if="isFullScreen" />
<FullscreenIcon v-else />
</button>
<Transition name="fade">
@@ -306,6 +306,10 @@ import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { useModrinthServersConsole } from '~/store/console.ts'
import FullscreenIcon from './icons/FullscreenIcon.vue'
import MinimizeIconVue from './icons/MinimizeIcon.vue.vue'
import LogLine from './LogLine.vue'
const { $cosmetics } = useNuxtApp()
const cosmetics = $cosmetics

View File

@@ -28,7 +28,7 @@
<div
class="grid size-16 place-content-center rounded-2xl border-[2px] border-solid border-button-border bg-button-bg shadow-sm"
>
<UiServersIconsLoaderIcon class="size-10" :loader="selectedLoader" />
<LoaderIcon class="size-10" :loader="selectedLoader" />
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -54,7 +54,7 @@
<div class="flex w-full flex-col gap-2 rounded-2xl bg-table-alternateRow p-4">
<div class="text-lg font-bold text-contrast">Minecraft version</div>
<UiServersTeleportDropdownMenu
<TeleportDropdownMenu
v-model="selectedMCVersion"
name="mcVersion"
:options="mcVersions"
@@ -102,13 +102,13 @@
<div
class="relative flex h-9 w-full items-center rounded-xl bg-button-bg px-4 opacity-50"
>
<UiServersIconsLoadingIcon class="mr-2 animate-spin" />
<LoadingIcon class="mr-2 animate-spin" />
Loading versions...
<DropdownIcon class="absolute right-4" />
</div>
</template>
<template v-else-if="selectedLoaderVersions.length > 0">
<UiServersTeleportDropdownMenu
<TeleportDropdownMenu
v-model="selectedLoaderVersion"
name="loaderVersion"
:options="selectedLoaderVersions"
@@ -211,6 +211,9 @@ import { $fetch } from 'ofetch'
import type { ModrinthServer } from '~/composables/servers/modrinth-servers.ts'
import type { BackupInProgressReason } from '~/pages/servers/manage/[id].vue'
import LoaderIcon from './icons/LoaderIcon.vue'
import LoadingIcon from './icons/LoadingIcon.vue'
const { addNotification } = injectNotificationManager()
const { formatMessage } = useVIntl()

View File

@@ -1,24 +1,24 @@
<template>
<div>
<UiServersServerGameLabel
<ServerGameLabel
v-if="showGameLabel"
:game="serverData.game"
:mc-version="serverData.mc_version ?? ''"
:is-link="linked"
/>
<UiServersServerLoaderLabel
<ServerLoaderLabel
:loader="serverData.loader"
:loader-version="serverData.loader_version ?? ''"
:no-separator="column"
:is-link="linked"
/>
<UiServersServerSubdomainLabel
<ServerSubdomainLabel
v-if="serverData.net?.domain"
:subdomain="serverData.net.domain"
:no-separator="column"
:is-link="linked"
/>
<UiServersServerUptimeLabel
<ServerUptimeLabel
v-if="uptimeSeconds"
:uptime-seconds="uptimeSeconds"
:no-separator="column"
@@ -27,6 +27,11 @@
</template>
<script setup lang="ts">
import ServerGameLabel from './ServerGameLabel.vue'
import ServerLoaderLabel from './ServerLoaderLabel.vue'
import ServerSubdomainLabel from './ServerSubdomainLabel.vue'
import ServerUptimeLabel from './ServerUptimeLabel.vue'
interface ServerInfoLabelsProps {
serverData: Record<string, any>
showGameLabel: boolean

View File

@@ -1,5 +1,5 @@
<template>
<LazyUiServersPlatformVersionSelectModal
<PlatformVersionSelectModal
ref="versionSelectModal"
:server="props.server"
:current-loader="ignoreCurrentInstallation ? undefined : (data?.loader as Loaders)"
@@ -8,13 +8,13 @@
@reinstall="emit('reinstall', $event)"
/>
<LazyUiServersPlatformMrpackModal
<PlatformMrpackModal
ref="mrpackModal"
:server="props.server"
@reinstall="emit('reinstall', $event)"
/>
<LazyUiServersPlatformChangeModpackVersionModal
<PlatformChangeModpackVersionModal
ref="modpackVersionModal"
:server="props.server"
:project="data?.project"
@@ -137,7 +137,7 @@
}"
:tabindex="props.server.general?.status === 'installing' ? -1 : 0"
>
<UiServersLoaderSelector
<LoaderSelector
:data="
ignoreCurrentInstallation
? {
@@ -165,6 +165,11 @@ import type { Loaders } from '@modrinth/utils'
import type { ModrinthServer } from '~/composables/servers/modrinth-servers.ts'
import type { BackupInProgressReason } from '~/pages/servers/manage/[id].vue'
import LoaderSelector from './LoaderSelector.vue'
import PlatformChangeModpackVersionModal from './PlatformChangeModpackVersionModal.vue'
import PlatformMrpackModal from './PlatformMrpackModal.vue'
import PlatformVersionSelectModal from './PlatformVersionSelectModal.vue'
const { formatMessage } = useVIntl()
const props = defineProps<{

View File

@@ -16,7 +16,7 @@
data-pyro-server-listing
:data-pyro-server-listing-id="server_id"
>
<UiServersServerIcon v-if="status !== 'suspended'" :image="image" />
<ServerIcon v-if="status !== 'suspended'" :image="image" />
<div
v-else
class="bg-bg-secondary flex size-24 items-center justify-center rounded-xl border-[1px] border-solid border-button-border bg-button-bg shadow-sm"
@@ -49,7 +49,7 @@
>
<SparklesIcon class="size-5 shrink-0" /> New server
</div>
<UiServersServerInfoLabels
<ServerInfoLabels
v-else
:server-data="{ game, mc_version, loader, loader_version, net }"
:show-game-label="showGameLabel"
@@ -63,7 +63,7 @@
v-if="status === 'suspended' && suspension_reason === 'upgrading'"
class="relative -mt-4 flex w-full flex-row items-center gap-2 rounded-b-3xl bg-bg-blue p-4 text-sm font-bold text-contrast"
>
<UiServersPanelSpinner />
<PanelSpinner />
Your server's hardware is currently being upgraded and will be back online shortly.
</div>
<div
@@ -71,8 +71,8 @@
class="relative -mt-4 flex w-full flex-col gap-2 rounded-b-3xl bg-bg-red p-4 text-sm font-bold text-contrast"
>
<div class="flex flex-row gap-2">
<UiServersIconsPanelErrorIcon class="!size-5" /> Your server has been cancelled. Please
update your billing information or contact Modrinth Support for more information.
<PanelErrorIcon class="!size-5" /> Your server has been cancelled. Please update your
billing information or contact Modrinth Support for more information.
</div>
<CopyCode :text="`${props.server_id}`" class="ml-auto" />
</div>
@@ -81,9 +81,8 @@
class="relative -mt-4 flex w-full flex-col gap-2 rounded-b-3xl bg-bg-red p-4 text-sm font-bold text-contrast"
>
<div class="flex flex-row gap-2">
<UiServersIconsPanelErrorIcon class="!size-5" /> Your server has been suspended:
{{ suspension_reason }}. Please update your billing information or contact Modrinth Support
for more information.
<PanelErrorIcon class="!size-5" /> Your server has been suspended: {{ suspension_reason }}.
Please update your billing information or contact Modrinth Support for more information.
</div>
<CopyCode :text="`${props.server_id}`" class="ml-auto" />
</div>
@@ -92,8 +91,8 @@
class="relative -mt-4 flex w-full flex-col gap-2 rounded-b-3xl bg-bg-red p-4 text-sm font-bold text-contrast"
>
<div class="flex flex-row gap-2">
<UiServersIconsPanelErrorIcon class="!size-5" /> Your server has been suspended. Please
update your billing information or contact Modrinth Support for more information.
<PanelErrorIcon class="!size-5" /> Your server has been suspended. Please update your
billing information or contact Modrinth Support for more information.
</div>
<CopyCode :text="`${props.server_id}`" class="ml-auto" />
</div>
@@ -107,6 +106,11 @@ import type { Project, Server } from '@modrinth/utils'
import { useModrinthServers } from '~/composables/servers/modrinth-servers.ts'
import PanelErrorIcon from './icons/PanelErrorIcon.vue'
import PanelSpinner from './PanelSpinner.vue'
import ServerIcon from './ServerIcon.vue'
import ServerInfoLabels from './ServerInfoLabels.vue'
const props = defineProps<Partial<Server>>()
if (props.server_id && props.status === 'available') {

View File

@@ -9,7 +9,7 @@
src="~/assets/images/servers/minecraft_server_icon.png"
/>
<div class="absolute inset-0 grid place-content-center">
<UiServersIconsLoadingIcon class="size-8 animate-spin text-contrast" />
<LoadingIcon class="size-8 animate-spin text-contrast" />
</div>
</div>
<div class="flex flex-col gap-4">
@@ -18,3 +18,7 @@
</div>
</div>
</template>
<script lang="ts" setup>
import LoadingIcon from './icons/LoadingIcon.vue'
</script>

View File

@@ -2,7 +2,7 @@
<div v-tooltip="'Change server loader'" class="flex min-w-0 flex-row items-center gap-4 truncate">
<div v-if="!noSeparator" class="experimental-styles-within h-6 w-0.5 bg-button-border"></div>
<div class="flex flex-row items-center gap-2">
<UiServersIconsLoaderIcon v-if="loader" :loader="loader" class="flex shrink-0 [&&]:size-5" />
<LoaderIcon v-if="loader" :loader="loader" class="flex shrink-0 [&&]:size-5" />
<div v-else class="size-5 shrink-0 animate-pulse rounded-full bg-button-border"></div>
<NuxtLink
v-if="isLink"
@@ -34,6 +34,7 @@
</template>
<script setup lang="ts">
import LoaderIcon from './icons/LoaderIcon.vue'
defineProps<{
noSeparator?: boolean
loader?: 'Fabric' | 'Quilt' | 'Forge' | 'NeoForge' | 'Paper' | 'Spigot' | 'Bukkit' | 'Vanilla'

View File

@@ -8,7 +8,7 @@
<div v-if="!noSeparator" class="experimental-styles-within h-6 w-0.5 bg-button-border"></div>
<div class="flex gap-2">
<UiServersIconsTimer class="flex size-5 shrink-0" />
<Timer class="flex size-5 shrink-0" />
<time class="truncate text-sm font-semibold" :aria-label="verboseUptime">
{{ formattedUptime }}
</time>
@@ -19,6 +19,8 @@
<script setup lang="ts">
import { computed } from 'vue'
import Timer from './icons/Timer.vue'
const props = defineProps<{
uptimeSeconds: number
noSeparator?: boolean