UNPKG

vuepress-theme-hope

Version:

A light vuepress theme with tons of features

1,273 lines (1,231 loc) 56.1 kB
import { a as usePageAuthor, c as usePure, d as useThemeLocale, l as useData$1, u as useTheme } from "./PageInfo-BBtUekHB.js"; import { useElementHover, useEventListener, useFullscreen, usePreferredDark, useScrollLock, useStorage, useThrottleFn, useToggle, watchImmediate } from "@vueuse/core"; import { Transition, TransitionGroup, computed, defineComponent, h, inject, nextTick, onMounted, onUnmounted, provide, ref, resolveComponent, shallowRef, toRef, watch, watchEffect } from "vue"; import { AutoLink, ClientOnly, Content, RouteLink, onContentUpdated, resolveRoute, useRoute, useRoutePath, withBase } from "vuepress/client"; import { RenderDefault, ensureEndingSlash, entries, fromEntries, hasGlobalComponent, isArray, isLinkAbsolute, isLinkExternal, isLinkWithProtocol, isNumber, isPlainObject, isSlotContentEmpty, isString, keys, startsWith, useRoutePaths } from "@vuepress/helper/client"; import { sidebarData } from "@temp/theme-hope/sidebar.js"; import "./client/styles/base/page-footer.scss"; import noopComponent from "@vuepress/helper/noopComponent"; import { IconBase, RepoIcon, isActiveLink, resolveRepoLink, resolveRepoType } from "vuepress-shared/client"; import "./client/styles/appearance/color-mode-switch.scss"; import "./client/styles/appearance/color-mode.scss"; import "./client/styles/appearance/theme-color-picker.scss"; import cssVariables from "./client/styles/variables.module.scss"; import "./client/styles/appearance/theme-color.scss"; import "./client/styles/appearance/toggle-full-screen-button.scss"; import "./client/styles/appearance/toggle-full-screen.scss"; import "./client/styles/appearance/appearance-button.scss"; import "./client/styles/navbar/navbar-dropdown.scss"; import "./client/styles/navbar/navbar-brand.scss"; import "./client/styles/navbar/navbar-links.scss"; import "./client/styles/navbar/nav-screen-menu.scss"; import "./client/styles/navbar/nav-screen-links.scss"; import "@vuepress/helper/transition/fade-in-down.css"; import "./client/styles/navbar/nav-screen.scss"; import "./client/styles/navbar/repo-link.scss"; import "./client/styles/navbar/toggle-navbar-button.scss"; import "./client/styles/navbar/toggle-sidebar-button.scss"; import "./client/styles/navbar/navbar.scss"; import "./client/styles/sidebar/sidebar-child.scss"; import "./client/styles/sidebar/sidebar-group.scss"; import "./client/styles/sidebar/sidebar-links.scss"; import "./client/styles/sidebar/sidebar.scss"; import "@vuepress/helper/transition/fade-in.css"; import "./client/styles/base/main-layout.scss"; import "./client/styles/base/markdown-content.scss"; import "./client/styles/base/skip-link.scss"; import "./client/styles/home/hero-slide-down-button.scss"; //#region src/client/composables/useDarkMode.ts const darkModeSymbol = Symbol(__VUEPRESS_DEV__ ? "darkMode" : ""); /** * Inject dark mode global computed * * @returns Dark mode global computed */ const useDarkMode = () => { const darkMode = inject(darkModeSymbol); if (!darkMode) throw new Error("useDarkMode() is called without provider."); return darkMode; }; const injectDarkMode = (app) => { const isDarkPreferred = usePreferredDark(); const theme = useTheme(); const config = computed(() => theme.value.darkmode ?? "switch"); const status = useStorage("vuepress-theme-hope-scheme", "auto"); const isDarkMode = computed(() => { switch (config.value) { case "disable": return false; case "enable": return true; case "auto": return isDarkPreferred.value; case "switch": return status.value === "dark" || status.value === "auto" && isDarkPreferred.value; case "toggle": return status.value === "dark"; default: return status.value === "dark" || status.value === "auto" && isDarkPreferred.value; } }); const canToggle = computed(() => { const darkmode = config.value; return darkmode === "switch" || darkmode === "toggle"; }); app.provide(darkModeSymbol, { canToggle, config, isDarkMode, status }); Object.defineProperties(app.config.globalProperties, { $isDarkMode: { get: () => isDarkMode.value } }); }; const setupDarkMode = () => { const { config, isDarkMode, status } = useDarkMode(); watchEffect(() => { if (config.value === "disable") status.value = "light"; else if (config.value === "enable") status.value = "dark"; else if (config.value === "toggle" && status.value === "auto") status.value = "light"; }); useEventListener("beforeprint", () => { if (isDarkMode.value) document.documentElement.dataset.theme = "light"; }); useEventListener("afterprint", () => { if (isDarkMode.value) document.documentElement.dataset.theme = "dark"; }); onMounted(() => { watchImmediate(isDarkMode, (value) => { document.documentElement.dataset.theme = value ? "dark" : "light"; }); }); }; //#endregion //#region src/client/utils/isLinkInternal.ts const isLinkInternal = (link) => !isLinkWithProtocol(link) && !isLinkExternal(link); //#endregion //#region src/client/utils/resolveLinkInfo.ts /** * Resolve AutoLink props from string * * @param item - The string to resolve * @param preferFull - Whether to prefer full title * @param currentPath - The current page path * @returns AutoLink props */ const resolveLinkInfo = (item, preferFull = false, currentPath) => { const { meta, path, notFound } = resolveRoute(item, currentPath); return notFound ? { text: path, link: path } : { text: !preferFull && meta.shortTitle ? meta.shortTitle : meta.title || path, link: path, icon: meta.icon }; }; //#endregion //#region src/client/utils/resolvePrefix.ts const resolvePrefix = (prefix = "", path = "") => isLinkWithProtocol(path) || isLinkAbsolute(path) ? path : `${ensureEndingSlash(prefix)}${path}`; //#endregion //#region src/client/utils/sidebar/resolveSidebarItems.ts /** * Resolve sidebar item * * @param options - Sidebar item config * @param pathPrefix - Current path prefix * @returns Resolved sidebar item */ const resolveSidebarItem = (options, pathPrefix) => { const config = isString(options) ? resolveLinkInfo(resolvePrefix(pathPrefix, options)) : isString(options.link) ? { ...options, link: isLinkInternal(options.link) ? resolveRoute(resolvePrefix(pathPrefix, options.link)).path : options.link } : options; if ("children" in config) { const prefix = resolvePrefix(pathPrefix, config.prefix); const children = config.children === "structure" ? sidebarData[prefix] : config.children; return { ...config, prefix, children: children.map((item) => resolveSidebarItem(item, prefix)) }; } return { ...config }; }; /** * Resolve sidebar items if the config is an array * * @param options - Resolve sidebar array item options * @returns Resolved sidebar items */ const resolveArraySidebarItems = ({ config, prefix = "" }) => config.map((item) => resolveSidebarItem(item, prefix)); /** * Resolve sidebar items if the config is a key -> value (path-prefix -> array) object * * @param options - Resolve multi sidebar options * @returns Resolved sidebar items */ const resolveMultiSidebarItems = ({ config, routePath }) => { const sidebarRoutes = keys(config).sort((x, y) => y.length - x.length); for (const base of sidebarRoutes) if (startsWith(decodeURI(routePath), base)) { const matched = config[base]; return resolveArraySidebarItems({ config: matched === "structure" ? sidebarData[base] : matched || [], prefix: base }); } console.warn(`${decodeURI(routePath)} is missing it's sidebar config.`); return []; }; /** * Resolve sidebar items global computed * * It should only be resolved and provided once * * @param options - Resolve sidebar options * @returns Resolved sidebar items */ const resolveSidebarItems = ({ config, routeLocale, routePath }) => config === "structure" ? resolveArraySidebarItems({ config: sidebarData[routeLocale], prefix: routeLocale }) : isArray(config) ? resolveArraySidebarItems({ config }) : isPlainObject(config) ? resolveMultiSidebarItems({ config, routePath }) : []; //#endregion //#region src/client/composables/sidebar/useSidebarItems.ts const sidebarItemsSymbol = Symbol(__VUEPRESS_DEV__ ? "sidebarItems" : ""); /** Create sidebar items ref and provide as global computed in setup */ const setupSidebarItems = () => { const { frontmatter, routeLocale, routePath, themeLocale } = useData$1(); const sidebarOptions = computed(() => frontmatter.value.home ? false : frontmatter.value.sidebar ?? themeLocale.value.sidebar ?? "structure"); provide(sidebarItemsSymbol, computed(() => resolveSidebarItems({ config: sidebarOptions.value, routeLocale: routeLocale.value, routePath: routePath.value }))); }; /** * Inject sidebar items global computed * * @returns Sidebar items global computed */ const useSidebarItems = () => { const sidebarItems = inject(sidebarItemsSymbol); if (!sidebarItems) throw new Error("useSidebarItems() is called without provider."); return sidebarItems; }; //#endregion //#region src/client/components/base/PageFooter.ts var PageFooter_default = defineComponent({ name: "PageFooter", setup() { const { frontmatter, theme, themeLocale } = useData$1(); const author = usePageAuthor(); const enabled = computed(() => { const { copyright, footer } = frontmatter.value; return footer !== false && Boolean(copyright || footer || themeLocale.value.displayFooter); }); const footer = computed(() => { const { footer } = frontmatter.value; return isString(footer) ? footer : themeLocale.value.footer ?? ""; }); const authorText = computed(() => author.value.map(({ name }) => name).join(", ")); const getCopyrightText = (license) => `Copyright © ${(/* @__PURE__ */ new Date()).getFullYear()} ${authorText.value} ${license ? `${license} Licensed` : ""}`; const copyright = computed(() => { const { copyright, license = "" } = frontmatter.value; const { license: globalLicense } = theme.value; const { copyright: globalCopyright } = themeLocale.value; return copyright ?? (license ? getCopyrightText(license) : globalCopyright ?? (authorText.value || globalLicense ? getCopyrightText(globalLicense) : false)); }); return () => enabled.value ? h("footer", { class: "vp-footer-wrapper", "vp-footer": "" }, [footer.value ? h("div", { class: "vp-footer", innerHTML: footer.value }) : null, copyright.value ? h("div", { class: "vp-copyright", innerHTML: copyright.value }) : null]) : null; } }); //#endregion //#region src/client/components/appearance/AppearanceIcon.ts const AppearanceIcon = () => h(IconBase, { name: "outlook" }, () => [h("path", { d: "M224 800c0 9.6 3.2 44.8 6.4 54.4 6.4 48-48 76.8-48 76.8s80 41.6 147.2 0 134.4-134.4 38.4-195.2c-22.4-12.8-41.6-19.2-57.6-19.2C259.2 716.8 227.2 761.6 224 800zM560 675.2l-32 51.2c-51.2 51.2-83.2 32-83.2 32 25.6 67.2 0 112-12.8 128 25.6 6.4 51.2 9.6 80 9.6 54.4 0 102.4-9.6 150.4-32l0 0c3.2 0 3.2-3.2 3.2-3.2 22.4-16 12.8-35.2 6.4-44.8-9.6-12.8-12.8-25.6-12.8-41.6 0-54.4 60.8-99.2 137.6-99.2 6.4 0 12.8 0 22.4 0 12.8 0 38.4 9.6 48-25.6 0-3.2 0-3.2 3.2-6.4 0-3.2 3.2-6.4 3.2-6.4 6.4-16 6.4-16 6.4-19.2 9.6-35.2 16-73.6 16-115.2 0-105.6-41.6-198.4-108.8-268.8C704 396.8 560 675.2 560 675.2zM224 419.2c0-28.8 22.4-51.2 51.2-51.2 28.8 0 51.2 22.4 51.2 51.2 0 28.8-22.4 51.2-51.2 51.2C246.4 470.4 224 448 224 419.2zM320 284.8c0-22.4 19.2-41.6 41.6-41.6 22.4 0 41.6 19.2 41.6 41.6 0 22.4-19.2 41.6-41.6 41.6C339.2 326.4 320 307.2 320 284.8zM457.6 208c0-12.8 12.8-25.6 25.6-25.6 12.8 0 25.6 12.8 25.6 25.6 0 12.8-12.8 25.6-25.6 25.6C470.4 233.6 457.6 220.8 457.6 208zM128 505.6C128 592 153.6 672 201.6 736c28.8-60.8 112-60.8 124.8-60.8-16-51.2 16-99.2 16-99.2l316.8-422.4c-48-19.2-99.2-32-150.4-32C297.6 118.4 128 291.2 128 505.6zM764.8 86.4c-22.4 19.2-390.4 518.4-390.4 518.4-22.4 28.8-12.8 76.8 22.4 99.2l9.6 6.4c35.2 22.4 80 12.8 99.2-25.6 0 0 6.4-12.8 9.6-19.2 54.4-105.6 275.2-524.8 288-553.6 6.4-19.2-3.2-32-19.2-32C777.6 76.8 771.2 80 764.8 86.4z" })]); AppearanceIcon.displayName = "AppearanceIcon"; //#endregion //#region src/client/components/appearance/ColorModeSwitch.ts const AutoColorModeIcon = () => h(IconBase, { name: "auto" }, () => h("path", { d: "M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z" })); AutoColorModeIcon.displayName = "AutoColorModeIcon"; const LightColorModeIcon = () => h(IconBase, { name: "light" }, () => h("path", { d: "M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z" })); LightColorModeIcon.displayName = "LightColorModeIcon"; const DarkColorModeIcon = () => h(IconBase, { name: "dark" }, () => h("path", { d: "M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z" })); DarkColorModeIcon.displayName = "DarkColorModeIcon"; var ColorModeSwitch_default = defineComponent({ name: "ColorModeSwitch", setup() { const { config, isDarkMode, status } = useDarkMode(); const isPure = usePure(); const updateDarkmodeStatus = () => { if (config.value === "switch") status.value = { light: "dark", dark: "auto", auto: "light" }[status.value]; else status.value = status.value === "light" ? "dark" : "light"; }; const toggleDarkmode = async (event) => { if (!(document.startViewTransition && !globalThis.matchMedia("(prefers-reduced-motion: reduce)").matches && !isPure.value)) { updateDarkmodeStatus(); return; } const x = event.clientX; const y = event.clientY; const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y)); const oldStatus = isDarkMode.value; await document.startViewTransition(async () => { updateDarkmodeStatus(); await nextTick(); }).ready; if (isDarkMode.value !== oldStatus) document.documentElement.animate({ clipPath: isDarkMode.value ? [`circle(${endRadius}px at ${x}px ${y}px)`, `circle(0px at ${x}px ${y}px)`] : [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`] }, { duration: 400, pseudoElement: isDarkMode.value ? "::view-transition-old(root)" : "::view-transition-new(root)" }); }; return () => h("button", { type: "button", class: "vp-color-mode-switch", id: "color-mode-switch", onClick: toggleDarkmode }, [ h(AutoColorModeIcon, { style: { display: status.value === "auto" ? "block" : "none" } }), h(DarkColorModeIcon, { style: { display: status.value === "dark" ? "block" : "none" } }), h(LightColorModeIcon, { style: { display: status.value === "light" ? "block" : "none" } }) ]); } }); //#endregion //#region src/client/composables/appearance/useAppearanceLocale.ts const useAppearanceLocale = () => { const themeLocale = useThemeLocale(); return computed(() => themeLocale.value.outlookLocales); }; //#endregion //#region src/client/components/appearance/ColorMode.ts var ColorMode_default = defineComponent({ name: "ColorMode", setup() { const appearanceLocale = useAppearanceLocale(); const { canToggle } = useDarkMode(); return () => canToggle.value ? h("div", { class: "vp-color-mode" }, [h("label", { class: "vp-color-mode-title", for: "color-mode-switch" }, appearanceLocale.value.darkmode), h(ColorModeSwitch_default)]) : null; } }); //#endregion //#region src/client/components/appearance/ThemeColorPicker.ts const THEME_COLOR_KEY = "VUEPRESS_THEME_COLOR"; var ThemeColorPicker_default = defineComponent({ name: "ThemeColorPicker", props: { /** * Theme color picker config * * 主题色选择器配置 */ themeColors: { type: Object, required: true } }, setup(props) { const { isDarkMode } = useDarkMode(); const activeThemeColors = computed(() => { const themeColors = new Map(Object.entries(props.themeColors)); for (const [name, color] of themeColors.entries()) { if (name.includes("light")) { if (!isDarkMode.value) themeColors.set(name.replace("light-", ""), color); themeColors.delete(name); } if (name.includes("dark")) { if (isDarkMode.value) themeColors.set(name.replace("dark-", ""), color); themeColors.delete(name); } } return [...themeColors.entries()].map(([name, color]) => ({ name, color })); }); const setThemeColor = (name = "") => { const classes = document.documentElement.classList; const colorNames = activeThemeColors.value.map(({ name }) => name); if (!name) { localStorage.removeItem(THEME_COLOR_KEY); classes.remove(...colorNames); return; } classes.remove(...colorNames.filter((themeColorClass) => themeColorClass !== name)); classes.add(name); localStorage.setItem(THEME_COLOR_KEY, name); }; onMounted(() => { const theme = localStorage.getItem(THEME_COLOR_KEY); if (theme) setThemeColor(theme); }); return () => h("ul", { class: "vp-theme-color-picker", id: "theme-color-picker" }, [h("li", h("span", { class: "theme-color", onClick: () => { setThemeColor(); } })), activeThemeColors.value.map(({ name, color }) => h("li", h("span", { style: { background: color }, onClick: () => { setThemeColor(name); } })))]); } }); //#endregion //#region src/client/components/appearance/ThemeColor.ts const hasMultipleThemeColors = cssVariables.hasMultipleThemeColors === "true"; const themeColors = hasMultipleThemeColors ? fromEntries(entries(cssVariables).filter(([key]) => key.startsWith("theme-"))) : {}; var ThemeColor_default = defineComponent({ name: "ThemeColor", setup() { const appearanceLocale = useAppearanceLocale(); return () => hasMultipleThemeColors ? h("div", { class: "vp-theme-color" }, [h("label", { class: "vp-theme-color-title", for: "theme-color-picker" }, appearanceLocale.value.themeColor), h(ThemeColorPicker_default, { themeColors })]) : null; } }); //#endregion //#region src/client/components/appearance/ToggleFullScreenButton.ts const CancelFullScreenIcon = () => h(IconBase, { name: "cancel-fullscreen" }, () => h("path", { d: "M778.468 78.62H247.922c-102.514 0-186.027 83.513-186.027 186.027V795.08c0 102.514 83.513 186.027 186.027 186.027h530.432c102.514 0 186.71-83.513 186.026-186.027V264.647C964.494 162.02 880.981 78.62 778.468 78.62zM250.88 574.35h171.122c23.324 0 43.122 19.115 43.804 43.805v171.121c0 24.008-19.114 43.122-43.122 43.122-12.06 0-22.641-5.006-30.378-12.743s-12.743-19.115-12.743-30.379V722.83L224.597 877.91c-16.953 16.952-44.6 16.952-61.553 0-16.953-16.954-16.953-44.602 0-61.554L318.009 661.39h-66.446c-24.007 0-43.122-19.114-43.122-43.122 0-24.12 18.432-43.918 42.439-43.918zm521.899-98.873H601.657c-23.325 0-43.122-19.114-43.805-43.804V260.55c0-24.007 19.115-43.122 43.122-43.122 12.06 0 22.642 5.007 30.379 12.743s12.743 19.115 12.743 30.38v66.445l154.965-154.965c16.953-16.953 44.601-16.953 61.554 0 16.953 16.953 16.953 44.6 0 61.554L705.536 388.55h66.446c24.007 0 43.122 19.115 43.122 43.122.114 24.007-18.318 43.804-42.325 43.804z" })); CancelFullScreenIcon.displayName = "CancelFullScreenIcon"; const EnterFullScreenIcon = () => h(IconBase, { name: "enter-fullscreen" }, () => h("path", { d: "M762.773 90.24h-497.28c-96.106 0-174.4 78.293-174.4 174.4v497.28c0 96.107 78.294 174.4 174.4 174.4h497.28c96.107 0 175.04-78.293 174.4-174.4V264.64c0-96.213-78.186-174.4-174.4-174.4zm-387.2 761.173H215.04c-21.867 0-40.427-17.92-41.067-41.066V649.92c0-22.507 17.92-40.427 40.427-40.427 11.307 0 21.227 4.694 28.48 11.947 7.253 7.253 11.947 17.92 11.947 28.48v62.293l145.28-145.28c15.893-15.893 41.813-15.893 57.706 0 15.894 15.894 15.894 41.814 0 57.707l-145.28 145.28h62.294c22.506 0 40.426 17.92 40.426 40.427s-17.173 41.066-39.68 41.066zM650.24 165.76h160.427c21.866 0 40.426 17.92 41.066 41.067v160.426c0 22.507-17.92 40.427-40.426 40.427-11.307 0-21.227-4.693-28.48-11.947-7.254-7.253-11.947-17.92-11.947-28.48v-62.186L625.6 450.347c-15.893 15.893-41.813 15.893-57.707 0-15.893-15.894-15.893-41.814 0-57.707l145.28-145.28H650.88c-22.507 0-40.427-17.92-40.427-40.427s17.174-41.173 39.787-41.173z" })); EnterFullScreenIcon.displayName = "EnterFullScreenIcon"; var ToggleFullScreenButton_default = defineComponent({ name: "ToggleFullScreenButton", setup() { const { isSupported, isFullscreen, toggle } = useFullscreen(); return () => isSupported.value ? h("button", { type: "button", id: "full-screen-switch", class: "full-screen", ariaPressed: isFullscreen.value, onClick: () => toggle() }, isFullscreen.value ? h(CancelFullScreenIcon) : h(EnterFullScreenIcon)) : null; } }); //#endregion //#region src/client/components/appearance/ToggleFullScreen.ts var ToggleFullScreen_default = defineComponent({ name: "ToggleFullScreenButton", setup() { const appearanceLocale = useAppearanceLocale(); const { isSupported } = useFullscreen(); return () => isSupported.value ? h("div", { class: "full-screen-wrapper" }, [h("label", { class: "full-screen-title", for: "full-screen-switch" }, appearanceLocale.value.fullscreen), h(ToggleFullScreenButton_default)]) : null; } }); //#endregion //#region src/client/components/appearance/AppearanceSettings.ts var AppearanceSettings_default = defineComponent({ name: "AppearanceSettings", setup() { const theme = useTheme(); const isPure = usePure(); const enableFullScreen = computed(() => !isPure.value && theme.value.fullscreen); return () => h(ClientOnly, () => [ hasMultipleThemeColors ? h(ThemeColor_default) : null, h(ColorMode_default), enableFullScreen.value ? h(ToggleFullScreen_default) : null ]); } }); //#endregion //#region src/client/components/appearance/AppearanceButton.ts var AppearanceButton_default = defineComponent({ name: "AppearanceButton", setup() { const theme = useTheme(); const { canToggle } = useDarkMode(); const { isSupported } = useFullscreen(); const isPure = usePure(); const open = ref(false); const enableFullScreen = computed(() => !isPure.value && theme.value.fullscreen && isSupported); const enabled = computed(() => hasMultipleThemeColors || canToggle.value || enableFullScreen.value); onContentUpdated(() => { open.value = false; }); return () => enabled.value ? h("div", { class: "vp-nav-item hide-in-mobile" }, canToggle.value && !enableFullScreen.value && !hasMultipleThemeColors ? h(ColorModeSwitch_default) : enableFullScreen.value && !canToggle.value && !hasMultipleThemeColors ? h(ToggleFullScreenButton_default) : h("button", { type: "button", class: ["vp-appearance-button", { open: open.value }], tabindex: "-1", "aria-hidden": true }, [h(AppearanceIcon), h("div", { class: "vp-appearance-dropdown" }, h(AppearanceSettings_default))])) : null; } }); //#endregion //#region src/client/components/navbar/I18nIcon.ts const I18nIcon = () => h(IconBase, { name: "i18n" }, () => [h("path", { d: "M379.392 460.8 494.08 575.488l-42.496 102.4L307.2 532.48 138.24 701.44l-71.68-72.704L234.496 460.8l-45.056-45.056c-27.136-27.136-51.2-66.56-66.56-108.544h112.64c7.68 14.336 16.896 27.136 26.112 35.84l45.568 46.08 45.056-45.056C382.976 312.32 409.6 247.808 409.6 204.8H0V102.4h256V0h102.4v102.4h256v102.4H512c0 70.144-37.888 161.28-87.04 210.944L378.88 460.8zM576 870.4 512 1024H409.6l256-614.4H768l256 614.4H921.6l-64-153.6H576zM618.496 768h196.608L716.8 532.48 618.496 768z" })]); I18nIcon.displayName = "I18nIcon"; //#endregion //#region src/client/components/base/AutoLink.ts const AutoLink$1 = ({ config, iconSizing = "both" }, { emit, slots }) => { const { icon } = config; return h(AutoLink, { config, onFocusout: () => { emit("focusout"); } }, { ...slots, before: slots.before ?? (icon ? () => h(resolveComponent("VPIcon"), { icon, sizing: iconSizing }) : null) }); }; AutoLink$1.displayName = "AutoLink"; //#endregion //#region src/client/components/navbar/NavbarDropdown.ts var NavbarDropdown_default = defineComponent({ name: "NavbarDropdown", props: { /** * Dropdown config * * 下拉列表配置 */ config: { type: Object, required: true } }, slots: Object, setup(props, { slots }) { const config = toRef(props, "config"); const dropdownAriaLabel = computed(() => config.value.ariaLabel ?? config.value.text); const open = ref(false); /** * Open the dropdown when user tab and click from keyboard. * * Use event.detail to detect tab and click from keyboard. The Tab + Click is UIEvent > * KeyboardEvent, so the detail is 0. * * @param event - Click event * @see https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail */ const handleDropdown = (event) => { if (event.detail === 0) open.value = !open.value; }; onContentUpdated(() => { open.value = false; }); return () => h("div", { class: ["vp-dropdown-wrapper", { open: open.value }] }, [h("button", { type: "button", class: "vp-dropdown-title", "aria-label": dropdownAriaLabel.value, onClick: handleDropdown }, [ slots.title?.() ?? [h(resolveComponent("VPIcon"), { icon: config.value.icon }), props.config.text], h("span", { class: "arrow" }), h("ul", { class: "vp-dropdown" }, config.value.children.map((child, index) => { const isLastChild = index === config.value.children.length - 1; return h("li", { class: "vp-dropdown-item" }, "children" in child ? [h("h4", { class: "vp-dropdown-subtitle" }, child.link ? h(AutoLink$1, { config: child, onFocusout: () => { if (child.children.length === 0 && isLastChild) open.value = false; } }) : child.text), h("ul", { class: "vp-dropdown-subitems" }, child.children.map((grandchild, grandIndex) => h("li", { class: "vp-dropdown-subitem" }, h(AutoLink$1, { config: grandchild, onFocusout: () => { if (grandIndex === child.children.length - 1 && isLastChild) open.value = false; } }))))] : h(AutoLink$1, { config: child, onFocusout: () => { if (isLastChild) open.value = false; } })); })) ])]); } }); //#endregion //#region src/client/composables/navbar/useNavbarLanguageDropdown.ts const isAllowedRootPath = (path) => path === "/" || /^\/?404(?:\.html)?$/u.test(path); /** * Get navbar config of select language dropdown * * @returns Navbar config of select language dropdown */ const useNavbarLanguageDropdown = () => { const { routeLocale, site, siteLocale, theme, themeLocale } = useData$1(); const routePaths = useRoutePaths(); const route = useRoute(); const isMounted = ref(false); const isRootLocaleClean = computed(() => { const subLocales = Object.keys(site.value.locales).filter((localePath) => localePath !== "/"); if (subLocales.length === 0) return false; const localeRegExp = new RegExp(`^(?:${subLocales.map((locale) => locale.replaceAll(/[.*+?^${}()|[\]\\]/gu, String.raw`\$&`)).join("|")})`, "u"); return routePaths.value.filter((path) => !localeRegExp.test(path)).every((path) => isAllowedRootPath(path)); }); onMounted(() => { isMounted.value = true; }); return computed(() => { let localePaths = keys(site.value.locales); const extraLocales = entries(theme.value.extraLocales ?? {}); if (isRootLocaleClean.value) localePaths = localePaths.filter((localePath) => localePath !== "/"); if (localePaths.length < 2 && extraLocales.length === 0) return null; const { path, fullPath } = route; const { navbarLocales } = themeLocale.value; return { text: "", ariaLabel: navbarLocales.selectLangAriaLabel, children: [...localePaths.map((targetLocalePath) => { const targetSiteLocale = site.value.locales[targetLocalePath] ?? {}; const targetThemeLocale = theme.value.locales[targetLocalePath] ?? {}; const targetLang = targetSiteLocale.lang; const text = targetThemeLocale.navbarLocales.langName; let link; if (targetLang === siteLocale.value.lang) link = fullPath; else { const targetLocalePage = path.replace(routeLocale.value, targetLocalePath); link = routePaths.value.some((item) => item === targetLocalePage) ? isMounted.value ? fullPath.replace(path, targetLocalePage) : targetLocalePage : targetThemeLocale.home ?? targetLocalePath; } return { text, link }; }), ...extraLocales.map(([text, localPath]) => ({ text, link: localPath.replace(":route", (isMounted.value ? fullPath : localPath).replace(routeLocale.value, "")) }))] }; }); }; //#endregion //#region src/client/components/navbar/LanguageDropdown.ts var LanguageDropdown_default = defineComponent({ name: "LanguageDropdown", setup() { const dropdown = useNavbarLanguageDropdown(); return () => dropdown.value ? h("div", { class: "vp-nav-item" }, h(NavbarDropdown_default, { config: dropdown.value }, { title: () => h(I18nIcon, { "aria-label": dropdown.value?.ariaLabel, style: { width: "1rem", height: "1rem", verticalAlign: "middle" } }) })) : null; } }); //#endregion //#region src/client/components/navbar/NavbarBrand.ts var NavbarBrand_default = defineComponent({ name: "NavbarBrand", setup() { const { routeLocale, siteLocale, themeLocale } = useData$1(); const siteBrandLink = computed(() => themeLocale.value.home ?? routeLocale.value); const siteTitle = computed(() => siteLocale.value.title); const siteBrandTitle = computed(() => themeLocale.value.navbarTitle ?? siteTitle.value); const siteBrandLogo = computed(() => themeLocale.value.logo ? withBase(themeLocale.value.logo) : null); const siteBrandLogoDark = computed(() => themeLocale.value.logoDark ? withBase(themeLocale.value.logoDark) : null); return () => h(RouteLink, { to: siteBrandLink.value, class: "vp-brand", "aria-label": themeLocale.value.routerLocales.home }, () => [ siteBrandLogo.value ? h("img", { class: ["vp-nav-logo", { light: Boolean(siteBrandLogoDark.value) }], src: siteBrandLogo.value, alt: "" }) : null, siteBrandLogoDark.value ? h("img", { class: ["vp-nav-logo dark"], src: siteBrandLogoDark.value, alt: "" }) : null, siteBrandTitle.value ? h("span", { class: ["vp-site-name", { "hide-in-pad": siteBrandLogo.value && (themeLocale.value.hideSiteNameOnMobile ?? true) }] }, siteBrandTitle.value) : null ]); } }); //#endregion //#region src/client/composables/navbar/useNavbarItems.ts const resolveNavbarItem = (item, prefix = "") => { if (isString(item)) return resolveLinkInfo(resolvePrefix(prefix, item)); if ("children" in item) return { ...item, ...item.link && isLinkInternal(item.link) ? { link: resolveRoute(resolvePrefix(prefix, item.link)).path } : {}, children: item.children.map((child) => resolveNavbarItem(child, resolvePrefix(prefix, item.prefix))) }; return { ...item, link: isLinkInternal(item.link) ? resolveRoute(resolvePrefix(prefix, item.link)).path : item.link }; }; const useNavbarItems = () => { const themeLocaleData = useThemeLocale(); return computed(() => (themeLocaleData.value.navbar || []).map((item) => resolveNavbarItem(item))); }; //#endregion //#region src/client/components/navbar/NavbarLinks.ts var NavbarLinks_default = defineComponent({ name: "NavbarLinks", setup() { const navbarConfig = useNavbarItems(); return () => navbarConfig.value.length > 0 ? h("nav", { class: "vp-nav-links" }, navbarConfig.value.map((config) => h("div", { class: "vp-nav-item hide-in-mobile" }, "children" in config ? h(NavbarDropdown_default, { config }) : h(AutoLink$1, { config, iconSizing: "height" })))) : null; } }); //#endregion //#region src/client/components/navbar/NavScreenMenu.ts const isLastItemOfArray = (item, arr) => arr[arr.length - 1] === item; var NavScreenMenu_default = defineComponent({ name: "NavScreenMenu", props: { /** * Navbar Screen nav-screen-menu list config * * 导航栏下拉列表配置 */ config: { type: Object, required: true } }, setup(props) { const config = toRef(props, "config"); const route = useRoute(); const ariaLabel = computed(() => config.value.ariaLabel ?? config.value.text); const open = ref(false); onContentUpdated(() => { open.value = false; }); watch(() => route.fullPath, () => { open.value = false; }); return () => [h("button", { type: "button", class: ["vp-nav-screen-menu-title", { active: open.value }], "aria-label": ariaLabel.value, onClick: () => { open.value = !open.value; } }, [h("span", { class: "text" }, [h(resolveComponent("VPIcon"), { icon: config.value.icon, sizing: "both" }), props.config.text]), h("span", { class: ["arrow", open.value ? "down" : "end"] })]), h("ul", { class: ["vp-nav-screen-menu", { hide: !open.value }] }, config.value.children.map((child) => h("li", { class: "vp-nav-screen-menu-item" }, "children" in child ? [h("h4", { class: "vp-nav-screen-menu-subtitle" }, child.link ? h(AutoLink$1, { config: child, onFocusout: () => { if (isLastItemOfArray(child, config.value.children) && child.children.length === 0) open.value = false; } }) : child.text), h("ul", { class: "vp-nav-screen-menu-subitems" }, child.children.map((grandchild) => h("li", { class: "vp-nav-screen-menu-subitem" }, h(AutoLink$1, { config: grandchild, onFocusout: () => { if (isLastItemOfArray(grandchild, child.children) && isLastItemOfArray(child, config.value.children)) open.value = false; } }))))] : h(AutoLink$1, { config: child, onFocusout: () => { if (isLastItemOfArray(child, config.value.children)) open.value = false; } }))))]; } }); //#endregion //#region src/client/components/navbar/NavScreenLinks.ts var NavScreenLinks_default = defineComponent({ name: "NavScreenLinks", setup() { const navbarConfig = useNavbarItems(); return () => navbarConfig.value.length > 0 ? h("nav", { class: "nav-screen-links" }, navbarConfig.value.map((config) => h("div", { class: "navbar-links-item" }, "children" in config ? h(NavScreenMenu_default, { config }) : h(AutoLink$1, { config })))) : null; } }); //#endregion //#region src/client/composables/useWindowSize.ts const { mobileBreakPoint, pcBreakPoint } = cssVariables; const getPixels = (length) => length.endsWith("px") ? Number(length.slice(0, -2)) : null; const useWindowSize = () => { const isMobile = ref(false); const isPC = ref(false); const windowSizeHandler = () => { isMobile.value = window.innerWidth <= (getPixels(mobileBreakPoint) ?? 719); isPC.value = window.innerWidth >= (getPixels(pcBreakPoint) ?? 1440); }; useEventListener("resize", windowSizeHandler, false); useEventListener("orientationchange", windowSizeHandler, false); onMounted(() => { windowSizeHandler(); }); return { isMobile, isPC }; }; //#endregion //#region src/client/components/navbar/NavScreen.ts var NavScreen_default = defineComponent({ name: "NavScreen", props: { /** * Whether to show the screen * * 是否显示 */ show: Boolean }, slots: Object, setup(props, { slots }) { const { isMobile } = useWindowSize(); const body = shallowRef(); const isLocked = useScrollLock(body); onContentUpdated(() => { isLocked.value = false; }); watch(isMobile, (value) => { if (!value && props.show) isLocked.value = false; }); onMounted(() => { body.value = document.body; }); onUnmounted(() => { isLocked.value = false; }); return () => h(Transition, { name: "fade-in-down", onEnter: () => { isLocked.value = true; }, onAfterLeave: () => { isLocked.value = false; } }, () => props.show ? h("div", { id: "nav-screen", class: "vp-nav-screen" }, h("div", { class: "vp-nav-screen-container" }, [ slots.navScreenTop?.(), h(NavScreenLinks_default), h("div", { class: "vp-appearance-wrapper" }, h(AppearanceSettings_default)), slots.navScreenBottom?.() ])) : null); } }); //#endregion //#region src/client/composables/navbar/useNavbarRepo.ts /** * Get navbar config of repository link * * @returns Repository link config, or null if it doesn't exist or is hidden */ const useNavbarRepo = () => { const themeLocale = useThemeLocale(); const repo = computed(() => themeLocale.value.repo); const repoLink = computed(() => repo.value ? resolveRepoLink(repo.value) : null); const repoType = computed(() => repo.value ? resolveRepoType(repo.value) : null); const repoLabel = computed(() => repoLink.value ? themeLocale.value.repoLabel ?? repoType.value ?? "Source" : null); return computed(() => { if (!repoLink.value || !repoLabel.value || themeLocale.value.repoDisplay === false) return null; return { type: repoType.value ?? "Source", label: repoLabel.value, link: repoLink.value }; }); }; //#endregion //#region src/client/components/navbar/RepoLink.ts var RepoLink_default = defineComponent({ name: "RepoLink", setup() { const repo = useNavbarRepo(); return () => repo.value ? h("div", { class: "vp-nav-item vp-action" }, h("a", { class: "vp-action-link", href: repo.value.link, target: "_blank", rel: "noopener noreferrer", "aria-label": repo.value.label }, h(RepoIcon, { type: repo.value.type, style: { width: "1.25rem", height: "1.25rem", verticalAlign: "middle" } }))) : null; } }); //#endregion //#region src/client/components/navbar/ToggleNavbarButton.ts const ToggleNavbarButton = ({ active = false }, { emit }) => h("button", { type: "button", class: ["vp-toggle-navbar-button", { "is-active": active }], "aria-label": "Toggle Navbar", "aria-expanded": active, "aria-controls": "nav-screen", onClick: () => { emit("toggle"); } }, h("span", [ h("span", { class: "vp-top" }), h("span", { class: "vp-middle" }), h("span", { class: "vp-bottom" }) ])); ToggleNavbarButton.displayName = "ToggleNavbarButton"; //#endregion //#region src/client/components/navbar/ToggleSidebarButton.ts const ToggleSidebarButton = (_, { emit }) => h("button", { type: "button", class: "vp-toggle-sidebar-button", title: "Toggle Sidebar", onClick: () => { emit("toggle"); } }, h("span", { class: "icon" })); ToggleSidebarButton.displayName = "ToggleSidebarButton"; ToggleSidebarButton.emits = ["toggle"]; //#endregion //#region src/client/composables/navbar/useNavbarAutoHide.ts const useNavbarAutoHide = () => { const themeLocale = useThemeLocale(); const { isMobile } = useWindowSize(); return computed(() => { const { navbarAutoHide = "mobile" } = themeLocale.value; return navbarAutoHide !== "none" && (navbarAutoHide === "always" || isMobile.value); }); }; //#endregion //#region src/client/components/navbar/Navbar.ts var Navbar_default = defineComponent({ name: "NavBar", emits: ["toggleSidebar"], slots: Object, setup(_props, { emit, slots }) { const themeLocale = useThemeLocale(); const { isMobile } = useWindowSize(); const showScreen = ref(false); const autoHide = useNavbarAutoHide(); const navbarLayout = computed(() => themeLocale.value.navbarLayout ?? { start: ["Brand"], center: ["Links"], end: [ "Language", "Repo", "Outlook", "Search" ] }); const navbarComponentMap = { Brand: NavbarBrand_default, Language: __VP_I18N__ ? LanguageDropdown_default : noopComponent, Links: NavbarLinks_default, Repo: RepoLink_default, Outlook: AppearanceButton_default, Search: hasGlobalComponent("SearchBox") ? resolveComponent("SearchBox") : noopComponent }; const getNavbarComponent = (component) => navbarComponentMap[component] ?? (hasGlobalComponent(component) ? resolveComponent(component) : noopComponent); onContentUpdated(() => { showScreen.value = false; }); watch(isMobile, (value) => { if (!value) showScreen.value = false; }); return () => [h("header", { key: "navbar", id: "navbar", class: ["vp-navbar", { "auto-hide": autoHide.value }], "vp-navbar": "" }, [ h("div", { class: "vp-navbar-start" }, [h(ToggleSidebarButton, { onToggle: () => { if (showScreen.value) showScreen.value = false; emit("toggleSidebar"); } }), navbarLayout.value.start?.map((item) => h(getNavbarComponent(item)))]), h("div", { class: "vp-navbar-center" }, [navbarLayout.value.center?.map((item) => h(getNavbarComponent(item)))]), h("div", { class: "vp-navbar-end" }, [navbarLayout.value.end?.map((item) => h(getNavbarComponent(item))), h(ToggleNavbarButton, { active: showScreen.value, onToggle: () => { showScreen.value = !showScreen.value; } })]) ]), h(NavScreen_default, { show: showScreen.value }, slots)]; } }); //#endregion //#region src/client/utils/isActiveItem.ts const isActiveItem = (route, item) => item.activeMatch ? new RegExp(item.activeMatch, "u").test(route.path) : isActiveLink(route, item.link); //#endregion //#region src/client/components/sidebar/SidebarChild.ts var SidebarChild_default = defineComponent({ name: "SidebarChild", props: { /** * Sidebar item config * * 侧边栏项目配置 */ config: { type: Object, required: true } }, setup(props) { const route = useRoute(); return () => isString(props.config.link) ? h(AutoLink$1, { class: ["vp-sidebar-link", { active: isActiveItem(route, props.config) }], config: { ...props.config, exact: true } }) : h("p", props, [h(resolveComponent("VPIcon"), { icon: props.config.icon, sizing: "both" }), props.config.text]); } }); //#endregion //#region src/client/utils/sidebar/isActiveSidebarItem.ts const isActiveSidebarItem = (route, item) => "children" in item ? Boolean(item.prefix) && isActiveLink(route, item.prefix) || item.children.some((child) => isActiveSidebarItem(route, child)) : isActiveItem(route, item); //#endregion //#region src/client/components/sidebar/SidebarGroup.ts var SidebarGroup_default = defineComponent({ name: "SidebarGroup", props: { /** * Sidebar group item config * * 侧边栏分组配置 */ config: { type: Object, required: true }, /** * Whether current group is open * * 当前分组是否展开 */ open: { type: Boolean, required: true } }, emits: ["toggle"], setup(props, { emit }) { const route = useRoute(); const hasBeenToggled = ref(false); const active = computed(() => isActiveSidebarItem(route, props.config)); const exact = computed(() => isActiveItem(route, props.config)); const shouldOpen = computed(() => props.open || props.config.expanded && !hasBeenToggled.value); return () => { const { collapsible, children, icon, prefix, link, text } = props.config; return h("section", { class: "vp-sidebar-group" }, [h(collapsible ? "button" : "p", { class: ["vp-sidebar-header", { clickable: collapsible || link, exact: exact.value, active: active.value }], ...collapsible ? { type: "button", onClick: () => { hasBeenToggled.value = true; emit("toggle"); } } : {} }, [ h(resolveComponent("VPIcon"), { icon, sizing: "both" }), link ? h(AutoLink$1, { class: "vp-sidebar-title no-external-link-icon", config: { text, link } }) : h("span", { class: "vp-sidebar-title" }, text), collapsible ? h("span", { class: ["vp-arrow", shouldOpen.value ? "down" : "end"] }) : null ]), shouldOpen.value || !collapsible ? h(SidebarLinks_default, { key: prefix, config: children }) : null]); }; } }); //#endregion //#region src/client/components/sidebar/SidebarLinks.ts var SidebarLinks_default = defineComponent({ name: "SidebarLinks", props: { /** * Sidebar links config * * 侧边栏链接配置 */ config: { type: Array, required: true } }, setup(props) { const route = useRoute(); const routePath = useRoutePath(); const openGroupIndex = ref(-1); const toggleGroup = (index) => { openGroupIndex.value = index === openGroupIndex.value ? -1 : index; }; watchImmediate(routePath, () => { openGroupIndex.value = props.config.findIndex((item) => isActiveSidebarItem(route, item)); }, { flush: "post" }); return () => h("ul", { class: "vp-sidebar-links" }, props.config.map((config, index) => h("li", "children" in config ? h(SidebarGroup_default, { config, open: index === openGroupIndex.value, onToggle: () => { toggleGroup(index); } }) : h(SidebarChild_default, { config })))); } }); //#endregion //#region src/client/components/sidebar/Sidebar.ts var Sidebar_default = defineComponent({ name: "SideBar", slots: Object, setup(_props, { slots }) { const route = useRoute(); const sidebarItems = useSidebarItems(); const sidebar = shallowRef(); onMounted(() => { watchImmediate(() => route.hash, (hash) => { const activeSidebarItem = document.querySelector(`.vp-sidebar a.vp-sidebar-link[href="${route.path}${hash}"]`); if (!activeSidebarItem) return; const { top: sidebarTop, height: sidebarHeight } = sidebar.value.getBoundingClientRect(); const { top: activeSidebarItemTop, height: activeSidebarItemHeight } = activeSidebarItem.getBoundingClientRect(); if (activeSidebarItemTop < sidebarTop) activeSidebarItem.scrollIntoView(true); else if (activeSidebarItemTop + activeSidebarItemHeight > sidebarTop + sidebarHeight) activeSidebarItem.scrollIntoView(false); }); }); return () => h("aside", { ref: sidebar, key: "sidebar", id: "sidebar", class: "vp-sidebar", "vp-sidebar": "" }, [ slots.sidebarTop?.(), slots.sidebarItems?.(sidebarItems.value) ?? h(SidebarLinks_default, { config: sidebarItems.value }), slots.sidebarBottom?.() ]); } }); //#endregion //#region src/client/components/base/MainLayout.ts var MainLayout_default = defineComponent({ name: "MainLayout", props: { /** * Extra class of container * * 容器额外类名 */ containerClass: String, /** * Whether disable navbar * * 是否禁用导航栏 */ noNavbar: Boolean, /** * Whether disable sidebar * * 是否禁用侧边栏 */ noSidebar: Boolean, /** Whether disable toc */ noToc: Boolean }, slots: Object, setup(props, { slots }) { const { frontmatter, theme, themeLocale } = useData$1(); const { isMobile, isPC } = useWindowSize(); const isPure = usePure(); const [isMobileSidebarOpen, toggleMobileSidebar] = useToggle(false); const [isDesktopSidebarCollapsed, toggleDesktopSidebar] = useToggle(false); const sidebarItems = useSidebarItems(); const body = shallowRef(); const isLocked = useScrollLock(body); const hideNavbar = ref(false); const autoHide = useNavbarAutoHide(); watch(autoHide, (value) => { if (!value) hideNavbar.value = false; }); const enableNavbar = computed(() => { if (props.noNavbar) return false; if (frontmatter.value.navbar === false || themeLocale.value.navbar === false) return false; return Boolean(themeLocale.value.logo ?? themeLocale.value.repo ?? themeLocale.value.navbar); }); const enableExternalLinkIcon = computed(() => frontmatter.value.externalLinkIcon ?? theme.value.externalLinkIcon ?? true); const enableToc = computed(() => !props.noToc && !frontmatter.value.home && Boolean(frontmatter.value.toc ?? themeLocale.value.toc ?? true)); const touchStart = { x: 0, y: 0 }; const onTouchStart = (event) => { touchStart.x = event.changedTouches[0].clientX; touchStart.y = event.changedTouches[0].clientY; }; const onTouchEnd = (event) => { const dx = event.changedTouches[0].clientX - touchStart.x; const dy = event.changedTouches[0].clientY - touchStart.y; if (Math.abs(dx) > Math.abs(dy) * 1.5 && Math.abs(dx) > 40) if (dx > 0 && touchStart.x <= 80) toggleMobileSidebar(true); else toggleMobileSidebar(false); }; let lastDistance = 0; useEventListener("scroll", useThrottleFn(() => { const distance = window.scrollY; if (distance <= 58 || distance < lastDistance) hideNavbar.value = false; else if (lastDistance + 200 < distance && !isMobileSidebarOpen.value && autoHide.value) hideNavbar.value = true; lastDistance = distance; }, 300, true)); watch(isMobile, (value) => { if (!value) toggleMobileSidebar(false); }); watch(isMobileSidebarOpen, (value) => { isLocked.value = value; }); onContentUpdated(() => { toggleMobileSidebar(false); }); onMounted(() => { body.value = document.body; }); onUnmounted(() => { isLocked.value = false; }); return () => { const sidebarTopContent = slots.sidebarTop?.(); const sidebarItemsContent = slots.sidebarItems?.(sidebarItems.value); const sidebarBottomContent = slots.sidebarBottom?.(); const isSidebarEmpty = isSlotContentEmpty(sidebarTopContent) && isSlotContentEmpty(sidebarItemsContent) && isSlotContentEmpty(sidebarBottomContent); const noSidebar = props.noSidebar || frontmatter.value.sidebar === false || (frontmatter.value.home || sidebarItems.value.length === 0) && isSidebarEmpty; return h(hasGlobalComponent("GlobalEncrypt") ? resolveComponent("GlobalEncrypt") : RenderDefault, () => h("div", { class: [ "theme-container", { "hide-navbar": hideNavbar.value, "no-navbar": !enableNavbar.value, "sidebar-collapsed": !isMobile.value && !isPC.value && isDesktopSidebarCollapsed.value, "sidebar-open": isMobile.value && isMobileSidebarOpen.value, "no-sidebar": noSidebar, "external-link-icon": enableExternalLinkIcon.value, pure: isPure.value, "has-toc": enableToc.value }, props.containerClass ?? "", frontmatter.value.containerClass ?? "" ], "vp-container": "", onTouchStart, onTouchEnd }, [ enabl