diff --git a/.vitepress/config.mts b/.vitepress/config.mts index e66afb7ba..6948b966a 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -8,13 +8,16 @@ import { transformItems, transformPageData } from "./transform"; // https://www.npmjs.com/package/vitepress-versioning-plugin export default defineVersionedConfig( { + // Removes .html from the end of URLs. cleanUrls: true, + // Mostly just for the favicon. head: [["link", { rel: "icon", sizes: "32x32", href: "/favicon.png" }]], // Prevent dead links from being reported as errors - allows partially translated pages to be built. ignoreDeadLinks: true, + // Adds a "Last Updated" block to the footer of pages, uses git to determine the last time a page's file was modified. lastUpdated: true, // Reduce the size of the dist by using a separate js file for the metadata. @@ -24,10 +27,12 @@ export default defineVersionedConfig( markdown: { config(md) { + // Use the snippet plugin (transclusion, etc.) md.use(snippetPlugin); }, languages: [ async () => + // Adds support for mcfunction language to shiki. await import("syntax-mcfunction/mcfunction.tmLanguage.json", { with: { type: "json" }, }).then((lang) => ({ ...(lang.default as any), name: "mcfunction" })), @@ -53,6 +58,7 @@ export default defineVersionedConfig( themeConfig: { search: { options: { + // Removes versioned and translated pages from search. _render(src, env, md) { if (env.frontmatter?.search === false) return ""; if (env.relativePath.startsWith("translated/")) return ""; @@ -66,6 +72,7 @@ export default defineVersionedConfig( transformPageData, + // Versioning plugin configuration. versioning: { latestVersion: "1.21.4", rewrites: { diff --git a/.vitepress/theme/CountryFlagsPolyfill.woff2 b/.vitepress/theme/CountryFlagsPolyfill.woff2 new file mode 100644 index 000000000..b9d6ea84b Binary files /dev/null and b/.vitepress/theme/CountryFlagsPolyfill.woff2 differ diff --git a/.vitepress/theme/components/AuthorsComponent.vue b/.vitepress/theme/components/AuthorsComponent.vue index 3ad153c56..30a88cedf 100644 --- a/.vitepress/theme/components/AuthorsComponent.vue +++ b/.vitepress/theme/components/AuthorsComponent.vue @@ -45,7 +45,7 @@ onContentUpdated(() => { class="author-avatar" :src="`https://wsrv.nl/?url=${encodeURIComponent( `https://github.com/${author}.png?size=32` - )}&af`" + )}&af&maxage=7d`" :alt="author" /> diff --git a/.vitepress/theme/components/VersionReminder.vue b/.vitepress/theme/components/VersionReminder.vue index bafb65aab..4b0c05a26 100644 --- a/.vitepress/theme/components/VersionReminder.vue +++ b/.vitepress/theme/components/VersionReminder.vue @@ -1,38 +1,29 @@ @@ -56,13 +47,13 @@ watchEffect(() => { @@ -82,8 +73,8 @@ watchEffect(() => { @@ -169,4 +160,4 @@ p.version { gap: 8px; } } - + \ No newline at end of file diff --git a/.vitepress/theme/components/VersionSwitcher.vue b/.vitepress/theme/components/VersionSwitcher.vue index ff6ef5b43..425beac9a 100644 --- a/.vitepress/theme/components/VersionSwitcher.vue +++ b/.vitepress/theme/components/VersionSwitcher.vue @@ -4,6 +4,7 @@ import VPFlyout from "vitepress/dist/client/theme-default/components/VPFlyout.vu import VPLink from "vitepress/dist/client/theme-default/components/VPLink.vue"; import { onBeforeMount, ref } from "vue"; +// Define component properties const props = defineProps<{ versioningPlugin: { versions: string[]; latestVersion: string }; screenMenu?: boolean; @@ -12,95 +13,95 @@ const props = defineProps<{ const data = useData(); const router = useRouter(); +// State refs const currentVersion = ref(props.versioningPlugin.latestVersion); const text = ref(""); +const isOpen = ref(false); +// Syncs the version switcher label text with theme data function refreshText() { text.value = data.theme.value.version.switcher; } -onContentUpdated(() => { - refreshText(); -}); +// Helper function to find the matching version from the current route path +function getVersionFromPath(path: string): string { + for (const v of props.versioningPlugin.versions) { + if (path.includes(`/${v}/`)) { + return v; + } + } + return props.versioningPlugin.latestVersion; +} +// Called after content updates to refresh label text +onContentUpdated(refreshText); + +// Before route change, update the current version router.onBeforeRouteChange = (to: string) => { if (to === "/") { currentVersion.value = props.versioningPlugin.latestVersion; return true; } - - for (const v of props.versioningPlugin.versions) { - if (to.includes(`/${v}/`)) { - currentVersion.value = v; - break; - } - } - + currentVersion.value = getVersionFromPath(to); return true; }; +// On mount, detect the version from the router's current path onBeforeMount(() => { - for (const v of props.versioningPlugin.versions) { - if (router.route.path.includes(`/${v}/`)) { - currentVersion.value = v; - break; - } - } + currentVersion.value = getVersionFromPath(router.route.path); }); -const isOpen = ref(false); +// Toggle the open state in screen menu mode const toggle = () => { isOpen.value = !isOpen.value; }; -function visitVersion(version) { - const localeKeys = Object.keys(data.site.value.locales); - // Check if the current path is localized - const isLocalized = localeKeys.some((key) => - router.route.path.startsWith(`/${key}/`) - ); - - let route; - const isOnLatest = - currentVersion.value === props.versioningPlugin.latestVersion; - if (isLocalized) { - // Get locale from the current path - const locale = localeKeys.find((key) => - router.route.path.startsWith(`/${key}/`) - ); - +// Constructs the appropriate route path for a given version +function buildRoutePath( + currentPath: string, + locale: string | null, + version: string, + isOnLatest: boolean +) { + if (locale) { + // Replace or add version segment while keeping the locale + // e.g. /en/ -> /en/1.2.0/ or /en/1.2.0/ -> /en/1.3.0/ if (!isOnLatest) { if (version === props.versioningPlugin.latestVersion) { - route = router.route.path.replace( - `/${locale}/${currentVersion.value}/`, - `/${locale}/` - ); + return currentPath.replace(`/${locale}/${currentVersion.value}/`, `/${locale}/`); } else { - route = router.route.path.replace( - `/${locale}/${currentVersion}/`, - `/${locale}/${version}/` - ); + // Replace any existing version segment with the new one + return currentPath.replace(`/${locale}/${currentVersion.value}/`, `/${locale}/${version}/`); } } else { - route = router.route.path.replace( - `/${locale}/`, - `/${locale}/${version}/` - ); + // If currently on latest, just add the new version segment + return currentPath.replace(`/${locale}/`, `/${locale}/${version}/`); } } else { + // Non-localized routes + // e.g. / -> /1.2.0/ or /1.2.0/ -> /1.3.0/ if (!isOnLatest) { if (version === props.versioningPlugin.latestVersion) { - route = router.route.path.replace(`/${currentVersion.value}/`, "/"); + return currentPath.replace(`/${currentVersion.value}/`, "/"); } else { - route = router.route.path.replace( - `/${currentVersion.value}/`, - `/${version}/` - ); + return currentPath.replace(`/${currentVersion.value}/`, `/${version}/`); } } else { - route = router.route.path.replace("/", `/${version}/`); + return currentPath.replace("/", `/${version}/`); } } +} + +// Navigate to the selected version +function visitVersion(version: string) { + const localeKeys = Object.keys(data.site.value.locales); + const isLocalized = localeKeys.some((key) => router.route.path.startsWith(`/${key}/`)); + const locale = isLocalized + ? localeKeys.find((key) => router.route.path.startsWith(`/${key}/`)) || null + : null; + + const isOnLatest = currentVersion.value === props.versioningPlugin.latestVersion; + const route = buildRoutePath(router.route.path, locale, version, isOnLatest); router.go(route); currentVersion.value = version; @@ -108,6 +109,7 @@ function visitVersion(version) {