diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue
index e2f78fe965..0830a814f9 100644
--- a/apps/app-frontend/src/App.vue
+++ b/apps/app-frontend/src/App.vue
@@ -1545,8 +1545,10 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
@@ -1570,7 +1572,11 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
v-tooltip.right="formatMessage(messages.modrinthHosting)"
to="/hosting/manage"
:is-primary="(r) => r.path === '/hosting/manage' || r.path === '/hosting/manage/'"
- :is-subpage="(r) => r.path.startsWith('/hosting/manage/') && r.path !== '/hosting/manage/'"
+ :is-subpage="
+ (r) =>
+ (r.path.startsWith('/hosting/manage/') && r.path !== '/hosting/manage/') ||
+ ((r.path.startsWith('/browse') || r.path.startsWith('/project')) && r.query.sid)
+ "
>
diff --git a/apps/app-frontend/src/pages/Browse.vue b/apps/app-frontend/src/pages/Browse.vue
index 8f166430fd..730a69fccb 100644
--- a/apps/app-frontend/src/pages/Browse.vue
+++ b/apps/app-frontend/src/pages/Browse.vue
@@ -57,11 +57,7 @@ import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
import { get as getSettings, set as setSettings } from '@/helpers/settings.ts'
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
import { get_instance_worlds } from '@/helpers/worlds'
-import {
- type BreadcrumbDefinition,
- useBreadcrumb,
- useRootBreadcrumb,
-} from '@/providers/breadcrumbs'
+import { type BreadcrumbDefinition, injectBreadcrumbManager } from '@/providers/breadcrumbs'
import { injectContentInstall } from '@/providers/content-install'
import { injectServerInstall } from '@/providers/server-install'
import {
@@ -179,45 +175,46 @@ const hiddenInstanceProjectIds = ref>(new Set())
const hiddenInstanceProjectIdsInitialized = ref(false)
const isServerInstance = ref(false)
-const instanceBreadcrumb = route.query.i
- ? useBreadcrumb({
- slot: 'instance',
- id: () => `instance:${String(displayedBrowseRoute.value.query.i ?? '')}`,
- label: () => instance.value?.name ?? formatMessage(commonMessages.loadingLabel),
- visual: () => ({
- type: 'image',
- src: instance.value?.icon_path ? convertFileSrc(instance.value.icon_path) : undefined,
- alt: instance.value?.name,
- tintBy: String(displayedBrowseRoute.value.query.i ?? ''),
- }),
- to: () => {
- const instancePath = `/instance/${encodeURIComponent(
- String(displayedBrowseRoute.value.query.i ?? ''),
- )}`
- return displayedBrowseRoute.value.query.from === 'worlds'
- ? `${instancePath}/worlds`
- : instancePath
- },
- })
- : undefined
+const breadcrumbManager = injectBreadcrumbManager()
+const instanceBreadcrumbDefinition = {
+ slot: 'instance',
+ id: () => `instance:${String(displayedBrowseRoute.value.query.i ?? '')}`,
+ label: () => instance.value?.name ?? formatMessage(commonMessages.loadingLabel),
+ visual: () => ({
+ type: 'image' as const,
+ src: instance.value?.icon_path ? convertFileSrc(instance.value.icon_path) : undefined,
+ alt: instance.value?.name,
+ tintBy: String(displayedBrowseRoute.value.query.i ?? ''),
+ }),
+ to: () => {
+ const instancePath = `/instance/${encodeURIComponent(
+ String(displayedBrowseRoute.value.query.i ?? ''),
+ )}`
+ return displayedBrowseRoute.value.query.from === 'worlds'
+ ? `${instancePath}/worlds`
+ : instancePath
+ },
+} satisfies BreadcrumbDefinition
+const serversBreadcrumbDefinition = {
+ slot: 'root',
+ id: 'servers',
+ label: () => formatMessage(commonMessages.serversLabel),
+ to: '/hosting/manage/',
+ visual: { type: 'icon', component: ServerStackIcon },
+} satisfies BreadcrumbDefinition
const serverBreadcrumbTo = ref(serverBackUrl.value)
watch(serverBackUrl, (value) => {
if (route.path.startsWith('/browse/')) {
serverBreadcrumbTo.value = value
}
})
-const serverBreadcrumb =
- !instanceBreadcrumb && serverIdQuery.value
- ? useBreadcrumb({
- slot: 'server',
- id: () => `server:${String(displayedBrowseRoute.value.query.sid ?? '')}`,
- label: () =>
- serverContextServerData.value?.name ?? formatMessage(commonMessages.loadingLabel),
- visual: { type: 'icon', component: ServerStackIcon },
- to: serverBreadcrumbTo,
- })
- : undefined
-const breadcrumbParent = instanceBreadcrumb ?? serverBreadcrumb
+const serverBreadcrumbDefinition = {
+ slot: 'server',
+ id: () => `server:${String(displayedBrowseRoute.value.query.sid ?? '')}`,
+ label: () => serverContextServerData.value?.name ?? formatMessage(commonMessages.loadingLabel),
+ visual: { type: 'icon', component: ServerStackIcon },
+ to: serverBreadcrumbTo,
+} satisfies BreadcrumbDefinition
const breadcrumbDefinition = {
slot: 'browse',
id: () =>
@@ -230,9 +227,27 @@ const breadcrumbDefinition = {
to: () => displayedBrowseRoute.value.fullPath,
visual: { type: 'icon', component: CompassIcon },
} satisfies BreadcrumbDefinition
-const browseBreadcrumb = breadcrumbParent
- ? useBreadcrumb(breadcrumbDefinition, { parent: breadcrumbParent })
- : useRootBreadcrumb(breadcrumbDefinition)
+
+function syncBreadcrumbs() {
+ if (displayedBrowseRoute.value.query.i) {
+ const instanceBreadcrumb = breadcrumbManager.reset(instanceBreadcrumbDefinition)
+ breadcrumbManager.push(breadcrumbDefinition, { parent: instanceBreadcrumb })
+ return
+ }
+
+ if (displayedBrowseRoute.value.query.sid) {
+ const serversBreadcrumb = breadcrumbManager.reset(serversBreadcrumbDefinition)
+ const serverBreadcrumb = breadcrumbManager.push(serverBreadcrumbDefinition, {
+ parent: serversBreadcrumb,
+ })
+ breadcrumbManager.push(breadcrumbDefinition, { parent: serverBreadcrumb })
+ return
+ }
+
+ breadcrumbManager.reset(breadcrumbDefinition)
+}
+
+watch(displayedBrowseRoute, syncBreadcrumbs, { immediate: true, flush: 'sync' })
debugLog('fetching tags (categories, loaders, gameVersions)')
const [categories, loaders, availableGameVersions] = await Promise.all([
@@ -587,7 +602,6 @@ function resetInstanceContext() {
hiddenInstanceProjectIds.value = new Set()
hiddenInstanceProjectIdsInitialized.value = false
isServerInstance.value = false
- browseBreadcrumb.reset()
void refreshInstalledProjectIds()
}
diff --git a/apps/app-frontend/src/providers/setup/server-install-content.ts b/apps/app-frontend/src/providers/setup/server-install-content.ts
index c48b90f2c1..c2556c8937 100644
--- a/apps/app-frontend/src/providers/setup/server-install-content.ts
+++ b/apps/app-frontend/src/providers/setup/server-install-content.ts
@@ -222,7 +222,29 @@ export function createServerInstallContent(opts: {
const isSetupServerContext = computed(() => !!serverIdQuery.value && !!serverFlowFrom.value)
const serverContextWorldId = ref(worldIdQuery.value)
- const serverContextServerData = ref(null)
+
+ function getCachedServer(serverId: string): Archon.Servers.v0.Server | null {
+ return (
+ queryClient.getQueryData(['servers', 'detail', serverId]) ??
+ queryClient
+ .getQueryData(['servers'])
+ ?.servers.find((server) => server.server_id === serverId) ??
+ null
+ )
+ }
+
+ async function ensureServer(serverId: string): Promise {
+ return queryClient.ensureQueryData({
+ queryKey: ['servers', 'detail', serverId],
+ queryFn: () => client.archon.servers_v0.get(serverId),
+ staleTime: 30_000,
+ })
+ }
+
+ const initialServerId = serverIdQuery.value
+ const serverContextServerData = ref(
+ initialServerId ? getCachedServer(initialServerId) : null,
+ )
const serverContentProjectIds = ref>(new Set())
const serverContentInstallKeys = ref>(new Set())
const queuedServerInstalls = ref