vuepress-theme-plume
Version:
A Blog&Document Theme for VuePress 2.0
1,508 lines (1,475 loc) • 58.1 kB
JavaScript
import { hasOwn, tryOnScopeDispose, useDark, useEventListener, useLocalStorage, useMediaQuery, useSessionStorage, useThrottleFn, watchDebounced } from "@vueuse/core";
import { computed, customRef, inject, nextTick, onMounted, onUnmounted, onUpdated, provide, readonly, ref, shallowRef, toValue, watch, watchEffect } from "vue";
import { decodeData, ensureLeadingSlash, isArray, isLinkAbsolute, isLinkExternal, isLinkWithProtocol, isPlainObject, isString, removeLeadingSlash } from "@vuepress/helper/client";
import { clientDataSymbol, onContentUpdated, resolveRoute, resolveRouteFullPath, usePageData, usePageFrontmatter, usePageLang, useRoute, useRouteLocale, useRouter, useSiteLocaleData } from "vuepress/client";
import { inBrowser, isActive, normalizeLink, normalizePrefix, resolveEditLink, resolveNavLink, toArray } from "../utils/index.js";
import { collections } from "@internal/collectionsData";
import { ensureEndingSlash, isPlainObject as isPlainObject$1, isString as isString$1, removeEndingSlash, removeLeadingSlash as removeLeadingSlash$1 } from "vuepress/shared";
import { themeData as themeData$1 } from "@internal/themePlumeData";
import { compare, genSaltSync } from "bcrypt-ts/browser";
import { encrypt as encrypt$1 } from "@internal/encrypt";
import { sidebar } from "@internal/sidebar";
import { useContributors as useContributors$1 } from "@vuepress/plugin-git/client";
import { icons } from "@internal/iconify";
import { postsData as postsData$1 } from "@internal/postsData";
import { articleTagColors } from "@internal/articleTagColors";
import { defineWatermarkConfig } from "@vuepress/plugin-watermark/client";
//#region src/client/composables/collections.ts
const collectionsRef = ref(collections);
const collectionItemRef = ref();
const forceCollection = ref();
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) __VUE_HMR_RUNTIME__.updateCollections = (data) => {
collectionsRef.value = data;
};
const useCollections = () => collectionsRef;
const useCollection = () => collectionItemRef;
function forceUpdateCollection(dir) {
forceCollection.value = dir;
}
function setupCollection() {
const routeLocale = useRouteLocale();
const { page } = useData();
const startWith = (link) => link ? page.value.path.startsWith(normalizeLink(routeLocale.value, removeLeadingSlash$1(link))) : false;
watchEffect(() => {
collectionItemRef.value = collectionsRef.value[routeLocale.value]?.find((item) => {
if (forceCollection.value) {
if (forceCollection.value === true) return item.type === "post";
return item.dir === forceCollection.value;
}
if (page.value.filePathRelative) return page.value.filePathRelative?.startsWith(normalizeLink(routeLocale.value, item.dir).slice(1));
else {
const { link, linkPrefix, dir, tagsLink, categoriesLink, archivesLink } = item;
return startWith(link) || startWith(linkPrefix) || startWith(dir) || startWith(tagsLink) || startWith(categoriesLink) || startWith(archivesLink);
}
});
});
}
//#endregion
//#region src/client/composables/theme-data.ts
const themeLocaleDataSymbol = Symbol(__VUEPRESS_DEV__ ? "themeLocaleData" : "");
const themeData = ref(themeData$1);
function useThemeData() {
return themeData;
}
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) __VUE_HMR_RUNTIME__.updateThemeData = (data) => {
themeData.value = data;
};
function useThemeLocaleData() {
const themeLocaleData = inject(themeLocaleDataSymbol);
if (!themeLocaleData) throw new Error("useThemeLocaleData() is called without provider.");
return themeLocaleData;
}
/**
* Merge the locales fields to the root fields
* according to the route path
*/
function resolveThemeLocaleData(theme, routeLocale) {
const { locales, ...baseOptions } = theme;
return {
...baseOptions,
...locales?.[routeLocale]
};
}
function setupThemeData(app) {
const themeData$2 = useThemeData();
const clientData = app._context.provides[clientDataSymbol];
const themeLocaleData = computed(() => resolveThemeLocaleData(themeData$2.value, clientData.routeLocale.value));
app.provide(themeLocaleDataSymbol, themeLocaleData);
Object.defineProperties(app.config.globalProperties, {
$theme: { get() {
return themeData$2.value;
} },
$themeLocale: { get() {
return themeLocaleData.value;
} }
});
}
//#endregion
//#region src/client/composables/dark-mode.ts
const darkModeSymbol = Symbol(__VUEPRESS_DEV__ ? "darkMode" : "");
function enableTransitions() {
if (typeof document === "undefined") return false;
return "startViewTransition" in document && window.matchMedia("(prefers-reduced-motion: no-preference)").matches;
}
function setupDarkMode(app) {
const theme = useThemeData();
const transition = theme.value.transition;
const disableTransition = enableTransitions() || (typeof transition === "object" ? transition.appearance === false : transition === false);
const appearance = theme.value.appearance;
const isDark = appearance === "force-dark" ? ref(true) : appearance ? useDark({
storageKey: "vuepress-theme-appearance",
attribute: "data-theme",
valueLight: "light",
valueDark: "dark",
disableTransition,
initialValue: () => typeof appearance === "string" ? appearance : "auto",
...typeof appearance === "object" ? appearance : {}
}) : ref(false);
app.provide(darkModeSymbol, isDark);
if (__VUEPRESS_DEV__ && appearance === "force-dark" && typeof document !== "undefined") document.documentElement.dataset.theme = "dark";
Object.defineProperty(app.config.globalProperties, "$isDark", { get: () => isDark });
useEventListener("beforeprint", () => {
if (isDark.value) document.documentElement.dataset.theme = "light";
});
useEventListener("afterprint", () => {
if (isDark.value) document.documentElement.dataset.theme = "dark";
});
}
/**
* Inject dark mode global computed
*/
function useDarkMode() {
const isDarkMode = inject(darkModeSymbol);
if (!isDarkMode) throw new Error("useDarkMode() is called without provider.");
return isDarkMode;
}
//#endregion
//#region src/client/composables/data.ts
function useData() {
const theme = useThemeLocaleData();
const page = usePageData();
const frontmatter = usePageFrontmatter();
const site = useSiteLocaleData();
const isDark = useDarkMode();
return {
theme,
page,
frontmatter,
lang: usePageLang(),
site,
isDark,
collection: useCollection()
};
}
//#endregion
//#region src/client/composables/encrypt-data.ts
const encrypt = ref(resolveEncryptData(encrypt$1));
function useEncryptData() {
return encrypt;
}
function resolveEncryptData([global, separator, admin, matches, rules]) {
const keys = matches.map((match) => decodeData(match));
return {
global,
separator,
matches: keys,
admins: admin.split(separator),
ruleList: Object.keys(rules).map((key) => ({
key,
match: keys[key],
rules: rules[key].split(separator)
}))
};
}
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) __VUE_HMR_RUNTIME__.updateEncrypt = (data) => {
encrypt.value = resolveEncryptData(data);
};
//#endregion
//#region src/client/composables/encrypt.ts
const EncryptSymbol = Symbol(__VUEPRESS_DEV__ ? "Encrypt" : "");
const storage = useSessionStorage("2a0a3d6afb2fdf1f", () => {
if (__VUEPRESS_SSR__) return {
s: ["", ""],
g: "",
p: {}
};
return {
s: [genSaltSync(10), genSaltSync(10)],
g: "",
p: {}
};
});
function mergeHash(hash) {
const [left, right] = storage.value.s;
return left + hash + right;
}
function splitHash(hash) {
const [left, right] = storage.value.s;
if (!hash.startsWith(left) || !hash.endsWith(right)) return "";
return hash.slice(left.length, hash.length - right.length);
}
const compareCache = /* @__PURE__ */ new Map();
async function compareDecrypt(content, hash, separator = ":") {
const key = [content, hash].join(separator);
if (compareCache.has(key)) return compareCache.get(key);
try {
const result = await compare(content, hash);
compareCache.set(key, result);
return result;
} catch {
compareCache.set(key, false);
return false;
}
}
const matchCache = /* @__PURE__ */ new Map();
function createMatchRegex(match) {
if (matchCache.has(match)) return matchCache.get(match);
const regex = new RegExp(match);
matchCache.set(match, regex);
return regex;
}
function toMatch(match, pagePath, filePathRelative) {
const relativePath = filePathRelative || "";
if (match[0] === "^") {
const regex = createMatchRegex(match);
return regex.test(pagePath) || regex.test(relativePath);
}
if (match.endsWith(".md")) return relativePath && relativePath.endsWith(match);
return pagePath.startsWith(match) || relativePath.startsWith(removeLeadingSlash$1(match));
}
function setupEncrypt() {
const { page } = useData();
const route = useRoute();
const encrypt$2 = useEncryptData();
const hasPageEncrypt = computed(() => {
const pagePath = route.path;
const filePathRelative = page.value.filePathRelative;
if (page.value._e) return true;
return encrypt$2.value.ruleList.length ? encrypt$2.value.matches.some((match) => toMatch(match, pagePath, filePathRelative)) : false;
});
const isGlobalDecrypted = computed(() => {
if (!encrypt$2.value.global) return true;
const hash = splitHash(storage.value.g);
return !!hash && encrypt$2.value.admins.includes(hash);
});
const hashList = computed(() => {
const pagePath = route.path;
const filePathRelative = page.value.filePathRelative;
const passwords = typeof page.value._e === "string" ? page.value._e.split(":") : [];
return [passwords.length ? {
key: pagePath.replace(/\//g, "").replace(/\.html$/, ""),
match: pagePath,
rules: passwords
} : void 0, ...encrypt$2.value.ruleList.length ? encrypt$2.value.ruleList.filter((item) => toMatch(item.match, pagePath, filePathRelative)) : []].filter(Boolean);
});
provide(EncryptSymbol, {
hasPageEncrypt,
isGlobalDecrypted,
isPageDecrypted: computed(() => {
if (!hasPageEncrypt.value) return true;
const hash = splitHash(storage.value.g || "");
if (hash && encrypt$2.value.admins.includes(hash)) return true;
for (const { key, rules } of hashList.value) if (hasOwn(storage.value.p, key)) {
const hash$1 = splitHash(storage.value.p[key]);
if (hash$1 && rules.includes(hash$1)) return true;
}
return false;
}),
hashList
});
}
function useEncrypt() {
const result = inject(EncryptSymbol);
if (!result) throw new Error("useEncrypt() is called without setup");
return result;
}
function useEncryptCompare() {
const encrypt$2 = useEncryptData();
const { page } = useData();
const route = useRoute();
const { hashList } = useEncrypt();
async function compareGlobal(password) {
if (!password) return false;
for (const admin of encrypt$2.value.admins) if (await compareDecrypt(password, admin, encrypt$2.value.separator)) {
storage.value.g = mergeHash(admin);
return true;
}
return false;
}
async function comparePage(password) {
if (!password) return false;
const pagePath = route.path;
const filePathRelative = page.value.filePathRelative;
let decrypted = false;
for (const { match, key, rules } of hashList.value) if (toMatch(match, pagePath, filePathRelative)) {
for (const rule of rules) if (await compareDecrypt(password, rule, encrypt$2.value.separator)) {
decrypted = true;
storage.value.p = {
...storage.value.p,
[key]: mergeHash(rule)
};
break;
}
if (decrypted) break;
}
if (!decrypted) decrypted = await compareGlobal(password);
return decrypted;
}
return {
compareGlobal,
comparePage
};
}
//#endregion
//#region src/client/composables/sidebar-data.ts
const { __auto__, __home__, ...items } = sidebar;
const sidebarData = ref(items);
const autoDirSidebar = ref(__auto__);
const autoHomeData = ref(__home__);
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) __VUE_HMR_RUNTIME__.updateSidebar = (data) => {
const { __auto__: __auto__$1, __home__: __home__$1, ...items$1 } = data;
sidebarData.value = items$1;
autoDirSidebar.value = __auto__$1;
autoHomeData.value = __home__$1;
};
const sidebarSymbol = Symbol(__VUEPRESS_DEV__ ? "sidebar" : "");
function setupSidebar() {
const { page, frontmatter } = useData();
const routeLocale = useRouteLocale();
const hasSidebar = computed(() => {
return frontmatter.value.pageLayout !== "home" && frontmatter.value.pageLayout !== "friends" && frontmatter.value.sidebar !== false && frontmatter.value.layout !== "NotFound";
});
provide(sidebarSymbol, computed(() => {
return hasSidebar.value ? getSidebar(typeof frontmatter.value.sidebar === "string" ? frontmatter.value.sidebar : page.value.path, routeLocale.value) : [];
}));
}
function useSidebarData() {
const sidebarData$1 = inject(sidebarSymbol);
if (!sidebarData$1) throw new Error("useSidebarData() is called without provider.");
return sidebarData$1;
}
/**
* Get the `Sidebar` from sidebar option. This method will ensure to get correct
* sidebar config from `MultiSideBarConfig` with various path combinations such
* as matching `guide/` and `/guide/`. If no matching config was found, it will
* return empty array.
*/
function getSidebar(routePath, routeLocal) {
const _sidebar = sidebarData.value[routeLocal];
if (_sidebar === "auto") return resolveSidebarItems(autoDirSidebar.value[routeLocal]);
else if (isArray(_sidebar)) return resolveSidebarItems(_sidebar, routeLocal);
else if (isPlainObject(_sidebar)) {
routePath = decodeURIComponent(routePath);
const dir = Object.keys(_sidebar).sort((a, b) => b.split("/").length - a.split("/").length).find((dir$1) => {
return routePath.startsWith(`${routeLocal}${removeLeadingSlash(dir$1)}`);
}) || "";
const sidebar$1 = dir ? _sidebar[dir] : void 0;
if (sidebar$1 === "auto") return resolveSidebarItems(dir ? autoDirSidebar.value[dir] : [], routeLocal);
else if (isArray(sidebar$1)) return resolveSidebarItems(sidebar$1, dir);
else if (isPlainObject(sidebar$1)) {
const prefix = normalizePrefix(routeLocal, sidebar$1.prefix);
return resolveSidebarItems(sidebar$1.items === "auto" ? autoDirSidebar.value[prefix] : sidebar$1.items, prefix);
}
}
return [];
}
function resolveSidebarItems(sidebarItems, _prefix = "") {
const resolved = [];
sidebarItems.forEach((item) => {
if (isString(item)) resolved.push(resolveNavLink(normalizeLink(_prefix, item)));
else {
const { link, items: items$1, prefix, dir, ...args } = item;
const navLink = { ...args };
if (link) {
navLink.link = link.startsWith("---") ? link : normalizeLink(_prefix, link);
const nav = resolveNavLink(navLink.link);
navLink.icon = nav.icon || navLink.icon;
navLink.badge = nav.badge || navLink.badge;
}
const nextPrefix = normalizePrefix(_prefix, prefix || dir);
if (items$1 === "auto") {
navLink.items = resolveSidebarItems(autoDirSidebar.value[nextPrefix], nextPrefix);
if (!navLink.link && autoHomeData.value[nextPrefix]) {
navLink.link = normalizeLink(autoHomeData.value[nextPrefix]);
const nav = resolveNavLink(navLink.link);
navLink.icon = nav.icon || navLink.icon;
navLink.badge = nav.badge || navLink.badge;
}
} else navLink.items = items$1?.length ? resolveSidebarItems(items$1, nextPrefix) : void 0;
resolved.push(navLink);
}
});
return resolved;
}
/**
* Get or generate sidebar group from the given sidebar items.
*/
function getSidebarGroups(sidebar$1) {
const groups = [];
let lastGroupIndex = 0;
for (const index in sidebar$1) {
const item = sidebar$1[index];
if (item.items) {
lastGroupIndex = groups.push(item);
continue;
}
if (!groups[lastGroupIndex]) groups.push({ items: [] });
groups[lastGroupIndex].items.push(item);
}
return groups;
}
function getSidebarFirstLink(sidebar$1) {
for (const item of sidebar$1) {
if (item.link) return item.link;
if (item.items) return getSidebarFirstLink(item.items);
}
return "";
}
//#endregion
//#region src/client/composables/sidebar.ts
/**
* Check if the given sidebar item contains any active link.
*/
function hasActiveLink(path, items$1) {
if (Array.isArray(items$1)) return items$1.some((item) => hasActiveLink(path, item));
return isActive(path, items$1.link ? resolveRouteFullPath(items$1.link) : void 0) ? true : items$1.items ? hasActiveLink(path, items$1.items) : false;
}
const containsActiveLink = hasActiveLink;
function useSidebar() {
const { theme, frontmatter, page } = useData();
const routeLocal = useRouteLocale();
const is960 = useMediaQuery("(min-width: 960px)");
const { isPageDecrypted } = useEncrypt();
const isOpen = ref(false);
const sidebarKey = computed(() => {
const _sidebar = sidebarData.value[routeLocal.value];
if (!_sidebar || _sidebar === "auto" || isArray(_sidebar)) return routeLocal.value;
return Object.keys(_sidebar).sort((a, b) => b.split("/").length - a.split("/").length).find((dir) => {
return page.value.path.startsWith(ensureLeadingSlash(dir));
}) || "";
});
const sidebar$1 = useSidebarData();
const hasSidebar = computed(() => {
return frontmatter.value.sidebar !== false && sidebar$1.value.length > 0 && frontmatter.value.pageLayout !== "home";
});
const hasAside = computed(() => {
if (frontmatter.value.pageLayout === "home" || frontmatter.value.home) return false;
if (frontmatter.value.pageLayout === "friends" || frontmatter.value.friends) return false;
if (!isPageDecrypted.value) return false;
if (frontmatter.value.aside != null) return !!frontmatter.value.aside;
return theme.value.aside !== false;
});
const leftAside = computed(() => {
if (hasAside.value) return frontmatter.value.aside == null ? theme.value.aside === "left" : frontmatter.value.aside === "left";
return false;
});
const isSidebarEnabled = computed(() => hasSidebar.value && is960.value);
const sidebarGroups = computed(() => {
return hasSidebar.value ? getSidebarGroups(sidebar$1.value) : [];
});
const open = () => {
isOpen.value = true;
};
const close = () => {
isOpen.value = false;
};
const toggle = () => {
if (isOpen.value) close();
else open();
};
return {
isOpen,
sidebar: sidebar$1,
sidebarKey,
sidebarGroups,
hasSidebar,
hasAside,
leftAside,
isSidebarEnabled,
open,
close,
toggle
};
}
/**
* a11y: cache the element that opened the Sidebar (the menu button) then
* focus that button again when Menu is closed with Escape key.
*/
function useCloseSidebarOnEscape(isOpen, close) {
let triggerElement;
watchEffect(() => {
triggerElement = isOpen.value ? document.activeElement : void 0;
});
onMounted(() => {
window.addEventListener("keyup", onEscape);
});
onUnmounted(() => {
window.removeEventListener("keyup", onEscape);
});
function onEscape(e) {
if (e.key === "Escape" && isOpen.value) {
close();
triggerElement?.focus();
}
}
}
function useSidebarControl(item) {
const { page } = useData();
const route = useRoute();
const collapsed = ref(false);
const collapsible = computed(() => {
return item.value.collapsed != null;
});
const isLink = computed(() => {
return !!item.value.link;
});
const isActiveLink = ref(false);
const updateIsActiveLink = () => {
isActiveLink.value = isActive(page.value.path, item.value.link ? resolveRouteFullPath(item.value.link) : void 0);
};
watch([
() => page.value.path,
item,
() => route.hash
], updateIsActiveLink);
onMounted(updateIsActiveLink);
const hasActiveLink$1 = computed(() => {
if (isActiveLink.value) return true;
return item.value.items ? containsActiveLink(page.value.path, item.value.items) : false;
});
const hasChildren = computed(() => {
return !!(item.value.items && item.value.items.length);
});
watch(() => [collapsible.value, item.value.collapsed], (n, o) => {
if (n[0] !== o?.[0] || n[1] !== o?.[1]) collapsed.value = !!(collapsible.value && item.value.collapsed);
}, { immediate: true });
watch(() => [
page.value.path,
isActiveLink.value,
hasActiveLink$1.value
], () => {
if (isActiveLink.value || hasActiveLink$1.value) collapsed.value = false;
}, {
immediate: true,
flush: "post"
});
const toggle = () => {
if (collapsible.value) collapsed.value = !collapsed.value;
};
return {
collapsed,
collapsible,
isLink,
isActiveLink,
hasActiveLink: hasActiveLink$1,
hasChildren,
toggle
};
}
//#endregion
//#region src/client/composables/aside.ts
function useAside() {
const { hasSidebar } = useSidebar();
const is960 = useMediaQuery("(min-width: 960px)");
const is1280 = useMediaQuery("(min-width: 1280px)");
return { isAsideEnabled: computed(() => {
if (!is1280.value && !is960.value) return false;
return hasSidebar.value ? is1280.value : is960.value;
}) };
}
//#endregion
//#region src/client/composables/bulletin.ts
const showBulletin = ref(false);
function useBulletin() {
const { theme } = useData();
return computed(() => theme.value.bulletin === true ? {} : theme.value.bulletin);
}
function useBulletinControl() {
const session = useSessionStorage("plume:bulletin", "");
const local = useLocalStorage("plume:bulletin", "");
const { page } = useData();
const bulletin = useBulletin();
const enableBulletin = computed(() => page.value.bulletin ?? true);
watch(() => bulletin.value?.lifetime, (lifetime) => {
const id = bulletin.value?.id;
if (lifetime === "session") showBulletin.value = session.value !== id;
else if (lifetime === "once") showBulletin.value = local.value !== id;
else showBulletin.value = true;
}, { immediate: true });
function close() {
showBulletin.value = false;
const lifetime = bulletin.value?.lifetime;
const id = bulletin.value?.id;
if (lifetime === "session") session.value = id;
else if (lifetime === "once") local.value = id;
}
return {
bulletin,
enableBulletin,
showBulletin,
close
};
}
//#endregion
//#region src/client/composables/contributors.ts
function useContributors() {
const { frontmatter } = useData();
const list = useContributors$1();
const theme = useThemeData();
const mode = computed(() => {
const config = theme.value.contributors;
if (isPlainObject$1(config)) return config.mode || "inline";
return "inline";
});
const contributors = computed(() => {
if ((frontmatter.value.contributors ?? !!theme.value.contributors) === false) return [];
return list.value;
});
return {
mode,
contributors,
hasContributors: computed(() => contributors.value.length > 0)
};
}
//#endregion
//#region src/client/composables/preset-locales.ts
const presetLocales = __PLUME_PRESET_LOCALE__;
function getPresetLocaleData(locale, name) {
return presetLocales[locale]?.[name] || presetLocales["/"][name];
}
//#endregion
//#region src/client/composables/copyright.ts
const LICENSE_URL = {
"CC0": {
url: "https://creativecommons.org/publicdomain/zero/1.0/",
icons: ["zero"]
},
"CC-BY-4.0": {
url: "https://creativecommons.org/licenses/by/4.0/",
icons: ["cc", "by"]
},
"CC-BY-NC-4.0": {
url: "https://creativecommons.org/licenses/by-nc/4.0/",
icons: [
"cc",
"by",
"nc"
]
},
"CC-BY-NC-SA-4.0": {
url: "https://creativecommons.org/licenses/by-nc-sa/4.0/",
icons: [
"cc",
"by",
"nc",
"sa"
]
},
"CC-BY-NC-ND-4.0": {
url: "https://creativecommons.org/licenses/by-nc-nd/4.0/",
icons: [
"cc",
"by",
"nc",
"nd"
]
},
"CC-BY-ND-4.0": {
url: "https://creativecommons.org/licenses/by-nd/4.0/",
icons: [
"cc",
"by",
"nd"
]
},
"CC-BY-SA-4.0": {
url: "https://creativecommons.org/licenses/by-sa/4.0/",
icons: [
"cc",
"by",
"sa"
]
}
};
function useCopyright(copyright) {
const { theme } = useData();
const routeLocale = useRouteLocale();
const { contributors } = useContributors();
const hasCopyright = computed(() => Boolean(copyright.value));
const creation = computed(() => copyright.value.creation || "original");
const license = computed(() => resolveLicense(copyright.value.license, routeLocale.value));
const author = computed(() => resolveAuthor(copyright.value.author, creation.value, contributors.value));
const sourceUrl = computed(() => {
if (creation.value === "original") {
if (__VUEPRESS_SSR__) return "";
const url = new URL(location.href.split("#")[0]);
url.searchParams.delete("giscus");
return url.toString();
}
return copyright.value.source;
});
return {
license,
author,
hasCopyright,
creation,
creationText: computed(() => {
const creation$1 = copyright.value.creation;
if (creation$1 === "translate") return theme.value.copyrightCreationTranslateText || "This article is translated from";
else if (creation$1 === "reprint") return theme.value.copyrightCreationReprintText || "This article is reprint from";
return theme.value.copyrightCreationOriginalText || "This article link: ";
}),
sourceUrl
};
}
function resolveLicense(license = "CC-BY-4.0", locale) {
const result = typeof license === "string" ? { name: license } : { ...license };
const fallback = LICENSE_URL[result.name];
const name = getPresetLocaleData(locale, result.name);
if (name) result.name = `${name} (${result.name})`;
result.url ||= fallback?.url;
result.icons = fallback?.icons;
return result;
}
function resolveAuthor(author, creation, contributors) {
const contributor = contributors[0];
if (!author && contributor && creation === "original") return contributor;
const options = typeof author === "string" ? { name: author } : author;
if (options && !options.url) {
const contributor$1 = contributors.find((c) => c.name === options.name);
if (contributor$1) options.url = contributor$1.url;
}
return options;
}
//#endregion
//#region src/client/composables/css-var.ts
/**
* Get css variable
* @param prop css variable name
* @param initialValue
*/
function useCssVar(prop, initialValue = "") {
const isDark = useDarkMode();
const variable = shallowRef(initialValue);
function updateCssVar() {
const _window = typeof window ? window : null;
const target = _window?.document?.documentElement;
const key = toValue(prop);
if (target && key) variable.value = _window.getComputedStyle(target).getPropertyValue(key)?.trim() || variable.value || initialValue;
}
watch([isDark, () => toValue(prop)], () => {
updateCssVar();
}, {
immediate: true,
flush: "post"
});
return computed(() => variable.value);
}
//#endregion
//#region src/client/composables/edit-link.ts
function useEditLink() {
const { theme, page, frontmatter } = useData();
const themeData$2 = useThemeData();
return computed(() => {
if (!(frontmatter.value.editLink ?? themeData$2.value.editLink ?? true)) return null;
const { docsRepo, docsBranch = "main", docsDir = "" } = themeData$2.value;
const { editLinkText } = theme.value;
if (!docsRepo) return null;
const editLink = resolveEditLink({
docsRepo,
docsBranch,
docsDir,
filePathRelative: page.value.filePathRelative,
editLinkPattern: frontmatter.value.editLinkPattern ?? theme.value.editLinkPattern
});
if (!editLink) return null;
return {
text: editLinkText ?? "Edit this page",
link: editLink
};
});
}
//#endregion
//#region src/client/composables/flyout.ts
const focusedElement = ref();
let active = false;
let listeners = 0;
function useFlyout(options) {
const focus = ref(false);
if (inBrowser) {
if (!active) activateFocusTracking();
listeners++;
const unwatch = watch(focusedElement, (el) => {
if (el === options.el.value || options.el.value?.contains(el)) {
focus.value = true;
options.onFocus?.();
} else {
focus.value = false;
options.onBlur?.();
}
});
onUnmounted(() => {
unwatch();
listeners--;
if (!listeners) deactivateFocusTracking();
});
}
return readonly(focus);
}
function activateFocusTracking() {
document.addEventListener("focusin", handleFocusIn);
active = true;
focusedElement.value = document.activeElement;
}
function deactivateFocusTracking() {
document.removeEventListener("focusin", handleFocusIn);
}
function handleFocusIn() {
focusedElement.value = document.activeElement;
}
//#endregion
//#region src/client/composables/icons.ts
const iconsData = ref(icons);
const useIconsData = () => iconsData;
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) __VUE_HMR_RUNTIME__.updateIcons = (data) => {
iconsData.value = data;
};
//#endregion
//#region src/client/utils/resolveNavLink.ts
function normalizeLink$1(base = "", link = "") {
return isLinkAbsolute(link) || isLinkWithProtocol(link) ? link : ensureLeadingSlash(`${base}/${link}`.replace(/\/+/g, "/"));
}
//#endregion
//#region src/client/composables/internal-link.ts
function useInternalLink() {
const { collection, theme } = useData();
const themeData$2 = useThemeData();
const routeLocale = useRouteLocale();
function resolveLink(link, fallback) {
link = link ? removeLeadingSlash$1(link) : "";
return ensureEndingSlash(normalizeLink$1(routeLocale.value, link || fallback));
}
const postCollection = computed(() => collection.value?.type === "post" ? collection.value : void 0);
const home = computed(() => ({
link: normalizeLink$1(routeLocale.value),
text: theme.value.homeText || themeData$2.value.homeText || "Home"
}));
const postsLink = computed(() => normalizeLink$1(routeLocale.value, resolveLink(postCollection.value?.link || postCollection.value?.dir, "posts/")));
return {
home,
posts: computed(() => postCollection.value?.postList !== false ? {
text: postCollection.value?.title || removeEndingSlash(postCollection.value?.dir || "").split("/").pop() || theme.value.postsText,
link: postsLink.value
} : void 0),
tags: computed(() => postCollection.value?.tags !== false ? {
text: postCollection.value?.tagsText || theme.value.tagText || themeData$2.value.tagText || "Tags",
link: resolveLink(postCollection.value?.tagsLink, "tags/")
} : void 0),
archive: computed(() => postCollection.value?.archives !== false ? {
text: postCollection.value?.archivesText || theme.value.archiveText || themeData$2.value.archiveText || "Archives",
link: resolveLink(postCollection.value?.archivesLink, "archives/")
} : void 0),
categories: computed(() => postCollection.value?.categories !== false ? {
text: postCollection.value?.categoriesText || theme.value.categoryText || themeData$2.value.categoryText || "Categories",
link: resolveLink(postCollection.value?.categoriesLink, "categories/")
} : void 0)
};
}
//#endregion
//#region src/client/composables/page.ts
function usePostsPageData() {
const { collection, page } = useData();
return {
isPosts: computed(() => collection.value?.type === "post"),
isPostsLayout: computed(() => {
const type = page.value.type;
return type === "posts" || type === "posts-archives" || type === "posts-tags" || type === "posts-categories";
})
};
}
//#endregion
//#region src/client/composables/langs.ts
function useLangs({ removeCurrent = true } = {}) {
const theme = useThemeData();
const { page, collection } = useData();
const routeLocale = useRouteLocale();
const { isPosts } = usePostsPageData();
const currentLang = computed(() => {
const link = routeLocale.value;
return {
text: theme.value.locales?.[link]?.selectLanguageName,
link
};
});
const resolvePath = (locale, url) => {
const { notFound, path } = resolveRoute(normalizeLink(locale, url.slice(routeLocale.value.length)));
return notFound ? void 0 : path;
};
const getPageLink = (locale) => {
let path;
if (page.value.filePathRelative) path = resolvePath(locale, `/${page.value.filePathRelative}`);
path ??= resolvePath(locale, page.value.path);
if (path) return path;
if (isPosts.value && collection.value) {
const col = collection.value;
return normalizeLink(locale, removeLeadingSlash$1(col.link || col.dir));
}
const home = theme.value.home || "/";
const fallbackResolve = resolveRoute(locale);
return fallbackResolve.notFound ? home : fallbackResolve.path;
};
return {
localeLinks: computed(() => Object.entries(theme.value.locales || {}).flatMap(([key, locale]) => removeCurrent && currentLang.value.text === locale.selectLanguageName ? [] : {
text: locale.selectLanguageName,
link: getPageLink(key)
})),
currentLang
};
}
//#endregion
//#region src/client/composables/latest-updated.ts
function useLastUpdated() {
const { theme, page, frontmatter } = useData();
const themeData$2 = useThemeData();
const lang = usePageLang();
const date = computed(() => page.value.git?.updatedTime ? new Date(page.value.git.updatedTime) : null);
const isoDatetime = computed(() => date.value?.toISOString());
const datetime = ref("");
const lastUpdatedText = computed(() => {
if (themeData$2.value.lastUpdated === false) return "";
return theme.value.lastUpdatedText || "Last updated";
});
onMounted(() => {
watchEffect(() => {
if (frontmatter.value.lastUpdated === false || themeData$2.value.lastUpdated === false) return;
datetime.value = date.value ? new Intl.DateTimeFormat(themeData$2.value.lastUpdated?.formatOptions?.forceLocale ? lang.value : void 0, themeData$2.value.lastUpdated?.formatOptions ?? {
dateStyle: "short",
timeStyle: "short"
}).format(date.value) : "";
});
});
return {
datetime,
isoDatetime,
lastUpdatedText
};
}
//#endregion
//#region src/client/composables/link.ts
function useLink(href, target) {
const route = useRoute();
const { page } = useData();
const isExternal = computed(() => {
const link$1 = toValue(href);
const rawTarget = toValue(target);
if (!link$1) return false;
if (rawTarget === "_blank" || isLinkExternal(link$1)) return true;
const filename = link$1.split(/[#?]/)[0]?.split("/").pop() || "";
if (filename === "" || filename.endsWith(".html") || filename.endsWith(".md")) return false;
return filename.includes(".");
});
const link = computed(() => {
const link$1 = toValue(href);
if (!link$1) return void 0;
if (isExternal.value) return link$1;
const path = resolveRouteFullPath(link$1, page.value.filePathRelative ? `/${page.value.filePathRelative}` : void 0);
if (path.includes("#")) {
if (path.slice(0, path.indexOf("#")) === route.path) return path.slice(path.indexOf("#"));
}
return path;
});
return {
isExternal,
isExternalProtocol: computed(() => {
if (!link.value || link.value[0] === "#") return false;
return isLinkWithProtocol(link.value);
}),
link
};
}
//#endregion
//#region src/client/composables/nav.ts
function useNavbarData() {
const { theme } = useData();
return computed(() => resolveNavbar(theme.value.navbar || []));
}
function resolveNavbar(navbar, _prefix = "") {
const resolved = [];
navbar.forEach((item) => {
if (typeof item === "string") resolved.push(resolveNavLink(normalizeLink(_prefix, item)));
else {
const { items: items$1, prefix, ...args } = item;
const res = { ...args };
if ("link" in res) res.link = normalizeLink(_prefix, res.link);
if (items$1?.length) res.items = resolveNavbar(items$1, normalizeLink(_prefix, prefix));
resolved.push(res);
}
});
return resolved;
}
function useNav() {
const isScreenOpen = ref(false);
function openScreen() {
isScreenOpen.value = true;
window.addEventListener("resize", closeScreenOnTabletWindow);
}
function closeScreen() {
isScreenOpen.value = false;
window.removeEventListener("resize", closeScreenOnTabletWindow);
}
function toggleScreen() {
if (isScreenOpen.value) closeScreen();
else openScreen();
}
/**
* Close screen when the user resizes the window wider than tablet size.
*/
function closeScreenOnTabletWindow() {
if (window.outerWidth >= 768) closeScreen();
}
const route = useRoute();
watch(() => route.path, closeScreen);
return {
isScreenOpen,
openScreen,
closeScreen,
toggleScreen
};
}
//#endregion
//#region src/client/composables/outline.ts
const resolvedHeaders = [];
const headersSymbol = Symbol(__VUEPRESS_DEV__ ? "headers" : "");
function setupHeaders() {
const { frontmatter, theme } = useData();
const headers = ref([]);
onContentUpdated(() => {
headers.value = getHeaders(frontmatter.value.outline ?? theme.value.outline);
});
provide(headersSymbol, headers);
return headers;
}
function useHeaders() {
const headers = inject(headersSymbol);
if (!headers) throw new Error("useHeaders() is called without provider.");
return headers;
}
function getHeaders(range) {
const heading = [
"h1",
"h2",
"h3",
"h4",
"h5",
"h6"
];
const ignores = Array.from(document.querySelectorAll(heading.map((h) => `.vp-demo-wrapper ${h}`).join(",")));
return resolveHeaders(Array.from(document.querySelectorAll(heading.map((h) => `.vp-doc ${h}`).join(","))).filter((el) => !ignores.includes(el) && el.id && el.hasChildNodes()).map((el) => {
const level = Number(el.tagName[1]);
return {
element: el,
title: serializeHeader(el),
link: `#${el.id}`,
level
};
}), range);
}
function serializeHeader(h) {
const anchor = h.firstChild;
const el = anchor?.firstChild;
let ret = "";
for (const node of Array.from(el?.childNodes ?? [])) if (node.nodeType === 1) {
if (node.classList.contains("vp-badge") || node.classList.contains("ignore-header")) continue;
const clone = node.cloneNode(true);
clearHeaderNodeList(Array.from(clone.childNodes));
ret += clone.textContent;
} else if (node.nodeType === 3) ret += node.textContent;
let next = anchor?.nextSibling;
while (next) {
if (next.nodeType === 1 || next.nodeType === 3) ret += next.textContent;
next = next.nextSibling;
}
return ret.trim();
}
function clearHeaderNodeList(list) {
if (list?.length) {
for (const node of list) if (node.nodeType === 1) if (node.classList.contains("ignore-header")) node.remove();
else clearHeaderNodeList(Array.from(node.childNodes));
}
}
function resolveHeaders(headers, range) {
if (range === false) return [];
const levelsRange = range || 2;
const [high, low] = typeof levelsRange === "number" ? [levelsRange, levelsRange] : levelsRange === "deep" ? [2, 6] : levelsRange;
headers = headers.filter((h) => h.level >= high && h.level <= low);
resolvedHeaders.length = 0;
for (const { element, link } of headers) resolvedHeaders.push({
element,
link
});
const ret = [];
outer: for (let i = 0; i < headers.length; i++) {
const cur = headers[i];
if (i === 0) ret.push(cur);
else {
for (let j = i - 1; j >= 0; j--) {
const prev = headers[j];
if (prev.level < cur.level) {
(prev.children || (prev.children = [])).push(cur);
continue outer;
}
}
ret.push(cur);
}
}
return ret;
}
function useActiveAnchor(container, marker) {
const { isAsideEnabled } = useAside();
const router = useRouter();
const routeHash = ref(router.currentRoute.value.hash);
let prevActiveLink = null;
const setActiveLink = () => {
if (!isAsideEnabled.value) return;
const scrollY = Math.round(window.scrollY);
const innerHeight$1 = window.innerHeight;
const offsetHeight = document.body.offsetHeight;
const isBottom = Math.abs(scrollY + innerHeight$1 - offsetHeight) < 1;
const headers = resolvedHeaders.map(({ element, link }) => ({
link,
top: getAbsoluteTop(element)
})).filter(({ top }) => !Number.isNaN(top)).sort((a, b) => a.top - b.top);
if (!headers.length) {
activateLink(null);
return;
}
if (scrollY < 1) {
activateLink(null);
return;
}
if (isBottom) {
activateLink(headers[headers.length - 1].link);
return;
}
let activeLink = null;
for (const { link, top } of headers) {
if (top > scrollY + 80) break;
activeLink = link;
}
activateLink(activeLink);
};
function activateLink(hash) {
routeHash.value = hash || "";
if (prevActiveLink) prevActiveLink.classList.remove("active");
if (hash == null) prevActiveLink = null;
else prevActiveLink = container.value?.querySelector(`a[href="${decodeURIComponent(hash)}"]`) ?? null;
const activeLink = prevActiveLink;
if (activeLink) {
activeLink.classList.add("active");
if (marker.value) {
marker.value.style.top = `${activeLink.offsetTop + 39}px`;
marker.value.style.opacity = "1";
}
} else if (marker.value) {
marker.value.style.top = "33px";
marker.value.style.opacity = "0";
}
}
const onScroll = useThrottleFn(setActiveLink, 100);
watchDebounced(routeHash, () => {
updateHash(router, routeHash.value);
}, { debounce: 500 });
onMounted(() => {
setTimeout(() => {
setActiveLink();
window.addEventListener("scroll", onScroll);
}, 1e3);
});
onUpdated(() => {
activateLink(location.hash);
});
onUnmounted(() => {
window.removeEventListener("scroll", onScroll);
});
}
function getAbsoluteTop(element) {
let offsetTop = 0;
while (element && element !== document.body) {
if (window.getComputedStyle(element).position === "fixed") return element.offsetTop;
offsetTop += element.offsetTop;
element = element.offsetParent;
}
return element ? offsetTop : NaN;
}
/**
* Update current hash and do not trigger `scrollBehavior`
*/
async function updateHash(router, hash) {
const { path, query } = router.currentRoute.value;
const { scrollBehavior } = router.options;
router.options.scrollBehavior = void 0;
await router.replace({
path,
query,
hash
});
router.options.scrollBehavior = scrollBehavior;
}
//#endregion
//#region src/client/composables/posts-data.ts
const postsData = ref(postsData$1);
function usePostsData() {
return postsData;
}
function useLocalePostList() {
const collection = useCollection();
const routeLocale = useRouteLocale();
return computed(() => {
if (collection.value) return postsData.value[normalizeLink$1(routeLocale.value, ensureEndingSlash(removeLeadingSlash$1(collection.value.dir)))] || [];
return [];
});
}
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) __VUE_HMR_RUNTIME__.updatePostsData = (data) => {
postsData.value = data;
};
//#endregion
//#region src/client/composables/posts-archives.ts
function useArchives() {
const themeData$2 = useThemeData();
const list = useLocalePostList();
const { theme } = useData();
return { archives: computed(() => {
const archives = [];
const countLocale = theme.value.archiveTotalText || themeData$2.value.archiveTotalText;
list.value.forEach((item) => {
const createTime = item.createTime?.split(/\s|T/)[0] || "";
const year = createTime.split("/")[0];
let current = archives.find((archive) => archive.title === year);
if (!current) {
current = {
title: year,
list: [],
label: ""
};
archives.push(current);
}
current.list.push({
title: item.title,
path: item.path,
createTime: createTime.slice(year.length + 1).replace(/\//g, "-")
});
});
archives.forEach((item) => {
item.label = countLocale?.replace("{count}", item.list.length.toString()) || "";
});
return archives;
}) };
}
//#endregion
//#region src/client/composables/posts-category.ts
function usePostsCategory() {
const postList = useLocalePostList();
return { categories: computed(() => {
const list = [];
postList.value.forEach((item) => {
const categoryList = item.categoryList;
if (!categoryList || categoryList.length === 0) list.push({
type: "post",
title: item.title,
path: item.path
});
else {
let cate = list;
let i = 0;
while (i < categoryList.length) {
const { id, name, sort } = categoryList[i];
const current = cate.find((item$1) => item$1.type === "category" && item$1.id === id);
if (!current) {
const items$1 = [];
cate.push({
type: "category",
title: name,
id,
sort,
items: items$1
});
cate = items$1;
} else cate = current.items;
i++;
}
cate.push({
type: "post",
title: item.title,
path: item.path
});
}
});
return sortCategory(list);
}) };
}
function sortCategory(items$1) {
for (const item of items$1) if (item.type === "category" && item.items.length) item.items = sortCategory(item.items);
return items$1.sort((a, b) => {
if (a.type === "category" && b.type === "category") return a.sort < b.sort ? -1 : 1;
if (a.type === "category" && b.type === "post") return -1;
if (a.type === "post" && b.type === "category") return 1;
return 0;
});
}
//#endregion
//#region src/client/composables/route-query.ts
const _queue = /* @__PURE__ */ new WeakMap();
function useRouteQuery(name, defaultValue, options = {}) {
const { mode = "replace", route = useRoute(), router = useRouter(), transform = (value) => value } = options;
if (!_queue.has(router)) _queue.set(router, /* @__PURE__ */ new Map());
const _queriesQueue = _queue.get(router);
let query = route.query[name];
tryOnScopeDispose(() => {
query = void 0;
});
let _trigger;
const proxy = customRef((track, trigger) => {
_trigger = trigger;
return {
get() {
track();
return transform(query !== void 0 ? query : toValue(defaultValue));
},
set(v) {
if (query === v) return;
query = v;
_queriesQueue.set(name, v);
trigger();
nextTick(() => {
if (_queriesQueue.size === 0) return;
const newQueries = Object.fromEntries(_queriesQueue.entries());
_queriesQueue.clear();
const { query: query$1, hash, path } = route;
router[toValue(mode)]({
path,
query: {
...query$1,
...newQueries
},
hash
});
});
}
};
});
watch(() => route.query[name], (v) => {
query = v;
_trigger();
}, { flush: "sync" });
return proxy;
}
//#endregion
//#region src/client/composables/tag-colors.ts
const tagColorsRef = ref(articleTagColors);
const useTagColors = () => tagColorsRef;
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) __VUE_HMR_RUNTIME__.updateArticleTagColors = (data) => {
tagColorsRef.value = data;
};
//#endregion
//#region src/client/composables/posts-tags.ts
function useTags() {
const { collection } = useData();
const list = useLocalePostList();
const colors = useTagColors();
const postCollection = computed(() => {
if (collection.value?.type === "post") return collection.value;
});
const tags = computed(() => {
const tagTheme = postCollection.value?.tagsTheme ?? "colored";
const tagMap = {};
list.value.forEach((item) => {
if (item.tags) toArray(item.tags).forEach((tag) => {
if (tagMap[tag]) tagMap[tag] += 1;
else tagMap[tag] = 1;
});
});
return Object.keys(tagMap).map((tag) => ({
name: tag,
count: tagMap[tag] > 99 ? "99+" : tagMap[tag],
className: colors.value[tag] ? `vp-tag-${colors.value[tag]}` : `tag-${tagTheme}`
}));
});
const currentTag = useRouteQuery("tag");
const postList = computed(() => {
if (!currentTag.value) return [];
return list.value.filter((item) => {
if (item.tags) return toArray(item.tags).includes(currentTag.value);
return false;
}).map((item) => ({
title: item.title,
path: item.path,
createTime: item.createTime.split(" ")[0].replace(/\//g, "-")
}));
});
const handleTagClick = (tag) => {
currentTag.value = tag;
};
return {
tags,
currentTag,
postList,
handleTagClick
};
}
//#endregion
//#region src/client/composables/posts-extract.ts
function usePostsExtract() {
const { collection } = useData();
const postList = useLocalePostList();
const { tags: tagsList } = useTags();
const { categories: categoryList } = usePostsCategory();
const links = useInternalLink();
return {
hasPostsExtract: computed(() => collection.value?.type === "post" && (collection.value.archives !== false || collection.value.tags !== false || collection.value.categories !== false)),
tags: computed(() => ({
link: links.tags.value?.link,
text: links.tags.value?.text,
total: tagsList.value.length
})),
archives: computed(() => ({
link: links.archive.value?.link,
text: links.archive.value?.text,
total: postList.value.length
})),
categories: computed(() => ({
link: links.categories.value?.link,
text: links.categories.value?.text,
total: getCategoriesTotal(categoryList.value)
}))
};
}
function getCategoriesTotal(categories) {
let total = 0;
for (const category of categories) if (category.type === "category") {
total += 1;
if (category.items.length) total += getCategoriesTotal(category.items);
}
return total;
}
//#endregion
//#region src/client/composables/posts-post-list.ts
const DEFAULT_PER_PAGE = 15;
function usePostListControl(homePage) {
const { collection } = useData();
const list = useLocalePostList();
const is960 = useMediaQuery("(max-width: 960px)");
const postCollection = computed(() => {
if (collection.value?.type === "post") return collection.value;
});
const postList = computed(() => {
const stickyList = list.value.filter((item) => item.sticky === true || typeof item.sticky === "number");
const otherList = list.value.filter((item) => item.sticky === void 0 || item.sticky === false);
return [...stickyList.sort((prev, next) => {
if (next.sticky === true && prev.sticky === true) return 0;
return next.sticky > prev.sticky ? 1 : -1;
}), ...otherList];
});
const page = useRouteQuery("p", 1, {
mode: "push",
transform(val) {
const page$1 = Number(val);
if (!Number.isNaN(page$1) && page$1 > 0) return page$1;
return 1;
}
});
const perPage = computed(() => {
if (postCollection.value?.pagination === false) return 0;
if (typeof postCollection.value?.pagination === "number") return postCollection.value.pagination;
return postCollection.value?.pagination?.perPage || DEFAULT_PER_PAGE;
});
const totalPage = computed(() => {
if (postCollection.value?.pagination === false) return 0;
return Math.ceil(postList.value.length / perPage.value);
});
const isLastPage = computed(() => page.value >= totalPage.value);
const isFirstPage = computed(() => page.value <= 1);
const isPaginationEnabled = computed(() => postCollection.value?.pagination !== false && totalPage.value > 1);
const finalList = computed(() => {
if (postCollection.value?.pagination === false) return postList.value;
if (postList.value.length <= perPage.value) return postList.value;
return postList.value.slice((page.valu