mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
feat: add PageFooter
This commit is contained in:
parent
b9ffac184a
commit
918fba3e5e
@ -3,6 +3,7 @@ import { usePageData } from '@vuepress/client'
|
||||
import type { PlumeThemePageData } from '../../shared/index.js'
|
||||
import { useDarkMode, useSidebar } from '../composables/index.js'
|
||||
import PageAside from './PageAside.vue'
|
||||
import PageFooter from './PageFooter.vue'
|
||||
import PageMeta from './PageMeta.vue'
|
||||
|
||||
const { hasSidebar, hasAside } = useSidebar()
|
||||
@ -32,6 +33,7 @@ const page = usePageData<PlumeThemePageData>()
|
||||
<main class="main">
|
||||
<PageMeta />
|
||||
<Content class="plume-content" />
|
||||
<PageFooter />
|
||||
<PageComment :darkmode="isDark" />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
190
theme/src/client/components/PageFooter.vue
Normal file
190
theme/src/client/components/PageFooter.vue
Normal file
@ -0,0 +1,190 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { useContributors, useEditNavLink, useLastUpdated, usePageNav, useThemeLocaleData } from '../composables/index.js'
|
||||
import AutoLink from './AutoLink.vue'
|
||||
import IconEdit from './icons/IconEdit.vue'
|
||||
|
||||
const themeLocale = useThemeLocaleData()
|
||||
const editNavLink = useEditNavLink()
|
||||
const lastUpdated = useLastUpdated()
|
||||
const contributors = useContributors()
|
||||
const { prev, next } = usePageNav()
|
||||
|
||||
const showFooter = computed(() => {
|
||||
return editNavLink.value || lastUpdated.value || contributors.value || prev.value || next.value
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<footer v-if="showFooter" class="page-footer">
|
||||
<div v-if="editNavLink || lastUpdated" class="edit-info">
|
||||
<div v-if="editNavLink" class="edit-link">
|
||||
<AutoLink class="edit-link-button" :href="editNavLink.link" :no-icon="true">
|
||||
<IconEdit class="edit-link-icon" aria-label="edit icon"/>
|
||||
{{ editNavLink.text }}
|
||||
</AutoLink>
|
||||
</div>
|
||||
|
||||
<div v-if="lastUpdated" class="last-updated">
|
||||
<p class="last-updated-text">
|
||||
{{ themeLocale.lastUpdatedText || 'Last updated' }}:
|
||||
<time :datetime="lastUpdated" class="last-updated-time">{{ lastUpdated }}</time>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="contributors && contributors.length" class="contributors">
|
||||
<span class="contributors-label">{{ themeLocale.contributorsText || 'Contributors' }}:</span>
|
||||
<span class="contributors-info">
|
||||
<template v-for="(contributor, index) in contributors" :key="contributor">
|
||||
<span class="contributor" :title="`email: ${contributor.email}`">
|
||||
{{ contributor.name }}
|
||||
</span>
|
||||
<template v-if="index !== contributors.length - 1">, </template>
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<nav v-if="prev?.link || next?.link" class="prev-next">
|
||||
<div class="pager">
|
||||
<AutoLink v-if="prev?.link" class="pager-link prev" :href="prev.link">
|
||||
<!--eslint-disable-next-line vue/no-v-html-->
|
||||
<span class="desc" v-html="themeLocale.prevPageLabel || 'Previous page'"></span>
|
||||
<!--eslint-disable-next-line vue/no-v-html-->
|
||||
<span class="title" v-html="prev.text"></span>
|
||||
</AutoLink>
|
||||
</div>
|
||||
<div class="pager">
|
||||
<AutoLink v-if="next?.link" class="pager-link next" :href="next.link">
|
||||
<!--eslint-disable-next-line vue/no-v-html-->
|
||||
<span class="desc" v-html="themeLocale.nextPageLabel || 'Next page'"></span>
|
||||
<!--eslint-disable-next-line vue/no-v-html-->
|
||||
<span class="title" v-html="next.text"></span>
|
||||
</AutoLink>
|
||||
</div>
|
||||
</nav>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-footer {
|
||||
margin-top: 96px;
|
||||
}
|
||||
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.edit-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-link-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 0;
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-brand-1);
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.edit-link-button:hover {
|
||||
color: var(--vp-c-brand-2);
|
||||
}
|
||||
|
||||
.edit-link-icon {
|
||||
margin-right: 8px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.last-updated-text {
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.last-updated-text {
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.contributors {
|
||||
padding-bottom: 6px;
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.contributors-label {
|
||||
padding-right: 10px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.contributors-info {
|
||||
color: var(--vp-c-text-2);
|
||||
.contributor {
|
||||
color: var(--vp-c-brand-2);
|
||||
}
|
||||
}
|
||||
|
||||
.prev-next {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
margin-top: 10px;
|
||||
padding-top: 24px;
|
||||
display: grid;
|
||||
grid-row-gap: 8px;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.prev-next {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-column-gap: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.pager-link {
|
||||
display: block;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 11px 16px 13px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
|
||||
.pager-link:hover {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.pager-link.next {
|
||||
margin-left: auto;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.desc {
|
||||
display: block;
|
||||
line-height: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.title {
|
||||
display: block;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-brand-1);
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -4,3 +4,4 @@ export * from './themeData.js'
|
||||
export * from './useResolveRouteWithRedirect.js'
|
||||
export * from './sidebar.js'
|
||||
export * from './aside.js'
|
||||
export * from './page.js'
|
||||
|
||||
215
theme/src/client/composables/page.ts
Normal file
215
theme/src/client/composables/page.ts
Normal file
@ -0,0 +1,215 @@
|
||||
import { usePageData, usePageFrontmatter, usePageLang } from '@vuepress/client'
|
||||
import { isArray, isPlainObject, isString } from '@vuepress/shared'
|
||||
import { useBlogPostData } from '@vuepress-plume/plugin-blog-data/client'
|
||||
import type { NotesSidebarItem } from '@vuepress-plume/plugin-notes-data'
|
||||
import { computed } from 'vue'
|
||||
import type { ComputedRef, Ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type {
|
||||
NavItemWithLink,
|
||||
PlumeThemeBlogPostItem,
|
||||
PlumeThemePageData,
|
||||
PlumeThemePageFrontmatter,
|
||||
} from '../../shared/index.js'
|
||||
import { useNavLink, useSidebar, useThemeLocaleData } from '../composables/index.js'
|
||||
import { resolveEditLink } from '../utils/index.js'
|
||||
|
||||
export const useEditNavLink = (): ComputedRef<null | NavItemWithLink> => {
|
||||
const themeLocale = useThemeLocaleData()
|
||||
const page = usePageData<PlumeThemePageData>()
|
||||
const frontmatter = usePageFrontmatter<PlumeThemePageFrontmatter>()
|
||||
|
||||
return computed(() => {
|
||||
const showEditLink =
|
||||
frontmatter.value.editLink ?? themeLocale.value.editLink ?? true
|
||||
if (!showEditLink) {
|
||||
return null
|
||||
}
|
||||
|
||||
const {
|
||||
repo,
|
||||
docsRepo = repo,
|
||||
docsBranch = 'main',
|
||||
docsDir = '',
|
||||
editLinkText,
|
||||
} = themeLocale.value
|
||||
|
||||
if (!docsRepo) return null
|
||||
|
||||
const editLink = resolveEditLink({
|
||||
docsRepo,
|
||||
docsBranch,
|
||||
docsDir,
|
||||
filePathRelative: page.value.filePathRelative,
|
||||
editLinkPattern:
|
||||
frontmatter.value.editLinkPattern ?? themeLocale.value.editLinkPattern,
|
||||
})
|
||||
|
||||
if (!editLink) return null
|
||||
|
||||
return {
|
||||
text: editLinkText ?? 'Edit this page',
|
||||
link: editLink,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const useLastUpdated = (): ComputedRef<null | string> => {
|
||||
const themeLocale = useThemeLocaleData()
|
||||
const page = usePageData<PlumeThemePageData>()
|
||||
const frontmatter = usePageFrontmatter<PlumeThemePageFrontmatter>()
|
||||
|
||||
return computed(() => {
|
||||
const showLastUpdated =
|
||||
frontmatter.value.lastUpdated ?? themeLocale.value.lastUpdated ?? true
|
||||
|
||||
if (!showLastUpdated) return null
|
||||
|
||||
if (!page.value.git?.updatedTime) return null
|
||||
|
||||
const updatedDate = new Date(page.value.git?.updatedTime)
|
||||
|
||||
return updatedDate.toLocaleString()
|
||||
})
|
||||
}
|
||||
|
||||
export const useContributors = (): ComputedRef<
|
||||
null | Required<PlumeThemePageData['git']>['contributors']
|
||||
> => {
|
||||
const themeLocale = useThemeLocaleData()
|
||||
const page = usePageData<PlumeThemePageData>()
|
||||
const frontmatter = usePageFrontmatter<PlumeThemePageFrontmatter>()
|
||||
|
||||
return computed(() => {
|
||||
const showContributors =
|
||||
frontmatter.value.contributors ?? themeLocale.value.contributors ?? true
|
||||
|
||||
if (!showContributors) return null
|
||||
|
||||
return page.value.git?.contributors ?? null
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve `prev` or `next` config from frontmatter
|
||||
*/
|
||||
const resolveFromFrontmatterConfig = (
|
||||
conf: unknown,
|
||||
): null | false | NavItemWithLink => {
|
||||
if (conf === false) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (isString(conf)) {
|
||||
return useNavLink(conf)
|
||||
}
|
||||
|
||||
if (isPlainObject<NavItemWithLink>(conf)) {
|
||||
return conf
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const flatSidebar = (
|
||||
sidebar: NotesSidebarItem[],
|
||||
res: NavItemWithLink[] = []
|
||||
): NavItemWithLink[] => {
|
||||
for (const item of sidebar) {
|
||||
if (item.link) {
|
||||
res.push({ link: item.link, text: item.text || item.dir || '' })
|
||||
}
|
||||
if (isArray(item.items) && item.items.length) {
|
||||
flatSidebar(item.items as NotesSidebarItem[], res)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve `prev` or `next` config from sidebar items
|
||||
*/
|
||||
const resolveFromSidebarItems = (
|
||||
sidebarItems: NavItemWithLink[],
|
||||
currentPath: string,
|
||||
offset: number,
|
||||
): null | NavItemWithLink => {
|
||||
const index = sidebarItems.findIndex((item) => item.link === currentPath)
|
||||
if (index !== -1) {
|
||||
const targetItem = sidebarItems[index + offset]
|
||||
if (targetItem?.link) {
|
||||
return {
|
||||
link: targetItem.link,
|
||||
text: targetItem.text,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const resolveFromBlogPostData = (
|
||||
postList: PlumeThemeBlogPostItem[],
|
||||
currentPath: string,
|
||||
offset: number,
|
||||
): null | NavItemWithLink => {
|
||||
const index = postList.findIndex((item) => item.path === currentPath)
|
||||
if (index !== -1) {
|
||||
const targetItem = postList[index + offset]
|
||||
if (!targetItem?.path) {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
link: targetItem.path,
|
||||
text: targetItem.title,
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const usePageNav = () => {
|
||||
const route = useRoute()
|
||||
const page = usePageData<PlumeThemePageData>()
|
||||
const frontmatter = usePageFrontmatter<PlumeThemePageFrontmatter>()
|
||||
const { sidebar } = useSidebar()
|
||||
const postList = useBlogPostData() as unknown as Ref<PlumeThemeBlogPostItem[]>
|
||||
const locale = usePageLang()
|
||||
|
||||
const prevNavList = computed(() => {
|
||||
const prevConfig = resolveFromFrontmatterConfig(frontmatter.value.prev)
|
||||
if (prevConfig !== false) {
|
||||
return prevConfig
|
||||
}
|
||||
if (page.value.isBlogPost) {
|
||||
return resolveFromBlogPostData(
|
||||
postList.value.filter(item => item.lang === locale.value),
|
||||
route.path,
|
||||
-1
|
||||
)
|
||||
} else {
|
||||
return resolveFromSidebarItems(flatSidebar(sidebar.value), route.path, -1)
|
||||
}
|
||||
})
|
||||
|
||||
const nextNavList = computed(() => {
|
||||
const nextConfig = resolveFromFrontmatterConfig(frontmatter.value.next)
|
||||
if (nextConfig !== false) {
|
||||
return nextConfig
|
||||
}
|
||||
if (page.value.isBlogPost) {
|
||||
return resolveFromBlogPostData(
|
||||
postList.value.filter(item => item.lang === locale.value),
|
||||
route.path,
|
||||
1
|
||||
)
|
||||
} else {
|
||||
return resolveFromSidebarItems(flatSidebar(sidebar.value), route.path, 1)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
prev: prevNavList,
|
||||
next: nextNavList,
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,13 @@
|
||||
import { isFunction, isString } from '@vuepress/shared'
|
||||
import { useRouter } from 'vue-router'
|
||||
import type { Router } from 'vue-router'
|
||||
import type { NavItemWithLink } from '../../shared/index.js'
|
||||
|
||||
declare module 'vue-router' {
|
||||
interface RouteMeta {
|
||||
title?: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a route with redirection
|
||||
@ -26,3 +33,21 @@ export const useResolveRouteWithRedirect = (
|
||||
...resolvedRedirectObj,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve NavLink props from string
|
||||
*
|
||||
* @example
|
||||
* - Input: '/README.md'
|
||||
* - Output: { text: 'Home', link: '/' }
|
||||
*/
|
||||
export const useNavLink = (item: string): NavItemWithLink => {
|
||||
// the route path of vue-router is url-encoded, and we expect users are using
|
||||
// non-url-encoded string in theme config, so we need to url-encode it first to
|
||||
// resolve the route correctly
|
||||
const resolved = useResolveRouteWithRedirect(encodeURI(item))
|
||||
return {
|
||||
text: resolved.meta.title || item,
|
||||
link: resolved.name === '404' ? item : resolved.fullPath,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,3 +2,5 @@ export * from './shared.js'
|
||||
export * from './normalizeLink.js'
|
||||
export * from './socialIcons.js'
|
||||
export * from './dom.js'
|
||||
export * from './resolveEditLink.js'
|
||||
export * from './resolveRepoType.js'
|
||||
|
||||
64
theme/src/client/utils/resolveEditLink.ts
Normal file
64
theme/src/client/utils/resolveEditLink.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import {
|
||||
isLinkHttp,
|
||||
removeEndingSlash,
|
||||
removeLeadingSlash,
|
||||
} from '@vuepress/shared'
|
||||
import { resolveRepoType } from './resolveRepoType.js'
|
||||
import type { RepoType } from './resolveRepoType.js'
|
||||
|
||||
export const editLinkPatterns: Record<Exclude<RepoType, null>, string> = {
|
||||
GitHub: ':repo/edit/:branch/:path',
|
||||
GitLab: ':repo/-/edit/:branch/:path',
|
||||
Gitee: ':repo/edit/:branch/:path',
|
||||
Bitbucket:
|
||||
':repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default',
|
||||
}
|
||||
|
||||
const resolveEditLinkPatterns = ({
|
||||
docsRepo,
|
||||
editLinkPattern,
|
||||
}: {
|
||||
docsRepo: string
|
||||
editLinkPattern?: string
|
||||
}): string | null => {
|
||||
if (editLinkPattern) {
|
||||
return editLinkPattern
|
||||
}
|
||||
|
||||
const repoType = resolveRepoType(docsRepo)
|
||||
if (repoType !== null) {
|
||||
return editLinkPatterns[repoType]
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export const resolveEditLink = ({
|
||||
docsRepo,
|
||||
docsBranch,
|
||||
docsDir,
|
||||
filePathRelative,
|
||||
editLinkPattern,
|
||||
}: {
|
||||
docsRepo: string
|
||||
docsBranch: string
|
||||
docsDir: string
|
||||
filePathRelative: string | null
|
||||
editLinkPattern?: string
|
||||
}): string | null => {
|
||||
if (!filePathRelative) return null
|
||||
|
||||
const pattern = resolveEditLinkPatterns({ docsRepo, editLinkPattern })
|
||||
if (!pattern) return null
|
||||
|
||||
return pattern
|
||||
.replace(
|
||||
/:repo/,
|
||||
isLinkHttp(docsRepo) ? docsRepo : `https://github.com/${docsRepo}`,
|
||||
)
|
||||
.replace(/:branch/, docsBranch)
|
||||
.replace(
|
||||
/:path/,
|
||||
removeLeadingSlash(`${removeEndingSlash(docsDir)}/${filePathRelative}`),
|
||||
)
|
||||
}
|
||||
11
theme/src/client/utils/resolveRepoType.ts
Normal file
11
theme/src/client/utils/resolveRepoType.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { isLinkHttp } from '@vuepress/shared'
|
||||
|
||||
export type RepoType = 'GitHub' | 'GitLab' | 'Gitee' | 'Bitbucket' | null
|
||||
|
||||
export const resolveRepoType = (repo: string): RepoType => {
|
||||
if (!isLinkHttp(repo) || /github\.com/.test(repo)) return 'GitHub'
|
||||
if (/bitbucket\.org/.test(repo)) return 'Bitbucket'
|
||||
if (/gitlab\.com/.test(repo)) return 'GitLab'
|
||||
if (/gitee\.com/.test(repo)) return 'Gitee'
|
||||
return null
|
||||
}
|
||||
@ -97,9 +97,9 @@ export const setupPlugins = (
|
||||
|
||||
options.git !== false
|
||||
? gitPlugin({
|
||||
createdTime: true,
|
||||
updatedTime: true,
|
||||
contributors: false,
|
||||
createdTime: false,
|
||||
updatedTime: localeOptions.lastUpdated !== false,
|
||||
contributors: localeOptions.contributors !== false,
|
||||
})
|
||||
: [],
|
||||
|
||||
@ -141,7 +141,10 @@ export const setupPlugins = (
|
||||
? docsearchPlugin(options.docsearch!)
|
||||
: [],
|
||||
|
||||
options.shikiji !== false ? shikijiPlugin(options.shikiji) : [],
|
||||
options.shikiji !== false ? shikijiPlugin({
|
||||
theme: { light: 'vitesse-light', dark: 'vitesse-dark' },
|
||||
...(options.shikiji ?? {}),
|
||||
}) : [],
|
||||
|
||||
options.copyCode !== false
|
||||
? copyCodePlugin({
|
||||
@ -154,7 +157,7 @@ export const setupPlugins = (
|
||||
? mdEnhancePlugin(
|
||||
Object.assign(
|
||||
{
|
||||
container: true, // info note tip warning danger details
|
||||
hint: true, // info note tip warning danger details d
|
||||
codetabs: true,
|
||||
tabs: true,
|
||||
align: true,
|
||||
|
||||
@ -34,6 +34,8 @@ export const plumeTheme = ({
|
||||
plugins: setupPlugins(app, themePlugins, localeOptions),
|
||||
onInitialized: async (app) => await setupPage(app, localeOptions),
|
||||
extendsPage: (page: Page<PlumeThemePageData>) => {
|
||||
page.data.filePathRelative = page.filePathRelative
|
||||
page.routeMeta.title = page.title
|
||||
autoCategory(app, page, localeOptions)
|
||||
pageContentRendered(page)
|
||||
},
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import type { NavItemWithLink } from ".";
|
||||
|
||||
export interface PlumeThemeHomeFrontmatter {
|
||||
home?: true
|
||||
banner?: string
|
||||
@ -15,7 +17,17 @@ export interface PlumeThemeHeroAction {
|
||||
text: string
|
||||
link?: string
|
||||
}
|
||||
export interface PlumeThemePostFrontmatter {
|
||||
|
||||
export interface PlumeThemePageFrontmatter {
|
||||
editLink?: boolean
|
||||
editLinkPattern?: string
|
||||
lastUpdated?: boolean
|
||||
contributors?: boolean
|
||||
prev?: string | NavItemWithLink
|
||||
next?: string | NavItemWithLink
|
||||
}
|
||||
|
||||
export interface PlumeThemePostFrontmatter extends PlumeThemePageFrontmatter {
|
||||
createTime?: string
|
||||
author?: string
|
||||
tags?: string[]
|
||||
@ -24,6 +36,6 @@ export interface PlumeThemePostFrontmatter {
|
||||
banner?: string
|
||||
}
|
||||
|
||||
export interface PlumeThemeNoteFrontmatter {
|
||||
export interface PlumeThemeNoteFrontmatter extends PlumeThemePageFrontmatter {
|
||||
createTime?: string
|
||||
}
|
||||
|
||||
@ -160,11 +160,11 @@ export interface PlumeThemeLocaleData extends LocaleData {
|
||||
/**
|
||||
* repository of navbar
|
||||
*/
|
||||
// repo?: null | string
|
||||
repo?: null | string
|
||||
/**
|
||||
* repository text of navbar
|
||||
*/
|
||||
// repoLabel?: string
|
||||
repoLabel?: string
|
||||
|
||||
/**
|
||||
* Navbar config
|
||||
@ -172,6 +172,76 @@ export interface PlumeThemeLocaleData extends LocaleData {
|
||||
* Set to `false` to disable navbar in current locale
|
||||
*/
|
||||
navbar?: false | NavItem[]
|
||||
/**
|
||||
* Page meta - edit link config
|
||||
*
|
||||
* Whether to show "Edit this page" or not
|
||||
*/
|
||||
editLink?: boolean
|
||||
|
||||
/**
|
||||
* Page meta - edit link config
|
||||
*
|
||||
* The text to replace the default "Edit this page"
|
||||
*/
|
||||
editLinkText?: string
|
||||
|
||||
/**
|
||||
* Page meta - edit link config
|
||||
*
|
||||
* Pattern of edit link
|
||||
*
|
||||
* @example ':repo/edit/:branch/:path'
|
||||
*/
|
||||
editLinkPattern?: string
|
||||
/**
|
||||
* Page meta - edit link config
|
||||
*
|
||||
* Use `repo` config by default
|
||||
*
|
||||
* Set this config if your docs is placed in a different repo
|
||||
*/
|
||||
docsRepo?: string
|
||||
|
||||
/**
|
||||
* Page meta - edit link config
|
||||
*
|
||||
* Set this config if the branch of your docs is not 'main'
|
||||
*/
|
||||
docsBranch?: string
|
||||
|
||||
/**
|
||||
* Page meta - edit link config
|
||||
*
|
||||
* Set this config if your docs is placed in sub dir of your `docsRepo`
|
||||
*/
|
||||
docsDir?: string
|
||||
/**
|
||||
* Page meta - last updated config
|
||||
*
|
||||
* Whether to show "Last Updated" or not
|
||||
*/
|
||||
lastUpdated?: boolean
|
||||
|
||||
/**
|
||||
* Page meta - last updated config
|
||||
*
|
||||
* The text to replace the default "Last Updated"
|
||||
*/
|
||||
lastUpdatedText?: string
|
||||
|
||||
/**
|
||||
* Page meta - contributors config
|
||||
*
|
||||
* Whether to show "Contributors" or not
|
||||
*/
|
||||
contributors?: boolean
|
||||
/**
|
||||
* Page meta - contributors config
|
||||
*
|
||||
* The text to replace the default "Contributors"
|
||||
*/
|
||||
contributorsText?: string
|
||||
/**
|
||||
* 外部链接打开方式
|
||||
*/
|
||||
@ -191,6 +261,10 @@ export interface PlumeThemeLocaleData extends LocaleData {
|
||||
|
||||
outlineLabel?: string
|
||||
|
||||
prevPageLabel?: string
|
||||
|
||||
nextPageLabel?: string
|
||||
|
||||
footer?:
|
||||
| false
|
||||
| {
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
export interface PlumeThemePageData {
|
||||
git: {
|
||||
createTime: number
|
||||
updateTime: number
|
||||
}
|
||||
import type { GitPluginPageData } from '@vuepress/plugin-git'
|
||||
|
||||
export interface PlumeThemePageData extends GitPluginPageData {
|
||||
isBlogPost: boolean
|
||||
type: 'blog' | 'product'
|
||||
categoryList?: PageCategoryData[]
|
||||
filePathRelative: string | null
|
||||
}
|
||||
|
||||
export interface PageCategoryData {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user